144 lines · 3.8 KB · Rust
RawDownload
// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2025 Google LLC. //! Sample DebugFS exporting platform driver that demonstrates the use of//! `Scope::dir` to create a variety of files without the need to separately//! track them all. use kernel::{    debugfs::{        Dir,        Scope, //    },    new_mutex,    prelude::*,    sizes::*,    str::CString,    sync::{        atomic::Atomic,        Mutex, //    },}; module! {    type: RustScopedDebugFs,    name: "rust_debugfs_scoped",    authors: ["Matthew Maurer"],    description: "Rust Scoped DebugFS usage sample",    license: "GPL",} fn remove_file_write(    mod_data: &ModuleData,    reader: &mut kernel::uaccess::UserSliceReader,) -> Result {    let mut buf = [0u8; 128];    if reader.len() >= buf.len() {        return Err(EINVAL);    }    let n = reader.len();    reader.read_slice(&mut buf[..n])?;     let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?.trim();    let nul_idx = s.len();    buf[nul_idx] = 0;    let to_remove = CStr::from_bytes_with_nul(&buf[..nul_idx + 1]).map_err(|_| EINVAL)?;    mod_data        .devices        .lock()        .retain(|device| device.name.to_bytes() != to_remove.to_bytes());    Ok(())} fn create_file_write(    mod_data: &ModuleData,    reader: &mut kernel::uaccess::UserSliceReader,) -> Result {    let mut buf = [0u8; 128];    if reader.len() > buf.len() {        return Err(EINVAL);    }    let n = reader.len();    reader.read_slice(&mut buf[..n])?;     let mut nums = KVec::new();     let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?.trim();    let mut items = s.split_whitespace();    let name_str = items.next().ok_or(EINVAL)?;    let name = CString::try_from_fmt(fmt!("{name_str}"))?;    let file_name = CString::try_from_fmt(fmt!("{name_str}"))?;    for sub in items {        nums.push(            Atomic::<usize>::new(sub.parse().map_err(|_| EINVAL)?),            GFP_KERNEL,        )?;    }    let blob = KBox::pin_init(new_mutex!([0x42; SZ_4K]), GFP_KERNEL)?;     let scope = KBox::pin_init(        mod_data.device_dir.scope(            DeviceData { name, nums, blob },            &file_name,            |dev_data, dir| {                for (idx, val) in dev_data.nums.iter().enumerate() {                    let Ok(name) = CString::try_from_fmt(fmt!("{idx}")) else {                        return;                    };                    dir.read_write_file(&name, val);                }                dir.read_write_binary_file(c"blob", &dev_data.blob);            },        ),        GFP_KERNEL,    )?;    (*mod_data.devices.lock()).push(scope, GFP_KERNEL)?;     Ok(())} struct RustScopedDebugFs {    _data: Pin<KBox<Scope<ModuleData>>>,} #[pin_data]struct ModuleData {    device_dir: Dir,    #[pin]    devices: Mutex<KVec<Pin<KBox<Scope<DeviceData>>>>>,} impl ModuleData {    fn init(device_dir: Dir) -> impl PinInit<Self> {        pin_init! {            Self {                device_dir: device_dir,                devices <- new_mutex!(KVec::new())            }        }    }} struct DeviceData {    name: CString,    nums: KVec<Atomic<usize>>,    blob: Pin<KBox<Mutex<[u8; SZ_4K]>>>,} fn init_control(base_dir: &Dir, dyn_dirs: Dir) -> impl PinInit<Scope<ModuleData>> + '_ {    base_dir.scope(ModuleData::init(dyn_dirs), c"control", |data, dir| {        dir.write_only_callback_file(c"create", data, &create_file_write);        dir.write_only_callback_file(c"remove", data, &remove_file_write);    })} impl kernel::Module for RustScopedDebugFs {    fn init(_module: &'static kernel::ThisModule) -> Result<Self> {        let base_dir = Dir::new(c"rust_scoped_debugfs");        let dyn_dirs = base_dir.subdir(c"dynamic");        Ok(Self {            _data: KBox::pin_init(init_control(&base_dir, dyn_dirs), GFP_KERNEL)?,        })    }}