[Intel-gfx] [PATCH igt] igt/gem_memfd: Exercise hugepages and memfd

Chris Wilson chris at chris-wilson.co.uk
Thu Oct 5 22:40:56 UTC 2017


Create an object from huge pages using HugeTLB (via memfd) and userptr.
Then exercise writing to it, and reading back via the mmap. A nice basic
test that works in principle, but lacks verification that we do use
hugepages in HW, i.e. we provide the opportunity for the driver to use
hugepage allocations, and check that basic operations still work.

Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
Cc: Matthew Auld <matthew.auld at intel.com>
---
 tests/Makefile.sources |   1 +
 tests/gem_memfd.c      | 287 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 288 insertions(+)
 create mode 100644 tests/gem_memfd.c

diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index eea3a647..59bb8a52 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -105,6 +105,7 @@ TESTS_progs = \
 	gem_lut_handle \
 	gem_madvise \
 	gem_media_fill \
+	gem_memfd \
 	gem_mmap \
 	gem_mmap_gtt \
 	gem_mmap_offset_exhaustion \
diff --git a/tests/gem_memfd.c b/tests/gem_memfd.c
new file mode 100644
index 00000000..05a42f19
--- /dev/null
+++ b/tests/gem_memfd.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright © 2017 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#include <sys/syscall.h>
+
+#include "igt.h"
+#include "igt_gt.h"
+
+#define PAGE_SIZE 4096
+
+#define HUGETLB_FLAG_ENCODE_SHIFT       26
+#define HUGETLB_FLAG_ENCODE_MASK        0x3f
+
+#define HUGETLB_FLAG_ENCODE_64KB        (16 << HUGETLB_FLAG_ENCODE_SHIFT)
+#define HUGETLB_FLAG_ENCODE_512KB       (19 << HUGETLB_FLAG_ENCODE_SHIFT)
+#define HUGETLB_FLAG_ENCODE_1MB         (20 << HUGETLB_FLAG_ENCODE_SHIFT)
+#define HUGETLB_FLAG_ENCODE_2MB         (21 << HUGETLB_FLAG_ENCODE_SHIFT)
+#define HUGETLB_FLAG_ENCODE_8MB         (23 << HUGETLB_FLAG_ENCODE_SHIFT)
+#define HUGETLB_FLAG_ENCODE_16MB        (24 << HUGETLB_FLAG_ENCODE_SHIFT)
+#define HUGETLB_FLAG_ENCODE_256MB       (28 << HUGETLB_FLAG_ENCODE_SHIFT)
+#define HUGETLB_FLAG_ENCODE_1GB         (30 << HUGETLB_FLAG_ENCODE_SHIFT)
+#define HUGETLB_FLAG_ENCODE_2GB         (31 << HUGETLB_FLAG_ENCODE_SHIFT)
+#define HUGETLB_FLAG_ENCODE_16GB        (34 << HUGETLB_FLAG_ENCODE_SHIFT)
+
+/* flags for memfd_create(2) (unsigned int) */
+#define MFD_CLOEXEC		0x0001U
+#define MFD_ALLOW_SEALING	0x0002U
+#define MFD_HUGETLB		0x0004U
+
+/*
+ * Huge page size encoding when MFD_HUGETLB is specified, and a huge page
+ * size other than the default is desired.  See hugetlb_encode.h.
+ * All known huge page size encodings are provided here.  It is the
+ * responsibility of the application to know which sizes are supported on
+ * the running system.  See mmap(2) man page for details.
+ */
+#define MFD_HUGE_SHIFT	HUGETLB_FLAG_ENCODE_SHIFT
+#define MFD_HUGE_MASK	HUGETLB_FLAG_ENCODE_MASK
+
+#define MFD_HUGE_64KB	HUGETLB_FLAG_ENCODE_64KB
+#define MFD_HUGE_512KB	HUGETLB_FLAG_ENCODE_512KB
+#define MFD_HUGE_1MB	HUGETLB_FLAG_ENCODE_1MB
+#define MFD_HUGE_2MB	HUGETLB_FLAG_ENCODE_2MB
+#define MFD_HUGE_8MB	HUGETLB_FLAG_ENCODE_8MB
+#define MFD_HUGE_16MB	HUGETLB_FLAG_ENCODE_16MB
+#define MFD_HUGE_256MB	HUGETLB_FLAG_ENCODE_256MB
+#define MFD_HUGE_1GB	HUGETLB_FLAG_ENCODE_1GB
+#define MFD_HUGE_2GB	HUGETLB_FLAG_ENCODE_2GB
+#define MFD_HUGE_16GB	HUGETLB_FLAG_ENCODE_16GB
+
+static inline int
+memfd_create(const char *name, unsigned int flags)
+{
+	return syscall(SYS_memfd_create, name, flags);
+}
+
+static bool has_userptr(int fd)
+{
+	uint32_t handle = 0;
+	void *ptr;
+
+	igt_assert(posix_memalign(&ptr, PAGE_SIZE, PAGE_SIZE) == 0);
+	if (__gem_userptr(fd, ptr, PAGE_SIZE, 0, 0, &handle) == 0)
+		gem_close(fd, handle);
+	free(ptr);
+
+	return handle;
+}
+
+static bool has_memfd_thp(void)
+{
+	int fd;
+
+	fd = memfd_create("thp", MFD_HUGETLB | MFD_HUGE_2MB); /* v4.14 */
+	if (fd != -1)
+		close(fd);
+
+	return fd != -1;
+}
+
+static void request_hugepages(unsigned long request)
+{
+	/* XXX check OS support for page sizes */
+	char path[] = "/proc/sys/vm/nr_hugepages";
+	FILE *file;
+	unsigned long count;
+
+	file = fopen(path, "r");
+	if (!file)
+		return;
+
+	fscanf(file, "%lu", &count);
+	fclose(file);
+
+	igt_debug("Request %lu huge pages, system has %lu\n", request, count);
+	if (count > request)
+		return;
+
+	file = fopen(path, "w");
+	if (!file)
+		return;
+
+	fprintf(file, "%lu", request);
+	fclose(file);
+}
+
+static void *huge_create(int fd, uint64_t *size, uint32_t *handle)
+{
+	unsigned int flags;
+	uint64_t align;
+	void *ptr;
+	int mem;
+
+	flags = MFD_HUGETLB;
+	if (*size >> 30) {
+		igt_info("Requesting 1GiB pages\n");
+		flags |= MFD_HUGE_1GB;
+		align = 1ull << 30;
+	} else if (*size >> 21) {
+		igt_info("Requesting 2MiB pages\n");
+		flags |= MFD_HUGE_2MB;
+		align = 1ull << 21;
+	} else if (*size >> 16) {
+		igt_info("Requesting 64KiB pages\n");
+		flags |= MFD_HUGE_64KB;
+		align = 1ull << 16;
+	} else {
+		igt_assert(0);
+	}
+
+	*size = ALIGN(*size, align);
+	intel_require_memory(1, *size, CHECK_RAM);
+	request_hugepages(*size / align);
+
+	mem = memfd_create("thp", flags);
+	igt_skip_on(mem == -1);
+	if (ftruncate(mem, *size))
+		igt_skip("Failed to ftruncate memfd to size %llu\n",
+			 (long long)*size);
+
+	ptr = mmap(NULL, *size,
+		   PROT_READ | PROT_WRITE,
+		   MAP_HUGETLB | MAP_SHARED | MAP_POPULATE,
+		   mem, 0);
+	close(mem);
+	if (ptr == MAP_FAILED)
+		igt_skip("Failed to mmap(MAP_POPULATE) memfd of size %llu\n",
+			 (long long)*size);
+
+	igt_assert_eq(__gem_userptr(fd, ptr, *size, 0, 0, handle), 0);
+	return ptr;
+}
+
+static void test_huge(int fd, uint64_t sz)
+{
+	struct drm_i915_gem_exec_object2 obj[2] = {};
+	struct drm_i915_gem_relocation_entry reloc;
+	struct drm_i915_gem_execbuffer2 execbuf = {
+		.buffers_ptr = to_user_pointer(obj),
+		.buffer_count = 2,
+	};
+	void *ptr;
+
+	ptr = huge_create(fd, &sz, &obj[0].handle);
+	obj[0].flags = EXEC_OBJECT_SUPPORTS_48B_ADDRESS;
+
+	/* XXX How to verify we have a hugepage object? */
+
+	memset(&reloc, 0, sizeof(reloc));
+	reloc.target_handle = obj[0].handle;
+	reloc.presumed_offset = 0;
+	reloc.offset = sizeof(uint32_t);
+	reloc.read_domains = I915_GEM_DOMAIN_RENDER;
+	reloc.write_domain = I915_GEM_DOMAIN_RENDER;
+	obj[1].relocs_ptr = to_user_pointer(&reloc);
+	obj[1].relocation_count = 1;
+
+	for (uint64_t page = 0; page < sz; page += 4096) {
+		unsigned int engine;
+		uint32_t *batch;
+		int x = 0;
+
+		obj[1].handle = gem_create(fd, 4096);
+		batch = gem_mmap__wc(fd, obj[1].handle, 0, 4096, PROT_WRITE);
+		gem_set_domain(fd, obj[1].handle,
+			       I915_GEM_DOMAIN_WC, I915_GEM_DOMAIN_WC);
+
+		for_each_engine(fd, engine) {
+			uint64_t offset;
+			uint32_t *b;
+
+			igt_assert(x*68 < 4096);
+			reloc.delta = page + x*68;
+			reloc.presumed_offset = obj[0].offset;
+			offset = reloc.delta + reloc.presumed_offset;
+
+			execbuf.flags = engine;
+			execbuf.batch_start_offset = x * 64;
+
+			b = batch + execbuf.batch_start_offset / sizeof(*batch);
+			b[0] = MI_STORE_DWORD_IMM;
+			b[1] = offset;
+			b[2] = offset >> 32;
+			b[3] = (page / 4096) ^ engine;
+			b[4] = MI_BATCH_BUFFER_END;
+			gem_execbuf(fd, &execbuf);
+
+			x++;
+		}
+
+		munmap(batch, 4096);
+		gem_close(fd, obj[1].handle);
+	}
+
+	gem_set_domain(fd, obj[0].handle, I915_GEM_DOMAIN_CPU, 0);
+	for (uint64_t page = 0; page < sz; page += 4096) {
+		unsigned int engine;
+		int x = 0;
+
+		for_each_engine(fd, engine) {
+			igt_assert_eq_u32(*(uint32_t *)(ptr + page + x),
+					  (page / 4096) ^ engine);
+			x += 68;
+		}
+	}
+
+	gem_close(fd, obj[0].handle);
+	munmap(ptr, sz);
+}
+
+igt_main
+{
+	const struct {
+		const char *name;
+		unsigned int order;
+	} sizes[] = {
+		{ "64k", 16 },
+		{ "2M", 21 },
+		{ "1G", 30 },
+		{},
+	}, *sz;
+	int fd;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require(gem_gtt_type(fd) >= 3); /* needs 64b ppgtt */
+
+		igt_require_gem(fd);
+		igt_require(gem_can_store_dword(fd, 0));
+
+		/* Pass huge pages to the driver, ala Vk */
+		igt_require(has_userptr(fd));
+		igt_require(has_memfd_thp());
+
+		igt_fork_hang_detector(fd);
+	}
+
+	for (sz = sizes; sz->name; sz++) {
+		igt_subtest_f("%s", sz->name)
+			test_huge(fd, 1ull << sz->order);
+	}
+
+	igt_fixture {
+		igt_stop_hang_detector();
+	}
+}
-- 
2.14.2



More information about the Intel-gfx mailing list