// SPDX-License-Identifier: GPL-2.0 // KUnit tests for the SPI core DMA mapping error paths. // // A mapping error must clear all SG tables and *_sg_mapped flags while // cur_{tx,rx}_dma_dev identify the devices used for the attempted mapping. // Zero-length transfers make sg_alloc_table() fail with -EINVAL, providing // deterministic failure injection without test hooks. #include #include #include #include #include #include "../internals.h" MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); #define SPI_DMA_TEST_LEN 256 #define SPI_DMA_TEST_XFERS 2 struct spi_dma_test_ctx { struct spi_controller *ctlr; struct spi_device *spi; struct device *dma_dev; struct device *stale_dma_dev; struct spi_transfer xfer[SPI_DMA_TEST_XFERS]; struct spi_message msg; void *buf[SPI_DMA_TEST_XFERS * 2]; }; static bool spi_dma_test_can_dma(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *xfer) { /* Opt every transfer into the core DMA mapping path. */ return true; } /* * A bare controller is sufficient because the mapping helpers do not * dereference ctlr->dev. With dma_tx and dma_rx unset, both directions use * dma_map_dev, so the controller need not be registered. */ static struct spi_dma_test_ctx *spi_dma_test_ctx_new(struct kunit *test) { struct spi_dma_test_ctx *ctx; ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); ctx->dma_dev = kunit_device_register(test, "spi-dma-error-path"); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->dma_dev); ctx->stale_dma_dev = kunit_device_register(test, "spi-dma-stale-device"); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->stale_dma_dev); /* Keep both devices valid if an assertion aborts the test. */ KUNIT_ASSERT_EQ(test, 0, dma_coerce_mask_and_coherent(ctx->dma_dev, DMA_BIT_MASK(64))); KUNIT_ASSERT_EQ(test, 0, dma_coerce_mask_and_coherent(ctx->stale_dma_dev, DMA_BIT_MASK(64))); ctx->ctlr = kunit_kzalloc(test, sizeof(*ctx->ctlr), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->ctlr); ctx->spi = kunit_kzalloc(test, sizeof(*ctx->spi), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->spi); ctx->ctlr->can_dma = spi_dma_test_can_dma; ctx->ctlr->dma_map_dev = ctx->dma_dev; /* Normally initialized by spi_register_controller(). */ ctx->ctlr->max_dma_len = INT_MAX; ctx->spi->controller = ctx->ctlr; spi_message_init(&ctx->msg); ctx->msg.spi = ctx->spi; return ctx; } static void *spi_dma_test_buf(struct kunit *test, struct spi_dma_test_ctx *ctx, unsigned int slot) { KUNIT_ASSERT_LT(test, slot, ARRAY_SIZE(ctx->buf)); ctx->buf[slot] = kunit_kzalloc(test, SPI_DMA_TEST_LEN, GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->buf[slot]); return ctx->buf[slot]; } /* * Emulate DMA devices retained from an earlier message. Using valid devices * also lets the unfixed path reach the assertions instead of dereferencing * NULL during cleanup. */ static void spi_dma_test_pin_stale_dma_devs(struct spi_dma_test_ctx *ctx) { ctx->ctlr->cur_tx_dma_dev = ctx->stale_dma_dev; ctx->ctlr->cur_rx_dma_dev = ctx->stale_dma_dev; } static void spi_dma_test_assert_dma_devs_published(struct kunit *test, struct spi_dma_test_ctx *ctx) { KUNIT_ASSERT_PTR_EQ(test, ctx->ctlr->cur_tx_dma_dev, ctx->dma_dev); KUNIT_ASSERT_PTR_EQ(test, ctx->ctlr->cur_rx_dma_dev, ctx->dma_dev); } static void spi_dma_test_assert_nothing_mapped(struct kunit *test, struct spi_dma_test_ctx *ctx, unsigned int nr_xfers) { unsigned int i; for (i = 0; i < nr_xfers; i++) { KUNIT_ASSERT_FALSE_MSG(test, ctx->xfer[i].tx_sg_mapped, "xfer[%u] still claims a TX mapping after __spi_map_msg() failed", i); KUNIT_ASSERT_FALSE_MSG(test, ctx->xfer[i].rx_sg_mapped, "xfer[%u] still claims an RX mapping after __spi_map_msg() failed", i); KUNIT_EXPECT_NULL(test, ctx->xfer[i].tx_sg.sgl); KUNIT_EXPECT_EQ(test, ctx->xfer[i].tx_sg.orig_nents, 0U); KUNIT_EXPECT_EQ(test, ctx->xfer[i].tx_sg.nents, 0U); KUNIT_EXPECT_NULL(test, ctx->xfer[i].rx_sg.sgl); KUNIT_EXPECT_EQ(test, ctx->xfer[i].rx_sg.orig_nents, 0U); KUNIT_EXPECT_EQ(test, ctx->xfer[i].rx_sg.nents, 0U); } } /* * xfer0 maps TX and RX; zero-length xfer1 then fails its TX mapping. * The failure must unwind xfer0 and update cur_*_dma_dev. */ static void spi_dma_later_tx_fail_rolls_back_earlier(struct kunit *test) { struct spi_dma_test_ctx *ctx = spi_dma_test_ctx_new(test); int ret; ctx->xfer[0].tx_buf = spi_dma_test_buf(test, ctx, 0); ctx->xfer[0].rx_buf = spi_dma_test_buf(test, ctx, 1); ctx->xfer[0].len = SPI_DMA_TEST_LEN; ctx->xfer[1].tx_buf = spi_dma_test_buf(test, ctx, 2); ctx->xfer[1].rx_buf = NULL; ctx->xfer[1].len = 0; /* forces -EINVAL */ spi_message_add_tail(&ctx->xfer[0], &ctx->msg); spi_message_add_tail(&ctx->xfer[1], &ctx->msg); spi_dma_test_pin_stale_dma_devs(ctx); ret = __spi_map_msg(ctx->ctlr, &ctx->msg); KUNIT_ASSERT_EQ(test, ret, -EINVAL); spi_dma_test_assert_dma_devs_published(test, ctx); spi_dma_test_assert_nothing_mapped(test, ctx, SPI_DMA_TEST_XFERS); KUNIT_EXPECT_EQ(test, 0, __spi_unmap_msg(ctx->ctlr, &ctx->msg)); } /* * xfer0 maps TX and RX; zero-length RX-only xfer1 then fails. * The failure must unwind xfer0 without leaving either mapping flag set. */ static void spi_dma_later_rx_fail_rolls_back_earlier(struct kunit *test) { struct spi_dma_test_ctx *ctx = spi_dma_test_ctx_new(test); int ret; ctx->xfer[0].tx_buf = spi_dma_test_buf(test, ctx, 0); ctx->xfer[0].rx_buf = spi_dma_test_buf(test, ctx, 1); ctx->xfer[0].len = SPI_DMA_TEST_LEN; ctx->xfer[1].tx_buf = NULL; ctx->xfer[1].rx_buf = spi_dma_test_buf(test, ctx, 2); ctx->xfer[1].len = 0; /* forces -EINVAL */ spi_message_add_tail(&ctx->xfer[0], &ctx->msg); spi_message_add_tail(&ctx->xfer[1], &ctx->msg); spi_dma_test_pin_stale_dma_devs(ctx); ret = __spi_map_msg(ctx->ctlr, &ctx->msg); KUNIT_ASSERT_EQ(test, ret, -EINVAL); spi_dma_test_assert_dma_devs_published(test, ctx); spi_dma_test_assert_nothing_mapped(test, ctx, SPI_DMA_TEST_XFERS); KUNIT_EXPECT_EQ(test, 0, __spi_unmap_msg(ctx->ctlr, &ctx->msg)); } /* Ensure the error unwind does not affect successful mappings. */ static void spi_dma_map_success_publishes_dma_devs(struct kunit *test) { struct spi_dma_test_ctx *ctx = spi_dma_test_ctx_new(test); int ret; ctx->xfer[0].tx_buf = spi_dma_test_buf(test, ctx, 0); ctx->xfer[0].rx_buf = spi_dma_test_buf(test, ctx, 1); ctx->xfer[0].len = SPI_DMA_TEST_LEN; spi_message_add_tail(&ctx->xfer[0], &ctx->msg); ret = __spi_map_msg(ctx->ctlr, &ctx->msg); KUNIT_ASSERT_EQ(test, ret, 0); KUNIT_EXPECT_TRUE(test, ctx->xfer[0].tx_sg_mapped); KUNIT_EXPECT_TRUE(test, ctx->xfer[0].rx_sg_mapped); KUNIT_EXPECT_PTR_EQ(test, ctx->ctlr->cur_tx_dma_dev, ctx->dma_dev); KUNIT_EXPECT_PTR_EQ(test, ctx->ctlr->cur_rx_dma_dev, ctx->dma_dev); KUNIT_EXPECT_EQ(test, 0, __spi_unmap_msg(ctx->ctlr, &ctx->msg)); KUNIT_EXPECT_FALSE(test, ctx->xfer[0].tx_sg_mapped); KUNIT_EXPECT_FALSE(test, ctx->xfer[0].rx_sg_mapped); KUNIT_EXPECT_NULL(test, ctx->xfer[0].tx_sg.sgl); KUNIT_EXPECT_NULL(test, ctx->xfer[0].rx_sg.sgl); } /* A transfer without buffers requires no DMA mapping. */ static void spi_dma_map_nothing_is_success(struct kunit *test) { struct spi_dma_test_ctx *ctx = spi_dma_test_ctx_new(test); int ret; ctx->xfer[0].tx_buf = NULL; ctx->xfer[0].rx_buf = NULL; ctx->xfer[0].len = SPI_DMA_TEST_LEN; spi_message_add_tail(&ctx->xfer[0], &ctx->msg); ret = __spi_map_msg(ctx->ctlr, &ctx->msg); KUNIT_EXPECT_EQ(test, ret, 0); spi_dma_test_assert_nothing_mapped(test, ctx, 1); } static struct kunit_case spi_dma_error_path_cases[] = { KUNIT_CASE(spi_dma_later_tx_fail_rolls_back_earlier), KUNIT_CASE(spi_dma_later_rx_fail_rolls_back_earlier), KUNIT_CASE(spi_dma_map_success_publishes_dma_devs), KUNIT_CASE(spi_dma_map_nothing_is_success), {} }; static struct kunit_suite spi_dma_error_path_suite = { .name = "spi_dma", .test_cases = spi_dma_error_path_cases, }; kunit_test_suite(spi_dma_error_path_suite); MODULE_DESCRIPTION("KUnit tests for SPI core DMA mapping"); MODULE_LICENSE("GPL");