Re: [PATCH v3] tools/xe-perf-recorder: Add mmio-trigger support

Shekhar Chauhan <[email protected]>
Newsgroups org.freedesktop.lists.igt-dev
Message-ID <[email protected]>
On 7/21/2026 6:08, Dixit, Ashutosh wrote:
> On Thu, 16 Jul 2026 00:14:32 -0700, Shekhar Chauhan wrote:
> Almost there, just a few remaining nit's.
>
>
>> Add mmio-trigger support to xe-perf-recorder, to justify the
>> whitelisting of OA MMIO Trigger Registers in XeKMD.
>>
>> v2: Add all OA unit types, fix includes, compile warnings and adding
>> extra triggger for file mode. (Ashutosh)
>> v3: Add a couple of asserts and commits. (Ashutosh)
> Incidentally, the subject line for your IGT patches should be '[PATCH i-g-t
> v3]' not [PATCH v3]. This can be configured in your .git/config:
>
> [format]
> 	subjectprefix = PATCH i-g-t
>
>> Signed-off-by: Shekhar Chauhan <[email protected]>
>> ---
>>   tools/xe-perf/xe_perf_recorder.c | 119 ++++++++++++++++++++++++++++++-
>>   1 file changed, 117 insertions(+), 2 deletions(-)
>>
>> diff --git a/tools/xe-perf/xe_perf_recorder.c b/tools/xe-perf/xe_perf_recorder.c
>> index f200fe9c9..7e7df9bca 100644
>> --- a/tools/xe-perf/xe_perf_recorder.c
>> +++ b/tools/xe-perf/xe_perf_recorder.c
>> @@ -26,19 +26,29 @@
>>   #include <unistd.h>
>>
>>   #include "igt_core.h"
>> +#include "intel_batchbuffer.h"
>>   #include "intel_chipset.h"
>> +#include "intel_reg.h"
> Looks like this is not needed, can you check.
>
>>   #include "ioctl_wrappers.h"
>>   #include "linux_scaffold.h"
>> +#include "xe/xe_ioctl.h"
>>   #include "xe/xe_oa.h"
>>   #include "xe/xe_oa_data.h"
>>   #include "xe/xe_query.h"
>>
>>   #include "xe_perf_recorder_commands.h"
>>
>> -#define ALIGN(v, a) (((v) + (a)-1) & ~((a)-1))
>> -#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))
>>   #define MAX(a,b) ((a) > (b) ? (a) : (b))
>>   #define MIN(a,b) ((a) < (b) ? (a) : (b))
>> +#define OAG_MMIOTRIGGER 0xdb1c
>> +#define OAMERT_MMIOTRIGGER 0x1453cc
>> +#define OAM_MMIOTRIGGER_OFFSET 0x1d0
>> +#define MEDIA_GT_GSI_OFFSET 0x380000
>> +#define XE_OAM_SAG_BASE_ADJ (MEDIA_GT_GSI_OFFSET + 0x13000)
>> +#define XE_OAM_SCMI_0_BASE_ADJ (MEDIA_GT_GSI_OFFSET + 0x14000)
>> +#define XE_OAM_SCMI_1_BASE_ADJ (MEDIA_GT_GSI_OFFSET + 0x14800)
>> +#define OAREPORT_REASON_MASK 0x3f
>> +#define OAREPORT_REASON_SHIFT 19
>>
>>   struct circular_buffer {
>> 	char   *data;
>> @@ -354,8 +364,50 @@ struct recording_context {
>> 	int oa_unit_id;
>> 	struct drm_xe_oa_unit *oa_unit;
>> 	struct drm_xe_engine_class_instance *hwe;
>> +
>> +	uint32_t vm;
>> +	uint32_t exec_queue;
>> +	struct intel_bb *ibb;
>>   };
>>
>> +static uint32_t oa_unit_mmio_trigger_reg(struct recording_context *ctx)
>> +{
>> +	const struct drm_xe_oa_unit *oau = ctx->oa_unit;
>> +
>> +	switch (oau->oa_unit_type) {
>> +	case DRM_XE_OA_UNIT_TYPE_OAM: {
>> +		struct drm_xe_query_oa_units *qoa = xe_oa_units(ctx->drm_fd);
>> +		uint8_t *poau = (uint8_t *)&qoa->oa_units[0];
>> +		int first_oam_id = -1;
>> +
>> +		/* Find the first OAM unit, as in oa_unit_by_type() */
>> +		for (int i = 0; i < qoa->num_oa_units; i++) {
>> +			struct drm_xe_oa_unit *u = (struct drm_xe_oa_unit *)poau;
>> +
>> +			if (u->oa_unit_type == DRM_XE_OA_UNIT_TYPE_OAM) {
>> +				first_oam_id = u->oa_unit_id;
>> +				break;
>> +			}
>> +			poau += sizeof(*u) + u->num_engines * sizeof(u->eci[0]);
>> +		}
>> +
>> +		assert(first_oam_id != -1);
>> +
>> +		if (oau->oa_unit_id == first_oam_id)
>> +			return XE_OAM_SCMI_0_BASE_ADJ + OAM_MMIOTRIGGER_OFFSET;
>> +		return XE_OAM_SCMI_1_BASE_ADJ + OAM_MMIOTRIGGER_OFFSET;
>> +	}
>> +	case DRM_XE_OA_UNIT_TYPE_OAM_SAG:
>> +		return XE_OAM_SAG_BASE_ADJ + OAM_MMIOTRIGGER_OFFSET;
>> +	case DRM_XE_OA_UNIT_TYPE_MERT:
>> +		return OAMERT_MMIOTRIGGER;
>> +	case DRM_XE_OA_UNIT_TYPE_OAG:
>> +		return OAG_MMIOTRIGGER;
>> +	default:
>> +		assert(0);
>> +	}
>> +}
>> +
>>   static void set_fd_flags(int fd, int flags)
>>   {
>> 	int old = fcntl(fd, F_GETFL, 0);
>> @@ -569,6 +621,28 @@ static bool write_stream_status(struct recording_context *ctx, FILE *output)
>> 	return true;
>>   }
>>
>> +static uint32_t
>> +report_reason(const uint32_t *report)
>> +{
>> +	return (report[0] >> OAREPORT_REASON_SHIFT) & OAREPORT_REASON_MASK;
>> +}
>> +
>> +static void
>> +check_mmio_trigger_report(struct recording_context *ctx, const void *report)
>> +{
>> +	const struct xe_oa_format *fmt = &oa_formats[ctx->metric_set->perf_oa_format];
>> +	const uint32_t *report_32 = report;
>> +	uint64_t value;
>> +
>> +	if (report_reason(report_32))
>> +		return;
>> +
>> +	value = (fmt->header == HDR_64_BIT) ? ((const uint64_t *)report)[2] : report_32[2];
>> +
>> +	if (value == 0xc0ffee01 || value == 0xc0ffee02)
>> +		fprintf(stdout, "Received trigger report with value 0x%" PRIx64 "\n", value);
>> +}
>> +
>>   static bool write_stream_data(struct recording_context *ctx,
>> 			      char *data, ssize_t size, FILE *output)
>>   {
>> @@ -582,6 +656,8 @@ static bool write_stream_data(struct recording_context *ctx,
>> 			.size = sizeof(header) + format_size,
>> 		};
>>
>> +		check_mmio_trigger_report(ctx, data + i * format_size);
>> +
> I just sent this patch fixing what seems to me to be a previously existing
> bug:
>
> 	https://patchwork.freedesktop.org/series/170781/
>
> Please review this patch and change your patch accordingly (assuming the
> above bug-fix is applied before your patch).
>
>
>> 		if (fwrite(&header, sizeof(header), 1, output) != 1)
>> 			return false;
>>
>> @@ -697,6 +773,22 @@ write_correlation_timestamps(struct recording_context *ctx, FILE *output)
>> 	return write_saved_correlation_timestamps(output, &corr);
>>   }
>>
>> +static void emit_mmio_triggered_report(struct intel_bb *ibb, uint32_t reg, uint32_t value)
>> +{
>> +	intel_bb_out(ibb, MI_LOAD_REGISTER_IMM(1));
>> +	intel_bb_out(ibb, reg);
>> +	intel_bb_out(ibb, value);
>> +}
>> +
>> +static void emit_oa_trigger(struct recording_context *ctx, uint32_t value)
>> +{
>> +	emit_mmio_triggered_report(ctx->ibb, oa_unit_mmio_trigger_reg(ctx), value);
>> +
>> +	intel_bb_flush_render(ctx->ibb);
>> +	intel_bb_sync(ctx->ibb);
>> +	intel_bb_reset(ctx->ibb, false);
>> +}
>> +
>>   static void
>>   read_command_file(struct recording_context *ctx)
>>   {
>> @@ -712,6 +804,9 @@ read_command_file(struct recording_context *ctx)
>> 		uint8_t *dump = malloc(len);
>> 		FILE *file;
>>
>> +		emit_oa_trigger(ctx, 0xc0ffee02);
>> +		write_perf_data(ctx->output_stream, ctx);
>> +
>> 		while (offset < len &&
>> 		       ((ret = read(ctx->command_fifo_fd,
>> 				    (void *) dump + offset, len - offset)) > 0
>> @@ -835,6 +930,13 @@ usage(const char *name)
>>   static void
>>   teardown_recording_context(struct recording_context *ctx)
>>   {
>> +	if (ctx->ibb)
>> +		intel_bb_destroy(ctx->ibb);
>> +	if (ctx->exec_queue)
>> +		xe_exec_queue_destroy(ctx->drm_fd, ctx->exec_queue);
>> +	if (ctx->vm)
>> +		xe_vm_destroy(ctx->drm_fd, ctx->vm);
>> +
>> 	if (ctx->topology)
>> 		free(ctx->topology);
>>
>> @@ -912,6 +1014,14 @@ static int assign_oa_unit(int fd, struct recording_context *ctx)
>> 	return -1;
>>   }
>>
>> +static void init_trigger_ctx(struct recording_context *ctx)
> Let's call this init_mmio_trigger_ctx.
>
>> +{
>> +	ctx->vm = xe_vm_create(ctx->drm_fd, 0, 0);
>> +	ctx->exec_queue = xe_exec_queue_create(ctx->drm_fd, ctx->vm, ctx->hwe, 0);
>> +	ctx->ibb = intel_bb_create_with_context(ctx->drm_fd, ctx->exec_queue, ctx->vm,
>> +				 NULL, BATCH_SZ);
> Fix alignment here.
>
>> +}
>> +
>>   int
>>   main(int argc, char *argv[])
>>   {
>> @@ -1180,6 +1290,9 @@ main(int argc, char *argv[])
>> 		goto fail;
>> 	}
>>
>> +	init_trigger_ctx(&ctx);
> If we move this right after xe_device_get(), then I think we can remove the
> new if checks in teardown_recording_context().
>
> Anyway, we can live with those if checks too, so either way is fine with
> me.

I'll address all the above comments but for this, I don't think it'll 
still be okay if I placed it further after xe_device_get() cause 
ctx->hwe is filled by assign_oa_unit() -> assign_hwe(), which runs after 
xe_device_get(). If we moved init_trigger_ctx() after xe_device_get(), 
I'd still pass NULL into hwe, which isn't right. If I even move it after 
assign_oa_unit(), intel_get_device_info() might return NULL on some 
case, and then assign_oa_unit() itself fails.

Overall, I just feel, let's just keep the checks..

-shekhar

>
>> +	emit_oa_trigger(&ctx, 0xc0ffee01);
>> +
>> 	corr_period_ns = corr_period * 1000000000ul;
>> 	poll_time_ns = corr_period_ns;
>>
>> @@ -1227,6 +1340,8 @@ main(int argc, char *argv[])
>> 		}
>> 	}
>>
>> +	/* Emit second OA trigger for the case where circular buffer is not used */
>> +	emit_oa_trigger(&ctx, 0xc0ffee02);
>> 	fprintf(stdout, "Exiting...\n");
>>
>> 	if (!write_perf_data(ctx.output_stream, &ctx)) {
>> --
>> 2.53.0
>>
> Thanks.
> --
> Ashutosh

-- 
Shekhar Chauhan
Linux Graphics Software Engineer
Intel Corporation
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.