[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