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 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 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 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 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 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 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