* [PATCH v7 0/2] iommu/arm-smmu-v3: Make the queue depths tunable, and shrink them in a kdump kernel
@ 2026-09-25 14:15 Kiryl Shutsemau
2026-09-25 14:15 ` [PATCH v7 1/2] iommu/arm-smmu-v3: Add a cmdq_max_n_shift module parameter Kiryl Shutsemau
2026-09-25 14:15 ` [PATCH v7 2/2] iommu/arm-smmu-v3: Default queue depths to one page in a kdump kernel Kiryl Shutsemau
0 siblings, 2 replies; 4+ messages in thread
From: Kiryl Shutsemau @ 2026-09-25 14:15 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Thierry Reding,
Jonathan Hunter, Jason Gunthorpe, Nicolin Chen, Breno Leitao
Cc: Kiryl Shutsemau (Meta),
Krishna Reddy, Pranjal Shrivastava, Mostafa Saleh, Ashish Mhetre,
Shameer Kolothum, Yuanhe Shu, Kyle McMartin, Usama Arif,
kernel-team, linux-arm-kernel, iommu, linux-tegra, linux-kernel
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
The queues are sized from the IDR1 maxima and allocated at probe, costing
megabytes per queue per SMMU instance. A kdump capture kernel pays that out
of a small crashkernel reservation, for queues it barely uses and two of
which it switches off anyway.
Patch 1 adds a cmdq_max_n_shift module parameter, decided in a per-queue
helper and floored at one page. Patch 2 has a kdump kernel size all three
queues at one page through the same helper. The parameter does not apply
there.
Yuanhe Shu tested v6 on an arm64 server with six SMMUv3 instances, 64K
pages and a 512 MiB crashkernel reservation. Without the series the queues
took 192 MiB and the capture kernel OOMed before makedumpfile ran. With it
they take about 1 MiB and the vmcore is saved.
Measured per instance under QEMU on -M virt,iommu=smmuv3 with the virtio
devices behind the SMMU, the capture kernel identified by elfcorehdr= on
the command line:
4K page 64K page
cmdq 1 MB -> 4 KB 8 MB -> 64 KB
evtq 1 MB -> 4 KB 16 MB -> 64 KB
cmdq_max_n_shift moves the command queue alone outside kdump and is
ignored inside it; zero gives one page. QEMU exposes no PRI queue, which
takes the same path. No CMD_SYNC timeout, GERROR or context fault in any
run. Build-tested across 4K/16K/64K, TEGRA241_CMDQV=n, CRASH_DUMP=n and
ARM_SMMU_V3=m, every commit warning-free.
v7:
- Flatten the depth helper as Jason suggested: floor the limit, then min
with the hardware maximum; callers pass their alignment cap as the
limit.
- Initialise cmdq_max_n_shift to CMDQ_MAX_SZ_SHIFT, so zero is no longer
the "default" sentinel; it asks for the smallest queue, one page.
- A kdump kernel always gets one page; cmdq_max_n_shift no longer
overrides it.
- Tags: Breno's and Jason's Reviewed-by on patch 1, Jason's Reviewed-by
and Yuanhe's Tested-by on patch 2.
v6: https://lore.kernel.org/all/20260909095228.2174031-1-kas@kernel.org/
v5: https://lore.kernel.org/all/20260907095835.1233352-1-kas@kernel.org/
v4: https://lore.kernel.org/all/20260902121724.3494954-1-kas@kernel.org/
v3: https://lore.kernel.org/all/20260706084708.8072-1-kas@kernel.org/
Range-diff against v6:
1: ee766da1c34e ! 1: 65d617205157 iommu/arm-smmu-v3: Add a cmdq_max_n_shift module parameter
@@ Commit message
Add cmdq_max_n_shift, a cap on the depth given as the log2 of the entry
count, the form the hardware itself takes in the LOG2SIZE field of
- CMDQ_BASE. Decide the depth in arm_smmu_cmdq_max_n_shift(), which caps the
- IDR1 value for natural alignment and then applies the parameter, so the
- queue is allocated at the requested size. The Tegra241 CMDQV sizes its
- VCMDQs from IDR1 itself, so route that through the same helper.
+ CMDQ_BASE. It defaults to the largest depth the driver allocates, so an
+ unset parameter changes nothing. Decide the depth in
+ arm_smmu_cmdq_max_n_shift(), which applies the parameter to the IDR1
+ value, so the queue is allocated at the requested size. The Tegra241 CMDQV
+ sizes its VCMDQs from IDR1 itself, so route that through the same helper.
Floor the request at one page worth of entries. Without the floor, a small
request trips the CMDQ_BATCH_ENTRIES check in arm_smmu_device_hw_probe()
@@ Commit message
fills the page.
Assisted-by: LLM
+ Reviewed-by: Breno Leitao <leitao@debian.org>
+ Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
## drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c ##
@@ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c: module_param(disable_msipolling, bo
MODULE_PARM_DESC(disable_msipolling,
"Disable MSI-based polling for CMD_SYNC completion.");
-+static unsigned int cmdq_max_n_shift;
++static unsigned int cmdq_max_n_shift = CMDQ_MAX_SZ_SHIFT;
+module_param(cmdq_max_n_shift, uint, 0444);
+MODULE_PARM_DESC(cmdq_max_n_shift,
-+ "Cap on the command queue depth, as log2 of the number of entries. Zero means the hardware maximum; the queue never shrinks below one page.");
++ "Cap on the command queue depth, as log2 of the number of entries. Defaults to the hardware maximum; the queue never shrinks below one page.");
+
static const struct iommu_ops arm_smmu_ops;
static struct iommu_dirty_ops arm_smmu_dirty_ops;
@@ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c: static struct iommu_dirty_ops arm_s
+
+/**
+ * arm_smmu_queue_max_n_shift() - pick the log2 depth of a queue
-+ * @ceiling: default log2 depth ceiling of the queue
++ * @hw_max_n_shift: log2 depth the hardware advertises in IDR1
+ * @ent_sz_shift: log2 of the queue entry size in bytes
-+ * @shift: log2 depth asked for, or zero for the default
++ * @limit_n_shift: log2 depth to cap the queue at
+ *
-+ * @shift is floored at one page, because coherent DMA is page granular: a
-+ * shallower queue occupies the same memory as one that fills the page, and
-+ * arm_smmu_init_one_queue() stops shrinking at a page too.
++ * @limit_n_shift is floored at one page, because coherent DMA is page
++ * granular: a shallower queue occupies the same memory as one that fills the
++ * page, and arm_smmu_init_one_queue() stops shrinking at a page too.
+ */
-+static u32 arm_smmu_queue_max_n_shift(u32 ceiling, u32 ent_sz_shift, u32 shift)
++static u32 arm_smmu_queue_max_n_shift(u32 hw_max_n_shift, u32 ent_sz_shift,
++ u32 limit_n_shift)
+{
-+ u32 floor = PAGE_SHIFT - ent_sz_shift;
++ u32 floor_n_shift = PAGE_SHIFT - ent_sz_shift;
+
-+ if (!shift)
-+ return ceiling;
-+
-+ return min(ceiling, max(shift, floor));
++ limit_n_shift = max(limit_n_shift, floor_n_shift);
++ return min(hw_max_n_shift, limit_n_shift);
+}
+
+/*
+ * Command queues are also allocated by the Tegra241 CMDQV for its VCMDQs, which
+ * need the same depth decision.
+ */
-+u32 arm_smmu_cmdq_max_n_shift(u32 ceiling)
++u32 arm_smmu_cmdq_max_n_shift(u32 hw_max_n_shift)
+{
+ /* Capped to ensure natural alignment */
-+ ceiling = min(CMDQ_MAX_SZ_SHIFT, ceiling);
-+
-+ return arm_smmu_queue_max_n_shift(ceiling, CMDQ_ENT_SZ_SHIFT,
-+ cmdq_max_n_shift);
++ return arm_smmu_queue_max_n_shift(hw_max_n_shift, CMDQ_ENT_SZ_SHIFT,
++ min(CMDQ_MAX_SZ_SHIFT, cmdq_max_n_shift));
+}
+
int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
@@ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h: static inline void arm_smmu_domain_
void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq *cmdq);
-+u32 arm_smmu_cmdq_max_n_shift(u32 ceiling);
++u32 arm_smmu_cmdq_max_n_shift(u32 hw_max_n_shift);
int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
struct arm_smmu_queue *q, void __iomem *page,
unsigned long prod_off, unsigned long cons_off,
2: a1fc639ce127 ! 2: 07a25755ccf1 iommu/arm-smmu-v3: Default queue depths to one page in a kdump kernel
@@ Commit message
that either serve the handful of devices used to save the dump or are
switched off outright, and it is memory the dump itself needs.
- Default all three depths to one page worth of entries when
- is_kdump_kernel(). The queues carry commands and fault records rather than
- DMA data, so dump throughput is unaffected. A shallower command queue only
- bounds how many commands may be in flight before a sync, which does not
- matter for the few devices that save the dump.
-
- An explicit cmdq_max_n_shift still wins, so a capture kernel that wants a
- deeper command queue can ask for one on the command line.
+ Size all three queues at one page worth of entries when is_kdump_kernel().
+ The queues carry commands and fault records rather than DMA data, so dump
+ throughput is unaffected. A shallower command queue only bounds how many
+ commands may be in flight before a sync, which does not matter for the few
+ devices that save the dump. The cmdq_max_n_shift parameter therefore does
+ not apply in a capture kernel.
Suggested-by: Kyle McMartin <jkkm@meta.com>
Assisted-by: LLM
+ Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
+ Tested-by: Yuanhe Shu <xiangzao@linux.alibaba.com>
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
## drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c ##
+@@ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c: MODULE_PARM_DESC(disable_msipolling,
+ static unsigned int cmdq_max_n_shift = CMDQ_MAX_SZ_SHIFT;
+ module_param(cmdq_max_n_shift, uint, 0444);
+ MODULE_PARM_DESC(cmdq_max_n_shift,
+- "Cap on the command queue depth, as log2 of the number of entries. Defaults to the hardware maximum; the queue never shrinks below one page.");
++ "Cap on the command queue depth, as log2 of the number of entries. Defaults to the hardware maximum; the queue never shrinks below one page. A kdump kernel always uses one page.");
+
+ static const struct iommu_ops arm_smmu_ops;
+ static struct iommu_dirty_ops arm_smmu_dirty_ops;
@@ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c: static struct iommu_dirty_ops arm_smmu_dirty_ops = {
* @ent_sz_shift: log2 of the queue entry size in bytes
- * @shift: log2 depth asked for, or zero for the default
+ * @limit_n_shift: log2 depth to cap the queue at
*
-+ * The default is @ceiling, except in a kdump capture kernel, which defaults to
-+ * one page worth of entries.
++ * A kdump capture kernel gets one page worth of entries whatever the limit.
+ *
- * @shift is floored at one page, because coherent DMA is page granular: a
- * shallower queue occupies the same memory as one that fills the page, and
- * arm_smmu_init_one_queue() stops shrinking at a page too.
-@@ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c: static u32 arm_smmu_queue_max_n_shift(u32 ceiling, u32 ent_sz_shift, u32 shift)
+ * @limit_n_shift is floored at one page, because coherent DMA is page
+ * granular: a shallower queue occupies the same memory as one that fills the
+ * page, and arm_smmu_init_one_queue() stops shrinking at a page too.
+@@ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c: static u32 arm_smmu_queue_max_n_shift(u32 hw_max_n_shift, u32 ent_sz_shift,
{
- u32 floor = PAGE_SHIFT - ent_sz_shift;
+ u32 floor_n_shift = PAGE_SHIFT - ent_sz_shift;
-- if (!shift)
-+ if (shift)
-+ shift = max(shift, floor);
-+ else if (is_kdump_kernel())
-+ shift = floor;
-+ else
- return ceiling;
-
-- return min(ceiling, max(shift, floor));
-+ return min(ceiling, shift);
-+}
++ if (is_kdump_kernel())
++ return min(hw_max_n_shift, floor_n_shift);
+
-+static inline u32 arm_smmu_evtq_max_n_shift(u32 ceiling)
-+{
-+ /* Capped to ensure natural alignment */
-+ ceiling = min(EVTQ_MAX_SZ_SHIFT, ceiling);
-+
-+ return arm_smmu_queue_max_n_shift(ceiling, EVTQ_ENT_SZ_SHIFT, 0);
-+}
-+
-+static inline u32 arm_smmu_priq_max_n_shift(u32 ceiling)
-+{
-+ /* Capped to ensure natural alignment */
-+ ceiling = min(PRIQ_MAX_SZ_SHIFT, ceiling);
-+
-+ return arm_smmu_queue_max_n_shift(ceiling, PRIQ_ENT_SZ_SHIFT, 0);
+ limit_n_shift = max(limit_n_shift, floor_n_shift);
+ return min(hw_max_n_shift, limit_n_shift);
}
++static inline u32 arm_smmu_evtq_max_n_shift(u32 hw_max_n_shift)
++{
++ /* Capped to ensure natural alignment */
++ return arm_smmu_queue_max_n_shift(hw_max_n_shift, EVTQ_ENT_SZ_SHIFT,
++ EVTQ_MAX_SZ_SHIFT);
++}
++
++static inline u32 arm_smmu_priq_max_n_shift(u32 hw_max_n_shift)
++{
++ /* Capped to ensure natural alignment */
++ return arm_smmu_queue_max_n_shift(hw_max_n_shift, PRIQ_ENT_SZ_SHIFT,
++ PRIQ_MAX_SZ_SHIFT);
++}
++
/*
+ * Command queues are also allocated by the Tegra241 CMDQV for its VCMDQs, which
+ * need the same depth decision.
@@ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c: static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
if (reg & IDR1_ATTR_TYPES_OVR)
smmu->features |= ARM_SMMU_FEAT_ATTR_TYPES_OVR;
Kiryl Shutsemau (Meta) (2):
iommu/arm-smmu-v3: Add a cmdq_max_n_shift module parameter
iommu/arm-smmu-v3: Default queue depths to one page in a kdump kernel
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 68 +++++++++++++++++--
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 1 +
.../iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 2 +-
3 files changed, 63 insertions(+), 8 deletions(-)
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
--
2.54.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v7 1/2] iommu/arm-smmu-v3: Add a cmdq_max_n_shift module parameter
2026-09-25 14:15 [PATCH v7 0/2] iommu/arm-smmu-v3: Make the queue depths tunable, and shrink them in a kdump kernel Kiryl Shutsemau
@ 2026-09-25 14:15 ` Kiryl Shutsemau
2026-09-25 14:15 ` [PATCH v7 2/2] iommu/arm-smmu-v3: Default queue depths to one page in a kdump kernel Kiryl Shutsemau
1 sibling, 0 replies; 4+ messages in thread
From: Kiryl Shutsemau @ 2026-09-25 14:15 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Thierry Reding,
Jonathan Hunter, Jason Gunthorpe, Nicolin Chen, Breno Leitao
Cc: Kiryl Shutsemau (Meta),
Krishna Reddy, Pranjal Shrivastava, Mostafa Saleh, Ashish Mhetre,
Shameer Kolothum, Yuanhe Shu, Kyle McMartin, Usama Arif,
kernel-team, linux-arm-kernel, iommu, linux-tegra, linux-kernel
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
The command queue depth comes straight from the maximum the hardware
advertises in IDR1, which reaches megabytes of coherent DMA per queue.
A system with several SMMUv3 instances pays that per instance, and the
Tegra241 CMDQV pays it again for every VCMDQ it preallocates.
Queue depth only bounds how many commands may be in flight before a sync.
A machine driving a handful of devices, or one with a tight memory budget,
has no use for the maximum, and no way to say so.
Add cmdq_max_n_shift, a cap on the depth given as the log2 of the entry
count, the form the hardware itself takes in the LOG2SIZE field of
CMDQ_BASE. It defaults to the largest depth the driver allocates, so an
unset parameter changes nothing. Decide the depth in
arm_smmu_cmdq_max_n_shift(), which applies the parameter to the IDR1
value, so the queue is allocated at the requested size. The Tegra241 CMDQV
sizes its VCMDQs from IDR1 itself, so route that through the same helper.
Floor the request at one page worth of entries. Without the floor, a small
request trips the CMDQ_BATCH_ENTRIES check in arm_smmu_device_hw_probe()
and the SMMU fails to probe. The floor also costs nothing: coherent DMA is
page granular, so a shallower queue occupies the same memory as one that
fills the page.
Assisted-by: LLM
Reviewed-by: Breno Leitao <leitao@debian.org>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 40 ++++++++++++++++++-
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 1 +
.../iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 2 +-
3 files changed, 40 insertions(+), 3 deletions(-)
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 5732f3ba0122..810d3ce75089 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -40,6 +40,11 @@ module_param(disable_msipolling, bool, 0444);
MODULE_PARM_DESC(disable_msipolling,
"Disable MSI-based polling for CMD_SYNC completion.");
+static unsigned int cmdq_max_n_shift = CMDQ_MAX_SZ_SHIFT;
+module_param(cmdq_max_n_shift, uint, 0444);
+MODULE_PARM_DESC(cmdq_max_n_shift,
+ "Cap on the command queue depth, as log2 of the number of entries. Defaults to the hardware maximum; the queue never shrinks below one page.");
+
static const struct iommu_ops arm_smmu_ops;
static struct iommu_dirty_ops arm_smmu_dirty_ops;
@@ -4412,6 +4417,37 @@ static struct iommu_dirty_ops arm_smmu_dirty_ops = {
};
/* Probing and initialisation functions */
+
+/**
+ * arm_smmu_queue_max_n_shift() - pick the log2 depth of a queue
+ * @hw_max_n_shift: log2 depth the hardware advertises in IDR1
+ * @ent_sz_shift: log2 of the queue entry size in bytes
+ * @limit_n_shift: log2 depth to cap the queue at
+ *
+ * @limit_n_shift is floored at one page, because coherent DMA is page
+ * granular: a shallower queue occupies the same memory as one that fills the
+ * page, and arm_smmu_init_one_queue() stops shrinking at a page too.
+ */
+static u32 arm_smmu_queue_max_n_shift(u32 hw_max_n_shift, u32 ent_sz_shift,
+ u32 limit_n_shift)
+{
+ u32 floor_n_shift = PAGE_SHIFT - ent_sz_shift;
+
+ limit_n_shift = max(limit_n_shift, floor_n_shift);
+ return min(hw_max_n_shift, limit_n_shift);
+}
+
+/*
+ * Command queues are also allocated by the Tegra241 CMDQV for its VCMDQs, which
+ * need the same depth decision.
+ */
+u32 arm_smmu_cmdq_max_n_shift(u32 hw_max_n_shift)
+{
+ /* Capped to ensure natural alignment */
+ return arm_smmu_queue_max_n_shift(hw_max_n_shift, CMDQ_ENT_SZ_SHIFT,
+ min(CMDQ_MAX_SZ_SHIFT, cmdq_max_n_shift));
+}
+
int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
struct arm_smmu_queue *q, void __iomem *page,
unsigned long prod_off, unsigned long cons_off,
@@ -5156,8 +5192,8 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
smmu->features |= ARM_SMMU_FEAT_ATTR_TYPES_OVR;
/* Queue sizes, capped to ensure natural alignment */
- smmu->cmdq.q.llq.max_n_shift = min_t(u32, CMDQ_MAX_SZ_SHIFT,
- FIELD_GET(IDR1_CMDQS, reg));
+ smmu->cmdq.q.llq.max_n_shift =
+ arm_smmu_cmdq_max_n_shift(FIELD_GET(IDR1_CMDQS, reg));
if (smmu->cmdq.q.llq.max_n_shift <= ilog2(CMDQ_BATCH_ENTRIES)) {
/*
* We don't support splitting up batches, so one batch of
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index 50f8321e979c..ea4c87bbe253 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -1165,6 +1165,7 @@ static inline void arm_smmu_domain_inv(struct arm_smmu_domain *smmu_domain)
void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq *cmdq);
+u32 arm_smmu_cmdq_max_n_shift(u32 hw_max_n_shift);
int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
struct arm_smmu_queue *q, void __iomem *page,
unsigned long prod_off, unsigned long cons_off,
diff --git a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
index 6644075c1431..710a4c694b94 100644
--- a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
+++ b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
@@ -663,7 +663,7 @@ static int tegra241_vcmdq_alloc_smmu_cmdq(struct tegra241_vcmdq *vcmdq)
/* Cap queue size to SMMU's IDR1.CMDQS and ensure natural alignment */
regval = readl_relaxed(smmu->base + ARM_SMMU_IDR1);
q->llq.max_n_shift =
- min_t(u32, CMDQ_MAX_SZ_SHIFT, FIELD_GET(IDR1_CMDQS, regval));
+ arm_smmu_cmdq_max_n_shift(FIELD_GET(IDR1_CMDQS, regval));
/* Use the common helper to init the VCMDQ, and then... */
ret = arm_smmu_init_one_queue(smmu, q, vcmdq->page0,
--
2.54.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v7 2/2] iommu/arm-smmu-v3: Default queue depths to one page in a kdump kernel
2026-09-25 14:15 [PATCH v7 0/2] iommu/arm-smmu-v3: Make the queue depths tunable, and shrink them in a kdump kernel Kiryl Shutsemau
2026-09-25 14:15 ` [PATCH v7 1/2] iommu/arm-smmu-v3: Add a cmdq_max_n_shift module parameter Kiryl Shutsemau
@ 2026-09-25 14:15 ` Kiryl Shutsemau
2026-09-25 15:36 ` Breno Leitao
1 sibling, 1 reply; 4+ messages in thread
From: Kiryl Shutsemau @ 2026-09-25 14:15 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Thierry Reding,
Jonathan Hunter, Jason Gunthorpe, Nicolin Chen, Breno Leitao
Cc: Kiryl Shutsemau (Meta),
Krishna Reddy, Pranjal Shrivastava, Mostafa Saleh, Ashish Mhetre,
Shameer Kolothum, Yuanhe Shu, Kyle McMartin, Usama Arif,
kernel-team, linux-arm-kernel, iommu, linux-tegra, linux-kernel
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
All three queues are sized from the maxima the hardware advertises in IDR1
and allocated at probe, up to 4 MB each on a 4K-page kernel. The capture
kernel already disables two of them: arm_smmu_device_reset() drops
CR0_EVTQEN and CR0_PRIQEN. It still allocates both at full size.
A kdump capture kernel runs from a small crashkernel reservation, and every
SMMUv3 instance pays that cost again, up to 12 MB apiece. It goes to queues
that either serve the handful of devices used to save the dump or are
switched off outright, and it is memory the dump itself needs.
Size all three queues at one page worth of entries when is_kdump_kernel().
The queues carry commands and fault records rather than DMA data, so dump
throughput is unaffected. A shallower command queue only bounds how many
commands may be in flight before a sync, which does not matter for the few
devices that save the dump. The cmdq_max_n_shift parameter therefore does
not apply in a capture kernel.
Suggested-by: Kyle McMartin <jkkm@meta.com>
Assisted-by: LLM
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Tested-by: Yuanhe Shu <xiangzao@linux.alibaba.com>
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 30 ++++++++++++++++-----
1 file changed, 24 insertions(+), 6 deletions(-)
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 810d3ce75089..a5ad57432dfd 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -43,7 +43,7 @@ MODULE_PARM_DESC(disable_msipolling,
static unsigned int cmdq_max_n_shift = CMDQ_MAX_SZ_SHIFT;
module_param(cmdq_max_n_shift, uint, 0444);
MODULE_PARM_DESC(cmdq_max_n_shift,
- "Cap on the command queue depth, as log2 of the number of entries. Defaults to the hardware maximum; the queue never shrinks below one page.");
+ "Cap on the command queue depth, as log2 of the number of entries. Defaults to the hardware maximum; the queue never shrinks below one page. A kdump kernel always uses one page.");
static const struct iommu_ops arm_smmu_ops;
static struct iommu_dirty_ops arm_smmu_dirty_ops;
@@ -4424,6 +4424,8 @@ static struct iommu_dirty_ops arm_smmu_dirty_ops = {
* @ent_sz_shift: log2 of the queue entry size in bytes
* @limit_n_shift: log2 depth to cap the queue at
*
+ * A kdump capture kernel gets one page worth of entries whatever the limit.
+ *
* @limit_n_shift is floored at one page, because coherent DMA is page
* granular: a shallower queue occupies the same memory as one that fills the
* page, and arm_smmu_init_one_queue() stops shrinking at a page too.
@@ -4433,10 +4435,27 @@ static u32 arm_smmu_queue_max_n_shift(u32 hw_max_n_shift, u32 ent_sz_shift,
{
u32 floor_n_shift = PAGE_SHIFT - ent_sz_shift;
+ if (is_kdump_kernel())
+ return min(hw_max_n_shift, floor_n_shift);
+
limit_n_shift = max(limit_n_shift, floor_n_shift);
return min(hw_max_n_shift, limit_n_shift);
}
+static inline u32 arm_smmu_evtq_max_n_shift(u32 hw_max_n_shift)
+{
+ /* Capped to ensure natural alignment */
+ return arm_smmu_queue_max_n_shift(hw_max_n_shift, EVTQ_ENT_SZ_SHIFT,
+ EVTQ_MAX_SZ_SHIFT);
+}
+
+static inline u32 arm_smmu_priq_max_n_shift(u32 hw_max_n_shift)
+{
+ /* Capped to ensure natural alignment */
+ return arm_smmu_queue_max_n_shift(hw_max_n_shift, PRIQ_ENT_SZ_SHIFT,
+ PRIQ_MAX_SZ_SHIFT);
+}
+
/*
* Command queues are also allocated by the Tegra241 CMDQV for its VCMDQs, which
* need the same depth decision.
@@ -5191,7 +5210,6 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
if (reg & IDR1_ATTR_TYPES_OVR)
smmu->features |= ARM_SMMU_FEAT_ATTR_TYPES_OVR;
- /* Queue sizes, capped to ensure natural alignment */
smmu->cmdq.q.llq.max_n_shift =
arm_smmu_cmdq_max_n_shift(FIELD_GET(IDR1_CMDQS, reg));
if (smmu->cmdq.q.llq.max_n_shift <= ilog2(CMDQ_BATCH_ENTRIES)) {
@@ -5206,10 +5224,10 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
return -ENXIO;
}
- smmu->evtq.q.llq.max_n_shift = min_t(u32, EVTQ_MAX_SZ_SHIFT,
- FIELD_GET(IDR1_EVTQS, reg));
- smmu->priq.q.llq.max_n_shift = min_t(u32, PRIQ_MAX_SZ_SHIFT,
- FIELD_GET(IDR1_PRIQS, reg));
+ smmu->evtq.q.llq.max_n_shift =
+ arm_smmu_evtq_max_n_shift(FIELD_GET(IDR1_EVTQS, reg));
+ smmu->priq.q.llq.max_n_shift =
+ arm_smmu_priq_max_n_shift(FIELD_GET(IDR1_PRIQS, reg));
/* SID/SSID sizes */
smmu->ssid_bits = FIELD_GET(IDR1_SSIDSIZE, reg);
--
2.54.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v7 2/2] iommu/arm-smmu-v3: Default queue depths to one page in a kdump kernel
2026-09-25 14:15 ` [PATCH v7 2/2] iommu/arm-smmu-v3: Default queue depths to one page in a kdump kernel Kiryl Shutsemau
@ 2026-09-25 15:36 ` Breno Leitao
0 siblings, 0 replies; 4+ messages in thread
From: Breno Leitao @ 2026-09-25 15:36 UTC (permalink / raw)
To: Kiryl Shutsemau
Cc: Will Deacon, Robin Murphy, Joerg Roedel, Thierry Reding,
Jonathan Hunter, Jason Gunthorpe, Nicolin Chen,
Kiryl Shutsemau (Meta),
Krishna Reddy, Pranjal Shrivastava, Mostafa Saleh, Ashish Mhetre,
Shameer Kolothum, Yuanhe Shu, Kyle McMartin, Usama Arif,
kernel-team, linux-arm-kernel, iommu, linux-tegra, linux-kernel
On Fri, Sep 25, 2026 at 03:15:30PM +0100, Kiryl Shutsemau wrote:
> From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
>
> All three queues are sized from the maxima the hardware advertises in IDR1
> and allocated at probe, up to 4 MB each on a 4K-page kernel. The capture
> kernel already disables two of them: arm_smmu_device_reset() drops
> CR0_EVTQEN and CR0_PRIQEN. It still allocates both at full size.
>
> A kdump capture kernel runs from a small crashkernel reservation, and every
> SMMUv3 instance pays that cost again, up to 12 MB apiece. It goes to queues
> that either serve the handful of devices used to save the dump or are
> switched off outright, and it is memory the dump itself needs.
>
> Size all three queues at one page worth of entries when is_kdump_kernel().
> The queues carry commands and fault records rather than DMA data, so dump
> throughput is unaffected. A shallower command queue only bounds how many
> commands may be in flight before a sync, which does not matter for the few
> devices that save the dump. The cmdq_max_n_shift parameter therefore does
> not apply in a capture kernel.
>
> Suggested-by: Kyle McMartin <jkkm@meta.com>
> Assisted-by: LLM
> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
> Tested-by: Yuanhe Shu <xiangzao@linux.alibaba.com>
> Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
Reviewed-by: Breno Leitao <leitao@debian.org>
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-25 15:36 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-25 14:15 [PATCH v7 0/2] iommu/arm-smmu-v3: Make the queue depths tunable, and shrink them in a kdump kernel Kiryl Shutsemau
2026-09-25 14:15 ` [PATCH v7 1/2] iommu/arm-smmu-v3: Add a cmdq_max_n_shift module parameter Kiryl Shutsemau
2026-09-25 14:15 ` [PATCH v7 2/2] iommu/arm-smmu-v3: Default queue depths to one page in a kdump kernel Kiryl Shutsemau
2026-09-25 15:36 ` Breno Leitao
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®