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