public inbox for io-uring@vger.kernel.org
 help / color / mirror / Atom feed
From: "Clément Léger" <cleger@meta.com>
To: <io-uring@vger.kernel.org>
Cc: "Clément Léger" <cleger@meta.com>, "Jens Axboe" <axboe@kernel.dk>
Subject: [PATCH liburing 2/3] test/zcrx: add ZCRX notification/stats tests
Date: Wed, 22 Apr 2026 06:57:21 -0700	[thread overview]
Message-ID: <20260422135724.528518-3-cleger@meta.com> (raw)
In-Reply-To: <20260422135724.528518-1-cleger@meta.com>

Enhance zcrx testsutie so that it covers notification registration
validation, stats region placement, and arm operation error paths.

Signed-off-by: Clément Léger <cleger@meta.com>
---
 test/zcrx.c | 376 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 376 insertions(+)

diff --git a/test/zcrx.c b/test/zcrx.c
index a0e8a863..b13eab43 100644
--- a/test/zcrx.c
+++ b/test/zcrx.c
@@ -811,6 +811,364 @@ static int test_zcrx_clone(void)
 	return 0;
 }
 
+static int try_register_ifq_with_notif(struct zcrx_notification_desc *notif,
+				       void *area)
+{
+	struct io_uring_region_desc rq_region = {
+		.size = get_rq_size(RQ_ENTRIES),
+		.user_addr = uring_ptr_to_u64(def_rq_mem),
+		.flags = IORING_MEM_REGION_TYPE_USER,
+	};
+	struct io_uring_zcrx_area_reg area_reg = {
+		.addr = uring_ptr_to_u64(area),
+		.len = AREA_SZ,
+		.flags = 0,
+	};
+	struct io_uring_zcrx_ifq_reg reg = {
+		.flags = ZCRX_REG_NODEV,
+		.rq_entries = RQ_ENTRIES,
+		.area_ptr = uring_ptr_to_u64(&area_reg),
+		.region_ptr = uring_ptr_to_u64(&rq_region),
+		.notif_desc = uring_ptr_to_u64(notif),
+	};
+
+	return try_register_zcrx(&reg);
+}
+
+static int test_notif_registration(void *area)
+{
+	struct zcrx_notification_desc notif;
+	int ret;
+
+	/* Valid registration with no-buffers notification */
+	memset(&notif, 0, sizeof(notif));
+	notif.user_data = 42;
+	notif.type_mask = ZCRX_NOTIF_NO_BUFFERS;
+
+	ret = try_register_ifq_with_notif(&notif, area);
+	if (ret) {
+		fprintf(stderr, "valid notif registration failed: %d\n", ret);
+		return T_EXIT_FAIL;
+	}
+
+	/* Valid registration with both notification types */
+	memset(&notif, 0, sizeof(notif));
+	notif.user_data = 42;
+	notif.type_mask = ZCRX_NOTIF_NO_BUFFERS |
+			  ZCRX_NOTIF_COPY;
+
+	ret = try_register_ifq_with_notif(&notif, area);
+	if (ret) {
+		fprintf(stderr, "valid notif both-types registration failed: %d\n", ret);
+		return T_EXIT_FAIL;
+	}
+
+	/* Invalid type_mask (bit beyond valid range) */
+	memset(&notif, 0, sizeof(notif));
+	notif.user_data = 42;
+	notif.type_mask = (1 << 31);
+
+	ret = try_register_ifq_with_notif(&notif, area);
+	if (ret != -EINVAL) {
+		fprintf(stderr, "invalid notif type_mask should fail with -EINVAL, got %d\n", ret);
+		return T_EXIT_FAIL;
+	}
+
+	/* Non-zero reserved field should fail */
+	memset(&notif, 0, sizeof(notif));
+	notif.user_data = 42;
+	notif.type_mask = ZCRX_NOTIF_NO_BUFFERS;
+	notif.__resv2[0] = 1;
+
+	ret = try_register_ifq_with_notif(&notif, area);
+	if (ret != -EINVAL) {
+		fprintf(stderr, "non-zero resv2 should fail with -EINVAL, got %d\n", ret);
+		return T_EXIT_FAIL;
+	}
+
+	/* Invalid notif flags */
+	memset(&notif, 0, sizeof(notif));
+	notif.user_data = 42;
+	notif.type_mask = ZCRX_NOTIF_NO_BUFFERS;
+	notif.flags = ~ZCRX_NOTIF_DESC_FLAG_STATS;
+
+	ret = try_register_ifq_with_notif(&notif, area);
+	if (ret != -EINVAL) {
+		fprintf(stderr, "invalid notif flags should fail with -EINVAL, got %d\n", ret);
+		return T_EXIT_FAIL;
+	}
+
+	return T_EXIT_PASS;
+}
+
+static int test_notif_stats(void *area)
+{
+	struct io_uring_query_zcrx zcrx_query = {};
+	struct io_uring_query_hdr zcrx_hdr = {
+		.size = sizeof(zcrx_query),
+		.query_data = uring_ptr_to_u64(&zcrx_query),
+		.query_op = IO_URING_QUERY_ZCRX,
+	};
+	struct io_uring_query_zcrx_notif notif_query = {};
+	struct io_uring_query_hdr notif_hdr = {
+		.size = sizeof(notif_query),
+		.query_data = uring_ptr_to_u64(&notif_query),
+		.query_op = IO_URING_QUERY_ZCRX_NOTIF,
+	};
+	struct zcrx_notification_desc notif;
+	size_t ring_size, rqes_end, stats_off, hdr_size;
+	int ret;
+	int tret = T_EXIT_FAIL;
+	void *ring_mem;
+
+	/*
+	 * Query the kernel for the actual refill ring header size and stats
+	 * struct layout so we can build a correctly-sized user region.
+	 */
+	ret = io_uring_register(-1, IORING_REGISTER_QUERY, &zcrx_hdr, 0);
+	if (ret < 0 || zcrx_hdr.result < 0) {
+		fprintf(stderr, "zcrx query not supported: %d\n", ret);
+		return T_EXIT_FAIL;
+	}
+
+	ret = io_uring_register(-1, IORING_REGISTER_QUERY, &notif_hdr, 0);
+	if (ret < 0 || notif_hdr.result < 0) {
+		fprintf(stderr, "zcrx notif query not supported: %d\n", ret);
+		return T_EXIT_FAIL;
+	}
+
+	if (!notif_query.notif_stats_size) {
+		fprintf(stderr, "notif stats not supported\n");
+		return T_EXIT_FAIL;
+	}
+
+	hdr_size = T_ALIGN_UP((__u64)zcrx_query.rq_hdr_size,
+			     zcrx_query.rq_hdr_alignment);
+	rqes_end = hdr_size + sizeof(struct io_uring_zcrx_rqe) * RQ_ENTRIES;
+	stats_off = T_ALIGN_UP(rqes_end, notif_query.notif_stats_off_alignment);
+	ring_size = stats_off + notif_query.notif_stats_size;
+	ring_size = T_ALIGN_UP(ring_size, page_size);
+
+	ring_mem = mmap(NULL, ring_size, PROT_READ | PROT_WRITE,
+			MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+	if (ring_mem == MAP_FAILED) {
+		perror("mmap ring_mem");
+		return T_EXIT_FAIL;
+	}
+
+	struct io_uring_region_desc region = {
+		.user_addr = uring_ptr_to_u64(ring_mem),
+		.size = ring_size,
+		.flags = IORING_MEM_REGION_TYPE_USER,
+	};
+	struct io_uring_zcrx_area_reg area_reg = {
+		.addr = uring_ptr_to_u64(area),
+		.len = AREA_SZ,
+		.flags = 0,
+	};
+
+	/* Valid stats offset */
+	memset(&notif, 0, sizeof(notif));
+	notif.user_data = 42;
+	notif.type_mask = ZCRX_NOTIF_NO_BUFFERS;
+	notif.flags = ZCRX_NOTIF_DESC_FLAG_STATS;
+	notif.stats_offset = stats_off;
+
+	struct io_uring_zcrx_ifq_reg reg = {
+		.flags = ZCRX_REG_NODEV,
+		.rq_entries = RQ_ENTRIES,
+		.area_ptr = uring_ptr_to_u64(&area_reg),
+		.region_ptr = uring_ptr_to_u64(&region),
+		.notif_desc = uring_ptr_to_u64(&notif),
+	};
+
+	/*
+	 * Pre-fill the stats area with non-zero values to verify the kernel
+	 * zeroes it on registration.
+	 */
+	memset((char *)ring_mem + stats_off, 0xff,
+	       sizeof(struct io_uring_zcrx_notif_stats));
+
+	ret = try_register_zcrx(&reg);
+	if (ret) {
+		fprintf(stderr, "notif stats registration failed: %d\n", ret);
+		goto unmap;
+	}
+
+	/* Verify the stats area was zeroed by the kernel */
+	struct io_uring_zcrx_notif_stats *stats =
+		(struct io_uring_zcrx_notif_stats *)((char *)ring_mem + stats_off);
+	if (stats->copy_count || stats->copy_bytes) {
+		fprintf(stderr, "stats not zeroed after registration\n");
+		goto unmap;
+	}
+
+	/* Misaligned stats offset should fail */
+	memset(&notif, 0, sizeof(notif));
+	notif.user_data = 42;
+	notif.type_mask = ZCRX_NOTIF_NO_BUFFERS;
+	notif.flags = ZCRX_NOTIF_DESC_FLAG_STATS;
+	notif.stats_offset = stats_off + 1;
+
+	region.user_addr = uring_ptr_to_u64(ring_mem);
+	reg.region_ptr = uring_ptr_to_u64(&region);
+	reg.notif_desc = uring_ptr_to_u64(&notif);
+
+	ret = try_register_zcrx(&reg);
+	if (ret != -EINVAL) {
+		fprintf(stderr, "misaligned stats_offset should fail, got %d\n", ret);
+		goto unmap;
+	}
+
+	/* Stats offset overlapping with rqes should fail */
+	notif.stats_offset = sizeof(struct io_uring_zcrx_rqe);
+
+	ret = try_register_zcrx(&reg);
+	if (ret != -ERANGE) {
+		fprintf(stderr, "overlapping stats_offset should fail, got %d\n", ret);
+		goto unmap;
+	}
+
+	/* Stats offset beyond region should fail */
+	notif.stats_offset = ring_size;
+
+	ret = try_register_zcrx(&reg);
+	if (ret != -ERANGE) {
+		fprintf(stderr, "out-of-bounds stats_offset should fail, got %d\n", ret);
+		goto unmap;
+	}
+
+	tret = T_EXIT_PASS;
+unmap:
+	munmap(ring_mem, ring_size);
+	return tret;
+}
+
+static int test_arm_notification(void *area)
+{
+	struct zcrx_notification_desc notif;
+	struct io_uring ring;
+	struct zcrx_ctrl ctrl;
+	__u32 zcrx_id;
+	int ret;
+
+	struct io_uring_zcrx_area_reg area_reg = {
+		.addr = uring_ptr_to_u64(area),
+		.len = AREA_SZ,
+		.flags = 0,
+	};
+	struct io_uring_region_desc rq_region = {
+		.size = get_rq_size(RQ_ENTRIES),
+		.user_addr = uring_ptr_to_u64(def_rq_mem),
+		.flags = IORING_MEM_REGION_TYPE_USER,
+	};
+	struct io_uring_zcrx_ifq_reg reg = {
+		.flags = ZCRX_REG_NODEV,
+		.rq_entries = RQ_ENTRIES,
+		.area_ptr = uring_ptr_to_u64(&area_reg),
+		.region_ptr = uring_ptr_to_u64(&rq_region),
+	};
+
+	/* First, register without notifications and check arm fails */
+	ret = t_create_ring(128, &ring, RING_FLAGS);
+	if (ret != T_SETUP_OK) {
+		fprintf(stderr, "ring create failed: %d\n", ret);
+		return T_EXIT_FAIL;
+	}
+
+	ret = io_uring_register_ifq(&ring, &reg);
+	if (ret) {
+		fprintf(stderr, "ifq register failed: %d\n", ret);
+		io_uring_queue_exit(&ring);
+		return T_EXIT_FAIL;
+	}
+	zcrx_id = reg.zcrx_id;
+
+	memset(&ctrl, 0, sizeof(ctrl));
+	ctrl.zcrx_id = zcrx_id;
+	ctrl.op = ZCRX_CTRL_ARM_NOTIFICATION;
+	ctrl.zc_arm_notif.type_mask = ZCRX_NOTIF_NO_BUFFERS;
+
+	ret = io_uring_register(ring.ring_fd, IORING_REGISTER_ZCRX_CTRL,
+				&ctrl, 0);
+	if (ret != -EINVAL) {
+		fprintf(stderr, "arm without prior notif should fail, got %d\n", ret);
+		io_uring_queue_exit(&ring);
+		return T_EXIT_FAIL;
+	}
+	io_uring_queue_exit(&ring);
+
+	/* Now register with notifications and try arm with invalid mask */
+	memset(&notif, 0, sizeof(notif));
+	notif.user_data = 42;
+	notif.type_mask = ZCRX_NOTIF_NO_BUFFERS;
+
+	reg.notif_desc = uring_ptr_to_u64(&notif);
+
+	ret = t_create_ring(128, &ring, RING_FLAGS);
+	if (ret != T_SETUP_OK) {
+		fprintf(stderr, "ring create failed: %d\n", ret);
+		return T_EXIT_FAIL;
+	}
+
+	ret = io_uring_register_ifq(&ring, &reg);
+	if (ret) {
+		fprintf(stderr, "notif ifq register failed: %d\n", ret);
+		io_uring_queue_exit(&ring);
+		return T_EXIT_FAIL;
+	}
+	zcrx_id = reg.zcrx_id;
+
+	/* Arm with invalid type (bit 31) should fail */
+	memset(&ctrl, 0, sizeof(ctrl));
+	ctrl.zcrx_id = zcrx_id;
+	ctrl.op = ZCRX_CTRL_ARM_NOTIFICATION;
+	ctrl.zc_arm_notif.type_mask = (1 << 31);
+
+	ret = io_uring_register(ring.ring_fd, IORING_REGISTER_ZCRX_CTRL,
+				&ctrl, 0);
+	if (ret != -EINVAL) {
+		fprintf(stderr, "arm with invalid mask should fail, got %d\n", ret);
+		io_uring_queue_exit(&ring);
+		return T_EXIT_FAIL;
+	}
+
+	/*
+	 * Arm for a type that hasn't fired yet should fail because the kernel
+	 * only allows rearming notifications that have already been delivered.
+	 */
+	memset(&ctrl, 0, sizeof(ctrl));
+	ctrl.zcrx_id = zcrx_id;
+	ctrl.op = ZCRX_CTRL_ARM_NOTIFICATION;
+	ctrl.zc_arm_notif.type_mask = ZCRX_NOTIF_NO_BUFFERS;
+
+	ret = io_uring_register(ring.ring_fd, IORING_REGISTER_ZCRX_CTRL,
+				&ctrl, 0);
+	if (ret != -EINVAL) {
+		fprintf(stderr, "arm for unfired notif should fail, got %d\n", ret);
+		io_uring_queue_exit(&ring);
+		return T_EXIT_FAIL;
+	}
+
+	/* Non-zero reserved in arm_notif should fail */
+	memset(&ctrl, 0, sizeof(ctrl));
+	ctrl.zcrx_id = zcrx_id;
+	ctrl.op = ZCRX_CTRL_ARM_NOTIFICATION;
+	ctrl.zc_arm_notif.type_mask = ZCRX_NOTIF_NO_BUFFERS;
+	ctrl.zc_arm_notif.__resv[0] = 1;
+
+	ret = io_uring_register(ring.ring_fd, IORING_REGISTER_ZCRX_CTRL,
+				&ctrl, 0);
+	if (ret != -EINVAL) {
+		fprintf(stderr, "arm with non-zero resv should fail, got %d\n", ret);
+		io_uring_queue_exit(&ring);
+		return T_EXIT_FAIL;
+	}
+
+	io_uring_queue_exit(&ring);
+	return T_EXIT_PASS;
+}
+
 static int test_rq_flush(void)
 {
 	struct t_executor ctx;
@@ -1112,6 +1470,24 @@ static int run_tests(void)
 		return T_EXIT_FAIL;
 	}
 
+	ret = test_notif_registration(def_area_mem);
+	if (ret) {
+		fprintf(stderr, "test_notif_registration failed\n");
+		return ret;
+	}
+
+	ret = test_notif_stats(def_area_mem);
+	if (ret) {
+		fprintf(stderr, "test_notif_stats failed\n");
+		return ret;
+	}
+
+	ret = test_arm_notification(def_area_mem);
+	if (ret) {
+		fprintf(stderr, "test_arm_notification failed\n");
+		return ret;
+	}
+
 	ret = test_recv();
 	if (ret) {
 		fprintf(stderr, "test_recv() failed %i\n", ret);
-- 
2.52.0


  parent reply	other threads:[~2026-04-22 13:58 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-22 13:57 [PATCH liburing 0/3] zcrx: add support for notifications and statistic Clément Léger
2026-04-22 13:57 ` [PATCH liburing 1/3] Update uapi headers to add ZCRX notification Clément Léger
2026-04-22 13:57 ` Clément Léger [this message]
2026-04-22 13:57 ` [PATCH liburing 3/3] examples/zcrx: add notification support Clément Léger

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=20260422135724.528518-3-cleger@meta.com \
    --to=cleger@meta.com \
    --cc=axboe@kernel.dk \
    --cc=io-uring@vger.kernel.org \
    /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