[PATCH 03/95] drm/amdgpu: Add sysfs API for UALink information

Alex Deucher <[email protected]>
Newsgroups org.freedesktop.lists.amd-gfx
Message-ID <[email protected]>
From: Felix Kuehling <[email protected]>

<device>/ualink/info: Directory contains current UALink attributes
- link_type: UALoE of UALink
- accel_state: unconfigured, configured, ready, active, error
- accel_id: Accelerator ID
- ppod_id: Physical pod ID (UUID)
- ppod_size: Number of accelerators in physical pod
- bandwidth: Estimated total bandwidth between pairs of accelerators
- latency: Estimated latency between pairs of accelerators
- vpod_id: Virtual pod ID
- vpod_size: Number of accelerators in virtual pod
- vpod_active_accels: List of all accelerator IDs in vpod
- local_accels: List of local accelerator IDs in vpod
- addr_mode: source-aliasing or source-identification

Signed-off-by: Felix Kuehling <[email protected]>
Reviewed-by: Mukul Joshi <[email protected]>
Signed-off-by: Alex Deucher <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/Makefile        |   3 +
 drivers/gpu/drm/amd/amdgpu/amdgpu.h        |   4 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c | 226 +++++++++++++++++++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h |  93 +++++++++
 drivers/gpu/drm/amd/amdgpu/soc_v1_0.c      |  12 ++
 5 files changed, 338 insertions(+)
 create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c
 create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h

diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
index ffb725fb10842..bb33119608a06 100644
--- a/drivers/gpu/drm/amd/amdgpu/Makefile
+++ b/drivers/gpu/drm/amd/amdgpu/Makefile
@@ -278,6 +278,9 @@ amdgpu-y += amdgpu_amdkfd.o
 # add gfx usermode queue
 amdgpu-y += amdgpu_userq.o
 
+# add UALink manager support
+amdgpu-y += amdgpu_ualink.o
+
 ifneq ($(CONFIG_HSA_AMD),)
 AMDKFD_PATH := ../amdkfd
 include $(FULL_AMD_PATH)/amdkfd/Makefile
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 8a7c89afc88e1..f619d95a5ccfe 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -116,6 +116,7 @@
 #include "amdgpu_mes.h"
 #include "amdgpu_sa.h"
 #include "amdgpu_acpi.h"
+#include "amdgpu_ualink.h"
 #if defined(CONFIG_DRM_AMD_ISP)
 #include "amdgpu_isp.h"
 #endif
@@ -799,6 +800,9 @@ struct amdgpu_device {
 	/* display related functionality */
 	struct amdgpu_display_manager dm;
 
+	/* UALink manager */
+	struct amdgpu_ualink_mgr	ualink;
+
 #if defined(CONFIG_DRM_AMD_ISP)
 	/* isp */
 	struct amdgpu_isp		isp;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c
new file mode 100644
index 0000000000000..374242372eab7
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "amdgpu_ualink.h"
+#include "amdgpu_xgmi.h"
+#include "amdgpu.h"
+#include <linux/sysfs.h>
+#include <linux/string.h>
+
+
+/****************************************************************************
+ * UALink info and configuration in sysfs
+ *
+ * Using kobj_attribute and not device_attribute here because the UALink
+ * attributes are part of their own nested kobjects and not the amdgpu
+ * device itself or a subdevice.
+ ****************************************************************************/
+
+#define UALINK_VALUE_SHOW(prefix, name, field, format)			\
+static ssize_t ualink_##prefix##_##name##_show(struct kobject *kobj,	\
+					       struct kobj_attribute *attr,\
+					       char *buf)		\
+{									\
+	struct amdgpu_ualink_##prefix *info = to_ualink_##prefix(kobj);	\
+									\
+	return sysfs_emit(buf, format"\n", info->field);		\
+}
+
+static ssize_t show_idbits(unsigned long *bits, unsigned int nbits, char *buf)
+{
+	ssize_t len = 0;
+	unsigned int id;
+
+	for_each_set_bit(id, bits, nbits)
+		len += sysfs_emit_at(buf, len, "%u ", id);
+	if (len)
+		len--;
+	return len + sysfs_emit_at(buf, len, "\n");
+}
+#define UALINK_IDBITS_SHOW(prefix, name, field)				\
+static ssize_t ualink_##prefix##_##name##_show(struct kobject *kobj,	\
+					       struct kobj_attribute *attr,\
+					       char *buf)		\
+{									\
+	struct amdgpu_ualink_##prefix *info = to_ualink_##prefix(kobj);	\
+									\
+	return show_idbits(info->field, sizeof(info->field)*8, buf);	\
+}
+
+static ssize_t show_idarray(const u32 *array, u32 size, char *buf)
+{
+	ssize_t len = 0;
+	unsigned int i;
+
+	for (i = 0; i < size; i++)
+		len += sysfs_emit_at(buf, len, "%u ", array[i]);
+	if (len)
+		len--;
+	return len + sysfs_emit_at(buf, len, "\n");
+}
+#define UALINK_IDARRAY_SHOW(prefix, name, field, size)			\
+static ssize_t ualink_##prefix##_##name##_show(struct kobject *kobj,	\
+					       struct kobj_attribute *attr,\
+					       char *buf)		\
+{									\
+	struct amdgpu_ualink_##prefix *info = to_ualink_##prefix(kobj);	\
+									\
+	return show_idarray(info->field, info->size, buf);		\
+}
+
+#define UALINK_UUID_SHOW(prefix, name, field)				\
+static ssize_t ualink_##prefix##_##name##_show(struct kobject *kobj,	\
+					       struct kobj_attribute *attr,\
+					       char *buf)		\
+{									\
+	struct amdgpu_ualink_##prefix *info = to_ualink_##prefix(kobj);	\
+									\
+	return sysfs_emit(buf, "%pU\n", &info->field);			\
+}
+
+static ssize_t show_enum(u32 x, const char * const values[], unsigned int n,
+			 char *buf)
+{
+	return sysfs_emit(buf, "%s\n", x < n && values[x] ? values[x] : "invalid");
+}
+#define UALINK_ENUM_SHOW(prefix, name, field)				\
+static ssize_t ualink_##prefix##_##name##_show(struct kobject *kobj,	\
+					       struct kobj_attribute *attr,\
+					       char *buf)		\
+{									\
+	struct amdgpu_ualink_##prefix *info = to_ualink_##prefix(kobj);	\
+									\
+	return show_enum(info->field, ualink_##name##_values,		\
+			 ARRAY_SIZE(ualink_##name##_values), buf);	\
+}
+
+static const char * const ualink_link_type_values[] = {
+	"UALoE", "UALink"
+};
+static const char * const ualink_addr_mode_values[] = {
+	"source-aliasing", "source-identification"
+};
+static const char * const ualink_accel_state_values[] = {
+	"unconfigured", "configured", "ready", "active", "error"
+};
+
+UALINK_ENUM_SHOW(info, link_type, link_type);
+UALINK_VALUE_SHOW(info, accel_id,  ppod.accel_id,  "%u");
+UALINK_VALUE_SHOW(info, bandwidth, ppod.bandwidth, "%u");
+UALINK_VALUE_SHOW(info, latency,   ppod.latency,   "%u");
+UALINK_UUID_SHOW(info, ppod_id, ppod.id);
+UALINK_VALUE_SHOW(info, ppod_size, ppod.size,      "%u");
+UALINK_VALUE_SHOW(info, vpod_id,   vpod.id,        "%u");
+UALINK_VALUE_SHOW(info, vpod_size, vpod.size,      "%u");
+UALINK_IDBITS_SHOW(info, vpod_active_accels, vpod.active_accel_bits);
+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);
+
+#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);
+static struct kobj_attribute ualink_info_bandwidth = UALINK_INFO_ATTR(bandwidth);
+static struct kobj_attribute ualink_info_latency   = UALINK_INFO_ATTR(latency);
+static struct kobj_attribute ualink_info_ppod_id   = UALINK_INFO_ATTR(ppod_id);
+static struct kobj_attribute ualink_info_ppod_size = UALINK_INFO_ATTR(ppod_size);
+static struct kobj_attribute ualink_info_vpod_id   = UALINK_INFO_ATTR(vpod_id);
+static struct kobj_attribute ualink_info_vpod_size = UALINK_INFO_ATTR(vpod_size);
+static struct kobj_attribute ualink_info_vpod_active_accels = UALINK_INFO_ATTR(vpod_active_accels);
+static struct kobj_attribute ualink_info_addr_mode = UALINK_INFO_ATTR(addr_mode);
+static struct kobj_attribute ualink_info_accel_state = UALINK_INFO_ATTR(accel_state);
+static struct kobj_attribute ualink_info_local_accels = UALINK_INFO_ATTR(local_accels);
+
+static const struct attribute *ualink_info_attrs[] = {
+	&ualink_info_link_type.attr,
+	&ualink_info_accel_id.attr,
+	&ualink_info_bandwidth.attr,
+	&ualink_info_latency.attr,
+	&ualink_info_ppod_id.attr,
+	&ualink_info_ppod_size.attr,
+	&ualink_info_vpod_id.attr,
+	&ualink_info_vpod_size.attr,
+	&ualink_info_vpod_active_accels.attr,
+	&ualink_info_addr_mode.attr,
+	&ualink_info_accel_state.attr,
+	&ualink_info_local_accels.attr,
+	NULL
+};
+
+static void ualink_info_release(struct kobject *kobj)
+{
+	struct amdgpu_ualink_info *info = to_ualink_info(kobj);
+
+	kfree(info);
+}
+
+static const struct kobj_type ualink_info_ktype = {
+	.release = ualink_info_release,
+	.sysfs_ops = &kobj_sysfs_ops
+};
+
+int amdgpu_ualink_sysfs_init(struct amdgpu_device *adev)
+{
+	struct amdgpu_ualink_info *info = NULL;
+	int r;
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->ppod.accel_id = 0xffffffff;
+	info->ppod.bandwidth = 0xffffffff;
+	info->ppod.latency = 0xffffffff;
+	info->vpod.id = 0xffffffff;
+	info->vpod.addr_mode = AMDGPU_UALINK_ADDR_MODE_MAX;
+
+	r = kobject_init_and_add(&info->kobj, &ualink_info_ktype,
+				 &adev->dev->kobj, "ualink");
+	if (r)
+		goto err_put_info;
+	r = sysfs_create_files(&info->kobj, ualink_info_attrs);
+	if (r)
+		goto err_del_info;
+
+	adev->ualink.info = info;
+
+	return r;
+
+err_del_info:
+	kobject_del(&info->kobj);
+err_put_info:
+	kobject_put(&info->kobj);
+	return r;
+}
+
+void amdgpu_ualink_sysfs_fini(struct amdgpu_device *adev)
+{
+	if (adev->ualink.info) {
+		sysfs_remove_files(&adev->ualink.info->kobj,
+				   ualink_info_attrs);
+		kobject_del(&adev->ualink.info->kobj);
+		kobject_put(&adev->ualink.info->kobj);
+		adev->ualink.info = NULL;
+	}
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h
new file mode 100644
index 0000000000000..390fb5653aaf5
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef AMDGPU_UALINK_H
+#define AMDGPU_UALINK_H
+
+#include <linux/uuid.h>
+
+#define AMDGPU_UALINK_ACCEL_MAX 256
+#define AMDGPU_UALINK_LOCAL_ACCELS_MAX 8
+
+enum amdgpu_ualink_type {
+	AMDGPU_UALINK_TYPE_UALOE = 0,
+	AMDGPU_UALINK_TYPE_UALINK = 1,
+	AMDGPU_UALINK_TYPE_MAX
+};
+
+enum amdgpu_ualink_accel_state {
+	AMDGPU_UALINK_ACCEL_STATE_UNCONFIGURED = 0,
+	AMDGPU_UALINK_ACCEL_STATE_CONFIGURED,
+	AMDGPU_UALINK_ACCEL_STATE_READY,
+	AMDGPU_UALINK_ACCEL_STATE_ACTIVE,
+	AMDGPU_UALINK_ACCEL_STATE_ERROR,
+	AMDGPU_UALINK_ACCEL_STATE_MAX
+};
+
+enum amdgpu_ualink_addr_mode {
+	AMDGPU_UALINK_ADDR_MODE_SOURCE_ALIAS = 0,
+	AMDGPU_UALINK_ADDR_MODE_SOURCE_IDENT = 1,
+	AMDGPU_UALINK_ADDR_MODE_MAX
+};
+
+/* Physical pod info shared between query and setup API */
+struct amdgpu_ualink_ppod_info {
+	u32 accel_id;
+	u32 bandwidth;
+	u32 latency;
+	u32 size;
+	uuid_t id;
+};
+
+/* Virtual pod info shared between query and config API */
+struct amdgpu_ualink_vpod_info {
+	u32 id;
+	u32 size;
+	u32 addr_mode;
+	DECLARE_BITMAP(active_accel_bits, AMDGPU_UALINK_ACCEL_MAX);
+};
+
+/* Query current UALink physical and virtual pod info */
+struct amdgpu_ualink_info {
+	struct kobject kobj;
+	enum amdgpu_ualink_type link_type;
+	enum amdgpu_ualink_accel_state accel_state;
+	struct amdgpu_ualink_ppod_info ppod;
+	struct amdgpu_ualink_vpod_info vpod;
+	/* Local accelerator array in the vpod derived from
+	 * vpod.active_accel_bits, in arbitrary order
+	 */
+	u32 n_local_accels;
+	u32 local_accels[AMDGPU_UALINK_LOCAL_ACCELS_MAX];
+};
+#define to_ualink_info(ko) container_of(ko, struct amdgpu_ualink_info, kobj)
+
+struct amdgpu_ualink_mgr {
+	struct amdgpu_ualink_info *info;
+};
+
+int amdgpu_ualink_sysfs_init(struct amdgpu_device *adev);
+void amdgpu_ualink_sysfs_fini(struct amdgpu_device *adev);
+
+#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c b/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c
index ac4a4de9c932a..d5f945617f488 100644
--- a/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c
@@ -444,6 +444,17 @@ static int soc_v1_0_common_late_init(struct amdgpu_ip_block *ip_block)
 
 static int soc_v1_0_common_sw_init(struct amdgpu_ip_block *ip_block)
 {
+	struct amdgpu_device *adev = ip_block->adev;
+
+	return amdgpu_ualink_sysfs_init(adev);
+}
+
+static int soc_v1_0_common_sw_fini(struct amdgpu_ip_block *ip_block)
+{
+	struct amdgpu_device *adev = ip_block->adev;
+
+	amdgpu_ualink_sysfs_fini(adev);
+
 	return 0;
 }
 
@@ -500,6 +511,7 @@ static const struct amd_ip_funcs soc_v1_0_common_ip_funcs = {
 	.early_init = soc_v1_0_common_early_init,
 	.late_init = soc_v1_0_common_late_init,
 	.sw_init = soc_v1_0_common_sw_init,
+	.sw_fini = soc_v1_0_common_sw_fini,
 	.hw_init = soc_v1_0_common_hw_init,
 	.hw_fini = soc_v1_0_common_hw_fini,
 	.suspend = soc_v1_0_common_suspend,
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.