1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
|
// 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
|