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 ¶ms,
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 ¶ms,
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