• 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 use std::collections::VecDeque;
16 use std::time::Duration;
17 
18 use crate::time::BootTime;
19 
20 // 24 hours.
21 const HISTORY_EXPIRY: Duration = Duration::from_secs(60 * 60 * 24);
22 
23 /// Stores the log of zram writeback size to calculate daily limit.
24 pub struct ZramWritebackHistory {
25     history: VecDeque<(u64, BootTime)>,
26 }
27 
28 impl ZramWritebackHistory {
29     /// Creates a new [ZramWritebackHistory].
new() -> Self30     pub fn new() -> Self {
31         Self { history: VecDeque::new() }
32     }
33 
34     /// Records a new log of zram writeback.
record(&mut self, pages: u64, now: BootTime)35     pub fn record(&mut self, pages: u64, now: BootTime) {
36         self.history.push_back((pages, now));
37     }
38 
39     /// Evicts expired records.
cleanup(&mut self, now: BootTime)40     pub fn cleanup(&mut self, now: BootTime) {
41         while !self.history.is_empty()
42             && now.saturating_duration_since(self.history.front().unwrap().1) > HISTORY_EXPIRY
43         {
44             self.history.pop_front();
45         }
46     }
47 
48     /// Calculates the daily limit of zram writeback left.
calculate_daily_limit(&self, max_pages_per_day: u64, now: BootTime) -> u6449     pub fn calculate_daily_limit(&self, max_pages_per_day: u64, now: BootTime) -> u64 {
50         let pages_written = self
51             .history
52             .iter()
53             .filter(|(_, t)| now.saturating_duration_since(*t) < HISTORY_EXPIRY)
54             .map(|(p, _)| p)
55             .sum::<u64>();
56         if pages_written >= max_pages_per_day {
57             return 0;
58         }
59         max_pages_per_day - pages_written
60     }
61 }
62 
63 #[cfg(test)]
64 mod tests {
65     use super::*;
66     use crate::time::TimeApi;
67     use crate::time::TimeApiImpl;
68 
69     #[test]
test_calculate_daily_limit()70     fn test_calculate_daily_limit() {
71         let mut history = ZramWritebackHistory::new();
72         let base_time = TimeApiImpl::get_boot_time();
73 
74         // records 1 day before is ignored.
75         history.record(1, base_time);
76         history.record(1, base_time);
77         history.record(2, base_time.checked_add(Duration::from_secs(1)).unwrap());
78         history.record(3, base_time.checked_add(HISTORY_EXPIRY).unwrap());
79         assert_eq!(
80             history.calculate_daily_limit(100, base_time.checked_add(HISTORY_EXPIRY).unwrap()),
81             95
82         );
83     }
84 
85     #[test]
test_calculate_daily_limit_empty()86     fn test_calculate_daily_limit_empty() {
87         let history = ZramWritebackHistory::new();
88         assert_eq!(history.calculate_daily_limit(100, TimeApiImpl::get_boot_time()), 100);
89     }
90 
91     #[test]
test_calculate_daily_limit_exceeds_max()92     fn test_calculate_daily_limit_exceeds_max() {
93         let mut history = ZramWritebackHistory::new();
94         let base_time = TimeApiImpl::get_boot_time();
95         // records 1 day before is ignored.
96         history.record(1, base_time);
97         history.record(2, base_time.checked_add(Duration::from_secs(1)).unwrap());
98         history.record(3, base_time.checked_add(HISTORY_EXPIRY).unwrap());
99 
100         assert_eq!(
101             history.calculate_daily_limit(1, base_time.checked_add(HISTORY_EXPIRY).unwrap()),
102             0
103         );
104         assert_eq!(
105             history.calculate_daily_limit(2, base_time.checked_add(HISTORY_EXPIRY).unwrap()),
106             0
107         );
108         assert_eq!(
109             history.calculate_daily_limit(3, base_time.checked_add(HISTORY_EXPIRY).unwrap()),
110             0
111         );
112         assert_eq!(
113             history.calculate_daily_limit(4, base_time.checked_add(HISTORY_EXPIRY).unwrap()),
114             0
115         );
116         assert_eq!(
117             history.calculate_daily_limit(5, base_time.checked_add(HISTORY_EXPIRY).unwrap()),
118             0
119         );
120         assert_eq!(
121             history.calculate_daily_limit(6, base_time.checked_add(HISTORY_EXPIRY).unwrap()),
122             1
123         );
124     }
125 
126     #[test]
test_calculate_daily_limit_after_cleanup()127     fn test_calculate_daily_limit_after_cleanup() {
128         let mut history = ZramWritebackHistory::new();
129         let base_time = TimeApiImpl::get_boot_time();
130         // records 1 day before will be cleaned up.
131         history.record(1, base_time);
132         history.record(1, base_time);
133         history.record(2, base_time.checked_add(Duration::from_secs(1)).unwrap());
134         history.record(3, base_time.checked_add(HISTORY_EXPIRY).unwrap());
135 
136         history.cleanup(base_time.checked_add(HISTORY_EXPIRY).unwrap());
137 
138         // The same result as test_calculate_daily_limit
139         assert_eq!(
140             history.calculate_daily_limit(100, base_time.checked_add(HISTORY_EXPIRY).unwrap()),
141             95
142         );
143     }
144 }
145