summaryrefslogtreecommitdiffstats
path: root/drivers/spi/tests
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2026-09-02 09:55:01 -0400
committerSteven Rostedt <rostedt@goodmis.org>2026-09-02 11:08:21 -0400
commit4617721c502b2ddaa4e324e86da4997edf738fa5 (patch)
tree350aa83b47da0b574a814795392b20b0bed83cbc /drivers/spi/tests
downloadlinux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.tar.gz
linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.zip
ftrace: Synchronize the initialization of ftrace_opsgrafted
There's some internal state that ftrace_ops needs to have set, but since it can be declared outside of the ftrace.c code, it calls ftrace_ops_init() on the ops in every global function. The issue is that if two tasks call it on the same ops at the same time it is possible to have the initialization of one corrupt the initialization of the other call. Create a ops_mutex to use to synchronize every initialization of the ftrace_ops. The mutex is taken within checking the ftrace_ops flag that states it was initializied but the flag is checked again after the mutex has been taken. Checking first outside the mutex allows it to shortcut having to take the mutex. But then the check needs to be done again after the mute is taken in case of races. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock") Reported-by: sashiko-bot@kernel.org Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/ Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'drivers/spi/tests')
-rw-r--r--drivers/spi/tests/Makefile3
-rw-r--r--drivers/spi/tests/spi-dma-kunit.c259
2 files changed, 262 insertions, 0 deletions
diff --git a/drivers/spi/tests/Makefile b/drivers/spi/tests/Makefile
new file mode 100644
index 000000000..26689e0cb
--- /dev/null
+++ b/drivers/spi/tests/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_SPI_DMA_KUNIT_TEST) += spi-dma-kunit.o
diff --git a/drivers/spi/tests/spi-dma-kunit.c b/drivers/spi/tests/spi-dma-kunit.c
new file mode 100644
index 000000000..ee5476ff3
--- /dev/null
+++ b/drivers/spi/tests/spi-dma-kunit.c
@@ -0,0 +1,259 @@
+// 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");