[PATCH] tests/intel/xe_svm_prefetch: Test to validate SVM prefetch ranges

Ghimiray, Himal Prasad himal.prasad.ghimiray at intel.com
Wed May 7 16:29:33 UTC 2025



On 07-05-2025 21:17, sai.gowtham.ch at intel.com wrote:
> From: Sai Gowtham Ch <sai.gowtham.ch at intel.com>
> 
> Test validates prefetch with single range and multi ranges,
> and stree testing of prefetch by binding address ranges with different memory
> regions to check the expected bahaviour.
> 
> Verified with KMD SVM prefetch changes
> https://gitlab.freedesktop.org/hghimira/kernel/-/tree/latest_v6_to_be_sent?ref_type=heads
> 
> Cc: Himal Prasad Ghimiray <himal.prasad.ghimiray at intel.com>
> Cc: Matthew Brost <matthew.brost at intel.com>
> Signed-off-by: Sai Gowtham Ch <sai.gowtham.ch at intel.com>
> ---
>   tests/intel/xe_svm_prefetch.c | 300 ++++++++++++++++++++++++++++++++++
>   tests/meson.build             |   1 +
>   2 files changed, 301 insertions(+)
>   create mode 100644 tests/intel/xe_svm_prefetch.c
> 
> diff --git a/tests/intel/xe_svm_prefetch.c b/tests/intel/xe_svm_prefetch.c
> new file mode 100644
> index 000000000..3e3853455
> --- /dev/null
> +++ b/tests/intel/xe_svm_prefetch.c
> @@ -0,0 +1,300 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2025 Intel Corporation
> + *
> + * Authors:
> + *      Sai Gowtham Ch <sai.gowtham.ch at intel.com>
> + */
> +
> +#include <fcntl.h>
> +#include <linux/mman.h>
> +#include <pthread.h>
> +#include <string.h>
> +
> +#include "igt.h"
> +#include "lib/igt_syncobj.h"
> +#include "xe_drm.h"
> +#include "xe/xe_ioctl.h"
> +#include "xe/xe_query.h"
> +
> +#define USER_FENCE_VALUE        0xdeadbeefdeadbeefull
> +#define VRAM            (0x1 << 0)
> +
> +#define bind_system_allocator(__sync, __num_sync)                   \
> +	__xe_vm_bind_assert(fd, vm, 0,                              \
> +			0, 0, 0, 0x1ull << 47,  

Should be platform dependent xe->va_bits, any thoughts on making this as 
subtests within xe_exec_system_allocator ?                    \
> +			DRM_XE_VM_BIND_OP_MAP,                      \
> +			DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR,        \
> +			(__sync), (__num_sync), 0, 0)
> +
> +#define unbind_system_allocator()                       \
> +	__xe_vm_bind(fd, vm, 0, 0, 0, 0, 0x1ull << 47,  \
> +			DRM_XE_VM_BIND_OP_UNMAP, 0,     \
> +			NULL, 0, 0, 0, 0)
> +#define WRITE_VALUE(data__, i__)        ({              	\
> +		(data__)->expected_data = rand() << 12 | (i__); \
> +		(data__)->expected_data;                        \
> +})
> +#define READ_VALUE(data__, i__) ((data__)->expected_data)
> +
> +/**
> + * TEST: Test to validate prefetch of svm
> + * Category: Core
> + * Mega feature: General Core features
> + * Sub-category: prefetch
> + * Functionality: svm
> + */
> +struct data {
> +	uint32_t batch[16];
> +	uint64_t pad;
> +	uint32_t data;
> +	uint64_t vm_sync;
> +	uint64_t exec_sync;
> +	uint32_t expected_data;
> +	uint64_t addr;
> +};
> +
> +static void write_dword_m(uint32_t *batch, uint64_t sdi_addr, uint32_t wdata,
> +			  int *idx)
> +{
> +	batch[(*idx)++] = MI_STORE_DWORD_IMM_GEN4;
> +	batch[(*idx)++] = sdi_addr;
> +	batch[(*idx)++] = sdi_addr >> 32;
> +	batch[(*idx)++] = wdata;
> +	batch[(*idx)++] = MI_BATCH_BUFFER_END;
> +}
> +
> +/**
> + * SUBTEST: basic-svm
> + * Description: Test validates basic svm.
> + * Test category: functionality test
> + */
> +static void test_basic(int fd, struct drm_xe_engine_class_instance *eci,
> +		       size_t bo_size, int num_dwords)
> +{
> +	uint64_t addr;
> +	struct drm_xe_sync sync[1] = {
> +		{ .type = DRM_XE_SYNC_TYPE_USER_FENCE, .flags = DRM_XE_SYNC_FLAG_SIGNAL,
> +		  .timeline_value = USER_FENCE_VALUE },
> +	};
> +	struct drm_xe_exec exec = {
> +		.num_batch_buffer = 1,
> +		.num_syncs = 1,
> +		.syncs = to_user_pointer(sync),
> +	};
> +	struct data *data;
> +	uint32_t exec_queues, vm;
> +	size_t aligned_size = bo_size ?: xe_get_default_alignment(fd);
> +
> +	vm = xe_vm_create(fd, DRM_XE_VM_CREATE_FLAG_LR_MODE | DRM_XE_VM_CREATE_FLAG_FAULT_MODE, 0);
> +	data = aligned_alloc(aligned_size, bo_size);
> +	igt_assert(data != NULL);
> +	memset(data, 0, bo_size);
> +
> +	exec_queues = xe_exec_queue_create(fd, vm, eci, 0);
> +
> +	sync[0].addr = to_user_pointer(&data[0].vm_sync);
> +	bind_system_allocator(sync, 1);
> +	xe_wait_ufence(fd, &data[0].vm_sync, USER_FENCE_VALUE, 0, NSEC_PER_SEC);
> +	data[0].vm_sync = 0;
> +
> +	addr = to_user_pointer(data);
> +	for (int i = 0; i <= num_dwords; i++) {
> +		uint64_t batch_offset = (char *)&data[i].batch - (char *)data;
> +		uint64_t batch_addr = addr + batch_offset;
> +		uint64_t sdi_offset = (char *)&(data[i].data) - (char *)data;
> +		uint64_t sdi_addr = addr + sdi_offset;
> +		int b = 0;
> +
> +		write_dword_m(data[i].batch, sdi_addr, WRITE_VALUE(&data[i], i), &b);
> +		igt_assert(b <= ARRAY_SIZE(data[i].batch));
> +
> +		exec.exec_queue_id = exec_queues;
> +		exec.address = batch_addr;
> +		sync[0].addr = addr + (char *)&data[i].exec_sync - (char *)data;
> +		xe_exec(fd, &exec);
> +		xe_wait_ufence(fd, &data[i].exec_sync, USER_FENCE_VALUE, exec_queues, NSEC_PER_SEC);
> +		data[i].exec_sync = 0;
> +		igt_assert_eq(data[i].data, READ_VALUE(&data[i], i));
> +	}
> +	unbind_system_allocator();
> +	free(data);
> +	xe_exec_queue_destroy(fd, exec_queues);
> +	xe_vm_destroy(fd, vm);
> +}
> +/**
> + * SUBTEST: prefetch-smem-%s
> + * Description: Test to validate functionality of Prefetch using SVM of size arg[1] at smem region
> + * Test category: functionality test
> + *
> + * SUBTEST: prefetch-vram-%s
> + * Description: Test to validate functionality of Prefetch using SVM of size arg[1] at vram region
> + * Test category: functionality test
> + *
> + * SUBTEST: bind-vram-%s
> + * Description: Validate prefetch-smem of size arg[1] by rebinding them with prefetch-vram to
> + *		check the behaviour, ideal behaviour is to migrate ranges
> + * Test category: functionality test
> + *
> + * SUBTEST: bind-smem-%s
> + * Description: Validate prefetch-smem of size arg[1] by rebinding them with prefetch-vram
> + *		check the behaviour, ideal behaviour is to migrate ranges
> + * Test category: functionality test
> + *
> + * SUBTEST: multi-range-smem-%s
> + * Description: Prefetch of mutliple ranges within arg[1] size to validate multiple ranges are created
> + * Test category: functionality test
> + *
> + * SUBTEST: multi-range-vram-%s
> + * Description: Prefetch of mutliple ranges within arg[1] size and check if multiple ranges are created
> + * Test category: functionality test
> + *
> + * SUBTEST: multi-range-bind-vram-%s
> + * Description: Validate multiple ranges of size arg[1] by binding them with multiple ranges at vram
> + * Test category: functionality test
> + *
> + * SUBTEST: multi-range-bind-smem-%s
> + * Description: Validate multiple ranges of size arg[1] by binding them with multiple ranges at smem
> + * Test category: functionality test
> + *
> + * arg[1]:
> + *
> + * @SZ_4K: SZ_4K
> + * @SZ_64K: SZ_64K
> + * @SZ_2M: SZ_2M
> + */
> +
> +#define MAX_BATCH_DWORDS 16
> +static void prefetch(int fd, struct drm_xe_engine_class_instance *eci,
> +		     size_t bo_size, unsigned int flags, int num_dwords, bool bind)
> +{
> +	struct data *data;
> +	uint64_t target_addr;
> +	uint64_t addr;
> +	u64 *exec_ufence = NULL;
> +	struct drm_xe_sync sync[1] = {
> +		{ .type = DRM_XE_SYNC_TYPE_USER_FENCE, .flags = DRM_XE_SYNC_FLAG_SIGNAL,
> +		  .timeline_value = USER_FENCE_VALUE },
> +	};
> +	struct drm_xe_exec exec = {
> +		.num_batch_buffer = 1,
> +		.num_syncs = 1,
> +		.syncs = to_user_pointer(sync),
> +	};
> +	size_t slice_size = bo_size;
> +	uint64_t batch_addr;
> +	uint32_t exec_queues, expected, vm, *result_ptr, *batch;
> +	size_t aligned_size = bo_size ?: xe_get_default_alignment(fd);
> +	int b;
> +
> +	bo_size = bo_size * num_dwords;
> +
> +	vm = xe_vm_create(fd, DRM_XE_VM_CREATE_FLAG_LR_MODE | DRM_XE_VM_CREATE_FLAG_FAULT_MODE, 0);
> +	data = aligned_alloc(aligned_size, bo_size);
> +	memset(data, 0, bo_size);
> +	addr = to_user_pointer(data);
> +
> +	exec_queues = xe_exec_queue_create(fd, vm, eci, 0);
> +	sync[0].addr = to_user_pointer(&data[0].vm_sync);
> +	bind_system_allocator(sync, 1);
> +	xe_wait_ufence(fd, &data[0].vm_sync, USER_FENCE_VALUE, 0, NSEC_PER_SEC);
> +	data[0].vm_sync = 0;
> +	exec_ufence = mmap(NULL, SZ_4K, PROT_READ |
> +				 PROT_WRITE, MAP_SHARED |
> +				 MAP_ANONYMOUS, -1, 0);
> +	igt_assert(exec_ufence != MAP_FAILED);
> +	memset(exec_ufence, 0, SZ_4K);
> +
> +	for (int i = 0; i < num_dwords; i++) {
> +		batch = (uint32_t *)((uint8_t *)data + i * slice_size);
> +		target_addr = addr + i * slice_size + 0x100;
> +
> +		b = 0;
> +		igt_assert(b + 5 <= MAX_BATCH_DWORDS);
> +		write_dword_m(batch, target_addr, 0xDEADBEEF + i, &b);
> +	}
> +
> +	sync[0].addr = to_user_pointer(exec_ufence);
> +	xe_vm_prefetch_async(fd, vm, 0, 0, addr, bo_size, sync, 1, flags & VRAM ? 1 : 0);
> +	xe_wait_ufence(fd, exec_ufence, USER_FENCE_VALUE, 0, NSEC_PER_SEC);
> +	if (bind) {
> +		exec_ufence[0] = 0;
> +		sync[0].addr = to_user_pointer(exec_ufence);
> +		xe_vm_prefetch_async(fd, vm, 0, 0, addr, bo_size, sync, 1, flags & VRAM ? 0 : 1);
> +		xe_wait_ufence(fd, exec_ufence, USER_FENCE_VALUE, 0, NSEC_PER_SEC);
> +		exec_ufence[0] = 0;
> +	}
> +
> +	exec.exec_queue_id = exec_queues;
> +	for (int i = 0; i < num_dwords; i++) {
> +		result_ptr = (uint32_t *)((uint8_t *)data + i * slice_size + 0x100);
> +		expected = 0xDEADBEEF + i;
> +
> +		batch_addr = addr + i * slice_size;
> +		exec.address = batch_addr;
> +		exec_ufence[0] = 0;
> +		sync[0].addr = to_user_pointer(exec_ufence);
> +		xe_exec(fd, &exec);
> +		xe_wait_ufence(fd, exec_ufence, USER_FENCE_VALUE, exec_queues, NSEC_PER_SEC);
> +		exec_ufence[0] = 0;
> +
> +		igt_assert_eq(*result_ptr, expected);
> +	}
> +	xe_exec_queue_destroy(fd, exec_queues);
> +	unbind_system_allocator();
> +	munmap(data, bo_size);
> +	xe_vm_destroy(fd, vm);
> +}
> +
> +igt_main
> +{
> +	int fd;
> +	struct drm_xe_engine *engine;
> +	const struct mode {
> +		const char *name;
> +		size_t size;
> +	} mode[] = {
> +		{ "SZ_4K", SZ_4K},
> +		{ "SZ_64K", SZ_64K},
> +		{ "SZ_2M", SZ_2M},
> +		{},
> +	}, *m;
> +
> +	igt_fixture {
> +		fd = drm_open_driver(DRIVER_XE);
> +		engine = xe_engine(fd, 1);
> +	}
> +
> +	igt_subtest("basic-svm")
> +		test_basic(fd, &engine->instance, SZ_64K, 1);
> +
> +	for ( m = mode; m->name; m++) {
> +		igt_subtest_f("prefetch-smem-%s", m->name)
> +			prefetch(fd, &engine->instance, m->size, 0, 1, 0);
> +
> +		igt_subtest_f("prefetch-vram-%s", m->name)
> +			prefetch(fd, &engine->instance, m->size, VRAM, 1, 0);
> +
> +		igt_subtest_f("bind-vram-%s", m->name)
> +			prefetch(fd, &engine->instance, m->size, 0, 1, 1);
> +
> +		igt_subtest_f("bind-smem-%s", m->name)
> +			prefetch(fd, &engine->instance, m->size, 0, 1, 1);
> +
> +		igt_subtest_f("multi-range-smem-%s", m->name)
> +			prefetch(fd, &engine->instance, m->size, 0, 10, 0);
> +
> +		igt_subtest_f("multi-range-vram-%s", m->name)
> +			prefetch(fd, &engine->instance, m->size, VRAM, 10, 0);
> +
> +		igt_subtest_f("multi-range-bind-vram-%s", m->name)
> +			prefetch(fd, &engine->instance, m->size, 0, 10, 1);
> +
> +		igt_subtest_f("multi-range-bind-smem-%s", m->name)
> +			prefetch(fd, &engine->instance, m->size, VRAM, 10, 1);
> +	}
> +
> +	igt_fixture {
> +		close(fd);
> +	}
> +}
> diff --git a/tests/meson.build b/tests/meson.build
> index 20ddddb89..84e109803 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -324,6 +324,7 @@ intel_xe_progs = [
>   	'xe_sriov_auto_provisioning',
>   	'xe_sriov_flr',
>   	'xe_sriov_scheduling',
> +        'xe_svm_prefetch',
>   	'xe_sysfs_defaults',
>   	'xe_sysfs_preempt_timeout',
>   	'xe_sysfs_scheduler',



More information about the igt-dev mailing list