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;