diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c index 124fb38eb465..8a2d64f0ebc3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c @@ -51,16 +51,16 @@ struct amdgpu_pasid_cb { /** * amdgpu_pasid_alloc - Allocate a PASID - * @bits: Maximum width of the PASID in bits, must be at least 1 + * @bits: Number of PASID bits supported by the hardware + * @fpriv: DRM file private to associate with the PASID * - * Uses kernel's IDR cyclic allocator (same as PID allocation). + * Uses the kernel's XArray cyclic allocator. * Allocates sequentially with automatic wrap-around. * - * Returns a positive integer on success. Returns %-EINVAL if bits==0. - * Returns %-ENOSPC if no PASID was available. Returns %-ENOMEM on - * memory allocation failure. + * Returns: + * Allocated PASID on success or a negative error code on failure. */ -int amdgpu_pasid_alloc(unsigned int bits) +int amdgpu_pasid_alloc(unsigned int bits, struct amdgpu_fpriv *fpriv) { u32 pasid; int r; @@ -68,9 +68,9 @@ int amdgpu_pasid_alloc(unsigned int bits) if (bits == 0) return -EINVAL; - r = xa_alloc_cyclic_irq(&amdgpu_pasid_xa, &pasid, xa_mk_value(0), - XA_LIMIT(1, (1U << bits) - 1), - &amdgpu_pasid_xa_next, GFP_KERNEL); + r = xa_alloc_cyclic_irq(&amdgpu_pasid_xa, &pasid, fpriv, + XA_LIMIT(1, (1U << bits) - 1), + &amdgpu_pasid_xa_next, GFP_KERNEL); if (r < 0) return r; @@ -106,6 +106,71 @@ static void amdgpu_pasid_free_cb(struct dma_fence *fence, kfree(cb); } +/** + * amdgpu_pasid_clear_owner - Clear the owner associated with a PASID + * @pasid: PASID whose owner should be cleared + * + * Replace the stored owner with NULL while keeping the PASID allocated. + * + * This is used by the delayed PASID free path so that future PASID + * lookups cannot resolve a stale DRM file-private object while the PASID + * is still waiting for outstanding fences before being released. + */ +static void amdgpu_pasid_clear_owner(u32 pasid) +{ + unsigned long flags; + + if (!pasid) + return; + + xa_lock_irqsave(&amdgpu_pasid_xa, flags); + __xa_store(&amdgpu_pasid_xa, pasid, NULL, GFP_ATOMIC); + xa_unlock_irqrestore(&amdgpu_pasid_xa, flags); +} + +/** + * amdgpu_pasid_lock - acquire the global PASID xarray lock + * @flags: storage for interrupt state + * + * Acquire the global PASID xarray lock with interrupts disabled. + * The saved interrupt state must be passed to + * amdgpu_pasid_unlock(). + */ +void amdgpu_pasid_lock(unsigned long *flags) +{ + xa_lock_irqsave(&amdgpu_pasid_xa, *flags); +} + +/** + * amdgpu_pasid_unlock - release the global PASID xarray lock + * @flags: interrupt state returned by amdgpu_pasid_lock() + * + * Release the global PASID xarray lock and restore the previous + * interrupt state. + */ +void amdgpu_pasid_unlock(unsigned long flags) +{ + xa_unlock_irqrestore(&amdgpu_pasid_xa, flags); +} + +/** + * amdgpu_pasid_get_fpriv_locked - get the DRM owner of a PASID + * @pasid: PASID to resolve + * + * The caller must hold the PASID XArray lock. + * + * Returns: + * Pointer to the owning DRM file private, or %NULL if the PASID has no + * current owner. + * + * The returned pointer is only valid while the PASID lock remains held + * and must not be retained after calling amdgpu_pasid_unlock(). + */ +struct amdgpu_fpriv *amdgpu_pasid_get_fpriv_locked(u32 pasid) +{ + return xa_load(&amdgpu_pasid_xa, pasid); +} + /** * amdgpu_pasid_free_delayed - free pasid when fences signal * @@ -121,6 +186,8 @@ void amdgpu_pasid_free_delayed(struct dma_resv *resv, struct dma_fence *fence; int r; + amdgpu_pasid_clear_owner(pasid); + r = dma_resv_get_singleton(resv, DMA_RESV_USAGE_BOOKKEEP, &fence); if (r) goto fallback; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h index a57919478d3b..4b55d0d9703a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h @@ -34,6 +34,7 @@ #define AMDGPU_NUM_VMID 16 struct amdgpu_device; +struct amdgpu_fpriv; struct amdgpu_vm; struct amdgpu_ring; struct amdgpu_sync; @@ -70,7 +71,10 @@ struct amdgpu_vmid_mgr { bool reserved_vmid; }; -int amdgpu_pasid_alloc(unsigned int bits); +int amdgpu_pasid_alloc(unsigned int bits, struct amdgpu_fpriv *fpriv); +void amdgpu_pasid_lock(unsigned long *flags); +void amdgpu_pasid_unlock(unsigned long flags); +struct amdgpu_fpriv *amdgpu_pasid_get_fpriv_locked(u32 pasid); void amdgpu_pasid_free(u32 pasid); void amdgpu_pasid_free_delayed(struct dma_resv *resv, u32 pasid); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 287ede40cd91..b2e2e3533c69 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -1488,7 +1488,7 @@ int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) goto out_suspend; } - pasid = amdgpu_pasid_alloc(16); + pasid = amdgpu_pasid_alloc(16, NULL); if (pasid < 0) { dev_warn(adev->dev, "No more PASIDs available!"); pasid = 0;