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

Magnus Lindholm <[email protected]>
Newsgroups org.kernel.vger.linux-alpha,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
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 <[email protected]>
---
 .../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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.