Add a per-drm_file eventfd registry to amdgpu and wire up the new render-node IOCTLs:
DRM_IOCTL_AMDGPU_EVENTFD_BIND DRM_IOCTL_AMDGPU_EVENTFD_UNBIND This allows userspace to bind an eventfd to an event_id on a per-file basis. The mapping is stored in an xarray protected by a mutex inside amdgpu_fpriv. Each event_id maps to a struct amdgpu_eventfd_entry containing the associated eventfd_ctx. Bind replaces any existing binding for the same event_id. Unbind removes the mapping and drops the eventfd reference. The registry is initialized during driver open and fully cleaned up during DRM release to ensure proper lifetime handling and to avoid leaking eventfd references. Entries are freed via RCU so future IRQ-side users can do lockless lookups. Cc: Harish Kasiviswanathan <[email protected]> Cc: Felix Kuehling <[email protected]> Cc: Alex Deucher <[email protected]> Cc: Christian König <[email protected]> Signed-off-by: Srinivasan Shanmugam <[email protected]> Change-Id: I78635bba4772843138a51a6152a3fa621cb07353 --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 20 ++++ drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 3 + drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 116 ++++++++++++++++++++++++ 3 files changed, 139 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 0f6e9cdbe7d8..2baeb0b20df1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -44,6 +44,8 @@ #include <linux/hashtable.h> #include <linux/dma-fence.h> #include <linux/pci.h> +#include <linux/xarray.h> +#include <linux/rcupdate.h> #include <drm/ttm/ttm_bo.h> #include <drm/ttm/ttm_placement.h> @@ -452,8 +454,26 @@ struct amdgpu_fpriv { /** GPU partition selection */ uint32_t xcp_id; + + /* eventfd registry for KFD-event unification (per drm_file) */ + struct mutex eventfd_lock; + struct xarray eventfd_xa; /* key: event_id (u32) -> struct amdgpu_eventfd_entry* */ +}; + +struct amdgpu_eventfd_entry { + struct rcu_head rcu; + struct eventfd_ctx *ctx; }; +struct drm_device; +struct drm_file; + +int amdgpu_eventfd_bind_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int amdgpu_eventfd_unbind_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +void amdgpu_eventfd_registry_fini(struct amdgpu_fpriv *fpriv); + 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 03814a23eb54..98e2fa78c787 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -2955,6 +2955,7 @@ static int amdgpu_drm_release(struct inode *inode, struct file *filp) fpriv->evf_mgr.fd_closing = true; amdgpu_eviction_fence_destroy(&fpriv->evf_mgr); amdgpu_userq_mgr_fini(&fpriv->userq_mgr); + amdgpu_eventfd_registry_fini(fpriv); drm_dev_exit(idx); } @@ -3062,6 +3063,8 @@ 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_BIND, amdgpu_eventfd_bind_ioctl, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(AMDGPU_EVENTFD_UNBIND, amdgpu_eventfd_unbind_ioctl, DRM_RENDER_ALLOW), }; static const struct drm_driver amdgpu_kms_driver = { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index f69332eed051..43da5bc36b7c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -39,6 +39,10 @@ #include <linux/uaccess.h> #include <linux/pci.h> #include <linux/pm_runtime.h> +#include <linux/eventfd.h> +#include <linux/rcupdate.h> +#include <linux/eventfd.h> +#include <linux/slab.h> #include "amdgpu_amdkfd.h" #include "amdgpu_gem.h" #include "amdgpu_display.h" @@ -634,6 +638,113 @@ static int amdgpu_hw_ip_info(struct amdgpu_device *adev, return 0; } +static void amdgpu_eventfd_entry_free_rcu(struct rcu_head *rcu) +{ + struct amdgpu_eventfd_entry *e = + container_of(rcu, struct amdgpu_eventfd_entry, rcu); + + if (e->ctx) + eventfd_ctx_put(e->ctx); + kfree(e); +} + +void amdgpu_eventfd_registry_fini(struct amdgpu_fpriv *fpriv) +{ + unsigned long index; + struct amdgpu_eventfd_entry *e; + + if (!fpriv) + return; + + /* + * Serialize with bind/unbind via eventfd_lock, + * and use xa_lock to safely erase while iterating. + */ + mutex_lock(&fpriv->eventfd_lock); + + xa_lock(&fpriv->eventfd_xa); + xa_for_each(&fpriv->eventfd_xa, index, e) { + __xa_erase(&fpriv->eventfd_xa, index); + call_rcu(&e->rcu, amdgpu_eventfd_entry_free_rcu); + } + xa_unlock(&fpriv->eventfd_xa); + + mutex_unlock(&fpriv->eventfd_lock); + + /* Wait for any RCU readers before destroying the xarray. */ + synchronize_rcu(); + xa_destroy(&fpriv->eventfd_xa); +} + +int amdgpu_eventfd_bind_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct amdgpu_device *adev = drm_to_adev(dev); + struct amdgpu_fpriv *fpriv = file_priv->driver_priv; + struct drm_amdgpu_eventfd_bind *args = data; + struct amdgpu_eventfd_entry *e, *old; + struct eventfd_ctx *ctx; + + if (!fpriv || !adev) + return -ENODEV; + if (args->flags) + return -EINVAL; + if (args->eventfd < 0) + return -EINVAL; + + ctx = eventfd_ctx_fdget(args->eventfd); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + e = kzalloc(sizeof(*e), GFP_KERNEL); + if (!e) { + eventfd_ctx_put(ctx); + return -ENOMEM; + } + e->ctx = ctx; + + mutex_lock(&fpriv->eventfd_lock); + old = xa_store(&fpriv->eventfd_xa, args->event_id, e, GFP_KERNEL); + mutex_unlock(&fpriv->eventfd_lock); + + if (xa_is_err(old)) { + int ret = xa_err(old); + + eventfd_ctx_put(ctx); + kfree(e); + return ret; + } + + /* Replace existing binding for same event_id. */ + if (old) + call_rcu(&old->rcu, amdgpu_eventfd_entry_free_rcu); + + return 0; +} + +int amdgpu_eventfd_unbind_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct amdgpu_fpriv *fpriv = file_priv->driver_priv; + struct drm_amdgpu_eventfd_unbind *args = data; + struct amdgpu_eventfd_entry *e; + + if (!fpriv) + return -ENODEV; + if (args->flags) + return -EINVAL; + + mutex_lock(&fpriv->eventfd_lock); + e = xa_erase(&fpriv->eventfd_xa, args->event_id); + mutex_unlock(&fpriv->eventfd_lock); + + if (!e) + return -ENOENT; + + call_rcu(&e->rcu, amdgpu_eventfd_entry_free_rcu); + return 0; +} + /* * Userspace get information ioctl */ @@ -1469,6 +1580,8 @@ int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) goto out_suspend; } + mutex_init(&fpriv->eventfd_lock); + xa_init(&fpriv->eventfd_xa); pasid = amdgpu_pasid_alloc(16); if (pasid < 0) { dev_warn(adev->dev, "No more PASIDs available!"); @@ -1538,6 +1651,7 @@ int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) if (pasid) amdgpu_pasid_free(pasid); + xa_destroy(&fpriv->eventfd_xa); kfree(fpriv); out_suspend: @@ -1568,6 +1682,8 @@ void amdgpu_driver_postclose_kms(struct drm_device *dev, if (!fpriv) return; + amdgpu_eventfd_registry_fini(fpriv); + pm_runtime_get_sync(dev->dev); if (amdgpu_device_ip_get_ip_block(adev, AMD_IP_BLOCK_TYPE_UVD) != NULL) -- 2.34.1
