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 policy to manage zram writeback feature.
16 //!
17 //! See "writeback" section in the kernel document for details.
18 //!
19 //! https://www.kernel.org/doc/Documentation/blockdev/zram.txt
20
21 mod history;
22 #[cfg(test)]
23 mod tests;
24
25 use std::time::Duration;
26
27 use crate::os::get_page_size;
28 use crate::os::MeminfoApi;
29 use crate::suspend_history::SuspendHistory;
30 use crate::time::BootTime;
31 use crate::zram::idle::calculate_idle_time;
32 use crate::zram::idle::set_zram_idle_time;
33 use crate::zram::writeback::history::ZramWritebackHistory;
34 use crate::zram::SysfsZramApi;
35
36 /// Error from [ZramWriteback].
37 #[derive(Debug, thiserror::Error)]
38 pub enum Error {
39 /// writeback too frequently
40 #[error("writeback too frequently")]
41 BackoffTime,
42 /// no more space for zram writeback
43 #[error("no pages in zram for zram writeback")]
44 Limit,
45 /// failed to parse writeback_limit
46 #[error("failed to parse writeback_limit")]
47 InvalidWritebackLimit,
48 /// failure on setting zram idle
49 #[error("calculate zram idle {0}")]
50 CalculateIdle(#[from] crate::zram::idle::CalculateError),
51 /// failure on setting zram idle
52 #[error("set zram idle {0}")]
53 MarkIdle(std::io::Error),
54 /// failure on writing to /sys/block/zram0/writeback
55 #[error("writeback: {0}")]
56 Writeback(std::io::Error),
57 /// failure on access to /sys/block/zram0/writeback_limit
58 #[error("writeback_limit: {0}")]
59 WritebackLimit(std::io::Error),
60 }
61
62 type Result<T> = std::result::Result<T, Error>;
63
64 /// Current zram writeback setup status
65 #[derive(Debug, PartialEq)]
66 pub enum ZramWritebackStatus {
67 /// Zram writeback is not supported by the kernel.
68 Unsupported,
69 /// Zram writeback is supported but not configured yet.
70 NotConfigured,
71 /// Zram writeback was already activated.
72 Activated,
73 }
74
75 /// Whether the zram writeback is activated on the device or not.
get_zram_writeback_status<Z: SysfsZramApi>() -> std::io::Result<ZramWritebackStatus>76 pub fn get_zram_writeback_status<Z: SysfsZramApi>() -> std::io::Result<ZramWritebackStatus> {
77 match Z::read_backing_dev() {
78 // If /sys/block/zram0/backing_dev is "none", zram writeback is not configured yet.
79 Ok(backing_dev) => {
80 if backing_dev.trim() == "none" {
81 Ok(ZramWritebackStatus::NotConfigured)
82 } else {
83 Ok(ZramWritebackStatus::Activated)
84 }
85 }
86 // If it can't access /sys/block/zram0/backing_dev, zram writeback feature is disabled on
87 // the kernel.
88 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(ZramWritebackStatus::Unsupported),
89 Err(e) => Err(e),
90 }
91 }
92
93 /// The parameters for zram writeback.
94 pub struct Params {
95 /// The backoff time since last writeback.
96 pub backoff_duration: Duration,
97 /// The minimum idle duration to writeback. This is used for [calculate_idle_time].
98 pub min_idle: Duration,
99 /// The maximum idle duration to writeback. This is used for [calculate_idle_time].
100 pub max_idle: Duration,
101 /// Whether writeback huge and idle pages or not.
102 pub huge_idle: bool,
103 /// Whether writeback idle pages or not.
104 pub idle: bool,
105 /// Whether writeback huge pages or not.
106 pub huge: bool,
107 /// Minimum bytes to writeback in 1 round.
108 pub min_bytes: u64,
109 /// Maximum bytes to writeback in 1 round.
110 pub max_bytes: u64,
111 /// Maximum bytes to writeback allowed in a day.
112 pub max_bytes_per_day: u64,
113 }
114
115 impl Default for Params {
default() -> Self116 fn default() -> Self {
117 Self {
118 // 10 minutes
119 backoff_duration: Duration::from_secs(600),
120 // 20 hours
121 min_idle: Duration::from_secs(20 * 3600),
122 // 25 hours
123 max_idle: Duration::from_secs(25 * 3600),
124 huge_idle: true,
125 idle: true,
126 huge: true,
127 // 5 MiB
128 min_bytes: 5 << 20,
129 // 300 MiB
130 max_bytes: 300 << 20,
131 // 1 GiB
132 max_bytes_per_day: 1 << 30,
133 }
134 }
135 }
136
137 /// The stats for zram writeback.
138 #[derive(Debug, Default)]
139 pub struct Stats {
140 /// orig_data_size of [crate::zram::stats::ZramMmStat].
141 pub orig_data_size: u64,
142 /// bd_count_pages of [crate::zram::stats::ZramBdStat].
143 pub current_writeback_pages: u64,
144 }
145
146 /// The detailed results of a zram writeback attempt.
147 #[derive(Debug, Default)]
148 pub struct WritebackDetails {
149 /// WritebackModeDetails for huge idle pages.
150 pub huge_idle: WritebackModeDetails,
151 /// WritebackModeDetails for idle pages.
152 pub idle: WritebackModeDetails,
153 /// WritebackModeDetails for huge pages.
154 pub huge: WritebackModeDetails,
155 /// Calculated writeback limit pages.
156 pub limit_pages: u64,
157 /// Writeback daily limit pages. This is calculated from the total written
158 /// back page per day.
159 pub daily_limit_pages: u64,
160 /// The content of /sys/block/zram0/writeback_limit just before starting
161 /// zram writeback. This is usually equals to the smaller of limit_pages and
162 /// daily_limit_pages unless kernel tweaks the updated writeback_limit
163 /// value.
164 pub actual_limit_pages: u64,
165 }
166
167 /// The detailed results of a zram writeback attempt per zram page type (i.e.
168 /// huge_idle, idle, huge pages).
169 #[derive(Debug, Default)]
170 pub struct WritebackModeDetails {
171 /// Number of pages written back.
172 pub written_pages: u64,
173 }
174
175 enum Mode {
176 HugeIdle,
177 Idle,
178 Huge,
179 }
180
load_current_writeback_limit<Z: SysfsZramApi>() -> Result<u64>181 fn load_current_writeback_limit<Z: SysfsZramApi>() -> Result<u64> {
182 let contents = Z::read_writeback_limit().map_err(Error::WritebackLimit)?;
183 contents.trim().parse().map_err(|_| Error::InvalidWritebackLimit)
184 }
185
186 /// ZramWriteback manages zram writeback policies.
187 pub struct ZramWriteback {
188 history: ZramWritebackHistory,
189 last_writeback_at: Option<BootTime>,
190 total_zram_pages: u64,
191 zram_writeback_pages: u64,
192 page_size: u64,
193 }
194
195 impl ZramWriteback {
196 /// Creates a new [ZramWriteback].
new(total_zram_size: u64, zram_writeback_size: u64) -> Self197 pub fn new(total_zram_size: u64, zram_writeback_size: u64) -> Self {
198 Self::new_with_page_size(total_zram_size, zram_writeback_size, get_page_size())
199 }
200
201 /// Creates a new [ZramWriteback] with a specified page size.
new_with_page_size( total_zram_size: u64, zram_writeback_size: u64, page_size: u64, ) -> Self202 pub fn new_with_page_size(
203 total_zram_size: u64,
204 zram_writeback_size: u64,
205 page_size: u64,
206 ) -> Self {
207 assert!(page_size != 0);
208 let total_zram_pages = total_zram_size / page_size;
209 let zram_writeback_pages = zram_writeback_size / page_size;
210 assert!(total_zram_pages != 0);
211 Self {
212 history: ZramWritebackHistory::new(),
213 last_writeback_at: None,
214 total_zram_pages,
215 zram_writeback_pages,
216 page_size,
217 }
218 }
219
220 /// Writes back idle or huge zram pages to disk.
mark_and_flush_pages<Z: SysfsZramApi, M: MeminfoApi>( &mut self, params: &Params, stats: &Stats, suspend_history: &SuspendHistory, now: BootTime, ) -> Result<WritebackDetails>221 pub fn mark_and_flush_pages<Z: SysfsZramApi, M: MeminfoApi>(
222 &mut self,
223 params: &Params,
224 stats: &Stats,
225 suspend_history: &SuspendHistory,
226 now: BootTime,
227 ) -> Result<WritebackDetails> {
228 if let Some(last_at) = self.last_writeback_at {
229 if now.saturating_duration_since(last_at) < params.backoff_duration {
230 return Err(Error::BackoffTime);
231 }
232 }
233
234 self.history.cleanup(now);
235 let daily_limit_pages =
236 self.history.calculate_daily_limit(params.max_bytes_per_day / self.page_size, now);
237 let limit_pages = self.calculate_writeback_limit(params, stats);
238 let mut details = WritebackDetails { limit_pages, daily_limit_pages, ..Default::default() };
239
240 let limit_pages = std::cmp::min(limit_pages, daily_limit_pages);
241 if limit_pages == 0 {
242 return Err(Error::Limit);
243 }
244 Z::write_writeback_limit(&limit_pages.to_string()).map_err(Error::WritebackLimit)?;
245 let mut writeback_limit = load_current_writeback_limit::<Z>()?;
246 details.actual_limit_pages = writeback_limit;
247
248 if params.huge_idle && writeback_limit > 0 {
249 writeback_limit = self.writeback::<Z, M>(
250 writeback_limit,
251 params,
252 Mode::HugeIdle,
253 suspend_history,
254 &mut details.huge_idle,
255 now,
256 )?;
257 }
258 if params.idle && writeback_limit > 0 {
259 writeback_limit = self.writeback::<Z, M>(
260 writeback_limit,
261 params,
262 Mode::Idle,
263 suspend_history,
264 &mut details.idle,
265 now,
266 )?;
267 }
268 if params.huge && writeback_limit > 0 {
269 self.writeback::<Z, M>(
270 writeback_limit,
271 params,
272 Mode::Huge,
273 suspend_history,
274 &mut details.huge,
275 now,
276 )?;
277 }
278
279 Ok(details)
280 }
281
calculate_writeback_limit(&self, params: &Params, stats: &Stats) -> u64282 fn calculate_writeback_limit(&self, params: &Params, stats: &Stats) -> u64 {
283 let min_pages = params.min_bytes / self.page_size;
284 let max_pages = params.max_bytes / self.page_size;
285 // All calculations are performed in basis points, 100 bps = 1.00%. The number of pages
286 // allowed to be written back follows a simple linear relationship. The allowable range is
287 // [min_pages, max_pages], and the writeback limit will be the (zram utilization) * the
288 // range, that is, the more zram we're using the more we're going to allow to be written
289 // back.
290 const BPS: u64 = 100 * 100;
291 let zram_utilization_bps =
292 stats.orig_data_size / self.page_size * BPS / self.total_zram_pages;
293 let limit_pages = zram_utilization_bps * max_pages / BPS;
294
295 // And try to limit it to the approximate number of free backing device pages (if it's
296 // less).
297 let free_bd_pages = self.zram_writeback_pages - stats.current_writeback_pages;
298 let limit_pages = std::cmp::min(limit_pages, free_bd_pages);
299
300 if limit_pages < min_pages {
301 // Configured to not writeback fewer than configured min_pages.
302 return 0;
303 }
304
305 // Finally enforce the limits, we won't even attempt writeback if we cannot writeback at
306 // least the min, and we will cap to the max if it's greater.
307 std::cmp::min(limit_pages, max_pages)
308 }
309
writeback<Z: SysfsZramApi, M: MeminfoApi>( &mut self, writeback_limit: u64, params: &Params, mode: Mode, suspend_history: &SuspendHistory, details: &mut WritebackModeDetails, now: BootTime, ) -> Result<u64>310 fn writeback<Z: SysfsZramApi, M: MeminfoApi>(
311 &mut self,
312 writeback_limit: u64,
313 params: &Params,
314 mode: Mode,
315 suspend_history: &SuspendHistory,
316 details: &mut WritebackModeDetails,
317 now: BootTime,
318 ) -> Result<u64> {
319 match mode {
320 Mode::HugeIdle | Mode::Idle => {
321 let idle_age = calculate_idle_time::<M>(params.min_idle, params.max_idle)?;
322 // Adjust idle age by suspend duration.
323 let idle_age = idle_age.saturating_add(
324 suspend_history.calculate_total_suspend_duration(idle_age, now),
325 );
326 set_zram_idle_time::<Z>(idle_age).map_err(Error::MarkIdle)?;
327 }
328 Mode::Huge => {}
329 }
330
331 let mode = match mode {
332 Mode::HugeIdle => "huge_idle",
333 Mode::Idle => "idle",
334 Mode::Huge => "huge",
335 };
336
337 let result = Z::writeback(mode);
338
339 // If reading writeback_limit fails, we assume that all writeback_limit was consumed
340 // conservatively.
341 let current_writeback_limit = load_current_writeback_limit::<Z>().unwrap_or(0);
342 let pages_written = writeback_limit.saturating_sub(current_writeback_limit);
343 self.history.record(pages_written, now);
344 details.written_pages = pages_written;
345
346 if let Err(e) = result {
347 // When zram writeback reaches the writeback_limit, kernel stop writing back and returns
348 // EIO. EIO with zero writeback_limit is not an error.
349 let is_writeback_limit_reached =
350 e.raw_os_error() == Some(libc::EIO) && current_writeback_limit == 0;
351 if !is_writeback_limit_reached {
352 return Err(Error::Writeback(e));
353 }
354 }
355
356 self.last_writeback_at = Some(now);
357
358 Ok(current_writeback_limit)
359 }
360 }
361