mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Magnus Lindholm <linmag7@gmail.com>
To: richard.henderson@linaro.org, mattst88@gmail.com,
	linux-kernel@vger.kernel.org, linux-alpha@vger.kernel.org
Cc: Magnus Lindholm <linmag7@gmail.com>, Maciej Rozycki <macro@orcam.me.uk>
Subject: [PATCH v2 1/2] alpha: respect dev->bus_dma_limit as the effective DMA address ceiling
Date: Mon, 24 Aug 2026 19:36:53 +0200	[thread overview]
Message-ID: <20260824181126.3559638-2-linmag7@gmail.com> (raw)
In-Reply-To: <20260824181126.3559638-1-linmag7@gmail.com>

The Alpha PCI DMA-mapping code has no way for platform code to cap a
device's addressable range below what it claims via dma_set_mask(),
even though dev->bus_dma_limit exists precisely for an upstream bridge
or bus to impose exactly that kind of constraint. Wire it in as the
effective upper bound everywhere an address range gets checked or a
DMA window gets selected:

 - pci_map_single_1() and sg_fill() already bound-check the direct-map
   window against max_dma; make max_dma itself
   min_not_zero(dma_mask, bus_dma_limit), and add the same
   paddr + dac_offset + size - 1 <= max_dma check to their DAC paths,
   which previously had no bound check at all beyond the bitmask test
   in pci_dac_dma_supported().
 - alpha_pci_map_sg()'s no-IOMMU fallback path (no alpha_mv.mv_pci_tbi)
   picks up the same ceiling instead of unconditionally using -1.
 - sg_fill() gains an explicit failure return when there is no IOMMU
   arena and the direct/DAC paths didn't apply. Previously this relied
   on max_dma = -1 making the (unchecked) DAC branch always succeed
   whenever dac_allowed was true; now that DAC has a real bound check,
   that combination is reachable and must not fall through into
   iommu_arena_alloc() with a NULL arena. Mirrors the DMA_MAPPING_ERROR
   return pci_map_single_1() already gives for the same situation.

DAC capability itself is unchanged: pci_dac_dma_supported() still
determines whether a device's DMA mask can address the DAC bit at all
(a capability question). bus_dma_limit is enforced separately, at each
mapping site, against the resulting address.

dev->bus_dma_limit is a numeric upper bound on the end address of a
transfer (see dma_capable() in include/linux/dma-direct.h), not
another bitmask to AND against.

bus_dma_limit defaults to 0 and min_not_zero() ignores a zero operand,
so all of this is a no-op until something actually sets bus_dma_limit
on a device - no behaviour change by itself.

This is infrastructure for a following patch that uses bus_dma_limit to
work around a Tsunami/Typhoon-specific DAC issue; kept separate since
it's a generic, self-contained change with no policy attached.

Suggested-by: Maciej Rozycki <macro@orcam.me.uk>
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
 arch/alpha/kernel/pci_iommu.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/arch/alpha/kernel/pci_iommu.c b/arch/alpha/kernel/pci_iommu.c
index 955b6ca61627..6191dcd560bd 100644
--- a/arch/alpha/kernel/pci_iommu.c
+++ b/arch/alpha/kernel/pci_iommu.c
@@ -228,7 +228,8 @@ pci_map_single_1(struct pci_dev *pdev, phys_addr_t paddr, size_t size,
 		 int dac_allowed)
 {
 	struct pci_controller *hose = pdev ? pdev->sysdata : pci_isa_hose;
-	dma_addr_t max_dma = pdev ? pdev->dma_mask : ISA_DMA_MASK;
+	dma_addr_t max_dma = pdev ? min_not_zero(pdev->dma_mask,
+		pdev->dev.bus_dma_limit) : ISA_DMA_MASK;
 	unsigned long offset = offset_in_page(paddr);
 	struct pci_iommu_arena *arena;
 	long npages, dma_ofs, i;
@@ -250,7 +251,8 @@ pci_map_single_1(struct pci_dev *pdev, phys_addr_t paddr, size_t size,
 #endif
 
 	/* Next, use DAC if selected earlier.  */
-	if (dac_allowed) {
+	if (dac_allowed
+	    && paddr + alpha_mv.pci_dac_offset + size - 1 <= max_dma) {
 		ret = paddr + alpha_mv.pci_dac_offset;
 
 		DBGA2("pci_map_single: [%pa,%zx] -> DAC %llx from %ps\n",
@@ -549,7 +551,8 @@ sg_fill(struct device *dev, struct scatterlist *leader, struct scatterlist *end,
 #endif
 
 	/* If physically contiguous and DAC is available, use it.  */
-	if (leader->dma_address == 0 && dac_allowed) {
+	if (leader->dma_address == 0 && dac_allowed
+	    && paddr + alpha_mv.pci_dac_offset + size - 1 <= max_dma) {
 		out->dma_address = paddr + alpha_mv.pci_dac_offset;
 		out->dma_length = size;
 
@@ -559,6 +562,10 @@ sg_fill(struct device *dev, struct scatterlist *leader, struct scatterlist *end,
 		return 0;
 	}
 
+	/* No IOMMU and direct/DAC didn't fit: nothing left to try.  */
+	if (!arena)
+		return -1;
+
 	/* Otherwise, we'll use the iommu to make the pages virtually
 	   contiguous.  */
 
@@ -654,12 +661,14 @@ static int alpha_pci_map_sg(struct device *dev, struct scatterlist *sg,
 	/* Second, figure out where we're going to map things.  */
 	if (alpha_mv.mv_pci_tbi) {
 		hose = pdev ? pdev->sysdata : pci_isa_hose;
-		max_dma = pdev ? pdev->dma_mask : ISA_DMA_MASK;
+		max_dma = pdev ? min_not_zero(pdev->dma_mask,
+			pdev->dev.bus_dma_limit) : ISA_DMA_MASK;
 		arena = hose->sg_pci;
 		if (!arena || arena->dma_base + arena->size - 1 > max_dma)
 			arena = hose->sg_isa;
 	} else {
-		max_dma = -1;
+		max_dma = pdev ? min_not_zero(pdev->dma_mask,
+			pdev->dev.bus_dma_limit) : -1;
 		arena = NULL;
 		hose = NULL;
 	}
@@ -719,7 +728,8 @@ static void alpha_pci_unmap_sg(struct device *dev, struct scatterlist *sg,
 		return;
 
 	hose = pdev ? pdev->sysdata : pci_isa_hose;
-	max_dma = pdev ? pdev->dma_mask : ISA_DMA_MASK;
+	max_dma = pdev ? min_not_zero(pdev->dma_mask,
+		pdev->dev.bus_dma_limit) : ISA_DMA_MASK;
 	arena = hose->sg_pci;
 	if (!arena || arena->dma_base + arena->size - 1 > max_dma)
 		arena = hose->sg_isa;
-- 
2.53.0


  reply	other threads:[~2026-08-24 18:11 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-24 17:36 [PATCH v2 0/2] alpha: disable DAC for 32-bit PCI cards on Tsunami/Typhoon Magnus Lindholm
2026-08-24 17:36 ` Magnus Lindholm [this message]
2026-08-24 17:36 ` [PATCH v2 2/2] " Magnus Lindholm

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=20260824181126.3559638-2-linmag7@gmail.com \
    --to=linmag7@gmail.com \
    --cc=linux-alpha@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=macro@orcam.me.uk \
    --cc=mattst88@gmail.com \
    --cc=richard.henderson@linaro.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

all inboxes | Powered by JetHome®