From: Danilo Krummrich <dakr@kernel.org>
To: dakr@kernel.org, abdiel.janulgue@gmail.com,
daniel.almeida@collabora.com, robin.murphy@arm.com,
a.hindborg@kernel.org, gregkh@linuxfoundation.org,
rafael@kernel.org, aliceryhl@google.com, acourbot@nvidia.com,
ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net,
bjorn3_gh@protonmail.com, lossin@kernel.org, tmgross@umich.edu,
tamird@kernel.org, work@onurozkan.dev, mmaurer@google.com
Cc: driver-core@lists.linux.dev, nova-gpu@lists.linux.dev,
dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
rust-for-linux@vger.kernel.org
Subject: [PATCH 1/4] rust: debugfs: drop 'static bound from ScopedDir file creation methods
Date: Sun, 30 Aug 2026 21:37:13 +0200 [thread overview]
Message-ID: <20260830193824.471089-2-dakr@kernel.org> (raw)
In-Reply-To: <20260830193824.471089-1-dakr@kernel.org>
Drop the T: 'static bound from ScopedDir's file creation methods
(read_binary_file(), read_only_file(), etc.) to support registering
debugfs files backed by types that contain non-'static references, such
as dma::Coherent<'a, T>.
The previous 'static bound existed because ScopedDir::create_file() took
&'static FileOps<T>, and &'static requires T: 'static for well-
formedness. However, this was overly conservative; FileOps instances are
always associated consts residing in static storage, so the pointer
passed to the C debugfs API is always valid for the file's lifetime.
Formalize this as a type invariant on FileOps. All instances reside in
static storage, enforced by requiring FileOps::new() to only be used in
const/static items. Replace the Deref impl with an explicit fops()
method that returns &'static bindings::file_operations, justified by the
type invariant.
With this, ScopedDir::create_file() takes &FileOps<T> (no 'static),
preserving the generic type safety (T links the fops to the data type)
while allowing non-'static T.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/debugfs.rs | 26 +++++------------
rust/kernel/debugfs/entry.rs | 4 +--
rust/kernel/debugfs/file_ops.rs | 52 +++++++++++++++++++--------------
3 files changed, 39 insertions(+), 43 deletions(-)
diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs
index d7b8014a6474..2beb55d444ca 100644
--- a/rust/kernel/debugfs.rs
+++ b/rust/kernel/debugfs.rs
@@ -538,7 +538,7 @@ pub fn dir<'dir2>(&'dir2 self, name: &CStr) -> ScopedDir<'data, 'dir2> {
}
}
- fn create_file<T: Sync>(&self, name: &CStr, data: &'data T, vtable: &'static FileOps<T>) {
+ fn create_file<T: Sync>(&self, name: &CStr, data: &'data T, vtable: &FileOps<T>) {
#[cfg(CONFIG_DEBUG_FS)]
core::mem::forget(Entry::file(name, &self.entry, data, vtable));
}
@@ -550,7 +550,7 @@ fn create_file<T: Sync>(&self, name: &CStr, data: &'data T, vtable: &'static Fil
/// This function does not produce an owning handle to the file. The created
/// file is removed when the [`Scope`] that this directory belongs
/// to is dropped.
- pub fn read_only_file<T: Writer + Send + Sync + 'static>(&self, name: &CStr, data: &'data T) {
+ pub fn read_only_file<T: Writer + Send + Sync>(&self, name: &CStr, data: &'data T) {
self.create_file(name, data, &T::FILE_OPS)
}
@@ -560,11 +560,7 @@ pub fn read_only_file<T: Writer + Send + Sync + 'static>(&self, name: &CStr, dat
///
/// This function does not produce an owning handle to the file. The created file is removed
/// when the [`Scope`] that this directory belongs to is dropped.
- pub fn read_binary_file<T: BinaryWriter + Send + Sync + 'static>(
- &self,
- name: &CStr,
- data: &'data T,
- ) {
+ pub fn read_binary_file<T: BinaryWriter + Send + Sync>(&self, name: &CStr, data: &'data T) {
self.create_file(name, data, &T::FILE_OPS)
}
@@ -596,11 +592,7 @@ pub fn read_callback_file<T, F>(&self, name: &CStr, data: &'data T, _f: &'static
/// This function does not produce an owning handle to the file. The created
/// file is removed when the [`Scope`] that this directory belongs
/// to is dropped.
- pub fn read_write_file<T: Writer + Reader + Send + Sync + 'static>(
- &self,
- name: &CStr,
- data: &'data T,
- ) {
+ pub fn read_write_file<T: Writer + Reader + Send + Sync>(&self, name: &CStr, data: &'data T) {
let vtable = &<T as ReadWriteFile<_>>::FILE_OPS;
self.create_file(name, data, vtable)
}
@@ -612,7 +604,7 @@ pub fn read_write_file<T: Writer + Reader + Send + Sync + 'static>(
///
/// This function does not produce an owning handle to the file. The created file is removed
/// when the [`Scope`] that this directory belongs to is dropped.
- pub fn read_write_binary_file<T: BinaryWriter + BinaryReader + Send + Sync + 'static>(
+ pub fn read_write_binary_file<T: BinaryWriter + BinaryReader + Send + Sync>(
&self,
name: &CStr,
data: &'data T,
@@ -655,7 +647,7 @@ pub fn read_write_callback_file<T, F, W>(
/// This function does not produce an owning handle to the file. The created
/// file is removed when the [`Scope`] that this directory belongs
/// to is dropped.
- pub fn write_only_file<T: Reader + Send + Sync + 'static>(&self, name: &CStr, data: &'data T) {
+ pub fn write_only_file<T: Reader + Send + Sync>(&self, name: &CStr, data: &'data T) {
let vtable = &<T as WriteFile<_>>::FILE_OPS;
self.create_file(name, data, vtable)
}
@@ -666,11 +658,7 @@ pub fn write_only_file<T: Reader + Send + Sync + 'static>(&self, name: &CStr, da
///
/// This function does not produce an owning handle to the file. The created file is removed
/// when the [`Scope`] that this directory belongs to is dropped.
- pub fn write_binary_file<T: BinaryReader + Send + Sync + 'static>(
- &self,
- name: &CStr,
- data: &'data T,
- ) {
+ pub fn write_binary_file<T: BinaryReader + Send + Sync>(&self, name: &CStr, data: &'data T) {
self.create_file(name, data, &T::FILE_OPS)
}
diff --git a/rust/kernel/debugfs/entry.rs b/rust/kernel/debugfs/entry.rs
index 46aad64896ec..88a870d8c295 100644
--- a/rust/kernel/debugfs/entry.rs
+++ b/rust/kernel/debugfs/entry.rs
@@ -74,7 +74,7 @@ pub(crate) unsafe fn dynamic_file<T>(
parent.as_ptr(),
core::ptr::from_ref(data) as *mut c_void,
core::ptr::null(),
- &**file_ops,
+ file_ops.fops(),
)
};
@@ -127,7 +127,7 @@ pub(crate) fn file<T>(
parent.as_ptr(),
core::ptr::from_ref(data) as *mut c_void,
core::ptr::null(),
- &**file_ops,
+ file_ops.fops(),
)
};
diff --git a/rust/kernel/debugfs/file_ops.rs b/rust/kernel/debugfs/file_ops.rs
index f15908f71c4a..7e1dd8c75ad9 100644
--- a/rust/kernel/debugfs/file_ops.rs
+++ b/rust/kernel/debugfs/file_ops.rs
@@ -20,14 +20,12 @@
use core::marker::PhantomData;
-#[cfg(CONFIG_DEBUG_FS)]
-use core::ops::Deref;
-
-/// # Invariant
+/// # Invariants
///
-/// `FileOps<T>` will always contain an `operations` which is safe to use for a file backed
-/// off an inode which has a pointer to a `T` in its private data that is safe to convert
-/// into a reference.
+/// - `FileOps<T>` will always contain an `operations` which is safe to use for a file backed
+/// off an inode which has a pointer to a `T` in its private data that is safe to convert
+/// into a reference.
+/// - Every instance of `FileOps<T>` resides in static storage.
pub(super) struct FileOps<T> {
#[cfg(CONFIG_DEBUG_FS)]
operations: bindings::file_operations,
@@ -39,9 +37,13 @@ pub(super) struct FileOps<T> {
impl<T> FileOps<T> {
/// # Safety
///
- /// The caller asserts that the provided `operations` is safe to use for a file whose
- /// inode has a pointer to `T` in its private data that is safe to convert into a reference.
+ /// - The caller asserts that the provided `operations` is safe to use for a file whose
+ /// inode has a pointer to `T` in its private data that is safe to convert into a reference.
+ /// - Must only be used to initialize a `const` or `static` item, to uphold the type invariant
+ /// that all `FileOps` instances reside in static storage.
const unsafe fn new(operations: bindings::file_operations, mode: u16) -> Self {
+ // INVARIANT: The caller is required to only use this in a `const` or `static` item,
+ // ensuring that all `FileOps` instances reside in static storage.
Self {
#[cfg(CONFIG_DEBUG_FS)]
operations,
@@ -65,11 +67,11 @@ pub(super) const fn adapt(&self) -> &FileOps<T::Inner> {
}
#[cfg(CONFIG_DEBUG_FS)]
-impl<T> Deref for FileOps<T> {
- type Target = bindings::file_operations;
-
- fn deref(&self) -> &Self::Target {
- &self.operations
+impl<T> FileOps<T> {
+ /// Returns a `'static` reference to the inner `file_operations`.
+ pub(crate) fn fops(&self) -> &'static bindings::file_operations {
+ // SAFETY: By the type invariant, `self` resides in static storage.
+ unsafe { core::mem::transmute(&self.operations) }
}
}
@@ -138,9 +140,10 @@ impl<T: Writer + Sync> ReadFile<T> for T {
..pin_init::zeroed()
};
// SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open`.
- // `open`'s only requirement beyond what is provided to all open functions is that the
- // inode's data pointer must point to a `T` that will outlive it, which matches the
- // `FileOps` requirements.
+ // - `open`'s only requirement beyond what is provided to all open functions is that the
+ // inode's data pointer must point to a `T` that will outlive it, which matches the
+ // `FileOps` requirements.
+ // - This is a `const` item, satisfying the static storage invariant.
unsafe { FileOps::new(operations, 0o400) }
};
}
@@ -194,9 +197,10 @@ impl<T: Writer + Reader + Sync> ReadWriteFile<T> for T {
// `writer_open`'s only requirement beyond what is provided to all open functions is that
// the inode's data pointer must point to a `T` that will outlive it, which matches the
// `FileOps` requirements.
- // `write` only requires that the file's private data pointer points to `seq_file`
- // which points to a `T` that will outlive it, which matches what `writer_open`
- // provides.
+ // - `write` only requires that the file's private data pointer points to `seq_file`
+ // which points to a `T` that will outlive it, which matches what `writer_open`
+ // provides.
+ // - This is a `const` item, satisfying the static storage invariant.
unsafe { FileOps::new(operations, 0o600) }
};
}
@@ -245,10 +249,11 @@ impl<T: Reader + Sync> WriteFile<T> for T {
..pin_init::zeroed()
};
// SAFETY:
- // * `write_only_open` populates the file private data with the inode private data
- // * `write_only_write`'s only requirement is that the private data of the file point to
+ // - `write_only_open` populates the file private data with the inode private data
+ // - `write_only_write`'s only requirement is that the private data of the file point to
// a `T` and be legal to convert to a shared reference, which `write_only_open`
// satisfies.
+ // - This is a `const` item, satisfying the static storage invariant.
unsafe { FileOps::new(operations, 0o200) }
};
}
@@ -303,6 +308,7 @@ impl<T: BinaryWriter + Sync> BinaryReadFile<T> for T {
// 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.
+ // - This is a `const` item, satisfying the static storage invariant.
unsafe { FileOps::new(operations, 0o400) }
};
}
@@ -357,6 +363,7 @@ impl<T: BinaryReader + Sync> BinaryWriteFile<T> for T {
// 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.
+ // - This is a `const` item, satisfying the static storage invariant.
unsafe { FileOps::new(operations, 0o200) }
};
}
@@ -383,6 +390,7 @@ impl<T: BinaryWriter + BinaryReader + Sync> BinaryReadWriteFile<T> for T {
// - `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.
+ // - This is a `const` item, satisfying the static storage invariant.
unsafe { FileOps::new(operations, 0o600) }
};
}
--
2.55.0
next prev parent reply other threads:[~2026-08-30 19:39 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-30 19:37 [PATCH 0/4] rust: dma: tie DMA allocations to the device's bound lifetime Danilo Krummrich
2026-08-30 19:37 ` Danilo Krummrich [this message]
2026-08-30 19:53 ` [PATCH 1/4] rust: debugfs: drop 'static bound from ScopedDir file creation methods sashiko-bot
2026-09-03 13:12 ` Gary Guo
2026-09-03 15:07 ` Danilo Krummrich
2026-09-03 15:16 ` Gary Guo
2026-08-30 19:37 ` [PATCH 2/4] rust: dma: tie CoherentHandle to the device's bound lifetime Danilo Krummrich
2026-09-03 13:12 ` Gary Guo
2026-08-30 19:37 ` [PATCH 3/4] samples: rust_dma: separate driver type from driver data Danilo Krummrich
2026-08-30 19:57 ` sashiko-bot
2026-09-03 13:13 ` Gary Guo
2026-08-30 19:37 ` [PATCH 4/4] rust: dma: tie Coherent and CoherentBox to the device's bound lifetime Danilo Krummrich
2026-08-30 19:47 ` sashiko-bot
2026-09-03 13:20 ` Gary Guo
2026-09-03 15:22 ` Danilo Krummrich
2026-09-03 15:42 ` Gary Guo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260830193824.471089-2-dakr@kernel.org \
--to=dakr@kernel.org \
--cc=a.hindborg@kernel.org \
--cc=abdiel.janulgue@gmail.com \
--cc=acourbot@nvidia.com \
--cc=aliceryhl@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=driver-core@lists.linux.dev \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=mmaurer@google.com \
--cc=nova-gpu@lists.linux.dev \
--cc=ojeda@kernel.org \
--cc=rafael@kernel.org \
--cc=robin.murphy@arm.com \
--cc=rust-for-linux@vger.kernel.org \
--cc=tamird@kernel.org \
--cc=tmgross@umich.edu \
--cc=work@onurozkan.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox