Skip to main content

starnix_modules_zram/
lib.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#![recursion_limit = "256"]
6
7use starnix_core::device::kobject::{Device, DeviceMetadata};
8use starnix_core::device::{DeviceMode, DeviceOps};
9use starnix_core::fs::sysfs::{BlockDeviceInfo, build_block_device_directory};
10use starnix_core::task::{CurrentTask, Kernel, KernelStats};
11use starnix_core::vfs::pseudo::dynamic_file::{DynamicFile, DynamicFileBuf, DynamicFileSource};
12use starnix_core::vfs::pseudo::simple_directory::SimpleDirectoryMutator;
13use starnix_core::vfs::pseudo::stub_empty_file::StubEmptyFile;
14use starnix_core::vfs::{
15    FileOps, FsNodeOps, NamespaceNode, fileops_impl_dataless, fileops_impl_noop_sync,
16    fileops_impl_seekless,
17};
18use starnix_logging::{bug_ref, log_error};
19
20use starnix_uapi::device_id::{DeviceId, ZRAM_MAJOR};
21use starnix_uapi::errno;
22use starnix_uapi::errors::Errno;
23use starnix_uapi::file_mode::mode;
24use starnix_uapi::open_flags::OpenFlags;
25use std::sync::{Arc, Weak};
26
27#[derive(Default, Clone)]
28pub struct ZramDevice {
29    kernel_stats: Arc<KernelStatsWrapper>,
30}
31
32impl ZramDevice {
33    fn get_stats(&self) -> Result<fidl_fuchsia_kernel::MemoryStatsCompression, Errno> {
34        self.kernel_stats.get_stats()
35    }
36}
37
38impl DeviceOps for ZramDevice {
39    fn open(
40        &self,
41        _current_task: &CurrentTask,
42        _id: DeviceId,
43        _node: &NamespaceNode,
44        _flags: OpenFlags,
45    ) -> Result<Box<dyn FileOps>, Errno> {
46        Ok(Box::new(self.clone()))
47    }
48}
49
50impl FileOps for ZramDevice {
51    fileops_impl_seekless!();
52    fileops_impl_dataless!();
53    fileops_impl_noop_sync!();
54}
55
56pub fn zram_device_init(kernel: &Kernel) -> Result<(), Errno> {
57    let zram_device = ZramDevice::default();
58    let zram_device_clone = zram_device.clone();
59    let registry = &kernel.device_registry;
60    registry.add_device_with_dir(
61        kernel,
62        "zram0".into(),
63        DeviceMetadata::new("zram0".into(), DeviceId::new(ZRAM_MAJOR, 0), DeviceMode::Block),
64        /* parent = */ None,
65        registry.objects.block_class(),
66        |device, dir| build_zram_device_directory(device, zram_device_clone, dir),
67        zram_device,
68    );
69    Ok(())
70}
71
72fn build_zram_device_directory(
73    device: &Device,
74    zram_device: ZramDevice,
75    dir: &SimpleDirectoryMutator,
76) {
77    let block_info = Arc::downgrade(&zram_device.kernel_stats) as Weak<dyn BlockDeviceInfo>;
78    build_block_device_directory(device, block_info, dir);
79    dir.entry(
80        "idle",
81        StubEmptyFile::new_node(bug_ref!("https://fxbug.dev/322892951")),
82        mode!(IFREG, 0o664),
83    );
84    for name in ["backing_dev", "recomp_algorithm", "recompress"] {
85        dir.entry(
86            name,
87            StubEmptyFile::new_node(bug_ref!("https://fxbug.dev/452096300")),
88            mode!(IFREG, 0o444),
89        );
90    }
91    dir.entry("mm_stat", MmStatFile::new_node(zram_device), mode!(IFREG, 0o444));
92}
93
94#[derive(Clone)]
95struct MmStatFile {
96    device: ZramDevice,
97}
98impl MmStatFile {
99    pub fn new_node(device: ZramDevice) -> impl FsNodeOps {
100        DynamicFile::new_node(Self { device })
101    }
102}
103impl DynamicFileSource for MmStatFile {
104    fn generate(
105        &self,
106        _current_task: &CurrentTask,
107        sink: &mut DynamicFileBuf,
108    ) -> Result<(), Errno> {
109        let stats = self.device.get_stats()?;
110
111        let compressed_storage_bytes = stats.compressed_storage_bytes.unwrap_or_default();
112        let compressed_fragmentation_bytes =
113            stats.compressed_fragmentation_bytes.unwrap_or_default();
114
115        let orig_data_size = stats.uncompressed_storage_bytes.unwrap_or_default();
116        // This value isn't entirely correct because we're still counting metadata and other
117        // non-fragmentation usage.
118        let compr_data_size = compressed_storage_bytes - compressed_fragmentation_bytes;
119        let mem_used_total = compressed_storage_bytes;
120        // The remaining values are not yet available from Zircon.
121        let mem_limit = 0;
122        let mem_used_max = 0;
123        let same_pages = 0;
124        let pages_compacted = 0;
125        let huge_pages = 0;
126
127        writeln!(
128            sink,
129            "{orig_data_size} {compr_data_size} {mem_used_total} {mem_limit} \
130                        {mem_used_max} {same_pages} {pages_compacted} {huge_pages}"
131        )?;
132        Ok(())
133    }
134}
135
136#[derive(Default)]
137struct KernelStatsWrapper(KernelStats);
138
139impl KernelStatsWrapper {
140    fn get_stats(&self) -> Result<fidl_fuchsia_kernel::MemoryStatsCompression, Errno> {
141        self.0.get().get_memory_stats_compression(zx::MonotonicInstant::INFINITE).map_err(|e| {
142            log_error!("FIDL error getting memory compression stats: {e}");
143            errno!(EIO)
144        })
145    }
146}
147
148impl BlockDeviceInfo for KernelStatsWrapper {
149    fn size(&self) -> Result<usize, Errno> {
150        Ok(self.get_stats()?.uncompressed_storage_bytes.unwrap_or_default() as usize)
151    }
152}