1 // SPDX-License-Identifier: GPL-2.0 AND MIT
2 /*
3 * Copyright © 2022 Intel Corporation
4 */
5
6 #include <uapi/drm/xe_drm.h>
7
8 #include <kunit/test.h>
9 #include <kunit/visibility.h>
10
11 #include "tests/xe_kunit_helpers.h"
12 #include "tests/xe_pci_test.h"
13
14 #include "xe_pci.h"
15 #include "xe_pm.h"
16
p2p_enabled(struct dma_buf_test_params * params)17 static bool p2p_enabled(struct dma_buf_test_params *params)
18 {
19 return IS_ENABLED(CONFIG_PCI_P2PDMA) && params->attach_ops &&
20 params->attach_ops->allow_peer2peer;
21 }
22
is_dynamic(struct dma_buf_test_params * params)23 static bool is_dynamic(struct dma_buf_test_params *params)
24 {
25 return IS_ENABLED(CONFIG_DMABUF_MOVE_NOTIFY) && params->attach_ops &&
26 params->attach_ops->move_notify;
27 }
28
check_residency(struct kunit * test,struct xe_bo * exported,struct xe_bo * imported,struct dma_buf * dmabuf)29 static void check_residency(struct kunit *test, struct xe_bo *exported,
30 struct xe_bo *imported, struct dma_buf *dmabuf)
31 {
32 struct dma_buf_test_params *params = to_dma_buf_test_params(test->priv);
33 u32 mem_type;
34 int ret;
35
36 xe_bo_assert_held(exported);
37 xe_bo_assert_held(imported);
38
39 mem_type = XE_PL_VRAM0;
40 if (!(params->mem_mask & XE_BO_FLAG_VRAM0))
41 /* No VRAM allowed */
42 mem_type = XE_PL_TT;
43 else if (params->force_different_devices && !p2p_enabled(params))
44 /* No P2P */
45 mem_type = XE_PL_TT;
46 else if (params->force_different_devices && !is_dynamic(params) &&
47 (params->mem_mask & XE_BO_FLAG_SYSTEM))
48 /* Pin migrated to TT */
49 mem_type = XE_PL_TT;
50
51 if (!xe_bo_is_mem_type(exported, mem_type)) {
52 KUNIT_FAIL(test, "Exported bo was not in expected memory type.\n");
53 return;
54 }
55
56 if (xe_bo_is_pinned(exported))
57 return;
58
59 /*
60 * Evict exporter. Note that the gem object dma_buf member isn't
61 * set from xe_gem_prime_export(), and it's needed for the move_notify()
62 * functionality, so hack that up here. Evicting the exported bo will
63 * evict also the imported bo through the move_notify() functionality if
64 * importer is on a different device. If they're on the same device,
65 * the exporter and the importer should be the same bo.
66 */
67 swap(exported->ttm.base.dma_buf, dmabuf);
68 ret = xe_bo_evict(exported, true);
69 swap(exported->ttm.base.dma_buf, dmabuf);
70 if (ret) {
71 if (ret != -EINTR && ret != -ERESTARTSYS)
72 KUNIT_FAIL(test, "Evicting exporter failed with err=%d.\n",
73 ret);
74 return;
75 }
76
77 /* Verify that also importer has been evicted to SYSTEM */
78 if (exported != imported && !xe_bo_is_mem_type(imported, XE_PL_SYSTEM)) {
79 KUNIT_FAIL(test, "Importer wasn't properly evicted.\n");
80 return;
81 }
82
83 /* Re-validate the importer. This should move also exporter in. */
84 ret = xe_bo_validate(imported, NULL, false);
85 if (ret) {
86 if (ret != -EINTR && ret != -ERESTARTSYS)
87 KUNIT_FAIL(test, "Validating importer failed with err=%d.\n",
88 ret);
89 return;
90 }
91
92 /*
93 * If on different devices, the exporter is kept in system if
94 * possible, saving a migration step as the transfer is just
95 * likely as fast from system memory.
96 */
97 if (params->mem_mask & XE_BO_FLAG_SYSTEM)
98 KUNIT_EXPECT_TRUE(test, xe_bo_is_mem_type(exported, XE_PL_TT));
99 else
100 KUNIT_EXPECT_TRUE(test, xe_bo_is_mem_type(exported, mem_type));
101
102 if (params->force_different_devices)
103 KUNIT_EXPECT_TRUE(test, xe_bo_is_mem_type(imported, XE_PL_TT));
104 else
105 KUNIT_EXPECT_TRUE(test, exported == imported);
106 }
107
xe_test_dmabuf_import_same_driver(struct xe_device * xe)108 static void xe_test_dmabuf_import_same_driver(struct xe_device *xe)
109 {
110 struct kunit *test = kunit_get_current_test();
111 struct dma_buf_test_params *params = to_dma_buf_test_params(test->priv);
112 struct drm_gem_object *import;
113 struct dma_buf *dmabuf;
114 struct xe_bo *bo;
115 size_t size;
116
117 /* No VRAM on this device? */
118 if (!ttm_manager_type(&xe->ttm, XE_PL_VRAM0) &&
119 (params->mem_mask & XE_BO_FLAG_VRAM0))
120 return;
121
122 size = PAGE_SIZE;
123 if ((params->mem_mask & XE_BO_FLAG_VRAM0) &&
124 xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K)
125 size = SZ_64K;
126
127 kunit_info(test, "running %s\n", __func__);
128 bo = xe_bo_create_user(xe, NULL, NULL, size, DRM_XE_GEM_CPU_CACHING_WC,
129 params->mem_mask);
130 if (IS_ERR(bo)) {
131 KUNIT_FAIL(test, "xe_bo_create() failed with err=%ld\n",
132 PTR_ERR(bo));
133 return;
134 }
135
136 dmabuf = xe_gem_prime_export(&bo->ttm.base, 0);
137 if (IS_ERR(dmabuf)) {
138 KUNIT_FAIL(test, "xe_gem_prime_export() failed with err=%ld\n",
139 PTR_ERR(dmabuf));
140 goto out;
141 }
142
143 import = xe_gem_prime_import(&xe->drm, dmabuf);
144 if (!IS_ERR(import)) {
145 struct xe_bo *import_bo = gem_to_xe_bo(import);
146
147 /*
148 * Did import succeed when it shouldn't due to lack of p2p support?
149 */
150 if (params->force_different_devices &&
151 !p2p_enabled(params) &&
152 !(params->mem_mask & XE_BO_FLAG_SYSTEM)) {
153 KUNIT_FAIL(test,
154 "xe_gem_prime_import() succeeded when it shouldn't have\n");
155 } else {
156 int err;
157
158 /* Is everything where we expect it to be? */
159 xe_bo_lock(import_bo, false);
160 err = xe_bo_validate(import_bo, NULL, false);
161
162 /* Pinning in VRAM is not allowed. */
163 if (!is_dynamic(params) &&
164 params->force_different_devices &&
165 !(params->mem_mask & XE_BO_FLAG_SYSTEM))
166 KUNIT_EXPECT_EQ(test, err, -EINVAL);
167 /* Otherwise only expect interrupts or success. */
168 else if (err && err != -EINTR && err != -ERESTARTSYS)
169 KUNIT_EXPECT_TRUE(test, !err || err == -EINTR ||
170 err == -ERESTARTSYS);
171
172 if (!err)
173 check_residency(test, bo, import_bo, dmabuf);
174 xe_bo_unlock(import_bo);
175 }
176 drm_gem_object_put(import);
177 } else if (PTR_ERR(import) != -EOPNOTSUPP) {
178 /* Unexpected error code. */
179 KUNIT_FAIL(test,
180 "xe_gem_prime_import failed with the wrong err=%ld\n",
181 PTR_ERR(import));
182 } else if (!params->force_different_devices ||
183 p2p_enabled(params) ||
184 (params->mem_mask & XE_BO_FLAG_SYSTEM)) {
185 /* Shouldn't fail if we can reuse same bo, use p2p or use system */
186 KUNIT_FAIL(test, "dynamic p2p attachment failed with err=%ld\n",
187 PTR_ERR(import));
188 }
189 dma_buf_put(dmabuf);
190 out:
191 drm_gem_object_put(&bo->ttm.base);
192 }
193
194 static const struct dma_buf_attach_ops nop2p_attach_ops = {
195 .allow_peer2peer = false,
196 .move_notify = xe_dma_buf_move_notify
197 };
198
199 /*
200 * We test the implementation with bos of different residency and with
201 * importers with different capabilities; some lacking p2p support and some
202 * lacking dynamic capabilities (attach_ops == NULL). We also fake
203 * different devices avoiding the import shortcut that just reuses the same
204 * gem object.
205 */
206 static const struct dma_buf_test_params test_params[] = {
207 {.mem_mask = XE_BO_FLAG_VRAM0,
208 .attach_ops = &xe_dma_buf_attach_ops},
209 {.mem_mask = XE_BO_FLAG_VRAM0,
210 .attach_ops = &xe_dma_buf_attach_ops,
211 .force_different_devices = true},
212
213 {.mem_mask = XE_BO_FLAG_VRAM0,
214 .attach_ops = &nop2p_attach_ops},
215 {.mem_mask = XE_BO_FLAG_VRAM0,
216 .attach_ops = &nop2p_attach_ops,
217 .force_different_devices = true},
218
219 {.mem_mask = XE_BO_FLAG_VRAM0},
220 {.mem_mask = XE_BO_FLAG_VRAM0,
221 .force_different_devices = true},
222
223 {.mem_mask = XE_BO_FLAG_SYSTEM,
224 .attach_ops = &xe_dma_buf_attach_ops},
225 {.mem_mask = XE_BO_FLAG_SYSTEM,
226 .attach_ops = &xe_dma_buf_attach_ops,
227 .force_different_devices = true},
228
229 {.mem_mask = XE_BO_FLAG_SYSTEM,
230 .attach_ops = &nop2p_attach_ops},
231 {.mem_mask = XE_BO_FLAG_SYSTEM,
232 .attach_ops = &nop2p_attach_ops,
233 .force_different_devices = true},
234
235 {.mem_mask = XE_BO_FLAG_SYSTEM},
236 {.mem_mask = XE_BO_FLAG_SYSTEM,
237 .force_different_devices = true},
238
239 {.mem_mask = XE_BO_FLAG_SYSTEM | XE_BO_FLAG_VRAM0,
240 .attach_ops = &xe_dma_buf_attach_ops},
241 {.mem_mask = XE_BO_FLAG_SYSTEM | XE_BO_FLAG_VRAM0,
242 .attach_ops = &xe_dma_buf_attach_ops,
243 .force_different_devices = true},
244
245 {.mem_mask = XE_BO_FLAG_SYSTEM | XE_BO_FLAG_VRAM0,
246 .attach_ops = &nop2p_attach_ops},
247 {.mem_mask = XE_BO_FLAG_SYSTEM | XE_BO_FLAG_VRAM0,
248 .attach_ops = &nop2p_attach_ops,
249 .force_different_devices = true},
250
251 {.mem_mask = XE_BO_FLAG_SYSTEM | XE_BO_FLAG_VRAM0},
252 {.mem_mask = XE_BO_FLAG_SYSTEM | XE_BO_FLAG_VRAM0,
253 .force_different_devices = true},
254
255 {}
256 };
257
dma_buf_run_device(struct xe_device * xe)258 static int dma_buf_run_device(struct xe_device *xe)
259 {
260 const struct dma_buf_test_params *params;
261 struct kunit *test = kunit_get_current_test();
262
263 xe_pm_runtime_get(xe);
264 for (params = test_params; params->mem_mask; ++params) {
265 struct dma_buf_test_params p = *params;
266
267 p.base.id = XE_TEST_LIVE_DMA_BUF;
268 test->priv = &p;
269 xe_test_dmabuf_import_same_driver(xe);
270 }
271 xe_pm_runtime_put(xe);
272
273 /* A non-zero return would halt iteration over driver devices */
274 return 0;
275 }
276
xe_dma_buf_kunit(struct kunit * test)277 static void xe_dma_buf_kunit(struct kunit *test)
278 {
279 struct xe_device *xe = test->priv;
280
281 dma_buf_run_device(xe);
282 }
283
284 static struct kunit_case xe_dma_buf_tests[] = {
285 KUNIT_CASE_PARAM(xe_dma_buf_kunit, xe_pci_live_device_gen_param),
286 {}
287 };
288
289 VISIBLE_IF_KUNIT
290 struct kunit_suite xe_dma_buf_test_suite = {
291 .name = "xe_dma_buf",
292 .test_cases = xe_dma_buf_tests,
293 .init = xe_kunit_helper_xe_device_live_test_init,
294 };
295 EXPORT_SYMBOL_IF_KUNIT(xe_dma_buf_test_suite);
296