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