[PATCH 1/1] alpha: enable DMA CMA support (HAVE_DMA_CONTIGUOUS)

From: Magnus Lindholm

Date: Thu Feb 19 2026 - 15:56:17 EST


Alpha currently does not support CONFIG_DMA_CMA, even though the
generic CMA infrastructure is available. As a result, coherent DMA
allocations rely solely on the buddy allocator and may fail for large
contiguous buffers.

Add architecture support for HAVE_DMA_CONTIGUOUS by:

- Selecting HAVE_DMA_CONTIGUOUS in arch/alpha/Kconfig.
- Ensuring early command-line parameters are parsed in setup_arch()
after Alpha-specific command line handling, so that the "cma="
early parameter is honored.
- Calling dma_contiguous_reserve() during early memory setup while
memblock is active.
- Extending alpha_pci_alloc_coherent() to fall back to
dma_alloc_from_contiguous() when __get_free_pages() fails.
- Extending alpha_pci_free_coherent() to release CMA-backed
allocations via dma_release_from_contiguous().

With these changes, Alpha systems can successfully reserve and use
CMA-backed physically contiguous memory for DMA allocations.

Tested on a DS10 with cma=64M:
- CMA reservation is correctly sized from the command line.

Signed-off-by: Magnus Lindholm <linmag7@xxxxxxxxx>
---
.../io/dma-contiguous/arch-support.txt | 2 +-
arch/alpha/Kconfig | 1 +
arch/alpha/kernel/pci_iommu.c | 46 +++++++++++++++++++
arch/alpha/kernel/setup.c | 16 +++++++
4 files changed, 64 insertions(+), 1 deletion(-)

diff --git a/Documentation/features/io/dma-contiguous/arch-support.txt b/Documentation/features/io/dma-contiguous/arch-support.txt
index 3c6ce35d704f..6cd205a991f6 100644
--- a/Documentation/features/io/dma-contiguous/arch-support.txt
+++ b/Documentation/features/io/dma-contiguous/arch-support.txt
@@ -6,7 +6,7 @@
-----------------------
| arch |status|
-----------------------
- | alpha: | TODO |
+ | alpha: | ok |
| arc: | TODO |
| arm: | ok |
| arm64: | ok |
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index 6c7dbf0adad6..e3ff6f7d93ab 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -2,6 +2,7 @@
config ALPHA
bool
default y
+ select HAVE_DMA_CONTIGUOUS
select ARCH_32BIT_USTAT_F_TINODE
select ARCH_HAS_CURRENT_STACK_POINTER
select ARCH_HAS_DMA_OPS if PCI
diff --git a/arch/alpha/kernel/pci_iommu.c b/arch/alpha/kernel/pci_iommu.c
index 955b6ca61627..08c18d49ca8e 100644
--- a/arch/alpha/kernel/pci_iommu.c
+++ b/arch/alpha/kernel/pci_iommu.c
@@ -15,6 +15,7 @@
#include <linux/iommu-helper.h>
#include <linux/string_choices.h>

+#include <linux/dma-map-ops.h>
#include <asm/io.h>
#include <asm/hwrpb.h>

@@ -409,12 +410,34 @@ static void *alpha_pci_alloc_coherent(struct device *dev, size_t size,
struct pci_dev *pdev = alpha_gendev_to_pci(dev);
void *cpu_addr;
long order = get_order(size);
+ unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
+ struct page *cma_page = NULL;

+ /* Match existing behavior: prefer normal memory first. */
gfp &= ~GFP_DMA;

try_again:
cpu_addr = (void *)__get_free_pages(gfp | __GFP_ZERO, order);
if (! cpu_addr) {
+ /*
+ * Fallback to CMA if enabled: this can migrate/compact
+ * movable pages out of the CMA area to form a contiguous bloc
+ */
+ if (IS_ENABLED(CONFIG_DMA_CMA)) {
+ cma_page = dma_alloc_from_contiguous(dev, count, order, gfp);
+ if (cma_page) {
+ cpu_addr = page_address(cma_page);
+ if (!cpu_addr) {
+ /* Very unlikely on Alpha, but be safe. */
+ dma_release_from_contiguous(dev, cma_page, count);
+ cma_page = NULL;
+ } else {
+ memset(cpu_addr, 0, size);
+ goto have_mem;
+ }
+ }
+ }
+
printk(KERN_INFO "pci_alloc_consistent: "
"get_free_pages failed from %ps\n",
__builtin_return_address(0));
@@ -422,11 +445,24 @@ static void *alpha_pci_alloc_coherent(struct device *dev, size_t size,
with vmalloc and sg if we can't find contiguous memory. */
return NULL;
}
+ /* __GFP_ZERO already did this, but keep the old behavior explicit. */
memset(cpu_addr, 0, size);

+have_mem:
*dma_addrp = pci_map_single_1(pdev, virt_to_phys(cpu_addr), size, 0);
if (*dma_addrp == DMA_MAPPING_ERROR) {
+ /*
+ * Free the memory using the right backend:
+ * - If it came from CMA, release to CMA
+ * - Otherwise free_pages()
+ */
+ if (IS_ENABLED(CONFIG_DMA_CMA)) {
+ if (dma_release_from_contiguous(dev, virt_to_page(cpu_addr), count))
+ goto map_failed_freed;
+ }
free_pages((unsigned long)cpu_addr, order);
+
+map_failed_freed:
if (alpha_mv.mv_pci_tbi || (gfp & GFP_DMA))
return NULL;
/* The address doesn't fit required mask and we
@@ -452,9 +488,19 @@ static void alpha_pci_free_coherent(struct device *dev, size_t size,
unsigned long attrs)
{
struct pci_dev *pdev = alpha_gendev_to_pci(dev);
+ unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
+
dma_unmap_single(&pdev->dev, dma_addr, size, DMA_BIDIRECTIONAL);
+
+ if (IS_ENABLED(CONFIG_DMA_CMA)) {
+ /* Returns true if cpu_addr belongs to a CMA allocation. */
+ if (dma_release_from_contiguous(dev, virt_to_page(cpu_addr), count))
+ goto out;
+ }
+
free_pages((unsigned long)cpu_addr, get_order(size));

+out:
DBGA2("pci_free_consistent: [%llx,%zx] from %ps\n",
dma_addr, size, __builtin_return_address(0));
}
diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c
index f0af444a69a4..24893bca39f5 100644
--- a/arch/alpha/kernel/setup.c
+++ b/arch/alpha/kernel/setup.c
@@ -46,6 +46,7 @@
#include <asm/io.h>
#include <linux/log2.h>
#include <linux/export.h>
+#include <linux/dma-map-ops.h>

static int alpha_panic_event(struct notifier_block *, unsigned long, void *);
static struct notifier_block alpha_panic_block = {
@@ -513,6 +514,13 @@ setup_arch(char **cmdline_p)
/* Replace the command line, now that we've killed it with strsep. */
strcpy(command_line, boot_command_line);

+ /*
+ * Alpha mutates command_line with strsep() above, so make sure
+ * early params (including "cma=") are parsed from the restored
+ * command line before any CMA reservation happens.
+ */
+ parse_early_param();
+
/* If we want SRM console printk echoing early, do it now. */
if (alpha_using_srm && srmcons_output) {
register_srm_console();
@@ -648,6 +656,14 @@ setup_arch(char **cmdline_p)
printk("Max ASN from HWRPB is bad (0x%lx)\n", hwrpb->max_asn);
}

+#ifdef CONFIG_CMA
+ /*
+ * Reserve CMA now that memblock knows RAM layout and early params
+ * (including cma=) have been parsed.
+ */
+ dma_contiguous_reserve(0);
+#endif
+
/*
* Identify the flock of penguins.
*/
--
2.52.0