AMDGPU already has a global PASID xarray used by the PASID allocator.
Currently allocated PASIDs store a dummy value in that xarray.
Add helper functions so DRM-owned PASIDs can store and retrieve their
owning DRM file-private object.
This prepares for using:
PASID -> fpriv -> vm
instead of the separate per-device:
PASID -> vm
mapping.
v3: (per Christian)
- Document that PASID allocation is intentionally kept separate
from fpriv registration because fpriv is not fully initialized
when the PASID is allocated.
Cc: Alex Deucher <[email protected]>
Suggested-by: Christian König <[email protected]>
Signed-off-by: Srinivasan Shanmugam <[email protected]>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c | 126 ++++++++++++++++++++++++
drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h | 13 +++
2 files changed, 139 insertions(+)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
index 684f40fce73f..5333937f0d04 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
@@ -78,6 +78,132 @@ int amdgpu_pasid_alloc(unsigned int bits)
return pasid;
}
+/**
+ * amdgpu_pasid_set_fpriv - register the DRM owner of a PASID
+ * @pasid: PASID allocated for the DRM client
+ * @fpriv: owning DRM file-private object
+ *
+ * PASID allocation is intentionally kept separate from owner
+ * registration because the DRM file-private object is not fully
+ * initialized when the PASID is allocated.
+ *
+ * This helper replaces the temporary allocation marker with the
+ * initialized DRM owner.
+ */
+int amdgpu_pasid_set_fpriv(u32 pasid,
+ struct amdgpu_fpriv *fpriv)
+{
+ unsigned long flags;
+ void *entry;
+ int r = 0;
+
+ if (!pasid || !fpriv)
+ return -EINVAL;
+
+ xa_lock_irqsave(&amdgpu_pasid_xa, flags);
+
+ entry = xa_load(&amdgpu_pasid_xa, pasid);
+
+ if (!entry) {
+ r = -ENOENT;
+ goto unlock;
+ }
+
+ if (!xa_is_value(entry)) {
+ r = -EBUSY;
+ goto unlock;
+ }
+
+ entry = __xa_store(&amdgpu_pasid_xa, pasid,
+ fpriv, GFP_ATOMIC);
+ r = xa_err(entry);
+
+unlock:
+ xa_unlock_irqrestore(&amdgpu_pasid_xa, flags);
+
+ return r;
+}
+
+/**
+ * amdgpu_pasid_clear_fpriv - unregister PASID ownership
+ * @pasid: PASID whose owner is being removed
+ * @fpriv: DRM file-private object owning the PASID
+ *
+ * Restore the PASID entry back to the allocation marker before the
+ * DRM file-private object is destroyed.
+ */
+void amdgpu_pasid_clear_fpriv(u32 pasid,
+ struct amdgpu_fpriv *fpriv)
+{
+ unsigned long flags;
+ void *entry;
+
+ if (!pasid || !fpriv)
+ return;
+
+ xa_lock_irqsave(&amdgpu_pasid_xa, flags);
+
+ entry = xa_load(&amdgpu_pasid_xa, pasid);
+
+ if (entry == fpriv)
+ __xa_store(&amdgpu_pasid_xa,
+ pasid,
+ xa_mk_value(0),
+ 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 fpriv from PASID
+ * @pasid: PASID to resolve
+ *
+ * Caller must hold the PASID XA lock.
+ *
+ * The returned pointer is only protected by the PASID XA lock.
+ * Callers must not continue using it after dropping the lock
+ * unless additional lifetime protection exists.
+ *
+ * This intentionally does not add kref/RCU lifetime handling.
+ */
+ struct amdgpu_fpriv *
+amdgpu_pasid_get_fpriv_locked(u32 pasid)
+{
+ void *entry;
+
+ entry = xa_load(&amdgpu_pasid_xa, pasid);
+
+ if (!entry || xa_is_value(entry))
+ return NULL;
+
+ return entry;
+}
+
/**
* amdgpu_pasid_free - Free a PASID
* @pasid: PASID to free
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h
index a57919478d3b..220a0ba0cfb6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h
@@ -38,6 +38,7 @@ struct amdgpu_vm;
struct amdgpu_ring;
struct amdgpu_sync;
struct amdgpu_job;
+struct amdgpu_fpriv;
struct amdgpu_vmid {
struct list_head list;
@@ -92,4 +93,16 @@ void amdgpu_vmid_reset_all(struct amdgpu_device *adev);
void amdgpu_vmid_mgr_init(struct amdgpu_device *adev);
void amdgpu_vmid_mgr_fini(struct amdgpu_device *adev);
+int amdgpu_pasid_set_fpriv(u32 pasid,
+ struct amdgpu_fpriv *fpriv);
+
+void amdgpu_pasid_clear_fpriv(u32 pasid,
+ 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);
+
#endif
--
2.34.1