296 lines · c
1// SPDX-License-Identifier: GPL-2.0 AND MIT2/*3 * Copyright © 2022 Intel Corporation4 */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 17static 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 23static 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 29static 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't61 * 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 will63 * evict also the imported bo through the move_notify() functionality if64 * 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 if94 * possible, saving a migration step as the transfer is just95 * 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 else100 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 else105 KUNIT_EXPECT_TRUE(test, exported == imported);106}107 108static 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);190out:191 drm_gem_object_put(&bo->ttm.base);192}193 194static const struct dma_buf_attach_ops nop2p_attach_ops = {195 .allow_peer2peer = false,196 .move_notify = xe_dma_buf_move_notify197};198 199/*200 * We test the implementation with bos of different residency and with201 * importers with different capabilities; some lacking p2p support and some202 * lacking dynamic capabilities (attach_ops == NULL). We also fake203 * different devices avoiding the import shortcut that just reuses the same204 * gem object.205 */206static 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 258static 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 277static 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 284static struct kunit_case xe_dma_buf_tests[] = {285 KUNIT_CASE_PARAM(xe_dma_buf_kunit, xe_pci_live_device_gen_param),286 {}287};288 289VISIBLE_IF_KUNIT290struct 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};295EXPORT_SYMBOL_IF_KUNIT(xe_dma_buf_test_suite);296