summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLijo Lazar <lijo.lazar@amd.com>2026-08-11 17:25:56 +0530
committerAlex Deucher <alexander.deucher@amd.com>2026-09-02 15:31:20 -0400
commitf04ff7571eb289890d2917d33229acdfa484fd30 (patch)
tree94c15c2addda7255773cdb3cc0427e69e5ba649c
parentced5c6b26dd2cd4cd82671661246767724ce30ae (diff)
downloadlinux-next-f04ff7571eb289890d2917d33229acdfa484fd30.tar.gz
linux-next-f04ff7571eb289890d2917d33229acdfa484fd30.zip
drm/amdgpu: Expose ualink info under each xcp
Mirror the read-only ualink info attributes on each secondary compute partition as a per-partition ualink node, so a partition-scoped consumer (e.g. a container that only sees its partition's device node) can read the ualink identity and state. Partition 0 shares the primary device, which already exposes that node, so it is skipped. An inactive partition device won't be having any attributes listed under ualink node. The per-partition attributes are served by thin wrappers that delegate to the existing device-level info show functions. A reference on the info kobject is held for the node's lifetime so it cannot be freed while a partition still uses it. Signed-off-by: Lijo Lazar <lijo.lazar@amd.com> Assisted-by: Claude (claude-opus-4.7) Reviewed-by: Felix Kuehling <felix.kuehling@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c165
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h7
4 files changed, 181 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c
index d7b8b497997d..b8e1eb8cf19c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c
@@ -768,6 +768,37 @@ UALINK_ENUM_SHOW(info, addr_mode, vpod.addr_mode);
UALINK_ENUM_SHOW(info, accel_state, accel_state);
UALINK_IDARRAY_SHOW(info, local_accels, local_accels, n_local_accels);
+static struct amdgpu_device *ualink_xcp_kobj_to_adev(struct kobject *kobj)
+{
+ struct amdgpu_xcp *xcp = container_of(kobj, struct amdgpu_xcp,
+ ualink.kobj);
+
+ return xcp->xcp_mgr->adev;
+}
+
+#define UALINK_XCP_INFO_SHOW(name) \
+static ssize_t ualink_xcp_info_##name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+{ \
+ struct amdgpu_device *adev = ualink_xcp_kobj_to_adev(kobj); \
+ \
+ return ualink_info_##name##_show(&adev->ualink.info->kobj, \
+ attr, buf); \
+}
+
+UALINK_XCP_INFO_SHOW(link_type)
+UALINK_XCP_INFO_SHOW(accel_id)
+UALINK_XCP_INFO_SHOW(bandwidth)
+UALINK_XCP_INFO_SHOW(latency)
+UALINK_XCP_INFO_SHOW(ppod_id)
+UALINK_XCP_INFO_SHOW(ppod_size)
+UALINK_XCP_INFO_SHOW(vpod_id)
+UALINK_XCP_INFO_SHOW(vpod_size)
+UALINK_XCP_INFO_SHOW(vpod_active_accels)
+UALINK_XCP_INFO_SHOW(addr_mode)
+UALINK_XCP_INFO_SHOW(accel_state)
+UALINK_XCP_INFO_SHOW(local_accels)
+
#define UALINK_INFO_ATTR(name) __ATTR(name, 0444, ualink_info_##name##_show, NULL)
static struct kobj_attribute ualink_info_link_type = UALINK_INFO_ATTR(link_type);
static struct kobj_attribute ualink_info_accel_id = UALINK_INFO_ATTR(accel_id);
@@ -798,6 +829,58 @@ static const struct attribute *ualink_info_attrs[] = {
NULL
};
+#define UALINK_XCP_INFO_ATTR(name) \
+ __ATTR(name, 0444, ualink_xcp_info_##name##_show, NULL)
+static struct kobj_attribute ualink_xcp_info_link_type = UALINK_XCP_INFO_ATTR(link_type);
+static struct kobj_attribute ualink_xcp_info_accel_id = UALINK_XCP_INFO_ATTR(accel_id);
+static struct kobj_attribute ualink_xcp_info_bandwidth = UALINK_XCP_INFO_ATTR(bandwidth);
+static struct kobj_attribute ualink_xcp_info_latency = UALINK_XCP_INFO_ATTR(latency);
+static struct kobj_attribute ualink_xcp_info_ppod_id = UALINK_XCP_INFO_ATTR(ppod_id);
+static struct kobj_attribute ualink_xcp_info_ppod_size = UALINK_XCP_INFO_ATTR(ppod_size);
+static struct kobj_attribute ualink_xcp_info_vpod_id = UALINK_XCP_INFO_ATTR(vpod_id);
+static struct kobj_attribute ualink_xcp_info_vpod_size = UALINK_XCP_INFO_ATTR(vpod_size);
+static struct kobj_attribute ualink_xcp_info_vpod_active_accels = UALINK_XCP_INFO_ATTR(vpod_active_accels);
+static struct kobj_attribute ualink_xcp_info_addr_mode = UALINK_XCP_INFO_ATTR(addr_mode);
+static struct kobj_attribute ualink_xcp_info_accel_state = UALINK_XCP_INFO_ATTR(accel_state);
+static struct kobj_attribute ualink_xcp_info_local_accels = UALINK_XCP_INFO_ATTR(local_accels);
+
+static struct attribute *ualink_xcp_info_attrs[] = {
+ &ualink_xcp_info_link_type.attr,
+ &ualink_xcp_info_accel_id.attr,
+ &ualink_xcp_info_bandwidth.attr,
+ &ualink_xcp_info_latency.attr,
+ &ualink_xcp_info_ppod_id.attr,
+ &ualink_xcp_info_ppod_size.attr,
+ &ualink_xcp_info_vpod_id.attr,
+ &ualink_xcp_info_vpod_size.attr,
+ &ualink_xcp_info_vpod_active_accels.attr,
+ &ualink_xcp_info_addr_mode.attr,
+ &ualink_xcp_info_accel_state.attr,
+ &ualink_xcp_info_local_accels.attr,
+ NULL
+};
+
+static umode_t ualink_xcp_info_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct amdgpu_xcp *xcp = container_of(kobj, struct amdgpu_xcp,
+ ualink.kobj);
+
+ if (!xcp->valid)
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group ualink_xcp_info_group = {
+ .attrs = ualink_xcp_info_attrs,
+ .is_visible = ualink_xcp_info_is_visible,
+};
+
+static const struct kobj_type ualink_xcp_info_ktype = {
+ .sysfs_ops = &kobj_sysfs_ops
+};
+
static void ualink_info_release(struct kobject *kobj)
{
kfree(to_ualink_info(kobj));
@@ -1495,6 +1578,9 @@ static int ualink_kobj_add(struct kobject *kobj, struct kobject *parent,
return r;
}
+static void amdgpu_ualink_xcp_sysfs_init(struct amdgpu_device *adev);
+static void amdgpu_ualink_xcp_sysfs_fini(struct amdgpu_device *adev);
+
int amdgpu_ualink_sysfs_init(struct amdgpu_device *adev)
{
struct amdgpu_ualink_info *info = adev->ualink.info;
@@ -1541,7 +1627,9 @@ int amdgpu_ualink_sysfs_init(struct amdgpu_device *adev)
goto err_config;
#endif
+ amdgpu_ualink_xcp_sysfs_init(adev);
adev->ualink.sysfs_init = true;
+
return 0;
err_config:
@@ -1560,6 +1648,7 @@ void amdgpu_ualink_sysfs_fini(struct amdgpu_device *adev)
if (!adev->ualink.sysfs_init)
return;
+ amdgpu_ualink_xcp_sysfs_fini(adev);
kobject_del(&adev->ualink.stations->kobj);
kobject_del(&adev->ualink.config->kobj);
kobject_del(&adev->ualink.setup->kobj);
@@ -1567,6 +1656,82 @@ void amdgpu_ualink_sysfs_fini(struct amdgpu_device *adev)
adev->ualink.sysfs_init = false;
}
+static int amdgpu_ualink_xcp_sysfs_add(struct amdgpu_xcp *xcp)
+{
+ struct amdgpu_device *adev = xcp->xcp_mgr->adev;
+ int r;
+
+ r = kobject_init_and_add(&xcp->ualink.kobj, &ualink_xcp_info_ktype,
+ &xcp->ddev->dev->kobj, "ualink");
+ if (r)
+ goto err;
+
+ r = sysfs_create_group(&xcp->ualink.kobj, &ualink_xcp_info_group);
+ if (r)
+ goto err;
+
+ /* pin info so it outlives this node regardless of teardown order */
+ kobject_get(&adev->ualink.info->kobj);
+ xcp->ualink.sysfs = true;
+ return 0;
+err:
+ kobject_put(&xcp->ualink.kobj);
+ return r;
+}
+
+void amdgpu_ualink_xcp_sysfs_update(struct amdgpu_xcp *xcp)
+{
+ if (!xcp->ualink.sysfs)
+ return;
+
+ sysfs_update_group(&xcp->ualink.kobj, &ualink_xcp_info_group);
+}
+
+static void amdgpu_ualink_xcp_sysfs_remove(struct amdgpu_xcp *xcp)
+{
+ struct amdgpu_device *adev = xcp->xcp_mgr->adev;
+
+ if (!xcp->ualink.sysfs)
+ return;
+
+ /* group is only populated for valid partitions */
+ if (xcp->valid)
+ sysfs_remove_group(&xcp->ualink.kobj, &ualink_xcp_info_group);
+ kobject_put(&adev->ualink.info->kobj);
+ kobject_put(&xcp->ualink.kobj);
+ xcp->ualink.sysfs = false;
+}
+
+static void amdgpu_ualink_xcp_sysfs_init(struct amdgpu_device *adev)
+{
+ struct amdgpu_xcp *xcp;
+ int i;
+
+ if (!adev->xcp_mgr)
+ return;
+
+ for (i = 0; i < MAX_XCP; i++) {
+ xcp = &adev->xcp_mgr->xcp[i];
+ if (!xcp->ddev || amdgpu_xcp_is_primary(xcp))
+ continue;
+ amdgpu_ualink_xcp_sysfs_add(xcp);
+ }
+}
+
+static void amdgpu_ualink_xcp_sysfs_fini(struct amdgpu_device *adev)
+{
+ struct amdgpu_xcp *xcp;
+ int i;
+
+ if (!adev->xcp_mgr)
+ return;
+
+ for (i = 0; i < MAX_XCP; i++) {
+ xcp = &adev->xcp_mgr->xcp[i];
+ amdgpu_ualink_xcp_sysfs_remove(xcp);
+ }
+}
+
static int amdgpu_ualink_npa_alloc_va(struct amdgpu_device *adev,
struct drm_mm_node *mm_node,
u64 va, u64 range_start,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h
index 8ec0bc80a0f7..1bd7181147dd 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h
@@ -31,6 +31,8 @@
struct amdgpu_device;
+struct amdgpu_xcp;
+
#define AMDGPU_UALINK_ACCEL_MAX 256
#define AMDGPU_UALINK_LOCAL_ACCELS_MAX 8
#define AMDGPU_UALINK_STATIONS_MAX 64
@@ -402,6 +404,7 @@ int amdgpu_ualink_resume_handler(struct amdgpu_device *adev);
int amdgpu_ualink_sysfs_init(struct amdgpu_device *adev);
void amdgpu_ualink_sysfs_fini(struct amdgpu_device *adev);
+void amdgpu_ualink_xcp_sysfs_update(struct amdgpu_xcp *xcp);
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,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
index fbc70a2d8ba5..c80ffa08920d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
@@ -1061,6 +1061,11 @@ static const struct kobj_type xcp_sysfs_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
};
+bool amdgpu_xcp_is_primary(struct amdgpu_xcp *xcp)
+{
+ return xcp->ddev == adev_to_drm(xcp->xcp_mgr->adev);
+}
+
static void amdgpu_xcp_sysfs_entries_fini(struct amdgpu_xcp_mgr *xcp_mgr, int n)
{
struct amdgpu_xcp *xcp;
@@ -1110,6 +1115,7 @@ static void amdgpu_xcp_sysfs_entries_update(struct amdgpu_xcp_mgr *xcp_mgr)
if (!xcp->ddev)
continue;
sysfs_update_group(&xcp->kobj, &amdgpu_xcp_attrs_group);
+ amdgpu_ualink_xcp_sysfs_update(xcp);
}
return;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
index 878c1c422893..33157409eda0 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
@@ -100,6 +100,11 @@ struct amdgpu_xcp_ip {
bool valid;
};
+struct amdgpu_xcp_ualink {
+ struct kobject kobj;
+ bool sysfs;
+};
+
struct amdgpu_xcp {
struct amdgpu_xcp_ip ip[AMDGPU_XCP_MAX_BLOCKS];
@@ -115,6 +120,7 @@ struct amdgpu_xcp {
struct amdgpu_sched gpu_sched[AMDGPU_HW_IP_NUM][AMDGPU_RING_PRIO_MAX];
struct amdgpu_xcp_mgr *xcp_mgr;
struct kobject kobj;
+ struct amdgpu_xcp_ualink ualink;
uint64_t unique_id;
};
@@ -191,6 +197,7 @@ int amdgpu_xcp_pre_partition_switch(struct amdgpu_xcp_mgr *xcp_mgr, u32 flags);
int amdgpu_xcp_post_partition_switch(struct amdgpu_xcp_mgr *xcp_mgr, u32 flags);
void amdgpu_xcp_sysfs_init(struct amdgpu_device *adev);
void amdgpu_xcp_sysfs_fini(struct amdgpu_device *adev);
+bool amdgpu_xcp_is_primary(struct amdgpu_xcp *xcp);
static inline int amdgpu_xcp_get_num_xcp(struct amdgpu_xcp_mgr *xcp_mgr)
{