pcp: zonequeues
Subsystems (6)
PATCH 1/4 pcp: zonequeues
Hi Jack,
Was thinking about some problems in this area, and I hacked up a possible implementation to improve things.
1/4 switches the per cpu pagesets in struct zone to a single list of zone pagesets for each CPU.
2/4 changes the per cpu list of pagesets to a list of pointers to pagesets, and allocates them dynamically.
3/4 changes the code to allow NULL pagesets. In that case, a single per-zone pageset is used, which is protected by the zone's spinlock.
4/4 changes setup so non local zones don't have associated pagesets.
It still needs some work - in particular, many NUMA systems probably don't want this. I guess benchmarks should be done, and maybe we could look at disabling the overhead of 3/4 and functional change of 4/4 depending on a CONFIG_ option.
Also, you say you might want "close" remote nodes to have pagesets, but 4/4 only does local nodes. I added a comment with patch 4/4 marked with XXX which should allow you to do this quite easily.
Not tested (only compiled) on a NUMA system, but the NULL pagesets logic appears to work OK. Boots on a small UMA SMP system. So just be careful with it.
Comments?
Signature · 1 line
SUSE Labs, Novell Inc.
include/linux/mmzone.h +17 -11
@@ -53,14 +53,15 @@ struct per_cpu_pages {struct per_cpu_pageset {struct per_cpu_pages pcp[2 ];/* 0: hot. 1: cold */ -#ifdef CONFIG_NUMA +}; + +struct per_cpu_zone_stats {unsigned long numa_hit;/* allocated in intended node */ unsigned long numa_miss;/* allocated in non intended node */ unsigned long numa_foreign;/* was intended here, hit elsewhere */ unsigned long interleave_hit;/* interleaver prefered this zone */ unsigned long local_node;/* allocation from local node */ unsigned long other_node;/* allocation from other node */ -#endif }____cacheline_aligned_in_smp ;#define ZONE_DMA 0
@@ -113,16 +114,19 @@ struct zone {unsigned long free_pages;unsigned long pages_min, pages_low, pages_high;/* -* We don't know if the memory that we're going to allocate will be freeable -* or/and it will be released eventually, so to avoid totally wasting several -* GB of ram we must reserve some of the lower zone memory (otherwise we risk -* to run OOM on the lower zones despite there's tons of freeable ram -* on the higher zones). This array is recalculated at runtime if the -* sysctl_lowmem_reserve_ratio sysctl changes. +* We don't know if the memory that we're going to allocate will be +* freeable or/and it will be released eventually, so to avoid totally +* wasting several GB of ram we must reserve some of the lower zone +* memory (otherwise we risk to run OOM on the lower zones despite +* there's tons of freeable ram on the higher zones). This array is +* recalculated at runtime if the sysctl_lowmem_reserve_ratio sysctl +* changes. */ unsigned long lowmem_reserve[MAX_NR_ZONES ]; -struct per_cpu_pageset pageset[NR_CPUS ]; +#ifdef CONFIG_NUMA +struct per_cpu_zone_stats stats[NR_CPUS ]; +#endif /* * free areas of different sizes
@@ -220,6 +224,8 @@ struct zone {*/ #define DEF_PRIORITY 12 +#define TOTAL_ZONES (MAX_NUMNODES * MAX_NR_ZONES) +/* * One allocation request operates on a zonelist. A zonelist * is a list of zones, the first one is the 'goal' of the
@@ -232,10 +238,9 @@ struct zone {* footprint of this construct is very small. */ struct zonelist { -struct zone *zones[MAX_NUMNODES *MAX_NR_ZONES +1 ];// NULL delimited +struct zone *zones[TOTAL_ZONES +1 ];// NULL delimited }; -/* * The pg_data_t structure is used in machines with CONFIG_DISCONTIGMEM * (mostly NUMA machines?) to denote a higher-level memory zone than the
@@ -275,6 +280,7 @@ void __get_zone_counts(unsigned long *acvoid get_zone_counts (unsigned long *active,unsigned long *inactive,unsigned long *free);void build_all_zonelists (void ); +void build_percpu_pagelists (void );void wakeup_kswapd (struct zone *zone,int order);int zone_watermark_ok (struct zone *z,int order,unsigned long mark,int alloc_type,int can_try_harder,int gfp_high);
mm/page_alloc.c +106 -51
261 lines not on this page · raw message
init/main.c +1 -0
@@ -454,6 +454,7 @@ asmlinkage void __init start_kernel(void*/ preempt_disable ();build_all_zonelists (); +build_percpu_pagelists ();page_alloc_init ();printk (KERN_NOTICE "Kernel command line: %s\n" , saved_command_line);parse_early_param ();
mm/mempolicy.c +1 -1
@@ -721,7 +721,7 @@ static struct page *alloc_page_interleav zl =NODE_DATA (nid)->node_zonelists + (gfp &GFP_ZONEMASK ); page =__alloc_pages (gfp, order, zl);if (page &&page_zone (page) == zl->zones[0 ]) { - zl->zones[0 ]->pageset[get_cpu ()].interleave_hit++; + zl->zones[0 ]->stats[get_cpu ()].interleave_hit++;put_cpu (); }return page;
PATCH 2/4 pcp: dynamic lists
2/4
Signature · 1 line
SUSE Labs, Novell Inc.
mm/page_alloc.c +4 -3
@@ -70,7 +70,7 @@ struct zone *zone_table[1 << (ZONES_SHIFEXPORT_SYMBOL (zone_table);struct zone_pagesets { -struct per_cpu_pageset p[TOTAL_ZONES ]; +struct per_cpu_pageset *p[TOTAL_ZONES ]; };#define this_zone_pagesets () (&__get_cpu_var(zone_pagesets))
@@ -80,7 +80,7 @@ struct zone_pagesets { (NODEZONE((zone)->zone_pgdat->node_id, zone_idx(zone)))#define zone_pageset (zp, zone) \ - (&zp->p[zone_pagesets_idx(zone)]) + (zp->p[zone_pagesets_idx(zone)])/* * List of pointers to per_cpu_pagesets for each zone.
@@ -1579,7 +1579,8 @@ void __init build_percpu_pagelists(void)struct per_cpu_pages *pcp; zp =cpu_zone_pagesets (cpu); - pageset = &zp->p[NODEZONE (nid, j)]; + pageset =alloc_bootmem_node (pgdat,sizeof (*pageset)); + zp->p[NODEZONE (nid, j)] = pageset; pcp = &pageset->pcp[0 ];/* hot */ pcp->count =0 ;
PATCH 3/4 pcp: NULL pagesets
3/4
include/linux/mmzone.h +3 -0
@@ -128,6 +128,9 @@ struct zone {struct per_cpu_zone_stats stats[NR_CPUS ];#endif +/* global pageset for CPUs without private pagesets for this zone */ +struct per_cpu_pageset pageset; +/* * free areas of different sizes */
mm/page_alloc.c +70 -32
221 lines not on this page · raw message
PATCH 4/4 pcp: only local pagesets
4/4
Signature · 1 line
SUSE Labs, Novell Inc.
mm/page_alloc.c +14 -4
@@ -1626,14 +1626,24 @@ void __init build_percpu_pagelists(void)init_percpu_pageset (&zone->pageset, batch);for (cpu =0 ; cpu <NR_CPUS ; cpu++) {struct zone_pagesets *zp; -struct per_cpu_pageset *pageset; +struct per_cpu_pageset *pageset =NULL ; zp =cpu_zone_pagesets (cpu); - pageset =alloc_bootmem_node (pgdat,sizeof (*pageset)); -init_percpu_pageset (pageset, batch); + +/* +* XXX: this test could be something like +* if (node_distance <= blah) +* which would allow pagesets on close +* remote nodes as well as the local node. +*/ +if (cpu_to_node (cpu) == nid) { + pageset =alloc_bootmem_node (pgdat, +sizeof (*pageset)); +init_percpu_pageset (pageset, batch); + } zp->p[NODEZONE (nid, j)] = pageset; - } +printk (KERN_DEBUG " %s zone: %lu pages, LIFO batch:%lu\n" , zone_names[j], zone->present_pages, batch); }
Quoted 6 lines · Nick Piggin
Not tested (only compiled) on a NUMA system, but the NULL pagesets logic appears to work OK. Boots on a small UMA SMP system. So just be careful with it.
Comments?
Oh, and you may want to look at increasing the pageset and batch sizes if you look at running this on a bigger system. Now that there are reduced pagesets, it might help improve lock contention and allocation efficiency.
Signature · 7 lines
SUSE Labs, Novell Inc. -- 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 7 lines · Nick Piggin
Hi Jack,
Was thinking about some problems in this area, and I hacked up a possible implementation to improve things.1/4 switches the per cpu pagesets in struct zone to a single list of zone pagesets for each CPU.
Just thinking out loud here... this patch (or something like it) would probably be a good idea regardless of the remote pageset removal patches following it.
Shouldn't be any changes in behaviour, but it gives you remote pagesets in local memory and hopefully better cache behaviour due to less packing needed, and the use of percpu.
But...
Quoted 27 lines
include/linux/mmzone.h +7
-7+ +struct per_cpu_zone_stats {unsigned long numa_hit;/* allocated in intended node */ unsigned long numa_miss;/* allocated in non intended node */ unsigned long numa_foreign;/* was intended here, hit elsewhere */ unsigned long interleave_hit;/* interleaver prefered this zone */ unsigned long local_node;/* allocation from local node */ unsigned long other_node;/* allocation from other node */ -#endif }____cacheline_aligned_in_smp ;#define ZONE_DMA 0@@ -113,16 +114,19 @@ struct zone {unsigned long free_pages;unsigned long pages_min, pages_low, pages_high;/* -* We don't know if the memory that we're going to allocate will be freeable -* or/and it will be released eventually, so to avoid totally wasting several -* GB of ram we must reserve some of the lower zone memory (otherwise we risk -* to run OOM on the lower zones despite there's tons of freeable ram -* on the higher zones). This array is recalculated at runtime if the -* sysctl_lowmem_reserve_ratio sysctl changes. +* We don't know if the memory that we're going to allocate will be +* freeable or/and it will be released eventually, so to avoid totally +* wasting several GB of ram we must reserve some of the lower zone +* memory (otherwise we risk to run OOM on the lower zones despite +* there's tons of freeable ram on the higher zones). This array is
include/linux/mmzone.h struct zone { · line 130
Quoted 10 lines
include/linux/mmzone.h +5
-1+* recalculated at runtime if the sysctl_lowmem_reserve_ratio sysctl +* changes. */ unsigned long lowmem_reserve[MAX_NR_ZONES ]; -struct per_cpu_pageset pageset[NR_CPUS ]; +#ifdef CONFIG_NUMA +struct per_cpu_zone_stats stats[NR_CPUS ]; +#endif
I wonder if this stats information should be in the pageset there in local memory as well? I initially moved it to its own structure so that the zone queues could be completely confined to page_alloc.
Signature · 7 lines
SUSE Labs, Novell Inc. -- 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 24 lines · Nick Piggin
Hi Jack,
Was thinking about some problems in this area, and I hacked up a possible implementation to improve things.1/4 switches the per cpu pagesets in struct zone to a single list of zone pagesets for each CPU.
2/4 changes the per cpu list of pagesets to a list of pointers to pagesets, and allocates them dynamically.
3/4 changes the code to allow NULL pagesets. In that case, a single per-zone pageset is used, which is protected by the zone's spinlock.
4/4 changes setup so non local zones don't have associated pagesets.
It still needs some work - in particular, many NUMA systems probably don't want this. I guess benchmarks should be done, and maybe we could look at disabling the overhead of 3/4 and functional change of 4/4 depending on a
CONFIG_option.Also, you say you might want "close" remote nodes to have pagesets, but 4/4 only does local nodes. I added a comment with patch 4/4 marked with XXX which should allow you to do this quite easily.
Quoted 6 lines
Not tested (only compiled) on a NUMA system, but the NULL pagesets logic appears to work OK. Boots on a small UMA SMP system. So just be careful with it.
Comments?
Nick
I tested the patch. I found one spot that was missed with the NUMA statistics but everything else looks fine. The patches fix both problems that I found - bad coloring & excessive pages in pagesets.
drivers/base/node.c +1 -1
@@ -87,7 +87,7 @@ static ssize_t node_read_numastat(structfor (i =0 ; i <MAX_NR_ZONES ; i++) {struct zone *z = &pg->node_zones[i];for (cpu =0 ; cpu <NR_CPUS ; cpu++) { -struct per_cpu_pageset *ps = &z->pageset[cpu]; +struct per_cpu_zone_stats *ps = &z->stats[cpu]; numa_hit += ps->numa_hit; numa_miss += ps->numa_miss; numa_foreign += ps->numa_foreign;
Signature · 11 lines
Thanks Jack Steiner ([email protected]) 651-683-5302 Principal Engineer SGI - Silicon Graphics, Inc. -- 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 11 lines · Jack Steiner
Quoted 2 lines · Nick Piggin
Comments?
Nick
I tested the patch. I found one spot that was missed with the NUMA statistics but everything else looks fine. The patches fix both problems that I found - bad coloring & excessive pages in pagesets.
Thanks. I'll think about how to make them more acceptable for merging.
Signature · 7 lines
SUSE Labs, Novell Inc. -- 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>
Seems that this also effectively addresses the issues raised with the pageset localization patches. Great work Nick!
Quoted 24 lines · Nick Piggin
Hi Jack,
Was thinking about some problems in this area, and I hacked up a possible implementation to improve things.1/4 switches the per cpu pagesets in struct zone to a single list of zone pagesets for each CPU.
2/4 changes the per cpu list of pagesets to a list of pointers to pagesets, and allocates them dynamically.
3/4 changes the code to allow NULL pagesets. In that case, a single per-zone pageset is used, which is protected by the zone's spinlock.
4/4 changes setup so non local zones don't have associated pagesets.
It still needs some work - in particular, many NUMA systems probably don't want this. I guess benchmarks should be done, and maybe we could look at disabling the overhead of 3/4 and functional change of 4/4 depending on a
CONFIG_option.Also, you say you might want "close" remote nodes to have pagesets, but 4/4 only does local nodes. I added a comment with patch 4/4 marked with XXX which should allow you to do this quite easily.
Quoted 9 lines
Not tested (only compiled) on a NUMA system, but the NULL pagesets logic appears to work OK. Boots on a small UMA SMP system. So just be careful with it.
Comments?
--
SUSE Labs, Novell Inc.
--
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 3 lines · Christoph Lameter
Seems that this also effectively addresses the issues raised with the pageset localization patches. Great work Nick!
I'd be interested to know what performance and lock contention looks like on your larger systems, because we're using lru_lock for the remote pageset... and there's only one of them.
Your interleaved pagecache allocation policy should be a good brute force benchmark - just have one or two processes on each node allocating pagecache pages (eg. from reading huge sparse files).
The other thing is, you may want to look at adjusting the criteria for using the remote pageset. It might be helpful to use per-cpu pagesets on near remote nodes...
Nick
Signature · 7 lines
SUSE Labs, Novell Inc. -- 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>