blob: 06f4fa938a6b76c02e136470cb61c396a7c9664c [file] [log] [blame]
// 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>(
&params,
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>(
&params,
&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
}