xref: /linux/tools/testing/selftests/vfio/vfio_pci_sriov_uapi_test.c (revision efe86f088f48f18c27b648e5724048947f3b7fb4)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include "lib/include/libvfio/assert.h"
3 #include <fcntl.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6 #include <sys/ioctl.h>
7 #include <linux/limits.h>
8 
9 #include <libvfio.h>
10 
11 #include "../kselftest_harness.h"
12 
13 #define UUID_1 "52ac9bff-3a88-4fbd-901a-0d767c3b6c97"
14 #define UUID_2 "88594674-90a0-47a9-aea8-9d9b352ac08a"
15 
16 static const char *pf_bdf;
17 static char *vf_bdf;
18 
19 static pid_t main_pid;
20 
21 static int container_setup(struct vfio_pci_device *device, const char *bdf,
22 			   const char *vf_token)
23 {
24 	vfio_pci_group_setup(device, bdf);
25 	vfio_container_set_iommu(device);
26 	__vfio_pci_group_get_device_fd(device, bdf, vf_token);
27 
28 	/* The device fd will be -1 in case of mismatched tokens */
29 	return (device->fd < 0);
30 }
31 
32 static int iommufd_setup(struct vfio_pci_device *device, const char *bdf,
33 			 const char *vf_token)
34 {
35 	vfio_pci_cdev_open(device, bdf);
36 	return __vfio_device_bind_iommufd(device->fd,
37 					  device->iommu->iommufd, vf_token);
38 }
39 
40 static int device_init(const char *bdf, struct iommu *iommu,
41 		       const char *vf_token, struct vfio_pci_device **out_dev)
42 {
43 	struct vfio_pci_device *device = vfio_pci_device_alloc(bdf, iommu);
44 	int ret;
45 
46 	if (iommu->mode->container_path)
47 		ret = container_setup(device, bdf, vf_token);
48 	else
49 		ret = iommufd_setup(device, bdf, vf_token);
50 
51 	*out_dev = device;
52 	return ret;
53 }
54 
55 static void device_cleanup(struct vfio_pci_device *device)
56 {
57 	if (!device)
58 		return;
59 
60 	if (device->fd > 0)
61 		VFIO_ASSERT_EQ(close(device->fd), 0);
62 
63 	if (device->group_fd)
64 		VFIO_ASSERT_EQ(close(device->group_fd), 0);
65 
66 	vfio_pci_device_free(device);
67 }
68 
69 FIXTURE(vfio_pci_sriov_uapi_test) {
70 	struct vfio_pci_device *pf;
71 	struct vfio_pci_device *vf;
72 	struct iommu *iommu;
73 	char *pf_token;
74 };
75 
76 FIXTURE_VARIANT(vfio_pci_sriov_uapi_test) {
77 	const char *iommu_mode;
78 	char *vf_token;
79 };
80 
81 #define FIXTURE_VARIANT_ADD_IOMMU_MODE(_iommu_mode, _name, _vf_token)		\
82 FIXTURE_VARIANT_ADD(vfio_pci_sriov_uapi_test, _iommu_mode ## _ ## _name) {	\
83 	.iommu_mode = #_iommu_mode,						\
84 	.vf_token = (_vf_token),						\
85 }
86 
87 FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES(same_uuid, UUID_1);
88 FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES(diff_uuid, UUID_2);
89 FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES(null_uuid, NULL);
90 
91 FIXTURE_SETUP(vfio_pci_sriov_uapi_test)
92 {
93 	self->iommu = iommu_init(variant->iommu_mode);
94 
95 	self->pf_token = UUID_1;
96 	ASSERT_EQ(device_init(pf_bdf, self->iommu, self->pf_token, &self->pf), 0);
97 }
98 
99 FIXTURE_TEARDOWN(vfio_pci_sriov_uapi_test)
100 {
101 	device_cleanup(self->vf);
102 	device_cleanup(self->pf);
103 	iommu_cleanup(self->iommu);
104 }
105 
106 /*
107  * This asserts if the VF device is successfully created if its token matches
108  * with the token used to create/override the PF or fails during a mismatch.
109  */
110 #define ASSERT_COND_VF_CREATION(_ret) do {					\
111 	if (!variant->vf_token || strcmp(self->pf_token, variant->vf_token)) {	\
112 		ASSERT_NE((_ret), 0);						\
113 	} else {								\
114 		ASSERT_EQ((_ret), 0);						\
115 	}									\
116 } while (0)
117 
118 /*
119  * Validate if the UAPI handles correctly and incorrectly set token on the VF.
120  */
121 TEST_F(vfio_pci_sriov_uapi_test, init_token_match)
122 {
123 	int ret;
124 
125 	ret = device_init(vf_bdf, self->iommu, variant->vf_token, &self->vf);
126 	ASSERT_COND_VF_CREATION(ret);
127 }
128 
129 /*
130  * After closing the PF, validate if the VF access still needs the right token.
131  */
132 TEST_F(vfio_pci_sriov_uapi_test, pf_early_close)
133 {
134 	int ret;
135 
136 	device_cleanup(self->pf);
137 
138 	/* Clean the 'pf' to avoid calling device_cleanup() again. */
139 	self->pf = NULL;
140 
141 	ret = device_init(vf_bdf, self->iommu, variant->vf_token, &self->vf);
142 	ASSERT_COND_VF_CREATION(ret);
143 }
144 
145 /*
146  * After PF device init, override the existing token and validate if the newly
147  * set token is the one that's active.
148  */
149 TEST_F(vfio_pci_sriov_uapi_test, override_token)
150 {
151 	int ret;
152 
153 	self->pf_token = UUID_2;
154 	vfio_device_set_vf_token(self->pf->fd, self->pf_token);
155 
156 	ret = device_init(vf_bdf, self->iommu, variant->vf_token, &self->vf);
157 	ASSERT_COND_VF_CREATION(ret);
158 }
159 
160 static void vf_teardown(void)
161 {
162 	/*
163 	 * The child processes, created by TEST_F()s, inherits this atexit()
164 	 * handler. Hence, check and destroy the VF only when the main/parent
165 	 * process exits.
166 	 */
167 	if (getpid() != main_pid)
168 		return;
169 
170 	free(vf_bdf);
171 	sysfs_sriov_numvfs_set(pf_bdf, 0);
172 }
173 
174 static void vf_setup(void)
175 {
176 	char *vf_driver;
177 	int nr_vfs;
178 
179 	nr_vfs = sysfs_sriov_totalvfs_get(pf_bdf);
180 	if (nr_vfs <= 0)
181 		ksft_exit_skip("SR-IOV may not be supported by the PF: %s\n", pf_bdf);
182 
183 	nr_vfs = sysfs_sriov_numvfs_get(pf_bdf);
184 	if (nr_vfs != 0)
185 		ksft_exit_skip("SR-IOV already configured for the PF: %s\n", pf_bdf);
186 
187 	/* Create only one VF for testing */
188 	sysfs_sriov_numvfs_set(pf_bdf, 1);
189 
190 	/*
191 	 * Setup an exit handler to destroy the VF in case of failures
192 	 * during further setup at the end of the test run.
193 	 */
194 	main_pid = getpid();
195 	VFIO_ASSERT_EQ(atexit(vf_teardown), 0);
196 
197 	vf_bdf = sysfs_sriov_vf_bdf_get(pf_bdf, 0);
198 
199 	/*
200 	 * The VF inherits the driver from the PF.
201 	 * Ensure this is 'vfio-pci' before proceeding.
202 	 */
203 	vf_driver = sysfs_driver_get(vf_bdf);
204 	VFIO_ASSERT_NE(vf_driver, NULL);
205 	VFIO_ASSERT_EQ(strcmp(vf_driver, "vfio-pci"), 0);
206 	free(vf_driver);
207 
208 	printf("Created 1 VF (%s) under the PF: %s\n", vf_bdf, pf_bdf);
209 }
210 
211 int main(int argc, char *argv[])
212 {
213 	pf_bdf = vfio_selftests_get_bdf(&argc, argv);
214 	vf_setup();
215 
216 	return test_harness_run(argc, argv);
217 }
218