mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v3] fault-inject: rust: add a Rust API for fault-injection
@ 2026-11-13 14:33 Andreas Hindborg
  0 siblings, 0 replies; only message in thread
From: Andreas Hindborg @ 2026-11-13 14:33 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Alice Ryhl, Trevor Gross, Danilo Krummrich,
	Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
	Onur Özkan
  Cc: linux-kernel, rust-for-linux, Akinobu Mita, Andreas Hindborg

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>



^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-11-13 14:33 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-13 14:33 [PATCH v3] fault-inject: rust: add a Rust API for fault-injection Andreas Hindborg

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®