From: Mukul Joshi <[email protected]>

This patch adds the implementation for exporting a ualink
handle associated with a BO. If the BO doesn't have a
ualink handle generated yet, we first allocate a unique
ualink handle, add it to a lookup table and return the
handle to user-space.

Signed-off-by: Mukul Joshi <[email protected]>
Reviewed-by: Felix Kuehling <[email protected]>
Signed-off-by: Alex Deucher <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c | 131 +++++++++++++++++++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h |  67 +++++++++++
 2 files changed, 198 insertions(+)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c
index 51078e83e3e1a..fb698c15af909 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c
@@ -24,6 +24,7 @@
 
 #include <linux/xarray.h>
 #include <drm/drm_mm.h>
+#include <linux/random.h>
 #include "amdgpu.h"
 #include "amdgpu_ualink.h"
 #include "amdgpu_xgmi.h"
@@ -1221,6 +1222,136 @@ static void amdgpu_ualink_npa_mm_fini(struct 
amdgpu_device *adev)
        drm_mm_takedown(&adev->ualink.npa_mm.mm);
 }
 
+/* The caller of this function is expected to hold the XA lock when calling
+ * this function.
+ */
+static void amdgpu_generate_ualink_handle(struct amdgpu_device *adev,
+                                  struct amdgpu_ualink_handle *handle)
+{
+       bool unique;
+
+       do {
+               handle->handle_lo = get_random_u64();
+               /* Replace bottom 10 bits in handle_lo with accId */
+               handle->handle_lo &= ~AMDGPU_UALINK_HANDLE_ACCID_MASK;
+               handle->handle_lo |= adev->ualink.info->ppod.accel_id;
+
+               /* Don't generate/store a Handle with value 0. */
+               if (!handle->handle_lo)
+                       continue;
+
+               /* Find if the handle already exists in the exporter xarray.
+                * If it already exists, then regenerate the handle since we
+                * want the handle to be unique.
+                */
+               unique = !xa_load(&adev->ualink.exp_xa, handle->handle_lo);
+       } while (!unique);
+
+       handle->handle_hi = get_random_u64();
+       dev_dbg(adev->dev, "GENERATE-HANDLE: generated handle: %llx:%llx\n",
+               handle->handle_hi, handle->handle_lo);
+}
+
+static void amdgpu_ualink_exp_cleanup_worker(struct work_struct *work)
+{
+}
+
+int amdgpu_ualink_export_handle(struct drm_device *dev, struct drm_file *filp,
+                               u32 gem_handle,
+                               struct amdgpu_ualink_handle *handle_out)
+{
+       struct amdgpu_ualink_exp_xa_node *exp_xa_node;
+       struct amdgpu_ualink_handle handle;
+       struct drm_gem_object *gobj;
+       struct amdgpu_device *adev;
+       struct amdgpu_bo *robj;
+       int r = 0, i;
+
+       gobj = drm_gem_object_lookup(filp, gem_handle);
+       if (!gobj)
+               return -ENOENT;
+
+       robj = gem_to_amdgpu_bo(gobj);
+       adev = amdgpu_ttm_adev(robj->tbo.bdev);
+
+       if (!(robj->preferred_domains & AMDGPU_GEM_DOMAIN_VRAM)) {
+               dev_err(adev->dev, "Only VRAM BOs can be exported\n");
+               r = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!robj->ualink_handle_lo) {
+               /* If no ualink handle generated for BO, then generate one and
+                * add it to the exporter Xarray.
+                */
+               exp_xa_node = kzalloc(sizeof(*exp_xa_node), GFP_KERNEL);
+               if (!exp_xa_node) {
+                       dev_err(adev->dev, "Failed to allocate exp_xa_node\n");
+                       r = -ENOMEM;
+                       goto out;
+               }
+
+               amdgpu_bo_ref(robj);
+               exp_xa_node->bo = robj;
+               init_completion(&exp_xa_node->npa_done);
+               bitmap_zero(exp_xa_node->importers_bitmap,
+                           AMDGPU_UALINK_ACCEL_MAX);
+               bitmap_zero(exp_xa_node->npa_release_bitmap,
+                       AMDGPU_UALINK_ACCEL_MAX);
+               kref_init(&exp_xa_node->refcount);
+               mutex_init(&exp_xa_node->node_lock);
+               INIT_WORK(&exp_xa_node->cleanup_work,
+                         amdgpu_ualink_exp_cleanup_worker);
+               for (i = 0; i < AMDGPU_UALINK_ACCEL_MAX; i++) {
+                       INIT_LIST_HEAD(&exp_xa_node->importer_entries[i].list);
+                       exp_xa_node->importer_entries[i].parent = exp_xa_node;
+               }
+               /* DMABuf handle for local import of fabric handles */
+               exp_xa_node->dmabuf = 
drm_gem_prime_handle_to_dmabuf(&adev->ddev, filp,
+                                               gem_handle, DRM_CLOEXEC | 
DRM_RDWR);
+               if (IS_ERR(exp_xa_node->dmabuf)) {
+                       r = PTR_ERR(exp_xa_node->dmabuf);
+                       dev_err(adev->dev, "Failed to generate DMABuf for the 
BO\n");
+                       kfree(exp_xa_node);
+                       goto out;
+               }
+
+               xa_lock(&adev->ualink.exp_xa);
+               amdgpu_generate_ualink_handle(adev, &handle);
+               exp_xa_node->handle = handle;
+               r = __xa_insert(&adev->ualink.exp_xa, handle.handle_lo,
+                               exp_xa_node, GFP_KERNEL);
+               xa_unlock(&adev->ualink.exp_xa);
+               if (r) {
+                       dev_err(adev->dev, "Failed to insert exp_xa_node into 
XA: %d\n", r);
+                       dma_buf_put(exp_xa_node->dmabuf);
+                       amdgpu_bo_unref(&robj);
+                       kfree(exp_xa_node);
+                       goto out;
+               }
+
+               robj->ualink_handle_lo = handle.handle_lo;
+               /* Return the generated handle back to the caller */
+               *handle_out = handle;
+       } else {
+               handle_out->handle_lo = robj->ualink_handle_lo;
+
+               /* Do a sanity check to ensure the handle exists in the XA */
+               xa_lock(&adev->ualink.exp_xa);
+               exp_xa_node = xa_load(&adev->ualink.exp_xa,
+                                     robj->ualink_handle_lo);
+               xa_unlock(&adev->ualink.exp_xa);
+               WARN(!exp_xa_node, "Exp XA: Handle_Lo: %llx not found",
+                    robj->ualink_handle_lo);
+               if (exp_xa_node)
+                       handle_out->handle_hi = exp_xa_node->handle.handle_hi;
+       }
+
+out:
+       drm_gem_object_put(gobj);
+       return r;
+}
+
 int amdgpu_ualink_manager_start(struct amdgpu_device *adev)
 {
        int i, r;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h
index cb971ac8bdd9e..4efb0ece4d113 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h
@@ -35,6 +35,8 @@ struct amdgpu_device;
 #define AMDGPU_UALINK_LOCAL_ACCELS_MAX 8
 #define AMDGPU_UALINK_STATIONS_MAX 64
 
+#define AMDGPU_UALINK_HANDLE_ACCID_MASK                        GENMASK_ULL(9, 
0)
+
 enum amdgpu_ualink_conn_state {
        AMDGPU_UALINK_CONN_NOT_READY                    = 0,
        AMDGPU_UALINK_CONN_IN_PROGRESS                  = 1,
@@ -125,6 +127,16 @@ struct amdgpu_ualink_station_config {
 };
 #define to_ualink_station_config(ko) container_of(ko, struct 
amdgpu_ualink_station_config, kobj)
 
+struct amdgpu_ualink_handle {
+       union {
+               struct {
+                       u64 handle_lo;
+                       u64 handle_hi;
+               };
+               u64 handle[2];
+       };
+};
+
 struct amdgpu_ualink_npa_mm {
        struct drm_mm                   mm;
        u64                             va_start;
@@ -132,6 +144,58 @@ struct amdgpu_ualink_npa_mm {
        struct mutex                    mm_lock;
 };
 
+struct amdgpu_ualink_importer_entry {
+       struct drm_mm_node                      *mm_node;
+       u64                                     npa_addr;
+
+       /* Keep track of if the connection got reset */
+       u32                                     generation_count;
+
+       /* Used to connect all handles exported to a particular importer */
+       struct list_head                        list;
+
+       /* Pointer to the parent XA node. */
+       struct amdgpu_ualink_exp_xa_node        *parent;
+};
+
+struct amdgpu_ualink_exp_xa_node {
+       /* 128-bit handle for the BO */
+       struct amdgpu_ualink_handle             handle;
+
+       /* Pointer to the BO thats exported.*/
+       struct amdgpu_bo                        *bo;
+
+       /* Dmabuf corresponding to the BO */
+       struct dma_buf                          *dmabuf;
+
+       /* Mutex to protect the node from concurrent access */
+       struct mutex                            node_lock;
+
+       /* Used for storing importer info in source identification mode */
+       struct amdgpu_ualink_importer_entry     
importer_entries[AMDGPU_UALINK_ACCEL_MAX];
+
+       /* Used to track all importers of this BO. This is set when the
+        * exporter sends back the NPA-RSP message.
+        */
+       DECLARE_BITMAP(importers_bitmap, AMDGPU_UALINK_ACCEL_MAX);
+
+       /* This bitmap is used to send NPA-REVOKE messages to all the importers.
+        * And to track the NPA-RELEASE response for each NPA-REVOKE message 
sent.
+        * A bit is cleared in this bitmap when the NPA RELEASE message is
+        * received in response to the NPA-REVOKE message.
+        */
+       DECLARE_BITMAP(npa_release_bitmap, AMDGPU_UALINK_ACCEL_MAX);
+
+       /* Use to signal responses received from all importers */
+       struct completion                       npa_done;
+
+       /* Refcount to track lifetime of this node */
+       struct kref                             refcount;
+
+       /* Work to cleanup the node. */
+       struct work_struct                      cleanup_work;
+};
+
 struct amdgpu_ualink_connection {
        struct completion hello_done;
        struct mutex lock;
@@ -203,4 +267,7 @@ int amdgpu_ualink_init(struct amdgpu_device *adev);
 void amdgpu_ualink_fini(struct amdgpu_device *adev);
 int amdgpu_ualink_manager_start(struct amdgpu_device *adev);
 void amdgpu_ualink_manager_stop(struct amdgpu_device *adev);
+int amdgpu_ualink_export_handle(struct drm_device *dev, struct drm_file *filp,
+                               u32 gem_handle,
+                               struct amdgpu_ualink_handle *handle_out);
 #endif
-- 
2.55.0

Reply via email to