From: Pranjal Shrivastava <praan@google.com>
To: iommu@lists.linux.dev
Cc: Will Deacon <will@kernel.org>, Joerg Roedel <joro@8bytes.org>,
Robin Murphy <robin.murphy@arm.com>,
Jason Gunthorpe <jgg@ziepe.ca>,
Mostafa Saleh <smostafa@google.com>,
Nicolin Chen <nicolinc@nvidia.com>,
Daniel Mentz <danielmentz@google.com>,
Ashish Mhetre <amhetre@nvidia.com>,
linux-arm-kernel@lists.infradead.org,
Pranjal Shrivastava <praan@google.com>
Subject: [PATCH v7 11/11] iommu/arm-smmu-v3: Add KUnit unit tests for Runtime PM
Date: Wed, 27 May 2026 22:14:07 +0000 [thread overview]
Message-ID: <20260527221407.1756491-12-praan@google.com> (raw)
In-Reply-To: <20260527221407.1756491-1-praan@google.com>
Introduce a kunit selftests to verify the Runtime PM elision gating,
post-suspend elisions and progress on resumption under active
invalidation load. Simulate concurrent HW suspension using a timer.
Mock all HW registers and CMDQ buffers by allocating them on RAM.
Make the mock CMDQ self-consuming to avoid hitting queue_full scenarios.
Signed-off-by: Pranjal Shrivastava <praan@google.com>
---
.../iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c | 150 ++++++++++++++++++
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 1 +
2 files changed, 151 insertions(+)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c
index add671363c82..ef9a17343ed8 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c
@@ -3,6 +3,10 @@
* Copyright 2024 Google LLC.
*/
#include <kunit/test.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
#include <linux/io-pgtable.h>
#include "arm-smmu-v3.h"
@@ -771,7 +775,153 @@ static void arm_smmu_v3_invs_test(struct kunit *test)
kfree(test_b);
}
+struct arm_smmu_test_pm_context {
+ u32 *mock_prod_reg;
+ u64 *mock_base;
+ unsigned long *mock_valid_map;
+};
+
+/*
+ * Shared helper to allocate and map a fully functional, self-consuming mock
+ * CMDQ. By redirecting both prod_reg and cons_reg to the same memory location,
+ * every producer write is instantly reflected as a consumer completion.
+ */
+static struct arm_smmu_test_pm_context *
+arm_smmu_v3_test_init_mock_cmdq(struct kunit *test, struct arm_smmu_device *smmu)
+{
+ struct arm_smmu_test_pm_context *ctx;
+ struct arm_smmu_cmdq *cmdq = &smmu->cmdq;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ /* Use a standard 1024-entry queue for safe word bitmask alignment */
+ ctx->mock_prod_reg = kunit_kzalloc(test, sizeof(u32), GFP_KERNEL);
+ ctx->mock_base = kunit_kzalloc(test, 1024 * 16, GFP_KERNEL);
+ ctx->mock_valid_map = kunit_kzalloc(test, BITS_TO_LONGS(1024) * sizeof(long), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, ctx->mock_prod_reg);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->mock_base);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->mock_valid_map);
+
+ smmu->features = 0;
+ /* 1024 entries */
+ cmdq->q.llq.max_n_shift = 10;
+ /* SMMUv3 commands are 2 dwords (16 bytes) */
+ cmdq->q.ent_dwords = 2;
+ cmdq->q.base = (__le64 *)ctx->mock_base;
+ cmdq->valid_map = (atomic_long_t *)ctx->mock_valid_map;
+
+ /* Self-Consuming, prod == cons always ensures queue empty */
+ cmdq->q.prod_reg = (__force u32 __iomem *)ctx->mock_prod_reg;
+ cmdq->q.cons_reg = (__force u32 __iomem *)ctx->mock_prod_reg;
+
+ /* Initialize memory indices */
+ atomic_set(&cmdq->q.llq.atomic.prod, 0);
+ atomic_set(&cmdq->q.llq.atomic.cons, 0);
+ atomic_set(&cmdq->owner_prod, 0);
+ *ctx->mock_prod_reg = 0;
+
+ return ctx;
+}
+
+struct arm_smmu_test_timer_context {
+ struct kunit *test;
+ struct arm_smmu_device *smmu;
+ struct arm_smmu_test_pm_context *pm_ctx;
+ struct timer_list timer;
+ bool stopped;
+};
+
+/* Asynchronous timer callback running in softirq context */
+static void arm_smmu_v3_test_rpm_timer_callback(struct timer_list *t)
+{
+ struct arm_smmu_test_timer_context *ctx =
+ timer_container_of(ctx, t, timer);
+ struct arm_smmu_cmdq *cmdq = &ctx->smmu->cmdq;
+
+ /*
+ * Asynchronously set the STOP_FLAG (Close the gate).
+ * Simulate a concurrent runtime suspend event interrupting
+ * the invalidations happening under active load.
+ */
+ atomic_or(CMDQ_PROD_STOP_FLAG, &cmdq->q.llq.atomic.prod);
+
+ /* Signal to the main storm loop that suspend has triggered */
+ WRITE_ONCE(ctx->stopped, true);
+}
+
+/*
+ * Verify SMMU PM Runtime gating, elision, and post-suspend resumption
+ * safety sequentially under active stress.
+ */
+static void arm_smmu_v3_test_rpm_stress_race(struct kunit *test)
+{
+ struct arm_smmu_device *smmu = kunit_kzalloc(test, sizeof(*smmu), GFP_KERNEL);
+ struct arm_smmu_test_pm_context *pm_ctx;
+ struct arm_smmu_test_timer_context *timer_ctx;
+ struct arm_smmu_cmdq *cmdq = &smmu->cmdq;
+ struct arm_smmu_cmd cmd = arm_smmu_make_cmd_cfgi_all();
+ u32 stopped_prod;
+ int i, ret;
+
+ KUNIT_ASSERT_NOT_NULL(test, smmu);
+ pm_ctx = arm_smmu_v3_test_init_mock_cmdq(test, smmu);
+
+ timer_ctx = kunit_kmalloc(test, sizeof(*timer_ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, timer_ctx);
+ timer_ctx->test = test;
+ timer_ctx->smmu = smmu;
+ timer_ctx->pm_ctx = pm_ctx;
+ timer_ctx->stopped = false;
+
+ /* Setup the kernel software timer */
+ timer_setup(&timer_ctx->timer, arm_smmu_v3_test_rpm_timer_callback, 0);
+
+ /* Start the timer to fire in 10 jiffies */
+ mod_timer(&timer_ctx->timer, jiffies + msecs_to_jiffies(10));
+
+ /* Execute the unmap storm until the timer triggers */
+ while (!READ_ONCE(timer_ctx->stopped)) {
+ ret = arm_smmu_cmdq_issue_cmdlist(smmu, cmdq, &cmd, 1, false);
+ if (ret != 0) {
+ KUNIT_FAIL(test, "Unmap Storm loop failed to submit cmd list: %d\n", ret);
+ break;
+ }
+
+ usleep_range(50, 100);
+ }
+
+ timer_delete_sync(&timer_ctx->timer);
+
+ /* Establish the globally stable, post-storm register baseline index */
+ stopped_prod = *pm_ctx->mock_prod_reg;
+
+ /*
+ * Attempt multiple unmaps while the SMMU is disabled (STOP_GATE is set)
+ * Every single invalidation must be instantly elided, returning 0
+ * immediately and leaving the mock MMIO register completely frozen.
+ */
+ for (i = 0; i < 1000; i++) {
+ ret = arm_smmu_cmdq_issue_cmdlist(smmu, cmdq, &cmd, 1, false);
+ if (ret != 0) {
+ KUNIT_FAIL(test, "Gated storm loop failed at iteration %d: %d\n", i, ret);
+ break;
+ }
+ }
+ KUNIT_EXPECT_EQ(test, stopped_prod, *pm_ctx->mock_prod_reg);
+
+ /*
+ * Clear the STOP_FLAG (resume SMMU). A new invalidation must now
+ * successfully commit, moving the prod_reg by exactly 1 slot
+ */
+ atomic_andnot(CMDQ_PROD_STOP_FLAG, &cmdq->q.llq.atomic.prod);
+ KUNIT_EXPECT_EQ(test, 0, arm_smmu_cmdq_issue_cmdlist(smmu, cmdq, &cmd, 1, false));
+ KUNIT_EXPECT_EQ(test, stopped_prod + 1, *pm_ctx->mock_prod_reg);
+}
+
static struct kunit_case arm_smmu_v3_test_cases[] = {
+ KUNIT_CASE(arm_smmu_v3_test_rpm_stress_race),
KUNIT_CASE(arm_smmu_v3_write_ste_test_bypass_to_abort),
KUNIT_CASE(arm_smmu_v3_write_ste_test_abort_to_bypass),
KUNIT_CASE(arm_smmu_v3_write_ste_test_cdtable_to_abort),
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index d42f78777571..dd7cfd3c16f4 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -871,6 +871,7 @@ int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu,
local_irq_restore(flags);
return ret;
}
+EXPORT_SYMBOL_IF_KUNIT(arm_smmu_cmdq_issue_cmdlist);
static int arm_smmu_cmdq_issue_cmd_p(struct arm_smmu_device *smmu,
struct arm_smmu_cmd *cmd, bool sync)
--
2.54.0.794.g4f17f83d09-goog
next prev parent reply other threads:[~2026-05-27 22:15 UTC|newest]
Thread overview: 40+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-27 22:13 [PATCH v7 00/11] iommu/arm-smmu-v3: Implement Runtime/System Sleep ops Pranjal Shrivastava
2026-05-27 22:13 ` [PATCH v7 01/11] iommu/arm-smmu-v3: Refactor arm_smmu_setup_irqs Pranjal Shrivastava
2026-05-27 22:13 ` [PATCH v7 02/11] iommu/arm-smmu-v3: Add a helper to drain cmd queues Pranjal Shrivastava
2026-05-28 1:35 ` Nicolin Chen
2026-05-28 10:34 ` Pranjal Shrivastava
2026-05-28 22:09 ` Nicolin Chen
2026-05-29 14:32 ` Pranjal Shrivastava
2026-05-27 22:13 ` [PATCH v7 03/11] iommu/tegra241-cmdqv: Add a helper to drain VCMDQs Pranjal Shrivastava
2026-05-27 22:14 ` [PATCH v7 04/11] iommu/tegra241-cmdqv: Restore PROD and CONS after resume Pranjal Shrivastava
2026-05-28 18:14 ` Nicolin Chen
2026-05-27 22:14 ` [PATCH v7 05/11] iommu/arm-smmu-v3: Cache and restore MSI config Pranjal Shrivastava
2026-05-28 18:36 ` Nicolin Chen
2026-05-28 21:57 ` Pranjal Shrivastava
2026-05-28 22:03 ` Nicolin Chen
2026-05-27 22:14 ` [PATCH v7 06/11] iommu/arm-smmu-v3: Handle gerror during suspend Pranjal Shrivastava
2026-05-28 18:53 ` Nicolin Chen
2026-05-28 21:59 ` Pranjal Shrivastava
2026-05-27 22:14 ` [PATCH v7 07/11] iommu/arm-smmu-v3: Add CMDQ_PROD_STOP_FLAG to gate CMDQ submissions Pranjal Shrivastava
2026-05-28 19:41 ` Nicolin Chen
2026-05-28 21:57 ` Pranjal Shrivastava
2026-05-27 22:14 ` [PATCH v7 08/11] iommu/arm-smmu-v3: Implement pm_runtime & system sleep ops Pranjal Shrivastava
2026-05-28 19:39 ` Nicolin Chen
2026-05-28 21:21 ` Pranjal Shrivastava
2026-05-28 22:13 ` Nicolin Chen
2026-05-28 23:30 ` Pranjal Shrivastava
2026-05-27 22:14 ` [PATCH v7 09/11] iommu/arm-smmu-v3: Enable pm_runtime and setup devlinks Pranjal Shrivastava
2026-05-28 20:13 ` Nicolin Chen
2026-05-28 21:36 ` Pranjal Shrivastava
2026-05-27 22:14 ` [PATCH v7 10/11] iommu/arm-smmu-v3: Invoke pm_runtime before hw access Pranjal Shrivastava
2026-05-28 20:28 ` Nicolin Chen
2026-05-28 21:46 ` Pranjal Shrivastava
2026-05-28 22:01 ` Nicolin Chen
2026-05-28 22:25 ` Pranjal Shrivastava
2026-05-28 23:18 ` Nicolin Chen
2026-05-27 22:14 ` Pranjal Shrivastava [this message]
2026-05-28 21:43 ` [PATCH v7 11/11] iommu/arm-smmu-v3: Add KUnit unit tests for Runtime PM Nicolin Chen
2026-05-28 23:10 ` Pranjal Shrivastava
2026-05-28 23:21 ` Nicolin Chen
2026-05-28 23:33 ` Pranjal Shrivastava
2026-05-28 18:05 ` [PATCH v7 00/11] iommu/arm-smmu-v3: Implement Runtime/System Sleep ops Nicolin Chen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260527221407.1756491-12-praan@google.com \
--to=praan@google.com \
--cc=amhetre@nvidia.com \
--cc=danielmentz@google.com \
--cc=iommu@lists.linux.dev \
--cc=jgg@ziepe.ca \
--cc=joro@8bytes.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=nicolinc@nvidia.com \
--cc=robin.murphy@arm.com \
--cc=smostafa@google.com \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox