public inbox for io-uring@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH liburing 0/2] liburing mmap support
@ 2026-01-29 22:12 Gabriel Krisman Bertazi
  2026-01-29 22:12 ` [PATCH liburing 1/2] liburing: Add support to IORING_OP_MMAP Gabriel Krisman Bertazi
  2026-01-29 22:12 ` [PATCH liburing 2/2] mmap.t: Introduce IORING_OP_MMAP test case Gabriel Krisman Bertazi
  0 siblings, 2 replies; 3+ messages in thread
From: Gabriel Krisman Bertazi @ 2026-01-29 22:12 UTC (permalink / raw)
  To: axboe; +Cc: io-uring, Gabriel Krisman Bertazi

Hi,

This implements IORING_MMAP support.  I'm leaving the manpage of this
patchset, at least until we agree with the API.

Gabriel Krisman Bertazi (2):
  liburing: Add support to IORING_OP_MMAP
  mmap.t: Introduce IORING_OP_MMAP test case

 src/include/liburing.h          |   8 +
 src/include/liburing/io_uring.h |  10 +
 src/liburing-ffi.map            |   2 +
 test/Makefile                   |   1 +
 test/mmap.c                     | 372 ++++++++++++++++++++++++++++++++
 5 files changed, 393 insertions(+)
 create mode 100644 test/mmap.c

-- 
2.52.0


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH liburing 1/2] liburing: Add support to IORING_OP_MMAP
  2026-01-29 22:12 [PATCH liburing 0/2] liburing mmap support Gabriel Krisman Bertazi
@ 2026-01-29 22:12 ` Gabriel Krisman Bertazi
  2026-01-29 22:12 ` [PATCH liburing 2/2] mmap.t: Introduce IORING_OP_MMAP test case Gabriel Krisman Bertazi
  1 sibling, 0 replies; 3+ messages in thread
From: Gabriel Krisman Bertazi @ 2026-01-29 22:12 UTC (permalink / raw)
  To: axboe; +Cc: io-uring, Gabriel Krisman Bertazi

Signed-off-by: Gabriel Krisman Bertazi <krisman@suse.de>
---
 src/include/liburing.h          |  8 ++++++++
 src/include/liburing/io_uring.h | 10 ++++++++++
 src/liburing-ffi.map            |  2 ++
 3 files changed, 20 insertions(+)

diff --git a/src/include/liburing.h b/src/include/liburing.h
index 861e9673..903c60b7 100644
--- a/src/include/liburing.h
+++ b/src/include/liburing.h
@@ -1669,6 +1669,14 @@ IOURINGINLINE void io_uring_prep_pipe(struct io_uring_sqe *sqe, int *fds,
 	sqe->pipe_flags = (__u32) pipe_flags;
 }
 
+IOURINGINLINE void io_uring_prep_mmap(struct io_uring_sqe *sqe, int fd,
+				      struct io_uring_mmap_desc *descs,
+				      int nr_maps, int flags)
+{
+	io_uring_prep_rw(IORING_OP_MMAP, sqe, fd, descs, nr_maps, 0);
+	sqe->mmap_flags = flags;
+}
+
 /* setup pipe directly into the fixed file table */
 IOURINGINLINE void io_uring_prep_pipe_direct(struct io_uring_sqe *sqe, int *fds,
 					     int pipe_flags,
diff --git a/src/include/liburing/io_uring.h b/src/include/liburing/io_uring.h
index 3e88e796..2e1ac37d 100644
--- a/src/include/liburing/io_uring.h
+++ b/src/include/liburing/io_uring.h
@@ -74,6 +74,7 @@ struct io_uring_sqe {
 		__u32		install_fd_flags;
 		__u32		nop_flags;
 		__u32		pipe_flags;
+		__u32		mmap_flags;
 	};
 	__u64	user_data;	/* data to be passed back at completion time */
 	/* pack this to avoid bogus arm OABI complaints */
@@ -311,6 +312,7 @@ enum io_uring_op {
 	IORING_OP_PIPE,
 	IORING_OP_NOP128,
 	IORING_OP_URING_CMD128,
+	IORING_OP_MMAP,
 
 	/* this goes last, obviously */
 	IORING_OP_LAST,
@@ -1069,6 +1071,14 @@ struct io_uring_zcrx_ifq_reg {
 	__u64	__resv[3];
 };
 
+struct io_uring_mmap_desc {
+	void *addr;
+	unsigned long len;
+	unsigned long pgoff;
+	unsigned int prot;
+	unsigned int flags;
+};
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/liburing-ffi.map b/src/liburing-ffi.map
index 7202ffd0..b16de5aa 100644
--- a/src/liburing-ffi.map
+++ b/src/liburing-ffi.map
@@ -264,4 +264,6 @@ LIBURING_2.13 {
 LIBURING_2.14 {
 	global:
 		io_uring_register_task_restrictions;
+		io_uring_prep_mmap;
+
 } LIBURING_2.13;
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH liburing 2/2] mmap.t: Introduce IORING_OP_MMAP test case
  2026-01-29 22:12 [PATCH liburing 0/2] liburing mmap support Gabriel Krisman Bertazi
  2026-01-29 22:12 ` [PATCH liburing 1/2] liburing: Add support to IORING_OP_MMAP Gabriel Krisman Bertazi
@ 2026-01-29 22:12 ` Gabriel Krisman Bertazi
  1 sibling, 0 replies; 3+ messages in thread
From: Gabriel Krisman Bertazi @ 2026-01-29 22:12 UTC (permalink / raw)
  To: axboe; +Cc: io-uring, Gabriel Krisman Bertazi

Signed-off-by: Gabriel Krisman Bertazi <krisman@suse.de>
---
 test/Makefile |   1 +
 test/mmap.c   | 372 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 373 insertions(+)
 create mode 100644 test/mmap.c

diff --git a/test/Makefile b/test/Makefile
index 64862f34..8fe56ae4 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -141,6 +141,7 @@ test_srcs := \
 	link_drain.c \
 	link-timeout.c \
 	linked-defer-close.c \
+	mmap.c \
 	madvise.c \
 	min-timeout.c \
 	min-timeout-wait.c \
diff --git a/test/mmap.c b/test/mmap.c
new file mode 100644
index 00000000..b0ec326b
--- /dev/null
+++ b/test/mmap.c
@@ -0,0 +1,372 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test mmap operation
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include "liburing.h"
+#include "helpers.h"
+
+static bool check_hugetlb()
+{
+	/* Cheap (to implement) check whether can mmap a 2MB hugetlb
+	 * page.
+	 */
+	void *x = mmap(NULL, 2*1024*1024,
+		       PROT_READ|PROT_WRITE,
+		       MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB|MAP_HUGE_2MB,
+		       -1, 0);
+	if (x == (void*)-1) {
+		munmap(x, 2*1024*1024);
+		return false;
+	}
+	munmap(x, 2*1024*1024);
+	return true;
+}
+
+static unsigned char buf[BUFSIZ];
+#define MAGIC_CHAR 0xf3
+
+const char *func;
+int pos;
+#define CATCH_FAULT(x) (func = __func__, pos=__LINE__, x)
+
+static void do_sigsev(int sig, siginfo_t *si, void *unused)
+{
+	printf("SIGSEGV on 0x%lx (%s:%d). OP_MMAP likely broken\n",
+	       (long) si->si_addr, func, pos);
+	exit(T_EXIT_FAIL);
+}
+
+int create_memfd(int size, int flags)
+{
+	int fd;
+	int remain = size;
+	int ret;
+	char path[10];
+	static int i = 0;
+
+	/* just a unique name for each call */
+	snprintf(path, 10, "t%d", i++);
+
+	if (size % sizeof(int)) {
+		printf("memfd bad size %d\n", size);
+		return -1;
+	}
+	fd = memfd_create(path, flags);
+	if (!fd) {
+		printf("memfd %d\n", fd);
+		return -1;
+	}
+	if (ftruncate(fd, size)) {
+		fprintf(stderr, "ftruncate");
+		return -1;
+	}
+	while (remain > 0) {
+		ret = write(fd, buf, MIN(size,BUFSIZ));
+		if (ret < 0) {
+			fprintf(stderr, "write");
+			return -1;
+		}
+		remain -= ret;
+	}
+	return fd;
+}
+
+int uring_mmap(struct io_uring *ring, int sqe_flags,
+	       int fd, struct io_uring_mmap_desc *descs,
+	       int nr_descs, int flags)
+{
+	struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
+	struct io_uring_cqe *cqe;
+	int ret;
+
+	io_uring_prep_mmap(sqe, fd, descs, nr_descs, flags);
+	sqe->flags |= sqe_flags;
+	io_uring_submit(ring);
+	io_uring_wait_cqe(ring, &cqe);
+
+	ret = cqe->res;
+	io_uring_cqe_seen(ring, cqe);
+	return ret;
+}
+
+int test_map_file(struct io_uring *ring, unsigned int ring_flags)
+{
+	int fd, ret;
+	struct io_uring_mmap_desc desc = {
+		.addr  = NULL,
+		.len   = BUFSIZ,
+		.pgoff = 0,
+		.prot  = (PROT_READ|PROT_WRITE),
+		.flags = MAP_PRIVATE
+	};
+
+	fd = create_memfd(BUFSIZ, 0);
+	if (fd < 0) {
+		return T_EXIT_SKIP;
+	}
+
+	ret = uring_mmap(ring, 0, fd, &desc, 1, 0);
+	if (ret != 1) {
+		fprintf(stderr, "mmap at %s:%d failed.  got %d",
+			func, __LINE__, ret);
+		return T_EXIT_FAIL;
+	}
+
+	if ((long long) desc.addr <= 0) {
+		fprintf(stderr, "bad desc.addr fail %lld\n",
+			(long long)desc.addr);
+		return T_EXIT_FAIL;
+	}
+
+	if (CATCH_FAULT(memcmp(desc.addr, buf, BUFSIZ))) {
+		fprintf(stderr, "check_buf fail\n");
+		return T_EXIT_FAIL;
+	}
+	if (munmap(desc.addr, BUFSIZ)) {
+		fprintf(stderr, "unmap fail\n");
+		return T_EXIT_FAIL;
+	}
+	/* same, with a fixed file. */
+	desc.addr = NULL;
+	if (io_uring_register_files(ring, &fd, 1)) {
+		fprintf(stderr, "register fail\n");
+		return T_EXIT_FAIL;
+	}
+
+	ret = uring_mmap(ring, IOSQE_FIXED_FILE, 0, &desc, 1, 0);
+	if (ret != 1) {
+		fprintf(stderr, "mmap at %s:%d failed.  got %d",
+			func, __LINE__, ret);
+		return T_EXIT_FAIL;
+	}
+
+	if ((long long)(desc.addr) < 0) {
+		fprintf(stderr, "bad desc.addr fail %lld\n",
+			(long long)desc.addr);
+		return T_EXIT_FAIL;
+	}
+
+	if (CATCH_FAULT(memcmp(desc.addr, buf, BUFSIZ))) {
+		fprintf(stderr, "check_buf fail\n");
+		return T_EXIT_FAIL;
+	}
+	return 0;
+}
+
+int test_map_anon(struct io_uring *ring, unsigned int ring_flags)
+{
+	struct io_uring_mmap_desc descs[4];
+	void *addrs[4];
+	int ret;
+
+	for (int i = 0 ; i < 4; i++) {
+		descs[i].addr = NULL;
+		descs[i].len = 1024;
+		descs[i].pgoff = 0;
+		descs[i].prot  = (PROT_READ|PROT_WRITE);
+		descs[i].flags = MAP_PRIVATE;
+	}
+
+	ret = uring_mmap(ring, 0, -1, descs, 4, MAP_ANONYMOUS);
+	if (ret != 4) {
+		fprintf(stderr, "mmap at %s:%d failed.  got %d\n",
+			__func__, __LINE__, ret);
+		return T_EXIT_FAIL;
+	}
+
+	for (int i = 0 ; i < 4; i++) {
+
+		if ((long long)(descs[i].addr) < 0) {
+			fprintf(stderr, "bad desc.addr fail %lld\n",
+				(long long)descs[i].addr);
+			return T_EXIT_FAIL;
+		}
+
+		/* Ensure it is mapped.  SIGSEV on error */
+		CATCH_FAULT(*((char*)descs[i].addr) = MAGIC_CHAR);
+		munmap((char*)descs[i].addr, 1024);
+	}
+
+	/* reuse the addr from the first test to verify MAP_FIXED.  We
+	 * know the region is free since we just unmapped it.
+	 */
+	for (int i = 0 ; i < 4; i++) {
+		addrs[i] = descs[i].addr;
+		descs[i].flags |= MAP_FIXED;
+	}
+
+	ret = uring_mmap(ring, 0, -1, descs, 4, MAP_ANONYMOUS);
+	if (ret != 4) {
+		fprintf(stderr, "mmap at %s:%d failed.  got %d\n",
+			__func__, __LINE__, ret);
+		return T_EXIT_FAIL;
+	}
+
+	for (int i = 0 ; i < 4; i++) {
+		/* Ensure it is mapped.  SIGSEV on error */
+		CATCH_FAULT(*((char*)descs[i].addr) = MAGIC_CHAR);
+
+		if (descs[i].addr != addrs[i]) {
+			fprintf(stderr, "MAP_FIXED failed");
+			return T_EXIT_FAIL;
+		}
+	}
+	return 0;
+}
+
+int test_map_anon_hugetlb(struct io_uring *ring, unsigned int ring_flags)
+{
+	struct io_uring_mmap_desc desc = {
+		.addr = NULL,
+		.len = 1*1024*1024,
+		.pgoff = 0,
+		.prot  = (PROT_READ|PROT_WRITE),
+		.flags = MAP_PRIVATE|MAP_HUGE_2MB,
+	};
+	int ret;
+
+	ret = uring_mmap(ring, 0, -1, &desc, 1,
+			 MAP_ANONYMOUS|MAP_HUGETLB);
+	if (ret != 1) {
+		fprintf(stderr, "mmap at %s:%d failed.  got %d\n",
+			__func__, __LINE__, ret);
+		return T_EXIT_FAIL;
+	}
+
+	if ((long long)(desc.addr) < 0) {
+		fprintf(stderr, "bad desc.addr fail %lld\n",
+			(long long)desc.addr);
+		return T_EXIT_FAIL;
+	}
+
+	/* Ensure it is mapped.  SIGSEV on error */
+	CATCH_FAULT(*((char*)desc.addr) = MAGIC_CHAR);
+	return 0;
+}
+
+
+int test_weird(struct io_uring *ring, unsigned int ring_flags)
+{
+	struct io_uring_mmap_desc desc;
+	int ret, fd = create_memfd(BUFSIZ, 0);
+	if (fd < 0) {
+		return T_EXIT_SKIP;
+	}
+
+	desc.addr = NULL;
+	desc.len = 1024;
+	desc.pgoff = 0;
+	desc.prot  = (PROT_READ|PROT_WRITE);
+	desc.flags = MAP_PRIVATE;
+
+	/* This is wrong because attempt MAP_ANONYMOUS with fd.  It
+	 * fails early, because bad flag is on SQE.
+	 */
+	ret = uring_mmap(ring, 0, fd, &desc, 1, MAP_ANONYMOUS);
+	if (ret != -EINVAL) {
+		fprintf(stderr, "mmap at %s:%d failed.  got %d",
+			__func__, __LINE__, ret);
+		return T_EXIT_FAIL;
+	}
+
+	/* This is wrong because attempt MAP_ANONYMOUS with fd.  It
+	 * fails late, because bad flag is on desc.
+	 */
+	desc.flags = MAP_PRIVATE|MAP_ANONYMOUS;
+	ret = uring_mmap(ring, 0, fd, &desc, 1, 0);
+	if (ret != 1) {
+		fprintf(stderr, "mmap at %s:%d failed.  got %d",
+			__func__, __LINE__, ret);
+		return T_EXIT_FAIL;
+	}
+
+	if ((long long) desc.addr != -EINVAL) {
+		printf("mmap ANONYMOUS with fd should have failed %lx\n",
+		       (uintptr_t)desc.addr);
+	}
+
+	/* This is wrong because we can't have MAP_HUGETLB in the
+	 * descriptor.  It fails late, because bad flag is on desc.
+	 */
+	desc.flags = MAP_PRIVATE|MAP_HUGETLB;
+	ret = uring_mmap(ring, 0, fd, &desc, 1, 0);
+	if (ret != 1) {
+		fprintf(stderr, "mmap at %s:%d failed.  got %d",
+			__func__, __LINE__, ret);
+		return T_EXIT_FAIL;
+	}
+
+	if ((long long) desc.addr != -EINVAL) {
+		printf("mmap ANONYMOUS with fd should have failed %lx\n",
+		       (uintptr_t)desc.addr);
+	}
+
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	struct io_uring_probe *probe;
+	struct io_uring ring;
+	int ret = 0;
+	struct sigaction sa;
+	bool has_hugetlb = check_hugetlb();
+
+	sa.sa_flags = SA_SIGINFO;
+	sigemptyset(&sa.sa_mask);
+	sa.sa_sigaction = do_sigsev;
+	if (sigaction(SIGSEGV, &sa, NULL) == -1) {
+		fprintf(stderr,
+			"failed to register signal handle. continuing.");
+	}
+	memset(buf, MAGIC_CHAR, BUFSIZ);
+
+	probe = io_uring_get_probe();
+	if (!probe)
+		return T_EXIT_SKIP;
+	if (!io_uring_opcode_supported(probe, IORING_OP_MMAP))
+		return T_EXIT_SKIP;
+
+	ret = t_create_ring(10, &ring, IORING_SETUP_SUBMIT_ALL);
+	if (ret < 0) {
+		fprintf(stderr, "queue_init: %s\n",
+			strerror(-ret));
+		return T_SETUP_SKIP;
+	}
+
+	ret |= test_map_anon(&ring, 0);
+	if (ret) {
+		fprintf(stderr, "test_map_anon failed\n");
+		return T_EXIT_FAIL;
+	}
+
+	ret |= test_map_file(&ring, 0);
+	if (ret) {
+		fprintf(stderr, "test_map_file failed\n");
+		return T_EXIT_FAIL;
+	}
+
+	if (has_hugetlb) {
+		ret |= test_map_anon_hugetlb(&ring, 0);
+		if (ret) {
+			fprintf(stderr, "test_map_file failed\n");
+			return T_EXIT_FAIL;
+		}
+	}
+
+	ret |= test_weird(&ring, 0);
+	if (ret) {
+		fprintf(stderr, "test_map_weird failed\n");
+	}
+
+	return ret;
+}
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-01-29 22:12 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-29 22:12 [PATCH liburing 0/2] liburing mmap support Gabriel Krisman Bertazi
2026-01-29 22:12 ` [PATCH liburing 1/2] liburing: Add support to IORING_OP_MMAP Gabriel Krisman Bertazi
2026-01-29 22:12 ` [PATCH liburing 2/2] mmap.t: Introduce IORING_OP_MMAP test case Gabriel Krisman Bertazi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox