linux/drivers/gpu/drm/xe/xe_sriov_pf_service.c
Michal Wajdeczko a6c384b24f 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: Michal Wajdeczko <michal.wajdeczko@intel.com>
Reviewed-by: Piotr Piórkowski <piotr.piorkowski@intel.com>
Acked-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://lore.kernel.org/r/20250713103625.1964-7-michal.wajdeczko@intel.com
2025-07-14 18:19:31 +02:00

217 lines
6.3 KiB
C

// SPDX-License-Identifier: MIT
/*
* Copyright © 2023-2025 Intel Corporation
*/
#include "abi/guc_relay_actions_abi.h"
#include "xe_device_types.h"
#include "xe_sriov.h"
#include "xe_sriov_pf_helpers.h"
#include "xe_sriov_printk.h"
#include "xe_sriov_pf_service.h"
#include "xe_sriov_pf_service_types.h"
/**
* xe_sriov_pf_service_init - Early initialization of the SR-IOV PF service.
* @xe: the &xe_device to initialize
*
* Performs early initialization of the SR-IOV PF service.
*
* This function can only be called on PF.
*/
void xe_sriov_pf_service_init(struct xe_device *xe)
{
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);
xe_assert(xe, IS_SRIOV_PF(xe));
/* base versions may differ between platforms */
xe->sriov.pf.service.version.base.major = GUC_RELAY_VERSION_BASE_MAJOR;
xe->sriov.pf.service.version.base.minor = GUC_RELAY_VERSION_BASE_MINOR;
/* latest version is same for all platforms */
xe->sriov.pf.service.version.latest.major = GUC_RELAY_VERSION_LATEST_MAJOR;
xe->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_device *xe,
u32 wanted_major, u32 wanted_minor,
u32 *major, u32 *minor)
{
struct xe_sriov_pf_service_version base = xe->sriov.pf.service.version.base;
struct xe_sriov_pf_service_version latest = xe->sriov.pf.service.version.latest;
xe_assert(xe, IS_SRIOV_PF(xe));
xe_assert(xe, base.major);
xe_assert(xe, base.major <= latest.major);
xe_assert(xe, (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_assert(xe, 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_device *xe, u32 vfid, u32 major, u32 minor)
{
xe_sriov_pf_assert_vfid(xe, vfid);
xe_assert(xe, major || minor);
xe->sriov.pf.vfs[vfid].version.major = major;
xe->sriov.pf.vfs[vfid].version.minor = minor;
}
static void pf_disconnect(struct xe_device *xe, u32 vfid)
{
xe_sriov_pf_assert_vfid(xe, vfid);
xe->sriov.pf.vfs[vfid].version.major = 0;
xe->sriov.pf.vfs[vfid].version.minor = 0;
}
/**
* xe_sriov_pf_service_is_negotiated - Check if VF has negotiated given ABI version.
* @xe: the &xe_device
* @vfid: the VF identifier
* @major: the major version to check
* @minor: the minor version to check
*
* Performs early initialization of the SR-IOV PF service.
*
* This function can only be called on PF.
*
* Returns: true if VF can use given ABI version functionality.
*/
bool xe_sriov_pf_service_is_negotiated(struct xe_device *xe, u32 vfid, u32 major, u32 minor)
{
xe_sriov_pf_assert_vfid(xe, vfid);
return major == xe->sriov.pf.vfs[vfid].version.major &&
minor <= xe->sriov.pf.vfs[vfid].version.minor;
}
/**
* xe_sriov_pf_service_handshake_vf - Confirm a connection with the VF.
* @xe: the &xe_device
* @vfid: the VF identifier
* @wanted_major: the major service version expected by the VF
* @wanted_minor: the minor service version expected by the VF
* @major: the major service version to be used by the VF
* @minor: the minor service version to be used by the VF
*
* Negotiate a VF/PF ABI version to allow VF use the PF services.
*
* This function can only be called on PF.
*
* Return: 0 on success or a negative error code on failure.
*/
int xe_sriov_pf_service_handshake_vf(struct xe_device *xe, u32 vfid,
u32 wanted_major, u32 wanted_minor,
u32 *major, u32 *minor)
{
int err;
xe_sriov_dbg_verbose(xe, "VF%u wants ABI version %u.%u\n",
vfid, wanted_major, wanted_minor);
err = pf_negotiate_version(xe, wanted_major, wanted_minor, major, minor);
if (err < 0) {
xe_sriov_notice(xe, "VF%u failed to negotiate ABI %u.%u (%pe)\n",
vfid, wanted_major, wanted_minor, ERR_PTR(err));
pf_disconnect(xe, vfid);
} else {
xe_sriov_dbg(xe, "VF%u negotiated ABI version %u.%u\n",
vfid, *major, *minor);
pf_connect(xe, vfid, *major, *minor);
}
return err;
}
/**
* xe_sriov_pf_service_reset_vf - Reset a connection with the VF.
* @xe: the &xe_device
* @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_sriov_pf_service_reset_vf(struct xe_device *xe, unsigned int vfid)
{
pf_disconnect(xe, vfid);
}
static void print_pf_version(struct drm_printer *p, const char *name,
const struct xe_sriov_pf_service_version *version)
{
drm_printf(p, "%s:\t%u.%u\n", name, version->major, version->minor);
}
/**
* xe_sriov_pf_service_print_versions - Print ABI versions negotiated with VFs.
* @xe: the &xe_device
* @p: the &drm_printer
*
* This function is for PF use only.
*/
void xe_sriov_pf_service_print_versions(struct xe_device *xe, struct drm_printer *p)
{
unsigned int n, total_vfs = xe_sriov_pf_get_totalvfs(xe);
struct xe_sriov_pf_service_version *version;
char name[8];
xe_assert(xe, IS_SRIOV_PF(xe));
print_pf_version(p, "base", &xe->sriov.pf.service.version.base);
print_pf_version(p, "latest", &xe->sriov.pf.service.version.latest);
for (n = 1; n <= total_vfs; n++) {
version = &xe->sriov.pf.vfs[n].version;
if (!version->major && !version->minor)
continue;
print_pf_version(p, xe_sriov_function_name(n, name, sizeof(name)), version);
}
}
#if IS_BUILTIN(CONFIG_DRM_XE_KUNIT_TEST)
#include "tests/xe_sriov_pf_service_kunit.c"
#endif