mirror of
https://github.com/torvalds/linux.git
synced 2026-09-25 09:41:03 +02:00
Add KUnit tests for the __spi_map_msg() error paths. The tests verify
that a later TX or RX mapping failure clears the mapping state of
earlier transfers and leaves cur_{tx,rx}_dma_dev identifying the
current mapping device.
A zero-length transfer causes sg_alloc_table() to return -EINVAL,
providing deterministic failure injection without test hooks.
Additional cases cover successful map/unmap and a message which
requires no mapping.
Build the DMA suite as a separate translation unit, exposing the two
internal mapping helpers only for KUnit through the local internal
header. Enable SPI in the default and all-tests KUnit configurations so
the suite is exercised there.
Signed-off-by: Honghui Jiang <jiang_hh2019@163.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://patch.msgid.link/20260814031419.43378-5-jiang_hh2019@163.com
Signed-off-by: Mark Brown <broonie@kernel.org>
260 lines
8.1 KiB
C
260 lines
8.1 KiB
C
// 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");
|