summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/xe/xe_sriov_pf_service.c
blob: eee3b2a1ba412fef41abd05b5a93277ecb21a8f0 (plain)
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