Files
linux/samples/rust/rust_debugfs_scoped.rs
Linus Torvalds 416f99c3b1 Merge tag 'driver-core-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core
Pull driver core updates from Danilo Krummrich:
 "Arch Topology:
   - Move parse_acpi_topology() from arm64 to common code for reuse in
     RISC-V

  CPU:
   - Expose housekeeping CPUs through /sys/devices/system/cpu/housekeeping
   - Print a newline (or 0x0A) instead of '(null)' reading
     /sys/devices/system/cpu/nohz_full when nohz_full= is not set

  debugfs
   - Remove (broken) 'no-mount' mode
   - Remove redundant access mode checks in debugfs_get_tree() and
     debugfs_create_*() functions

  Devres:
   - Remove unused devm_free_percpu() helper
   - Move devm_alloc_percpu() from device.h to devres.h

  Firmware Loader:
   - Replace simple_strtol() with kstrtoint()
   - Do not call cancel_store() when no upload is in progress

  kernfs:
   - Increase struct super_block::maxbytes to MAX_LFS_FILESIZE
   - Fix a missing unwind path in __kernfs_new_node()

  Misc:
   - Increase the name size in struct auxiliary_device_id to 40
     characters
   - Replace system_unbound_wq with system_dfl_wq and add WQ_PERCPU to
     alloc_workqueue()

  Platform:
   - Replace ERR_PTR() with IOMEM_ERR_PTR() in platform ioremap
     functions

  Rust:
   - Auxiliary:
      - Unregister auxiliary device on parent device unbind
      - Move parent() to impl Device; implement device context aware
        parent() for Device<Bound>
      - Illustrate how to safely obtain a driver's device private data
        when calling from an auxiliary driver into the parant device
        driver

   - DebugFs:
      - Implement support for binary large objects

   - Device:
      - Let probe() return the driver's device private data as pinned
        initializer, i.e. impl PinInit<Self, Error>
      - Implement safe accessor for a driver's device private data for
        Device<Bound> (returned reference can't out-live driver binding
        and guarantees the correct private data type)
      - Implement AsBusDevice trait, to be used by class device
        abstractions to derive the bus device type of the parent device

   - DMA:
      - Store raw pointer of allocation as NonNull
      - Use start_ptr() and start_ptr_mut() to inherit correct
        mutability of self

   - FS:
      - Add file::Offset type alias

   - I2C:
      - Add abstractions for I2C device / driver infrastructure
      - Implement abstractions for manual I2C device registrations

   - I/O:
      - Use "kernel vertical" style for imports
      - Define ResourceSize as resource_size_t
      - Move ResourceSize to top-level I/O module
      - Add type alias for phys_addr_t
      - Implement Rust version of read_poll_timeout_atomic()

   - PCI:
      - Use "kernel vertical" style for imports
      - Move I/O and IRQ infrastructure to separate files
      - Add support for PCI interrupt vectors
      - Implement TryInto<IrqRequest<'a>> for IrqVector<'a> to convert
        an IrqVector bound to specific pci::Device into an IrqRequest
        bound to the same pci::Device's parent Device
      - Leverage pin_init_scope() to get rid of redundant Result in IRQ
        methods

   - PinInit:
      - Add {pin_}init_scope() to execute code before creating an
        initializer

   - Platform:
      - Leverage pin_init_scope() to get rid of redundant Result in IRQ
        methods

   - Timekeeping:
      - Implement abstraction of udelay()

   - Uaccess:
      - Implement read_slice_partial() and read_slice_file() for
        UserSliceReader
      - Implement write_slice_partial() and write_slice_file() for
        UserSliceWriter

  sysfs:
   - Prepare the constification of struct attribute"

* tag 'driver-core-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core: (75 commits)
  rust: pci: fix build failure when CONFIG_PCI_MSI is disabled
  debugfs: Fix default access mode config check
  debugfs: Remove broken no-mount mode
  debugfs: Remove redundant access mode checks
  driver core: Check drivers_autoprobe for all added devices
  driver core: WQ_PERCPU added to alloc_workqueue users
  driver core: replace use of system_unbound_wq with system_dfl_wq
  tick/nohz: Expose housekeeping CPUs in sysfs
  tick/nohz: avoid showing '(null)' if nohz_full= not set
  sysfs/cpu: Use DEVICE_ATTR_RO for nohz_full attribute
  kernfs: fix memory leak of kernfs_iattrs in __kernfs_new_node
  fs/kernfs: raise sb->maxbytes to MAX_LFS_FILESIZE
  mod_devicetable: Bump auxiliary_device_id name size
  sysfs: simplify attribute definition macros
  samples/kobject: constify 'struct foo_attribute'
  samples/kobject: add is_visible() callback to attribute group
  sysfs: attribute_group: enable const variants of is_visible()
  sysfs: introduce __SYSFS_FUNCTION_ALTERNATIVE()
  sysfs: transparently handle const pointers in ATTRIBUTE_GROUPS()
  sysfs: attribute_group: allow registration of const attribute
  ...
2025-12-05 21:29:02 -08:00

141 lines
3.9 KiB
Rust

// 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};
use kernel::prelude::*;
use kernel::sizes::*;
use kernel::sync::atomic::Atomic;
use kernel::sync::Mutex;
use kernel::{c_str, new_mutex, str::CString};
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_str!("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_str!("control"),
|data, dir| {
dir.write_only_callback_file(c_str!("create"), data, &create_file_write);
dir.write_only_callback_file(c_str!("remove"), data, &remove_file_write);
},
)
}
impl kernel::Module for RustScopedDebugFs {
fn init(_module: &'static kernel::ThisModule) -> Result<Self> {
let base_dir = Dir::new(c_str!("rust_scoped_debugfs"));
let dyn_dirs = base_dir.subdir(c_str!("dynamic"));
Ok(Self {
_data: KBox::pin_init(init_control(&base_dir, dyn_dirs), GFP_KERNEL)?,
})
}
}