| // Copyright 2024, The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| use std::convert::From; |
| use std::ops::DerefMut; |
| use std::path::Path; |
| use std::sync::Arc; |
| use std::sync::Mutex; |
| |
| use anyhow::Context; |
| use binder::Interface; |
| use binder::Result as BinderResult; |
| use log::error; |
| use mmd::os::MeminfoApiImpl; |
| use mmd::time::TimeApi; |
| use mmd::time::TimeApiImpl; |
| use mmd::zram::recompression::Error as ZramRecompressionError; |
| use mmd::zram::writeback::Error as ZramWritebackError; |
| use mmd::zram::SysfsZramApiImpl; |
| use mmd_aidl_interface::aidl::android::os::IMmd::IMmd; |
| use nix::sys::statvfs::statvfs; |
| |
| use crate::properties::BoolProp; |
| use crate::properties::SecondsProp; |
| use crate::properties::U64Prop; |
| use crate::ZramContext; |
| use crate::ZRAM_WRITEBACK_FILE_PATH; |
| |
| pub struct MmdService { |
| ctx: Arc<Mutex<ZramContext>>, |
| } |
| |
| impl MmdService { |
| pub fn new(ctx: Arc<Mutex<ZramContext>>) -> Self { |
| Self { ctx } |
| } |
| } |
| |
| impl Interface for MmdService {} |
| |
| impl IMmd for MmdService { |
| fn doZramMaintenance(&self) -> BinderResult<()> { |
| let mut ctx = self.ctx.lock().expect("mmd aborts on panics"); |
| let ZramContext { zram_writeback, zram_recompression, suspend_history, .. } = |
| ctx.deref_mut(); |
| |
| // Execute recompression before writeback. |
| if let Some(zram_recompression) = zram_recompression.as_mut() { |
| let params = load_zram_recompression_params(); |
| match zram_recompression.mark_and_recompress::<SysfsZramApiImpl, MeminfoApiImpl>( |
| ¶ms, |
| suspend_history, |
| TimeApiImpl::get_boot_time(), |
| ) { |
| Ok(_) | Err(ZramRecompressionError::BackoffTime) => {} |
| Err(e) => error!("failed to zram recompress: {e:?}"), |
| } |
| } |
| |
| if let Some(zram_writeback) = zram_writeback.as_mut() { |
| let params = load_zram_writeback_params(); |
| let stats = match load_zram_writeback_stats() { |
| Ok(v) => v, |
| Err(e) => { |
| error!("failed to load zram writeback stats: {e:?}"); |
| return Ok(()); |
| } |
| }; |
| match zram_writeback.mark_and_flush_pages::<SysfsZramApiImpl, MeminfoApiImpl>( |
| ¶ms, |
| &stats, |
| suspend_history, |
| TimeApiImpl::get_boot_time(), |
| ) { |
| Ok(_) | Err(ZramWritebackError::BackoffTime) | Err(ZramWritebackError::Limit) => {} |
| Err(e) => error!("failed to zram writeback: {e:?}"), |
| } |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| fn load_zram_writeback_params() -> mmd::zram::writeback::Params { |
| let mut params = mmd::zram::writeback::Params::default(); |
| params.backoff_duration = SecondsProp::ZramWritebackBackoff.get(params.backoff_duration); |
| params.min_idle = SecondsProp::ZramWritebackMinIdle.get(params.min_idle); |
| params.max_idle = SecondsProp::ZramWritebackMaxIdle.get(params.max_idle); |
| params.huge_idle = BoolProp::ZramWritebackHugeIdleEnabled.get(params.huge_idle); |
| params.idle = BoolProp::ZramWritebackIdleEnabled.get(params.idle); |
| params.huge = BoolProp::ZramWritebackHugeEnabled.get(params.huge); |
| params.min_bytes = U64Prop::ZramWritebackMinBytes.get(params.min_bytes); |
| params.max_bytes = U64Prop::ZramWritebackMaxBytes.get(params.max_bytes); |
| params.max_bytes_per_day = U64Prop::ZramWritebackMaxBytesPerDay.get(params.max_bytes_per_day); |
| // min_free_diskspace_pct should be 0 ~ 100. If ZramWritebackMinFreeSpacePercentage is too big, |
| // set 100% as fallback. |
| params.min_free_diskspace_pct = U64Prop::ZramWritebackMinFreeSpacePercentage |
| .get(params.min_free_diskspace_pct as u64) |
| .try_into() |
| .unwrap_or(100); |
| params |
| } |
| |
| fn load_zram_writeback_stats() -> anyhow::Result<mmd::zram::writeback::Stats> { |
| let mm_stat = |
| mmd::zram::stats::ZramMmStat::load::<SysfsZramApiImpl>().context("load mm_stat")?; |
| let bd_stat = |
| mmd::zram::stats::ZramBdStat::load::<SysfsZramApiImpl>().context("load bd_stat")?; |
| let file_stat = statvfs( |
| Path::new(ZRAM_WRITEBACK_FILE_PATH) |
| .parent() |
| .expect("parent of ZRAM_WRITEBACK_FILE_PATH must be not none."), |
| ) |
| .context("statvfs writeback backing file")?; |
| Ok(mmd::zram::writeback::Stats { |
| orig_data_size: mm_stat.orig_data_size, |
| current_writeback_pages: bd_stat.bd_count_pages, |
| // libc::c_ulong and libc::fsblkcnt_t can be non-u64 on some platforms. |
| #[allow(clippy::useless_conversion)] |
| total_disksize: u64::from(file_stat.blocks()) * u64::from(file_stat.fragment_size()), |
| // libc::c_ulong and libc::fsblkcnt_t can be non-u64 on some platforms. |
| #[allow(clippy::useless_conversion)] |
| free_disksize: u64::from(file_stat.blocks_free()) * u64::from(file_stat.block_size()), |
| }) |
| } |
| |
| fn load_zram_recompression_params() -> mmd::zram::recompression::Params { |
| let mut params = mmd::zram::recompression::Params::default(); |
| params.backoff_duration = SecondsProp::ZramRecompressionBackoff.get(params.backoff_duration); |
| params.min_idle = SecondsProp::ZramRecompressionMinIdle.get(params.min_idle); |
| params.max_idle = SecondsProp::ZramRecompressionMaxIdle.get(params.max_idle); |
| params.huge_idle = BoolProp::ZramRecompressionHugeIdleEnabled.get(params.huge_idle); |
| params.idle = BoolProp::ZramRecompressionIdleEnabled.get(params.idle); |
| params.huge = BoolProp::ZramRecompressionHugeEnabled.get(params.huge); |
| params.max_mib = U64Prop::ZramRecompressionThresholdMib.get(params.max_mib); |
| params |
| } |