The current EVENTFD UAPI uses a userspace-defined event identifier. For render-node event notifications, userspace should subscribe to kernel-defined event types instead, allowing the kernel to define event semantics and signaling sources.
Add a shared AMDGPU EVENTFD event type enum and update the EVENTFD UAPI to use event_type instead of event_id. Value 0 is reserved and rejected by the existing event_type validation. queue_id remains part of the UAPI and is used to distinguish queue-scoped subscriptions from GPU/device-scoped subscriptions. Eventfd signaling remains notification-only. v10: (per Christian) - Add a comment clarifying that the flags field is currently unused, reserved for future UAPI extensions, and must be zero. - Introduce event_type directly in the EVENTFD UAPI. - Remove the INVALID event type; value 0 remains rejected by validation. - Squash the event_id to event_type rename into the original UAPI patch. v11: (per Christian) - Keep the event_id to event_type rename in this patch so that later patches focus only on queue reference handling. - Move queue reference conversion and queue-scoped EVENTFD handling into the subsequent patch to reduce noise in this change. Cc: Alex Deucher <[email protected]> Cc: Christian König <[email protected]> Signed-off-by: Srinivasan Shanmugam <[email protected]> --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 5 + drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.c | 107 +++++++++----------- drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.h | 17 ++-- drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 37 +++++++ include/uapi/drm/amdgpu_drm.h | 43 ++++++++ 6 files changed, 145 insertions(+), 65 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index a75c68195df9..54bc31ee795a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -460,6 +460,11 @@ struct amdgpu_fpriv { struct amdgpu_eventfd_mgr eventfd_mgr; }; +struct drm_device; +struct drm_file; + +int amdgpu_eventfd_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); + int amdgpu_file_to_fpriv(struct file *filp, struct amdgpu_fpriv **fpriv); /* diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 1781c0c3d010..aaa4dd57099f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -3076,6 +3076,7 @@ const struct drm_ioctl_desc amdgpu_ioctls_kms[] = { DRM_IOCTL_DEF_DRV(AMDGPU_USERQ_SIGNAL, amdgpu_userq_signal_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_USERQ_WAIT, amdgpu_userq_wait_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_GEM_LIST_HANDLES, amdgpu_gem_list_handles_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(AMDGPU_EVENTFD, amdgpu_eventfd_ioctl, DRM_RENDER_ALLOW), }; static const struct drm_driver amdgpu_kms_driver = { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.c index 4955ee942a1c..c777d563f1b5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.c @@ -24,14 +24,10 @@ /* * Render-node eventfd subscription infrastructure. * - * This module provides a simple event notification mechanism for render-node - * clients using Linux eventfd objects. + * EVENTFD is notification-only. It wakes userspace when a GPU event happens. + * Event metadata/details are expected to be consumed separately through the + * corresponding wait/event path. * - * Userspace can bind an eventfd to a userspace-defined event_id. When the - * driver signals that event_id, all eventfds bound to it are notified. - * - * This mechanism is intended to support lightweight GPU event notifications - * without polling from userspace. */ #include <linux/slab.h> @@ -42,19 +38,19 @@ #define AMDGPU_EVENTFD_MAX_BINDS 4096 /** - * amdgpu_eventfd_id_alloc - allocate an event id container - * @event_id: userspace-defined event identifier + * amdgpu_eventfd_id_alloc - allocate an event type container + * @event_type: kernel-defined AMDGPU event type * - * Each event_id represents a notification category. Multiple eventfds can - * be bound to the same event_id. + * Each event type represents a notification category. Multiple eventfds + * can be bound to the same event type. * * This function allocates the container which stores the list of eventfds - * associated with that event_id. + * associated with that event type. * * 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(u32 event_type) { struct amdgpu_eventfd_id *id; @@ -62,43 +58,41 @@ static struct amdgpu_eventfd_id *amdgpu_eventfd_id_alloc(u32 event_id) if (!id) return NULL; - id->event_id = event_id; + id->event_type = event_type; INIT_HLIST_HEAD(&id->entries); id->n_entries = 0; + return id; } /** - * amdgpu_eventfd_id_get_or_create - find or create an event_id entry + * amdgpu_eventfd_id_get_or_create - lookup or create an event type * @mgr: eventfd manager - * @event_id: event identifier - * - * This helper returns the container associated with the given event_id. - * If it does not exist, it will create one. + * @event_type: kernel-defined AMDGPU event type * - * The function is designed to be callable without holding any locks. - * Memory allocation is done outside the xarray lock to avoid blocking - * inside critical sections. + * Find the container associated with the specified event type. If none + * exists, allocate and insert a new one. * * Return: - * Pointer to the event_id structure or NULL on failure. + * Existing event type container or a newly allocated one. */ 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, + u32 event_type) { struct amdgpu_eventfd_id *id; struct amdgpu_eventfd_id *new_id; - XA_STATE(xas, &mgr->ids, event_id); + XA_STATE(xas, &mgr->ids, event_type); unsigned long flags; int r; xa_lock_irqsave(&mgr->ids, flags); - id = xa_load(&mgr->ids, event_id); + id = xa_load(&mgr->ids, event_type); xa_unlock_irqrestore(&mgr->ids, flags); if (id) return id; - new_id = amdgpu_eventfd_id_alloc(event_id); + new_id = amdgpu_eventfd_id_alloc(event_type); if (!new_id) return NULL; @@ -140,7 +134,7 @@ amdgpu_eventfd_id_get_or_create(struct amdgpu_eventfd_mgr *mgr, u32 event_id) * * event_id -> list of eventfds * - * The xarray provides efficient lookup of event_id containers. + * The xarray provides efficient lookup of event type containers. */ void amdgpu_eventfd_mgr_init(struct amdgpu_eventfd_mgr *mgr) { @@ -186,23 +180,19 @@ void amdgpu_eventfd_mgr_fini(struct amdgpu_eventfd_mgr *mgr) } /** - * amdgpu_eventfd_bind - bind eventfd to an event_id + * amdgpu_eventfd_bind - bind an eventfd to an event type * @mgr: eventfd manager - * @event_id: userspace event identifier + * @event_type: kernel-defined AMDGPU event type * @eventfd: eventfd file descriptor * - * This function allows userspace to subscribe to notifications for a - * specific event_id. - * - * Multiple eventfds can be bound to the same event_id. - * - * Duplicate bindings of the same eventfd are treated as success and do - * not create additional entries. + * Register an eventfd subscription for the specified event type. + * Duplicate subscriptions are treated as success. * * Return: - * 0 on success, negative error code on failure. + * 0 on success or a negative error code. */ -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, int eventfd) { struct amdgpu_eventfd_id *id; struct amdgpu_eventfd_entry *e = NULL, *it; @@ -211,7 +201,7 @@ int amdgpu_eventfd_bind(struct amdgpu_eventfd_mgr *mgr, u32 event_id, int eventf bool dup = false; int r = 0; /* duplicate binds are treated as success */ - if (!mgr || !event_id || eventfd < 0) + if (!mgr || !event_type || eventfd < 0) return -EINVAL; /* @@ -229,7 +219,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, event_type); if (!id) { r = -ENOMEM; goto out_put_ctx; @@ -259,7 +249,7 @@ int amdgpu_eventfd_bind(struct amdgpu_eventfd_mgr *mgr, u32 event_id, int eventf /* * Re-check duplicate under lock to close the race with another bind() - * for the same (event_id, ctx). + * for the same event_type and eventfd context. */ xa_lock_irqsave(&mgr->ids, flags); hlist_for_each_entry(it, &id->entries, hnode) { @@ -291,17 +281,18 @@ int amdgpu_eventfd_bind(struct amdgpu_eventfd_mgr *mgr, u32 event_id, int eventf } /** - * amdgpu_eventfd_unbind - remove eventfd binding + * amdgpu_eventfd_unbind - remove an eventfd subscription * @mgr: eventfd manager - * @event_id: event identifier + * @event_type: kernel-defined AMDGPU event type * @eventfd: eventfd file descriptor * - * Removes an existing binding between an event_id and an eventfd. + * Remove an existing subscription for the specified event type. * * Return: - * 0 if removed, -ENOENT if binding does not exist. + * 0 on success or a negative error code. */ -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, int eventfd) { struct amdgpu_eventfd_id *id; struct amdgpu_eventfd_entry *e; @@ -310,7 +301,7 @@ int amdgpu_eventfd_unbind(struct amdgpu_eventfd_mgr *mgr, u32 event_id, int even unsigned long flags; bool removed = false; - if (!mgr || !event_id || eventfd < 0) + if (!mgr || !event_type || eventfd < 0) return -EINVAL; ctx = eventfd_ctx_fdget(eventfd); @@ -319,7 +310,7 @@ int amdgpu_eventfd_unbind(struct amdgpu_eventfd_mgr *mgr, u32 event_id, int even xa_lock_irqsave(&mgr->ids, flags); - id = xa_load(&mgr->ids, event_id); + id = xa_load(&mgr->ids, event_type); if (!id) goto out_unlock; @@ -337,7 +328,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, event_type); kfree(id); } @@ -352,22 +343,22 @@ 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 - signal all eventfds bound to an event type * @mgr: eventfd manager - * @event_id: event identifier + * @event_type: kernel-defined AMDGPU event type * - * This function is typically called from interrupt context. + * Notify all subscribers associated with the specified event type. * - * All eventfds registered for the given event_id will be signaled. - * Userspace processes waiting on those eventfds will wake up. + * Signaling is notification-only and does not carry additional payload. */ -void amdgpu_eventfd_signal(struct amdgpu_eventfd_mgr *mgr, u32 event_id) +void amdgpu_eventfd_signal(struct amdgpu_eventfd_mgr *mgr, + u32 event_type) { struct amdgpu_eventfd_id *id; struct amdgpu_eventfd_entry *e; unsigned long flags; - if (!mgr || !event_id) + if (!mgr || !event_type) return; /* @@ -376,7 +367,7 @@ void amdgpu_eventfd_signal(struct amdgpu_eventfd_mgr *mgr, u32 event_id) */ xa_lock_irqsave(&mgr->ids, flags); - id = xa_load(&mgr->ids, event_id); + id = xa_load(&mgr->ids, event_type); 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..e13b86240afc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_eventfd.h @@ -28,9 +28,9 @@ #ifndef __AMDGPU_EVENTFD_H__ #define __AMDGPU_EVENTFD_H__ +#include <linux/atomic.h> #include <linux/eventfd.h> #include <linux/xarray.h> -#include <linux/atomic.h> struct amdgpu_eventfd_entry { struct eventfd_ctx *ctx; @@ -38,22 +38,25 @@ struct amdgpu_eventfd_entry { }; struct amdgpu_eventfd_id { - u32 event_id; + u32 event_type; struct hlist_head entries; u32 n_entries; }; struct amdgpu_eventfd_mgr { - struct xarray ids; /* event_id -> struct amdgpu_eventfd_id* */ - atomic_t bind_count; /* total binds across all event_ids */ + struct xarray ids; /* event_type -> struct amdgpu_eventfd_id* */ + atomic_t bind_count; /* total binds across all event types */ }; 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, int eventfd); +int amdgpu_eventfd_unbind(struct amdgpu_eventfd_mgr *mgr, + u32 event_type, 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); #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 f5719500527f..7cc54b7e3b81 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -634,6 +634,43 @@ static int amdgpu_hw_ip_info(struct amdgpu_device *adev, return 0; } +int amdgpu_eventfd_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct amdgpu_fpriv *fpriv = file_priv->driver_priv; + struct drm_amdgpu_eventfd *args = data; + + if (!fpriv) + return -EINVAL; + + /* + * flags are reserved for future UAPI extensions and must be zero. + */ + if (args->flags || !args->event_type || args->eventfd < 0) + return -EINVAL; + + /* + * Queue-scoped subscriptions are enabled by the later + * queue-reference routing patch. Until then, keep + * queue_id zero. + */ + if (args->queue_id) + return -EINVAL; + + switch (args->op) { + case DRM_AMDGPU_EVENTFD_OP_BIND: + return amdgpu_eventfd_bind(&fpriv->eventfd_mgr, + args->event_type, + args->eventfd); + case DRM_AMDGPU_EVENTFD_OP_UNBIND: + return amdgpu_eventfd_unbind(&fpriv->eventfd_mgr, + args->event_type, + args->eventfd); + default: + return -EINVAL; + } +} + /* * Userspace get information ioctl */ diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 9f3090db2f16..bf4fc61cf0c7 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -39,6 +39,7 @@ extern "C" { #endif #define DRM_AMDGPU_GEM_CREATE 0x00 +#define DRM_AMDGPU_EVENTFD 0x1A #define DRM_AMDGPU_GEM_MMAP 0x01 #define DRM_AMDGPU_CTX 0x02 #define DRM_AMDGPU_BO_LIST 0x03 @@ -79,6 +80,8 @@ extern "C" { #define DRM_IOCTL_AMDGPU_USERQ_SIGNAL DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_USERQ_SIGNAL, struct drm_amdgpu_userq_signal) #define DRM_IOCTL_AMDGPU_USERQ_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_USERQ_WAIT, struct drm_amdgpu_userq_wait) #define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles) +#define DRM_IOCTL_AMDGPU_EVENTFD \ + DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_EVENTFD, struct drm_amdgpu_eventfd) /** * DOC: memory domains @@ -204,6 +207,46 @@ union drm_amdgpu_gem_create { struct drm_amdgpu_gem_create_out out; }; +enum drm_amdgpu_event_type { + DRM_AMDGPU_EVENT_TYPE_USERQ_EOP = 1, + DRM_AMDGPU_EVENT_TYPE_QUEUE_RESET = 2, + DRM_AMDGPU_EVENT_TYPE_MEMORY_EXCEPTION = 3, + DRM_AMDGPU_EVENT_TYPE_SCRATCH = 4, + DRM_AMDGPU_EVENT_TYPE_GPU_RESET = 5, +}; + +enum drm_amdgpu_eventfd_op { + DRM_AMDGPU_EVENTFD_OP_BIND = 0, + DRM_AMDGPU_EVENTFD_OP_UNBIND = 1, +}; + +/** + * struct drm_amdgpu_eventfd - bind or unbind an eventfd to an AMDGPU event + * @op: operation type, see &enum drm_amdgpu_eventfd_op + * @event_type: kernel-defined AMDGPU event type + * @eventfd: eventfd file descriptor + * @queue_id: queue identifier for queue-scoped subscriptions, or 0 for + * device/GPU-scoped subscriptions + * @flags: must be 0 + * + * This ioctl lets userspace register or unregister eventfd notifications + * for a render-node event. + * + * Eventfd signaling is notification-only. + * + * USERQ_EOP, QUEUE_RESET, and SCRATCH are queue-scoped events. + * Userspace specifies @queue_id when registering these subscriptions. + * + * MEMORY_EXCEPTION is currently GPU-scoped and requires @queue_id = 0. + */ +struct drm_amdgpu_eventfd { + __u32 op; + __u32 event_type; + __s32 eventfd; + __u32 queue_id; + __u32 flags; +}; + /** Opcode to create new residency list. */ #define AMDGPU_BO_LIST_OP_CREATE 0 /** Opcode to destroy previously created residency list */ -- 2.34.1
