summaryrefslogtreecommitdiff
path: root/rust/kernel/debugfs/file_ops.rs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2025-12-05 21:29:02 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2025-12-05 21:29:02 -0800
commit416f99c3b16f582a3fc6d64a1f77f39d94b76de5 (patch)
tree8322813d7c49c3a99550eaf80c245bc163685b83 /rust/kernel/debugfs/file_ops.rs
parentb1ae17cd0f0a2ffe1e9da007587c8eebb1bf8c69 (diff)
parent473b9f331718267815649cd93801da832200db71 (diff)
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 ...
Diffstat (limited to 'rust/kernel/debugfs/file_ops.rs')
-rw-r--r--rust/kernel/debugfs/file_ops.rs140
1 files changed, 139 insertions, 1 deletions
diff --git a/rust/kernel/debugfs/file_ops.rs b/rust/kernel/debugfs/file_ops.rs
index 9ad5e3fa6f69..8a0442d6dd7a 100644
--- a/rust/kernel/debugfs/file_ops.rs
+++ b/rust/kernel/debugfs/file_ops.rs
@@ -1,9 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2025 Google LLC.
-use super::{Reader, Writer};
+use super::{BinaryReader, BinaryWriter, Reader, Writer};
use crate::debugfs::callback_adapters::Adapter;
use crate::fmt;
+use crate::fs::file;
use crate::prelude::*;
use crate::seq_file::SeqFile;
use crate::seq_print;
@@ -245,3 +246,140 @@ impl<T: Reader + Sync> WriteFile<T> for T {
unsafe { FileOps::new(operations, 0o200) }
};
}
+
+extern "C" fn blob_read<T: BinaryWriter>(
+ file: *mut bindings::file,
+ buf: *mut c_char,
+ count: usize,
+ ppos: *mut bindings::loff_t,
+) -> isize {
+ // SAFETY:
+ // - `file` is a valid pointer to a `struct file`.
+ // - The type invariant of `FileOps` guarantees that `private_data` points to a valid `T`.
+ let this = unsafe { &*((*file).private_data.cast::<T>()) };
+
+ // SAFETY:
+ // - `ppos` is a valid `file::Offset` pointer.
+ // - We have exclusive access to `ppos`.
+ let pos: &mut file::Offset = unsafe { &mut *ppos };
+
+ let mut writer = UserSlice::new(UserPtr::from_ptr(buf.cast()), count).writer();
+
+ let ret = || -> Result<isize> {
+ let written = this.write_to_slice(&mut writer, pos)?;
+
+ Ok(written.try_into()?)
+ }();
+
+ match ret {
+ Ok(n) => n,
+ Err(e) => e.to_errno() as isize,
+ }
+}
+
+/// Representation of [`FileOps`] for read only binary files.
+pub(crate) trait BinaryReadFile<T> {
+ const FILE_OPS: FileOps<T>;
+}
+
+impl<T: BinaryWriter + Sync> BinaryReadFile<T> for T {
+ const FILE_OPS: FileOps<T> = {
+ let operations = bindings::file_operations {
+ read: Some(blob_read::<T>),
+ llseek: Some(bindings::default_llseek),
+ open: Some(bindings::simple_open),
+ // SAFETY: `file_operations` supports zeroes in all fields.
+ ..unsafe { core::mem::zeroed() }
+ };
+
+ // SAFETY:
+ // - The private data of `struct inode` does always contain a pointer to a valid `T`.
+ // - `simple_open()` stores the `struct inode`'s private data in the private data of the
+ // corresponding `struct file`.
+ // - `blob_read()` re-creates a reference to `T` from the `struct file`'s private data.
+ // - `default_llseek()` does not access the `struct file`'s private data.
+ unsafe { FileOps::new(operations, 0o400) }
+ };
+}
+
+extern "C" fn blob_write<T: BinaryReader>(
+ file: *mut bindings::file,
+ buf: *const c_char,
+ count: usize,
+ ppos: *mut bindings::loff_t,
+) -> isize {
+ // SAFETY:
+ // - `file` is a valid pointer to a `struct file`.
+ // - The type invariant of `FileOps` guarantees that `private_data` points to a valid `T`.
+ let this = unsafe { &*((*file).private_data.cast::<T>()) };
+
+ // SAFETY:
+ // - `ppos` is a valid `file::Offset` pointer.
+ // - We have exclusive access to `ppos`.
+ let pos: &mut file::Offset = unsafe { &mut *ppos };
+
+ let mut reader = UserSlice::new(UserPtr::from_ptr(buf.cast_mut().cast()), count).reader();
+
+ let ret = || -> Result<isize> {
+ let read = this.read_from_slice(&mut reader, pos)?;
+
+ Ok(read.try_into()?)
+ }();
+
+ match ret {
+ Ok(n) => n,
+ Err(e) => e.to_errno() as isize,
+ }
+}
+
+/// Representation of [`FileOps`] for write only binary files.
+pub(crate) trait BinaryWriteFile<T> {
+ const FILE_OPS: FileOps<T>;
+}
+
+impl<T: BinaryReader + Sync> BinaryWriteFile<T> for T {
+ const FILE_OPS: FileOps<T> = {
+ let operations = bindings::file_operations {
+ write: Some(blob_write::<T>),
+ llseek: Some(bindings::default_llseek),
+ open: Some(bindings::simple_open),
+ // SAFETY: `file_operations` supports zeroes in all fields.
+ ..unsafe { core::mem::zeroed() }
+ };
+
+ // SAFETY:
+ // - The private data of `struct inode` does always contain a pointer to a valid `T`.
+ // - `simple_open()` stores the `struct inode`'s private data in the private data of the
+ // corresponding `struct file`.
+ // - `blob_write()` re-creates a reference to `T` from the `struct file`'s private data.
+ // - `default_llseek()` does not access the `struct file`'s private data.
+ unsafe { FileOps::new(operations, 0o200) }
+ };
+}
+
+/// Representation of [`FileOps`] for read/write binary files.
+pub(crate) trait BinaryReadWriteFile<T> {
+ const FILE_OPS: FileOps<T>;
+}
+
+impl<T: BinaryWriter + BinaryReader + Sync> BinaryReadWriteFile<T> for T {
+ const FILE_OPS: FileOps<T> = {
+ let operations = bindings::file_operations {
+ read: Some(blob_read::<T>),
+ write: Some(blob_write::<T>),
+ llseek: Some(bindings::default_llseek),
+ open: Some(bindings::simple_open),
+ // SAFETY: `file_operations` supports zeroes in all fields.
+ ..unsafe { core::mem::zeroed() }
+ };
+
+ // SAFETY:
+ // - The private data of `struct inode` does always contain a pointer to a valid `T`.
+ // - `simple_open()` stores the `struct inode`'s private data in the private data of the
+ // corresponding `struct file`.
+ // - `blob_read()` re-creates a reference to `T` from the `struct file`'s private data.
+ // - `blob_write()` re-creates a reference to `T` from the `struct file`'s private data.
+ // - `default_llseek()` does not access the `struct file`'s private data.
+ unsafe { FileOps::new(operations, 0o600) }
+ };
+}