]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
of: reserved_mem: replace CMA quirks by generic methods
authorMarek Szyprowski <m.szyprowski@samsung.com>
Wed, 25 Mar 2026 09:00:20 +0000 (10:00 +0100)
committerRob Herring (Arm) <robh@kernel.org>
Thu, 26 Mar 2026 19:12:02 +0000 (14:12 -0500)
Add optional reserved memory callbacks to perform region verification and
early fixup, then move all CMA related code in of_reserved_mem.c to them.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://patch.msgid.link/20260325090023.3175348-5-m.szyprowski@samsung.com
Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
drivers/of/of_reserved_mem.c
include/linux/cma.h
include/linux/dma-map-ops.h
include/linux/of_reserved_mem.h
kernel/dma/contiguous.c

index 4dd0d6f6a4b0d4a5feaeb836038b800af64223a2..5dd585bcf8a824e90d5cd7d430f984d8ae5e50cd 100644 (file)
@@ -24,8 +24,6 @@
 #include <linux/slab.h>
 #include <linux/memblock.h>
 #include <linux/kmemleak.h>
-#include <linux/cma.h>
-#include <linux/dma-map-ops.h>
 
 #include "of_private.h"
 
@@ -106,6 +104,11 @@ static void __init alloc_reserved_mem_array(void)
 
 static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem,
                                              unsigned long node);
+static int fdt_validate_reserved_mem_node(unsigned long node,
+                                         phys_addr_t *align);
+static int fdt_fixup_reserved_mem_node(unsigned long node,
+                                      phys_addr_t base, phys_addr_t size);
+
 /*
  * fdt_reserved_mem_save_node() - save fdt node for second pass initialization
  */
@@ -154,21 +157,19 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
                                             const char *uname)
 {
        phys_addr_t base, size;
-       int i, len;
+       int i, len, err;
        const __be32 *prop;
-       bool nomap, default_cma;
+       bool nomap;
 
        prop = of_flat_dt_get_addr_size_prop(node, "reg", &len);
        if (!prop)
                return -ENOENT;
 
        nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
-       default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL);
 
-       if (default_cma && cma_skip_dt_default_reserved_mem()) {
-               pr_err("Skipping dt linux,cma-default for \"cma=\" kernel param.\n");
-               return -EINVAL;
-       }
+       err = fdt_validate_reserved_mem_node(node, NULL);
+       if (err && err != -ENODEV)
+               return err;
 
        for (i = 0; i < len; i++) {
                u64 b, s;
@@ -179,10 +180,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
                size = s;
 
                if (size && early_init_dt_reserve_memory(base, size, nomap) == 0) {
-                       /* Architecture specific contiguous memory fixup. */
-                       if (of_flat_dt_is_compatible(node, "shared-dma-pool") &&
-                           of_get_flat_dt_prop(node, "reusable", NULL))
-                               dma_contiguous_early_fixup(base, size);
+                       fdt_fixup_reserved_mem_node(node, base, size);
                        pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %lu MiB\n",
                                uname, &base, (unsigned long)(size / SZ_1M));
                } else {
@@ -253,17 +251,19 @@ void __init fdt_scan_reserved_mem_reg_nodes(void)
 
        fdt_for_each_subnode(child, fdt, node) {
                const char *uname;
-               bool default_cma = of_get_flat_dt_prop(child, "linux,cma-default", NULL);
                u64 b, s;
+               int ret;
 
                if (!of_fdt_device_is_available(fdt, child))
                        continue;
-               if (default_cma && cma_skip_dt_default_reserved_mem())
-                       continue;
 
                if (!of_flat_dt_get_addr_size(child, "reg", &b, &s))
                        continue;
 
+               ret = fdt_validate_reserved_mem_node(child, NULL);
+               if (ret && ret != -ENODEV)
+                       continue;
+
                base = b;
                size = s;
 
@@ -397,7 +397,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
        phys_addr_t base = 0, align = 0, size;
        int i, len;
        const __be32 *prop;
-       bool nomap, default_cma;
+       bool nomap;
        int ret;
 
        prop = of_get_flat_dt_prop(node, "size", &len);
@@ -421,19 +421,10 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
        }
 
        nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
-       default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL);
-
-       if (default_cma && cma_skip_dt_default_reserved_mem()) {
-               pr_err("Skipping dt linux,cma-default for \"cma=\" kernel param.\n");
-               return -EINVAL;
-       }
 
-       /* Need adjust the alignment to satisfy the CMA requirement */
-       if (IS_ENABLED(CONFIG_CMA)
-           && of_flat_dt_is_compatible(node, "shared-dma-pool")
-           && of_get_flat_dt_prop(node, "reusable", NULL)
-           && !nomap)
-               align = max_t(phys_addr_t, align, CMA_MIN_ALIGNMENT_BYTES);
+       ret = fdt_validate_reserved_mem_node(node, &align);
+       if (ret && ret != -ENODEV)
+               return ret;
 
        prop = of_flat_dt_get_addr_size_prop(node, "alloc-ranges", &len);
        if (prop) {
@@ -468,18 +459,76 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
                       uname, (unsigned long)(size / SZ_1M));
                return -ENOMEM;
        }
-       /* Architecture specific contiguous memory fixup. */
-       if (of_flat_dt_is_compatible(node, "shared-dma-pool") &&
-           of_get_flat_dt_prop(node, "reusable", NULL))
-               dma_contiguous_early_fixup(base, size);
+
+       fdt_fixup_reserved_mem_node(node, base, size);
+
        /* Save region in the reserved_mem array */
        fdt_reserved_mem_save_node(node, uname, base, size);
        return 0;
 }
 
+extern const struct of_device_id __reservedmem_of_table[];
 static const struct of_device_id __rmem_of_table_sentinel
        __used __section("__reservedmem_of_table_end");
 
+/**
+ * fdt_fixup_reserved_mem_node() - call fixup function for a reserved memory node
+ * @node: FDT node to fixup
+ * @base: base address of the reserved memory region
+ * @size: size of the reserved memory region
+ *
+ * This function iterates through the reserved memory drivers and calls
+ * the node_fixup callback for the compatible entry matching the node.
+ *
+ * Return: 0 on success, -ENODEV if no compatible match found
+ */
+static int __init fdt_fixup_reserved_mem_node(unsigned long node,
+                                       phys_addr_t base, phys_addr_t size)
+{
+       const struct of_device_id *i;
+       int ret = -ENODEV;
+
+       for (i = __reservedmem_of_table; ret == -ENODEV &&
+            i < &__rmem_of_table_sentinel; i++) {
+               const struct reserved_mem_ops *ops = i->data;
+
+               if (!of_flat_dt_is_compatible(node, i->compatible))
+                       continue;
+
+               if (ops->node_fixup)
+                       ret = ops->node_fixup(node, base, size);
+       }
+       return ret;
+}
+
+/**
+ * fdt_validate_reserved_mem_node() - validate a reserved memory node
+ * @node: FDT node to validate
+ * @align: pointer to store the validated alignment (may be modified by callback)
+ *
+ * This function iterates through the reserved memory drivers and calls
+ * the node_validate callback for the compatible entry matching the node.
+ *
+ * Return: 0 on success, -ENODEV if no compatible match found
+ */
+static int __init fdt_validate_reserved_mem_node(unsigned long node, phys_addr_t *align)
+{
+       const struct of_device_id *i;
+       int ret = -ENODEV;
+
+       for (i = __reservedmem_of_table; ret == -ENODEV &&
+            i < &__rmem_of_table_sentinel; i++) {
+               const struct reserved_mem_ops *ops = i->data;
+
+               if (!of_flat_dt_is_compatible(node, i->compatible))
+                       continue;
+
+               if (ops->node_validate)
+                       ret = ops->node_validate(node, align);
+       }
+       return ret;
+}
+
 /**
  * __reserved_mem_init_node() - initialize a reserved memory region
  * @rmem: reserved_mem structure to initialize
@@ -494,7 +543,6 @@ static const struct of_device_id __rmem_of_table_sentinel
 static int __init __reserved_mem_init_node(struct reserved_mem *rmem,
                                           unsigned long node)
 {
-       extern const struct of_device_id __reservedmem_of_table[];
        const struct of_device_id *i;
        int ret = -ENODEV;
 
@@ -511,7 +559,7 @@ static int __init __reserved_mem_init_node(struct reserved_mem *rmem,
                        rmem->ops = ops;
                        pr_info("initialized node %s, compatible id %s\n",
                                rmem->name, compat);
-                       break;
+                       return ret;
                }
        }
        return ret;
index d0793eaaadaa49ffc5196a5b721b54b9d262d4d3..8555d38a97b1a5e496255b6c73fda03e7b1e990f 100644 (file)
@@ -61,14 +61,4 @@ extern int cma_for_each_area(int (*it)(struct cma *cma, void *data), void *data)
 extern bool cma_intersects(struct cma *cma, unsigned long start, unsigned long end);
 
 extern void cma_reserve_pages_on_error(struct cma *cma);
-
-#ifdef CONFIG_DMA_CMA
-extern bool cma_skip_dt_default_reserved_mem(void);
-#else
-static inline bool cma_skip_dt_default_reserved_mem(void)
-{
-       return false;
-}
-#endif
-
 #endif
index 60b63756df821d839436618f1fca2bfa3eabe075..55ecd2934225c02598dab1d38540e45c4c42c8fe 100644 (file)
@@ -147,9 +147,6 @@ static inline void dma_free_contiguous(struct device *dev, struct page *page,
 {
        __free_pages(page, get_order(size));
 }
-static inline void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size)
-{
-}
 #endif /* CONFIG_DMA_CMA*/
 
 #ifdef CONFIG_DMA_DECLARE_COHERENT
index 747a1e73d5dd99b73adc478c53473c79e1f7011b..e8b20b29fa68c237a4344655c878ff05931a48ea 100644 (file)
@@ -18,6 +18,9 @@ struct reserved_mem {
 };
 
 struct reserved_mem_ops {
+       int     (*node_validate)(unsigned long fdt_node, phys_addr_t *align);
+       int     (*node_fixup)(unsigned long fdt_node, phys_addr_t base,
+                             phys_addr_t size);
        int     (*node_init)(unsigned long fdt_node, struct reserved_mem *rmem);
        int     (*device_init)(struct reserved_mem *rmem,
                               struct device *dev);
index efeebda925371ec9e4fc5912604f2a9ebbfa1f6d..65d216663e81dd9c52b7dc38b673e7f5cfc07d32 100644 (file)
@@ -91,16 +91,6 @@ static int __init early_cma(char *p)
 }
 early_param("cma", early_cma);
 
-/*
- * cma_skip_dt_default_reserved_mem - This is called from the
- * reserved_mem framework to detect if the default cma region is being
- * set by the "cma=" kernel parameter.
- */
-bool __init cma_skip_dt_default_reserved_mem(void)
-{
-       return size_cmdline != -1;
-}
-
 #ifdef CONFIG_DMA_NUMA_CMA
 
 static struct cma *dma_contiguous_numa_area[MAX_NUMNODES];
@@ -470,25 +460,65 @@ static void rmem_cma_device_release(struct reserved_mem *rmem,
        dev->cma_area = NULL;
 }
 
+static int __init __rmem_cma_verify_node(unsigned long node)
+{
+       if (!of_get_flat_dt_prop(node, "reusable", NULL) ||
+           of_get_flat_dt_prop(node, "no-map", NULL))
+               return -ENODEV;
+
+       if (size_cmdline != -1 &&
+           of_get_flat_dt_prop(node, "linux,cma-default", NULL)) {
+               pr_err("Skipping dt linux,cma-default node in favor for \"cma=\" kernel param.\n");
+               return -EBUSY;
+       }
+       return 0;
+}
+
+static int __init rmem_cma_validate(unsigned long node, phys_addr_t *align)
+{
+       int ret = __rmem_cma_verify_node(node);
+
+       if (ret)
+               return ret;
+
+       if (align)
+               *align = max_t(phys_addr_t, *align, CMA_MIN_ALIGNMENT_BYTES);
+
+       return 0;
+}
+
+static int __init rmem_cma_fixup(unsigned long node, phys_addr_t base,
+                                   phys_addr_t size)
+{
+       int ret = __rmem_cma_verify_node(node);
+
+       if (ret)
+               return ret;
+
+       /* Architecture specific contiguous memory fixup. */
+       dma_contiguous_early_fixup(base, size);
+       return 0;
+}
+
 static int __init rmem_cma_setup(unsigned long node, struct reserved_mem *rmem)
 {
        bool default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL);
        struct cma *cma;
-       int err;
+       int ret;
 
-       if (!of_get_flat_dt_prop(node, "reusable", NULL) ||
-           of_get_flat_dt_prop(node, "no-map", NULL))
-               return -ENODEV;
+       ret = __rmem_cma_verify_node(node);
+       if (ret)
+               return ret;
 
        if (!IS_ALIGNED(rmem->base | rmem->size, CMA_MIN_ALIGNMENT_BYTES)) {
                pr_err("Reserved memory: incorrect alignment of CMA region\n");
                return -EINVAL;
        }
 
-       err = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name, &cma);
-       if (err) {
+       ret = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name, &cma);
+       if (ret) {
                pr_err("Reserved memory: unable to setup CMA region\n");
-               return err;
+               return ret;
        }
 
        if (default_cma)
@@ -499,14 +529,16 @@ static int __init rmem_cma_setup(unsigned long node, struct reserved_mem *rmem)
        pr_info("Reserved memory: created CMA memory pool at %pa, size %ld MiB\n",
                &rmem->base, (unsigned long)rmem->size / SZ_1M);
 
-       err = dma_heap_cma_register_heap(cma);
-       if (err)
+       ret = dma_heap_cma_register_heap(cma);
+       if (ret)
                pr_warn("Couldn't register CMA heap.");
 
        return 0;
 }
 
 static const struct reserved_mem_ops rmem_cma_ops = {
+       .node_validate  = rmem_cma_validate,
+       .node_fixup     = rmem_cma_fixup,
        .node_init      = rmem_cma_setup,
        .device_init    = rmem_cma_device_init,
        .device_release = rmem_cma_device_release,