• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2025, 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 utilities to report Atoms for mmd managed resources.
16 
17 use log::error;
18 use mmd::os::get_page_size;
19 use mmd::zram::recompression::Error as ZramRecompressionError;
20 use mmd::zram::setup::ZramActivationError;
21 use mmd::zram::stats::ZramBdStat;
22 use mmd::zram::stats::ZramMmStat;
23 use mmd::zram::writeback::Error as ZramWritebackError;
24 use mmd::zram::writeback::WritebackDetails;
25 use mmd::zram::SysfsZramApi;
26 use mmd::zram::SysfsZramApiImpl;
27 use statslog_rust::zram_bd_stat_mmd::ZramBdStatMmd;
28 use statslog_rust::zram_maintenance_executed::RecompressionResult;
29 use statslog_rust::zram_maintenance_executed::WritebackResult;
30 use statslog_rust::zram_maintenance_executed::ZramMaintenanceExecuted;
31 use statslog_rust::zram_mm_stat_mmd::ZramMmStatMmd;
32 use statslog_rust::zram_setup_executed::CompAlgorithmSetupResult;
33 use statslog_rust::zram_setup_executed::RecompressionSetupResult;
34 use statslog_rust::zram_setup_executed::WritebackSetupResult;
35 use statslog_rust::zram_setup_executed::ZramSetupExecuted;
36 use statslog_rust::zram_setup_executed::ZramSetupResult;
37 use statspull_rust::StatsPullResult;
38 
39 const KB: u64 = 1024;
40 
41 /// Converts u64 number to i64
42 ///
43 /// If the value is more than i64::MAX, i64::MAX is returned.
u64_to_i64(v: u64) -> i6444 fn u64_to_i64(v: u64) -> i64 {
45     // The try_into() conversion fails only if the value is more than i64::MAX.
46     v.try_into().unwrap_or(i64::MAX)
47 }
48 
49 /// Create the default ZramMaintenanceExecuted
create_default_maintenance_atom() -> ZramMaintenanceExecuted50 pub fn create_default_maintenance_atom() -> ZramMaintenanceExecuted {
51     ZramMaintenanceExecuted {
52         writeback_result: WritebackResult::WritebackNotSupported,
53         writeback_huge_idle_pages: 0,
54         writeback_huge_pages: 0,
55         writeback_idle_pages: 0,
56         writeback_latency_millis: 0,
57         writeback_limit_kb: 0,
58         writeback_daily_limit_kb: 0,
59         writeback_actual_limit_kb: 0,
60         writeback_total_kb: 0,
61         recompression_result: RecompressionResult::RecompressionNotSupported,
62         recompress_latency_millis: 0,
63         interval_from_previous_seconds: 0,
64     }
65 }
66 
67 /// Update [ZramMaintenanceExecuted] based on the result of zram writeback.
update_writeback_metrics( atom: &mut ZramMaintenanceExecuted, result: &Result<WritebackDetails, ZramWritebackError>, )68 pub fn update_writeback_metrics(
69     atom: &mut ZramMaintenanceExecuted,
70     result: &Result<WritebackDetails, ZramWritebackError>,
71 ) {
72     atom.writeback_result = match result {
73         Ok(_) => WritebackResult::WritebackSuccess,
74         Err(ZramWritebackError::BackoffTime) => WritebackResult::WritebackBackoffTime,
75         Err(ZramWritebackError::Limit) => WritebackResult::WritebackLimit,
76         Err(ZramWritebackError::InvalidWritebackLimit) => WritebackResult::WritebackInvalidLimit,
77         Err(ZramWritebackError::CalculateIdle(_)) => WritebackResult::WritebackCalculateIdleFail,
78         Err(ZramWritebackError::MarkIdle(_)) => WritebackResult::WritebackMarkIdleFail,
79         Err(ZramWritebackError::Writeback(_)) => WritebackResult::WritebackTriggerFail,
80         Err(ZramWritebackError::WritebackLimit(_)) => {
81             WritebackResult::WritebackAccessWritebackLimitFail
82         }
83     };
84 
85     if let Ok(details) = result {
86         let kb_per_page = get_page_size() / KB;
87         atom.writeback_huge_idle_pages = u64_to_i64(details.huge_idle.written_pages);
88         atom.writeback_idle_pages = u64_to_i64(details.idle.written_pages);
89         atom.writeback_huge_pages = u64_to_i64(details.huge.written_pages);
90         atom.writeback_limit_kb = u64_to_i64(details.limit_pages.saturating_mul(kb_per_page));
91         atom.writeback_daily_limit_kb =
92             u64_to_i64(details.daily_limit_pages.saturating_mul(kb_per_page));
93         atom.writeback_actual_limit_kb =
94             u64_to_i64(details.actual_limit_pages.saturating_mul(kb_per_page));
95         atom.writeback_total_kb = u64_to_i64(
96             (details
97                 .huge_idle
98                 .written_pages
99                 .saturating_add(details.idle.written_pages)
100                 .saturating_add(details.huge.written_pages))
101             .saturating_mul(kb_per_page),
102         );
103     }
104 }
105 
106 /// Update [ZramMaintenanceExecuted] based on the result of zram recompression.
update_recompress_metrics( atom: &mut ZramMaintenanceExecuted, result: &Result<(), ZramRecompressionError>, )107 pub fn update_recompress_metrics(
108     atom: &mut ZramMaintenanceExecuted,
109     result: &Result<(), ZramRecompressionError>,
110 ) {
111     atom.recompression_result = match result {
112         Ok(_) => RecompressionResult::RecompressionSuccess,
113         Err(ZramRecompressionError::BackoffTime) => RecompressionResult::RecompressionBackoffTime,
114         Err(ZramRecompressionError::CalculateIdle(_)) => {
115             RecompressionResult::RecompressionCalculateIdleFail
116         }
117         Err(ZramRecompressionError::MarkIdle(_)) => RecompressionResult::RecompressionMarkIdleFail,
118         Err(ZramRecompressionError::Recompress(_)) => RecompressionResult::RecompressionTriggerFail,
119     };
120 }
121 
122 /// Reports ZramMmStatMmd atom.
report_zram_mm_stat() -> StatsPullResult123 pub fn report_zram_mm_stat() -> StatsPullResult {
124     match generate_zram_mm_stat_atom::<SysfsZramApiImpl>() {
125         Ok(atom) => vec![Box::new(atom)],
126         Err(e) => {
127             error!("failed to load mm stat atom: {:?}", e);
128             vec![]
129         }
130     }
131 }
132 
generate_zram_mm_stat_atom<Z: SysfsZramApi>() -> Result<ZramMmStatMmd, mmd::zram::stats::Error>133 fn generate_zram_mm_stat_atom<Z: SysfsZramApi>() -> Result<ZramMmStatMmd, mmd::zram::stats::Error> {
134     let stat = ZramMmStat::load::<Z>()?;
135     let kb_per_page = get_page_size() / KB;
136     Ok(ZramMmStatMmd {
137         orig_data_kb: u64_to_i64(stat.orig_data_size / KB),
138         compr_data_kb: u64_to_i64(stat.compr_data_size / KB),
139         mem_used_total_kb: u64_to_i64(stat.mem_used_total / KB),
140         mem_limit_kb: (stat.mem_limit / (KB as u32)).into(),
141         mem_used_max_kb: stat.mem_used_max / (KB as i64),
142         same_pages_kb: u64_to_i64(stat.same_pages.saturating_mul(kb_per_page)),
143         pages_compacted_kb: (stat.pages_compacted as i64).saturating_mul(kb_per_page as i64),
144         huge_pages_kb: u64_to_i64(stat.huge_pages.unwrap_or(0).saturating_mul(kb_per_page)),
145         huge_pages_since_kb: u64_to_i64(
146             stat.huge_pages_since.unwrap_or(0).saturating_mul(kb_per_page),
147         ),
148     })
149 }
150 
151 /// Reports ZramBdStatMmd atom.
report_zram_bd_stat() -> StatsPullResult152 pub fn report_zram_bd_stat() -> StatsPullResult {
153     match generate_zram_bd_stat_atom::<SysfsZramApiImpl>() {
154         Ok(atom) => vec![Box::new(atom)],
155         Err(e) => {
156             error!("failed to load bd stat atom: {:?}", e);
157             vec![]
158         }
159     }
160 }
161 
generate_zram_bd_stat_atom<Z: SysfsZramApi>() -> Result<ZramBdStatMmd, mmd::zram::stats::Error>162 fn generate_zram_bd_stat_atom<Z: SysfsZramApi>() -> Result<ZramBdStatMmd, mmd::zram::stats::Error> {
163     let stat = ZramBdStat::load::<Z>()?;
164     let kb_per_page = get_page_size() / KB;
165     Ok(ZramBdStatMmd {
166         bd_count_kb: u64_to_i64(stat.bd_count_pages.saturating_mul(kb_per_page)),
167         bd_reads_kb: u64_to_i64(stat.bd_reads_pages.saturating_mul(kb_per_page)),
168         bd_writes_kb: u64_to_i64(stat.bd_writes_pages.saturating_mul(kb_per_page)),
169     })
170 }
171 
172 /// Update [ZramSetupExecuted] based on the result of zram activation.
update_zram_setup_metrics( atom: &mut ZramSetupExecuted, result: &Result<(), ZramActivationError>, )173 pub fn update_zram_setup_metrics(
174     atom: &mut ZramSetupExecuted,
175     result: &Result<(), ZramActivationError>,
176 ) {
177     atom.zram_setup_result = match result {
178         Ok(_) => ZramSetupResult::ZramSetupSuccess,
179         Err(ZramActivationError::UpdateZramDiskSize(_)) => {
180             ZramSetupResult::ZramSetupUpdateDiskSizeFail
181         }
182         Err(ZramActivationError::SwapOn(_)) => ZramSetupResult::ZramSetupSwapOnFail,
183         Err(ZramActivationError::ExecuteMkSwap(_)) | Err(ZramActivationError::MkSwap(_)) => {
184             ZramSetupResult::ZramSetupMkSwapFail
185         }
186     };
187 }
188 
189 /// Create the default ZramSetupExecuted atom.
create_default_setup_atom() -> ZramSetupExecuted190 pub fn create_default_setup_atom() -> ZramSetupExecuted {
191     ZramSetupExecuted {
192         zram_setup_result: ZramSetupResult::ZramSetupUnspecified,
193         comp_algorithm_setup_result: CompAlgorithmSetupResult::CompAlgorithmSetupUnspecified,
194         writeback_setup_result: WritebackSetupResult::WritebackSetupUnspecified,
195         recompression_setup_result: RecompressionSetupResult::RecompressionSetupUnspecified,
196         zram_size_mb: 0,
197         writeback_size_mb: 0,
198     }
199 }
200 
201 #[cfg(test)]
202 mod tests {
203     use mmd::zram::writeback::WritebackModeDetails;
204     use mmd::zram::MockSysfsZramApi;
205     use mmd::zram::ZRAM_API_MTX;
206 
207     use super::*;
208 
209     #[test]
test_update_writeback_metrics_success()210     fn test_update_writeback_metrics_success() {
211         let mut atom = create_default_maintenance_atom();
212 
213         update_writeback_metrics(
214             &mut atom,
215             &Ok(WritebackDetails {
216                 huge_idle: WritebackModeDetails { written_pages: 1 },
217                 idle: WritebackModeDetails { written_pages: 12345 },
218                 huge: WritebackModeDetails { written_pages: u64::MAX },
219                 limit_pages: 1,
220                 daily_limit_pages: 6789,
221                 actual_limit_pages: u64::MAX,
222             }),
223         );
224 
225         assert!(matches!(atom.writeback_result, WritebackResult::WritebackSuccess));
226         assert_eq!(atom.writeback_huge_idle_pages, 1);
227         assert_eq!(atom.writeback_idle_pages, 12345);
228         assert_eq!(atom.writeback_huge_pages, i64::MAX);
229         let kb_per_page = get_page_size() as i64 / 1024;
230         assert_eq!(atom.writeback_limit_kb, kb_per_page);
231         assert_eq!(atom.writeback_daily_limit_kb, 6789 * kb_per_page);
232         assert_eq!(atom.writeback_actual_limit_kb, i64::MAX);
233         assert_eq!(atom.writeback_total_kb, i64::MAX);
234     }
235 
236     #[test]
test_update_writeback_metrics_writeback_total_kb()237     fn test_update_writeback_metrics_writeback_total_kb() {
238         let mut atom = create_default_maintenance_atom();
239 
240         update_writeback_metrics(
241             &mut atom,
242             &Ok(WritebackDetails {
243                 huge_idle: WritebackModeDetails { written_pages: 10 },
244                 idle: WritebackModeDetails { written_pages: 200 },
245                 huge: WritebackModeDetails { written_pages: 3000 },
246                 ..Default::default()
247             }),
248         );
249 
250         let kb_per_page = get_page_size() as i64 / 1024;
251         assert_eq!(atom.writeback_total_kb, 3210 * kb_per_page);
252     }
253 
254     #[test]
test_update_writeback_metrics_on_failure()255     fn test_update_writeback_metrics_on_failure() {
256         let mut atom = create_default_maintenance_atom();
257         update_writeback_metrics(&mut atom, &Err(ZramWritebackError::BackoffTime));
258         assert!(matches!(atom.writeback_result, WritebackResult::WritebackBackoffTime));
259     }
260 
261     #[test]
test_update_recompress_metrics_success()262     fn test_update_recompress_metrics_success() {
263         let mut atom = create_default_maintenance_atom();
264         update_recompress_metrics(&mut atom, &Ok(()));
265         assert!(matches!(atom.recompression_result, RecompressionResult::RecompressionSuccess));
266     }
267 
268     #[test]
test_update_recompress_metrics_on_failure()269     fn test_update_recompress_metrics_on_failure() {
270         let mut atom = create_default_maintenance_atom();
271         update_recompress_metrics(&mut atom, &Err(ZramRecompressionError::BackoffTime));
272         assert!(matches!(atom.recompression_result, RecompressionResult::RecompressionBackoffTime));
273     }
274 
275     #[test]
test_generate_zram_mm_stat_atom()276     fn test_generate_zram_mm_stat_atom() {
277         let _m = ZRAM_API_MTX.lock();
278         let mock = MockSysfsZramApi::read_mm_stat_context();
279         mock.expect().returning(move || {
280             Ok(format!("123456 {} 1023 1024 1235 1 {} 12345 {}", u64::MAX, u32::MAX, u64::MAX))
281         });
282 
283         let result = generate_zram_mm_stat_atom::<MockSysfsZramApi>();
284 
285         assert!(result.is_ok());
286         let kb_per_page = get_page_size() as i64 / 1024;
287         let atom = result.unwrap();
288         assert_eq!(atom.orig_data_kb, 120);
289         assert_eq!(atom.compr_data_kb, (u64::MAX / KB) as i64);
290         assert_eq!(atom.mem_used_total_kb, 0);
291         assert_eq!(atom.mem_limit_kb, 1);
292         assert_eq!(atom.mem_used_max_kb, 1);
293         assert_eq!(atom.same_pages_kb, kb_per_page);
294         assert_eq!(atom.pages_compacted_kb, u32::MAX as i64 * kb_per_page);
295         assert_eq!(atom.huge_pages_kb, 12345 * kb_per_page);
296         assert_eq!(atom.huge_pages_since_kb, i64::MAX);
297     }
298 
299     #[test]
test_generate_zram_mm_stat_atom_without_huge_pages()300     fn test_generate_zram_mm_stat_atom_without_huge_pages() {
301         let _m = ZRAM_API_MTX.lock();
302         let mock = MockSysfsZramApi::read_mm_stat_context();
303         mock.expect().returning(|| Ok("12345 2 3 4 5 6 7".to_string()));
304 
305         let result = generate_zram_mm_stat_atom::<MockSysfsZramApi>();
306 
307         assert!(result.is_ok());
308         let kb_per_page = get_page_size() as i64 / 1024;
309         let atom = result.unwrap();
310         assert_eq!(atom.orig_data_kb, 12);
311         assert_eq!(atom.pages_compacted_kb, 7 * kb_per_page);
312         assert_eq!(atom.huge_pages_kb, 0);
313         assert_eq!(atom.huge_pages_since_kb, 0);
314     }
315 
316     #[test]
test_generate_zram_bd_stat_atom()317     fn test_generate_zram_bd_stat_atom() {
318         let _m = ZRAM_API_MTX.lock();
319         let mock = MockSysfsZramApi::read_bd_stat_context();
320         mock.expect().returning(move || Ok(format!("1 12345 {}", u64::MAX)));
321 
322         let result = generate_zram_bd_stat_atom::<MockSysfsZramApi>();
323 
324         assert!(result.is_ok());
325         let kb_per_page = get_page_size() as i64 / 1024;
326         let atom = result.unwrap();
327         assert_eq!(atom.bd_count_kb, kb_per_page);
328         assert_eq!(atom.bd_reads_kb, 12345 * kb_per_page);
329         assert_eq!(atom.bd_writes_kb, i64::MAX);
330     }
331 }
332