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