From nobody Wed Oct 8 12:50:08 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 77C9327E042 for ; Fri, 27 Jun 2025 23:18:55 +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=1751066337; cv=none; b=jhTQ5o7zD3psigqK9/vwpL0kVRg5fibuArdMvnnIL5BAiaCYzPbmhhapo/zqC6D3F9Elr6riGHS9RSR5cV3d2VmKLf+XONQVna5yNZ10HF3+frRyOkcTvE7lw9qT8MjvNz18gebfva/HKdyvtqDq9PebKkG0KN6dfXFRsF4o+Hk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751066337; c=relaxed/simple; bh=mx4vr3xsZbWWyuXLJVx0noMJ6L9O5fA3UXhOWAagXpk=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Du0NL1o9JixsgRoVHyePZaal294K8hPZGR744NWtIBW3n1DpN7n18YO1APPWCl65wUC3wWhNugCq87GVNT0H3LTLqfPN/4ftagh5ilLp3L7/WzYiqayBVXL7OKo7XzIa8wZWMo5U+WrBh2qeEB/ROa3oAROatF5uWq41MCTXeUM= 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=f4fOBTeh; 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="f4fOBTeh" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-3138e671316so1986947a91.0 for ; Fri, 27 Jun 2025 16:18:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1751066335; x=1751671135; 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=WWBYj6LXkY+sbts5Ks2w5a5WSEUUf8P7kitBMqSoq8s=; b=f4fOBTehQcjgvsYoP4YOW3HLcAkLgvjWPSxLoA/C1gOM87hlsKur57zrWof3f0fUOC 2F9zXJqjgeR4pudfoODqoPlhhypZD51ZXAyIHzhOB0DhPUnzIN5o4zrfxPnHjbRKuWqY bup5PO8L7k6Lo6Pi2S6nN9/xMifXIES8zFUwftsDfdPdUnnu9/3JgSLUab7qJj+/gDJl NRxLaEgmvC4p6oYRLDzq4A/8v6pZZQvSBAshQy9SP02hvnFrroiDB7sVv1jHJd1oicfH UfICIlo+U0Gb76JmhaYblOEVXan81NVBIy/xrk+pDsrIxIl6U/BdLawZuUPJvFdtbo0W syVQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751066335; x=1751671135; 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=WWBYj6LXkY+sbts5Ks2w5a5WSEUUf8P7kitBMqSoq8s=; b=ejuzQhqdxLNaP+kyHFqulEB+xazSCkF1lcNBBjmMaEDyuP87hErcZD2XRX3HjbfX0D Wl6CwhbzIXH1ixa9RpoLV8XD3gs82Kw5VtHmpIw+2iHHIir7q2mQjgbqMEhTQqXJ9Hhu bWMUd0zyn/mxNvPnxOD0WJq+80L0lOp5gmLyESgdpTos7qgjHCxALt1iY1HMfPYpxfPn VuB1m5Zr/WPQOYE8P0MeVBTsCdDNDi00MMZpr8mQbIwPQ3cyD3FP27Qw6WbEs2Eqejcj wkbEys84/6dEFl3HPFtGDXk69jSeNgdGkbnCoSVtXABE3m+JCo1UEZUwTdfc1mKMi0Md ffhQ== X-Gm-Message-State: AOJu0YzM0DHzYwuhF5qCNHa0++OPgrLBAZwhHingrnWRjeKS3wEVtrpV vM0V72cE3PzgLQh2WO27Fro9q2tuk30UrbvZZ9DhgkGYoewmSiYYOsyT6GoZpD3/5NlRpt6PfLE w0pko4k6a+Q== X-Google-Smtp-Source: AGHT+IEe1az+j0BJXbQANCoSwc6QB/5j1Ts9K/N3RuCTtXStYM1usQmOlj0d9CIZAE6dN7r+2lNiH9l44ryE X-Received: from pjboi12.prod.google.com ([2002:a17:90b:3a0c:b0:2fc:c98:ea47]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:2b50:b0:30a:9feb:1e15 with SMTP id 98e67ed59e1d1-316d69bfbeamr14320669a91.8.1751066334800; Fri, 27 Jun 2025 16:18:54 -0700 (PDT) Date: Fri, 27 Jun 2025 23:18:24 +0000 In-Reply-To: <20250627-debugfs-rust-v8-0-c6526e413d40@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250627-debugfs-rust-v8-0-c6526e413d40@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1751066331; l=7586; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=mx4vr3xsZbWWyuXLJVx0noMJ6L9O5fA3UXhOWAagXpk=; b=MK0NlxO5/VM5swSL4YtqA9y4Myu5Wd/27eOOQVe0dtvj0dy99mxjanydEc4zoHM33y+G+jdNV 3bMaqfdNoJ+CLOvwYs7Nd0ZXUjaXrhGoZIE8gDzZukb1sC+Iil8N94p X-Mailer: b4 0.14.2 Message-ID: <20250627-debugfs-rust-v8-1-c6526e413d40@google.com> Subject: [PATCH v8 1/6] 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 , Dirk Behme 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. Tested-by: Dirk Behme Signed-off-by: Matthew Maurer Reviewed-by: Alice Ryhl --- MAINTAINERS | 2 + rust/bindings/bindings_helper.h | 1 + rust/kernel/debugfs.rs | 90 +++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/debugfs/entry.rs | 58 ++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 5 files changed, 152 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index c3f7fbd0d67afe8376ea84bc17d70e9fa4b89bf6..551e3a6a16d9051a2d58a77614c= 458287da2bdcc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7366,6 +7366,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_id.rs F: rust/kernel/devres.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..2359bd11cd664fb9f7206f8fe38= f758dc43d2cb8 --- /dev/null +++ b/rust/kernel/debugfs.rs @@ -0,0 +1,90 @@ +// 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; +#[cfg(CONFIG_DEBUG_FS)] +use entry::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::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.727.gbf7dc18ff4-goog From nobody Wed Oct 8 12:50:08 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 1DBAC290BC8 for ; Fri, 27 Jun 2025 23:18:56 +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=1751066338; cv=none; b=WnDcKmd1rQBaq30GH7Dxu5+OKXEglb/6SNSIWnZuDt+Bzx+7d/QKiXoXTwM7VEhIxli/3sPV79cQfxaUGbo4Xt8deNvAwcHDUcJWLbi4TBbBSbgB2c29ERCjb4mtLHqf4al2y3goXaMZNRBXH/yo3YM52EJfErzo7VJnF4Za0X8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751066338; c=relaxed/simple; bh=zv535cnLSXE0Li+uSUzrbqjrIvtvJoeKTwTy0R3Hldw=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=XBF2QsWQJ7oa+hNsq3qovULpgzMHiDszo12T2F4sPEOHESpmQl/omKbsQL93kahxHUgK6jl8SPDSOB2gUo26adlE2DLBoVpt42b7rfCgnvGSI2Sli9lCMiK2waHY4WtQAPLfXNTdWlZEYKV+JHQB2vX2hSQ4jxk4rigJNi7KcaE= 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=IE6kMU7z; 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="IE6kMU7z" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-3132c8437ffso102359a91.1 for ; Fri, 27 Jun 2025 16:18:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1751066336; x=1751671136; 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=XE7RB2hkCNj3GvqoAw6x/sKXqs8W0tVM52Lnf/rgWxo=; b=IE6kMU7z9VzMtZeWsXyFcqPKee4f2G8M+UoIwnbluugD6HfaesyqWSx3venFHJJQbh L5+G/Vn+3OLGMLWycySLPYYLZsrCIAHRPC/J1S/Kma/gFQAh4aSfjxBERTC4NbtF2l/8 +KnsYnSVXO9SyeG/j/zx+uReKEXyB4P5tQOO1+UpaXc0XrUg4PGf7Vb3Xv2OofkWhS40 nld+HSAVF/Ifv2bI0d03ZNPZZiYukLZOQVqkW3AsfkmNcc3r/sAy+VPqnH9+WJRsPy23 NQCyTABjibMvcOtx55GnaWsvxdqPyvHCHEJAhgrncdzRZJF1kTietk2nyR9YGrXfT4EX da8g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751066336; x=1751671136; 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=XE7RB2hkCNj3GvqoAw6x/sKXqs8W0tVM52Lnf/rgWxo=; b=l4nA/RMRfN4YFVowaAvBnljg6kLPJetVSEsDG1S26g5xsZJzRh1gsAQC025HW1KgHP HOwVSJMAqaPunZWDHncrwyWgs7ZpKgJ2bHaEMfRI7U45kfKsHYQBc9o7YuQz9tb2AJog 1n+Yxr+gO4seq1zqW+lpJv/cdV+IY8guABm0TpjqnPxIXB2FGr9Jd1RKmBIbBqlZ4dR4 QiMN1+NbF5lyTvMRQ+iZc67W8E+tVvma14xRmMYUtzMCf5YnDbXSDqiDxLrO3BQ0MsZX ila0PSbXzI4m/Iw4QMPrNlikuYLxO+7isYxJGBAON/AnIksUrRX/lNfM5a68F8Hx1nDf Yadg== X-Gm-Message-State: AOJu0Ywq32nTDLJGJF+VW4/lao6Ecy1L23xVTXMeJX9eulrs7Q8/AOx5 mg6Qxu55pJEiM1DbWFou+G5J9pYcRo3SRxC7DlGLvMWg5Rgrikiq6OfU7wlmNLkRUWSMEJPJy0D ddUNH4TTtrw== X-Google-Smtp-Source: AGHT+IF0ec7xiOhmnUZPpXxN0NzlLxCuAAWInLMq2oD00r3d/wF0waFCR+xNOm0giOGZ0ePdPkw6rSKh/4I8 X-Received: from pjbsl7.prod.google.com ([2002:a17:90b:2e07:b0:314:626:7b97]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90a:d603:b0:313:d79e:1f48 with SMTP id 98e67ed59e1d1-318c9243d32mr8295824a91.16.1751066336366; Fri, 27 Jun 2025 16:18:56 -0700 (PDT) Date: Fri, 27 Jun 2025 23:18:25 +0000 In-Reply-To: <20250627-debugfs-rust-v8-0-c6526e413d40@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250627-debugfs-rust-v8-0-c6526e413d40@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1751066331; l=7283; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=zv535cnLSXE0Li+uSUzrbqjrIvtvJoeKTwTy0R3Hldw=; b=HvJjD2UsDIdg39pm6QodxSwqYTnBxsNbWc4Syy7wT/pAesbHeXb5IqFZROPSV+FVvVWtGLpA4 feGOo3h5XxYA2oXw3LckbtrIW/9P2hlxotCVvxS1TNCOidkftnAc73j X-Mailer: b4 0.14.2 Message-ID: <20250627-debugfs-rust-v8-2-c6526e413d40@google.com> Subject: [PATCH v8 2/6] 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 , Dirk Behme 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 `Display` implementation is used because `seq_printf` needs to route through `%pA`, which in turn routes through Arguments. Tested-by: Dirk Behme Signed-off-by: Matthew Maurer Reviewed-by: Alice Ryhl --- rust/kernel/debugfs.rs | 62 +++++++++++++++++++++++++++++++++= ++++ rust/kernel/debugfs/display_file.rs | 61 +++++++++++++++++++++++++++++++++= +++ rust/kernel/debugfs/entry.rs | 8 +++++ 3 files changed, 131 insertions(+) diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs index 2359bd11cd664fb9f7206f8fe38f758dc43d2cb8..1f20d85da56fcb89476552feefc= 9d97fab43cc04 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; #[cfg(CONFIG_DEBUG_FS)] @@ -59,6 +62,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::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::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. @@ -75,6 +115,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 @@ -88,3 +144,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, +} diff --git a/rust/kernel/debugfs/display_file.rs b/rust/kernel/debugfs/disp= lay_file.rs new file mode 100644 index 0000000000000000000000000000000000000000..e4b551f7092884ad12e18a32cc2= 43d0d037931a6 --- /dev/null +++ b/rust/kernel/debugfs/display_file.rs @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2025 Google LLC. + +use crate::prelude::*; +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, +) -> c_int { + // 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 c_void, +) -> c_int { + // 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::), + // 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.727.gbf7dc18ff4-goog From nobody Wed Oct 8 12:50:08 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 A65BE291C0E for ; Fri, 27 Jun 2025 23:18:58 +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=1751066340; cv=none; b=TlvEXGJxIIHyQ8HzLIaN7q78H/sXJ8ogxh56unr2cmxMuxUOKfJS2dzpzaA45U/O2HrHnRx/n4gzAlj6OY10Ru6xKY0NHHbPP9mlZHKKbMrUkXXtKrNnifvHnVcLP55ZnHpisqdLHzKpQoN/e5TrhQR3RQc/kvQgAsodhVzCs0E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751066340; c=relaxed/simple; bh=83dRpM/03A3Py3qB3VHZ341i3AVbxRvhjg0jVkwG3aE=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=G/hKgTZOygkLma8r9OWpejq8s/DmSnEVX/Yd3ppFrWck0o4MX759UPY6XxqPJ9QJHGUyyVWoeg/rj6b7vCadBrRelGggMcXN6W5aEd+BuFe84h0Q5XENGpoBC2hGWrxEEnA1PdJ2+uKGFR+HsV1tWCXQfHbBWXi/mMm7o3/ABfo= 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=h6TCPgJ/; 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="h6TCPgJ/" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-313f702d37fso78937a91.3 for ; Fri, 27 Jun 2025 16:18:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1751066338; x=1751671138; 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=IC9J+HBqr3YpeYxryDec+V8fkN6Ro/VZxjRt8PTyhaY=; b=h6TCPgJ/5yK+S7KXqdhH2p60ZkzuUZw3k729lLDTTbC4OpmJ68deuiAeN/aFRlHsyg a7NHBnjGDpzhrgukKsc0jbI3A/jyXyh5GgxIJGVZqw+Pyt4V6PCbn8krVEp+WXO3wgit n6BSd/sXzpYgKcr2YKPV6rQb4CQkDHV3iz4niyUl7um1JETQb3PoDcfrzaVq1D9M6zNP +AC4PlMfPbSdq1t8ydIcy6CeS01b9uBjaUuObB2OT2gU39mBWicS+5MeX5hnESCnuOzF D7sKjuuMKUVFOSEa1T/d2f0mSJBJp+7bNfrQlg+cPOB8PEf/pvyAdkWZJWigVH+rl1te J92w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751066338; x=1751671138; 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=IC9J+HBqr3YpeYxryDec+V8fkN6Ro/VZxjRt8PTyhaY=; b=thncusGbsWZB9ayxlx3i1pgtUpmzDi0gE0TapoUBYJEC1Xe8t0W1xeXXlaJ/k6PPTk gOLgutAh1BnQVe+uk8Iep84FT81U7f04bocu1z1mMqA8tyYIqE002yxLsb7aV2A/lc9d ZKtC54I+IHAR/PD9mjpPHzVTtxlSkqrTunG6MiVRIJRd6fy08+AGhUX1KHBygpcvze07 PQFW14nv5wrm0nXczHYguAi/MFYuTLzZL3E+znDqxDk2MACU2XIHQbdTHMFXSt23RxvL KwTkS4Cax2HYVEKNPAuhkh3e/hz+iEtugC22MCNRMnQ845riXSSGeJLCO1RVF639OOAY BG6g== X-Gm-Message-State: AOJu0YzJpViBBu75NPVWLuKZ4FL8dfsDLmBsQlm4z+v80A6rkcBs6xO7 0ZQazgyV7ssPGf7DKRYzu/iDN6iytsqz74dtx4DSd3tj6ZsyXxn/r/QWGw22Cb1lbab5xwo3amo 8yt50zPXFnQ== X-Google-Smtp-Source: AGHT+IHX2FXBjpY4uMqprP0OC45FxV2aElOLRN7uPFvb94ssMTGraXlGlYHjUyqIVNaqlTMiRKov5mRWmpai X-Received: from pjbrs7.prod.google.com ([2002:a17:90b:2b87:b0:315:b7f8:7ff]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:268d:b0:312:e91c:e340 with SMTP id 98e67ed59e1d1-318c9283adfmr6910414a91.35.1751066337995; Fri, 27 Jun 2025 16:18:57 -0700 (PDT) Date: Fri, 27 Jun 2025 23:18:26 +0000 In-Reply-To: <20250627-debugfs-rust-v8-0-c6526e413d40@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250627-debugfs-rust-v8-0-c6526e413d40@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1751066331; l=3205; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=83dRpM/03A3Py3qB3VHZ341i3AVbxRvhjg0jVkwG3aE=; b=7sgs14fHMCWS+uz+aLGE2uGJw2KLBaqu3JfVhUdboDuXGvlalcBdHCrmN0Ng30+m+8a7Y+A2i rloM3MoYtRtC5HfWswe+Zp5QhKcMTot2Hs/npNrAdW2ig0ygr/Zd+IZ X-Mailer: b4 0.14.2 Message-ID: <20250627-debugfs-rust-v8-3-c6526e413d40@google.com> Subject: [PATCH v8 3/6] rust: types: Support &'static and &'static mut ForeignOwnable 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 , Dirk Behme Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable These types live forever and do not require cleanup, so they can serve as `ForeignOwnable`. Tested-by: Dirk Behme Signed-off-by: Matthew Maurer Reviewed-by: Alice Ryhl --- rust/kernel/types.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 58 insertions(+) diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index 22985b6f69820d6df8ff3aae0bf815fad36a9d92..0a2b15cd05f91c69ef9c678555b= 845a23c19b82c 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -142,6 +142,64 @@ unsafe fn borrow<'a>(_: *mut Self::PointedTo) -> Self:= :Borrowed<'a> {} unsafe fn borrow_mut<'a>(_: *mut Self::PointedTo) -> Self::BorrowedMut= <'a> {} } =20 +// SAFETY: The `into_foreign` function derives its pointer from a referenc= e, so it is correctly +// aligned. +unsafe impl ForeignOwnable for &'static T { + type PointedTo =3D T; + type Borrowed<'a> =3D &'a T; + type BorrowedMut<'a> =3D &'a T; + + fn into_foreign(self) -> *mut Self::PointedTo { + core::ptr::from_ref(self).cast_mut() + } + + unsafe fn from_foreign(foreign: *mut Self::PointedTo) -> Self { + // SAFETY: from_foreign has stricter restrictions than borrow + unsafe { Self::borrow(foreign) } + } + + unsafe fn borrow<'a>(foreign: *mut Self::PointedTo) -> Self::Borrowed<= 'a> { + // SAFETY: We know the original reference lived forever, so we can= convert it back + unsafe { &*foreign } + } + + unsafe fn borrow_mut<'a>(foreign: *mut Self::PointedTo) -> Self::Borro= wedMut<'a> { + // SAFETY: borrow_mut has stricter restrictions than borrow + unsafe { Self::borrow(foreign) } + } +} + +// SAFETY: The `into_foreign` function derives its pointer from a referenc= e, so it is correctly +// aligned. +unsafe impl ForeignOwnable for &'static mut T { + type PointedTo =3D T; + type Borrowed<'a> =3D &'a T; + type BorrowedMut<'a> =3D &'a mut T; + + fn into_foreign(self) -> *mut Self::PointedTo { + core::ptr::from_mut(self) + } + + unsafe fn from_foreign(foreign: *mut Self::PointedTo) -> Self { + // SAFETY: from_foreign has stricter restrictions than `borrow_mut` + unsafe { Self::borrow_mut(foreign) } + } + + unsafe fn borrow<'a>(foreign: *mut Self::PointedTo) -> Self::Borrowed<= 'a> { + // SAFETY: We know the original reference lived forever, and the r= equirements on the + // function indicate that `from_foreign` and `borrow_mut` will not= happen concurrently, so + // we can do a shared borrow. + unsafe { &*foreign } + } + + unsafe fn borrow_mut<'a>(foreign: *mut Self::PointedTo) -> Self::Borro= wedMut<'a> { + // SAFETY: We know the original reference lived forever, and the r= equirements on the + // function indicate that no other borrows will happen concurrentl= y, so we can do a + // unique borrow. + unsafe { &mut *foreign } + } +} + /// Runs a cleanup function/closure when dropped. /// /// The [`ScopeGuard::dismiss`] function prevents the cleanup function fro= m running. --=20 2.50.0.727.gbf7dc18ff4-goog From nobody Wed Oct 8 12:50:08 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 6BA36292B25 for ; Fri, 27 Jun 2025 23:19:00 +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=1751066343; cv=none; b=c6m97CrdOfy6zBo+FW69SCdQEQNDR+GGsCiX2OwLsb21M10g3rk4RLMJxtuYyUgKqFmntD+SlfD4bncOaS+LZFYJdxaoDgFMA3ywBC+lXVSRdQUHzCwMDvyKSixc5g5GO1v5cgTAgTdJGpnSDgZtnj6t9GLQIX+ri56U4pSj1uQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751066343; c=relaxed/simple; bh=CCzVBiivBVXMSUo5O7/V/lLtK76aY9HUiuVRZJLegEc=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=IbsGXC0pIGOjYS/RFFb/VRlxuugfnwRm9Qwvixf9uD5m8FAXIQpjnso4yWBqgKc6NroE0oviMD0LsVvY2crxBZiU87o/taS6PQBNAN7XAFl57/FSlV9l3wYYx6HbUu1oIrIpg01PAHyjcwmRStK9VrZp7MYm4GFpHkG3t4fU8jk= 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=iW5iYdD/; 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="iW5iYdD/" Received: by mail-pf1-f202.google.com with SMTP id d2e1a72fcca58-7377139d8b1so330883b3a.0 for ; Fri, 27 Jun 2025 16:19:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1751066340; x=1751671140; 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=R3e8+g6QT9hwNSxzHZzZqHUNIxultYpbJvJe5qexWds=; b=iW5iYdD/4lMbxc9zXjs+BWcm+IBkrYuDvqbW9brB3dJvksupwwuhR2jXIQkelcWa2m tlt7J7tq76/r2Btk5nZEnAHZmsdVyEgvHU+FRXwz5B2ILoY8v0aiHnb/eqXkWNB3W0y5 04BUeRBm1s4QfRE7kpb/q2hp5Toc6Ha5/x0LBTOIrfYlJ/S9tMo2BechJpE245kHOBcC 2sK6I6D8FX66Z4yhg9cIhC6P+/LZA63zqlIi08ksKKDsyLv48/jLRkugJeZi3AP8sP2T 0tWkz2HqkYHX+lZzFRuNW7WaKQdZn5hF/s6q2AE0I8kq7MS8L8xMgxDnzIln1NI7DyvK 6UcA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751066340; x=1751671140; 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=R3e8+g6QT9hwNSxzHZzZqHUNIxultYpbJvJe5qexWds=; b=Btm9VosqRO/OpIQUSnVU1Ld04DhXnxlN1zIN0E1+6uuwQthWIIO+W1lptK5j7mUosM h8iY4tyaKO91rqpAasPaiwe8yvWBIr7clV3+Yx9lDsLwNpqgCKX2S8fmkruMcOjcn7ed pnfl3Bg6y0ap8lQtkReWj7Q8uOcCOhg9yIp8Bn6JRsDBK/esW8qUo+2PLdVvm8qvLhEs AerndmU1kHaFuKcB8Cx1GgO0t1ymKBKbIchzOP2w1ubv9blmlDnEYNJXdwsGuVb+jcCw qyQOWfHDscVsLrSuWQxpulJrAe98Q/GGRmShm61Tfe1Fam9Lkzjf8FFZQFVudyxaH+DW 5P2g== X-Gm-Message-State: AOJu0YyHLbZRHL4E8SI0TgYHD2jZCGLgeru3QzNn3Ku94ZV/MWT1pWhI HnDzcQCBHSXHIosd9HyNQOAlnYZJbeSE+88tZBklcwjII5Ow1FHBHfNyJbjFnfivlUyetODTRVp p6TU7iV9fKQ== X-Google-Smtp-Source: AGHT+IGVBBGm2MURUVDVjdiFxppjr7Yri9nVRQzS7G0XJ7Be6h7Im6R3zQR+XQsT95t1tcbfAJWnoy/wnMMp X-Received: from pgac18.prod.google.com ([2002:a05:6a02:2952:b0:b2f:4c1b:e1a0]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a20:3d08:b0:218:9b3e:e8bd with SMTP id adf61e73a8af0-220a12a662fmr7686662637.10.1751066339765; Fri, 27 Jun 2025 16:18:59 -0700 (PDT) Date: Fri, 27 Jun 2025 23:18:27 +0000 In-Reply-To: <20250627-debugfs-rust-v8-0-c6526e413d40@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250627-debugfs-rust-v8-0-c6526e413d40@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1751066331; l=10601; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=CCzVBiivBVXMSUo5O7/V/lLtK76aY9HUiuVRZJLegEc=; b=G8n4sb4Xu2zurni/h3aQ7uYSYPthrOZaRXO2LJv2K/E21jlFyPU77UIesfpoPrV7LO+Dr1LKu 7NQjH2DTrVEBuOOLdpO34CSUDPgWiMmm64GfIfqrbBvP6PUVvsLgpv3 X-Mailer: b4 0.14.2 Message-ID: <20250627-debugfs-rust-v8-4-c6526e413d40@google.com> Subject: [PATCH v8 4/6] 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 , Dirk Behme Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable This allows `File`s to be backed by `ForeignOwnable` 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. Tested-by: Dirk Behme Signed-off-by: Matthew Maurer Reviewed-by: Alice Ryhl --- rust/kernel/debugfs.rs | 99 +++++++++++++++++++++++++++++++--= ---- rust/kernel/debugfs/display_file.rs | 49 +++++++++++------- 2 files changed, 115 insertions(+), 33 deletions(-) diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs index 1f20d85da56fcb89476552feefc9d97fab43cc04..929e55ee5629f6888edf29997b9= ed77d274e11c8 100644 --- a/rust/kernel/debugfs.rs +++ b/rust/kernel/debugfs.rs @@ -5,11 +5,11 @@ //! //! C header: [`include/linux/debugfs.h`](srctree/include/linux/debugfs.h) =20 -#[cfg(CONFIG_DEBUG_FS)] -use crate::prelude::GFP_KERNEL; +use crate::prelude::*; use crate::str::CStr; #[cfg(CONFIG_DEBUG_FS)] use crate::sync::Arc; +use crate::types::ForeignOwnable; use core::fmt::Display; =20 #[cfg(CONFIG_DEBUG_FS)] @@ -63,40 +63,52 @@ 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(&self, name: &CStr, da= ta: D) -> File + where + for<'a> D::Borrowed<'a>: Display, + { + let mut file =3D File { + _entry: Entry::empty(), + _foreign: ForeignHolder::new(data), + }; + let Some(parent) =3D &self.0 else { - return File { - _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 _, + file._foreign.data, core::ptr::null(), - &::VTABLE, + &::VTABLE, ) }; =20 // 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::new(ptr, Some(parent.clone())) }; + file._entry =3D unsafe { 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(&self, _name: &CStr, data: D) -> File + where + for<'a> D::Borrowed<'a>: Display, + { + File { + _foreign: ForeignHolder::new(data), + } } =20 /// Create a DebugFS subdirectory. @@ -127,7 +139,21 @@ 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 { + /// + /// ``` + /// # use kernel::c_str; + /// # use kernel::debugfs::Dir; + /// # use kernel::prelude::*; + /// let val =3D KBox::new(300, GFP_KERNEL)?; + /// let dir =3D Dir::new(c_str!("my_debugfs_dir")); + /// dir.display_file(c_str!("foo"), val); + /// // "my_debugfs_dir/foo" now contains the number 300. + /// # Ok::<(), Error>(()) + /// ``` + pub fn display_file(&self, name: &CSt= r, data: D) -> File + where + for<'a> D::Borrowed<'a>: Display, + { self.create_file(name, data) } =20 @@ -147,6 +173,51 @@ pub fn new(name: &CStr) -> Self { =20 /// Handle to a DebugFS file. pub struct File { + // This order is load-bearing for drops - `_entry` must be dropped bef= ore `_foreign` #[cfg(CONFIG_DEBUG_FS)] _entry: Entry, + _foreign: ForeignHolder, +} + +struct ForeignHolder { + data: *mut c_void, + drop_hook: unsafe fn(*mut c_void), +} + +// SAFETY: We only construct `ForeignHolder` using a pointer from a `Forei= gnOwnable` which +// is also `Sync`. +unsafe impl Sync for ForeignHolder {} +// SAFETY: We only construct `ForeignHolder` using a pointer from a `Forei= gnOwnable` which +// is also `Send`. +unsafe impl Send for ForeignHolder {} + +/// Helper function to drop a `D`-typed foreign ownable from its foreign r= epresentation, useful for +/// cases where you want the type erased. +/// # Safety +/// * The foreign pointer passed in must have come from `D`'s `ForeignOwna= ble::into_foreign` +/// * There must be no outstanding `ForeignOwnable::borrow{,mut}` +/// * The pointer must not have been `ForeignOwnable::from_foreign`'d +unsafe fn drop_helper(foreign: *mut c_void) { + // SAFETY: By safetydocs, we meet the requirements for `from_foreign` + drop(unsafe { D::from_foreign(foreign as _) }) +} + +impl ForeignHolder { + fn new(data: D) -> Self { + Self { + data: data.into_foreign() as _, + drop_hook: drop_helper::, + } + } +} + +impl Drop for ForeignHolder { + fn drop(&mut self) { + // SAFETY: `drop_hook` corresponds to the original `ForeignOwnable= ` instance's `drop`. + // This is only used in the case of `File`, so the only place borr= ows occur is through the + // DebugFS file owned by `_entry`. Since `_entry` occurs earlier i= n the struct, it will be + // dropped first, so no borrows will be ongoing. We know no `from_= foreign` has occurred + // because this pointer is not exposed anywhere that is called. + unsafe { (self.drop_hook)(self.data) } + } } diff --git a/rust/kernel/debugfs/display_file.rs b/rust/kernel/debugfs/disp= lay_file.rs index e4b551f7092884ad12e18a32cc243d0d037931a6..0c2dd756fa866425d1b7771bece= aa2fb43bf11e5 100644 --- a/rust/kernel/debugfs/display_file.rs +++ b/rust/kernel/debugfs/display_file.rs @@ -4,42 +4,48 @@ use crate::prelude::*; use crate::seq_file::SeqFile; use crate::seq_print; +use crate::types::ForeignOwnable; use core::fmt::Display; =20 /// 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. +/// * `inode`'s private pointer must be the foreign representation of `D`,= and no mutable borrows +/// are outstanding. /// * `file` must point to a live, not-yet-initialized file object. -pub(crate) unsafe extern "C" fn display_open( +pub(crate) unsafe extern "C" fn display_open( inode: *mut bindings::inode, file: *mut bindings::file, -) -> c_int { +) -> c_int +where + for<'a> D::Borrowed<'a>: Display, +{ // 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) } + // * The `data` pointer passed in the third argument is valid by calle= r preconditions. + unsafe { bindings::single_open(file, Some(display_act::), (*inode).= i_private) } } =20 /// 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` must point to a live `seq_file` whose private data is the foreig= n representation of `D`, +/// and no mutable borrows are outsstanding. +pub(crate) unsafe extern "C" fn display_act( seq: *mut bindings::seq_file, _: *mut c_void, -) -> c_int { - // 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 +) -> c_int +where + for<'a> D::Borrowed<'a>: Display, +{ + // SAFETY: By caller precondition, this pointer is live, has the right= type, and has no mutable + // borrows outstanding. + let data =3D unsafe { D::borrow((*seq).private as _) }; + // SAFETY: By caller precondition, `seq` points to a live `seq_file`, = so we can lift // it. let seq_file =3D unsafe { SeqFile::from_raw(seq) }; seq_print!(seq_file, "{}", data); @@ -47,15 +53,20 @@ } =20 // Work around lack of generic const items. -pub(crate) trait DisplayFile: Display + Sized { +pub(crate) trait DisplayFile { + const VTABLE: bindings::file_operations; +} + +impl DisplayFile for D +where + for<'a> D::Borrowed<'a>: Display, +{ 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::), + open: Some(display_open::), // SAFETY: `file_operations` supports zeroes in all fields. ..unsafe { core::mem::zeroed() } }; } - -impl DisplayFile for T {} --=20 2.50.0.727.gbf7dc18ff4-goog From nobody Wed Oct 8 12:50:08 2025 Received: from mail-pf1-f201.google.com (mail-pf1-f201.google.com [209.85.210.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 05DF6292B43 for ; Fri, 27 Jun 2025 23:19:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751066343; cv=none; b=fyNbCw7DnW6zo/LCk6134vzWzXvuqmO3/EtdCtrcgcriCT/2DaLXhPYrvUQ0UjkVh3Bjht1aBsWFKQpkRl/GB7UaghJteGmbZ4fZ00qkODpqz/33QZY97Jg18aA6nM+X9PTRLeeC5Lcfv1WAOH/iNOLG1cJC0qZoJOaQR6WJJSw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751066343; c=relaxed/simple; bh=Gc2so4smdIf1i7j4gTeIU1kaRlnXyNxsJaXUAE7o+/U=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=q8O+okQFIMQH2HLnmas8Hn/7vwRI+uJRDlzZqaYgmAjujBgB3TjFAAbR5sGGZAo9cyYtNABtoPrwRX+UgfUJL2yBnF+jamMgzj/ynGrFYKOIW8Ws5fcl0xpW22fmVGCjIAAec3Wp12pl6NMLQhxmjUDDMbG7971eYcscA124NOE= 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=po83a66J; arc=none smtp.client-ip=209.85.210.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="po83a66J" Received: by mail-pf1-f201.google.com with SMTP id d2e1a72fcca58-747ddba7c90so366797b3a.0 for ; Fri, 27 Jun 2025 16:19:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1751066341; x=1751671141; 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=wqFaGm4GB0KNYo2CZziN4flSoFd+I3+UISgZDI1zvM4=; b=po83a66JxPvOH1ZmfVVfgaC8M0NwQucnYMw6eUXZM/rWHoorfU+Hs7WBx5NHPiwNNu 7ftsX9HFLfX/b2b2khGSeEPcfpUKQJ/+I4E90ZlsgpY53FWyZGlcA2Por537yfYxMoBg zW/UlHGOPc9jyLvgFdEtoREK0asBOB2T9VZ6mDZUg2Xo/KphXt3OO7gVhpNUwSaZo8Ny m/jY/Hfau9yP/e3SfjoorjuqkNpbfHTZlnBsRLzA4CT7v91po+L5FohqsF09Vb8V31sI Qkafam0FXQvRAh2R83W3ig4cdmZhXNukimagxaWy3QDLjSbAhRa2gwUhIZyZnAIr0H4R AeXQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751066341; x=1751671141; 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=wqFaGm4GB0KNYo2CZziN4flSoFd+I3+UISgZDI1zvM4=; b=rJMxgDDe9LNRxm/nQsb97tOW8rkCBqgc/IvEKp9GbLGI9EyjFBPoLRCYB2NxcChYU2 Dm/cT2IClTM/AdTu6dWfnZKHIhHuluM6/g5BxM4XFIMjUk7B8dabtshHaMKUt9JbyJBx jgNSBEdz3B+Wx6DxZS91aaTHvojLzICfUtzHxXqC/IxQbX0AusYo5ELSjZ9sXKKSunMJ iPl4oPvXNrhFXtu/JJc8C65N0znf8bRlpNnE93h5fXQGe56VRLbnGq8AL7Dcscf+KwL6 pF16wUEEpySJcM1O7ZIy0s0b9QM7uI4VmiLngBLUJgc/heVvn6/i2mEPsscean39B5fX ryQQ== X-Gm-Message-State: AOJu0YxX/BUbuL8Wl11yXKxuJfYv0sE3s+GPYtswq9bb58pKfuKKZf69 oA8sCamyYW2eY+aWC8KlLuG+3giUfPVc3DZ66njHGnT8Ug28DYOEztq1uNAykxEquk23vsNVhI9 VS0oJskMfig== X-Google-Smtp-Source: AGHT+IFDyZVe59e1MBXO1oPmvtzkDr7PIQbOg90cm+dhUGt0VZUUBO/sieOBpJwoyIDRyGYZaerypthGmizH X-Received: from pfbf3.prod.google.com ([2002:a05:6a00:ad83:b0:748:4f7c:c605]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a00:2409:b0:730:9946:5973 with SMTP id d2e1a72fcca58-74af6e39feemr6156140b3a.5.1751066341323; Fri, 27 Jun 2025 16:19:01 -0700 (PDT) Date: Fri, 27 Jun 2025 23:18:28 +0000 In-Reply-To: <20250627-debugfs-rust-v8-0-c6526e413d40@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250627-debugfs-rust-v8-0-c6526e413d40@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1751066331; l=6477; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=Gc2so4smdIf1i7j4gTeIU1kaRlnXyNxsJaXUAE7o+/U=; b=NnjWslr8ptGBmFKeniz9+u0oXUjE79BjuxcBOaP34x0Gbvc1Tc1q7nO3wQbBUflUsoXSKqAfu RLqE54dGrgIDYfsz8A5jmu2j2i8oECONjNTKMg68zF8MDcC9hmj7q7V X-Mailer: b4 0.14.2 Message-ID: <20250627-debugfs-rust-v8-5-c6526e413d40@google.com> Subject: [PATCH v8 5/6] 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 , Dirk Behme 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. Tested-by: Dirk Behme Signed-off-by: Matthew Maurer Reviewed-by: Alice Ryhl --- rust/kernel/debugfs.rs | 45 ++++++++++++++++++++ rust/kernel/debugfs/display_file.rs | 85 +++++++++++++++++++++++++++++++++= +++- 2 files changed, 129 insertions(+), 1 deletion(-) diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs index 929e55ee5629f6888edf29997b9ed77d274e11c8..d74b599e8534536b10502e6db8c= 6f3197f7ab4a5 100644 --- a/rust/kernel/debugfs.rs +++ b/rust/kernel/debugfs.rs @@ -10,7 +10,9 @@ #[cfg(CONFIG_DEBUG_FS)] use crate::sync::Arc; use crate::types::ForeignOwnable; +use core::fmt; use core::fmt::Display; +use core::ops::Deref; =20 #[cfg(CONFIG_DEBUG_FS)] mod display_file; @@ -157,6 +159,49 @@ pub fn display_file(&= self, name: &CStr, data: D 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: ForeignOwnable + Send + Sync, + T, + F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync, + >( + &self, + name: &CStr, + data: D, + f: &'static F, + ) -> File + where + for<'a> D::Borrowed<'a>: Deref, + { + #[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 0c2dd756fa866425d1b7771beceaa2fb43bf11e5..b38675a90e1b2e359fb54afd910= 62b26d1c32ba2 100644 --- a/rust/kernel/debugfs/display_file.rs +++ b/rust/kernel/debugfs/display_file.rs @@ -5,7 +5,9 @@ use crate::seq_file::SeqFile; use crate::seq_print; use crate::types::ForeignOwnable; -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`. /// @@ -70,3 +72,84 @@ impl DisplayFile for D ..unsafe { core::mem::zeroed() } }; } + +/// 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, + } + } +} + +pub(crate) struct BorrowedAdapter<'a, D: ForeignOwnable, F> { + borrowed: D::Borrowed<'a>, + _formatter: PhantomData, +} + +// SAFETY: We delegate to D's implementation of `ForeignOwnable`, so `into= _foreign` produced aligned +// pointers. +unsafe impl ForeignOwnable for FormatAdapter { + type PointedTo =3D D::PointedTo; + type Borrowed<'a> =3D BorrowedAdapter<'a, D, F>; + type BorrowedMut<'a> =3D Self::Borrowed<'a>; + fn into_foreign(self) -> *mut Self::PointedTo { + self.inner.into_foreign() + } + unsafe fn from_foreign(foreign: *mut Self::PointedTo) -> Self { + Self { + // SAFETY: `into_foreign` is delegated, so a delegated `from_f= oreign` is safe. + inner: unsafe { D::from_foreign(foreign) }, + _formatter: PhantomData, + } + } + unsafe fn borrow<'a>(foreign: *mut Self::PointedTo) -> Self::Borrowed<= 'a> { + BorrowedAdapter { + // SAFETY: `into_foreign` is delegated, so a delegated `borrow= ` is safe. + borrowed: unsafe { D::borrow(foreign) }, + _formatter: PhantomData, + } + } + unsafe fn borrow_mut<'a>(foreign: *mut Self::PointedTo) -> Self::Borro= wedMut<'a> { + // SAFETY: `borrow_mut` has stricter requirements than `borrow` + unsafe { Self::borrow(foreign) } + } +} + +impl<'a, D: ForeignOwnable: Deref>, T, F> Displ= ay + for BorrowedAdapter<'a, D, F> +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.borrowed, fmt) + } +} + +/// 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.727.gbf7dc18ff4-goog From nobody Wed Oct 8 12:50:08 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 A1609292B5E for ; Fri, 27 Jun 2025 23:19:03 +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=1751066345; cv=none; b=hJEK7T1okez/2l7SCqoPaEi8ETFG/zSlgpQq+CYHUAqS7We68/Z/t4wBHWGd7RjYLdmJu5KosplgUz+83oMsqwACUEZrgJf0WyxFfeUjMIbAkT543A29gMNVZ9QMLP+6+e0GRUOA6EKymvTj3ZMaWQ7NW2EDqpEpSlXLFFIYEtM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751066345; c=relaxed/simple; bh=zLOPr23btb/P2Q+bNErI5riOTp9hfWGYj59cKhKyl3M=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=lE3DTCYa8iZDH7uF1S3XxIKvaMUfgWnAbHx/+muujp0HCQKvvC3BSJfhzcJb5rsd67Bo+XnU1IMDKhb7xwbDYNbLWxk3kte+XgBAA4f56ogBuO/iwEpXSYXfo7yhHojJAmd2G6Eo5DC0di3CfHUOoEXj7eRLtWZ85tT+2dOaOHI= 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=MwTE/p1f; 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="MwTE/p1f" Received: by mail-pj1-f73.google.com with SMTP id 98e67ed59e1d1-313fb0ec33bso2355677a91.2 for ; Fri, 27 Jun 2025 16:19:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1751066343; x=1751671143; 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=FYSKWILaHkU1OfYdFyMg39AM+0yljyAGiysqEp8n70A=; b=MwTE/p1fE75+Zz3OTjavzk7dUKmYmGiXFNVKVKs/6SFre2H2BADlGlLUrsX+zFzp2/ N+YDFO5ZHJ/M/n9BzQR+6vBmCXQOFpyN3jI+7xDajJiMTXODzyTTnE/D3AyGB3T//JWM XLet1tOLlnuuhQ/C0b3SzOxEUWvZguZHe1GqaPZxLSNlJ7NRgst9/4BXlfswQMQz5yAC FrGIs+G4IqBkEP48pkPMzFFCZyCh8Yio9Z9g7Y+yBnu9hwljz4V1yAF2tvT4I9tqu+4+ gki0liqlrvjt9cOYIOlRIIAgWTYovqXEO45h31ozSeWyCVJWdqWk53CFgG3PhVhSTc5S Uw8g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751066343; x=1751671143; 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=FYSKWILaHkU1OfYdFyMg39AM+0yljyAGiysqEp8n70A=; b=sluc7MEnJ1Gb2sNCc39O4/lE0jMGbgEt3kQfeLf8tVBJaBbmqdUe8yJyViVyFVDi7B edJ7l9iSzeoV8vYosLLcUKwgXsDRlAvJwCQ4hSQLHmOWnBC4d3nNjr7yJV9nbD1Yd+DB QRsXfycrXeJCOgpsjN7po2P48O/U73Idlzw90Ij3i7Pf315N60rYTjz0P0YiZyR0NC2k gC0SvNBKAm2jVvPz2xT6RIUumVvt+ajJQGcvL9+cmlwVk1i5+zkC9NJFikPdX8jlRpyp TlgK2ji1vlDpZGvzP73nUOYJVaWKlLPoqzbxawXYWnxSVo7I2kFYID5oBjgF3jO5jrUB MGAA== X-Gm-Message-State: AOJu0Ywqgddeov6VWUKw6QrH9CuOnvIYy8vsOlLNXWXi/vLSYVlV0omd MEQcivXyVT98DjFX1hP9BUc7Vtgi2ew0xlyLwOMjJWo8S+rH64VFrtGfLu2GmUGUuw5ZEwOzO1g 3pVn/RWMp7w== X-Google-Smtp-Source: AGHT+IE+Y7GHm/K1Vq+yCasbBH4sgxeF6wx8bTZ4uGjwS1XAJoUvWQvS6NqJ5QvX+Ml437xKVT5J2EZTcIUG X-Received: from pjj5.prod.google.com ([2002:a17:90b:5545:b0:314:3438:8e79]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:4b0d:b0:311:df4b:4b93 with SMTP id 98e67ed59e1d1-318c9106951mr7504640a91.7.1751066343031; Fri, 27 Jun 2025 16:19:03 -0700 (PDT) Date: Fri, 27 Jun 2025 23:18:29 +0000 In-Reply-To: <20250627-debugfs-rust-v8-0-c6526e413d40@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250627-debugfs-rust-v8-0-c6526e413d40@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1751066331; l=5627; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=zLOPr23btb/P2Q+bNErI5riOTp9hfWGYj59cKhKyl3M=; b=0pqWdWwjSCuBN0BzLJf896SS+iXUk/wx513y49aaWkud6WntkFbHaaTEzJ1okWZl0yAKgDKY2 cpJ61WNa5RoBZzpH4tAt/d5kBHlIlf1bHGfak58BZiRVYx4zJ/U4V2L X-Mailer: b4 0.14.2 Message-ID: <20250627-debugfs-rust-v8-6-c6526e413d40@google.com> Subject: [PATCH v8 6/6] 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 , Dirk Behme Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Provides an example of using the Rust DebugFS bindings. Tested-by: Dirk Behme Signed-off-by: Matthew Maurer Reviewed-by: Alice Ryhl --- 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 551e3a6a16d9051a2d58a77614c458287da2bdcc..f55c99cb2907655d109cb1fa248= cdec803b5ce9a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7374,6 +7374,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..01101db41ae31b08a86d048cdd2= 7da8ef9bb23a2 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 Module" + depends on DEBUG_FS + help + This option builds the Rust DebugFS Test module 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.727.gbf7dc18ff4-goog