All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions
@ 2026-09-21 20:00 James Zhu
  0 siblings, 0 replies; 36+ messages in thread
From: James Zhu @ 2026-09-21 20:00 UTC (permalink / raw)
  To: amd-gfx, alexander.deucher, Bing.Ma; +Cc: jamesz, Bing Ma

Add the DRM_AMDGPU_PROFILER ioctl number and its argument structures, the
UAPI for GPU performance monitoring features such as SPM (Stream
Performance Monitor).

struct drm_amdgpu_profiler_args carries an operation selector and a union
of per-operation parameters, currently AMDGPU_PROFILER_OP_GET_VER and
AMDGPU_PROFILER_OP_SPM. The SPM operations and the layout of the
destination buffer are described by struct drm_amdgpu_spm_args and
struct drm_amdgpu_spm_buffer_header.

The ioctl number and struct layout are frozen here. The implementation
lands in the rest of this series and the ioctl is not registered in
amdgpu_ioctls_kms[] until then, so this header change alone does not make
the interface reachable.

Signed-off-by: James Zhu <James.Zhu@amd.com>
Reviewed-by: Bing Ma <bing.ma@amd.com>
---
 include/uapi/drm/amdgpu_drm.h | 124 ++++++++++++++++++++++++++++++++++
 1 file changed, 124 insertions(+)

diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h
index 9e5fa6f53e59..285949d63eb6 100644
--- a/include/uapi/drm/amdgpu_drm.h
+++ b/include/uapi/drm/amdgpu_drm.h
@@ -60,6 +60,7 @@ extern "C" {
 #define DRM_AMDGPU_GEM_LIST_HANDLES	0x19
 #define DRM_AMDGPU_PROC_OPTIONS		0x1A
 #define DRM_AMDGPU_TRAP			0x1B
+#define DRM_AMDGPU_PROFILER		0x1C
 
 #define DRM_IOCTL_AMDGPU_GEM_CREATE	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create)
 #define DRM_IOCTL_AMDGPU_GEM_MMAP	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap)
@@ -83,6 +84,7 @@ extern "C" {
 #define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles)
 #define DRM_IOCTL_AMDGPU_PROC_OPTIONS	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_PROC_OPTIONS, struct drm_amdgpu_proc_options)
 #define DRM_IOCTL_AMDGPU_TRAP	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_TRAP, union drm_amdgpu_trap)
+#define DRM_IOCTL_AMDGPU_PROFILER	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_PROFILER, struct drm_amdgpu_profiler_args)
 
 /**
  * DOC: memory domains
@@ -1821,6 +1823,128 @@ struct drm_amdgpu_proc_options {
 	} kfd_sigbus_delay;
 };
 
+/*
+ * Supported SPM (Stream Performance Monitor) Operations
+ */
+/**
+ * drm_amdgpu_spm_op - SPM ioctl operations
+ */
+enum drm_amdgpu_spm_op {
+	AMDGPU_SPM_OP_ACQUIRE = 1,
+	AMDGPU_SPM_OP_RELEASE,
+	AMDGPU_SPM_OP_SET_DEST_BUF
+};
+
+/**
+ * drm_amdgpu_spm_args - Arguments for SPM ioctl
+ *
+ * @dest_buf: [in] used for the address of the destination buffer
+ *            in @AMDGPU_SPM_OP_SET_DEST_BUF
+ * @buf_size: [in] size of the destination buffer
+ * @op: [in] specifies the operation to perform
+ * @timeout: [in/out] timeout in milliseconds (in), amount of time left
+ *           in the timeout window (out)
+ * @bytes_copied: [out] total amount of data that was copied to the previous dest_buf
+ * @has_data_loss: [out] non-zero if data was lost (e.g. due to ring buffer overflow),
+ *                 zero otherwise
+ * @reserved: must be zero for forward compatibility
+ *
+ * This ioctl performs different functions depending on the @op parameter.
+ *
+ * AMDGPU_SPM_OP_ACQUIRE
+ * ------------------------
+ *
+ * Acquires exclusive access of SPM on the specified XCP for the calling process.
+ * This must be called before using AMDGPU_SPM_OP_SET_DEST_BUF.
+ *
+ * AMDGPU_SPM_OP_RELEASE
+ * ------------------------
+ *
+ * Releases exclusive access of SPM on the specified XCP for the calling process,
+ * which allows another process to acquire it in the future.
+ *
+ * AMDGPU_SPM_OP_SET_DEST_BUF
+ * -----------------------------
+ *
+ * If @dest_buf is NULL, the destination buffer address is unset and copying of counters
+ * is stopped.
+ *
+ * If @dest_buf is not NULL, it specifies the pointer to a new destination buffer.
+ * @buf_size specifies the size of the buffer.
+ *
+ * If @timeout is non-0, the call will wait for up to @timeout ms for the previous
+ * buffer to be filled. If the previous buffer is filled before the timeout, @timeout
+ * is updated with the time remaining. If the timeout is exceeded, the function
+ * copies any partial data available into the previous user buffer and returns success.
+ * The amount of valid data in the previous user buffer is indicated by @bytes_copied.
+ *
+ * If @timeout is 0, the function immediately replaces the previous destination buffer
+ * without waiting for the previous buffer to be filled. That means the previous buffer
+ * may only be partially filled, and @bytes_copied will indicate how much data has been
+ * copied to it.
+ *
+ * If data was lost, e.g. due to a ring buffer overflow, @has_data_loss will be non-0.
+ *
+ * Returns negative error code on failure, 0 on success.
+ */
+struct drm_amdgpu_spm_args {
+	__u64 dest_buf;
+	__u32 buf_size;
+	__u32 op;
+	__u32 timeout;
+	__u32 bytes_copied;
+	__u32 has_data_loss;
+	__u32 reserved[9];	/* must be zero; pads this struct to 64 bytes */
+};
+
+/**
+ * drm_amdgpu_spm_buffer_header - SPM Buffer header for drm_amdgpu_spm_args->dest_buf
+ *
+ * @version:       [out] SPM version (0-23: minor, 24-31: major)
+ * @bytes_copied:  [out] Amount of data copied for this sub-block
+ * @has_data_loss: [out] Boolean indicating whether data was lost for this sub-block
+ *                       (e.g. due to a ring-buffer overflow)
+ * @reserved:      Reserved for future use, must be zero
+ *
+ * This header precedes the SPM data in the destination buffer. The reserved
+ * field maintains 64-byte cache line alignment (32 bytes total).
+ */
+struct drm_amdgpu_spm_buffer_header {
+	__u32 version; /* 0-23: minor, 24-31: major */
+	__u32 bytes_copied;
+	__u32 has_data_loss;
+	__u32 reserved[5]; /* pad to 32 bytes (64-byte cache line / 2) */
+};
+
+/* Supported Profiler Operation */
+#define AMDGPU_PROFILER_OP_GET_VER		1 /* get profiler UAPI version */
+#define AMDGPU_PROFILER_OP_SPM			2
+
+/*
+ * Profiler IOCTL
+ */
+/**
+ * struct drm_amdgpu_profiler_args - Profiler IOCTL arguments
+ * @op: Operation to perform (AMDGPU_PROFILER_OP_*)
+ * @pad: Must be zero for input validation
+ * @data: Operation-specific parameters (union)
+ *
+ * GET_VER operation:
+ * Returns the profiler UAPI version in @data.version as (MAJOR<<24) | MINOR.
+ * Input: @data must be zero (reserved for forward compatibility).
+ *
+ * Design note: GET_VER is intentionally a parameter-free query returning
+ * the global profiler UAPI version.
+ */
+struct drm_amdgpu_profiler_args {
+	__u32 op;				/* AMDGPU_PROFILER_OP_* */
+	__u32 pad;				/* must be zero */
+	union {
+		__u32 version;		/* GET_VER: out (MAJOR<<24) | MINOR */
+		struct drm_amdgpu_spm_args spm;
+	} data;
+};
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions
@ 2026-09-21 20:03 James Zhu
  2026-09-21 20:03 ` [PATCH 02/12] drm/amdgpu: Add profiler UAPI infrastructure James Zhu
                   ` (12 more replies)
  0 siblings, 13 replies; 36+ messages in thread
From: James Zhu @ 2026-09-21 20:03 UTC (permalink / raw)
  To: amd-gfx, alexander.deucher, Bing.Ma; +Cc: jamesz, Bing Ma

Add the DRM_AMDGPU_PROFILER ioctl number and its argument structures, the
UAPI for GPU performance monitoring features such as SPM (Stream
Performance Monitor).

struct drm_amdgpu_profiler_args carries an operation selector and a union
of per-operation parameters, currently AMDGPU_PROFILER_OP_GET_VER and
AMDGPU_PROFILER_OP_SPM. The SPM operations and the layout of the
destination buffer are described by struct drm_amdgpu_spm_args and
struct drm_amdgpu_spm_buffer_header.

The ioctl number and struct layout are frozen here. The implementation
lands in the rest of this series and the ioctl is not registered in
amdgpu_ioctls_kms[] until then, so this header change alone does not make
the interface reachable.

Signed-off-by: James Zhu <James.Zhu@amd.com>
Reviewed-by: Bing Ma <bing.ma@amd.com>
---
 include/uapi/drm/amdgpu_drm.h | 124 ++++++++++++++++++++++++++++++++++
 1 file changed, 124 insertions(+)

diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h
index 9e5fa6f53e59..285949d63eb6 100644
--- a/include/uapi/drm/amdgpu_drm.h
+++ b/include/uapi/drm/amdgpu_drm.h
@@ -60,6 +60,7 @@ extern "C" {
 #define DRM_AMDGPU_GEM_LIST_HANDLES	0x19
 #define DRM_AMDGPU_PROC_OPTIONS		0x1A
 #define DRM_AMDGPU_TRAP			0x1B
+#define DRM_AMDGPU_PROFILER		0x1C
 
 #define DRM_IOCTL_AMDGPU_GEM_CREATE	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create)
 #define DRM_IOCTL_AMDGPU_GEM_MMAP	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap)
@@ -83,6 +84,7 @@ extern "C" {
 #define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles)
 #define DRM_IOCTL_AMDGPU_PROC_OPTIONS	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_PROC_OPTIONS, struct drm_amdgpu_proc_options)
 #define DRM_IOCTL_AMDGPU_TRAP	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_TRAP, union drm_amdgpu_trap)
+#define DRM_IOCTL_AMDGPU_PROFILER	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_PROFILER, struct drm_amdgpu_profiler_args)
 
 /**
  * DOC: memory domains
@@ -1821,6 +1823,128 @@ struct drm_amdgpu_proc_options {
 	} kfd_sigbus_delay;
 };
 
+/*
+ * Supported SPM (Stream Performance Monitor) Operations
+ */
+/**
+ * drm_amdgpu_spm_op - SPM ioctl operations
+ */
+enum drm_amdgpu_spm_op {
+	AMDGPU_SPM_OP_ACQUIRE = 1,
+	AMDGPU_SPM_OP_RELEASE,
+	AMDGPU_SPM_OP_SET_DEST_BUF
+};
+
+/**
+ * drm_amdgpu_spm_args - Arguments for SPM ioctl
+ *
+ * @dest_buf: [in] used for the address of the destination buffer
+ *            in @AMDGPU_SPM_OP_SET_DEST_BUF
+ * @buf_size: [in] size of the destination buffer
+ * @op: [in] specifies the operation to perform
+ * @timeout: [in/out] timeout in milliseconds (in), amount of time left
+ *           in the timeout window (out)
+ * @bytes_copied: [out] total amount of data that was copied to the previous dest_buf
+ * @has_data_loss: [out] non-zero if data was lost (e.g. due to ring buffer overflow),
+ *                 zero otherwise
+ * @reserved: must be zero for forward compatibility
+ *
+ * This ioctl performs different functions depending on the @op parameter.
+ *
+ * AMDGPU_SPM_OP_ACQUIRE
+ * ------------------------
+ *
+ * Acquires exclusive access of SPM on the specified XCP for the calling process.
+ * This must be called before using AMDGPU_SPM_OP_SET_DEST_BUF.
+ *
+ * AMDGPU_SPM_OP_RELEASE
+ * ------------------------
+ *
+ * Releases exclusive access of SPM on the specified XCP for the calling process,
+ * which allows another process to acquire it in the future.
+ *
+ * AMDGPU_SPM_OP_SET_DEST_BUF
+ * -----------------------------
+ *
+ * If @dest_buf is NULL, the destination buffer address is unset and copying of counters
+ * is stopped.
+ *
+ * If @dest_buf is not NULL, it specifies the pointer to a new destination buffer.
+ * @buf_size specifies the size of the buffer.
+ *
+ * If @timeout is non-0, the call will wait for up to @timeout ms for the previous
+ * buffer to be filled. If the previous buffer is filled before the timeout, @timeout
+ * is updated with the time remaining. If the timeout is exceeded, the function
+ * copies any partial data available into the previous user buffer and returns success.
+ * The amount of valid data in the previous user buffer is indicated by @bytes_copied.
+ *
+ * If @timeout is 0, the function immediately replaces the previous destination buffer
+ * without waiting for the previous buffer to be filled. That means the previous buffer
+ * may only be partially filled, and @bytes_copied will indicate how much data has been
+ * copied to it.
+ *
+ * If data was lost, e.g. due to a ring buffer overflow, @has_data_loss will be non-0.
+ *
+ * Returns negative error code on failure, 0 on success.
+ */
+struct drm_amdgpu_spm_args {
+	__u64 dest_buf;
+	__u32 buf_size;
+	__u32 op;
+	__u32 timeout;
+	__u32 bytes_copied;
+	__u32 has_data_loss;
+	__u32 reserved[9];	/* must be zero; pads this struct to 64 bytes */
+};
+
+/**
+ * drm_amdgpu_spm_buffer_header - SPM Buffer header for drm_amdgpu_spm_args->dest_buf
+ *
+ * @version:       [out] SPM version (0-23: minor, 24-31: major)
+ * @bytes_copied:  [out] Amount of data copied for this sub-block
+ * @has_data_loss: [out] Boolean indicating whether data was lost for this sub-block
+ *                       (e.g. due to a ring-buffer overflow)
+ * @reserved:      Reserved for future use, must be zero
+ *
+ * This header precedes the SPM data in the destination buffer. The reserved
+ * field maintains 64-byte cache line alignment (32 bytes total).
+ */
+struct drm_amdgpu_spm_buffer_header {
+	__u32 version; /* 0-23: minor, 24-31: major */
+	__u32 bytes_copied;
+	__u32 has_data_loss;
+	__u32 reserved[5]; /* pad to 32 bytes (64-byte cache line / 2) */
+};
+
+/* Supported Profiler Operation */
+#define AMDGPU_PROFILER_OP_GET_VER		1 /* get profiler UAPI version */
+#define AMDGPU_PROFILER_OP_SPM			2
+
+/*
+ * Profiler IOCTL
+ */
+/**
+ * struct drm_amdgpu_profiler_args - Profiler IOCTL arguments
+ * @op: Operation to perform (AMDGPU_PROFILER_OP_*)
+ * @pad: Must be zero for input validation
+ * @data: Operation-specific parameters (union)
+ *
+ * GET_VER operation:
+ * Returns the profiler UAPI version in @data.version as (MAJOR<<24) | MINOR.
+ * Input: @data must be zero (reserved for forward compatibility).
+ *
+ * Design note: GET_VER is intentionally a parameter-free query returning
+ * the global profiler UAPI version.
+ */
+struct drm_amdgpu_profiler_args {
+	__u32 op;				/* AMDGPU_PROFILER_OP_* */
+	__u32 pad;				/* must be zero */
+	union {
+		__u32 version;		/* GET_VER: out (MAJOR<<24) | MINOR */
+		struct drm_amdgpu_spm_args spm;
+	} data;
+};
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH 02/12] drm/amdgpu: Add profiler UAPI infrastructure
  2026-09-21 20:03 [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions James Zhu
@ 2026-09-21 20:03 ` James Zhu
  2026-09-21 20:03 ` [PATCH 03/12] drm/amdgpu: add generic RLC SPM interface layer James Zhu
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 36+ messages in thread
From: James Zhu @ 2026-09-21 20:03 UTC (permalink / raw)
  To: amd-gfx, alexander.deucher, Bing.Ma; +Cc: jamesz, Bing Ma

Add the DRM_AMDGPU_PROFILER ioctl handler and its manager, the base for
GPU performance monitoring features such as SPM (Stream Performance
Monitor).

The manager is per-XCP (eXtended Compute Partition) and refcounted, so
several clients can share a partition. Clients with no partition assigned
are rejected on a partitioned device, to keep partition 0's counters
isolated. Teardown is split into a prepare/fini pair so that in-flight
ioctls are drained across drm_dev_unplug().

The ioctl is not registered in amdgpu_ioctls_kms[] and the SPM operations
return -EOPNOTSUPP. Both land later in this series, so the UAPI only
becomes reachable once it is fully implemented.

Signed-off-by: James Zhu <James.Zhu@amd.com>
Reviewed-by: Bing Ma <bing.ma@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/Makefile          |   2 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu.h          |   8 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_device.c   |   3 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c      |  16 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c      |  11 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_profiler.c | 303 +++++++++++++++++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_profiler.h |  92 ++++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c      |  98 ++++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h      |  43 +++
 9 files changed, 575 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_profiler.c
 create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_profiler.h
 create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
 create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h

diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
index ec199715ee76..b17d8f7b4e8f 100644
--- a/drivers/gpu/drm/amd/amdgpu/Makefile
+++ b/drivers/gpu/drm/amd/amdgpu/Makefile
@@ -72,7 +72,7 @@ amdgpu-y += amdgpu_device.o amdgpu_reg_access.o amdgpu_doorbell_mgr.o amdgpu_kms
 	amdgpu_eeprom.o amdgpu_mca.o amdgpu_psp_ta.o amdgpu_lsdma.o amdgpu_lockdep.o \
 	amdgpu_ring_mux.o amdgpu_xcp.o amdgpu_seq64.o amdgpu_dev_coredump.o \
 	amdgpu_cper.o amdgpu_userq_fence.o amdgpu_eviction_fence.o amdgpu_ip.o \
-	amdgpu_wb.o amdgpu_trap.o amdgpu_events.o
+	amdgpu_wb.o amdgpu_trap.o amdgpu_events.o amdgpu_profiler.o amdgpu_spm.o
 
 amdgpu-$(CONFIG_PROC_FS) += amdgpu_fdinfo.o
 
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index e3763c197138..244215320916 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -110,6 +110,7 @@
 #include "amdgpu_seq64.h"
 #include "amdgpu_reg_state.h"
 #include "amdgpu_userq.h"
+#include "amdgpu_profiler.h"
 #include "amdgpu_eviction_fence.h"
 #include "amdgpu_wb.h"
 #include "amdgpu_ip.h"
@@ -424,6 +425,11 @@ struct amdgpu_fpriv {
 
 	/** GPU partition selection */
 	uint32_t		xcp_id;
+	/** Profiler XCP manager this fd holds a reference on, latched by
+	 *  amdgpu_profiler_mgr_init(). NULL if no reference was taken, which
+	 *  gates the matching release and the profiler ioctl.
+	 */
+	struct amdgpu_profiler_xcp_mgr	*prof_xcp_mgr;
 };
 
 int amdgpu_file_to_fpriv(struct file *filp, struct amdgpu_fpriv **fpriv);
@@ -960,6 +966,8 @@ struct amdgpu_device {
 
 	struct amdgpu_uma_carveout_info uma_info;
 
+	struct amdgpu_profiler_mgr      prof_mgr;
+
 	/* KFD
 	 * Must be last --ends in a flexible-array member.
 	 */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 380989f4c965..a4703bc28359 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -3686,6 +3686,7 @@ int amdgpu_device_init(struct amdgpu_device *adev,
 	mutex_init(&adev->gfx.userq_sch_mutex);
 	mutex_init(&adev->gfx.workload_profile_mutex);
 	mutex_init(&adev->vcn.workload_profile_mutex);
+	amdgpu_profiler_device_init(adev);
 
 	spin_lock_init(&adev->irq.lock);
 
@@ -4244,6 +4245,8 @@ void amdgpu_device_fini_sw(struct amdgpu_device *adev)
 	kfree(adev->pci_state);
 	kfree(adev->pcie_reset_ctx.swds_pcistate);
 	kfree(adev->pcie_reset_ctx.swus_pcistate);
+
+	amdgpu_profiler_device_sw_fini(adev);
 }
 
 /**
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index a93ee106ab92..03bbf101bf23 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -2556,9 +2556,25 @@ amdgpu_pci_remove(struct pci_dev *pdev)
 	struct amdgpu_device *adev = drm_to_adev(dev);
 
 	amdgpu_ras_eeprom_check_and_recover(adev);
+	/*
+	 * Signal profiler teardown and wake any blocked SPM waiters so their
+	 * ioctls can exit. This must run before drm_dev_unplug(dev) below,
+	 * which is what drains in-flight profiler ioctls via SRCU: a profiler
+	 * ioctl blocked in a client-supplied SPM wait would otherwise stall
+	 * that unplug until the client's timeout expired. (XCP partition nodes
+	 * redirect their ioctls to the primary drm_device in
+	 * amdgpu_xcp_dev_alloc(), so they register on the primary device's SRCU
+	 * and amdgpu_xcp_dev_unplug() itself never waits on a profiler ioctl.)
+	 */
+	amdgpu_profiler_device_prepare_fini(adev);
 	amdgpu_xcp_dev_unplug(adev);
 	amdgpu_gmc_prepare_nps_mode_change(adev);
 	drm_dev_unplug(dev);
+	/*
+	 * Release profiler/SPM state now that drm_dev_unplug() guarantees no
+	 * ioctl is in flight. HW is still live (before amdgpu_driver_unload_kms()).
+	 */
+	amdgpu_profiler_device_fini(adev);
 
 	if (adev->pm.rpm_mode != AMDGPU_RUNPM_NONE) {
 		pm_runtime_get_sync(dev->dev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
index 47e4680db797..57fa69c0382d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
@@ -1647,6 +1647,10 @@ int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
 			dev_dbg(adev->dev, "trap handler not enabled");
 	}
 
+	/* Profiler is optional - don't fail open if unavailable */
+	if (amdgpu_profiler_mgr_init(adev, fpriv))
+		dev_dbg(adev->dev, "Can't setup profiler, continuing without it\n");
+
 	amdgpu_ctx_mgr_init(&fpriv->ctx_mgr, adev);
 
 	file_priv->driver_priv = fpriv;
@@ -1693,6 +1697,13 @@ void amdgpu_driver_postclose_kms(struct drm_device *dev,
 
 	pm_runtime_get_sync(dev->dev);
 
+	/* Drop the profiler reference here rather than in ->release: postclose
+	 * also runs for in-kernel drm_client teardown (drm_file_free), and
+	 * before kfree(fpriv) below. Placed after pm_runtime_get_sync() so a
+	 * later SPM release can touch registers on a resumed device.
+	 */
+	amdgpu_profiler_mgr_fini(file_priv);
+
 	if (amdgpu_device_ip_get_ip_block(adev, AMD_IP_BLOCK_TYPE_UVD) != NULL)
 		amdgpu_uvd_free_handles(adev, file_priv);
 	if (amdgpu_device_ip_get_ip_block(adev, AMD_IP_BLOCK_TYPE_VCE) != NULL)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_profiler.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_profiler.c
new file mode 100644
index 000000000000..da322d2e6431
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_profiler.c
@@ -0,0 +1,303 @@
+// 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 <drm/drm_drv.h>
+
+#include "amdgpu.h"
+
+/*
+ * Profiler revision change log
+ *
+ * 1.0 - Initial revision
+ */
+
+#define AMDGPU_PROFILER_VERSION_MAJOR 1
+#define AMDGPU_PROFILER_VERSION_MINOR 0
+
+int amdgpu_profiler_ioctl(struct drm_device *dev,
+			  void *data,
+			  struct drm_file *filp)
+{
+	struct amdgpu_device *adev = drm_to_adev(dev);
+	struct amdgpu_fpriv *fpriv = filp->driver_priv;
+	struct amdgpu_profiler_mgr *prof_mgr = &adev->prof_mgr;
+	struct drm_amdgpu_profiler_args *args = data;
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr = fpriv->prof_xcp_mgr;
+	int ret = 0, idx;
+
+	if (args->pad)
+		return -EINVAL;
+
+	/* Reject clients that never took a profiler reference, i.e. never
+	 * successfully called amdgpu_profiler_mgr_init(). Without this an fd
+	 * that never initialized could ride on another client's XCP-0
+	 * reference and reach amdgpu_spm_ioctl().
+	 */
+	if (!prof_xcp_mgr)
+		return -EACCES;
+
+	/* Prevent ioctl from racing with device removal. drm_dev_enter()
+	 * fails once drm_dev_unplug() has been called, and drm_dev_unplug()
+	 * blocks until every in-flight drm_dev_enter() section (this ioctl
+	 * included) has exited. The torn_down flag, set before unplug, keeps
+	 * new ioctls from touching resources that are about to be released.
+	 */
+	if (!drm_dev_enter(dev, &idx))
+		return -ENODEV;
+
+	mutex_lock(&prof_mgr->mutex);
+	if (!prof_xcp_mgr->refcount || prof_xcp_mgr->torn_down) {
+		dev_dbg(adev->dev, "Profiler IOCTL op %u on xcp %u %s\n",
+			args->op, prof_xcp_mgr->xcp_id,
+			prof_xcp_mgr->torn_down ? "torn down" : "not initialized");
+		mutex_unlock(&prof_mgr->mutex);
+		ret = -ENODEV;
+		goto exit;
+	}
+	/*
+	 * Dispatch without prof_mgr->mutex: holding it here would let a
+	 * client-supplied SPM wait in amdgpu_spm_ioctl() stall every other
+	 * profiler fd. The fd's own reference keeps refcount >= 1 so a
+	 * concurrent close (amdgpu_profiler_mgr_fini) cannot release the
+	 * manager (and its spm_mgr) under us; device teardown is instead
+	 * excluded by the drm_dev_enter() section that drm_dev_unplug() drains.
+	 * The reference does not serialize spm_mgr mutation against
+	 * amdgpu_spm_mgr_release_owner() on another fd's close, so the SPM
+	 * layer serializes that itself by taking prof_xcp_mgr->mutex around
+	 * spm_mgr->file on both the acquire and the release/close paths.
+	 */
+	mutex_unlock(&prof_mgr->mutex);
+
+	switch (args->op) {
+	case AMDGPU_PROFILER_OP_GET_VER:
+		if (memchr_inv(&args->data, 0, sizeof(args->data))) {
+			ret = -EINVAL;
+		} else {
+			args->data.version = ((u32)AMDGPU_PROFILER_VERSION_MAJOR << 24) |
+					     (AMDGPU_PROFILER_VERSION_MINOR & GENMASK(23, 0));
+			ret = 0;
+		}
+		break;
+
+	case AMDGPU_PROFILER_OP_SPM:
+		ret = amdgpu_spm_ioctl(dev, &args->data.spm, filp);
+		break;
+
+	default:
+		dev_dbg(adev->dev, "Invalid profiler op: %u\n", args->op);
+		ret = -EINVAL;
+		break;
+	}
+
+exit:
+	drm_dev_exit(idx);
+	return ret;
+}
+
+void amdgpu_profiler_device_init(struct amdgpu_device *adev)
+{
+	struct amdgpu_profiler_mgr *prof_mgr = &adev->prof_mgr;
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr;
+	int xcp_id;
+
+	mutex_init(&prof_mgr->mutex);
+
+	/* Initialize adev, xcp_id, and the per-XCP mutex for all XCP managers
+	 * at device init. adev and xcp_id remain constant for the device
+	 * lifetime, eliminating the need for mutex protection when reading
+	 * them. Both these mutexes and prof_mgr->mutex are only destroyed by
+	 * amdgpu_profiler_device_sw_fini(), which runs after the last drm_file
+	 * is gone, so teardown paths (amdgpu_spm_mgr_release_owner() and
+	 * amdgpu_spm_mgr_fini()) can take them regardless of the fd refcount.
+	 */
+	for (xcp_id = 0; xcp_id < MAX_XCP; xcp_id++) {
+		prof_xcp_mgr = &prof_mgr->prof_xcp_mgr[xcp_id];
+		prof_xcp_mgr->adev = adev;
+		prof_xcp_mgr->xcp_id = xcp_id;
+		mutex_init(&prof_xcp_mgr->mutex);
+	}
+}
+
+void amdgpu_profiler_device_sw_fini(struct amdgpu_device *adev)
+{
+	struct amdgpu_profiler_mgr *prof_mgr = &adev->prof_mgr;
+	int xcp_id;
+
+	for (xcp_id = 0; xcp_id < MAX_XCP; xcp_id++)
+		mutex_destroy(&prof_mgr->prof_xcp_mgr[xcp_id].mutex);
+
+	mutex_destroy(&prof_mgr->mutex);
+}
+
+int amdgpu_profiler_mgr_init(struct amdgpu_device *adev,
+			     struct amdgpu_fpriv *fpriv)
+{
+	struct amdgpu_profiler_mgr *prof_mgr = &adev->prof_mgr;
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr;
+	u32 xcp_id;
+
+	/* On a partitioned device, reject clients with no partition
+	 * assigned (AMDGPU_XCP_NO_PARTITION): amdgpu_prof_xcp_id() maps
+	 * them to XCP 0, which would expose partition 0's performance
+	 * counters as a cross-partition side channel. Enforce isolation
+	 * here, since amdgpu_xcp_get_xcc_mask() returns the full device
+	 * mask for NO_PARTITION.
+	 */
+	if (fpriv->xcp_id == AMDGPU_XCP_NO_PARTITION &&
+	    adev->xcp_mgr && adev->xcp_mgr->mode != AMDGPU_XCP_MODE_NONE)
+		return -EINVAL;
+
+	/* fpriv->xcp_id is mutable (rewritten by amdgpu_xcp_select_scheds on
+	 * first submit), so the resolved manager is latched into
+	 * fpriv->prof_xcp_mgr below and close drops the refcount on that same
+	 * XCP.
+	 */
+	xcp_id = amdgpu_prof_xcp_id(fpriv->xcp_id);
+	if (xcp_id >= MAX_XCP || !adev->ddev.render)
+		return -ENODEV;
+
+	mutex_lock(&prof_mgr->mutex);
+
+	prof_xcp_mgr = &prof_mgr->prof_xcp_mgr[xcp_id];
+
+	/* Prevent initialization during device teardown */
+	if (prof_xcp_mgr->torn_down) {
+		mutex_unlock(&prof_mgr->mutex);
+		return -ENODEV;
+	}
+
+	if (!prof_xcp_mgr->refcount) {
+		/* First open - initialize everything */
+		prof_xcp_mgr->refcount = 1;
+		dev_dbg(adev->dev, "Initialize profiler on render %u\n",
+			adev->ddev.render->index);
+		amdgpu_spm_mgr_init(&prof_xcp_mgr->spm_mgr);
+	} else {
+		/* Subsequent opens - just increment refcount */
+		prof_xcp_mgr->refcount++;
+	}
+
+	/* Latch the reference. Only set on the success path, so release and
+	 * the profiler ioctl act only for clients that actually took one.
+	 */
+	fpriv->prof_xcp_mgr = prof_xcp_mgr;
+
+	mutex_unlock(&prof_mgr->mutex);
+	return 0;
+}
+
+static void amdgpu_profiler_xcp_mgr_release(struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr)
+{
+	amdgpu_spm_mgr_fini(&prof_xcp_mgr->spm_mgr);
+}
+
+void amdgpu_profiler_mgr_fini(struct drm_file *filp)
+{
+	struct amdgpu_fpriv *fpriv = filp->driver_priv;
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr = fpriv->prof_xcp_mgr;
+	struct amdgpu_profiler_mgr *prof_mgr;
+
+	/* Only clients that successfully took a reference may drop one. fpriv
+	 * is kzalloc'd, so a client that failed or never called mgr_init has
+	 * this NULL and must not decrement any refcount.
+	 */
+	if (!prof_xcp_mgr)
+		return;
+
+	/* prof_xcp_mgr->adev is set at device init: after unplug
+	 * filp->minor->dev may point at a partition drm_device, so
+	 * drm_to_adev() would be bogus here.
+	 */
+	prof_mgr = &prof_xcp_mgr->adev->prof_mgr;
+
+	/* Release outside prof_mgr->mutex: the release path can block on
+	 * prof_xcp_mgr->mutex during another fd's acquire, and holding
+	 * prof_mgr->mutex there would stall all profiler ioctls. No-op unless
+	 * this fd owns the session.
+	 */
+	amdgpu_spm_mgr_release_owner(&prof_xcp_mgr->spm_mgr, filp);
+
+	mutex_lock(&prof_mgr->mutex);
+	/* Skip if device_fini already tore down this XCP manager */
+	if (!prof_xcp_mgr->torn_down) {
+		if (!WARN_ON(!prof_xcp_mgr->refcount) &&
+		    --prof_xcp_mgr->refcount == 0)
+			amdgpu_profiler_xcp_mgr_release(prof_xcp_mgr);
+	}
+	mutex_unlock(&prof_mgr->mutex);
+}
+
+void amdgpu_profiler_device_prepare_fini(struct amdgpu_device *adev)
+{
+	struct amdgpu_profiler_mgr *prof_mgr = &adev->prof_mgr;
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr;
+	int xcp_id;
+
+	/*
+	 * First stage of PCI-removal teardown, run *before* drm_dev_unplug().
+	 *
+	 * Mark every XCP torn down so no new ioctl proceeds. teardown_cb
+	 * provides the hook for waking a thread blocked in an SPM wait so its
+	 * ioctl can return promptly, but no callback is registered until a
+	 * later patch wires it up; until then this stage only sets torn_down.
+	 * drm_dev_unplug() then drains all in-flight profiler ioctls via SRCU
+	 * without stalling on a client-supplied SPM timeout. The actual
+	 * resource release happens afterwards in amdgpu_profiler_device_fini().
+	 */
+	mutex_lock(&prof_mgr->mutex);
+	for (xcp_id = 0; xcp_id < MAX_XCP; xcp_id++)
+		prof_mgr->prof_xcp_mgr[xcp_id].torn_down = true;
+
+	for (xcp_id = 0; xcp_id < MAX_XCP; xcp_id++) {
+		prof_xcp_mgr = &prof_mgr->prof_xcp_mgr[xcp_id];
+		if (prof_xcp_mgr->refcount > 0 && prof_xcp_mgr->teardown_cb)
+			prof_xcp_mgr->teardown_cb(prof_xcp_mgr);
+	}
+	mutex_unlock(&prof_mgr->mutex);
+}
+
+void amdgpu_profiler_device_fini(struct amdgpu_device *adev)
+{
+	struct amdgpu_profiler_mgr *prof_mgr = &adev->prof_mgr;
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr;
+	int xcp_id;
+
+	/*
+	 * Second stage of PCI-removal teardown, run *after* drm_dev_unplug().
+	 *
+	 * drm_dev_unplug() has already drained every in-flight profiler ioctl
+	 * via SRCU, so nothing can be running here and no active-ioctl drain is
+	 * needed. torn_down was set in amdgpu_profiler_device_prepare_fini().
+	 * Release the HW/SPM resources of any XCP that still holds a session;
+	 * the device is still live (this runs before amdgpu_driver_unload_kms()).
+	 */
+	mutex_lock(&prof_mgr->mutex);
+	for (xcp_id = 0; xcp_id < MAX_XCP; xcp_id++) {
+		prof_xcp_mgr = &prof_mgr->prof_xcp_mgr[xcp_id];
+		if (prof_xcp_mgr->refcount > 0) {
+			amdgpu_profiler_xcp_mgr_release(prof_xcp_mgr);
+			prof_xcp_mgr->refcount = 0;
+		}
+	}
+	mutex_unlock(&prof_mgr->mutex);
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_profiler.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_profiler.h
new file mode 100644
index 000000000000..a220f05c9512
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_profiler.h
@@ -0,0 +1,92 @@
+/* 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_PROFILER_H_
+#define AMDGPU_PROFILER_H_
+
+#include <linux/mutex.h>
+
+#include "amdgpu_xcp.h"
+#include "amdgpu_spm.h"
+
+/* Map XCP ID to profiler array index.
+ * AMDGPU_XCP_NO_PARTITION maps to 0. On a partitioned device the SPM
+ * consumer rejects NO_PARTITION before this mapping (see
+ * amdgpu_profiler_mgr_init()), so mapping to 0 only applies to
+ * non-partitioned systems, which legitimately use XCP 0.
+ */
+static inline u32 amdgpu_prof_xcp_id(u32 xcp_id)
+{
+	return xcp_id == AMDGPU_XCP_NO_PARTITION ? 0 : xcp_id;
+}
+
+/*
+ * y is the member name within amdgpu_profiler_xcp_mgr (e.g., spm_mgr)
+ */
+#define to_prof_xcp_mgr(x, y) \
+	container_of(x, struct amdgpu_profiler_xcp_mgr, y)
+
+struct amdgpu_profiler_xcp_mgr {
+	/* track multiple file opens (protected by prof_mgr->mutex) */
+	unsigned int                   refcount;
+	/* back-pointer to device */
+	struct amdgpu_device           *adev;
+	/* protect xcp_mgr shared resources */
+	struct mutex                   mutex;
+	u32                            xcp_id;
+	/* device removal in progress */
+	bool                           torn_down;
+	/* Called during device teardown (before drm_dev_unplug()) to wake
+	 * threads blocked in SPM waits so their ioctls can exit.
+	 * IMPORTANT: Invoked while holding prof_mgr->mutex.
+	 * Callback MUST NOT attempt to acquire prof_mgr->mutex to avoid deadlock.
+	 */
+	void (*teardown_cb)(struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr);
+
+	/*
+	 * Union scaffolding for future profiler sub-managers (PC, SPM2, etc.).
+	 * Currently only SPM, but designed for extensibility.
+	 */
+	union {
+		struct amdgpu_spm_mgr      spm_mgr;
+	};
+};
+
+struct amdgpu_profiler_mgr {
+	/* protect profiler manager share resource */
+	struct mutex                   mutex;
+	struct amdgpu_profiler_xcp_mgr prof_xcp_mgr[MAX_XCP];
+};
+
+int amdgpu_profiler_ioctl(struct drm_device *dev, void *data,
+			  struct drm_file *filp);
+void amdgpu_profiler_device_init(struct amdgpu_device *adev);
+void amdgpu_profiler_device_sw_fini(struct amdgpu_device *adev);
+int amdgpu_profiler_mgr_init(struct amdgpu_device *adev,
+			     struct amdgpu_fpriv *fpriv);
+void amdgpu_profiler_mgr_fini(struct drm_file *filp);
+void amdgpu_profiler_device_prepare_fini(struct amdgpu_device *adev);
+void amdgpu_profiler_device_fini(struct amdgpu_device *adev);
+
+#endif /* AMDGPU_PROFILER_H_ */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
new file mode 100644
index 000000000000..06747dd22b85
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
@@ -0,0 +1,98 @@
+// 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.h"
+
+/* Check if the calling file owns the SPM session */
+static bool amdgpu_spm_is_owner(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *filp)
+{
+	return spm_mgr->file == filp;
+}
+
+int amdgpu_spm_ioctl(struct drm_device *dev, struct drm_amdgpu_spm_args *args,
+		     struct drm_file *filp)
+{
+	if (memchr_inv(args->reserved, 0, sizeof(args->reserved)))
+		return -EINVAL;
+
+	switch (args->op) {
+	case AMDGPU_SPM_OP_ACQUIRE:
+		/* Implemented in a later patch */
+		return -EOPNOTSUPP;
+
+	case AMDGPU_SPM_OP_RELEASE:
+		/* Implemented in a later patch */
+		return -EOPNOTSUPP;
+
+	case AMDGPU_SPM_OP_SET_DEST_BUF:
+		/* Implemented in a later patch */
+		return -EOPNOTSUPP;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static void amdgpu_spm_init_device(struct amdgpu_spm_mgr *spm_mgr)
+{
+	/* TODO */
+}
+
+static int amdgpu_spm_release(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *filp)
+{
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr = to_prof_xcp_mgr(spm_mgr, spm_mgr);
+
+	/* Full teardown lands with AMDGPU_SPM_OP_RELEASE. Re-check ownership
+	 * under the mutex so a close and a concurrent device teardown cannot
+	 * both release the same session.
+	 */
+	mutex_lock(&prof_xcp_mgr->mutex);
+	if (!spm_mgr->file) {
+		mutex_unlock(&prof_xcp_mgr->mutex);
+		return 0;
+	}
+	if (filp && !amdgpu_spm_is_owner(spm_mgr, filp)) {
+		mutex_unlock(&prof_xcp_mgr->mutex);
+		return -EPERM;
+	}
+	spm_mgr->file = NULL;
+	mutex_unlock(&prof_xcp_mgr->mutex);
+
+	return 0;
+}
+
+void amdgpu_spm_mgr_init(struct amdgpu_spm_mgr *spm_mgr)
+{
+	amdgpu_spm_init_device(spm_mgr);
+}
+
+void amdgpu_spm_mgr_release_owner(struct amdgpu_spm_mgr *spm_mgr,
+				  struct drm_file *filp)
+{
+	amdgpu_spm_release(spm_mgr, filp);
+}
+
+void amdgpu_spm_mgr_fini(struct amdgpu_spm_mgr *spm_mgr)
+{
+	amdgpu_spm_release(spm_mgr, NULL);
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
new file mode 100644
index 000000000000..59fa641ebb86
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
@@ -0,0 +1,43 @@
+/* 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_SPM_H_
+#define AMDGPU_SPM_H_
+
+struct drm_device;
+struct drm_file;
+struct drm_amdgpu_spm_args;
+
+struct amdgpu_spm_mgr {
+	struct drm_file *file;
+};
+
+int amdgpu_spm_ioctl(struct drm_device *dev, struct drm_amdgpu_spm_args *args,
+		     struct drm_file *filp);
+
+void amdgpu_spm_mgr_init(struct amdgpu_spm_mgr *spm_mgr);
+void amdgpu_spm_mgr_fini(struct amdgpu_spm_mgr *spm_mgr);
+void amdgpu_spm_mgr_release_owner(struct amdgpu_spm_mgr *spm_mgr,
+				  struct drm_file *filp);
+
+#endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH 03/12] drm/amdgpu: add generic RLC SPM interface layer
  2026-09-21 20:03 [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions James Zhu
  2026-09-21 20:03 ` [PATCH 02/12] drm/amdgpu: Add profiler UAPI infrastructure James Zhu
@ 2026-09-21 20:03 ` James Zhu
  2026-09-21 20:03 ` [PATCH 04/12] drm/amdgpu: add amdgpu_xcp_get_xcc_mask helper James Zhu
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 36+ messages in thread
From: James Zhu @ 2026-09-21 20:03 UTC (permalink / raw)
  To: amd-gfx, alexander.deucher, Bing.Ma; +Cc: jamesz, Bing Ma

Every GFX IP programs the RLC SPM (Streaming Performance Monitor) block
through its own registers, so without a common layer the SPM manager
would have to open-code that per-IP sequencing at each call site. Add a
hardware-agnostic layer: the manager drives SPM through a few generic
entry points and each IP only supplies register programming via an
amdgpu_spm_funcs table in adev->gfx.spmfuncs.

All SPM programming is issued through the KIQ ring so it is serialised
against other KIQ users and the RLC firmware; each op emits a polling
fence and waits so the firmware has consumed the packet before the call
returns. The per-IP callbacks run under the KIQ ring_lock and may only
emit packets, and set_spm_config_size tells the generic layer how much
ring space one op needs.

acquire reserves a VMID on the GFX hub for the SPM stream; release stops
the stream and waits for that stop to complete before freeing the VMID,
so the RLC can never keep streaming into a VMID that has been reused.

Signed-off-by: James Zhu <James.Zhu@amd.com>
Reviewed-by: Bing Ma <bing.ma@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h |  15 ++
 drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c | 230 ++++++++++++++++++++++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h |   7 +
 3 files changed, 252 insertions(+)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
index aefd4f03b443..fd87075962f9 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
@@ -174,6 +174,20 @@ struct amdgpu_kiq {
 	void			*mqd_backup;
 };
 
+struct amdgpu_spm_funcs {
+	void (*start)(struct amdgpu_device *adev, int xcc_id);
+	void (*stop)(struct amdgpu_device *adev, int xcc_id);
+	void (*set_rdptr)(struct amdgpu_device *adev, int xcc_id, u32 rptr);
+	void (*set_spm_perfmon_ring_buf)(struct amdgpu_device *adev, int xcc_id,
+					 u64 gpu_addr, u32 size);
+	/* Dwords amdgpu_rlc_spm_kiq_submit() reserves on the KIQ ring for one
+	 * submission. Must cover the largest of the callbacks above plus the
+	 * polling fence emitted after them; amdgpu_ring_write() has no bounds
+	 * check, so a short value silently corrupts the ring.
+	 */
+	u32 set_spm_config_size;
+};
+
 /*
  * GFX configurations
  */
@@ -429,6 +443,7 @@ struct amdgpu_gfx {
 	struct amdgpu_mec_bitmap	mec_bitmap[AMDGPU_MAX_GC_INSTANCES];
 	struct amdgpu_kiq		kiq[AMDGPU_MAX_GC_INSTANCES];
 	struct amdgpu_imu		imu;
+	const struct amdgpu_spm_funcs		*spmfuncs;
 	bool				rs64_enable; /* firmware format */
 	const struct firmware		*me_fw;	/* ME firmware */
 	uint32_t			me_fw_version;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c
index 002fae3c380e..79641e537fbb 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c
@@ -622,3 +622,233 @@ void amdgpu_init_rlc_reg_funcs(struct amdgpu_device *adev)
 	else
 		adev->gfx.rlc.reg_funcs = &amdgpu_rlc_reg_funcs;
 }
+
+/**
+ * amdgpu_rlc_spm_kiq_submit - helper for SPM KIQ command submission
+ * @adev: amdgpu device pointer
+ * @xcc_id: XCC instance ID
+ * @emit_fn: callback to emit commands into the ring
+ * @emit_ctx: context pointer passed to emit_fn
+ *
+ * Common pattern for SPM KIQ operations: lock, alloc, emit, fence, commit,
+ * unlock, wait. Centralizes error/undo paths.
+ *
+ * Callers guarantee spmfuncs is non-NULL.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+static int amdgpu_rlc_spm_kiq_submit(struct amdgpu_device *adev, int xcc_id,
+				     void (*emit_fn)(struct amdgpu_device *, int, void *),
+				     void *emit_ctx)
+{
+	struct amdgpu_ring *kiq_ring = &adev->gfx.kiq[xcc_id].ring;
+	unsigned long flags;
+	signed long r;
+	u32 seq;
+	int ret;
+
+	spin_lock_irqsave(&adev->gfx.kiq[xcc_id].ring_lock, flags);
+	ret = amdgpu_ring_alloc(kiq_ring, adev->gfx.spmfuncs->set_spm_config_size);
+	if (ret) {
+		spin_unlock_irqrestore(&adev->gfx.kiq[xcc_id].ring_lock, flags);
+		return ret;
+	}
+
+	emit_fn(adev, xcc_id, emit_ctx);
+
+	ret = amdgpu_fence_emit_polling(kiq_ring, &seq, MAX_KIQ_REG_WAIT);
+	if (ret) {
+		amdgpu_ring_undo(kiq_ring);
+		spin_unlock_irqrestore(&adev->gfx.kiq[xcc_id].ring_lock, flags);
+		dev_warn(adev->dev, "%s SPM KIQ fence emit failed (%d)\n", __func__, ret);
+		return ret;
+	}
+
+	amdgpu_ring_commit(kiq_ring);
+	spin_unlock_irqrestore(&adev->gfx.kiq[xcc_id].ring_lock, flags);
+
+	r = amdgpu_fence_wait_polling(kiq_ring, seq, MAX_KIQ_REG_WAIT);
+	if (r < 1) {
+		dev_warn(adev->dev, "%s SPM KIQ fence timeout (seq=%u)\n", __func__, seq);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static void amdgpu_rlc_spm_emit_start(struct amdgpu_device *adev, int xcc_id,
+			   void *ctx)
+{
+	adev->gfx.spmfuncs->start(adev, xcc_id);
+}
+
+static void amdgpu_rlc_spm_emit_stop(struct amdgpu_device *adev, int xcc_id,
+			  void *ctx)
+{
+	adev->gfx.spmfuncs->stop(adev, xcc_id);
+}
+
+/**
+ * amdgpu_rlc_spm_cntl - start or stop SPM hardware stream
+ * @adev: amdgpu device pointer
+ * @xcc_id: XCC instance ID (must be < adev->gfx.xcc_mask bit count)
+ * @cntl: true to start, false to stop
+ *
+ * Caller must ensure xcc_id is valid - no bounds checking is performed.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int amdgpu_rlc_spm_cntl(struct amdgpu_device *adev, int xcc_id, bool cntl)
+{
+	if (!adev->gfx.spmfuncs)
+		return -EINVAL;
+
+	if (!(cntl ? adev->gfx.spmfuncs->start : adev->gfx.spmfuncs->stop))
+		return -EINVAL;
+
+	if (!adev->gfx.spmfuncs->set_spm_config_size) {
+		if (cntl)
+			adev->gfx.spmfuncs->start(adev, xcc_id);
+		else
+			adev->gfx.spmfuncs->stop(adev, xcc_id);
+		return 0;
+	}
+
+	return amdgpu_rlc_spm_kiq_submit(adev, xcc_id,
+					 cntl ? amdgpu_rlc_spm_emit_start : amdgpu_rlc_spm_emit_stop, NULL);
+}
+
+struct amdgpu_rlc_spm_rdptr_ctx {
+	u32 rptr;
+};
+
+static void amdgpu_rlc_spm_emit_set_rdptr(struct amdgpu_device *adev, int xcc_id, void *ctx)
+{
+	struct amdgpu_rlc_spm_rdptr_ctx *rdptr_ctx = ctx;
+
+	adev->gfx.spmfuncs->set_rdptr(adev, xcc_id, rdptr_ctx->rptr);
+}
+
+/**
+ * amdgpu_rlc_spm_set_rdptr - update SPM ring read pointer
+ * @adev: amdgpu device pointer
+ * @xcc_id: XCC instance ID (must be < adev->gfx.xcc_mask bit count)
+ * @rptr: new read pointer offset
+ *
+ * Caller must ensure xcc_id is valid - no bounds checking is performed.
+ */
+void amdgpu_rlc_spm_set_rdptr(struct amdgpu_device *adev, int xcc_id, u32 rptr)
+{
+	struct amdgpu_rlc_spm_rdptr_ctx rdptr_ctx = { .rptr = rptr };
+	int ret;
+
+	if (!adev->gfx.spmfuncs || !adev->gfx.spmfuncs->set_rdptr)
+		return;
+
+	if (!adev->gfx.spmfuncs->set_spm_config_size) {
+		adev->gfx.spmfuncs->set_rdptr(adev, xcc_id, rptr);
+		return;
+	}
+
+	ret = amdgpu_rlc_spm_kiq_submit(adev, xcc_id, amdgpu_rlc_spm_emit_set_rdptr, &rdptr_ctx);
+	if (ret)
+		dev_warn(adev->dev, "Failed to update SPM rdptr to 0x%x on XCC %d (%d)\n",
+			 rptr, xcc_id, ret);
+}
+
+struct amdgpu_rlc_spm_perfmon_ring_ctx {
+	u64 gpu_addr;
+	u32 size;
+};
+
+static void amdgpu_rlc_spm_emit_perfmon_ring_buf(struct amdgpu_device *adev, int xcc_id, void *ctx)
+{
+	struct amdgpu_rlc_spm_perfmon_ring_ctx *ring_ctx = ctx;
+
+	adev->gfx.spmfuncs->set_spm_perfmon_ring_buf(adev, xcc_id,
+						     ring_ctx->gpu_addr,
+						     ring_ctx->size);
+}
+
+/**
+ * amdgpu_rlc_spm_acquire - set up SPM for a specific XCC instance
+ * @adev: amdgpu device pointer
+ * @xcc_id: XCC instance ID (must be < adev->gfx.xcc_mask bit count)
+ * @vm: virtual memory context for VMID allocation
+ * @gpu_addr: GPU address of SPM ring buffer
+ * @size: size of SPM ring buffer in bytes
+ *
+ * Allocates a reserved VMID, initializes SPM VMID to 0, and programs the
+ * ring buffer address and size. On failure, reverts VMID to 0xf and frees
+ * the reserved VMID.
+ *
+ * Caller must ensure xcc_id is valid - no bounds checking is performed.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int amdgpu_rlc_spm_acquire(struct amdgpu_device *adev, int xcc_id,
+			   struct amdgpu_vm *vm, u64 gpu_addr, u32 size)
+{
+	struct amdgpu_rlc_spm_perfmon_ring_ctx ring_ctx = { .gpu_addr = gpu_addr, .size = size };
+	int r;
+
+	if (!adev->gfx.spmfuncs ||
+	    !adev->gfx.spmfuncs->set_spm_perfmon_ring_buf ||
+	    !adev->gfx.rlc.funcs ||
+	    !adev->gfx.rlc.funcs->update_spm_vmid)
+		return -EINVAL;
+
+	r = amdgpu_vmid_alloc_reserved(adev, vm, AMDGPU_GFXHUB(xcc_id));
+	if (r)
+		return r;
+
+	/* init spm vmid with 0x0 (outside ring_lock to avoid sleep-in-atomic) */
+	adev->gfx.rlc.funcs->update_spm_vmid(adev, xcc_id, NULL, 0);
+
+	if (!adev->gfx.spmfuncs->set_spm_config_size) {
+		adev->gfx.spmfuncs->set_spm_perfmon_ring_buf(adev, xcc_id, gpu_addr, size);
+		return 0;
+	}
+
+	/* set spm ring registers */
+	r = amdgpu_rlc_spm_kiq_submit(adev, xcc_id, amdgpu_rlc_spm_emit_perfmon_ring_buf, &ring_ctx);
+	if (r)
+		goto err_revert;
+
+	return 0;
+
+err_revert:
+	adev->gfx.rlc.funcs->update_spm_vmid(adev, xcc_id, NULL, 0xf);
+	amdgpu_vmid_free_reserved(adev, vm, AMDGPU_GFXHUB(xcc_id));
+	return r;
+}
+
+/**
+ * amdgpu_rlc_spm_release - tear down SPM for a specific XCC instance
+ * @adev: amdgpu device pointer
+ * @xcc_id: XCC instance ID (must be < adev->gfx.xcc_mask bit count)
+ * @vm: virtual memory context for VMID release
+ *
+ * Stops the SPM stream via KIQ, waits for completion, then reverts SPM VMID
+ * to 0xf and frees the reserved VMID. Waiting ensures the firmware has stopped
+ * SPM before the VMID is reclaimed, preventing VMID reuse while hardware is
+ * still active. On stop failure, still reverts VMID and frees reservation
+ * (best-effort cleanup) to prevent resource leaks.
+ *
+ * Caller must ensure xcc_id is valid - no bounds checking is performed.
+ */
+void amdgpu_rlc_spm_release(struct amdgpu_device *adev, int xcc_id, struct amdgpu_vm *vm)
+{
+	int r;
+
+	r = amdgpu_rlc_spm_cntl(adev, xcc_id, false);
+	if (r)
+		dev_warn(adev->dev, "SPM stop failed on XCC %d (error %d), proceeding with VMID cleanup\n",
+			 xcc_id, r);
+
+	/* revert spm vmid with 0xf */
+	if (adev->gfx.rlc.funcs && adev->gfx.rlc.funcs->update_spm_vmid)
+		adev->gfx.rlc.funcs->update_spm_vmid(adev, xcc_id, NULL, 0xf);
+
+	amdgpu_vmid_free_reserved(adev, vm, AMDGPU_GFXHUB(xcc_id));
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h
index 959d60c90dcd..18b925a0d164 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h
@@ -26,6 +26,8 @@
 
 #include "clearstate_defs.h"
 
+struct amdgpu_vm;
+
 #define AMDGPU_MAX_RLC_INSTANCES	8
 
 /* firmware ID used in rlc toc */
@@ -384,4 +386,9 @@ int amdgpu_gfx_rlc_init_microcode(struct amdgpu_device *adev,
 void amdgpu_early_init_rlc_reg_funcs(struct amdgpu_device *adev);
 void amdgpu_init_rlc_reg_funcs(struct amdgpu_device *adev);
 
+int amdgpu_rlc_spm_cntl(struct amdgpu_device *adev, int xcc_id, bool cntl);
+int amdgpu_rlc_spm_acquire(struct amdgpu_device *adev, int xcc_id,
+			   struct amdgpu_vm *vm, u64 gpu_addr, u32 size);
+void amdgpu_rlc_spm_release(struct amdgpu_device *adev, int xcc_id, struct amdgpu_vm *vm);
+void amdgpu_rlc_spm_set_rdptr(struct amdgpu_device *adev, int xcc_id, u32 rptr);
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH 04/12] drm/amdgpu: add amdgpu_xcp_get_xcc_mask helper
  2026-09-21 20:03 [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions James Zhu
  2026-09-21 20:03 ` [PATCH 02/12] drm/amdgpu: Add profiler UAPI infrastructure James Zhu
  2026-09-21 20:03 ` [PATCH 03/12] drm/amdgpu: add generic RLC SPM interface layer James Zhu
@ 2026-09-21 20:03 ` James Zhu
  2026-09-22  5:22   ` Lazar, Lijo
  2026-09-21 20:03 ` [PATCH 05/12] drm/amdgpu: add profiler/spm operation AMDGPU_SPM_OP_ACQUIRE James Zhu
                   ` (9 subsequent siblings)
  12 siblings, 1 reply; 36+ messages in thread
From: James Zhu @ 2026-09-21 20:03 UTC (permalink / raw)
  To: amd-gfx, alexander.deucher, Bing.Ma; +Cc: jamesz, Bing Ma

Add amdgpu_xcp_get_xcc_mask() to return the XCC instance mask for a given
XCP partition. This is needed by upcoming SPM (Stream Performance Monitor)
support, which programs hardware per partition and must operate only on
the XCCs that belong to a given XCP.

For partitioned systems the helper returns only the XCCs belonging to the
specified XCP. When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the device is
not partitioned) it returns the device's full XCC mask (e.g. 0xFF for an
8-XCC device). Note that the full device mask is returned even on a
partitioned device; callers that require partition isolation (e.g. SPM)
must reject AMDGPU_XCP_NO_PARTITION on a partitioned device themselves.

amdgpu_xcp_get_num_xcc() is reworked to take (adev, xcp_id) and derive the
count from this helper via hweight32(). The helper is defined out of line
in amdgpu_xcp.c so that the static inline in amdgpu_xcp.h never has to
dereference the (incomplete) struct amdgpu_device; it only passes the
pointer through.

This is a user-visible change: amdgpu_xcp_get_num_xcc() previously
returned 1 for non-partitioned systems (and for AMDGPU_XCP_NO_PARTITION),
and now returns the actual XCC count (e.g. 8 for an 8-XCC device). CWSR
callers (amdgpu_kms.c, amdgpu_userq.c, mes_userqueue.c) scale the per-XCC
control-stack and debug-memory sizes by num_xcc, so the uAPI-visible
ctl_stack_size and dbg_mem_size values grow accordingly to match the
actual hardware resource requirements, which were previously undersized.

Fixes: 4cd417bfec49 ("drm/amdgpu: Add helper function to get xcc count")
Signed-off-by: James Zhu <James.Zhu@amd.com>
Reviewed-by: Bing Ma <bing.ma@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c    |  2 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c  |  2 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c    | 48 ++++++++++++++++++++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h    | 32 +++++++--------
 drivers/gpu/drm/amd/amdgpu/mes_userqueue.c |  3 +-
 5 files changed, 66 insertions(+), 21 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
index 57fa69c0382d..7782ccd4d28f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
@@ -1501,7 +1501,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
 		fpriv = (struct amdgpu_fpriv *)filp->driver_priv;
 		if (!amdgpu_trap_is_enabled(adev) || !fpriv->trap_obj)
 			return -EOPNOTSUPP;
-		num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv->xcp_id);
+		num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
 		cwsr_info.ctl_stack_size =
 			adev->trap_info->cwsr.xcc_ctl_stack_sz * num_xcc;
 		cwsr_info.dbg_mem_size =
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index db09e879b85e..d19af9cb6ee1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -258,7 +258,7 @@ int amdgpu_userq_input_trap_params_validate(struct amdgpu_usermode_queue *queue,
 	int num_xcc;
 	int r;
 
-	num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv->xcp_id);
+	num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
 	r = amdgpu_trap_validate_params(queue->userq_mgr->adev, trap_params,
 					num_xcc);
 	if (r)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
index 2a56f5ec9c76..90846d55292b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
@@ -390,6 +390,54 @@ int amdgpu_xcp_get_inst_details(struct amdgpu_xcp *xcp,
 	return 0;
 }
 
+/**
+ * amdgpu_xcp_get_xcc_mask - Get XCC instance mask for a partition
+ * @adev: amdgpu device pointer
+ * @xcp_id: XCP partition ID
+ * @xcc_mask: output parameter for XCC instance mask
+ *
+ * Returns the XCC instance mask for the specified XCP partition.
+ * For partitioned systems, returns only the XCCs belonging to this XCP.
+ *
+ * When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the device is not
+ * partitioned), the device's full XCC mask is returned (e.g. 0xFF for an
+ * 8-XCC device). Note that the full device mask is returned even on a
+ * partitioned device: callers that require partition isolation (e.g. SPM)
+ * must reject AMDGPU_XCP_NO_PARTITION on a partitioned device themselves.
+ *
+ * Note: On error, xcc_mask is undefined. Callers must check return code
+ * and only read xcc_mask when return == 0.
+ *
+ * Return: 0 on success (non-partitioned systems or valid XCP found),
+ *         -EINVAL if xcc_mask is NULL, no matching XCP id is found, or the
+ *         partition's GFX IP details are unavailable
+ */
+int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
+			    uint32_t *xcc_mask)
+{
+	struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr;
+	struct amdgpu_xcp *xcp;
+	int i;
+
+	if (!xcc_mask)
+		return -EINVAL;
+
+	if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION) {
+		/* Non-partitioned: return the device's full XCC mask. */
+		*xcc_mask = adev->gfx.xcc_mask;
+		return 0;
+	}
+
+	/* Partitioned system: get this XCP's XCCs */
+	for_each_xcp(xcp_mgr, xcp, i) {
+		if (xcp->id == xcp_id)
+			return amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
+							   xcc_mask);
+	}
+
+	return -EINVAL;
+}
+
 int amdgpu_xcp_dev_register(struct amdgpu_device *adev,
 			const struct pci_device_id *ent)
 {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
index 692508be23fb..6eed82ad49b1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
@@ -44,6 +44,7 @@
 #define XCP_INST_MASK(num_inst, xcp_id)		\
 	(num_inst ? GENMASK(num_inst - 1, 0) << (xcp_id * num_inst) : 0)
 
+struct amdgpu_device;
 struct amdgpu_fpriv;
 
 enum AMDGPU_XCP_IP_BLOCK {
@@ -219,26 +220,23 @@ amdgpu_get_next_xcp(struct amdgpu_xcp_mgr *xcp_mgr, int *from)
 	for (i = 0, xcp = amdgpu_get_next_xcp(xcp_mgr, &i); xcp; \
 	     ++i, xcp = amdgpu_get_next_xcp(xcp_mgr, &i))
 
-static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_xcp_mgr *xcp_mgr,
-					 int xcp_id)
+int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
+			    uint32_t *xcc_mask);
+
+static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_device *adev, int xcp_id)
 {
-	struct amdgpu_xcp *xcp;
 	uint32_t xcc_mask;
-	int i, r;
-
-	if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION)
+	int r, count;
+
+	/* Derive the XCC count from the partition's XCC mask (or the full
+	 * device mask when not partitioned). amdgpu_xcp_get_xcc_mask()
+	 * handles both partitioned and non-partitioned cases.
+	 */
+	r = amdgpu_xcp_get_xcc_mask(adev, xcp_id, &xcc_mask);
+	if (unlikely(r))
 		return 1;
-	for_each_xcp(xcp_mgr, xcp, i) {
-		if (xcp->id == xcp_id) {
-			r = amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
-							&xcc_mask);
-			if (unlikely(r))
-				return 1;
-			else
-				return hweight32(xcc_mask);
-		}
-	}
 
-	return 1;
+	count = hweight32(xcc_mask);
+	return count ? count : 1;
 }
 #endif
diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
index 16aa22b25098..1c7b2f3227e5 100644
--- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
+++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
@@ -503,8 +503,7 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue,
 				uq_mgr_to_fpriv(queue->userq_mgr);
 			int num_xcc;
 
-			num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr,
-							 fpriv->xcp_id);
+			num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
 			trap_params.ctx_save_area_address =
 				compute_mqd->ctx_save_area_va;
 			trap_params.cwsr_sz = compute_mqd->ctx_save_area_size;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH 05/12] drm/amdgpu: add profiler/spm operation AMDGPU_SPM_OP_ACQUIRE
  2026-09-21 20:03 [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions James Zhu
                   ` (2 preceding siblings ...)
  2026-09-21 20:03 ` [PATCH 04/12] drm/amdgpu: add amdgpu_xcp_get_xcc_mask helper James Zhu
@ 2026-09-21 20:03 ` James Zhu
  2026-09-21 20:03 ` [PATCH 06/12] drm/amdgpu: add profiler/spm operation AMDGPU_SPM_OP_SET_DEST_BUF James Zhu
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 36+ messages in thread
From: James Zhu @ 2026-09-21 20:03 UTC (permalink / raw)
  To: amd-gfx, alexander.deucher, Bing.Ma; +Cc: jamesz, Bing Ma

Implement AMDGPU_SPM_OP_ACQUIRE to allow userspace to request exclusive
access to the SPM hardware. Only one drm_file can own SPM at a time; a
second acquire returns -EBUSY.

On acquire the driver does more than record ownership:

 - allocates the per-XCP SPM counter state (amdgpu_spm_cntr) and records
   the owning drm_file and the calling thread group leader, pinned via
   get_task_struct(). The matching put_task_struct() runs on this patch's
   acquire-failure path;

 - for every XCC instance in the XCP's xcc_mask, allocates a 4 MB GTT
   ring buffer BO (amdgpu_bo_create_kernel(), AMDGPU_GEM_DOMAIN_GTT) for
   the SPM data stream, reserving the trailing 32 bytes as a metadata
   area (usable ring_size = 4 MB - 0x20);

 - calls amdgpu_rlc_spm_acquire() per instance, which allocates a
   reserved VMID (amdgpu_vmid_alloc_reserved()), programs the SPM VMID
   into the RLC and programs the perfmon ring registers (ring base, size
   and read pointer) via KIQ.

On failure at any instance the acquire path invokes the per-instance
release helper for every instance in xcc_mask, drops the task reference
and frees the counter state. _amdgpu_spm_release() is only a stub in
this patch, so the per-instance hardware resources (ring BOs, reserved
VMID and RLC perfmon programming) are not torn down on this error path
yet; only the task reference and the amdgpu_spm_cntr allocation are
cleaned up here.

A new per-XCP mutex is added to struct amdgpu_profiler_xcp_mgr to
serialize acquire/release against the shared XCP SPM resources. It is
initialized once for the device lifetime in amdgpu_profiler_device_init()
(alongside adev/xcp_id) so it stays valid for teardown paths such as
amdgpu_spm_mgr_release_owner() and amdgpu_spm_mgr_fini(). The existing
spm_irq_lock spinlock continues to guard spm_cntr publication against the
interrupt handler.

AMDGPU_SPM_OP_RELEASE and full teardown of these resources land in a
follow-up patch.

Signed-off-by: James Zhu <James.Zhu@amd.com>
Reviewed-by: Bing Ma <bing.ma@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c | 151 +++++++++++++++++++++++-
 drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h |  28 +++++
 2 files changed, 177 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
index 06747dd22b85..36a2d2d1f0ed 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
@@ -23,22 +23,36 @@
 
 #include "amdgpu.h"
 
+#define AMDGPU_SPM_RING_SIZE		(4 * 1024 * 1024)
+#define AMDGPU_SPM_META_SIZE		0x20
+
+static int amdgpu_spm_acquire(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *filp);
+static void _amdgpu_spm_release(struct amdgpu_spm_mgr *spm_mgr, int inst, struct drm_file *filp);
+
 /* Check if the calling file owns the SPM session */
 static bool amdgpu_spm_is_owner(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *filp)
 {
 	return spm_mgr->file == filp;
 }
 
+static void amdgpu_spm_work(struct work_struct *work)
+{
+	/* TODO: dump spm ring buffer to user buffer */
+}
+
 int amdgpu_spm_ioctl(struct drm_device *dev, struct drm_amdgpu_spm_args *args,
 		     struct drm_file *filp)
 {
+	struct amdgpu_fpriv *fpriv = filp->driver_priv;
+	struct amdgpu_device *adev = drm_to_adev(dev);
+	struct amdgpu_spm_mgr *spm_mgr = &fpriv->prof_xcp_mgr->spm_mgr;
+
 	if (memchr_inv(args->reserved, 0, sizeof(args->reserved)))
 		return -EINVAL;
 
 	switch (args->op) {
 	case AMDGPU_SPM_OP_ACQUIRE:
-		/* Implemented in a later patch */
-		return -EOPNOTSUPP;
+		return amdgpu_spm_acquire(spm_mgr, filp);
 
 	case AMDGPU_SPM_OP_RELEASE:
 		/* Implemented in a later patch */
@@ -49,11 +63,144 @@ int amdgpu_spm_ioctl(struct drm_device *dev, struct drm_amdgpu_spm_args *args,
 		return -EOPNOTSUPP;
 
 	default:
+		dev_dbg(adev->dev, "Invalid option: %u\n", args->op);
 		return -EINVAL;
 	}
 }
 
 static void amdgpu_spm_init_device(struct amdgpu_spm_mgr *spm_mgr)
+{
+	spm_mgr->spm_cntr = NULL;
+	spin_lock_init(&spm_mgr->spm_irq_lock);
+	/* spm_work lives for the device lifetime; initialize it once here so
+	 * it is ready before any interrupt can schedule it.
+	 */
+	INIT_WORK(&spm_mgr->spm_work, amdgpu_spm_work);
+}
+
+static int _amdgpu_spm_acquire(struct amdgpu_spm_mgr *spm_mgr, int inst, struct drm_file *filp)
+{
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr = to_prof_xcp_mgr(spm_mgr, spm_mgr);
+	struct amdgpu_device *adev = prof_xcp_mgr->adev;
+	struct amdgpu_spm_base *spm;
+	int ret = 0;
+
+	if (WARN_ON_ONCE(inst >= MAX_XCC))
+		return -EINVAL;
+
+	spm = &spm_mgr->spm_cntr->spm[inst];
+
+	/* allocate 4M spm ring buffer */
+	spm->ring_size = AMDGPU_SPM_RING_SIZE;
+
+	ret = amdgpu_bo_create_kernel(adev, spm->ring_size, PAGE_SIZE,
+				      AMDGPU_GEM_DOMAIN_GTT, &spm->spm_obj,
+				      &spm->gpu_addr, &spm->cpu_addr);
+
+	if (ret) {
+		memset(spm, 0, sizeof(*spm));
+		goto out;
+	}
+
+	ret = amdgpu_rlc_spm_acquire(adev, inst, drm_priv_to_vm(filp),
+				     spm->gpu_addr, spm->ring_size);
+	if (ret)
+		goto rlc_spm_acquire_failure;
+
+	/*
+	 * The first 8 DWs of the buffer are the Meta data area (RLC writes
+	 * the wptr there); the ring itself starts at AMDGPU_SPM_META_SIZE.
+	 */
+	spm->ring_size -= AMDGPU_SPM_META_SIZE;
+
+	goto out;
+
+rlc_spm_acquire_failure:
+	amdgpu_bo_free_kernel(&spm->spm_obj, &spm->gpu_addr, &spm->cpu_addr);
+	memset(spm, 0, sizeof(*spm));
+out:
+	return ret;
+}
+
+static int amdgpu_spm_acquire(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *filp)
+{
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr = to_prof_xcp_mgr(spm_mgr, spm_mgr);
+	struct amdgpu_device *adev = prof_xcp_mgr->adev;
+	u32 xcp_id = prof_xcp_mgr->xcp_id;
+	struct amdgpu_spm_cntr *spm_cntr;
+	unsigned long flags;
+	u32 xcc_mask;
+	int ret = 0;
+	int inst;
+
+	mutex_lock(&prof_xcp_mgr->mutex);
+
+	/* spm_mgr->file is the single source of truth for the busy check: it is
+	 * assigned last on the acquire success path and cleared on teardown.
+	 */
+	if (spm_mgr->file) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ret = amdgpu_xcp_get_xcc_mask(adev, xcp_id, &xcc_mask);
+	if (ret)
+		goto out;
+
+	spm_cntr = kzalloc(sizeof(*spm_cntr), GFP_KERNEL);
+	if (!spm_cntr) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	get_task_struct(current->group_leader);
+	spm_mgr->lead_thread = current->group_leader;
+
+	/* Initialize the per-session worker mutex before the acquire loop so it
+	 * is ready before spm_cntr is published. spm_work is initialized once in
+	 * amdgpu_spm_init_device().
+	 */
+	mutex_init(&spm_cntr->spm_worker_mutex);
+	spm_cntr->xcc_mask = xcc_mask;
+
+	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+	spm_mgr->spm_cntr = spm_cntr;
+	spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+
+	for_each_inst(inst, xcc_mask) {
+		ret = _amdgpu_spm_acquire(spm_mgr, inst, filp);
+		if (ret)
+			goto acquire_spm_failure;
+	}
+
+	spm_mgr->file = filp;
+
+	goto out;
+
+acquire_spm_failure:
+	/* Iterate all instances in xcc_mask; _amdgpu_spm_release uses ring_size
+	 * as a sentinel to skip instances that were never acquired.
+	 */
+	for_each_inst(inst, xcc_mask)
+		_amdgpu_spm_release(spm_mgr, inst, filp);
+	/* Drain any pending work before dropping lead_thread and clearing
+	 * spm_cntr. Nothing can schedule the work during acquire, so this is
+	 * defensive ordering.
+	 */
+	cancel_work_sync(&spm_mgr->spm_work);
+	put_task_struct(spm_mgr->lead_thread);
+	spm_mgr->lead_thread = NULL;
+	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+	spm_mgr->spm_cntr = NULL;
+	spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+	mutex_destroy(&spm_cntr->spm_worker_mutex);
+	kfree(spm_cntr);
+
+out:
+	mutex_unlock(&prof_xcp_mgr->mutex);
+	return ret;
+}
+
+static void _amdgpu_spm_release(struct amdgpu_spm_mgr *spm_mgr, int inst, struct drm_file *filp)
 {
 	/* TODO */
 }
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
index 59fa641ebb86..d59c3d93a64d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
@@ -27,9 +27,37 @@
 struct drm_device;
 struct drm_file;
 struct drm_amdgpu_spm_args;
+struct amdgpu_bo;
+
+struct amdgpu_spm_base {
+	u64    gpu_addr;
+	u32    ring_size;
+	void   *cpu_addr;	/* Metadata area (first u32 = wptr) + ring data (u64 array) */
+	struct amdgpu_bo *spm_obj;
+};
+
+struct amdgpu_spm_cntr {
+	/* Indexed by XCC instance id (from for_each_inst or xcc_id parameter).
+	 * Each spm_mgr/spm_cntr is per-XCP partition, and spm[] tracks each
+	 * XCC instance within that partition. Array sized MAX_XCC to handle
+	 * the maximum possible XCC instance ID.
+	 */
+	struct amdgpu_spm_base spm[MAX_XCC];
+	u32 xcc_mask;  /* Cached from acquire; avoids error handling in release */
+	/* Protects dest-buffer state and worker operations */
+	struct mutex spm_worker_mutex;
+};
 
 struct amdgpu_spm_mgr {
 	struct drm_file *file;
+
+	struct task_struct *lead_thread;
+
+	/* spm data */
+	struct amdgpu_spm_cntr *spm_cntr;
+	struct work_struct spm_work;
+	/* Protects spm_cntr publication and the SPM state read in the IRQ path */
+	spinlock_t spm_irq_lock;
 };
 
 int amdgpu_spm_ioctl(struct drm_device *dev, struct drm_amdgpu_spm_args *args,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH 06/12] drm/amdgpu: add profiler/spm operation AMDGPU_SPM_OP_SET_DEST_BUF
  2026-09-21 20:03 [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions James Zhu
                   ` (3 preceding siblings ...)
  2026-09-21 20:03 ` [PATCH 05/12] drm/amdgpu: add profiler/spm operation AMDGPU_SPM_OP_ACQUIRE James Zhu
@ 2026-09-21 20:03 ` James Zhu
  2026-09-21 20:03 ` [PATCH 07/12] drm/amdgpu: add profiler/spm operation AMDGPU_SPM_OP_RELEASE James Zhu
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 36+ messages in thread
From: James Zhu @ 2026-09-21 20:03 UTC (permalink / raw)
  To: amd-gfx, alexander.deucher, Bing.Ma; +Cc: jamesz, Bing Ma

Implement AMDGPU_SPM_OP_SET_DEST_BUF, which attaches a user destination
buffer to an acquired SPM session and (re)starts the hardware monitor.

The buffer is split evenly across the XCC instances of the partition,
each chunk rounded down to 32 bytes and prefixed with a
drm_amdgpu_spm_buffer_header (SPM version, bytes copied, data-loss flag)
so userspace can interpret each chunk on its own. Passing dest_buf =
NULL detaches the buffer and stops SPM on every instance, so the same
operation also serves as the stop path.

Ownership is validated before any state is touched. The optional wait
for the previous buffer to fill drops prof_xcp_mgr->mutex so that other
XCPs can make progress and device teardown is not blocked, so ownership,
teardown and session identity are re-checked after waking.

If starting an instance fails, only the instances started by this call
are stopped again. Partial state from earlier instances is not rolled
back, so userspace must treat any error as fatal and release the session.

Also add amdgpu_spm_interrupt(), the ring-buffer watermark handler. It
runs under spm_irq_lock, records data loss on pre-GFX12 and schedules the
drain work directly on GFX12+. It is wired up to the IH by a later patch.

Note: in this patch the wait condition (are_users_buf_filled) is only
cleared, never set. The waker and the condition setter arrive with the
"implement data dump from spm ring buffer" patch, so until then the wait
can only end on timeout or signal.

Signed-off-by: James Zhu <James.Zhu@amd.com>
Reviewed-by: Bing Ma <bing.ma@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h |   1 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c |   6 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h |   1 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c | 387 +++++++++++++++++++++++-
 drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h |  17 ++
 5 files changed, 410 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
index fd87075962f9..dceee30091fd 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
@@ -487,6 +487,7 @@ struct amdgpu_gfx {
 	struct amdgpu_irq_src		priv_inst_irq;
 	struct amdgpu_irq_src		bad_op_irq;
 	struct amdgpu_irq_src		cp_ecc_error_irq;
+	struct amdgpu_irq_src		spm_irq;
 	struct amdgpu_irq_src		sq_irq;
 	struct amdgpu_irq_src		rlc_gc_fed_irq;
 	struct amdgpu_irq_src		rlc_poison_irq;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c
index 79641e537fbb..36b7dbecc8b1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c
@@ -26,6 +26,7 @@
 #include "amdgpu.h"
 #include "amdgpu_gfx.h"
 #include "amdgpu_rlc.h"
+#include "amdgpu_spm.h"
 
 /**
  * amdgpu_gfx_rlc_enter_safe_mode - Set RLC into safe mode
@@ -852,3 +853,8 @@ void amdgpu_rlc_spm_release(struct amdgpu_device *adev, int xcc_id, struct amdgp
 
 	amdgpu_vmid_free_reserved(adev, vm, AMDGPU_GFXHUB(xcc_id));
 }
+
+void amdgpu_rlc_spm_interrupt(struct amdgpu_device *adev, int xcc_id)
+{
+	amdgpu_spm_interrupt(adev, xcc_id);
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h
index 18b925a0d164..64a4bd36b02c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h
@@ -391,4 +391,5 @@ int amdgpu_rlc_spm_acquire(struct amdgpu_device *adev, int xcc_id,
 			   struct amdgpu_vm *vm, u64 gpu_addr, u32 size);
 void amdgpu_rlc_spm_release(struct amdgpu_device *adev, int xcc_id, struct amdgpu_vm *vm);
 void amdgpu_rlc_spm_set_rdptr(struct amdgpu_device *adev, int xcc_id, u32 rptr);
+void amdgpu_rlc_spm_interrupt(struct amdgpu_device *adev, int xcc_id);
 #endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
index 36a2d2d1f0ed..43b8970f1673 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
@@ -23,11 +23,22 @@
 
 #include "amdgpu.h"
 
+/*
+ * SPM revision change log
+ *
+ * 0.1 - Initial revision
+ */
+
+#define AMDGPU_SPM_MAJOR_VERSION	0
+#define AMDGPU_SPM_MINOR_VERSION	1
+
 #define AMDGPU_SPM_RING_SIZE		(4 * 1024 * 1024)
 #define AMDGPU_SPM_META_SIZE		0x20
 
 static int amdgpu_spm_acquire(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *filp);
 static void _amdgpu_spm_release(struct amdgpu_spm_mgr *spm_mgr, int inst, struct drm_file *filp);
+static int amdgpu_spm_set_dest_buf(struct amdgpu_spm_mgr *spm_mgr, void *data,
+				   struct drm_file *filp);
 
 /* Check if the calling file owns the SPM session */
 static bool amdgpu_spm_is_owner(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *filp)
@@ -59,8 +70,7 @@ int amdgpu_spm_ioctl(struct drm_device *dev, struct drm_amdgpu_spm_args *args,
 		return -EOPNOTSUPP;
 
 	case AMDGPU_SPM_OP_SET_DEST_BUF:
-		/* Implemented in a later patch */
-		return -EOPNOTSUPP;
+		return amdgpu_spm_set_dest_buf(spm_mgr, args, filp);
 
 	default:
 		dev_dbg(adev->dev, "Invalid option: %u\n", args->op);
@@ -161,6 +171,7 @@ static int amdgpu_spm_acquire(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *f
 	 */
 	mutex_init(&spm_cntr->spm_worker_mutex);
 	spm_cntr->xcc_mask = xcc_mask;
+	init_waitqueue_head(&spm_cntr->spm_buf_wq);
 
 	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
 	spm_mgr->spm_cntr = spm_cntr;
@@ -170,6 +181,7 @@ static int amdgpu_spm_acquire(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *f
 		ret = _amdgpu_spm_acquire(spm_mgr, inst, filp);
 		if (ret)
 			goto acquire_spm_failure;
+		spm_mgr->spm_cntr->spm_use_cnt++;
 	}
 
 	spm_mgr->file = filp;
@@ -200,6 +212,50 @@ static int amdgpu_spm_acquire(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *f
 	return ret;
 }
 
+static int _spm_start(struct amdgpu_spm_mgr *spm_mgr,
+		      struct amdgpu_device *adev, int inst)
+{
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr = to_prof_xcp_mgr(spm_mgr, spm_mgr);
+	struct amdgpu_spm_base *spm = &spm_mgr->spm_cntr->spm[inst];
+	unsigned long flags;
+	int ret;
+
+	lockdep_assert_held(&prof_xcp_mgr->mutex);
+
+	/* Set flag before enabling hardware to avoid dropping interrupts.
+	 * amdgpu_rlc_spm_cntl() will reset SPM and wptr will become 0,
+	 * adjust rptr accordingly.
+	 */
+	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+	spm->is_spm_started = true;
+	WRITE_ONCE(spm->ring_rptr, 0);
+	spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+
+	ret = amdgpu_rlc_spm_cntl(adev, inst, 1);
+	if (ret) {
+		spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+		spm->is_spm_started = false;
+		spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+	}
+	return ret;
+}
+
+static void _spm_stop(struct amdgpu_spm_mgr *spm_mgr,
+		      struct amdgpu_device *adev, int inst)
+{
+	struct amdgpu_spm_base *spm = &spm_mgr->spm_cntr->spm[inst];
+	unsigned long flags;
+
+	amdgpu_rlc_spm_cntl(adev, inst, 0);
+	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+	spm->is_spm_started = false;
+	/* amdgpu_rlc_spm_cntl() will reset SPM and wptr will become 0.
+	 * Adjust rptr accordingly.
+	 */
+	WRITE_ONCE(spm->ring_rptr, 0);
+	spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+}
+
 static void _amdgpu_spm_release(struct amdgpu_spm_mgr *spm_mgr, int inst, struct drm_file *filp)
 {
 	/* TODO */
@@ -228,6 +284,295 @@ static int amdgpu_spm_release(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *f
 	return 0;
 }
 
+static int spm_update_dest_info(struct amdgpu_spm_mgr *spm_mgr,
+				int inst, struct drm_amdgpu_spm_args *user_spm_data,
+				struct drm_amdgpu_spm_args *user_spm_ptr)
+{
+	struct amdgpu_spm_base *spm = &spm_mgr->spm_cntr->spm[inst];
+	unsigned long flags;
+	int ret = 0;
+
+	mutex_lock(&spm_mgr->spm_cntr->spm_worker_mutex);
+	if (spm->has_user_buf) {
+		struct drm_amdgpu_spm_buffer_header spm_header;
+		u64 __user *user_address;
+		bool data_loss;
+
+		/* The interrupt handler sets has_data_loss under spm_irq_lock,
+		 * so read it under the same lock and report one consistent value.
+		 */
+		spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+		data_loss = spm->has_data_loss;
+		spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+
+		user_spm_ptr->bytes_copied += spm->size_copied;
+		user_spm_ptr->has_data_loss |= data_loss;
+
+		memset(&spm_header, 0, sizeof(spm_header));
+		user_address = (u64 __user *)((u8 __user *)spm->ubuf.user_addr -
+					      sizeof(spm_header));
+		spm_header.version = ((u32)AMDGPU_SPM_MAJOR_VERSION << 24) |
+					AMDGPU_SPM_MINOR_VERSION;
+		spm_header.bytes_copied = spm->size_copied;
+		spm_header.has_data_loss = data_loss;
+
+		if (copy_to_user(user_address, &spm_header, sizeof(spm_header))) {
+			ret = -EFAULT;
+			goto out;
+		}
+
+		/* Commit state changes only after successful copy_to_user */
+		spm->has_user_buf = false;
+		WRITE_ONCE(spm_mgr->spm_cntr->have_users_buf_cnt,
+			   spm_mgr->spm_cntr->have_users_buf_cnt - 1);
+	}
+	if (user_spm_data->dest_buf) {
+		spm->ubuf.user_addr = (u64 __user *)user_spm_data->dest_buf;
+		spm->ubuf.ubufsize = user_spm_data->buf_size;
+		/* reserve space for drm_amdgpu_spm_buffer_header */
+		spm->ubuf.user_addr = (u64 __user *)((u8 __user *)spm->ubuf.user_addr +
+						     sizeof(struct drm_amdgpu_spm_buffer_header));
+		spm->ubuf.ubufsize -= sizeof(struct drm_amdgpu_spm_buffer_header);
+		spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+		spm->has_data_loss = false;
+		spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+		spm->size_copied = 0;
+		spm->is_user_buf_filled = false;
+		spm->has_user_buf = true;
+		WRITE_ONCE(spm_mgr->spm_cntr->are_users_buf_filled, false);
+		WRITE_ONCE(spm_mgr->spm_cntr->have_users_buf_cnt,
+			   spm_mgr->spm_cntr->have_users_buf_cnt + 1);
+	}
+out:
+	mutex_unlock(&spm_mgr->spm_cntr->spm_worker_mutex);
+	return ret;
+}
+
+static int spm_wait_for_fill_awake(struct amdgpu_device *adev,
+				   struct amdgpu_spm_cntr *spm_cntr,
+				   struct drm_amdgpu_spm_args *user_spm_data)
+{
+	long timeout = msecs_to_jiffies(user_spm_data->timeout);
+	unsigned long start_jiffies = jiffies;
+	long rem;
+	int ret = 0;
+
+	rem = wait_event_interruptible_timeout(spm_cntr->spm_buf_wq,
+						READ_ONCE(spm_cntr->are_users_buf_filled),
+						timeout);
+
+	switch (rem) {
+	case -ERESTARTSYS:
+		ret = -ERESTARTSYS;
+		/* Subtract elapsed time from timeout so we wait that much
+		 * less when the call gets restarted.
+		 */
+		timeout -= (jiffies - start_jiffies);
+		if (timeout <= 0) {
+			ret = -ETIME;
+			timeout = 0;
+			dev_dbg(adev->dev, "[%s] interrupted by signal\n", __func__);
+		}
+		break;
+
+	case 0:
+		/* Timeout expired */
+		ret = -ETIME;
+		timeout = 0;
+		break;
+
+	default:
+		/* Woken up before timeout */
+		timeout = rem;
+		break;
+	}
+	user_spm_data->timeout = jiffies_to_msecs(timeout);
+
+	return ret;
+}
+
+static int amdgpu_spm_set_dest_buf(struct amdgpu_spm_mgr *spm_mgr, void *data,
+				   struct drm_file *filp)
+{
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr = to_prof_xcp_mgr(spm_mgr, spm_mgr);
+	struct drm_amdgpu_spm_args user_spm_data, *user_spm_ptr;
+	struct amdgpu_device *adev = prof_xcp_mgr->adev;
+	struct amdgpu_spm_cntr *spm_cntr;
+	bool need_schedule = false;
+	bool drained = false;
+	u32 started_mask = 0;
+	u32 xcc_mask;
+	u32 ubufsize;
+	int ret = 0;
+	int inst;
+
+	dev_dbg(adev->dev, "SPM start to set new destination buffer.\n");
+	mutex_lock(&prof_xcp_mgr->mutex);
+
+	/* Check ownership */
+	if (!amdgpu_spm_is_owner(spm_mgr, filp)) {
+		ret = -EPERM;
+		goto out;
+	}
+
+	spm_cntr = spm_mgr->spm_cntr;
+	if (!spm_cntr ||
+	    !spm_cntr->spm_use_cnt) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* Use the xcc_mask cached at acquire time rather than re-querying it:
+	 * spm[] was set up against that mask and spm_use_cnt counts exactly its
+	 * instances. Re-querying could return a different mask and desynchronize
+	 * the per-instance chunk size from the instances iterated below.
+	 */
+	xcc_mask = spm_cntr->xcc_mask;
+
+	user_spm_ptr = (struct drm_amdgpu_spm_args *)data;
+	/* spm_use_cnt is guaranteed non-zero by the check above */
+	ubufsize = user_spm_ptr->buf_size / spm_cntr->spm_use_cnt;
+	ubufsize = round_down(ubufsize, 32);
+
+	memcpy(&user_spm_data, user_spm_ptr, sizeof(user_spm_data));
+
+	/* Only validate ubufsize when dest_buf is provided (non-NULL).
+	 * When dest_buf=NULL (stop/unset operation), buf_size=0 is valid.
+	 */
+	if (user_spm_data.dest_buf &&
+	    ubufsize <= sizeof(struct drm_amdgpu_spm_buffer_header)) {
+		ret = -EINVAL;
+		goto out;
+	}
+	user_spm_data.buf_size = ubufsize;
+
+	/* have_users_buf_cnt and are_users_buf_filled are protected by
+	 * spm_worker_mutex. Use READ_ONCE for lockless reads to avoid races
+	 * with worker modifications.
+	 */
+	if (user_spm_data.timeout && READ_ONCE(spm_cntr->have_users_buf_cnt) &&
+	    !READ_ONCE(spm_cntr->are_users_buf_filled)) {
+		/* Drop mutex during wait to allow concurrent operations on other XCPs
+		 * and to prevent blocking device teardown. Safe because wait condition
+		 * (are_users_buf_filled, releasing) doesn't require this mutex.
+		 */
+		mutex_unlock(&prof_xcp_mgr->mutex);
+
+		dev_dbg(adev->dev, "SPM waiting for fill awake, timeout = %u ms.\n",
+			user_spm_data.timeout);
+		ret = spm_wait_for_fill_awake(adev, spm_cntr, &user_spm_data);
+
+		/* Re-acquire mutex */
+		mutex_lock(&prof_xcp_mgr->mutex);
+
+		/* Write back updated timeout to userspace before checking
+		 * cancellation conditions, so userspace always sees the
+		 * remaining timeout regardless of which error path we take.
+		 */
+		user_spm_ptr->timeout = user_spm_data.timeout;
+
+		/* Check if XCP was torn down while we slept */
+		if (READ_ONCE(prof_xcp_mgr->torn_down)) {
+			ret = -ECANCELED;
+			goto out;
+		}
+
+		if (!amdgpu_spm_is_owner(spm_mgr, filp)) {
+			ret = -ECANCELED;
+			goto out;
+		}
+
+		if (spm_mgr->spm_cntr != spm_cntr) {
+			ret = -ECANCELED;
+			goto out;
+		}
+
+		if (ret == -ETIME) {
+			/* Copy (partial) data to user buffer after a timeout */
+			schedule_work(&spm_mgr->spm_work);
+			flush_work(&spm_mgr->spm_work);
+			drained = true;
+			/* This is not an error */
+			ret = 0;
+		} else if (ret) {
+			goto out;
+		}
+	} else if (!user_spm_data.timeout && READ_ONCE(spm_cntr->have_users_buf_cnt)) {
+		/* Copy (partial) data to user buffer */
+		schedule_work(&spm_mgr->spm_work);
+		flush_work(&spm_mgr->spm_work);
+		drained = true;
+	}
+
+	/* Drain before stopping: _spm_stop() clears is_spm_started and resets
+	 * rptr, so anything left in the ring is unrecoverable afterwards.
+	 */
+	if (!user_spm_data.dest_buf && !drained &&
+	    READ_ONCE(spm_cntr->have_users_buf_cnt)) {
+		schedule_work(&spm_mgr->spm_work);
+		flush_work(&spm_mgr->spm_work);
+	}
+
+	user_spm_ptr->bytes_copied = 0;
+	user_spm_ptr->has_data_loss = 0;
+	user_spm_ptr->timeout = user_spm_data.timeout;
+	for_each_inst(inst, xcc_mask) {
+		struct amdgpu_spm_base *spm = &spm_cntr->spm[inst];
+
+		if (READ_ONCE(spm->has_user_buf) || user_spm_data.dest_buf) {
+			/* Get info about filled space in previous output buffer.
+			 * Setup new dest buf if provided.
+			 */
+			ret = spm_update_dest_info(spm_mgr, inst, &user_spm_data, user_spm_ptr);
+			if (ret)
+				goto start_failure;
+		}
+
+		if (user_spm_data.dest_buf) {
+			/* Start SPM if necessary */
+			if (!READ_ONCE(spm->is_spm_started)) {
+				ret = _spm_start(spm_mgr, adev, inst);
+				if (ret)
+					goto start_failure;
+				started_mask |= (1u << inst);
+			} else {
+				/* If SPM was already started, there may already
+				 * be data in the ring-buffer that needs to be read.
+				 */
+				need_schedule = true;
+			}
+			user_spm_data.dest_buf += ubufsize;
+		} else {
+			/* Lock out the drain worker while SPM is stopped, so it
+			 * cannot write a stale rptr over the reset.
+			 */
+			mutex_lock(&spm_cntr->spm_worker_mutex);
+			_spm_stop(spm_mgr, adev, inst);
+			mutex_unlock(&spm_cntr->spm_worker_mutex);
+		}
+	}
+	goto out;
+
+start_failure:
+	/* Stop only instances that were started in this iteration.
+	 * Partial state (bytes_copied, ubuf, has_user_buf) from earlier instances
+	 * is not rolled back. Userspace must treat any error as fatal and release
+	 * the SPM session.
+	 */
+	mutex_lock(&spm_cntr->spm_worker_mutex);
+	for_each_inst(inst, started_mask)
+		_spm_stop(spm_mgr, adev, inst);
+	mutex_unlock(&spm_cntr->spm_worker_mutex);
+
+out:
+	mutex_unlock(&prof_xcp_mgr->mutex);
+	if (need_schedule)
+		schedule_work(&spm_mgr->spm_work);
+
+	dev_dbg(adev->dev, "SPM finish to set new destination buffer, ret = %d.\n", ret);
+	return ret;
+}
+
 void amdgpu_spm_mgr_init(struct amdgpu_spm_mgr *spm_mgr)
 {
 	amdgpu_spm_init_device(spm_mgr);
@@ -243,3 +588,41 @@ void amdgpu_spm_mgr_fini(struct amdgpu_spm_mgr *spm_mgr)
 {
 	amdgpu_spm_release(spm_mgr, NULL);
 }
+
+void amdgpu_spm_interrupt(struct amdgpu_device *adev, int xcc_id)
+{
+	u8 xcp_id;
+	unsigned long flags;
+	struct amdgpu_spm_mgr *spm_mgr;
+	int part;
+
+	if (WARN_ON_ONCE(xcc_id >= MAX_XCC))
+		return;
+
+	part = adev->xcp_mgr ? amdgpu_xcp_get_partition(adev->xcp_mgr, AMDGPU_XCP_GFX, xcc_id) : 1;
+	if (part <= 0)
+		return;
+
+	/* GFX inst_masks are disjoint per XCP, so exactly one bit is set. */
+	xcp_id = fls(part) - 1;
+	if (WARN_ON_ONCE(xcp_id >= MAX_XCP))
+		return;
+
+	spm_mgr = &adev->prof_mgr.prof_xcp_mgr[xcp_id].spm_mgr;
+
+	/* Watermark threshold is triggered.
+	 * Pre-GFX12: HW interrupt only sets has_data_loss flag; drain work is
+	 * scheduled by the 100us polling thread in amdgpu_spm_monitor_thread.
+	 * GFX12+: HW interrupt directly schedules drain work as polling is not needed.
+	 */
+	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+	if (spm_mgr->spm_cntr && spm_mgr->spm_cntr->spm[xcc_id].is_spm_started) {
+		if (amdgpu_ip_version(adev, GC_HWIP, 0) < IP_VERSION(12, 0, 0))
+			spm_mgr->spm_cntr->spm[xcc_id].has_data_loss = true;
+		else
+			schedule_work(&spm_mgr->spm_work);
+	}
+	spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+
+	dev_dbg(adev->dev, "[SPM#%d:%d] ring buffer stall.\n", xcp_id, xcc_id);
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
index d59c3d93a64d..eaeb880e209c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
@@ -29,11 +29,23 @@ struct drm_file;
 struct drm_amdgpu_spm_args;
 struct amdgpu_bo;
 
+struct spm_user_buf {
+	u64 __user *user_addr;
+	u32 ubufsize;
+};
+
 struct amdgpu_spm_base {
+	struct spm_user_buf ubuf;
 	u64    gpu_addr;
 	u32    ring_size;
+	u32    ring_rptr;
+	u32    size_copied;
+	bool   has_data_loss;
 	void   *cpu_addr;	/* Metadata area (first u32 = wptr) + ring data (u64 array) */
 	struct amdgpu_bo *spm_obj;
+	bool   has_user_buf;
+	bool   is_user_buf_filled;
+	bool   is_spm_started;
 };
 
 struct amdgpu_spm_cntr {
@@ -44,8 +56,12 @@ struct amdgpu_spm_cntr {
 	 */
 	struct amdgpu_spm_base spm[MAX_XCC];
 	u32 xcc_mask;  /* Cached from acquire; avoids error handling in release */
+	u32 spm_use_cnt;
 	/* Protects dest-buffer state and worker operations */
 	struct mutex spm_worker_mutex;
+	wait_queue_head_t spm_buf_wq;
+	u32   have_users_buf_cnt;       /* Updated under spm_worker_mutex; read locklessly via READ_ONCE */
+	bool  are_users_buf_filled;     /* Updated under spm_worker_mutex; read locklessly via READ_ONCE */
 };
 
 struct amdgpu_spm_mgr {
@@ -67,5 +83,6 @@ void amdgpu_spm_mgr_init(struct amdgpu_spm_mgr *spm_mgr);
 void amdgpu_spm_mgr_fini(struct amdgpu_spm_mgr *spm_mgr);
 void amdgpu_spm_mgr_release_owner(struct amdgpu_spm_mgr *spm_mgr,
 				  struct drm_file *filp);
+void amdgpu_spm_interrupt(struct amdgpu_device *adev, int xcc_id);
 
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH 07/12] drm/amdgpu: add profiler/spm operation AMDGPU_SPM_OP_RELEASE
  2026-09-21 20:03 [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions James Zhu
                   ` (4 preceding siblings ...)
  2026-09-21 20:03 ` [PATCH 06/12] drm/amdgpu: add profiler/spm operation AMDGPU_SPM_OP_SET_DEST_BUF James Zhu
@ 2026-09-21 20:03 ` James Zhu
  2026-09-21 20:03 ` [PATCH 08/12] drm/amdgpu: implement data dump from spm ring buffer James Zhu
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 36+ messages in thread
From: James Zhu @ 2026-09-21 20:03 UTC (permalink / raw)
  To: amd-gfx, alexander.deucher, Bing.Ma; +Cc: jamesz, Bing Ma

Implement AMDGPU_SPM_OP_RELEASE so userspace can end an SPM session and
return the partition to an idle state. Only the owning drm_file may
release a session; any other caller gets -EPERM.

Ownership is validated under prof_xcp_mgr->mutex, then the releasing flag
is set and spm_buf_wq is woken while the mutex is still held, so a thread
blocked in spm_wait_for_fill_awake() observes the flag and returns instead
of touching a session that is going away.

Stop hardware SPM and free the per-instance ring buffers for every XCC
instance of the partition, using the xcc_mask cached at acquire time so
the teardown loops always match the instances that were actually set up.
Both loops run under spm_worker_mutex to lock out the drain worker, which
would otherwise keep updating rptr after the reset and read the ring after
it is freed. spm_cntr and lead_thread are cleared under spm_irq_lock
before cancel_work_sync(), so a work item that is already queued bails
out; the lead thread reference is dropped only once the worker has
drained, because the worker dereferences its snapshot in get_task_mm().

Add the active_waiters counter and the waiters_done wait queue to keep
spm_cntr alive across the unlocked sleep in spm_wait_for_fill_awake().
A waiter increments the counter under prof_xcp_mgr->mutex before dropping
it and decrements it once it is done touching spm_cntr; RELEASE waits for
zero before kfree(). After waking, a waiter revalidates teardown state,
ownership, spm_cntr identity and the releasing flag, returning -ECANCELED
on any mismatch.

Register amdgpu_spm_teardown() as the XCP teardown callback so that device
or partition teardown also sets releasing and wakes blocked waiters.

Signed-off-by: James Zhu <James.Zhu@amd.com>
Reviewed-by: Bing Ma <bing.ma@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c | 200 +++++++++++++++++++++++-
 drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h |   3 +
 2 files changed, 195 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
index 43b8970f1673..b3132f9a8c0a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
@@ -21,6 +21,8 @@
  * OTHER DEALINGS IN THE SOFTWARE.
  */
 
+#include <linux/sched/task.h>
+
 #include "amdgpu.h"
 
 /*
@@ -36,6 +38,7 @@
 #define AMDGPU_SPM_META_SIZE		0x20
 
 static int amdgpu_spm_acquire(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *filp);
+static int amdgpu_spm_release(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *filp);
 static void _amdgpu_spm_release(struct amdgpu_spm_mgr *spm_mgr, int inst, struct drm_file *filp);
 static int amdgpu_spm_set_dest_buf(struct amdgpu_spm_mgr *spm_mgr, void *data,
 				   struct drm_file *filp);
@@ -66,8 +69,7 @@ int amdgpu_spm_ioctl(struct drm_device *dev, struct drm_amdgpu_spm_args *args,
 		return amdgpu_spm_acquire(spm_mgr, filp);
 
 	case AMDGPU_SPM_OP_RELEASE:
-		/* Implemented in a later patch */
-		return -EOPNOTSUPP;
+		return amdgpu_spm_release(spm_mgr, filp);
 
 	case AMDGPU_SPM_OP_SET_DEST_BUF:
 		return amdgpu_spm_set_dest_buf(spm_mgr, args, filp);
@@ -172,6 +174,8 @@ static int amdgpu_spm_acquire(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *f
 	mutex_init(&spm_cntr->spm_worker_mutex);
 	spm_cntr->xcc_mask = xcc_mask;
 	init_waitqueue_head(&spm_cntr->spm_buf_wq);
+	atomic_set(&spm_cntr->active_waiters, 0);
+	init_waitqueue_head(&spm_cntr->waiters_done);
 
 	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
 	spm_mgr->spm_cntr = spm_cntr;
@@ -258,30 +262,174 @@ static void _spm_stop(struct amdgpu_spm_mgr *spm_mgr,
 
 static void _amdgpu_spm_release(struct amdgpu_spm_mgr *spm_mgr, int inst, struct drm_file *filp)
 {
-	/* TODO */
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr = to_prof_xcp_mgr(spm_mgr, spm_mgr);
+	struct amdgpu_device *adev = prof_xcp_mgr->adev;
+	struct amdgpu_spm_base *spm;
+	unsigned long flags;
+
+	if (WARN_ON_ONCE(inst >= MAX_XCC))
+		return;
+
+	spm = &spm_mgr->spm_cntr->spm[inst];
+
+	/* ring_size is cleared on acquire-failure and prior release,
+	 * so it doubles as the 'allocated' sentinel for this early-out.
+	 */
+	if (!spm->ring_size)
+		return;
+
+	amdgpu_rlc_spm_release(adev, inst, drm_priv_to_vm(filp));
+	amdgpu_bo_free_kernel(&spm->spm_obj, &spm->gpu_addr, &spm->cpu_addr);
+
+	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+	memset(spm, 0, sizeof(*spm));
+	spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+
+	--spm_mgr->spm_cntr->spm_use_cnt;
 }
 
 static int amdgpu_spm_release(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *filp)
 {
 	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr = to_prof_xcp_mgr(spm_mgr, spm_mgr);
+	struct amdgpu_device *adev = prof_xcp_mgr->adev;
+	struct amdgpu_spm_cntr *spm_cntr;
+	struct task_struct *lead_thread;
+	unsigned long flags;
+	u32 xcc_mask;
+	int inst;
+	int ret = 0;
 
-	/* Full teardown lands with AMDGPU_SPM_OP_RELEASE. Re-check ownership
-	 * under the mutex so a close and a concurrent device teardown cannot
-	 * both release the same session.
-	 */
 	mutex_lock(&prof_xcp_mgr->mutex);
+
+	/* Ownership check + snapshot + release flag + wake must be atomic
+	 * to prevent double-release UAF. If spm_cntr is already NULL, a
+	 * concurrent releaser has already won the race.
+	 */
 	if (!spm_mgr->file) {
 		mutex_unlock(&prof_xcp_mgr->mutex);
 		return 0;
 	}
+
 	if (filp && !amdgpu_spm_is_owner(spm_mgr, filp)) {
 		mutex_unlock(&prof_xcp_mgr->mutex);
 		return -EPERM;
 	}
+
+	spm_cntr = spm_mgr->spm_cntr;
+	if (WARN_ON_ONCE(!spm_cntr)) {
+		/* Invariant violation: file set but spm_cntr NULL. Clear all
+		 * session state, including the lead thread reference, to
+		 * prevent a permanent wedge and a task_struct leak.
+		 */
+		spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+		lead_thread = spm_mgr->lead_thread;
+		spm_mgr->lead_thread = NULL;
+		spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+		if (lead_thread)
+			put_task_struct(lead_thread);
+		spm_mgr->file = NULL;
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* Wake waiters while still holding mutex. Waiters in
+	 * spm_wait_for_fill_awake() will drop and reacquire mutex
+	 * during their wait. The active_waiters counter prevents
+	 * spm_cntr from being freed until all waiters finish.
+	 */
+	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+	WRITE_ONCE(spm_cntr->releasing, true);
+	spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+	wake_up_all(&spm_cntr->spm_buf_wq);
+
+	/* Use cached xcc_mask from acquire to avoid error handling */
+	xcc_mask = spm_cntr->xcc_mask;
+
+	/* Stop hardware SPM for all instances and free their ring buffers.
+	 * spm_worker_mutex locks out the drain worker, which would otherwise
+	 * keep updating rptr after the reset and read the ring after it is
+	 * freed.
+	 */
+	mutex_lock(&spm_cntr->spm_worker_mutex);
+	for_each_inst(inst, xcc_mask)
+		_spm_stop(spm_mgr, adev, inst);
+
+	for_each_inst(inst, xcc_mask)
+		_amdgpu_spm_release(spm_mgr, inst, filp);
+	mutex_unlock(&spm_cntr->spm_worker_mutex);
+
+	lead_thread = spm_mgr->lead_thread;
+
+	/* Clear spm_cntr and lead_thread under spm_irq_lock so the work
+	 * handler can detect release in progress and bail out. Must be done
+	 * BEFORE cancel_work_sync() to ensure racing work sees NULL.
+	 */
+	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+	spm_mgr->spm_cntr = NULL;
+	spm_mgr->lead_thread = NULL;
+	spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
 	spm_mgr->file = NULL;
+
+	/* Cancel any pending/running work. If work is running, this waits
+	 * for it to complete. The work handler will see NULL spm_cntr and
+	 * bail out early.
+	 */
+	cancel_work_sync(&spm_mgr->spm_work);
+
+	/* Drop the lead thread reference only after the worker is drained:
+	 * it dereferences its snapshot in get_task_mm().
+	 */
+	if (lead_thread)
+		put_task_struct(lead_thread);
+
+	/* Destroy the worker mutex only after cancel_work_sync() has drained
+	 * any in-flight work handler, which may still hold it.
+	 */
+	mutex_destroy(&spm_cntr->spm_worker_mutex);
+
+	/* Drop the mutex only now: a new ACQUIRE could otherwise queue work
+	 * on the shared spm_work and have cancel_work_sync() eat its drain.
+	 */
 	mutex_unlock(&prof_xcp_mgr->mutex);
 
+	/* Wait for all waiters to exit. Correctness relies on every waiter
+	 * checking 'releasing' flag so active_waiters drains to 0. This is
+	 * guaranteed by current code (spm_wait_for_fill_awake checks releasing),
+	 * but future code must maintain this invariant or this will deadlock.
+	 */
+	wait_event(spm_cntr->waiters_done,
+		   atomic_read(&spm_cntr->active_waiters) == 0);
+
+	/* Acquire spm_irq_lock before freeing to ensure no waiter is between
+	 * dec_and_test and wake_up while holding the lock. The waiter's
+	 * spin_lock will either happen before this (waiter will complete before
+	 * wait_event above returns) or after kfree (safe - waiter won't touch
+	 * spm_cntr after releasing the lock).
+	 */
+	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+	spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+
+	kfree(spm_cntr);
 	return 0;
+
+out:
+	mutex_unlock(&prof_xcp_mgr->mutex);
+	return ret;
+}
+
+static void amdgpu_spm_teardown(struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr)
+{
+	struct amdgpu_spm_mgr *spm_mgr = &prof_xcp_mgr->spm_mgr;
+	struct amdgpu_spm_cntr *spm_cntr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+	spm_cntr = spm_mgr->spm_cntr;
+	if (spm_cntr) {
+		WRITE_ONCE(spm_cntr->releasing, true);
+		wake_up_all(&spm_cntr->spm_buf_wq);
+	}
+	spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
 }
 
 static int spm_update_dest_info(struct amdgpu_spm_mgr *spm_mgr,
@@ -358,6 +506,7 @@ static int spm_wait_for_fill_awake(struct amdgpu_device *adev,
 	int ret = 0;
 
 	rem = wait_event_interruptible_timeout(spm_cntr->spm_buf_wq,
+						READ_ONCE(spm_cntr->releasing) ||
 						READ_ONCE(spm_cntr->are_users_buf_filled),
 						timeout);
 
@@ -400,6 +549,7 @@ static int amdgpu_spm_set_dest_buf(struct amdgpu_spm_mgr *spm_mgr, void *data,
 	struct amdgpu_spm_cntr *spm_cntr;
 	bool need_schedule = false;
 	bool drained = false;
+	bool did_wait = false;
 	u32 started_mask = 0;
 	u32 xcc_mask;
 	u32 ubufsize;
@@ -452,6 +602,11 @@ static int amdgpu_spm_set_dest_buf(struct amdgpu_spm_mgr *spm_mgr, void *data,
 	 */
 	if (user_spm_data.timeout && READ_ONCE(spm_cntr->have_users_buf_cnt) &&
 	    !READ_ONCE(spm_cntr->are_users_buf_filled)) {
+		/* Increment active_waiters while holding mutex to prevent
+		 * release from freeing spm_cntr before we enter the wait.
+		 */
+		atomic_inc(&spm_cntr->active_waiters);
+
 		/* Drop mutex during wait to allow concurrent operations on other XCPs
 		 * and to prevent blocking device teardown. Safe because wait condition
 		 * (are_users_buf_filled, releasing) doesn't require this mutex.
@@ -461,6 +616,7 @@ static int amdgpu_spm_set_dest_buf(struct amdgpu_spm_mgr *spm_mgr, void *data,
 		dev_dbg(adev->dev, "SPM waiting for fill awake, timeout = %u ms.\n",
 			user_spm_data.timeout);
 		ret = spm_wait_for_fill_awake(adev, spm_cntr, &user_spm_data);
+		did_wait = true;
 
 		/* Re-acquire mutex */
 		mutex_lock(&prof_xcp_mgr->mutex);
@@ -487,6 +643,11 @@ static int amdgpu_spm_set_dest_buf(struct amdgpu_spm_mgr *spm_mgr, void *data,
 			goto out;
 		}
 
+		if (READ_ONCE(spm_cntr->releasing)) {
+			ret = -ECANCELED;
+			goto out;
+		}
+
 		if (ret == -ETIME) {
 			/* Copy (partial) data to user buffer after a timeout */
 			schedule_work(&spm_mgr->spm_work);
@@ -565,17 +726,40 @@ static int amdgpu_spm_set_dest_buf(struct amdgpu_spm_mgr *spm_mgr, void *data,
 	mutex_unlock(&spm_cntr->spm_worker_mutex);
 
 out:
-	mutex_unlock(&prof_xcp_mgr->mutex);
+	/* Decrement active_waiters after all spm_cntr usage is complete.
+	 * Use spm_irq_lock to coordinate with release's wait+free sequence:
+	 * - Waiter: dec under lock, then wake (if last)
+	 * - Releaser: lock, check counter, unlock, wait, lock, free, unlock
+	 * This ensures the releaser cannot free spm_cntr while waiter holds the lock.
+	 */
+	if (did_wait) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+		if (atomic_dec_and_test(&spm_cntr->active_waiters))
+			wake_up(&spm_cntr->waiters_done);
+		spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+	}
+
+	/* Schedule work while still holding prof_xcp_mgr->mutex to prevent
+	 * amdgpu_spm_release() from racing and clearing spm_cntr before the
+	 * work item is queued.
+	 */
 	if (need_schedule)
 		schedule_work(&spm_mgr->spm_work);
 
+	mutex_unlock(&prof_xcp_mgr->mutex);
+
 	dev_dbg(adev->dev, "SPM finish to set new destination buffer, ret = %d.\n", ret);
 	return ret;
 }
 
 void amdgpu_spm_mgr_init(struct amdgpu_spm_mgr *spm_mgr)
 {
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr = to_prof_xcp_mgr(spm_mgr, spm_mgr);
+
 	amdgpu_spm_init_device(spm_mgr);
+	prof_xcp_mgr->teardown_cb = amdgpu_spm_teardown;
 }
 
 void amdgpu_spm_mgr_release_owner(struct amdgpu_spm_mgr *spm_mgr,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
index eaeb880e209c..760fc04e7a6e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
@@ -62,6 +62,9 @@ struct amdgpu_spm_cntr {
 	wait_queue_head_t spm_buf_wq;
 	u32   have_users_buf_cnt;       /* Updated under spm_worker_mutex; read locklessly via READ_ONCE */
 	bool  are_users_buf_filled;     /* Updated under spm_worker_mutex; read locklessly via READ_ONCE */
+	bool  releasing;                /* Set before teardown to unblock waiters */
+	atomic_t active_waiters;        /* Tracks threads in spm_wait_for_fill_awake */
+	wait_queue_head_t waiters_done; /* Signaled when active_waiters reaches 0 */
 };
 
 struct amdgpu_spm_mgr {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH 08/12] drm/amdgpu: implement data dump from spm ring buffer
  2026-09-21 20:03 [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions James Zhu
                   ` (5 preceding siblings ...)
  2026-09-21 20:03 ` [PATCH 07/12] drm/amdgpu: add profiler/spm operation AMDGPU_SPM_OP_RELEASE James Zhu
@ 2026-09-21 20:03 ` James Zhu
  2026-09-21 20:03 ` [PATCH 09/12] drm/amdgpu: workaround for spm ring buffer overflow James Zhu
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 36+ messages in thread
From: James Zhu @ 2026-09-21 20:03 UTC (permalink / raw)
  To: amd-gfx, alexander.deucher, Bing.Ma; +Cc: jamesz, Bing Ma

Implement the drain path that copies SPM counter records out of the
per-XCC ring buffers into the destination buffer installed by
AMDGPU_SPM_OP_SET_DEST_BUF; amdgpu_spm_work() was a stub until now.

The worker snapshots spm_cntr and lead_thread together under
spm_irq_lock and bails out if either is NULL, so a work item still queued
when RELEASE runs does not touch a dying session. It attaches the lead
thread's mm with kthread_use_mm() so copy_to_user() targets the profiler's
address space, and drains the XCC instances of the partition under
spm_worker_mutex, which locks out SET_DEST_BUF and the RELEASE teardown.

Per instance, amdgpu_spm_read_ring_buffer() snapshots is_spm_started and
ring_rptr in one spm_irq_lock critical section, since _spm_stop() clears
the flag and resets rptr under the same lock. The hardware write pointer
is rejected unless it is below ring_size, because a corrupted value would
make the copy run past the ring and leak kernel memory to userspace.
Records are copied in one segment, or in two when the producer has
wrapped, and the current rptr is always published back to the RLC with
amdgpu_rlc_spm_set_rdptr().

Copies stop at the end of the user buffer and mark it filled, which sets
are_users_buf_filled and wakes spm_buf_wq so a thread blocked in
SET_DEST_BUF can install a new buffer. Nothing is propagated out of the
worker; a failed copy, a bad wptr, or a missing or full user buffer set
has_data_loss as a hint that the ring may have stalled. SET_DEST_BUF
flushes the worker after installing a buffer unless every instance was
started by that same call. The polling thread that schedules the work is
added by a later patch.

Signed-off-by: James Zhu <James.Zhu@amd.com>
Reviewed-by: Bing Ma <bing.ma@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c | 226 +++++++++++++++++++++++-
 1 file changed, 225 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
index b3132f9a8c0a..d17fb2963981 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
@@ -21,6 +21,8 @@
  * OTHER DEALINGS IN THE SOFTWARE.
  */
 
+#include <linux/kthread.h>
+#include <linux/sched/mm.h>
 #include <linux/sched/task.h>
 
 #include "amdgpu.h"
@@ -49,9 +51,227 @@ static bool amdgpu_spm_is_owner(struct amdgpu_spm_mgr *spm_mgr, struct drm_file
 	return spm_mgr->file == filp;
 }
 
+static int amdgpu_spm_data_copy(struct amdgpu_spm_mgr *spm_mgr,
+				struct amdgpu_spm_cntr *spm_cntr, u32 ring_rptr,
+				u32 size_to_copy, int inst)
+{
+	struct amdgpu_spm_base *spm = &spm_cntr->spm[inst];
+	u64 __user *user_address;
+	u64 *ring_buf;
+	u32 user_buf_space_left;
+	unsigned long flags;
+	u32 new_rptr;
+
+	if (!spm->ubuf.user_addr)
+		return -EFAULT;
+
+	user_address = (u64 __user *)((void __user *)spm->ubuf.user_addr + spm->size_copied);
+	/* From RLC spec, ring_rptr = 0 points to spm->cpu_addr + AMDGPU_SPM_META_SIZE */
+	ring_buf = (u64 *)((u8 *)spm->cpu_addr + ring_rptr + AMDGPU_SPM_META_SIZE);
+
+	user_buf_space_left = spm->ubuf.ubufsize - spm->size_copied;
+
+	if (size_to_copy < user_buf_space_left) {
+		if (copy_to_user(user_address, ring_buf, size_to_copy)) {
+			spm->has_data_loss = true;
+			return -EFAULT;
+		}
+		spm->size_copied += size_to_copy;
+		new_rptr = ring_rptr + size_to_copy;
+	} else {
+		if (copy_to_user(user_address, ring_buf, user_buf_space_left)) {
+			spm->has_data_loss = true;
+			return -EFAULT;
+		}
+
+		spm->size_copied = spm->ubuf.ubufsize;
+		new_rptr = ring_rptr + user_buf_space_left;
+		spm->is_user_buf_filled = true;
+	}
+
+	/* _spm_stop() resets rptr to 0 and is serialized against the drain
+	 * path by spm_worker_mutex, so this store cannot resurrect a stale
+	 * rptr. spm_irq_lock pairs with the rptr snapshot taken under it.
+	 */
+	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+	WRITE_ONCE(spm->ring_rptr, new_rptr);
+	spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+
+	return 0;
+}
+
+static int amdgpu_spm_read_ring_buffer(struct amdgpu_spm_mgr *spm_mgr,
+				       struct amdgpu_spm_cntr *spm_cntr, int inst)
+{
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr = to_prof_xcp_mgr(spm_mgr, spm_mgr);
+	struct amdgpu_spm_base *spm = &spm_cntr->spm[inst];
+	struct amdgpu_device *adev = prof_xcp_mgr->adev;
+	unsigned long flags;
+	u32 size_to_copy;
+	u32 ring_wptr;
+	u32 ring_rptr;
+	bool is_started;
+	int ret = 0;
+
+	/* Snapshot is_spm_started and ring_rptr together under spm_irq_lock to prevent
+	 * _spm_stop from resetting ring_rptr=0 between the check and access.
+	 * If SPM is not started, ring_rptr is stale and must not be accessed.
+	 */
+	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+	is_started = spm->is_spm_started;
+	ring_rptr = spm->ring_rptr;
+	spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+
+	if (!is_started)
+		return 0;
+
+	ring_wptr = READ_ONCE(((u32 *)spm->cpu_addr)[0]);
+	dma_rmb();
+
+	/* Validate hardware-provided wptr to prevent OOB kernel read.
+	 * Corrupted wptr could cause size_to_copy to exceed ring buffer
+	 * bounds, leaking kernel memory to userspace.
+	 */
+	if (ring_wptr >= spm->ring_size) {
+		spm->has_data_loss = true;
+		dev_dbg(adev->dev, "[SPM#%d] Invalid wptr:0x%x >= ring_size:0x%x\n",
+			inst, ring_wptr, spm->ring_size);
+		goto exit;
+	}
+
+	/* SPM might stall if we cannot copy data out of SPM ringbuffer.
+	 * spm->has_data_loss is only a hint here since stall is only a
+	 * possibility and data loss might not happen. But it is a useful
+	 * hint for user mode profiler to take extra actions.
+	 */
+	if (!spm->has_user_buf || spm->is_user_buf_filled) {
+		spm->has_data_loss = true;
+		/* Mark user buffer as filled when no user buffer was set
+		 * or buffer is already full, signaling profiler to stop.
+		 */
+		if (!spm->is_user_buf_filled)
+			spm->is_user_buf_filled = true;
+		dev_dbg(adev->dev, "[SPM#%d] [%d|%d] rptr:0x%x--wptr:0x%x\n", inst,
+			spm->has_user_buf, spm->is_user_buf_filled, ring_rptr, ring_wptr);
+		goto exit;
+	}
+
+	if (ring_rptr == ring_wptr)
+		goto exit;
+
+	if (ring_wptr > ring_rptr) {
+		size_to_copy = ring_wptr - ring_rptr;
+		ret = amdgpu_spm_data_copy(spm_mgr, spm_cntr, ring_rptr, size_to_copy, inst);
+	} else {
+		/* Copy tail segment [rptr..ring_size) */
+		size_to_copy = spm->ring_size - ring_rptr;
+		ret = amdgpu_spm_data_copy(spm_mgr, spm_cntr, ring_rptr, size_to_copy, inst);
+
+		/* Copy wrapped segment [0..ring_wptr) if tail copy completed */
+		if (spm->ring_size == READ_ONCE(spm->ring_rptr)) {
+			if (ring_wptr == 0) {
+				/* No wrapped data; reset rptr to start. */
+				spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+				WRITE_ONCE(spm->ring_rptr, 0);
+				spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+				goto exit;
+			}
+			/* Reset rptr to 0 before copying wrapped segment. */
+			spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+			WRITE_ONCE(spm->ring_rptr, 0);
+			spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+
+			size_to_copy = ring_wptr;
+			if (!ret && !spm->is_user_buf_filled)
+				ret = amdgpu_spm_data_copy(spm_mgr, spm_cntr, 0,
+							   size_to_copy, inst);
+		}
+	}
+
+exit:
+	/* Read rptr under spm_irq_lock to pair with the snapshot above. */
+	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+	ring_rptr = spm->ring_rptr;
+	spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+	amdgpu_rlc_spm_set_rdptr(adev, inst, ring_rptr);
+	return ret;
+}
+
 static void amdgpu_spm_work(struct work_struct *work)
 {
-	/* TODO: dump spm ring buffer to user buffer */
+	struct amdgpu_spm_mgr *spm_mgr = container_of(work,
+						      struct amdgpu_spm_mgr,
+						      spm_work);
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr =
+						      to_prof_xcp_mgr(spm_mgr, spm_mgr);
+	struct amdgpu_device *adev = prof_xcp_mgr->adev;
+	u32 xcp_id = prof_xcp_mgr->xcp_id;
+	struct amdgpu_spm_cntr *spm_cntr;
+	struct task_struct *lead_thread;
+	struct mm_struct *mm = NULL;
+	unsigned long flags;
+	u32 xcc_mask;
+	int ret;
+
+	/* Snapshot spm_cntr and lead_thread under spm_irq_lock.
+	 * Protects against amdgpu_spm_release() racing: release sets both
+	 * to NULL under this lock, so either we see both non-NULL (safe to
+	 * proceed) or one/both NULL (bail out).
+	 */
+	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+	spm_cntr = spm_mgr->spm_cntr;
+	lead_thread = spm_mgr->lead_thread;
+	spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+
+	if (!spm_cntr || !lead_thread)
+		return;
+
+	ret = amdgpu_xcp_get_xcc_mask(adev, xcp_id, &xcc_mask);
+	if (ret) {
+		dev_err(adev->dev, "Failed to get XCC mask for XCP %u\n", xcp_id);
+		return;
+	}
+
+	mm = get_task_mm(lead_thread);
+	if (mm) {
+		kthread_use_mm(mm);
+		{ /* attach mm */
+			bool filled;
+			int inst;
+
+			mutex_lock(&spm_cntr->spm_worker_mutex);
+			WRITE_ONCE(spm_cntr->are_users_buf_filled, false);
+			/* XCC instance index space is consistent across:
+			 * - acquire: for_each_inst(inst, xcc_mask) -> spm[inst]
+			 * - drain work: for_each_inst(inst, xcc_mask) -> spm[inst]
+			 * - interrupt: spm[xcc_id] where xcc_id is HW XCC instance
+			 * All use the same XCC-instance index space from xcc_mask.
+			 */
+			for_each_inst(inst, xcc_mask) {
+				struct amdgpu_spm_base *spm = &spm_cntr->spm[inst];
+
+				/* Errors logged but not propagated: this is async work,
+				 * copy_to_user errors are surfaced via has_data_loss flag.
+				 */
+				ret = amdgpu_spm_read_ring_buffer(spm_mgr, spm_cntr, inst);
+				if (ret)
+					dev_dbg(adev->dev,
+						"[SPM#%d] ring buffer drain failed: %d\n",
+						inst, ret);
+				if (spm->is_user_buf_filled)
+					WRITE_ONCE(spm_cntr->are_users_buf_filled, true);
+			}
+			filled = READ_ONCE(spm_cntr->are_users_buf_filled);
+			mutex_unlock(&spm_cntr->spm_worker_mutex);
+			if (filled) {
+				dev_dbg(adev->dev, "SPM wake up buffer work queue.\n");
+				wake_up(&spm_cntr->spm_buf_wq);
+			}
+		} /* detach mm */
+		kthread_unuse_mm(mm);
+		/* release the mm structure */
+		mmput(mm);
+	}
 }
 
 int amdgpu_spm_ioctl(struct drm_device *dev, struct drm_amdgpu_spm_args *args,
@@ -712,6 +932,10 @@ static int amdgpu_spm_set_dest_buf(struct amdgpu_spm_mgr *spm_mgr, void *data,
 			mutex_unlock(&spm_cntr->spm_worker_mutex);
 		}
 	}
+
+	if (xcc_mask & ~started_mask)
+		flush_work(&spm_mgr->spm_work);
+
 	goto out;
 
 start_failure:
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH 09/12] drm/amdgpu: workaround for spm ring buffer overflow
  2026-09-21 20:03 [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions James Zhu
                   ` (6 preceding siblings ...)
  2026-09-21 20:03 ` [PATCH 08/12] drm/amdgpu: implement data dump from spm ring buffer James Zhu
@ 2026-09-21 20:03 ` James Zhu
  2026-09-21 20:03 ` [PATCH 10/12] drm/amdgpu: add SPM ring-buffer polling thread for pre-GFX12 James Zhu
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 36+ messages in thread
From: James Zhu @ 2026-09-21 20:03 UTC (permalink / raw)
  To: amd-gfx, alexander.deucher, Bing.Ma; +Cc: jamesz, Bing Ma

Pre-GFX12 SPM has a hardware bug: when the ring fills, hardware keeps
writing past the end instead of wrapping back to the start. Work around
it by reserving a region past the ring end, detecting how much of it was
written, and moving that data to where hardware should have put it.

Add spm_overflow_reserved to amdgpu_spm_mgr, 0x400 on pre-GFX12 and 0 on
GFX12+, and subtract it from spm->ring_size before
amdgpu_rlc_spm_acquire() so hardware is programmed with a shorter ring.
The reserve is carved out of the existing allocation, so the buffer
object size is unchanged.

amdgpu_spm_preset() fills the reserve with AMDGPU_SPM_OVERFLOW_MAGIC, one
sentinel per 0x20 block: acquire arms the whole region, each drain
re-arms only the part it consumed. On the wrap branch the drain scans at
the same stride for the first surviving sentinel, and that offset is the
overflow size. The data is copied to the start of the ring, which is safe
because overflow and wrap are mutually exclusive. A dma_wmb() after
re-arming keeps the sentinels visible before the read pointer hands the
space back to hardware.

Counter data matching the magic on a 0x20 boundary ends the scan early
and under-reports the overflow; this is accepted as unlikely.

Signed-off-by: James Zhu <James.Zhu@amd.com>
Reviewed-by: Bing Ma <bing.ma@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c | 71 +++++++++++++++++++++++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h |  4 ++
 2 files changed, 75 insertions(+)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
index d17fb2963981..532d1bc34f89 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
@@ -39,6 +39,9 @@
 #define AMDGPU_SPM_RING_SIZE		(4 * 1024 * 1024)
 #define AMDGPU_SPM_META_SIZE		0x20
 
+/* used to detect SPM overflow */
+#define AMDGPU_SPM_OVERFLOW_MAGIC	0xBEEFABCDDEADABCDULL
+
 static int amdgpu_spm_acquire(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *filp);
 static int amdgpu_spm_release(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *filp);
 static void _amdgpu_spm_release(struct amdgpu_spm_mgr *spm_mgr, int inst, struct drm_file *filp);
@@ -51,6 +54,34 @@ static bool amdgpu_spm_is_owner(struct amdgpu_spm_mgr *spm_mgr, struct drm_file
 	return spm_mgr->file == filp;
 }
 
+/* Re-arm overflow detection region with magic values.
+ *
+ * Called with size = spm_overflow_reserved at acquire time to arm the entire
+ * reserved region. Called with size = overflow_size on each drain: the
+ * sentinel scan defines overflow_size as how far HW wrote into the reserve,
+ * so [overflow_size, spm_overflow_reserved) still holds its acquire-time
+ * magic and only the consumed portion needs re-arming.
+ */
+static void amdgpu_spm_preset(struct amdgpu_spm_base *spm, u32 size)
+{
+	u64 *overflow_ptr, *overflow_end_ptr;
+
+	overflow_ptr = (u64 *)((u8 *)spm->cpu_addr
+				+ spm->ring_size + AMDGPU_SPM_META_SIZE);
+	overflow_end_ptr = overflow_ptr + (size >> 3);
+	/* Write one magic sentinel per 0x20-byte block.
+	 * overflow_ptr is u64*, so += 4 advances 0x20 bytes (4 * 8 = 32).
+	 * Matches the 0x20 detection stride in overflow scanning.
+	 */
+	for (; overflow_ptr < overflow_end_ptr; overflow_ptr += 4)
+		*overflow_ptr = AMDGPU_SPM_OVERFLOW_MAGIC;
+
+	/* Sentinels must be globally visible before HW may write the reserve
+	 * again, i.e. before the rdptr update or the SPM enable that follows.
+	 */
+	dma_wmb();
+}
+
 static int amdgpu_spm_data_copy(struct amdgpu_spm_mgr *spm_mgr,
 				struct amdgpu_spm_cntr *spm_cntr, u32 ring_rptr,
 				u32 size_to_copy, int inst)
@@ -107,6 +138,7 @@ static int amdgpu_spm_read_ring_buffer(struct amdgpu_spm_mgr *spm_mgr,
 	struct amdgpu_spm_base *spm = &spm_cntr->spm[inst];
 	struct amdgpu_device *adev = prof_xcp_mgr->adev;
 	unsigned long flags;
+	u32 overflow_size = 0;
 	u32 size_to_copy;
 	u32 ring_wptr;
 	u32 ring_rptr;
@@ -163,6 +195,34 @@ static int amdgpu_spm_read_ring_buffer(struct amdgpu_spm_mgr *spm_mgr,
 		size_to_copy = ring_wptr - ring_rptr;
 		ret = amdgpu_spm_data_copy(spm_mgr, spm_cntr, ring_rptr, size_to_copy, inst);
 	} else {
+		u64 *ring_start, *ring_end;
+
+		ring_start = (u64 *)((u8 *)spm->cpu_addr + AMDGPU_SPM_META_SIZE);
+		ring_end = ring_start + (spm->ring_size >> 3);
+		/* Detect overflow size by scanning for first magic sentinel.
+		 * Heuristic limitation: if genuine counter data at a 0x20 boundary
+		 * equals AMDGPU_SPM_OVERFLOW_MAGIC (0xBEEFABCDDEADABCD), we stop
+		 * early and under-report overflow. Low probability, but acknowledged
+		 * trade-off.
+		 */
+		for ( ; overflow_size < spm_mgr->spm_overflow_reserved;
+		      overflow_size += AMDGPU_SPM_META_SIZE) {
+			u64 *overflow_ptr = ring_end + (overflow_size >> 3);
+
+			if (READ_ONCE(*overflow_ptr) == AMDGPU_SPM_OVERFLOW_MAGIC)
+				break;
+		}
+		if (overflow_size) {
+			dev_dbg(adev->dev, "SPM ring buffer overflow size 0x%x\n", overflow_size);
+			/* HW BUG WORKAROUND: On pre-GFX12, when the ring fills, hardware
+			 * incorrectly writes overflow data to [ring_size..ring_size+overflow_size)
+			 * instead of wrapping to [0..overflow_size). Copy it to where it should
+			 * have been. Overflow and wrap are mutually exclusive: if overflow occurs,
+			 * hardware failed to wrap, so no valid data exists at [0..overflow_size).
+			 */
+			memcpy(ring_start, ring_end, overflow_size);
+		}
+
 		/* Copy tail segment [rptr..ring_size) */
 		size_to_copy = spm->ring_size - ring_rptr;
 		ret = amdgpu_spm_data_copy(spm_mgr, spm_cntr, ring_rptr, size_to_copy, inst);
@@ -189,6 +249,7 @@ static int amdgpu_spm_read_ring_buffer(struct amdgpu_spm_mgr *spm_mgr,
 	}
 
 exit:
+	amdgpu_spm_preset(spm, overflow_size);
 	/* Read rptr under spm_irq_lock to pair with the snapshot above. */
 	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
 	ring_rptr = spm->ring_rptr;
@@ -302,6 +363,13 @@ int amdgpu_spm_ioctl(struct drm_device *dev, struct drm_amdgpu_spm_args *args,
 
 static void amdgpu_spm_init_device(struct amdgpu_spm_mgr *spm_mgr)
 {
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr = to_prof_xcp_mgr(spm_mgr, spm_mgr);
+	struct amdgpu_device *adev = prof_xcp_mgr->adev;
+
+	/* pre-gfx12 spm has a hardware bug to cause overflow */
+	if (amdgpu_ip_version(adev, GC_HWIP, 0) < IP_VERSION(12, 0, 0))
+		spm_mgr->spm_overflow_reserved = 0x400;
+
 	spm_mgr->spm_cntr = NULL;
 	spin_lock_init(&spm_mgr->spm_irq_lock);
 	/* spm_work lives for the device lifetime; initialize it once here so
@@ -334,6 +402,8 @@ static int _amdgpu_spm_acquire(struct amdgpu_spm_mgr *spm_mgr, int inst, struct
 		goto out;
 	}
 
+	/* reserve space to fix spm overflow */
+	spm->ring_size -= spm_mgr->spm_overflow_reserved;
 	ret = amdgpu_rlc_spm_acquire(adev, inst, drm_priv_to_vm(filp),
 				     spm->gpu_addr, spm->ring_size);
 	if (ret)
@@ -344,6 +414,7 @@ static int _amdgpu_spm_acquire(struct amdgpu_spm_mgr *spm_mgr, int inst, struct
 	 * the wptr there); the ring itself starts at AMDGPU_SPM_META_SIZE.
 	 */
 	spm->ring_size -= AMDGPU_SPM_META_SIZE;
+	amdgpu_spm_preset(spm, spm_mgr->spm_overflow_reserved);
 
 	goto out;
 
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
index 760fc04e7a6e..552c5701e08c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
@@ -77,6 +77,10 @@ struct amdgpu_spm_mgr {
 	struct work_struct spm_work;
 	/* Protects spm_cntr publication and the SPM state read in the IRQ path */
 	spinlock_t spm_irq_lock;
+	/* 0x400 for pre-GFX12 overflow workaround, 0 for GFX12+.
+	 * Must be 0x20-aligned to match preset/scan stride.
+	 */
+	u32    spm_overflow_reserved;
 };
 
 int amdgpu_spm_ioctl(struct drm_device *dev, struct drm_amdgpu_spm_args *args,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH 10/12] drm/amdgpu: add SPM ring-buffer polling thread for pre-GFX12
  2026-09-21 20:03 [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions James Zhu
                   ` (7 preceding siblings ...)
  2026-09-21 20:03 ` [PATCH 09/12] drm/amdgpu: workaround for spm ring buffer overflow James Zhu
@ 2026-09-21 20:03 ` James Zhu
  2026-09-21 20:03 ` [PATCH 11/12] drm/amdgpu: add profiler/spm support for gfx12_0 James Zhu
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 36+ messages in thread
From: James Zhu @ 2026-09-21 20:03 UTC (permalink / raw)
  To: amd-gfx, alexander.deucher, Bing.Ma; +Cc: jamesz, Bing Ma

Implement a kernel thread for polling SPM ring buffer on pre-GFX12
hardware that lacks IRQ support for profiler events.

The thread runs at 100us intervals to prevent ring buffer overflow on
hardware without watermark interrupts. When the buffer exceeds the
50% watermark threshold, the thread schedules drain work via
schedule_work(). The 100us polling cadence ensures the 4MB ring buffer
cannot overflow at typical SPM data rates while limiting the thread to
~10k wakeups/s per active render node.

The thread is created on the first _spm_start() and stopped from
_spm_stop() with kthread_stop_put(). The creator holds a task_struct
reference for the thread's lifetime, because the thread can still
return early if amdgpu_xcp_get_xcc_mask() fails.

Signed-off-by: James Zhu <James.Zhu@amd.com>
Reviewed-by: Bing Ma <bing.ma@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c | 149 +++++++++++++++++++++++-
 drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h |   2 +
 2 files changed, 150 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
index 532d1bc34f89..1b10618dde08 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.c
@@ -21,6 +21,7 @@
  * OTHER DEALINGS IN THE SOFTWARE.
  */
 
+#include <linux/delay.h>
 #include <linux/kthread.h>
 #include <linux/sched/mm.h>
 #include <linux/sched/task.h>
@@ -54,6 +55,105 @@ static bool amdgpu_spm_is_owner(struct amdgpu_spm_mgr *spm_mgr, struct drm_file
 	return spm_mgr->file == filp;
 }
 
+static int amdgpu_spm_monitor_thread(void *param)
+{
+	struct amdgpu_spm_mgr *spm_mgr = param;
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr =
+							to_prof_xcp_mgr(spm_mgr, spm_mgr);
+	struct amdgpu_device *adev = prof_xcp_mgr->adev;
+	u32 xcp_id = prof_xcp_mgr->xcp_id;
+	u32 xcc_mask;
+	int ret;
+
+	ret = amdgpu_xcp_get_xcc_mask(adev, xcp_id, &xcc_mask);
+	if (ret)
+		return 0;
+
+	/* Snapshot spm_cntr once per iteration: kthread_stop() (synchronous)
+	 * joins this thread before release clears and frees spm_cntr.
+	 */
+	while (!kthread_should_stop()) {
+		struct amdgpu_spm_cntr *spm_cntr = READ_ONCE(spm_mgr->spm_cntr);
+		bool need_schedule = false;
+		u32 inst;
+
+		if (!spm_cntr)
+			break;
+
+		/* Pre-GFX12 lacks watermark interrupts, poll every 100us to prevent overflow */
+		usleep_range(100, 110);
+
+		if (!mutex_trylock(&spm_cntr->spm_worker_mutex))
+			continue;
+
+		for_each_inst(inst, xcc_mask) {
+			struct amdgpu_spm_base *spm = &spm_cntr->spm[inst];
+			u32 last_drain_rptr;
+			u32 ring_size;
+			u32 ring_rptr;
+			u32 ring_wptr;
+			bool above_watermark;
+
+			if (!spm->is_spm_started || !spm->ring_size)
+				continue;
+
+			ring_size = spm->ring_size;
+			ring_rptr = READ_ONCE(spm->ring_rptr);
+			last_drain_rptr = READ_ONCE(spm->last_drain_rptr);
+			ring_wptr = READ_ONCE(((u32 *)spm->cpu_addr)[0]);
+
+			if (ring_wptr >= ring_size)
+				continue;
+
+			/* Evaluate watermark condition per-instance independently */
+			above_watermark = (ring_rptr != last_drain_rptr &&
+					   (ring_size + ring_wptr - ring_rptr) % ring_size >
+					   (ring_size >> 1));
+
+			if (above_watermark) {
+				WRITE_ONCE(spm->last_drain_rptr, ring_rptr);
+				dev_dbg(adev->dev,
+					"[SPM#%d] soft interrupt rptr:0x%08x--wptr:0x%08x\n",
+					 inst, ring_rptr, ring_wptr);
+				need_schedule = true;
+			} else if (need_schedule) {
+				/* Another instance triggered drain, update our reference too */
+				WRITE_ONCE(spm->last_drain_rptr, ring_rptr);
+			}
+		}
+		mutex_unlock(&spm_cntr->spm_worker_mutex);
+		if (need_schedule)
+			schedule_work(&spm_mgr->spm_work);
+	}
+
+	/* The thread never clears spm_monitor_thread: the pointer and its
+	 * task reference are owned by the start/stop paths.
+	 */
+	return 0;
+}
+
+static int amdgpu_spm_monitor_thread_start(struct amdgpu_spm_mgr *spm_mgr)
+{
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr = to_prof_xcp_mgr(spm_mgr, spm_mgr);
+	struct amdgpu_device *adev = prof_xcp_mgr->adev;
+	struct task_struct *task;
+
+	task = kthread_create(amdgpu_spm_monitor_thread, spm_mgr, "spm_%d_%u",
+			      adev->ddev.render->index, prof_xcp_mgr->xcp_id);
+	if (IS_ERR(task)) {
+		dev_dbg(adev->dev, "Failed to create spm monitor thread (%ld).\n",
+			PTR_ERR(task));
+		return PTR_ERR(task);
+	}
+
+	/* Pin the task_struct before waking: the thread can exit on its own. */
+	get_task_struct(task);
+	spm_mgr->spm_monitor_thread = task;
+	wake_up_process(task);
+
+	return 0;
+}
+
 /* Re-arm overflow detection region with magic values.
  *
  * Called with size = spm_overflow_reserved at acquire time to arm the entire
@@ -191,6 +291,7 @@ static int amdgpu_spm_read_ring_buffer(struct amdgpu_spm_mgr *spm_mgr,
 	if (ring_rptr == ring_wptr)
 		goto exit;
 
+	WRITE_ONCE(spm->last_drain_rptr, ring_rptr);
 	if (ring_wptr > ring_rptr) {
 		size_to_copy = ring_wptr - ring_rptr;
 		ret = amdgpu_spm_data_copy(spm_mgr, spm_cntr, ring_rptr, size_to_copy, inst);
@@ -415,6 +516,7 @@ static int _amdgpu_spm_acquire(struct amdgpu_spm_mgr *spm_mgr, int inst, struct
 	 */
 	spm->ring_size -= AMDGPU_SPM_META_SIZE;
 	amdgpu_spm_preset(spm, spm_mgr->spm_overflow_reserved);
+	spm->last_drain_rptr = ~0;
 
 	goto out;
 
@@ -479,6 +581,10 @@ static int amdgpu_spm_acquire(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *f
 		spm_mgr->spm_cntr->spm_use_cnt++;
 	}
 
+	/* No thread can be live here: release and both start/stop failure paths
+	 * clear it, and a live session blocks re-acquire.
+	 */
+	WARN_ON_ONCE(spm_mgr->spm_monitor_thread);
 	spm_mgr->file = filp;
 
 	goto out;
@@ -524,13 +630,39 @@ static int _spm_start(struct amdgpu_spm_mgr *spm_mgr,
 	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
 	spm->is_spm_started = true;
 	WRITE_ONCE(spm->ring_rptr, 0);
+	WRITE_ONCE(spm->last_drain_rptr, ~0);
 	spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
 
+	/* Pre-GFX12 lacks HW watermark interrupts, so start a polling thread.
+	 * GFX12+ uses amdgpu_spm_interrupt() instead. spm_monitor_thread is
+	 * never touched from IRQ context, so the mutex above is enough.
+	 */
+	if (amdgpu_ip_version(adev, GC_HWIP, 0) < IP_VERSION(12, 0, 0) &&
+	    !spm_mgr->spm_monitor_thread) {
+		ret = amdgpu_spm_monitor_thread_start(spm_mgr);
+		if (ret) {
+			/* Thread failed to start, revert state */
+			dev_dbg(adev->dev,
+				"Failed to start SPM monitor thread, ret = %d\n", ret);
+			spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
+			spm->is_spm_started = false;
+			spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+			return ret;
+		}
+	}
+
 	ret = amdgpu_rlc_spm_cntl(adev, inst, 1);
 	if (ret) {
+		struct task_struct *monitor_thread;
+
 		spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
 		spm->is_spm_started = false;
+		monitor_thread = spm_mgr->spm_monitor_thread;
+		spm_mgr->spm_monitor_thread = NULL;
 		spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
+
+		if (monitor_thread)
+			kthread_stop_put(monitor_thread);
 	}
 	return ret;
 }
@@ -538,9 +670,23 @@ static int _spm_start(struct amdgpu_spm_mgr *spm_mgr,
 static void _spm_stop(struct amdgpu_spm_mgr *spm_mgr,
 		      struct amdgpu_device *adev, int inst)
 {
+	struct amdgpu_profiler_xcp_mgr *prof_xcp_mgr = to_prof_xcp_mgr(spm_mgr, spm_mgr);
 	struct amdgpu_spm_base *spm = &spm_mgr->spm_cntr->spm[inst];
+	struct task_struct *monitor_thread;
 	unsigned long flags;
 
+	lockdep_assert_held(&prof_xcp_mgr->mutex);
+
+	/* Stopping any instance tears down the manager-scoped monitor thread.
+	 * Safe because callers always stop the whole started set, which is
+	 * all-or-nothing.
+	 */
+	monitor_thread = spm_mgr->spm_monitor_thread;
+	spm_mgr->spm_monitor_thread = NULL;
+
+	if (monitor_thread)
+		kthread_stop_put(monitor_thread);
+
 	amdgpu_rlc_spm_cntl(adev, inst, 0);
 	spin_lock_irqsave(&spm_mgr->spm_irq_lock, flags);
 	spm->is_spm_started = false;
@@ -548,6 +694,7 @@ static void _spm_stop(struct amdgpu_spm_mgr *spm_mgr,
 	 * Adjust rptr accordingly.
 	 */
 	WRITE_ONCE(spm->ring_rptr, 0);
+	WRITE_ONCE(spm->last_drain_rptr, ~0);
 	spin_unlock_irqrestore(&spm_mgr->spm_irq_lock, flags);
 }
 
@@ -639,7 +786,7 @@ static int amdgpu_spm_release(struct amdgpu_spm_mgr *spm_mgr, struct drm_file *f
 	/* Stop hardware SPM for all instances and free their ring buffers.
 	 * spm_worker_mutex locks out the drain worker, which would otherwise
 	 * keep updating rptr after the reset and read the ring after it is
-	 * freed.
+	 * freed. Monitor thread is stopped on the first _spm_stop() call.
 	 */
 	mutex_lock(&spm_cntr->spm_worker_mutex);
 	for_each_inst(inst, xcc_mask)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
index 552c5701e08c..b057d763fddf 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_spm.h
@@ -46,6 +46,7 @@ struct amdgpu_spm_base {
 	bool   has_user_buf;
 	bool   is_user_buf_filled;
 	bool   is_spm_started;
+	u32    last_drain_rptr;
 };
 
 struct amdgpu_spm_cntr {
@@ -68,6 +69,7 @@ struct amdgpu_spm_cntr {
 };
 
 struct amdgpu_spm_mgr {
+	struct task_struct *spm_monitor_thread;
 	struct drm_file *file;
 
 	struct task_struct *lead_thread;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH 11/12] drm/amdgpu: add profiler/spm support for gfx12_0
  2026-09-21 20:03 [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions James Zhu
                   ` (8 preceding siblings ...)
  2026-09-21 20:03 ` [PATCH 10/12] drm/amdgpu: add SPM ring-buffer polling thread for pre-GFX12 James Zhu
@ 2026-09-21 20:03 ` James Zhu
  2026-09-21 20:03 ` [PATCH 12/12] drm/amdgpu: bump KMS driver version to 3.65 for profiler UAPI James Zhu
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 36+ messages in thread
From: James Zhu @ 2026-09-21 20:03 UTC (permalink / raw)
  To: amd-gfx, alexander.deucher, Bing.Ma; +Cc: jamesz

Add the gfx_v12_0_spm_funcs backend. Ring setup writes
RLC_SPM_PERFMON_RING_BASE_LO/HI, _RING_SIZE, the segment threshold and
clears CP_CP_PERFMON_CNTL. start selects PERFMON_RING_MODE in
RLC_SPM_PERFMON_CNTL, resets the perfmon state through CP_CP_PERFMON_CNTL,
clears RLC_SPM_ACCUM_MODE and rewinds both RLC_SPM_RING_WRPTR and
RLC_SPM_RING_RDPTR; stop stops counting, resets the perfmon state and
rewinds the pointers again. set_rdptr updates RLC_SPM_RING_RDPTR as the
consumer drains the ring. All callbacks bracket their register access with
amdgpu_gfx_off_ctrl() so GFXOFF cannot swallow the writes, and program
GET_INST(GC, 0) since gfx12 is single-XCC.

Add the gfx.spm_irq source for
GFX_12_0_0__SRCID__RLC_STRM_PERF_MONITOR_INTERRUPT on SOC15_IH_CLIENTID_RLC,
toggle it through RLC_SPM_INT_CNTL in the set-interrupt-state callback,
take a reference in hw_init and drop it in hw_fini, and forward the
interrupt to the SPM manager via amdgpu_rlc_spm_interrupt(). Because gfx12
reports ring progress by interrupt, no polling thread is needed.

Signed-off-by: James Zhu <James.Zhu@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c | 133 +++++++++++++++++++++++++
 1 file changed, 133 insertions(+)

diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
index 398e9c463ec6..e02ab1046fb6 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
@@ -272,6 +272,8 @@ static void gfx_v12_0_set_mqd_funcs(struct amdgpu_device *adev);
 static void gfx_v12_0_set_imu_funcs(struct amdgpu_device *adev);
 static int gfx_v12_0_get_cu_info(struct amdgpu_device *adev,
 				 struct amdgpu_cu_info *cu_info);
+static void gfx_v12_0_update_spm_vmid(struct amdgpu_device *adev,
+				      int xcc_id, struct amdgpu_ring *ring, unsigned vmid);
 static uint64_t gfx_v12_0_get_gpu_clock_counter(struct amdgpu_device *adev);
 static void gfx_v12_0_select_se_sh(struct amdgpu_device *adev, u32 se_num,
 				   u32 sh_num, u32 instance, int xcc_id);
@@ -1467,6 +1469,13 @@ static int gfx_v12_0_sw_init(struct amdgpu_ip_block *ip_block)
 						  num_compute_rings);
 	}
 
+	/* SPM */
+	r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_RLC,
+			      GFX_12_0_0__SRCID__RLC_STRM_PERF_MONITOR_INTERRUPT,
+			      &adev->gfx.spm_irq);
+	if (r)
+		return r;
+
 	/* EOP Event */
 	r = amdgpu_irq_add_id(adev, SOC21_IH_CLIENTID_GRBM_CP,
 			      GFX_12_0_0__SRCID__CP_EOP_INTERRUPT,
@@ -3854,6 +3863,10 @@ static int gfx_v12_0_hw_init(struct amdgpu_ip_block *ip_block)
 	if (r)
 		return r;
 
+	r = amdgpu_irq_get(adev, &adev->gfx.spm_irq, 0);
+	if (r)
+		goto err_spm_irq;
+
 	r = amdgpu_irq_get(adev, &adev->gfx.priv_inst_irq, 0);
 	if (r)
 		goto err_priv_inst;
@@ -3873,6 +3886,8 @@ static int gfx_v12_0_hw_init(struct amdgpu_ip_block *ip_block)
 err_bad_op:
 	amdgpu_irq_put(adev, &adev->gfx.priv_inst_irq, 0);
 err_priv_inst:
+	amdgpu_irq_put(adev, &adev->gfx.spm_irq, 0);
+err_spm_irq:
 	amdgpu_irq_put(adev, &adev->gfx.priv_reg_irq, 0);
 	return r;
 }
@@ -3887,6 +3902,7 @@ static int gfx_v12_0_hw_fini(struct amdgpu_ip_block *ip_block)
 	gfx_v12_0_set_userq_eop_interrupts(adev, false);
 	amdgpu_irq_put(adev, &adev->gfx.bad_op_irq, 0);
 	amdgpu_irq_put(adev, &adev->gfx.priv_inst_irq, 0);
+	amdgpu_irq_put(adev, &adev->gfx.spm_irq, 0);
 	amdgpu_irq_put(adev, &adev->gfx.priv_reg_irq, 0);
 
 	if (!adev->no_hw_access) {
@@ -3972,6 +3988,85 @@ static uint64_t gfx_v12_0_get_gpu_clock_counter(struct amdgpu_device *adev)
 	return clock;
 }
 
+static void gfx_v12_0_spm_start(struct amdgpu_device *adev, int xcc_id)
+{
+	uint32_t data = 0;
+
+	amdgpu_gfx_off_ctrl(adev, false);
+
+	data = RREG32_SOC15(GC, GET_INST(GC, 0), regRLC_SPM_PERFMON_CNTL);
+	data |= RLC_SPM_PERFMON_CNTL__PERFMON_RING_MODE_MASK;
+	WREG32_SOC15(GC, GET_INST(GC, 0), regRLC_SPM_PERFMON_CNTL, data);
+
+	data = REG_SET_FIELD(0, CP_CP_PERFMON_CNTL, SPM_PERFMON_STATE,
+			CP_PERFMON_STATE_DISABLE_AND_RESET);
+	WREG32_SOC15(GC, GET_INST(GC, 0), regCP_CP_PERFMON_CNTL, data);
+	WREG32_SOC15(GC, GET_INST(GC, 0), regRLC_SPM_ACCUM_MODE, 0);
+
+	WREG32_SOC15(GC, GET_INST(GC, 0), regRLC_SPM_RING_WRPTR, 0);
+	WREG32_SOC15(GC, GET_INST(GC, 0), regRLC_SPM_RING_RDPTR, 0);
+
+	WREG32_SOC15(GC, GET_INST(GC, 0), regRLC_SPM_INT_CNTL, 1);
+
+	amdgpu_gfx_off_ctrl(adev, true);
+}
+
+static void gfx_v12_0_spm_stop(struct amdgpu_device *adev, int xcc_id)
+{
+	uint32_t data = 0;
+
+	amdgpu_gfx_off_ctrl(adev, false);
+
+	data = REG_SET_FIELD(0, CP_CP_PERFMON_CNTL, SPM_PERFMON_STATE,
+			CP_PERFMON_STATE_STOP_COUNTING);
+	WREG32_SOC15(GC, GET_INST(GC, 0), regCP_CP_PERFMON_CNTL, data);
+
+	data = REG_SET_FIELD(0, CP_CP_PERFMON_CNTL, SPM_PERFMON_STATE,
+			CP_PERFMON_STATE_DISABLE_AND_RESET);
+	WREG32_SOC15(GC, GET_INST(GC, 0), regCP_CP_PERFMON_CNTL, data);
+
+	WREG32_SOC15(GC, GET_INST(GC, 0), regRLC_SPM_RING_WRPTR, 0);
+	WREG32_SOC15(GC, GET_INST(GC, 0), regRLC_SPM_RING_RDPTR, 0);
+
+	WREG32_SOC15(GC, GET_INST(GC, 0), regRLC_SPM_INT_CNTL, 0);
+
+	amdgpu_gfx_off_ctrl(adev, true);
+}
+
+static void gfx_v12_0_spm_set_rdptr(struct amdgpu_device *adev, int xcc_id, u32 rptr)
+{
+	amdgpu_gfx_off_ctrl(adev, false);
+	WREG32_SOC15(GC, GET_INST(GC, 0), regRLC_SPM_RING_RDPTR, rptr);
+	amdgpu_gfx_off_ctrl(adev, true);
+}
+
+static void gfx_v12_0_set_spm_perfmon_ring_buf(struct amdgpu_device *adev,
+					    int xcc_id, u64 gpu_addr, u32 size)
+{
+	amdgpu_gfx_off_ctrl(adev, false);
+	WREG32_SOC15(GC, GET_INST(GC, 0), regRLC_SPM_PERFMON_RING_BASE_LO,
+		     lower_32_bits(gpu_addr));
+	WREG32_SOC15(GC, GET_INST(GC, 0), regRLC_SPM_PERFMON_RING_BASE_HI,
+		     upper_32_bits(gpu_addr));
+	WREG32_SOC15(GC, GET_INST(GC, 0), regRLC_SPM_PERFMON_RING_SIZE, size);
+	WREG32_SOC15(GC, GET_INST(GC, 0), regRLC_SPM_SEGMENT_THRESHOLD, 0x1);
+	WREG32_SOC15(GC, GET_INST(GC, 0), regCP_CP_PERFMON_CNTL, 0);
+	amdgpu_gfx_off_ctrl(adev, true);
+}
+
+static const struct amdgpu_spm_funcs gfx_v12_0_spm_funcs = {
+	.start = &gfx_v12_0_spm_start,
+	.stop = &gfx_v12_0_spm_stop,
+	.set_rdptr = &gfx_v12_0_spm_set_rdptr,
+	.set_spm_perfmon_ring_buf = &gfx_v12_0_set_spm_perfmon_ring_buf,
+	.set_spm_config_size = 0,
+};
+
+static void gfx_v12_0_set_spm_funcs(struct amdgpu_device *adev)
+{
+	adev->gfx.spmfuncs = &gfx_v12_0_spm_funcs;
+}
+
 static int gfx_v12_0_early_init(struct amdgpu_ip_block *ip_block)
 {
 	struct amdgpu_device *adev = ip_block->adev;
@@ -4004,6 +4099,7 @@ static int gfx_v12_0_early_init(struct amdgpu_ip_block *ip_block)
 						  AMDGPU_MAX_COMPUTE_RINGS);
 	}
 
+	gfx_v12_0_set_spm_funcs(adev);
 	gfx_v12_0_set_kiq_pm4_funcs(adev);
 	gfx_v12_0_set_ring_funcs(adev);
 	gfx_v12_0_set_irq_funcs(adev);
@@ -5505,11 +5601,48 @@ static const struct amdgpu_irq_src_funcs gfx_v12_0_priv_inst_irq_funcs = {
 	.process = gfx_v12_0_priv_inst_irq,
 };
 
+static int gfx_v12_0_spm_set_interrupt_state(struct amdgpu_device *adev,
+					  struct amdgpu_irq_src *src,
+					  unsigned int type,
+					  enum amdgpu_interrupt_state state)
+{
+
+	switch (state) {
+	case AMDGPU_IRQ_STATE_DISABLE:
+		WREG32_SOC15(GC, GET_INST(GC, 0), regRLC_SPM_INT_CNTL, 0);
+		break;
+	case AMDGPU_IRQ_STATE_ENABLE:
+		WREG32_SOC15(GC, GET_INST(GC, 0), regRLC_SPM_INT_CNTL, 1);
+		break;
+	default:
+		break;
+	}
+
+
+	return 0;
+}
+
+static int gfx_v12_0_set_spm_irq(struct amdgpu_device *adev,
+			     struct amdgpu_irq_src *source,
+			     struct amdgpu_iv_entry *entry)
+{
+	amdgpu_rlc_spm_interrupt(adev, 0);
+	return 0;
+}
+
+static const struct amdgpu_irq_src_funcs gfx_v12_0_set_spm_irq_funcs = {
+	.set = gfx_v12_0_spm_set_interrupt_state,
+	.process = gfx_v12_0_set_spm_irq,
+};
+
 static void gfx_v12_0_set_irq_funcs(struct amdgpu_device *adev)
 {
 	adev->gfx.eop_irq.num_types = AMDGPU_CP_IRQ_LAST;
 	adev->gfx.eop_irq.funcs = &gfx_v12_0_eop_irq_funcs;
 
+	adev->gfx.spm_irq.num_types = 1;
+	adev->gfx.spm_irq.funcs = &gfx_v12_0_set_spm_irq_funcs;
+
 	adev->gfx.priv_reg_irq.num_types = 1;
 	adev->gfx.priv_reg_irq.funcs = &gfx_v12_0_priv_reg_irq_funcs;
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH 12/12] drm/amdgpu: bump KMS driver version to 3.65 for profiler UAPI
  2026-09-21 20:03 [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions James Zhu
                   ` (9 preceding siblings ...)
  2026-09-21 20:03 ` [PATCH 11/12] drm/amdgpu: add profiler/spm support for gfx12_0 James Zhu
@ 2026-09-21 20:03 ` James Zhu
  2026-09-22 15:09 ` [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions Timur Kristóf
  2026-09-22 15:13 ` Christian König
  12 siblings, 0 replies; 36+ messages in thread
From: James Zhu @ 2026-09-21 20:03 UTC (permalink / raw)
  To: amd-gfx, alexander.deucher, Bing.Ma; +Cc: jamesz, Bing Ma

Advertise the AMDGPU_PROFILER ioctl and profiler/SPM UAPI by bumping
KMS_DRIVER_MINOR to 65.

The bump is deferred to the end of the series, after every SPM operation
is fully implemented, so that a 3.65.0 driver reliably indicates a
working profiler/SPM interface rather than a stubbed one. This keeps the
version a dependable capability signal if the series is bisected or only
partially applied.

Register the profiler ioctl here rather than with the infrastructure
patch, so the ABI only becomes reachable once every SPM operation is
implemented.

Signed-off-by: James Zhu <James.Zhu@amd.com>
Reviewed-by: Bing Ma <bing.ma@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 03bbf101bf23..7607b1e1776c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -127,9 +127,10 @@
  * - 3.62.0 - Add AMDGPU_IDS_FLAGS_MODE_PF, AMDGPU_IDS_FLAGS_MODE_VF & AMDGPU_IDS_FLAGS_MODE_PT
  * - 3.63.0 - GFX12 display DCC supports 256B max compressed block size
  * - 3.64.0 - Userq IP support query
+ * - 3.65.0 - Add AMDGPU_PROFILER ioctl and profiler UAPI structs
  */
 #define KMS_DRIVER_MAJOR	3
-#define KMS_DRIVER_MINOR	64
+#define KMS_DRIVER_MINOR	65
 #define KMS_DRIVER_PATCHLEVEL	0
 
 /*
@@ -3112,6 +3113,7 @@ const struct drm_ioctl_desc amdgpu_ioctls_kms[] = {
 	DRM_IOCTL_DEF_DRV(AMDGPU_USERQ_WAIT, amdgpu_userq_wait_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(AMDGPU_GEM_LIST_HANDLES, amdgpu_gem_list_handles_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(AMDGPU_PROC_OPTIONS, amdgpu_proc_options_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(AMDGPU_PROFILER, amdgpu_profiler_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
 };
 
 static const struct drm_driver amdgpu_kms_driver = {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [PATCH 04/12] drm/amdgpu: add amdgpu_xcp_get_xcc_mask helper
  2026-09-21 20:03 ` [PATCH 04/12] drm/amdgpu: add amdgpu_xcp_get_xcc_mask helper James Zhu
@ 2026-09-22  5:22   ` Lazar, Lijo
  2026-09-22 13:35     ` James Zhu
  0 siblings, 1 reply; 36+ messages in thread
From: Lazar, Lijo @ 2026-09-22  5:22 UTC (permalink / raw)
  To: James Zhu, amd-gfx, alexander.deucher, Bing.Ma; +Cc: jamesz



On 22-Sep-26 1:33 AM, James Zhu wrote:
> Add amdgpu_xcp_get_xcc_mask() to return the XCC instance mask for a given
> XCP partition. This is needed by upcoming SPM (Stream Performance Monitor)
> support, which programs hardware per partition and must operate only on
> the XCCs that belong to a given XCP.
> 
> For partitioned systems the helper returns only the XCCs belonging to the
> specified XCP. When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the device is
> not partitioned) it returns the device's full XCC mask (e.g. 0xFF for an
> 8-XCC device). Note that the full device mask is returned even on a
> partitioned device; callers that require partition isolation (e.g. SPM)
> must reject AMDGPU_XCP_NO_PARTITION on a partitioned device themselves.
> 

Could you clarifiy on this part - xcp_id = AMDGPU_XCP_NO_PARTITION usage 
on 8-XCC device?

The default state on a multi-XCC device after driver load is all XCCs in 
a single partition called SPX. It should have at least one partition valid.

Thanks,
Lioj

> amdgpu_xcp_get_num_xcc() is reworked to take (adev, xcp_id) and derive the
> count from this helper via hweight32(). The helper is defined out of line
> in amdgpu_xcp.c so that the static inline in amdgpu_xcp.h never has to
> dereference the (incomplete) struct amdgpu_device; it only passes the
> pointer through.
> 
> This is a user-visible change: amdgpu_xcp_get_num_xcc() previously
> returned 1 for non-partitioned systems (and for AMDGPU_XCP_NO_PARTITION),
> and now returns the actual XCC count (e.g. 8 for an 8-XCC device). CWSR
> callers (amdgpu_kms.c, amdgpu_userq.c, mes_userqueue.c) scale the per-XCC
> control-stack and debug-memory sizes by num_xcc, so the uAPI-visible
> ctl_stack_size and dbg_mem_size values grow accordingly to match the
> actual hardware resource requirements, which were previously undersized.
> 
> Fixes: 4cd417bfec49 ("drm/amdgpu: Add helper function to get xcc count")
> Signed-off-by: James Zhu <James.Zhu@amd.com>
> Reviewed-by: Bing Ma <bing.ma@amd.com>
> ---
>   drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c    |  2 +-
>   drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c  |  2 +-
>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c    | 48 ++++++++++++++++++++++
>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h    | 32 +++++++--------
>   drivers/gpu/drm/amd/amdgpu/mes_userqueue.c |  3 +-
>   5 files changed, 66 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
> index 57fa69c0382d..7782ccd4d28f 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
> @@ -1501,7 +1501,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
>   		fpriv = (struct amdgpu_fpriv *)filp->driver_priv;
>   		if (!amdgpu_trap_is_enabled(adev) || !fpriv->trap_obj)
>   			return -EOPNOTSUPP;
> -		num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv->xcp_id);
> +		num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>   		cwsr_info.ctl_stack_size =
>   			adev->trap_info->cwsr.xcc_ctl_stack_sz * num_xcc;
>   		cwsr_info.dbg_mem_size =
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> index db09e879b85e..d19af9cb6ee1 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
> @@ -258,7 +258,7 @@ int amdgpu_userq_input_trap_params_validate(struct amdgpu_usermode_queue *queue,
>   	int num_xcc;
>   	int r;
>   
> -	num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv->xcp_id);
> +	num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>   	r = amdgpu_trap_validate_params(queue->userq_mgr->adev, trap_params,
>   					num_xcc);
>   	if (r)
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
> index 2a56f5ec9c76..90846d55292b 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
> @@ -390,6 +390,54 @@ int amdgpu_xcp_get_inst_details(struct amdgpu_xcp *xcp,
>   	return 0;
>   }
>   
> +/**
> + * amdgpu_xcp_get_xcc_mask - Get XCC instance mask for a partition
> + * @adev: amdgpu device pointer
> + * @xcp_id: XCP partition ID
> + * @xcc_mask: output parameter for XCC instance mask
> + *
> + * Returns the XCC instance mask for the specified XCP partition.
> + * For partitioned systems, returns only the XCCs belonging to this XCP.
> + *
> + * When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the device is not
> + * partitioned), the device's full XCC mask is returned (e.g. 0xFF for an
> + * 8-XCC device). Note that the full device mask is returned even on a
> + * partitioned device: callers that require partition isolation (e.g. SPM)
> + * must reject AMDGPU_XCP_NO_PARTITION on a partitioned device themselves.
> + *
> + * Note: On error, xcc_mask is undefined. Callers must check return code
> + * and only read xcc_mask when return == 0.
> + *
> + * Return: 0 on success (non-partitioned systems or valid XCP found),
> + *         -EINVAL if xcc_mask is NULL, no matching XCP id is found, or the
> + *         partition's GFX IP details are unavailable
> + */
> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
> +			    uint32_t *xcc_mask)
> +{
> +	struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr;
> +	struct amdgpu_xcp *xcp;
> +	int i;
> +
> +	if (!xcc_mask)
> +		return -EINVAL;
> +
> +	if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION) {
> +		/* Non-partitioned: return the device's full XCC mask. */
> +		*xcc_mask = adev->gfx.xcc_mask;
> +		return 0;
> +	}
> +
> +	/* Partitioned system: get this XCP's XCCs */
> +	for_each_xcp(xcp_mgr, xcp, i) {
> +		if (xcp->id == xcp_id)
> +			return amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
> +							   xcc_mask);
> +	}
> +
> +	return -EINVAL;
> +}
> +
>   int amdgpu_xcp_dev_register(struct amdgpu_device *adev,
>   			const struct pci_device_id *ent)
>   {
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
> index 692508be23fb..6eed82ad49b1 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
> @@ -44,6 +44,7 @@
>   #define XCP_INST_MASK(num_inst, xcp_id)		\
>   	(num_inst ? GENMASK(num_inst - 1, 0) << (xcp_id * num_inst) : 0)
>   
> +struct amdgpu_device;
>   struct amdgpu_fpriv;
>   
>   enum AMDGPU_XCP_IP_BLOCK {
> @@ -219,26 +220,23 @@ amdgpu_get_next_xcp(struct amdgpu_xcp_mgr *xcp_mgr, int *from)
>   	for (i = 0, xcp = amdgpu_get_next_xcp(xcp_mgr, &i); xcp; \
>   	     ++i, xcp = amdgpu_get_next_xcp(xcp_mgr, &i))
>   
> -static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_xcp_mgr *xcp_mgr,
> -					 int xcp_id)
> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
> +			    uint32_t *xcc_mask);
> +
> +static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_device *adev, int xcp_id)
>   {
> -	struct amdgpu_xcp *xcp;
>   	uint32_t xcc_mask;
> -	int i, r;
> -
> -	if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION)
> +	int r, count;
> +
> +	/* Derive the XCC count from the partition's XCC mask (or the full
> +	 * device mask when not partitioned). amdgpu_xcp_get_xcc_mask()
> +	 * handles both partitioned and non-partitioned cases.
> +	 */
> +	r = amdgpu_xcp_get_xcc_mask(adev, xcp_id, &xcc_mask);
> +	if (unlikely(r))
>   		return 1;
> -	for_each_xcp(xcp_mgr, xcp, i) {
> -		if (xcp->id == xcp_id) {
> -			r = amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
> -							&xcc_mask);
> -			if (unlikely(r))
> -				return 1;
> -			else
> -				return hweight32(xcc_mask);
> -		}
> -	}
>   
> -	return 1;
> +	count = hweight32(xcc_mask);
> +	return count ? count : 1;
>   }
>   #endif
> diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
> index 16aa22b25098..1c7b2f3227e5 100644
> --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
> +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
> @@ -503,8 +503,7 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue,
>   				uq_mgr_to_fpriv(queue->userq_mgr);
>   			int num_xcc;
>   
> -			num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr,
> -							 fpriv->xcp_id);
> +			num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>   			trap_params.ctx_save_area_address =
>   				compute_mqd->ctx_save_area_va;
>   			trap_params.cwsr_sz = compute_mqd->ctx_save_area_size;


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 04/12] drm/amdgpu: add amdgpu_xcp_get_xcc_mask helper
  2026-09-22  5:22   ` Lazar, Lijo
@ 2026-09-22 13:35     ` James Zhu
  2026-09-22 13:45       ` Lazar, Lijo
  0 siblings, 1 reply; 36+ messages in thread
From: James Zhu @ 2026-09-22 13:35 UTC (permalink / raw)
  To: Lazar, Lijo, James Zhu, amd-gfx, alexander.deucher, Bing.Ma

[-- Attachment #1: Type: text/plain, Size: 10072 bytes --]


On 2026-09-22 01:22, Lazar, Lijo wrote:
>
>
> On 22-Sep-26 1:33 AM, James Zhu wrote:
>> Add amdgpu_xcp_get_xcc_mask() to return the XCC instance mask for a 
>> given
>> XCP partition. This is needed by upcoming SPM (Stream Performance 
>> Monitor)
>> support, which programs hardware per partition and must operate only on
>> the XCCs that belong to a given XCP.
>>
>> For partitioned systems the helper returns only the XCCs belonging to 
>> the
>> specified XCP. When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the device is
>> not partitioned) it returns the device's full XCC mask (e.g. 0xFF for an
>> 8-XCC device). Note that the full device mask is returned even on a
>> partitioned device; callers that require partition isolation (e.g. SPM)
>> must reject AMDGPU_XCP_NO_PARTITION on a partitioned device themselves.
>>
>
> Could you clarifiy on this part - xcp_id = AMDGPU_XCP_NO_PARTITION 
> usage on 8-XCC device?
>
> The default state on a multi-XCC device after driver load is all XCCs 
> in a single partition called SPX. It should have at least one 
> partition valid.

[JZ] Yes, 8-XCC device should not reach AMDGPU_XCP_NO_PARTITION case.

the if(!xcp_mgr ||xcp_id ==AMDGPU_XCP_NO_PARTITION) mainly covers non 
partition device, I can remove the above comments which don't reflect 
the source accurately.
> Thanks,
> Lioj
>
>> amdgpu_xcp_get_num_xcc() is reworked to take (adev, xcp_id) and 
>> derive the
>> count from this helper via hweight32(). The helper is defined out of 
>> line
>> in amdgpu_xcp.c so that the static inline in amdgpu_xcp.h never has to
>> dereference the (incomplete) struct amdgpu_device; it only passes the
>> pointer through.
>>
>> This is a user-visible change: amdgpu_xcp_get_num_xcc() previously
>> returned 1 for non-partitioned systems (and for 
>> AMDGPU_XCP_NO_PARTITION),
>> and now returns the actual XCC count (e.g. 8 for an 8-XCC device). CWSR
>> callers (amdgpu_kms.c, amdgpu_userq.c, mes_userqueue.c) scale the 
>> per-XCC
>> control-stack and debug-memory sizes by num_xcc, so the uAPI-visible
>> ctl_stack_size and dbg_mem_size values grow accordingly to match the
>> actual hardware resource requirements, which were previously undersized.
>>
>> Fixes: 4cd417bfec49 ("drm/amdgpu: Add helper function to get xcc count")
>> Signed-off-by: James Zhu <James.Zhu@amd.com>
>> Reviewed-by: Bing Ma <bing.ma@amd.com>
>> ---
>>   drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c    |  2 +-
>>   drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c  |  2 +-
>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c    | 48 ++++++++++++++++++++++
>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h    | 32 +++++++--------
>>   drivers/gpu/drm/amd/amdgpu/mes_userqueue.c |  3 +-
>>   5 files changed, 66 insertions(+), 21 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c 
>> b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>> index 57fa69c0382d..7782ccd4d28f 100644
>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>> @@ -1501,7 +1501,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, 
>> void *data, struct drm_file *filp)
>>           fpriv = (struct amdgpu_fpriv *)filp->driver_priv;
>>           if (!amdgpu_trap_is_enabled(adev) || !fpriv->trap_obj)
>>               return -EOPNOTSUPP;
>> -        num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv->xcp_id);
>> +        num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>           cwsr_info.ctl_stack_size =
>>               adev->trap_info->cwsr.xcc_ctl_stack_sz * num_xcc;
>>           cwsr_info.dbg_mem_size =
>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c 
>> b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>> index db09e879b85e..d19af9cb6ee1 100644
>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>> @@ -258,7 +258,7 @@ int 
>> amdgpu_userq_input_trap_params_validate(struct amdgpu_usermode_queue 
>> *queue,
>>       int num_xcc;
>>       int r;
>>   -    num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv->xcp_id);
>> +    num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>       r = amdgpu_trap_validate_params(queue->userq_mgr->adev, 
>> trap_params,
>>                       num_xcc);
>>       if (r)
>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c 
>> b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>> index 2a56f5ec9c76..90846d55292b 100644
>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>> @@ -390,6 +390,54 @@ int amdgpu_xcp_get_inst_details(struct 
>> amdgpu_xcp *xcp,
>>       return 0;
>>   }
>>   +/**
>> + * amdgpu_xcp_get_xcc_mask - Get XCC instance mask for a partition
>> + * @adev: amdgpu device pointer
>> + * @xcp_id: XCP partition ID
>> + * @xcc_mask: output parameter for XCC instance mask
>> + *
>> + * Returns the XCC instance mask for the specified XCP partition.
>> + * For partitioned systems, returns only the XCCs belonging to this 
>> XCP.
>> + *
>> + * When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the device is not
>> + * partitioned), the device's full XCC mask is returned (e.g. 0xFF 
>> for an
>> + * 8-XCC device). Note that the full device mask is returned even on a
>> + * partitioned device: callers that require partition isolation 
>> (e.g. SPM)
>> + * must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>> themselves.
>> + *
>> + * Note: On error, xcc_mask is undefined. Callers must check return 
>> code
>> + * and only read xcc_mask when return == 0.
>> + *
>> + * Return: 0 on success (non-partitioned systems or valid XCP found),
>> + *         -EINVAL if xcc_mask is NULL, no matching XCP id is found, 
>> or the
>> + *         partition's GFX IP details are unavailable
>> + */
>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
>> +                uint32_t *xcc_mask)
>> +{
>> +    struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr;
>> +    struct amdgpu_xcp *xcp;
>> +    int i;
>> +
>> +    if (!xcc_mask)
>> +        return -EINVAL;
>> +
>> +    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION) {
>> +        /* Non-partitioned: return the device's full XCC mask. */
>> +        *xcc_mask = adev->gfx.xcc_mask;
>> +        return 0;
>> +    }
>> +
>> +    /* Partitioned system: get this XCP's XCCs */
>> +    for_each_xcp(xcp_mgr, xcp, i) {
>> +        if (xcp->id == xcp_id)
>> +            return amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
>> +                               xcc_mask);
>> +    }
>> +
>> +    return -EINVAL;
>> +}
>> +
>>   int amdgpu_xcp_dev_register(struct amdgpu_device *adev,
>>               const struct pci_device_id *ent)
>>   {
>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h 
>> b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>> index 692508be23fb..6eed82ad49b1 100644
>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>> @@ -44,6 +44,7 @@
>>   #define XCP_INST_MASK(num_inst, xcp_id)        \
>>       (num_inst ? GENMASK(num_inst - 1, 0) << (xcp_id * num_inst) : 0)
>>   +struct amdgpu_device;
>>   struct amdgpu_fpriv;
>>     enum AMDGPU_XCP_IP_BLOCK {
>> @@ -219,26 +220,23 @@ amdgpu_get_next_xcp(struct amdgpu_xcp_mgr 
>> *xcp_mgr, int *from)
>>       for (i = 0, xcp = amdgpu_get_next_xcp(xcp_mgr, &i); xcp; \
>>            ++i, xcp = amdgpu_get_next_xcp(xcp_mgr, &i))
>>   -static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_xcp_mgr 
>> *xcp_mgr,
>> -                     int xcp_id)
>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
>> +                uint32_t *xcc_mask);
>> +
>> +static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_device *adev, 
>> int xcp_id)
>>   {
>> -    struct amdgpu_xcp *xcp;
>>       uint32_t xcc_mask;
>> -    int i, r;
>> -
>> -    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION)
>> +    int r, count;
>> +
>> +    /* Derive the XCC count from the partition's XCC mask (or the full
>> +     * device mask when not partitioned). amdgpu_xcp_get_xcc_mask()
>> +     * handles both partitioned and non-partitioned cases.
>> +     */
>> +    r = amdgpu_xcp_get_xcc_mask(adev, xcp_id, &xcc_mask);
>> +    if (unlikely(r))
>>           return 1;
>> -    for_each_xcp(xcp_mgr, xcp, i) {
>> -        if (xcp->id == xcp_id) {
>> -            r = amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
>> -                            &xcc_mask);
>> -            if (unlikely(r))
>> -                return 1;
>> -            else
>> -                return hweight32(xcc_mask);
>> -        }
>> -    }
>>   -    return 1;
>> +    count = hweight32(xcc_mask);
>> +    return count ? count : 1;
>>   }
>>   #endif
>> diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c 
>> b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>> index 16aa22b25098..1c7b2f3227e5 100644
>> --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>> +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>> @@ -503,8 +503,7 @@ static int mes_userq_mqd_create(struct 
>> amdgpu_usermode_queue *queue,
>>                   uq_mgr_to_fpriv(queue->userq_mgr);
>>               int num_xcc;
>>   -            num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr,
>> -                             fpriv->xcp_id);
>> +            num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>               trap_params.ctx_save_area_address =
>>                   compute_mqd->ctx_save_area_va;
>>               trap_params.cwsr_sz = compute_mqd->ctx_save_area_size;
>

[-- Attachment #2: Type: text/html, Size: 22579 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 04/12] drm/amdgpu: add amdgpu_xcp_get_xcc_mask helper
  2026-09-22 13:35     ` James Zhu
@ 2026-09-22 13:45       ` Lazar, Lijo
  2026-09-22 14:04         ` James Zhu
  2026-09-22 14:11         ` James Zhu
  0 siblings, 2 replies; 36+ messages in thread
From: Lazar, Lijo @ 2026-09-22 13:45 UTC (permalink / raw)
  To: James Zhu, James Zhu, amd-gfx, alexander.deucher, Bing.Ma



On 22-Sep-26 7:05 PM, James Zhu wrote:
> 
> On 2026-09-22 01:22, Lazar, Lijo wrote:
>>
>>
>> On 22-Sep-26 1:33 AM, James Zhu wrote:
>>> Add amdgpu_xcp_get_xcc_mask() to return the XCC instance mask for a 
>>> given
>>> XCP partition. This is needed by upcoming SPM (Stream Performance 
>>> Monitor)
>>> support, which programs hardware per partition and must operate only on
>>> the XCCs that belong to a given XCP.
>>>
>>> For partitioned systems the helper returns only the XCCs belonging to 
>>> the
>>> specified XCP. When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the device is
>>> not partitioned) it returns the device's full XCC mask (e.g. 0xFF for an
>>> 8-XCC device). Note that the full device mask is returned even on a
>>> partitioned device; callers that require partition isolation (e.g. SPM)
>>> must reject AMDGPU_XCP_NO_PARTITION on a partitioned device themselves.
>>>
>>
>> Could you clarifiy on this part - xcp_id = AMDGPU_XCP_NO_PARTITION 
>> usage on 8-XCC device?
>>
>> The default state on a multi-XCC device after driver load is all XCCs 
>> in a single partition called SPX. It should have at least one 
>> partition valid.
> 
> [JZ] Yes, 8-XCC device should not reach AMDGPU_XCP_NO_PARTITION case.
> 
> the if(!xcp_mgr ||xcp_id ==AMDGPU_XCP_NO_PARTITION) mainly covers non 
> partition device, I can remove the above comments which don't reflect 
> the source accurately.

Then I'm not sure what issue this is fixing.

Multi-xcc devices will be in SPX mode and will return a mask with all 
the XCCs. Non-multi xcc devices are expected to have this set to 1 
anyway to reflect that a single GC is present always.

Thanks,
Lijo

>> Thanks,
>> Lioj
>>
>>> amdgpu_xcp_get_num_xcc() is reworked to take (adev, xcp_id) and 
>>> derive the
>>> count from this helper via hweight32(). The helper is defined out of 
>>> line
>>> in amdgpu_xcp.c so that the static inline in amdgpu_xcp.h never has to
>>> dereference the (incomplete) struct amdgpu_device; it only passes the
>>> pointer through.
>>>
>>> This is a user-visible change: amdgpu_xcp_get_num_xcc() previously
>>> returned 1 for non-partitioned systems (and for 
>>> AMDGPU_XCP_NO_PARTITION),
>>> and now returns the actual XCC count (e.g. 8 for an 8-XCC device). CWSR
>>> callers (amdgpu_kms.c, amdgpu_userq.c, mes_userqueue.c) scale the 
>>> per-XCC
>>> control-stack and debug-memory sizes by num_xcc, so the uAPI-visible
>>> ctl_stack_size and dbg_mem_size values grow accordingly to match the
>>> actual hardware resource requirements, which were previously undersized.
>>>
>>> Fixes: 4cd417bfec49 ("drm/amdgpu: Add helper function to get xcc count")
>>> Signed-off-by: James Zhu <James.Zhu@amd.com>
>>> Reviewed-by: Bing Ma <bing.ma@amd.com>
>>> ---
>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c    |  2 +-
>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c  |  2 +-
>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c    | 48 ++++++++++++++++++++++
>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h    | 32 +++++++--------
>>>   drivers/gpu/drm/amd/amdgpu/mes_userqueue.c |  3 +-
>>>   5 files changed, 66 insertions(+), 21 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/ 
>>> drm/amd/amdgpu/amdgpu_kms.c
>>> index 57fa69c0382d..7782ccd4d28f 100644
>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>> @@ -1501,7 +1501,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, 
>>> void *data, struct drm_file *filp)
>>>           fpriv = (struct amdgpu_fpriv *)filp->driver_priv;
>>>           if (!amdgpu_trap_is_enabled(adev) || !fpriv->trap_obj)
>>>               return -EOPNOTSUPP;
>>> -        num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv->xcp_id);
>>> +        num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>           cwsr_info.ctl_stack_size =
>>>               adev->trap_info->cwsr.xcc_ctl_stack_sz * num_xcc;
>>>           cwsr_info.dbg_mem_size =
>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/ 
>>> drm/amd/amdgpu/amdgpu_userq.c
>>> index db09e879b85e..d19af9cb6ee1 100644
>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>> @@ -258,7 +258,7 @@ int 
>>> amdgpu_userq_input_trap_params_validate(struct amdgpu_usermode_queue 
>>> *queue,
>>>       int num_xcc;
>>>       int r;
>>>   -    num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv->xcp_id);
>>> +    num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>       r = amdgpu_trap_validate_params(queue->userq_mgr->adev, 
>>> trap_params,
>>>                       num_xcc);
>>>       if (r)
>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/ 
>>> drm/amd/amdgpu/amdgpu_xcp.c
>>> index 2a56f5ec9c76..90846d55292b 100644
>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>> @@ -390,6 +390,54 @@ int amdgpu_xcp_get_inst_details(struct 
>>> amdgpu_xcp *xcp,
>>>       return 0;
>>>   }
>>>   +/**
>>> + * amdgpu_xcp_get_xcc_mask - Get XCC instance mask for a partition
>>> + * @adev: amdgpu device pointer
>>> + * @xcp_id: XCP partition ID
>>> + * @xcc_mask: output parameter for XCC instance mask
>>> + *
>>> + * Returns the XCC instance mask for the specified XCP partition.
>>> + * For partitioned systems, returns only the XCCs belonging to this 
>>> XCP.
>>> + *
>>> + * When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the device is not
>>> + * partitioned), the device's full XCC mask is returned (e.g. 0xFF 
>>> for an
>>> + * 8-XCC device). Note that the full device mask is returned even on a
>>> + * partitioned device: callers that require partition isolation 
>>> (e.g. SPM)
>>> + * must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>>> themselves.
>>> + *
>>> + * Note: On error, xcc_mask is undefined. Callers must check return 
>>> code
>>> + * and only read xcc_mask when return == 0.
>>> + *
>>> + * Return: 0 on success (non-partitioned systems or valid XCP found),
>>> + *         -EINVAL if xcc_mask is NULL, no matching XCP id is found, 
>>> or the
>>> + *         partition's GFX IP details are unavailable
>>> + */
>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
>>> +                uint32_t *xcc_mask)
>>> +{
>>> +    struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr;
>>> +    struct amdgpu_xcp *xcp;
>>> +    int i;
>>> +
>>> +    if (!xcc_mask)
>>> +        return -EINVAL;
>>> +
>>> +    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION) {
>>> +        /* Non-partitioned: return the device's full XCC mask. */
>>> +        *xcc_mask = adev->gfx.xcc_mask;
>>> +        return 0;
>>> +    }
>>> +
>>> +    /* Partitioned system: get this XCP's XCCs */
>>> +    for_each_xcp(xcp_mgr, xcp, i) {
>>> +        if (xcp->id == xcp_id)
>>> +            return amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
>>> +                               xcc_mask);
>>> +    }
>>> +
>>> +    return -EINVAL;
>>> +}
>>> +
>>>   int amdgpu_xcp_dev_register(struct amdgpu_device *adev,
>>>               const struct pci_device_id *ent)
>>>   {
>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h b/drivers/gpu/ 
>>> drm/amd/amdgpu/amdgpu_xcp.h
>>> index 692508be23fb..6eed82ad49b1 100644
>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>> @@ -44,6 +44,7 @@
>>>   #define XCP_INST_MASK(num_inst, xcp_id)        \
>>>       (num_inst ? GENMASK(num_inst - 1, 0) << (xcp_id * num_inst) : 0)
>>>   +struct amdgpu_device;
>>>   struct amdgpu_fpriv;
>>>     enum AMDGPU_XCP_IP_BLOCK {
>>> @@ -219,26 +220,23 @@ amdgpu_get_next_xcp(struct amdgpu_xcp_mgr 
>>> *xcp_mgr, int *from)
>>>       for (i = 0, xcp = amdgpu_get_next_xcp(xcp_mgr, &i); xcp; \
>>>            ++i, xcp = amdgpu_get_next_xcp(xcp_mgr, &i))
>>>   -static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_xcp_mgr 
>>> *xcp_mgr,
>>> -                     int xcp_id)
>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
>>> +                uint32_t *xcc_mask);
>>> +
>>> +static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_device *adev, 
>>> int xcp_id)
>>>   {
>>> -    struct amdgpu_xcp *xcp;
>>>       uint32_t xcc_mask;
>>> -    int i, r;
>>> -
>>> -    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION)
>>> +    int r, count;
>>> +
>>> +    /* Derive the XCC count from the partition's XCC mask (or the full
>>> +     * device mask when not partitioned). amdgpu_xcp_get_xcc_mask()
>>> +     * handles both partitioned and non-partitioned cases.
>>> +     */
>>> +    r = amdgpu_xcp_get_xcc_mask(adev, xcp_id, &xcc_mask);
>>> +    if (unlikely(r))
>>>           return 1;
>>> -    for_each_xcp(xcp_mgr, xcp, i) {
>>> -        if (xcp->id == xcp_id) {
>>> -            r = amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
>>> -                            &xcc_mask);
>>> -            if (unlikely(r))
>>> -                return 1;
>>> -            else
>>> -                return hweight32(xcc_mask);
>>> -        }
>>> -    }
>>>   -    return 1;
>>> +    count = hweight32(xcc_mask);
>>> +    return count ? count : 1;
>>>   }
>>>   #endif
>>> diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/ 
>>> gpu/drm/amd/amdgpu/mes_userqueue.c
>>> index 16aa22b25098..1c7b2f3227e5 100644
>>> --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>> +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>> @@ -503,8 +503,7 @@ static int mes_userq_mqd_create(struct 
>>> amdgpu_usermode_queue *queue,
>>>                   uq_mgr_to_fpriv(queue->userq_mgr);
>>>               int num_xcc;
>>>   -            num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr,
>>> -                             fpriv->xcp_id);
>>> +            num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>               trap_params.ctx_save_area_address =
>>>                   compute_mqd->ctx_save_area_va;
>>>               trap_params.cwsr_sz = compute_mqd->ctx_save_area_size;
>>


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 04/12] drm/amdgpu: add amdgpu_xcp_get_xcc_mask helper
  2026-09-22 13:45       ` Lazar, Lijo
@ 2026-09-22 14:04         ` James Zhu
  2026-09-22 14:11         ` James Zhu
  1 sibling, 0 replies; 36+ messages in thread
From: James Zhu @ 2026-09-22 14:04 UTC (permalink / raw)
  To: Lazar, Lijo, James Zhu, amd-gfx, alexander.deucher, Bing.Ma


On 2026-09-22 09:45, Lazar, Lijo wrote:
>
>
> On 22-Sep-26 7:05 PM, James Zhu wrote:
>>
>> On 2026-09-22 01:22, Lazar, Lijo wrote:
>>>
>>>
>>> On 22-Sep-26 1:33 AM, James Zhu wrote:
>>>> Add amdgpu_xcp_get_xcc_mask() to return the XCC instance mask for a 
>>>> given
>>>> XCP partition. This is needed by upcoming SPM (Stream Performance 
>>>> Monitor)
>>>> support, which programs hardware per partition and must operate 
>>>> only on
>>>> the XCCs that belong to a given XCP.
>>>>
>>>> For partitioned systems the helper returns only the XCCs belonging 
>>>> to the
>>>> specified XCP. When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the 
>>>> device is
>>>> not partitioned) it returns the device's full XCC mask (e.g. 0xFF 
>>>> for an
>>>> 8-XCC device). Note that the full device mask is returned even on a
>>>> partitioned device; callers that require partition isolation (e.g. 
>>>> SPM)
>>>> must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>>>> themselves.
>>>>
>>>
>>> Could you clarifiy on this part - xcp_id = AMDGPU_XCP_NO_PARTITION 
>>> usage on 8-XCC device?
>>>
>>> The default state on a multi-XCC device after driver load is all 
>>> XCCs in a single partition called SPX. It should have at least one 
>>> partition valid.
>>
>> [JZ] Yes, 8-XCC device should not reach AMDGPU_XCP_NO_PARTITION case.
>>
>> the if(!xcp_mgr ||xcp_id ==AMDGPU_XCP_NO_PARTITION) mainly covers non 
>> partition device, I can remove the above comments which don't reflect 
>> the source accurately.
>
> Then I'm not sure what issue this is fixing.
>
> Multi-xcc devices will be in SPX mode and will return a mask with all 
> the XCCs. Non-multi xcc devices are expected to have this set to 1 
> anyway to reflect that a single GC is present always.
[JZ]This is a non-functional refactor and cleanup following the 
introduction of  amdgpu_xcp_get_xcc_mask(). Since 
amdgpu_xcp_get_xcc_mask() and
amdgpu_xcp_get_num_xcc() share similar logic, consolidate the duplicate code

https://gerrit-git.amd.com/plugins/gitiles/brahma/ec/linux/+/refs/heads/amd-unified-interface/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h#222

Thanks!

James

>
> Thanks,
> Lijo
>
>>> Thanks,
>>> Lioj
>>>
>>>> amdgpu_xcp_get_num_xcc() is reworked to take (adev, xcp_id) and 
>>>> derive the
>>>> count from this helper via hweight32(). The helper is defined out 
>>>> of line
>>>> in amdgpu_xcp.c so that the static inline in amdgpu_xcp.h never has to
>>>> dereference the (incomplete) struct amdgpu_device; it only passes the
>>>> pointer through.
>>>>
>>>> This is a user-visible change: amdgpu_xcp_get_num_xcc() previously
>>>> returned 1 for non-partitioned systems (and for 
>>>> AMDGPU_XCP_NO_PARTITION),
>>>> and now returns the actual XCC count (e.g. 8 for an 8-XCC device). 
>>>> CWSR
>>>> callers (amdgpu_kms.c, amdgpu_userq.c, mes_userqueue.c) scale the 
>>>> per-XCC
>>>> control-stack and debug-memory sizes by num_xcc, so the uAPI-visible
>>>> ctl_stack_size and dbg_mem_size values grow accordingly to match the
>>>> actual hardware resource requirements, which were previously 
>>>> undersized.
>>>>
>>>> Fixes: 4cd417bfec49 ("drm/amdgpu: Add helper function to get xcc 
>>>> count")
>>>> Signed-off-by: James Zhu <James.Zhu@amd.com>
>>>> Reviewed-by: Bing Ma <bing.ma@amd.com>
>>>> ---
>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c    |  2 +-
>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c  |  2 +-
>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c    | 48 
>>>> ++++++++++++++++++++++
>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h    | 32 +++++++--------
>>>>   drivers/gpu/drm/amd/amdgpu/mes_userqueue.c |  3 +-
>>>>   5 files changed, 66 insertions(+), 21 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/ 
>>>> drm/amd/amdgpu/amdgpu_kms.c
>>>> index 57fa69c0382d..7782ccd4d28f 100644
>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>>> @@ -1501,7 +1501,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, 
>>>> void *data, struct drm_file *filp)
>>>>           fpriv = (struct amdgpu_fpriv *)filp->driver_priv;
>>>>           if (!amdgpu_trap_is_enabled(adev) || !fpriv->trap_obj)
>>>>               return -EOPNOTSUPP;
>>>> -        num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, 
>>>> fpriv->xcp_id);
>>>> +        num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>           cwsr_info.ctl_stack_size =
>>>>               adev->trap_info->cwsr.xcc_ctl_stack_sz * num_xcc;
>>>>           cwsr_info.dbg_mem_size =
>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c 
>>>> b/drivers/gpu/ drm/amd/amdgpu/amdgpu_userq.c
>>>> index db09e879b85e..d19af9cb6ee1 100644
>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>>> @@ -258,7 +258,7 @@ int 
>>>> amdgpu_userq_input_trap_params_validate(struct 
>>>> amdgpu_usermode_queue *queue,
>>>>       int num_xcc;
>>>>       int r;
>>>>   -    num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv->xcp_id);
>>>> +    num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>       r = amdgpu_trap_validate_params(queue->userq_mgr->adev, 
>>>> trap_params,
>>>>                       num_xcc);
>>>>       if (r)
>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/ 
>>>> drm/amd/amdgpu/amdgpu_xcp.c
>>>> index 2a56f5ec9c76..90846d55292b 100644
>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>>> @@ -390,6 +390,54 @@ int amdgpu_xcp_get_inst_details(struct 
>>>> amdgpu_xcp *xcp,
>>>>       return 0;
>>>>   }
>>>>   +/**
>>>> + * amdgpu_xcp_get_xcc_mask - Get XCC instance mask for a partition
>>>> + * @adev: amdgpu device pointer
>>>> + * @xcp_id: XCP partition ID
>>>> + * @xcc_mask: output parameter for XCC instance mask
>>>> + *
>>>> + * Returns the XCC instance mask for the specified XCP partition.
>>>> + * For partitioned systems, returns only the XCCs belonging to 
>>>> this XCP.
>>>> + *
>>>> + * When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the device is not
>>>> + * partitioned), the device's full XCC mask is returned (e.g. 0xFF 
>>>> for an
>>>> + * 8-XCC device). Note that the full device mask is returned even 
>>>> on a
>>>> + * partitioned device: callers that require partition isolation 
>>>> (e.g. SPM)
>>>> + * must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>>>> themselves.
>>>> + *
>>>> + * Note: On error, xcc_mask is undefined. Callers must check 
>>>> return code
>>>> + * and only read xcc_mask when return == 0.
>>>> + *
>>>> + * Return: 0 on success (non-partitioned systems or valid XCP found),
>>>> + *         -EINVAL if xcc_mask is NULL, no matching XCP id is 
>>>> found, or the
>>>> + *         partition's GFX IP details are unavailable
>>>> + */
>>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
>>>> +                uint32_t *xcc_mask)
>>>> +{
>>>> +    struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr;
>>>> +    struct amdgpu_xcp *xcp;
>>>> +    int i;
>>>> +
>>>> +    if (!xcc_mask)
>>>> +        return -EINVAL;
>>>> +
>>>> +    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION) {
>>>> +        /* Non-partitioned: return the device's full XCC mask. */
>>>> +        *xcc_mask = adev->gfx.xcc_mask;
>>>> +        return 0;
>>>> +    }
>>>> +
>>>> +    /* Partitioned system: get this XCP's XCCs */
>>>> +    for_each_xcp(xcp_mgr, xcp, i) {
>>>> +        if (xcp->id == xcp_id)
>>>> +            return amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
>>>> +                               xcc_mask);
>>>> +    }
>>>> +
>>>> +    return -EINVAL;
>>>> +}
>>>> +
>>>>   int amdgpu_xcp_dev_register(struct amdgpu_device *adev,
>>>>               const struct pci_device_id *ent)
>>>>   {
>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h b/drivers/gpu/ 
>>>> drm/amd/amdgpu/amdgpu_xcp.h
>>>> index 692508be23fb..6eed82ad49b1 100644
>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>>> @@ -44,6 +44,7 @@
>>>>   #define XCP_INST_MASK(num_inst, xcp_id)        \
>>>>       (num_inst ? GENMASK(num_inst - 1, 0) << (xcp_id * num_inst) : 0)
>>>>   +struct amdgpu_device;
>>>>   struct amdgpu_fpriv;
>>>>     enum AMDGPU_XCP_IP_BLOCK {
>>>> @@ -219,26 +220,23 @@ amdgpu_get_next_xcp(struct amdgpu_xcp_mgr 
>>>> *xcp_mgr, int *from)
>>>>       for (i = 0, xcp = amdgpu_get_next_xcp(xcp_mgr, &i); xcp; \
>>>>            ++i, xcp = amdgpu_get_next_xcp(xcp_mgr, &i))
>>>>   -static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_xcp_mgr 
>>>> *xcp_mgr,
>>>> -                     int xcp_id)
>>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
>>>> +                uint32_t *xcc_mask);
>>>> +
>>>> +static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_device 
>>>> *adev, int xcp_id)
>>>>   {
>>>> -    struct amdgpu_xcp *xcp;
>>>>       uint32_t xcc_mask;
>>>> -    int i, r;
>>>> -
>>>> -    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION)
>>>> +    int r, count;
>>>> +
>>>> +    /* Derive the XCC count from the partition's XCC mask (or the 
>>>> full
>>>> +     * device mask when not partitioned). amdgpu_xcp_get_xcc_mask()
>>>> +     * handles both partitioned and non-partitioned cases.
>>>> +     */
>>>> +    r = amdgpu_xcp_get_xcc_mask(adev, xcp_id, &xcc_mask);
>>>> +    if (unlikely(r))
>>>>           return 1;
>>>> -    for_each_xcp(xcp_mgr, xcp, i) {
>>>> -        if (xcp->id == xcp_id) {
>>>> -            r = amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
>>>> -                            &xcc_mask);
>>>> -            if (unlikely(r))
>>>> -                return 1;
>>>> -            else
>>>> -                return hweight32(xcc_mask);
>>>> -        }
>>>> -    }
>>>>   -    return 1;
>>>> +    count = hweight32(xcc_mask);
>>>> +    return count ? count : 1;
>>>>   }
>>>>   #endif
>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/ 
>>>> gpu/drm/amd/amdgpu/mes_userqueue.c
>>>> index 16aa22b25098..1c7b2f3227e5 100644
>>>> --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>>> +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>>> @@ -503,8 +503,7 @@ static int mes_userq_mqd_create(struct 
>>>> amdgpu_usermode_queue *queue,
>>>>                   uq_mgr_to_fpriv(queue->userq_mgr);
>>>>               int num_xcc;
>>>>   -            num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr,
>>>> -                             fpriv->xcp_id);
>>>> +            num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>               trap_params.ctx_save_area_address =
>>>>                   compute_mqd->ctx_save_area_va;
>>>>               trap_params.cwsr_sz = compute_mqd->ctx_save_area_size;
>>>
>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 04/12] drm/amdgpu: add amdgpu_xcp_get_xcc_mask helper
  2026-09-22 13:45       ` Lazar, Lijo
  2026-09-22 14:04         ` James Zhu
@ 2026-09-22 14:11         ` James Zhu
  2026-09-22 14:26           ` Lazar, Lijo
  1 sibling, 1 reply; 36+ messages in thread
From: James Zhu @ 2026-09-22 14:11 UTC (permalink / raw)
  To: Lazar, Lijo, James Zhu, amd-gfx, alexander.deucher, Bing.Ma


On 2026-09-22 09:45, Lazar, Lijo wrote:
>
>
> On 22-Sep-26 7:05 PM, James Zhu wrote:
>>
>> On 2026-09-22 01:22, Lazar, Lijo wrote:
>>>
>>>
>>> On 22-Sep-26 1:33 AM, James Zhu wrote:
>>>> Add amdgpu_xcp_get_xcc_mask() to return the XCC instance mask for a 
>>>> given
>>>> XCP partition. This is needed by upcoming SPM (Stream Performance 
>>>> Monitor)
>>>> support, which programs hardware per partition and must operate 
>>>> only on
>>>> the XCCs that belong to a given XCP.
>>>>
>>>> For partitioned systems the helper returns only the XCCs belonging 
>>>> to the
>>>> specified XCP. When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the 
>>>> device is
>>>> not partitioned) it returns the device's full XCC mask (e.g. 0xFF 
>>>> for an
>>>> 8-XCC device). Note that the full device mask is returned even on a
>>>> partitioned device; callers that require partition isolation (e.g. 
>>>> SPM)
>>>> must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>>>> themselves.
>>>>
>>>
>>> Could you clarifiy on this part - xcp_id = AMDGPU_XCP_NO_PARTITION 
>>> usage on 8-XCC device?
>>>
>>> The default state on a multi-XCC device after driver load is all 
>>> XCCs in a single partition called SPX. It should have at least one 
>>> partition valid.
>>
>> [JZ] Yes, 8-XCC device should not reach AMDGPU_XCP_NO_PARTITION case.
>>
>> the if(!xcp_mgr ||xcp_id ==AMDGPU_XCP_NO_PARTITION) mainly covers non 
>> partition device, I can remove the above comments which don't reflect 
>> the source accurately.
>
> Then I'm not sure what issue this is fixing.
>
> Multi-xcc devices will be in SPX mode and will return a mask with all 
> the XCCs. Non-multi xcc devices are expected to have this set to 1 
> anyway to reflect that a single GC is present always.
[JZ] Just realized a Fixes tag is added. I think somehow AI review added 
it, and I did not check properly, is should be removed also. Sorry for 
this confusing!
>
> Thanks,
> Lijo
>
>>> Thanks,
>>> Lioj
>>>
>>>> amdgpu_xcp_get_num_xcc() is reworked to take (adev, xcp_id) and 
>>>> derive the
>>>> count from this helper via hweight32(). The helper is defined out 
>>>> of line
>>>> in amdgpu_xcp.c so that the static inline in amdgpu_xcp.h never has to
>>>> dereference the (incomplete) struct amdgpu_device; it only passes the
>>>> pointer through.
>>>>
>>>> This is a user-visible change: amdgpu_xcp_get_num_xcc() previously
>>>> returned 1 for non-partitioned systems (and for 
>>>> AMDGPU_XCP_NO_PARTITION),
>>>> and now returns the actual XCC count (e.g. 8 for an 8-XCC device). 
>>>> CWSR
>>>> callers (amdgpu_kms.c, amdgpu_userq.c, mes_userqueue.c) scale the 
>>>> per-XCC
>>>> control-stack and debug-memory sizes by num_xcc, so the uAPI-visible
>>>> ctl_stack_size and dbg_mem_size values grow accordingly to match the
>>>> actual hardware resource requirements, which were previously 
>>>> undersized.
>>>>
>>>> Fixes: 4cd417bfec49 ("drm/amdgpu: Add helper function to get xcc 
>>>> count")
>>>> Signed-off-by: James Zhu <James.Zhu@amd.com>
>>>> Reviewed-by: Bing Ma <bing.ma@amd.com>
>>>> ---
>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c    |  2 +-
>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c  |  2 +-
>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c    | 48 
>>>> ++++++++++++++++++++++
>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h    | 32 +++++++--------
>>>>   drivers/gpu/drm/amd/amdgpu/mes_userqueue.c |  3 +-
>>>>   5 files changed, 66 insertions(+), 21 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/ 
>>>> drm/amd/amdgpu/amdgpu_kms.c
>>>> index 57fa69c0382d..7782ccd4d28f 100644
>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>>> @@ -1501,7 +1501,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, 
>>>> void *data, struct drm_file *filp)
>>>>           fpriv = (struct amdgpu_fpriv *)filp->driver_priv;
>>>>           if (!amdgpu_trap_is_enabled(adev) || !fpriv->trap_obj)
>>>>               return -EOPNOTSUPP;
>>>> -        num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, 
>>>> fpriv->xcp_id);
>>>> +        num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>           cwsr_info.ctl_stack_size =
>>>>               adev->trap_info->cwsr.xcc_ctl_stack_sz * num_xcc;
>>>>           cwsr_info.dbg_mem_size =
>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c 
>>>> b/drivers/gpu/ drm/amd/amdgpu/amdgpu_userq.c
>>>> index db09e879b85e..d19af9cb6ee1 100644
>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>>> @@ -258,7 +258,7 @@ int 
>>>> amdgpu_userq_input_trap_params_validate(struct 
>>>> amdgpu_usermode_queue *queue,
>>>>       int num_xcc;
>>>>       int r;
>>>>   -    num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv->xcp_id);
>>>> +    num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>       r = amdgpu_trap_validate_params(queue->userq_mgr->adev, 
>>>> trap_params,
>>>>                       num_xcc);
>>>>       if (r)
>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/ 
>>>> drm/amd/amdgpu/amdgpu_xcp.c
>>>> index 2a56f5ec9c76..90846d55292b 100644
>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>>> @@ -390,6 +390,54 @@ int amdgpu_xcp_get_inst_details(struct 
>>>> amdgpu_xcp *xcp,
>>>>       return 0;
>>>>   }
>>>>   +/**
>>>> + * amdgpu_xcp_get_xcc_mask - Get XCC instance mask for a partition
>>>> + * @adev: amdgpu device pointer
>>>> + * @xcp_id: XCP partition ID
>>>> + * @xcc_mask: output parameter for XCC instance mask
>>>> + *
>>>> + * Returns the XCC instance mask for the specified XCP partition.
>>>> + * For partitioned systems, returns only the XCCs belonging to 
>>>> this XCP.
>>>> + *
>>>> + * When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the device is not
>>>> + * partitioned), the device's full XCC mask is returned (e.g. 0xFF 
>>>> for an
>>>> + * 8-XCC device). Note that the full device mask is returned even 
>>>> on a
>>>> + * partitioned device: callers that require partition isolation 
>>>> (e.g. SPM)
>>>> + * must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>>>> themselves.
>>>> + *
>>>> + * Note: On error, xcc_mask is undefined. Callers must check 
>>>> return code
>>>> + * and only read xcc_mask when return == 0.
>>>> + *
>>>> + * Return: 0 on success (non-partitioned systems or valid XCP found),
>>>> + *         -EINVAL if xcc_mask is NULL, no matching XCP id is 
>>>> found, or the
>>>> + *         partition's GFX IP details are unavailable
>>>> + */
>>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
>>>> +                uint32_t *xcc_mask)
>>>> +{
>>>> +    struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr;
>>>> +    struct amdgpu_xcp *xcp;
>>>> +    int i;
>>>> +
>>>> +    if (!xcc_mask)
>>>> +        return -EINVAL;
>>>> +
>>>> +    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION) {
>>>> +        /* Non-partitioned: return the device's full XCC mask. */
>>>> +        *xcc_mask = adev->gfx.xcc_mask;
>>>> +        return 0;
>>>> +    }
>>>> +
>>>> +    /* Partitioned system: get this XCP's XCCs */
>>>> +    for_each_xcp(xcp_mgr, xcp, i) {
>>>> +        if (xcp->id == xcp_id)
>>>> +            return amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
>>>> +                               xcc_mask);
>>>> +    }
>>>> +
>>>> +    return -EINVAL;
>>>> +}
>>>> +
>>>>   int amdgpu_xcp_dev_register(struct amdgpu_device *adev,
>>>>               const struct pci_device_id *ent)
>>>>   {
>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h b/drivers/gpu/ 
>>>> drm/amd/amdgpu/amdgpu_xcp.h
>>>> index 692508be23fb..6eed82ad49b1 100644
>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>>> @@ -44,6 +44,7 @@
>>>>   #define XCP_INST_MASK(num_inst, xcp_id)        \
>>>>       (num_inst ? GENMASK(num_inst - 1, 0) << (xcp_id * num_inst) : 0)
>>>>   +struct amdgpu_device;
>>>>   struct amdgpu_fpriv;
>>>>     enum AMDGPU_XCP_IP_BLOCK {
>>>> @@ -219,26 +220,23 @@ amdgpu_get_next_xcp(struct amdgpu_xcp_mgr 
>>>> *xcp_mgr, int *from)
>>>>       for (i = 0, xcp = amdgpu_get_next_xcp(xcp_mgr, &i); xcp; \
>>>>            ++i, xcp = amdgpu_get_next_xcp(xcp_mgr, &i))
>>>>   -static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_xcp_mgr 
>>>> *xcp_mgr,
>>>> -                     int xcp_id)
>>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
>>>> +                uint32_t *xcc_mask);
>>>> +
>>>> +static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_device 
>>>> *adev, int xcp_id)
>>>>   {
>>>> -    struct amdgpu_xcp *xcp;
>>>>       uint32_t xcc_mask;
>>>> -    int i, r;
>>>> -
>>>> -    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION)
>>>> +    int r, count;
>>>> +
>>>> +    /* Derive the XCC count from the partition's XCC mask (or the 
>>>> full
>>>> +     * device mask when not partitioned). amdgpu_xcp_get_xcc_mask()
>>>> +     * handles both partitioned and non-partitioned cases.
>>>> +     */
>>>> +    r = amdgpu_xcp_get_xcc_mask(adev, xcp_id, &xcc_mask);
>>>> +    if (unlikely(r))
>>>>           return 1;
>>>> -    for_each_xcp(xcp_mgr, xcp, i) {
>>>> -        if (xcp->id == xcp_id) {
>>>> -            r = amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
>>>> -                            &xcc_mask);
>>>> -            if (unlikely(r))
>>>> -                return 1;
>>>> -            else
>>>> -                return hweight32(xcc_mask);
>>>> -        }
>>>> -    }
>>>>   -    return 1;
>>>> +    count = hweight32(xcc_mask);
>>>> +    return count ? count : 1;
>>>>   }
>>>>   #endif
>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/ 
>>>> gpu/drm/amd/amdgpu/mes_userqueue.c
>>>> index 16aa22b25098..1c7b2f3227e5 100644
>>>> --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>>> +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>>> @@ -503,8 +503,7 @@ static int mes_userq_mqd_create(struct 
>>>> amdgpu_usermode_queue *queue,
>>>>                   uq_mgr_to_fpriv(queue->userq_mgr);
>>>>               int num_xcc;
>>>>   -            num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr,
>>>> -                             fpriv->xcp_id);
>>>> +            num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>               trap_params.ctx_save_area_address =
>>>>                   compute_mqd->ctx_save_area_va;
>>>>               trap_params.cwsr_sz = compute_mqd->ctx_save_area_size;
>>>
>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 04/12] drm/amdgpu: add amdgpu_xcp_get_xcc_mask helper
  2026-09-22 14:11         ` James Zhu
@ 2026-09-22 14:26           ` Lazar, Lijo
  2026-09-22 15:10             ` James Zhu
  0 siblings, 1 reply; 36+ messages in thread
From: Lazar, Lijo @ 2026-09-22 14:26 UTC (permalink / raw)
  To: James Zhu, James Zhu, amd-gfx, alexander.deucher, Bing.Ma



On 22-Sep-26 7:41 PM, James Zhu wrote:
> 
> On 2026-09-22 09:45, Lazar, Lijo wrote:
>>
>>
>> On 22-Sep-26 7:05 PM, James Zhu wrote:
>>>
>>> On 2026-09-22 01:22, Lazar, Lijo wrote:
>>>>
>>>>
>>>> On 22-Sep-26 1:33 AM, James Zhu wrote:
>>>>> Add amdgpu_xcp_get_xcc_mask() to return the XCC instance mask for a 
>>>>> given
>>>>> XCP partition. This is needed by upcoming SPM (Stream Performance 
>>>>> Monitor)
>>>>> support, which programs hardware per partition and must operate 
>>>>> only on
>>>>> the XCCs that belong to a given XCP.
>>>>>
>>>>> For partitioned systems the helper returns only the XCCs belonging 
>>>>> to the
>>>>> specified XCP. When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the 
>>>>> device is
>>>>> not partitioned) it returns the device's full XCC mask (e.g. 0xFF 
>>>>> for an
>>>>> 8-XCC device). Note that the full device mask is returned even on a
>>>>> partitioned device; callers that require partition isolation (e.g. 
>>>>> SPM)
>>>>> must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>>>>> themselves.
>>>>>
>>>>
>>>> Could you clarifiy on this part - xcp_id = AMDGPU_XCP_NO_PARTITION 
>>>> usage on 8-XCC device?
>>>>
>>>> The default state on a multi-XCC device after driver load is all 
>>>> XCCs in a single partition called SPX. It should have at least one 
>>>> partition valid.
>>>
>>> [JZ] Yes, 8-XCC device should not reach AMDGPU_XCP_NO_PARTITION case.
>>>
>>> the if(!xcp_mgr ||xcp_id ==AMDGPU_XCP_NO_PARTITION) mainly covers non 
>>> partition device, I can remove the above comments which don't reflect 
>>> the source accurately.
>>
>> Then I'm not sure what issue this is fixing.
>>
>> Multi-xcc devices will be in SPX mode and will return a mask with all 
>> the XCCs. Non-multi xcc devices are expected to have this set to 1 
>> anyway to reflect that a single GC is present always.
> [JZ] Just realized a Fixes tag is added. I think somehow AI review added 
> it, and I did not check properly, is should be removed also. Sorry for 
> this confusing!

I see that it also added an external function like this -

amdgpu_xcp_get_xcc_mask

The expectation is any IP instance mask within a partition needs to 
obtained through - amdgpu_xcp_get_inst_details

Also, amdgpu_xcp_get_xcc_mask provides this which is not correct -

*xcc_mask = adev->gfx.xcc_mask

xcp instance mask operates only return logical mask of instances present 
(xcc_mask could include disabled ones also).

Thanks,
Lijo

>>
>> Thanks,
>> Lijo
>>
>>>> Thanks,
>>>> Lioj
>>>>
>>>>> amdgpu_xcp_get_num_xcc() is reworked to take (adev, xcp_id) and 
>>>>> derive the
>>>>> count from this helper via hweight32(). The helper is defined out 
>>>>> of line
>>>>> in amdgpu_xcp.c so that the static inline in amdgpu_xcp.h never has to
>>>>> dereference the (incomplete) struct amdgpu_device; it only passes the
>>>>> pointer through.
>>>>>
>>>>> This is a user-visible change: amdgpu_xcp_get_num_xcc() previously
>>>>> returned 1 for non-partitioned systems (and for 
>>>>> AMDGPU_XCP_NO_PARTITION),
>>>>> and now returns the actual XCC count (e.g. 8 for an 8-XCC device). 
>>>>> CWSR
>>>>> callers (amdgpu_kms.c, amdgpu_userq.c, mes_userqueue.c) scale the 
>>>>> per-XCC
>>>>> control-stack and debug-memory sizes by num_xcc, so the uAPI-visible
>>>>> ctl_stack_size and dbg_mem_size values grow accordingly to match the
>>>>> actual hardware resource requirements, which were previously 
>>>>> undersized.
>>>>>
>>>>> Fixes: 4cd417bfec49 ("drm/amdgpu: Add helper function to get xcc 
>>>>> count")
>>>>> Signed-off-by: James Zhu <James.Zhu@amd.com>
>>>>> Reviewed-by: Bing Ma <bing.ma@amd.com>
>>>>> ---
>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c    |  2 +-
>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c  |  2 +-
>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c    | 48 +++++++++++++++++ 
>>>>> +++++
>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h    | 32 +++++++--------
>>>>>   drivers/gpu/drm/amd/amdgpu/mes_userqueue.c |  3 +-
>>>>>   5 files changed, 66 insertions(+), 21 deletions(-)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/ 
>>>>> drm/amd/amdgpu/amdgpu_kms.c
>>>>> index 57fa69c0382d..7782ccd4d28f 100644
>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>>>> @@ -1501,7 +1501,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, 
>>>>> void *data, struct drm_file *filp)
>>>>>           fpriv = (struct amdgpu_fpriv *)filp->driver_priv;
>>>>>           if (!amdgpu_trap_is_enabled(adev) || !fpriv->trap_obj)
>>>>>               return -EOPNOTSUPP;
>>>>> -        num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv- 
>>>>> >xcp_id);
>>>>> +        num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>>           cwsr_info.ctl_stack_size =
>>>>>               adev->trap_info->cwsr.xcc_ctl_stack_sz * num_xcc;
>>>>>           cwsr_info.dbg_mem_size =
>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/ 
>>>>> gpu/ drm/amd/amdgpu/amdgpu_userq.c
>>>>> index db09e879b85e..d19af9cb6ee1 100644
>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>>>> @@ -258,7 +258,7 @@ int 
>>>>> amdgpu_userq_input_trap_params_validate(struct 
>>>>> amdgpu_usermode_queue *queue,
>>>>>       int num_xcc;
>>>>>       int r;
>>>>>   -    num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv->xcp_id);
>>>>> +    num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>>       r = amdgpu_trap_validate_params(queue->userq_mgr->adev, 
>>>>> trap_params,
>>>>>                       num_xcc);
>>>>>       if (r)
>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/ 
>>>>> drm/amd/amdgpu/amdgpu_xcp.c
>>>>> index 2a56f5ec9c76..90846d55292b 100644
>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>>>> @@ -390,6 +390,54 @@ int amdgpu_xcp_get_inst_details(struct 
>>>>> amdgpu_xcp *xcp,
>>>>>       return 0;
>>>>>   }
>>>>>   +/**
>>>>> + * amdgpu_xcp_get_xcc_mask - Get XCC instance mask for a partition
>>>>> + * @adev: amdgpu device pointer
>>>>> + * @xcp_id: XCP partition ID
>>>>> + * @xcc_mask: output parameter for XCC instance mask
>>>>> + *
>>>>> + * Returns the XCC instance mask for the specified XCP partition.
>>>>> + * For partitioned systems, returns only the XCCs belonging to 
>>>>> this XCP.
>>>>> + *
>>>>> + * When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the device is not
>>>>> + * partitioned), the device's full XCC mask is returned (e.g. 0xFF 
>>>>> for an
>>>>> + * 8-XCC device). Note that the full device mask is returned even 
>>>>> on a
>>>>> + * partitioned device: callers that require partition isolation 
>>>>> (e.g. SPM)
>>>>> + * must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>>>>> themselves.
>>>>> + *
>>>>> + * Note: On error, xcc_mask is undefined. Callers must check 
>>>>> return code
>>>>> + * and only read xcc_mask when return == 0.
>>>>> + *
>>>>> + * Return: 0 on success (non-partitioned systems or valid XCP found),
>>>>> + *         -EINVAL if xcc_mask is NULL, no matching XCP id is 
>>>>> found, or the
>>>>> + *         partition's GFX IP details are unavailable
>>>>> + */
>>>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
>>>>> +                uint32_t *xcc_mask)
>>>>> +{
>>>>> +    struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr;
>>>>> +    struct amdgpu_xcp *xcp;
>>>>> +    int i;
>>>>> +
>>>>> +    if (!xcc_mask)
>>>>> +        return -EINVAL;
>>>>> +
>>>>> +    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION) {
>>>>> +        /* Non-partitioned: return the device's full XCC mask. */
>>>>> +        *xcc_mask = adev->gfx.xcc_mask;
>>>>> +        return 0;
>>>>> +    }
>>>>> +
>>>>> +    /* Partitioned system: get this XCP's XCCs */
>>>>> +    for_each_xcp(xcp_mgr, xcp, i) {
>>>>> +        if (xcp->id == xcp_id)
>>>>> +            return amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
>>>>> +                               xcc_mask);
>>>>> +    }
>>>>> +
>>>>> +    return -EINVAL;
>>>>> +}
>>>>> +
>>>>>   int amdgpu_xcp_dev_register(struct amdgpu_device *adev,
>>>>>               const struct pci_device_id *ent)
>>>>>   {
>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h b/drivers/gpu/ 
>>>>> drm/amd/amdgpu/amdgpu_xcp.h
>>>>> index 692508be23fb..6eed82ad49b1 100644
>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>>>> @@ -44,6 +44,7 @@
>>>>>   #define XCP_INST_MASK(num_inst, xcp_id)        \
>>>>>       (num_inst ? GENMASK(num_inst - 1, 0) << (xcp_id * num_inst) : 0)
>>>>>   +struct amdgpu_device;
>>>>>   struct amdgpu_fpriv;
>>>>>     enum AMDGPU_XCP_IP_BLOCK {
>>>>> @@ -219,26 +220,23 @@ amdgpu_get_next_xcp(struct amdgpu_xcp_mgr 
>>>>> *xcp_mgr, int *from)
>>>>>       for (i = 0, xcp = amdgpu_get_next_xcp(xcp_mgr, &i); xcp; \
>>>>>            ++i, xcp = amdgpu_get_next_xcp(xcp_mgr, &i))
>>>>>   -static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_xcp_mgr 
>>>>> *xcp_mgr,
>>>>> -                     int xcp_id)
>>>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
>>>>> +                uint32_t *xcc_mask);
>>>>> +
>>>>> +static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_device 
>>>>> *adev, int xcp_id)
>>>>>   {
>>>>> -    struct amdgpu_xcp *xcp;
>>>>>       uint32_t xcc_mask;
>>>>> -    int i, r;
>>>>> -
>>>>> -    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION)
>>>>> +    int r, count;
>>>>> +
>>>>> +    /* Derive the XCC count from the partition's XCC mask (or the 
>>>>> full
>>>>> +     * device mask when not partitioned). amdgpu_xcp_get_xcc_mask()
>>>>> +     * handles both partitioned and non-partitioned cases.
>>>>> +     */
>>>>> +    r = amdgpu_xcp_get_xcc_mask(adev, xcp_id, &xcc_mask);
>>>>> +    if (unlikely(r))
>>>>>           return 1;
>>>>> -    for_each_xcp(xcp_mgr, xcp, i) {
>>>>> -        if (xcp->id == xcp_id) {
>>>>> -            r = amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
>>>>> -                            &xcc_mask);
>>>>> -            if (unlikely(r))
>>>>> -                return 1;
>>>>> -            else
>>>>> -                return hweight32(xcc_mask);
>>>>> -        }
>>>>> -    }
>>>>>   -    return 1;
>>>>> +    count = hweight32(xcc_mask);
>>>>> +    return count ? count : 1;
>>>>>   }
>>>>>   #endif
>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/ 
>>>>> gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>> index 16aa22b25098..1c7b2f3227e5 100644
>>>>> --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>> @@ -503,8 +503,7 @@ static int mes_userq_mqd_create(struct 
>>>>> amdgpu_usermode_queue *queue,
>>>>>                   uq_mgr_to_fpriv(queue->userq_mgr);
>>>>>               int num_xcc;
>>>>>   -            num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr,
>>>>> -                             fpriv->xcp_id);
>>>>> +            num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>>               trap_params.ctx_save_area_address =
>>>>>                   compute_mqd->ctx_save_area_va;
>>>>>               trap_params.cwsr_sz = compute_mqd->ctx_save_area_size;
>>>>
>>


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions
  2026-09-21 20:03 [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions James Zhu
                   ` (10 preceding siblings ...)
  2026-09-21 20:03 ` [PATCH 12/12] drm/amdgpu: bump KMS driver version to 3.65 for profiler UAPI James Zhu
@ 2026-09-22 15:09 ` Timur Kristóf
  2026-09-22 15:20   ` James Zhu
  2026-09-22 15:13 ` Christian König
  12 siblings, 1 reply; 36+ messages in thread
From: Timur Kristóf @ 2026-09-22 15:09 UTC (permalink / raw)
  To: amd-gfx, alexander.deucher, Bing.Ma; +Cc: jamesz, Bing Ma, James Zhu

On Monday, September 21, 2026 10:03:02 PM Central European Summer Time James 
Zhu wrote:
> Add the DRM_AMDGPU_PROFILER ioctl number and its argument structures, the
> UAPI for GPU performance monitoring features such as SPM (Stream
> Performance Monitor).
> 
> struct drm_amdgpu_profiler_args carries an operation selector and a union
> of per-operation parameters, currently AMDGPU_PROFILER_OP_GET_VER and
> AMDGPU_PROFILER_OP_SPM. The SPM operations and the layout of the
> destination buffer are described by struct drm_amdgpu_spm_args and
> struct drm_amdgpu_spm_buffer_header.
> 
> The ioctl number and struct layout are frozen here. The implementation
> lands in the rest of this series and the ioctl is not registered in
> amdgpu_ioctls_kms[] until then, so this header change alone does not make
> the interface reachable.
> 
> Signed-off-by: James Zhu <James.Zhu@amd.com>
> Reviewed-by: Bing Ma <bing.ma@amd.com>

Hi,

Can you guys please clarify why this is necessary and what the use case is?

Are we sure we want a uAPI for this? RADV has been using SPM from userspace 
for many years now.

Thanks & best regards,
Timur


> ---
>  include/uapi/drm/amdgpu_drm.h | 124 ++++++++++++++++++++++++++++++++++
>  1 file changed, 124 insertions(+)
> 
> diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h
> index 9e5fa6f53e59..285949d63eb6 100644
> --- a/include/uapi/drm/amdgpu_drm.h
> +++ b/include/uapi/drm/amdgpu_drm.h
> @@ -60,6 +60,7 @@ extern "C" {
>  #define DRM_AMDGPU_GEM_LIST_HANDLES	0x19
>  #define DRM_AMDGPU_PROC_OPTIONS		0x1A
>  #define DRM_AMDGPU_TRAP			0x1B
> +#define DRM_AMDGPU_PROFILER		0x1C
> 
>  #define DRM_IOCTL_AMDGPU_GEM_CREATE	DRM_IOWR(DRM_COMMAND_BASE +
> DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) #define
> DRM_IOCTL_AMDGPU_GEM_MMAP	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP,
> union drm_amdgpu_gem_mmap) @@ -83,6 +84,7 @@ extern "C" {
>  #define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES DRM_IOWR(DRM_COMMAND_BASE +
> DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles) #define
> DRM_IOCTL_AMDGPU_PROC_OPTIONS	DRM_IOWR(DRM_COMMAND_BASE +
> DRM_AMDGPU_PROC_OPTIONS, struct drm_amdgpu_proc_options) #define
> DRM_IOCTL_AMDGPU_TRAP	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_TRAP, 
union
> drm_amdgpu_trap) +#define
> DRM_IOCTL_AMDGPU_PROFILER	DRM_IOWR(DRM_COMMAND_BASE + 
DRM_AMDGPU_PROFILER,
> struct drm_amdgpu_profiler_args)
> 
>  /**
>   * DOC: memory domains
> @@ -1821,6 +1823,128 @@ struct drm_amdgpu_proc_options {
>  	} kfd_sigbus_delay;
>  };
> 
> +/*
> + * Supported SPM (Stream Performance Monitor) Operations
> + */
> +/**
> + * drm_amdgpu_spm_op - SPM ioctl operations
> + */
> +enum drm_amdgpu_spm_op {
> +	AMDGPU_SPM_OP_ACQUIRE = 1,
> +	AMDGPU_SPM_OP_RELEASE,
> +	AMDGPU_SPM_OP_SET_DEST_BUF
> +};
> +
> +/**
> + * drm_amdgpu_spm_args - Arguments for SPM ioctl
> + *
> + * @dest_buf: [in] used for the address of the destination buffer
> + *            in @AMDGPU_SPM_OP_SET_DEST_BUF
> + * @buf_size: [in] size of the destination buffer
> + * @op: [in] specifies the operation to perform
> + * @timeout: [in/out] timeout in milliseconds (in), amount of time left
> + *           in the timeout window (out)
> + * @bytes_copied: [out] total amount of data that was copied to the
> previous dest_buf + * @has_data_loss: [out] non-zero if data was lost (e.g.
> due to ring buffer overflow), + *                 zero otherwise
> + * @reserved: must be zero for forward compatibility
> + *
> + * This ioctl performs different functions depending on the @op parameter.
> + *
> + * AMDGPU_SPM_OP_ACQUIRE
> + * ------------------------
> + *
> + * Acquires exclusive access of SPM on the specified XCP for the calling
> process. + * This must be called before using AMDGPU_SPM_OP_SET_DEST_BUF.
> + *
> + * AMDGPU_SPM_OP_RELEASE
> + * ------------------------
> + *
> + * Releases exclusive access of SPM on the specified XCP for the calling
> process, + * which allows another process to acquire it in the future.
> + *
> + * AMDGPU_SPM_OP_SET_DEST_BUF
> + * -----------------------------
> + *
> + * If @dest_buf is NULL, the destination buffer address is unset and
> copying of counters + * is stopped.
> + *
> + * If @dest_buf is not NULL, it specifies the pointer to a new destination
> buffer. + * @buf_size specifies the size of the buffer.
> + *
> + * If @timeout is non-0, the call will wait for up to @timeout ms for the
> previous + * buffer to be filled. If the previous buffer is filled before
> the timeout, @timeout + * is updated with the time remaining. If the
> timeout is exceeded, the function + * copies any partial data available
> into the previous user buffer and returns success. + * The amount of valid
> data in the previous user buffer is indicated by @bytes_copied. + *
> + * If @timeout is 0, the function immediately replaces the previous
> destination buffer + * without waiting for the previous buffer to be
> filled. That means the previous buffer + * may only be partially filled,
> and @bytes_copied will indicate how much data has been + * copied to it.
> + *
> + * If data was lost, e.g. due to a ring buffer overflow, @has_data_loss
> will be non-0. + *
> + * Returns negative error code on failure, 0 on success.
> + */
> +struct drm_amdgpu_spm_args {
> +	__u64 dest_buf;
> +	__u32 buf_size;
> +	__u32 op;
> +	__u32 timeout;
> +	__u32 bytes_copied;
> +	__u32 has_data_loss;
> +	__u32 reserved[9];	/* must be zero; pads this struct to 64 
bytes */
> +};
> +
> +/**
> + * drm_amdgpu_spm_buffer_header - SPM Buffer header for
> drm_amdgpu_spm_args->dest_buf + *
> + * @version:       [out] SPM version (0-23: minor, 24-31: major)
> + * @bytes_copied:  [out] Amount of data copied for this sub-block
> + * @has_data_loss: [out] Boolean indicating whether data was lost for this
> sub-block + *                       (e.g. due to a ring-buffer overflow)
> + * @reserved:      Reserved for future use, must be zero
> + *
> + * This header precedes the SPM data in the destination buffer. The
> reserved + * field maintains 64-byte cache line alignment (32 bytes total).
> + */
> +struct drm_amdgpu_spm_buffer_header {
> +	__u32 version; /* 0-23: minor, 24-31: major */
> +	__u32 bytes_copied;
> +	__u32 has_data_loss;
> +	__u32 reserved[5]; /* pad to 32 bytes (64-byte cache line / 2) */
> +};
> +
> +/* Supported Profiler Operation */
> +#define AMDGPU_PROFILER_OP_GET_VER		1 /* get profiler UAPI 
version */
> +#define AMDGPU_PROFILER_OP_SPM			2
> +
> +/*
> + * Profiler IOCTL
> + */
> +/**
> + * struct drm_amdgpu_profiler_args - Profiler IOCTL arguments
> + * @op: Operation to perform (AMDGPU_PROFILER_OP_*)
> + * @pad: Must be zero for input validation
> + * @data: Operation-specific parameters (union)
> + *
> + * GET_VER operation:
> + * Returns the profiler UAPI version in @data.version as (MAJOR<<24) |
> MINOR. + * Input: @data must be zero (reserved for forward compatibility).
> + *
> + * Design note: GET_VER is intentionally a parameter-free query returning
> + * the global profiler UAPI version.
> + */
> +struct drm_amdgpu_profiler_args {
> +	__u32 op;				/* 
AMDGPU_PROFILER_OP_* */
> +	__u32 pad;				/* must be zero */
> +	union {
> +		__u32 version;		/* GET_VER: out 
(MAJOR<<24) | MINOR */
> +		struct drm_amdgpu_spm_args spm;
> +	} data;
> +};
> +
>  #if defined(__cplusplus)
>  }
>  #endif





^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 04/12] drm/amdgpu: add amdgpu_xcp_get_xcc_mask helper
  2026-09-22 14:26           ` Lazar, Lijo
@ 2026-09-22 15:10             ` James Zhu
  2026-09-23  3:39               ` Lazar, Lijo
  0 siblings, 1 reply; 36+ messages in thread
From: James Zhu @ 2026-09-22 15:10 UTC (permalink / raw)
  To: Lazar, Lijo, James Zhu, amd-gfx, alexander.deucher, Bing.Ma


On 2026-09-22 10:26, Lazar, Lijo wrote:
>
>
> On 22-Sep-26 7:41 PM, James Zhu wrote:
>>
>> On 2026-09-22 09:45, Lazar, Lijo wrote:
>>>
>>>
>>> On 22-Sep-26 7:05 PM, James Zhu wrote:
>>>>
>>>> On 2026-09-22 01:22, Lazar, Lijo wrote:
>>>>>
>>>>>
>>>>> On 22-Sep-26 1:33 AM, James Zhu wrote:
>>>>>> Add amdgpu_xcp_get_xcc_mask() to return the XCC instance mask for 
>>>>>> a given
>>>>>> XCP partition. This is needed by upcoming SPM (Stream Performance 
>>>>>> Monitor)
>>>>>> support, which programs hardware per partition and must operate 
>>>>>> only on
>>>>>> the XCCs that belong to a given XCP.
>>>>>>
>>>>>> For partitioned systems the helper returns only the XCCs 
>>>>>> belonging to the
>>>>>> specified XCP. When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the 
>>>>>> device is
>>>>>> not partitioned) it returns the device's full XCC mask (e.g. 0xFF 
>>>>>> for an
>>>>>> 8-XCC device). Note that the full device mask is returned even on a
>>>>>> partitioned device; callers that require partition isolation 
>>>>>> (e.g. SPM)
>>>>>> must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>>>>>> themselves.
>>>>>>
>>>>>
>>>>> Could you clarifiy on this part - xcp_id = AMDGPU_XCP_NO_PARTITION 
>>>>> usage on 8-XCC device?
>>>>>
>>>>> The default state on a multi-XCC device after driver load is all 
>>>>> XCCs in a single partition called SPX. It should have at least one 
>>>>> partition valid.
>>>>
>>>> [JZ] Yes, 8-XCC device should not reach AMDGPU_XCP_NO_PARTITION case.
>>>>
>>>> the if(!xcp_mgr ||xcp_id ==AMDGPU_XCP_NO_PARTITION) mainly covers 
>>>> non partition device, I can remove the above comments which don't 
>>>> reflect the source accurately.
>>>
>>> Then I'm not sure what issue this is fixing.
>>>
>>> Multi-xcc devices will be in SPX mode and will return a mask with 
>>> all the XCCs. Non-multi xcc devices are expected to have this set to 
>>> 1 anyway to reflect that a single GC is present always.
>> [JZ] Just realized a Fixes tag is added. I think somehow AI review 
>> added it, and I did not check properly, is should be removed also. 
>> Sorry for this confusing!
>
> I see that it also added an external function like this -
>
> amdgpu_xcp_get_xcc_mask
>
> The expectation is any IP instance mask within a partition needs to 
> obtained through - amdgpu_xcp_get_inst_details
>
> Also, amdgpu_xcp_get_xcc_mask provides this which is not correct -
>
> *xcc_mask = adev->gfx.xcc_mask
>
> xcp instance mask operates only return logical mask of instances 
> present (xcc_mask could include disabled ones also).
[JZ] Here is only for non-partition device. I think adev->gfx.xcc_mask=0x1.
I can change it to below code to clear the confusing

*xcc_mask = BIT(0);

How Do you think?
Thanks!

James

>
> Thanks,
> Lijo
>
>>>
>>> Thanks,
>>> Lijo
>>>
>>>>> Thanks,
>>>>> Lioj
>>>>>
>>>>>> amdgpu_xcp_get_num_xcc() is reworked to take (adev, xcp_id) and 
>>>>>> derive the
>>>>>> count from this helper via hweight32(). The helper is defined out 
>>>>>> of line
>>>>>> in amdgpu_xcp.c so that the static inline in amdgpu_xcp.h never 
>>>>>> has to
>>>>>> dereference the (incomplete) struct amdgpu_device; it only passes 
>>>>>> the
>>>>>> pointer through.
>>>>>>
>>>>>> This is a user-visible change: amdgpu_xcp_get_num_xcc() previously
>>>>>> returned 1 for non-partitioned systems (and for 
>>>>>> AMDGPU_XCP_NO_PARTITION),
>>>>>> and now returns the actual XCC count (e.g. 8 for an 8-XCC 
>>>>>> device). CWSR
>>>>>> callers (amdgpu_kms.c, amdgpu_userq.c, mes_userqueue.c) scale the 
>>>>>> per-XCC
>>>>>> control-stack and debug-memory sizes by num_xcc, so the uAPI-visible
>>>>>> ctl_stack_size and dbg_mem_size values grow accordingly to match the
>>>>>> actual hardware resource requirements, which were previously 
>>>>>> undersized.
>>>>>>
>>>>>> Fixes: 4cd417bfec49 ("drm/amdgpu: Add helper function to get xcc 
>>>>>> count")
>>>>>> Signed-off-by: James Zhu <James.Zhu@amd.com>
>>>>>> Reviewed-by: Bing Ma <bing.ma@amd.com>
>>>>>> ---
>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c    |  2 +-
>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c  |  2 +-
>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c    | 48 
>>>>>> +++++++++++++++++ +++++
>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h    | 32 +++++++--------
>>>>>>   drivers/gpu/drm/amd/amdgpu/mes_userqueue.c |  3 +-
>>>>>>   5 files changed, 66 insertions(+), 21 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c 
>>>>>> b/drivers/gpu/ drm/amd/amdgpu/amdgpu_kms.c
>>>>>> index 57fa69c0382d..7782ccd4d28f 100644
>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>>>>> @@ -1501,7 +1501,7 @@ int amdgpu_info_ioctl(struct drm_device 
>>>>>> *dev, void *data, struct drm_file *filp)
>>>>>>           fpriv = (struct amdgpu_fpriv *)filp->driver_priv;
>>>>>>           if (!amdgpu_trap_is_enabled(adev) || !fpriv->trap_obj)
>>>>>>               return -EOPNOTSUPP;
>>>>>> -        num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv- 
>>>>>> >xcp_id);
>>>>>> +        num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>>>           cwsr_info.ctl_stack_size =
>>>>>> adev->trap_info->cwsr.xcc_ctl_stack_sz * num_xcc;
>>>>>>           cwsr_info.dbg_mem_size =
>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/ 
>>>>>> gpu/ drm/amd/amdgpu/amdgpu_userq.c
>>>>>> index db09e879b85e..d19af9cb6ee1 100644
>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>>>>> @@ -258,7 +258,7 @@ int 
>>>>>> amdgpu_userq_input_trap_params_validate(struct 
>>>>>> amdgpu_usermode_queue *queue,
>>>>>>       int num_xcc;
>>>>>>       int r;
>>>>>>   -    num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, 
>>>>>> fpriv->xcp_id);
>>>>>> +    num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>>>       r = amdgpu_trap_validate_params(queue->userq_mgr->adev, 
>>>>>> trap_params,
>>>>>>                       num_xcc);
>>>>>>       if (r)
>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c 
>>>>>> b/drivers/gpu/ drm/amd/amdgpu/amdgpu_xcp.c
>>>>>> index 2a56f5ec9c76..90846d55292b 100644
>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>>>>> @@ -390,6 +390,54 @@ int amdgpu_xcp_get_inst_details(struct 
>>>>>> amdgpu_xcp *xcp,
>>>>>>       return 0;
>>>>>>   }
>>>>>>   +/**
>>>>>> + * amdgpu_xcp_get_xcc_mask - Get XCC instance mask for a partition
>>>>>> + * @adev: amdgpu device pointer
>>>>>> + * @xcp_id: XCP partition ID
>>>>>> + * @xcc_mask: output parameter for XCC instance mask
>>>>>> + *
>>>>>> + * Returns the XCC instance mask for the specified XCP partition.
>>>>>> + * For partitioned systems, returns only the XCCs belonging to 
>>>>>> this XCP.
>>>>>> + *
>>>>>> + * When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the device is not
>>>>>> + * partitioned), the device's full XCC mask is returned (e.g. 
>>>>>> 0xFF for an
>>>>>> + * 8-XCC device). Note that the full device mask is returned 
>>>>>> even on a
>>>>>> + * partitioned device: callers that require partition isolation 
>>>>>> (e.g. SPM)
>>>>>> + * must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>>>>>> themselves.
>>>>>> + *
>>>>>> + * Note: On error, xcc_mask is undefined. Callers must check 
>>>>>> return code
>>>>>> + * and only read xcc_mask when return == 0.
>>>>>> + *
>>>>>> + * Return: 0 on success (non-partitioned systems or valid XCP 
>>>>>> found),
>>>>>> + *         -EINVAL if xcc_mask is NULL, no matching XCP id is 
>>>>>> found, or the
>>>>>> + *         partition's GFX IP details are unavailable
>>>>>> + */
>>>>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
>>>>>> +                uint32_t *xcc_mask)
>>>>>> +{
>>>>>> +    struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr;
>>>>>> +    struct amdgpu_xcp *xcp;
>>>>>> +    int i;
>>>>>> +
>>>>>> +    if (!xcc_mask)
>>>>>> +        return -EINVAL;
>>>>>> +
>>>>>> +    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION) {
>>>>>> +        /* Non-partitioned: return the device's full XCC mask. */
>>>>>> +        *xcc_mask = adev->gfx.xcc_mask;
>>>>>> +        return 0;
>>>>>> +    }
>>>>>> +
>>>>>> +    /* Partitioned system: get this XCP's XCCs */
>>>>>> +    for_each_xcp(xcp_mgr, xcp, i) {
>>>>>> +        if (xcp->id == xcp_id)
>>>>>> +            return amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
>>>>>> +                               xcc_mask);
>>>>>> +    }
>>>>>> +
>>>>>> +    return -EINVAL;
>>>>>> +}
>>>>>> +
>>>>>>   int amdgpu_xcp_dev_register(struct amdgpu_device *adev,
>>>>>>               const struct pci_device_id *ent)
>>>>>>   {
>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h 
>>>>>> b/drivers/gpu/ drm/amd/amdgpu/amdgpu_xcp.h
>>>>>> index 692508be23fb..6eed82ad49b1 100644
>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>>>>> @@ -44,6 +44,7 @@
>>>>>>   #define XCP_INST_MASK(num_inst, xcp_id)        \
>>>>>>       (num_inst ? GENMASK(num_inst - 1, 0) << (xcp_id * num_inst) 
>>>>>> : 0)
>>>>>>   +struct amdgpu_device;
>>>>>>   struct amdgpu_fpriv;
>>>>>>     enum AMDGPU_XCP_IP_BLOCK {
>>>>>> @@ -219,26 +220,23 @@ amdgpu_get_next_xcp(struct amdgpu_xcp_mgr 
>>>>>> *xcp_mgr, int *from)
>>>>>>       for (i = 0, xcp = amdgpu_get_next_xcp(xcp_mgr, &i); xcp; \
>>>>>>            ++i, xcp = amdgpu_get_next_xcp(xcp_mgr, &i))
>>>>>>   -static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_xcp_mgr 
>>>>>> *xcp_mgr,
>>>>>> -                     int xcp_id)
>>>>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
>>>>>> +                uint32_t *xcc_mask);
>>>>>> +
>>>>>> +static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_device 
>>>>>> *adev, int xcp_id)
>>>>>>   {
>>>>>> -    struct amdgpu_xcp *xcp;
>>>>>>       uint32_t xcc_mask;
>>>>>> -    int i, r;
>>>>>> -
>>>>>> -    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION)
>>>>>> +    int r, count;
>>>>>> +
>>>>>> +    /* Derive the XCC count from the partition's XCC mask (or 
>>>>>> the full
>>>>>> +     * device mask when not partitioned). amdgpu_xcp_get_xcc_mask()
>>>>>> +     * handles both partitioned and non-partitioned cases.
>>>>>> +     */
>>>>>> +    r = amdgpu_xcp_get_xcc_mask(adev, xcp_id, &xcc_mask);
>>>>>> +    if (unlikely(r))
>>>>>>           return 1;
>>>>>> -    for_each_xcp(xcp_mgr, xcp, i) {
>>>>>> -        if (xcp->id == xcp_id) {
>>>>>> -            r = amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
>>>>>> -                            &xcc_mask);
>>>>>> -            if (unlikely(r))
>>>>>> -                return 1;
>>>>>> -            else
>>>>>> -                return hweight32(xcc_mask);
>>>>>> -        }
>>>>>> -    }
>>>>>>   -    return 1;
>>>>>> +    count = hweight32(xcc_mask);
>>>>>> +    return count ? count : 1;
>>>>>>   }
>>>>>>   #endif
>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c 
>>>>>> b/drivers/ gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>>> index 16aa22b25098..1c7b2f3227e5 100644
>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>>> @@ -503,8 +503,7 @@ static int mes_userq_mqd_create(struct 
>>>>>> amdgpu_usermode_queue *queue,
>>>>>>                   uq_mgr_to_fpriv(queue->userq_mgr);
>>>>>>               int num_xcc;
>>>>>>   -            num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr,
>>>>>> -                             fpriv->xcp_id);
>>>>>> +            num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>>>               trap_params.ctx_save_area_address =
>>>>>>                   compute_mqd->ctx_save_area_va;
>>>>>>               trap_params.cwsr_sz = compute_mqd->ctx_save_area_size;
>>>>>
>>>
>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions
  2026-09-21 20:03 [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions James Zhu
                   ` (11 preceding siblings ...)
  2026-09-22 15:09 ` [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions Timur Kristóf
@ 2026-09-22 15:13 ` Christian König
  2026-09-22 15:29   ` James Zhu
  12 siblings, 1 reply; 36+ messages in thread
From: Christian König @ 2026-09-22 15:13 UTC (permalink / raw)
  To: James Zhu, amd-gfx, alexander.deucher, Bing.Ma; +Cc: jamesz

On 9/21/26 22:03, James Zhu wrote:
> Add the DRM_AMDGPU_PROFILER ioctl number and its argument structures, the
> UAPI for GPU performance monitoring features such as SPM (Stream
> Performance Monitor).
> 
> struct drm_amdgpu_profiler_args carries an operation selector and a union
> of per-operation parameters, currently AMDGPU_PROFILER_OP_GET_VER and
> AMDGPU_PROFILER_OP_SPM. The SPM operations and the layout of the
> destination buffer are described by struct drm_amdgpu_spm_args and
> struct drm_amdgpu_spm_buffer_header.
> 
> The ioctl number and struct layout are frozen here. The implementation
> lands in the rest of this series and the ioctl is not registered in
> amdgpu_ioctls_kms[] until then, so this header change alone does not make
> the interface reachable.
> 
> Signed-off-by: James Zhu <James.Zhu@amd.com>
> Reviewed-by: Bing Ma <bing.ma@amd.com>
> ---
>  include/uapi/drm/amdgpu_drm.h | 124 ++++++++++++++++++++++++++++++++++
>  1 file changed, 124 insertions(+)
> 
> diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h
> index 9e5fa6f53e59..285949d63eb6 100644
> --- a/include/uapi/drm/amdgpu_drm.h
> +++ b/include/uapi/drm/amdgpu_drm.h
> @@ -60,6 +60,7 @@ extern "C" {
>  #define DRM_AMDGPU_GEM_LIST_HANDLES	0x19
>  #define DRM_AMDGPU_PROC_OPTIONS		0x1A
>  #define DRM_AMDGPU_TRAP			0x1B
> +#define DRM_AMDGPU_PROFILER		0x1C
>  
>  #define DRM_IOCTL_AMDGPU_GEM_CREATE	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create)
>  #define DRM_IOCTL_AMDGPU_GEM_MMAP	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap)
> @@ -83,6 +84,7 @@ extern "C" {
>  #define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles)
>  #define DRM_IOCTL_AMDGPU_PROC_OPTIONS	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_PROC_OPTIONS, struct drm_amdgpu_proc_options)
>  #define DRM_IOCTL_AMDGPU_TRAP	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_TRAP, union drm_amdgpu_trap)
> +#define DRM_IOCTL_AMDGPU_PROFILER	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_PROFILER, struct drm_amdgpu_profiler_args)
>  
>  /**
>   * DOC: memory domains
> @@ -1821,6 +1823,128 @@ struct drm_amdgpu_proc_options {
>  	} kfd_sigbus_delay;
>  };
>  
> +/*
> + * Supported SPM (Stream Performance Monitor) Operations
> + */
> +/**
> + * drm_amdgpu_spm_op - SPM ioctl operations
> + */
> +enum drm_amdgpu_spm_op {
> +	AMDGPU_SPM_OP_ACQUIRE = 1,
> +	AMDGPU_SPM_OP_RELEASE,
> +	AMDGPU_SPM_OP_SET_DEST_BUF
> +};
> +
> +/**
> + * drm_amdgpu_spm_args - Arguments for SPM ioctl
> + *
> + * @dest_buf: [in] used for the address of the destination buffer
> + *            in @AMDGPU_SPM_OP_SET_DEST_BUF
> + * @buf_size: [in] size of the destination buffer
> + * @op: [in] specifies the operation to perform
> + * @timeout: [in/out] timeout in milliseconds (in), amount of time left
> + *           in the timeout window (out)
> + * @bytes_copied: [out] total amount of data that was copied to the previous dest_buf
> + * @has_data_loss: [out] non-zero if data was lost (e.g. due to ring buffer overflow),
> + *                 zero otherwise
> + * @reserved: must be zero for forward compatibility
> + *
> + * This ioctl performs different functions depending on the @op parameter.
> + *
> + * AMDGPU_SPM_OP_ACQUIRE
> + * ------------------------
> + *
> + * Acquires exclusive access of SPM on the specified XCP for the calling process.
> + * This must be called before using AMDGPU_SPM_OP_SET_DEST_BUF.
> + *
> + * AMDGPU_SPM_OP_RELEASE
> + * ------------------------
> + *
> + * Releases exclusive access of SPM on the specified XCP for the calling process,
> + * which allows another process to acquire it in the future.

Clear NAK on that. We already have an interface for SPM VMID acquire/release.

And please CC me on such patches in the future.

Regards,
Christian.

> + *
> + * AMDGPU_SPM_OP_SET_DEST_BUF
> + * -----------------------------
> + *
> + * If @dest_buf is NULL, the destination buffer address is unset and copying of counters
> + * is stopped.
> + *
> + * If @dest_buf is not NULL, it specifies the pointer to a new destination buffer.
> + * @buf_size specifies the size of the buffer.
> + *
> + * If @timeout is non-0, the call will wait for up to @timeout ms for the previous
> + * buffer to be filled. If the previous buffer is filled before the timeout, @timeout
> + * is updated with the time remaining. If the timeout is exceeded, the function
> + * copies any partial data available into the previous user buffer and returns success.
> + * The amount of valid data in the previous user buffer is indicated by @bytes_copied.
> + *
> + * If @timeout is 0, the function immediately replaces the previous destination buffer
> + * without waiting for the previous buffer to be filled. That means the previous buffer
> + * may only be partially filled, and @bytes_copied will indicate how much data has been
> + * copied to it.
> + *
> + * If data was lost, e.g. due to a ring buffer overflow, @has_data_loss will be non-0.
> + *
> + * Returns negative error code on failure, 0 on success.
> + */
> +struct drm_amdgpu_spm_args {
> +	__u64 dest_buf;
> +	__u32 buf_size;
> +	__u32 op;
> +	__u32 timeout;
> +	__u32 bytes_copied;
> +	__u32 has_data_loss;
> +	__u32 reserved[9];	/* must be zero; pads this struct to 64 bytes */
> +};
> +
> +/**
> + * drm_amdgpu_spm_buffer_header - SPM Buffer header for drm_amdgpu_spm_args->dest_buf
> + *
> + * @version:       [out] SPM version (0-23: minor, 24-31: major)
> + * @bytes_copied:  [out] Amount of data copied for this sub-block
> + * @has_data_loss: [out] Boolean indicating whether data was lost for this sub-block
> + *                       (e.g. due to a ring-buffer overflow)
> + * @reserved:      Reserved for future use, must be zero
> + *
> + * This header precedes the SPM data in the destination buffer. The reserved
> + * field maintains 64-byte cache line alignment (32 bytes total).
> + */
> +struct drm_amdgpu_spm_buffer_header {
> +	__u32 version; /* 0-23: minor, 24-31: major */
> +	__u32 bytes_copied;
> +	__u32 has_data_loss;
> +	__u32 reserved[5]; /* pad to 32 bytes (64-byte cache line / 2) */
> +};
> +
> +/* Supported Profiler Operation */
> +#define AMDGPU_PROFILER_OP_GET_VER		1 /* get profiler UAPI version */
> +#define AMDGPU_PROFILER_OP_SPM			2
> +
> +/*
> + * Profiler IOCTL
> + */
> +/**
> + * struct drm_amdgpu_profiler_args - Profiler IOCTL arguments
> + * @op: Operation to perform (AMDGPU_PROFILER_OP_*)
> + * @pad: Must be zero for input validation
> + * @data: Operation-specific parameters (union)
> + *
> + * GET_VER operation:
> + * Returns the profiler UAPI version in @data.version as (MAJOR<<24) | MINOR.
> + * Input: @data must be zero (reserved for forward compatibility).
> + *
> + * Design note: GET_VER is intentionally a parameter-free query returning
> + * the global profiler UAPI version.
> + */
> +struct drm_amdgpu_profiler_args {
> +	__u32 op;				/* AMDGPU_PROFILER_OP_* */
> +	__u32 pad;				/* must be zero */
> +	union {
> +		__u32 version;		/* GET_VER: out (MAJOR<<24) | MINOR */
> +		struct drm_amdgpu_spm_args spm;
> +	} data;
> +};
> +
>  #if defined(__cplusplus)
>  }
>  #endif


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions
  2026-09-22 15:09 ` [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions Timur Kristóf
@ 2026-09-22 15:20   ` James Zhu
  2026-09-22 15:22     ` Timur Kristóf
  0 siblings, 1 reply; 36+ messages in thread
From: James Zhu @ 2026-09-22 15:20 UTC (permalink / raw)
  To: Timur Kristóf, amd-gfx, alexander.deucher, Bing.Ma; +Cc: James Zhu

[-- Attachment #1: Type: text/plain, Size: 7689 bytes --]


On 2026-09-22 11:09, Timur Kristóf wrote:
> On Monday, September 21, 2026 10:03:02 PM Central European Summer Time James
> Zhu wrote:
>> Add the DRM_AMDGPU_PROFILER ioctl number and its argument structures, the
>> UAPI for GPU performance monitoring features such as SPM (Stream
>> Performance Monitor).
>>
>> struct drm_amdgpu_profiler_args carries an operation selector and a union
>> of per-operation parameters, currently AMDGPU_PROFILER_OP_GET_VER and
>> AMDGPU_PROFILER_OP_SPM. The SPM operations and the layout of the
>> destination buffer are described by struct drm_amdgpu_spm_args and
>> struct drm_amdgpu_spm_buffer_header.
>>
>> The ioctl number and struct layout are frozen here. The implementation
>> lands in the rest of this series and the ioctl is not registered in
>> amdgpu_ioctls_kms[] until then, so this header change alone does not make
>> the interface reachable.
>>
>> Signed-off-by: James Zhu<James.Zhu@amd.com>
>> Reviewed-by: Bing Ma<bing.ma@amd.com>
> Hi,
>
> Can you guys please clarify why this is necessary and what the use case is?
>
> Are we sure we want a uAPI for this? RADV has been using SPM from userspace
> for many years now.

[JZ] current RADV SPM is for graphic engine. These patches are for 
compute engine SPM.

Thanks!

James

>
> Thanks & best regards,
> Timur
>
>
>> ---
>>   include/uapi/drm/amdgpu_drm.h | 124 ++++++++++++++++++++++++++++++++++
>>   1 file changed, 124 insertions(+)
>>
>> diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h
>> index 9e5fa6f53e59..285949d63eb6 100644
>> --- a/include/uapi/drm/amdgpu_drm.h
>> +++ b/include/uapi/drm/amdgpu_drm.h
>> @@ -60,6 +60,7 @@ extern "C" {
>>   #define DRM_AMDGPU_GEM_LIST_HANDLES	0x19
>>   #define DRM_AMDGPU_PROC_OPTIONS		0x1A
>>   #define DRM_AMDGPU_TRAP			0x1B
>> +#define DRM_AMDGPU_PROFILER		0x1C
>>
>>   #define DRM_IOCTL_AMDGPU_GEM_CREATE	DRM_IOWR(DRM_COMMAND_BASE +
>> DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) #define
>> DRM_IOCTL_AMDGPU_GEM_MMAP	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP,
>> union drm_amdgpu_gem_mmap) @@ -83,6 +84,7 @@ extern "C" {
>>   #define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES DRM_IOWR(DRM_COMMAND_BASE +
>> DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles) #define
>> DRM_IOCTL_AMDGPU_PROC_OPTIONS	DRM_IOWR(DRM_COMMAND_BASE +
>> DRM_AMDGPU_PROC_OPTIONS, struct drm_amdgpu_proc_options) #define
>> DRM_IOCTL_AMDGPU_TRAP	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_TRAP,
> union
>> drm_amdgpu_trap) +#define
>> DRM_IOCTL_AMDGPU_PROFILER	DRM_IOWR(DRM_COMMAND_BASE +
> DRM_AMDGPU_PROFILER,
>> struct drm_amdgpu_profiler_args)
>>
>>   /**
>>    * DOC: memory domains
>> @@ -1821,6 +1823,128 @@ struct drm_amdgpu_proc_options {
>>   	} kfd_sigbus_delay;
>>   };
>>
>> +/*
>> + * Supported SPM (Stream Performance Monitor) Operations
>> + */
>> +/**
>> + * drm_amdgpu_spm_op - SPM ioctl operations
>> + */
>> +enum drm_amdgpu_spm_op {
>> +	AMDGPU_SPM_OP_ACQUIRE = 1,
>> +	AMDGPU_SPM_OP_RELEASE,
>> +	AMDGPU_SPM_OP_SET_DEST_BUF
>> +};
>> +
>> +/**
>> + * drm_amdgpu_spm_args - Arguments for SPM ioctl
>> + *
>> + * @dest_buf: [in] used for the address of the destination buffer
>> + *            in @AMDGPU_SPM_OP_SET_DEST_BUF
>> + * @buf_size: [in] size of the destination buffer
>> + * @op: [in] specifies the operation to perform
>> + * @timeout: [in/out] timeout in milliseconds (in), amount of time left
>> + *           in the timeout window (out)
>> + * @bytes_copied: [out] total amount of data that was copied to the
>> previous dest_buf + * @has_data_loss: [out] non-zero if data was lost (e.g.
>> due to ring buffer overflow), + *                 zero otherwise
>> + * @reserved: must be zero for forward compatibility
>> + *
>> + * This ioctl performs different functions depending on the @op parameter.
>> + *
>> + * AMDGPU_SPM_OP_ACQUIRE
>> + * ------------------------
>> + *
>> + * Acquires exclusive access of SPM on the specified XCP for the calling
>> process. + * This must be called before using AMDGPU_SPM_OP_SET_DEST_BUF.
>> + *
>> + * AMDGPU_SPM_OP_RELEASE
>> + * ------------------------
>> + *
>> + * Releases exclusive access of SPM on the specified XCP for the calling
>> process, + * which allows another process to acquire it in the future.
>> + *
>> + * AMDGPU_SPM_OP_SET_DEST_BUF
>> + * -----------------------------
>> + *
>> + * If @dest_buf is NULL, the destination buffer address is unset and
>> copying of counters + * is stopped.
>> + *
>> + * If @dest_buf is not NULL, it specifies the pointer to a new destination
>> buffer. + * @buf_size specifies the size of the buffer.
>> + *
>> + * If @timeout is non-0, the call will wait for up to @timeout ms for the
>> previous + * buffer to be filled. If the previous buffer is filled before
>> the timeout, @timeout + * is updated with the time remaining. If the
>> timeout is exceeded, the function + * copies any partial data available
>> into the previous user buffer and returns success. + * The amount of valid
>> data in the previous user buffer is indicated by @bytes_copied. + *
>> + * If @timeout is 0, the function immediately replaces the previous
>> destination buffer + * without waiting for the previous buffer to be
>> filled. That means the previous buffer + * may only be partially filled,
>> and @bytes_copied will indicate how much data has been + * copied to it.
>> + *
>> + * If data was lost, e.g. due to a ring buffer overflow, @has_data_loss
>> will be non-0. + *
>> + * Returns negative error code on failure, 0 on success.
>> + */
>> +struct drm_amdgpu_spm_args {
>> +	__u64 dest_buf;
>> +	__u32 buf_size;
>> +	__u32 op;
>> +	__u32 timeout;
>> +	__u32 bytes_copied;
>> +	__u32 has_data_loss;
>> +	__u32 reserved[9];	/* must be zero; pads this struct to 64
> bytes */
>> +};
>> +
>> +/**
>> + * drm_amdgpu_spm_buffer_header - SPM Buffer header for
>> drm_amdgpu_spm_args->dest_buf + *
>> + * @version:       [out] SPM version (0-23: minor, 24-31: major)
>> + * @bytes_copied:  [out] Amount of data copied for this sub-block
>> + * @has_data_loss: [out] Boolean indicating whether data was lost for this
>> sub-block + *                       (e.g. due to a ring-buffer overflow)
>> + * @reserved:      Reserved for future use, must be zero
>> + *
>> + * This header precedes the SPM data in the destination buffer. The
>> reserved + * field maintains 64-byte cache line alignment (32 bytes total).
>> + */
>> +struct drm_amdgpu_spm_buffer_header {
>> +	__u32 version; /* 0-23: minor, 24-31: major */
>> +	__u32 bytes_copied;
>> +	__u32 has_data_loss;
>> +	__u32 reserved[5]; /* pad to 32 bytes (64-byte cache line / 2) */
>> +};
>> +
>> +/* Supported Profiler Operation */
>> +#define AMDGPU_PROFILER_OP_GET_VER		1 /* get profiler UAPI
> version */
>> +#define AMDGPU_PROFILER_OP_SPM			2
>> +
>> +/*
>> + * Profiler IOCTL
>> + */
>> +/**
>> + * struct drm_amdgpu_profiler_args - Profiler IOCTL arguments
>> + * @op: Operation to perform (AMDGPU_PROFILER_OP_*)
>> + * @pad: Must be zero for input validation
>> + * @data: Operation-specific parameters (union)
>> + *
>> + * GET_VER operation:
>> + * Returns the profiler UAPI version in @data.version as (MAJOR<<24) |
>> MINOR. + * Input: @data must be zero (reserved for forward compatibility).
>> + *
>> + * Design note: GET_VER is intentionally a parameter-free query returning
>> + * the global profiler UAPI version.
>> + */
>> +struct drm_amdgpu_profiler_args {
>> +	__u32 op;				/*
> AMDGPU_PROFILER_OP_* */
>> +	__u32 pad;				/* must be zero */
>> +	union {
>> +		__u32 version;		/* GET_VER: out
> (MAJOR<<24) | MINOR */
>> +		struct drm_amdgpu_spm_args spm;
>> +	} data;
>> +};
>> +
>>   #if defined(__cplusplus)
>>   }
>>   #endif
>
>
>

[-- Attachment #2: Type: text/html, Size: 9084 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions
  2026-09-22 15:20   ` James Zhu
@ 2026-09-22 15:22     ` Timur Kristóf
  2026-09-22 15:32       ` James Zhu
  2026-09-22 15:40       ` Alex Deucher
  0 siblings, 2 replies; 36+ messages in thread
From: Timur Kristóf @ 2026-09-22 15:22 UTC (permalink / raw)
  To: amd-gfx, alexander.deucher, Bing.Ma, James Zhu
  Cc: James Zhu, Natalie Vock, Christian König

On Tuesday, September 22, 2026 5:20:41 PM Central European Summer Time James 
Zhu wrote:
> On 2026-09-22 11:09, Timur Kristóf wrote:
> > On Monday, September 21, 2026 10:03:02 PM Central European Summer Time
> > James> 
> > Zhu wrote:
> >> Add the DRM_AMDGPU_PROFILER ioctl number and its argument structures, the
> >> UAPI for GPU performance monitoring features such as SPM (Stream
> >> Performance Monitor).
> >> 
> >> struct drm_amdgpu_profiler_args carries an operation selector and a union
> >> of per-operation parameters, currently AMDGPU_PROFILER_OP_GET_VER and
> >> AMDGPU_PROFILER_OP_SPM. The SPM operations and the layout of the
> >> destination buffer are described by struct drm_amdgpu_spm_args and
> >> struct drm_amdgpu_spm_buffer_header.
> >> 
> >> The ioctl number and struct layout are frozen here. The implementation
> >> lands in the rest of this series and the ioctl is not registered in
> >> amdgpu_ioctls_kms[] until then, so this header change alone does not make
> >> the interface reachable.
> >> 
> >> Signed-off-by: James Zhu<James.Zhu@amd.com>
> >> Reviewed-by: Bing Ma<bing.ma@amd.com>
> > 
> > Hi,
> > 
> > Can you guys please clarify why this is necessary and what the use case
> > is?
> > 
> > Are we sure we want a uAPI for this? RADV has been using SPM from
> > userspace
> > for many years now.
> 
> [JZ] current RADV SPM is for graphic engine. These patches are for
> compute engine SPM.
> 
> Thanks!

Is there any reason why it can't be done in userspace for compute too?


> > 
> >> ---
> >> 
> >>   include/uapi/drm/amdgpu_drm.h | 124 ++++++++++++++++++++++++++++++++++
> >>   1 file changed, 124 insertions(+)
> >> 
> >> diff --git a/include/uapi/drm/amdgpu_drm.h
> >> b/include/uapi/drm/amdgpu_drm.h
> >> index 9e5fa6f53e59..285949d63eb6 100644
> >> --- a/include/uapi/drm/amdgpu_drm.h
> >> +++ b/include/uapi/drm/amdgpu_drm.h
> >> @@ -60,6 +60,7 @@ extern "C" {
> >> 
> >>   #define DRM_AMDGPU_GEM_LIST_HANDLES	0x19
> >>   #define DRM_AMDGPU_PROC_OPTIONS		0x1A
> >>   #define DRM_AMDGPU_TRAP			0x1B
> >> 
> >> +#define DRM_AMDGPU_PROFILER		0x1C
> >> 
> >>   #define DRM_IOCTL_AMDGPU_GEM_CREATE	DRM_IOWR(DRM_COMMAND_BASE +
> >> 
> >> DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) #define
> >> DRM_IOCTL_AMDGPU_GEM_MMAP	DRM_IOWR(DRM_COMMAND_BASE +
> >> DRM_AMDGPU_GEM_MMAP,
> >> union drm_amdgpu_gem_mmap) @@ -83,6 +84,7 @@ extern "C" {
> >> 
> >>   #define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES DRM_IOWR(DRM_COMMAND_BASE +
> >> 
> >> DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles) #define
> >> DRM_IOCTL_AMDGPU_PROC_OPTIONS	DRM_IOWR(DRM_COMMAND_BASE +
> >> DRM_AMDGPU_PROC_OPTIONS, struct drm_amdgpu_proc_options) #define
> >> DRM_IOCTL_AMDGPU_TRAP	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_TRAP,
> > 
> > union
> > 
> >> drm_amdgpu_trap) +#define
> >> DRM_IOCTL_AMDGPU_PROFILER	DRM_IOWR(DRM_COMMAND_BASE +
> > 
> > DRM_AMDGPU_PROFILER,
> > 
> >> struct drm_amdgpu_profiler_args)
> >> 
> >>   /**
> >>   
> >>    * DOC: memory domains
> >> 
> >> @@ -1821,6 +1823,128 @@ struct drm_amdgpu_proc_options {
> >> 
> >>   	} kfd_sigbus_delay;
> >>   
> >>   };
> >> 
> >> +/*
> >> + * Supported SPM (Stream Performance Monitor) Operations
> >> + */
> >> +/**
> >> + * drm_amdgpu_spm_op - SPM ioctl operations
> >> + */
> >> +enum drm_amdgpu_spm_op {
> >> +	AMDGPU_SPM_OP_ACQUIRE = 1,
> >> +	AMDGPU_SPM_OP_RELEASE,
> >> +	AMDGPU_SPM_OP_SET_DEST_BUF
> >> +};
> >> +
> >> +/**
> >> + * drm_amdgpu_spm_args - Arguments for SPM ioctl
> >> + *
> >> + * @dest_buf: [in] used for the address of the destination buffer
> >> + *            in @AMDGPU_SPM_OP_SET_DEST_BUF
> >> + * @buf_size: [in] size of the destination buffer
> >> + * @op: [in] specifies the operation to perform
> >> + * @timeout: [in/out] timeout in milliseconds (in), amount of time left
> >> + *           in the timeout window (out)
> >> + * @bytes_copied: [out] total amount of data that was copied to the
> >> previous dest_buf + * @has_data_loss: [out] non-zero if data was lost
> >> (e.g.
> >> due to ring buffer overflow), + *                 zero otherwise
> >> + * @reserved: must be zero for forward compatibility
> >> + *
> >> + * This ioctl performs different functions depending on the @op
> >> parameter.
> >> + *
> >> + * AMDGPU_SPM_OP_ACQUIRE
> >> + * ------------------------
> >> + *
> >> + * Acquires exclusive access of SPM on the specified XCP for the calling
> >> process. + * This must be called before using AMDGPU_SPM_OP_SET_DEST_BUF.
> >> + *
> >> + * AMDGPU_SPM_OP_RELEASE
> >> + * ------------------------
> >> + *
> >> + * Releases exclusive access of SPM on the specified XCP for the calling
> >> process, + * which allows another process to acquire it in the future.
> >> + *
> >> + * AMDGPU_SPM_OP_SET_DEST_BUF
> >> + * -----------------------------
> >> + *
> >> + * If @dest_buf is NULL, the destination buffer address is unset and
> >> copying of counters + * is stopped.
> >> + *
> >> + * If @dest_buf is not NULL, it specifies the pointer to a new
> >> destination
> >> buffer. + * @buf_size specifies the size of the buffer.
> >> + *
> >> + * If @timeout is non-0, the call will wait for up to @timeout ms for
> >> the
> >> previous + * buffer to be filled. If the previous buffer is filled before
> >> the timeout, @timeout + * is updated with the time remaining. If the
> >> timeout is exceeded, the function + * copies any partial data available
> >> into the previous user buffer and returns success. + * The amount of
> >> valid
> >> data in the previous user buffer is indicated by @bytes_copied. + *
> >> + * If @timeout is 0, the function immediately replaces the previous
> >> destination buffer + * without waiting for the previous buffer to be
> >> filled. That means the previous buffer + * may only be partially filled,
> >> and @bytes_copied will indicate how much data has been + * copied to it.
> >> + *
> >> + * If data was lost, e.g. due to a ring buffer overflow, @has_data_loss
> >> will be non-0. + *
> >> + * Returns negative error code on failure, 0 on success.
> >> + */
> >> +struct drm_amdgpu_spm_args {
> >> +	__u64 dest_buf;
> >> +	__u32 buf_size;
> >> +	__u32 op;
> >> +	__u32 timeout;
> >> +	__u32 bytes_copied;
> >> +	__u32 has_data_loss;
> >> +	__u32 reserved[9];	/* must be zero; pads this struct to 64
> > 
> > bytes */
> > 
> >> +};
> >> +
> >> +/**
> >> + * drm_amdgpu_spm_buffer_header - SPM Buffer header for
> >> drm_amdgpu_spm_args->dest_buf + *
> >> + * @version:       [out] SPM version (0-23: minor, 24-31: major)
> >> + * @bytes_copied:  [out] Amount of data copied for this sub-block
> >> + * @has_data_loss: [out] Boolean indicating whether data was lost for
> >> this
> >> sub-block + *                       (e.g. due to a ring-buffer overflow)
> >> + * @reserved:      Reserved for future use, must be zero
> >> + *
> >> + * This header precedes the SPM data in the destination buffer. The
> >> reserved + * field maintains 64-byte cache line alignment (32 bytes
> >> total).
> >> + */
> >> +struct drm_amdgpu_spm_buffer_header {
> >> +	__u32 version; /* 0-23: minor, 24-31: major */
> >> +	__u32 bytes_copied;
> >> +	__u32 has_data_loss;
> >> +	__u32 reserved[5]; /* pad to 32 bytes (64-byte cache line / 2) */
> >> +};
> >> +
> >> +/* Supported Profiler Operation */
> >> +#define AMDGPU_PROFILER_OP_GET_VER		1 /* get profiler UAPI
> > 
> > version */
> > 
> >> +#define AMDGPU_PROFILER_OP_SPM			2
> >> +
> >> +/*
> >> + * Profiler IOCTL
> >> + */
> >> +/**
> >> + * struct drm_amdgpu_profiler_args - Profiler IOCTL arguments
> >> + * @op: Operation to perform (AMDGPU_PROFILER_OP_*)
> >> + * @pad: Must be zero for input validation
> >> + * @data: Operation-specific parameters (union)
> >> + *
> >> + * GET_VER operation:
> >> + * Returns the profiler UAPI version in @data.version as (MAJOR<<24) |
> >> MINOR. + * Input: @data must be zero (reserved for forward
> >> compatibility).
> >> + *
> >> + * Design note: GET_VER is intentionally a parameter-free query
> >> returning
> >> + * the global profiler UAPI version.
> >> + */
> >> +struct drm_amdgpu_profiler_args {
> >> +	__u32 op;				/*
> > 
> > AMDGPU_PROFILER_OP_* */
> > 
> >> +	__u32 pad;				/* must be zero */
> >> +	union {
> >> +		__u32 version;		/* GET_VER: out
> > 
> > (MAJOR<<24) | MINOR */
> > 
> >> +		struct drm_amdgpu_spm_args spm;
> >> +	} data;
> >> +};
> >> +
> >> 
> >>   #if defined(__cplusplus)
> >>   }
> >>   #endif





^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions
  2026-09-22 15:13 ` Christian König
@ 2026-09-22 15:29   ` James Zhu
  0 siblings, 0 replies; 36+ messages in thread
From: James Zhu @ 2026-09-22 15:29 UTC (permalink / raw)
  To: Christian König, James Zhu, amd-gfx, alexander.deucher,
	Bing.Ma

[-- Attachment #1: Type: text/plain, Size: 7849 bytes --]


On 2026-09-22 11:13, Christian König wrote:
> On 9/21/26 22:03, James Zhu wrote:
>> Add the DRM_AMDGPU_PROFILER ioctl number and its argument structures, the
>> UAPI for GPU performance monitoring features such as SPM (Stream
>> Performance Monitor).
>>
>> struct drm_amdgpu_profiler_args carries an operation selector and a union
>> of per-operation parameters, currently AMDGPU_PROFILER_OP_GET_VER and
>> AMDGPU_PROFILER_OP_SPM. The SPM operations and the layout of the
>> destination buffer are described by struct drm_amdgpu_spm_args and
>> struct drm_amdgpu_spm_buffer_header.
>>
>> The ioctl number and struct layout are frozen here. The implementation
>> lands in the rest of this series and the ioctl is not registered in
>> amdgpu_ioctls_kms[] until then, so this header change alone does not make
>> the interface reachable.
>>
>> Signed-off-by: James Zhu<James.Zhu@amd.com>
>> Reviewed-by: Bing Ma<bing.ma@amd.com>
>> ---
>>   include/uapi/drm/amdgpu_drm.h | 124 ++++++++++++++++++++++++++++++++++
>>   1 file changed, 124 insertions(+)
>>
>> diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h
>> index 9e5fa6f53e59..285949d63eb6 100644
>> --- a/include/uapi/drm/amdgpu_drm.h
>> +++ b/include/uapi/drm/amdgpu_drm.h
>> @@ -60,6 +60,7 @@ extern "C" {
>>   #define DRM_AMDGPU_GEM_LIST_HANDLES	0x19
>>   #define DRM_AMDGPU_PROC_OPTIONS		0x1A
>>   #define DRM_AMDGPU_TRAP			0x1B
>> +#define DRM_AMDGPU_PROFILER		0x1C
>>   
>>   #define DRM_IOCTL_AMDGPU_GEM_CREATE	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create)
>>   #define DRM_IOCTL_AMDGPU_GEM_MMAP	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap)
>> @@ -83,6 +84,7 @@ extern "C" {
>>   #define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles)
>>   #define DRM_IOCTL_AMDGPU_PROC_OPTIONS	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_PROC_OPTIONS, struct drm_amdgpu_proc_options)
>>   #define DRM_IOCTL_AMDGPU_TRAP	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_TRAP, union drm_amdgpu_trap)
>> +#define DRM_IOCTL_AMDGPU_PROFILER	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_PROFILER, struct drm_amdgpu_profiler_args)
>>   
>>   /**
>>    * DOC: memory domains
>> @@ -1821,6 +1823,128 @@ struct drm_amdgpu_proc_options {
>>   	} kfd_sigbus_delay;
>>   };
>>   
>> +/*
>> + * Supported SPM (Stream Performance Monitor) Operations
>> + */
>> +/**
>> + * drm_amdgpu_spm_op - SPM ioctl operations
>> + */
>> +enum drm_amdgpu_spm_op {
>> +	AMDGPU_SPM_OP_ACQUIRE = 1,
>> +	AMDGPU_SPM_OP_RELEASE,
>> +	AMDGPU_SPM_OP_SET_DEST_BUF
>> +};
>> +
>> +/**
>> + * drm_amdgpu_spm_args - Arguments for SPM ioctl
>> + *
>> + * @dest_buf: [in] used for the address of the destination buffer
>> + *            in @AMDGPU_SPM_OP_SET_DEST_BUF
>> + * @buf_size: [in] size of the destination buffer
>> + * @op: [in] specifies the operation to perform
>> + * @timeout: [in/out] timeout in milliseconds (in), amount of time left
>> + *           in the timeout window (out)
>> + * @bytes_copied: [out] total amount of data that was copied to the previous dest_buf
>> + * @has_data_loss: [out] non-zero if data was lost (e.g. due to ring buffer overflow),
>> + *                 zero otherwise
>> + * @reserved: must be zero for forward compatibility
>> + *
>> + * This ioctl performs different functions depending on the @op parameter.
>> + *
>> + * AMDGPU_SPM_OP_ACQUIRE
>> + * ------------------------
>> + *
>> + * Acquires exclusive access of SPM on the specified XCP for the calling process.
>> + * This must be called before using AMDGPU_SPM_OP_SET_DEST_BUF.
>> + *
>> + * AMDGPU_SPM_OP_RELEASE
>> + * ------------------------
>> + *
>> + * Releases exclusive access of SPM on the specified XCP for the calling process,
>> + * which allows another process to acquire it in the future.
> Clear NAK on that. We already have an interface for SPM VMID acquire/release.
>
> And please CC me on such patches in the future.
[JZ] Sure, I CC to you on these patches.

Here is SPM OP acquire / release, not SPM VMID acquire/release.

in v6.12-0003-drm-amdgpu-add-generic-RLC-SPM-interface-layer.patch
amdgpu_vmid_alloc_reserved / amdgpu_vmid_free_reserved are used

for SPM VMID acquire/release.
Do we have new SPM VMID acquire/release interface releases? If yes, 
please share with me, I can replace with new ones.

Thanks!

James

>
> Regards,
> Christian.
>
>> + *
>> + * AMDGPU_SPM_OP_SET_DEST_BUF
>> + * -----------------------------
>> + *
>> + * If @dest_buf is NULL, the destination buffer address is unset and copying of counters
>> + * is stopped.
>> + *
>> + * If @dest_buf is not NULL, it specifies the pointer to a new destination buffer.
>> + * @buf_size specifies the size of the buffer.
>> + *
>> + * If @timeout is non-0, the call will wait for up to @timeout ms for the previous
>> + * buffer to be filled. If the previous buffer is filled before the timeout, @timeout
>> + * is updated with the time remaining. If the timeout is exceeded, the function
>> + * copies any partial data available into the previous user buffer and returns success.
>> + * The amount of valid data in the previous user buffer is indicated by @bytes_copied.
>> + *
>> + * If @timeout is 0, the function immediately replaces the previous destination buffer
>> + * without waiting for the previous buffer to be filled. That means the previous buffer
>> + * may only be partially filled, and @bytes_copied will indicate how much data has been
>> + * copied to it.
>> + *
>> + * If data was lost, e.g. due to a ring buffer overflow, @has_data_loss will be non-0.
>> + *
>> + * Returns negative error code on failure, 0 on success.
>> + */
>> +struct drm_amdgpu_spm_args {
>> +	__u64 dest_buf;
>> +	__u32 buf_size;
>> +	__u32 op;
>> +	__u32 timeout;
>> +	__u32 bytes_copied;
>> +	__u32 has_data_loss;
>> +	__u32 reserved[9];	/* must be zero; pads this struct to 64 bytes */
>> +};
>> +
>> +/**
>> + * drm_amdgpu_spm_buffer_header - SPM Buffer header for drm_amdgpu_spm_args->dest_buf
>> + *
>> + * @version:       [out] SPM version (0-23: minor, 24-31: major)
>> + * @bytes_copied:  [out] Amount of data copied for this sub-block
>> + * @has_data_loss: [out] Boolean indicating whether data was lost for this sub-block
>> + *                       (e.g. due to a ring-buffer overflow)
>> + * @reserved:      Reserved for future use, must be zero
>> + *
>> + * This header precedes the SPM data in the destination buffer. The reserved
>> + * field maintains 64-byte cache line alignment (32 bytes total).
>> + */
>> +struct drm_amdgpu_spm_buffer_header {
>> +	__u32 version; /* 0-23: minor, 24-31: major */
>> +	__u32 bytes_copied;
>> +	__u32 has_data_loss;
>> +	__u32 reserved[5]; /* pad to 32 bytes (64-byte cache line / 2) */
>> +};
>> +
>> +/* Supported Profiler Operation */
>> +#define AMDGPU_PROFILER_OP_GET_VER		1 /* get profiler UAPI version */
>> +#define AMDGPU_PROFILER_OP_SPM			2
>> +
>> +/*
>> + * Profiler IOCTL
>> + */
>> +/**
>> + * struct drm_amdgpu_profiler_args - Profiler IOCTL arguments
>> + * @op: Operation to perform (AMDGPU_PROFILER_OP_*)
>> + * @pad: Must be zero for input validation
>> + * @data: Operation-specific parameters (union)
>> + *
>> + * GET_VER operation:
>> + * Returns the profiler UAPI version in @data.version as (MAJOR<<24) | MINOR.
>> + * Input: @data must be zero (reserved for forward compatibility).
>> + *
>> + * Design note: GET_VER is intentionally a parameter-free query returning
>> + * the global profiler UAPI version.
>> + */
>> +struct drm_amdgpu_profiler_args {
>> +	__u32 op;				/* AMDGPU_PROFILER_OP_* */
>> +	__u32 pad;				/* must be zero */
>> +	union {
>> +		__u32 version;		/* GET_VER: out (MAJOR<<24) | MINOR */
>> +		struct drm_amdgpu_spm_args spm;
>> +	} data;
>> +};
>> +
>>   #if defined(__cplusplus)
>>   }
>>   #endif

[-- Attachment #2: Type: text/html, Size: 8794 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions
  2026-09-22 15:22     ` Timur Kristóf
@ 2026-09-22 15:32       ` James Zhu
  2026-09-22 15:34         ` Timur Kristóf
  2026-09-22 15:40       ` Alex Deucher
  1 sibling, 1 reply; 36+ messages in thread
From: James Zhu @ 2026-09-22 15:32 UTC (permalink / raw)
  To: Timur Kristóf, amd-gfx, alexander.deucher, Bing.Ma
  Cc: James Zhu, Natalie Vock, Christian König


On 2026-09-22 11:22, Timur Kristóf wrote:
> On Tuesday, September 22, 2026 5:20:41 PM Central European Summer Time James
> Zhu wrote:
>> On 2026-09-22 11:09, Timur Kristóf wrote:
>>> On Monday, September 21, 2026 10:03:02 PM Central European Summer Time
>>> James>
>>> Zhu wrote:
>>>> Add the DRM_AMDGPU_PROFILER ioctl number and its argument structures, the
>>>> UAPI for GPU performance monitoring features such as SPM (Stream
>>>> Performance Monitor).
>>>>
>>>> struct drm_amdgpu_profiler_args carries an operation selector and a union
>>>> of per-operation parameters, currently AMDGPU_PROFILER_OP_GET_VER and
>>>> AMDGPU_PROFILER_OP_SPM. The SPM operations and the layout of the
>>>> destination buffer are described by struct drm_amdgpu_spm_args and
>>>> struct drm_amdgpu_spm_buffer_header.
>>>>
>>>> The ioctl number and struct layout are frozen here. The implementation
>>>> lands in the rest of this series and the ioctl is not registered in
>>>> amdgpu_ioctls_kms[] until then, so this header change alone does not make
>>>> the interface reachable.
>>>>
>>>> Signed-off-by: James Zhu<James.Zhu@amd.com>
>>>> Reviewed-by: Bing Ma<bing.ma@amd.com>
>>> Hi,
>>>
>>> Can you guys please clarify why this is necessary and what the use case
>>> is?
>>>
>>> Are we sure we want a uAPI for this? RADV has been using SPM from
>>> userspace
>>> for many years now.
>> [JZ] current RADV SPM is for graphic engine. These patches are for
>> compute engine SPM.
>>
>> Thanks!
> Is there any reason why it can't be done in userspace for compute too?
[JZ] for compute engine, user space is using ROCM stack , not RADV.
>
>
>>>> ---
>>>>
>>>>    include/uapi/drm/amdgpu_drm.h | 124 ++++++++++++++++++++++++++++++++++
>>>>    1 file changed, 124 insertions(+)
>>>>
>>>> diff --git a/include/uapi/drm/amdgpu_drm.h
>>>> b/include/uapi/drm/amdgpu_drm.h
>>>> index 9e5fa6f53e59..285949d63eb6 100644
>>>> --- a/include/uapi/drm/amdgpu_drm.h
>>>> +++ b/include/uapi/drm/amdgpu_drm.h
>>>> @@ -60,6 +60,7 @@ extern "C" {
>>>>
>>>>    #define DRM_AMDGPU_GEM_LIST_HANDLES	0x19
>>>>    #define DRM_AMDGPU_PROC_OPTIONS		0x1A
>>>>    #define DRM_AMDGPU_TRAP			0x1B
>>>>
>>>> +#define DRM_AMDGPU_PROFILER		0x1C
>>>>
>>>>    #define DRM_IOCTL_AMDGPU_GEM_CREATE	DRM_IOWR(DRM_COMMAND_BASE +
>>>>
>>>> DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) #define
>>>> DRM_IOCTL_AMDGPU_GEM_MMAP	DRM_IOWR(DRM_COMMAND_BASE +
>>>> DRM_AMDGPU_GEM_MMAP,
>>>> union drm_amdgpu_gem_mmap) @@ -83,6 +84,7 @@ extern "C" {
>>>>
>>>>    #define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES DRM_IOWR(DRM_COMMAND_BASE +
>>>>
>>>> DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles) #define
>>>> DRM_IOCTL_AMDGPU_PROC_OPTIONS	DRM_IOWR(DRM_COMMAND_BASE +
>>>> DRM_AMDGPU_PROC_OPTIONS, struct drm_amdgpu_proc_options) #define
>>>> DRM_IOCTL_AMDGPU_TRAP	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_TRAP,
>>> union
>>>
>>>> drm_amdgpu_trap) +#define
>>>> DRM_IOCTL_AMDGPU_PROFILER	DRM_IOWR(DRM_COMMAND_BASE +
>>> DRM_AMDGPU_PROFILER,
>>>
>>>> struct drm_amdgpu_profiler_args)
>>>>
>>>>    /**
>>>>    
>>>>     * DOC: memory domains
>>>>
>>>> @@ -1821,6 +1823,128 @@ struct drm_amdgpu_proc_options {
>>>>
>>>>    	} kfd_sigbus_delay;
>>>>    
>>>>    };
>>>>
>>>> +/*
>>>> + * Supported SPM (Stream Performance Monitor) Operations
>>>> + */
>>>> +/**
>>>> + * drm_amdgpu_spm_op - SPM ioctl operations
>>>> + */
>>>> +enum drm_amdgpu_spm_op {
>>>> +	AMDGPU_SPM_OP_ACQUIRE = 1,
>>>> +	AMDGPU_SPM_OP_RELEASE,
>>>> +	AMDGPU_SPM_OP_SET_DEST_BUF
>>>> +};
>>>> +
>>>> +/**
>>>> + * drm_amdgpu_spm_args - Arguments for SPM ioctl
>>>> + *
>>>> + * @dest_buf: [in] used for the address of the destination buffer
>>>> + *            in @AMDGPU_SPM_OP_SET_DEST_BUF
>>>> + * @buf_size: [in] size of the destination buffer
>>>> + * @op: [in] specifies the operation to perform
>>>> + * @timeout: [in/out] timeout in milliseconds (in), amount of time left
>>>> + *           in the timeout window (out)
>>>> + * @bytes_copied: [out] total amount of data that was copied to the
>>>> previous dest_buf + * @has_data_loss: [out] non-zero if data was lost
>>>> (e.g.
>>>> due to ring buffer overflow), + *                 zero otherwise
>>>> + * @reserved: must be zero for forward compatibility
>>>> + *
>>>> + * This ioctl performs different functions depending on the @op
>>>> parameter.
>>>> + *
>>>> + * AMDGPU_SPM_OP_ACQUIRE
>>>> + * ------------------------
>>>> + *
>>>> + * Acquires exclusive access of SPM on the specified XCP for the calling
>>>> process. + * This must be called before using AMDGPU_SPM_OP_SET_DEST_BUF.
>>>> + *
>>>> + * AMDGPU_SPM_OP_RELEASE
>>>> + * ------------------------
>>>> + *
>>>> + * Releases exclusive access of SPM on the specified XCP for the calling
>>>> process, + * which allows another process to acquire it in the future.
>>>> + *
>>>> + * AMDGPU_SPM_OP_SET_DEST_BUF
>>>> + * -----------------------------
>>>> + *
>>>> + * If @dest_buf is NULL, the destination buffer address is unset and
>>>> copying of counters + * is stopped.
>>>> + *
>>>> + * If @dest_buf is not NULL, it specifies the pointer to a new
>>>> destination
>>>> buffer. + * @buf_size specifies the size of the buffer.
>>>> + *
>>>> + * If @timeout is non-0, the call will wait for up to @timeout ms for
>>>> the
>>>> previous + * buffer to be filled. If the previous buffer is filled before
>>>> the timeout, @timeout + * is updated with the time remaining. If the
>>>> timeout is exceeded, the function + * copies any partial data available
>>>> into the previous user buffer and returns success. + * The amount of
>>>> valid
>>>> data in the previous user buffer is indicated by @bytes_copied. + *
>>>> + * If @timeout is 0, the function immediately replaces the previous
>>>> destination buffer + * without waiting for the previous buffer to be
>>>> filled. That means the previous buffer + * may only be partially filled,
>>>> and @bytes_copied will indicate how much data has been + * copied to it.
>>>> + *
>>>> + * If data was lost, e.g. due to a ring buffer overflow, @has_data_loss
>>>> will be non-0. + *
>>>> + * Returns negative error code on failure, 0 on success.
>>>> + */
>>>> +struct drm_amdgpu_spm_args {
>>>> +	__u64 dest_buf;
>>>> +	__u32 buf_size;
>>>> +	__u32 op;
>>>> +	__u32 timeout;
>>>> +	__u32 bytes_copied;
>>>> +	__u32 has_data_loss;
>>>> +	__u32 reserved[9];	/* must be zero; pads this struct to 64
>>> bytes */
>>>
>>>> +};
>>>> +
>>>> +/**
>>>> + * drm_amdgpu_spm_buffer_header - SPM Buffer header for
>>>> drm_amdgpu_spm_args->dest_buf + *
>>>> + * @version:       [out] SPM version (0-23: minor, 24-31: major)
>>>> + * @bytes_copied:  [out] Amount of data copied for this sub-block
>>>> + * @has_data_loss: [out] Boolean indicating whether data was lost for
>>>> this
>>>> sub-block + *                       (e.g. due to a ring-buffer overflow)
>>>> + * @reserved:      Reserved for future use, must be zero
>>>> + *
>>>> + * This header precedes the SPM data in the destination buffer. The
>>>> reserved + * field maintains 64-byte cache line alignment (32 bytes
>>>> total).
>>>> + */
>>>> +struct drm_amdgpu_spm_buffer_header {
>>>> +	__u32 version; /* 0-23: minor, 24-31: major */
>>>> +	__u32 bytes_copied;
>>>> +	__u32 has_data_loss;
>>>> +	__u32 reserved[5]; /* pad to 32 bytes (64-byte cache line / 2) */
>>>> +};
>>>> +
>>>> +/* Supported Profiler Operation */
>>>> +#define AMDGPU_PROFILER_OP_GET_VER		1 /* get profiler UAPI
>>> version */
>>>
>>>> +#define AMDGPU_PROFILER_OP_SPM			2
>>>> +
>>>> +/*
>>>> + * Profiler IOCTL
>>>> + */
>>>> +/**
>>>> + * struct drm_amdgpu_profiler_args - Profiler IOCTL arguments
>>>> + * @op: Operation to perform (AMDGPU_PROFILER_OP_*)
>>>> + * @pad: Must be zero for input validation
>>>> + * @data: Operation-specific parameters (union)
>>>> + *
>>>> + * GET_VER operation:
>>>> + * Returns the profiler UAPI version in @data.version as (MAJOR<<24) |
>>>> MINOR. + * Input: @data must be zero (reserved for forward
>>>> compatibility).
>>>> + *
>>>> + * Design note: GET_VER is intentionally a parameter-free query
>>>> returning
>>>> + * the global profiler UAPI version.
>>>> + */
>>>> +struct drm_amdgpu_profiler_args {
>>>> +	__u32 op;				/*
>>> AMDGPU_PROFILER_OP_* */
>>>
>>>> +	__u32 pad;				/* must be zero */
>>>> +	union {
>>>> +		__u32 version;		/* GET_VER: out
>>> (MAJOR<<24) | MINOR */
>>>
>>>> +		struct drm_amdgpu_spm_args spm;
>>>> +	} data;
>>>> +};
>>>> +
>>>>
>>>>    #if defined(__cplusplus)
>>>>    }
>>>>    #endif
>
>
>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions
  2026-09-22 15:32       ` James Zhu
@ 2026-09-22 15:34         ` Timur Kristóf
  0 siblings, 0 replies; 36+ messages in thread
From: Timur Kristóf @ 2026-09-22 15:34 UTC (permalink / raw)
  To: amd-gfx, alexander.deucher, Bing.Ma, James Zhu
  Cc: James Zhu, Natalie Vock, Christian König

On Tuesday, September 22, 2026 5:32:12 PM Central European Summer Time James 
Zhu wrote:
> On 2026-09-22 11:22, Timur Kristóf wrote:
> > On Tuesday, September 22, 2026 5:20:41 PM Central European Summer Time
> > James> 
> > Zhu wrote:
> >> On 2026-09-22 11:09, Timur Kristóf wrote:
> >>> On Monday, September 21, 2026 10:03:02 PM Central European Summer Time
> >>> James>
> >>> 
> >>> Zhu wrote:
> >>>> Add the DRM_AMDGPU_PROFILER ioctl number and its argument structures,
> >>>> the
> >>>> UAPI for GPU performance monitoring features such as SPM (Stream
> >>>> Performance Monitor).
> >>>> 
> >>>> struct drm_amdgpu_profiler_args carries an operation selector and a
> >>>> union
> >>>> of per-operation parameters, currently AMDGPU_PROFILER_OP_GET_VER and
> >>>> AMDGPU_PROFILER_OP_SPM. The SPM operations and the layout of the
> >>>> destination buffer are described by struct drm_amdgpu_spm_args and
> >>>> struct drm_amdgpu_spm_buffer_header.
> >>>> 
> >>>> The ioctl number and struct layout are frozen here. The implementation
> >>>> lands in the rest of this series and the ioctl is not registered in
> >>>> amdgpu_ioctls_kms[] until then, so this header change alone does not
> >>>> make
> >>>> the interface reachable.
> >>>> 
> >>>> Signed-off-by: James Zhu<James.Zhu@amd.com>
> >>>> Reviewed-by: Bing Ma<bing.ma@amd.com>
> >>> 
> >>> Hi,
> >>> 
> >>> Can you guys please clarify why this is necessary and what the use case
> >>> is?
> >>> 
> >>> Are we sure we want a uAPI for this? RADV has been using SPM from
> >>> userspace
> >>> for many years now.
> >> 
> >> [JZ] current RADV SPM is for graphic engine. These patches are for
> >> compute engine SPM.
> >> 
> >> Thanks!
> > 
> > Is there any reason why it can't be done in userspace for compute too?
> 
> [JZ] for compute engine, user space is using ROCM stack , not RADV.

Is there any reason why SPM can't be handled from userspace?
(Regardless of whether that userspace is RADV or ROCm)





^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions
  2026-09-22 15:22     ` Timur Kristóf
  2026-09-22 15:32       ` James Zhu
@ 2026-09-22 15:40       ` Alex Deucher
  2026-09-22 15:43         ` Christian König
  1 sibling, 1 reply; 36+ messages in thread
From: Alex Deucher @ 2026-09-22 15:40 UTC (permalink / raw)
  To: Timur Kristóf
  Cc: amd-gfx, alexander.deucher, Bing.Ma, James Zhu, James Zhu,
	Natalie Vock, Christian König

On Tue, Sep 22, 2026 at 11:22 AM Timur Kristóf <timur.kristof@gmail.com> wrote:
>
> On Tuesday, September 22, 2026 5:20:41 PM Central European Summer Time James
> Zhu wrote:
> > On 2026-09-22 11:09, Timur Kristóf wrote:
> > > On Monday, September 21, 2026 10:03:02 PM Central European Summer Time
> > > James>
> > > Zhu wrote:
> > >> Add the DRM_AMDGPU_PROFILER ioctl number and its argument structures, the
> > >> UAPI for GPU performance monitoring features such as SPM (Stream
> > >> Performance Monitor).
> > >>
> > >> struct drm_amdgpu_profiler_args carries an operation selector and a union
> > >> of per-operation parameters, currently AMDGPU_PROFILER_OP_GET_VER and
> > >> AMDGPU_PROFILER_OP_SPM. The SPM operations and the layout of the
> > >> destination buffer are described by struct drm_amdgpu_spm_args and
> > >> struct drm_amdgpu_spm_buffer_header.
> > >>
> > >> The ioctl number and struct layout are frozen here. The implementation
> > >> lands in the rest of this series and the ioctl is not registered in
> > >> amdgpu_ioctls_kms[] until then, so this header change alone does not make
> > >> the interface reachable.
> > >>
> > >> Signed-off-by: James Zhu<James.Zhu@amd.com>
> > >> Reviewed-by: Bing Ma<bing.ma@amd.com>
> > >
> > > Hi,
> > >
> > > Can you guys please clarify why this is necessary and what the use case
> > > is?
> > >
> > > Are we sure we want a uAPI for this? RADV has been using SPM from
> > > userspace
> > > for many years now.
> >
> > [JZ] current RADV SPM is for graphic engine. These patches are for
> > compute engine SPM.
> >
> > Thanks!
>
> Is there any reason why it can't be done in userspace for compute too?

There's only one SPM so there needs to be arbitration to avoid
multiple applications trying to use it at the same time.  It also only
works on a single vmid at a time so the application using it has to
request a fixed vmid.

Alex

>
>
> > >
> > >> ---
> > >>
> > >>   include/uapi/drm/amdgpu_drm.h | 124 ++++++++++++++++++++++++++++++++++
> > >>   1 file changed, 124 insertions(+)
> > >>
> > >> diff --git a/include/uapi/drm/amdgpu_drm.h
> > >> b/include/uapi/drm/amdgpu_drm.h
> > >> index 9e5fa6f53e59..285949d63eb6 100644
> > >> --- a/include/uapi/drm/amdgpu_drm.h
> > >> +++ b/include/uapi/drm/amdgpu_drm.h
> > >> @@ -60,6 +60,7 @@ extern "C" {
> > >>
> > >>   #define DRM_AMDGPU_GEM_LIST_HANDLES      0x19
> > >>   #define DRM_AMDGPU_PROC_OPTIONS          0x1A
> > >>   #define DRM_AMDGPU_TRAP                  0x1B
> > >>
> > >> +#define DRM_AMDGPU_PROFILER               0x1C
> > >>
> > >>   #define DRM_IOCTL_AMDGPU_GEM_CREATE      DRM_IOWR(DRM_COMMAND_BASE +
> > >>
> > >> DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) #define
> > >> DRM_IOCTL_AMDGPU_GEM_MMAP  DRM_IOWR(DRM_COMMAND_BASE +
> > >> DRM_AMDGPU_GEM_MMAP,
> > >> union drm_amdgpu_gem_mmap) @@ -83,6 +84,7 @@ extern "C" {
> > >>
> > >>   #define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES DRM_IOWR(DRM_COMMAND_BASE +
> > >>
> > >> DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles) #define
> > >> DRM_IOCTL_AMDGPU_PROC_OPTIONS      DRM_IOWR(DRM_COMMAND_BASE +
> > >> DRM_AMDGPU_PROC_OPTIONS, struct drm_amdgpu_proc_options) #define
> > >> DRM_IOCTL_AMDGPU_TRAP      DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_TRAP,
> > >
> > > union
> > >
> > >> drm_amdgpu_trap) +#define
> > >> DRM_IOCTL_AMDGPU_PROFILER  DRM_IOWR(DRM_COMMAND_BASE +
> > >
> > > DRM_AMDGPU_PROFILER,
> > >
> > >> struct drm_amdgpu_profiler_args)
> > >>
> > >>   /**
> > >>
> > >>    * DOC: memory domains
> > >>
> > >> @@ -1821,6 +1823,128 @@ struct drm_amdgpu_proc_options {
> > >>
> > >>    } kfd_sigbus_delay;
> > >>
> > >>   };
> > >>
> > >> +/*
> > >> + * Supported SPM (Stream Performance Monitor) Operations
> > >> + */
> > >> +/**
> > >> + * drm_amdgpu_spm_op - SPM ioctl operations
> > >> + */
> > >> +enum drm_amdgpu_spm_op {
> > >> +  AMDGPU_SPM_OP_ACQUIRE = 1,
> > >> +  AMDGPU_SPM_OP_RELEASE,
> > >> +  AMDGPU_SPM_OP_SET_DEST_BUF
> > >> +};
> > >> +
> > >> +/**
> > >> + * drm_amdgpu_spm_args - Arguments for SPM ioctl
> > >> + *
> > >> + * @dest_buf: [in] used for the address of the destination buffer
> > >> + *            in @AMDGPU_SPM_OP_SET_DEST_BUF
> > >> + * @buf_size: [in] size of the destination buffer
> > >> + * @op: [in] specifies the operation to perform
> > >> + * @timeout: [in/out] timeout in milliseconds (in), amount of time left
> > >> + *           in the timeout window (out)
> > >> + * @bytes_copied: [out] total amount of data that was copied to the
> > >> previous dest_buf + * @has_data_loss: [out] non-zero if data was lost
> > >> (e.g.
> > >> due to ring buffer overflow), + *                 zero otherwise
> > >> + * @reserved: must be zero for forward compatibility
> > >> + *
> > >> + * This ioctl performs different functions depending on the @op
> > >> parameter.
> > >> + *
> > >> + * AMDGPU_SPM_OP_ACQUIRE
> > >> + * ------------------------
> > >> + *
> > >> + * Acquires exclusive access of SPM on the specified XCP for the calling
> > >> process. + * This must be called before using AMDGPU_SPM_OP_SET_DEST_BUF.
> > >> + *
> > >> + * AMDGPU_SPM_OP_RELEASE
> > >> + * ------------------------
> > >> + *
> > >> + * Releases exclusive access of SPM on the specified XCP for the calling
> > >> process, + * which allows another process to acquire it in the future.
> > >> + *
> > >> + * AMDGPU_SPM_OP_SET_DEST_BUF
> > >> + * -----------------------------
> > >> + *
> > >> + * If @dest_buf is NULL, the destination buffer address is unset and
> > >> copying of counters + * is stopped.
> > >> + *
> > >> + * If @dest_buf is not NULL, it specifies the pointer to a new
> > >> destination
> > >> buffer. + * @buf_size specifies the size of the buffer.
> > >> + *
> > >> + * If @timeout is non-0, the call will wait for up to @timeout ms for
> > >> the
> > >> previous + * buffer to be filled. If the previous buffer is filled before
> > >> the timeout, @timeout + * is updated with the time remaining. If the
> > >> timeout is exceeded, the function + * copies any partial data available
> > >> into the previous user buffer and returns success. + * The amount of
> > >> valid
> > >> data in the previous user buffer is indicated by @bytes_copied. + *
> > >> + * If @timeout is 0, the function immediately replaces the previous
> > >> destination buffer + * without waiting for the previous buffer to be
> > >> filled. That means the previous buffer + * may only be partially filled,
> > >> and @bytes_copied will indicate how much data has been + * copied to it.
> > >> + *
> > >> + * If data was lost, e.g. due to a ring buffer overflow, @has_data_loss
> > >> will be non-0. + *
> > >> + * Returns negative error code on failure, 0 on success.
> > >> + */
> > >> +struct drm_amdgpu_spm_args {
> > >> +  __u64 dest_buf;
> > >> +  __u32 buf_size;
> > >> +  __u32 op;
> > >> +  __u32 timeout;
> > >> +  __u32 bytes_copied;
> > >> +  __u32 has_data_loss;
> > >> +  __u32 reserved[9];      /* must be zero; pads this struct to 64
> > >
> > > bytes */
> > >
> > >> +};
> > >> +
> > >> +/**
> > >> + * drm_amdgpu_spm_buffer_header - SPM Buffer header for
> > >> drm_amdgpu_spm_args->dest_buf + *
> > >> + * @version:       [out] SPM version (0-23: minor, 24-31: major)
> > >> + * @bytes_copied:  [out] Amount of data copied for this sub-block
> > >> + * @has_data_loss: [out] Boolean indicating whether data was lost for
> > >> this
> > >> sub-block + *                       (e.g. due to a ring-buffer overflow)
> > >> + * @reserved:      Reserved for future use, must be zero
> > >> + *
> > >> + * This header precedes the SPM data in the destination buffer. The
> > >> reserved + * field maintains 64-byte cache line alignment (32 bytes
> > >> total).
> > >> + */
> > >> +struct drm_amdgpu_spm_buffer_header {
> > >> +  __u32 version; /* 0-23: minor, 24-31: major */
> > >> +  __u32 bytes_copied;
> > >> +  __u32 has_data_loss;
> > >> +  __u32 reserved[5]; /* pad to 32 bytes (64-byte cache line / 2) */
> > >> +};
> > >> +
> > >> +/* Supported Profiler Operation */
> > >> +#define AMDGPU_PROFILER_OP_GET_VER                1 /* get profiler UAPI
> > >
> > > version */
> > >
> > >> +#define AMDGPU_PROFILER_OP_SPM                    2
> > >> +
> > >> +/*
> > >> + * Profiler IOCTL
> > >> + */
> > >> +/**
> > >> + * struct drm_amdgpu_profiler_args - Profiler IOCTL arguments
> > >> + * @op: Operation to perform (AMDGPU_PROFILER_OP_*)
> > >> + * @pad: Must be zero for input validation
> > >> + * @data: Operation-specific parameters (union)
> > >> + *
> > >> + * GET_VER operation:
> > >> + * Returns the profiler UAPI version in @data.version as (MAJOR<<24) |
> > >> MINOR. + * Input: @data must be zero (reserved for forward
> > >> compatibility).
> > >> + *
> > >> + * Design note: GET_VER is intentionally a parameter-free query
> > >> returning
> > >> + * the global profiler UAPI version.
> > >> + */
> > >> +struct drm_amdgpu_profiler_args {
> > >> +  __u32 op;                               /*
> > >
> > > AMDGPU_PROFILER_OP_* */
> > >
> > >> +  __u32 pad;                              /* must be zero */
> > >> +  union {
> > >> +          __u32 version;          /* GET_VER: out
> > >
> > > (MAJOR<<24) | MINOR */
> > >
> > >> +          struct drm_amdgpu_spm_args spm;
> > >> +  } data;
> > >> +};
> > >> +
> > >>
> > >>   #if defined(__cplusplus)
> > >>   }
> > >>   #endif
>
>
>
>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions
  2026-09-22 15:40       ` Alex Deucher
@ 2026-09-22 15:43         ` Christian König
  2026-09-23  1:20           ` Ma, Bing
  0 siblings, 1 reply; 36+ messages in thread
From: Christian König @ 2026-09-22 15:43 UTC (permalink / raw)
  To: Alex Deucher, Timur Kristóf
  Cc: amd-gfx, alexander.deucher, Bing.Ma, James Zhu, James Zhu,
	Natalie Vock



On 9/22/26 17:40, Alex Deucher wrote:
> On Tue, Sep 22, 2026 at 11:22 AM Timur Kristóf <timur.kristof@gmail.com> wrote:
>>
>> On Tuesday, September 22, 2026 5:20:41 PM Central European Summer Time James
>> Zhu wrote:
>>> On 2026-09-22 11:09, Timur Kristóf wrote:
>>>> On Monday, September 21, 2026 10:03:02 PM Central European Summer Time
>>>> James>
>>>> Zhu wrote:
>>>>> Add the DRM_AMDGPU_PROFILER ioctl number and its argument structures, the
>>>>> UAPI for GPU performance monitoring features such as SPM (Stream
>>>>> Performance Monitor).
>>>>>
>>>>> struct drm_amdgpu_profiler_args carries an operation selector and a union
>>>>> of per-operation parameters, currently AMDGPU_PROFILER_OP_GET_VER and
>>>>> AMDGPU_PROFILER_OP_SPM. The SPM operations and the layout of the
>>>>> destination buffer are described by struct drm_amdgpu_spm_args and
>>>>> struct drm_amdgpu_spm_buffer_header.
>>>>>
>>>>> The ioctl number and struct layout are frozen here. The implementation
>>>>> lands in the rest of this series and the ioctl is not registered in
>>>>> amdgpu_ioctls_kms[] until then, so this header change alone does not make
>>>>> the interface reachable.
>>>>>
>>>>> Signed-off-by: James Zhu<James.Zhu@amd.com>
>>>>> Reviewed-by: Bing Ma<bing.ma@amd.com>
>>>>
>>>> Hi,
>>>>
>>>> Can you guys please clarify why this is necessary and what the use case
>>>> is?
>>>>
>>>> Are we sure we want a uAPI for this? RADV has been using SPM from
>>>> userspace
>>>> for many years now.
>>>
>>> [JZ] current RADV SPM is for graphic engine. These patches are for
>>> compute engine SPM.
>>>
>>> Thanks!
>>
>> Is there any reason why it can't be done in userspace for compute too?
> 
> There's only one SPM so there needs to be arbitration to avoid
> multiple applications trying to use it at the same time.  It also only
> works on a single vmid at a time so the application using it has to
> request a fixed vmid.

Yeah, we already have an API for reserving a VMID for SPM.

We also have a TODO item to make that available for user queues which then both ROCm and gfx can use.

So as far as I can see that work here is completely unnecessary.

Christian.

> 
> Alex
> 
>>
>>
>>>>
>>>>> ---
>>>>>
>>>>>   include/uapi/drm/amdgpu_drm.h | 124 ++++++++++++++++++++++++++++++++++
>>>>>   1 file changed, 124 insertions(+)
>>>>>
>>>>> diff --git a/include/uapi/drm/amdgpu_drm.h
>>>>> b/include/uapi/drm/amdgpu_drm.h
>>>>> index 9e5fa6f53e59..285949d63eb6 100644
>>>>> --- a/include/uapi/drm/amdgpu_drm.h
>>>>> +++ b/include/uapi/drm/amdgpu_drm.h
>>>>> @@ -60,6 +60,7 @@ extern "C" {
>>>>>
>>>>>   #define DRM_AMDGPU_GEM_LIST_HANDLES      0x19
>>>>>   #define DRM_AMDGPU_PROC_OPTIONS          0x1A
>>>>>   #define DRM_AMDGPU_TRAP                  0x1B
>>>>>
>>>>> +#define DRM_AMDGPU_PROFILER               0x1C
>>>>>
>>>>>   #define DRM_IOCTL_AMDGPU_GEM_CREATE      DRM_IOWR(DRM_COMMAND_BASE +
>>>>>
>>>>> DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) #define
>>>>> DRM_IOCTL_AMDGPU_GEM_MMAP  DRM_IOWR(DRM_COMMAND_BASE +
>>>>> DRM_AMDGPU_GEM_MMAP,
>>>>> union drm_amdgpu_gem_mmap) @@ -83,6 +84,7 @@ extern "C" {
>>>>>
>>>>>   #define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES DRM_IOWR(DRM_COMMAND_BASE +
>>>>>
>>>>> DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles) #define
>>>>> DRM_IOCTL_AMDGPU_PROC_OPTIONS      DRM_IOWR(DRM_COMMAND_BASE +
>>>>> DRM_AMDGPU_PROC_OPTIONS, struct drm_amdgpu_proc_options) #define
>>>>> DRM_IOCTL_AMDGPU_TRAP      DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_TRAP,
>>>>
>>>> union
>>>>
>>>>> drm_amdgpu_trap) +#define
>>>>> DRM_IOCTL_AMDGPU_PROFILER  DRM_IOWR(DRM_COMMAND_BASE +
>>>>
>>>> DRM_AMDGPU_PROFILER,
>>>>
>>>>> struct drm_amdgpu_profiler_args)
>>>>>
>>>>>   /**
>>>>>
>>>>>    * DOC: memory domains
>>>>>
>>>>> @@ -1821,6 +1823,128 @@ struct drm_amdgpu_proc_options {
>>>>>
>>>>>    } kfd_sigbus_delay;
>>>>>
>>>>>   };
>>>>>
>>>>> +/*
>>>>> + * Supported SPM (Stream Performance Monitor) Operations
>>>>> + */
>>>>> +/**
>>>>> + * drm_amdgpu_spm_op - SPM ioctl operations
>>>>> + */
>>>>> +enum drm_amdgpu_spm_op {
>>>>> +  AMDGPU_SPM_OP_ACQUIRE = 1,
>>>>> +  AMDGPU_SPM_OP_RELEASE,
>>>>> +  AMDGPU_SPM_OP_SET_DEST_BUF
>>>>> +};
>>>>> +
>>>>> +/**
>>>>> + * drm_amdgpu_spm_args - Arguments for SPM ioctl
>>>>> + *
>>>>> + * @dest_buf: [in] used for the address of the destination buffer
>>>>> + *            in @AMDGPU_SPM_OP_SET_DEST_BUF
>>>>> + * @buf_size: [in] size of the destination buffer
>>>>> + * @op: [in] specifies the operation to perform
>>>>> + * @timeout: [in/out] timeout in milliseconds (in), amount of time left
>>>>> + *           in the timeout window (out)
>>>>> + * @bytes_copied: [out] total amount of data that was copied to the
>>>>> previous dest_buf + * @has_data_loss: [out] non-zero if data was lost
>>>>> (e.g.
>>>>> due to ring buffer overflow), + *                 zero otherwise
>>>>> + * @reserved: must be zero for forward compatibility
>>>>> + *
>>>>> + * This ioctl performs different functions depending on the @op
>>>>> parameter.
>>>>> + *
>>>>> + * AMDGPU_SPM_OP_ACQUIRE
>>>>> + * ------------------------
>>>>> + *
>>>>> + * Acquires exclusive access of SPM on the specified XCP for the calling
>>>>> process. + * This must be called before using AMDGPU_SPM_OP_SET_DEST_BUF.
>>>>> + *
>>>>> + * AMDGPU_SPM_OP_RELEASE
>>>>> + * ------------------------
>>>>> + *
>>>>> + * Releases exclusive access of SPM on the specified XCP for the calling
>>>>> process, + * which allows another process to acquire it in the future.
>>>>> + *
>>>>> + * AMDGPU_SPM_OP_SET_DEST_BUF
>>>>> + * -----------------------------
>>>>> + *
>>>>> + * If @dest_buf is NULL, the destination buffer address is unset and
>>>>> copying of counters + * is stopped.
>>>>> + *
>>>>> + * If @dest_buf is not NULL, it specifies the pointer to a new
>>>>> destination
>>>>> buffer. + * @buf_size specifies the size of the buffer.
>>>>> + *
>>>>> + * If @timeout is non-0, the call will wait for up to @timeout ms for
>>>>> the
>>>>> previous + * buffer to be filled. If the previous buffer is filled before
>>>>> the timeout, @timeout + * is updated with the time remaining. If the
>>>>> timeout is exceeded, the function + * copies any partial data available
>>>>> into the previous user buffer and returns success. + * The amount of
>>>>> valid
>>>>> data in the previous user buffer is indicated by @bytes_copied. + *
>>>>> + * If @timeout is 0, the function immediately replaces the previous
>>>>> destination buffer + * without waiting for the previous buffer to be
>>>>> filled. That means the previous buffer + * may only be partially filled,
>>>>> and @bytes_copied will indicate how much data has been + * copied to it.
>>>>> + *
>>>>> + * If data was lost, e.g. due to a ring buffer overflow, @has_data_loss
>>>>> will be non-0. + *
>>>>> + * Returns negative error code on failure, 0 on success.
>>>>> + */
>>>>> +struct drm_amdgpu_spm_args {
>>>>> +  __u64 dest_buf;
>>>>> +  __u32 buf_size;
>>>>> +  __u32 op;
>>>>> +  __u32 timeout;
>>>>> +  __u32 bytes_copied;
>>>>> +  __u32 has_data_loss;
>>>>> +  __u32 reserved[9];      /* must be zero; pads this struct to 64
>>>>
>>>> bytes */
>>>>
>>>>> +};
>>>>> +
>>>>> +/**
>>>>> + * drm_amdgpu_spm_buffer_header - SPM Buffer header for
>>>>> drm_amdgpu_spm_args->dest_buf + *
>>>>> + * @version:       [out] SPM version (0-23: minor, 24-31: major)
>>>>> + * @bytes_copied:  [out] Amount of data copied for this sub-block
>>>>> + * @has_data_loss: [out] Boolean indicating whether data was lost for
>>>>> this
>>>>> sub-block + *                       (e.g. due to a ring-buffer overflow)
>>>>> + * @reserved:      Reserved for future use, must be zero
>>>>> + *
>>>>> + * This header precedes the SPM data in the destination buffer. The
>>>>> reserved + * field maintains 64-byte cache line alignment (32 bytes
>>>>> total).
>>>>> + */
>>>>> +struct drm_amdgpu_spm_buffer_header {
>>>>> +  __u32 version; /* 0-23: minor, 24-31: major */
>>>>> +  __u32 bytes_copied;
>>>>> +  __u32 has_data_loss;
>>>>> +  __u32 reserved[5]; /* pad to 32 bytes (64-byte cache line / 2) */
>>>>> +};
>>>>> +
>>>>> +/* Supported Profiler Operation */
>>>>> +#define AMDGPU_PROFILER_OP_GET_VER                1 /* get profiler UAPI
>>>>
>>>> version */
>>>>
>>>>> +#define AMDGPU_PROFILER_OP_SPM                    2
>>>>> +
>>>>> +/*
>>>>> + * Profiler IOCTL
>>>>> + */
>>>>> +/**
>>>>> + * struct drm_amdgpu_profiler_args - Profiler IOCTL arguments
>>>>> + * @op: Operation to perform (AMDGPU_PROFILER_OP_*)
>>>>> + * @pad: Must be zero for input validation
>>>>> + * @data: Operation-specific parameters (union)
>>>>> + *
>>>>> + * GET_VER operation:
>>>>> + * Returns the profiler UAPI version in @data.version as (MAJOR<<24) |
>>>>> MINOR. + * Input: @data must be zero (reserved for forward
>>>>> compatibility).
>>>>> + *
>>>>> + * Design note: GET_VER is intentionally a parameter-free query
>>>>> returning
>>>>> + * the global profiler UAPI version.
>>>>> + */
>>>>> +struct drm_amdgpu_profiler_args {
>>>>> +  __u32 op;                               /*
>>>>
>>>> AMDGPU_PROFILER_OP_* */
>>>>
>>>>> +  __u32 pad;                              /* must be zero */
>>>>> +  union {
>>>>> +          __u32 version;          /* GET_VER: out
>>>>
>>>> (MAJOR<<24) | MINOR */
>>>>
>>>>> +          struct drm_amdgpu_spm_args spm;
>>>>> +  } data;
>>>>> +};
>>>>> +
>>>>>
>>>>>   #if defined(__cplusplus)
>>>>>   }
>>>>>   #endif
>>
>>
>>
>>


^ permalink raw reply	[flat|nested] 36+ messages in thread

* RE: [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions
  2026-09-22 15:43         ` Christian König
@ 2026-09-23  1:20           ` Ma, Bing
  2026-09-23  7:47             ` Christian König
  0 siblings, 1 reply; 36+ messages in thread
From: Ma, Bing @ 2026-09-23  1:20 UTC (permalink / raw)
  To: Koenig, Christian, Alex Deucher, Timur Kristóf
  Cc: amd-gfx@lists.freedesktop.org, Deucher,  Alexander, Zhu, James,
	Zhu, James, Natalie Vock

AMD General

Hi Christian,

Could you share a link to this TODO item?

I'd like to verify whether this has been addressed through MES. ROCm uses the user-mode queue, and its VMID is assigned by MES rather than by the kernel driver. If MES support is available, ROCm could move SPM support to user mode.

If this TODO has indeed been resolved through MES, we'll review the timeline for that MES support. Currently, ROCm SPM still goes through the kernel driver path, and we will need to keep using that path until the required MES support becomes available.

Best,

Bing

-----Original Message-----
From: Koenig, Christian <Christian.Koenig@amd.com>
Sent: Tuesday, September 22, 2026 8:44 AM
To: Alex Deucher <alexdeucher@gmail.com>; Timur Kristóf <timur.kristof@gmail.com>
Cc: amd-gfx@lists.freedesktop.org; Deucher, Alexander <Alexander.Deucher@amd.com>; Ma, Bing <Bing.Ma@amd.com>; Zhu, James <James.Zhu@amd.com>; Zhu, James <James.Zhu@amd.com>; Natalie Vock <nat@pixelcluster.dev>
Subject: Re: [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions



On 9/22/26 17:40, Alex Deucher wrote:
> On Tue, Sep 22, 2026 at 11:22 AM Timur Kristóf <timur.kristof@gmail.com> wrote:
>>
>> On Tuesday, September 22, 2026 5:20:41 PM Central European Summer
>> Time James Zhu wrote:
>>> On 2026-09-22 11:09, Timur Kristóf wrote:
>>>> On Monday, September 21, 2026 10:03:02 PM Central European Summer
>>>> Time
>>>> James>
>>>> Zhu wrote:
>>>>> Add the DRM_AMDGPU_PROFILER ioctl number and its argument
>>>>> structures, the UAPI for GPU performance monitoring features such
>>>>> as SPM (Stream Performance Monitor).
>>>>>
>>>>> struct drm_amdgpu_profiler_args carries an operation selector and
>>>>> a union of per-operation parameters, currently
>>>>> AMDGPU_PROFILER_OP_GET_VER and AMDGPU_PROFILER_OP_SPM. The SPM
>>>>> operations and the layout of the destination buffer are described
>>>>> by struct drm_amdgpu_spm_args and struct drm_amdgpu_spm_buffer_header.
>>>>>
>>>>> The ioctl number and struct layout are frozen here. The
>>>>> implementation lands in the rest of this series and the ioctl is
>>>>> not registered in amdgpu_ioctls_kms[] until then, so this header
>>>>> change alone does not make the interface reachable.
>>>>>
>>>>> Signed-off-by: James Zhu<James.Zhu@amd.com>
>>>>> Reviewed-by: Bing Ma<bing.ma@amd.com>
>>>>
>>>> Hi,
>>>>
>>>> Can you guys please clarify why this is necessary and what the use
>>>> case is?
>>>>
>>>> Are we sure we want a uAPI for this? RADV has been using SPM from
>>>> userspace for many years now.
>>>
>>> [JZ] current RADV SPM is for graphic engine. These patches are for
>>> compute engine SPM.
>>>
>>> Thanks!
>>
>> Is there any reason why it can't be done in userspace for compute too?
>
> There's only one SPM so there needs to be arbitration to avoid
> multiple applications trying to use it at the same time.  It also only
> works on a single vmid at a time so the application using it has to
> request a fixed vmid.

Yeah, we already have an API for reserving a VMID for SPM.

We also have a TODO item to make that available for user queues which then both ROCm and gfx can use.

So as far as I can see that work here is completely unnecessary.

Christian.

>
> Alex
>
>>
>>
>>>>
>>>>> ---
>>>>>
>>>>>   include/uapi/drm/amdgpu_drm.h | 124 ++++++++++++++++++++++++++++++++++
>>>>>   1 file changed, 124 insertions(+)
>>>>>
>>>>> diff --git a/include/uapi/drm/amdgpu_drm.h
>>>>> b/include/uapi/drm/amdgpu_drm.h index 9e5fa6f53e59..285949d63eb6
>>>>> 100644
>>>>> --- a/include/uapi/drm/amdgpu_drm.h
>>>>> +++ b/include/uapi/drm/amdgpu_drm.h
>>>>> @@ -60,6 +60,7 @@ extern "C" {
>>>>>
>>>>>   #define DRM_AMDGPU_GEM_LIST_HANDLES      0x19
>>>>>   #define DRM_AMDGPU_PROC_OPTIONS          0x1A
>>>>>   #define DRM_AMDGPU_TRAP                  0x1B
>>>>>
>>>>> +#define DRM_AMDGPU_PROFILER               0x1C
>>>>>
>>>>>   #define DRM_IOCTL_AMDGPU_GEM_CREATE      DRM_IOWR(DRM_COMMAND_BASE +
>>>>>
>>>>> DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) #define
>>>>> DRM_IOCTL_AMDGPU_GEM_MMAP  DRM_IOWR(DRM_COMMAND_BASE +
>>>>> DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap) @@ -83,6 +84,7 @@
>>>>> extern "C" {
>>>>>
>>>>>   #define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES
>>>>> DRM_IOWR(DRM_COMMAND_BASE +
>>>>>
>>>>> DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles) #define
>>>>> DRM_IOCTL_AMDGPU_PROC_OPTIONS      DRM_IOWR(DRM_COMMAND_BASE +
>>>>> DRM_AMDGPU_PROC_OPTIONS, struct drm_amdgpu_proc_options) #define
>>>>> DRM_IOCTL_AMDGPU_TRAP      DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_TRAP,
>>>>
>>>> union
>>>>
>>>>> drm_amdgpu_trap) +#define
>>>>> DRM_IOCTL_AMDGPU_PROFILER  DRM_IOWR(DRM_COMMAND_BASE +
>>>>
>>>> DRM_AMDGPU_PROFILER,
>>>>
>>>>> struct drm_amdgpu_profiler_args)
>>>>>
>>>>>   /**
>>>>>
>>>>>    * DOC: memory domains
>>>>>
>>>>> @@ -1821,6 +1823,128 @@ struct drm_amdgpu_proc_options {
>>>>>
>>>>>    } kfd_sigbus_delay;
>>>>>
>>>>>   };
>>>>>
>>>>> +/*
>>>>> + * Supported SPM (Stream Performance Monitor) Operations  */
>>>>> +/**
>>>>> + * drm_amdgpu_spm_op - SPM ioctl operations  */ enum
>>>>> +drm_amdgpu_spm_op {
>>>>> +  AMDGPU_SPM_OP_ACQUIRE = 1,
>>>>> +  AMDGPU_SPM_OP_RELEASE,
>>>>> +  AMDGPU_SPM_OP_SET_DEST_BUF
>>>>> +};
>>>>> +
>>>>> +/**
>>>>> + * drm_amdgpu_spm_args - Arguments for SPM ioctl
>>>>> + *
>>>>> + * @dest_buf: [in] used for the address of the destination buffer
>>>>> + *            in @AMDGPU_SPM_OP_SET_DEST_BUF
>>>>> + * @buf_size: [in] size of the destination buffer
>>>>> + * @op: [in] specifies the operation to perform
>>>>> + * @timeout: [in/out] timeout in milliseconds (in), amount of time left
>>>>> + *           in the timeout window (out)
>>>>> + * @bytes_copied: [out] total amount of data that was copied to
>>>>> +the
>>>>> previous dest_buf + * @has_data_loss: [out] non-zero if data was
>>>>> lost (e.g.
>>>>> due to ring buffer overflow), + *                 zero otherwise
>>>>> + * @reserved: must be zero for forward compatibility
>>>>> + *
>>>>> + * This ioctl performs different functions depending on the @op
>>>>> parameter.
>>>>> + *
>>>>> + * AMDGPU_SPM_OP_ACQUIRE
>>>>> + * ------------------------
>>>>> + *
>>>>> + * Acquires exclusive access of SPM on the specified XCP for the
>>>>> + calling
>>>>> process. + * This must be called before using AMDGPU_SPM_OP_SET_DEST_BUF.
>>>>> + *
>>>>> + * AMDGPU_SPM_OP_RELEASE
>>>>> + * ------------------------
>>>>> + *
>>>>> + * Releases exclusive access of SPM on the specified XCP for the
>>>>> + calling
>>>>> process, + * which allows another process to acquire it in the future.
>>>>> + *
>>>>> + * AMDGPU_SPM_OP_SET_DEST_BUF
>>>>> + * -----------------------------
>>>>> + *
>>>>> + * If @dest_buf is NULL, the destination buffer address is unset
>>>>> + and
>>>>> copying of counters + * is stopped.
>>>>> + *
>>>>> + * If @dest_buf is not NULL, it specifies the pointer to a new
>>>>> destination
>>>>> buffer. + * @buf_size specifies the size of the buffer.
>>>>> + *
>>>>> + * If @timeout is non-0, the call will wait for up to @timeout ms
>>>>> + for
>>>>> the
>>>>> previous + * buffer to be filled. If the previous buffer is filled
>>>>> before the timeout, @timeout + * is updated with the time
>>>>> remaining. If the timeout is exceeded, the function + * copies any
>>>>> partial data available into the previous user buffer and returns
>>>>> success. + * The amount of valid data in the previous user buffer
>>>>> is indicated by @bytes_copied. + *
>>>>> + * If @timeout is 0, the function immediately replaces the
>>>>> + previous
>>>>> destination buffer + * without waiting for the previous buffer to
>>>>> be filled. That means the previous buffer + * may only be
>>>>> partially filled, and @bytes_copied will indicate how much data has been + * copied to it.
>>>>> + *
>>>>> + * If data was lost, e.g. due to a ring buffer overflow,
>>>>> + @has_data_loss
>>>>> will be non-0. + *
>>>>> + * Returns negative error code on failure, 0 on success.
>>>>> + */
>>>>> +struct drm_amdgpu_spm_args {
>>>>> +  __u64 dest_buf;
>>>>> +  __u32 buf_size;
>>>>> +  __u32 op;
>>>>> +  __u32 timeout;
>>>>> +  __u32 bytes_copied;
>>>>> +  __u32 has_data_loss;
>>>>> +  __u32 reserved[9];      /* must be zero; pads this struct to 64
>>>>
>>>> bytes */
>>>>
>>>>> +};
>>>>> +
>>>>> +/**
>>>>> + * drm_amdgpu_spm_buffer_header - SPM Buffer header for
>>>>> drm_amdgpu_spm_args->dest_buf + *
>>>>> + * @version:       [out] SPM version (0-23: minor, 24-31: major)
>>>>> + * @bytes_copied:  [out] Amount of data copied for this sub-block
>>>>> + * @has_data_loss: [out] Boolean indicating whether data was lost
>>>>> + for
>>>>> this
>>>>> sub-block + *                       (e.g. due to a ring-buffer overflow)
>>>>> + * @reserved:      Reserved for future use, must be zero
>>>>> + *
>>>>> + * This header precedes the SPM data in the destination buffer.
>>>>> + The
>>>>> reserved + * field maintains 64-byte cache line alignment (32
>>>>> bytes total).
>>>>> + */
>>>>> +struct drm_amdgpu_spm_buffer_header {
>>>>> +  __u32 version; /* 0-23: minor, 24-31: major */
>>>>> +  __u32 bytes_copied;
>>>>> +  __u32 has_data_loss;
>>>>> +  __u32 reserved[5]; /* pad to 32 bytes (64-byte cache line / 2)
>>>>> +*/ };
>>>>> +
>>>>> +/* Supported Profiler Operation */
>>>>> +#define AMDGPU_PROFILER_OP_GET_VER                1 /* get profiler UAPI
>>>>
>>>> version */
>>>>
>>>>> +#define AMDGPU_PROFILER_OP_SPM                    2
>>>>> +
>>>>> +/*
>>>>> + * Profiler IOCTL
>>>>> + */
>>>>> +/**
>>>>> + * struct drm_amdgpu_profiler_args - Profiler IOCTL arguments
>>>>> + * @op: Operation to perform (AMDGPU_PROFILER_OP_*)
>>>>> + * @pad: Must be zero for input validation
>>>>> + * @data: Operation-specific parameters (union)
>>>>> + *
>>>>> + * GET_VER operation:
>>>>> + * Returns the profiler UAPI version in @data.version as
>>>>> +(MAJOR<<24) |
>>>>> MINOR. + * Input: @data must be zero (reserved for forward
>>>>> compatibility).
>>>>> + *
>>>>> + * Design note: GET_VER is intentionally a parameter-free query
>>>>> returning
>>>>> + * the global profiler UAPI version.
>>>>> + */
>>>>> +struct drm_amdgpu_profiler_args {
>>>>> +  __u32 op;                               /*
>>>>
>>>> AMDGPU_PROFILER_OP_* */
>>>>
>>>>> +  __u32 pad;                              /* must be zero */
>>>>> +  union {
>>>>> +          __u32 version;          /* GET_VER: out
>>>>
>>>> (MAJOR<<24) | MINOR */
>>>>
>>>>> +          struct drm_amdgpu_spm_args spm;
>>>>> +  } data;
>>>>> +};
>>>>> +
>>>>>
>>>>>   #if defined(__cplusplus)
>>>>>   }
>>>>>   #endif
>>
>>
>>
>>


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 04/12] drm/amdgpu: add amdgpu_xcp_get_xcc_mask helper
  2026-09-22 15:10             ` James Zhu
@ 2026-09-23  3:39               ` Lazar, Lijo
  2026-09-23 12:36                 ` James Zhu
  0 siblings, 1 reply; 36+ messages in thread
From: Lazar, Lijo @ 2026-09-23  3:39 UTC (permalink / raw)
  To: James Zhu, James Zhu, amd-gfx, alexander.deucher, Bing.Ma



On 22-Sep-26 8:40 PM, James Zhu wrote:
> 
> On 2026-09-22 10:26, Lazar, Lijo wrote:
>>
>>
>> On 22-Sep-26 7:41 PM, James Zhu wrote:
>>>
>>> On 2026-09-22 09:45, Lazar, Lijo wrote:
>>>>
>>>>
>>>> On 22-Sep-26 7:05 PM, James Zhu wrote:
>>>>>
>>>>> On 2026-09-22 01:22, Lazar, Lijo wrote:
>>>>>>
>>>>>>
>>>>>> On 22-Sep-26 1:33 AM, James Zhu wrote:
>>>>>>> Add amdgpu_xcp_get_xcc_mask() to return the XCC instance mask for 
>>>>>>> a given
>>>>>>> XCP partition. This is needed by upcoming SPM (Stream Performance 
>>>>>>> Monitor)
>>>>>>> support, which programs hardware per partition and must operate 
>>>>>>> only on
>>>>>>> the XCCs that belong to a given XCP.
>>>>>>>
>>>>>>> For partitioned systems the helper returns only the XCCs 
>>>>>>> belonging to the
>>>>>>> specified XCP. When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the 
>>>>>>> device is
>>>>>>> not partitioned) it returns the device's full XCC mask (e.g. 0xFF 
>>>>>>> for an
>>>>>>> 8-XCC device). Note that the full device mask is returned even on a
>>>>>>> partitioned device; callers that require partition isolation 
>>>>>>> (e.g. SPM)
>>>>>>> must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>>>>>>> themselves.
>>>>>>>
>>>>>>
>>>>>> Could you clarifiy on this part - xcp_id = AMDGPU_XCP_NO_PARTITION 
>>>>>> usage on 8-XCC device?
>>>>>>
>>>>>> The default state on a multi-XCC device after driver load is all 
>>>>>> XCCs in a single partition called SPX. It should have at least one 
>>>>>> partition valid.
>>>>>
>>>>> [JZ] Yes, 8-XCC device should not reach AMDGPU_XCP_NO_PARTITION case.
>>>>>
>>>>> the if(!xcp_mgr ||xcp_id ==AMDGPU_XCP_NO_PARTITION) mainly covers 
>>>>> non partition device, I can remove the above comments which don't 
>>>>> reflect the source accurately.
>>>>
>>>> Then I'm not sure what issue this is fixing.
>>>>
>>>> Multi-xcc devices will be in SPX mode and will return a mask with 
>>>> all the XCCs. Non-multi xcc devices are expected to have this set to 
>>>> 1 anyway to reflect that a single GC is present always.
>>> [JZ] Just realized a Fixes tag is added. I think somehow AI review 
>>> added it, and I did not check properly, is should be removed also. 
>>> Sorry for this confusing!
>>
>> I see that it also added an external function like this -
>>
>> amdgpu_xcp_get_xcc_mask
>>
>> The expectation is any IP instance mask within a partition needs to 
>> obtained through - amdgpu_xcp_get_inst_details
>>
>> Also, amdgpu_xcp_get_xcc_mask provides this which is not correct -
>>
>> *xcc_mask = adev->gfx.xcc_mask
>>
>> xcp instance mask operates only return logical mask of instances 
>> present (xcc_mask could include disabled ones also).
> [JZ] Here is only for non-partition device. I think adev->gfx.xcc_mask=0x1.
> I can change it to below code to clear the confusing
> 
> *xcc_mask = BIT(0);
> 
> How Do you think?
> Thanks!
> 

Once that is done, it's the same as what exists now (without the extra 
function).

Also, why do you need get_xcc_mask function? If the difficulty is in 
getting amdgpu_xcp* pointer from xcp_id, then a wrapper could be added 
and used like

xcp = amdgpu_xcp_get_obj(adev, xcp_id);
amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX, 
&xcc_mask);

Thanks,
Lijo

> James
> 
>>
>> Thanks,
>> Lijo
>>
>>>>
>>>> Thanks,
>>>> Lijo
>>>>
>>>>>> Thanks,
>>>>>> Lioj
>>>>>>
>>>>>>> amdgpu_xcp_get_num_xcc() is reworked to take (adev, xcp_id) and 
>>>>>>> derive the
>>>>>>> count from this helper via hweight32(). The helper is defined out 
>>>>>>> of line
>>>>>>> in amdgpu_xcp.c so that the static inline in amdgpu_xcp.h never 
>>>>>>> has to
>>>>>>> dereference the (incomplete) struct amdgpu_device; it only passes 
>>>>>>> the
>>>>>>> pointer through.
>>>>>>>
>>>>>>> This is a user-visible change: amdgpu_xcp_get_num_xcc() previously
>>>>>>> returned 1 for non-partitioned systems (and for 
>>>>>>> AMDGPU_XCP_NO_PARTITION),
>>>>>>> and now returns the actual XCC count (e.g. 8 for an 8-XCC 
>>>>>>> device). CWSR
>>>>>>> callers (amdgpu_kms.c, amdgpu_userq.c, mes_userqueue.c) scale the 
>>>>>>> per-XCC
>>>>>>> control-stack and debug-memory sizes by num_xcc, so the uAPI-visible
>>>>>>> ctl_stack_size and dbg_mem_size values grow accordingly to match the
>>>>>>> actual hardware resource requirements, which were previously 
>>>>>>> undersized.
>>>>>>>
>>>>>>> Fixes: 4cd417bfec49 ("drm/amdgpu: Add helper function to get xcc 
>>>>>>> count")
>>>>>>> Signed-off-by: James Zhu <James.Zhu@amd.com>
>>>>>>> Reviewed-by: Bing Ma <bing.ma@amd.com>
>>>>>>> ---
>>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c    |  2 +-
>>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c  |  2 +-
>>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c    | 48 +++++++++++++++ 
>>>>>>> ++ +++++
>>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h    | 32 +++++++--------
>>>>>>>   drivers/gpu/drm/amd/amdgpu/mes_userqueue.c |  3 +-
>>>>>>>   5 files changed, 66 insertions(+), 21 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/ 
>>>>>>> gpu/ drm/amd/amdgpu/amdgpu_kms.c
>>>>>>> index 57fa69c0382d..7782ccd4d28f 100644
>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>>>>>> @@ -1501,7 +1501,7 @@ int amdgpu_info_ioctl(struct drm_device 
>>>>>>> *dev, void *data, struct drm_file *filp)
>>>>>>>           fpriv = (struct amdgpu_fpriv *)filp->driver_priv;
>>>>>>>           if (!amdgpu_trap_is_enabled(adev) || !fpriv->trap_obj)
>>>>>>>               return -EOPNOTSUPP;
>>>>>>> -        num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv- 
>>>>>>> >xcp_id);
>>>>>>> +        num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>>>>           cwsr_info.ctl_stack_size =
>>>>>>> adev->trap_info->cwsr.xcc_ctl_stack_sz * num_xcc;
>>>>>>>           cwsr_info.dbg_mem_size =
>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/ 
>>>>>>> gpu/ drm/amd/amdgpu/amdgpu_userq.c
>>>>>>> index db09e879b85e..d19af9cb6ee1 100644
>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>>>>>> @@ -258,7 +258,7 @@ int 
>>>>>>> amdgpu_userq_input_trap_params_validate(struct 
>>>>>>> amdgpu_usermode_queue *queue,
>>>>>>>       int num_xcc;
>>>>>>>       int r;
>>>>>>>   -    num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv- 
>>>>>>> >xcp_id);
>>>>>>> +    num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>>>>       r = amdgpu_trap_validate_params(queue->userq_mgr->adev, 
>>>>>>> trap_params,
>>>>>>>                       num_xcc);
>>>>>>>       if (r)
>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/ 
>>>>>>> gpu/ drm/amd/amdgpu/amdgpu_xcp.c
>>>>>>> index 2a56f5ec9c76..90846d55292b 100644
>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>>>>>> @@ -390,6 +390,54 @@ int amdgpu_xcp_get_inst_details(struct 
>>>>>>> amdgpu_xcp *xcp,
>>>>>>>       return 0;
>>>>>>>   }
>>>>>>>   +/**
>>>>>>> + * amdgpu_xcp_get_xcc_mask - Get XCC instance mask for a partition
>>>>>>> + * @adev: amdgpu device pointer
>>>>>>> + * @xcp_id: XCP partition ID
>>>>>>> + * @xcc_mask: output parameter for XCC instance mask
>>>>>>> + *
>>>>>>> + * Returns the XCC instance mask for the specified XCP partition.
>>>>>>> + * For partitioned systems, returns only the XCCs belonging to 
>>>>>>> this XCP.
>>>>>>> + *
>>>>>>> + * When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the device is not
>>>>>>> + * partitioned), the device's full XCC mask is returned (e.g. 
>>>>>>> 0xFF for an
>>>>>>> + * 8-XCC device). Note that the full device mask is returned 
>>>>>>> even on a
>>>>>>> + * partitioned device: callers that require partition isolation 
>>>>>>> (e.g. SPM)
>>>>>>> + * must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>>>>>>> themselves.
>>>>>>> + *
>>>>>>> + * Note: On error, xcc_mask is undefined. Callers must check 
>>>>>>> return code
>>>>>>> + * and only read xcc_mask when return == 0.
>>>>>>> + *
>>>>>>> + * Return: 0 on success (non-partitioned systems or valid XCP 
>>>>>>> found),
>>>>>>> + *         -EINVAL if xcc_mask is NULL, no matching XCP id is 
>>>>>>> found, or the
>>>>>>> + *         partition's GFX IP details are unavailable
>>>>>>> + */
>>>>>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
>>>>>>> +                uint32_t *xcc_mask)
>>>>>>> +{
>>>>>>> +    struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr;
>>>>>>> +    struct amdgpu_xcp *xcp;
>>>>>>> +    int i;
>>>>>>> +
>>>>>>> +    if (!xcc_mask)
>>>>>>> +        return -EINVAL;
>>>>>>> +
>>>>>>> +    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION) {
>>>>>>> +        /* Non-partitioned: return the device's full XCC mask. */
>>>>>>> +        *xcc_mask = adev->gfx.xcc_mask;
>>>>>>> +        return 0;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    /* Partitioned system: get this XCP's XCCs */
>>>>>>> +    for_each_xcp(xcp_mgr, xcp, i) {
>>>>>>> +        if (xcp->id == xcp_id)
>>>>>>> +            return amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
>>>>>>> +                               xcc_mask);
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    return -EINVAL;
>>>>>>> +}
>>>>>>> +
>>>>>>>   int amdgpu_xcp_dev_register(struct amdgpu_device *adev,
>>>>>>>               const struct pci_device_id *ent)
>>>>>>>   {
>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h b/drivers/ 
>>>>>>> gpu/ drm/amd/amdgpu/amdgpu_xcp.h
>>>>>>> index 692508be23fb..6eed82ad49b1 100644
>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>>>>>> @@ -44,6 +44,7 @@
>>>>>>>   #define XCP_INST_MASK(num_inst, xcp_id)        \
>>>>>>>       (num_inst ? GENMASK(num_inst - 1, 0) << (xcp_id * 
>>>>>>> num_inst) : 0)
>>>>>>>   +struct amdgpu_device;
>>>>>>>   struct amdgpu_fpriv;
>>>>>>>     enum AMDGPU_XCP_IP_BLOCK {
>>>>>>> @@ -219,26 +220,23 @@ amdgpu_get_next_xcp(struct amdgpu_xcp_mgr 
>>>>>>> *xcp_mgr, int *from)
>>>>>>>       for (i = 0, xcp = amdgpu_get_next_xcp(xcp_mgr, &i); xcp; \
>>>>>>>            ++i, xcp = amdgpu_get_next_xcp(xcp_mgr, &i))
>>>>>>>   -static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_xcp_mgr 
>>>>>>> *xcp_mgr,
>>>>>>> -                     int xcp_id)
>>>>>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int xcp_id,
>>>>>>> +                uint32_t *xcc_mask);
>>>>>>> +
>>>>>>> +static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_device 
>>>>>>> *adev, int xcp_id)
>>>>>>>   {
>>>>>>> -    struct amdgpu_xcp *xcp;
>>>>>>>       uint32_t xcc_mask;
>>>>>>> -    int i, r;
>>>>>>> -
>>>>>>> -    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION)
>>>>>>> +    int r, count;
>>>>>>> +
>>>>>>> +    /* Derive the XCC count from the partition's XCC mask (or 
>>>>>>> the full
>>>>>>> +     * device mask when not partitioned). amdgpu_xcp_get_xcc_mask()
>>>>>>> +     * handles both partitioned and non-partitioned cases.
>>>>>>> +     */
>>>>>>> +    r = amdgpu_xcp_get_xcc_mask(adev, xcp_id, &xcc_mask);
>>>>>>> +    if (unlikely(r))
>>>>>>>           return 1;
>>>>>>> -    for_each_xcp(xcp_mgr, xcp, i) {
>>>>>>> -        if (xcp->id == xcp_id) {
>>>>>>> -            r = amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
>>>>>>> -                            &xcc_mask);
>>>>>>> -            if (unlikely(r))
>>>>>>> -                return 1;
>>>>>>> -            else
>>>>>>> -                return hweight32(xcc_mask);
>>>>>>> -        }
>>>>>>> -    }
>>>>>>>   -    return 1;
>>>>>>> +    count = hweight32(xcc_mask);
>>>>>>> +    return count ? count : 1;
>>>>>>>   }
>>>>>>>   #endif
>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/ 
>>>>>>> drivers/ gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>>>> index 16aa22b25098..1c7b2f3227e5 100644
>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>>>> @@ -503,8 +503,7 @@ static int mes_userq_mqd_create(struct 
>>>>>>> amdgpu_usermode_queue *queue,
>>>>>>>                   uq_mgr_to_fpriv(queue->userq_mgr);
>>>>>>>               int num_xcc;
>>>>>>>   -            num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr,
>>>>>>> -                             fpriv->xcp_id);
>>>>>>> +            num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>>>>               trap_params.ctx_save_area_address =
>>>>>>>                   compute_mqd->ctx_save_area_va;
>>>>>>>               trap_params.cwsr_sz = compute_mqd->ctx_save_area_size;
>>>>>>
>>>>
>>


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions
  2026-09-23  1:20           ` Ma, Bing
@ 2026-09-23  7:47             ` Christian König
  2026-09-24 12:33               ` Ma, Bing
  0 siblings, 1 reply; 36+ messages in thread
From: Christian König @ 2026-09-23  7:47 UTC (permalink / raw)
  To: Ma, Bing, Alex Deucher, Timur Kristóf
  Cc: amd-gfx@lists.freedesktop.org, Deucher, Alexander, Zhu, James,
	Natalie Vock

Hi Bing,

that won't work like this. The MES integration is part of the upcomming KFD/KGD unification.

That will probably take a coulple of month if not years to be completed.

Since this here is clearly breaking the already existing SPM support for Graphics I have to fundamentally reject the ROCm SPM approach.

I'm not sure why the ROCm SPM patches were developed in the first place, with the approach taking in that patches here that feature is clearly not going to fly.

I will sync up with Jenny and Alex on this.

Regards,
Christian.

On 9/23/26 03:20, Ma, Bing wrote:
> AMD General
> 
> Hi Christian,
> 
> Could you share a link to this TODO item?
> 
> I'd like to verify whether this has been addressed through MES. ROCm uses the user-mode queue, and its VMID is assigned by MES rather than by the kernel driver. If MES support is available, ROCm could move SPM support to user mode.
> 
> If this TODO has indeed been resolved through MES, we'll review the timeline for that MES support. Currently, ROCm SPM still goes through the kernel driver path, and we will need to keep using that path until the required MES support becomes available.
> 
> Best,
> 
> Bing
> 
> -----Original Message-----
> From: Koenig, Christian <Christian.Koenig@amd.com>
> Sent: Tuesday, September 22, 2026 8:44 AM
> To: Alex Deucher <alexdeucher@gmail.com>; Timur Kristóf <timur.kristof@gmail.com>
> Cc: amd-gfx@lists.freedesktop.org; Deucher, Alexander <Alexander.Deucher@amd.com>; Ma, Bing <Bing.Ma@amd.com>; Zhu, James <James.Zhu@amd.com>; Zhu, James <James.Zhu@amd.com>; Natalie Vock <nat@pixelcluster.dev>
> Subject: Re: [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions
> 
> 
> 
> On 9/22/26 17:40, Alex Deucher wrote:
>> On Tue, Sep 22, 2026 at 11:22 AM Timur Kristóf <timur.kristof@gmail.com> wrote:
>>>
>>> On Tuesday, September 22, 2026 5:20:41 PM Central European Summer
>>> Time James Zhu wrote:
>>>> On 2026-09-22 11:09, Timur Kristóf wrote:
>>>>> On Monday, September 21, 2026 10:03:02 PM Central European Summer
>>>>> Time
>>>>> James>
>>>>> Zhu wrote:
>>>>>> Add the DRM_AMDGPU_PROFILER ioctl number and its argument
>>>>>> structures, the UAPI for GPU performance monitoring features such
>>>>>> as SPM (Stream Performance Monitor).
>>>>>>
>>>>>> struct drm_amdgpu_profiler_args carries an operation selector and
>>>>>> a union of per-operation parameters, currently
>>>>>> AMDGPU_PROFILER_OP_GET_VER and AMDGPU_PROFILER_OP_SPM. The SPM
>>>>>> operations and the layout of the destination buffer are described
>>>>>> by struct drm_amdgpu_spm_args and struct drm_amdgpu_spm_buffer_header.
>>>>>>
>>>>>> The ioctl number and struct layout are frozen here. The
>>>>>> implementation lands in the rest of this series and the ioctl is
>>>>>> not registered in amdgpu_ioctls_kms[] until then, so this header
>>>>>> change alone does not make the interface reachable.
>>>>>>
>>>>>> Signed-off-by: James Zhu<James.Zhu@amd.com>
>>>>>> Reviewed-by: Bing Ma<bing.ma@amd.com>
>>>>>
>>>>> Hi,
>>>>>
>>>>> Can you guys please clarify why this is necessary and what the use
>>>>> case is?
>>>>>
>>>>> Are we sure we want a uAPI for this? RADV has been using SPM from
>>>>> userspace for many years now.
>>>>
>>>> [JZ] current RADV SPM is for graphic engine. These patches are for
>>>> compute engine SPM.
>>>>
>>>> Thanks!
>>>
>>> Is there any reason why it can't be done in userspace for compute too?
>>
>> There's only one SPM so there needs to be arbitration to avoid
>> multiple applications trying to use it at the same time.  It also only
>> works on a single vmid at a time so the application using it has to
>> request a fixed vmid.
> 
> Yeah, we already have an API for reserving a VMID for SPM.
> 
> We also have a TODO item to make that available for user queues which then both ROCm and gfx can use.
> 
> So as far as I can see that work here is completely unnecessary.
> 
> Christian.
> 
>>
>> Alex
>>
>>>
>>>
>>>>>
>>>>>> ---
>>>>>>
>>>>>>   include/uapi/drm/amdgpu_drm.h | 124 ++++++++++++++++++++++++++++++++++
>>>>>>   1 file changed, 124 insertions(+)
>>>>>>
>>>>>> diff --git a/include/uapi/drm/amdgpu_drm.h
>>>>>> b/include/uapi/drm/amdgpu_drm.h index 9e5fa6f53e59..285949d63eb6
>>>>>> 100644
>>>>>> --- a/include/uapi/drm/amdgpu_drm.h
>>>>>> +++ b/include/uapi/drm/amdgpu_drm.h
>>>>>> @@ -60,6 +60,7 @@ extern "C" {
>>>>>>
>>>>>>   #define DRM_AMDGPU_GEM_LIST_HANDLES      0x19
>>>>>>   #define DRM_AMDGPU_PROC_OPTIONS          0x1A
>>>>>>   #define DRM_AMDGPU_TRAP                  0x1B
>>>>>>
>>>>>> +#define DRM_AMDGPU_PROFILER               0x1C
>>>>>>
>>>>>>   #define DRM_IOCTL_AMDGPU_GEM_CREATE      DRM_IOWR(DRM_COMMAND_BASE +
>>>>>>
>>>>>> DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) #define
>>>>>> DRM_IOCTL_AMDGPU_GEM_MMAP  DRM_IOWR(DRM_COMMAND_BASE +
>>>>>> DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap) @@ -83,6 +84,7 @@
>>>>>> extern "C" {
>>>>>>
>>>>>>   #define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES
>>>>>> DRM_IOWR(DRM_COMMAND_BASE +
>>>>>>
>>>>>> DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles) #define
>>>>>> DRM_IOCTL_AMDGPU_PROC_OPTIONS      DRM_IOWR(DRM_COMMAND_BASE +
>>>>>> DRM_AMDGPU_PROC_OPTIONS, struct drm_amdgpu_proc_options) #define
>>>>>> DRM_IOCTL_AMDGPU_TRAP      DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_TRAP,
>>>>>
>>>>> union
>>>>>
>>>>>> drm_amdgpu_trap) +#define
>>>>>> DRM_IOCTL_AMDGPU_PROFILER  DRM_IOWR(DRM_COMMAND_BASE +
>>>>>
>>>>> DRM_AMDGPU_PROFILER,
>>>>>
>>>>>> struct drm_amdgpu_profiler_args)
>>>>>>
>>>>>>   /**
>>>>>>
>>>>>>    * DOC: memory domains
>>>>>>
>>>>>> @@ -1821,6 +1823,128 @@ struct drm_amdgpu_proc_options {
>>>>>>
>>>>>>    } kfd_sigbus_delay;
>>>>>>
>>>>>>   };
>>>>>>
>>>>>> +/*
>>>>>> + * Supported SPM (Stream Performance Monitor) Operations  */
>>>>>> +/**
>>>>>> + * drm_amdgpu_spm_op - SPM ioctl operations  */ enum
>>>>>> +drm_amdgpu_spm_op {
>>>>>> +  AMDGPU_SPM_OP_ACQUIRE = 1,
>>>>>> +  AMDGPU_SPM_OP_RELEASE,
>>>>>> +  AMDGPU_SPM_OP_SET_DEST_BUF
>>>>>> +};
>>>>>> +
>>>>>> +/**
>>>>>> + * drm_amdgpu_spm_args - Arguments for SPM ioctl
>>>>>> + *
>>>>>> + * @dest_buf: [in] used for the address of the destination buffer
>>>>>> + *            in @AMDGPU_SPM_OP_SET_DEST_BUF
>>>>>> + * @buf_size: [in] size of the destination buffer
>>>>>> + * @op: [in] specifies the operation to perform
>>>>>> + * @timeout: [in/out] timeout in milliseconds (in), amount of time left
>>>>>> + *           in the timeout window (out)
>>>>>> + * @bytes_copied: [out] total amount of data that was copied to
>>>>>> +the
>>>>>> previous dest_buf + * @has_data_loss: [out] non-zero if data was
>>>>>> lost (e.g.
>>>>>> due to ring buffer overflow), + *                 zero otherwise
>>>>>> + * @reserved: must be zero for forward compatibility
>>>>>> + *
>>>>>> + * This ioctl performs different functions depending on the @op
>>>>>> parameter.
>>>>>> + *
>>>>>> + * AMDGPU_SPM_OP_ACQUIRE
>>>>>> + * ------------------------
>>>>>> + *
>>>>>> + * Acquires exclusive access of SPM on the specified XCP for the
>>>>>> + calling
>>>>>> process. + * This must be called before using AMDGPU_SPM_OP_SET_DEST_BUF.
>>>>>> + *
>>>>>> + * AMDGPU_SPM_OP_RELEASE
>>>>>> + * ------------------------
>>>>>> + *
>>>>>> + * Releases exclusive access of SPM on the specified XCP for the
>>>>>> + calling
>>>>>> process, + * which allows another process to acquire it in the future.
>>>>>> + *
>>>>>> + * AMDGPU_SPM_OP_SET_DEST_BUF
>>>>>> + * -----------------------------
>>>>>> + *
>>>>>> + * If @dest_buf is NULL, the destination buffer address is unset
>>>>>> + and
>>>>>> copying of counters + * is stopped.
>>>>>> + *
>>>>>> + * If @dest_buf is not NULL, it specifies the pointer to a new
>>>>>> destination
>>>>>> buffer. + * @buf_size specifies the size of the buffer.
>>>>>> + *
>>>>>> + * If @timeout is non-0, the call will wait for up to @timeout ms
>>>>>> + for
>>>>>> the
>>>>>> previous + * buffer to be filled. If the previous buffer is filled
>>>>>> before the timeout, @timeout + * is updated with the time
>>>>>> remaining. If the timeout is exceeded, the function + * copies any
>>>>>> partial data available into the previous user buffer and returns
>>>>>> success. + * The amount of valid data in the previous user buffer
>>>>>> is indicated by @bytes_copied. + *
>>>>>> + * If @timeout is 0, the function immediately replaces the
>>>>>> + previous
>>>>>> destination buffer + * without waiting for the previous buffer to
>>>>>> be filled. That means the previous buffer + * may only be
>>>>>> partially filled, and @bytes_copied will indicate how much data has been + * copied to it.
>>>>>> + *
>>>>>> + * If data was lost, e.g. due to a ring buffer overflow,
>>>>>> + @has_data_loss
>>>>>> will be non-0. + *
>>>>>> + * Returns negative error code on failure, 0 on success.
>>>>>> + */
>>>>>> +struct drm_amdgpu_spm_args {
>>>>>> +  __u64 dest_buf;
>>>>>> +  __u32 buf_size;
>>>>>> +  __u32 op;
>>>>>> +  __u32 timeout;
>>>>>> +  __u32 bytes_copied;
>>>>>> +  __u32 has_data_loss;
>>>>>> +  __u32 reserved[9];      /* must be zero; pads this struct to 64
>>>>>
>>>>> bytes */
>>>>>
>>>>>> +};
>>>>>> +
>>>>>> +/**
>>>>>> + * drm_amdgpu_spm_buffer_header - SPM Buffer header for
>>>>>> drm_amdgpu_spm_args->dest_buf + *
>>>>>> + * @version:       [out] SPM version (0-23: minor, 24-31: major)
>>>>>> + * @bytes_copied:  [out] Amount of data copied for this sub-block
>>>>>> + * @has_data_loss: [out] Boolean indicating whether data was lost
>>>>>> + for
>>>>>> this
>>>>>> sub-block + *                       (e.g. due to a ring-buffer overflow)
>>>>>> + * @reserved:      Reserved for future use, must be zero
>>>>>> + *
>>>>>> + * This header precedes the SPM data in the destination buffer.
>>>>>> + The
>>>>>> reserved + * field maintains 64-byte cache line alignment (32
>>>>>> bytes total).
>>>>>> + */
>>>>>> +struct drm_amdgpu_spm_buffer_header {
>>>>>> +  __u32 version; /* 0-23: minor, 24-31: major */
>>>>>> +  __u32 bytes_copied;
>>>>>> +  __u32 has_data_loss;
>>>>>> +  __u32 reserved[5]; /* pad to 32 bytes (64-byte cache line / 2)
>>>>>> +*/ };
>>>>>> +
>>>>>> +/* Supported Profiler Operation */
>>>>>> +#define AMDGPU_PROFILER_OP_GET_VER                1 /* get profiler UAPI
>>>>>
>>>>> version */
>>>>>
>>>>>> +#define AMDGPU_PROFILER_OP_SPM                    2
>>>>>> +
>>>>>> +/*
>>>>>> + * Profiler IOCTL
>>>>>> + */
>>>>>> +/**
>>>>>> + * struct drm_amdgpu_profiler_args - Profiler IOCTL arguments
>>>>>> + * @op: Operation to perform (AMDGPU_PROFILER_OP_*)
>>>>>> + * @pad: Must be zero for input validation
>>>>>> + * @data: Operation-specific parameters (union)
>>>>>> + *
>>>>>> + * GET_VER operation:
>>>>>> + * Returns the profiler UAPI version in @data.version as
>>>>>> +(MAJOR<<24) |
>>>>>> MINOR. + * Input: @data must be zero (reserved for forward
>>>>>> compatibility).
>>>>>> + *
>>>>>> + * Design note: GET_VER is intentionally a parameter-free query
>>>>>> returning
>>>>>> + * the global profiler UAPI version.
>>>>>> + */
>>>>>> +struct drm_amdgpu_profiler_args {
>>>>>> +  __u32 op;                               /*
>>>>>
>>>>> AMDGPU_PROFILER_OP_* */
>>>>>
>>>>>> +  __u32 pad;                              /* must be zero */
>>>>>> +  union {
>>>>>> +          __u32 version;          /* GET_VER: out
>>>>>
>>>>> (MAJOR<<24) | MINOR */
>>>>>
>>>>>> +          struct drm_amdgpu_spm_args spm;
>>>>>> +  } data;
>>>>>> +};
>>>>>> +
>>>>>>
>>>>>>   #if defined(__cplusplus)
>>>>>>   }
>>>>>>   #endif
>>>
>>>
>>>
>>>
> 


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 04/12] drm/amdgpu: add amdgpu_xcp_get_xcc_mask helper
  2026-09-23  3:39               ` Lazar, Lijo
@ 2026-09-23 12:36                 ` James Zhu
  2026-09-23 14:55                   ` Lazar, Lijo
  0 siblings, 1 reply; 36+ messages in thread
From: James Zhu @ 2026-09-23 12:36 UTC (permalink / raw)
  To: Lazar, Lijo, James Zhu, amd-gfx, alexander.deucher, Bing.Ma


On 2026-09-22 23:39, Lazar, Lijo wrote:
>
>
> On 22-Sep-26 8:40 PM, James Zhu wrote:
>>
>> On 2026-09-22 10:26, Lazar, Lijo wrote:
>>>
>>>
>>> On 22-Sep-26 7:41 PM, James Zhu wrote:
>>>>
>>>> On 2026-09-22 09:45, Lazar, Lijo wrote:
>>>>>
>>>>>
>>>>> On 22-Sep-26 7:05 PM, James Zhu wrote:
>>>>>>
>>>>>> On 2026-09-22 01:22, Lazar, Lijo wrote:
>>>>>>>
>>>>>>>
>>>>>>> On 22-Sep-26 1:33 AM, James Zhu wrote:
>>>>>>>> Add amdgpu_xcp_get_xcc_mask() to return the XCC instance mask 
>>>>>>>> for a given
>>>>>>>> XCP partition. This is needed by upcoming SPM (Stream 
>>>>>>>> Performance Monitor)
>>>>>>>> support, which programs hardware per partition and must operate 
>>>>>>>> only on
>>>>>>>> the XCCs that belong to a given XCP.
>>>>>>>>
>>>>>>>> For partitioned systems the helper returns only the XCCs 
>>>>>>>> belonging to the
>>>>>>>> specified XCP. When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the 
>>>>>>>> device is
>>>>>>>> not partitioned) it returns the device's full XCC mask (e.g. 
>>>>>>>> 0xFF for an
>>>>>>>> 8-XCC device). Note that the full device mask is returned even 
>>>>>>>> on a
>>>>>>>> partitioned device; callers that require partition isolation 
>>>>>>>> (e.g. SPM)
>>>>>>>> must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>>>>>>>> themselves.
>>>>>>>>
>>>>>>>
>>>>>>> Could you clarifiy on this part - xcp_id = 
>>>>>>> AMDGPU_XCP_NO_PARTITION usage on 8-XCC device?
>>>>>>>
>>>>>>> The default state on a multi-XCC device after driver load is all 
>>>>>>> XCCs in a single partition called SPX. It should have at least 
>>>>>>> one partition valid.
>>>>>>
>>>>>> [JZ] Yes, 8-XCC device should not reach AMDGPU_XCP_NO_PARTITION 
>>>>>> case.
>>>>>>
>>>>>> the if(!xcp_mgr ||xcp_id ==AMDGPU_XCP_NO_PARTITION) mainly covers 
>>>>>> non partition device, I can remove the above comments which don't 
>>>>>> reflect the source accurately.
>>>>>
>>>>> Then I'm not sure what issue this is fixing.
>>>>>
>>>>> Multi-xcc devices will be in SPX mode and will return a mask with 
>>>>> all the XCCs. Non-multi xcc devices are expected to have this set 
>>>>> to 1 anyway to reflect that a single GC is present always.
>>>> [JZ] Just realized a Fixes tag is added. I think somehow AI review 
>>>> added it, and I did not check properly, is should be removed also. 
>>>> Sorry for this confusing!
>>>
>>> I see that it also added an external function like this -
>>>
>>> amdgpu_xcp_get_xcc_mask
>>>
>>> The expectation is any IP instance mask within a partition needs to 
>>> obtained through - amdgpu_xcp_get_inst_details
>>>
>>> Also, amdgpu_xcp_get_xcc_mask provides this which is not correct -
>>>
>>> *xcc_mask = adev->gfx.xcc_mask
>>>
>>> xcp instance mask operates only return logical mask of instances 
>>> present (xcc_mask could include disabled ones also).
>> [JZ] Here is only for non-partition device. I think 
>> adev->gfx.xcc_mask=0x1.
>> I can change it to below code to clear the confusing
>>
>> *xcc_mask = BIT(0);
>>
>> How Do you think?
>> Thanks!
>>
>
> Once that is done, it's the same as what exists now (without the extra 
> function).
>
> Also, why do you need get_xcc_mask function? If the difficulty is in 
> getting amdgpu_xcp* pointer from xcp_id, then a wrapper could be added 
> and used like
>
> xcp = amdgpu_xcp_get_obj(adev, xcp_id);
> amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX, &xcc_mask);
[JZ] Basically, amdgpu_xcp_get_xcc_mask is the above two's combination.
>
> Thanks,
> Lijo
>
>> James
>>
>>>
>>> Thanks,
>>> Lijo
>>>
>>>>>
>>>>> Thanks,
>>>>> Lijo
>>>>>
>>>>>>> Thanks,
>>>>>>> Lioj
>>>>>>>
>>>>>>>> amdgpu_xcp_get_num_xcc() is reworked to take (adev, xcp_id) and 
>>>>>>>> derive the
>>>>>>>> count from this helper via hweight32(). The helper is defined 
>>>>>>>> out of line
>>>>>>>> in amdgpu_xcp.c so that the static inline in amdgpu_xcp.h never 
>>>>>>>> has to
>>>>>>>> dereference the (incomplete) struct amdgpu_device; it only 
>>>>>>>> passes the
>>>>>>>> pointer through.
>>>>>>>>
>>>>>>>> This is a user-visible change: amdgpu_xcp_get_num_xcc() previously
>>>>>>>> returned 1 for non-partitioned systems (and for 
>>>>>>>> AMDGPU_XCP_NO_PARTITION),
>>>>>>>> and now returns the actual XCC count (e.g. 8 for an 8-XCC 
>>>>>>>> device). CWSR
>>>>>>>> callers (amdgpu_kms.c, amdgpu_userq.c, mes_userqueue.c) scale 
>>>>>>>> the per-XCC
>>>>>>>> control-stack and debug-memory sizes by num_xcc, so the 
>>>>>>>> uAPI-visible
>>>>>>>> ctl_stack_size and dbg_mem_size values grow accordingly to 
>>>>>>>> match the
>>>>>>>> actual hardware resource requirements, which were previously 
>>>>>>>> undersized.
>>>>>>>>
>>>>>>>> Fixes: 4cd417bfec49 ("drm/amdgpu: Add helper function to get 
>>>>>>>> xcc count")
>>>>>>>> Signed-off-by: James Zhu <James.Zhu@amd.com>
>>>>>>>> Reviewed-by: Bing Ma <bing.ma@amd.com>
>>>>>>>> ---
>>>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c    |  2 +-
>>>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c  |  2 +-
>>>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c    | 48 
>>>>>>>> +++++++++++++++ ++ +++++
>>>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h    | 32 +++++++--------
>>>>>>>>   drivers/gpu/drm/amd/amdgpu/mes_userqueue.c |  3 +-
>>>>>>>>   5 files changed, 66 insertions(+), 21 deletions(-)
>>>>>>>>
>>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/ 
>>>>>>>> gpu/ drm/amd/amdgpu/amdgpu_kms.c
>>>>>>>> index 57fa69c0382d..7782ccd4d28f 100644
>>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>>>>>>> @@ -1501,7 +1501,7 @@ int amdgpu_info_ioctl(struct drm_device 
>>>>>>>> *dev, void *data, struct drm_file *filp)
>>>>>>>>           fpriv = (struct amdgpu_fpriv *)filp->driver_priv;
>>>>>>>>           if (!amdgpu_trap_is_enabled(adev) || !fpriv->trap_obj)
>>>>>>>>               return -EOPNOTSUPP;
>>>>>>>> -        num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv- 
>>>>>>>> >xcp_id);
>>>>>>>> +        num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>>>>>           cwsr_info.ctl_stack_size =
>>>>>>>> adev->trap_info->cwsr.xcc_ctl_stack_sz * num_xcc;
>>>>>>>>           cwsr_info.dbg_mem_size =
>>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c 
>>>>>>>> b/drivers/ gpu/ drm/amd/amdgpu/amdgpu_userq.c
>>>>>>>> index db09e879b85e..d19af9cb6ee1 100644
>>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>>>>>>> @@ -258,7 +258,7 @@ int 
>>>>>>>> amdgpu_userq_input_trap_params_validate(struct 
>>>>>>>> amdgpu_usermode_queue *queue,
>>>>>>>>       int num_xcc;
>>>>>>>>       int r;
>>>>>>>>   -    num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv- 
>>>>>>>> >xcp_id);
>>>>>>>> +    num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>>>>>       r = amdgpu_trap_validate_params(queue->userq_mgr->adev, 
>>>>>>>> trap_params,
>>>>>>>>                       num_xcc);
>>>>>>>>       if (r)
>>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/ 
>>>>>>>> gpu/ drm/amd/amdgpu/amdgpu_xcp.c
>>>>>>>> index 2a56f5ec9c76..90846d55292b 100644
>>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>>>>>>> @@ -390,6 +390,54 @@ int amdgpu_xcp_get_inst_details(struct 
>>>>>>>> amdgpu_xcp *xcp,
>>>>>>>>       return 0;
>>>>>>>>   }
>>>>>>>>   +/**
>>>>>>>> + * amdgpu_xcp_get_xcc_mask - Get XCC instance mask for a 
>>>>>>>> partition
>>>>>>>> + * @adev: amdgpu device pointer
>>>>>>>> + * @xcp_id: XCP partition ID
>>>>>>>> + * @xcc_mask: output parameter for XCC instance mask
>>>>>>>> + *
>>>>>>>> + * Returns the XCC instance mask for the specified XCP partition.
>>>>>>>> + * For partitioned systems, returns only the XCCs belonging to 
>>>>>>>> this XCP.
>>>>>>>> + *
>>>>>>>> + * When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the device is not
>>>>>>>> + * partitioned), the device's full XCC mask is returned (e.g. 
>>>>>>>> 0xFF for an
>>>>>>>> + * 8-XCC device). Note that the full device mask is returned 
>>>>>>>> even on a
>>>>>>>> + * partitioned device: callers that require partition 
>>>>>>>> isolation (e.g. SPM)
>>>>>>>> + * must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>>>>>>>> themselves.
>>>>>>>> + *
>>>>>>>> + * Note: On error, xcc_mask is undefined. Callers must check 
>>>>>>>> return code
>>>>>>>> + * and only read xcc_mask when return == 0.
>>>>>>>> + *
>>>>>>>> + * Return: 0 on success (non-partitioned systems or valid XCP 
>>>>>>>> found),
>>>>>>>> + *         -EINVAL if xcc_mask is NULL, no matching XCP id is 
>>>>>>>> found, or the
>>>>>>>> + *         partition's GFX IP details are unavailable
>>>>>>>> + */
>>>>>>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int 
>>>>>>>> xcp_id,
>>>>>>>> +                uint32_t *xcc_mask)
>>>>>>>> +{
>>>>>>>> +    struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr;
>>>>>>>> +    struct amdgpu_xcp *xcp;
>>>>>>>> +    int i;
>>>>>>>> +
>>>>>>>> +    if (!xcc_mask)
>>>>>>>> +        return -EINVAL;
>>>>>>>> +
>>>>>>>> +    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION) {
>>>>>>>> +        /* Non-partitioned: return the device's full XCC mask. */
>>>>>>>> +        *xcc_mask = adev->gfx.xcc_mask;
>>>>>>>> +        return 0;
>>>>>>>> +    }
>>>>>>>> +
>>>>>>>> +    /* Partitioned system: get this XCP's XCCs */
>>>>>>>> +    for_each_xcp(xcp_mgr, xcp, i) {
>>>>>>>> +        if (xcp->id == xcp_id)
>>>>>>>> +            return amdgpu_xcp_get_inst_details(xcp, 
>>>>>>>> AMDGPU_XCP_GFX,
>>>>>>>> +                               xcc_mask);
>>>>>>>> +    }
>>>>>>>> +
>>>>>>>> +    return -EINVAL;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>>   int amdgpu_xcp_dev_register(struct amdgpu_device *adev,
>>>>>>>>               const struct pci_device_id *ent)
>>>>>>>>   {
>>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h b/drivers/ 
>>>>>>>> gpu/ drm/amd/amdgpu/amdgpu_xcp.h
>>>>>>>> index 692508be23fb..6eed82ad49b1 100644
>>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>>>>>>> @@ -44,6 +44,7 @@
>>>>>>>>   #define XCP_INST_MASK(num_inst, xcp_id)        \
>>>>>>>>       (num_inst ? GENMASK(num_inst - 1, 0) << (xcp_id * 
>>>>>>>> num_inst) : 0)
>>>>>>>>   +struct amdgpu_device;
>>>>>>>>   struct amdgpu_fpriv;
>>>>>>>>     enum AMDGPU_XCP_IP_BLOCK {
>>>>>>>> @@ -219,26 +220,23 @@ amdgpu_get_next_xcp(struct amdgpu_xcp_mgr 
>>>>>>>> *xcp_mgr, int *from)
>>>>>>>>       for (i = 0, xcp = amdgpu_get_next_xcp(xcp_mgr, &i); xcp; \
>>>>>>>>            ++i, xcp = amdgpu_get_next_xcp(xcp_mgr, &i))
>>>>>>>>   -static inline int amdgpu_xcp_get_num_xcc(struct 
>>>>>>>> amdgpu_xcp_mgr *xcp_mgr,
>>>>>>>> -                     int xcp_id)
>>>>>>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int 
>>>>>>>> xcp_id,
>>>>>>>> +                uint32_t *xcc_mask);
>>>>>>>> +
>>>>>>>> +static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_device 
>>>>>>>> *adev, int xcp_id)
>>>>>>>>   {
>>>>>>>> -    struct amdgpu_xcp *xcp;
>>>>>>>>       uint32_t xcc_mask;
>>>>>>>> -    int i, r;
>>>>>>>> -
>>>>>>>> -    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION)
>>>>>>>> +    int r, count;
>>>>>>>> +
>>>>>>>> +    /* Derive the XCC count from the partition's XCC mask (or 
>>>>>>>> the full
>>>>>>>> +     * device mask when not partitioned). 
>>>>>>>> amdgpu_xcp_get_xcc_mask()
>>>>>>>> +     * handles both partitioned and non-partitioned cases.
>>>>>>>> +     */
>>>>>>>> +    r = amdgpu_xcp_get_xcc_mask(adev, xcp_id, &xcc_mask);
>>>>>>>> +    if (unlikely(r))
>>>>>>>>           return 1;
>>>>>>>> -    for_each_xcp(xcp_mgr, xcp, i) {
>>>>>>>> -        if (xcp->id == xcp_id) {
>>>>>>>> -            r = amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
>>>>>>>> -                            &xcc_mask);
>>>>>>>> -            if (unlikely(r))
>>>>>>>> -                return 1;
>>>>>>>> -            else
>>>>>>>> -                return hweight32(xcc_mask);
>>>>>>>> -        }
>>>>>>>> -    }
>>>>>>>>   -    return 1;
>>>>>>>> +    count = hweight32(xcc_mask);
>>>>>>>> +    return count ? count : 1;
>>>>>>>>   }
>>>>>>>>   #endif
>>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/ 
>>>>>>>> drivers/ gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>>>>> index 16aa22b25098..1c7b2f3227e5 100644
>>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>>>>> @@ -503,8 +503,7 @@ static int mes_userq_mqd_create(struct 
>>>>>>>> amdgpu_usermode_queue *queue,
>>>>>>>> uq_mgr_to_fpriv(queue->userq_mgr);
>>>>>>>>               int num_xcc;
>>>>>>>>   -            num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr,
>>>>>>>> -                             fpriv->xcp_id);
>>>>>>>> +            num_xcc = amdgpu_xcp_get_num_xcc(adev, 
>>>>>>>> fpriv->xcp_id);
>>>>>>>>               trap_params.ctx_save_area_address =
>>>>>>>>                   compute_mqd->ctx_save_area_va;
>>>>>>>>               trap_params.cwsr_sz = 
>>>>>>>> compute_mqd->ctx_save_area_size;
>>>>>>>
>>>>>
>>>
>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 04/12] drm/amdgpu: add amdgpu_xcp_get_xcc_mask helper
  2026-09-23 12:36                 ` James Zhu
@ 2026-09-23 14:55                   ` Lazar, Lijo
  2026-09-23 15:08                     ` James Zhu
  0 siblings, 1 reply; 36+ messages in thread
From: Lazar, Lijo @ 2026-09-23 14:55 UTC (permalink / raw)
  To: James Zhu, James Zhu, amd-gfx, alexander.deucher, Bing.Ma



On 23-Sep-26 6:06 PM, James Zhu wrote:
> 
> On 2026-09-22 23:39, Lazar, Lijo wrote:
>>
>>
>> On 22-Sep-26 8:40 PM, James Zhu wrote:
>>>
>>> On 2026-09-22 10:26, Lazar, Lijo wrote:
>>>>
>>>>
>>>> On 22-Sep-26 7:41 PM, James Zhu wrote:
>>>>>
>>>>> On 2026-09-22 09:45, Lazar, Lijo wrote:
>>>>>>
>>>>>>
>>>>>> On 22-Sep-26 7:05 PM, James Zhu wrote:
>>>>>>>
>>>>>>> On 2026-09-22 01:22, Lazar, Lijo wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>> On 22-Sep-26 1:33 AM, James Zhu wrote:
>>>>>>>>> Add amdgpu_xcp_get_xcc_mask() to return the XCC instance mask 
>>>>>>>>> for a given
>>>>>>>>> XCP partition. This is needed by upcoming SPM (Stream 
>>>>>>>>> Performance Monitor)
>>>>>>>>> support, which programs hardware per partition and must operate 
>>>>>>>>> only on
>>>>>>>>> the XCCs that belong to a given XCP.
>>>>>>>>>
>>>>>>>>> For partitioned systems the helper returns only the XCCs 
>>>>>>>>> belonging to the
>>>>>>>>> specified XCP. When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the 
>>>>>>>>> device is
>>>>>>>>> not partitioned) it returns the device's full XCC mask (e.g. 
>>>>>>>>> 0xFF for an
>>>>>>>>> 8-XCC device). Note that the full device mask is returned even 
>>>>>>>>> on a
>>>>>>>>> partitioned device; callers that require partition isolation 
>>>>>>>>> (e.g. SPM)
>>>>>>>>> must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>>>>>>>>> themselves.
>>>>>>>>>
>>>>>>>>
>>>>>>>> Could you clarifiy on this part - xcp_id = 
>>>>>>>> AMDGPU_XCP_NO_PARTITION usage on 8-XCC device?
>>>>>>>>
>>>>>>>> The default state on a multi-XCC device after driver load is all 
>>>>>>>> XCCs in a single partition called SPX. It should have at least 
>>>>>>>> one partition valid.
>>>>>>>
>>>>>>> [JZ] Yes, 8-XCC device should not reach AMDGPU_XCP_NO_PARTITION 
>>>>>>> case.
>>>>>>>
>>>>>>> the if(!xcp_mgr ||xcp_id ==AMDGPU_XCP_NO_PARTITION) mainly covers 
>>>>>>> non partition device, I can remove the above comments which don't 
>>>>>>> reflect the source accurately.
>>>>>>
>>>>>> Then I'm not sure what issue this is fixing.
>>>>>>
>>>>>> Multi-xcc devices will be in SPX mode and will return a mask with 
>>>>>> all the XCCs. Non-multi xcc devices are expected to have this set 
>>>>>> to 1 anyway to reflect that a single GC is present always.
>>>>> [JZ] Just realized a Fixes tag is added. I think somehow AI review 
>>>>> added it, and I did not check properly, is should be removed also. 
>>>>> Sorry for this confusing!
>>>>
>>>> I see that it also added an external function like this -
>>>>
>>>> amdgpu_xcp_get_xcc_mask
>>>>
>>>> The expectation is any IP instance mask within a partition needs to 
>>>> obtained through - amdgpu_xcp_get_inst_details
>>>>
>>>> Also, amdgpu_xcp_get_xcc_mask provides this which is not correct -
>>>>
>>>> *xcc_mask = adev->gfx.xcc_mask
>>>>
>>>> xcp instance mask operates only return logical mask of instances 
>>>> present (xcc_mask could include disabled ones also).
>>> [JZ] Here is only for non-partition device. I think adev- 
>>> >gfx.xcc_mask=0x1.
>>> I can change it to below code to clear the confusing
>>>
>>> *xcc_mask = BIT(0);
>>>
>>> How Do you think?
>>> Thanks!
>>>
>>
>> Once that is done, it's the same as what exists now (without the extra 
>> function).
>>
>> Also, why do you need get_xcc_mask function? If the difficulty is in 
>> getting amdgpu_xcp* pointer from xcp_id, then a wrapper could be added 
>> and used like
>>
>> xcp = amdgpu_xcp_get_obj(adev, xcp_id);
>> amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX, &xcc_mask);
> [JZ] Basically, amdgpu_xcp_get_xcc_mask is the above two's combination.

Ok, then suggest to use the two-step route instead of adding specific 
wrapper per IP.

Adding the first one (get xcp from xcp_id) could be helpful for other 
scenarios also.

Thanks,
Lijo

>>
>> Thanks,
>> Lijo
>>
>>> James
>>>
>>>>
>>>> Thanks,
>>>> Lijo
>>>>
>>>>>>
>>>>>> Thanks,
>>>>>> Lijo
>>>>>>
>>>>>>>> Thanks,
>>>>>>>> Lioj
>>>>>>>>
>>>>>>>>> amdgpu_xcp_get_num_xcc() is reworked to take (adev, xcp_id) and 
>>>>>>>>> derive the
>>>>>>>>> count from this helper via hweight32(). The helper is defined 
>>>>>>>>> out of line
>>>>>>>>> in amdgpu_xcp.c so that the static inline in amdgpu_xcp.h never 
>>>>>>>>> has to
>>>>>>>>> dereference the (incomplete) struct amdgpu_device; it only 
>>>>>>>>> passes the
>>>>>>>>> pointer through.
>>>>>>>>>
>>>>>>>>> This is a user-visible change: amdgpu_xcp_get_num_xcc() previously
>>>>>>>>> returned 1 for non-partitioned systems (and for 
>>>>>>>>> AMDGPU_XCP_NO_PARTITION),
>>>>>>>>> and now returns the actual XCC count (e.g. 8 for an 8-XCC 
>>>>>>>>> device). CWSR
>>>>>>>>> callers (amdgpu_kms.c, amdgpu_userq.c, mes_userqueue.c) scale 
>>>>>>>>> the per-XCC
>>>>>>>>> control-stack and debug-memory sizes by num_xcc, so the uAPI- 
>>>>>>>>> visible
>>>>>>>>> ctl_stack_size and dbg_mem_size values grow accordingly to 
>>>>>>>>> match the
>>>>>>>>> actual hardware resource requirements, which were previously 
>>>>>>>>> undersized.
>>>>>>>>>
>>>>>>>>> Fixes: 4cd417bfec49 ("drm/amdgpu: Add helper function to get 
>>>>>>>>> xcc count")
>>>>>>>>> Signed-off-by: James Zhu <James.Zhu@amd.com>
>>>>>>>>> Reviewed-by: Bing Ma <bing.ma@amd.com>
>>>>>>>>> ---
>>>>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c    |  2 +-
>>>>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c  |  2 +-
>>>>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c    | 48 +++++++++++++ 
>>>>>>>>> ++ ++ +++++
>>>>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h    | 32 +++++++--------
>>>>>>>>>   drivers/gpu/drm/amd/amdgpu/mes_userqueue.c |  3 +-
>>>>>>>>>   5 files changed, 66 insertions(+), 21 deletions(-)
>>>>>>>>>
>>>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/ 
>>>>>>>>> gpu/ drm/amd/amdgpu/amdgpu_kms.c
>>>>>>>>> index 57fa69c0382d..7782ccd4d28f 100644
>>>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>>>>>>>> @@ -1501,7 +1501,7 @@ int amdgpu_info_ioctl(struct drm_device 
>>>>>>>>> *dev, void *data, struct drm_file *filp)
>>>>>>>>>           fpriv = (struct amdgpu_fpriv *)filp->driver_priv;
>>>>>>>>>           if (!amdgpu_trap_is_enabled(adev) || !fpriv->trap_obj)
>>>>>>>>>               return -EOPNOTSUPP;
>>>>>>>>> -        num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv- 
>>>>>>>>> >xcp_id);
>>>>>>>>> +        num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>>>>>>           cwsr_info.ctl_stack_size =
>>>>>>>>> adev->trap_info->cwsr.xcc_ctl_stack_sz * num_xcc;
>>>>>>>>>           cwsr_info.dbg_mem_size =
>>>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/ 
>>>>>>>>> drivers/ gpu/ drm/amd/amdgpu/amdgpu_userq.c
>>>>>>>>> index db09e879b85e..d19af9cb6ee1 100644
>>>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>>>>>>>> @@ -258,7 +258,7 @@ int 
>>>>>>>>> amdgpu_userq_input_trap_params_validate(struct 
>>>>>>>>> amdgpu_usermode_queue *queue,
>>>>>>>>>       int num_xcc;
>>>>>>>>>       int r;
>>>>>>>>>   -    num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv- 
>>>>>>>>> >xcp_id);
>>>>>>>>> +    num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>>>>>>       r = amdgpu_trap_validate_params(queue->userq_mgr->adev, 
>>>>>>>>> trap_params,
>>>>>>>>>                       num_xcc);
>>>>>>>>>       if (r)
>>>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/ 
>>>>>>>>> gpu/ drm/amd/amdgpu/amdgpu_xcp.c
>>>>>>>>> index 2a56f5ec9c76..90846d55292b 100644
>>>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>>>>>>>> @@ -390,6 +390,54 @@ int amdgpu_xcp_get_inst_details(struct 
>>>>>>>>> amdgpu_xcp *xcp,
>>>>>>>>>       return 0;
>>>>>>>>>   }
>>>>>>>>>   +/**
>>>>>>>>> + * amdgpu_xcp_get_xcc_mask - Get XCC instance mask for a 
>>>>>>>>> partition
>>>>>>>>> + * @adev: amdgpu device pointer
>>>>>>>>> + * @xcp_id: XCP partition ID
>>>>>>>>> + * @xcc_mask: output parameter for XCC instance mask
>>>>>>>>> + *
>>>>>>>>> + * Returns the XCC instance mask for the specified XCP partition.
>>>>>>>>> + * For partitioned systems, returns only the XCCs belonging to 
>>>>>>>>> this XCP.
>>>>>>>>> + *
>>>>>>>>> + * When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the device is not
>>>>>>>>> + * partitioned), the device's full XCC mask is returned (e.g. 
>>>>>>>>> 0xFF for an
>>>>>>>>> + * 8-XCC device). Note that the full device mask is returned 
>>>>>>>>> even on a
>>>>>>>>> + * partitioned device: callers that require partition 
>>>>>>>>> isolation (e.g. SPM)
>>>>>>>>> + * must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>>>>>>>>> themselves.
>>>>>>>>> + *
>>>>>>>>> + * Note: On error, xcc_mask is undefined. Callers must check 
>>>>>>>>> return code
>>>>>>>>> + * and only read xcc_mask when return == 0.
>>>>>>>>> + *
>>>>>>>>> + * Return: 0 on success (non-partitioned systems or valid XCP 
>>>>>>>>> found),
>>>>>>>>> + *         -EINVAL if xcc_mask is NULL, no matching XCP id is 
>>>>>>>>> found, or the
>>>>>>>>> + *         partition's GFX IP details are unavailable
>>>>>>>>> + */
>>>>>>>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int 
>>>>>>>>> xcp_id,
>>>>>>>>> +                uint32_t *xcc_mask)
>>>>>>>>> +{
>>>>>>>>> +    struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr;
>>>>>>>>> +    struct amdgpu_xcp *xcp;
>>>>>>>>> +    int i;
>>>>>>>>> +
>>>>>>>>> +    if (!xcc_mask)
>>>>>>>>> +        return -EINVAL;
>>>>>>>>> +
>>>>>>>>> +    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION) {
>>>>>>>>> +        /* Non-partitioned: return the device's full XCC mask. */
>>>>>>>>> +        *xcc_mask = adev->gfx.xcc_mask;
>>>>>>>>> +        return 0;
>>>>>>>>> +    }
>>>>>>>>> +
>>>>>>>>> +    /* Partitioned system: get this XCP's XCCs */
>>>>>>>>> +    for_each_xcp(xcp_mgr, xcp, i) {
>>>>>>>>> +        if (xcp->id == xcp_id)
>>>>>>>>> +            return amdgpu_xcp_get_inst_details(xcp, 
>>>>>>>>> AMDGPU_XCP_GFX,
>>>>>>>>> +                               xcc_mask);
>>>>>>>>> +    }
>>>>>>>>> +
>>>>>>>>> +    return -EINVAL;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>>   int amdgpu_xcp_dev_register(struct amdgpu_device *adev,
>>>>>>>>>               const struct pci_device_id *ent)
>>>>>>>>>   {
>>>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h b/drivers/ 
>>>>>>>>> gpu/ drm/amd/amdgpu/amdgpu_xcp.h
>>>>>>>>> index 692508be23fb..6eed82ad49b1 100644
>>>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>>>>>>>> @@ -44,6 +44,7 @@
>>>>>>>>>   #define XCP_INST_MASK(num_inst, xcp_id)        \
>>>>>>>>>       (num_inst ? GENMASK(num_inst - 1, 0) << (xcp_id * 
>>>>>>>>> num_inst) : 0)
>>>>>>>>>   +struct amdgpu_device;
>>>>>>>>>   struct amdgpu_fpriv;
>>>>>>>>>     enum AMDGPU_XCP_IP_BLOCK {
>>>>>>>>> @@ -219,26 +220,23 @@ amdgpu_get_next_xcp(struct amdgpu_xcp_mgr 
>>>>>>>>> *xcp_mgr, int *from)
>>>>>>>>>       for (i = 0, xcp = amdgpu_get_next_xcp(xcp_mgr, &i); xcp; \
>>>>>>>>>            ++i, xcp = amdgpu_get_next_xcp(xcp_mgr, &i))
>>>>>>>>>   -static inline int amdgpu_xcp_get_num_xcc(struct 
>>>>>>>>> amdgpu_xcp_mgr *xcp_mgr,
>>>>>>>>> -                     int xcp_id)
>>>>>>>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int 
>>>>>>>>> xcp_id,
>>>>>>>>> +                uint32_t *xcc_mask);
>>>>>>>>> +
>>>>>>>>> +static inline int amdgpu_xcp_get_num_xcc(struct amdgpu_device 
>>>>>>>>> *adev, int xcp_id)
>>>>>>>>>   {
>>>>>>>>> -    struct amdgpu_xcp *xcp;
>>>>>>>>>       uint32_t xcc_mask;
>>>>>>>>> -    int i, r;
>>>>>>>>> -
>>>>>>>>> -    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION)
>>>>>>>>> +    int r, count;
>>>>>>>>> +
>>>>>>>>> +    /* Derive the XCC count from the partition's XCC mask (or 
>>>>>>>>> the full
>>>>>>>>> +     * device mask when not partitioned). 
>>>>>>>>> amdgpu_xcp_get_xcc_mask()
>>>>>>>>> +     * handles both partitioned and non-partitioned cases.
>>>>>>>>> +     */
>>>>>>>>> +    r = amdgpu_xcp_get_xcc_mask(adev, xcp_id, &xcc_mask);
>>>>>>>>> +    if (unlikely(r))
>>>>>>>>>           return 1;
>>>>>>>>> -    for_each_xcp(xcp_mgr, xcp, i) {
>>>>>>>>> -        if (xcp->id == xcp_id) {
>>>>>>>>> -            r = amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX,
>>>>>>>>> -                            &xcc_mask);
>>>>>>>>> -            if (unlikely(r))
>>>>>>>>> -                return 1;
>>>>>>>>> -            else
>>>>>>>>> -                return hweight32(xcc_mask);
>>>>>>>>> -        }
>>>>>>>>> -    }
>>>>>>>>>   -    return 1;
>>>>>>>>> +    count = hweight32(xcc_mask);
>>>>>>>>> +    return count ? count : 1;
>>>>>>>>>   }
>>>>>>>>>   #endif
>>>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/ 
>>>>>>>>> drivers/ gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>>>>>> index 16aa22b25098..1c7b2f3227e5 100644
>>>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>>>>>> @@ -503,8 +503,7 @@ static int mes_userq_mqd_create(struct 
>>>>>>>>> amdgpu_usermode_queue *queue,
>>>>>>>>> uq_mgr_to_fpriv(queue->userq_mgr);
>>>>>>>>>               int num_xcc;
>>>>>>>>>   -            num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr,
>>>>>>>>> -                             fpriv->xcp_id);
>>>>>>>>> +            num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv- 
>>>>>>>>> >xcp_id);
>>>>>>>>>               trap_params.ctx_save_area_address =
>>>>>>>>>                   compute_mqd->ctx_save_area_va;
>>>>>>>>>               trap_params.cwsr_sz = compute_mqd- 
>>>>>>>>> >ctx_save_area_size;
>>>>>>>>
>>>>>>
>>>>
>>


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 04/12] drm/amdgpu: add amdgpu_xcp_get_xcc_mask helper
  2026-09-23 14:55                   ` Lazar, Lijo
@ 2026-09-23 15:08                     ` James Zhu
  0 siblings, 0 replies; 36+ messages in thread
From: James Zhu @ 2026-09-23 15:08 UTC (permalink / raw)
  To: Lazar, Lijo, James Zhu, amd-gfx, alexander.deucher, Bing.Ma


On 2026-09-23 10:55, Lazar, Lijo wrote:
>
>
> On 23-Sep-26 6:06 PM, James Zhu wrote:
>>
>> On 2026-09-22 23:39, Lazar, Lijo wrote:
>>>
>>>
>>> On 22-Sep-26 8:40 PM, James Zhu wrote:
>>>>
>>>> On 2026-09-22 10:26, Lazar, Lijo wrote:
>>>>>
>>>>>
>>>>> On 22-Sep-26 7:41 PM, James Zhu wrote:
>>>>>>
>>>>>> On 2026-09-22 09:45, Lazar, Lijo wrote:
>>>>>>>
>>>>>>>
>>>>>>> On 22-Sep-26 7:05 PM, James Zhu wrote:
>>>>>>>>
>>>>>>>> On 2026-09-22 01:22, Lazar, Lijo wrote:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On 22-Sep-26 1:33 AM, James Zhu wrote:
>>>>>>>>>> Add amdgpu_xcp_get_xcc_mask() to return the XCC instance mask 
>>>>>>>>>> for a given
>>>>>>>>>> XCP partition. This is needed by upcoming SPM (Stream 
>>>>>>>>>> Performance Monitor)
>>>>>>>>>> support, which programs hardware per partition and must 
>>>>>>>>>> operate only on
>>>>>>>>>> the XCCs that belong to a given XCP.
>>>>>>>>>>
>>>>>>>>>> For partitioned systems the helper returns only the XCCs 
>>>>>>>>>> belonging to the
>>>>>>>>>> specified XCP. When @xcp_id is AMDGPU_XCP_NO_PARTITION (or 
>>>>>>>>>> the device is
>>>>>>>>>> not partitioned) it returns the device's full XCC mask (e.g. 
>>>>>>>>>> 0xFF for an
>>>>>>>>>> 8-XCC device). Note that the full device mask is returned 
>>>>>>>>>> even on a
>>>>>>>>>> partitioned device; callers that require partition isolation 
>>>>>>>>>> (e.g. SPM)
>>>>>>>>>> must reject AMDGPU_XCP_NO_PARTITION on a partitioned device 
>>>>>>>>>> themselves.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Could you clarifiy on this part - xcp_id = 
>>>>>>>>> AMDGPU_XCP_NO_PARTITION usage on 8-XCC device?
>>>>>>>>>
>>>>>>>>> The default state on a multi-XCC device after driver load is 
>>>>>>>>> all XCCs in a single partition called SPX. It should have at 
>>>>>>>>> least one partition valid.
>>>>>>>>
>>>>>>>> [JZ] Yes, 8-XCC device should not reach AMDGPU_XCP_NO_PARTITION 
>>>>>>>> case.
>>>>>>>>
>>>>>>>> the if(!xcp_mgr ||xcp_id ==AMDGPU_XCP_NO_PARTITION) mainly 
>>>>>>>> covers non partition device, I can remove the above comments 
>>>>>>>> which don't reflect the source accurately.
>>>>>>>
>>>>>>> Then I'm not sure what issue this is fixing.
>>>>>>>
>>>>>>> Multi-xcc devices will be in SPX mode and will return a mask 
>>>>>>> with all the XCCs. Non-multi xcc devices are expected to have 
>>>>>>> this set to 1 anyway to reflect that a single GC is present always.
>>>>>> [JZ] Just realized a Fixes tag is added. I think somehow AI 
>>>>>> review added it, and I did not check properly, is should be 
>>>>>> removed also. Sorry for this confusing!
>>>>>
>>>>> I see that it also added an external function like this -
>>>>>
>>>>> amdgpu_xcp_get_xcc_mask
>>>>>
>>>>> The expectation is any IP instance mask within a partition needs 
>>>>> to obtained through - amdgpu_xcp_get_inst_details
>>>>>
>>>>> Also, amdgpu_xcp_get_xcc_mask provides this which is not correct -
>>>>>
>>>>> *xcc_mask = adev->gfx.xcc_mask
>>>>>
>>>>> xcp instance mask operates only return logical mask of instances 
>>>>> present (xcc_mask could include disabled ones also).
>>>> [JZ] Here is only for non-partition device. I think adev- 
>>>> >gfx.xcc_mask=0x1.
>>>> I can change it to below code to clear the confusing
>>>>
>>>> *xcc_mask = BIT(0);
>>>>
>>>> How Do you think?
>>>> Thanks!
>>>>
>>>
>>> Once that is done, it's the same as what exists now (without the 
>>> extra function).
>>>
>>> Also, why do you need get_xcc_mask function? If the difficulty is in 
>>> getting amdgpu_xcp* pointer from xcp_id, then a wrapper could be 
>>> added and used like
>>>
>>> xcp = amdgpu_xcp_get_obj(adev, xcp_id);
>>> amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX, &xcc_mask);
>> [JZ] Basically, amdgpu_xcp_get_xcc_mask is the above two's combination.
>
> Ok, then suggest to use the two-step route instead of adding specific 
> wrapper per IP.
>
> Adding the first one (get xcp from xcp_id) could be helpful for other 
> scenarios also.
[JZ] yeah, but this is just for discussion. We will drop these SPM 
implementation.
>
> Thanks,
> Lijo
>
>>>
>>> Thanks,
>>> Lijo
>>>
>>>> James
>>>>
>>>>>
>>>>> Thanks,
>>>>> Lijo
>>>>>
>>>>>>>
>>>>>>> Thanks,
>>>>>>> Lijo
>>>>>>>
>>>>>>>>> Thanks,
>>>>>>>>> Lioj
>>>>>>>>>
>>>>>>>>>> amdgpu_xcp_get_num_xcc() is reworked to take (adev, xcp_id) 
>>>>>>>>>> and derive the
>>>>>>>>>> count from this helper via hweight32(). The helper is defined 
>>>>>>>>>> out of line
>>>>>>>>>> in amdgpu_xcp.c so that the static inline in amdgpu_xcp.h 
>>>>>>>>>> never has to
>>>>>>>>>> dereference the (incomplete) struct amdgpu_device; it only 
>>>>>>>>>> passes the
>>>>>>>>>> pointer through.
>>>>>>>>>>
>>>>>>>>>> This is a user-visible change: amdgpu_xcp_get_num_xcc() 
>>>>>>>>>> previously
>>>>>>>>>> returned 1 for non-partitioned systems (and for 
>>>>>>>>>> AMDGPU_XCP_NO_PARTITION),
>>>>>>>>>> and now returns the actual XCC count (e.g. 8 for an 8-XCC 
>>>>>>>>>> device). CWSR
>>>>>>>>>> callers (amdgpu_kms.c, amdgpu_userq.c, mes_userqueue.c) scale 
>>>>>>>>>> the per-XCC
>>>>>>>>>> control-stack and debug-memory sizes by num_xcc, so the uAPI- 
>>>>>>>>>> visible
>>>>>>>>>> ctl_stack_size and dbg_mem_size values grow accordingly to 
>>>>>>>>>> match the
>>>>>>>>>> actual hardware resource requirements, which were previously 
>>>>>>>>>> undersized.
>>>>>>>>>>
>>>>>>>>>> Fixes: 4cd417bfec49 ("drm/amdgpu: Add helper function to get 
>>>>>>>>>> xcc count")
>>>>>>>>>> Signed-off-by: James Zhu <James.Zhu@amd.com>
>>>>>>>>>> Reviewed-by: Bing Ma <bing.ma@amd.com>
>>>>>>>>>> ---
>>>>>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c    | 2 +-
>>>>>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c  | 2 +-
>>>>>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c    | 48 
>>>>>>>>>> +++++++++++++ ++ ++ +++++
>>>>>>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h    | 32 
>>>>>>>>>> +++++++--------
>>>>>>>>>>   drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 3 +-
>>>>>>>>>>   5 files changed, 66 insertions(+), 21 deletions(-)
>>>>>>>>>>
>>>>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c 
>>>>>>>>>> b/drivers/ gpu/ drm/amd/amdgpu/amdgpu_kms.c
>>>>>>>>>> index 57fa69c0382d..7782ccd4d28f 100644
>>>>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>>>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
>>>>>>>>>> @@ -1501,7 +1501,7 @@ int amdgpu_info_ioctl(struct drm_device 
>>>>>>>>>> *dev, void *data, struct drm_file *filp)
>>>>>>>>>>           fpriv = (struct amdgpu_fpriv *)filp->driver_priv;
>>>>>>>>>>           if (!amdgpu_trap_is_enabled(adev) || !fpriv->trap_obj)
>>>>>>>>>>               return -EOPNOTSUPP;
>>>>>>>>>> -        num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, 
>>>>>>>>>> fpriv- >xcp_id);
>>>>>>>>>> +        num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>>>>>>>           cwsr_info.ctl_stack_size =
>>>>>>>>>> adev->trap_info->cwsr.xcc_ctl_stack_sz * num_xcc;
>>>>>>>>>>           cwsr_info.dbg_mem_size =
>>>>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/ 
>>>>>>>>>> drivers/ gpu/ drm/amd/amdgpu/amdgpu_userq.c
>>>>>>>>>> index db09e879b85e..d19af9cb6ee1 100644
>>>>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>>>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
>>>>>>>>>> @@ -258,7 +258,7 @@ int 
>>>>>>>>>> amdgpu_userq_input_trap_params_validate(struct 
>>>>>>>>>> amdgpu_usermode_queue *queue,
>>>>>>>>>>       int num_xcc;
>>>>>>>>>>       int r;
>>>>>>>>>>   -    num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr, fpriv- 
>>>>>>>>>> >xcp_id);
>>>>>>>>>> +    num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv->xcp_id);
>>>>>>>>>>       r = amdgpu_trap_validate_params(queue->userq_mgr->adev, 
>>>>>>>>>> trap_params,
>>>>>>>>>>                       num_xcc);
>>>>>>>>>>       if (r)
>>>>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c 
>>>>>>>>>> b/drivers/ gpu/ drm/amd/amdgpu/amdgpu_xcp.c
>>>>>>>>>> index 2a56f5ec9c76..90846d55292b 100644
>>>>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>>>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
>>>>>>>>>> @@ -390,6 +390,54 @@ int amdgpu_xcp_get_inst_details(struct 
>>>>>>>>>> amdgpu_xcp *xcp,
>>>>>>>>>>       return 0;
>>>>>>>>>>   }
>>>>>>>>>>   +/**
>>>>>>>>>> + * amdgpu_xcp_get_xcc_mask - Get XCC instance mask for a 
>>>>>>>>>> partition
>>>>>>>>>> + * @adev: amdgpu device pointer
>>>>>>>>>> + * @xcp_id: XCP partition ID
>>>>>>>>>> + * @xcc_mask: output parameter for XCC instance mask
>>>>>>>>>> + *
>>>>>>>>>> + * Returns the XCC instance mask for the specified XCP 
>>>>>>>>>> partition.
>>>>>>>>>> + * For partitioned systems, returns only the XCCs belonging 
>>>>>>>>>> to this XCP.
>>>>>>>>>> + *
>>>>>>>>>> + * When @xcp_id is AMDGPU_XCP_NO_PARTITION (or the device is 
>>>>>>>>>> not
>>>>>>>>>> + * partitioned), the device's full XCC mask is returned 
>>>>>>>>>> (e.g. 0xFF for an
>>>>>>>>>> + * 8-XCC device). Note that the full device mask is returned 
>>>>>>>>>> even on a
>>>>>>>>>> + * partitioned device: callers that require partition 
>>>>>>>>>> isolation (e.g. SPM)
>>>>>>>>>> + * must reject AMDGPU_XCP_NO_PARTITION on a partitioned 
>>>>>>>>>> device themselves.
>>>>>>>>>> + *
>>>>>>>>>> + * Note: On error, xcc_mask is undefined. Callers must check 
>>>>>>>>>> return code
>>>>>>>>>> + * and only read xcc_mask when return == 0.
>>>>>>>>>> + *
>>>>>>>>>> + * Return: 0 on success (non-partitioned systems or valid 
>>>>>>>>>> XCP found),
>>>>>>>>>> + *         -EINVAL if xcc_mask is NULL, no matching XCP id 
>>>>>>>>>> is found, or the
>>>>>>>>>> + *         partition's GFX IP details are unavailable
>>>>>>>>>> + */
>>>>>>>>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int 
>>>>>>>>>> xcp_id,
>>>>>>>>>> +                uint32_t *xcc_mask)
>>>>>>>>>> +{
>>>>>>>>>> +    struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr;
>>>>>>>>>> +    struct amdgpu_xcp *xcp;
>>>>>>>>>> +    int i;
>>>>>>>>>> +
>>>>>>>>>> +    if (!xcc_mask)
>>>>>>>>>> +        return -EINVAL;
>>>>>>>>>> +
>>>>>>>>>> +    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION) {
>>>>>>>>>> +        /* Non-partitioned: return the device's full XCC 
>>>>>>>>>> mask. */
>>>>>>>>>> +        *xcc_mask = adev->gfx.xcc_mask;
>>>>>>>>>> +        return 0;
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    /* Partitioned system: get this XCP's XCCs */
>>>>>>>>>> +    for_each_xcp(xcp_mgr, xcp, i) {
>>>>>>>>>> +        if (xcp->id == xcp_id)
>>>>>>>>>> +            return amdgpu_xcp_get_inst_details(xcp, 
>>>>>>>>>> AMDGPU_XCP_GFX,
>>>>>>>>>> +                               xcc_mask);
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    return -EINVAL;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>>   int amdgpu_xcp_dev_register(struct amdgpu_device *adev,
>>>>>>>>>>               const struct pci_device_id *ent)
>>>>>>>>>>   {
>>>>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h 
>>>>>>>>>> b/drivers/ gpu/ drm/amd/amdgpu/amdgpu_xcp.h
>>>>>>>>>> index 692508be23fb..6eed82ad49b1 100644
>>>>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>>>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h
>>>>>>>>>> @@ -44,6 +44,7 @@
>>>>>>>>>>   #define XCP_INST_MASK(num_inst, xcp_id) \
>>>>>>>>>>       (num_inst ? GENMASK(num_inst - 1, 0) << (xcp_id * 
>>>>>>>>>> num_inst) : 0)
>>>>>>>>>>   +struct amdgpu_device;
>>>>>>>>>>   struct amdgpu_fpriv;
>>>>>>>>>>     enum AMDGPU_XCP_IP_BLOCK {
>>>>>>>>>> @@ -219,26 +220,23 @@ amdgpu_get_next_xcp(struct 
>>>>>>>>>> amdgpu_xcp_mgr *xcp_mgr, int *from)
>>>>>>>>>>       for (i = 0, xcp = amdgpu_get_next_xcp(xcp_mgr, &i); xcp; \
>>>>>>>>>>            ++i, xcp = amdgpu_get_next_xcp(xcp_mgr, &i))
>>>>>>>>>>   -static inline int amdgpu_xcp_get_num_xcc(struct 
>>>>>>>>>> amdgpu_xcp_mgr *xcp_mgr,
>>>>>>>>>> -                     int xcp_id)
>>>>>>>>>> +int amdgpu_xcp_get_xcc_mask(struct amdgpu_device *adev, int 
>>>>>>>>>> xcp_id,
>>>>>>>>>> +                uint32_t *xcc_mask);
>>>>>>>>>> +
>>>>>>>>>> +static inline int amdgpu_xcp_get_num_xcc(struct 
>>>>>>>>>> amdgpu_device *adev, int xcp_id)
>>>>>>>>>>   {
>>>>>>>>>> -    struct amdgpu_xcp *xcp;
>>>>>>>>>>       uint32_t xcc_mask;
>>>>>>>>>> -    int i, r;
>>>>>>>>>> -
>>>>>>>>>> -    if (!xcp_mgr || xcp_id == AMDGPU_XCP_NO_PARTITION)
>>>>>>>>>> +    int r, count;
>>>>>>>>>> +
>>>>>>>>>> +    /* Derive the XCC count from the partition's XCC mask 
>>>>>>>>>> (or the full
>>>>>>>>>> +     * device mask when not partitioned). 
>>>>>>>>>> amdgpu_xcp_get_xcc_mask()
>>>>>>>>>> +     * handles both partitioned and non-partitioned cases.
>>>>>>>>>> +     */
>>>>>>>>>> +    r = amdgpu_xcp_get_xcc_mask(adev, xcp_id, &xcc_mask);
>>>>>>>>>> +    if (unlikely(r))
>>>>>>>>>>           return 1;
>>>>>>>>>> -    for_each_xcp(xcp_mgr, xcp, i) {
>>>>>>>>>> -        if (xcp->id == xcp_id) {
>>>>>>>>>> -            r = amdgpu_xcp_get_inst_details(xcp, 
>>>>>>>>>> AMDGPU_XCP_GFX,
>>>>>>>>>> -                            &xcc_mask);
>>>>>>>>>> -            if (unlikely(r))
>>>>>>>>>> -                return 1;
>>>>>>>>>> -            else
>>>>>>>>>> -                return hweight32(xcc_mask);
>>>>>>>>>> -        }
>>>>>>>>>> -    }
>>>>>>>>>>   -    return 1;
>>>>>>>>>> +    count = hweight32(xcc_mask);
>>>>>>>>>> +    return count ? count : 1;
>>>>>>>>>>   }
>>>>>>>>>>   #endif
>>>>>>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/ 
>>>>>>>>>> drivers/ gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>>>>>>> index 16aa22b25098..1c7b2f3227e5 100644
>>>>>>>>>> --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>>>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
>>>>>>>>>> @@ -503,8 +503,7 @@ static int mes_userq_mqd_create(struct 
>>>>>>>>>> amdgpu_usermode_queue *queue,
>>>>>>>>>> uq_mgr_to_fpriv(queue->userq_mgr);
>>>>>>>>>>               int num_xcc;
>>>>>>>>>>   -            num_xcc = amdgpu_xcp_get_num_xcc(adev->xcp_mgr,
>>>>>>>>>> -                             fpriv->xcp_id);
>>>>>>>>>> +            num_xcc = amdgpu_xcp_get_num_xcc(adev, fpriv- 
>>>>>>>>>> >xcp_id);
>>>>>>>>>>               trap_params.ctx_save_area_address =
>>>>>>>>>> compute_mqd->ctx_save_area_va;
>>>>>>>>>>               trap_params.cwsr_sz = compute_mqd- 
>>>>>>>>>> >ctx_save_area_size;
>>>>>>>>>
>>>>>>>
>>>>>
>>>
>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* RE: [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions
  2026-09-23  7:47             ` Christian König
@ 2026-09-24 12:33               ` Ma, Bing
  0 siblings, 0 replies; 36+ messages in thread
From: Ma, Bing @ 2026-09-24 12:33 UTC (permalink / raw)
  To: Koenig, Christian, Alex Deucher, Timur Kristóf
  Cc: amd-gfx@lists.freedesktop.org, Deucher,  Alexander, Zhu, James,
	Natalie Vock

AMD General

Hi Christian,

>> That will probably take a coulple of month if not years to be completed.
If so, ROCm SPM support will be impacted because ROCProfiler cannot use the Graphics path for the reasons I listed below. I am not the one who can decide to remove this support.

>> Since this here is clearly breaking the already existing SPM support for Graphics I have to fundamentally reject the ROCm SPM approach.
Other than the exclusive check (to make sure only one SPM support path, either rocr or pal, is active), the ROCm SPM code path (KFD + amdgpu_amdkfd) does not interfere with the existing SPM support for Graphics.

>> I'm not sure why the ROCm SPM patches were developed in the first place, with the approach taking in that patches here that feature is clearly not going to fly.
ROCm SPM support has existed for many years on DKMS, Felix has originally developed it along with ROCProfiler team. It is not a new patch. For the existing architecture we are using now, we use KFD ioctl instead of amdgpu ioctl. KGD support is through amdgpu_amdkfd interface. The feature is currently enabled and functional.

Best,

Bing

-----Original Message-----
From: Koenig, Christian <Christian.Koenig@amd.com>
Sent: Wednesday, September 23, 2026 12:48 AM
To: Ma, Bing <Bing.Ma@amd.com>; Alex Deucher <alexdeucher@gmail.com>; Timur Kristóf <timur.kristof@gmail.com>
Cc: amd-gfx@lists.freedesktop.org; Deucher, Alexander <Alexander.Deucher@amd.com>; Zhu, James <James.Zhu@amd.com>; Natalie Vock <nat@pixelcluster.dev>
Subject: Re: [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions

Hi Bing,

that won't work like this. The MES integration is part of the upcomming KFD/KGD unification.

That will probably take a coulple of month if not years to be completed.

Since this here is clearly breaking the already existing SPM support for Graphics I have to fundamentally reject the ROCm SPM approach.

I'm not sure why the ROCm SPM patches were developed in the first place, with the approach taking in that patches here that feature is clearly not going to fly.

I will sync up with Jenny and Alex on this.

Regards,
Christian.

On 9/23/26 03:20, Ma, Bing wrote:
> AMD General
>
> Hi Christian,
>
> Could you share a link to this TODO item?
>
> I'd like to verify whether this has been addressed through MES. ROCm uses the user-mode queue, and its VMID is assigned by MES rather than by the kernel driver. If MES support is available, ROCm could move SPM support to user mode.
>
> If this TODO has indeed been resolved through MES, we'll review the timeline for that MES support. Currently, ROCm SPM still goes through the kernel driver path, and we will need to keep using that path until the required MES support becomes available.
>
> Best,
>
> Bing
>
> -----Original Message-----
> From: Koenig, Christian <Christian.Koenig@amd.com>
> Sent: Tuesday, September 22, 2026 8:44 AM
> To: Alex Deucher <alexdeucher@gmail.com>; Timur Kristóf
> <timur.kristof@gmail.com>
> Cc: amd-gfx@lists.freedesktop.org; Deucher, Alexander
> <Alexander.Deucher@amd.com>; Ma, Bing <Bing.Ma@amd.com>; Zhu, James
> <James.Zhu@amd.com>; Zhu, James <James.Zhu@amd.com>; Natalie Vock
> <nat@pixelcluster.dev>
> Subject: Re: [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions
>
>
>
> On 9/22/26 17:40, Alex Deucher wrote:
>> On Tue, Sep 22, 2026 at 11:22 AM Timur Kristóf <timur.kristof@gmail.com> wrote:
>>>
>>> On Tuesday, September 22, 2026 5:20:41 PM Central European Summer
>>> Time James Zhu wrote:
>>>> On 2026-09-22 11:09, Timur Kristóf wrote:
>>>>> On Monday, September 21, 2026 10:03:02 PM Central European Summer
>>>>> Time
>>>>> James>
>>>>> Zhu wrote:
>>>>>> Add the DRM_AMDGPU_PROFILER ioctl number and its argument
>>>>>> structures, the UAPI for GPU performance monitoring features such
>>>>>> as SPM (Stream Performance Monitor).
>>>>>>
>>>>>> struct drm_amdgpu_profiler_args carries an operation selector and
>>>>>> a union of per-operation parameters, currently
>>>>>> AMDGPU_PROFILER_OP_GET_VER and AMDGPU_PROFILER_OP_SPM. The SPM
>>>>>> operations and the layout of the destination buffer are described
>>>>>> by struct drm_amdgpu_spm_args and struct drm_amdgpu_spm_buffer_header.
>>>>>>
>>>>>> The ioctl number and struct layout are frozen here. The
>>>>>> implementation lands in the rest of this series and the ioctl is
>>>>>> not registered in amdgpu_ioctls_kms[] until then, so this header
>>>>>> change alone does not make the interface reachable.
>>>>>>
>>>>>> Signed-off-by: James Zhu<James.Zhu@amd.com>
>>>>>> Reviewed-by: Bing Ma<bing.ma@amd.com>
>>>>>
>>>>> Hi,
>>>>>
>>>>> Can you guys please clarify why this is necessary and what the use
>>>>> case is?
>>>>>
>>>>> Are we sure we want a uAPI for this? RADV has been using SPM from
>>>>> userspace for many years now.
>>>>
>>>> [JZ] current RADV SPM is for graphic engine. These patches are for
>>>> compute engine SPM.
>>>>
>>>> Thanks!
>>>
>>> Is there any reason why it can't be done in userspace for compute too?
>>
>> There's only one SPM so there needs to be arbitration to avoid
>> multiple applications trying to use it at the same time.  It also
>> only works on a single vmid at a time so the application using it has
>> to request a fixed vmid.
>
> Yeah, we already have an API for reserving a VMID for SPM.
>
> We also have a TODO item to make that available for user queues which then both ROCm and gfx can use.
>
> So as far as I can see that work here is completely unnecessary.
>
> Christian.
>
>>
>> Alex
>>
>>>
>>>
>>>>>
>>>>>> ---
>>>>>>
>>>>>>   include/uapi/drm/amdgpu_drm.h | 124 ++++++++++++++++++++++++++++++++++
>>>>>>   1 file changed, 124 insertions(+)
>>>>>>
>>>>>> diff --git a/include/uapi/drm/amdgpu_drm.h
>>>>>> b/include/uapi/drm/amdgpu_drm.h index 9e5fa6f53e59..285949d63eb6
>>>>>> 100644
>>>>>> --- a/include/uapi/drm/amdgpu_drm.h
>>>>>> +++ b/include/uapi/drm/amdgpu_drm.h
>>>>>> @@ -60,6 +60,7 @@ extern "C" {
>>>>>>
>>>>>>   #define DRM_AMDGPU_GEM_LIST_HANDLES      0x19
>>>>>>   #define DRM_AMDGPU_PROC_OPTIONS          0x1A
>>>>>>   #define DRM_AMDGPU_TRAP                  0x1B
>>>>>>
>>>>>> +#define DRM_AMDGPU_PROFILER               0x1C
>>>>>>
>>>>>>   #define DRM_IOCTL_AMDGPU_GEM_CREATE      DRM_IOWR(DRM_COMMAND_BASE +
>>>>>>
>>>>>> DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) #define
>>>>>> DRM_IOCTL_AMDGPU_GEM_MMAP  DRM_IOWR(DRM_COMMAND_BASE +
>>>>>> DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap) @@ -83,6 +84,7 @@
>>>>>> extern "C" {
>>>>>>
>>>>>>   #define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES
>>>>>> DRM_IOWR(DRM_COMMAND_BASE +
>>>>>>
>>>>>> DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles) #define
>>>>>> DRM_IOCTL_AMDGPU_PROC_OPTIONS      DRM_IOWR(DRM_COMMAND_BASE +
>>>>>> DRM_AMDGPU_PROC_OPTIONS, struct drm_amdgpu_proc_options) #define
>>>>>> DRM_IOCTL_AMDGPU_TRAP      DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_TRAP,
>>>>>
>>>>> union
>>>>>
>>>>>> drm_amdgpu_trap) +#define
>>>>>> DRM_IOCTL_AMDGPU_PROFILER  DRM_IOWR(DRM_COMMAND_BASE +
>>>>>
>>>>> DRM_AMDGPU_PROFILER,
>>>>>
>>>>>> struct drm_amdgpu_profiler_args)
>>>>>>
>>>>>>   /**
>>>>>>
>>>>>>    * DOC: memory domains
>>>>>>
>>>>>> @@ -1821,6 +1823,128 @@ struct drm_amdgpu_proc_options {
>>>>>>
>>>>>>    } kfd_sigbus_delay;
>>>>>>
>>>>>>   };
>>>>>>
>>>>>> +/*
>>>>>> + * Supported SPM (Stream Performance Monitor) Operations  */
>>>>>> +/**
>>>>>> + * drm_amdgpu_spm_op - SPM ioctl operations  */ enum
>>>>>> +drm_amdgpu_spm_op {
>>>>>> +  AMDGPU_SPM_OP_ACQUIRE = 1,
>>>>>> +  AMDGPU_SPM_OP_RELEASE,
>>>>>> +  AMDGPU_SPM_OP_SET_DEST_BUF
>>>>>> +};
>>>>>> +
>>>>>> +/**
>>>>>> + * drm_amdgpu_spm_args - Arguments for SPM ioctl
>>>>>> + *
>>>>>> + * @dest_buf: [in] used for the address of the destination buffer
>>>>>> + *            in @AMDGPU_SPM_OP_SET_DEST_BUF
>>>>>> + * @buf_size: [in] size of the destination buffer
>>>>>> + * @op: [in] specifies the operation to perform
>>>>>> + * @timeout: [in/out] timeout in milliseconds (in), amount of time left
>>>>>> + *           in the timeout window (out)
>>>>>> + * @bytes_copied: [out] total amount of data that was copied to
>>>>>> +the
>>>>>> previous dest_buf + * @has_data_loss: [out] non-zero if data was
>>>>>> lost (e.g.
>>>>>> due to ring buffer overflow), + *                 zero otherwise
>>>>>> + * @reserved: must be zero for forward compatibility
>>>>>> + *
>>>>>> + * This ioctl performs different functions depending on the @op
>>>>>> parameter.
>>>>>> + *
>>>>>> + * AMDGPU_SPM_OP_ACQUIRE
>>>>>> + * ------------------------
>>>>>> + *
>>>>>> + * Acquires exclusive access of SPM on the specified XCP for the
>>>>>> + calling
>>>>>> process. + * This must be called before using AMDGPU_SPM_OP_SET_DEST_BUF.
>>>>>> + *
>>>>>> + * AMDGPU_SPM_OP_RELEASE
>>>>>> + * ------------------------
>>>>>> + *
>>>>>> + * Releases exclusive access of SPM on the specified XCP for the
>>>>>> + calling
>>>>>> process, + * which allows another process to acquire it in the future.
>>>>>> + *
>>>>>> + * AMDGPU_SPM_OP_SET_DEST_BUF
>>>>>> + * -----------------------------
>>>>>> + *
>>>>>> + * If @dest_buf is NULL, the destination buffer address is unset
>>>>>> + and
>>>>>> copying of counters + * is stopped.
>>>>>> + *
>>>>>> + * If @dest_buf is not NULL, it specifies the pointer to a new
>>>>>> destination
>>>>>> buffer. + * @buf_size specifies the size of the buffer.
>>>>>> + *
>>>>>> + * If @timeout is non-0, the call will wait for up to @timeout
>>>>>> + ms for
>>>>>> the
>>>>>> previous + * buffer to be filled. If the previous buffer is
>>>>>> filled before the timeout, @timeout + * is updated with the time
>>>>>> remaining. If the timeout is exceeded, the function + * copies
>>>>>> any partial data available into the previous user buffer and
>>>>>> returns success. + * The amount of valid data in the previous
>>>>>> user buffer is indicated by @bytes_copied. + *
>>>>>> + * If @timeout is 0, the function immediately replaces the
>>>>>> + previous
>>>>>> destination buffer + * without waiting for the previous buffer to
>>>>>> be filled. That means the previous buffer + * may only be
>>>>>> partially filled, and @bytes_copied will indicate how much data has been + * copied to it.
>>>>>> + *
>>>>>> + * If data was lost, e.g. due to a ring buffer overflow,
>>>>>> + @has_data_loss
>>>>>> will be non-0. + *
>>>>>> + * Returns negative error code on failure, 0 on success.
>>>>>> + */
>>>>>> +struct drm_amdgpu_spm_args {
>>>>>> +  __u64 dest_buf;
>>>>>> +  __u32 buf_size;
>>>>>> +  __u32 op;
>>>>>> +  __u32 timeout;
>>>>>> +  __u32 bytes_copied;
>>>>>> +  __u32 has_data_loss;
>>>>>> +  __u32 reserved[9];      /* must be zero; pads this struct to 64
>>>>>
>>>>> bytes */
>>>>>
>>>>>> +};
>>>>>> +
>>>>>> +/**
>>>>>> + * drm_amdgpu_spm_buffer_header - SPM Buffer header for
>>>>>> drm_amdgpu_spm_args->dest_buf + *
>>>>>> + * @version:       [out] SPM version (0-23: minor, 24-31: major)
>>>>>> + * @bytes_copied:  [out] Amount of data copied for this
>>>>>> + sub-block
>>>>>> + * @has_data_loss: [out] Boolean indicating whether data was
>>>>>> + lost for
>>>>>> this
>>>>>> sub-block + *                       (e.g. due to a ring-buffer overflow)
>>>>>> + * @reserved:      Reserved for future use, must be zero
>>>>>> + *
>>>>>> + * This header precedes the SPM data in the destination buffer.
>>>>>> + The
>>>>>> reserved + * field maintains 64-byte cache line alignment (32
>>>>>> bytes total).
>>>>>> + */
>>>>>> +struct drm_amdgpu_spm_buffer_header {
>>>>>> +  __u32 version; /* 0-23: minor, 24-31: major */
>>>>>> +  __u32 bytes_copied;
>>>>>> +  __u32 has_data_loss;
>>>>>> +  __u32 reserved[5]; /* pad to 32 bytes (64-byte cache line / 2)
>>>>>> +*/ };
>>>>>> +
>>>>>> +/* Supported Profiler Operation */
>>>>>> +#define AMDGPU_PROFILER_OP_GET_VER                1 /* get profiler UAPI
>>>>>
>>>>> version */
>>>>>
>>>>>> +#define AMDGPU_PROFILER_OP_SPM                    2
>>>>>> +
>>>>>> +/*
>>>>>> + * Profiler IOCTL
>>>>>> + */
>>>>>> +/**
>>>>>> + * struct drm_amdgpu_profiler_args - Profiler IOCTL arguments
>>>>>> + * @op: Operation to perform (AMDGPU_PROFILER_OP_*)
>>>>>> + * @pad: Must be zero for input validation
>>>>>> + * @data: Operation-specific parameters (union)
>>>>>> + *
>>>>>> + * GET_VER operation:
>>>>>> + * Returns the profiler UAPI version in @data.version as
>>>>>> +(MAJOR<<24) |
>>>>>> MINOR. + * Input: @data must be zero (reserved for forward
>>>>>> compatibility).
>>>>>> + *
>>>>>> + * Design note: GET_VER is intentionally a parameter-free query
>>>>>> returning
>>>>>> + * the global profiler UAPI version.
>>>>>> + */
>>>>>> +struct drm_amdgpu_profiler_args {
>>>>>> +  __u32 op;                               /*
>>>>>
>>>>> AMDGPU_PROFILER_OP_* */
>>>>>
>>>>>> +  __u32 pad;                              /* must be zero */
>>>>>> +  union {
>>>>>> +          __u32 version;          /* GET_VER: out
>>>>>
>>>>> (MAJOR<<24) | MINOR */
>>>>>
>>>>>> +          struct drm_amdgpu_spm_args spm;
>>>>>> +  } data;
>>>>>> +};
>>>>>> +
>>>>>>
>>>>>>   #if defined(__cplusplus)
>>>>>>   }
>>>>>>   #endif
>>>
>>>
>>>
>>>
>


^ permalink raw reply	[flat|nested] 36+ messages in thread

end of thread, other threads:[~2026-09-24 12:33 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-21 20:03 [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions James Zhu
2026-09-21 20:03 ` [PATCH 02/12] drm/amdgpu: Add profiler UAPI infrastructure James Zhu
2026-09-21 20:03 ` [PATCH 03/12] drm/amdgpu: add generic RLC SPM interface layer James Zhu
2026-09-21 20:03 ` [PATCH 04/12] drm/amdgpu: add amdgpu_xcp_get_xcc_mask helper James Zhu
2026-09-22  5:22   ` Lazar, Lijo
2026-09-22 13:35     ` James Zhu
2026-09-22 13:45       ` Lazar, Lijo
2026-09-22 14:04         ` James Zhu
2026-09-22 14:11         ` James Zhu
2026-09-22 14:26           ` Lazar, Lijo
2026-09-22 15:10             ` James Zhu
2026-09-23  3:39               ` Lazar, Lijo
2026-09-23 12:36                 ` James Zhu
2026-09-23 14:55                   ` Lazar, Lijo
2026-09-23 15:08                     ` James Zhu
2026-09-21 20:03 ` [PATCH 05/12] drm/amdgpu: add profiler/spm operation AMDGPU_SPM_OP_ACQUIRE James Zhu
2026-09-21 20:03 ` [PATCH 06/12] drm/amdgpu: add profiler/spm operation AMDGPU_SPM_OP_SET_DEST_BUF James Zhu
2026-09-21 20:03 ` [PATCH 07/12] drm/amdgpu: add profiler/spm operation AMDGPU_SPM_OP_RELEASE James Zhu
2026-09-21 20:03 ` [PATCH 08/12] drm/amdgpu: implement data dump from spm ring buffer James Zhu
2026-09-21 20:03 ` [PATCH 09/12] drm/amdgpu: workaround for spm ring buffer overflow James Zhu
2026-09-21 20:03 ` [PATCH 10/12] drm/amdgpu: add SPM ring-buffer polling thread for pre-GFX12 James Zhu
2026-09-21 20:03 ` [PATCH 11/12] drm/amdgpu: add profiler/spm support for gfx12_0 James Zhu
2026-09-21 20:03 ` [PATCH 12/12] drm/amdgpu: bump KMS driver version to 3.65 for profiler UAPI James Zhu
2026-09-22 15:09 ` [PATCH 01/12] drm/amdgpu: add profiler UAPI definitions Timur Kristóf
2026-09-22 15:20   ` James Zhu
2026-09-22 15:22     ` Timur Kristóf
2026-09-22 15:32       ` James Zhu
2026-09-22 15:34         ` Timur Kristóf
2026-09-22 15:40       ` Alex Deucher
2026-09-22 15:43         ` Christian König
2026-09-23  1:20           ` Ma, Bing
2026-09-23  7:47             ` Christian König
2026-09-24 12:33               ` Ma, Bing
2026-09-22 15:13 ` Christian König
2026-09-22 15:29   ` James Zhu
  -- strict thread matches above, loose matches on Subject: below --
2026-09-21 20:00 James Zhu

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.