* [PATCH v5 0/2] iommu/arm-smmu-v3: Make the queue depths tunable, and shrink them in a kdump kernel
@ 2026-09-07 9:58 Kiryl Shutsemau (Meta)
2026-09-07 9:58 ` [PATCH v5 1/2] iommu/arm-smmu-v3: Add a cmdq_max_entries module parameter Kiryl Shutsemau (Meta)
2026-09-07 9:58 ` [PATCH v5 2/2] iommu/arm-smmu-v3: Default queue depths to one page in a kdump kernel Kiryl Shutsemau (Meta)
0 siblings, 2 replies; 8+ messages in thread
From: Kiryl Shutsemau (Meta) @ 2026-09-07 9:58 UTC (permalink / raw)
To: Will Deacon, Robin Murphy, Joerg Roedel, Nicolin Chen
Cc: Jason Gunthorpe, Pranjal Shrivastava, Mostafa Saleh,
Thierry Reding, Krishna Reddy, Jonathan Hunter, Breno Leitao,
Kyle McMartin, Usama Arif, kernel-team, linux-arm-kernel, iommu,
linux-tegra, linux-kernel, Kiryl Shutsemau (Meta)
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_entries module parameter, decided in a per-queue
helper and floored at one page. Patch 2 has a kdump kernel default all
three depths to one page through the same helper. An explicit
cmdq_max_entries still wins.
Measured per instance under QEMU on -M virt,iommu=smmuv3, through a real
panic and kexec into a capture kernel:
4K page 64K page
cmdq 1 MB -> 4 KB 8 MB -> 64 KB
evtq 1 MB -> 4 KB 16 MB -> 64 KB
Every clamped queue lands on exactly one page. cmdq_max_entries moves the
command queue alone and beats the kdump default; the capture kernel
attached four devices with no CMD_SYNC timeout, GERROR or context fault.
QEMU exposes no PRI queue, which takes the same path. Build-tested across
4K/16K/64K, TEGRA241_CMDQV=n, CRASH_DUMP=n and =m, every commit
warning-free.
v5:
- Rename cmdq_entries to cmdq_max_entries (Nicolin).
- Take Nicolin's refactor: ceiling/floor naming, the alignment cap inside
the per-queue helpers, evtq/priq helpers, FIELD_GET() at the call sites.
- Keep the override in entries rather than a shift: ilog2(1) is 0 and
collides with the "use the default" sentinel, so cmdq_max_entries=1
would have given the hardware maximum instead of the floor.
- min() for min_t() now that the types allow it. Changelogs tightened.
v4: https://lore.kernel.org/all/20260902121724.3494954-1-kas@kernel.org/
v3: https://lore.kernel.org/all/20260706084708.8072-1-kas@kernel.org/
Kiryl Shutsemau (Meta) (2):
iommu/arm-smmu-v3: Add a cmdq_max_entries 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 | 78 +++++++++++++++++--
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 1 +
.../iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 2 +-
3 files changed, 73 insertions(+), 8 deletions(-)
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
--
2.54.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH v5 1/2] iommu/arm-smmu-v3: Add a cmdq_max_entries module parameter 2026-09-07 9:58 [PATCH v5 0/2] iommu/arm-smmu-v3: Make the queue depths tunable, and shrink them in a kdump kernel Kiryl Shutsemau (Meta) @ 2026-09-07 9:58 ` Kiryl Shutsemau (Meta) 2026-09-07 21:56 ` Nicolin Chen 2026-09-07 9:58 ` [PATCH v5 2/2] iommu/arm-smmu-v3: Default queue depths to one page in a kdump kernel Kiryl Shutsemau (Meta) 1 sibling, 1 reply; 8+ messages in thread From: Kiryl Shutsemau (Meta) @ 2026-09-07 9:58 UTC (permalink / raw) To: Will Deacon, Robin Murphy, Joerg Roedel, Nicolin Chen Cc: Jason Gunthorpe, Pranjal Shrivastava, Mostafa Saleh, Thierry Reding, Krishna Reddy, Jonathan Hunter, Breno Leitao, Kyle McMartin, Usama Arif, kernel-team, linux-arm-kernel, iommu, linux-tegra, linux-kernel, Kiryl Shutsemau (Meta) 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_entries, an upper bound on the number of command queue entries. 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. Round the request down to a power of two and floor it 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. Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org> Assisted-by: Claude-Code:claude-opus-5 --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 45 ++++++++++++++++++- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 1 + .../iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 2 +- 3 files changed, 45 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..4550b1105e9c 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_entries; +module_param(cmdq_max_entries, uint, 0444); +MODULE_PARM_DESC(cmdq_max_entries, + "Upper bound on the number of command queue entries, rounded down to a power of two and up to at least one page. Zero means the hardware maximum."); + static const struct iommu_ops arm_smmu_ops; static struct iommu_dirty_ops arm_smmu_dirty_ops; @@ -4412,6 +4417,42 @@ 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 + * @ceiling: default log2 depth ceiling of the queue + * @ent_sz_shift: log2 of the queue entry size in bytes + * @entries: number of entries to cap the queue at, or zero for the default + * + * @entries is rounded down to a power of two and 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 entries) +{ + u32 floor = PAGE_SHIFT - ent_sz_shift; + + if (!entries) + return ceiling; + + return min(ceiling, max(ilog2(entries), floor)); +} + +/* + * 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) +{ + /* 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_entries); +} + 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 +5197,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..11ae4d8f4ad2 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 ceiling); 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] 8+ messages in thread
* Re: [PATCH v5 1/2] iommu/arm-smmu-v3: Add a cmdq_max_entries module parameter 2026-09-07 9:58 ` [PATCH v5 1/2] iommu/arm-smmu-v3: Add a cmdq_max_entries module parameter Kiryl Shutsemau (Meta) @ 2026-09-07 21:56 ` Nicolin Chen 2026-09-08 9:19 ` Kiryl Shutsemau 0 siblings, 1 reply; 8+ messages in thread From: Nicolin Chen @ 2026-09-07 21:56 UTC (permalink / raw) To: Kiryl Shutsemau (Meta) Cc: Will Deacon, Robin Murphy, Joerg Roedel, Jason Gunthorpe, Pranjal Shrivastava, Mostafa Saleh, Thierry Reding, Krishna Reddy, Jonathan Hunter, Breno Leitao, Kyle McMartin, Usama Arif, kernel-team, linux-arm-kernel, iommu, linux-tegra, linux-kernel On Mon, Sep 07, 2026 at 10:58:34AM +0100, Kiryl Shutsemau (Meta) wrote: I still think that cmdq_max_n_shift can slightly tidy things here. > +static u32 arm_smmu_queue_max_n_shift(u32 ceiling, u32 ent_sz_shift, > + u32 entries) Here, all three inputs would have been "shifts", instead of two "shifts" and one "number of entries". > +{ > + u32 floor = PAGE_SHIFT - ent_sz_shift; > + > + if (!entries) > + return ceiling; > + > + return min(ceiling, max(ilog2(entries), floor)); And I see Sashiko keeps complaining against the ilog2 here: " Does this trigger a build failure due to strict type checking? The ilog2(entries) expression returns a signed int when entries is derived from a runtime value like the module parameter, while floor is explicitly declared as an unsigned u32. Since the kernel's max() macro enforces strict type compatibility, this mismatch evaluates to 0 in the type checking logic, triggering a signedness error during compilation. Should this use max_t(u32, ilog2(entries), floor) instead? " Though Sashiko complained about the min_t() in v4 as well.. With cmdq_max_n_shift, there is no ilog2 and no type mismatch. If you can address or justify the Sashiko finding, I am fine with the cmdq_max_entries though.. So, in either way, the patch looks good to me, Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v5 1/2] iommu/arm-smmu-v3: Add a cmdq_max_entries module parameter 2026-09-07 21:56 ` Nicolin Chen @ 2026-09-08 9:19 ` Kiryl Shutsemau 2026-09-08 21:56 ` Nicolin Chen 0 siblings, 1 reply; 8+ messages in thread From: Kiryl Shutsemau @ 2026-09-08 9:19 UTC (permalink / raw) To: Nicolin Chen Cc: Will Deacon, Robin Murphy, Joerg Roedel, Jason Gunthorpe, Pranjal Shrivastava, Mostafa Saleh, Thierry Reding, Krishna Reddy, Jonathan Hunter, Breno Leitao, Kyle McMartin, Usama Arif, kernel-team, linux-arm-kernel, iommu, linux-tegra, linux-kernel On Mon, Sep 07, 2026 at 02:56:26PM -0700, Nicolin Chen wrote: > On Mon, Sep 07, 2026 at 10:58:34AM +0100, Kiryl Shutsemau (Meta) wrote: > > I still think that cmdq_max_n_shift can slightly tidy things here. > > > +static u32 arm_smmu_queue_max_n_shift(u32 ceiling, u32 ent_sz_shift, > > + u32 entries) > > Here, all three inputs would have been "shifts", instead of two > "shifts" and one "number of entries". > > > +{ > > + u32 floor = PAGE_SHIFT - ent_sz_shift; > > + > > + if (!entries) > > + return ceiling; > > + > > + return min(ceiling, max(ilog2(entries), floor)); > > And I see Sashiko keeps complaining against the ilog2 here: It does build: GCC 15 and clang 21, at -O2 and -Os, without a warning. But the reason is not obvious. ilog2() on a runtime u32 returns int, and minmax.h only accepts an int against a u32 when __is_nonneg() can prove it non-negative at compile time. __ilog2_u32() is fls(n) - 1, so that proof only exists because the if (entries) guard lets the compiler see entries != 0 through the inlined fls(). But this is fragile. If a compiler does not get there, or a later change that moves the guard, it turns it into a BUILD_BUG_ON. Rather than a max_t() cast, we can give the shift its type first: if (entries) { new_ceiling = ilog2(entries); new_ceiling = max(new_ceiling, floor); } else if (is_kdump_kernel()) { Two u32s, nothing left for the compiler to prove, same result. If it looks good, I can re-spin v6 with the change. Thanks for the review and the test! -- Kiryl Shutsemau / Kirill A. Shutemov ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v5 1/2] iommu/arm-smmu-v3: Add a cmdq_max_entries module parameter 2026-09-08 9:19 ` Kiryl Shutsemau @ 2026-09-08 21:56 ` Nicolin Chen 2026-09-09 9:50 ` Kiryl Shutsemau 0 siblings, 1 reply; 8+ messages in thread From: Nicolin Chen @ 2026-09-08 21:56 UTC (permalink / raw) To: Kiryl Shutsemau Cc: Will Deacon, Robin Murphy, Joerg Roedel, Jason Gunthorpe, Pranjal Shrivastava, Mostafa Saleh, Thierry Reding, Krishna Reddy, Jonathan Hunter, Breno Leitao, Kyle McMartin, Usama Arif, kernel-team, linux-arm-kernel, iommu, linux-tegra, linux-kernel On Tue, Sep 08, 2026 at 10:19:44AM +0100, Kiryl Shutsemau wrote: > On Mon, Sep 07, 2026 at 02:56:26PM -0700, Nicolin Chen wrote: > > On Mon, Sep 07, 2026 at 10:58:34AM +0100, Kiryl Shutsemau (Meta) wrote: > > > > I still think that cmdq_max_n_shift can slightly tidy things here. > > > > > +static u32 arm_smmu_queue_max_n_shift(u32 ceiling, u32 ent_sz_shift, > > > + u32 entries) > > > > Here, all three inputs would have been "shifts", instead of two > > "shifts" and one "number of entries". > > > > > +{ > > > + u32 floor = PAGE_SHIFT - ent_sz_shift; > > > + > > > + if (!entries) > > > + return ceiling; > > > + > > > + return min(ceiling, max(ilog2(entries), floor)); > > > > And I see Sashiko keeps complaining against the ilog2 here: > > It does build: GCC 15 and clang 21, at -O2 and -Os, without a warning. > > But the reason is not obvious. > > ilog2() on a runtime u32 returns int, and minmax.h only accepts an int > against a u32 when __is_nonneg() can prove it non-negative at compile > time. > > __ilog2_u32() is fls(n) - 1, so that proof only exists because the > if (entries) guard lets the compiler see entries != 0 through the inlined > fls(). > > But this is fragile. If a compiler does not get there, or a later change > that moves the guard, it turns it into a BUILD_BUG_ON. > > Rather than a max_t() cast, we can give the shift its type first: > > if (entries) { > new_ceiling = ilog2(entries); > new_ceiling = max(new_ceiling, floor); > } else if (is_kdump_kernel()) { > > Two u32s, nothing left for the compiler to prove, same result. > > If it looks good, I can re-spin v6 with the change. It looks probably okay.. though I still don't get why you aren't picking the straightforward "max_n_shift" over "max_entries". max_n_shift is used by both HW and SW, and it does not have such converting problem or need min/max, making the code cleaner :-/ Nicolin ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v5 1/2] iommu/arm-smmu-v3: Add a cmdq_max_entries module parameter 2026-09-08 21:56 ` Nicolin Chen @ 2026-09-09 9:50 ` Kiryl Shutsemau 0 siblings, 0 replies; 8+ messages in thread From: Kiryl Shutsemau @ 2026-09-09 9:50 UTC (permalink / raw) To: Nicolin Chen Cc: Will Deacon, Robin Murphy, Joerg Roedel, Jason Gunthorpe, Pranjal Shrivastava, Mostafa Saleh, Thierry Reding, Krishna Reddy, Jonathan Hunter, Breno Leitao, Kyle McMartin, Usama Arif, kernel-team, linux-arm-kernel, iommu, linux-tegra, linux-kernel On Tue, Sep 08, 2026 at 02:56:20PM -0700, Nicolin Chen wrote: > On Tue, Sep 08, 2026 at 10:19:44AM +0100, Kiryl Shutsemau wrote: > > On Mon, Sep 07, 2026 at 02:56:26PM -0700, Nicolin Chen wrote: > > > On Mon, Sep 07, 2026 at 10:58:34AM +0100, Kiryl Shutsemau (Meta) wrote: > > > > > > I still think that cmdq_max_n_shift can slightly tidy things here. > > > > > > > +static u32 arm_smmu_queue_max_n_shift(u32 ceiling, u32 ent_sz_shift, > > > > + u32 entries) > > > > > > Here, all three inputs would have been "shifts", instead of two > > > "shifts" and one "number of entries". > > > > > > > +{ > > > > + u32 floor = PAGE_SHIFT - ent_sz_shift; > > > > + > > > > + if (!entries) > > > > + return ceiling; > > > > + > > > > + return min(ceiling, max(ilog2(entries), floor)); > > > > > > And I see Sashiko keeps complaining against the ilog2 here: > > > > It does build: GCC 15 and clang 21, at -O2 and -Os, without a warning. > > > > But the reason is not obvious. > > > > ilog2() on a runtime u32 returns int, and minmax.h only accepts an int > > against a u32 when __is_nonneg() can prove it non-negative at compile > > time. > > > > __ilog2_u32() is fls(n) - 1, so that proof only exists because the > > if (entries) guard lets the compiler see entries != 0 through the inlined > > fls(). > > > > But this is fragile. If a compiler does not get there, or a later change > > that moves the guard, it turns it into a BUILD_BUG_ON. > > > > Rather than a max_t() cast, we can give the shift its type first: > > > > if (entries) { > > new_ceiling = ilog2(entries); > > new_ceiling = max(new_ceiling, floor); > > } else if (is_kdump_kernel()) { > > > > Two u32s, nothing left for the compiler to prove, same result. > > > > If it looks good, I can re-spin v6 with the change. > > It looks probably okay.. though I still don't get why you aren't > picking the straightforward "max_n_shift" over "max_entries". > > max_n_shift is used by both HW and SW, and it does not have such > converting problem or need min/max, making the code cleaner :-/ Okay, fair enough. max_n_shift it is. Will post v6. -- Kiryl Shutsemau / Kirill A. Shutemov ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v5 2/2] iommu/arm-smmu-v3: Default queue depths to one page in a kdump kernel 2026-09-07 9:58 [PATCH v5 0/2] iommu/arm-smmu-v3: Make the queue depths tunable, and shrink them in a kdump kernel Kiryl Shutsemau (Meta) 2026-09-07 9:58 ` [PATCH v5 1/2] iommu/arm-smmu-v3: Add a cmdq_max_entries module parameter Kiryl Shutsemau (Meta) @ 2026-09-07 9:58 ` Kiryl Shutsemau (Meta) 2026-09-07 21:57 ` Nicolin Chen 1 sibling, 1 reply; 8+ messages in thread From: Kiryl Shutsemau (Meta) @ 2026-09-07 9:58 UTC (permalink / raw) To: Will Deacon, Robin Murphy, Joerg Roedel, Nicolin Chen Cc: Jason Gunthorpe, Pranjal Shrivastava, Mostafa Saleh, Thierry Reding, Krishna Reddy, Jonathan Hunter, Breno Leitao, Kyle McMartin, Usama Arif, kernel-team, linux-arm-kernel, iommu, linux-tegra, linux-kernel, Kiryl Shutsemau (Meta) 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. 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_entries still wins, so a capture kernel that wants a deeper command queue can ask for one on the command line. Suggested-by: Kyle McMartin <jkkm@meta.com> Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org> Assisted-by: Claude-Code:claude-opus-5 --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 37 +++++++++++++++++---- 1 file changed, 30 insertions(+), 7 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 4550b1105e9c..67dca0cb487b 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -4424,6 +4424,9 @@ static struct iommu_dirty_ops arm_smmu_dirty_ops = { * @ent_sz_shift: log2 of the queue entry size in bytes * @entries: number of entries to cap the queue at, or zero for the default * + * The default is @ceiling, except in a kdump capture kernel, which defaults to + * one page worth of entries. + * * @entries is rounded down to a power of two and 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 @@ -4433,11 +4436,32 @@ static u32 arm_smmu_queue_max_n_shift(u32 ceiling, u32 ent_sz_shift, u32 entries) { u32 floor = PAGE_SHIFT - ent_sz_shift; + u32 new_ceiling; - if (!entries) + if (entries) + new_ceiling = max(ilog2(entries), floor); + else if (is_kdump_kernel()) + new_ceiling = floor; + else return ceiling; - return min(ceiling, max(ilog2(entries), floor)); + return min(ceiling, new_ceiling); +} + +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); } /* @@ -5196,7 +5220,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)) { @@ -5211,10 +5234,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] 8+ messages in thread
* Re: [PATCH v5 2/2] iommu/arm-smmu-v3: Default queue depths to one page in a kdump kernel 2026-09-07 9:58 ` [PATCH v5 2/2] iommu/arm-smmu-v3: Default queue depths to one page in a kdump kernel Kiryl Shutsemau (Meta) @ 2026-09-07 21:57 ` Nicolin Chen 0 siblings, 0 replies; 8+ messages in thread From: Nicolin Chen @ 2026-09-07 21:57 UTC (permalink / raw) To: Kiryl Shutsemau (Meta) Cc: Will Deacon, Robin Murphy, Joerg Roedel, Jason Gunthorpe, Pranjal Shrivastava, Mostafa Saleh, Thierry Reding, Krishna Reddy, Jonathan Hunter, Breno Leitao, Kyle McMartin, Usama Arif, kernel-team, linux-arm-kernel, iommu, linux-tegra, linux-kernel On Mon, Sep 07, 2026 at 10:58:35AM +0100, Kiryl Shutsemau (Meta) wrote: > 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. > > 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_entries still wins, so a capture kernel that wants a > deeper command queue can ask for one on the command line. > > Suggested-by: Kyle McMartin <jkkm@meta.com> > Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org> > Assisted-by: Claude-Code:claude-opus-5 Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-09-09 9:50 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-09-07 9:58 [PATCH v5 0/2] iommu/arm-smmu-v3: Make the queue depths tunable, and shrink them in a kdump kernel Kiryl Shutsemau (Meta) 2026-09-07 9:58 ` [PATCH v5 1/2] iommu/arm-smmu-v3: Add a cmdq_max_entries module parameter Kiryl Shutsemau (Meta) 2026-09-07 21:56 ` Nicolin Chen 2026-09-08 9:19 ` Kiryl Shutsemau 2026-09-08 21:56 ` Nicolin Chen 2026-09-09 9:50 ` Kiryl Shutsemau 2026-09-07 9:58 ` [PATCH v5 2/2] iommu/arm-smmu-v3: Default queue depths to one page in a kdump kernel Kiryl Shutsemau (Meta) 2026-09-07 21:57 ` Nicolin Chen
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®