From: Michael Bommarito <michael.bommarito@gmail.com>
To: Ilya Dryomov <idryomov@gmail.com>
Cc: Viacheslav Dubeyko <slava@dubeyko.com>,
ceph-devel@vger.kernel.org, linux-kernel@vger.kernel.org,
stable@vger.kernel.org
Subject: [PATCH v2 2/2] libceph: add KUnit coverage for OSD sparse-read extent validation
Date: Tue, 14 Jul 2026 07:51:40 -0400 [thread overview]
Message-ID: <20260714115141.3768034-3-michael.bommarito@gmail.com> (raw)
In-Reply-To: <20260714115141.3768034-1-michael.bommarito@gmail.com>
Add KUnit coverage for the sparse-read extent-map validation added by the
previous patch. The tests drive the real osd_sparse_read() state machine
over a synthesized OSD reply (no OSD or network): an in-range single
extent is accepted and advances the cursor, and an extent whose offset
lies outside the original request range is rejected with -EREMOTEIO before
the message-data cursor is advanced.
The suite is included from osd_client.c so it can exercise the static
sparse-read parser and cursor-advance helpers without exporting test-only
symbols, guarded by CONFIG_CEPH_LIB_KUNIT_TEST.
Assisted-by: Codex:gpt-5-5-xhigh
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
net/ceph/Kconfig | 12 +++
net/ceph/osd_client-kunit.c | 146 ++++++++++++++++++++++++++++++++++++
net/ceph/osd_client.c | 4 +
3 files changed, 162 insertions(+)
create mode 100644 net/ceph/osd_client-kunit.c
diff --git a/net/ceph/Kconfig b/net/ceph/Kconfig
index 7e2528cde4b94..6b78fe4a0f8a4 100644
--- a/net/ceph/Kconfig
+++ b/net/ceph/Kconfig
@@ -45,3 +45,15 @@ config CEPH_LIB_USE_DNS_RESOLVER
Documentation/networking/dns_resolver.rst
If unsure, say N.
+
+config CEPH_LIB_KUNIT_TEST
+ bool "KUnit tests for the Ceph core library" if !KUNIT_ALL_TESTS
+ depends on CEPH_LIB && KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds KUnit coverage for selected Ceph core-library parser
+ and state-machine helpers. The tests exercise internal libceph
+ behavior with synthesized state and are intended for developer
+ validation rather than production systems.
+
+ If unsure, say N.
diff --git a/net/ceph/osd_client-kunit.c b/net/ceph/osd_client-kunit.c
new file mode 100644
index 0000000000000..713f24675f68f
--- /dev/null
+++ b/net/ceph/osd_client-kunit.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit coverage for net/ceph/osd_client.c internals.
+ *
+ * Included from osd_client.c so the test can drive the real static sparse-read
+ * parser and cursor-advance helper without exporting test-only symbols.
+ */
+
+#include <kunit/test.h>
+
+struct ceph_osd_sparse_read_test {
+ struct ceph_osd osd;
+ struct ceph_connection con;
+ struct ceph_msg *msg;
+ struct ceph_msg_data_cursor cursor;
+ struct ceph_osd_request *req;
+ struct page **pages;
+ struct page *page;
+};
+
+static int ceph_osd_sparse_read_test_init(struct kunit *test)
+{
+ struct ceph_osd_sparse_read_test *ctx;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ ctx->req = kunit_kzalloc(test, struct_size(ctx->req, r_ops, 1),
+ GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->req);
+
+ ctx->pages = kunit_kcalloc(test, 1, sizeof(*ctx->pages), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->pages);
+
+ ctx->page = alloc_page(GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->page);
+ ctx->pages[0] = ctx->page;
+
+ ctx->msg = ceph_msg_new2(CEPH_MSG_OSD_OPREPLY, 0, 1, GFP_KERNEL, true);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->msg);
+
+ osd_init(&ctx->osd);
+ ctx->osd.o_sparse_op_idx = -1;
+ ceph_init_sparse_read(&ctx->osd.o_sparse_read);
+
+ request_init(ctx->req);
+ ctx->req->r_tid = 1;
+ ctx->req->r_num_ops = 1;
+ osd_req_op_extent_init(ctx->req, 0, CEPH_OSD_OP_SPARSE_READ,
+ 0, PAGE_SIZE, 0, 0);
+ KUNIT_ASSERT_EQ(test, ctx->req->r_ops[0].op, CEPH_OSD_OP_SPARSE_READ);
+
+ insert_request(&ctx->osd.o_requests, ctx->req);
+
+ ctx->msg->hdr.tid = cpu_to_le64(ctx->req->r_tid);
+ ceph_msg_data_add_pages(ctx->msg, ctx->pages, PAGE_SIZE, 0, false);
+ ceph_msg_data_cursor_init(&ctx->cursor, ctx->msg, PAGE_SIZE);
+
+ ctx->con.private = &ctx->osd;
+ ctx->con.in_msg = ctx->msg;
+
+ test->priv = ctx;
+ return 0;
+}
+
+static void ceph_osd_sparse_read_test_exit(struct kunit *test)
+{
+ struct ceph_osd_sparse_read_test *ctx = test->priv;
+
+ if (!ctx)
+ return;
+
+ if (ctx->req && !RB_EMPTY_NODE(&ctx->req->r_node))
+ erase_request(&ctx->osd.o_requests, ctx->req);
+ ceph_init_sparse_read(&ctx->osd.o_sparse_read);
+ if (ctx->msg)
+ ceph_msg_put(ctx->msg);
+ if (ctx->page)
+ __free_page(ctx->page);
+}
+
+static int ceph_osd_sparse_read_feed_one_extent(struct kunit *test,
+ u64 off, u64 len, u32 datalen)
+{
+ struct ceph_osd_sparse_read_test *ctx = test->priv;
+ struct ceph_sparse_read *sr = &ctx->osd.o_sparse_read;
+ char *buf = NULL;
+ int ret;
+
+ ret = osd_sparse_read(&ctx->con, &ctx->cursor, &buf);
+ KUNIT_ASSERT_EQ(test, ret, (int)sizeof(sr->sr_count));
+ KUNIT_ASSERT_PTR_EQ(test, buf, (char *)&sr->sr_count);
+
+ sr->sr_count = (__force u32)cpu_to_le32(1);
+ ret = osd_sparse_read(&ctx->con, &ctx->cursor, &buf);
+ KUNIT_ASSERT_EQ(test, ret, (int)sizeof(*sr->sr_extent));
+ KUNIT_ASSERT_NOT_NULL(test, sr->sr_extent);
+ KUNIT_ASSERT_PTR_EQ(test, buf, (char *)sr->sr_extent);
+
+ sr->sr_extent[0].off = (__force u64)cpu_to_le64(off);
+ sr->sr_extent[0].len = (__force u64)cpu_to_le64(len);
+ ret = osd_sparse_read(&ctx->con, &ctx->cursor, &buf);
+ KUNIT_ASSERT_EQ(test, ret, (int)sizeof(sr->sr_datalen));
+ KUNIT_ASSERT_PTR_EQ(test, buf, (char *)&sr->sr_datalen);
+
+ sr->sr_datalen = (__force u32)cpu_to_le32(datalen);
+ return osd_sparse_read(&ctx->con, &ctx->cursor, &buf);
+}
+
+static void ceph_osd_sparse_read_in_range_extent(struct kunit *test)
+{
+ struct ceph_osd_sparse_read_test *ctx = test->priv;
+ struct ceph_sparse_read *sr = &ctx->osd.o_sparse_read;
+ int ret;
+
+ ret = ceph_osd_sparse_read_feed_one_extent(test, 0, 16, 16);
+
+ KUNIT_EXPECT_EQ(test, ret, 16);
+ KUNIT_EXPECT_EQ(test, sr->sr_pos, 16);
+ KUNIT_EXPECT_EQ(test, ctx->cursor.sr_resid, 16);
+ KUNIT_EXPECT_EQ(test, ctx->cursor.resid, PAGE_SIZE);
+}
+
+static void ceph_osd_sparse_read_rejects_out_of_range_extent(struct kunit *test)
+{
+ int ret;
+
+ ret = ceph_osd_sparse_read_feed_one_extent(test, PAGE_SIZE + 16, 16, 16);
+
+ KUNIT_EXPECT_EQ(test, ret, -EREMOTEIO);
+}
+
+static struct kunit_case ceph_osd_sparse_read_test_cases[] = {
+ KUNIT_CASE(ceph_osd_sparse_read_in_range_extent),
+ KUNIT_CASE(ceph_osd_sparse_read_rejects_out_of_range_extent),
+ {}
+};
+
+static struct kunit_suite ceph_osd_sparse_read_test_suite = {
+ .name = "ceph_osd_sparse_read",
+ .init = ceph_osd_sparse_read_test_init,
+ .exit = ceph_osd_sparse_read_test_exit,
+ .test_cases = ceph_osd_sparse_read_test_cases,
+};
+
+kunit_test_suite(ceph_osd_sparse_read_test_suite);
diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index 76ba3abdad9b1..b9ab9608ec88e 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -5951,3 +5951,7 @@ static const struct ceph_connection_operations osd_con_ops = {
.handle_auth_done = osd_handle_auth_done,
.handle_auth_bad_method = osd_handle_auth_bad_method,
};
+
+#ifdef CONFIG_CEPH_LIB_KUNIT_TEST
+#include "osd_client-kunit.c"
+#endif
--
2.53.0
next prev parent reply other threads:[~2026-07-14 11:51 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-14 11:51 [PATCH v2 0/2] libceph: validate OSD sparse-read extent maps (+KUnit) Michael Bommarito
2026-07-14 11:51 ` [PATCH v2 1/2] libceph: validate OSD extent maps before cursor advance Michael Bommarito
2026-07-14 11:51 ` Michael Bommarito [this message]
2026-07-14 17:43 ` [PATCH v2 2/2] libceph: add KUnit coverage for OSD sparse-read extent validation Viacheslav Dubeyko
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=20260714115141.3768034-3-michael.bommarito@gmail.com \
--to=michael.bommarito@gmail.com \
--cc=ceph-devel@vger.kernel.org \
--cc=idryomov@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=slava@dubeyko.com \
--cc=stable@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
Powered by JetHome