aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/rust/kernel
AgeCommit message (Collapse)AuthorFilesLines
2026-07-21rust: irq: fix C header path in module docsYounes Akhouayri1-1/+1
The IRQ module documentation displays `include/linux/device.h`, while the link points to `include/linux/interrupt.h`. The module wraps the IRQ registration interfaces declared in `include/linux/interrupt.h`, so fix the displayed header path. Fixes: 1f54d5e5cd2a ("rust: irq: add irq module") Link: https://github.com/Rust-for-Linux/linux/issues/1246 Signed-off-by: Younes Akhouayri <git@younes.io> Link: https://patch.msgid.link/20260717-docs-irq-rustdoc-header-v1-1-36749192aa04@younes.io Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-21rust: io: document const-offset requirement for infallible accessorsGary Guo1-0/+16
Due to the usage of `build_assert!` for address validity checking, these accessors want constant offsets. Non-constant offsets can work but it depend on compiler optimization levels, so it should be avoided. Signed-off-by: Gary Guo <gary@garyguo.net> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Link: https://patch.msgid.link/20260716142545.3622278-1-gary@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-21rust: device: make lifetime on `Core` and `CoreInternal` invariantGary Guo1-2/+8
Currently the lifetime on `Core` and `CoreInternal` is covariant. This means that they can be coerced into shorter living lifetimes. On `probe` function, signature has `&'bound Device<Core<'a>>`; the type's wellformness would imply `'a: 'bound` and thus the type can be coerced `&'bound Device<Core<'bound>>`, defeating the purpose of having the lifetime bound to prevent users of the `Core` type to escape the function. Fix this by making the lifetime invariant, so the coercion is impossible. The lifetime here only needs to be "branded" so it does not coerce or unify with other lifetimes, so we do not need to ensure `'bound: 'a`. This requires modifying `nova-core` which relies on this implied bound due to pre-2024 capture rule. The "use" bound can be removed if built with edition 2024. Fixes: 24799831d631 ("rust: device: make Core and CoreInternal lifetime-parameterized") Signed-off-by: Gary Guo <gary@garyguo.net> Link: https://patch.msgid.link/20260713201455.640151-1-gary@kernel.org [ Fixup the debugfs sample to use an explicit lifetime instead of Core<'_>. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-19rust: drm: fix non-const `read8` in unit testGary Guo1-1/+1
With CONFIG_CC_OPTIMIZE_FOR_SIZE, the address validity check in non-const `read8` invocaction is not optimized away, leading to build failure. Fixes: d055768429b3 ("rust: drm: gem: shmem: Add vmap functions") Signed-off-by: Gary Guo <gary@garyguo.net> Link: https://patch.msgid.link/20260716142545.3622278-2-gary@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-18rust: time: fix as_micros_ceil() to round correctly for negative DeltaFUJITA Tomonori1-2/+9
The ceiling-division idiom `(n + d - 1) / d` only produces the correct result when `n` is non-negative. For example, if n = -1000 (exactly -1us), the old code computed (-1000 + 999) / 1000 == 0 instead of -1. For negative n, truncating division already rounds towards positive infinity, so no bias is needed in that case. Fixes: fae0cdc12340 ("rust: time: Introduce Delta type") Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com> Acked-by: Andreas Hindborg <a.hindborg@kernel.org> Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260713225235.3243480-1-tomo@flapping.org Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
2026-07-18rust: device: move drvdata_borrow() to InternalBoundContextDanilo Krummrich1-16/+2
Move drvdata_borrow() from impl Device<CoreInternal<'a>> to impl<Ctx: InternalBoundContext> Device<Ctx>, making it available from both CoreInternal and BoundInternal contexts. Fold drvdata_unchecked() (previously on Device<Bound>) directly into drvdata_borrow(), since it was only called from there and the generic context cannot resolve methods through the deref chain. Signed-off-by: Danilo Krummrich <dakr@kernel.org> Reviewed-By: Markus Probst <markus.probst@posteo.de> Link: https://patch.msgid.link/20260530132736.3298549-2-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-07-18rust: device: add BoundInternal device context and InternalBoundContext traitDanilo Krummrich1-2/+41
Add a new BoundInternal device context type for cases where bus abstractions need access to internal device infrastructure, where CoreInternal would not be justified. Introduce the InternalBoundContext marker trait, implemented by both CoreInternal and BoundInternal, to allow methods that require internal bus abstraction access to a bound device to be generic over both contexts. The deref hierarchy now has two branches: - CoreInternal<'a> => Core<'a> => Bound => Normal - BoundInternal => Bound => Normal Update impl_device_context_deref! and impl_device_context_into_aref! macros to emit the BoundInternal => Bound deref and the corresponding ARef conversion. Signed-off-by: Danilo Krummrich <dakr@kernel.org> Reviewed-By: Markus Probst <markus.probst@posteo.de> Link: https://patch.msgid.link/20260530132736.3298549-1-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-07-17rust: firmware: avoid UB in example by passing parameterMiguel Ojeda1-6/+2
The `Firmware` example crafts an `ARef<Device>` using a null pointer, which breaks the safety requirements of `Device::get_device()`. Instead, pass an `ARef` via a parameter, which is simpler, avoids UB and removes an `unsafe` block. Signed-off-by: Miguel Ojeda <ojeda@kernel.org> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Reviewed-by: Gary Guo <gary@garyguo.net> Link: https://patch.msgid.link/20260711180231.229525-1-ojeda@kernel.org [ Also drop the second superfluous empty line. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-17rust: miscdevice: fix write_iter safety docsYilin Chen1-1/+1
The write_iter callback consumes data from the supplied iov_iter and wraps it as an IovIterSource. Its Safety docs required a valid iov_iter for writing, but the implementation and the IovIterSource contract require one that is valid for reading. Update the docs to match that direction. Assisted-by: Codex:GPT-5 Signed-off-by: Yilin Chen <1479826151@qq.com> Link: https://patch.msgid.link/tencent_8CD671E0F35223030143524D045F3BCAD506@qq.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-07-17rust: poll: use kfree_rcu() for PollCondVarAlice Ryhl1-1/+72
Rust Binder currently uses PollCondVar, but it calls synchronize_rcu() in the destructor, which we would like to avoid. Add a variation of PollCondVar that kfree_rcu() instead. One could avoid the `rcu` field and allocate the rcu_head on drop using a fallback to synchronize_rcu() on ENOMEM. However, I'd prefer to avoid the potential for synchronize_rcu(), and Binder will only use this for a small fraction of processes, so even if it changes which kmalloc bucket it falls into, the extra memory is not a problem. Signed-off-by: Alice Ryhl <aliceryhl@google.com> Reviewed-by: Boqun Feng <boqun@kernel.org> Link: https://patch.msgid.link/20260707-upgrade-poll-v6-1-4b8fae7bf1d9@google.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-07-17rust: netlink: add raw netlink abstractionAlice Ryhl2-0/+339
This implements a safe and relatively simple API over the netlink API, that allows you to add different attributes to a netlink message and broadcast it. As the first user of this API only makes use of broadcast, only broadcast messages are supported here. This API is intended to be safe and to be easy to use in *generated* code. This is because netlink is generally used with yaml files that describe the underlying API, and the python generator outputs C code (or, soon, Rust code) that lets you use the API more easily. So for example, if there is a string field, the code generator will output a method that internall calls `put_string()` with the right attr type. Reviewed-by: Matthew Maurer <mmaurer@google.com> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Reviewed-by: Carlos Llamas <cmllamas@google.com> Acked-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Alice Ryhl <aliceryhl@google.com> Link: https://patch.msgid.link/20260707-binder-netlink-v7-2-42b40e4b1ac8@google.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-07-17rust: net: add rust/kernel/net to NETWORKING [GENERAL]Alice Ryhl1-0/+0
To ensure that networking code can be found in a single shared place, add it to the relevant NETWORKING entry. The net.rs file is moved into the net/ directory so that it's included under the MAINTAINERS entry without needing a separate rust/kernel/net.rs entry. Reviewed-by: Carlos Llamas <cmllamas@google.com> Acked-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Alice Ryhl <aliceryhl@google.com> Link: https://patch.msgid.link/20260707-binder-netlink-v7-1-42b40e4b1ac8@google.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-07-17rust_binder: Add dynamic debug logging maskJahnavi MN1-0/+7
Implement a dynamic debug logging mask (`debug_mask`) for the `rust_binder` module to allow dynamic runtime configuration of log levels. This enables parity with the legacy C driver's debug mask. Since the Rust `module!` macro in the current kernel build does not yet support declaring module parameters directly in Rust, we define the `debug_mask` variable in Rust as an `Atomic<u32>` exported via FFI using `#[no_mangle]`, and link to it as `extern` in a C companion file to expose it to the kernel runtime. To verify the setup, instrument process lifecycle events (open, flush, and release) in `process.rs` under the new `BINDER_DEBUG_OPEN_CLOSE` logging mask. These entry-point events are chosen for initial validation because they represent the start of the Binder lifecycle and occur at low frequency, allowing simple runtime verification of the dynamic toggle without log noise. Reviewed-by: Carlos Llamas <cmllamas@google.com> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Signed-off-by: Jahnavi MN <jahnavimn@google.com> Link: https://patch.msgid.link/20260716-rust_binder_debug_mask-v4-1-3d7436c2d2f2@google.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-07-17usb: rust: mark Device and Interface methods as inlineNicolás Antinori1-0/+4
When building the kernel using llvm-19.1.7-rust-1.85.1-x86_64, the following symbols are generated: $ nm vmlinux | grep ' _R'.*usb.*Device | rustfilt ... ffffffff823f2490 T <kernel::usb::Device as kernel::sync::aref::AlwaysRefCounted>::dec_ref ffffffff823f2470 T <kernel::usb::Device as kernel::sync::aref::AlwaysRefCounted>::inc_ref ... $ nm vmlinux | grep ' _R'.*usb.*Interface | rustfilt ffffffff823f2450 T <kernel::usb::Interface as kernel::sync::aref::AlwaysRefCounted>::dec_ref ffffffff823f2430 T <kernel::usb::Interface as kernel::sync::aref::AlwaysRefCounted>::inc_ref ... However, these Rust symbols are trivial wrappers around the `usb_get_dev`, `usb_put_dev`, `usb_get_intf` and `usb_put_intf` functions. It doesn't make sense to go through a trivial wrapper for these functions. Link: https://github.com/Rust-for-Linux/linux/issues/1145 Suggested-by: Alice Ryhl <aliceryhl@google.com> Signed-off-by: Nicolás Antinori <nico.antinori.7@gmail.com> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Reviewed-by: Gary Guo <gary@garyguo.net> Link: https://patch.msgid.link/20260616223614.16444-1-nico.antinori.7@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-07-16rust: device: avoid trailing ; in printing macrosAlice Ryhl1-11/+9
These macros are used like expressions, so they should not emit a semicolon. This is being turned into a hard error in a future release of Rust. error: trailing semicolon in macro used in expression position --> drivers/gpu/nova-core/firmware/fsp.rs:79:34 | 79 | .inspect_err(|_| dev_err!(dev, "FMC firmware missing '{}' section\n", name)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813> = note: this error originates in the macro `dev_err` (in Nightly builds, run with -Z macro-backtrace for more info) [ I was doubly surprised since upstream made it a deny-by-default lint a year ago for Rust 1.91.0, and yet we didn't see it; plus I hadn't seen this in my CI even yesterday. It turns out this just landed into today's nightly (nightly-2026-07-16, using upstream commit d0babd8b6): Link: https://github.com/rust-lang/rust/pull/159222 which says: "The `semicolon_in_expressions_from_macros` lint previously suppressed warnings about non-local macros. This masks a lint that will subsequently become a hard error." So that explains it. And this is the PR that will make it a hard error at some point in the future: Link: https://github.com/rust-lang/rust/pull/159218 Thus starting with Rust 1.99.0 (expected 2026-10-01), we will be seeing the deny-by-default lint above, so clean it up already. - Miguel ] Cc: stable@vger.kernel.org # Needed in 6.18.y and later. Link: https://github.com/rust-lang/rust/issues/79813 Signed-off-by: Alice Ryhl <aliceryhl@google.com> Reviewed-by: Gary Guo <gary@garyguo.net> Acked-by: Danilo Krummrich <dakr@kernel.org> Link: https://github.com/rust-lang/rust/pull/159218 Link: https://github.com/rust-lang/rust/pull/159222 Link: https://patch.msgid.link/20260716-device-trail-semicolon-v1-1-f48e9dcfae15@google.com [ Fixed typo. ] Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
2026-07-15rust: sync: add SRCU abstractionOnur Özkan2-0/+173
Add a Rust abstraction for sleepable RCU (SRCU), backed by C srcu_struct. Provide FFI helpers and a safe wrapper with a guard-based API for read-side critical sections. Cleanup is handled via `PinnedDrop`. It first checks for active read-side sections and emits a warning if any guards were leaked. In that case, it waits in `synchronize_srcu()` rather than risking a UAF by freeing the `srcu_struct` that is still reachable from the C side. It then uses `srcu_barrier()` to drain pending callbacks before finally calling `cleanup_srcu_struct()`. Signed-off-by: Onur Özkan <work@onurozkan.dev> Reviewed-by: Gary Guo <gary@garyguo.net> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Reviewed-by: Boqun Feng <boqun@kernel.org> Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
2026-07-15cpufreq: intel_pstate: Adjust the .adjust_perf() driver callbackRafael J. Wysocki1-2/+9
In some cases, the processor may not actually stick to the "desired" performance level programmed through the driver's .adjust_perf() callback and may go above it, which may not be desirable (for instance, there may be a UCLAMP_MAX limit set for the task currently running on the given CPU which should be respected). Address that by adjusting the .adjust_perf() callback to take an additional argument, max_perf, representing the maximum allowed performance level of the CPU and update the intel_pstate driver to take that argument into account as appropriate. Accordingly, adjust cpufreq_driver_adjust_perf() and the other existing user of .adjust_perf(), which is the amd-pstate driver (but the behavior of that driver is not changed). While at it, also update the cpufreq_driver_adjust_perf() documentation to reflect this change and some previous code changes that have not been taken into account in it. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Zhongqiu Han <zhongqiu.han@oss.qualcomm.com> Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org> Link: https://patch.msgid.link/6277654.lOV4Wx5bFT@rafael.j.wysocki [ rjw: Adjusted Rust function formatting ] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2026-07-14rust: devres: ensure revocation is complete before device finishes unbindingDanilo Krummrich1-0/+7
Now that the revocation Completion is in place, also address the symmetric case. When Devres::drop() wins the is_available swap and the devres callback loses, the callback returns to devres_release_all() without waiting. This means device unbinding can complete while Devres::drop() is still executing drop_in_place() on another CPU, which is a problem if T's destructor accesses device state. Make the synchronization bidirectional. Whichever side performs drop_in_place() signals the Completion, and the other side waits. This does not reintroduce the nested Devres deadlock fixed by commit ba268514ea14 ("rust: devres: fix race condition due to nesting"), because that deadlock was caused by drop waiting for the release callback to return (the old 'devm' Completion). Here, both sides only wait for drop_in_place() to finish, which completes within the current call chain. The Arc<Inner<T>> keeps the Inner allocation alive independently. Cc: stable@vger.kernel.org Fixes: ba268514ea14 ("rust: devres: fix race condition due to nesting") Reviewed-by: Gary Guo <gary@garyguo.net> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Link: https://patch.msgid.link/20260628200304.2365598-1-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-14rust: devres: fix race between concurrent revokersDanilo Krummrich1-2/+16
There is a potential race condition when two paths try to revoke a Devres concurrently. The driver core's devres_release_all() calls Revocable::revoke() via the release callback, while Devres::drop() calls revoke_nosync() on another CPU. The revoker that does not claim the is_available swap returns immediately, but the revoker that did may still be executing drop_in_place() on the inner data. This can cause a use-after-free when the other revoker's caller proceeds to drop adjacent resources that drop_in_place() still references (e.g., Devres<DmaMappedSgt> racing with SGTable freeing the backing sg_table and pages). Fix this by adding a Completion. The release callback signals the Completion after revoke() finishes, and Devres::drop() waits for it when it loses the is_available swap. This ensures the wrapped object is fully torn down before Devres::drop() returns. Cc: stable@vger.kernel.org Reported-by: Sashiko <sashiko-bot@kernel.org> Closes: https://lore.kernel.org/dri-devel/20260612202841.2577C1F000E9@smtp.kernel.org/ Fixes: 05aa6fb1c21d ("rust: scatterlist: Add abstraction for sg_table") Reviewed-by: Gary Guo <gary@garyguo.net> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Link: https://patch.msgid.link/20260628174451.2275679-1-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-14Merge tag 'rust-io-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core into drm-rust-nextDanilo Krummrich11-622/+1541
I/O type generalization and projection This series presents a major rework of I/O types, as a summary: - Make I/O regions typed. The existing untyped region still exists with a dynamically sized `Region` type. - Create I/O view types to represent subregion of a full I/O region mapped. A projection macro is added to allow safely create such subviews. - Split I/O traits, make I/O views play a central role, avoid duplicate monomorphization and less `unsafe` code. - Add a `SysMem` backend, and make `Coherent` implement `Io`. - Add copying methods (memcpy_{from,to}io and friends). This series generalize `Mmio` type from just an untyped region to typed representations (so `MmioRaw<T>` is `__iomem *T`). This allows us to remove the `IoKnownSize` trait; the information is sourced from just the pointer from the `KnownSize` trait instead. Building on top of that, `Mmio` and `ConfigSpace` have been converted to typed views of I/O regions rather than just a big chunk of untyped I/O memory. These changes made it possible to implement `Io` trait for `Coherent<T>`. Shared system memory, `SysMem` is also added to the series, given it similarity in implementation compared to `Coherent`. In fact, the series use `SysMem` to implement `Coherent`'s I/O methods. Built on these generalization, this series add `io_project!()`. `io_project!()` performs a safe way to project a bigger view to a small subviews, and some Nova code has been converted in this series to demonstrate cleanups possible with this addition. New `io_read!()`, `io_write!()` has been added that supersedes `dma_read!()`, `dma_write!()` macro. Although, they work for primitives only (to be exact, types that the backend is `IoCapable` of). One feature that was lost from the old `dma_read!()` and `dma_write!()` series was the ability to read/write a large structs. However, the semantics was unclear to begin with, as there was no guarantee about their atomicity even for structs that were small enough to fit in u32. Suggested-by: Danilo Krummrich <dakr@kernel.org> Link: https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/Generic.20I.2FO.20backends/near/571198078 This is a stable tag for other trees to merge. Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-13Merge patch series "rust: I/O type generalization and projection"Danilo Krummrich10-556/+1512
Gary Guo <gary@garyguo.net> says: This series presents a major rework of I/O types, as a summary: - Make I/O regions typed. The existing untyped region still exists with a dynamically sized `Region` type. - Create I/O view types to represent subregion of a full I/O region mapped. A projection macro is added to allow safely create such subviews. - Split I/O traits, make I/O views play a central role, avoid duplicate monomorphization and less `unsafe` code. - Add a `SysMem` backend, and make `Coherent` implement `Io`. - Add copying methods (memcpy_{from,to}io and friends). This series generalize `Mmio` type from just an untyped region to typed representations (so `MmioRaw<T>` is `__iomem *T`). This allows us to remove the `IoKnownSize` trait; the information is sourced from just the pointer from the `KnownSize` trait instead. Building on top of that, `Mmio` and `ConfigSpace` have been converted to typed views of I/O regions rather than just a big chunk of untyped I/O memory. These changes made it possible to implement `Io` trait for `Coherent<T>`. Shared system memory, `SysMem` is also added to the series, given it similarity in implementation compared to `Coherent`. In fact, the series use `SysMem` to implement `Coherent`'s I/O methods. Built on these generalization, this series add `io_project!()`. `io_project!()` performs a safe way to project a bigger view to a small subviews, and some Nova code has been converted in this series to demonstrate cleanups possible with this addition. New `io_read!()`, `io_write!()` has been added that supersedes `dma_read!()`, `dma_write!()` macro. Although, they work for primitives only (to be exact, types that the backend is `IoCapable` of). One feature that was lost from the old `dma_read!()` and `dma_write!()` series was the ability to read/write a large structs. However, the semantics was unclear to begin with, as there was no guarantee about their atomicity even for structs that were small enough to fit in u32. Suggested-by: Danilo Krummrich <dakr@kernel.org> Link: https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/Generic.20I.2FO.20backends/near/571198078 Link: https://patch.msgid.link/20260706-io_projection-v6-0-72cd5d055d54@garyguo.net Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-13Merge patch series "ForLt/CovariantForLt split, auxiliary closure API and DevresLt"Danilo Krummrich7-78/+317
Danilo Krummrich <dakr@kernel.org> says: The ForLt trait currently guarantees covariance, which allows safe lifetime shortening via cast_ref(). However, some types (e.g. those containing Mutex<&'bound T>) are invariant over their lifetime parameter and cannot safely use cast_ref(). This series splits ForLt into two traits: - ForLt: base trait for all lifetime-parameterized types, providing only the Of<'a> GAT. - CovariantForLt: unsafe subtrait that guarantees covariance, providing a safe cast_ref() method. For invariant types, a closure-based API (registration_data_with()) is added to the auxiliary subsystem. The closure's HRTB prevents the caller from choosing a concrete lifetime, which would be unsound for invariant types. On top of that, this series adds DevresLt<F: ForLt>, a thin wrapper around Devres<F::Of<'static>> that shortens the stored 'static lifetime back to the caller's borrow scope. DevresLt provides both closure-based access (access_with/try_access_with for ForLt types) and direct reference access (access/try_access for CovariantForLt types). Also implement ForLt and CovariantForLt for Bar, IoMem and ExclusiveIoMem, and update their into_devres() methods to return DevresLt. Provide convenience type aliases DevresBar, DevresIoMem and DevresExclusiveIoMem. Link: https://patch.msgid.link/20260626183630.2585057-1-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-13rust: drm: Fix typo in FEAT_RENDER documentationYounes Akhouayri1-1/+1
Correct the spelling of "privilege" in the DRIVER_RENDER Rustdoc. Signed-off-by: Younes Akhouayri <git@younes.io> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Link: https://patch.msgid.link/20260712-docs-drm-feat-render-rustdoc-typo-v1-1-c9df1cbbce4b@younes.io Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-13rust: drm: gpuvm: implement Send and Sync for GpuVaAlloc and GpuVmBoSami Tolvanen2-0/+17
Moving a GpuVaAlloc or GpuVmBo between threads currently forces drivers to write their own unsafe Send and Sync impls. Provide the markers in the abstraction instead. GpuVaAlloc wraps only uninitialised memory and exposes none of it. GpuVmBo hands out the driver data and GEM object by shared reference and drops them in its deferred put; the DriverGpuVm trait already guarantees both are Send + Sync, so both impls are unconditional. Signed-off-by: Sami Tolvanen <samitolvanen@google.com> Link: https://patch.msgid.link/20260611-gpuvm-sync-send-v4-2-6c7f4ab2778a@google.com Signed-off-by: Alice Ryhl <aliceryhl@google.com>
2026-07-13rust: drm: gpuvm: require Send + Sync for the driver's associated dataSami Tolvanen1-11/+15
DriverGpuVm permitted !Send/!Sync associated data on an abstraction whose handles are shared and dropped across threads: obtain() runs from many threads and the VA API performs deferred cross-thread drops. That is unsound. Require Send + Sync on the trait and its associated data so the GpuVm and UniqueRefGpuVm handle impls need no per-impl bounds. Fixes: 82b78182eacf ("rust: drm: add base GPUVM immediate mode abstraction") Signed-off-by: Sami Tolvanen <samitolvanen@google.com> Link: https://patch.msgid.link/20260611-gpuvm-sync-send-v4-1-6c7f4ab2778a@google.com Signed-off-by: Alice Ryhl <aliceryhl@google.com>
2026-07-13rust: drm: fix GEM object pointer safety docsYilin Chen1-1/+2
IntoGEMObject::from_raw() receives a pointer to struct drm_gem_object, not a pointer to Self. The previous documentation used Self even though the function argument is the embedded GEM object pointer. However, the pointer must not be any arbitrary valid drm_gem_object. The implementations recover Self with container_of(), so the GEM object must be embedded in a valid Self instance. This patch documents that requirement explicitly. Assisted-by: Codex:GPT-5 Signed-off-by: Yilin Chen <1479826151@qq.com> Link: https://patch.msgid.link/tencent_4426892E62B77DEA2AE898E899A871940005@qq.com Signed-off-by: Alice Ryhl <aliceryhl@google.com>
2026-07-12rust: drm: Pass registration data to ioctl handlersDanilo Krummrich1-2/+6
Pass registration data to ioctl handlers via drm::Device<Registered>::registration_data_with(). The closure's HRTB ties the lifetime to the closure scope, and the pointer cast shortens it from 'static internally. The reference is valid for the duration of the drm_dev_enter/exit critical section held by RegistrationGuard. Reviewed-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-19-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-12rust: drm: Add RegistrationData to drm::DriverDanilo Krummrich3-41/+99
Add a RegistrationData GAT (Generic Associated Type) to drm::Driver. The lifetime parameter is tied to the parent bus device binding scope. Registration<'a, T> takes ownership of the data via Pin<KBox<_>>, storing it with its real lifetime. The pointer is written to drm::Device before drm_dev_register() to ensure it is already in place when ioctls arrive. Device<T, Registered>::registration_data_with() provides access with the lifetime shortened from 'static via a pointer cast. Since Registration::drop() calls drm_dev_unplug(), which performs an SRCU barrier waiting for all drm_dev_enter() critical sections to complete, the data is guaranteed to remain valid for the duration of any RegistrationGuard. Reviewed-by: Lyude Paul <lyude@redhat.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-18-dakr@kernel.org [ Move registration_data_unchecked() to Device<T, Registered> impl block. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-12rust: drm: add AsRef<ParentDevice<Bound>> for Device<Registered>Danilo Krummrich1-0/+14
Implement AsRef<T::ParentDevice<Bound>> for Device<T, Registered>, providing access to the bound parent bus device for registered DRM devices. Since a Device<T, Registered> guarantees that the parent bus device is bound, the conversion to T::ParentDevice<Bound> is safe. Reviewed-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-16-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-12rust: drm: return ParentDevice from Device AsRefDanilo Krummrich3-5/+11
Change AsRef for drm::Device to return &T::ParentDevice<device::Normal> instead of &device::Device, and restrict it to the Normal context. Device<T, Registered> still gets this through Deref coercion. This provides access to the typed parent bus device rather than the raw base device. Reviewed-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-15-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-12rust: drm: Wrap ioctl dispatch in RegistrationGuardDanilo Krummrich1-3/+42
Make Ioctl handlers receive a &Device<T, Registered> reference, proving at the type level that the device is registered and its parent bus device is bound. This is achieved by calling registration_guard() on the Device<T, Ioctl> obtained in ioctl dispatch context. If the device has been unplugged, the ioctl returns -ENODEV without calling the handler. To resolve the driver type parameter T for type inference, which the compiler cannot propagate through method resolution and associated-type projections alone, a dead-code closure and a helper function are used as a type-inference anchor. Reviewed-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-14-dakr@kernel.org [ Use imperative mood in commit message; clarify __dev_ctx_cast() doc comment to reflect Ioctl-to-Registered cast. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-12rust: drm: Add RegistrationGuard for drm_dev_enter/exit critical sectionsDanilo Krummrich3-13/+83
DRM ioctls do not guarantee that the parent bus device is still bound. However, since DRM device registration is managed through Devres, using drm_dev_unplug() on unregistration ensures that between drm_dev_enter() and drm_dev_exit() the parent device must be bound. Add RegistrationGuard, a guard object representing a drm_dev_enter/exit SRCU critical section that dereferences to &Device<T, Registered>. The guard is obtained from Device<T, Ioctl> and proves at runtime that the device is still registered. Switch Registration::drop from drm_dev_unregister() to drm_dev_unplug() to provide the SRCU barrier that RegistrationGuard's safety argument relies on. Reviewed-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-13-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-12rust: drm: add Ioctl device context typestateDanilo Krummrich2-3/+32
Add the Ioctl DeviceContext for DRM devices that have been registered with userspace previously. A Device<T, Ioctl> has been registered at some point, but may be concurrently unregistering or already unregistered. drm_dev_enter() can guard against this, ensuring the device remains registered for the duration of the critical section. This typestate will be used in ioctl dispatch context where registration is guaranteed by the DRM core, and RegistrationGuard can safely be acquired. Reviewed-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-12-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-12rust: drm: pin ioctl Device reference to Normal contextDanilo Krummrich1-1/+2
Explicitly annotate the Device reference produced by from_raw() in the ioctl dispatch macro as Device<_, Normal>. Without this annotation, the context is inferred from the handler's first parameter type, which would allow a handler declaring &Device<T, Registered> to obtain a Registered reference without runtime proof via RegistrationGuard. Reviewed-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-11-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-12rust: drm: split Deref for Device context typestatesDanilo Krummrich1-1/+15
Split the Deref implementation for drm::Device by context: - Device<T> (Normal) dereferences to T::Data. - Device<T, Registered> dereferences to Device<T> (Normal). Reviewed-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-10-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-12rust: drm/gem: remove DeviceContext from shmem::ObjectDanilo Krummrich1-70/+51
Now that AlwaysRefCounted is restricted to the Normal GEM Object context, there is no use for instantiating Object<T, C> with a non-Normal context. Remove the DeviceContext generic parameter from shmem::Object and all associated types (VMap, VMapRef, VMapOwned, DmaResvGuard, SGTableMap), simplifying the API. Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Reviewed-by: Lyude Paul <lyude@redhat.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-9-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-12rust: drm: restrict AlwaysRefCounted to Normal GEM Object contextDanilo Krummrich4-123/+119
Restrict AlwaysRefCounted for gem::Object and gem::shmem::Object to the Normal context, since only Normal objects should be independently reference-counted. To avoid cascading through IntoGEMObject (which had AlwaysRefCounted as a supertrait), remove AlwaysRefCounted from IntoGEMObject's supertraits and instead add it as an explicit bound on lookup_handle(), which is the only BaseObject method that returns an ARef. Since Object::new() and shmem::Object::new() return ARef<Self>, move them to Normal-only impl blocks. Similarly, simplify ObjectConfig and shmem's parent_resv_obj field to the Normal context. Remove the DeviceContext generic from DriverObject::new() and Driver::Object, since GEM objects can only be constructed in the Normal context. Simplify DriverAllocImpl accordingly. Reviewed-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-8-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-12rust: drm: restrict AlwaysRefCounted to Normal Device contextDanilo Krummrich1-4/+5
Restrict the AlwaysRefCounted implementation for drm::Device to the Normal context. Registered devices represent a non-owning view of a device within a RegistrationGuard scope and must not be independently reference-counted. Reviewed-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-7-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-12rust: drm: change default DeviceContext to NormalDanilo Krummrich3-9/+10
Change the default DeviceContext from Registered to Normal for drm::Device, gem::Object, gem::shmem::Object and gem::shmem::ObjectConfig. Normal is the general-purpose, reference-counted context suitable for most uses; Registered represents a device that was registered with userspace and will become a non-owning context obtained through a RegistrationGuard. Update the create_handle/lookup_handle bounds from Object<Registered> to Object<Normal> to match the new default context of GEM objects, and update the driver device type aliases (NovaDevice, TyrDrmDevice) to default to Normal. Reviewed-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-6-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-12rust: drm: Add Driver::ParentDevice associated typeDanilo Krummrich3-4/+10
Add a ParentDevice associated type to the Driver trait, allowing each DRM driver to declare its parent bus device type (e.g. auxiliary::Device, platform::Device). Change UnregisteredDevice::new() to take &T::ParentDevice<Bound>, ensuring at the type level that the DRM device's parent matches the declared bus device type. Reviewed-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-5-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-12rust: faux: add Device type with AsBusDevice supportDanilo Krummrich2-14/+66
Add a faux::Device type that wraps struct faux_device and implements AsBusDevice, enabling faux devices to be used as parent devices for subsystems that require a bus device, such as DRM. Update Registration to return &faux::Device<Bound> via AsRef. Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-4-dakr@kernel.org [ Drop redundant 'struct device' invariant; implied by valid struct faux_device. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-12rust: drm: rename Uninit DeviceContext to NormalDanilo Krummrich2-66/+28
Rename the Uninit DeviceContext to Normal to better reflect its purpose as the general-purpose, reference-counted device context. The Uninit name was a leftover from when DRM device private data initialization was planned to split across UnregisteredDevice::new() and Registration::new(); with the subsequent introduction of RegistrationData, this distinction is no longer needed. This also simplifies the DeviceContext documentation, trimming the multi-stage initialization description that no longer applies. Subsequent patches will refine the semantics of the Registered context accordingly. No functional change. Reviewed-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-3-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-12rust: drm: ioctl: fix unbounded lifetimes in ioctl handler argumentsDanilo Krummrich1-0/+6
References to dev, data, and file in the declare_drm_ioctls! macro are created via unsafe pointer dereferences, producing unbounded lifetimes. If an ioctl handler explicitly annotates its parameters with 'static, the compiler accepts this, allowing the handler to stash references that outlive the ioctl call. Fix this by adding a higher-ranked function pointer coercion that enforces the handler accepts universally quantified lifetimes: let _: for<'a> fn(&'a _, &'a mut _, &'a _) -> _ = $func; Since the handler must be coercible to a function pointer accepting any lifetime 'a, it can no longer demand 'static on any parameter. Cc: stable@vger.kernel.org Fixes: 9a69570682b1 ("rust: drm: ioctl: Add DRM ioctl abstraction") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/all/20260620011346.A47D01F000E9@smtp.kernel.org/ Suggested-by: Gary Guo <gary@garyguo.net> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Reviewed-by: Lyude Paul <lyude@redhat.com> Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com> Link: https://patch.msgid.link/20260628145406.2107056-2-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-11rust: io: mem: return DevresLt from IoMem/ExclusiveIoMem::into_devres()Danilo Krummrich1-20/+45
Implement ForLt and CovariantForLt for IoMem<'static, SIZE> and ExclusiveIoMem<'static, SIZE> so that DevresLt can shorten the stored 'static lifetime back to the caller's borrow lifetime. CovariantForLt is sound because both types only hold &'a Device<Bound>, which is covariant over 'a. Since DevresLt::new() handles the lifetime transmutation internally, into_devres() no longer needs an explicit transmute to 'static. Add DevresIoMem<SIZE> and DevresExclusiveIoMem<SIZE> type aliases. Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Link: https://patch.msgid.link/20260626183630.2585057-8-dakr@kernel.org [ Add default SIZE parameter to DevresIoMem. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-11rust: pci: return DevresLt from Bar::into_devres()Danilo Krummrich2-11/+27
Implement ForLt and CovariantForLt for Bar<'static, SIZE> so that DevresLt can shorten the stored 'static lifetime back to the caller's borrow lifetime. CovariantForLt is sound because Bar<'a, SIZE> only holds &'a Device<Bound>, which is covariant over 'a. Since DevresLt::new() handles the lifetime transmutation internally, into_devres() no longer needs an explicit transmute to Bar<'static>. Add a DevresBar<SIZE> type alias for convenience. Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Link: https://patch.msgid.link/20260626183630.2585057-7-dakr@kernel.org [ Add default SIZE parameter to DevresBar. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-11rust: devres: add DevresLt for ForLt-aware device resource accessDanilo Krummrich1-0/+106
Devres<T> stores resources as T and returns &'a T from access(). For lifetime-parameterized types like Bar<'a, SIZE> that are transmuted to 'static for storage, this exposes the synthetic 'static lifetime to callers -- any method on the stored type that returns a reference with its lifetime parameter would yield a &'static reference, which is unsound. Add DevresLt<F: ForLt>, a thin wrapper around Devres<F::Of<'static>> that shortens the stored 'static lifetime to the caller's borrow lifetime in all access methods. DevresLt::new() is unsafe because the caller must guarantee that the data remains valid for the device's full bound scope; the internal transmute from F::Of<'a> to F::Of<'static> would otherwise allow use-after-free. Two access patterns are provided: - CovariantForLt types get direct-reference accessors (access, try_access) that return shortened references via CovariantForLt::cast_ref. - Plain ForLt types use closure-based accessors (access_with, try_access_with) whose universally quantified lifetime prevents callers from smuggling in concrete short-lived references. Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Link: https://patch.msgid.link/20260626183630.2585057-6-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-11rust: auxiliary: add registration_data_with() for ForLt typesDanilo Krummrich1-26/+67
Add registration_data_with() taking a for<'a> closure that receives Pin<&'a F::Of<'a>>, which works with any ForLt type. Taking a for<'a> closure rather than returning a direct reference prevents callers from choosing a concrete lifetime for the data, which is required for soundness with non-covariant ForLt types. Extract the common null-check, TypeId-check and KBox-borrow logic into a private registration_data_pinned() helper shared by both registration_data_with() and the existing registration_data(). Relax Registration's bound from CovariantForLt to ForLt so that non-covariant types can be registered. Reviewed-by: Gary Guo <gary@garyguo.net> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Link: https://patch.msgid.link/20260626183630.2585057-4-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-11rust: types: introduce ForLt base trait for CovariantForLtDanilo Krummrich2-18/+67
Add a new ForLt trait as a base for CovariantForLt: - ForLt (non-unsafe): represents a type generic over a lifetime, with no covariance guarantee. - CovariantForLt (unsafe): becomes a subtrait of ForLt that additionally proves the type is covariant over its lifetime parameter, providing a safe cast_ref() method. This split allows non-covariant types (e.g. types behind a Mutex) to implement ForLt and participate in DevresLt / registration data patterns that use HRTB closures for sound access, without requiring a covariance proof that would fail to compile. Both macros share the UnsafeForLtImpl helper type, distinguished by a const generic N: ForLt! emits N = 0 (no covariance proof), CovariantForLt! emits N = 1 (with compile-time covariance proof). Reviewed-by: Gary Guo <gary@garyguo.net> Acked-by: Miguel Ojeda <ojeda@kernel.org> Link: https://patch.msgid.link/20260626183630.2585057-3-dakr@kernel.org [ Merge ForLt pub use, inline resolve_hrt/ty_static, add intra-doc links. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-11rust: types: rename ForLt to CovariantForLtDanilo Krummrich3-40/+42
Rename ForLt to CovariantForLt to prepare for the introduction of a new ForLt base trait that does not require covariance. The existing ForLt trait requires covariance, which enables the safe cast_ref() method. This rename preserves the same semantics under a more precise name, making room for a weaker ForLt trait in a subsequent commit. No functional change. Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Reviewed-by: Gary Guo <gary@garyguo.net> Acked-by: Miguel Ojeda <ojeda@kernel.org> Link: https://patch.msgid.link/20260626183630.2585057-2-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
2026-07-11rust: io: implement `IoSysMap`Gary Guo1-0/+137
Add an enum as sum type for `Mmio` and `SysMem`. This serves similar purpose of `iosys_map`. Thanks to Rust's type system, all of projection and struct read/write can be handled by the generic I/O projection mechanism (i.e. `io_project!`, `io_read!, `io_write!`) for free, and there is no need to provide things like `iosys_map_rd_field` or `iosys_map_wr_field`. An enum type also makes it very easy to construct or destruct. This could be made more generic by implementing on a general purpose sum type like `Either`; however this is kept specific unless a need arises that warrants this to be generic over other I/O backends. Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Signed-off-by: Gary Guo <gary@garyguo.net> Link: https://patch.msgid.link/20260706-io_projection-v6-20-72cd5d055d54@garyguo.net Signed-off-by: Danilo Krummrich <dakr@kernel.org>