drm/amdgpu: Implement UALink handle export

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 <mukul.joshi@amd.com>
Reviewed-by: Felix Kuehling <felix.kuehling@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Mukul Joshi 2026-06-03 17:32:05 -04:00 committed by Alex Deucher
parent 869fdd2445
commit c67cb19811
2 changed files with 198 additions and 0 deletions

View File

@ -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;

View File

@ -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