The current EVENTFD manager matches subscriptions only by event identifier. That is enough for coarse notification, but it does not support the queue-scoped routing needed by queue completion style events.
Extend the subscription key from a single event identifier to the pair (event_type, queue_id). For device/GPU-scoped events, queue_id is 0. For queue-scoped events, queue_id selects the queue-specific subscription. EVENTFD remains notification-only. Also fix the queue-aware bind path to compute the packed subscription key before lookup/insert, and allow fd 0 by rejecting only negative file descriptors. This change keeps the existing manager design and binding model intact, while making queue_id meaningful for queue-scoped wakeups. Cc: Alex Deucher <[email protected]> Cc: Christian König <[email protected]> Signed-off-by: Srinivasan Shanmugam <[email protected]> --- drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.c | 92 ++++++++++++++------- drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.h | 16 +++- drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 20 +++-- 3 files changed, 86 insertions(+), 42 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.c index 0b0c9268aedc..fefc85ca916e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.c @@ -36,25 +36,39 @@ #include <linux/slab.h> #include <linux/err.h> +#include <drm/amdgpu_drm.h> #include "amdgpu_eventfd.h" #define AMDGPU_EVENTFD_MAX_BINDS 4096 +static bool amdgpu_eventfd_valid_type(u32 event_type) +{ + switch (event_type) { + case DRM_AMDGPU_EVENT_TYPE_USERQ_EOP: + case DRM_AMDGPU_EVENT_TYPE_QUEUE_RESET: + case DRM_AMDGPU_EVENT_TYPE_MEMORY_EXCEPTION: + case DRM_AMDGPU_EVENT_TYPE_SCRATCH: + return true; + default: + return false; + } +} + /** * amdgpu_eventfd_id_alloc - allocate an event id container - * @event_id: userspace-defined event identifier + * @key: packed (event_type, queue_id) subscription key * - * Each event_id represents a notification category. Multiple eventfds can - * be bound to the same event_id. + * Each key represents one subscription category. Multiple eventfds can + * be bound to the same key. * * This function allocates the container which stores the list of eventfds - * associated with that event_id. + * associated with that subscription key. * * Return: * Pointer to the newly allocated structure or NULL on failure. */ -static struct amdgpu_eventfd_id *amdgpu_eventfd_id_alloc(u32 event_id) +static struct amdgpu_eventfd_id *amdgpu_eventfd_id_alloc(u64 key) { struct amdgpu_eventfd_id *id; @@ -62,7 +76,7 @@ static struct amdgpu_eventfd_id *amdgpu_eventfd_id_alloc(u32 event_id) if (!id) return NULL; - id->event_id = event_id; + id->key = key; INIT_HLIST_HEAD(&id->entries); id->n_entries = 0; return id; @@ -71,9 +85,9 @@ static struct amdgpu_eventfd_id *amdgpu_eventfd_id_alloc(u32 event_id) /** * amdgpu_eventfd_id_get_or_create - find or create an event_id entry * @mgr: eventfd manager - * @event_id: event identifier + * @key: packed (event_type, queue_id) subscription key * - * This helper returns the container associated with the given event_id. + * This helper returns the container associated with the given key. * If it does not exist, it will create one. * * The function is designed to be callable without holding any locks. @@ -84,7 +98,7 @@ static struct amdgpu_eventfd_id *amdgpu_eventfd_id_alloc(u32 event_id) * Pointer to the event_id structure or NULL on failure. */ static struct amdgpu_eventfd_id * -amdgpu_eventfd_id_get_or_create(struct amdgpu_eventfd_mgr *mgr, u32 event_id) +amdgpu_eventfd_id_get_or_create(struct amdgpu_eventfd_mgr *mgr, u64 key) { struct amdgpu_eventfd_id *id; struct amdgpu_eventfd_id *new_id; @@ -92,19 +106,19 @@ amdgpu_eventfd_id_get_or_create(struct amdgpu_eventfd_mgr *mgr, u32 event_id) int r; xa_lock_irqsave(&mgr->ids, flags); - id = xa_load(&mgr->ids, event_id); + id = xa_load(&mgr->ids, key); xa_unlock_irqrestore(&mgr->ids, flags); if (id) return id; - new_id = amdgpu_eventfd_id_alloc(event_id); + new_id = amdgpu_eventfd_id_alloc(key); if (!new_id) return NULL; xa_lock_irqsave(&mgr->ids, flags); /* Re-check after taking the lock in case another thread inserted it. */ - id = xa_load(&mgr->ids, event_id); + id = xa_load(&mgr->ids, key); if (id) { xa_unlock_irqrestore(&mgr->ids, flags); kfree(new_id); @@ -115,9 +129,9 @@ amdgpu_eventfd_id_get_or_create(struct amdgpu_eventfd_mgr *mgr, u32 event_id) * xa_insert() returns -EBUSY if an entry already exists. * Since we are in irqsave context here, use GFP_ATOMIC. */ - r = xa_insert(&mgr->ids, event_id, new_id, GFP_ATOMIC); + r = xa_insert(&mgr->ids, key, new_id, GFP_ATOMIC); if (r == -EBUSY) - id = xa_load(&mgr->ids, event_id); + id = xa_load(&mgr->ids, key); xa_unlock_irqrestore(&mgr->ids, flags); @@ -190,13 +204,14 @@ void amdgpu_eventfd_mgr_fini(struct amdgpu_eventfd_mgr *mgr) } /** - * amdgpu_eventfd_bind - bind eventfd to an event_id + * amdgpu_eventfd_bind - bind eventfd to an EVENTFD subscription * @mgr: eventfd manager - * @event_id: userspace event identifier + * @event_type: kernel-defined event type + * @queue_id: queue identifier, or 0 for device/GPU-scoped events * @eventfd: eventfd file descriptor * * This function allows userspace to subscribe to notifications for a - * specific event_id. + * specific (event_type, queue_id) pair. * * Multiple eventfds can be bound to the same event_id. * @@ -206,17 +221,21 @@ void amdgpu_eventfd_mgr_fini(struct amdgpu_eventfd_mgr *mgr) * Return: * 0 on success, negative error code on failure. */ -int amdgpu_eventfd_bind(struct amdgpu_eventfd_mgr *mgr, u32 event_id, int eventfd) +int amdgpu_eventfd_bind(struct amdgpu_eventfd_mgr *mgr, u32 event_type, + u32 queue_id, int eventfd) { struct amdgpu_eventfd_id *id; struct amdgpu_eventfd_entry *e, *it; struct eventfd_ctx *ctx; + u64 key; unsigned long flags; bool dup = false; - if (!mgr || !event_id || eventfd < 0) + if (!mgr || !eventfd || eventfd < 0 || !amdgpu_eventfd_valid_type(event_type)) return -EINVAL; + key = amdgpu_eventfd_key(event_type, queue_id); + /* * Enforce total bind limit without a separate manager lock. * For duplicate binds, we decrement back before returning success. @@ -232,7 +251,7 @@ int amdgpu_eventfd_bind(struct amdgpu_eventfd_mgr *mgr, u32 event_id, int eventf return PTR_ERR(ctx); } - id = amdgpu_eventfd_id_get_or_create(mgr, event_id); + id = amdgpu_eventfd_id_get_or_create(mgr, key); if (!id) { eventfd_ctx_put(ctx); atomic_dec(&mgr->bind_count); @@ -294,9 +313,10 @@ int amdgpu_eventfd_bind(struct amdgpu_eventfd_mgr *mgr, u32 event_id, int eventf } /** - * amdgpu_eventfd_unbind - remove eventfd binding + * amdgpu_eventfd_unbind - remove EVENTFD binding * @mgr: eventfd manager - * @event_id: event identifier + * @event_type: kernel-defined event type + * @queue_id: queue identifier, or 0 for device/GPU-scoped events * @eventfd: eventfd file descriptor * * Removes an existing binding between an event_id and an eventfd. @@ -304,25 +324,29 @@ int amdgpu_eventfd_bind(struct amdgpu_eventfd_mgr *mgr, u32 event_id, int eventf * Return: * 0 if removed, -ENOENT if binding does not exist. */ -int amdgpu_eventfd_unbind(struct amdgpu_eventfd_mgr *mgr, u32 event_id, int eventfd) +int amdgpu_eventfd_unbind(struct amdgpu_eventfd_mgr *mgr, u32 event_type, + u32 queue_id, int eventfd) { struct amdgpu_eventfd_id *id; + u64 key; struct amdgpu_eventfd_entry *e; struct hlist_node *tmp; struct eventfd_ctx *ctx; unsigned long flags; bool removed = false; - if (!mgr || !event_id || eventfd < 0) + if (!mgr || eventfd < 0 || !amdgpu_eventfd_valid_type(event_type)) return -EINVAL; + key = amdgpu_eventfd_key(event_type, queue_id); + ctx = eventfd_ctx_fdget(eventfd); if (IS_ERR(ctx)) return PTR_ERR(ctx); xa_lock_irqsave(&mgr->ids, flags); - id = xa_load(&mgr->ids, event_id); + id = xa_load(&mgr->ids, key); if (!id) goto out_unlock; @@ -338,7 +362,7 @@ int amdgpu_eventfd_unbind(struct amdgpu_eventfd_mgr *mgr, u32 event_id, int even atomic_dec(&mgr->bind_count); if (!id->n_entries) { - __xa_erase(&mgr->ids, event_id); + __xa_erase(&mgr->ids, key); kfree(id); } break; @@ -353,31 +377,37 @@ int amdgpu_eventfd_unbind(struct amdgpu_eventfd_mgr *mgr, u32 event_id, int even } /** - * amdgpu_eventfd_signal - notify all eventfds bound to event_id + * amdgpu_eventfd_signal - notify all eventfds bound to subscription key * @mgr: eventfd manager * @event_id: event identifier + * @event_type: kernel-defined event type + * @queue_id: queue identifier, or 0 for device/GPU-scoped events * * This function is typically called from interrupt context. * - * All eventfds registered for the given event_id will be signaled. + * All eventfds registered for the given subscription will be signaled. * Userspace processes waiting on those eventfds will wake up. */ -void amdgpu_eventfd_signal(struct amdgpu_eventfd_mgr *mgr, u32 event_id) +void amdgpu_eventfd_signal(struct amdgpu_eventfd_mgr *mgr, u32 event_type, + u32 queue_id) { struct amdgpu_eventfd_id *id; struct amdgpu_eventfd_entry *e; + u64 key; unsigned long flags; - if (!mgr || !event_id) + if (!mgr || !amdgpu_eventfd_valid_type(event_type)) return; + key = amdgpu_eventfd_key(event_type, queue_id); + /* * IRQ-safe signaling path: keep xarray lock held while iterating and * signaling. eventfd_signal() is IRQ-safe. */ xa_lock_irqsave(&mgr->ids, flags); - id = xa_load(&mgr->ids, event_id); + id = xa_load(&mgr->ids, key); if (id) { hlist_for_each_entry(e, &id->entries, hnode) eventfd_signal(e->ctx); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.h index 248afb1f2f14..6e7eb513fbc5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.h @@ -38,7 +38,7 @@ struct amdgpu_eventfd_entry { }; struct amdgpu_eventfd_id { - u32 event_id; + u64 key; struct hlist_head entries; u32 n_entries; }; @@ -48,12 +48,20 @@ struct amdgpu_eventfd_mgr { atomic_t bind_count; /* total binds across all event_ids */ }; +static inline u64 amdgpu_eventfd_key(u32 event_type, u32 queue_id) +{ + return ((u64)event_type << 32) | queue_id; +} + void amdgpu_eventfd_mgr_init(struct amdgpu_eventfd_mgr *mgr); void amdgpu_eventfd_mgr_fini(struct amdgpu_eventfd_mgr *mgr); -int amdgpu_eventfd_bind(struct amdgpu_eventfd_mgr *mgr, u32 event_id, int eventfd); -int amdgpu_eventfd_unbind(struct amdgpu_eventfd_mgr *mgr, u32 event_id, int eventfd); +int amdgpu_eventfd_bind(struct amdgpu_eventfd_mgr *mgr, u32 event_type, + u32 queue_id, int eventfd); +int amdgpu_eventfd_unbind(struct amdgpu_eventfd_mgr *mgr, u32 event_type, + u32 queue_id, int eventfd); -void amdgpu_eventfd_signal(struct amdgpu_eventfd_mgr *mgr, u32 event_id); +void amdgpu_eventfd_signal(struct amdgpu_eventfd_mgr *mgr, u32 event_type, + u32 queue_id); #endif /* __AMDGPU_EVENTFD_H__ */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 75fc3a74db28..22dfd22210c4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -646,21 +646,27 @@ int amdgpu_eventfd_ioctl(struct drm_device *dev, void *data, if (args->flags || !args->event_type || args->eventfd < 0) return -EINVAL; - /* - * queue_id is reserved for future queue-specific subscriptions. - * Keep it zero for now. - */ - if (args->queue_id) + switch (args->event_type) { + case DRM_AMDGPU_EVENT_TYPE_USERQ_EOP: + case DRM_AMDGPU_EVENT_TYPE_QUEUE_RESET: + case DRM_AMDGPU_EVENT_TYPE_SCRATCH: + break; + case DRM_AMDGPU_EVENT_TYPE_MEMORY_EXCEPTION: + if (args->queue_id) + return -EINVAL; + break; + default: return -EINVAL; + } switch (args->op) { case DRM_AMDGPU_EVENTFD_OP_BIND: return amdgpu_eventfd_bind(&fpriv->eventfd_mgr, - args->event_type, + args->event_type, args->queue_id, args->eventfd); case DRM_AMDGPU_EVENTFD_OP_UNBIND: return amdgpu_eventfd_unbind(&fpriv->eventfd_mgr, - args->event_type, + args->event_type, args->queue_id, args->eventfd); default: return -EINVAL; -- 2.34.1
