blob: 4feba40e1ae4c5e88d03aee1d11be0aad9bd8d08 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![recursion_limit = "256"]
use starnix_core::device::kobject::{Device, DeviceMetadata};
use starnix_core::device::{DeviceMode, DeviceOps};
use starnix_core::fs::sysfs::{BlockDeviceInfo, build_block_device_directory};
use starnix_core::task::{CurrentTask, Kernel, KernelStats};
use starnix_core::vfs::pseudo::dynamic_file::{DynamicFile, DynamicFileBuf, DynamicFileSource};
use starnix_core::vfs::pseudo::simple_directory::SimpleDirectoryMutator;
use starnix_core::vfs::pseudo::stub_empty_file::StubEmptyFile;
use starnix_core::vfs::{
FileOps, FsNodeOps, NamespaceNode, fileops_impl_dataless, fileops_impl_noop_sync,
fileops_impl_seekless,
};
use starnix_logging::{bug_ref, log_error};
use starnix_sync::{FileOpsCore, Locked, Unlocked};
use starnix_uapi::device_type::{DeviceType, ZRAM_MAJOR};
use starnix_uapi::errno;
use starnix_uapi::errors::Errno;
use starnix_uapi::file_mode::mode;
use starnix_uapi::open_flags::OpenFlags;
use std::sync::{Arc, Weak};
#[derive(Default, Clone)]
pub struct ZramDevice {
kernel_stats: Arc<KernelStatsWrapper>,
}
impl ZramDevice {
fn get_stats(&self) -> Result<fidl_fuchsia_kernel::MemoryStatsCompression, Errno> {
self.kernel_stats.get_stats()
}
}
impl DeviceOps for ZramDevice {
fn open(
&self,
_locked: &mut Locked<FileOpsCore>,
_current_task: &CurrentTask,
_id: DeviceType,
_node: &NamespaceNode,
_flags: OpenFlags,
) -> Result<Box<dyn FileOps>, Errno> {
Ok(Box::new(self.clone()))
}
}
impl FileOps for ZramDevice {
fileops_impl_seekless!();
fileops_impl_dataless!();
fileops_impl_noop_sync!();
}
pub fn zram_device_init(locked: &mut Locked<Unlocked>, kernel: &Kernel) -> Result<(), Errno> {
let zram_device = ZramDevice::default();
let zram_device_clone = zram_device.clone();
let registry = &kernel.device_registry;
registry.register_device_with_dir(
locked,
kernel,
"zram0".into(),
DeviceMetadata::new("zram0".into(), DeviceType::new(ZRAM_MAJOR, 0), DeviceMode::Block),
registry.objects.virtual_block_class(),
|device, dir| build_zram_device_directory(device, zram_device_clone, dir),
zram_device,
)?;
Ok(())
}
fn build_zram_device_directory(
device: &Device,
zram_device: ZramDevice,
dir: &SimpleDirectoryMutator,
) {
let block_info = Arc::downgrade(&zram_device.kernel_stats) as Weak<dyn BlockDeviceInfo>;
build_block_device_directory(device, block_info, dir);
dir.entry(
"idle",
StubEmptyFile::new_node(bug_ref!("https://fxbug.dev/322892951")),
mode!(IFREG, 0o664),
);
dir.entry("mm_stat", MmStatFile::new_node(zram_device), mode!(IFREG, 0o444));
}
#[derive(Clone)]
struct MmStatFile {
device: ZramDevice,
}
impl MmStatFile {
pub fn new_node(device: ZramDevice) -> impl FsNodeOps {
DynamicFile::new_node(Self { device })
}
}
impl DynamicFileSource for MmStatFile {
fn generate(
&self,
_current_task: &CurrentTask,
sink: &mut DynamicFileBuf,
) -> Result<(), Errno> {
let stats = self.device.get_stats()?;
let compressed_storage_bytes = stats.compressed_storage_bytes.unwrap_or_default();
let compressed_fragmentation_bytes =
stats.compressed_fragmentation_bytes.unwrap_or_default();
let orig_data_size = stats.uncompressed_storage_bytes.unwrap_or_default();
// This value isn't entirely correct because we're still counting metadata and other
// non-fragmentation usage.
let compr_data_size = compressed_storage_bytes - compressed_fragmentation_bytes;
let mem_used_total = compressed_storage_bytes;
// The remaining values are not yet available from Zircon.
let mem_limit = 0;
let mem_used_max = 0;
let same_pages = 0;
let pages_compacted = 0;
let huge_pages = 0;
writeln!(
sink,
"{orig_data_size} {compr_data_size} {mem_used_total} {mem_limit} \
{mem_used_max} {same_pages} {pages_compacted} {huge_pages}"
)?;
Ok(())
}
}
#[derive(Default)]
struct KernelStatsWrapper(KernelStats);
impl KernelStatsWrapper {
fn get_stats(&self) -> Result<fidl_fuchsia_kernel::MemoryStatsCompression, Errno> {
self.0.get().get_memory_stats_compression(zx::MonotonicInstant::INFINITE).map_err(|e| {
log_error!("FIDL error getting memory compression stats: {e}");
errno!(EIO)
})
}
}
impl BlockDeviceInfo for KernelStatsWrapper {
fn size(&self) -> Result<usize, Errno> {
Ok(self.get_stats()?.uncompressed_storage_bytes.unwrap_or_default() as usize)
}
}