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>,
linux-arm-kernel@lists.infradead.org,
Pranjal Shrivastava <praan@google.com>
Subject: [PATCH v9 12/12] iommu/arm-smmu-v3: Add KUnit unit tests for Runtime PM
Date: Tue, 28 Jul 2026 21:09:28 +0000 [thread overview]
Message-ID: <20260728210928.1050849-13-praan@google.com> (raw)
In-Reply-To: <20260728210928.1050849-1-praan@google.com>
Introduce 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 | 171 ++++++++++++++++++
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 1 +
2 files changed, 172 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..2654bd497fd3 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,6 +775,171 @@ static void arm_smmu_v3_invs_test(struct kunit *test)
kfree(test_b);
}
+struct arm_smmu_mock_cmdq {
+ u32 mock_prod_reg;
+};
+
+/* Helper to allocate a self-consuming mock cmdq */
+static void arm_smmu_v3_test_init_mock_cmdq(struct kunit *test,
+ struct arm_smmu_device *smmu,
+ struct arm_smmu_mock_cmdq *mock)
+{
+ struct arm_smmu_cmdq *cmdq = &smmu->cmdq;
+ unsigned long *mock_valid_map;
+ u64 *mock_base;
+
+ mock_base = kunit_kzalloc(test, 1024 * sizeof(struct arm_smmu_cmd), GFP_KERNEL);
+ mock_valid_map = kunit_kzalloc(test, BITS_TO_LONGS(1024) * sizeof(long), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, mock_base);
+ KUNIT_ASSERT_NOT_NULL(test, mock_valid_map);
+
+ smmu->features = 0;
+ /* 1024 entries */
+ cmdq->q.llq.max_n_shift = 10;
+ cmdq->q.ent_dwords = CMDQ_ENT_DWORDS;
+ cmdq->q.base = (__le64 *)mock_base;
+ cmdq->valid_map = (atomic_long_t *)mock_valid_map;
+
+ /* Self-Consuming, prod == cons always ensures queue empty */
+ cmdq->q.prod_reg = (__force u32 __iomem *)&mock->mock_prod_reg;
+ cmdq->q.cons_reg = (__force u32 __iomem *)&mock->mock_prod_reg;
+
+ atomic_set(&cmdq->q.llq.atomic.prod, 0);
+ atomic_set(&cmdq->q.llq.atomic.cons, 0);
+ atomic_set(&cmdq->owner_prod, 0);
+ mock->mock_prod_reg = 0;
+}
+
+struct arm_smmu_test_timer_context {
+ struct arm_smmu_device *smmu;
+ struct timer_list timer;
+ bool suspended;
+};
+
+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;
+
+ /* Simulate a concurrent suspend event interrupting the invalidations */
+ atomic_or(CMDQ_PROD_STOP_FLAG, &cmdq->q.llq.atomic.prod);
+ WRITE_ONCE(ctx->suspended, true);
+}
+
+/*
+ * Verify SMMU PM Runtime gating, elision, and post-suspend resumption
+ * safety sequentially under active stress.
+ */
+static void arm_smmu_v3_rpm_test_stress_race(struct kunit *test)
+{
+ struct arm_smmu_cmd cmd = arm_smmu_make_cmd_cfgi_all();
+ struct arm_smmu_test_timer_context timer_ctx = {0};
+ struct arm_smmu_device mock_smmu = smmu;
+ struct arm_smmu_cmdq *cmdq = &mock_smmu.cmdq;
+ struct arm_smmu_mock_cmdq mock = {0};
+ u32 stopped_prod;
+ int i;
+
+ arm_smmu_v3_test_init_mock_cmdq(test, &mock_smmu, &mock);
+
+ timer_ctx.smmu = &mock_smmu;
+
+ timer_setup(&timer_ctx.timer, arm_smmu_v3_test_rpm_timer_callback, 0);
+ mod_timer(&timer_ctx.timer, jiffies + msecs_to_jiffies(10));
+
+ /* Execute the unmap storm until the timer triggers */
+ while (!READ_ONCE(timer_ctx.suspended)) {
+ if (arm_smmu_cmdq_issue_cmdlist(&mock_smmu, cmdq, &cmd, 1, false))
+ break;
+ usleep_range(50, 100);
+ }
+
+ timer_delete_sync(&timer_ctx.timer);
+
+ /* Establish the post-storm prod_reg index */
+ stopped_prod = mock.mock_prod_reg;
+
+ /*
+ * Attempt multiple unmaps while the SMMU is disabled (STOP_GATE is set)
+ * Every single invalidation must get elided and return 0. The prod_reg
+ * shall remain completely frozen after all of these submissions.
+ */
+ for (i = 0; i < 1000; i++) {
+ if (arm_smmu_cmdq_issue_cmdlist(&mock_smmu, cmdq, &cmd, 1, false))
+ break;
+ }
+ KUNIT_EXPECT_EQ(test, stopped_prod, mock.mock_prod_reg);
+
+ /*
+ * Clear the STOP_FLAG (resume the SMMU). A new invalidation must
+ * now successfully commit prod_idx & move the prod_reg by exactly 1.
+ */
+ atomic_andnot(CMDQ_PROD_STOP_FLAG, &cmdq->q.llq.atomic.prod);
+ KUNIT_EXPECT_EQ(test, 0, arm_smmu_cmdq_issue_cmdlist(&mock_smmu, cmdq, &cmd, 1, false));
+ KUNIT_EXPECT_EQ(test, stopped_prod + 1, mock.mock_prod_reg);
+}
+
+struct arm_smmu_test_kthread_context {
+ struct arm_smmu_device *smmu;
+ int error;
+};
+
+static int arm_smmu_v3_test_kthread_worker(void *data)
+{
+ struct arm_smmu_cmd cmd = arm_smmu_make_cmd_cfgi_all();
+ struct arm_smmu_test_kthread_context *ctx = data;
+ struct arm_smmu_cmdq *cmdq = &ctx->smmu->cmdq;
+
+ while (!kthread_should_stop()) {
+ if (arm_smmu_cmdq_issue_cmdlist(ctx->smmu, cmdq, &cmd, 1, false))
+ WRITE_ONCE(ctx->error, 1);
+ usleep_range(50, 100);
+ }
+ return 0;
+}
+
+static void arm_smmu_v3_rpm_test_kthread_race(struct kunit *test)
+{
+ struct arm_smmu_test_kthread_context ctx = {0};
+ struct arm_smmu_device mock_smmu = smmu;
+ struct arm_smmu_mock_cmdq mock = {0};
+ struct task_struct *thread1, *thread2;
+ u32 stopped_prod;
+
+ ctx.smmu = &mock_smmu;
+ arm_smmu_v3_test_init_mock_cmdq(test, &mock_smmu, &mock);
+
+ thread1 = kthread_run(arm_smmu_v3_test_kthread_worker, &ctx, "smmu_w1");
+ thread2 = kthread_run(arm_smmu_v3_test_kthread_worker, &ctx, "smmu_w2");
+ if (IS_ERR(thread1) || IS_ERR(thread2)) {
+ if (!IS_ERR(thread1))
+ kthread_stop(thread1);
+ if (!IS_ERR(thread2))
+ kthread_stop(thread2);
+ return;
+ }
+
+ usleep_range(1000, 2000);
+
+ /* Gate the CMDQ */
+ atomic_or(CMDQ_PROD_STOP_FLAG, &mock_smmu.cmdq.q.llq.atomic.prod);
+ stopped_prod = mock.mock_prod_reg;
+
+ usleep_range(1000, 2000);
+ KUNIT_EXPECT_EQ(test, stopped_prod, mock.mock_prod_reg);
+
+ /* Open the gate */
+ atomic_andnot(CMDQ_PROD_STOP_FLAG, &mock_smmu.cmdq.q.llq.atomic.prod);
+ usleep_range(1000, 2000);
+ KUNIT_EXPECT_NE(test, stopped_prod, mock.mock_prod_reg);
+
+ kthread_stop(thread1);
+ kthread_stop(thread2);
+ KUNIT_EXPECT_EQ(test, 0, ctx.error);
+}
+
static struct kunit_case arm_smmu_v3_test_cases[] = {
KUNIT_CASE(arm_smmu_v3_write_ste_test_bypass_to_abort),
KUNIT_CASE(arm_smmu_v3_write_ste_test_abort_to_bypass),
@@ -797,6 +966,8 @@ static struct kunit_case arm_smmu_v3_test_cases[] = {
KUNIT_CASE(arm_smmu_v3_write_cd_test_sva_clear),
KUNIT_CASE(arm_smmu_v3_write_cd_test_sva_release),
KUNIT_CASE(arm_smmu_v3_invs_test),
+ KUNIT_CASE(arm_smmu_v3_rpm_test_stress_race),
+ KUNIT_CASE(arm_smmu_v3_rpm_test_kthread_race),
{},
};
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 97e7284fcfcc..9106ffb75f7a 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -935,6 +935,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);
bool arm_smmu_erratum_repeat_tlbi_cfgi(void)
{
--
2.55.0.487.gaf234c4eb3-goog
next prev parent reply other threads:[~2026-07-28 21:10 UTC|newest]
Thread overview: 40+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-28 21:09 [PATCH v9 00/12] iommu/arm-smmu-v3: Implement Runtime/System Sleep ops Pranjal Shrivastava
2026-07-28 21:09 ` [PATCH v9 01/12] iommu/arm-smmu-v3: Refactor arm_smmu_setup_irqs Pranjal Shrivastava
2026-08-25 16:36 ` Jason Gunthorpe
2026-08-25 17:35 ` Pranjal Shrivastava
2026-08-25 18:47 ` Nicolin Chen
2026-08-25 19:01 ` Pranjal Shrivastava
2026-07-28 21:09 ` [PATCH v9 02/12] iommu/arm-smmu-v3: Add a helper to drain cmd queues Pranjal Shrivastava
2026-08-25 16:36 ` Jason Gunthorpe
2026-08-25 17:37 ` Pranjal Shrivastava
2026-08-25 18:20 ` Nicolin Chen
2026-08-25 18:57 ` Pranjal Shrivastava
2026-07-28 21:09 ` [PATCH v9 03/12] iommu/tegra241-cmdqv: Add a helper to drain VCMDQs Pranjal Shrivastava
2026-07-28 21:09 ` [PATCH v9 04/12] iommu/tegra241-cmdqv: Restore PROD and CONS after resume Pranjal Shrivastava
2026-07-28 21:09 ` [PATCH v9 05/12] iommu/arm-smmu-v3: Cache and restore MSI config Pranjal Shrivastava
2026-08-25 16:36 ` Jason Gunthorpe
2026-08-25 18:01 ` Pranjal Shrivastava
2026-07-28 21:09 ` [PATCH v9 06/12] iommu/arm-smmu-v3: Handle gerror during suspend Pranjal Shrivastava
2026-08-25 16:36 ` Jason Gunthorpe
2026-08-25 18:06 ` Pranjal Shrivastava
2026-07-28 21:09 ` [PATCH v9 07/12] iommu/arm-smmu-v3: Add CMDQ_PROD_STOP_FLAG to gate CMDQ submissions Pranjal Shrivastava
2026-08-25 16:36 ` Jason Gunthorpe
2026-08-25 18:38 ` Pranjal Shrivastava
2026-07-28 21:09 ` [PATCH v9 08/12] iommu/tegra241-cmdqv: Add a helper to quiesce VCMDQs Pranjal Shrivastava
2026-08-25 16:36 ` Jason Gunthorpe
2026-08-25 18:46 ` Pranjal Shrivastava
2026-07-28 21:09 ` [PATCH v9 09/12] iommu/arm-smmu-v3: Implement pm_runtime & system sleep ops Pranjal Shrivastava
2026-08-25 16:36 ` Jason Gunthorpe
2026-08-25 18:53 ` Pranjal Shrivastava
2026-08-25 20:17 ` Jason Gunthorpe
2026-08-26 11:51 ` Pranjal Shrivastava
2026-08-26 13:47 ` Jason Gunthorpe
2026-08-26 14:17 ` Pranjal Shrivastava
2026-07-28 21:09 ` [PATCH v9 10/12] iommu/arm-smmu-v3: Enable pm_runtime and setup devlinks Pranjal Shrivastava
2026-07-28 21:09 ` [PATCH v9 11/12] iommu/arm-smmu-v3: Invoke pm_runtime before hw access Pranjal Shrivastava
2026-08-30 21:11 ` Daniel Mentz
2026-07-28 21:09 ` Pranjal Shrivastava [this message]
2026-08-25 13:33 ` [PATCH v9 00/12] iommu/arm-smmu-v3: Implement Runtime/System Sleep ops Jason Gunthorpe
2026-08-25 18:50 ` Pranjal Shrivastava
2026-08-25 20:14 ` Jason Gunthorpe
2026-08-26 11:55 ` Pranjal Shrivastava
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=20260728210928.1050849-13-praan@google.com \
--to=praan@google.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.