mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Andreas Hindborg <a.hindborg@kernel.org>
To: "Miguel Ojeda" <ojeda@kernel.org>,
	"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <lossin@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Danilo Krummrich" <dakr@kernel.org>,
	"Daniel Almeida" <daniel.almeida@collabora.com>,
	"Tamir Duberstein" <tamird@kernel.org>,
	"Alexandre Courbot" <acourbot@nvidia.com>,
	"Onur Özkan" <work@onurozkan.dev>
Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
	 Akinobu Mita <akinobu.mita@gmail.com>,
	 Andreas Hindborg <a.hindborg@kernel.org>
Subject: [PATCH v3] fault-inject: rust: add a Rust API for fault-injection
Date: Fri, 13 Nov 2026 15:33:20 +0100	[thread overview]
Message-ID: <20261113-rust-fault-inject-v3-1-d8bd240f22dc@kernel.org> (raw)

Add a way for Rust code to create fault-injection control points. The
control points can be attached to a configfs tree as default groups and
controlled from user space. On the kernel side, provide a `should_fail`
method to query if an operation should fail.

Cc: Akinobu Mita <akinobu.mita@gmail.com>
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
This patch is a dependency for the rust null block driver fault
injection feature.
---
Changes in v3:
- Sort the `fault-inject.h` include in `bindings_helper.h` (Miguel).
- Use backticks for `FaultConfig` and `self` in code comments (Miguel).
- Link to [`true`] in the `should_fail` rustdoc (Miguel).
- Replace the stale `INVARIANT` comment that referred to a no longer
  existing `ffi_init` call (Miguel).
- Add a `PinnedDrop` for `FaultConfig` that releases the reference on the
  embedded `struct config_item` acquired by `fault_config_init`. Nothing
  released it before, so creating a `FaultConfig` and dropping it leaked
  the resources configfs associated with the item, including the name
  allocation for names of 20 bytes or more.
- Implement `CDefaultGroup` as an unsafe trait, following the configfs
  change, and document why `FaultConfig` meets the requirements.
- Document the remaining `CDefaultGroup` safety requirement, that
  the implementer never attaches the group itself.
- Say what the `Send` and `Sync` impls rely on instead of claiming
  that `struct fault_attr` applies internal synchronization.
- Add an example for the module.
- Link the module documentation to `UnattachedGroup` and
  `Group::new`.
- Gate the module on `FAULT_INJECTION_CONFIGFS` alone, merge the
  two `impl FaultConfig` blocks and drop a `cast_mut`.
- Link to v2: https://msgid.link/20260605-rust-fault-inject-v2-1-aaf978883cc2@kernel.org

Changes in v2:
- Mark `should_fail` as `#[inline]` (Gary).
- Expand `should_fail` rustdoc to describe the `size` parameter,
  including the negative-`size` replenish semantics, and link to the
  fault injection documentation (Gary).
- Link to v1: https://msgid.link/20260215-rust-fault-inject-v1-1-6ec459cb5ccb@kernel.org
---
 rust/bindings/bindings_helper.h |   1 +
 rust/kernel/fault_injection.rs  | 144 ++++++++++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs              |   2 +
 3 files changed, 147 insertions(+)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 3791af0aadc7..08a10a7214b0 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -58,6 +58,7 @@
 #include <linux/dma-resv.h>
 #include <linux/errname.h>
 #include <linux/ethtool.h>
+#include <linux/fault-inject.h>
 #include <linux/fdtable.h>
 #include <linux/file.h>
 #include <linux/firmware.h>
diff --git a/rust/kernel/fault_injection.rs b/rust/kernel/fault_injection.rs
new file mode 100644
index 000000000000..a7d30451fd71
--- /dev/null
+++ b/rust/kernel/fault_injection.rs
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Fault injection capabilities infrastructure.
+//!
+//! This module provides a Rust API for the kernel fault injection framework. Fault injection allows
+//! simulation of failures in kernel code paths to test error handling.
+//!
+//! [`FaultConfig`] represents a fault injection control point that can be:
+//!
+//! - Attached to a configfs tree as a default group, allowing userspace control of fault injection
+//!   parameters. Wrap it in an [`UnattachedGroup`] and pass that to [`Group::new`].
+//! - Queried via [`FaultConfig::should_fail`] to determine if an operation should be simulated as
+//!   failing.
+//!
+//! Please see the [fault injection documentation] for details on configuring and using fault
+//! injection from userspace.
+//!
+//! C header: [`include/linux/fault-inject.h`](srctree/include/linux/fault-inject.h)
+//!
+//! # Examples
+//!
+//! ```
+//! use kernel::configfs::UnattachedGroup;
+//! use kernel::fault_injection::FaultConfig;
+//!
+//! // The token grants permission to attach the control point to a `configfs::Group` as a
+//! // default group. Pass it to `configfs::Group::new`.
+//! let control_point = UnattachedGroup::new(FaultConfig::new(c"timeout_inject"), GFP_KERNEL)?;
+//!
+//! // Keep a handle for querying the control point from the code path under test.
+//! let timeout_inject = control_point.clone_arc();
+//!
+//! // A control point does not inject failures until userspace sets a non-zero probability.
+//! assert!(!timeout_inject.should_fail(1));
+//! # Ok::<(), Error>(())
+//! ```
+//!
+//! [fault injection documentation]: srctree/Documentation/fault-injection/fault-injection.rst
+//! [`UnattachedGroup`]: kernel::configfs::UnattachedGroup
+//! [`Group::new`]: kernel::configfs::Group::new
+
+use crate::{prelude::*, types::Opaque};
+
+/// A fault injection control point.
+///
+/// This type wraps a `struct fault_config` from the C fault injection framework. It provides a way
+/// to create controllable fault injection points that can be configured via configfs.
+///
+/// When attached to a configfs subsystem as a default group, userspace can configure fault
+/// injection parameters through the configfs interface. The kernel code can then query
+/// [`FaultConfig::should_fail`] to determine whether to simulate a failure.
+///
+/// # Invariants
+///
+/// - `self.inner` is always a valid `struct fault_config`.
+/// - `self` owns the reference on the `struct config_item` embedded in `self.inner.group` that
+///   `fault_config_init` acquired.
+#[pin_data(PinnedDrop)]
+pub struct FaultConfig {
+    #[pin]
+    inner: Opaque<bindings::fault_config>,
+}
+
+#[pinned_drop]
+impl PinnedDrop for FaultConfig {
+    fn drop(self: Pin<&mut Self>) {
+        // SAFETY: By type invariant, `self.inner` is a valid `struct fault_config`, so the embedded
+        // `struct config_item` is initialized, and `self` owns the reference that
+        // `fault_config_init` acquired on it.
+        //
+        // `fault_config_type` does not install a `release` operation, so this does not free the
+        // allocation backing `self`. It only releases the resources that configfs associated with
+        // the item.
+        unsafe { bindings::config_item_put(&raw mut (*self.inner.get()).group.cg_item) };
+    }
+}
+
+impl FaultConfig {
+    /// Create a new [`FaultConfig`].
+    ///
+    /// If attached to a configfs group, this [`FaultConfig`] will appear as a directory named
+    /// `name`.
+    pub fn new(name: &CStr) -> impl PinInit<Self> + use<'_> {
+        pin_init!(Self {
+            // INVARIANT: The allocation is zeroed and `fault_config_init` initializes the
+            // embedded `struct config_group`, so `self.inner` is a valid `struct fault_config`.
+            inner <- Opaque::zeroed().chain(|inner| {
+                let ptr = inner.get();
+                // SAFETY: `ptr` points to a zeroed allocation and the second argument is null
+                // terminated string.
+                unsafe { bindings::fault_config_init(ptr, name.as_char_ptr()) };
+                Ok(())
+            }),
+        })
+    }
+
+    /// Query for failure.
+    ///
+    /// Returns [`true`] if the operation should fail.
+    ///
+    /// `size` is the amount of the resource consumed by the operation. It is subtracted from the
+    /// configured `space` budget on each call, and failure injection is suppressed while `space` is
+    /// greater than `size`. For allocation-style users this is typically the number of bytes; users
+    /// that inject based on the number of operations performed pass `1`. A negative `size`
+    /// replenishes the `space` budget by `-size` (i.e. grows the budget) and suppresses the failure
+    /// for that call.
+    ///
+    /// See the `space` field in the [fault injection documentation] for
+    /// details.
+    ///
+    /// [fault injection documentation]: srctree/Documentation/fault-injection/fault-injection.rst
+    #[inline]
+    pub fn should_fail(&self, size: isize) -> bool {
+        // SAFETY: By type invariant, `self` is always valid.
+        let attr = unsafe { &raw mut (*self.inner.get()).attr };
+
+        // SAFETY: By type invariant, `self` is always valid.
+        unsafe { bindings::should_fail(attr, size) }
+    }
+}
+
+// SAFETY: `fault_config_init` initializes the embedded `struct config_group` with
+// `config_group_init_type_name`, and by type invariant `self.inner` stays a valid `struct
+// fault_config` in place for as long as `self` is live. `group_ptr` returns a pointer to a field of
+// `self.inner`, so it returns the same pointer on every call. By type invariant, `self` owns the
+// reference on the embedded `struct config_item` and releases it when dropped. No method of
+// `FaultConfig` adds the group as a default group of any group.
+unsafe impl kernel::configfs::CDefaultGroup for FaultConfig {
+    fn group_ptr(&self) -> *mut bindings::config_group {
+        // SAFETY: By type invariant, `self.inner` is valid.
+        unsafe { &raw mut (*self.inner.get()).group }
+    }
+}
+
+// SAFETY: `FaultConfig` has no task affinity. The only operation it performs outside of
+// `should_fail` is the `config_item_put` in `PinnedDrop`, which may run in any task.
+unsafe impl Send for FaultConfig {}
+
+// SAFETY: The only method taking `&self` is `should_fail`, which the C fault injection framework
+// supports calling concurrently from any task. The `times` and `space` counters it updates are
+// atomics, and the verbose reporting path is serialized by `attr.ratelimit_state`. The remaining
+// fields of `struct fault_attr` are plain integers that C races against concurrent configfs
+// stores by design: they are statistical knobs and an inexact call counter.
+unsafe impl Sync for FaultConfig {}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 1ab41129d557..156074709ed1 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -73,6 +73,8 @@
 #[cfg(CONFIG_DRM = "y")]
 pub mod drm;
 pub mod error;
+#[cfg(CONFIG_FAULT_INJECTION_CONFIGFS)]
+pub mod fault_injection;
 pub mod faux;
 #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
 pub mod firmware;

---
base-commit: fd9230005e85472b81c1c7c16afe93dd878996b1
change-id: 20260215-rust-fault-inject-bc62f1083502
prerequisite-change-id: 20260215-configfs-c-default-groups-bdb0a44633a6:v4
prerequisite-patch-id: 54d13c089786f6c33633e3df846ae2a573542dd6

Best regards,
--  
Andreas Hindborg <a.hindborg@kernel.org>



                 reply	other threads:[~2026-11-13 14:33 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261113-rust-fault-inject-v3-1-d8bd240f22dc@kernel.org \
    --to=a.hindborg@kernel.org \
    --cc=acourbot@nvidia.com \
    --cc=akinobu.mita@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun@kernel.org \
    --cc=dakr@kernel.org \
    --cc=daniel.almeida@collabora.com \
    --cc=gary@garyguo.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tamird@kernel.org \
    --cc=tmgross@umich.edu \
    --cc=work@onurozkan.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®