Commit a6c384b2 authored by Michal Wajdeczko's avatar Michal Wajdeczko
Browse files

drm/xe/pf: Stop requiring VF/PF version negotiation on every GT



While some VF/PF relay actions must be handled on the GT level,
like query for runtime registers, it was clarified by the arch
team that initial version negotiation can be done by the VF just
once, by using any available GuC/GT.

Move handling of the VF/PF ABI version negotiation on the PF side
from the GT level functions to the device level functions.

Signed-off-by: default avatarMichal Wajdeczko <michal.wajdeczko@intel.com>
Reviewed-by: default avatarPiotr Piórkowski <piotr.piorkowski@intel.com>
Acked-by: default avatarRodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://lore.kernel.org/r/20250713103625.1964-7-michal.wajdeczko@intel.com
parent d962178a
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -163,7 +163,8 @@ xe-$(CONFIG_PCI_IOV) += \
	xe_lmtt_2l.o \
	xe_lmtt_ml.o \
	xe_pci_sriov.o \
	xe_sriov_pf.o
	xe_sriov_pf.o \
	xe_sriov_pf_service.o

# include helpers for tests even when XE is built-in
ifdef CONFIG_DRM_XE_KUNIT_TEST
+227 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0 AND MIT
/*
 * Copyright © 2024 Intel Corporation
 * Copyright © 2024-2025 Intel Corporation
 */

#include <kunit/test.h>
@@ -17,7 +17,6 @@ static int pf_service_test_init(struct kunit *test)
		.subplatform = XE_SUBPLATFORM_NONE,
	};
	struct xe_device *xe;
	struct xe_gt *gt;

	test->priv = &fake;
	xe_kunit_helper_xe_device_test_init(test);
@@ -25,187 +24,183 @@ static int pf_service_test_init(struct kunit *test)
	xe = test->priv;
	KUNIT_ASSERT_EQ(test, xe_sriov_init(xe), 0);

	gt = xe_device_get_gt(xe, 0);
	pf_init_versions(gt);

	xe_sriov_pf_service_init(xe);
	/*
	 * sanity check:
	 * - all supported platforms VF/PF ABI versions must be defined
	 * - base version can't be newer than latest
	 */
	KUNIT_ASSERT_NE(test, 0, gt->sriov.pf.service.version.base.major);
	KUNIT_ASSERT_NE(test, 0, gt->sriov.pf.service.version.latest.major);
	KUNIT_ASSERT_LE(test, gt->sriov.pf.service.version.base.major,
			gt->sriov.pf.service.version.latest.major);
	if (gt->sriov.pf.service.version.base.major == gt->sriov.pf.service.version.latest.major)
		KUNIT_ASSERT_LE(test, gt->sriov.pf.service.version.base.minor,
				gt->sriov.pf.service.version.latest.minor);

	test->priv = gt;
	KUNIT_ASSERT_NE(test, 0, xe->sriov.pf.service.version.base.major);
	KUNIT_ASSERT_NE(test, 0, xe->sriov.pf.service.version.latest.major);
	KUNIT_ASSERT_LE(test, xe->sriov.pf.service.version.base.major,
			xe->sriov.pf.service.version.latest.major);
	if (xe->sriov.pf.service.version.base.major == xe->sriov.pf.service.version.latest.major)
		KUNIT_ASSERT_LE(test, xe->sriov.pf.service.version.base.minor,
				xe->sriov.pf.service.version.latest.minor);
	return 0;
}

static void pf_negotiate_any(struct kunit *test)
{
	struct xe_gt *gt = test->priv;
	struct xe_device *xe = test->priv;
	u32 major, minor;

	KUNIT_ASSERT_EQ(test, 0,
			pf_negotiate_version(gt, VF2PF_HANDSHAKE_MAJOR_ANY,
			pf_negotiate_version(xe, VF2PF_HANDSHAKE_MAJOR_ANY,
					     VF2PF_HANDSHAKE_MINOR_ANY,
					     &major, &minor));
	KUNIT_ASSERT_EQ(test, major, gt->sriov.pf.service.version.latest.major);
	KUNIT_ASSERT_EQ(test, minor, gt->sriov.pf.service.version.latest.minor);
	KUNIT_ASSERT_EQ(test, major, xe->sriov.pf.service.version.latest.major);
	KUNIT_ASSERT_EQ(test, minor, xe->sriov.pf.service.version.latest.minor);
}

static void pf_negotiate_base_match(struct kunit *test)
{
	struct xe_gt *gt = test->priv;
	struct xe_device *xe = test->priv;
	u32 major, minor;

	KUNIT_ASSERT_EQ(test, 0,
			pf_negotiate_version(gt,
					     gt->sriov.pf.service.version.base.major,
					     gt->sriov.pf.service.version.base.minor,
			pf_negotiate_version(xe,
					     xe->sriov.pf.service.version.base.major,
					     xe->sriov.pf.service.version.base.minor,
					     &major, &minor));
	KUNIT_ASSERT_EQ(test, major, gt->sriov.pf.service.version.base.major);
	KUNIT_ASSERT_EQ(test, minor, gt->sriov.pf.service.version.base.minor);
	KUNIT_ASSERT_EQ(test, major, xe->sriov.pf.service.version.base.major);
	KUNIT_ASSERT_EQ(test, minor, xe->sriov.pf.service.version.base.minor);
}

static void pf_negotiate_base_newer(struct kunit *test)
{
	struct xe_gt *gt = test->priv;
	struct xe_device *xe = test->priv;
	u32 major, minor;

	KUNIT_ASSERT_EQ(test, 0,
			pf_negotiate_version(gt,
					     gt->sriov.pf.service.version.base.major,
					     gt->sriov.pf.service.version.base.minor + 1,
			pf_negotiate_version(xe,
					     xe->sriov.pf.service.version.base.major,
					     xe->sriov.pf.service.version.base.minor + 1,
					     &major, &minor));
	KUNIT_ASSERT_EQ(test, major, gt->sriov.pf.service.version.base.major);
	KUNIT_ASSERT_GE(test, minor, gt->sriov.pf.service.version.base.minor);
	if (gt->sriov.pf.service.version.base.major == gt->sriov.pf.service.version.latest.major)
		KUNIT_ASSERT_LE(test, minor, gt->sriov.pf.service.version.latest.minor);
	KUNIT_ASSERT_EQ(test, major, xe->sriov.pf.service.version.base.major);
	KUNIT_ASSERT_GE(test, minor, xe->sriov.pf.service.version.base.minor);
	if (xe->sriov.pf.service.version.base.major == xe->sriov.pf.service.version.latest.major)
		KUNIT_ASSERT_LE(test, minor, xe->sriov.pf.service.version.latest.minor);
	else
		KUNIT_FAIL(test, "FIXME: don't know how to test multi-version yet!\n");
}

static void pf_negotiate_base_next(struct kunit *test)
{
	struct xe_gt *gt = test->priv;
	struct xe_device *xe = test->priv;
	u32 major, minor;

	KUNIT_ASSERT_EQ(test, 0,
			pf_negotiate_version(gt,
					     gt->sriov.pf.service.version.base.major + 1, 0,
			pf_negotiate_version(xe,
					     xe->sriov.pf.service.version.base.major + 1, 0,
					     &major, &minor));
	KUNIT_ASSERT_GE(test, major, gt->sriov.pf.service.version.base.major);
	KUNIT_ASSERT_LE(test, major, gt->sriov.pf.service.version.latest.major);
	if (major == gt->sriov.pf.service.version.latest.major)
		KUNIT_ASSERT_LE(test, minor, gt->sriov.pf.service.version.latest.minor);
	KUNIT_ASSERT_GE(test, major, xe->sriov.pf.service.version.base.major);
	KUNIT_ASSERT_LE(test, major, xe->sriov.pf.service.version.latest.major);
	if (major == xe->sriov.pf.service.version.latest.major)
		KUNIT_ASSERT_LE(test, minor, xe->sriov.pf.service.version.latest.minor);
	else
		KUNIT_FAIL(test, "FIXME: don't know how to test multi-version yet!\n");
}

static void pf_negotiate_base_older(struct kunit *test)
{
	struct xe_gt *gt = test->priv;
	struct xe_device *xe = test->priv;
	u32 major, minor;

	if (!gt->sriov.pf.service.version.base.minor)
	if (!xe->sriov.pf.service.version.base.minor)
		kunit_skip(test, "no older minor\n");

	KUNIT_ASSERT_NE(test, 0,
			pf_negotiate_version(gt,
					     gt->sriov.pf.service.version.base.major,
					     gt->sriov.pf.service.version.base.minor - 1,
			pf_negotiate_version(xe,
					     xe->sriov.pf.service.version.base.major,
					     xe->sriov.pf.service.version.base.minor - 1,
					     &major, &minor));
}

static void pf_negotiate_base_prev(struct kunit *test)
{
	struct xe_gt *gt = test->priv;
	struct xe_device *xe = test->priv;
	u32 major, minor;

	KUNIT_ASSERT_NE(test, 0,
			pf_negotiate_version(gt,
					     gt->sriov.pf.service.version.base.major - 1, 1,
			pf_negotiate_version(xe,
					     xe->sriov.pf.service.version.base.major - 1, 1,
					     &major, &minor));
}

static void pf_negotiate_latest_match(struct kunit *test)
{
	struct xe_gt *gt = test->priv;
	struct xe_device *xe = test->priv;
	u32 major, minor;

	KUNIT_ASSERT_EQ(test, 0,
			pf_negotiate_version(gt,
					     gt->sriov.pf.service.version.latest.major,
					     gt->sriov.pf.service.version.latest.minor,
			pf_negotiate_version(xe,
					     xe->sriov.pf.service.version.latest.major,
					     xe->sriov.pf.service.version.latest.minor,
					     &major, &minor));
	KUNIT_ASSERT_EQ(test, major, gt->sriov.pf.service.version.latest.major);
	KUNIT_ASSERT_EQ(test, minor, gt->sriov.pf.service.version.latest.minor);
	KUNIT_ASSERT_EQ(test, major, xe->sriov.pf.service.version.latest.major);
	KUNIT_ASSERT_EQ(test, minor, xe->sriov.pf.service.version.latest.minor);
}

static void pf_negotiate_latest_newer(struct kunit *test)
{
	struct xe_gt *gt = test->priv;
	struct xe_device *xe = test->priv;
	u32 major, minor;

	KUNIT_ASSERT_EQ(test, 0,
			pf_negotiate_version(gt,
					     gt->sriov.pf.service.version.latest.major,
					     gt->sriov.pf.service.version.latest.minor + 1,
			pf_negotiate_version(xe,
					     xe->sriov.pf.service.version.latest.major,
					     xe->sriov.pf.service.version.latest.minor + 1,
					     &major, &minor));
	KUNIT_ASSERT_EQ(test, major, gt->sriov.pf.service.version.latest.major);
	KUNIT_ASSERT_EQ(test, minor, gt->sriov.pf.service.version.latest.minor);
	KUNIT_ASSERT_EQ(test, major, xe->sriov.pf.service.version.latest.major);
	KUNIT_ASSERT_EQ(test, minor, xe->sriov.pf.service.version.latest.minor);
}

static void pf_negotiate_latest_next(struct kunit *test)
{
	struct xe_gt *gt = test->priv;
	struct xe_device *xe = test->priv;
	u32 major, minor;

	KUNIT_ASSERT_EQ(test, 0,
			pf_negotiate_version(gt,
					     gt->sriov.pf.service.version.latest.major + 1, 0,
			pf_negotiate_version(xe,
					     xe->sriov.pf.service.version.latest.major + 1, 0,
					     &major, &minor));
	KUNIT_ASSERT_EQ(test, major, gt->sriov.pf.service.version.latest.major);
	KUNIT_ASSERT_EQ(test, minor, gt->sriov.pf.service.version.latest.minor);
	KUNIT_ASSERT_EQ(test, major, xe->sriov.pf.service.version.latest.major);
	KUNIT_ASSERT_EQ(test, minor, xe->sriov.pf.service.version.latest.minor);
}

static void pf_negotiate_latest_older(struct kunit *test)
{
	struct xe_gt *gt = test->priv;
	struct xe_device *xe = test->priv;
	u32 major, minor;

	if (!gt->sriov.pf.service.version.latest.minor)
	if (!xe->sriov.pf.service.version.latest.minor)
		kunit_skip(test, "no older minor\n");

	KUNIT_ASSERT_EQ(test, 0,
			pf_negotiate_version(gt,
					     gt->sriov.pf.service.version.latest.major,
					     gt->sriov.pf.service.version.latest.minor - 1,
			pf_negotiate_version(xe,
					     xe->sriov.pf.service.version.latest.major,
					     xe->sriov.pf.service.version.latest.minor - 1,
					     &major, &minor));
	KUNIT_ASSERT_EQ(test, major, gt->sriov.pf.service.version.latest.major);
	KUNIT_ASSERT_EQ(test, minor, gt->sriov.pf.service.version.latest.minor - 1);
	KUNIT_ASSERT_EQ(test, major, xe->sriov.pf.service.version.latest.major);
	KUNIT_ASSERT_EQ(test, minor, xe->sriov.pf.service.version.latest.minor - 1);
}

static void pf_negotiate_latest_prev(struct kunit *test)
{
	struct xe_gt *gt = test->priv;
	struct xe_device *xe = test->priv;
	u32 major, minor;

	if (gt->sriov.pf.service.version.base.major == gt->sriov.pf.service.version.latest.major)
	if (xe->sriov.pf.service.version.base.major == xe->sriov.pf.service.version.latest.major)
		kunit_skip(test, "no prev major");

	KUNIT_ASSERT_EQ(test, 0,
			pf_negotiate_version(gt,
					     gt->sriov.pf.service.version.latest.major - 1,
					     gt->sriov.pf.service.version.base.minor + 1,
			pf_negotiate_version(xe,
					     xe->sriov.pf.service.version.latest.major - 1,
					     xe->sriov.pf.service.version.base.minor + 1,
					     &major, &minor));
	KUNIT_ASSERT_EQ(test, major, gt->sriov.pf.service.version.latest.major - 1);
	KUNIT_ASSERT_GE(test, major, gt->sriov.pf.service.version.base.major);
	KUNIT_ASSERT_EQ(test, major, xe->sriov.pf.service.version.latest.major - 1);
	KUNIT_ASSERT_GE(test, major, xe->sriov.pf.service.version.base.major);
}

static struct kunit_case pf_service_test_cases[] = {
+5 −2
Original line number Diff line number Diff line
@@ -15,10 +15,11 @@
#include "xe_gt_sriov_pf_helpers.h"
#include "xe_gt_sriov_pf_migration.h"
#include "xe_gt_sriov_pf_monitor.h"
#include "xe_gt_sriov_pf_service.h"
#include "xe_gt_sriov_printk.h"
#include "xe_guc_ct.h"
#include "xe_sriov.h"
#include "xe_sriov_pf_service.h"
#include "xe_tile.h"

static const char *control_cmd_to_string(u32 cmd)
{
@@ -1064,7 +1065,9 @@ static bool pf_exit_vf_flr_reset_data(struct xe_gt *gt, unsigned int vfid)
	if (!pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_RESET_DATA))
		return false;

	xe_gt_sriov_pf_service_reset(gt, vfid);
	if (xe_tile_is_root(gt->tile) && xe_gt_is_main_type(gt))
		xe_sriov_pf_service_reset_vf(gt_to_xe(gt), vfid);

	xe_gt_sriov_pf_monitor_flr(gt, vfid);

	pf_enter_vf_flr_reset_mmio(gt, vfid);
+0 −5
Original line number Diff line number Diff line
@@ -77,11 +77,6 @@ static const struct drm_info_list pf_info[] = {
		.show = xe_gt_debugfs_simple_show,
		.data = xe_gt_sriov_pf_service_print_runtime,
	},
	{
		"negotiated_versions",
		.show = xe_gt_debugfs_simple_show,
		.data = xe_gt_sriov_pf_service_print_version,
	},
	{
		"adverse_events",
		.show = xe_gt_debugfs_simple_show,
+6 −160
Original line number Diff line number Diff line
@@ -19,91 +19,7 @@
#include "xe_gt_sriov_pf_service_types.h"
#include "xe_guc_ct.h"
#include "xe_guc_hxg_helpers.h"

static void pf_init_versions(struct xe_gt *gt)
{
	BUILD_BUG_ON(!GUC_RELAY_VERSION_BASE_MAJOR && !GUC_RELAY_VERSION_BASE_MINOR);
	BUILD_BUG_ON(GUC_RELAY_VERSION_BASE_MAJOR > GUC_RELAY_VERSION_LATEST_MAJOR);

	/* base versions may differ between platforms */
	gt->sriov.pf.service.version.base.major = GUC_RELAY_VERSION_BASE_MAJOR;
	gt->sriov.pf.service.version.base.minor = GUC_RELAY_VERSION_BASE_MINOR;

	/* latest version is same for all platforms */
	gt->sriov.pf.service.version.latest.major = GUC_RELAY_VERSION_LATEST_MAJOR;
	gt->sriov.pf.service.version.latest.minor = GUC_RELAY_VERSION_LATEST_MINOR;
}

/* Return: 0 on success or a negative error code on failure. */
static int pf_negotiate_version(struct xe_gt *gt,
				u32 wanted_major, u32 wanted_minor,
				u32 *major, u32 *minor)
{
	struct xe_gt_sriov_pf_service_version base = gt->sriov.pf.service.version.base;
	struct xe_gt_sriov_pf_service_version latest = gt->sriov.pf.service.version.latest;

	xe_gt_assert(gt, base.major);
	xe_gt_assert(gt, base.major <= latest.major);
	xe_gt_assert(gt, (base.major < latest.major) || (base.minor <= latest.minor));

	/* VF doesn't care - return our latest  */
	if (wanted_major == VF2PF_HANDSHAKE_MAJOR_ANY &&
	    wanted_minor == VF2PF_HANDSHAKE_MINOR_ANY) {
		*major = latest.major;
		*minor = latest.minor;
		return 0;
	}

	/* VF wants newer than our - return our latest  */
	if (wanted_major > latest.major) {
		*major = latest.major;
		*minor = latest.minor;
		return 0;
	}

	/* VF wants older than min required - reject */
	if (wanted_major < base.major ||
	    (wanted_major == base.major && wanted_minor < base.minor)) {
		return -EPERM;
	}

	/* previous major - return wanted, as we should still support it */
	if (wanted_major < latest.major) {
		/* XXX: we are not prepared for multi-versions yet */
		xe_gt_assert(gt, base.major == latest.major);
		return -ENOPKG;
	}

	/* same major - return common minor */
	*major = wanted_major;
	*minor = min_t(u32, latest.minor, wanted_minor);
	return 0;
}

static void pf_connect(struct xe_gt *gt, u32 vfid, u32 major, u32 minor)
{
	xe_gt_sriov_pf_assert_vfid(gt, vfid);
	xe_gt_assert(gt, major || minor);

	gt->sriov.pf.vfs[vfid].version.major = major;
	gt->sriov.pf.vfs[vfid].version.minor = minor;
}

static void pf_disconnect(struct xe_gt *gt, u32 vfid)
{
	xe_gt_sriov_pf_assert_vfid(gt, vfid);

	gt->sriov.pf.vfs[vfid].version.major = 0;
	gt->sriov.pf.vfs[vfid].version.minor = 0;
}

static bool pf_is_negotiated(struct xe_gt *gt, u32 vfid, u32 major, u32 minor)
{
	xe_gt_sriov_pf_assert_vfid(gt, vfid);

	return major == gt->sriov.pf.vfs[vfid].version.major &&
	       minor <= gt->sriov.pf.vfs[vfid].version.minor;
}
#include "xe_sriov_pf_service.h"

static const struct xe_reg tgl_runtime_regs[] = {
	RPM_CONFIG0,			/* _MMIO(0x0d00) */
@@ -285,8 +201,6 @@ int xe_gt_sriov_pf_service_init(struct xe_gt *gt)
{
	int err;

	pf_init_versions(gt);

	err = pf_alloc_runtime_info(gt);
	if (unlikely(err))
		goto failed;
@@ -311,47 +225,6 @@ void xe_gt_sriov_pf_service_update(struct xe_gt *gt)
	pf_prepare_runtime_info(gt);
}

/**
 * xe_gt_sriov_pf_service_reset - Reset a connection with the VF.
 * @gt: the &xe_gt
 * @vfid: the VF identifier
 *
 * Reset a VF driver negotiated VF/PF ABI version.
 * After that point, the VF driver will have to perform new version handshake
 * to continue use of the PF services again.
 *
 * This function can only be called on PF.
 */
void xe_gt_sriov_pf_service_reset(struct xe_gt *gt, unsigned int vfid)
{
	pf_disconnect(gt, vfid);
}

/* Return: 0 on success or a negative error code on failure. */
static int pf_process_handshake(struct xe_gt *gt, u32 vfid,
				u32 wanted_major, u32 wanted_minor,
				u32 *major, u32 *minor)
{
	int err;

	xe_gt_sriov_dbg_verbose(gt, "VF%u wants ABI version %u.%u\n",
				vfid, wanted_major, wanted_minor);

	err = pf_negotiate_version(gt, wanted_major, wanted_minor, major, minor);

	if (err < 0) {
		xe_gt_sriov_notice(gt, "VF%u failed to negotiate ABI %u.%u (%pe)\n",
				   vfid, wanted_major, wanted_minor, ERR_PTR(err));
		pf_disconnect(gt, vfid);
	} else {
		xe_gt_sriov_dbg(gt, "VF%u negotiated ABI version %u.%u\n",
				vfid, *major, *minor);
		pf_connect(gt, vfid, *major, *minor);
	}

	return 0;
}

/* Return: length of the response message or a negative error code on failure. */
static int pf_process_handshake_msg(struct xe_gt *gt, u32 origin,
				    const u32 *request, u32 len, u32 *response, u32 size)
@@ -371,7 +244,8 @@ static int pf_process_handshake_msg(struct xe_gt *gt, u32 origin,
	wanted_major = FIELD_GET(VF2PF_HANDSHAKE_REQUEST_MSG_1_MAJOR, request[1]);
	wanted_minor = FIELD_GET(VF2PF_HANDSHAKE_REQUEST_MSG_1_MINOR, request[1]);

	err = pf_process_handshake(gt, origin, wanted_major, wanted_minor, &major, &minor);
	err = xe_sriov_pf_service_handshake_vf(gt_to_xe(gt), origin, wanted_major, wanted_minor,
					       &major, &minor);
	if (err < 0)
		return err;

@@ -430,8 +304,10 @@ static int pf_process_runtime_query_msg(struct xe_gt *gt, u32 origin,
	u32 remaining = 0;
	int ret;

	if (!pf_is_negotiated(gt, origin, 1, 0))
	/* this action is available from ABI 1.0 */
	if (!xe_sriov_pf_service_is_negotiated(gt_to_xe(gt), origin, 1, 0))
		return -EACCES;

	if (unlikely(msg_len > VF2PF_QUERY_RUNTIME_REQUEST_MSG_LEN))
		return -EMSGSIZE;
	if (unlikely(msg_len < VF2PF_QUERY_RUNTIME_REQUEST_MSG_LEN))
@@ -528,33 +404,3 @@ int xe_gt_sriov_pf_service_print_runtime(struct xe_gt *gt, struct drm_printer *p

	return 0;
}

/**
 * xe_gt_sriov_pf_service_print_version - Print ABI versions negotiated with VFs.
 * @gt: the &xe_gt
 * @p: the &drm_printer
 *
 * This function is for PF use only.
 */
int xe_gt_sriov_pf_service_print_version(struct xe_gt *gt, struct drm_printer *p)
{
	struct xe_device *xe = gt_to_xe(gt);
	unsigned int n, total_vfs = xe_sriov_pf_get_totalvfs(xe);
	struct xe_gt_sriov_pf_service_version *version;

	xe_gt_assert(gt, IS_SRIOV_PF(xe));

	for (n = 1; n <= total_vfs; n++) {
		version = &gt->sriov.pf.vfs[n].version;
		if (!version->major && !version->minor)
			continue;

		drm_printf(p, "VF%u:\t%u.%u\n", n, version->major, version->minor);
	}

	return 0;
}

#if IS_BUILTIN(CONFIG_DRM_XE_KUNIT_TEST)
#include "tests/xe_gt_sriov_pf_service_test.c"
#endif
Loading