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