Commit b729ea27 authored by Riana Tauro's avatar Riana Tauro Committed by Lucas De Marchi
Browse files

drm/xe: Add engine activity support



GuC provides support to read engine counters to calculate the
engine activity. KMD exposes two counters via the PMU interface to
calculate engine activity

Engine Active Ticks(engine-active-ticks) - active ticks of engine
Engine Total Ticks (engine-total-ticks) - total ticks of engine

Engine activity percentage can be calculated as below
Engine activity % = (engine active ticks/engine total ticks) * 100.

v2: fix cosmetic review comments
    add forcewake for gpm_ts (Umesh)

v3: fix CI hooks error
    change function parameters and unpin bo on error
    of allocate_activity_buffers
    fix kernel-doc (Umesh)
    use engine activity (Umesh, Lucas)
    rename xe_engine_activity to xe_guc_engine_*
    fix commit message to use engine activity (Lucas, Umesh)

v4: add forcewake in PMU layer

v5: fix makefile
    use drmm_kcalloc instead of kmalloc_array
    remove managed bo
    skip init for VF
    fix cosmetic review comments (Michal)

Signed-off-by: default avatarRiana Tauro <riana.tauro@intel.com>
Reviewed-by: default avatarUmesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20250224053903.2253539-2-riana.tauro@intel.com


Signed-off-by: default avatarLucas De Marchi <lucas.demarchi@intel.com>
parent 8b4b3af8
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ xe-y += xe_bb.o \
	xe_guc_capture.o \
	xe_guc_ct.o \
	xe_guc_db_mgr.o \
	xe_guc_engine_activity.o \
	xe_guc_hwconfig.o \
	xe_guc_id_mgr.o \
	xe_guc_klv_helpers.o \
+1 −0
Original line number Diff line number Diff line
@@ -140,6 +140,7 @@ enum xe_guc_action {
	XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_LRC = 0x4601,
	XE_GUC_ACTION_CLIENT_SOFT_RESET = 0x5507,
	XE_GUC_ACTION_SET_ENG_UTIL_BUFF = 0x550A,
	XE_GUC_ACTION_SET_DEVICE_ENGINE_ACTIVITY_BUFFER = 0x550C,
	XE_GUC_ACTION_NOTIFY_MEMORY_CAT_ERROR = 0x6000,
	XE_GUC_ACTION_REPORT_PAGE_FAULT_REQ_DESC = 0x6002,
	XE_GUC_ACTION_PAGE_FAULT_RES_DESC = 0x6003,
+2 −0
Original line number Diff line number Diff line
@@ -358,6 +358,8 @@
#define   RENDER_AWAKE_STATUS			REG_BIT(1)
#define   MEDIA_SLICE0_AWAKE_STATUS		REG_BIT(0)

#define MISC_STATUS_0				XE_REG(0xa500)

#define FORCEWAKE_MEDIA_VDBOX(n)		XE_REG(0xa540 + (n) * 4)
#define FORCEWAKE_MEDIA_VEBOX(n)		XE_REG(0xa560 + (n) * 4)
#define FORCEWAKE_GSC				XE_REG(0xa618)
+321 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: MIT
/*
 * Copyright © 2025 Intel Corporation
 */

#include <drm/drm_managed.h>

#include "abi/guc_actions_abi.h"
#include "regs/xe_gt_regs.h"

#include "xe_bo.h"
#include "xe_force_wake.h"
#include "xe_gt_printk.h"
#include "xe_guc.h"
#include "xe_guc_engine_activity.h"
#include "xe_guc_ct.h"
#include "xe_hw_engine.h"
#include "xe_map.h"
#include "xe_mmio.h"

#define TOTAL_QUANTA 0x8000

static struct iosys_map engine_activity_map(struct xe_guc *guc, struct xe_hw_engine *hwe)
{
	struct xe_guc_engine_activity *engine_activity = &guc->engine_activity;
	struct engine_activity_buffer *buffer = &engine_activity->device_buffer;
	u16 guc_class = xe_engine_class_to_guc_class(hwe->class);
	size_t offset;

	offset = offsetof(struct guc_engine_activity_data,
			  engine_activity[guc_class][hwe->logical_instance]);

	return IOSYS_MAP_INIT_OFFSET(&buffer->activity_bo->vmap, offset);
}

static struct iosys_map engine_metadata_map(struct xe_guc *guc)
{
	struct xe_guc_engine_activity *engine_activity = &guc->engine_activity;
	struct engine_activity_buffer *buffer = &engine_activity->device_buffer;

	return buffer->metadata_bo->vmap;
}

static int allocate_engine_activity_group(struct xe_guc *guc)
{
	struct xe_guc_engine_activity *engine_activity = &guc->engine_activity;
	struct xe_device *xe = guc_to_xe(guc);
	u32 num_activity_group = 1; /* Will be modified for VF */

	engine_activity->eag  = drmm_kcalloc(&xe->drm, num_activity_group,
					     sizeof(struct engine_activity_group), GFP_KERNEL);

	if (!engine_activity->eag)
		return -ENOMEM;

	engine_activity->num_activity_group = num_activity_group;

	return 0;
}

static int allocate_engine_activity_buffers(struct xe_guc *guc,
					    struct engine_activity_buffer *buffer)
{
	u32 metadata_size = sizeof(struct guc_engine_activity_metadata);
	u32 size = sizeof(struct guc_engine_activity_data);
	struct xe_gt *gt = guc_to_gt(guc);
	struct xe_tile *tile = gt_to_tile(gt);
	struct xe_bo *bo, *metadata_bo;

	metadata_bo = xe_bo_create_pin_map(gt_to_xe(gt), tile, NULL, PAGE_ALIGN(metadata_size),
					   ttm_bo_type_kernel, XE_BO_FLAG_SYSTEM |
					   XE_BO_FLAG_GGTT | XE_BO_FLAG_GGTT_INVALIDATE);

	if (IS_ERR(metadata_bo))
		return PTR_ERR(metadata_bo);

	bo = xe_bo_create_pin_map(gt_to_xe(gt), tile, NULL, PAGE_ALIGN(size),
				  ttm_bo_type_kernel, XE_BO_FLAG_VRAM_IF_DGFX(tile) |
				  XE_BO_FLAG_GGTT | XE_BO_FLAG_GGTT_INVALIDATE);

	if (IS_ERR(bo)) {
		xe_bo_unpin_map_no_vm(metadata_bo);
		return PTR_ERR(bo);
	}

	buffer->metadata_bo = metadata_bo;
	buffer->activity_bo = bo;
	return 0;
}

static void free_engine_activity_buffers(struct engine_activity_buffer *buffer)
{
	xe_bo_unpin_map_no_vm(buffer->metadata_bo);
	xe_bo_unpin_map_no_vm(buffer->activity_bo);
}

static struct engine_activity *hw_engine_to_engine_activity(struct xe_hw_engine *hwe)
{
	struct xe_guc *guc = &hwe->gt->uc.guc;
	struct engine_activity_group *eag = &guc->engine_activity.eag[0];
	u16 guc_class = xe_engine_class_to_guc_class(hwe->class);

	return &eag->engine[guc_class][hwe->logical_instance];
}

static u64 cpu_ns_to_guc_tsc_tick(ktime_t ns, u32 freq)
{
	return mul_u64_u32_div(ns, freq, NSEC_PER_SEC);
}

#define read_engine_activity_record(xe_, map_, field_) \
	xe_map_rd_field(xe_, map_, 0, struct guc_engine_activity, field_)

#define read_metadata_record(xe_, map_, field_) \
	xe_map_rd_field(xe_, map_, 0, struct guc_engine_activity_metadata, field_)

static u64 get_engine_active_ticks(struct xe_guc *guc, struct xe_hw_engine *hwe)
{
	struct engine_activity *ea = hw_engine_to_engine_activity(hwe);
	struct guc_engine_activity *cached_activity = &ea->activity;
	struct guc_engine_activity_metadata *cached_metadata = &ea->metadata;
	struct xe_guc_engine_activity *engine_activity = &guc->engine_activity;
	struct iosys_map activity_map, metadata_map;
	struct xe_device *xe =  guc_to_xe(guc);
	struct xe_gt *gt = guc_to_gt(guc);
	u32 last_update_tick, global_change_num;
	u64 active_ticks, gpm_ts;
	u16 change_num;

	activity_map = engine_activity_map(guc, hwe);
	metadata_map = engine_metadata_map(guc);
	global_change_num = read_metadata_record(xe, &metadata_map, global_change_num);

	/* GuC has not initialized activity data yet, return 0 */
	if (!global_change_num)
		goto update;

	if (global_change_num == cached_metadata->global_change_num)
		goto update;

	cached_metadata->global_change_num = global_change_num;
	change_num = read_engine_activity_record(xe, &activity_map, change_num);

	if (!change_num || change_num == cached_activity->change_num)
		goto update;

	/* read engine activity values */
	last_update_tick = read_engine_activity_record(xe, &activity_map, last_update_tick);
	active_ticks = read_engine_activity_record(xe, &activity_map, active_ticks);

	/* activity calculations */
	ea->running = !!last_update_tick;
	ea->total += active_ticks - cached_activity->active_ticks;
	ea->active = 0;

	/* cache the counter */
	cached_activity->change_num = change_num;
	cached_activity->last_update_tick = last_update_tick;
	cached_activity->active_ticks = active_ticks;

update:
	if (ea->running) {
		gpm_ts = xe_mmio_read64_2x32(&gt->mmio, MISC_STATUS_0) >>
			 engine_activity->gpm_timestamp_shift;
		ea->active = lower_32_bits(gpm_ts) - cached_activity->last_update_tick;
	}

	return ea->total + ea->active;
}

static u64 get_engine_total_ticks(struct xe_guc *guc, struct xe_hw_engine *hwe)
{
	struct engine_activity *ea = hw_engine_to_engine_activity(hwe);
	struct guc_engine_activity_metadata *cached_metadata = &ea->metadata;
	struct guc_engine_activity *cached_activity = &ea->activity;
	struct iosys_map activity_map, metadata_map;
	struct xe_device *xe = guc_to_xe(guc);
	ktime_t now, cpu_delta;
	u64 numerator;
	u16 quanta_ratio;

	activity_map = engine_activity_map(guc, hwe);
	metadata_map = engine_metadata_map(guc);

	if (!cached_metadata->guc_tsc_frequency_hz)
		cached_metadata->guc_tsc_frequency_hz = read_metadata_record(xe, &metadata_map,
									     guc_tsc_frequency_hz);

	quanta_ratio = read_engine_activity_record(xe, &activity_map, quanta_ratio);
	cached_activity->quanta_ratio = quanta_ratio;

	/* Total ticks calculations */
	now = ktime_get();
	cpu_delta = now - ea->last_cpu_ts;
	ea->last_cpu_ts = now;
	numerator = (ea->quanta_remainder_ns + cpu_delta) * cached_activity->quanta_ratio;
	ea->quanta_ns += numerator / TOTAL_QUANTA;
	ea->quanta_remainder_ns = numerator % TOTAL_QUANTA;
	ea->quanta = cpu_ns_to_guc_tsc_tick(ea->quanta_ns, cached_metadata->guc_tsc_frequency_hz);

	return ea->quanta;
}

static int enable_engine_activity_stats(struct xe_guc *guc)
{
	struct xe_guc_engine_activity *engine_activity = &guc->engine_activity;
	struct engine_activity_buffer *buffer = &engine_activity->device_buffer;
	u32 action[] = {
		XE_GUC_ACTION_SET_DEVICE_ENGINE_ACTIVITY_BUFFER,
		xe_bo_ggtt_addr(buffer->metadata_bo),
		0,
		xe_bo_ggtt_addr(buffer->activity_bo),
		0,
	};

	/* Blocking here to ensure the buffers are ready before reading them */
	return xe_guc_ct_send_block(&guc->ct, action, ARRAY_SIZE(action));
}

static void engine_activity_set_cpu_ts(struct xe_guc *guc)
{
	struct xe_guc_engine_activity *engine_activity = &guc->engine_activity;
	struct engine_activity_group *eag = &engine_activity->eag[0];
	int i, j;

	for (i = 0; i < GUC_MAX_ENGINE_CLASSES; i++)
		for (j = 0; j < GUC_MAX_INSTANCES_PER_CLASS; j++)
			eag->engine[i][j].last_cpu_ts = ktime_get();
}

static u32 gpm_timestamp_shift(struct xe_gt *gt)
{
	u32 reg;

	reg = xe_mmio_read32(&gt->mmio, RPM_CONFIG0);

	return 3 - REG_FIELD_GET(RPM_CONFIG0_CTC_SHIFT_PARAMETER_MASK, reg);
}

/**
 * xe_guc_engine_activity_active_ticks - Get engine active ticks
 * @guc: The GuC object
 * @hwe: The hw_engine object
 *
 * Return: accumulated ticks @hwe was active since engine activity stats were enabled.
 */
u64 xe_guc_engine_activity_active_ticks(struct xe_guc *guc, struct xe_hw_engine *hwe)
{
	return get_engine_active_ticks(guc, hwe);
}

/**
 * xe_guc_engine_activity_total_ticks - Get engine total ticks
 * @guc: The GuC object
 * @hwe: The hw_engine object
 *
 * Return: accumulated quanta of ticks allocated for the engine
 */
u64 xe_guc_engine_activity_total_ticks(struct xe_guc *guc, struct xe_hw_engine *hwe)
{
	return get_engine_total_ticks(guc, hwe);
}

/**
 * xe_guc_engine_activity_enable_stats - Enable engine activity stats
 * @guc: The GuC object
 *
 * Enable engine activity stats and set initial timestamps
 */
void xe_guc_engine_activity_enable_stats(struct xe_guc *guc)
{
	int ret;

	ret = enable_engine_activity_stats(guc);
	if (ret)
		xe_gt_err(guc_to_gt(guc), "failed to enable activity stats%d\n", ret);
	else
		engine_activity_set_cpu_ts(guc);
}

static void engine_activity_fini(void *arg)
{
	struct xe_guc_engine_activity *engine_activity = arg;
	struct engine_activity_buffer *buffer = &engine_activity->device_buffer;

	free_engine_activity_buffers(buffer);
}

/**
 * xe_guc_engine_activity_init - Initialize the engine activity data
 * @guc: The GuC object
 *
 * Return: 0 on success, negative error code otherwise.
 */
int xe_guc_engine_activity_init(struct xe_guc *guc)
{
	struct xe_guc_engine_activity *engine_activity = &guc->engine_activity;
	struct xe_gt *gt = guc_to_gt(guc);
	struct xe_device *xe = gt_to_xe(gt);
	int ret;

	if (IS_SRIOV_VF(xe))
		return 0;

	ret = allocate_engine_activity_group(guc);
	if (ret) {
		xe_gt_err(gt, "failed to allocate engine activity group (%pe)\n", ERR_PTR(ret));
		return ret;
	}

	ret = allocate_engine_activity_buffers(guc, &engine_activity->device_buffer);
	if (ret) {
		xe_gt_err(gt, "failed to allocate engine activity buffers (%pe)\n", ERR_PTR(ret));
		return ret;
	}

	engine_activity->gpm_timestamp_shift = gpm_timestamp_shift(gt);

	return devm_add_action_or_reset(gt_to_xe(gt)->drm.dev, engine_activity_fini,
					engine_activity);
}
+18 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: MIT */
/*
 * Copyright © 2025 Intel Corporation
 */

#ifndef _XE_GUC_ENGINE_ACTIVITY_H_
#define _XE_GUC_ENGINE_ACTIVITY_H_

#include <linux/types.h>

struct xe_hw_engine;
struct xe_guc;

int xe_guc_engine_activity_init(struct xe_guc *guc);
void xe_guc_engine_activity_enable_stats(struct xe_guc *guc);
u64 xe_guc_engine_activity_active_ticks(struct xe_guc *guc, struct xe_hw_engine *hwe);
u64 xe_guc_engine_activity_total_ticks(struct xe_guc *guc, struct xe_hw_engine *hwe);
#endif
Loading