File Index
Files and directories
Sign in
FILE

rust_debugfs_scoped.rs

File preview with syntax highlighting.

linux-6.19.14/samples/rust/rust_debugfs_scoped.rs RUST
  1// SPDX-License-Identifier: GPL-2.0
  2
  3// Copyright (C) 2025 Google LLC.
  4
  5//! Sample DebugFS exporting platform driver that demonstrates the use of
  6//! `Scope::dir` to create a variety of files without the need to separately
  7//! track them all.
  8
  9use kernel::debugfs::{Dir, Scope};
 10use kernel::prelude::*;
 11use kernel::sizes::*;
 12use kernel::sync::atomic::Atomic;
 13use kernel::sync::Mutex;
 14use kernel::{c_str, new_mutex, str::CString};
 15
 16module! {
 17    type: RustScopedDebugFs,
 18    name: "rust_debugfs_scoped",
 19    authors: ["Matthew Maurer"],
 20    description: "Rust Scoped DebugFS usage sample",
 21    license: "GPL",
 22}
 23
 24fn remove_file_write(
 25    mod_data: &ModuleData,
 26    reader: &mut kernel::uaccess::UserSliceReader,
 27) -> Result {
 28    let mut buf = [0u8; 128];
 29    if reader.len() >= buf.len() {
 30        return Err(EINVAL);
 31    }
 32    let n = reader.len();
 33    reader.read_slice(&mut buf[..n])?;
 34
 35    let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?.trim();
 36    let nul_idx = s.len();
 37    buf[nul_idx] = 0;
 38    let to_remove = CStr::from_bytes_with_nul(&buf[..nul_idx + 1]).map_err(|_| EINVAL)?;
 39    mod_data
 40        .devices
 41        .lock()
 42        .retain(|device| device.name.to_bytes() != to_remove.to_bytes());
 43    Ok(())
 44}
 45
 46fn create_file_write(
 47    mod_data: &ModuleData,
 48    reader: &mut kernel::uaccess::UserSliceReader,
 49) -> Result {
 50    let mut buf = [0u8; 128];
 51    if reader.len() > buf.len() {
 52        return Err(EINVAL);
 53    }
 54    let n = reader.len();
 55    reader.read_slice(&mut buf[..n])?;
 56
 57    let mut nums = KVec::new();
 58
 59    let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?.trim();
 60    let mut items = s.split_whitespace();
 61    let name_str = items.next().ok_or(EINVAL)?;
 62    let name = CString::try_from_fmt(fmt!("{name_str}"))?;
 63    let file_name = CString::try_from_fmt(fmt!("{name_str}"))?;
 64    for sub in items {
 65        nums.push(
 66            Atomic::<usize>::new(sub.parse().map_err(|_| EINVAL)?),
 67            GFP_KERNEL,
 68        )?;
 69    }
 70    let blob = KBox::pin_init(new_mutex!([0x42; SZ_4K]), GFP_KERNEL)?;
 71
 72    let scope = KBox::pin_init(
 73        mod_data.device_dir.scope(
 74            DeviceData { name, nums, blob },
 75            &file_name,
 76            |dev_data, dir| {
 77                for (idx, val) in dev_data.nums.iter().enumerate() {
 78                    let Ok(name) = CString::try_from_fmt(fmt!("{idx}")) else {
 79                        return;
 80                    };
 81                    dir.read_write_file(&name, val);
 82                }
 83                dir.read_write_binary_file(c_str!("blob"), &dev_data.blob);
 84            },
 85        ),
 86        GFP_KERNEL,
 87    )?;
 88    (*mod_data.devices.lock()).push(scope, GFP_KERNEL)?;
 89
 90    Ok(())
 91}
 92
 93struct RustScopedDebugFs {
 94    _data: Pin<KBox<Scope<ModuleData>>>,
 95}
 96
 97#[pin_data]
 98struct ModuleData {
 99    device_dir: Dir,
100    #[pin]
101    devices: Mutex<KVec<Pin<KBox<Scope<DeviceData>>>>>,
102}
103
104impl ModuleData {
105    fn init(device_dir: Dir) -> impl PinInit<Self> {
106        pin_init! {
107            Self {
108                device_dir: device_dir,
109                devices <- new_mutex!(KVec::new())
110            }
111        }
112    }
113}
114
115struct DeviceData {
116    name: CString,
117    nums: KVec<Atomic<usize>>,
118    blob: Pin<KBox<Mutex<[u8; SZ_4K]>>>,
119}
120
121fn init_control(base_dir: &Dir, dyn_dirs: Dir) -> impl PinInit<Scope<ModuleData>> + '_ {
122    base_dir.scope(
123        ModuleData::init(dyn_dirs),
124        c_str!("control"),
125        |data, dir| {
126            dir.write_only_callback_file(c_str!("create"), data, &create_file_write);
127            dir.write_only_callback_file(c_str!("remove"), data, &remove_file_write);
128        },
129    )
130}
131
132impl kernel::Module for RustScopedDebugFs {
133    fn init(_module: &'static kernel::ThisModule) -> Result<Self> {
134        let base_dir = Dir::new(c_str!("rust_scoped_debugfs"));
135        let dyn_dirs = base_dir.subdir(c_str!("dynamic"));
136        Ok(Self {
137            _data: KBox::pin_init(init_control(&base_dir, dyn_dirs), GFP_KERNEL)?,
138        })
139    }
140}
~/ Read-only file preview.