From c825a1d7d2d830e3e44fa8b3e42f06056ad2fa69 Mon Sep 17 00:00:00 2001 From: Laura Abbott <lauraa@codeaurora.org> Date: Wed, 13 Nov 2013 11:21:24 -0800 Subject: [PATCH] cma: Add support for removed regions In addition to reserving memory from the system, there may be uses where memory should be completely removed from control of the linux page allocator. Add the appropriate information to be able to remove the memory. Change-Id: Ia2e959e0858fb240ab5c0deee49b0d6e4aecfc00 Signed-off-by: Laura Abbott <lauraa@codeaurora.org> --- Documentation/devicetree/bindings/memory.txt | 5 ++++ drivers/base/dma-contiguous.c | 27 +++++++++++++++++--- include/linux/dma-contiguous.h | 9 ++++--- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/memory.txt b/Documentation/devicetree/bindings/memory.txt index 699177d31710..b28bd82aa405 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 1eeb5ce1b14b..3b51cc4ebc70 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 cea8a6fd64f0..594d836df37f 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; -- GitLab