From nobody Fri Oct 10 04:43:19 2025 Received: from mail-pg1-f202.google.com (mail-pg1-f202.google.com [209.85.215.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EEFA119F42C for ; Wed, 18 Jun 2025 02:28:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750213699; cv=none; b=FqKtg/eajgcf6GF+CcA0hKikEAHTGhShwzc7fRK9deyZCii9XnvfqYeWdbVlqHlL3ygGs1RXG5Vqd9Bb110KTTB+pVEkYUQvZpHErhxjD+c+pPBYl2WO9CBwi1zJeOgZrLttnoBdgABTVyLIqr2/de3fT2yM+JVGGMYVckXPA+E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750213699; c=relaxed/simple; bh=c91v0oZSa1f3RWSJbPI/Wr8as6LUX6oZYXpz1cZXU4w=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=qA+1s/SSPP+wVD3U3VFRa9wkwzzXp2DSi6G9SRudyV8MLhykcWu0P2AhoeDZPeZV1Ksl4adGK9fGrwxZTMZ5fDn6hJYaBOOAcLogijG42TLWYVR8hGmdgqByLqjEA801nOwpM4KsRUSMhtCgaG5AqFg2eKT83hCUefzeZ0e8HcU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--mmaurer.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=SHMhnzXp; arc=none smtp.client-ip=209.85.215.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--mmaurer.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="SHMhnzXp" Received: by mail-pg1-f202.google.com with SMTP id 41be03b00d2f7-b2eeff19115so8105275a12.0 for ; Tue, 17 Jun 2025 19:28:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1750213697; x=1750818497; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=AdgGyioAlpkKqnAzoZUsP7EnTyjVQhraIGn06X2d058=; b=SHMhnzXp7uCEfl1WF3jmZqIX8LyxBqM91A/9ytWtjadcnE8m5ngA8PfL3okxPd7IzB zuiNCfgshQ+PeRuKG+CNIctrpmQ1LgTq0iO7tAVeNKi2Lu+A7FbjTfGmVTX+iQp+mvH6 8R4RNu/x6r/gWAEwEBmrJH9pHd6DpNCJ8AearVzuMYNJQHXHworN778mQlhQiRRmCyNf RkQ76b6GkOV09fKfKfuTKfymtgYtdbrBtUiOjSRfC4KuMtIUMpG8rIFYgSiTuuT+T3ks H7Xn1E7JwaJWMUsSPEYGq3DNUMtupRXjOR78xsF4UxBuCgGCcOP1f5ogtO9Xhy6TCbya C3uw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750213697; x=1750818497; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=AdgGyioAlpkKqnAzoZUsP7EnTyjVQhraIGn06X2d058=; b=UuEgwv53T0PAFidxIgy0LaT4Z6Y5BhW2XyHZ2ufJW+3HNTdn/7ObEgaGiuxjKuZfFP YzRWx16rZvmgUb9TSwcAP6eDKXKOMw2FRHFDNSTLC69DqNgGvyziYxDet/qG8YmkMOa8 pKUUU12LqP72Gi2Xq2C/9JcWFeK+X0eCU4E8dNffFQJh+tXeGGeBdhainMfa2D4A6nxw jKIhZEKgYUN9S28aOfOMQTj7aYNmwOXUWt5B8tU+6X2CS2eB6fzLtVZJr7jYEe4t1mBy feZFBthkkK0pe6dyH1k5eaIvd7mkaKawUFiOVx1V4FbCC8QDTl8AlQCBcJbh6ke6xOcn CFpA== X-Gm-Message-State: AOJu0YwEVTSYo84YLWajZLjmjXkgmd5ytMOtTEJbshYoJYkd04R9zST6 2UCIVdqb8b4B2p0bbJVEazfAX4zV+u/GamJm35cG3jz7Buhjg7sc0UHAI3fkcRnJokqrlBYJ/kO drAg5DQYN8A== X-Google-Smtp-Source: AGHT+IHQoOX+BrqTUCm/O8EOIxNbaBK2exfCPiELR02vgoPnaMHTN1gMaoiO1O1+N32pH0UA6ktp2C3TD0oy X-Received: from pgac10.prod.google.com ([2002:a05:6a02:294a:b0:b2c:3a2e:ac7]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:a343:b0:21f:4631:811c with SMTP id adf61e73a8af0-21fbd556858mr21618936637.19.1750213697334; Tue, 17 Jun 2025 19:28:17 -0700 (PDT) Date: Wed, 18 Jun 2025 02:28:13 +0000 In-Reply-To: <20250618-debugfs-rust-v6-0-72cae211b133@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250618-debugfs-rust-v6-0-72cae211b133@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1750213694; l=7508; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=c91v0oZSa1f3RWSJbPI/Wr8as6LUX6oZYXpz1cZXU4w=; b=EFdrhCeNGoeFPIHxGRT9W2s1EVCBCWFyS6Peod1Ov2+bo3mono66oIM3+lu82cr75uYqOiycv WGtL8B9VtV2CBEYEDNA4nHrkc8j0xex2cw++mZ+AUfvKTQ4aGMm0JA1 X-Mailer: b4 0.14.2 Message-ID: <20250618-debugfs-rust-v6-1-72cae211b133@google.com> Subject: [PATCH v6 1/5] rust: debugfs: Bind DebugFS directory creation From: Matthew Maurer To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , "Rafael J. Wysocki" , Sami Tolvanen , Timur Tabi , Benno Lossin Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Matthew Maurer Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Support creating DebugFS directories and subdirectories. Similar to the original DebugFS API, errors are hidden. Directories persist until their handle and the handles of any child objects have been dropped. Signed-off-by: Matthew Maurer Tested-by: Dirk Behme --- MAINTAINERS | 2 + rust/bindings/bindings_helper.h | 1 + rust/kernel/debugfs.rs | 88 +++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/debugfs/entry.rs | 58 +++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 5 files changed, 150 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index ec49e19734256f0c0c0960b722293edeb851562a..fda422023e30fe8821e4cd79b50= 3693ecc6eb48c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7373,6 +7373,8 @@ F: include/linux/kobj* F: include/linux/property.h F: include/linux/sysfs.h F: lib/kobj* +F: rust/kernel/debugfs.rs +F: rust/kernel/debugfs/ F: rust/kernel/device.rs F: rust/kernel/device/ F: rust/kernel/device_id.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 8cbb660e2ec218021d16e6e0144acf6f4d7cca13..655d88b8e7fe717190ccfb7b817= 3e84213bf8331 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs new file mode 100644 index 0000000000000000000000000000000000000000..e390278b8b7384f1d9e2b3d7a4f= 5f365f6447d0f --- /dev/null +++ b/rust/kernel/debugfs.rs @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2025 Google LLC. + +//! DebugFS Abstraction +//! +//! C header: [`include/linux/debugfs.h`](srctree/include/linux/debugfs.h) + +#[cfg(CONFIG_DEBUG_FS)] +use crate::prelude::GFP_KERNEL; +use crate::str::CStr; +#[cfg(CONFIG_DEBUG_FS)] +use crate::sync::Arc; + +#[cfg(CONFIG_DEBUG_FS)] +mod entry; + +/// Owning handle to a DebugFS directory. +/// +/// This directory will be cleaned up when the handle and all child direct= ory/file handles have +/// been dropped. +// We hold a reference to our parent if it exists to prevent the dentry we= point to from being +// cleaned up when our parent is removed. +pub struct Dir(#[cfg(CONFIG_DEBUG_FS)] Option>); + +impl Dir { + /// Create a new directory in DebugFS. If `parent` is [`None`], it wil= l be created at the root. + #[cfg(CONFIG_DEBUG_FS)] + fn create(name: &CStr, parent: Option<&Dir>) -> Self { + let parent_ptr =3D match parent { + // If the parent couldn't be allocated, just early-return + Some(Dir(None)) =3D> return Self(None), + Some(Dir(Some(entry))) =3D> entry.as_ptr(), + None =3D> core::ptr::null_mut(), + }; + // SAFETY: + // * `name` argument points to a NUL-terminated string that lives = across the call, by + // invariants of `&CStr`. + // * If `parent` is `None`, `parent_ptr` is null to mean create at= root. + // * If `parent` is `Some`, `parent_ptr` is a live dentry debugfs = pointer. + let dir =3D unsafe { bindings::debugfs_create_dir(name.as_char_ptr= (), parent_ptr) }; + + Self( + // If Arc creation fails, the `Entry` will be dropped, so the = directory will be cleaned + // up. + Arc::new( + // SAFETY: `debugfs_create_dir` either returns an error co= de or a legal `dentry` + // pointer, and the parent is the same one passed to `debu= gfs_create_dir` + unsafe { entry::Entry::new(dir, parent.and_then(|dir| dir.= 0.clone())) }, + GFP_KERNEL, + ) + .ok(), + ) + } + + #[cfg(not(CONFIG_DEBUG_FS))] + fn create(_name: &CStr, _parent: Option<&Dir>) -> Self { + Self() + } + + /// Create a DebugFS subdirectory. + /// + /// Subdirectory handles cannot outlive the directory handle they were= created from. + /// + /// # Examples + /// + /// ``` + /// # use kernel::c_str; + /// # use kernel::debugfs::Dir; + /// let parent =3D Dir::new(c_str!("parent")); + /// let child =3D parent.subdir(c_str!("child")); + /// ``` + pub fn subdir(&self, name: &CStr) -> Self { + Dir::create(name, Some(self)) + } + + /// Create a new directory in DebugFS at the root. + /// + /// # Examples + /// + /// ``` + /// # use kernel::c_str; + /// # use kernel::debugfs::Dir; + /// let debugfs =3D Dir::new(c_str!("parent")); + /// ``` + pub fn new(name: &CStr) -> Self { + Dir::create(name, None) + } +} diff --git a/rust/kernel/debugfs/entry.rs b/rust/kernel/debugfs/entry.rs new file mode 100644 index 0000000000000000000000000000000000000000..ae0e2c4e1d58e878ebb081a71e4= ac0f4a7d99b91 --- /dev/null +++ b/rust/kernel/debugfs/entry.rs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2025 Google LLC. + +use crate::sync::Arc; + +/// Owning handle to a DebugFS entry. +/// +/// # Invariants +/// +/// The wrapped pointer will always be `NULL`, an error, or an owned Debug= FS `dentry`. +pub(crate) struct Entry { + entry: *mut bindings::dentry, + _parent: Option>, +} + +// SAFETY: [`Entry`] is just a `dentry` under the hood, which the API prom= ises can be transferred +// between threads. +unsafe impl Send for Entry {} + +// SAFETY: All the native functions we re-export use interior locking, and= the contents of the +// struct are opaque to Rust. +unsafe impl Sync for Entry {} + +impl Entry { + /// Constructs a new DebugFS [`Entry`] from the underlying pointer. + /// + /// # Safety + /// + /// The pointer must either be an error code, `NULL`, or represent a t= ransfer of ownership of a + /// live DebugFS directory. If this is a child directory or file, `'a`= must be less than the + /// lifetime of the parent directory. + /// + /// If the dentry has a parent, it must be provided as the parent argu= ment. + pub(crate) unsafe fn new(entry: *mut bindings::dentry, parent: Option<= Arc>) -> Self { + Self { + entry, + _parent: parent, + } + } + + /// Returns the pointer representation of the DebugFS directory. + /// + /// # Guarantees + /// + /// Due to the type invariant, the value returned from this function w= ill always be an error + /// code, NULL, or a live DebugFS directory. + pub(crate) fn as_ptr(&self) -> *mut bindings::dentry { + self.entry + } +} + +impl Drop for Entry { + fn drop(&mut self) { + // SAFETY: `debugfs_remove` can take `NULL`, error values, and leg= al DebugFS dentries. + // `as_ptr` guarantees that the pointer is of this form. + unsafe { bindings::debugfs_remove(self.as_ptr()) } + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 6b4774b2b1c37f4da1866e993be6230bc6715841..43f40b1baa9717ea71e4586042e= 9e6979491ad37 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -66,6 +66,7 @@ pub mod cpufreq; pub mod cpumask; pub mod cred; +pub mod debugfs; pub mod device; pub mod device_id; pub mod devres; --=20 2.50.0.rc2.696.g1fc2a0284f-goog From nobody Fri Oct 10 04:43:19 2025 Received: from mail-pf1-f202.google.com (mail-pf1-f202.google.com [209.85.210.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DAD9E72606 for ; Wed, 18 Jun 2025 02:28:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750213701; cv=none; b=GYZaYpaNvjaXxkikJe05tchujBWrXTcxo9b2yFRfu1onmFS6tUO7AWVbbWynlhHpjC0Ti1+R7RIuRGXzox30+hFaUpA3wtDV7wja/AWBeUArfNJ0DbH/rbxUbRzAoX4128foLTIrZxhMhedueBoeDFfsPYmGdtrZ6XYnmychYrQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750213701; c=relaxed/simple; bh=arncnwztjoqLIa7H95YpFTsDUfZi47rpF4nw6cr7M0A=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=rSR5p6knfCIToK7/wfcdr7sz6uFT6V32g1+Ae94XIvxq2b6o5gVOhYCOb6ThAc8ZtAKB44FfwU6hfztIEiJL05IHDkL6JdLuLJ6sr33ZD715kNVU4cb+csjlbYaI2Ab5qCxbBDng55F5mAa6WkSO1iOWxLucq03cR7Wc+5zLj3w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--mmaurer.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=vdjFDRAC; arc=none smtp.client-ip=209.85.210.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--mmaurer.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="vdjFDRAC" Received: by mail-pf1-f202.google.com with SMTP id d2e1a72fcca58-748e1e474f8so906709b3a.2 for ; Tue, 17 Jun 2025 19:28:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1750213699; x=1750818499; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=5g9C/2aXjP3q3IRmwt44Q9zBgZjDUhb/vz3CKf6T5mY=; b=vdjFDRACDmxxBoJjdJscinXKo2meX5kgIP40IaMDE4jzhZDqlqjNGRIK03N6K6WxZm nZcAg1rxI2dkaZGOTGgSf50Fg2nW2TpzrBfOs8JYahirao0CnRVzkaiEk5ot5SfSCIyk q9jxTo+YS4TrLNw4c06nR7m9btE4IraAXCvfm8ZLmRiqFriJrGBRGfcrbY4pdKmppFeK h18MQvkoDxhaAo4aLD2Re3+Ams1WfnYl1INkl8FbsKTrszTx5jNLK7ZEzgZ/LrGNzJC/ OyXaeK6uv4qcdNMVH5BM8YlFxrMY/XOks8rzIqf/uOom8spTS8idv7TJSXDqRCpgmbAw 2gNg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750213699; x=1750818499; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=5g9C/2aXjP3q3IRmwt44Q9zBgZjDUhb/vz3CKf6T5mY=; b=vE93oQiJV9ZFwRg48o50uSwT4vE5x9Q0weougsfCXUnp4aLa6u07GcCPv1mi7QCdw7 QOQl79CInJfu+ZrUHR2E9q1ni2JK1sicHL9TP1TcsCp6F33b4JldVv6rsjz4ec9MQyxp MIEkwbnh4MP7/qRjLC+sh2mqtgggia736mtNn5vJ5ONYyhpXHnfYGeqi3HvJCWy4TuAM f4tAva4vaRSNev1QTtWHfCCXgrl5GYXk0w/XvRUS4OBFR+Pq0aiI4dbZe6ZYeWQC6lcM a/c1ndK3og+6kP62gHaizIElzEdRvTIIiZKRjCrln5XGB89TM2iHpLKNYjJdrRnpA42A W4PQ== X-Gm-Message-State: AOJu0YyyZX+CbZCxF2PcsfamMhPbZ3IEGc/6jxaVXB+dWnb/whZr2Q9E 1U5+UpcTwuBYNxgDCELBY7fyYlRsh0UDHxXDzzuYzlmsI2cHV6+n1UMz1fqVl85vii7K3x4Tgfz Uie2mDokv1A== X-Google-Smtp-Source: AGHT+IF5WvYUSe54T6Ow+jbHfSuXMsSRK3PMsFOXvOFipiuoXcxFOhmVI/04CxZ+x/0s0oKv6VvB3fTb+57u X-Received: from pfbde14.prod.google.com ([2002:a05:6a00:468e:b0:748:dd5c:e882]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a00:1790:b0:736:3d7c:236c with SMTP id d2e1a72fcca58-7489d062437mr20439275b3a.14.1750213698962; Tue, 17 Jun 2025 19:28:18 -0700 (PDT) Date: Wed, 18 Jun 2025 02:28:14 +0000 In-Reply-To: <20250618-debugfs-rust-v6-0-72cae211b133@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250618-debugfs-rust-v6-0-72cae211b133@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1750213694; l=7330; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=arncnwztjoqLIa7H95YpFTsDUfZi47rpF4nw6cr7M0A=; b=RPkAyspbgBp0kXzMBUSWsawJSmXKkKlsU5ypJfOiEBT6ZbLFGMzqyTkkOnEyCEKTIa5ViV+6z WuIx7BS/XsjBshg7TA4rDpYrKfXoR+UweHV8tDoIF/KpixeI3QdpprL X-Mailer: b4 0.14.2 Message-ID: <20250618-debugfs-rust-v6-2-72cae211b133@google.com> Subject: [PATCH v6 2/5] rust: debugfs: Bind file creation for long-lived Display From: Matthew Maurer To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , "Rafael J. Wysocki" , Sami Tolvanen , Timur Tabi , Benno Lossin Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Matthew Maurer Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Allows creation of files for references that live forever and lack metadata through the `Display` implementation. The reference must live forever because we do not have a maximum lifetime for the file we are creating. The `Display` implementation is used because `seq_printf` needs to route through `%pA`, which in turn routes through Arguments. Signed-off-by: Matthew Maurer Tested-by: Dirk Behme --- rust/kernel/debugfs.rs | 62 +++++++++++++++++++++++++++++++++= ++++ rust/kernel/debugfs/display_file.rs | 60 +++++++++++++++++++++++++++++++++= ++ rust/kernel/debugfs/entry.rs | 8 +++++ 3 files changed, 130 insertions(+) diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs index e390278b8b7384f1d9e2b3d7a4f5f365f6447d0f..6a89557d8cf49327d2984d15741= ffb6640defd70 100644 --- a/rust/kernel/debugfs.rs +++ b/rust/kernel/debugfs.rs @@ -10,7 +10,10 @@ use crate::str::CStr; #[cfg(CONFIG_DEBUG_FS)] use crate::sync::Arc; +use core::fmt::Display; =20 +#[cfg(CONFIG_DEBUG_FS)] +mod display_file; #[cfg(CONFIG_DEBUG_FS)] mod entry; =20 @@ -57,6 +60,43 @@ fn create(_name: &CStr, _parent: Option<&Dir>) -> Self { Self() } =20 + #[cfg(CONFIG_DEBUG_FS)] + fn create_file(&self, name: &CStr, data: &'static = T) -> File { + let Some(parent) =3D &self.0 else { + return File { + _entry: entry::Entry::empty(), + }; + }; + // SAFETY: + // * `name` is a NUL-terminated C string, living across the call, = by `CStr` invariant. + // * `parent` is a live `dentry` since we have a reference to it. + // * `vtable` is all stock `seq_file` implementations except for `= 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 we know because + // we have a static reference. + let ptr =3D unsafe { + bindings::debugfs_create_file_full( + name.as_char_ptr(), + 0o444, + parent.as_ptr(), + data as *const _ as *mut _, + core::ptr::null(), + &::VTABLE, + ) + }; + + // SAFETY: `debugfs_create_file_full` either returns an error code= or a legal + // dentry pointer, so `Entry::new` is safe to call here. + let entry =3D unsafe { entry::Entry::new(ptr, Some(parent.clone())= ) }; + + File { _entry: entry } + } + + #[cfg(not(CONFIG_DEBUG_FS))] + fn create_file(&self, _name: &CStr, _data: &'stati= c T) -> File { + File {} + } + /// Create a DebugFS subdirectory. /// /// Subdirectory handles cannot outlive the directory handle they were= created from. @@ -73,6 +113,22 @@ pub fn subdir(&self, name: &CStr) -> Self { Dir::create(name, Some(self)) } =20 + /// Create a file in a DebugFS directory with the provided name, and c= ontents from invoking + /// [`Display::fmt`] on the provided reference. + /// + /// # Examples + /// + /// ``` + /// # use kernel::c_str; + /// # use kernel::debugfs::Dir; + /// let dir =3D Dir::new(c_str!("my_debugfs_dir")); + /// dir.display_file(c_str!("foo"), &200); + /// // "my_debugfs_dir/foo" now contains the number 200. + /// ``` + pub fn display_file(&self, name: &CStr, data: &'st= atic T) -> File { + self.create_file(name, data) + } + /// Create a new directory in DebugFS at the root. /// /// # Examples @@ -86,3 +142,9 @@ pub fn new(name: &CStr) -> Self { Dir::create(name, None) } } + +/// Handle to a DebugFS file. +pub struct File { + #[cfg(CONFIG_DEBUG_FS)] + _entry: entry::Entry, +} diff --git a/rust/kernel/debugfs/display_file.rs b/rust/kernel/debugfs/disp= lay_file.rs new file mode 100644 index 0000000000000000000000000000000000000000..65e37e34d7b587482492dc29663= 7a3bc517b9fe3 --- /dev/null +++ b/rust/kernel/debugfs/display_file.rs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2025 Google LLC. + +use crate::seq_file::SeqFile; +use crate::seq_print; +use core::fmt::Display; + +/// Implements `open` for `file_operations` via `single_open` to fill out = a `seq_file`. +/// +/// # Safety +/// +/// * `inode`'s private pointer must point to a value of type `T` which wi= ll outlive the `inode` +/// and will not be mutated during this call. +/// * `file` must point to a live, not-yet-initialized file object. +pub(crate) unsafe extern "C" fn display_open( + inode: *mut bindings::inode, + file: *mut bindings::file, +) -> i32 { + // SAFETY: + // * `file` is acceptable by caller precondition. + // * `print_act` will be called on a `seq_file` with private data set = to the third argument, + // so we meet its safety requirements. + // * The `data` pointer passed in the third argument is a valid `T` po= inter that outlives + // this call by caller preconditions. + unsafe { bindings::single_open(file, Some(display_act::), (*inode).= i_private) } +} + +/// Prints private data stashed in a seq_file to that seq file. +/// +/// # Safety +/// +/// `seq` must point to a live `seq_file` whose private data is a live poi= nter to a `T` which is +/// not being mutated. +pub(crate) unsafe extern "C" fn display_act( + seq: *mut bindings::seq_file, + _: *mut core::ffi::c_void, +) -> i32 { + // SAFETY: By caller precondition, this pointer is live, points to a v= alue of type `T`, and + // is not being mutated. + let data =3D unsafe { &*((*seq).private as *mut T) }; + // SAFETY: By caller precondition, `seq_file` points to a live `seq_fi= le`, so we can lift + // it. + let seq_file =3D unsafe { SeqFile::from_raw(seq) }; + seq_print!(seq_file, "{}", data); + 0 +} + +// Work around lack of generic const items. +pub(crate) trait DisplayFile: Display + Sized { + const VTABLE: bindings::file_operations =3D bindings::file_operations { + read: Some(bindings::seq_read), + llseek: Some(bindings::seq_lseek), + release: Some(bindings::single_release), + open: Some(display_open:: as _), + // SAFETY: `file_operations` supports zeroes in all fields. + ..unsafe { core::mem::zeroed() } + }; +} + +impl DisplayFile for T {} diff --git a/rust/kernel/debugfs/entry.rs b/rust/kernel/debugfs/entry.rs index ae0e2c4e1d58e878ebb081a71e4ac0f4a7d99b91..2baaf31c326c3071b92b5bc3755= 2907fa1102246 100644 --- a/rust/kernel/debugfs/entry.rs +++ b/rust/kernel/debugfs/entry.rs @@ -38,6 +38,14 @@ pub(crate) unsafe fn new(entry: *mut bindings::dentry, p= arent: Option } } =20 + /// Constructs a placeholder DebugFS [`Entry`]. + pub(crate) fn empty() -> Self { + Self { + entry: core::ptr::null_mut(), + _parent: None, + } + } + /// Returns the pointer representation of the DebugFS directory. /// /// # Guarantees --=20 2.50.0.rc2.696.g1fc2a0284f-goog From nobody Fri Oct 10 04:43:19 2025 Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 12B861B4223 for ; Wed, 18 Jun 2025 02:28:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750213702; cv=none; b=emrZCGIDhNuC+xYvkATifG6VhVrX3EKEFVmxvgVkqDWY4GRWtST46MMsdBj6hgu1Euu0AxPQh9qZTg5Yo+j53j0jjmESFBXAPgClfeEa1sgaZNkVnKjy3Y/2s5Z9bsdJOjrOv2Y+py+CWGZe01uDJwl//QQCC8O9fVb9vq6e4gA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750213702; c=relaxed/simple; bh=7DbO/U0Rj2wyZtj+j8Z2FBKA+3rtUseZ8Hbvsru8fEM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=YJqIXURl0iPHvhgxIp6o3tsUBkceGO+znXyg3jeieKQNJULBeD9NtP09vJCek9E+ASicAGHC1gtsB/AnIOLawi6uCetF/C762FbkKwAEFK508+MVekLpJcmt4I0UEOU3u7xHB8dSWtnWlyBaj1is9o3I8kakwMW8hG2ktNKMQNk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--mmaurer.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=V9Nfm9Gp; arc=none smtp.client-ip=209.85.214.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--mmaurer.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="V9Nfm9Gp" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-234b133b428so47572995ad.3 for ; Tue, 17 Jun 2025 19:28:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1750213700; x=1750818500; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=DMo2kOcPe/ZWoI8EPcGG6fh15t/rhnw+MiWtIgoq4ZU=; b=V9Nfm9GpKI5HKyae4HvjYdg1CYuL5jU5fnZyb560EUSgPTFLSMSnLCtsqvxsM2Y9xS G3fUGNVLLbn8MD1czCz+JR1ofdl7L3+vBm3wjXO1Xsr/5tmn3oqi8TYMrY7P53Sr8DQg MnHaaK8MZCvnyx/kQlPjUvhGIAemx2bbz+gEpuYAuTOtdVtHhKSsxZZ3j1kuum3/b/Ro 6ZbLIEJiVA3I/h4y35K4eMJEI5ivREa7cqlitmvqyJqSU204H209OqB0kKZGEFlL1FP+ E2qIw6R/7qITwIUHvzjJsWX1foRO6DVtCb31vImUflD5PKHSV5iOA4smnvFK2+NG/DJI MsXw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750213700; x=1750818500; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=DMo2kOcPe/ZWoI8EPcGG6fh15t/rhnw+MiWtIgoq4ZU=; b=AsrH11Nw8Q5tKNafNwgyt6btoZv5zGbNWlwQr0GF5DwdYvmk6oyOj/lypFuTKbrAR8 kLLUkk8MsLEdCjcYuf8dOvW1Mijm1vTzkruDPRMNobluaSCnHs6WXk1kupcxMKZnmyGh 5EMlw1VKRWMDoY2JuwJcb5ShCc87+23Ypxtlek4bHzEGm2Sgx9lSrBlk4YvBadgMPBS6 NfSXs16qtupI/QtCOTchoNNYNE+Nbxd3zmf0TMUzQK5wC5YMJzeLMfX455rjWdAlKAXH lNvcYZ/MBgVIlN4V+EtdFN1/+da7kEKMq+7PtkqTHue1enmrnhxTdCU8N3sDIOhYigNq 5xEg== X-Gm-Message-State: AOJu0YwPVMCzNu4bLPit0cGGf67u0Udorre/kCByL6Jui3T//+afW9No ggeAAGXK1IgSzFIh2ycdQUYzqGf/tJrb86iyqLzFWpeqJlKHt7euZbve7+MCszXfvYUfxVWLlAI DmIDXxhwc3g== X-Google-Smtp-Source: AGHT+IHLLmO2+2COBaCiPQUQ2aZiyF2aMmTHgGG9hkU78sySo5jkiz9ScIwnxSFaP9yWZPDCHZU1r3AFS2b0 X-Received: from plly18.prod.google.com ([2002:a17:902:7c92:b0:223:4c5f:3494]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a17:902:a613:b0:236:7333:f183 with SMTP id d9443c01a7336-2367333f74emr154142285ad.19.1750213700484; Tue, 17 Jun 2025 19:28:20 -0700 (PDT) Date: Wed, 18 Jun 2025 02:28:15 +0000 In-Reply-To: <20250618-debugfs-rust-v6-0-72cae211b133@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250618-debugfs-rust-v6-0-72cae211b133@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1750213694; l=4592; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=7DbO/U0Rj2wyZtj+j8Z2FBKA+3rtUseZ8Hbvsru8fEM=; b=+GncevBBFmPb+hkR23fxazLrX2DyWqwKtQRDSzgmSa/c74FiR0bg8QZWEX64eVGOhwQUQ3OPY V1Mh+9iicqMCrX5Snibeexc6j949EbZHPl4v85RZVgyuLJtw3gVPXi4 X-Mailer: b4 0.14.2 Message-ID: <20250618-debugfs-rust-v6-3-72cae211b133@google.com> Subject: [PATCH v6 3/5] rust: debugfs: Support arbitrary owned backing for File From: Matthew Maurer To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , "Rafael J. Wysocki" , Sami Tolvanen , Timur Tabi , Benno Lossin Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Matthew Maurer Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable This allows `File`s to be backed by `Deref` rather than just `&'static T`. This means that dynamically allocated objects can be attached to `File`s without needing to take extra steps to create a pinned reference that's guaranteed to live long enough. Signed-off-by: Matthew Maurer Tested-by: Dirk Behme --- rust/kernel/debugfs.rs | 51 ++++++++++++++++++++++++++++++++++++++--------= ---- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs index 6a89557d8cf49327d2984d15741ffb6640defd70..cd83f21cf2818f406575941ebbc= 6c426575643e4 100644 --- a/rust/kernel/debugfs.rs +++ b/rust/kernel/debugfs.rs @@ -5,12 +5,13 @@ //! //! C header: [`include/linux/debugfs.h`](srctree/include/linux/debugfs.h) =20 -#[cfg(CONFIG_DEBUG_FS)] +use crate::alloc::KBox; use crate::prelude::GFP_KERNEL; use crate::str::CStr; #[cfg(CONFIG_DEBUG_FS)] use crate::sync::Arc; use core::fmt::Display; +use core::ops::Deref; =20 #[cfg(CONFIG_DEBUG_FS)] mod display_file; @@ -61,40 +62,59 @@ fn create(_name: &CStr, _parent: Option<&Dir>) -> Self { } =20 #[cfg(CONFIG_DEBUG_FS)] - fn create_file(&self, name: &CStr, data: &'static = T) -> File { + fn create_file + 'static + Send + Sync, T: Disp= lay>( + &self, + name: &CStr, + data: D, + ) -> File { + let mut file =3D File { + _entry: entry::Entry::empty(), + _data: None, + }; + let Some(data) =3D KBox::new(data, GFP_KERNEL).ok() else { + return file; + }; + let Some(parent) =3D &self.0 else { - return File { - _entry: entry::Entry::empty(), - }; + return file; }; + // SAFETY: // * `name` is a NUL-terminated C string, living across the call, = by `CStr` invariant. // * `parent` is a live `dentry` since we have a reference to it. // * `vtable` is all stock `seq_file` implementations except for `= 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 we know because - // we have a static reference. + // we have an owning `D` in the `File`, and we tear down the fil= e during `Drop`. let ptr =3D unsafe { bindings::debugfs_create_file_full( name.as_char_ptr(), 0o444, parent.as_ptr(), - data as *const _ as *mut _, + data.deref() as *const _ as *mut _, core::ptr::null(), &::VTABLE, ) }; =20 + file._data =3D Some(data); + // SAFETY: `debugfs_create_file_full` either returns an error code= or a legal // dentry pointer, so `Entry::new` is safe to call here. - let entry =3D unsafe { entry::Entry::new(ptr, Some(parent.clone())= ) }; + file._entry =3D unsafe { entry::Entry::new(ptr, Some(parent.clone(= ))) }; =20 - File { _entry: entry } + file } =20 #[cfg(not(CONFIG_DEBUG_FS))] - fn create_file(&self, _name: &CStr, _data: &'stati= c T) -> File { - File {} + fn create_file + 'static + Send + Sync, T: Disp= lay>( + &self, + _name: &CStr, + data: D, + ) -> File { + File { + _data: KBox::new(data, GFP_KERNEL).ok().map(|x| x as _), + } } =20 /// Create a DebugFS subdirectory. @@ -125,7 +145,11 @@ pub fn subdir(&self, name: &CStr) -> Self { /// dir.display_file(c_str!("foo"), &200); /// // "my_debugfs_dir/foo" now contains the number 200. /// ``` - pub fn display_file(&self, name: &CStr, data: &'st= atic T) -> File { + pub fn display_file + 'static + Send + Sync, T:= Display>( + &self, + name: &CStr, + data: D, + ) -> File { self.create_file(name, data) } =20 @@ -147,4 +171,7 @@ pub fn new(name: &CStr) -> Self { pub struct File { #[cfg(CONFIG_DEBUG_FS)] _entry: entry::Entry, + // The data needs to be kept in a `Box` to prevent it from moving when= the file does, as + // this might invalidate the pointer that's been passed to debugfs. + _data: Option>, } --=20 2.50.0.rc2.696.g1fc2a0284f-goog From nobody Fri Oct 10 04:43:19 2025 Received: from mail-pj1-f73.google.com (mail-pj1-f73.google.com [209.85.216.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AA46F1CEACB for ; Wed, 18 Jun 2025 02:28:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750213705; cv=none; b=BI/iHvktxEC8id/M6iGVie3bYV7SNgE4CEz5t7NTut1v0ZkPqMKS7soNTwDdrpV0pv1ybTB43ig8wjKDLI3f5cEC5dRaBEkqtfIkm7ueOcmUAWOhNaUAvCXjTq86rNn/BVAifPrsdoKSoFlMv7ihiRnIVIIXmbIrvPuSQnegKmU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750213705; c=relaxed/simple; bh=oKl2z85IL+nZOqvaoXori7duEOA4yiwEy+q0plhD4uo=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=mc11fOmw7lfWTaFw238o1wjeUW3oHq+FRgdPSTp9hEWZBRJUHiFQDttbMzGJvEqJ/hArcqJKAZeX2wGEkY7NuEimZikZUmPeFwMlJxp02WxeW4x6wxE99JyJplSTSscRBtpPLFwwiAeW4Kqx60l8vxC70xzwFGTvxkwuRUfc+Cc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--mmaurer.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=2TpPziff; arc=none smtp.client-ip=209.85.216.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--mmaurer.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="2TpPziff" Received: by mail-pj1-f73.google.com with SMTP id 98e67ed59e1d1-31332dc2b59so5506398a91.0 for ; Tue, 17 Jun 2025 19:28:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1750213702; x=1750818502; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Ji5LW4YxsgtGLzHKx3kLMSLSenw4FylgJNlVG7ZKEt0=; b=2TpPziffMtiOew/A55p7sPKNV1vEOYUxzHOjxRCWLk2Qs5yCWkzBPOIpAkBfrit2yR mXKuhCx9NVgrGPwaaZUL7RkaSv7ivmLFBtLzdaa0jW267hX8cCndsOBbbhw9F+bAD5uf jyNKo69te2PbepWyn6m+2qAwDANq1ApR4/j2Fp/yL90PCm8hySJGEgOI/add9McRF1Z1 2dc35t4BYFvub1G88dDW2Yfgwix3m6Gf9iAeFfxPJAJ5wH/epPSji00FTdlCiJRX/whc XeDmdujUgrh9aTrC8rKD3x2l3yF9K7WJN/ccT5PCycuY5DZe2EyUgYLpAXU9Egy5zz5y mBUg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750213702; x=1750818502; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Ji5LW4YxsgtGLzHKx3kLMSLSenw4FylgJNlVG7ZKEt0=; b=HWUwwauLpAG2bbsULmS7INQFp/8Ni5uE1AjG3+WYDaorLRBBz/DQMFtGIAx9z5OZTu A8gVA9ZCLQLLxUP8r+1Z1nOkrnoDgOslLNRUfiJvSFCp6ZlPU/ZPUOy59VzZCtrs26mr zXIGUzZAia/exugLyCEQx5MGVc8ttQdvxrXx3yFvw7iyIalYcW/WGaCFkGlD29ulmSPn vDcZCsU7Unw+MRzX9e//QDmK63dszGbhIKSt3s6Ne8narQ5cnWeW906fPfUZU7pJ0vrV aISHtpJP5B4DxYeyG2/A71mtowSZ8aofV1rNsETfiHOj4QLtoWGOm+CmDWlrrS1U2Wni 0qYA== X-Gm-Message-State: AOJu0YxK1AjtdG3nR1yRdtrF0FgXbBF4gN/2tMLCGF/lRHLto8QZvFhD uHEZs4pHgi4A6lMRClX5zJ4Pa/OgKyAHO7Hmf4DS92CwNJbvY+M4nv/LamrFAQUIVjoAKExZHdA 2O3YRBcGViw== X-Google-Smtp-Source: AGHT+IHAQpDx0027kALvg1jSe/gar3r1FYw4JgvW4VzKKZpfVlGqi7+hrvadAqyyRO0ld8W2IiZRCSTaLsiI X-Received: from pjbtc8.prod.google.com ([2002:a17:90b:5408:b0:2fb:fa85:1678]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:5847:b0:312:1b53:5ea8 with SMTP id 98e67ed59e1d1-313f1ce2aa1mr23177238a91.24.1750213702122; Tue, 17 Jun 2025 19:28:22 -0700 (PDT) Date: Wed, 18 Jun 2025 02:28:16 +0000 In-Reply-To: <20250618-debugfs-rust-v6-0-72cae211b133@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250618-debugfs-rust-v6-0-72cae211b133@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1750213694; l=4975; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=oKl2z85IL+nZOqvaoXori7duEOA4yiwEy+q0plhD4uo=; b=JfyQqUjWgCEhfJVm3XmVU9Mcp3CRaqdG8HbDyyicg5nbnzGjcEPq+0U/NFwsTKdij84wHXHye CL4qseLURoXBz69rJh20wKqp7a/H3zhKEQ6peB4FLh/bIGzwBVfAS90 X-Mailer: b4 0.14.2 Message-ID: <20250618-debugfs-rust-v6-4-72cae211b133@google.com> Subject: [PATCH v6 4/5] rust: debugfs: Support format hooks From: Matthew Maurer To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , "Rafael J. Wysocki" , Sami Tolvanen , Timur Tabi , Benno Lossin Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Matthew Maurer Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Rather than always using Display, allow hooking arbitrary functions to arbitrary files. Display technically has the expressiveness to do this, but requires a new type be declared for every different way to render things, which can be very clumsy. Signed-off-by: Matthew Maurer Tested-by: Dirk Behme --- rust/kernel/debugfs.rs | 41 ++++++++++++++++++++++++++ rust/kernel/debugfs/display_file.rs | 57 +++++++++++++++++++++++++++++++++= +++- 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs index cd83f21cf2818f406575941ebbc6c426575643e4..78544653b46d959c1520b121017= 0d22a0e973989 100644 --- a/rust/kernel/debugfs.rs +++ b/rust/kernel/debugfs.rs @@ -10,6 +10,7 @@ use crate::str::CStr; #[cfg(CONFIG_DEBUG_FS)] use crate::sync::Arc; +use core::fmt; use core::fmt::Display; use core::ops::Deref; =20 @@ -153,6 +154,46 @@ pub fn display_file + 'static += Send + Sync, T: Display>( self.create_file(name, data) } =20 + /// Create a file in a DebugFS directory with the provided name, and c= ontents from invoking `f` + /// on the provided reference. + /// + /// `f` must be a function item or a non-capturing closure, or this wi= ll fail to compile. + /// + /// # Examples + /// + /// ``` + /// # use core::sync::atomic::{AtomicU32, Ordering}; + /// # use kernel::c_str; + /// # use kernel::debugfs::Dir; + /// let dir =3D Dir::new(c_str!("foo")); + /// static MY_ATOMIC: AtomicU32 =3D AtomicU32::new(3); + /// let file =3D dir.fmt_file(c_str!("bar"), &MY_ATOMIC, &|val, f| { + /// let out =3D val.load(Ordering::Relaxed); + /// writeln!(f, "{out:#010x}") + /// }); + /// MY_ATOMIC.store(10, Ordering::Relaxed); + /// ``` + pub fn fmt_file< + D: Deref + 'static + Send + Sync, + T: Sync, + F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync, + >( + &self, + name: &CStr, + data: D, + f: &'static F, + ) -> File { + #[cfg(CONFIG_DEBUG_FS)] + let data_adapted =3D display_file::FormatAdapter::new(data, f); + #[cfg(not(CONFIG_DEBUG_FS))] + let data_adapted =3D { + // Mark used + let (_, _) =3D (data, f); + &0 + }; + self.display_file(name, data_adapted) + } + /// Create a new directory in DebugFS at the root. /// /// # Examples diff --git a/rust/kernel/debugfs/display_file.rs b/rust/kernel/debugfs/disp= lay_file.rs index 65e37e34d7b587482492dc296637a3bc517b9fe3..75f2e1c560840324117bc1bc73a= a89e3823aaf0e 100644 --- a/rust/kernel/debugfs/display_file.rs +++ b/rust/kernel/debugfs/display_file.rs @@ -3,7 +3,9 @@ =20 use crate::seq_file::SeqFile; use crate::seq_print; -use core::fmt::Display; +use core::fmt::{Display, Formatter, Result}; +use core::marker::PhantomData; +use core::ops::Deref; =20 /// Implements `open` for `file_operations` via `single_open` to fill out = a `seq_file`. /// @@ -58,3 +60,56 @@ pub(crate) trait DisplayFile: Display + Sized { } =20 impl DisplayFile for T {} + +/// Adapter to implement `Display` via a callback with the same representa= tion as `T`. +/// +/// # Invariants +/// +/// If an instance for `FormatAdapter<_, F>` is constructed, `F` is inhabi= ted. +#[repr(transparent)] +pub(crate) struct FormatAdapter { + inner: D, + _formatter: PhantomData, +} + +impl FormatAdapter { + pub(crate) fn new(inner: D, _f: &'static F) -> Self { + // INVARIANT: We were passed a reference to F, so it is inhabited. + FormatAdapter { + inner, + _formatter: PhantomData, + } + } +} + +impl, T, F> Display for FormatAdapter +where + F: Fn(&T, &mut Formatter<'_>) -> Result + 'static, +{ + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + // SAFETY: FormatAdapter<_, F> can only be constructed if F is inh= abited + let f: &F =3D unsafe { materialize_zst_fmt() }; + f(&self.inner, fmt) + } +} + +impl Deref for FormatAdapter { + type Target =3D Self; + fn deref(&self) -> &Self { + self + } +} + +/// For types with a unique value, produce a static reference to it. +/// +/// # Safety +/// +/// The caller asserts that F is inhabited +unsafe fn materialize_zst_fmt() -> &'static F { + const { assert!(core::mem::size_of::() =3D=3D 0) }; + let zst_dangle: core::ptr::NonNull =3D core::ptr::NonNull::dangling= (); + // SAFETY: While the pointer is dangling, it is a dangling pointer to = a ZST, based on the + // assertion above. The type is also inhabited, by the caller's assert= ion. This means + // we can materialize it. + unsafe { zst_dangle.as_ref() } +} --=20 2.50.0.rc2.696.g1fc2a0284f-goog From nobody Fri Oct 10 04:43:19 2025 Received: from mail-pj1-f74.google.com (mail-pj1-f74.google.com [209.85.216.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3F0641A238C for ; Wed, 18 Jun 2025 02:28:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750213707; cv=none; b=oLQ6DS4dm6vh4Ol7NWevu0TEVsuTM9xn5qZrMK44VcGdeI14oAWt6IgkpnyIUuAimL4TaKyRMEdUQz2qNn0/s7wcc7k38ALpgfnRROsQZxa6HoUVT13bZDTkpabsmWZoKf6D9hn3ti3sNorF47AXiAdGIcuNpvgqlxXfyXw1nLc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750213707; c=relaxed/simple; bh=HZtfPBC4n0E27+dbcVZVZ57XQoajwE/SYNBOe9gGdCY=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=E021GsBCsho/Qk+oA7IdtKN1jTbwM51ooSzZQ1Ccbgb//oTzRtU6xC43WWbJKBV4R9JO+PyGMhrUTNtRZpn/9E5P633ESm0Ru2PMjSY8faBWkeL8yvLdxqur+hOcuveGoMDqs67aHC8dfU20yhJaL9RBO931HlIck5p3lQAZPRo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--mmaurer.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=geIOGMQX; arc=none smtp.client-ip=209.85.216.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--mmaurer.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="geIOGMQX" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-311e7337f26so4840307a91.3 for ; Tue, 17 Jun 2025 19:28:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1750213703; x=1750818503; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=K6Q8Nw6mlyBHF9qBVYKCPAg9oNZqZ6LaJAuX3s9e570=; b=geIOGMQXdjkim59obsi87FbgMjlBTnwcvbWhjSyRAnYBZ6b5wlmx5KZDFddHxJruJB BQosrHrOCENHXLbs7V4j67kuh1L4YJNAALQVtb+p5wZ0I+gy/jfoIxmJAjf9/iBlEiK6 0WnutmZDWlTdt3bdp/vOup7sXDdJWzBHCWkAEhwRVXcTAG2sUeTa7S7FL0girZoUThGY FKBZw+rglk2H9umEVOVlwLCO2OE3PR9jlCdmrB7sbrZuFODpCydd9+hY3+cSyyajkmqb s7ZKSkLCi7cwKWHlpWFkpm60J6CZnvv0aurm6GZeORI0pOIEVJ+W1JAbhhYJ90TGcJyz meLw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750213703; x=1750818503; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=K6Q8Nw6mlyBHF9qBVYKCPAg9oNZqZ6LaJAuX3s9e570=; b=rp4BwB7c+7ohzJ91DqOjrXpPhfBkgCG0UtS8RitQrZWHZwbTkj7lJRRFBPjx94U8fX 8Jr7xO473syt81CbWSFc2sA94E/pVpqSLngahSM57BM40K7IXA1WoixwGBuqu20mtNp4 AFrKso3rgAwjAOzjbUXFMatXU+YXhkD1eOMS0ubtgqbRJU2Xh7+iY6+l3WmeuzF4N28m nWY5l9mJnwgEjS3y7TuLOPHQIzU6D2YnHu2l1QM+sQnzVGBjiy8GItGDGgoYLOMHb3FZ 1amT5oBmNgbzz/GPpt6pPDl3l/Dfo11Bnr6JuzFpTQ6C30Mmxs7AdFFR4+YYKknGPueh 1dzw== X-Gm-Message-State: AOJu0Ywu09LZa5EEIIOjW1in2ZxrftlRVrBxuKQAtyTFpiLLmtd1EwRt d3+UyhwEwVK/ysvmBqNDLxZxSbh5jrJVooNGYjJdhYalgB15pr0d8B6UMV9PXFsorOQiqC+x3P5 j7rFqMrGCpQ== X-Google-Smtp-Source: AGHT+IGMrocnmpZoKTP5CgUfolQB0XBI3kC+HyC17pQsyW5dUa36ShR993kUy5qzOR4MNM5qDUQa7TyU0WUa X-Received: from pjboh15.prod.google.com ([2002:a17:90b:3a4f:b0:311:485b:d057]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:52c7:b0:311:eb85:96df with SMTP id 98e67ed59e1d1-313f1ca8c36mr26423642a91.17.1750213703508; Tue, 17 Jun 2025 19:28:23 -0700 (PDT) Date: Wed, 18 Jun 2025 02:28:17 +0000 In-Reply-To: <20250618-debugfs-rust-v6-0-72cae211b133@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250618-debugfs-rust-v6-0-72cae211b133@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1750213694; l=5582; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=HZtfPBC4n0E27+dbcVZVZ57XQoajwE/SYNBOe9gGdCY=; b=vOdS7amzhEE+WxWKkSQfdNvmMAOmuUiZelrtNUFxVti2MQ21qFT2joyBN0EsX5s6IgB7FY/K+ xuMaZYoWqCsBr0b42v/0XQF3VfN2PB6cp2Q6kFbb0f9vqLgR9UVhgl5 X-Mailer: b4 0.14.2 Message-ID: <20250618-debugfs-rust-v6-5-72cae211b133@google.com> Subject: [PATCH v6 5/5] rust: samples: Add debugfs sample From: Matthew Maurer To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , "Rafael J. Wysocki" , Sami Tolvanen , Timur Tabi , Benno Lossin Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Matthew Maurer Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Provides an example of using the Rust DebugFS bindings. Signed-off-by: Matthew Maurer Tested-by: Dirk Behme --- MAINTAINERS | 1 + samples/rust/Kconfig | 11 +++++++ samples/rust/Makefile | 1 + samples/rust/rust_debugfs.rs | 76 ++++++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 89 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index fda422023e30fe8821e4cd79b503693ecc6eb48c..7faa088cc08514acfec371b043d= 324bb52d2e7ba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7382,6 +7382,7 @@ F: rust/kernel/devres.rs F: rust/kernel/driver.rs F: rust/kernel/faux.rs F: rust/kernel/platform.rs +F: samples/rust/rust_debugfs.rs F: samples/rust/rust_driver_platform.rs F: samples/rust/rust_driver_faux.rs =20 diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index 7f7371a004ee0a8f67dca99c836596709a70c4fa..8eb2088b66b0646c7a1fba9cdcc= c9a6bb328defb 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -62,6 +62,17 @@ config SAMPLE_RUST_DMA =20 If unsure, say N. =20 +config SAMPLE_RUST_DEBUGFS + tristate "DebugFS Test Driver" + depends on DEBUG_FS + help + This option builds the Rust DebugFS Test driver sample. + + To compile this as a module, choose M here: + the module will be called rust_debugfs. + + If unsure, say N. + config SAMPLE_RUST_DRIVER_PCI tristate "PCI Driver" depends on PCI diff --git a/samples/rust/Makefile b/samples/rust/Makefile index bd2faad63b4f3befe7d1ed5139fe25c7a8b6d7f6..61276222a99f8cc6d7f84c26d05= 33b30815ebadd 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -4,6 +4,7 @@ ccflags-y +=3D -I$(src) # needed for trace events obj-$(CONFIG_SAMPLE_RUST_MINIMAL) +=3D rust_minimal.o obj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE) +=3D rust_misc_device.o obj-$(CONFIG_SAMPLE_RUST_PRINT) +=3D rust_print.o +obj-$(CONFIG_SAMPLE_RUST_DEBUGFS) +=3D rust_debugfs.o obj-$(CONFIG_SAMPLE_RUST_DMA) +=3D rust_dma.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) +=3D rust_driver_pci.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) +=3D rust_driver_platform.o diff --git a/samples/rust/rust_debugfs.rs b/samples/rust/rust_debugfs.rs new file mode 100644 index 0000000000000000000000000000000000000000..6af9177c711a07764f0323e03a7= 2d115287f1205 --- /dev/null +++ b/samples/rust/rust_debugfs.rs @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2025 Google LLC. + +//! Sample DebugFS exporting module + +use core::sync::atomic::{AtomicU32, Ordering}; +use kernel::c_str; +use kernel::debugfs::{Dir, File}; +use kernel::prelude::*; +use kernel::sync::{new_mutex, Arc}; + +module! { + type: RustDebugFs, + name: "rust_debugfs", + authors: ["Matthew Maurer"], + description: "Rust DebugFS usage sample", + license: "GPL", +} + +struct RustDebugFs { + // As we only hold these for drop effect (to remove the directory/file= s) we have a leading + // underscore to indicate to the compiler that we don't expect to use = this field directly. + _debugfs: Dir, + _subdir: Dir, + _file: File, + _file_2: File, +} + +static EXAMPLE: AtomicU32 =3D AtomicU32::new(8); + +impl kernel::Module for RustDebugFs { + fn init(_this: &'static ThisModule) -> Result { + // Create a debugfs directory in the root of the filesystem called= "sample_debugfs". + let debugfs =3D Dir::new(c_str!("sample_debugfs")); + + // Create a subdirectory, so "sample_debugfs/subdir" now exists. + let sub =3D debugfs.subdir(c_str!("subdir")); + + // Create a single file in the subdirectory called "example" that = will read from the + // `EXAMPLE` atomic variable. + // We `forget` the result to avoid deleting the file at the end of= the scope. + let file =3D sub.fmt_file(c_str!("example"), &EXAMPLE, &|example, = f| { + writeln!(f, "Reading atomic: {}", example.load(Ordering::Relax= ed)) + }); + // Now, "sample_debugfs/subdir/example" will print "Reading atomic= : 8\n" when read. + + // Change the value in the variable displayed by the file. This is= intended to demonstrate + // that the module can continue to change the value used by the fi= le. + EXAMPLE.store(10, Ordering::Relaxed); + // Now, "sample_debugfs/subdir/example" will print "Reading atomic= : 10\n" when read. + + // In addition to globals, we can also attach any kind of owned da= ta. Most commonly, this + // will look like an `Arc` as those can be shared with t= he rest of the module. + let my_arc =3D Arc::pin_init(new_mutex!(10), GFP_KERNEL)?; + // An `Arc>` doesn't implement display, so let's give= explicit instructions on + // how to print it + let file_2 =3D sub.fmt_file(c_str!("arc_backed"), my_arc.clone(), = &|val, f| { + writeln!(f, "locked value: {:#010x}", *val.lock()) + }); + + // Since it's an `Arc` and we cloned it, we continue to have acces= s to `my_arc`. If this + // were real, we'd probably stash it in our module struct and do s= omething with it when + // handling real calls. + *my_arc.lock() =3D 99; + + // Save the handles we want to preserve to our module object. They= will be automatically + // cleaned up when our module is unloaded. + Ok(Self { + _debugfs: debugfs, + _subdir: sub, + _file: file, + _file_2: file_2, + }) + } +} --=20 2.50.0.rc2.696.g1fc2a0284f-goog