diff --git a/Documentation/devicetree/bindings/memory.txt b/Documentation/devicetree/bindings/memory.txt index 699177d3171042617f57b67e1a4327aec69d65df..b28bd82aa405bcedb05680742fe116d0be3a414f 100644 --- a/Documentation/devicetree/bindings/memory.txt +++ b/Documentation/devicetree/bindings/memory.txt @@ -38,6 +38,7 @@ wit the following convention: (linux,default-contiguous-region); (linux,reserve-region); (linux,memory-limit); + (linux,remove-completely); label = (unique_name); }; @@ -58,6 +59,10 @@ linux,memory-limit: property specifying an upper bound on the physical address is specificed, the region may be placed anywhere in the physical address space. 0 may be used to specify lowmem (i.e. the region will be placed in the direct mapped lowmem region) +linux,remove-completely: property indicating the memory will be removed from the + linux page allocator completely. This means that page structures + associated with the memory will not be valid. This binding is + expected to be used in conjunction with linux,reserve-region. label: an internal name used for automatically associating the cma region with a given device. The label is optional; if the label is not given the client is responsible for diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 1eeb5ce1b14b871e5c4e1d4c4e64bedc81ed1c15..3b51cc4ebc70a30deed098134f822702b1c99d05 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -38,6 +38,7 @@ #include <linux/swap.h> #include <linux/mm_types.h> #include <linux/dma-contiguous.h> +#include <linux/dma-removed.h> #include <trace/events/kmem.h> struct cma { @@ -220,6 +221,7 @@ int __init cma_fdt_scan(unsigned long node, const char *uname, __be32 *prop; char *name; bool in_system; + bool remove; unsigned long size_cells = dt_root_size_cells; unsigned long addr_cells = dt_root_addr_cells; phys_addr_t limit = MEMBLOCK_ALLOC_ANYWHERE; @@ -250,10 +252,13 @@ int __init cma_fdt_scan(unsigned long node, const char *uname, if (prop) limit = be32_to_cpu(prop[0]); + remove = + of_get_flat_dt_prop(node, "linux,remove-completely", NULL) ? 1 : 0; + pr_info("Found %s, memory base %pa, size %ld MiB, limit %pa\n", uname, &base, (unsigned long)size / SZ_1M, &limit); dma_contiguous_reserve_area(size, &base, limit, name, - in_system); + in_system, remove); return 0; } @@ -295,7 +300,7 @@ void __init dma_contiguous_reserve(phys_addr_t limit) (unsigned long)sel_size / SZ_1M); if (dma_contiguous_reserve_area(sel_size, &base, limit, NULL, - CMA_RESERVE_AREA) == 0) + CMA_RESERVE_AREA, false) == 0) dma_contiguous_def_base = base; } #ifdef CONFIG_OF @@ -319,7 +324,7 @@ void __init dma_contiguous_reserve(phys_addr_t limit) */ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t *res_base, phys_addr_t limit, const char *name, - bool to_system) + bool to_system, bool remove) { phys_addr_t base = *res_base; phys_addr_t alignment; @@ -365,6 +370,15 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t *res_base, } } + if (remove) { + if (!to_system) { + memblock_free(base, size); + memblock_remove(base, size); + } else { + WARN(1, "Removing is incompatible with staying in the system\n"); + } + } + /* * Each reserved area must be initialised later, when more kernel * subsystems (like slab allocator) are available. @@ -380,7 +394,8 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t *res_base, &base); /* Architecture specific contiguous memory fixup. */ - dma_contiguous_early_fixup(base, size); + if (!remove) + dma_contiguous_early_fixup(base, size); return 0; err: pr_err("CMA: failed to reserve %ld MiB\n", (unsigned long)size / SZ_1M); @@ -430,6 +445,10 @@ static void cma_assign_device_from_dt(struct device *dev) return; dev_set_cma_area(dev, cma); + + if (of_property_read_bool(node, "linux,remove-completely")) + set_dma_ops(dev, &removed_dma_ops); + pr_info("Assigned CMA region at %lx to %s device\n", (unsigned long)value, dev_name(dev)); } diff --git a/include/linux/dma-contiguous.h b/include/linux/dma-contiguous.h index cea8a6fd64f0bfa2ffb3abef1b40413f9cd6a0a6..594d836df37f54d9903edf12516d2e64a2149ba6 100644 --- a/include/linux/dma-contiguous.h +++ b/include/linux/dma-contiguous.h @@ -74,7 +74,8 @@ void dma_contiguous_reserve(phys_addr_t addr_limit); int dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t *res_base, phys_addr_t limit, const char *name, - bool in_system); + bool in_system, + bool remove); int dma_contiguous_add_device(struct device *dev, phys_addr_t base); @@ -95,7 +96,8 @@ static inline int dma_declare_contiguous(struct device *dev, phys_addr_t size, phys_addr_t base, phys_addr_t limit) { int ret; - ret = dma_contiguous_reserve_area(size, &base, limit, NULL, true); + ret = dma_contiguous_reserve_area(size, &base, limit, NULL, true, + false); if (ret == 0) ret = dma_contiguous_add_device(dev, base); return ret; @@ -107,7 +109,8 @@ static inline int dma_declare_contiguous_reserved(struct device *dev, phys_addr_t limit) { int ret; - ret = dma_contiguous_reserve_area(size, &base, limit, NULL, false); + ret = dma_contiguous_reserve_area(size, &base, limit, NULL, false, + false); if (ret == 0) ret = dma_contiguous_add_device(dev, base); return ret;