Commit 264015f8 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'libnvdimm-for-4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm

Pull libnvdimm updates from Dan Williams:
 "Outside of the new ACPI-NFIT hot-add support this pull request is more
  notable for what it does not contain, than what it does.  There were a
  handful of development topics this cycle, dax get_user_pages, dax
  fsync, and raw block dax, that need more more iteration and will wait
  for 4.5.

  The patches to make devm and the pmem driver NUMA aware have been in
  -next for several weeks.  The hot-add support has not, but is
  contained to the NFIT driver and is passing unit tests.  The coredump
  support is straightforward and was looked over by Jeff.  All of it has
  received a 0day build success notification across 107 configs.

  Summary:

   - Add support for the ACPI 6.0 NFIT hot add mechanism to process
     updates of the NFIT at runtime.

   - Teach the coredump implementation how to filter out DAX mappings.

   - Introduce NUMA hints for allocations made by the pmem driver, and
     as a side effect all devm allocations now hint their NUMA node by
     default"

* tag 'libnvdimm-for-4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm:
  coredump: add DAX filtering for FDPIC ELF coredumps
  coredump: add DAX filtering for ELF coredumps
  acpi: nfit: Add support for hot-add
  nfit: in acpi_nfit_init, break on a 0-length table
  pmem, memremap: convert to numa aware allocations
  devm_memremap_pages: use numa_mem_id
  devm: make allocations numa aware by default
  devm_memremap: convert to return ERR_PTR
  devm_memunmap: use devres_release()
  pmem: kill memremap_pmem()
  x86, mm: quiet arch_add_memory()
parents d55fc378 ab27a8d0
...@@ -1605,16 +1605,16 @@ Documentation/accounting. ...@@ -1605,16 +1605,16 @@ Documentation/accounting.
--------------------------------------------------------------- ---------------------------------------------------------------
When a process is dumped, all anonymous memory is written to a core file as When a process is dumped, all anonymous memory is written to a core file as
long as the size of the core file isn't limited. But sometimes we don't want long as the size of the core file isn't limited. But sometimes we don't want
to dump some memory segments, for example, huge shared memory. Conversely, to dump some memory segments, for example, huge shared memory or DAX.
sometimes we want to save file-backed memory segments into a core file, not Conversely, sometimes we want to save file-backed memory segments into a core
only the individual files. file, not only the individual files.
/proc/<pid>/coredump_filter allows you to customize which memory segments /proc/<pid>/coredump_filter allows you to customize which memory segments
will be dumped when the <pid> process is dumped. coredump_filter is a bitmask will be dumped when the <pid> process is dumped. coredump_filter is a bitmask
of memory types. If a bit of the bitmask is set, memory segments of the of memory types. If a bit of the bitmask is set, memory segments of the
corresponding memory type are dumped, otherwise they are not dumped. corresponding memory type are dumped, otherwise they are not dumped.
The following 7 memory types are supported: The following 9 memory types are supported:
- (bit 0) anonymous private memory - (bit 0) anonymous private memory
- (bit 1) anonymous shared memory - (bit 1) anonymous shared memory
- (bit 2) file-backed private memory - (bit 2) file-backed private memory
...@@ -1623,20 +1623,22 @@ The following 7 memory types are supported: ...@@ -1623,20 +1623,22 @@ The following 7 memory types are supported:
effective only if the bit 2 is cleared) effective only if the bit 2 is cleared)
- (bit 5) hugetlb private memory - (bit 5) hugetlb private memory
- (bit 6) hugetlb shared memory - (bit 6) hugetlb shared memory
- (bit 7) DAX private memory
- (bit 8) DAX shared memory
Note that MMIO pages such as frame buffer are never dumped and vDSO pages Note that MMIO pages such as frame buffer are never dumped and vDSO pages
are always dumped regardless of the bitmask status. are always dumped regardless of the bitmask status.
Note bit 0-4 doesn't effect any hugetlb memory. hugetlb memory are only Note that bits 0-4 don't affect hugetlb or DAX memory. hugetlb memory is
effected by bit 5-6. only affected by bit 5-6, and DAX is only affected by bits 7-8.
Default value of coredump_filter is 0x23; this means all anonymous memory The default value of coredump_filter is 0x33; this means all anonymous memory
segments and hugetlb private memory are dumped. segments, ELF header pages and hugetlb private memory are dumped.
If you don't want to dump all shared memory segments attached to pid 1234, If you don't want to dump all shared memory segments attached to pid 1234,
write 0x21 to the process's proc file. write 0x31 to the process's proc file.
$ echo 0x21 > /proc/1234/coredump_filter $ echo 0x31 > /proc/1234/coredump_filter
When a new process is created, the process inherits the bitmask status from its When a new process is created, the process inherits the bitmask status from its
parent. It is useful to set up coredump_filter before the program runs. parent. It is useful to set up coredump_filter before the program runs.
......
...@@ -354,7 +354,7 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, ...@@ -354,7 +354,7 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range,
} }
for (i = 0; i < nr_range; i++) for (i = 0; i < nr_range; i++)
printk(KERN_DEBUG " [mem %#010lx-%#010lx] page %s\n", pr_debug(" [mem %#010lx-%#010lx] page %s\n",
mr[i].start, mr[i].end - 1, mr[i].start, mr[i].end - 1,
page_size_string(&mr[i])); page_size_string(&mr[i]));
...@@ -401,7 +401,7 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, ...@@ -401,7 +401,7 @@ unsigned long __init_refok init_memory_mapping(unsigned long start,
unsigned long ret = 0; unsigned long ret = 0;
int nr_range, i; int nr_range, i;
pr_info("init_memory_mapping: [mem %#010lx-%#010lx]\n", pr_debug("init_memory_mapping: [mem %#010lx-%#010lx]\n",
start, end - 1); start, end - 1);
memset(mr, 0, sizeof(mr)); memset(mr, 0, sizeof(mr));
......
...@@ -1270,7 +1270,7 @@ static int __meminit vmemmap_populate_hugepages(unsigned long start, ...@@ -1270,7 +1270,7 @@ static int __meminit vmemmap_populate_hugepages(unsigned long start,
/* check to see if we have contiguous blocks */ /* check to see if we have contiguous blocks */
if (p_end != p || node_start != node) { if (p_end != p || node_start != node) {
if (p_start) if (p_start)
printk(KERN_DEBUG " [%lx-%lx] PMD -> [%p-%p] on node %d\n", pr_debug(" [%lx-%lx] PMD -> [%p-%p] on node %d\n",
addr_start, addr_end-1, p_start, p_end-1, node_start); addr_start, addr_end-1, p_start, p_end-1, node_start);
addr_start = addr; addr_start = addr;
node_start = node; node_start = node;
...@@ -1368,7 +1368,7 @@ void register_page_bootmem_memmap(unsigned long section_nr, ...@@ -1368,7 +1368,7 @@ void register_page_bootmem_memmap(unsigned long section_nr,
void __meminit vmemmap_populate_print_last(void) void __meminit vmemmap_populate_print_last(void)
{ {
if (p_start) { if (p_start) {
printk(KERN_DEBUG " [%lx-%lx] PMD -> [%p-%p] on node %d\n", pr_debug(" [%lx-%lx] PMD -> [%p-%p] on node %d\n",
addr_start, addr_end-1, p_start, p_end-1, node_start); addr_start, addr_end-1, p_start, p_end-1, node_start);
p_start = NULL; p_start = NULL;
p_end = NULL; p_end = NULL;
......
This diff is collapsed.
...@@ -48,6 +48,7 @@ enum { ...@@ -48,6 +48,7 @@ enum {
struct nfit_spa { struct nfit_spa {
struct acpi_nfit_system_address *spa; struct acpi_nfit_system_address *spa;
struct list_head list; struct list_head list;
int is_registered;
}; };
struct nfit_dcr { struct nfit_dcr {
...@@ -97,6 +98,7 @@ struct acpi_nfit_desc { ...@@ -97,6 +98,7 @@ struct acpi_nfit_desc {
struct nvdimm_bus_descriptor nd_desc; struct nvdimm_bus_descriptor nd_desc;
struct acpi_table_nfit *nfit; struct acpi_table_nfit *nfit;
struct mutex spa_map_mutex; struct mutex spa_map_mutex;
struct mutex init_mutex;
struct list_head spa_maps; struct list_head spa_maps;
struct list_head memdevs; struct list_head memdevs;
struct list_head flushes; struct list_head flushes;
......
...@@ -82,12 +82,12 @@ static struct devres_group * node_to_group(struct devres_node *node) ...@@ -82,12 +82,12 @@ static struct devres_group * node_to_group(struct devres_node *node)
} }
static __always_inline struct devres * alloc_dr(dr_release_t release, static __always_inline struct devres * alloc_dr(dr_release_t release,
size_t size, gfp_t gfp) size_t size, gfp_t gfp, int nid)
{ {
size_t tot_size = sizeof(struct devres) + size; size_t tot_size = sizeof(struct devres) + size;
struct devres *dr; struct devres *dr;
dr = kmalloc_track_caller(tot_size, gfp); dr = kmalloc_node_track_caller(tot_size, gfp, nid);
if (unlikely(!dr)) if (unlikely(!dr))
return NULL; return NULL;
...@@ -106,24 +106,25 @@ static void add_dr(struct device *dev, struct devres_node *node) ...@@ -106,24 +106,25 @@ static void add_dr(struct device *dev, struct devres_node *node)
} }
#ifdef CONFIG_DEBUG_DEVRES #ifdef CONFIG_DEBUG_DEVRES
void * __devres_alloc(dr_release_t release, size_t size, gfp_t gfp, void * __devres_alloc_node(dr_release_t release, size_t size, gfp_t gfp, int nid,
const char *name) const char *name)
{ {
struct devres *dr; struct devres *dr;
dr = alloc_dr(release, size, gfp | __GFP_ZERO); dr = alloc_dr(release, size, gfp | __GFP_ZERO, nid);
if (unlikely(!dr)) if (unlikely(!dr))
return NULL; return NULL;
set_node_dbginfo(&dr->node, name, size); set_node_dbginfo(&dr->node, name, size);
return dr->data; return dr->data;
} }
EXPORT_SYMBOL_GPL(__devres_alloc); EXPORT_SYMBOL_GPL(__devres_alloc_node);
#else #else
/** /**
* devres_alloc - Allocate device resource data * devres_alloc - Allocate device resource data
* @release: Release function devres will be associated with * @release: Release function devres will be associated with
* @size: Allocation size * @size: Allocation size
* @gfp: Allocation flags * @gfp: Allocation flags
* @nid: NUMA node
* *
* Allocate devres of @size bytes. The allocated area is zeroed, then * Allocate devres of @size bytes. The allocated area is zeroed, then
* associated with @release. The returned pointer can be passed to * associated with @release. The returned pointer can be passed to
...@@ -132,16 +133,16 @@ EXPORT_SYMBOL_GPL(__devres_alloc); ...@@ -132,16 +133,16 @@ EXPORT_SYMBOL_GPL(__devres_alloc);
* RETURNS: * RETURNS:
* Pointer to allocated devres on success, NULL on failure. * Pointer to allocated devres on success, NULL on failure.
*/ */
void * devres_alloc(dr_release_t release, size_t size, gfp_t gfp) void * devres_alloc_node(dr_release_t release, size_t size, gfp_t gfp, int nid)
{ {
struct devres *dr; struct devres *dr;
dr = alloc_dr(release, size, gfp | __GFP_ZERO); dr = alloc_dr(release, size, gfp | __GFP_ZERO, nid);
if (unlikely(!dr)) if (unlikely(!dr))
return NULL; return NULL;
return dr->data; return dr->data;
} }
EXPORT_SYMBOL_GPL(devres_alloc); EXPORT_SYMBOL_GPL(devres_alloc_node);
#endif #endif
/** /**
...@@ -776,7 +777,7 @@ void * devm_kmalloc(struct device *dev, size_t size, gfp_t gfp) ...@@ -776,7 +777,7 @@ void * devm_kmalloc(struct device *dev, size_t size, gfp_t gfp)
struct devres *dr; struct devres *dr;
/* use raw alloc_dr for kmalloc caller tracing */ /* use raw alloc_dr for kmalloc caller tracing */
dr = alloc_dr(devm_kmalloc_release, size, gfp); dr = alloc_dr(devm_kmalloc_release, size, gfp, dev_to_node(dev));
if (unlikely(!dr)) if (unlikely(!dr))
return NULL; return NULL;
......
...@@ -150,18 +150,15 @@ static struct pmem_device *pmem_alloc(struct device *dev, ...@@ -150,18 +150,15 @@ static struct pmem_device *pmem_alloc(struct device *dev,
return ERR_PTR(-EBUSY); return ERR_PTR(-EBUSY);
} }
if (pmem_should_map_pages(dev)) { if (pmem_should_map_pages(dev))
void *addr = devm_memremap_pages(dev, res); pmem->virt_addr = (void __pmem *) devm_memremap_pages(dev, res);
else
pmem->virt_addr = (void __pmem *) devm_memremap(dev,
pmem->phys_addr, pmem->size,
ARCH_MEMREMAP_PMEM);
if (IS_ERR(addr)) if (IS_ERR(pmem->virt_addr))
return addr; return (void __force *) pmem->virt_addr;
pmem->virt_addr = (void __pmem *) addr;
} else {
pmem->virt_addr = memremap_pmem(dev, pmem->phys_addr,
pmem->size);
if (!pmem->virt_addr)
return ERR_PTR(-ENXIO);
}
return pmem; return pmem;
} }
...@@ -179,9 +176,10 @@ static void pmem_detach_disk(struct pmem_device *pmem) ...@@ -179,9 +176,10 @@ static void pmem_detach_disk(struct pmem_device *pmem)
static int pmem_attach_disk(struct device *dev, static int pmem_attach_disk(struct device *dev,
struct nd_namespace_common *ndns, struct pmem_device *pmem) struct nd_namespace_common *ndns, struct pmem_device *pmem)
{ {
int nid = dev_to_node(dev);
struct gendisk *disk; struct gendisk *disk;
pmem->pmem_queue = blk_alloc_queue(GFP_KERNEL); pmem->pmem_queue = blk_alloc_queue_node(GFP_KERNEL, nid);
if (!pmem->pmem_queue) if (!pmem->pmem_queue)
return -ENOMEM; return -ENOMEM;
...@@ -191,7 +189,7 @@ static int pmem_attach_disk(struct device *dev, ...@@ -191,7 +189,7 @@ static int pmem_attach_disk(struct device *dev,
blk_queue_bounce_limit(pmem->pmem_queue, BLK_BOUNCE_ANY); blk_queue_bounce_limit(pmem->pmem_queue, BLK_BOUNCE_ANY);
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, pmem->pmem_queue); queue_flag_set_unlocked(QUEUE_FLAG_NONROT, pmem->pmem_queue);
disk = alloc_disk(0); disk = alloc_disk_node(0, nid);
if (!disk) { if (!disk) {
blk_cleanup_queue(pmem->pmem_queue); blk_cleanup_queue(pmem->pmem_queue);
return -ENOMEM; return -ENOMEM;
...@@ -363,8 +361,8 @@ static int nvdimm_namespace_attach_pfn(struct nd_namespace_common *ndns) ...@@ -363,8 +361,8 @@ static int nvdimm_namespace_attach_pfn(struct nd_namespace_common *ndns)
/* establish pfn range for lookup, and switch to direct map */ /* establish pfn range for lookup, and switch to direct map */
pmem = dev_get_drvdata(dev); pmem = dev_get_drvdata(dev);
memunmap_pmem(dev, pmem->virt_addr); devm_memunmap(dev, (void __force *) pmem->virt_addr);
pmem->virt_addr = (void __pmem *)devm_memremap_pages(dev, &nsio->res); pmem->virt_addr = (void __pmem *) devm_memremap_pages(dev, &nsio->res);
if (IS_ERR(pmem->virt_addr)) { if (IS_ERR(pmem->virt_addr)) {
rc = PTR_ERR(pmem->virt_addr); rc = PTR_ERR(pmem->virt_addr);
goto err; goto err;
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/utsname.h> #include <linux/utsname.h>
#include <linux/coredump.h> #include <linux/coredump.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/dax.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/param.h> #include <asm/param.h>
#include <asm/page.h> #include <asm/page.h>
...@@ -1236,6 +1237,15 @@ static unsigned long vma_dump_size(struct vm_area_struct *vma, ...@@ -1236,6 +1237,15 @@ static unsigned long vma_dump_size(struct vm_area_struct *vma,
if (vma->vm_flags & VM_DONTDUMP) if (vma->vm_flags & VM_DONTDUMP)
return 0; return 0;
/* support for DAX */
if (vma_is_dax(vma)) {
if ((vma->vm_flags & VM_SHARED) && FILTER(DAX_SHARED))
goto whole;
if (!(vma->vm_flags & VM_SHARED) && FILTER(DAX_PRIVATE))
goto whole;
return 0;
}
/* Hugetlb memory check */ /* Hugetlb memory check */
if (vma->vm_flags & VM_HUGETLB) { if (vma->vm_flags & VM_HUGETLB) {
if ((vma->vm_flags & VM_SHARED) && FILTER(HUGETLB_SHARED)) if ((vma->vm_flags & VM_SHARED) && FILTER(HUGETLB_SHARED))
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/elf-fdpic.h> #include <linux/elf-fdpic.h>
#include <linux/elfcore.h> #include <linux/elfcore.h>
#include <linux/coredump.h> #include <linux/coredump.h>
#include <linux/dax.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/param.h> #include <asm/param.h>
...@@ -1231,6 +1232,20 @@ static int maydump(struct vm_area_struct *vma, unsigned long mm_flags) ...@@ -1231,6 +1232,20 @@ static int maydump(struct vm_area_struct *vma, unsigned long mm_flags)
return 0; return 0;
} }
/* support for DAX */
if (vma_is_dax(vma)) {
if (vma->vm_flags & VM_SHARED) {
dump_ok = test_bit(MMF_DUMP_DAX_SHARED, &mm_flags);
kdcore("%08lx: %08lx: %s (DAX shared)", vma->vm_start,
vma->vm_flags, dump_ok ? "yes" : "no");
} else {
dump_ok = test_bit(MMF_DUMP_DAX_PRIVATE, &mm_flags);
kdcore("%08lx: %08lx: %s (DAX private)", vma->vm_start,
vma->vm_flags, dump_ok ? "yes" : "no");
}
return dump_ok;
}
/* By default, dump shared memory if mapped from an anonymous file. */ /* By default, dump shared memory if mapped from an anonymous file. */
if (vma->vm_flags & VM_SHARED) { if (vma->vm_flags & VM_SHARED) {
if (file_inode(vma->vm_file)->i_nlink == 0) { if (file_inode(vma->vm_file)->i_nlink == 0) {
......
...@@ -604,13 +604,21 @@ typedef void (*dr_release_t)(struct device *dev, void *res); ...@@ -604,13 +604,21 @@ typedef void (*dr_release_t)(struct device *dev, void *res);
typedef int (*dr_match_t)(struct device *dev, void *res, void *match_data); typedef int (*dr_match_t)(struct device *dev, void *res, void *match_data);
#ifdef CONFIG_DEBUG_DEVRES #ifdef CONFIG_DEBUG_DEVRES
extern void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp, extern void *__devres_alloc_node(dr_release_t release, size_t size, gfp_t gfp,
const char *name); int nid, const char *name);
#define devres_alloc(release, size, gfp) \ #define devres_alloc(release, size, gfp) \
__devres_alloc(release, size, gfp, #release) __devres_alloc_node(release, size, gfp, NUMA_NO_NODE, #release)
#define devres_alloc_node(release, size, gfp, nid) \
__devres_alloc_node(release, size, gfp, nid, #release)
#else #else
extern void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp); extern void *devres_alloc_node(dr_release_t release, size_t size, gfp_t gfp,
int nid);
static inline void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
{
return devres_alloc_node(release, size, gfp, NUMA_NO_NODE);
}
#endif #endif
extern void devres_for_each_res(struct device *dev, dr_release_t release, extern void devres_for_each_res(struct device *dev, dr_release_t release,
dr_match_t match, void *match_data, dr_match_t match, void *match_data,
void (*fn)(struct device *, void *, void *), void (*fn)(struct device *, void *, void *),
......
...@@ -65,11 +65,6 @@ static inline void memcpy_from_pmem(void *dst, void __pmem const *src, size_t si ...@@ -65,11 +65,6 @@ static inline void memcpy_from_pmem(void *dst, void __pmem const *src, size_t si
memcpy(dst, (void __force const *) src, size); memcpy(dst, (void __force const *) src, size);
} }
static inline void memunmap_pmem(struct device *dev, void __pmem *addr)
{
devm_memunmap(dev, (void __force *) addr);
}
static inline bool arch_has_pmem_api(void) static inline bool arch_has_pmem_api(void)
{ {
return IS_ENABLED(CONFIG_ARCH_HAS_PMEM_API); return IS_ENABLED(CONFIG_ARCH_HAS_PMEM_API);
...@@ -93,7 +88,7 @@ static inline bool arch_has_wmb_pmem(void) ...@@ -93,7 +88,7 @@ static inline bool arch_has_wmb_pmem(void)
* These defaults seek to offer decent performance and minimize the * These defaults seek to offer decent performance and minimize the
* window between i/o completion and writes being durable on media. * window between i/o completion and writes being durable on media.
* However, it is undefined / architecture specific whether * However, it is undefined / architecture specific whether
* default_memremap_pmem + default_memcpy_to_pmem is sufficient for * ARCH_MEMREMAP_PMEM + default_memcpy_to_pmem is sufficient for
* making data durable relative to i/o completion. * making data durable relative to i/o completion.
*/ */
static inline void default_memcpy_to_pmem(void __pmem *dst, const void *src, static inline void default_memcpy_to_pmem(void __pmem *dst, const void *src,
...@@ -116,25 +111,6 @@ static inline void default_clear_pmem(void __pmem *addr, size_t size) ...@@ -116,25 +111,6 @@ static inline void default_clear_pmem(void __pmem *addr, size_t size)
memset((void __force *)addr, 0, size); memset((void __force *)addr, 0, size);
} }
/**
* memremap_pmem - map physical persistent memory for pmem api
* @offset: physical address of persistent memory
* @size: size of the mapping
*
* Establish a mapping of the architecture specific memory type expected
* by memcpy_to_pmem() and wmb_pmem(). For example, it may be
* the case that an uncacheable or writethrough mapping is sufficient,
* or a writeback mapping provided memcpy_to_pmem() and
* wmb_pmem() arrange for the data to be written through the
* cache to persistent media.
*/
static inline void __pmem *memremap_pmem(struct device *dev,
resource_size_t offset, unsigned long size)
{
return (void __pmem *) devm_memremap(dev, offset, size,
ARCH_MEMREMAP_PMEM);
}
/** /**
* memcpy_to_pmem - copy data to persistent memory * memcpy_to_pmem - copy data to persistent memory
* @dst: destination buffer for the copy * @dst: destination buffer for the copy
......
...@@ -484,9 +484,11 @@ static inline int get_dumpable(struct mm_struct *mm) ...@@ -484,9 +484,11 @@ static inline int get_dumpable(struct mm_struct *mm)
#define MMF_DUMP_ELF_HEADERS 6 #define MMF_DUMP_ELF_HEADERS 6
#define MMF_DUMP_HUGETLB_PRIVATE 7 #define MMF_DUMP_HUGETLB_PRIVATE 7
#define MMF_DUMP_HUGETLB_SHARED 8 #define MMF_DUMP_HUGETLB_SHARED 8
#define MMF_DUMP_DAX_PRIVATE 9
#define MMF_DUMP_DAX_SHARED 10
#define MMF_DUMP_FILTER_SHIFT MMF_DUMPABLE_BITS #define MMF_DUMP_FILTER_SHIFT MMF_DUMPABLE_BITS
#define MMF_DUMP_FILTER_BITS 7 #define MMF_DUMP_FILTER_BITS 9
#define MMF_DUMP_FILTER_MASK \ #define MMF_DUMP_FILTER_MASK \
(((1 << MMF_DUMP_FILTER_BITS) - 1) << MMF_DUMP_FILTER_SHIFT) (((1 << MMF_DUMP_FILTER_BITS) - 1) << MMF_DUMP_FILTER_SHIFT)
#define MMF_DUMP_FILTER_DEFAULT \ #define MMF_DUMP_FILTER_DEFAULT \
......
...@@ -124,9 +124,10 @@ void *devm_memremap(struct device *dev, resource_size_t offset, ...@@ -124,9 +124,10 @@ void *devm_memremap(struct device *dev, resource_size_t offset,
{ {
void **ptr, *addr; void **ptr, *addr;
ptr = devres_alloc(devm_memremap_release, sizeof(*ptr), GFP_KERNEL); ptr = devres_alloc_node(devm_memremap_release, sizeof(*ptr), GFP_KERNEL,
dev_to_node(dev));
if (!ptr) if (!ptr)
return NULL; return ERR_PTR(-ENOMEM);
addr = memremap(offset, size, flags); addr = memremap(offset, size, flags);
if (addr) { if (addr) {
...@@ -141,9 +142,8 @@ EXPORT_SYMBOL(devm_memremap); ...@@ -141,9 +142,8 @@ EXPORT_SYMBOL(devm_memremap);
void devm_memunmap(struct device *dev, void *addr) void devm_memunmap(struct device *dev, void *addr)
{ {
WARN_ON(devres_destroy(dev, devm_memremap_release, devm_memremap_match, WARN_ON(devres_release(dev, devm_memremap_release,
addr)); devm_memremap_match, addr));
memunmap(addr);
} }
EXPORT_SYMBOL(devm_memunmap); EXPORT_SYMBOL(devm_memunmap);
...@@ -176,8 +176,8 @@ void *devm_memremap_pages(struct device *dev, struct resource *res) ...@@ -176,8 +176,8 @@ void *devm_memremap_pages(struct device *dev, struct resource *res)
if (is_ram == REGION_INTERSECTS) if (is_ram == REGION_INTERSECTS)
return __va(res->start); return __va(res->start);
page_map = devres_alloc(devm_memremap_pages_release, page_map = devres_alloc_node(devm_memremap_pages_release,
sizeof(*page_map), GFP_KERNEL); sizeof(*page_map), GFP_KERNEL, dev_to_node(dev));
if (!page_map) if (!page_map)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
...@@ -185,7 +185,7 @@ void *devm_memremap_pages(struct device *dev, struct resource *res) ...@@ -185,7 +185,7 @@ void *devm_memremap_pages(struct device *dev, struct resource *res)
nid = dev_to_node(dev); nid = dev_to_node(dev);
if (nid < 0) if (nid < 0)
nid = 0; nid = numa_mem_id();
error = arch_add_memory(nid, res->start, resource_size(res), true); error = arch_add_memory(nid, res->start, resource_size(res), true);
if (error) { if (error) {
......
...@@ -17,8 +17,10 @@ ...@@ -17,8 +17,10 @@
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h>
#include <linux/ndctl.h> #include <linux/ndctl.h>
#include <linux/sizes.h> #include <linux/sizes.h>
#include <linux/list.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <nfit.h> #include <nfit.h>
#include <nd.h> #include <nd.h>
...@@ -44,6 +46,15 @@ ...@@ -44,6 +46,15 @@
* +------+ | blk5.0 | pm1.0 | 3 region5 * +------+ | blk5.0 | pm1.0 | 3 region5
* +-------------------------+----------+-+-------+ * +-------------------------+----------+-+-------+
* *
* +--+---+
* | cpu1 |
* +--+---+ (Hotplug DIMM)
* | +----------------------------------------------+
* +--+---+ | blk6.0/pm7.0 | 4 region6/7
* | imc0 +--+----------------------------------------------+
* +------+
*
*
* *) In this layout we have four dimms and two memory controllers in one * *) In this layout we have four dimms and two memory controllers in one
* socket. Each unique interface (BLK or PMEM) to DPA space * socket. Each unique interface (BLK or PMEM) to DPA space
* is identified by a region device with a dynamically assigned id. * is identified by a region device with a dynamically assigned id.
...@@ -85,8 +96,8 @@ ...@@ -85,8 +96,8 @@
* reference an NVDIMM. * reference an NVDIMM.
*/ */
enum { enum {
NUM_PM = 2, NUM_PM = 3,
NUM_DCR = 4, NUM_DCR = 5,
NUM_BDW = NUM_DCR, NUM_BDW = NUM_DCR,
NUM_SPA = NUM_PM + NUM_DCR + NUM_BDW, NUM_SPA = NUM_PM + NUM_DCR + NUM_BDW,
NUM_MEM = NUM_DCR + NUM_BDW + 2 /* spa0 iset */ + 4 /* spa1 iset */, NUM_MEM = NUM_DCR + NUM_BDW + 2 /* spa0 iset */ + 4 /* spa1 iset */,
...@@ -115,6 +126,7 @@ static u32 handle[NUM_DCR] = { ...@@ -115,6 +126,7 @@ static u32 handle[NUM_DCR] = {
[1] = NFIT_DIMM_HANDLE(0, 0, 0, 0, 1), [1] = NFIT_DIMM_HANDLE(0, 0, 0, 0, 1),
[2] = NFIT_DIMM_HANDLE(0, 0, 1, 0, 0), [2] = NFIT_DIMM_HANDLE(0, 0, 1, 0, 0),
[3] = NFIT_DIMM_HANDLE(0, 0, 1, 0, 1), [3] = NFIT_DIMM_HANDLE(0, 0, 1, 0, 1),
[4] = NFIT_DIMM_HANDLE(0, 1, 0, 0, 0),
}; };
struct nfit_test { struct nfit_test {
...@@ -138,6 +150,7 @@ struct nfit_test { ...@@ -138,6 +150,7 @@ struct nfit_test {
dma_addr_t *dcr_dma; dma_addr_t *dcr_dma;
int (*alloc)(struct nfit_test *t); int (*alloc)(struct nfit_test *t);
void (*setup)(struct nfit_test *t); void (*setup)(struct nfit_test *t);
int setup_hotplug;
}; };
static struct nfit_test *to_nfit_test(struct device *dev) static struct nfit_test *to_nfit_test(struct device *dev)
...@@ -428,6 +441,10 @@ static int nfit_test0_alloc(struct nfit_test *t) ...@@ -428,6 +441,10 @@ static int nfit_test0_alloc(struct nfit_test *t)
if (!t->spa_set[1]) if (!t->spa_set[1])
return -ENOMEM; return -ENOMEM;
t->spa_set[2] = test_alloc_coherent(t, SPA0_SIZE, &t->spa_set_dma[2]);
if (!t->spa_set[2])
return -ENOMEM;
for (i = 0; i < NUM_DCR; i++) { for (i = 0; i < NUM_DCR; i++) {
t->dimm[i] = test_alloc(t, DIMM_SIZE, &t->dimm_dma[i]); t->dimm[i] = test_alloc(t, DIMM_SIZE, &t->dimm_dma[i]);
if (!t->dimm[i]) if (!t->dimm[i])
...@@ -950,6 +967,126 @@ static void nfit_test0_setup(struct nfit_test *t) ...@@ -950,6 +967,126 @@ static void nfit_test0_setup(struct nfit_test *t)
flush->hint_count = 1; flush->hint_count = 1;
flush->hint_address[0] = t->flush_dma[3]; flush->hint_address[0] = t->flush_dma[3];
if (t->setup_hotplug) {
offset = offset + sizeof(struct acpi_nfit_flush_address) * 4;
/* dcr-descriptor4 */
dcr = nfit_buf + offset;
dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION;
dcr->header.length = sizeof(struct acpi_nfit_control_region);
dcr->region_index = 4+1;
dcr->vendor_id = 0xabcd;
dcr->device_id = 0;
dcr->revision_id = 1;
dcr->serial_number = ~handle[4];
dcr->windows = 1;
dcr->window_size = DCR_SIZE;
dcr->command_offset = 0;
dcr->command_size = 8;
dcr->status_offset = 8;
dcr->status_size = 4;
offset = offset + sizeof(struct acpi_nfit_control_region);
/* bdw4 (spa/dcr4, dimm4) */
bdw = nfit_buf + offset;
bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION;
bdw->header.length = sizeof(struct acpi_nfit_data_region);
bdw->region_index = 4+1;
bdw->windows = 1;
bdw->offset = 0;
bdw->size = BDW_SIZE;
bdw->capacity = DIMM_SIZE;
bdw->start_address = 0;
offset = offset + sizeof(struct acpi_nfit_data_region);
/* spa10 (dcr4) dimm4 */
spa = nfit_buf + offset;
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
spa->header.length = sizeof(*spa);
memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16);
spa->range_index = 10+1;
spa->address = t->dcr_dma[4];
spa->length = DCR_SIZE;
/*
* spa11 (single-dimm interleave for hotplug, note storage
* does not actually alias the related block-data-window
* regions)
*/
spa = nfit_buf + offset + sizeof(*spa);
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
spa->header.length = sizeof(*spa);
memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_PM), 16);
spa->range_index = 11+1;
spa->address = t->spa_set_dma[2];
spa->length = SPA0_SIZE;
/* spa12 (bdw for dcr4) dimm4 */
spa = nfit_buf + offset + sizeof(*spa) * 2;
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
spa->header.length = sizeof(*spa);
memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16);
spa->range_index = 12+1;
spa->address = t->dimm_dma[4];
spa->length = DIMM_SIZE;
offset = offset + sizeof(*spa) * 3;
/* mem-region14 (spa/dcr4, dimm4) */
memdev = nfit_buf + offset;
memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
memdev->header.length = sizeof(*memdev);
memdev->device_handle = handle[4];
memdev->physical_id = 4;
memdev->region_id = 0;
memdev->range_index = 10+1;
memdev->region_index = 4+1;
memdev->region_size = 0;
memdev->region_offset = 0;
memdev->address = 0;
memdev->interleave_index = 0;
memdev->interleave_ways = 1;
/* mem-region15 (spa0, dimm4) */
memdev = nfit_buf + offset +
sizeof(struct acpi_nfit_memory_map);
memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
memdev->header.length = sizeof(*memdev);
memdev->device_handle = handle[4];
memdev->physical_id = 4;
memdev->region_id = 0;
memdev->range_index = 11+1;
memdev->region_index = 4+1;
memdev->region_size = SPA0_SIZE;
memdev->region_offset = t->spa_set_dma[2];
memdev->address = 0;
memdev->interleave_index = 0;
memdev->interleave_ways = 1;
/* mem-region16 (spa/dcr4, dimm4) */
memdev = nfit_buf + offset +
sizeof(struct acpi_nfit_memory_map) * 2;
memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
memdev->header.length = sizeof(*memdev);
memdev->device_handle = handle[4];
memdev->physical_id = 4;
memdev->region_id = 0;
memdev->range_index = 12+1;
memdev->region_index = 4+1;
memdev->region_size = 0;
memdev->region_offset = 0;
memdev->address = 0;
memdev->interleave_index = 0;
memdev->interleave_ways = 1;
offset = offset + sizeof(struct acpi_nfit_memory_map) * 3;
/* flush3 (dimm4) */
flush = nfit_buf + offset;
flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS;
flush->header.length = sizeof(struct acpi_nfit_flush_address);
flush->device_handle = handle[4];
flush->hint_count = 1;
flush->hint_address[0] = t->flush_dma[4];
}
acpi_desc = &t->acpi_desc; acpi_desc = &t->acpi_desc;
set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_dsm_force_en); set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_dsm_force_en);
set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_dsm_force_en); set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_dsm_force_en);
...@@ -1108,6 +1245,29 @@ static int nfit_test_probe(struct platform_device *pdev) ...@@ -1108,6 +1245,29 @@ static int nfit_test_probe(struct platform_device *pdev)
if (!acpi_desc->nvdimm_bus) if (!acpi_desc->nvdimm_bus)
return -ENXIO; return -ENXIO;
INIT_LIST_HEAD(&acpi_desc->spa_maps);
INIT_LIST_HEAD(&acpi_desc->spas);
INIT_LIST_HEAD(&acpi_desc->dcrs);
INIT_LIST_HEAD(&acpi_desc->bdws);
INIT_LIST_HEAD(&acpi_desc->idts);
INIT_LIST_HEAD(&acpi_desc->flushes);
INIT_LIST_HEAD(&acpi_desc->memdevs);
INIT_LIST_HEAD(&acpi_desc->dimms);
mutex_init(&acpi_desc->spa_map_mutex);
mutex_init(&acpi_desc->init_mutex);
rc = acpi_nfit_init(acpi_desc, nfit_test->nfit_size);
if (rc) {
nvdimm_bus_unregister(acpi_desc->nvdimm_bus);
return rc;
}
if (nfit_test->setup != nfit_test0_setup)
return 0;
nfit_test->setup_hotplug = 1;
nfit_test->setup(nfit_test);
rc = acpi_nfit_init(acpi_desc, nfit_test->nfit_size); rc = acpi_nfit_init(acpi_desc, nfit_test->nfit_size);
if (rc) { if (rc) {
nvdimm_bus_unregister(acpi_desc->nvdimm_bus); nvdimm_bus_unregister(acpi_desc->nvdimm_bus);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment