Re: [PATCH v2 2/9] rust: debugfs: add SeqShow trait and seq_file file operations

Alice Ryhl <[email protected]> Mon, 3 Aug 2026 17:05:55 +0200
Newsgroups dev.linux.lists.driver-core,org.freedesktop.lists.dri-devel,org.kernel.vger.rust-for-linux
Message-ID <CAH5fLgh9oZ9jRATbLM+s+tNR_KjJvdYuJ4w_vxXKwCmRd28E5w@mail.gmail.com>
On Fri, Jul 31, 2026 at 6:52=E2=80=AFPM Alvin Sun <[email protected]> wro=
te:
>
>
> On 7/31/26 21:08, Alice Ryhl wrote:
> > On Fri, Jul 31, 2026 at 01:05:40AM +0800, Alvin Sun wrote:
> >> +/// Show callback for `SeqShow` types.
> >> +///
> >> +/// # Safety
> >> +///
> >> +/// The seq_file core guarantees that `seq` points to a live `seq_fil=
e` and that
> >> +/// `seq->private` is a valid pointer to a `T` with no outstanding mu=
table
> >> +/// references.
> >> +unsafe extern "C" fn seq_file_show<S: SeqShow<T>, T: Sync>(
> >> +    seq: *mut bindings::seq_file,
> >> +    _: *mut crate::ffi::c_void,
> >> +) -> crate::ffi::c_int {
> >> +    // SAFETY: `seq->private` is a valid `T` pointer with no outstand=
ing
> >> +    // mutable references.
> >> +    let data =3D unsafe { &*((*seq).private.cast::<T>()) };
> >> +    // SAFETY: `seq` points to a live `seq_file`.
> >> +    let m =3D unsafe { SeqFile::from_raw(seq) };
> >> +    from_result(|| S::show(data, m).map(|()| 0))
> >> +}
> >> +
> >> +/// Renders `data` into a seq_file.
> >> +///
> >> +/// `data` is the value stashed as the debugfs file's `i_private` at =
creation
> >> +/// time. `show` is invoked on each read to produce the file's conten=
ts.
> >> +///
> >> +/// `open` and `release` are optional lifecycle hooks called during f=
ile open
> >> +/// and release. They can be used to manage the lifetime of `data` (e=
.g.,
> >> +/// reference counting). Default implementations are no-ops.
> >> +pub trait SeqShow<T> {
> >> +    /// Writes debugfs output for the file.
> >> +    fn show(data: &T, m: &SeqFile) -> Result;
> >> +
> >> +    /// Called during file open, before `single_open`.
> >> +    fn open(_data: &T) -> Result {
> >> +        Ok(())
> >> +    }
> >> +
> >> +    /// Called during file release, before `single_release`.
> >> +    ///
> >> +    /// # Safety
> >> +    ///
> >> +    /// `data` must point to valid memory, kept alive by actions take=
n in
> >> +    /// [`Self::open`] (e.g., incrementing a reference count).
> >> +    unsafe fn release(_data: NonNull<T>) {}
> >> +}
> > Hmm. So in a later patch you implement SeqShow in the following manner:
> >
> >      fn open(dev: &drm::Device<T, drm::Normal>) -> Result {
> >          // Hold a device reference so the device is not freed while th=
e file is open.
> >          dev.inc_ref();
> >          Ok(())
> >      }
> >
> >      unsafe fn release(dev: NonNull<drm::Device<T, drm::Normal>>) {
> >          // Drop the reference taken in `open`.
> >          // SAFETY: `dev` is valid per this function's safety contract.
> >          unsafe { drm::Device::<T>::dec_ref(dev) };
> >      }
> >
> > I don't understand why this increment/decrement pair is necessary.
> > Just based on the code, it *looks* like you have them because otherwise
> > `show()` might get called with a dangling pointer to the drm device, bu=
t
> > the inc_ref() in open() ensures it stays alive.
> >
> > However, earlier in this patch you said:
> >
> >> The seq_file core guarantees that `seq` points to a live `seq_file` an=
d
> >> that `seq->private` is a valid pointer to a `T` with no outstanding
> >> mutable references.
> > So based on this, you are saying that open()/release() do not need to d=
o
> > anything special for the value to stay alive.
> >
> > These seem contradictory. Could you clarify the situation?
>
> You're right. The `SeqShow` trait documentation was inaccurate -- the
> framework does not manage the lifetime of `data`, so `inc_ref`/`dec_ref`
> in `DrmSeqShow` is necessary to prevent `show()` from accessing a freed
> device.
>
> Will fix the doc in the next revision.

I'm not convinced that works soundness-wise. If I just leave
open/release empty, then I will be able to access a &T inside of
show(), but there's nothing keeping it alive, so I could dereference a
freed &T in safe code that way.

I think we need the seq file to instead own some sort of value. Then,
show() can be passed the a reference to value it has ownership of, and
release() can be said type's destructor.

Alice