296 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			296 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0 AND MIT
 | |
| /*
 | |
|  * Copyright © 2022 Intel Corporation
 | |
|  */
 | |
| 
 | |
| #include <uapi/drm/xe_drm.h>
 | |
| 
 | |
| #include <kunit/test.h>
 | |
| #include <kunit/visibility.h>
 | |
| 
 | |
| #include "tests/xe_kunit_helpers.h"
 | |
| #include "tests/xe_pci_test.h"
 | |
| 
 | |
| #include "xe_pci.h"
 | |
| #include "xe_pm.h"
 | |
| 
 | |
| static bool p2p_enabled(struct dma_buf_test_params *params)
 | |
| {
 | |
| 	return IS_ENABLED(CONFIG_PCI_P2PDMA) && params->attach_ops &&
 | |
| 		params->attach_ops->allow_peer2peer;
 | |
| }
 | |
| 
 | |
| static bool is_dynamic(struct dma_buf_test_params *params)
 | |
| {
 | |
| 	return IS_ENABLED(CONFIG_DMABUF_MOVE_NOTIFY) && params->attach_ops &&
 | |
| 		params->attach_ops->move_notify;
 | |
| }
 | |
| 
 | |
| static void check_residency(struct kunit *test, struct xe_bo *exported,
 | |
| 			    struct xe_bo *imported, struct dma_buf *dmabuf)
 | |
| {
 | |
| 	struct dma_buf_test_params *params = to_dma_buf_test_params(test->priv);
 | |
| 	u32 mem_type;
 | |
| 	int ret;
 | |
| 
 | |
| 	xe_bo_assert_held(exported);
 | |
| 	xe_bo_assert_held(imported);
 | |
| 
 | |
| 	mem_type = XE_PL_VRAM0;
 | |
| 	if (!(params->mem_mask & XE_BO_FLAG_VRAM0))
 | |
| 		/* No VRAM allowed */
 | |
| 		mem_type = XE_PL_TT;
 | |
| 	else if (params->force_different_devices && !p2p_enabled(params))
 | |
| 		/* No P2P */
 | |
| 		mem_type = XE_PL_TT;
 | |
| 	else if (params->force_different_devices && !is_dynamic(params) &&
 | |
| 		 (params->mem_mask & XE_BO_FLAG_SYSTEM))
 | |
| 		/* Pin migrated to TT */
 | |
| 		mem_type = XE_PL_TT;
 | |
| 
 | |
| 	if (!xe_bo_is_mem_type(exported, mem_type)) {
 | |
| 		KUNIT_FAIL(test, "Exported bo was not in expected memory type.\n");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (xe_bo_is_pinned(exported))
 | |
| 		return;
 | |
| 
 | |
| 	/*
 | |
| 	 * Evict exporter. Note that the gem object dma_buf member isn't
 | |
| 	 * set from xe_gem_prime_export(), and it's needed for the move_notify()
 | |
| 	 * functionality, so hack that up here. Evicting the exported bo will
 | |
| 	 * evict also the imported bo through the move_notify() functionality if
 | |
| 	 * importer is on a different device. If they're on the same device,
 | |
| 	 * the exporter and the importer should be the same bo.
 | |
| 	 */
 | |
| 	swap(exported->ttm.base.dma_buf, dmabuf);
 | |
| 	ret = xe_bo_evict(exported, true);
 | |
| 	swap(exported->ttm.base.dma_buf, dmabuf);
 | |
| 	if (ret) {
 | |
| 		if (ret != -EINTR && ret != -ERESTARTSYS)
 | |
| 			KUNIT_FAIL(test, "Evicting exporter failed with err=%d.\n",
 | |
| 				   ret);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* Verify that also importer has been evicted to SYSTEM */
 | |
| 	if (exported != imported && !xe_bo_is_mem_type(imported, XE_PL_SYSTEM)) {
 | |
| 		KUNIT_FAIL(test, "Importer wasn't properly evicted.\n");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* Re-validate the importer. This should move also exporter in. */
 | |
| 	ret = xe_bo_validate(imported, NULL, false);
 | |
| 	if (ret) {
 | |
| 		if (ret != -EINTR && ret != -ERESTARTSYS)
 | |
| 			KUNIT_FAIL(test, "Validating importer failed with err=%d.\n",
 | |
| 				   ret);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * If on different devices, the exporter is kept in system  if
 | |
| 	 * possible, saving a migration step as the transfer is just
 | |
| 	 * likely as fast from system memory.
 | |
| 	 */
 | |
| 	if (params->mem_mask & XE_BO_FLAG_SYSTEM)
 | |
| 		KUNIT_EXPECT_TRUE(test, xe_bo_is_mem_type(exported, XE_PL_TT));
 | |
| 	else
 | |
| 		KUNIT_EXPECT_TRUE(test, xe_bo_is_mem_type(exported, mem_type));
 | |
| 
 | |
| 	if (params->force_different_devices)
 | |
| 		KUNIT_EXPECT_TRUE(test, xe_bo_is_mem_type(imported, XE_PL_TT));
 | |
| 	else
 | |
| 		KUNIT_EXPECT_TRUE(test, exported == imported);
 | |
| }
 | |
| 
 | |
| static void xe_test_dmabuf_import_same_driver(struct xe_device *xe)
 | |
| {
 | |
| 	struct kunit *test = kunit_get_current_test();
 | |
| 	struct dma_buf_test_params *params = to_dma_buf_test_params(test->priv);
 | |
| 	struct drm_gem_object *import;
 | |
| 	struct dma_buf *dmabuf;
 | |
| 	struct xe_bo *bo;
 | |
| 	size_t size;
 | |
| 
 | |
| 	/* No VRAM on this device? */
 | |
| 	if (!ttm_manager_type(&xe->ttm, XE_PL_VRAM0) &&
 | |
| 	    (params->mem_mask & XE_BO_FLAG_VRAM0))
 | |
| 		return;
 | |
| 
 | |
| 	size = PAGE_SIZE;
 | |
| 	if ((params->mem_mask & XE_BO_FLAG_VRAM0) &&
 | |
| 	    xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K)
 | |
| 		size = SZ_64K;
 | |
| 
 | |
| 	kunit_info(test, "running %s\n", __func__);
 | |
| 	bo = xe_bo_create_user(xe, NULL, NULL, size, DRM_XE_GEM_CPU_CACHING_WC,
 | |
| 			       params->mem_mask);
 | |
| 	if (IS_ERR(bo)) {
 | |
| 		KUNIT_FAIL(test, "xe_bo_create() failed with err=%ld\n",
 | |
| 			   PTR_ERR(bo));
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	dmabuf = xe_gem_prime_export(&bo->ttm.base, 0);
 | |
| 	if (IS_ERR(dmabuf)) {
 | |
| 		KUNIT_FAIL(test, "xe_gem_prime_export() failed with err=%ld\n",
 | |
| 			   PTR_ERR(dmabuf));
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	import = xe_gem_prime_import(&xe->drm, dmabuf);
 | |
| 	if (!IS_ERR(import)) {
 | |
| 		struct xe_bo *import_bo = gem_to_xe_bo(import);
 | |
| 
 | |
| 		/*
 | |
| 		 * Did import succeed when it shouldn't due to lack of p2p support?
 | |
| 		 */
 | |
| 		if (params->force_different_devices &&
 | |
| 		    !p2p_enabled(params) &&
 | |
| 		    !(params->mem_mask & XE_BO_FLAG_SYSTEM)) {
 | |
| 			KUNIT_FAIL(test,
 | |
| 				   "xe_gem_prime_import() succeeded when it shouldn't have\n");
 | |
| 		} else {
 | |
| 			int err;
 | |
| 
 | |
| 			/* Is everything where we expect it to be? */
 | |
| 			xe_bo_lock(import_bo, false);
 | |
| 			err = xe_bo_validate(import_bo, NULL, false);
 | |
| 
 | |
| 			/* Pinning in VRAM is not allowed. */
 | |
| 			if (!is_dynamic(params) &&
 | |
| 			    params->force_different_devices &&
 | |
| 			    !(params->mem_mask & XE_BO_FLAG_SYSTEM))
 | |
| 				KUNIT_EXPECT_EQ(test, err, -EINVAL);
 | |
| 			/* Otherwise only expect interrupts or success. */
 | |
| 			else if (err && err != -EINTR && err != -ERESTARTSYS)
 | |
| 				KUNIT_EXPECT_TRUE(test, !err || err == -EINTR ||
 | |
| 						  err == -ERESTARTSYS);
 | |
| 
 | |
| 			if (!err)
 | |
| 				check_residency(test, bo, import_bo, dmabuf);
 | |
| 			xe_bo_unlock(import_bo);
 | |
| 		}
 | |
| 		drm_gem_object_put(import);
 | |
| 	} else if (PTR_ERR(import) != -EOPNOTSUPP) {
 | |
| 		/* Unexpected error code. */
 | |
| 		KUNIT_FAIL(test,
 | |
| 			   "xe_gem_prime_import failed with the wrong err=%ld\n",
 | |
| 			   PTR_ERR(import));
 | |
| 	} else if (!params->force_different_devices ||
 | |
| 		   p2p_enabled(params) ||
 | |
| 		   (params->mem_mask & XE_BO_FLAG_SYSTEM)) {
 | |
| 		/* Shouldn't fail if we can reuse same bo, use p2p or use system */
 | |
| 		KUNIT_FAIL(test, "dynamic p2p attachment failed with err=%ld\n",
 | |
| 			   PTR_ERR(import));
 | |
| 	}
 | |
| 	dma_buf_put(dmabuf);
 | |
| out:
 | |
| 	drm_gem_object_put(&bo->ttm.base);
 | |
| }
 | |
| 
 | |
| static const struct dma_buf_attach_ops nop2p_attach_ops = {
 | |
| 	.allow_peer2peer = false,
 | |
| 	.move_notify = xe_dma_buf_move_notify
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * We test the implementation with bos of different residency and with
 | |
|  * importers with different capabilities; some lacking p2p support and some
 | |
|  * lacking dynamic capabilities (attach_ops == NULL). We also fake
 | |
|  * different devices avoiding the import shortcut that just reuses the same
 | |
|  * gem object.
 | |
|  */
 | |
| static const struct dma_buf_test_params test_params[] = {
 | |
| 	{.mem_mask = XE_BO_FLAG_VRAM0,
 | |
| 	 .attach_ops = &xe_dma_buf_attach_ops},
 | |
| 	{.mem_mask = XE_BO_FLAG_VRAM0,
 | |
| 	 .attach_ops = &xe_dma_buf_attach_ops,
 | |
| 	 .force_different_devices = true},
 | |
| 
 | |
| 	{.mem_mask = XE_BO_FLAG_VRAM0,
 | |
| 	 .attach_ops = &nop2p_attach_ops},
 | |
| 	{.mem_mask = XE_BO_FLAG_VRAM0,
 | |
| 	 .attach_ops = &nop2p_attach_ops,
 | |
| 	 .force_different_devices = true},
 | |
| 
 | |
| 	{.mem_mask = XE_BO_FLAG_VRAM0},
 | |
| 	{.mem_mask = XE_BO_FLAG_VRAM0,
 | |
| 	 .force_different_devices = true},
 | |
| 
 | |
| 	{.mem_mask = XE_BO_FLAG_SYSTEM,
 | |
| 	 .attach_ops = &xe_dma_buf_attach_ops},
 | |
| 	{.mem_mask = XE_BO_FLAG_SYSTEM,
 | |
| 	 .attach_ops = &xe_dma_buf_attach_ops,
 | |
| 	 .force_different_devices = true},
 | |
| 
 | |
| 	{.mem_mask = XE_BO_FLAG_SYSTEM,
 | |
| 	 .attach_ops = &nop2p_attach_ops},
 | |
| 	{.mem_mask = XE_BO_FLAG_SYSTEM,
 | |
| 	 .attach_ops = &nop2p_attach_ops,
 | |
| 	 .force_different_devices = true},
 | |
| 
 | |
| 	{.mem_mask = XE_BO_FLAG_SYSTEM},
 | |
| 	{.mem_mask = XE_BO_FLAG_SYSTEM,
 | |
| 	 .force_different_devices = true},
 | |
| 
 | |
| 	{.mem_mask = XE_BO_FLAG_SYSTEM | XE_BO_FLAG_VRAM0,
 | |
| 	 .attach_ops = &xe_dma_buf_attach_ops},
 | |
| 	{.mem_mask = XE_BO_FLAG_SYSTEM | XE_BO_FLAG_VRAM0,
 | |
| 	 .attach_ops = &xe_dma_buf_attach_ops,
 | |
| 	 .force_different_devices = true},
 | |
| 
 | |
| 	{.mem_mask = XE_BO_FLAG_SYSTEM | XE_BO_FLAG_VRAM0,
 | |
| 	 .attach_ops = &nop2p_attach_ops},
 | |
| 	{.mem_mask = XE_BO_FLAG_SYSTEM | XE_BO_FLAG_VRAM0,
 | |
| 	 .attach_ops = &nop2p_attach_ops,
 | |
| 	 .force_different_devices = true},
 | |
| 
 | |
| 	{.mem_mask = XE_BO_FLAG_SYSTEM | XE_BO_FLAG_VRAM0},
 | |
| 	{.mem_mask = XE_BO_FLAG_SYSTEM | XE_BO_FLAG_VRAM0,
 | |
| 	 .force_different_devices = true},
 | |
| 
 | |
| 	{}
 | |
| };
 | |
| 
 | |
| static int dma_buf_run_device(struct xe_device *xe)
 | |
| {
 | |
| 	const struct dma_buf_test_params *params;
 | |
| 	struct kunit *test = kunit_get_current_test();
 | |
| 
 | |
| 	xe_pm_runtime_get(xe);
 | |
| 	for (params = test_params; params->mem_mask; ++params) {
 | |
| 		struct dma_buf_test_params p = *params;
 | |
| 
 | |
| 		p.base.id = XE_TEST_LIVE_DMA_BUF;
 | |
| 		test->priv = &p;
 | |
| 		xe_test_dmabuf_import_same_driver(xe);
 | |
| 	}
 | |
| 	xe_pm_runtime_put(xe);
 | |
| 
 | |
| 	/* A non-zero return would halt iteration over driver devices */
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void xe_dma_buf_kunit(struct kunit *test)
 | |
| {
 | |
| 	struct xe_device *xe = test->priv;
 | |
| 
 | |
| 	dma_buf_run_device(xe);
 | |
| }
 | |
| 
 | |
| static struct kunit_case xe_dma_buf_tests[] = {
 | |
| 	KUNIT_CASE_PARAM(xe_dma_buf_kunit, xe_pci_live_device_gen_param),
 | |
| 	{}
 | |
| };
 | |
| 
 | |
| VISIBLE_IF_KUNIT
 | |
| struct kunit_suite xe_dma_buf_test_suite = {
 | |
| 	.name = "xe_dma_buf",
 | |
| 	.test_cases = xe_dma_buf_tests,
 | |
| 	.init = xe_kunit_helper_xe_device_live_test_init,
 | |
| };
 | |
| EXPORT_SYMBOL_IF_KUNIT(xe_dma_buf_test_suite);
 |