• 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 //! 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