1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! This module provides functions to load zram stats.
16 
17 #[cfg(test)]
18 mod tests;
19 
20 use std::ops::Deref;
21 
22 use crate::zram::SysfsZramApi;
23 
24 /// Error from loading zram stats.
25 #[derive(Debug, thiserror::Error)]
26 pub enum Error {
27     /// Stat file format is invalid.
28     #[error("failed to parse")]
29     Parse,
30     /// Failed to read stat file.
31     #[error("failed to read: {0}")]
32     Io(#[from] std::io::Error),
33 }
34 
35 type Result<T> = std::result::Result<T, Error>;
36 
parse_next<T: std::str::FromStr>( iter: &mut impl Iterator<Item = impl Deref<Target = str>>, ) -> Result<T>37 fn parse_next<T: std::str::FromStr>(
38     iter: &mut impl Iterator<Item = impl Deref<Target = str>>,
39 ) -> Result<T> {
40     iter.next().ok_or(Error::Parse)?.parse().map_err(|_| Error::Parse)
41 }
42 
parse_next_optional<T: std::str::FromStr>( iter: &mut impl Iterator<Item = impl Deref<Target = str>>, ) -> Result<Option<T>>43 fn parse_next_optional<T: std::str::FromStr>(
44     iter: &mut impl Iterator<Item = impl Deref<Target = str>>,
45 ) -> Result<Option<T>> {
46     iter.next().map(|v| v.parse()).transpose().map_err(|_| Error::Parse)
47 }
48 
49 /// Loads /sys/block/zram0/disksize
load_total_zram_size<Z: SysfsZramApi>() -> Result<u64>50 pub fn load_total_zram_size<Z: SysfsZramApi>() -> Result<u64> {
51     let contents = Z::read_disksize()?;
52     contents.trim().parse().map_err(|_| Error::Parse)
53 }
54 
55 /// Stats from /sys/block/zram0/mm_stat
56 #[derive(Debug, Default, PartialEq, Eq)]
57 pub struct ZramMmStat {
58     /// Uncompressed size of data stored in this disk. This excludes same-element-filled pages
59     /// (same_pages) since no memory is allocated for them. Unit: bytes
60     pub orig_data_size: u64,
61     /// Compressed size of data stored in this disk.
62     pub compr_data_size: u64,
63     /// The amount of memory allocated for this disk. This includes allocator fragmentation and
64     /// metadata overhead, allocated for this disk. So, allocator space efficiency can be calculated
65     /// using compr_data_size and this statistic. Unit: bytes
66     pub mem_used_total: u64,
67     /// The maximum amount of memory ZRAM can use to store The compressed data.
68     pub mem_limit: u32,
69     /// The maximum amount of memory zram have consumed to store the data.
70     ///
71     /// In zram_drv.h we define max_used_pages as atomic_long_t which could be negative, but
72     /// negative value does not make sense for the variable.
73     pub mem_used_max: i64,
74     /// The number of same element filled pages written to this disk. No memory is allocated for
75     /// such pages.
76     pub same_pages: u64,
77     /// The number of pages freed during compaction.
78     pub pages_compacted: u32,
79     /// The number of incompressible pages.
80     /// Start supporting from v4.19.
81     pub huge_pages: Option<u64>,
82     /// The number of huge pages since zram set up.
83     /// Start supporting from v5.15.
84     pub huge_pages_since: Option<u64>,
85 }
86 
87 impl ZramMmStat {
88     /// Parse /sys/block/zram0/mm_stat.
load<Z: SysfsZramApi>() -> Result<Self>89     pub fn load<Z: SysfsZramApi>() -> Result<Self> {
90         let contents = Z::read_mm_stat()?;
91         let mut values = contents.split_whitespace();
92         Ok(ZramMmStat {
93             orig_data_size: parse_next(&mut values)?,
94             compr_data_size: parse_next(&mut values)?,
95             mem_used_total: parse_next(&mut values)?,
96             mem_limit: parse_next(&mut values)?,
97             mem_used_max: parse_next(&mut values)?,
98             same_pages: parse_next(&mut values)?,
99             pages_compacted: parse_next(&mut values)?,
100             huge_pages: parse_next_optional(&mut values)?,
101             huge_pages_since: parse_next_optional(&mut values)?,
102         })
103     }
104 }
105 
106 /// Stats from /sys/block/zram0/bd_stat
107 #[derive(Debug, Default, PartialEq, Eq)]
108 pub struct ZramBdStat {
109     /// Size of data written in backing device. Unit: page
110     pub bd_count_pages: u64,
111     /// The number of reads from backing device. Unit: page
112     pub bd_reads_pages: u64,
113     /// The number of writes to backing device. Unit: page
114     pub bd_writes_pages: u64,
115 }
116 
117 impl ZramBdStat {
118     /// Parse /sys/block/zram0/bd_stat.
load<Z: SysfsZramApi>() -> Result<Self>119     pub fn load<Z: SysfsZramApi>() -> Result<Self> {
120         let contents = Z::read_bd_stat()?;
121         let mut values = contents.split_whitespace();
122         Ok(ZramBdStat {
123             bd_count_pages: parse_next(&mut values)?,
124             bd_reads_pages: parse_next(&mut values)?,
125             bd_writes_pages: parse_next(&mut values)?,
126         })
127     }
128 }
129