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