• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 use std::ops::DerefMut;
16 use std::sync::Arc;
17 use std::sync::Mutex;
18 use std::time::Instant;
19 
20 use anyhow::Context;
21 use binder::Interface;
22 use binder::Result as BinderResult;
23 use log::error;
24 use log::info;
25 use mmd::os::MeminfoApiImpl;
26 use mmd::suspend_history::SuspendHistory;
27 use mmd::time::TimeApi;
28 use mmd::time::TimeApiImpl;
29 use mmd::zram::recompression::Error as ZramRecompressionError;
30 use mmd::zram::recompression::ZramRecompression;
31 use mmd::zram::writeback::Error as ZramWritebackError;
32 use mmd::zram::writeback::ZramWriteback;
33 use mmd::zram::SysfsZramApiImpl;
34 use mmd_aidl_interface::aidl::android::os::IMmd::IMmd;
35 use statslog_rust::zram_maintenance_executed::ZramMaintenanceExecuted;
36 
37 use crate::atom::create_default_maintenance_atom;
38 use crate::atom::update_recompress_metrics;
39 use crate::atom::update_writeback_metrics;
40 use crate::properties::BoolProp;
41 use crate::properties::SecondsProp;
42 use crate::properties::U64Prop;
43 use crate::ZramContext;
44 use crate::DEFAULT_ZRAM_RECOMPRESSION_ENABLED;
45 use crate::DEFAULT_ZRAM_WRITEBACK_ENABLED;
46 
47 pub struct MmdService {
48     ctx: Arc<Mutex<ZramContext>>,
49 }
50 
51 impl MmdService {
new(ctx: Arc<Mutex<ZramContext>>) -> Self52     pub fn new(ctx: Arc<Mutex<ZramContext>>) -> Self {
53         Self { ctx }
54     }
55 }
56 
57 impl Interface for MmdService {}
58 
59 impl IMmd for MmdService {
doZramMaintenanceAsync(&self) -> BinderResult<()>60     fn doZramMaintenanceAsync(&self) -> BinderResult<()> {
61         let mut atom = create_default_maintenance_atom();
62         let mut ctx = self.ctx.lock().expect("mmd aborts on panics");
63 
64         let now = Instant::now();
65         atom.interval_from_previous_seconds =
66             now.duration_since(ctx.last_maintenance_at).as_secs().try_into().unwrap_or(i64::MAX);
67         ctx.last_maintenance_at = now;
68 
69         let ZramContext { zram_writeback, zram_recompression, suspend_history, .. } =
70             ctx.deref_mut();
71 
72         // Execute writeback before recompression. Current kernel decompresses
73         // pages in zram before writing it back to disk.
74         if BoolProp::ZramWritebackEnabled.get(DEFAULT_ZRAM_WRITEBACK_ENABLED) {
75             if let Some(zram_writeback) = zram_writeback.as_mut() {
76                 handle_zram_writeback(zram_writeback, suspend_history, &mut atom);
77             }
78         }
79 
80         if BoolProp::ZramRecompressionEnabled.get(DEFAULT_ZRAM_RECOMPRESSION_ENABLED) {
81             if let Some(zram_recompression) = zram_recompression.as_mut() {
82                 handle_zram_recompression(zram_recompression, suspend_history, &mut atom);
83             }
84         }
85 
86         if let Err(e) = atom.stats_write() {
87             error!("failed to submit ZramMaintenanceExecuted atom: {e:?}");
88         }
89 
90         Ok(())
91     }
92 
isZramMaintenanceSupported(&self) -> BinderResult<bool>93     fn isZramMaintenanceSupported(&self) -> BinderResult<bool> {
94         let ctx = self.ctx.lock().expect("mmd aborts on panics");
95         Ok(ctx.zram_writeback.is_some() || ctx.zram_recompression.is_some())
96     }
97 }
98 
handle_zram_recompression( zram_recompression: &mut ZramRecompression, suspend_history: &SuspendHistory, atom: &mut ZramMaintenanceExecuted, )99 fn handle_zram_recompression(
100     zram_recompression: &mut ZramRecompression,
101     suspend_history: &SuspendHistory,
102     atom: &mut ZramMaintenanceExecuted,
103 ) {
104     let params = load_zram_recompression_params();
105 
106     let start = Instant::now();
107     let result = zram_recompression.mark_and_recompress::<SysfsZramApiImpl, MeminfoApiImpl>(
108         &params,
109         suspend_history,
110         TimeApiImpl::get_boot_time(),
111     );
112     atom.recompress_latency_millis = start.elapsed().as_millis().try_into().unwrap_or(i64::MAX);
113 
114     update_recompress_metrics(atom, &result);
115 
116     match result {
117         Ok(_) | Err(ZramRecompressionError::BackoffTime) => {}
118         Err(e) => error!("failed to zram recompress: {e:?}"),
119     }
120 }
121 
handle_zram_writeback( zram_writeback: &mut ZramWriteback, suspend_history: &SuspendHistory, atom: &mut ZramMaintenanceExecuted, )122 fn handle_zram_writeback(
123     zram_writeback: &mut ZramWriteback,
124     suspend_history: &SuspendHistory,
125     atom: &mut ZramMaintenanceExecuted,
126 ) {
127     let params = load_zram_writeback_params();
128     let stats = match load_zram_writeback_stats() {
129         Ok(v) => v,
130         Err(e) => {
131             error!("failed to load zram writeback stats: {e:?}");
132             atom.writeback_result =
133                 statslog_rust::zram_maintenance_executed::WritebackResult::WritebackLoadStatsFail;
134             return;
135         }
136     };
137 
138     let start = Instant::now();
139     let result = zram_writeback.mark_and_flush_pages::<SysfsZramApiImpl, MeminfoApiImpl>(
140         &params,
141         &stats,
142         suspend_history,
143         TimeApiImpl::get_boot_time(),
144     );
145     atom.writeback_latency_millis = start.elapsed().as_millis().try_into().unwrap_or(i64::MAX);
146 
147     update_writeback_metrics(atom, &result);
148 
149     match result {
150         Ok(details) => {
151             let total_written_pages = details
152                 .huge_idle
153                 .written_pages
154                 .saturating_add(details.idle.written_pages)
155                 .saturating_add(details.huge.written_pages);
156             if total_written_pages > 0 {
157                 info!(
158                     "zram writeback: huge_idle: {} pages, idle: {} pages, huge: {} pages",
159                     details.huge_idle.written_pages,
160                     details.idle.written_pages,
161                     details.huge.written_pages
162                 );
163             }
164         }
165         Err(ZramWritebackError::BackoffTime) | Err(ZramWritebackError::Limit) => {}
166         Err(e) => error!("failed to zram writeback: {e:?}"),
167     }
168 }
169 
load_zram_writeback_params() -> mmd::zram::writeback::Params170 fn load_zram_writeback_params() -> mmd::zram::writeback::Params {
171     let mut params = mmd::zram::writeback::Params::default();
172     params.backoff_duration = SecondsProp::ZramWritebackBackoff.get(params.backoff_duration);
173     params.min_idle = SecondsProp::ZramWritebackMinIdle.get(params.min_idle);
174     params.max_idle = SecondsProp::ZramWritebackMaxIdle.get(params.max_idle);
175     params.huge_idle = BoolProp::ZramWritebackHugeIdleEnabled.get(params.huge_idle);
176     params.idle = BoolProp::ZramWritebackIdleEnabled.get(params.idle);
177     params.huge = BoolProp::ZramWritebackHugeEnabled.get(params.huge);
178     params.min_bytes = U64Prop::ZramWritebackMinBytes.get(params.min_bytes);
179     params.max_bytes = U64Prop::ZramWritebackMaxBytes.get(params.max_bytes);
180     params.max_bytes_per_day = U64Prop::ZramWritebackMaxBytesPerDay.get(params.max_bytes_per_day);
181     params
182 }
183 
load_zram_writeback_stats() -> anyhow::Result<mmd::zram::writeback::Stats>184 fn load_zram_writeback_stats() -> anyhow::Result<mmd::zram::writeback::Stats> {
185     let mm_stat =
186         mmd::zram::stats::ZramMmStat::load::<SysfsZramApiImpl>().context("load mm_stat")?;
187     let bd_stat =
188         mmd::zram::stats::ZramBdStat::load::<SysfsZramApiImpl>().context("load bd_stat")?;
189     Ok(mmd::zram::writeback::Stats {
190         orig_data_size: mm_stat.orig_data_size,
191         current_writeback_pages: bd_stat.bd_count_pages,
192     })
193 }
194 
load_zram_recompression_params() -> mmd::zram::recompression::Params195 fn load_zram_recompression_params() -> mmd::zram::recompression::Params {
196     let mut params = mmd::zram::recompression::Params::default();
197     params.backoff_duration = SecondsProp::ZramRecompressionBackoff.get(params.backoff_duration);
198     params.min_idle = SecondsProp::ZramRecompressionMinIdle.get(params.min_idle);
199     params.max_idle = SecondsProp::ZramRecompressionMaxIdle.get(params.max_idle);
200     params.huge_idle = BoolProp::ZramRecompressionHugeIdleEnabled.get(params.huge_idle);
201     params.idle = BoolProp::ZramRecompressionIdleEnabled.get(params.idle);
202     params.huge = BoolProp::ZramRecompressionHugeEnabled.get(params.huge);
203     params.threshold_bytes = U64Prop::ZramRecompressionThresholdBytes.get(params.threshold_bytes);
204     params
205 }
206 
207 #[cfg(test)]
208 mod tests {
209     use mmd::suspend_history::SuspendHistory;
210     use mmd::zram::recompression::ZramRecompression;
211     use mmd::zram::writeback::ZramWriteback;
212 
213     use super::*;
214 
215     #[test]
test_is_zram_maintenance_supported()216     fn test_is_zram_maintenance_supported() {
217         assert!(!MmdService::new(Arc::new(Mutex::new(ZramContext {
218             zram_writeback: None,
219             zram_recompression: None,
220             suspend_history: SuspendHistory::new(),
221             last_maintenance_at: Instant::now(),
222         })))
223         .isZramMaintenanceSupported()
224         .unwrap());
225         assert!(MmdService::new(Arc::new(Mutex::new(ZramContext {
226             zram_writeback: Some(ZramWriteback::new(1024 * 1024, 1024 * 1024)),
227             zram_recompression: None,
228             suspend_history: SuspendHistory::new(),
229             last_maintenance_at: Instant::now(),
230         })))
231         .isZramMaintenanceSupported()
232         .unwrap());
233         assert!(MmdService::new(Arc::new(Mutex::new(ZramContext {
234             zram_writeback: None,
235             zram_recompression: Some(ZramRecompression::new()),
236             suspend_history: SuspendHistory::new(),
237             last_maintenance_at: Instant::now(),
238         })))
239         .isZramMaintenanceSupported()
240         .unwrap());
241         assert!(MmdService::new(Arc::new(Mutex::new(ZramContext {
242             zram_writeback: Some(ZramWriteback::new(1024 * 1024, 1024 * 1024)),
243             zram_recompression: Some(ZramRecompression::new()),
244             suspend_history: SuspendHistory::new(),
245             last_maintenance_at: Instant::now(),
246         })))
247         .isZramMaintenanceSupported()
248         .unwrap());
249     }
250 }
251