DNUMA: Runtime NUMA memory layout reconfiguration
Subsystems (7)
These patches allow the NUMA memory layout (meaning the mapping of a page to a node) to be changed at runtime in place (without hotplugging).
= Why/when is this useful? =
In virtual machines (VMs) running on NUMA systems both [a] if/when the hypervisor decides to move their backing memory around (compacting, prioritizing another VMs desired layout, etc) and [b] in general for migration of VMs.
The hardware is _already_ changing the NUMA layout underneath us. We have powerpc64 systems with firmware that currently move the backing memory around, and have the ability to notify Linux of new NUMA info.
= Code & testing =
web:
https://github.com/jmesmon/linux/tree/dnuma/v26
git:
https://github.com/jmesmon/linux.git dnuma/v26
commit range:
7e4f3230c9161706ebe9d37d774398082dc352de^..01e16461cf4a914feb1a34ed8dd7b28f3e842645
Some patches are marked "XXX: ...", they are only for testing or temporary documentation purposes.
A debugfs interface allows the NUMA memory layout to be changed. Basically, you don't need to have wierd systems to test this, in fact, I've done all my testing so far in plain old qemu-i386.
A script which stripes the memory between nodes or pushes all memory to a (potentially new) node is avaliable here:
https://raw.github.com/jmesmon/trifles/master/bin/dnuma-test
Related LSF/MM Topic proposal:
http://permalink.gmane.org/gmane.linux.kernel.mm/95342
= How are you managing to do this? =
Reconfiguration of page->node mappings is done at the page allocator level by both pulling out free pages (when a new memory layout is commited) & redirecting pages on free to their new node.
Because we can't change page_node(A) while A is allocated, a rbtree holding the mapping from pfn ranges to node ids ('struct memlayout') is introduced to track the pfn->node mapping for yet-to-be-transplanted pages. A lookup in this rbtree occurs on any page allocator path that decides which zone to free a page to.
To avoid horrible performance due to rbtree lookups all the time, the rbtree is only consulted when the page is marked with a new pageflag (LookupNode).
= Current Limitations =
For the reconfiguration to be effective (and not make the allocator make poorer choices), updating the cpu->node mappings is also needed. This patchset does _not_ handle this. Also missing is a way to update topology (node distances), which is less fatal.
These patches only work on SPARSEMEM and the node id _must_ fit in the pageflags (can't be pushed out to the section). This generally means that 32-bit platforms are out (unless you hack MAX_PHYS{ADDR,MEM}_BITS like I do for testing).
This code does the reconfiguration without hotplugging memory at all (1 errant page doesn't keep us from fixing the rest of them). But it still depends on MEMORY_HOTPLUG for functions that online nodes & adjust zone/pgdat size.
Things that need doing (but aren't quite bugs):
- While the interface is meant to be driven via a hypervisor/firmware, that portion is not yet included.
- notifier for kernel users of memory that need/want their allocations on a particular node (NODE_DATA(), for instance).
- notifier for userspace.
- a way to allocate things from the appropriate node prior to the page allocator being fully updated (could just be "allocate it wrong now & reallocate later").
- Make memlayout faster (potentially via per-node allocation, different data structure, or more/smarter caching).
- (potentially) propagation of updated layout knowledge into kmem_caches (SL*B).
Known Bugs:
- Transplant of free pages is _very_ slow due to excessive use of stop_machine() via zone_pcp_update() & build_zonelists(). On my i5 laptop, it take ~9 minutes to stripe the layout in blocks of 256 pfns to 3 nodes on a 128MB 8 cpu x86_32 VM booted with 2 nodes.
- memory leak when SLUB is used (struct kmem_cache_nodes are leaked), SLAB appears fine.
- Locking of managed_pages/present_pages needs adjustment, or they need to be updated outside of the free page path.
- Exported numa/memory info in sysfs isn't updated (numactl --show segfaults, numactl --hardware shows new nodes as nearly empty).
- Uses pageflag setters without "owning" pages, could cause loss of pageflag updates when combined with non-atomic pageflag users in mm/*.
- some strange sleeps while atomic (for me they occur when memory is moved out of all the boot nodes)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
cover DNUMA: Runtime NUMA memory layout reconfiguration
Some people asked me to send the email patches for this instead of just posting a git tree link
For reference, this is the original message:
http://lkml.org/lkml/2013/2/27/374
--
arch/x86/Kconfig | 1 - arch/x86/include/asm/sparsemem.h | 4 +- arch/x86/mm/numa.c | 32 +++- include/linux/dnuma.h | 96 +++++++++++ include/linux/memlayout.h | 111 +++++++++++++ include/linux/memory_hotplug.h | 4 + include/linux/mm.h | 7 +- include/linux/page-flags.h | 18 ++ include/linux/rbtree.h | 11 ++ init/main.c | 2 + lib/rbtree.c | 40 +++++ mm/Kconfig | 44 +++++ mm/Makefile | 2 + mm/dnuma.c | 351 +++++++++++++++++++++++++++++++++++++++ mm/internal.h | 13 +- mm/memlayout-debugfs.c | 323 +++++++++++++++++++++++++++++++++++ mm/memlayout-debugfs.h | 35 ++++ mm/memlayout.c | 267 +++++++++++++++++++++++++++++ mm/memory_hotplug.c | 53 +++--- mm/page_alloc.c | 112 +++++++++++-- 20 files changed, 1486 insertions(+), 40 deletions(-)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 1/24 XXX: reduce MAX_PHYSADDR_BITS & MAX_PHYSMEM_BITS in PAE.
This is a hack I use to allow PAE to be enabled & still fit the node into the pageflags (PAE is enabled as a workaround for a kvm bug).
--- arch/x86/include/asm/sparsemem.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/x86/include/asm/sparsemem.h +2 -2
@@ -17,8 +17,8 @@#ifdef CONFIG_X86_32 # ifdef CONFIG_X86_PAE # define SECTION_SIZE_BITS 29 -# define MAX_PHYSADDR_BITS 36 -# define MAX_PHYSMEM_BITS 36 +# define MAX_PHYSADDR_BITS 32 +# define MAX_PHYSMEM_BITS 32# else # define SECTION_SIZE_BITS 26# define MAX_PHYSADDR_BITS 32
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 2/24 XXX: x86/Kconfig: simplify NUMA config for NUMA_EMU on X86_32.
In reply to Cody P SchaferNUMA_EMU depends on NUMA.
NUMA depends on (X86_64 || (X86_32 && ( list of extended platforms))).
This forced enabling an extended platform when using numa emulation on x86_32, which is silly.
Remoing the list of extended platforms (plus EXPERIMENTAL) results in NUMA depending on X86_64 || X86_32, so simply remove all dependencies from (except SMP) from NUMA.
--- arch/x86/Kconfig | 1 - 1 file changed, 1 deletion(-)
arch/x86/Kconfig +0 -1
@@ -1228,7 +1228,6 @@ config DIRECT_GBPAGES config NUMA bool "Numa Memory Allocation and Scheduler Support" depends on SMP - depends on X86_64 || (X86_32 && HIGHMEM64G && (X86_NUMAQ || X86_BIGSMP || X86_SUMMIT && ACPI)) default y if (X86_NUMAQ || X86_SUMMIT || X86_BIGSMP) ---help--- Enable NUMA (Non Uniform Memory Access) support.
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 3/24 XXX: memory_hotplug locking note in online_pages.
In reply to Cody P Schafer--- mm/memory_hotplug.c | 1 + 1 file changed, 1 insertion(+)
mm/memory_hotplug.c +1 -0
@@ -984,6 +984,7 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ zone->managed_pages += onlined_pages; zone->present_pages += onlined_pages; +/* FIXME: should be protected by pgdat_resize_lock() */ zone->zone_pgdat->node_present_pages += onlined_pages;if (onlined_pages) {node_states_set_node (zone_to_nid (zone), &arg);
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 4/24 rbtree: add postorder iteration functions.
In reply to Cody P SchaferAdd postorder iteration functions for rbtree. These are useful for safely freeing an entire rbtree without modifying the tree at all.
--- include/linux/rbtree.h | 4 ++++ lib/rbtree.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+)
include/linux/rbtree.h +4 -0
@@ -68,6 +68,10 @@ extern struct rb_node *rb_prev(const struct rb_node *);extern struct rb_node *rb_first (const struct rb_root *);extern struct rb_node *rb_last (const struct rb_root *); +/* Postorder iteration - always visit the parent after it's children */ +extern struct rb_node *rb_first_postorder (const struct rb_root *); +extern struct rb_node *rb_next_postorder (const struct rb_node *); +/* Fast replacement of a single node without remove/rebalance/add/rebalance */ extern void rb_replace_node (struct rb_node *victim,struct rb_node *new,struct rb_root *root);
lib/rbtree.c +40 -0
@@ -518,3 +518,43 @@ void rb_replace_node(struct rb_node *victim, struct rb_node *new, *new = *victim; }EXPORT_SYMBOL (rb_replace_node); + +static struct rb_node *rb_left_deepest_node (const struct rb_node *node) +{ +for (;;) { +if (node->rb_left) + node = node->rb_left; +else if (node->rb_right) + node = node->rb_right; +else +return (struct rb_node *)node; + } +} + +struct rb_node *rb_next_postorder (const struct rb_node *node) +{ +const struct rb_node *parent; +if (!node) +return NULL ; + parent =rb_parent (node); + +/* If we're sitting on node, we've already seen our children */ +if (parent && node == parent->rb_left && parent->rb_right) { +/* If we are the parent's left node, go to the parent's right +* node then all the way down to the left */ +return rb_left_deepest_node (parent->rb_right); + }else +/* Otherwise we are the parent's right node, and the parent +* should be next */ +return (struct rb_node *)parent; +} +EXPORT_SYMBOL (rb_next_postorder); + +struct rb_node *rb_first_postorder (const struct rb_root *root) +{ +if (!root->rb_node) +return NULL ; + +return rb_left_deepest_node (root->rb_node); +} +EXPORT_SYMBOL (rb_first_postorder);
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 5/24 rbtree: add rbtree_postorder_for_each_entry_safe() helper.
In reply to Cody P Schafer--- include/linux/rbtree.h | 7 +++++++ 1 file changed, 7 insertions(+)
include/linux/rbtree.h +7 -0
@@ -85,4 +85,11 @@ static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, *rb_link = node; } +#define rbtree_postorder_for_each_entry_safe (pos, n, root, field) \ + for (pos = rb_entry(rb_first_postorder(root), typeof(*pos), field), \ + n = rb_entry(rb_next_postorder(&pos->field), typeof(*pos), field); \ + &pos->field; \ + pos = n, \ + n = rb_entry(rb_next_postorder(&pos->field), typeof(*pos), field)) +#endif /* _LINUX_RBTREE_H */
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 6/24 mm/memory_hotplug: factor out zone+pgdat growth.
In reply to Cody P SchaferCreate a new function grow_pgdat_and_zone() which handles locking + growth of a zone & the pgdat which it is associated with.
--- include/linux/memory_hotplug.h | 3 +++ mm/memory_hotplug.c | 17 +++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-)
include/linux/memory_hotplug.h +3 -0
@@ -78,6 +78,9 @@ static inline void zone_seqlock_init(struct zone *zone) {seqlock_init (&zone->span_seqlock); } +extern void grow_pgdat_and_zone (struct zone *zone,unsigned long start_pfn, +unsigned long end_pfn); +extern int zone_grow_free_lists (struct zone *zone,unsigned long new_nr_pages);extern int zone_grow_waitqueues (struct zone *zone,unsigned long nr_pages);extern int add_one_highpage (struct page *page,int pfn,int bad_ppro);
mm/memory_hotplug.c +11 -6
@@ -390,13 +390,22 @@ static void grow_pgdat_span(struct pglist_data *pgdat, unsigned long start_pfn, pgdat->node_start_pfn; } +void grow_pgdat_and_zone (struct zone *zone,unsigned long start_pfn, +unsigned long end_pfn) +{ +unsigned long flags; +pgdat_resize_lock (zone->zone_pgdat, &flags); +grow_zone_span (zone, start_pfn, end_pfn); +grow_pgdat_span (zone->zone_pgdat, start_pfn, end_pfn); +pgdat_resize_unlock (zone->zone_pgdat, &flags); +} +static int __meminit __add_zone (struct zone *zone,unsigned long phys_start_pfn) {struct pglist_data *pgdat = zone->zone_pgdat;int nr_pages =PAGES_PER_SECTION ;int nid = pgdat->node_id;int zone_type; -unsigned long flags;int ret; zone_type = zone - pgdat->node_zones;
@@ -404,11 +413,7 @@ static int __meminit __add_zone(struct zone *zone, unsigned long phys_start_pfn)if (ret)return ret; -pgdat_resize_lock (zone->zone_pgdat, &flags); -grow_zone_span (zone, phys_start_pfn, phys_start_pfn + nr_pages); -grow_pgdat_span (zone->zone_pgdat, phys_start_pfn, - phys_start_pfn + nr_pages); -pgdat_resize_unlock (zone->zone_pgdat, &flags); +grow_pgdat_and_zone (zone, phys_start_pfn, phys_start_pfn + nr_pages);memmap_init_zone (nr_pages, nid, zone_type, phys_start_pfn,MEMMAP_HOTPLUG );return 0 ;
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 7/24 memory_hotplug: export ensure_zone_is_initialized() in mm/internal.h
In reply to Cody P SchaferExport ensure_zone_is_initialized() so that it can be used to initialize new zones within the dynamic numa code.
--- mm/internal.h | 8 ++++++++ mm/memory_hotplug.c | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-)
mm/internal.h +8 -0
@@ -105,6 +105,14 @@ extern void prep_compound_page(struct page *page, unsigned long order);extern bool is_free_buddy_page (struct page *page);#endif +#ifdef CONFIG_MEMORY_HOTPLUG +/* +* in mm/memory_hotplug.c +*/ +extern int ensure_zone_is_initialized (struct zone *zone, +unsigned long start_pfn,unsigned long num_pages); +#endif +#if definedCONFIG_COMPACTION || definedCONFIG_CMA /*
mm/memory_hotplug.c +1 -1
@@ -284,7 +284,7 @@ static void fix_zone_id(struct zone *zone, unsigned long start_pfn,/* Can fail with -ENOMEM from allocating a wait table with vmalloc() or * alloc_bootmem_node_nopanic() */ -static int __ref ensure_zone_is_initialized (struct zone *zone, +int __ref ensure_zone_is_initialized (struct zone *zone,unsigned long start_pfn,unsigned long num_pages) {if (!zone_is_initialized (zone))
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 8/24 mm/memory_hotplug: use {pgdat,zone}_is_empty() when resizing zones & pgdats
In reply to Cody P SchaferUse the *_is_empty() helpers to be more clear about what we're actually checking for.
--- mm/memory_hotplug.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
mm/memory_hotplug.c +2 -2
@@ -242,7 +242,7 @@ static void grow_zone_span(struct zone *zone, unsigned long start_pfn,zone_span_writelock (zone); old_zone_end_pfn = zone->zone_start_pfn + zone->spanned_pages; -if (!zone->spanned_pages || start_pfn < zone->zone_start_pfn) +if (zone_is_empty (zone) || start_pfn < zone->zone_start_pfn) zone->zone_start_pfn = start_pfn; zone->spanned_pages = max(old_zone_end_pfn, end_pfn) -
@@ -383,7 +383,7 @@ static void grow_pgdat_span(struct pglist_data *pgdat, unsigned long start_pfn,unsigned long old_pgdat_end_pfn = pgdat->node_start_pfn + pgdat->node_spanned_pages; -if (!pgdat->node_spanned_pages || start_pfn < pgdat->node_start_pfn) +if (pgdat_is_empty (pgdat) || start_pfn < pgdat->node_start_pfn) pgdat->node_start_pfn = start_pfn; pgdat->node_spanned_pages = max(old_pgdat_end_pfn, end_pfn) -
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 9/24 mm: add nid_zone() helper
In reply to Cody P SchaferAdd nid_zone(), which returns the zone corresponding to a given nid & zonenum.
--- include/linux/mm.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
include/linux/mm.h +6 -1
@@ -707,9 +707,14 @@ static inline void page_nid_reset_last(struct page *page) }#endif +static inline struct zone *nid_zone (int nid,enum zone_type zonenum) +{ +return &NODE_DATA (nid)->node_zones[zonenum]; +} +static inline struct zone *page_zone (const struct page *page) { -return &NODE_DATA (page_to_nid (page))->node_zones[page_zonenum (page)]; +return nid_zone (page_to_nid (page),page_zonenum (page)); }#ifdef SECTION_IN_PAGE_FLAGS
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 10/24 page_alloc: add return_pages_to_zone() when DYNAMIC_NUMA is enabled.
In reply to Cody P SchaferAdd return_pages_to_zone(), which uses return_page_to_zone(). It is a minimized version of __free_pages_ok() which handles adding pages which have been removed from another zone into a new zone.
--- mm/internal.h | 5 ++++- mm/page_alloc.c | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-)
mm/internal.h +4 -1
@@ -104,6 +104,10 @@ extern void prep_compound_page(struct page *page, unsigned long order);#ifdef CONFIG_MEMORY_FAILURE extern bool is_free_buddy_page (struct page *page);#endif +#ifdef CONFIG_DYNAMIC_NUMA +void return_pages_to_zone (struct page *page,unsigned int order, +struct zone *zone); +#endif #ifdef CONFIG_MEMORY_HOTPLUG /*
@@ -114,7 +118,6 @@ extern int ensure_zone_is_initialized(struct zone *zone,#endif #if definedCONFIG_COMPACTION || definedCONFIG_CMA -/* * in mm/compaction.c */
mm/page_alloc.c +17 -0
@@ -449,6 +449,12 @@ static inline void set_page_order(struct page *page, int order)__SetPageBuddy (page); } +static inline void set_free_page_order (struct page *page,int order) +{ +set_page_private (page, order); +VM_BUG_ON (!PageBuddy (page)); +} +static inline void rmv_page_order (struct page *page) {__ClearPageBuddy (page);
@@ -745,6 +751,17 @@ static void __free_pages_ok(struct page *page, unsigned int order)local_irq_restore (flags); } +#ifdef CONFIG_DYNAMIC_NUMA +void return_pages_to_zone (struct page *page,unsigned int order, +struct zone *zone) +{ +unsigned long flags; +local_irq_save (flags); +free_one_page (zone, page, order,get_freepage_migratetype (page)); +local_irq_restore (flags); +} +#endif +/* * Read access to zone->managed_pages is safe because it's unsigned long, * but we still need to serialize writers. Currently all callers of
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 11/24 page_alloc: in move_freepages(), skip pages instead of VM_BUG on node differences.
With dynamic numa, pages are going to be gradully moved from one node to another, causing the page ranges that move_freepages() examines to contain pages that actually belong to another node.
When dynamic numa is enabled, we skip these pages instead of VM_BUGing out on them.
This additionally moves the VM_BUG_ON() (which detects a change in node) so that it follows the pfn_valid_within() check.
--- mm/page_alloc.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
mm/page_alloc.c +14 -3
@@ -964,6 +964,7 @@ int move_freepages(struct zone *zone,struct page *page;unsigned long order;int pages_moved =0 ; +int zone_nid =zone_to_nid (zone);#ifndef CONFIG_HOLES_IN_ZONE /*
@@ -977,14 +978,24 @@ int move_freepages(struct zone *zone,#endif for (page = start_page; page <= end_page;) { -/* Make sure we are not inadvertently changing nodes */ -VM_BUG_ON (page_to_nid (page) !=zone_to_nid (zone)); -if (!pfn_valid_within (page_to_pfn (page))) { page++;continue ; } +if (page_to_nid (page) != zone_nid) { +#ifndef CONFIG_DYNAMIC_NUMA +/* +* In the normal case (without Dynamic NUMA), all pages +* in a pageblock should belong to the same zone (and +* as a result all have the same nid). +*/ +VM_BUG_ON (page_to_nid (page) != zone_nid); +#endif + page++; +continue ; + } +if (!PageBuddy (page)) { page++;continue ;
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 12/24 page_alloc: when dynamic numa is enabled, don't check that all pages in a block belong to the same zone
In reply to Cody P SchaferWhen dynamic numa is enabled, the last or first page in a pageblock may have been transplanted to a new zone (or may not yet be transplanted to a new zone).
Disable a BUG_ON() which checks that the start_page and end_page are in the same zone, if they are not in the proper zone they will simply be skipped.
--- mm/page_alloc.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
mm/page_alloc.c +9 -6
@@ -966,13 +966,16 @@ int move_freepages(struct zone *zone,int pages_moved =0 ;int zone_nid =zone_to_nid (zone); -#ifndef CONFIG_HOLES_IN_ZONE +#if !defined(CONFIG_HOLES_IN_ZONE ) && !defined(CONFIG_DYNAMIC_NUMA )/* -* page_zone is not safe to call in this context when -* CONFIG_HOLES_IN_ZONE is set. This bug check is probably redundant -* anyway as we check zone boundaries in move_freepages_block(). -* Remove at a later date when no bug reports exist related to -* grouping pages by mobility +* With CONFIG_HOLES_IN_ZONE set, this check is unsafe as start_page or +* end_page may not be "valid". +* With CONFIG_DYNAMIC_NUMA set, this condition is a valid occurence & +* not a bug. +* +* This bug check is probably redundant anyway as we check zone +* boundaries in move_freepages_block(). Remove at a later date when +* no bug reports exist related to grouping pages by mobility */ BUG_ON (page_zone (start_page) !=page_zone (end_page));#endif
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 13/24 page-flags dnuma: reserve a pageflag for determining if a page needs a node lookup.
In reply to Cody P SchaferAdd a pageflag called "lookup_node"/ PG_lookup_node / Page*LookupNode().
Used by dynamic numa to indicate when a page has a new node assignment waiting for it.
--- include/linux/page-flags.h | 18 ++++++++++++++++++ mm/page_alloc.c | 3 +++ 2 files changed, 21 insertions(+)
include/linux/page-flags.h +18 -0
@@ -109,6 +109,9 @@ enum pageflags {#ifdef CONFIG_TRANSPARENT_HUGEPAGE PG_compound_lock,#endif +#ifdef CONFIG_DYNAMIC_NUMA + PG_lookup_node,/* need to do an extra lookup to determine actual node */ +#endif __NR_PAGEFLAGS,/* Filesystems */
@@ -275,6 +278,17 @@ PAGEFLAG_FALSE(HWPoison)#define __PG_HWPOISON 0#endif +/* Setting is unconditional, simply leads to an extra lookup. +* Clearing must be conditional so we don't miss any memlayout changes. +*/ +#ifdef CONFIG_DYNAMIC_NUMA +PAGEFLAG(LookupNode, lookup_node) +TESTCLEARFLAG(LookupNode, lookup_node) +#else +PAGEFLAG_FALSE(LookupNode) +TESTCLEARFLAG_FALSE(LookupNode) +#endif + u64 stable_page_flags(struct page *page); static inline int PageUptodate(struct page *page)
@@ -509,7 +523,11 @@ static inline void ClearPageSlabPfmemalloc(struct page *page)* Pages being prepped should not have any flags set. It they are set, * there has been a kernel bug or struct page corruption. */ +#ifndef CONFIG_DYNAMIC_NUMA #define PAGE_FLAGS_CHECK_AT_PREP ((1 << NR_PAGEFLAGS) - 1) +#else +#define PAGE_FLAGS_CHECK_AT_PREP (((1 << NR_PAGEFLAGS) - 1) & ~(1 << PG_lookup_node)) +#endif #define PAGE_FLAGS_PRIVATE \ (1 << PG_private | 1 << PG_private_2)
mm/page_alloc.c +3 -0
@@ -6449,6 +6449,9 @@ static const struct trace_print_flags pageflag_names[] = { #ifdef CONFIG_TRANSPARENT_HUGEPAGE {1UL << PG_compound_lock, "compound_lock" }, #endif +#ifdef CONFIG_DYNAMIC_NUMA + {1UL << PG_lookup_node, "lookup_node" }, +#endif }; static void dump_page_flags(unsigned long flags)
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 14/24 memory_hotplug: factor out locks in mem_online_cpu()
In reply to Cody P SchaferIn dynamic numa, when onlining nodes, lock_memory_hotplug() is already held when mem_online_node()'s functionality is needed.
Factor out the locking and create a new function __mem_online_node() to allow reuse.
--- include/linux/memory_hotplug.h | 1 + mm/memory_hotplug.c | 29 ++++++++++++++++------------- 2 files changed, 17 insertions(+), 13 deletions(-)
include/linux/memory_hotplug.h +1 -0
@@ -248,6 +248,7 @@ static inline int is_mem_section_removable(unsigned long pfn,static inline void try_offline_node (int nid) {}#endif /* CONFIG_MEMORY_HOTREMOVE */ +extern int __mem_online_node (int nid);extern int mem_online_node (int nid);extern int add_memory (int nid,u64 start,u64 size);extern int arch_add_memory (int nid,u64 start,u64 size);
mm/memory_hotplug.c +16 -13
@@ -1058,26 +1058,29 @@ static void rollback_node_hotadd(int nid, pg_data_t *pgdat)return ; } - -/* -* called by cpu_up() to online a node without onlined memory. -*/ -int mem_online_node (int nid) +int __mem_online_node (int nid) { -pg_data_t *pgdat; -int ret; +pg_data_t *pgdat; +int ret; -lock_memory_hotplug (); pgdat =hotadd_new_pgdat (nid,0 ); -if (!pgdat) { - ret = -ENOMEM ; - goto out; - } +if (!pgdat) +return -ENOMEM ; +node_set_online (nid); ret =register_one_node (nid);BUG_ON (ret); +return ret; +} -out: +/* +* called by cpu_up() to online a node without onlined memory. +*/ +int mem_online_node (int nid) +{ +int ret; +lock_memory_hotplug (); + ret =__mem_online_node (nid);unlock_memory_hotplug ();return ret; }
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 15/24 mm: add memlayout & dnuma to track pfn->nid & transplant pages between nodes
In reply to Cody P SchaferOn certain systems, the hypervisor can (and will) relocate physical addresses as seen in a VM between real NUMA nodes. For example, IBM's Power systems which are using PHYP (their proprietary hypervisor).
This change set introduces the infrastructure for tracking & dynamically changing "memory layouts" (or "memlayouts"): the mapping between page ranges & the actual backing NUMA node.
A memlayout is an rbtree which maps pfns (really, ranges of pfns) to a node. This mapping (combined with the LookupNode pageflag) is used to "transplant" (move pages between nodes) pages when they are freed back to the page allocator.
Additionally, when a new memlayout is commited the currently free pages that are now in the wrong zone's freelist are immidiately transplanted.
--- include/linux/dnuma.h | 96 +++++++++++++ include/linux/memlayout.h | 110 +++++++++++++++ mm/Kconfig | 19 +++ mm/Makefile | 1 + mm/dnuma.c | 349 ++++++++++++++++++++++++++++++++++++++++++++++ mm/memlayout.c | 238 +++++++++++++++++++++++++++++++ 6 files changed, 813 insertions(+) create mode 100644 include/linux/dnuma.h create mode 100644 include/linux/memlayout.h create mode 100644 mm/dnuma.c create mode 100644 mm/memlayout.c
include/linux/dnuma.h new file +96 -0
@@ -0,0 +1,96 @@ +#ifndef LINUX_DNUMA_H_ +#define LINUX_DNUMA_H_ + +#include <linux/mm.h> +#include <linux/mmzone.h> +#include <linux/memlayout.h> +#include <linux/spinlock.h> +#include <linux/atomic.h> + +#ifdef CONFIG_DYNAMIC_NUMA +/* Must be called _before_ setting a new_ml to the pfn_to_node_map */ +void dnuma_online_required_nodes_and_zones (struct memlayout *new_ml); + +/* Must be called _after_ setting a new_ml to the pfn_to_node_map */ +void dnuma_move_free_pages (struct memlayout *new_ml); +void dnuma_mark_page_range (struct memlayout *new_ml); + +static inline bool dnuma_is_active (void ) +{ +struct memlayout *ml; +bool ret; + +rcu_read_lock (); + ml =rcu_dereference (pfn_to_node_map); + ret = ml && (ml->type !=ML_INITIAL ); +rcu_read_unlock (); + +return ret; +} + +static inline bool dnuma_has_memlayout (void ) +{ +return !!rcu_access_pointer (pfn_to_node_map); +} + +static inline int dnuma_page_needs_move (struct page *page) +{ +int new_nid, old_nid; + +if (!TestClearPageLookupNode (page)) +return NUMA_NO_NODE ; + +/* FIXME: this does rcu_lock, deref, unlock */ +if (WARN_ON (!dnuma_is_active ())) +return NUMA_NO_NODE ; + +/* FIXME: and so does this (rcu lock, deref, and unlock) */ + new_nid =memlayout_pfn_to_nid (page_to_pfn (page)); + old_nid =page_to_nid (page); + +if (new_nid ==NUMA_NO_NODE ) { +pr_alert ("dnuma: pfn %05lx has moved from node %d to a non-memlayout range.\n" , +page_to_pfn (page), old_nid); +return NUMA_NO_NODE ; + } + +if (new_nid == old_nid) +return NUMA_NO_NODE ; + +if (WARN_ON (!zone_is_initialized (nid_zone (new_nid,page_zonenum (page))))) +return NUMA_NO_NODE ; + +return new_nid; +} + +void dnuma_post_free_to_new_zone (struct page *page,int order); +void dnuma_prior_free_to_new_zone (struct page *page,int order, +struct zone *dest_zone, +int dest_nid); + +#else /* !defined CONFIG_DYNAMIC_NUMA */ + +static inline bool dnuma_is_active (void ) +{ +return false; +} + +static inline void dnuma_prior_free_to_new_zone (struct page *page,int order, +struct zone *dest_zone, +int dest_nid) +{ +BUG (); +} + +static inline void dnuma_post_free_to_new_zone (struct page *page,int order) +{ +BUG (); +} + +static inline int dnuma_page_needs_move (struct page *page) +{ +return NUMA_NO_NODE ; +} +#endif /* !defined CONFIG_DYNAMIC_NUMA */ + +#endif /* defined LINUX_DNUMA_H_ */
include/linux/memlayout.h new file +110 -0
110 lines not on this page · raw message
mm/Kconfig +19 -0
@@ -169,6 +169,25 @@ config MOVABLE_NODE config HAVE_BOOTMEM_INFO_NODE def_bool n +config DYNAMIC_NUMA + bool "Dynamic Numa: Allow NUMA layout to change after boot time" + depends on NUMA + depends on !DISCONTIGMEM + depends on MEMORY_HOTPLUG # locking + mem_online_node(). + help + Dynamic Numa (DNUMA) allows the movement of pages between NUMA nodes at + run time. + + Typically, this is used on systems running under a hypervisor which + may move the running VM based on the hypervisors needs. On such a + system, this config option enables Linux to update it's knowledge of + the memory layout. + + If the feature is not used but is enabled, there is a small amount of overhead (an + additional pointer NULL check) added to all page frees. + + Choose Y if you have one of these systems (XXX: which ones?), otherwise choose N. + # eventually, we can have this option just 'select SPARSEMEM' config MEMORY_HOTPLUG bool "Allow for memory hot-add"
mm/Makefile +1 -0
@@ -58,3 +58,4 @@ obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o obj-$(CONFIG_CLEANCACHE) += cleancache.o obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o +obj-$(CONFIG_DYNAMIC_NUMA) += dnuma.o memlayout.o
mm/dnuma.c new file +349 -0
349 lines not on this page · raw message
mm/memlayout.c new file +238 -0
238 lines not on this page · raw message
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 16/24 mm: memlayout+dnuma: add debugfs interface
In reply to Cody P SchaferAdd a debugfs interface to dnuma/memlayout. It keeps track of a variable backlog of memory layouts, provides some statistics on dnuma moved pages & cache performance, and allows the setting of a new global memlayout.
TODO: split out statistics, backlog, & write interfaces from eachother.
--- include/linux/memlayout.h | 1 + mm/Kconfig | 25 ++++ mm/Makefile | 1 + mm/dnuma.c | 2 + mm/memlayout-debugfs.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++ mm/memlayout-debugfs.h | 35 +++++ mm/memlayout.c | 17 ++- 7 files changed, 402 insertions(+), 2 deletions(-) create mode 100644 mm/memlayout-debugfs.c create mode 100644 mm/memlayout-debugfs.h
include/linux/memlayout.h +1 -0
@@ -53,6 +53,7 @@ struct memlayout { };extern __rcu struct memlayout *pfn_to_node_map; +extern struct mutex memlayout_lock;/* update-side lock */ /* FIXME: overflow potential in completion check */ #define ml_for_each_pfn_in_range (rme, pfn) \
mm/Kconfig +25 -0
@@ -188,6 +188,31 @@ config DYNAMIC_NUMA Choose Y if you have one of these systems (XXX: which ones?), otherwise choose N. +config DNUMA_DEBUGFS + bool "Export DNUMA & memlayout internals via debugfs" + depends on DYNAMIC_NUMA + help + Provides + +config DNUMA_BACKLOG + int "Number of old memlayouts to keep (0 = None, -1 = unlimited)" + depends on DNUMA_DEBUGFS + help + Allows access to old memory layouts & statistics in debugfs. + + Each memlayout will consume some memory, and when set to -1 + (unlimited), this can result in unbounded kernel memory use. + +config DNUMA_DEBUGFS_WRITE + bool "Change NUMA layout via debugfs" + depends on DNUMA_DEBUGFS + help + Enable the use of <debugfs>/memlayout/{start,end,node,commit} + + Write a PFN to 'start' & 'end', then a node id to 'node'. + Repeat this until you are satisfied with your memory layout, then + write '1' to 'commit'. + # eventually, we can have this option just 'select SPARSEMEM' config MEMORY_HOTPLUG bool "Allow for memory hot-add"
mm/Makefile +1 -0
@@ -59,3 +59,4 @@ obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o obj-$(CONFIG_CLEANCACHE) += cleancache.o obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o obj-$(CONFIG_DYNAMIC_NUMA) += dnuma.o memlayout.o +obj-$(CONFIG_DNUMA_DEBUGFS) += memlayout-debugfs.o
mm/dnuma.c +2 -0
@@ -9,6 +9,7 @@#include <linux/memory.h> #include "internal.h" +#include "memlayout-debugfs.h" /* Issues due to pageflag_blocks attached to zones with Discontig Mem (& * Flatmem??).
@@ -140,6 +141,7 @@ static void node_states_set_node(int node, struct memory_notify *arg)void dnuma_post_free_to_new_zone (struct page *page,int order) {adjust_zone_present_pages (page_zone (page), (1 << order)); +ml_stat_count_moved_pages (order); } static void dnuma_prior_return_to_new_zone(struct page *page, int order,
mm/memlayout-debugfs.c new file +323 -0
323 lines not on this page · raw message
mm/memlayout-debugfs.h new file +35 -0
@@ -0,0 +1,35 @@ +#ifndef LINUX_MM_MEMLAYOUT_DEBUGFS_H_ +#define LINUX_MM_MEMLAYOUT_DEBUGFS_H_ + +#include <linux/memlayout.h> + +void ml_backlog_feed (struct memlayout *ml); + +#ifdef CONFIG_DNUMA_DEBUGFS +void ml_stat_count_moved_pages (int order); +void ml_stat_cache_hit (void ); +void ml_stat_cache_miss (void ); +void ml_dbgfs_init (struct memlayout *ml); +void ml_dbgfs_create_range (struct memlayout *ml,struct rangemap_entry *rme); +void ml_destroy_dbgfs (struct memlayout *ml); +void ml_dbgfs_set_current (struct memlayout *ml); + +#else /* !defined(CONFIG_DNUMA_DEBUGFS) */ +static inline void ml_stat_count_moved_pages (int order) +{} +static inline void ml_stat_cache_hit (void ) +{} +static inline void ml_stat_cache_miss (void ) +{} + +static inline void ml_dbgfs_init (struct memlayout *ml) +{} +static inline void ml_dbgfs_create_range (struct memlayout *ml,struct rangemap_entry *rme) +{} +static inline void ml_destroy_dbgfs (struct memlayout *ml) +{} +static inline void ml_dbgfs_set_current (struct memlayout *ml) +{} +#endif + +#endif
mm/memlayout.c +15 -2
@@ -14,6 +14,8 @@#include <linux/rcupdate.h> #include <linux/slab.h> +#include "memlayout-debugfs.h" +/* protected by memlayout_lock */ __rcu struct memlayout *pfn_to_node_map;DEFINE_MUTEX (memlayout_lock);
@@ -88,6 +90,8 @@ int memlayout_new_range(struct memlayout *ml, unsigned long pfn_start,rb_link_node (&rme->node, parent, new);rb_insert_color (&rme->node, &ml->root); + +ml_dbgfs_create_range (ml, rme);return 0 ; }
@@ -109,9 +113,12 @@ int memlayout_pfn_to_nid(unsigned long pfn) rme =ACCESS_ONCE (ml->cache);if (rme &&rme_bounds_pfn (rme, pfn)) {rcu_read_unlock (); +ml_stat_cache_hit ();return rme->nid; } +ml_stat_cache_miss (); + node = ml->root.rb_node;while (node) {struct rangemap_entry *rme =rb_entry (node,typeof (*rme), node);
@@ -140,6 +147,7 @@ out:void memlayout_destroy (struct memlayout *ml) { +ml_destroy_dbgfs (ml);ml_destroy_mem (ml); }
@@ -158,6 +166,7 @@ struct memlayout *memlayout_create(enum memlayout_type type) ml->type = type; ml->cache =NULL ; +ml_dbgfs_init (ml);return ml; }
@@ -167,11 +176,12 @@ void memlayout_commit(struct memlayout *ml)if (ml->type ==ML_INITIAL ) {if (WARN (dnuma_has_memlayout (),"memlayout marked first is not first, ignoring.\n" )) { -memlayout_destroy (ml); +ml_backlog_feed (ml);return ; }mutex_lock (&memlayout_lock); +ml_dbgfs_set_current (ml);rcu_assign_pointer (pfn_to_node_map, ml);mutex_unlock (&memlayout_lock);return ;
@@ -182,13 +192,16 @@ void memlayout_commit(struct memlayout *ml)unlock_memory_hotplug ();mutex_lock (&memlayout_lock); + +ml_dbgfs_set_current (ml); + old_ml =rcu_dereference_protected (pfn_to_node_map,mutex_is_locked (&memlayout_lock));rcu_assign_pointer (pfn_to_node_map, ml);synchronize_rcu (); -memlayout_destroy (old_ml); +ml_backlog_feed (old_ml);/* Must be called only after the new value for pfn_to_node_map has * propogated to all tasks, otherwise some pages may lookup the old
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 17/24 page_alloc: use dnuma to transplant newly freed pages in __free_pages_ok()
In reply to Cody P Schafer__free_pages_ok() handles higher order (order != 0) pages. Transplant hook is added here as this is where the struct zone to free to is decided.
--- mm/page_alloc.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
mm/page_alloc.c +13 -1
@@ -59,6 +59,7 @@#include <linux/migrate.h> #include <linux/page-debug-flags.h> #include <linux/sched/rt.h> +#include <linux/dnuma.h> #include <asm/tlbflush.h> #include <asm/div64.h>
@@ -739,6 +740,13 @@ static void __free_pages_ok(struct page *page, unsigned int order) {unsigned long flags;int migratetype; +int dest_nid =dnuma_page_needs_move (page); +struct zone *zone; + +if (dest_nid !=NUMA_NO_NODE ) + zone =nid_zone (dest_nid,page_zonenum (page)); +else + zone =page_zone (page);if (!free_pages_prepare (page, order))return ;
@@ -747,7 +755,11 @@ static void __free_pages_ok(struct page *page, unsigned int order)__count_vm_events (PGFREE ,1 << order); migratetype =get_pageblock_migratetype (page);set_freepage_migratetype (page, migratetype); -free_one_page (page_zone (page), page, order, migratetype); +if (dest_nid !=NUMA_NO_NODE ) +dnuma_prior_free_to_new_zone (page, order, zone, dest_nid); +free_one_page (zone, page, order, migratetype); +if (dest_nid !=NUMA_NO_NODE ) +dnuma_post_free_to_new_zone (page, order);local_irq_restore (flags); }
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 18/24 page_alloc: use dnuma to transplant newly freed pages in free_hot_cold_page()
In reply to Cody P Schaferfree_hot_cold_page() is used for order == 0 pages, and is where the page's zone is decided.
In the normal case, these pages are freed to the per-cpu lists. When a page needs transplanting (ie: the actual node it belongs to has changed, and it needs to be moved to another zone), the pcp lists are skipped & the page is freed via free_one_page().
--- mm/page_alloc.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
mm/page_alloc.c +10 -0
@@ -1364,6 +1364,7 @@ void mark_free_pages(struct zone *zone)*/ void free_hot_cold_page (struct page *page,int cold) { +int dest_nid;struct zone *zone =page_zone (page);struct per_cpu_pages *pcp;unsigned long flags;
@@ -1377,6 +1378,15 @@ void free_hot_cold_page(struct page *page, int cold)local_irq_save (flags);__count_vm_event (PGFREE ); + dest_nid =dnuma_page_needs_move (page); +if (dest_nid !=NUMA_NO_NODE ) { +struct zone *dest_zone =nid_zone (dest_nid,page_zonenum (page)); +dnuma_prior_free_to_new_zone (page,0 , dest_zone, dest_nid); +free_one_page (dest_zone, page,0 , migratetype); +dnuma_post_free_to_new_zone (page,0 ); + goto out; + } +/* * We only track unmovable, reclaimable and movable on pcp lists. * Free ISOLATE pages back to the allocator because they are being
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 19/24 page_alloc: transplant pages that are being flushed from the per-cpu lists
In reply to Cody P SchaferIn free_pcppages_bulk(), check if a page needs to be moved to a new node/zone & then perform the transplant (in a slightly defered manner).
--- mm/page_alloc.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-)
mm/page_alloc.c +35 -1
@@ -650,13 +650,14 @@ static void free_pcppages_bulk(struct zone *zone, int count,int migratetype =0 ;int batch_free =0 ;int to_free = count; +struct page *pos, *page; +LIST_HEAD (need_move);spin_lock (&zone->lock); zone->all_unreclaimable =0 ; zone->pages_scanned =0 ;while (to_free) { -struct page *page;struct list_head *list;/*
@@ -679,11 +680,23 @@ static void free_pcppages_bulk(struct zone *zone, int count, do { int mt; /* migratetype of the to-be-freed page */ + int dest_nid; page = list_entry(list->prev, struct page, lru); /* must delete as __free_one_page list manipulates */ list_del(&page->lru); mt = get_freepage_migratetype(page); + + dest_nid = dnuma_page_needs_move(page); + if (dest_nid != NUMA_NO_NODE) { + dnuma_prior_free_to_new_zone(page, 0, + nid_zone(dest_nid, + page_zonenum(page)), + dest_nid); + list_add(&page->lru, &need_move); + continue; + } + /* MIGRATE_MOVABLE list may include MIGRATE_RESERVEs */ __free_one_page(page, zone, 0, mt); trace_mm_page_pcpu_drain(page, 0, mt);
@@ -695,6 +708,27 @@ static void free_pcppages_bulk(struct zone *zone, int count, }while (--to_free && --batch_free && !list_empty (list)); }spin_unlock (&zone->lock); + +list_for_each_entry_safe (page, pos, &need_move, lru) { +struct zone *dest_zone =page_zone (page); +int mt; + +spin_lock (&dest_zone->lock); + +VM_BUG_ON (dest_zone !=page_zone (page)); +pr_devel ("freeing pcp page %pK with changed node\n" , page); +list_del (&page->lru); + mt =get_freepage_migratetype (page); +__free_one_page (page, dest_zone,0 , mt); +trace_mm_page_pcpu_drain (page,0 , mt); + +/* XXX: fold into "post_free_to_new_zone()" ? */ +if (is_migrate_cma (mt)) +__mod_zone_page_state (dest_zone,NR_FREE_CMA_PAGES ,1 ); +dnuma_post_free_to_new_zone (page,0 ); + +spin_unlock (&dest_zone->lock); + } } static void free_one_page(struct zone *zone, struct page *page, int order,
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 20/24 x86: memlayout: add a arch specific inital memlayout setter.
In reply to Cody P SchaferOn x86, we have numa_info specifically to track the numa layout, which is precisely the data memlayout needs, so use it to create an initial memlayout.
--- arch/x86/mm/numa.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
arch/x86/mm/numa.c +24 -0
@@ -11,6 +11,7 @@#include <linux/nodemask.h> #include <linux/sched.h> #include <linux/topology.h> +#include <linux/dnuma.h> #include <asm/e820.h> #include <asm/proto.h>
@@ -32,6 +33,29 @@ __initdata#endif ; +#ifdef CONFIG_DYNAMIC_NUMA +void __init memlayout_global_init (void ) +{ +struct numa_meminfo *mi = &numa_meminfo; +int i; +struct numa_memblk *blk; +struct memlayout *ml =memlayout_create (ML_INITIAL ); +if (WARN_ON (!ml)) +return ; + +pr_devel ("x86/memlayout: adding ranges from numa_meminfo\n" ); +for (i =0 ; i < mi->nr_blks; i++) { + blk = mi->blk + i; +pr_devel (" adding range {%LX[%LX]-%LX[%LX]}:%d\n" , +PFN_DOWN (blk->start), blk->start,PFN_DOWN (blk->end -PAGE_SIZE /2 -1 ), blk->end -1 , blk->nid); +memlayout_new_range (ml,PFN_DOWN (blk->start),PFN_DOWN (blk->end -PAGE_SIZE /2 -1 ), blk->nid); + } +pr_devel (" done adding ranges from numa_meminfo\n" ); + +memlayout_commit (ml); +} +#endif +static int numa_distance_cnt;static u8 *numa_distance;
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 21/24 init/main: call memlayout_global_init() in start_kernel().
In reply to Cody P Schafermemlayout_global_init() initializes the first memlayout, which is assumed to match the initial page-flag nid settings.
This is done in start_kernel() as the initdata used to populate the memlayout is purged from memory early in the boot process (XXX: When?).
--- init/main.c | 2 ++ 1 file changed, 2 insertions(+)
init/main.c +2 -0
@@ -72,6 +72,7 @@#include <linux/ptrace.h> #include <linux/blkdev.h> #include <linux/elevator.h> +#include <linux/memlayout.h> #include <asm/io.h> #include <asm/bugs.h>
@@ -618,6 +619,7 @@ asmlinkage void __init start_kernel(void)security_init ();dbg_late_init ();vfs_caches_init (totalram_pages); +memlayout_global_init ();signals_init ();/* rootfs populating might need page-writeback */ page_writeback_init ();
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 22/24 dnuma: memlayout: add memory_add_physaddr_to_nid() for memory_hotplug
--- mm/memlayout.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
mm/memlayout.c +16 -0
@@ -249,3 +249,19 @@ void memlayout_global_init(void)memlayout_commit (ml); } + +#ifdef CONFIG_MEMORY_HOTPLUG +/* +* Provides a default memory_add_physaddr_to_nid() for memory hotplug, unless +* overridden by the arch. +*/ +__weak +int memory_add_physaddr_to_nid (u64 start) +{ +int nid =memlayout_pfn_to_nid (PFN_DOWN (start)); +if (nid ==NUMA_NO_NODE ) +return 0 ; +return nid; +} +EXPORT_SYMBOL_GPL (memory_add_physaddr_to_nid); +#endif
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 23/24 x86/mm/numa: when dnuma is enabled, use memlayout to handle memory hotplug's physaddr_to_nid.
In reply to Cody P SchaferWhen a memlayout is tracked (ie: CONFIG_DYNAMIC_NUMA is enabled), rather than iterate over numa_meminfo, a lookup can be done using memlayout.
--- arch/x86/mm/numa.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/x86/mm/numa.c +2 -2
@@ -28,7 +28,7 @@ struct pglist_data *node_data[MAX_NUMNODES] __read_mostly;EXPORT_SYMBOL (node_data); static struct numa_meminfo numa_meminfo -#ifndef CONFIG_MEMORY_HOTPLUG +#if !defined(CONFIG_MEMORY_HOTPLUG ) || defined(CONFIG_DYNAMIC_NUMA )__initdata #endif ;
@@ -832,7 +832,7 @@ EXPORT_SYMBOL(cpumask_of_node);#endif /* !CONFIG_DEBUG_PER_CPU_MAPS */ -#ifdef CONFIG_MEMORY_HOTPLUG +#if defined(CONFIG_MEMORY_HOTPLUG ) && !defined(CONFIG_DYNAMIC_NUMA )int memory_add_physaddr_to_nid (u64 start) {struct numa_meminfo *mi = &numa_meminfo;
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 24/24 XXX: x86/mm/numa: Avoid spamming warnings due to lack of cpu reconfig
In reply to Cody P Schaferthe code wants to map a node id to a cpu mask, but we don't update the arch specific cpu masks when onlining a new node. For now, avoid this warning (as it is expected) when DYNAMIC_NUMA is enabled.
Modifying __mem_online_node() to fix this up would be ideal.
--- arch/x86/mm/numa.c | 4 ++++ 1 file changed, 4 insertions(+)
arch/x86/mm/numa.c +4 -0
@@ -813,10 +813,14 @@ void __cpuinit numa_remove_cpu(int cpu)const struct cpumask *cpumask_of_node (int node) {if (node >= nr_node_ids) { +/* XXX: this ifdef should be removed when proper cpu to node +* mapping updates are added */ +#ifndef CONFIG_DYNAMIC_NUMA printk (KERN_WARNING "cpumask_of_node(%d): node > nr_node_ids(%d)\n" , node, nr_node_ids);dump_stack (); +#endif return cpu_none_mask; }if (node_to_cpumask_map[node] ==NULL ) {
Signature · 7 lines
1.8.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Hi Cody,
Quoted 4 lines · Cody P Schafer
Some people asked me to send the email patches for this instead of just posting a git tree link
For reference, this is the original message:
http://lkml.org/lkml/2013/2/27/374
Could you show me your test codes?
Quoted 24 lines
--
arch/x86/Kconfig | 1 - arch/x86/include/asm/sparsemem.h | 4 +- arch/x86/mm/numa.c | 32 +++- include/linux/dnuma.h | 96 +++++++++++ include/linux/memlayout.h | 111 +++++++++++++ include/linux/memory_hotplug.h | 4 + include/linux/mm.h | 7 +- include/linux/page-flags.h | 18 ++ include/linux/rbtree.h | 11 ++ init/main.c | 2 + lib/rbtree.c | 40 +++++ mm/Kconfig | 44 +++++ mm/Makefile | 2 + mm/dnuma.c | 351 +++++++++++++++++++++++++++++++++++++++ mm/internal.h | 13 +- mm/memlayout-debugfs.c | 323 +++++++++++++++++++++++++++++++++++ mm/memlayout-debugfs.h | 35 ++++ mm/memlayout.c | 267 +++++++++++++++++++++++++++++ mm/memory_hotplug.c | 53 +++--- mm/page_alloc.c | 112 +++++++++++-- 20 files changed, 1486 insertions(+), 40 deletions(-)
Quoted 5 lines
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 10 lines · Simon Jeons
Hi Cody,
Quoted 5 lines · Cody P Schafer
Some people asked me to send the email patches for this instead of just posting a git tree link
For reference, this is the original message:
http://lkml.org/lkml/2013/2/27/374Could you show me your test codes?
Sure, I linked to it in the original email
I generally run something like dnuma-test s 1 3 512, which creates stripes with size='512 pages' and distributes them between nodes 1, 2, and 3.
Also, this patchset has some major issues (not updating the watermarks, for example). I've been working on ironing them out, and plan on sending another patchset out "soon". Current tree is https://github.com/jmesmon/linux/tree/dnuma/v31 (keep in mind that this has a few commits in it that I just use for development).
Quoted 26 lines
Quoted 26 lines
--
arch/x86/Kconfig | 1 - arch/x86/include/asm/sparsemem.h | 4 +- arch/x86/mm/numa.c | 32 +++- include/linux/dnuma.h | 96 +++++++++++ include/linux/memlayout.h | 111 +++++++++++++ include/linux/memory_hotplug.h | 4 + include/linux/mm.h | 7 +- include/linux/page-flags.h | 18 ++ include/linux/rbtree.h | 11 ++ init/main.c | 2 + lib/rbtree.c | 40 +++++ mm/Kconfig | 44 +++++ mm/Makefile | 2 + mm/dnuma.c | 351 +++++++++++++++++++++++++++++++++++++++ mm/internal.h | 13 +- mm/memlayout-debugfs.c | 323 +++++++++++++++++++++++++++++++++++ mm/memlayout-debugfs.h | 35 ++++ mm/memlayout.c | 267 +++++++++++++++++++++++++++++ mm/memory_hotplug.c | 53 +++--- mm/page_alloc.c | 112 +++++++++++-- 20 files changed, 1486 insertions(+), 40 deletions(-)
Quoted 6 lines
Quoted 5 lines
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>