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 <oliveroh@xxxxxxxxxxx> Not-Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@xxxxxxxxxxxxx> --- 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