From nobody Wed Oct 8 19:55:46 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 BC84026AA88 for ; Tue, 24 Jun 2025 23:25:30 +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=1750807532; cv=none; b=f7vdlAXyOelSbnZWcrozfdhGUogmGnFPFDBf1mnYv6NIOoRykShr5Z7qeupYBP/d2u4Zl43URALB+w1FcuFq07Nj7OvqEgqxkJ40IO3KYL504Opabjf3A1YPJlgulYZ1xXUk9RLfPqSNfmhrrcZqtYR+m6RCiVYd3ijrzLDCERc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750807532; c=relaxed/simple; bh=+xp41C/RfBpKkqE2Sxk4nZLaUiDWnhTMhBINwGivj98=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=AyvW2dnzFK/2Bpkw/ogQEtjBI8Iwv0rcn7aeenrofhuQveDN8jUVyL+CWIoTWflz7SDI/QCpchOueDjD5y+2Mu81wYzSPuocQkJiTCftaMiySvCBXx213Xq1cG5xUSpXVOhYxGCw4ei9EwuizWctHB2Sd7OWo3utT1uz/HTW/cs= 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=Vhp298hR; 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="Vhp298hR" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-3132c8437ffso6471750a91.1 for ; Tue, 24 Jun 2025 16:25:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1750807530; x=1751412330; 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=/u7X21DjoTPTxomPOqlU4vHtlMJD+vn/x0gh1izMaRc=; b=Vhp298hR3/GKyFVEvRu6s26CdjM6xPMvVlBuCPOFBnBPlekGgHuS2NfU10plnugHnR ZvUW3hxipuE5rj7E9YcLqsDpIvxRBtzKm4MskujLzhuDGVQ7mGp2+MKjWctp19T0HYze 5E/mtUB6wsDQLV/M0GStUhIIkIYfS4yO0kaxrFuXgo/+aV3FEP4MgImSH9cCcaNg5eq3 WgmaWDz+4C4C6N0vgn4inlc362Ds1AyhmT/bmiB4APQ7q2vw8WQxH9/Qq5CKGOR+006R WNH3VbCU0OMwo0ym/qoIC/37HJ9L7IiwsuLJpSNgV+W9cRPHneuJeI3HOfqundPp5qsM XkdQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750807530; x=1751412330; 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=/u7X21DjoTPTxomPOqlU4vHtlMJD+vn/x0gh1izMaRc=; b=shnL4Jzwl1PJSrCQMToRugzDM7lg8bnUpsQjX4tO0Njp8lKOBrEmQoVPtCrpNe0C7V GSgdGZqPd/dDgkrCZp+unwFP7omPpzix2lzxkjje9T+lEvW53/Vnt4QW4o+PtVQGfipL eMj1jXx2MaPFct8g33TIeLL2Qd3/4Y1lgVOX+HW9+NOSOnOyGvwD6t4dgcXIt4OeODmn lz2Ls7vts5DRiiNmfNHfYRlvK4jnNpqhIV/pnVUHYsJiBjDBkGiM9y24ruN8WZnCypEG yx5dVHQQI+AzNG0twzJUs65XIAQq+1UC+k2Opr1eX3j+0ST26kLM2YcJE18fLIe7HXlA m4JQ== X-Gm-Message-State: AOJu0Ywi3wUDjLf2BG2PsvSpMzy+gL0IDCDW+OIOFc2l/HZjAp0RxLgT naAjYeAIAAcmTvkS2e0PMRbmzlJqIeRiuaAwz45+7Id04O66YFmqUAN0lmjwnS5z6UdQkebC+Cm 8jsXUOTMyCA== X-Google-Smtp-Source: AGHT+IHyklagcPPuIpqO44y6dk4CBr/pVhvvzuFnM/jRch5NtGNtw1eN/CDkEdGm6nwiLwOuwlsfNTYcEy/W X-Received: from pjbsr13.prod.google.com ([2002:a17:90b:4e8d:b0:311:8076:14f1]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:4988:b0:313:d79e:1f48 with SMTP id 98e67ed59e1d1-315f2675beemr1149688a91.16.1750807530092; Tue, 24 Jun 2025 16:25:30 -0700 (PDT) Date: Tue, 24 Jun 2025 23:25:20 +0000 In-Reply-To: <20250624-debugfs-rust-v7-0-9c8835a7a20f@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250624-debugfs-rust-v7-0-9c8835a7a20f@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1750807526; l=7537; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=+xp41C/RfBpKkqE2Sxk4nZLaUiDWnhTMhBINwGivj98=; b=QG8NOTQwzEEj3qrpQze05r6EKB80xbtOCS9Y+slO9tHjvEJtgayszWc22LzBzILyfmPWdMTKw Ig7eEZJwTIuAIKnED4KTDAZRNFXVMdG+7ZnlyZfyiBq1NNsx+pVCWU5 X-Mailer: b4 0.14.2 Message-ID: <20250624-debugfs-rust-v7-1-9c8835a7a20f@google.com> Subject: [PATCH v7 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 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 | 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.714.g196bf9f422-goog From nobody Wed Oct 8 19:55:46 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 5849726B0BE for ; Tue, 24 Jun 2025 23:25:32 +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=1750807534; cv=none; b=NCKUeIE1ZWJtbKX1wNkUUf0uHNQBi8+x1z3BOMWHWwTT7befcptgnC/PMZK8ZHoYVTuVH/Wyo1wVQTRKyYYXvcBjtZWyixjfxATPn4FXMNA92253vY/tSyk5OW2SSKI7b/9X0NzVcaKd+5j31E+UDwdIHyoMjZeKev9InJDGLI8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750807534; c=relaxed/simple; bh=eWFtMg+1mUstLz1AnFmKGbl4X0Ap1ZmRq13QxZaZr14=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=TQMnvyQ71hYLS7NBEXCOWwsxOrWSA73iKX4h+GX9h0YIEOlO7CsgM8v5lzf6dweqFYBZej7W/Tlz7i1BRNEfXKDEzgiVcXSyvcP0MSG79Cu6/01qlFZrJshY86Z8zWbVRkf4H6gqHq9kPX0hG3YfEawCxSNNE3wY9FTlsxArTx8= 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=DVYJGrWx; 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="DVYJGrWx" Received: by mail-pf1-f201.google.com with SMTP id d2e1a72fcca58-7491814d6f2so905283b3a.3 for ; Tue, 24 Jun 2025 16:25:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1750807531; x=1751412331; 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=0XFj7DwWuJaySgVlNklxUH82PEHx6pCmOK1QZq0Gviw=; b=DVYJGrWxjq364A6bg4syZLnLd8UBWlVRD0s/0PQnYSI0oIvuloL9OvoEQHprnkrMbB b2g8PgJggc00Fh7x8iKd/03XTc7xR/nDcITtwjKOxRjiDH2uy1PuiWcLhKutL0uuvNjZ csmSJiAbHdBYUg++gymBoLgMH4yxAcn7hRJ028SnAZV2GeBXjb0DR+J4YSrFG4w7gpts QX4heN4fopbybt84NjeEiQWFwyqprRJ65tRIeCgGzxrG6bj3/vmUDX2NED0BNR5wJxBM sf6/Uc4D5tP84HxnGZfW/qMfFrpJ8vjoWZhUnk65P0HP9+P0CsYp0LwX8YiDAjQ7EgeR ySoA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750807531; x=1751412331; 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=0XFj7DwWuJaySgVlNklxUH82PEHx6pCmOK1QZq0Gviw=; b=Tzm9jKNBPPYTuTrc474dhTThkjlkkJQ1c9OQqo2yN4b+u81J5QAMHQOPOVMu0bLqfP PmHePQreZhhtMXLGoII6eubt4AceDmDJIkrpzP1pKCGUwcB3fJF0GVeR6pzyFQE4tkdF VwvWZT5tmk8mXNrzk23Ry8jGtPngK/Tp9evGHXVNh5UssClUYS4uWH6YKjKXyP3N63aO 97FEHUJNi8semY8PK0wEPWvHC3qJBvfAf5R1AnSjwKHI8ql5Svl5bFLhzZplzuV8xFkC e3F4Sz+V/guJuMflLbM2jtAgeMYMUiB6ljBDMmzfMrMm/s35J6SzD7uy8HaddcXMVqIc TMzg== X-Gm-Message-State: AOJu0YyYGAA7WqHJLE6eqBRIhNlkbqMgSELt8+/SfY4AAesccAadcpID SITbpFmHDmWpVm8K8VOKqPGGO3ZXgAElJmnjsQ6Asfhz1jHks6ABOFcRxDzFCDyW1TlcErTxwzE iC7aT6pUepg== X-Google-Smtp-Source: AGHT+IGJcn/EykG+jT5OLFYGdRt3qZuwFyOC3e8e4pPnmsh56KpRwNnABv2ZTEHRxb/6BHFTngcWEJMV3ym2 X-Received: from pfbde4.prod.google.com ([2002:a05:6a00:4684:b0:741:8e1a:2d09]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:aa7:8893:0:b0:748:ff39:a0ed with SMTP id d2e1a72fcca58-74ad45b3fc0mr1280034b3a.20.1750807531520; Tue, 24 Jun 2025 16:25:31 -0700 (PDT) Date: Tue, 24 Jun 2025 23:25:21 +0000 In-Reply-To: <20250624-debugfs-rust-v7-0-9c8835a7a20f@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250624-debugfs-rust-v7-0-9c8835a7a20f@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1750807526; l=7234; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=eWFtMg+1mUstLz1AnFmKGbl4X0Ap1ZmRq13QxZaZr14=; b=sQMWG9TzZ8Ejh9Im87WE0ksTMM5FUPvSrLruugwPkCpAPFP/kQrxEqc1uyNsImCPyasfVlfXH uYrOGEtsF/oDPEiDI0IlLJo5EMuRFc99bs56ZHxUQs6vqwVvb0yuOh6 X-Mailer: b4 0.14.2 Message-ID: <20250624-debugfs-rust-v7-2-9c8835a7a20f@google.com> Subject: [PATCH v7 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 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. Signed-off-by: Matthew Maurer Tested-by: Dirk Behme --- 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.714.g196bf9f422-goog From nobody Wed Oct 8 19:55:46 2025 Received: from mail-pg1-f201.google.com (mail-pg1-f201.google.com [209.85.215.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 EF61C26B750 for ; Tue, 24 Jun 2025 23:25:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750807535; cv=none; b=X9MGUl6HVajfwsOSrhwmtfqQnUnzMpmIAZmGf4+NAJ89giMeYPc9G57oVGcdzAE8sYaGXRlwazLWrSNhlvHZjIR+Q+nYCYxbAkfboUrpjKo7/EJ918J4ls+LW2I0ft8afZ33TgF9t4vIjgzfVi1JpTFJv7MnZ7N6tgyNr6QPisw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750807535; c=relaxed/simple; bh=csrmOtgDOUPu84tv7PeAGJkBv9W4D79BKUWoKWfBICc=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=OjtEcnZsH9EkYEQNfd8GsJF36ytZO8l3r4qY5emROkhGf3g0kETnex/o3pd5AF9SlaB9103ekQlNda5QxlH6JQqtrJQEqFQB5wp17QNtWX07pxrDZep8Z3jMKfhOpUahAIz0MLoB7WWGnJ+iFjl9hZXm804oDdo/4cXu23Jhegs= 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=vLv50ce5; arc=none smtp.client-ip=209.85.215.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="vLv50ce5" Received: by mail-pg1-f201.google.com with SMTP id 41be03b00d2f7-b3183193374so4198876a12.1 for ; Tue, 24 Jun 2025 16:25:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1750807533; x=1751412333; 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=jpHn/Mf98CTz3w1q9Q5YkPhYg7ss8fRZAZdslJSSZ2w=; b=vLv50ce5PqFTzHC4D1ivqemyvOFggxUl7Peb4XYNWuN+eQqeQtEbjBZ+YG7HXBE6Zl t1UtRXh7RjARMippVTLbcLjhWtnicsdNxI/ncTP43aEFhuOegNNVIslqxoc/XJ7fJyV7 SKizI4GZTNcnAvu615KahBb6f983ux+hDWR8hqyIBLe9EKFYy9HADT+XJB2QMM2qXmw/ tK14qDi2OrDc/U97H0zFBdfS2VDnm4j8/ZjjgcQ2nDTfkhoFqe9ckKKz7Krq+gbo4qV1 QScteN9fK3aoi6lA0VxD5sRDhOeCkKxvNpNrWgc5vazaodUtPNTQlRa8Bpf46pAcr9zA Si7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750807533; x=1751412333; 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=jpHn/Mf98CTz3w1q9Q5YkPhYg7ss8fRZAZdslJSSZ2w=; b=wspeGCQTy2a/A/W3R1ref6HAGaGRnZo5CpF6/QiO4YYEwN1CxLPFbVKYxMk2R4XkZm d7piC/TGvoMwlJJYS+jkXxEoYPQ/3CZYwvdh1hmuQWC5OAN4qA8ic0NigUpUZdcFvN/c hRsXbF2P+9oHxkRoro6FL/EvgVddfE4bOudWUZCfp+HqW9SscT0Ni49CmHirXaCiES8r W40Y+CAlnTs9k94pQfp3nexLeLaxHgJ2f0Jw5Omw24mIj3RvXlwd9yoBQnxv0cMFD42v b0GXhfvTvAJzqjXIXSEPyB7y2C8rJuFkaims6YGnf31TMdQ7pZPvO5nQ1GmHq3ItMV6q G/3A== X-Gm-Message-State: AOJu0Yz+V4xEapx6/q+9h5HBZwgLlpID+Ox3UbpYFI5HGRimfhU9vVcK ogmy2MngxKRCfWyr418xrS0qsPit1FUMoT8c6hFlh5qOBLmpjylxYnmiQfa/7qlJcm7SxIXfk0C 6+PlgirAgVw== X-Google-Smtp-Source: AGHT+IEU3WSnV/iYS+a5AiCRRNPTzXx/GpONFhMr5wKKYYchsgqip2PjcSWvWvbf8EDrhqqxX+S/mAdfK9y4 X-Received: from pgww8.prod.google.com ([2002:a05:6a02:2c88:b0:b31:c9e0:b48]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:9996:b0:21f:775d:107e with SMTP id adf61e73a8af0-2207f20ad9bmr1219035637.17.1750807533234; Tue, 24 Jun 2025 16:25:33 -0700 (PDT) Date: Tue, 24 Jun 2025 23:25:22 +0000 In-Reply-To: <20250624-debugfs-rust-v7-0-9c8835a7a20f@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250624-debugfs-rust-v7-0-9c8835a7a20f@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1750807526; l=3137; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=csrmOtgDOUPu84tv7PeAGJkBv9W4D79BKUWoKWfBICc=; b=7e3OUzW7UAi5cjOdMhqW3Qbh8mz1+Pvlsdj4Lv2je6H6Mq0sUdorH00u999tBHoADar7VDMPx Lkgob2aCw1jAWDWOIDEaXyQhAu/KJLt9vA9hiDtrT0uCIC/9Xsq6Jge X-Mailer: b4 0.14.2 Message-ID: <20250624-debugfs-rust-v7-3-9c8835a7a20f@google.com> Subject: [PATCH v7 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 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`. Signed-off-by: Matthew Maurer Tested-by: Dirk Behme --- rust/kernel/types.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 58 insertions(+) diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index 22985b6f69820d6df8ff3aae0bf815fad36a9d92..6f9617b5b491426b1be5f3a27dc= 2c48ad1854da8 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 { + self as *const _ as _ + } + + 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 { + self as *const _ as _ + } + + 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.714.g196bf9f422-goog From nobody Wed Oct 8 19:55:46 2025 Received: from mail-pl1-f202.google.com (mail-pl1-f202.google.com [209.85.214.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 AF92826B76F for ; Tue, 24 Jun 2025 23:25:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750807537; cv=none; b=Cuv3UaLTVOeYmWtrl8Q+G80FTYY9I2xjPZ9sMRg1q/qZFGA5URd8s10f/U8kjx4gtFxO2k/1tS7wg/SO+0EdrF6lmC0lY/bQ0wUvzLJ49Wp5Xceu+FkIR0Phw30lLZ9n1yqXevku98W2GxmB2hiEvsBFM3fiWXB7QgWFmo1Mj14= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750807537; c=relaxed/simple; bh=WjpW1mXe8cskLJAhrTgyC0GBDNPR7e42hLnbyajfepA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=XlQobORtFFZDD6FHYJ1OMBDaiMq15NfJ5vxf4KPgD/MPHb+TREruzOmhtxRg8VD0pqxDXCfkI8lgAOamnOzx6kMmlBW/p6gZ7y8OyhlcsbbInOCsxCn/WcsSLVR2cYVxtm7RFDEwP+CLOn4ljZPogH9QPR1dwatzGSsT0mwfiqM= 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=30CdnvrT; arc=none smtp.client-ip=209.85.214.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="30CdnvrT" Received: by mail-pl1-f202.google.com with SMTP id d9443c01a7336-235089528a0so2639205ad.1 for ; Tue, 24 Jun 2025 16:25:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1750807535; x=1751412335; 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=VcQSqDmTI7Mi8sBsMKZd9s5lqLXk3Ve+cqvRIqdaUAg=; b=30CdnvrT8jqeh+CBuaMvbYk1kYL5o7OOdHOfO6pSjRyHtKrlFqJtlY+YRqfiJP4ayc YsIiMHXiji8jqdkfIrGkiuIlwxeNMchNJefL0vM/Pd/ENAR84TgwQMP0n/OG6EInnNTc rlGoH/oiZQO1IaeMH63OqeR3kbDMimJ3644x2484OfkWvLaNCW/SbtUvSnUpCHs86SHf fLx7U16WlNsk4D7/JmkjjOQ1pU2vQT4oihKOXQKEnecnOvP/DE58VNesO3v03v9pNe8i fumOgTxlNz1QVKlX9u6x+TdVwnpdLxnI8amYt7n/i7OuZRXfHiBq/2eN7S8Cqlf6MWeY NeuQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750807535; x=1751412335; 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=VcQSqDmTI7Mi8sBsMKZd9s5lqLXk3Ve+cqvRIqdaUAg=; b=ul/GtoYaDtDv41lNl6dLG5lmJrpP61kbaLxr1ovacBM8Weftr5RcJ2Qr9hbzWB6PNm 8QmtcB0tlXRiUdk7g2Oq2YZCLeO+bOHuiK1g/4TQCub/IEFduTjdH7cTqTv81DV8X6OI kS/+gTJvT/z6bmzP64+trZuuL5OSi0n9RmAyAPlYUMICdTwnog6yPwZA2rkT5+Mf7tCt V/bzMjD9AwpywdfMNWdZSeKmUtl2GPqEzGxGk1rDvPFZaFBNU8KFqxTugTgVoB64vzLt s1Vx/U9RtxV2oysmB24kkkUiA9Su8lm68ef2sp/sAaXHAtrH5Hv5okk/0o85PjvNh71S 2Aag== X-Gm-Message-State: AOJu0Yw7oeFZtnmePeRloWUnmKN9WD7ggJq3v82dwTJkK7h5zc0Ks24i mn+PghRpZwNKHWyf2ShHJHPi7SqVrrYG+vQr5h0bUna/wWielRijxe4oPIqFNac3iU5Bdg7NzZ0 TNDMbdgup7g== X-Google-Smtp-Source: AGHT+IGjx99C5o9m+4WWlNaQnnXrLLQSddoO+vJgJOK0xKwJg5ldBl9NJcByshGyyqzME+hLSTB0LTL5V/xl X-Received: from plhq12.prod.google.com ([2002:a17:903:11cc:b0:236:8084:af9f]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a17:902:d4c7:b0:234:1e11:95a3 with SMTP id d9443c01a7336-23802495bd7mr90979425ad.13.1750807534912; Tue, 24 Jun 2025 16:25:34 -0700 (PDT) Date: Tue, 24 Jun 2025 23:25:23 +0000 In-Reply-To: <20250624-debugfs-rust-v7-0-9c8835a7a20f@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250624-debugfs-rust-v7-0-9c8835a7a20f@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1750807526; l=10552; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=WjpW1mXe8cskLJAhrTgyC0GBDNPR7e42hLnbyajfepA=; b=uZE0jQrjzKnXjGQBP79LSCDmJwmGqeNJQIz17sQXljWw0cGzZZ+iXKGjich27TWHB7QSmv3OQ r4+huigDnBgBZ9OqnR8NyIk+GGOx0Rs68jQPdOGz645Waeewbm/3DvI X-Mailer: b4 0.14.2 Message-ID: <20250624-debugfs-rust-v7-4-9c8835a7a20f@google.com> Subject: [PATCH v7 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 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. Signed-off-by: Matthew Maurer Tested-by: Dirk Behme --- 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.714.g196bf9f422-goog From nobody Wed Oct 8 19:55:46 2025 Received: from mail-pl1-f202.google.com (mail-pl1-f202.google.com [209.85.214.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 5410726C38E for ; Tue, 24 Jun 2025 23:25:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750807538; cv=none; b=Z0KEmEUH+SIqsiB21cnQjqN3ICLgrImOES3GiLALyzW1I/kcYVGxpB4f5ONzsNqCFnHNKrIrjkGmj0hIN7ZwM266wJBTJgnqCcVNlzNYBOqBOndyXoq8CZuGn8wvUwdNLDXEsv3q9flRFk8IK7/MCO2yy7gboLkQpV9cQAK8d/I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750807538; c=relaxed/simple; bh=CAjW4qWEATQAhifD7XkDU8mnRpNkpHK/ka6oGRsQ4qs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=n06jcFBRdrhte62Dk7PI3CWoXDAGH6reSafvbAZBAHnN/nKxukdkdOvc5W/MO17sgTTThCIcjkr8cF1xOYltZiOF0OpjXD2xHdVqRrlOpbgNwBGUkThxMCNFwDcRoKbdCu9xdripZOO469tvqXS04uaqaBgCkEhl3qlpK8joVkY= 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=mA+/KCOJ; arc=none smtp.client-ip=209.85.214.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="mA+/KCOJ" Received: by mail-pl1-f202.google.com with SMTP id d9443c01a7336-235c897d378so8614535ad.1 for ; Tue, 24 Jun 2025 16:25:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1750807536; x=1751412336; 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=gpUS+EX77lZKoaVBWmim3dqnGXias1I977cCfQoRsAA=; b=mA+/KCOJR5BUcUo88K/tgyFsrrlJVrB2VJn74HdXAkgdm8VWru7XcIQihLZk2pc2jF 6WZiDR6t7VjuZVLJCoHCsuGK2139dOwprsi3MT0mnnSzWAns2VGMPeqzHH5NXddaXvte VoNH9H1oMhMzaGpTLAqF40VvzwnA1qgs2+ExSSzC6UHU0CBADvY6pHOvFKeuFSBukXd9 LSlQSacpqov+6XF3yOHbNkbqDoIUJ3m2phCL0D7NpKT1niBOxm4vPueZvh2uRkexWmLH xs3vyMfSKW6dRdrtDowI9sX177Yqmu5XoyHyGu/p3vSe79OplqHFke0DWWena6ho8uBA UIXQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750807536; x=1751412336; 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=gpUS+EX77lZKoaVBWmim3dqnGXias1I977cCfQoRsAA=; b=OLFuN/0DreQ2HyuKhktE8JFvz/68iV2ztuZQGJw8Uuh7WjcAovAi+mLl9pb0OuGiRj 0G17WakqOp/0GA9bS095/nS9MJvx9lDPSTcmi352MWyekVKfCyRZacSCp2H3WGbUi701 xvvt/4/xGFnzBy0VHKhGhv7JzlgA4e+073oPOZunD5muuPrNEbAluc3h0Gd7DOr0V4on UhRxVYMNaAvHL7gkKsKL5hMpUf5WDaJ8Y16ehCewdy1NeYC+GuM3Jz+21CJ3oZKaQNtD 2t0txXr9yb2U/qURmCTR9q+1g32ingbl2j8xj1KePSvy0L7V55wgdOVte2puD9wXMMEP rtrg== X-Gm-Message-State: AOJu0Yw1flV4QhCKBiX2oE9tmoMk3L0SUzvQoksqMv9pCUmIhj56RWhg jrrMx4s+APWAFOm9UiaKogurmyK/WrE1SoRDoXmQzESYY+b4fDxTn7g84/t0e0fqBWsPR0jCHwY NCFQhoNjxqg== X-Google-Smtp-Source: AGHT+IEDp9updf55BfUA08pmBhU8YEuSsF1HIz2zGLaUKLG0XPXQu8Q68M6BPJ2uiz0kFU7UI6Lz8+REqopx X-Received: from plbmi11.prod.google.com ([2002:a17:902:fccb:b0:234:c918:aa1b]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a17:902:ea0e:b0:235:f298:cbbe with SMTP id d9443c01a7336-23823fc3f70mr19229955ad.12.1750807536641; Tue, 24 Jun 2025 16:25:36 -0700 (PDT) Date: Tue, 24 Jun 2025 23:25:24 +0000 In-Reply-To: <20250624-debugfs-rust-v7-0-9c8835a7a20f@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250624-debugfs-rust-v7-0-9c8835a7a20f@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1750807526; l=6428; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=CAjW4qWEATQAhifD7XkDU8mnRpNkpHK/ka6oGRsQ4qs=; b=kryJzDpp6myFWv9gqYuI4yuSx3w9/sqq3gr4Gp89vHVyREZmgBuhlYOpGJE/UjM6jQeBuhO4S wz+ISRrNIMID3mbPaFrE6bUgDWVhw0BmCwRrWduzSnMxF0qQE0uyTfV X-Mailer: b4 0.14.2 Message-ID: <20250624-debugfs-rust-v7-5-9c8835a7a20f@google.com> Subject: [PATCH v7 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 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 | 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.714.g196bf9f422-goog From nobody Wed Oct 8 19:55:46 2025 Received: from mail-pl1-f202.google.com (mail-pl1-f202.google.com [209.85.214.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 18E2526D4CA for ; Tue, 24 Jun 2025 23:25:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750807540; cv=none; b=JW4pGLle9cHwWcq5stXzeU/Az7T7Csnulo+SoKCtReKbHEwzs0tomwWu5dovx7pR/g4EZq0PpWu6ktdCFCBzNpReo5Wngp5B1CttNhB73MQp014C89rKGzOqKdIxTSb4QUHp4o5nLJShAtBy9S+qclAcaWFN2z7gdrp4I1RNqmg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750807540; c=relaxed/simple; bh=WDzSTNiQY+qV7dKBRUBzob8WCH99zUFjPP8hF3kQqxA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=o85nRw7Z2JtCx0ugwDR17xeZDti6gZf5YL1LTWItTm1gFqS81Ye6Z9Qi/ADLiiIKx8Cvjquj9juvVTEvGKvoZZUl+XwPcU/vzRz4M2yn1IAvYZPqkKvpcqHKfZRx+DtkIMFwv2cs4aL23oy3vZ1OUg8s2cBSPGNplp2sHoHBvLo= 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=mn7bUtZC; arc=none smtp.client-ip=209.85.214.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="mn7bUtZC" Received: by mail-pl1-f202.google.com with SMTP id d9443c01a7336-2355651d204so9220655ad.2 for ; Tue, 24 Jun 2025 16:25:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1750807538; x=1751412338; 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=wkeVq1E5WpVIx5plYjGpkBVjaJU2TFimIzI1IO1cqVg=; b=mn7bUtZCAsPtoRXDY6RONBoNfo6odNKLU+TKm3MoQ5xs1ipOhl3FnNtjZbWSpkML5L iROJLiOPAfcshIFIFldKejcEJc32LkeAL9B8lhsOWg9mhh9khNorzZ2ybrfnEqL8LhPv sjpLwjrQptyQ4i6YYgPfr9iyfiQ363Xiht7trS587iQUhx/K80/BePZ3NwZcbJrqmElk bGDPhPePXyCuEfwSoE/078JRl3QtWLmh6k8iW2vl2QOACpT76iAS+scfDDDYiNrm2GuY wvknotfaKaJXFA6iuu1VwKUZyVbvuahMiz+E7AcMpfpMtuUthsyWNCJSrsmI9sMfM1wA Y4FQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750807538; x=1751412338; 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=wkeVq1E5WpVIx5plYjGpkBVjaJU2TFimIzI1IO1cqVg=; b=lUeZ16ngx17U4N6jmAGaQw5YRJ5VFbG4ImOJtHDpICb09iXwtAwwmIOZI8V0oIB2Ac 8zzlWdqggeHUiUEGAtI17d9zQWhImCGyRyDxAmfY55NrgH3pLNOHU0LuODdfojQEGRbI Rt4WmELExLpIahhMAe/M5a9qOBwXUWdp/RXjJFgRO835w8vbqOYhEFd0xpvQSxgOlSkO KURMEKaWqzhKaFH7CV23JVYNLkSQCEdeAUmsLSUEVQ1jeUghl1egtEJ7HA72LyStxS+f RfjpyQl0rXx064NWxNjzAnHuS7lKIz4vBSY17SxntUzYKqcYdZdMwy+AXEd0Bmpr5S0J tSWg== X-Gm-Message-State: AOJu0YxLvV6TVO0YcBcx6LiDin8zBv4cI0dovinKH8qg3oeeJL7QANoK ZKp5TtKs+oYWeHQC2EsPOvM9t2t+4PRL8HaJ8s6SZ2dbHipUTgaoSgwnTo/AveANY2etNPCbUle elGzYNEdDKA== X-Google-Smtp-Source: AGHT+IG59WXtCObOt4XcsMlpLJMNcD870RwnX1ihIW7Swuyf7GgiywBE9MtajTjTBGqQkYhu7FHulTUEDEK2 X-Received: from pjff12.prod.google.com ([2002:a17:90b:562c:b0:312:f31a:af55]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:1a0d:b0:234:c65f:6c0f with SMTP id d9443c01a7336-23823f7e593mr11576885ad.8.1750807538509; Tue, 24 Jun 2025 16:25:38 -0700 (PDT) Date: Tue, 24 Jun 2025 23:25:25 +0000 In-Reply-To: <20250624-debugfs-rust-v7-0-9c8835a7a20f@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250624-debugfs-rust-v7-0-9c8835a7a20f@google.com> X-Developer-Key: i=mmaurer@google.com; a=ed25519; pk=2Ezhl7+fEjTOMVFpplDeak2AdQ8cjJieLRVJdNzrW+E= X-Developer-Signature: v=1; a=ed25519-sha256; t=1750807526; l=5578; i=mmaurer@google.com; s=20250429; h=from:subject:message-id; bh=WDzSTNiQY+qV7dKBRUBzob8WCH99zUFjPP8hF3kQqxA=; b=v82r7IiZAkdfElUQ+cJ0GXgZDJ+IVd49arpB5Tsb6nsKe2eIuF685RVdXOWn9LK/+La+Ghe0n mCsrgCmUfUsDwZi9++meuohf5ApmzYrMxGor5szoc3KzS7uevhc/OAc X-Mailer: b4 0.14.2 Message-ID: <20250624-debugfs-rust-v7-6-9c8835a7a20f@google.com> Subject: [PATCH v7 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 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 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.714.g196bf9f422-goog