• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! Helpers to calculate values derived from page size.
6 //!
7 //! This has performance benefits from:
8 //!
9 //! * Avoiding calling `sysconf(_SC_PAGESIZE)` multiple times by caching the shift bit.
10 //! * Using the (faster) shift instruction instead of (slower) multiply/divide instruction.
11 
12 use std::fs;
13 use std::str;
14 
15 use anyhow::Context;
16 use base::info;
17 use base::pagesize;
18 use once_cell::sync::Lazy;
19 
20 const TRANSPARENT_HUGEPAGE_SIZE_PATH: &str = "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size";
21 
22 static PAGESIZE_SHIFT: Lazy<u8> = Lazy::new(|| {
23     let pagesize_shift = pagesize().trailing_zeros();
24     // pagesize() should be power of 2 in almost all cases. vmm-swap feature does not support
25     // systems in which page size is not power of 2.
26     if 1 << pagesize_shift != pagesize() {
27         panic!("page size is not power of 2");
28     }
29     // pagesize_shift must be less than 64 since usize has at most 64 bits.
30     pagesize_shift as u8
31 });
32 
33 /// The transparent hugepage size loaded from /sys/kernel/mm/transparent_hugepage/hpage_pmd_size.
34 ///
35 /// If it fails to load the hugepage size, it fallbacks to use 2MB.
36 pub static THP_SIZE: Lazy<usize> = Lazy::new(|| {
37     match load_transparent_hugepage_size() {
38         Ok(transparent_hugepage_size) => transparent_hugepage_size,
39         Err(e) => {
40             info!(
41                 "failed to load huge page size: {:?}, maybe the THP is disabled. \
42                 fallback to 2MB as hugepage size.",
43                 e
44             );
45             2 * 1024 * 1024 // = 2MB
46         }
47     }
48 });
49 
load_transparent_hugepage_size() -> anyhow::Result<usize>50 fn load_transparent_hugepage_size() -> anyhow::Result<usize> {
51     let buf = fs::read(TRANSPARENT_HUGEPAGE_SIZE_PATH).context("read thp size file")?;
52     let text = str::from_utf8(&buf).context("utf8")?;
53     let hugepage_size = text.trim().parse::<usize>().context("parse usize")?;
54     Ok(hugepage_size)
55 }
56 
57 /// Helper methods to calculate values derived from page size.
58 ///
59 /// This has performance benefits from:
60 ///
61 /// * Avoiding calling `sysconf(_SC_PAGESIZE)` multiple times by caching the shift bit.
62 /// * Using the (faster) shift instruction instead of (slower) multiply/divide instruction.
63 #[derive(Clone, Copy, Debug)]
64 pub struct PagesizeShift(u8);
65 
66 /// The page index of the page which contains the "addr".
67 #[inline]
addr_to_page_idx(addr: usize) -> usize68 pub fn addr_to_page_idx(addr: usize) -> usize {
69     addr >> *PAGESIZE_SHIFT
70 }
71 
72 /// The head address of the page.
73 #[inline]
page_idx_to_addr(page_idx: usize) -> usize74 pub fn page_idx_to_addr(page_idx: usize) -> usize {
75     page_idx << *PAGESIZE_SHIFT
76 }
77 
78 /// The head address of the page which contains the "addr".
79 #[inline]
page_base_addr(addr: usize) -> usize80 pub fn page_base_addr(addr: usize) -> usize {
81     let pagesize_shift = *PAGESIZE_SHIFT;
82     (addr >> pagesize_shift) << pagesize_shift
83 }
84 
85 /// Returns whether the address/size is aligned with page.
86 #[inline]
is_page_aligned(v: usize) -> bool87 pub fn is_page_aligned(v: usize) -> bool {
88     let mask = (1 << *PAGESIZE_SHIFT) - 1;
89     v & mask == 0
90 }
91 
92 /// Converts the bytes to number of pages.
93 ///
94 /// This rounds down if the `size_in_bytes` is not multiple of page size.
95 #[inline]
bytes_to_pages(size_in_bytes: usize) -> usize96 pub fn bytes_to_pages(size_in_bytes: usize) -> usize {
97     size_in_bytes >> *PAGESIZE_SHIFT
98 }
99 
100 /// Converts number of pages to byte size.
101 #[inline]
pages_to_bytes(num_of_pages: usize) -> usize102 pub fn pages_to_bytes(num_of_pages: usize) -> usize {
103     num_of_pages << *PAGESIZE_SHIFT
104 }
105 
106 /// Returns whether the address/size is aligned with hugepage.
107 #[inline]
is_hugepage_aligned(v: usize) -> bool108 pub fn is_hugepage_aligned(v: usize) -> bool {
109     v & (*THP_SIZE - 1) == 0
110 }
111 
112 /// Rounds up the address/size with the hugepage size.
113 #[inline]
round_up_hugepage_size(v: usize) -> usize114 pub fn round_up_hugepage_size(v: usize) -> usize {
115     let hugepage_size = *THP_SIZE;
116     (v + hugepage_size - 1) & !(hugepage_size - 1)
117 }
118 
119 #[cfg(test)]
120 mod tests {
121 
122     use super::*;
123 
124     #[test]
test_addr_to_page_idx()125     fn test_addr_to_page_idx() {
126         let addr = 10 * pagesize();
127         assert_eq!(addr_to_page_idx(addr - 1), 9);
128         assert_eq!(addr_to_page_idx(addr), 10);
129         assert_eq!(addr_to_page_idx(addr + 1), 10);
130     }
131 
132     #[test]
test_page_idx_to_addr()133     fn test_page_idx_to_addr() {
134         assert_eq!(page_idx_to_addr(10), 10 * pagesize());
135     }
136 
137     #[test]
test_page_base_addr()138     fn test_page_base_addr() {
139         let addr = 10 * pagesize();
140         assert_eq!(page_base_addr(addr - 1), addr - pagesize());
141         assert_eq!(page_base_addr(addr), addr);
142         assert_eq!(page_base_addr(addr + 1), addr);
143     }
144 
145     #[test]
test_is_page_aligned()146     fn test_is_page_aligned() {
147         let addr = 10 * pagesize();
148         assert!(!is_page_aligned(addr - 1));
149         assert!(is_page_aligned(addr));
150         assert!(!is_page_aligned(addr + 1));
151     }
152 
153     #[test]
test_bytes_to_pages()154     fn test_bytes_to_pages() {
155         assert_eq!(bytes_to_pages(10 * pagesize()), 10);
156         assert_eq!(bytes_to_pages(10 * pagesize() + 1), 10);
157     }
158 
159     #[test]
test_pages_to_bytes()160     fn test_pages_to_bytes() {
161         assert_eq!(pages_to_bytes(1), pagesize());
162         assert_eq!(pages_to_bytes(10), 10 * pagesize());
163     }
164 
165     #[test]
test_is_hugepage_aligned()166     fn test_is_hugepage_aligned() {
167         let addr = 10 * *THP_SIZE;
168         assert!(!is_hugepage_aligned(addr - 1));
169         assert!(is_hugepage_aligned(addr));
170         assert!(!is_hugepage_aligned(addr - 1));
171         assert!(!is_hugepage_aligned(pagesize()));
172     }
173 
174     #[test]
test_round_up_hugepage_size()175     fn test_round_up_hugepage_size() {
176         let addr = 10 * *THP_SIZE;
177 
178         assert_eq!(round_up_hugepage_size(0), 0);
179         assert_eq!(round_up_hugepage_size(addr - 1), addr);
180         assert_eq!(round_up_hugepage_size(addr), addr);
181         assert_eq!(round_up_hugepage_size(addr + 1), addr + *THP_SIZE);
182         assert_eq!(round_up_hugepage_size(pagesize()), *THP_SIZE);
183     }
184 }
185