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 use std::time::Instant;
27 
28 use crate::os::get_page_size;
29 use crate::os::MeminfoApi;
30 use crate::zram::idle::calculate_idle_time;
31 use crate::zram::idle::set_zram_idle_time;
32 use crate::zram::writeback::history::ZramWritebackHistory;
33 use crate::zram::SysfsZramApi;
34 
35 /// Error from [ZramWriteback].
36 #[derive(Debug, thiserror::Error)]
37 pub enum Error {
38     /// writeback too frequently
39     #[error("writeback too frequently")]
40     BackoffTime,
41     /// no more space for zram writeback
42     #[error("no pages in zram for zram writeback")]
43     Limit,
44     /// failed to parse writeback_limit
45     #[error("failed to parse writeback_limit")]
46     InvalidWritebackLimit,
47     /// failure on setting zram idle
48     #[error("calculate zram idle {0}")]
49     CalculateIdle(#[from] crate::zram::idle::CalculateError),
50     /// failure on setting zram idle
51     #[error("set zram idle {0}")]
52     MarkIdle(std::io::Error),
53     /// failure on writing to /sys/block/zram0/writeback
54     #[error("writeback: {0}")]
55     Writeback(std::io::Error),
56     /// failure on access to /sys/block/zram0/writeback_limit
57     #[error("writeback_limit: {0}")]
58     WritebackLimit(std::io::Error),
59 }
60 
61 type Result<T> = std::result::Result<T, Error>;
62 
63 /// Whether the zram writeback is activated on the device or not.
is_zram_writeback_activated<Z: SysfsZramApi>() -> std::io::Result<bool>64 pub fn is_zram_writeback_activated<Z: SysfsZramApi>() -> std::io::Result<bool> {
65     match Z::read_backing_dev() {
66         // If /sys/block/zram0/backing_dev is "none", zram writeback is not configured yet.
67         Ok(backing_dev) => Ok(backing_dev != "none"),
68         // If it can't access /sys/block/zram0/backing_dev, zram writeback feature is disabled on
69         // the kernel.
70         Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false),
71         Err(e) => Err(e),
72     }
73 }
74 
75 /// The parameters for zram writeback.
76 pub struct Params {
77     /// The backoff time since last writeback.
78     pub backoff_duration: Duration,
79     /// The minimum idle duration to writeback. This is used for [calculate_idle_time].
80     pub min_idle: Duration,
81     /// The maximum idle duration to writeback. This is used for [calculate_idle_time].
82     pub max_idle: Duration,
83     /// Whether writeback huge and idle pages or not.
84     pub huge_idle: bool,
85     /// Whether writeback idle pages or not.
86     pub idle: bool,
87     /// Whether writeback huge pages or not.
88     pub huge: bool,
89     /// Minimum bytes to writeback in 1 round.
90     pub min_bytes: u64,
91     /// Maximum bytes to writeback in 1 round.
92     pub max_bytes: u64,
93     /// Maximum bytes to writeback allowed in a day.
94     pub max_bytes_per_day: u64,
95 }
96 
97 impl Default for Params {
default() -> Self98     fn default() -> Self {
99         Self {
100             // 10 minutes
101             backoff_duration: Duration::from_secs(600),
102             // 20 hours
103             min_idle: Duration::from_secs(20 * 3600),
104             // 25 hours
105             max_idle: Duration::from_secs(25 * 3600),
106             huge_idle: true,
107             idle: true,
108             huge: true,
109             // 5 MiB
110             min_bytes: 5 << 20,
111             // 300 MiB
112             max_bytes: 300 << 20,
113             // 1 GiB
114             max_bytes_per_day: 1 << 30,
115         }
116     }
117 }
118 
119 /// The stats for zram writeback.
120 #[derive(Debug, Default)]
121 pub struct Stats {
122     /// orig_data_size of [crate::zram::stats::ZramMmStat].
123     pub orig_data_size: u64,
124     /// bd_count_pages of [crate::zram::stats::ZramBdStat].
125     pub current_writeback_pages: u64,
126 }
127 
128 enum Mode {
129     HugeIdle,
130     Idle,
131     Huge,
132 }
133 
load_current_writeback_limit<Z: SysfsZramApi>() -> Result<u64>134 fn load_current_writeback_limit<Z: SysfsZramApi>() -> Result<u64> {
135     let contents = Z::read_writeback_limit().map_err(Error::WritebackLimit)?;
136     contents.trim().parse().map_err(|_| Error::InvalidWritebackLimit)
137 }
138 
139 /// ZramWriteback manages zram writeback policies.
140 pub struct ZramWriteback {
141     history: ZramWritebackHistory,
142     last_writeback_at: Option<Instant>,
143     total_zram_pages: u64,
144     zram_writeback_pages: u64,
145     page_size: u64,
146 }
147 
148 impl ZramWriteback {
149     /// Creates a new [ZramWriteback].
new(total_zram_size: u64, zram_writeback_size: u64) -> Self150     pub fn new(total_zram_size: u64, zram_writeback_size: u64) -> Self {
151         Self::new_with_page_size(total_zram_size, zram_writeback_size, get_page_size())
152     }
153 
154     /// Creates a new [ZramWriteback] with a specified page size.
new_with_page_size( total_zram_size: u64, zram_writeback_size: u64, page_size: u64, ) -> Self155     pub fn new_with_page_size(
156         total_zram_size: u64,
157         zram_writeback_size: u64,
158         page_size: u64,
159     ) -> Self {
160         assert!(page_size != 0);
161         let total_zram_pages = total_zram_size / page_size;
162         let zram_writeback_pages = zram_writeback_size / page_size;
163         assert!(total_zram_pages != 0);
164         Self {
165             history: ZramWritebackHistory::new(),
166             last_writeback_at: None,
167             total_zram_pages,
168             zram_writeback_pages,
169             page_size,
170         }
171     }
172 
173     /// Writes back idle or huge zram pages to disk.
mark_and_flush_pages<Z: SysfsZramApi, M: MeminfoApi>( &mut self, params: &Params, stats: &Stats, now: Instant, ) -> Result<()>174     pub fn mark_and_flush_pages<Z: SysfsZramApi, M: MeminfoApi>(
175         &mut self,
176         params: &Params,
177         stats: &Stats,
178         now: Instant,
179     ) -> Result<()> {
180         if let Some(last_at) = self.last_writeback_at {
181             if now - last_at < params.backoff_duration {
182                 return Err(Error::BackoffTime);
183             }
184         }
185 
186         self.history.cleanup(now);
187         let daily_limit_pages =
188             self.history.calculate_daily_limit(params.max_bytes_per_day / self.page_size, now);
189         let limit_pages = self.calculate_writeback_limit(params, stats);
190         let limit_pages = std::cmp::min(limit_pages, daily_limit_pages);
191         if limit_pages == 0 {
192             return Err(Error::Limit);
193         }
194         Z::write_writeback_limit(&limit_pages.to_string()).map_err(Error::WritebackLimit)?;
195         let mut writeback_limit = load_current_writeback_limit::<Z>()?;
196 
197         if params.huge_idle && writeback_limit > 0 {
198             writeback_limit =
199                 self.writeback::<Z, M>(writeback_limit, params, Mode::HugeIdle, now)?;
200         }
201         if params.idle && writeback_limit > 0 {
202             writeback_limit = self.writeback::<Z, M>(writeback_limit, params, Mode::Idle, now)?;
203         }
204         if params.huge && writeback_limit > 0 {
205             self.writeback::<Z, M>(writeback_limit, params, Mode::Huge, now)?;
206         }
207 
208         Ok(())
209     }
210 
calculate_writeback_limit(&self, params: &Params, stats: &Stats) -> u64211     fn calculate_writeback_limit(&self, params: &Params, stats: &Stats) -> u64 {
212         let min_pages = params.min_bytes / self.page_size;
213         let max_pages = params.max_bytes / self.page_size;
214         // All calculations are performed in basis points, 100 bps = 1.00%. The number of pages
215         // allowed to be written back follows a simple linear relationship. The allowable range is
216         // [min_pages, max_pages], and the writeback limit will be the (zram utilization) * the
217         // range, that is, the more zram we're using the more we're going to allow to be written
218         // back.
219         const BPS: u64 = 100 * 100;
220         let zram_utilization_bps =
221             stats.orig_data_size / self.page_size * BPS / self.total_zram_pages;
222         let limit_pages = zram_utilization_bps * max_pages / BPS;
223 
224         // And try to limit it to the approximate number of free backing device pages (if it's
225         // less).
226         let free_bd_pages = self.zram_writeback_pages - stats.current_writeback_pages;
227         let limit_pages = std::cmp::min(limit_pages, free_bd_pages);
228 
229         if limit_pages < min_pages {
230             // Configured to not writeback fewer than configured min_pages.
231             return 0;
232         }
233 
234         // Finally enforce the limits, we won't even attempt writeback if we cannot writeback at
235         // least the min, and we will cap to the max if it's greater.
236         std::cmp::min(limit_pages, max_pages)
237     }
238 
writeback<Z: SysfsZramApi, M: MeminfoApi>( &mut self, writeback_limit: u64, params: &Params, mode: Mode, now: Instant, ) -> Result<u64>239     fn writeback<Z: SysfsZramApi, M: MeminfoApi>(
240         &mut self,
241         writeback_limit: u64,
242         params: &Params,
243         mode: Mode,
244         now: Instant,
245     ) -> Result<u64> {
246         match mode {
247             Mode::HugeIdle | Mode::Idle => {
248                 let idle_age = calculate_idle_time::<M>(params.min_idle, params.max_idle)?;
249                 // TODO: adjust the idle_age by suspend duration.
250                 set_zram_idle_time::<Z>(idle_age).map_err(Error::MarkIdle)?;
251             }
252             Mode::Huge => {}
253         }
254 
255         let mode = match mode {
256             Mode::HugeIdle => "huge_idle",
257             Mode::Idle => "idle",
258             Mode::Huge => "huge",
259         };
260 
261         if let Err(e) = Z::writeback(mode) {
262             // If writeback fails, we assume that all writeback_limit was consumed conservatively.
263             self.history.record(writeback_limit, now);
264             return Err(Error::Writeback(e));
265         }
266 
267         self.last_writeback_at = Some(now);
268 
269         // If reading writeback_limit fails, we assume that all writeback_limit was consumed
270         // conservatively.
271         let current_writeback_limit = load_current_writeback_limit::<Z>().unwrap_or(0);
272         self.history.record(writeback_limit.saturating_sub(current_writeback_limit), now);
273 
274         Ok(current_writeback_limit)
275     }
276 }
277