diff options
| author | Philipp Stanner <phasta@kernel.org> | 2026-09-05 10:53:42 +0200 |
|---|---|---|
| committer | Danilo Krummrich <dakr@kernel.org> | 2026-09-07 19:43:43 +0200 |
| commit | 3282d591601949e2970b73024a00e54f0f6fdb05 (patch) | |
| tree | 937acb73c6ee75f09e2f844699493e4b234d7440 /rust/helpers | |
| parent | e6a2c988ed96a5a3af51ed97ecba980521bf2fc0 (diff) | |
rust: Add dma_fence abstractions
DMA fences are synchronisation primitives that will be needed by all
Rust GPU drivers.
The dma_fence framework sets a number of rules, notably:
- fences must only be signaled once
- all fences must be signaled at some point
- fence error codes must only be set before signaling
- every pointer to a fence must be backed by a reference
All those rules are being addressed by these abstractions.
To cleanly decouple fence issuers and consumers, two types are provided:
- DriverFence: the only fence type that can be signaled and that
carries driver-specific data.
- Fence: the fence type to be shared with other drivers and / or
userspace. The only type callbacks can be registered on.
Cannot be signaled.
Hereby, a Fence lives in the same chunk of memory as a DriverFence. Both
share the refcount of the underlying C dma_fence. Since this
implementation does not provide a custom dma_fence_backend_ops.release()
function, the memory is freed by the dma_fence backend once the refcount
drops to 0.
To create a DriverFence, the user must first allocate a
DriverFenceAllocation, so that the creation of the DriverFence later on
can always succeed. Otherwise, deadlocks could occur if fences need to
be created in a GPU job submission path.
Synchronization is ensured by the dma_fence backend.
All DriverFence's created through this abstraction must be signaled by
the creator with an error code. In case a DriverFence drops without
being signaled beforehand, it is signaled with -ECANCELLED as its
error and a warning is printed. This allows the Rust abstraction to very
cleanly decouple fence issuer and consumer by relying on the decoupling
mechanisms in the C backend, which ensures through RCU and the
'signaled' fence-flag that dma_fence_backend_ops functions cannot
access the potentially unloaded driver code anymore.
Signalling fences on drop thus grants many advantages. Not signaling
fences on drop would risk deadlock and does not grant real advantages:
By definition only the drivers can ensure that a fence always represents
the hardware's state correctly.
This implementation models a DmaFenceContext object on which fences are
to be created, thereby ensuring correct sequence numbering according to
the timeline.
dma_fence supports a variety of callbacks. The mandatory callbacks
(get_timeline_name() and get_driver_name()) are implemented in this
patch. For convenience, they store those name parameters in the fence
context, saving the driver from implementing these two callbacks.
Support for other callbacks (like for hardware signaling) is prepared
for through the fact that both DriverFence and Fence live in the same
allocation, allowing for usage of container_of from the callback to
access the driver-specific data.
It is expected that other callbacks, added in the future, also mostly
operate on the generic data in the FenceContext. To make this safe, the
implementation ensures through a lifetime that a DriverFence cannot
outlive its FenceContext.
Synchronization for dma_fence_ops callbacks is ensured by only running the
Rust deconstructor delayed with call_rcu(), which prevents UAF-bugs
should a DriverFence drop while a Fence callback is currently operating
on the associated driver data. Since they can also operate on the
FenceContext's data, its drop implementation also performs the necessary
delay with rcu_barrier().
An additional issue discovered during the review process of this code is
that there is (currently) no mechanism in Rust to prevent someone from
circumventing the DriverFence's FenceContext-reference's lifetime by
"forgetting" the fence, e.g. with core::mem::forget(). This would enable
UAF bugs on the FenceContext. Throw a panic if this happens and document
a path towards a more robust solution.
Add abstractions for dma_fence in Rust.
Signed-off-by: Philipp Stanner <phasta@kernel.org>
Tested-by: Daniel Almeida <daniel.almeida@collabora.com>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Link: https://patch.msgid.link/20260905085343.1827305-3-phasta@kernel.org
[ In dma_fence_callback(), split combined unsafe block into separate
blocks for container_of!() and pointer dereference. - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Diffstat (limited to 'rust/helpers')
| -rw-r--r-- | rust/helpers/dma_fence.c | 49 | ||||
| -rw-r--r-- | rust/helpers/helpers.c | 1 |
2 files changed, 50 insertions, 0 deletions
diff --git a/rust/helpers/dma_fence.c b/rust/helpers/dma_fence.c new file mode 100644 index 000000000000..549f6b6a7171 --- /dev/null +++ b/rust/helpers/dma_fence.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/dma-fence.h> + +__rust_helper void rust_helper_dma_fence_get(struct dma_fence *f) +{ + dma_fence_get(f); +} + +__rust_helper void rust_helper_dma_fence_put(struct dma_fence *f) +{ + dma_fence_put(f); +} + +__rust_helper bool rust_helper_dma_fence_begin_signalling(void) +{ + return dma_fence_begin_signalling(); +} + +__rust_helper void rust_helper_dma_fence_end_signalling(bool cookie) +{ + dma_fence_end_signalling(cookie); +} + +__rust_helper bool rust_helper_dma_fence_is_signaled(struct dma_fence *f) +{ + return dma_fence_is_signaled(f); +} + +__rust_helper bool rust_helper_dma_fence_test_signaled_flag(struct dma_fence *f) +{ + return dma_fence_test_signaled_flag(f); +} + +__rust_helper void rust_helper_dma_fence_lock_irqsave(struct dma_fence *f, unsigned long *flags) +{ + dma_fence_lock_irqsave(f, *flags); +} + +__rust_helper void rust_helper_dma_fence_unlock_irqrestore(struct dma_fence *f, + unsigned long *flags) +{ + dma_fence_unlock_irqrestore(f, *flags); +} + +__rust_helper void rust_helper_dma_fence_set_error(struct dma_fence *f, int error) +{ + dma_fence_set_error(f, error); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 440fb7638e3c..609689fba3dd 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -57,6 +57,7 @@ #include "cred.c" #include "device.c" #include "dma.c" +#include "dma_fence.c" #ifdef CONFIG_DMA_SHARED_BUFFER #include "dma-resv.c" #endif |
