xref: /linux/tools/testing/selftests/vfio/vfio_pci_driver_test.c (revision fded8da4bc38df6e1475ac5998934c490b96215d)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <sys/ioctl.h>
3 #include <sys/mman.h>
4 
5 #include <linux/sizes.h>
6 #include <linux/vfio.h>
7 
8 #include <vfio_util.h>
9 
10 #include "../kselftest_harness.h"
11 
12 static const char *device_bdf;
13 
14 #define ASSERT_NO_MSI(_eventfd) do {			\
15 	u64 __value;					\
16 							\
17 	ASSERT_EQ(-1, read(_eventfd, &__value, 8));	\
18 	ASSERT_EQ(EAGAIN, errno);			\
19 } while (0)
20 
21 static void region_setup(struct vfio_pci_device *device,
22 			 struct vfio_dma_region *region, u64 size)
23 {
24 	const int flags = MAP_SHARED | MAP_ANONYMOUS;
25 	const int prot = PROT_READ | PROT_WRITE;
26 	void *vaddr;
27 
28 	vaddr = mmap(NULL, size, prot, flags, -1, 0);
29 	VFIO_ASSERT_NE(vaddr, MAP_FAILED);
30 
31 	region->vaddr = vaddr;
32 	region->iova = (u64)vaddr;
33 	region->size = size;
34 
35 	vfio_pci_dma_map(device, region);
36 }
37 
38 static void region_teardown(struct vfio_pci_device *device,
39 			    struct vfio_dma_region *region)
40 {
41 	vfio_pci_dma_unmap(device, region);
42 	VFIO_ASSERT_EQ(munmap(region->vaddr, region->size), 0);
43 }
44 
45 FIXTURE(vfio_pci_driver_test) {
46 	struct vfio_pci_device *device;
47 	struct vfio_dma_region memcpy_region;
48 	void *vaddr;
49 	int msi_fd;
50 
51 	u64 size;
52 	void *src;
53 	void *dst;
54 	iova_t src_iova;
55 	iova_t dst_iova;
56 	iova_t unmapped_iova;
57 };
58 
59 FIXTURE_SETUP(vfio_pci_driver_test)
60 {
61 	struct vfio_pci_driver *driver;
62 
63 	self->device = vfio_pci_device_init(device_bdf, VFIO_TYPE1_IOMMU);
64 
65 	driver = &self->device->driver;
66 
67 	region_setup(self->device, &self->memcpy_region, SZ_1G);
68 	region_setup(self->device, &driver->region, SZ_2M);
69 
70 	/* Any IOVA that doesn't overlap memcpy_region and driver->region. */
71 	self->unmapped_iova = 8UL * SZ_1G;
72 
73 	vfio_pci_driver_init(self->device);
74 	self->msi_fd = self->device->msi_eventfds[driver->msi];
75 
76 	/*
77 	 * Use the maximum size supported by the device for memcpy operations,
78 	 * slimmed down to fit into the memcpy region (divided by 2 so src and
79 	 * dst regions do not overlap).
80 	 */
81 	self->size = self->device->driver.max_memcpy_size;
82 	self->size = min(self->size, self->memcpy_region.size / 2);
83 
84 	self->src = self->memcpy_region.vaddr;
85 	self->dst = self->src + self->size;
86 
87 	self->src_iova = to_iova(self->device, self->src);
88 	self->dst_iova = to_iova(self->device, self->dst);
89 }
90 
91 FIXTURE_TEARDOWN(vfio_pci_driver_test)
92 {
93 	struct vfio_pci_driver *driver = &self->device->driver;
94 
95 	vfio_pci_driver_remove(self->device);
96 
97 	region_teardown(self->device, &self->memcpy_region);
98 	region_teardown(self->device, &driver->region);
99 
100 	vfio_pci_device_cleanup(self->device);
101 }
102 
103 TEST_F(vfio_pci_driver_test, init_remove)
104 {
105 	int i;
106 
107 	for (i = 0; i < 10; i++) {
108 		vfio_pci_driver_remove(self->device);
109 		vfio_pci_driver_init(self->device);
110 	}
111 }
112 
113 TEST_F(vfio_pci_driver_test, memcpy_success)
114 {
115 	fcntl_set_nonblock(self->msi_fd);
116 
117 	memset(self->src, 'x', self->size);
118 	memset(self->dst, 'y', self->size);
119 
120 	ASSERT_EQ(0, vfio_pci_driver_memcpy(self->device,
121 					    self->src_iova,
122 					    self->dst_iova,
123 					    self->size));
124 
125 	ASSERT_EQ(0, memcmp(self->src, self->dst, self->size));
126 	ASSERT_NO_MSI(self->msi_fd);
127 }
128 
129 TEST_F(vfio_pci_driver_test, memcpy_from_unmapped_iova)
130 {
131 	fcntl_set_nonblock(self->msi_fd);
132 
133 	/*
134 	 * Ignore the return value since not all devices will detect and report
135 	 * accesses to unmapped IOVAs as errors.
136 	 */
137 	vfio_pci_driver_memcpy(self->device, self->unmapped_iova,
138 			       self->dst_iova, self->size);
139 
140 	ASSERT_NO_MSI(self->msi_fd);
141 }
142 
143 TEST_F(vfio_pci_driver_test, memcpy_to_unmapped_iova)
144 {
145 	fcntl_set_nonblock(self->msi_fd);
146 
147 	/*
148 	 * Ignore the return value since not all devices will detect and report
149 	 * accesses to unmapped IOVAs as errors.
150 	 */
151 	vfio_pci_driver_memcpy(self->device, self->src_iova,
152 			       self->unmapped_iova, self->size);
153 
154 	ASSERT_NO_MSI(self->msi_fd);
155 }
156 
157 TEST_F(vfio_pci_driver_test, send_msi)
158 {
159 	u64 value;
160 
161 	vfio_pci_driver_send_msi(self->device);
162 	ASSERT_EQ(8, read(self->msi_fd, &value, 8));
163 	ASSERT_EQ(1, value);
164 }
165 
166 TEST_F(vfio_pci_driver_test, mix_and_match)
167 {
168 	u64 value;
169 	int i;
170 
171 	for (i = 0; i < 10; i++) {
172 		memset(self->src, 'x', self->size);
173 		memset(self->dst, 'y', self->size);
174 
175 		ASSERT_EQ(0, vfio_pci_driver_memcpy(self->device,
176 						    self->src_iova,
177 						    self->dst_iova,
178 						    self->size));
179 
180 		ASSERT_EQ(0, memcmp(self->src, self->dst, self->size));
181 
182 		vfio_pci_driver_memcpy(self->device,
183 				       self->unmapped_iova,
184 				       self->dst_iova,
185 				       self->size);
186 
187 		vfio_pci_driver_send_msi(self->device);
188 		ASSERT_EQ(8, read(self->msi_fd, &value, 8));
189 		ASSERT_EQ(1, value);
190 	}
191 }
192 
193 TEST_F_TIMEOUT(vfio_pci_driver_test, memcpy_storm, 60)
194 {
195 	struct vfio_pci_driver *driver = &self->device->driver;
196 	u64 total_size;
197 	u64 count;
198 
199 	fcntl_set_nonblock(self->msi_fd);
200 
201 	/*
202 	 * Perform up to 250GiB worth of DMA reads and writes across several
203 	 * memcpy operations. Some devices can support even more but the test
204 	 * will take too long.
205 	 */
206 	total_size = 250UL * SZ_1G;
207 	count = min(total_size / self->size, driver->max_memcpy_count);
208 
209 	printf("Kicking off %lu memcpys of size 0x%lx\n", count, self->size);
210 	vfio_pci_driver_memcpy_start(self->device,
211 				     self->src_iova,
212 				     self->dst_iova,
213 				     self->size, count);
214 
215 	ASSERT_EQ(0, vfio_pci_driver_memcpy_wait(self->device));
216 	ASSERT_NO_MSI(self->msi_fd);
217 }
218 
219 int main(int argc, char *argv[])
220 {
221 	struct vfio_pci_device *device;
222 
223 	device_bdf = vfio_selftests_get_bdf(&argc, argv);
224 
225 	device = vfio_pci_device_init(device_bdf, VFIO_TYPE1_IOMMU);
226 	if (!device->driver.ops) {
227 		fprintf(stderr, "No driver found for device %s\n", device_bdf);
228 		return KSFT_SKIP;
229 	}
230 	vfio_pci_device_cleanup(device);
231 
232 	return test_harness_run(argc, argv);
233 }
234