[PATCH 2/2] nvmet-pci: add KUnit coverage for endpoint queue IDs

Michael Bommarito <[email protected]>
Newsgroups org.infradead.lists.linux-nvme,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <[email protected]>
Add KUnit coverage for the PCI endpoint target queue-id boundary. The tests
model the case where target-core max_qid is larger than the endpoint
transport's ctrl->nr_queues, confirm the common qid check accepts the
malformed id, and verify the endpoint callbacks reject out-of-range
Create/Delete SQ/CQ requests before indexing transport-private arrays.

This covers the regression fixed by the preceding patch.

Assisted-by: Codex:gpt-5-5-xhigh
Signed-off-by: Michael Bommarito <[email protected]>
---
 drivers/nvme/target/Kconfig   |  11 ++++
 drivers/nvme/target/pci-epf.c | 120 ++++++++++++++++++++++++++++++++++
 2 files changed, 131 insertions(+)

diff --git a/drivers/nvme/target/Kconfig b/drivers/nvme/target/Kconfig
index 4904097dfd490..ea64bbe9882c5 100644
--- a/drivers/nvme/target/Kconfig
+++ b/drivers/nvme/target/Kconfig
@@ -127,3 +127,14 @@ config NVME_TARGET_PCI_EPF
 	  capable PCI controller.
 
 	  If unsure, say N.
+
+config NVMET_PCI_EPF_KUNIT_TEST
+	bool "NVMe PCI endpoint target KUnit tests" if !KUNIT_ALL_TESTS
+	depends on KUNIT
+	depends on NVME_TARGET_PCI_EPF=y
+	default KUNIT_ALL_TESTS
+	help
+	  KUnit tests for the NVMe PCI endpoint target transport.
+	  These tests exercise transport-private queue ID checks for
+	  Create/Delete SQ/CQ commands when target-core max_qid is larger than
+	  the endpoint controller's available queue arrays.
diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c
index 5bddda09c0538..e2eb96f32fab5 100644
--- a/drivers/nvme/target/pci-epf.c
+++ b/drivers/nvme/target/pci-epf.c
@@ -20,6 +20,9 @@
 #include <linux/pci-epf.h>
 #include <linux/pci_regs.h>
 #include <linux/slab.h>
+#if IS_ENABLED(CONFIG_NVMET_PCI_EPF_KUNIT_TEST)
+#include <kunit/test.h>
+#endif
 
 #include "nvmet.h"
 
@@ -2667,3 +2670,120 @@ module_exit(nvmet_pci_epf_cleanup_module);
 MODULE_DESCRIPTION("NVMe PCI Endpoint Function target driver");
 MODULE_AUTHOR("Damien Le Moal <[email protected]>");
 MODULE_LICENSE("GPL");
+
+#if IS_ENABLED(CONFIG_NVMET_PCI_EPF_KUNIT_TEST)
+
+struct nvmet_pci_epf_kunit_ctx {
+	struct nvmet_ctrl tctrl;
+	struct nvmet_subsys subsys;
+	struct nvmet_pci_epf_ctrl ctrl;
+	struct nvmet_pci_epf nvme_epf;
+};
+
+static int nvmet_pci_epf_kunit_init(struct kunit *test)
+{
+	struct nvmet_pci_epf_kunit_ctx *ctx;
+	unsigned int qid;
+
+	ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+	ctx->subsys.max_qid = 8;
+	ctx->tctrl.subsys = &ctx->subsys;
+	ctx->tctrl.drvdata = &ctx->ctrl;
+	ctx->tctrl.cqs = kunit_kcalloc(test, ctx->subsys.max_qid + 1,
+				       sizeof(*ctx->tctrl.cqs), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, ctx->tctrl.cqs);
+	ctx->tctrl.sqs = kunit_kcalloc(test, ctx->subsys.max_qid + 1,
+				       sizeof(*ctx->tctrl.sqs), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, ctx->tctrl.sqs);
+
+	ctx->ctrl.nr_queues = 2;
+	ctx->ctrl.tctrl = &ctx->tctrl;
+	ctx->ctrl.nvme_epf = &ctx->nvme_epf;
+	ctx->ctrl.sq = kunit_kcalloc(test, ctx->ctrl.nr_queues,
+				     sizeof(*ctx->ctrl.sq), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, ctx->ctrl.sq);
+	ctx->ctrl.cq = kunit_kcalloc(test, ctx->ctrl.nr_queues,
+				     sizeof(*ctx->ctrl.cq), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, ctx->ctrl.cq);
+
+	for (qid = 0; qid < ctx->ctrl.nr_queues; qid++) {
+		nvmet_pci_epf_init_queue(&ctx->ctrl, qid, true);
+		nvmet_pci_epf_init_queue(&ctx->ctrl, qid, false);
+	}
+
+	test->priv = ctx;
+	return 0;
+}
+
+static void nvmet_pci_epf_qid_control_test(struct kunit *test)
+{
+	struct nvmet_pci_epf_kunit_ctx *ctx = test->priv;
+	u16 status;
+
+	status = nvmet_check_io_cqid(&ctx->tctrl, 1, true);
+	KUNIT_EXPECT_EQ(test, status, (u16)NVME_SC_SUCCESS);
+
+	status = nvmet_pci_epf_create_cq(&ctx->tctrl, 1, 0, 1, 0, 0);
+	KUNIT_EXPECT_EQ(test, status,
+			(u16)(NVME_SC_INVALID_QUEUE | NVME_STATUS_DNR));
+}
+
+static void nvmet_pci_epf_qid_oob_test(struct kunit *test)
+{
+	struct nvmet_pci_epf_kunit_ctx *ctx = test->priv;
+	u16 bad_qid = ctx->ctrl.nr_queues;
+	u16 status;
+
+	status = nvmet_check_io_cqid(&ctx->tctrl, bad_qid, true);
+	KUNIT_EXPECT_EQ(test, status, (u16)NVME_SC_SUCCESS);
+
+	status = nvmet_pci_epf_create_cq(&ctx->tctrl, bad_qid, 0, 1, 0, 0);
+	KUNIT_EXPECT_EQ(test, status,
+			(u16)(NVME_SC_QID_INVALID | NVME_STATUS_DNR));
+}
+
+static void nvmet_pci_epf_qid_reject_all_test(struct kunit *test)
+{
+	struct nvmet_pci_epf_kunit_ctx *ctx = test->priv;
+	u16 bad_qid = ctx->ctrl.nr_queues;
+	u16 status;
+
+	status = nvmet_pci_epf_create_cq(&ctx->tctrl, bad_qid, 0, 1, 0, 0);
+	KUNIT_EXPECT_EQ(test, status,
+			(u16)(NVME_SC_QID_INVALID | NVME_STATUS_DNR));
+
+	status = nvmet_pci_epf_create_sq(&ctx->tctrl, bad_qid, 1, 0, 1, 0);
+	KUNIT_EXPECT_EQ(test, status,
+			(u16)(NVME_SC_QID_INVALID | NVME_STATUS_DNR));
+
+	status = nvmet_pci_epf_create_sq(&ctx->tctrl, 1, bad_qid, 0, 1, 0);
+	KUNIT_EXPECT_EQ(test, status,
+			(u16)(NVME_SC_QID_INVALID | NVME_STATUS_DNR));
+
+	status = nvmet_pci_epf_delete_cq(&ctx->tctrl, bad_qid);
+	KUNIT_EXPECT_EQ(test, status,
+			(u16)(NVME_SC_QID_INVALID | NVME_STATUS_DNR));
+
+	status = nvmet_pci_epf_delete_sq(&ctx->tctrl, bad_qid);
+	KUNIT_EXPECT_EQ(test, status,
+			(u16)(NVME_SC_QID_INVALID | NVME_STATUS_DNR));
+}
+
+static struct kunit_case nvmet_pci_epf_qid_test_cases[] = {
+	KUNIT_CASE(nvmet_pci_epf_qid_control_test),
+	KUNIT_CASE(nvmet_pci_epf_qid_oob_test),
+	KUNIT_CASE(nvmet_pci_epf_qid_reject_all_test),
+	{}
+};
+
+static struct kunit_suite nvmet_pci_epf_qid_test_suite = {
+	.name = "nvmet_pci_epf_qid",
+	.init = nvmet_pci_epf_kunit_init,
+	.test_cases = nvmet_pci_epf_qid_test_cases,
+};
+
+kunit_test_suite(nvmet_pci_epf_qid_test_suite);
+
+#endif
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.