Blob Blame History Raw
From: Heiko Carstens <heiko.carstens@de.ibm.com>
Subject: s390/mm: prevent memory offline for memory blocks with cma areas
Patch-mainline: v4.14-rc1
Git-commit: 34ad7cdc1bb2ea65934d235be89fabf1bb40d824
References: bnc#1066983, LTC#159171

Description:  vmcp: make use of contiguous memory allocator
Symptom:      The vmcp tool fails to execute a cp command because the kernel
              was unable to allocate memory.
Problem:      Allocation of memory for the response area of the diagnose 8
              command may require the kernel to allocate large contiguous
              memory areas. If memory is fragmented, large order allocations
              can likely fail and this causes vmcp to fail.
Solution:     Make use of the contiguous memory allocator. This should make
              sure that allocation of large response buffers will almost
              always succeed.
Reproduction: -

Upstream-Description:

              s390/mm: prevent memory offline for memory blocks with cma areas

              Memory blocks that contain areas for the contiguous memory allocator
              (cma) should not be allowed to go offline. Otherwise this would render
              cma completely useless.
              This might make sense on other architectures where memory might be
              taken offline due to hardware errors, but not on architectures which
              support memory hotplug for load balancing.

              Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
              Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>


Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Acked-by: Hannes Reinecke <hare@suse.com>
---
 arch/s390/mm/init.c |   53 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

--- a/arch/s390/mm/init.c
+++ b/arch/s390/mm/init.c
@@ -26,6 +26,7 @@
 #include <linux/poison.h>
 #include <linux/initrd.h>
 #include <linux/export.h>
+#include <linux/cma.h>
 #include <linux/gfp.h>
 #include <linux/memblock.h>
 #include <asm/processor.h>
@@ -164,6 +165,58 @@ unsigned long memory_block_size_bytes(vo
 }
 
 #ifdef CONFIG_MEMORY_HOTPLUG
+
+#ifdef CONFIG_CMA
+
+/* Prevent memory blocks which contain cma regions from going offline */
+
+struct s390_cma_mem_data {
+	unsigned long start;
+	unsigned long end;
+};
+
+static int s390_cma_check_range(struct cma *cma, void *data)
+{
+	struct s390_cma_mem_data *mem_data;
+	unsigned long start, end;
+
+	mem_data = data;
+	start = cma_get_base(cma);
+	end = start + cma_get_size(cma);
+	if (end < mem_data->start)
+		return 0;
+	if (start >= mem_data->end)
+		return 0;
+	return -EBUSY;
+}
+
+static int s390_cma_mem_notifier(struct notifier_block *nb,
+				 unsigned long action, void *data)
+{
+	struct s390_cma_mem_data mem_data;
+	struct memory_notify *arg;
+	int rc = 0;
+
+	arg = data;
+	mem_data.start = arg->start_pfn << PAGE_SHIFT;
+	mem_data.end = mem_data.start + (arg->nr_pages << PAGE_SHIFT);
+	if (action == MEM_GOING_OFFLINE)
+		rc = cma_for_each_area(s390_cma_check_range, &mem_data);
+	return notifier_from_errno(rc);
+}
+
+static struct notifier_block s390_cma_mem_nb = {
+	.notifier_call = s390_cma_mem_notifier,
+};
+
+static int __init s390_cma_mem_init(void)
+{
+	return register_memory_notifier(&s390_cma_mem_nb);
+}
+device_initcall(s390_cma_mem_init);
+
+#endif /* CONFIG_CMA */
+
 int arch_add_memory(int nid, u64 start, u64 size, bool want_memblock)
 {
 	unsigned long start_pfn = PFN_DOWN(start);