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

Reply via email to