at master 4.0 kB view raw
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}