From: Gyeyoung Baek <gye976@gmail.com>
To: igt-dev@lists.freedesktop.org
Cc: imagination@lists.freedesktop.org, alessio.belle@imgtec.com,
luigi.santivetti@imgtec.com, donald.robson@imgtec.com,
robert.mazur@imgtec.com, matt.coster@imgtec.com,
sarah.walker@imgtec.com, frank.binns@imgtec.com,
Gyeyoung Baek <gye976@gmail.com>
Subject: [PATCH i-g-t] tests/imagination: Add VM map and remap tests
Date: Sun, 27 Sep 2026 00:23:41 +0900 [thread overview]
Message-ID: <20260926152341.783867-1-gye976@gmail.com> (raw)
Add pvr_vm_map, which tests DRM_IOCTL_PVR_VM_MAP and
DRM_IOCTL_PVR_VM_UNMAP directly.
For each of the following three cases, test five scenarios and check
that the memory of the buffer objects is returned in each of them:
1. Inside one 2MiB region
2. Across a 2MiB boundary
3. Across a 1GiB boundary
- map-*: map a range and unmap it.
- replace-*: map a range that completely covers a smaller existing
mapping.
- remap-next-*: map over the left part of a mapping, which splits it.
- remap-prev-*: map over the right part of a mapping, which splits it.
- remap-both-*: map over the middle of a mapping, which splits it.
See the following document for the overlap cases:
https://docs.kernel.org/gpu/drm-mm.html#drm-gpuvm
Signed-off-by: Gyeyoung Baek <gye976@gmail.com>
---
tests/imagination/meson.build | 1 +
tests/imagination/pvr_vm_map.c | 192 +++++++++++++++++++++++++++++++++
2 files changed, 193 insertions(+)
create mode 100644 tests/imagination/pvr_vm_map.c
diff --git a/tests/imagination/meson.build b/tests/imagination/meson.build
index 7ae8ddd0a..80d807c28 100644
--- a/tests/imagination/meson.build
+++ b/tests/imagination/meson.build
@@ -8,6 +8,7 @@ pvr_progs = [ 'pvr_dev_query',
'pvr_runtime_info',
'pvr_submit_jobs',
'pvr_vm_context',
+ 'pvr_vm_map',
]
pvr_deps = test_deps
diff --git a/tests/imagination/pvr_vm_map.c b/tests/imagination/pvr_vm_map.c
new file mode 100644
index 000000000..8cf8cc7a4
--- /dev/null
+++ b/tests/imagination/pvr_vm_map.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include "igt.h"
+#include "igt_os.h"
+#include "igt_pvr.h"
+#include "igt_sizes.h"
+
+#include "pvr_drm.h"
+
+/*
+ * vm_map/unmap/remap() are each run on a range of RANGE_PAGES pages (p0..p3),
+ * placed in one of three ways. The drawings are not to scale.
+ *
+ * in-2m Inside one 2MiB region. No boundary is crossed.
+ *
+ * |<------------ 2MiB region ----------->|
+ * | [p0|p1|p2|p3] |
+ *
+ * cross-2m Across a 2MiB boundary.
+ *
+ * |<--- 2MiB region A --->|<--- 2MiB region B --->|
+ * [p0|p1]|[p2|p3]
+ * ^ 2MiB boundary
+ *
+ * cross-1g Across a 1GiB boundary, which is also a 2MiB boundary.
+ *
+ * |<--- 1GiB region A --->|<--- 1GiB region B --->|
+ * [p0|p1]|[p2|p3]
+ * ^ 1GiB boundary
+ */
+
+#define RANGE_PAGES 4
+#define LEAK_BO_SIZE SZ_32M
+#define LEAK_THRESHOLD SZ_8M
+
+static uint64_t align_up(uint64_t value, uint64_t align)
+{
+ return (value + align - 1) & ~(align - 1);
+}
+
+static void assert_shmem_released(uint64_t baseline)
+{
+ uint64_t now = 0;
+
+ for (int i = 0; i < 20; i++) {
+ now = igt_get_meminfo("Shmem");
+ if (now < baseline + LEAK_THRESHOLD)
+ return;
+ usleep(100 * 1000);
+ }
+
+ igt_assert_f(false, "%" PRIu64 " bytes of shmem still held after close\n", now - baseline);
+}
+
+static void map_range(int fd, uint64_t addr, uint64_t size)
+{
+ const uint64_t baseline = igt_get_meminfo("Shmem");
+ const uint32_t vm_ctx = igt_pvr_ioctl_create_vm_context(fd, 0);
+ size_t bo_size = LEAK_BO_SIZE;
+ const uint32_t handle = igt_pvr_ioctl_create_bo(fd, &bo_size);
+
+ igt_pvr_ioctl_vm_map(fd, vm_ctx, handle, addr, 0, size);
+ igt_pvr_ioctl_vm_unmap(fd, vm_ctx, addr, size);
+
+ gem_close(fd, handle);
+ igt_pvr_ioctl_destroy_vm_context(fd, vm_ctx, 0);
+
+ assert_shmem_released(baseline);
+}
+
+/*
+ * A new mapping is mapped over an old one. The overlaps follow "DOC: Split and Merge" in
+ * drivers/gpu/drm/drm_gpuvm.c: replace is case 14, remap-next 5, remap-prev 7, remap-both 11.
+ */
+static void remap_range(int fd, uint64_t old_addr, uint64_t old_size,
+ uint64_t new_addr, uint64_t new_size)
+{
+ const uint64_t baseline = igt_get_meminfo("Shmem");
+ const uint32_t vm_ctx = igt_pvr_ioctl_create_vm_context(fd, 0);
+ const uint64_t old_end = old_addr + old_size;
+ const uint64_t new_end = new_addr + new_size;
+ size_t size_a = LEAK_BO_SIZE;
+ size_t size_b = new_size;
+ const uint32_t handle_a = igt_pvr_ioctl_create_bo(fd, &size_a);
+ const uint32_t handle_b = igt_pvr_ioctl_create_bo(fd, &size_b);
+
+ igt_pvr_ioctl_vm_map(fd, vm_ctx, handle_a, old_addr, 0, old_size);
+ igt_pvr_ioctl_vm_map(fd, vm_ctx, handle_b, new_addr, 0, new_size);
+
+ if (new_addr > old_addr)
+ igt_pvr_ioctl_vm_unmap(fd, vm_ctx, old_addr, new_addr - old_addr);
+ igt_pvr_ioctl_vm_unmap(fd, vm_ctx, new_addr, new_size);
+ if (new_end < old_end)
+ igt_pvr_ioctl_vm_unmap(fd, vm_ctx, new_end, old_end - new_end);
+
+ gem_close(fd, handle_a);
+ gem_close(fd, handle_b);
+ igt_pvr_ioctl_destroy_vm_context(fd, vm_ctx, 0);
+
+ assert_shmem_released(baseline);
+}
+
+struct place {
+ const char *name;
+ uint64_t addr;
+};
+
+int igt_main()
+{
+ struct place places[] = { { "in-2m", 0 }, { "cross-2m", 0 }, { "cross-1g", 0 } };
+ struct drm_pvr_heap heap;
+ uint64_t size;
+ int fd;
+
+ igt_fixture()
+ {
+ fd = drm_open_driver(DRIVER_POWERVR);
+ heap = igt_pvr_find_general_heap(fd);
+
+ size = RANGE_PAGES * sysconf(_SC_PAGESIZE);
+ places[0].addr = align_up(heap.base, SZ_2M) + SZ_1M;
+ places[1].addr = align_up(heap.base + 1, SZ_2M) - size / 2;
+ places[2].addr = align_up(heap.base + 1, SZ_1G) - size / 2;
+
+ igt_require(places[2].addr + size <= heap.base + heap.size);
+ }
+
+ for (size_t i = 0; i < ARRAY_SIZE(places); i++) {
+ const char *name = places[i].name;
+ const uint64_t addr = places[i].addr;
+
+ igt_describe_f("A range can be mapped and unmapped, and the page tables it needs are available (%s)",
+ name);
+ igt_subtest_f("map-%s", name)
+ {
+ /*
+ * map |nnnnnnnn| a range is mapped, then unmapped
+ */
+ map_range(fd, addr, size);
+ }
+
+ igt_describe_f("A mapping that covers an old one replaces it and leaves nothing of it behind (%s)",
+ name);
+ igt_subtest_f("replace-%s", name)
+ {
+ /*
+ * replace | oooo | o = old mapping, n = new mapping
+ * |nnnnnnnn| nothing is left of the old one
+ */
+ remap_range(fd, addr + size / 4, size / 2, addr, size);
+ }
+
+ igt_describe_f("Mapping over the left part of a mapping leaves its right part, and the original "
+ "buffer object is released once everything is closed (%s)", name);
+ igt_subtest_f("remap-next-%s", name)
+ {
+ /*
+ * remap-next |oooooooo| the right part of the old mapping is left
+ * |nnnn |
+ */
+ remap_range(fd, addr, size, addr, size / 2);
+ }
+
+ igt_describe_f("Mapping over the right part of a mapping leaves its left part, and the original "
+ "buffer object is released once everything is closed (%s)", name);
+ igt_subtest_f("remap-prev-%s", name)
+ {
+ /*
+ * remap-prev |oooooooo| the left part of the old mapping is left
+ * | nnnn|
+ */
+ remap_range(fd, addr, size, addr + size / 2, size / 2);
+ }
+
+ igt_describe_f("Mapping over the middle of a mapping leaves its left and right parts, and the "
+ "original buffer object is released once everything is closed (%s)",
+ name);
+ igt_subtest_f("remap-both-%s", name)
+ {
+ /*
+ * remap-both |oooooooo| left and right parts of the old one are left
+ * | nn |
+ */
+ remap_range(fd, addr, size, addr + size / 2, size / 4);
+ }
+ }
+}
--
2.43.0
next reply other threads:[~2026-09-27 8:03 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-26 15:23 Gyeyoung Baek [this message]
2026-09-27 8:43 ` ✓ Xe.CI.BAT: success for tests/imagination: Add VM map and remap tests Patchwork
2026-09-27 9:04 ` ✓ i915.CI.BAT: " Patchwork
2026-09-27 9:58 ` ✗ Xe.CI.FULL: failure " Patchwork
2026-09-27 10:57 ` ✓ i915.CI.Full: success " Patchwork
2026-09-28 11:31 ` [PATCH i-g-t] " Kamil Konieczny
2026-09-29 12:33 ` Gyeyoung Baek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260926152341.783867-1-gye976@gmail.com \
--to=gye976@gmail.com \
--cc=alessio.belle@imgtec.com \
--cc=donald.robson@imgtec.com \
--cc=frank.binns@imgtec.com \
--cc=igt-dev@lists.freedesktop.org \
--cc=imagination@lists.freedesktop.org \
--cc=luigi.santivetti@imgtec.com \
--cc=matt.coster@imgtec.com \
--cc=robert.mazur@imgtec.com \
--cc=sarah.walker@imgtec.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox