From: Alex Markuze <amarkuze@redhat.com>
To: Jens Axboe <axboe@kernel.dk>,
"David S . Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>
Cc: Pavel Begunkov <asml.silence@gmail.com>,
Simon Horman <horms@kernel.org>,
Kuniyuki Iwashima <kuniyu@google.com>,
Willem de Bruijn <willemb@google.com>,
Neal Cardwell <ncardwell@google.com>,
John Fastabend <john.fastabend@gmail.com>,
Jakub Sitnicki <jakub@cloudflare.com>,
Jiayuan Chen <jiayuan.chen@linux.dev>,
io-uring@vger.kernel.org, netdev@vger.kernel.org,
bpf@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [RFC PATCH net-next 12/12] io_uring/opaque: add basic KUnit tests
Date: Tue, 10 Nov 2026 13:20:06 +0000 [thread overview]
Message-ID: <20261110132006.3280986-13-amarkuze@redhat.com> (raw)
In-Reply-To: <20261110132006.3280986-1-amarkuze@redhat.com>
Check receive-budget admission and watermark transitions, then build a
page-backed immutable version and check its vector, payload, held version
reference and final backing/reference release.
Use existing internal helpers without adding production test hooks. These
are basic helper checks; the matching liburing example exercises a real
ring and loopback TCP connection.
Signed-off-by: Alex Markuze <amarkuze@redhat.com>
---
io_uring/Kconfig | 10 ++++
io_uring/Makefile | 1 +
io_uring/opaque_test.c | 128 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 139 insertions(+)
create mode 100644 io_uring/opaque_test.c
diff --git a/io_uring/Kconfig b/io_uring/Kconfig
index 5b696cf266f6..db6e3c5a317d 100644
--- a/io_uring/Kconfig
+++ b/io_uring/Kconfig
@@ -30,3 +30,13 @@ config IO_URING_BPF_OPS
def_bool y
depends on IO_URING
depends on BPF_SYSCALL && BPF_JIT && DEBUG_INFO_BTF
+
+config IO_URING_OPAQUE_OBJ_KUNIT_TEST
+ bool "KUnit smoke tests for opaque objects" if !KUNIT_ALL_TESTS
+ depends on KUNIT=y && IO_URING_OPAQUE_OBJ
+ default KUNIT_ALL_TESTS
+ help
+ Check receive-budget admission and watermark transitions, and the
+ vector representation and reference lifetime of an immutable version.
+ The tests call internal helpers without a userspace ring or TCP socket.
+ They are a small smoke test, not a pressure or concurrency suite.
diff --git a/io_uring/Makefile b/io_uring/Makefile
index 912b1861c0a5..b7fcc2d3c714 100644
--- a/io_uring/Makefile
+++ b/io_uring/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_IO_URING) += io_uring.o opdef.o kbuf.o rsrc.o notif.o \
obj-$(CONFIG_IO_URING_ZCRX) += zcrx.o
obj-$(CONFIG_IO_URING_OPAQUE_OBJ) += opaque.o opaque_register.o opaque_compact.o
+obj-$(CONFIG_IO_URING_OPAQUE_OBJ_KUNIT_TEST) += opaque_test.o
obj-$(CONFIG_IO_WQ) += io-wq.o
obj-$(CONFIG_FUTEX) += futex.o
obj-$(CONFIG_EPOLL) += epoll.o
diff --git a/io_uring/opaque_test.c b/io_uring/opaque_test.c
new file mode 100644
index 000000000000..cba5829de05f
--- /dev/null
+++ b/io_uring/opaque_test.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <kunit/test.h>
+#include <linux/highmem.h>
+
+#include "opaque_internal.h"
+
+struct opaque_basic {
+ struct io_opaque_store *store;
+ struct io_opaque_data *data;
+ struct page *page;
+};
+
+static void opaque_basic_release(void *arg)
+{
+ struct opaque_basic *c = arg;
+
+ if (!IS_ERR_OR_NULL(c->store)) {
+ io_opaque_data_put(c->store, c->data);
+ /* Also release a budget assertion's unfinished reservation. */
+ if (atomic64_read(&c->store->bytes) > 0)
+ io_opaque_uncharge(c->store, atomic64_read(&c->store->bytes));
+ io_opaque_free(c->store);
+ }
+ if (c->page)
+ put_page(c->page);
+}
+
+static struct opaque_basic *opaque_basic_new(struct kunit *test)
+{
+ struct io_uring_opaque_config config = {
+ .hard_limit = 8 * PAGE_SIZE,
+ .compact_headroom = 2 * PAGE_SIZE,
+ .reply_reserve = PAGE_SIZE,
+ .high_watermark = 4 * PAGE_SIZE,
+ .low_watermark = 2 * PAGE_SIZE,
+ .max_object_size = PAGE_SIZE,
+ .max_objects = 4,
+ .max_streams = 1,
+ .max_extents = 8,
+ };
+ struct opaque_basic *c = kunit_kzalloc(test, sizeof(*c), GFP_KERNEL);
+ struct io_ring_ctx *ctx;
+
+ KUNIT_ASSERT_NOT_NULL(test, c);
+ KUNIT_ASSERT_EQ(test,
+ kunit_add_action_or_reset(test, opaque_basic_release, c), 0);
+ /* No user/mm accounting in this internal-helper fixture. */
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+ c->store = io_opaque_alloc(ctx, &config);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, c->store);
+ return c;
+}
+
+static void opaque_basic_budget(struct kunit *test)
+{
+ struct opaque_basic *c = opaque_basic_new(test);
+ struct io_opaque_store *store = c->store;
+
+ KUNIT_ASSERT_TRUE(test, io_opaque_charge(store, 3 * PAGE_SIZE, true));
+ KUNIT_EXPECT_EQ(test, io_opaque_memory_mode(store), IORING_OPAQUE_MEMORY_NORMAL);
+ KUNIT_ASSERT_TRUE(test, io_opaque_charge(store, PAGE_SIZE, true));
+ KUNIT_EXPECT_EQ(test, io_opaque_memory_mode(store), IORING_OPAQUE_MEMORY_LOWMEM);
+ KUNIT_EXPECT_FALSE(test, io_opaque_charge(store, 2 * PAGE_SIZE, true));
+ KUNIT_EXPECT_EQ(test, atomic64_read(&store->bytes), (s64)(4 * PAGE_SIZE));
+ io_opaque_uncharge(store, 2 * PAGE_SIZE);
+ KUNIT_EXPECT_EQ(test, io_opaque_memory_mode(store), IORING_OPAQUE_MEMORY_NORMAL);
+ io_opaque_uncharge(store, 2 * PAGE_SIZE);
+ KUNIT_EXPECT_EQ(test, atomic64_read(&store->bytes), 0LL);
+}
+
+static void opaque_basic_version(struct kunit *test)
+{
+ static const char payload[] = "opaque";
+ struct opaque_basic *c = opaque_basic_new(test);
+ struct io_opaque_extent *extent;
+ void *mapped;
+
+ c->data = io_opaque_data_alloc();
+ KUNIT_ASSERT_NOT_NULL(test, c->data);
+ c->page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ KUNIT_ASSERT_NOT_NULL(test, c->page);
+ mapped = kmap_local_page(c->page);
+ memcpy(mapped + 17, payload, sizeof(payload));
+ kunmap_local(mapped);
+ get_page(c->page);
+ extent = io_opaque_extent_new(c->store, c->page, 17, sizeof(payload), 0, false);
+ if (IS_ERR(extent))
+ put_page(c->page);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, extent);
+ list_add_tail(&extent->list, &c->data->extents);
+ c->data->length = sizeof(payload);
+ KUNIT_ASSERT_EQ(test, io_opaque_vector(c->data), 0);
+ KUNIT_ASSERT_EQ(test, c->data->nr, 1U);
+ KUNIT_EXPECT_PTR_EQ(test, c->data->bvec[0].bv_page, c->page);
+ KUNIT_EXPECT_EQ(test, c->data->bvec[0].bv_offset, 17U);
+ KUNIT_EXPECT_EQ(test, c->data->bvec[0].bv_len, (unsigned int)sizeof(payload));
+ KUNIT_EXPECT_EQ(test, c->data->charge, (u64)PAGE_SIZE);
+ KUNIT_EXPECT_EQ(test, atomic64_read(&c->store->bytes), (s64)PAGE_SIZE);
+ KUNIT_EXPECT_EQ(test, atomic_read(&c->store->extents), 1);
+ refcount_inc(&c->data->refs);
+ io_opaque_data_put(c->store, c->data);
+ KUNIT_EXPECT_EQ(test, refcount_read(&c->data->refs), 1U);
+ KUNIT_EXPECT_EQ(test, page_ref_count(c->page), 2);
+ mapped = kmap_local_page(c->data->bvec[0].bv_page);
+ KUNIT_EXPECT_MEMEQ(test, mapped + c->data->bvec[0].bv_offset, payload, sizeof(payload));
+ kunmap_local(mapped);
+ io_opaque_data_put(c->store, c->data);
+ c->data = NULL;
+ KUNIT_EXPECT_EQ(test, page_ref_count(c->page), 1);
+ KUNIT_EXPECT_EQ(test, atomic64_read(&c->store->bytes), 0LL);
+ KUNIT_EXPECT_EQ(test, atomic_read(&c->store->extents), 0);
+}
+
+static struct kunit_case opaque_basic_cases[] = {
+ KUNIT_CASE(opaque_basic_budget),
+ KUNIT_CASE(opaque_basic_version),
+ {}
+};
+
+static struct kunit_suite opaque_basic_suite = {
+ .name = "io_opaque_basic",
+ .test_cases = opaque_basic_cases,
+};
+
+kunit_test_suite(opaque_basic_suite);
+
+MODULE_LICENSE("GPL");
--
2.34.1
prev parent reply other threads:[~2026-11-10 13:20 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 13:19 [RFC PATCH net-next 00/12] io_uring: opaque TCP payload objects Alex Markuze
2026-11-10 13:19 ` [RFC PATCH net-next 01/12] net: add an exclusive TCP receive claim Alex Markuze
2026-11-10 13:19 ` [RFC PATCH net-next 02/12] io_uring/opaque: add data structures and the UAPI header Alex Markuze
2026-11-10 13:19 ` [RFC PATCH net-next 03/12] io_uring/opaque: add backing accounting and immutable versions Alex Markuze
2026-11-10 13:19 ` [RFC PATCH net-next 04/12] io_uring/opaque: add owned-page helpers for dense copies Alex Markuze
2026-11-10 13:19 ` [RFC PATCH net-next 05/12] io_uring/opaque: add store registration and IORING_OP_OPAQUE_OBJ Alex Markuze
2026-11-10 13:20 ` [RFC PATCH net-next 06/12] io_uring/opaque: allow rings to share a store Alex Markuze
2026-11-10 13:20 ` [RFC PATCH net-next 07/12] io_uring/opaque: add IORING_OP_OPAQUE_OBJ_SEND Alex Markuze
2026-11-10 13:20 ` [RFC PATCH net-next 08/12] io_uring/opaque: add vectored IORING_OP_OPAQUE_OBJ_SEND Alex Markuze
2026-11-10 13:20 ` [RFC PATCH net-next 09/12] io_uring/opaque: add IORING_OP_OPAQUE_COLLECT Alex Markuze
2026-11-10 13:20 ` [RFC PATCH net-next 10/12] io_uring/opaque: add IORING_OPAQUE_CREATE Alex Markuze
2026-11-10 13:20 ` [RFC PATCH net-next 11/12] io_uring/opaque: add manual and automatic compaction Alex Markuze
2026-11-10 13:20 ` Alex Markuze [this message]
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=20261110132006.3280986-13-amarkuze@redhat.com \
--to=amarkuze@redhat.com \
--cc=asml.silence@gmail.com \
--cc=axboe@kernel.dk \
--cc=bpf@vger.kernel.org \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=horms@kernel.org \
--cc=io-uring@vger.kernel.org \
--cc=jakub@cloudflare.com \
--cc=jiayuan.chen@linux.dev \
--cc=john.fastabend@gmail.com \
--cc=kuba@kernel.org \
--cc=kuniyu@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=ncardwell@google.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=willemb@google.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
all inboxes | Powered by JetHome®