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