| // 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 <kunit/device.h> |
| #include <kunit/test.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/limits.h> |
| #include <linux/spi/spi.h> |
| |
| #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"); |