This is an example driver with little bit of kernel hack to test ZONE_DEVICE based device memory management on POWER.
Signed-off-by: Anshuman Khandual <khand...@linux.vnet.ibm.com> --- arch/powerpc/mm/init_64.c | 12 ++- arch/powerpc/platforms/pseries/Makefile | 2 +- arch/powerpc/platforms/pseries/rmem.c | 186 ++++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+), 2 deletions(-) create mode 100644 arch/powerpc/platforms/pseries/rmem.c diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c index db73708..5cc286d 100644 --- a/arch/powerpc/mm/init_64.c +++ b/arch/powerpc/mm/init_64.c @@ -351,6 +351,17 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node) } #ifdef CONFIG_MEMORY_HOTPLUG +void dump_vmemmap(void) +{ + struct vmemmap_backing *vmem_back = vmemmap_list; + + for (; vmem_back; vmem_back = vmem_back->list) { + printk("RMEM: vmemmap backing (%lx %lx)\n", + vmem_back->virt_addr, vmem_back->phys); + } +} +EXPORT_SYMBOL(dump_vmemmap); + static unsigned long vmemmap_list_free(unsigned long start) { struct vmemmap_backing *vmem_back, *vmem_back_prev; @@ -482,5 +493,4 @@ struct page *realmode_pfn_to_page(unsigned long pfn) return page; } EXPORT_SYMBOL_GPL(realmode_pfn_to_page); - #endif /* CONFIG_SPARSEMEM_VMEMMAP/CONFIG_FLATMEM */ diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index fedc2ccf0..20a5e23 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -5,7 +5,7 @@ obj-y := lpar.o hvCall.o nvram.o reconfig.o \ of_helpers.o \ setup.o iommu.o event_sources.o ras.o \ firmware.o power.o dlpar.o mobility.o rng.o \ - pci.o pci_dlpar.o eeh_pseries.o msi.o + pci.o pci_dlpar.o eeh_pseries.o msi.o rmem.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SCANLOG) += scanlog.o obj-$(CONFIG_KEXEC) += kexec.o diff --git a/arch/powerpc/platforms/pseries/rmem.c b/arch/powerpc/platforms/pseries/rmem.c new file mode 100644 index 0000000..8c2287a --- /dev/null +++ b/arch/powerpc/platforms/pseries/rmem.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2016 Anshuman Khandual (khand...@linux.vnet.ibm.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/gfp.h> +#include <linux/kthread.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/memremap.h> + +#include <asm/firmware.h> +#include <asm/hvcall.h> +#include <asm/mmu.h> +#include <asm/pgalloc.h> +#include <asm/uaccess.h> +#include <linux/memory.h> +#include <asm/plpar_wrappers.h> +#include <asm/prom.h> + +#define DEVM_MAP_SIZE (1UL << PA_SECTION_SHIFT) * 8 + +extern void dump_vmemmap(void); +extern struct resmem rmem; + +unsigned long devmem_start; +unsigned long devmem_end; + +void driver_test_devmem(void) +{ + unsigned long i; + unsigned long start = devmem_start >> PAGE_SHIFT; + unsigned long end = devmem_end >> PAGE_SHIFT; + + for(i = start; i < end; i++) + *(unsigned long *)i = (char)i; + + for(i = start; i < end; i++) { + if (*(unsigned long *)i != (char)i) + printk("RMEM: Error data miscompare at %lx\n", i); + } + printk("RMEM: Data integrity test successful\n"); +} + +void driver_memory(unsigned long start_pfn, unsigned long end_pfn) +{ + printk("RMEM: Driver now owns PFN(%lx....%lx)\n", start_pfn, end_pfn); + + devmem_start = start_pfn; + devmem_end = end_pfn; + driver_test_devmem(); +} + +static void dump_reserved(void) +{ + unsigned long i; + + printk("RMEM: Reserved memory sections\n"); + for (i = 0; i < rmem.nr; i++) { + printk("RMEM: Base %llx Size: %llx Node: %llu\n", + rmem.mem[i][MEM_BASE], rmem.mem[i][MEM_SIZE], + rmem.mem[i][MEM_NODE]); + } +} + +static void dump_devmap(resource_size_t start) +{ + struct vmem_altmap *altmap; + struct dev_pagemap *pgmap; + struct page_map *pmap; + struct page *page; + unsigned long pfn; + + altmap = to_vmem_altmap((unsigned long)pfn_to_page(start >> PAGE_SHIFT)); + if (altmap) { + printk("RMEM: altmap->base_pfn %lu\n", altmap->base_pfn); + printk("RMEM: altmap->reserve %lu\n", altmap->reserve); + printk("RMEM: altmap->free %lu\n", altmap->free); + printk("RMEM: altmap->align %lu\n", altmap->align); + printk("RMEM: altmap->alloc %lu\n", altmap->alloc); + } + pmap = find_pagemap(start); + rcu_read_lock(); + pgmap = find_dev_pagemap(start); + rcu_read_unlock(); + printk("RMEM: pagemap (%lx)\n", (unsigned long)pmap); + printk("RMEM: dev_pagemap (%lx)\n", (unsigned long)pgmap); + printk("RMEM: pfn range (%lx %lx)\n", pfn_first(pmap), pfn_end(pmap)); + + for (pfn = pfn_first(pmap); pfn < pfn_end(pmap); pfn++) { + page = pfn_to_page(pfn); + printk("DEVM: pfn(%lx) page(%lx) pagemap(%lx) flags(%lx)\n", + pfn, (unsigned long)page, (unsigned long)page->pgmap, + page->flags); + } + driver_memory(pfn_first(pmap), pfn_end(pmap)); +} + +static void simple_translation_test(void __pmem *vaddr) +{ + unsigned long i; + + if (vaddr) { + unsigned long tmp; + + for (i = 0; i < DEVM_MAP_SIZE; i++) + tmp = *((unsigned long *)vaddr + i); + + printk("RMEM: Read access complete (%lx %lx)\n", + (unsigned long)vaddr, DEVM_MAP_SIZE); + } +} + +static int rmem_init(void) +{ + struct class *class; + struct device *dev; + struct resource *res; + struct percpu_ref *ref; + void __pmem *vaddr; + struct vmem_altmap *altmap; + struct vmem_altmap __altmap = { + .base_pfn = rmem.mem[0][0] >> PAGE_SHIFT, + .reserve = 0, + .free = 0x100, + .alloc = 0, + .align = 0, + }; + + printk("RMEM: Driver loaded\n"); + dump_reserved(); + + class = class_create(THIS_MODULE, "rmem"); + if (!class) { + printk("RMEM: class_create() failed\n"); + goto out; + } + + dev = device_create(class, NULL, MKDEV(100, 100), NULL, "rmem"); + if (!dev) { + printk("RMEM: device_create() failed\n"); + goto out; + } + + res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL); + if (!res) { + printk("RMEM: devm_kzalloc() failed\n"); + goto out; + } + + ref = devm_kzalloc(dev, sizeof(*ref), GFP_KERNEL); + if (!res) { + printk("RMEM: devm_kzalloc() failed\n"); + goto out; + } + + dump_vmemmap(); + altmap = &__altmap; + res->start = rmem.mem[0][0]; + res->end = rmem.mem[0][0] + DEVM_MAP_SIZE; + vaddr = devm_memremap_pages(dev, res, ref, altmap); + dump_vmemmap(); + + simple_translation_test(vaddr); + dump_devmap(res->start); + return 0; +out: + return -1; +} + +static void rmem_exit(void) +{ + printk("RMEM: rmem driver unloaded\n"); +} + +module_init(rmem_init); +module_exit(rmem_exit); + +MODULE_AUTHOR("Anshuman Khandual <khand...@linux.vnet.ibm.com>"); +MODULE_DESCRIPTION("Test driver for device memory"); +MODULE_LICENSE("GPL"); -- 1.8.3.1 _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@lists.ozlabs.org https://lists.ozlabs.org/listinfo/linuxppc-dev