This patch steal system RAM and use that to emulate pmem device using the
e820 platform driver.

This adds a new kernel command line 'pmemmap' which takes the format <size[KMG]>
to allocate memory early in the boot. This memory is later registered as
persistent memory range.

Based on original patch from Oliver OHalloran <olive...@au1.ibm.com>

Not-Signed-off-by: Aneesh Kumar K.V <aneesh.ku...@linux.ibm.com>
---
 drivers/nvdimm/Kconfig        |  13 ++++
 drivers/nvdimm/Makefile       |   1 +
 drivers/nvdimm/memblockpmem.c | 115 ++++++++++++++++++++++++++++++++++
 3 files changed, 129 insertions(+)
 create mode 100644 drivers/nvdimm/memblockpmem.c

diff --git a/drivers/nvdimm/Kconfig b/drivers/nvdimm/Kconfig
index 50d2a33de441..cbbbcbd4506b 100644
--- a/drivers/nvdimm/Kconfig
+++ b/drivers/nvdimm/Kconfig
@@ -115,4 +115,17 @@ config OF_PMEM
 config PMEM_PLATFORM_DEVICE
        bool
 
+config MEMBLOCK_PMEM
+       bool "pmemmap= parameter support"
+       default y
+       depends on HAVE_MEMBLOCK
+       select PMEM_PLATFORM_DEVICE
+       help
+         Add support for the pmemmap= kernel command line parameter. This is 
similar
+         to the memmap= parameter available on ACPI platforms, but it uses 
generic
+         kernel facilities (the memblock allocator) to reserve memory rather 
than adding
+         to the e820 table.
+
+         Select Y if unsure.
+
 endif
diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile
index 94f7f29146ce..0215ce0182e9 100644
--- a/drivers/nvdimm/Makefile
+++ b/drivers/nvdimm/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_ND_BTT) += nd_btt.o
 obj-$(CONFIG_ND_BLK) += nd_blk.o
 obj-$(CONFIG_PMEM_PLATFORM_DEVICE) += nd_e820.o
 obj-$(CONFIG_OF_PMEM) += of_pmem.o
+obj-$(CONFIG_MEMBLOCK_PMEM) += memblockpmem.o
 
 nd_pmem-y := pmem.o
 
diff --git a/drivers/nvdimm/memblockpmem.c b/drivers/nvdimm/memblockpmem.c
new file mode 100644
index 000000000000..d39772b75fcd
--- /dev/null
+++ b/drivers/nvdimm/memblockpmem.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2018 IBM Corporation
+ */
+
+#define pr_fmt(fmt) "memblock pmem: " fmt
+
+#include <linux/libnvdimm.h>
+#include <linux/bootmem.h>
+#include <linux/memblock.h>
+#include <linux/mmzone.h>
+#include <linux/cpu.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+
+/*
+ * Align pmem reservations to the section size so we don't have issues with
+ * memory hotplug
+ */
+#ifdef CONFIG_SPARSEMEM
+#define BOOTPMEM_ALIGN (1UL << SECTION_SIZE_BITS)
+#else
+#define BOOTPMEM_ALIGN PFN_DEFAULT_ALIGNMENT
+#endif
+
+static __initdata u64 pmem_size;
+static __initdata phys_addr_t pmem_stolen_memory;
+
+static void alloc_pmem_from_memblock(void)
+{
+
+       pmem_stolen_memory = memblock_alloc_base(pmem_size,
+                                                BOOTPMEM_ALIGN,
+                                                MEMBLOCK_ALLOC_ACCESSIBLE);
+       if (!pmem_stolen_memory) {
+               pr_err("Failed to allocate memory for PMEM from memblock\n");
+               return;
+       }
+
+       /*
+        * Remove from the memblock reserved range
+        */
+       memblock_free(pmem_stolen_memory, pmem_size);
+
+       /*
+        * Remove from the memblock memory range.
+        */
+       memblock_remove(pmem_stolen_memory, pmem_size);
+       pr_info("Allocated %ld memory at 0x%lx\n", (unsigned long)pmem_size,
+               (unsigned long)pmem_stolen_memory);
+       return;
+}
+
+/*
+ * pmemmap=ss[KMG]
+ *
+ * This is similar to the memremap=offset[KMG]!size[KMG] paramater
+ * for adding a legacy pmem range to the e820 map on x86, but it's
+ * platform agnostic.
+ *
+ * e.g. pmemmap=16G allocates 16G pmem region
+ */
+static int __init parse_pmemmap(char *p)
+{
+       char *old_p = p;
+
+       if (!p)
+               return -EINVAL;
+
+       pmem_size = memparse(p, &p);
+       if (p == old_p)
+               return -EINVAL;
+
+       alloc_pmem_from_memblock();
+       return 0;
+}
+early_param("pmemmap", parse_pmemmap);
+
+static __init int register_e820_pmem(void)
+{
+       struct resource *res, *conflict;
+        struct platform_device *pdev;
+
+       if (!pmem_stolen_memory)
+               return 0;
+
+       res = kzalloc(sizeof(*res), GFP_KERNEL);
+       if (!res)
+               return -1;
+
+       memset(res, 0, sizeof(*res));
+       res->start = pmem_stolen_memory;
+       res->end = pmem_stolen_memory + pmem_size - 1;
+       res->name = "Persistent Memory (legacy)";
+       res->desc = IORES_DESC_PERSISTENT_MEMORY_LEGACY;
+       res->flags = IORESOURCE_MEM;
+
+       conflict = insert_resource_conflict(&iomem_resource, res);
+       if (conflict) {
+               pr_err("%pR conflicts, try insert below %pR\n", res, conflict);
+               kfree(res);
+               return -1;
+       }
+       /*
+        * See drivers/nvdimm/e820.c for the implementation, this is
+        * simply here to trigger the module to load on demand.
+        */
+       pdev = platform_device_alloc("e820_pmem", -1);
+
+       return platform_device_add(pdev);
+}
+device_initcall(register_e820_pmem);
-- 
2.17.1

Reply via email to