Skip to contentSkip to conversation

Use two zonelists per node instead of multiple zonelists v11r3

PATCH In review Lee Schermerhorn 7 participants 37 messages Feb 27, 2008 – Mar 8, 2008

Subsystems (14)
lore
Share & tools
lore
#0 lore raw

cover Use two zonelists per node instead of multiple zonelists v11r3

[PATCH 0/6] Use two zonelists per node instead of multiple zonelists v11r3

This is a rebase of the two-zonelist patchset to 2.6.25-rc2-mm1.

Mel, still on vacation last I checked, asked me to repost these as I'd already rebased them and I've been testing them continually on each -mm tree for months, hoping to see them in -mm for wider testing.

I have a series of mempolicy cleanup patches, including a rework of the reference counting that depend on this series. David R. has a series of mempolicy enhancements out for review that, IMO, will benefit from this series. In both cases, the removal of the custom zonelist for MPOL_BIND is the important feature.

Lee

---

Changelog since V11r2
o Rebase to 2.6.25-rc2-mm1

Changelog since V10
o Rebase to 2.6.24-rc4-mm1
o Clear up warnings in fs/buffer.c early in the patchset

Changelog since V9
o Rebase to 2.6.24-rc2-mm1
o Lookup the nodemask for each allocator callsite in mempolicy.c
o Update NUMA statistics based on preferred zone, not first zonelist entry
o When __GFP_THISNODE is specified with MPOL_BIND and the current node is not in the allowed nodemask, the first node in the mask will be used
o Stick with using two zonelists instead of one because of excessive complexity with corner cases

Changelog since V8
o Rebase to 2.6.24-rc2
o Added ack for the OOM changes
o Behave correctly when GFP_THISNODE and a node ID are specified
o Clear up warning over type of nodes_intersects() function

Changelog since V7
o Rebase to 2.6.23-rc8-mm2

Changelog since V6
o Fix build bug in relation to memory controller combined with one-zonelist
o Use while() instead of a stupid looking for()
o Instead of encoding zone index information in a pointer, this version introduces a structure that stores a zone pointer and its index

Changelog since V5
o Rebase to 2.6.23-rc4-mm1
o Drop patch that replaces inline functions with macros

Changelog since V4
o Rebase to -mm kernel. Host of memoryless patches collisions dealt with
o Do not call wakeup_kswapd() for every zone in a zonelist
o Dropped the FASTCALL removal
o Have cursor in iterator advance earlier
o Use nodes_and in cpuset_nodes_valid_mems_allowed()
o Use defines instead of inlines, noticably better performance on gcc-3.4 No difference on later compilers such as gcc 4.1
o Dropped gfp_skip patch until it is proven to be of benefit. Tests are currently inconclusive but it definitly consumes at least one cache line

Changelog since V3
o Fix compile error in the parisc change
o Calculate gfp_zone only once in __alloc_pages
o Calculate classzone_idx properly in get_page_from_freelist
o Alter check so that zone id embedded may still be used on UP
o Use Kamezawa-sans suggestion for skipping zones in zonelist
o Add __alloc_pages_nodemask() to filter zonelist based on a nodemask. This removes the need for MPOL_BIND to have a custom zonelist
o Move zonelist iterators and helpers to mm.h
o Change _zones from struct zone * to unsigned long

Changelog since V2
o shrink_zones() uses zonelist instead of zonelist->zones
o hugetlb uses zonelist iterator
o zone_idx information is embedded in zonelist pointers
o replace NODE_DATA(nid)->node_zonelist with node_zonelist(nid)

Changelog since V1
o Break up the patch into 3 patches
o Introduce iterators for zonelists
o Performance regression test

The following patches replace multiple zonelists per node with two zonelists that are filtered based on the GFP flags. The patches as a set fix a bug with regard to the use of MPOL_BIND and ZONE_MOVABLE. With this patchset, the MPOL_BIND will apply to the two highest zones when the highest zone is ZONE_MOVABLE. This should be considered as an alternative fix for the MPOL_BIND+ZONE_MOVABLE in 2.6.23 to the previously discussed hack that filters only custom zonelists.

The first patch cleans up an inconsistency where direct reclaim uses zonelist->zones where other places use zonelist.

The second patch introduces a helper function node_zonelist() for looking up the appropriate zonelist for a GFP mask which simplifies patches later in the set.

The third patch defines/remembers the "preferred zone" for numa statistics, as it is no longer always the first zone in a zonelist.

The forth patch replaces multiple zonelists with two zonelists that are filtered. The two zonelists are due to the fact that the memoryless patchset introduces a second set of zonelists for __GFP_THISNODE.

The fifth patch introduces helper macros for retrieving the zone and node indices of entries in a zonelist.

The final patch introduces filtering of the zonelists based on a nodemask. Two zonelists exist per node, one for normal allocations and one for __GFP_THISNODE.

Performance results varied depending on the machine configuration. In real workloads the gain/loss will depend on how much the userspace portion of the benchmark benefits from having more cache available due to reduced referencing of zonelists.

These are the range of performance losses/gains when running against 2.6.24-rc4-mm1. The set and these machines are a mix of i386, x86_64 and ppc64 both NUMA and non-NUMA.

loss to gain

Total CPU time on Kernbench: -0.86% to  1.13%
Elapsed   time on Kernbench: -0.79% to  0.76%
page_test from aim9:         -4.37% to  0.79%
brk_test  from aim9:         -0.71% to  4.07%
fork_test from aim9:         -1.84% to  4.60%
exec_test from aim9:         -0.71% to  1.08%

--
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>

#1 lore raw

PATCH 1/6 Use zonelists instead of zones when direct reclaiming pages

[PATCH 1/6] Use zonelists instead of zones when direct reclaiming pages

V11r3 against 2.6.25-rc2-mm1

The allocator deals with zonelists which indicate the order in which zones should be targeted for an allocation. Similarly, direct reclaim of pages iterates over an array of zones. For consistency, this patch converts direct reclaim to use a zonelist. No functionality is changed by this patch. This simplifies zonelist iterators in the next patch.

 fs/buffer.c          |    8 ++++----
 include/linux/swap.h |    2 +-
 mm/page_alloc.c      |    2 +-
 mm/vmscan.c          |   21 ++++++++++++---------
 4 files changed, 18 insertions(+), 15 deletions(-)
fs/buffer.c +4 -4
Index: linux-2.6.25-rc2-mm1/fs/buffer.c
===================================================================
@@ -368,16 +368,16 @@ void invalidate_bdev(struct block_device
  */
 static void free_more_memory(void)
 {
-	struct zone **zones;
+	struct zonelist *zonelist;
 	pg_data_t *pgdat;
 
 	wakeup_pdflush(1024);
 	yield();
 
 	for_each_online_pgdat(pgdat) {
-		zones = pgdat->node_zonelists[gfp_zone(GFP_NOFS)].zones;
-		if (*zones)
-			try_to_free_pages(zones, 0, GFP_NOFS);
+		zonelist = &pgdat->node_zonelists[gfp_zone(GFP_NOFS)];
+		if (zonelist->zones[0])
+			try_to_free_pages(zonelist, 0, GFP_NOFS);
 	}
 }
 
include/linux/swap.h +1 -1
Index: linux-2.6.25-rc2-mm1/include/linux/swap.h
===================================================================
@@ -181,7 +181,7 @@ extern int rotate_reclaimable_page(struc
 extern void swap_setup(void);
 
 /* linux/mm/vmscan.c */
-extern unsigned long try_to_free_pages(struct zone **zones, int order,
+extern unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
 					gfp_t gfp_mask);
 extern unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *mem,
 							gfp_t gfp_mask);
mm/page_alloc.c +1 -1
Index: linux-2.6.25-rc2-mm1/mm/page_alloc.c
===================================================================
@@ -1635,7 +1635,7 @@ nofail_alloc:
 	reclaim_state.reclaimed_slab = 0;
 	p->reclaim_state = &reclaim_state;
 
-	did_some_progress = try_to_free_pages(zonelist->zones, order, gfp_mask);
+	did_some_progress = try_to_free_pages(zonelist, order, gfp_mask);
 
 	p->reclaim_state = NULL;
 	p->flags &= ~PF_MEMALLOC;
mm/vmscan.c +12 -9
Index: linux-2.6.25-rc2-mm1/mm/vmscan.c
===================================================================
@@ -1268,10 +1268,11 @@ static unsigned long shrink_zone(int pri
  * If a zone is deemed to be full of pinned pages then just give it a light
  * scan then give up on it.
  */
-static unsigned long shrink_zones(int priority, struct zone **zones,
+static unsigned long shrink_zones(int priority, struct zonelist *zonelist,
 					struct scan_control *sc)
 {
 	unsigned long nr_reclaimed = 0;
+	struct zone **zones = zonelist->zones;
 	int i;
 
 
@@ -1323,8 +1324,8 @@ static unsigned long shrink_zones(int pr
  * holds filesystem locks which prevent writeout this might not work, and the
  * allocation attempt will fail.
  */
-static unsigned long do_try_to_free_pages(struct zone **zones, gfp_t gfp_mask,
-					  struct scan_control *sc)
+static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
+					gfp_t gfp_mask, struct scan_control *sc)
 {
 	int priority;
 	int ret = 0;
@@ -1332,6 +1333,7 @@ static unsigned long do_try_to_free_page
 	unsigned long nr_reclaimed = 0;
 	struct reclaim_state *reclaim_state = current->reclaim_state;
 	unsigned long lru_pages = 0;
+	struct zone **zones = zonelist->zones;
 	int i;
 
 	if (scan_global_lru(sc))
@@ -1356,7 +1358,7 @@ static unsigned long do_try_to_free_page
 		sc->nr_io_pages = 0;
 		if (!priority)
 			disable_swap_token();
-		nr_reclaimed += shrink_zones(priority, zones, sc);
+		nr_reclaimed += shrink_zones(priority, zonelist, sc);
 		/*
 		 * Don't shrink slabs when reclaiming memory from
 		 * over limit cgroups
@@ -1421,7 +1423,8 @@ out:
 	return ret;
 }
 
-unsigned long try_to_free_pages(struct zone **zones, int order, gfp_t gfp_mask)
+unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
+								gfp_t gfp_mask)
 {
 	struct scan_control sc = {
 		.gfp_mask = gfp_mask,
@@ -1434,7 +1437,7 @@ unsigned long try_to_free_pages(struct z
 		.isolate_pages = isolate_pages_global,
 	};
 
-	return do_try_to_free_pages(zones, gfp_mask, &sc);
+	return do_try_to_free_pages(zonelist, gfp_mask, &sc);
 }
 
 #ifdef CONFIG_CGROUP_MEM_CONT
@@ -1452,11 +1455,11 @@ unsigned long try_to_free_mem_cgroup_pag
 		.mem_cgroup = mem_cont,
 		.isolate_pages = mem_cgroup_isolate_pages,
 	};
-	struct zone **zones;
+	struct zonelist *zonelist;
 	int target_zone = gfp_zone(GFP_HIGHUSER_MOVABLE);
 
-	zones = NODE_DATA(numa_node_id())->node_zonelists[target_zone].zones;
-	if (do_try_to_free_pages(zones, sc.gfp_mask, &sc))
+	zonelist = &NODE_DATA(numa_node_id())->node_zonelists[target_zone];
+	if (do_try_to_free_pages(zonelist, sc.gfp_mask, &sc))
 		return 1;
 	return 0;
 }

--
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>

#2 lore raw

PATCH 2/6 Introduce node_zonelist() for accessing the zonelist for a GFP mask

In reply to Lee Schermerhorn

[PATCH 2/6] Introduce node_zonelist() for accessing the zonelist for a GFP mask

V11r3 against 2.6.25-rc2-mm1

This patch introduces a node_zonelist() helper function. It is used to lookup the appropriate zonelist given a node and a GFP mask. The patch on its own is a cleanup but it helps clarify parts of the two-zonelist-per-node patchset. If necessary, it can be merged with the next patch in this set without problems.

 drivers/char/sysrq.c      |    3 +--
 fs/buffer.c               |    6 +++---
 include/linux/gfp.h       |    8 ++++++--
 include/linux/mempolicy.h |    2 +-
 mm/mempolicy.c            |    6 +++---
 mm/page_alloc.c           |    3 +--
 mm/slab.c                 |    3 +--
 mm/slub.c                 |    3 +--
 8 files changed, 17 insertions(+), 17 deletions(-)
drivers/char/sysrq.c +1 -2
Index: linux-2.6.25-rc2-mm1/drivers/char/sysrq.c
===================================================================
@@ -271,8 +271,7 @@ static struct sysrq_key_op sysrq_term_op
 
 static void moom_callback(struct work_struct *ignored)
 {
-	out_of_memory(&NODE_DATA(0)->node_zonelists[ZONE_NORMAL],
-			GFP_KERNEL, 0);
+	out_of_memory(node_zonelist(0, GFP_KERNEL), GFP_KERNEL, 0);
 }
 
 static DECLARE_WORK(moom_work, moom_callback);
fs/buffer.c +3 -3
Index: linux-2.6.25-rc2-mm1/fs/buffer.c
===================================================================
@@ -369,13 +369,13 @@ void invalidate_bdev(struct block_device
 static void free_more_memory(void)
 {
 	struct zonelist *zonelist;
-	pg_data_t *pgdat;
+	int nid;
 
 	wakeup_pdflush(1024);
 	yield();
 
-	for_each_online_pgdat(pgdat) {
-		zonelist = &pgdat->node_zonelists[gfp_zone(GFP_NOFS)];
+	for_each_online_node(nid) {
+		zonelist = node_zonelist(nid, GFP_NOFS);
 		if (zonelist->zones[0])
 			try_to_free_pages(zonelist, 0, GFP_NOFS);
 	}
include/linux/gfp.h +6 -2
Index: linux-2.6.25-rc2-mm1/include/linux/gfp.h
===================================================================
@@ -154,10 +154,15 @@ static inline enum zone_type gfp_zone(gf
 /*
  * We get the zone list from the current node and the gfp_mask.
  * This zone list contains a maximum of MAXNODES*MAX_NR_ZONES zones.
+ * There are many zonelists per node, two for each active zone.
  *
  * For the normal case of non-DISCONTIGMEM systems the NODE_DATA() gets
  * optimized to &contig_page_data at compile-time.
  */
+static inline struct zonelist *node_zonelist(int nid, gfp_t flags)
+{
+	return NODE_DATA(nid)->node_zonelists + gfp_zone(flags);
+}
 
 #ifndef HAVE_ARCH_FREE_PAGE
 static inline void arch_free_page(struct page *page, int order) { }
@@ -178,8 +183,7 @@ static inline struct page *alloc_pages_n
 	if (nid < 0)
 		nid = numa_node_id();
 
-	return __alloc_pages(gfp_mask, order,
-		NODE_DATA(nid)->node_zonelists + gfp_zone(gfp_mask));
+	return __alloc_pages(gfp_mask, order, node_zonelist(nid, gfp_mask));
 }
 
 #ifdef CONFIG_NUMA
include/linux/mempolicy.h +1 -1
Index: linux-2.6.25-rc2-mm1/include/linux/mempolicy.h
===================================================================
@@ -241,7 +241,7 @@ static inline void mpol_fix_fork_child_f
 static inline struct zonelist *huge_zonelist(struct vm_area_struct *vma,
  		unsigned long addr, gfp_t gfp_flags, struct mempolicy **mpol)
 {
-	return NODE_DATA(0)->node_zonelists + gfp_zone(gfp_flags);
+	return node_zonelist(0, gfp_flags);
 }
 
 static inline int do_migrate_pages(struct mm_struct *mm,
mm/mempolicy.c +3 -3
Index: linux-2.6.25-rc2-mm1/mm/mempolicy.c
===================================================================
@@ -1183,7 +1183,7 @@ static struct zonelist *zonelist_policy(
 		nd = 0;
 		BUG();
 	}
-	return NODE_DATA(nd)->node_zonelists + gfp_zone(gfp);
+	return node_zonelist(nd, gfp);
 }
 
 /* Do dynamic interleaving for a process */
@@ -1297,7 +1297,7 @@ struct zonelist *huge_zonelist(struct vm
 
 		nid = interleave_nid(pol, vma, addr, HPAGE_SHIFT);
 		__mpol_free(pol);		/* finished with pol */
-		return NODE_DATA(nid)->node_zonelists + gfp_zone(gfp_flags);
+		return node_zonelist(nid, gfp_flags);
 	}
 
 	zl = zonelist_policy(GFP_HIGHUSER, pol);
@@ -1319,7 +1319,7 @@ static struct page *alloc_page_interleav
 	struct zonelist *zl;
 	struct page *page;
 
-	zl = NODE_DATA(nid)->node_zonelists + gfp_zone(gfp);
+	zl = node_zonelist(nid, gfp);
 	page = __alloc_pages(gfp, order, zl);
 	if (page && page_zone(page) == zl->zones[0])
 		inc_zone_page_state(page, NUMA_INTERLEAVE_HIT);
mm/page_alloc.c +1 -2
Index: linux-2.6.25-rc2-mm1/mm/page_alloc.c
===================================================================
@@ -1783,10 +1783,9 @@ EXPORT_SYMBOL(free_pages);
 static unsigned int nr_free_zone_pages(int offset)
 {
 	/* Just pick one node, since fallback list is circular */
-	pg_data_t *pgdat = NODE_DATA(numa_node_id());
 	unsigned int sum = 0;
 
-	struct zonelist *zonelist = pgdat->node_zonelists + offset;
+	struct zonelist *zonelist = node_zonelist(numa_node_id(), GFP_KERNEL);
 	struct zone **zonep = zonelist->zones;
 	struct zone *zone;
 
mm/slab.c +1 -2
Index: linux-2.6.25-rc2-mm1/mm/slab.c
===================================================================
@@ -3251,8 +3251,7 @@ static void *fallback_alloc(struct kmem_
 	if (flags & __GFP_THISNODE)
 		return NULL;
 
-	zonelist = &NODE_DATA(slab_node(current->mempolicy))
-			->node_zonelists[gfp_zone(flags)];
+	zonelist = node_zonelist(slab_node(current->mempolicy), flags);
 	local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK);
 
 retry:
mm/slub.c +1 -2
Index: linux-2.6.25-rc2-mm1/mm/slub.c
===================================================================
@@ -1324,8 +1324,7 @@ static struct page *get_any_partial(stru
 			get_cycles() % 1024 > s->remote_node_defrag_ratio)
 		return NULL;
 
-	zonelist = &NODE_DATA(
-		slab_node(current->mempolicy))->node_zonelists[gfp_zone(flags)];
+	zonelist = node_zonelist(slab_node(current->mempolicy), flags);
 	for (z = zonelist->zones; *z; z++) {
 		struct kmem_cache_node *n;
 

--
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>

#3 lore raw

PATCH 3/6 Remember what the preferred zone is for zone_statistics

In reply to Lee Schermerhorn

[PATCH 3/6] Remember what the preferred zone is for zone_statistics

V11r3 against 2.6.25-rc2-mm1

On NUMA, zone_statistics() is used to record events like numa hit, miss and foreign. It assumes that the first zone in a zonelist is the preferred zone. When multiple zonelists are replaced by one that is filtered, this is no longer the case.

This patch records what the preferred zone is rather than assuming the first zone in the zonelist is it. This simplifies the reading of later patches in this set.

 include/linux/vmstat.h |    2 +-
 mm/page_alloc.c        |    9 +++++----
 mm/vmstat.c            |    6 +++---
 3 files changed, 9 insertions(+), 8 deletions(-)
include/linux/vmstat.h +1 -1
Index: linux-2.6.25-rc2-mm1/include/linux/vmstat.h
===================================================================
@@ -174,7 +174,7 @@ static inline unsigned long node_page_st
 		zone_page_state(&zones[ZONE_MOVABLE], item);
 }
 
-extern void zone_statistics(struct zonelist *, struct zone *);
+extern void zone_statistics(struct zone *, struct zone *);
 
 #else
 
mm/page_alloc.c +5 -4
Index: linux-2.6.25-rc2-mm1/mm/page_alloc.c
===================================================================
@@ -1060,7 +1060,7 @@ void split_page(struct page *page, unsig
  * we cheat by calling it from here, in the order > 0 path.  Saves a branch
  * or two.
  */
-static struct page *buffered_rmqueue(struct zonelist *zonelist,
+static struct page *buffered_rmqueue(struct zone *preferred_zone,
 			struct zone *zone, int order, gfp_t gfp_flags)
 {
 	unsigned long flags;
@@ -1112,7 +1112,7 @@ again:
 	}
 
 	__count_zone_vm_events(PGALLOC, zone, 1 << order);
-	zone_statistics(zonelist, zone);
+	zone_statistics(preferred_zone, zone);
 	local_irq_restore(flags);
 	put_cpu();
 
@@ -1393,7 +1393,7 @@ get_page_from_freelist(gfp_t gfp_mask, u
 	struct zone **z;
 	struct page *page = NULL;
 	int classzone_idx = zone_idx(zonelist->zones[0]);
-	struct zone *zone;
+	struct zone *zone, *preferred_zone;
 	nodemask_t *allowednodes = NULL;/* zonelist_cache approximation */
 	int zlc_active = 0;		/* set if using zonelist_cache */
 	int did_zlc_setup = 0;		/* just call zlc_setup() one time */
@@ -1405,6 +1405,7 @@ zonelist_scan:
 	 * See also cpuset_zone_allowed() comment in kernel/cpuset.c.
 	 */
 	z = zonelist->zones;
+	preferred_zone = *z;
 
 	do {
 		/*
@@ -1443,7 +1444,7 @@ zonelist_scan:
 			}
 		}
 
-		page = buffered_rmqueue(zonelist, zone, order, gfp_mask);
+		page = buffered_rmqueue(preferred_zone, zone, order, gfp_mask);
 		if (page)
 			break;
 this_zone_full:
mm/vmstat.c +3 -3
Index: linux-2.6.25-rc2-mm1/mm/vmstat.c
===================================================================
@@ -365,13 +365,13 @@ void refresh_cpu_vm_stats(int cpu)
  *
  * Must be called with interrupts disabled.
  */
-void zone_statistics(struct zonelist *zonelist, struct zone *z)
+void zone_statistics(struct zone *preferred_zone, struct zone *z)
 {
-	if (z->zone_pgdat == zonelist->zones[0]->zone_pgdat) {
+	if (z->zone_pgdat == preferred_zone->zone_pgdat) {
 		__inc_zone_state(z, NUMA_HIT);
 	} else {
 		__inc_zone_state(z, NUMA_MISS);
-		__inc_zone_state(zonelist->zones[0], NUMA_FOREIGN);
+		__inc_zone_state(preferred_zone, NUMA_FOREIGN);
 	}
 	if (z->node == numa_node_id())
 		__inc_zone_state(z, NUMA_LOCAL);

--
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>

Reviewed-by on v1 3/6
#4 lore raw

Re: [PATCH 3/6] Remember what the preferred zone is for zone_statistics

Quoted 3 lines

This patch records what the preferred zone is rather than assuming the first zone in the zonelist is it. This simplifies the reading of later patches in this set.

And is needed for correctness if GFP_THISNODE is used?

--
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>

#5 lore raw

Re: [PATCH 3/6] Remember what the preferred zone is for zone_statistics

Quoted 5 lines · Christoph Lameter
Quoted 3 lines

This patch records what the preferred zone is rather than assuming the first zone in the zonelist is it. This simplifies the reading of later patches in this set.

And is needed for correctness if GFP_THISNODE is used?

Mel can correct me.

I believe this is needed for MPOL_BIND allocations because we now use the zonelist for the local node--i.e., the node from which the allocation takes place--and search for the nearest node in the policy nodemask that satisfies the allocation. For the purpose of numa stats, a "numa_hit" occurs if the allocation succeeds on the first node in the zone list that is also in the policy nodemask--this is the "preferred node".

Lee

Quoted 2 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>

#6 lore raw

Re: [PATCH 3/6] Remember what the preferred zone is for zone_statistics

In reply to Christoph Lameter

On (27/02/08 14:00), Christoph Lameter didst pronounce:

Quoted 6 lines
Quoted 3 lines

This patch records what the preferred zone is rather than assuming the first zone in the zonelist is it. This simplifies the reading of later patches in this set.

And is needed for correctness if GFP_THISNODE is used?

Yes, I should have noted that. Without the patch, statistics could be updated in the wrong place.

Quoted 2 lines
Signature · 9 lines
Mel Gorman
Part-time Phd Student                          Linux Technology Center
University of Limerick                         IBM Dublin Software Lab

--
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>
#7 lore raw

Re: [PATCH 3/6] Remember what the preferred zone is for zone_statistics

In reply to Lee Schermerhorn
Quoted 10 lines · On Wed, 27 Feb 2008 16:47:28 -0500 Lee Schermerhorn <[email protected]>…

From: Mel Gorman <[email protected]>
[PATCH 3/6] Remember what the preferred zone is for zone_statistics

V11r3 against 2.6.25-rc2-mm1

On NUMA, zone_statistics() is used to record events like numa hit, miss and foreign. It assumes that the first zone in a zonelist is the preferred zone. When multiple zonelists are replaced by one that is filtered, this is no longer the case.

Quoted 7 lines

This patch records what the preferred zone is rather than assuming the first zone in the zonelist is it. This simplifies the reading of later patches in this set.

I have no objection to the direction but

mm/page_alloc.c void split_page(struct page *page, unsig · line 1065

Quoted 3 lines
mm/page_alloc.c +1 -0
+static struct page *buffered_rmqueue(struct zone *preferred_zone,
 			struct zone *zone, int order, gfp_t gfp_flags)
 {

Can't this be written like this ?

struct page *
buffered_rmqueue(struct zone *zone, int order, gfp_t gfp_flags, bool numa_hit)

Can't caller itself set this bool value ?

Thanks,
-Kame

--
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>

#8 lore raw

Re: [PATCH 3/6] Remember what the preferred zone is for zone_statistics

On (29/02/08 11:30), KAMEZAWA Hiroyuki didst pronounce:

Quoted 28 lines
Quoted 10 lines · On Wed, 27 Feb 2008 16:47:28 -0500 Lee Schermerhorn <[email protected]>…

From: Mel Gorman <[email protected]>
[PATCH 3/6] Remember what the preferred zone is for zone_statistics

V11r3 against 2.6.25-rc2-mm1

On NUMA, zone_statistics() is used to record events like numa hit, miss and foreign. It assumes that the first zone in a zonelist is the preferred zone. When multiple zonelists are replaced by one that is filtered, this is no longer the case.

Quoted 7 lines

This patch records what the preferred zone is rather than assuming the first zone in the zonelist is it. This simplifies the reading of later patches in this set.

I have no objection to the direction but

mm/page_alloc.c void split_page(struct page *page, unsig · line 1065

Quoted 3 lines
mm/page_alloc.c +1 -0
+static struct page *buffered_rmqueue(struct zone *preferred_zone,
 			struct zone *zone, int order, gfp_t gfp_flags)
 {
Quoted 7 lines

Can't this be written like this ?

struct page *
buffered_rmqueue(struct zone *zone, int order, gfp_t gfp_flags, bool numa_hit)

Can't caller itself set this bool value ?

Going that direction, the caller could call zone_statistics() instead of buffered_rmqueue() and it would be one less parameter to pass. However, as buffered_rmqueue() is probably inlined, I'm not sure a change would be beneficial.

Signature · 9 lines
Mel Gorman
Part-time Phd Student                          Linux Technology Center
University of Limerick                         IBM Dublin Software Lab

--
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>
#9 lore raw

PATCH 4/6 Use two zonelist that are filtered by GFP mask

In reply to Lee Schermerhorn

[PATCH 4/6] Use two zonelist that are filtered by GFP mask

V11r3 against 2.6.25-rc2-mm1

Currently a node has two sets of zonelists, one for each zone type in the system and a second set for GFP_THISNODE allocations. Based on the zones allowed by a gfp mask, one of these zonelists is selected. All of these zonelists consume memory and occupy cache lines.

This patch replaces the multiple zonelists per-node with two zonelists. The first contains all populated zones in the system, ordered by distance, for fallback allocations when the target/preferred node has no free pages. The second contains all populated zones in the node suitable for GFP_THISNODE allocations.

An iterator macro is introduced called for_each_zone_zonelist() that interates through each zone allowed by the GFP flags in the selected zonelist.

 arch/parisc/mm/init.c  |   11 ++-
 fs/buffer.c            |   10 +-
 include/linux/gfp.h    |   13 +++
 include/linux/mmzone.h |   65 ++++++++++++------
 mm/hugetlb.c           |    8 +-
 mm/oom_kill.c          |    8 +-
 mm/page_alloc.c        |  170 +++++++++++++++++++++----------------------------
 mm/slab.c              |    8 +-
 mm/slub.c              |    8 +-
 mm/vmscan.c            |   21 ++----
 10 files changed, 168 insertions(+), 154 deletions(-)
arch/parisc/mm/init.c +7 -4
Index: linux-2.6.25-rc2-mm1/arch/parisc/mm/init.c
===================================================================
@@ -603,15 +603,18 @@ void show_mem(void)
 #ifdef CONFIG_DISCONTIGMEM
 	{
 		struct zonelist *zl;
-		int i, j, k;
+		int i, j;
 
 		for (i = 0; i < npmem_ranges; i++) {
+			zl = node_zonelist(i);
 			for (j = 0; j < MAX_NR_ZONES; j++) {
-				zl = NODE_DATA(i)->node_zonelists + j;
+				struct zone **z;
+				struct zone *zone;
 
 				printk("Zone list for zone %d on node %d: ", j, i);
-				for (k = 0; zl->zones[k] != NULL; k++) 
-					printk("[%d/%s] ", zone_to_nid(zl->zones[k]), zl->zones[k]->name);
+				for_each_zone_zonelist(zone, z, zl, j)
+					printk("[%d/%s] ", zone_to_nid(zone),
+								zone->name);
 				printk("\n");
 			}
 		}
fs/buffer.c +6 -4
Index: linux-2.6.25-rc2-mm1/fs/buffer.c
===================================================================
@@ -368,16 +368,18 @@ void invalidate_bdev(struct block_device
  */
 static void free_more_memory(void)
 {
-	struct zonelist *zonelist;
+	struct zone **zones;
 	int nid;
 
 	wakeup_pdflush(1024);
 	yield();
 
 	for_each_online_node(nid) {
-		zonelist = node_zonelist(nid, GFP_NOFS);
-		if (zonelist->zones[0])
-			try_to_free_pages(zonelist, 0, GFP_NOFS);
+		zones = first_zones_zonelist(node_zonelist(nid, GFP_NOFS),
+						gfp_zone(GFP_NOFS));
+		if (*zones)
+			try_to_free_pages(node_zonelist(nid, GFP_NOFS), 0,
+						GFP_NOFS);
 	}
 }
 
include/linux/gfp.h +11 -2
Index: linux-2.6.25-rc2-mm1/include/linux/gfp.h
===================================================================
@@ -151,17 +151,26 @@ static inline enum zone_type gfp_zone(gf
  * virtual kernel addresses to the allocated page(s).
  */
 
+static inline int gfp_zonelist(gfp_t flags)
+{
+	if (NUMA_BUILD && unlikely(flags & __GFP_THISNODE))
+		return 1;
+
+	return 0;
+}
+
 /*
  * We get the zone list from the current node and the gfp_mask.
  * This zone list contains a maximum of MAXNODES*MAX_NR_ZONES zones.
- * There are many zonelists per node, two for each active zone.
+ * There are two zonelists per node, one for all zones with memory and
+ * one containing just zones from the node the zonelist belongs to.
  *
  * For the normal case of non-DISCONTIGMEM systems the NODE_DATA() gets
  * optimized to &contig_page_data at compile-time.
  */
 static inline struct zonelist *node_zonelist(int nid, gfp_t flags)
 {
-	return NODE_DATA(nid)->node_zonelists + gfp_zone(flags);
+	return NODE_DATA(nid)->node_zonelists + gfp_zonelist(flags);
 }
 
 #ifndef HAVE_ARCH_FREE_PAGE
include/linux/mmzone.h +43 -22

90 lines not on this page · raw message

mm/hugetlb.c +4 -4
Index: linux-2.6.25-rc2-mm1/mm/hugetlb.c
===================================================================
@@ -79,11 +79,11 @@ static struct page *dequeue_huge_page(st
 	struct mempolicy *mpol;
 	struct zonelist *zonelist = huge_zonelist(vma, address,
 					htlb_alloc_mask, &mpol);
-	struct zone **z;
+	struct zone *zone, **z;
 
-	for (z = zonelist->zones; *z; z++) {
-		nid = zone_to_nid(*z);
-		if (cpuset_zone_allowed_softwall(*z, htlb_alloc_mask) &&
+	for_each_zone_zonelist(zone, z, zonelist, MAX_NR_ZONES - 1) {
+		nid = zone_to_nid(zone);
+		if (cpuset_zone_allowed_softwall(zone, htlb_alloc_mask) &&
 		    !list_empty(&hugepage_freelists[nid])) {
 			page = list_entry(hugepage_freelists[nid].next,
 					  struct page, lru);
mm/oom_kill.c +5 -3
Index: linux-2.6.25-rc2-mm1/mm/oom_kill.c
===================================================================
@@ -174,12 +174,14 @@ static inline enum oom_constraint constr
 						    gfp_t gfp_mask)
 {
 #ifdef CONFIG_NUMA
+	struct zone *zone;
 	struct zone **z;
+	enum zone_type high_zoneidx = gfp_zone(gfp_mask);
 	nodemask_t nodes = node_states[N_HIGH_MEMORY];
 
-	for (z = zonelist->zones; *z; z++)
-		if (cpuset_zone_allowed_softwall(*z, gfp_mask))
-			node_clear(zone_to_nid(*z), nodes);
+	for_each_zone_zonelist(zone, z, zonelist, high_zoneidx)
+		if (cpuset_zone_allowed_softwall(zone, gfp_mask))
+			node_clear(zone_to_nid(zone), nodes);
 		else
 			return CONSTRAINT_CPUSET;
 
mm/page_alloc.c +73 -97

300 lines not on this page · raw message

mm/slab.c +5 -3
Index: linux-2.6.25-rc2-mm1/mm/slab.c
===================================================================
@@ -3245,6 +3245,8 @@ static void *fallback_alloc(struct kmem_
 	struct zonelist *zonelist;
 	gfp_t local_flags;
 	struct zone **z;
+	struct zone *zone;
+	enum zone_type high_zoneidx = gfp_zone(flags);
 	void *obj = NULL;
 	int nid;
 
@@ -3259,10 +3261,10 @@ retry:
 	 * Look through allowed nodes for objects available
 	 * from existing per node queues.
 	 */
-	for (z = zonelist->zones; *z && !obj; z++) {
-		nid = zone_to_nid(*z);
+	for_each_zone_zonelist(zone, z, zonelist, high_zoneidx) {
+		nid = zone_to_nid(zone);
 
-		if (cpuset_zone_allowed_hardwall(*z, flags) &&
+		if (cpuset_zone_allowed_hardwall(zone, flags) &&
 			cache->nodelists[nid] &&
 			cache->nodelists[nid]->free_objects)
 				obj = ____cache_alloc_node(cache,
mm/slub.c +5 -3
Index: linux-2.6.25-rc2-mm1/mm/slub.c
===================================================================
@@ -1300,6 +1300,8 @@ static struct page *get_any_partial(stru
 #ifdef CONFIG_NUMA
 	struct zonelist *zonelist;
 	struct zone **z;
+	struct zone *zone;
+	enum zone_type high_zoneidx = gfp_zone(flags);
 	struct page *page;
 
 	/*
@@ -1325,12 +1327,12 @@ static struct page *get_any_partial(stru
 		return NULL;
 
 	zonelist = node_zonelist(slab_node(current->mempolicy), flags);
-	for (z = zonelist->zones; *z; z++) {
+	for_each_zone_zonelist(zone, z, zonelist, high_zoneidx) {
 		struct kmem_cache_node *n;
 
-		n = get_node(s, zone_to_nid(*z));
+		n = get_node(s, zone_to_nid(zone));
 
-		if (n && cpuset_zone_allowed_hardwall(*z, flags) &&
+		if (n && cpuset_zone_allowed_hardwall(zone, flags) &&
 				n->nr_partial > MIN_PARTIAL) {
 			page = get_partial_node(n);
 			if (page)
mm/vmscan.c +9 -12
Index: linux-2.6.25-rc2-mm1/mm/vmscan.c
===================================================================
@@ -1271,15 +1271,13 @@ static unsigned long shrink_zone(int pri
 static unsigned long shrink_zones(int priority, struct zonelist *zonelist,
 					struct scan_control *sc)
 {
+	enum zone_type high_zoneidx = gfp_zone(sc->gfp_mask);
 	unsigned long nr_reclaimed = 0;
-	struct zone **zones = zonelist->zones;
-	int i;
-
+	struct zone **z;
+	struct zone *zone;
 
 	sc->all_unreclaimable = 1;
-	for (i = 0; zones[i] != NULL; i++) {
-		struct zone *zone = zones[i];
-
+	for_each_zone_zonelist(zone, z, zonelist, high_zoneidx) {
 		if (!populated_zone(zone))
 			continue;
 		/*
@@ -1333,8 +1331,9 @@ static unsigned long do_try_to_free_page
 	unsigned long nr_reclaimed = 0;
 	struct reclaim_state *reclaim_state = current->reclaim_state;
 	unsigned long lru_pages = 0;
-	struct zone **zones = zonelist->zones;
-	int i;
+	struct zone **z;
+	struct zone *zone;
+	enum zone_type high_zoneidx = gfp_zone(gfp_mask);
 
 	if (scan_global_lru(sc))
 		count_vm_event(ALLOCSTALL);
@@ -1342,8 +1341,7 @@ static unsigned long do_try_to_free_page
 	 * mem_cgroup will not do shrink_slab.
 	 */
 	if (scan_global_lru(sc)) {
-		for (i = 0; zones[i] != NULL; i++) {
-			struct zone *zone = zones[i];
+		for_each_zone_zonelist(zone, z, zonelist, high_zoneidx) {
 
 			if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL))
 				continue;
@@ -1409,8 +1407,7 @@ out:
 		priority = 0;
 
 	if (scan_global_lru(sc)) {
-		for (i = 0; zones[i] != NULL; i++) {
-			struct zone *zone = zones[i];
+		for_each_zone_zonelist(zone, z, zonelist, high_zoneidx) {
 
 			if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL))
 				continue;

--
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>

maintainer
#10 lore raw

Re: [PATCH 4/6] Use two zonelist that are filtered by GFP mask

Quoted 29 lines · On Wed, 27 Feb 2008 16:47:34 -0500 Lee Schermerhorn <[email protected]>…
include/linux/mmzone.h +29 -0
+/* Returns the first zone at or below highest_zoneidx in a zonelist */
+static inline struct zone **first_zones_zonelist(struct zonelist *zonelist,
+					enum zone_type highest_zoneidx)
+{
+	struct zone **z;
+
+	/* Find the first suitable zone to use for the allocation */
+	z = zonelist->zones;
+	while (*z && zone_idx(*z) > highest_zoneidx)
+		z++;
+
+	return z;
+}
+
+/* Returns the next zone at or below highest_zoneidx in a zonelist */
+static inline struct zone **next_zones_zonelist(struct zone **z,
+					enum zone_type highest_zoneidx)
+{
+	/* Find the next suitable zone to use for the allocation */
+	while (*z && zone_idx(*z) > highest_zoneidx)
+		z++;
+
+	return z;
+}
+
+/**
+ * for_each_zone_zonelist - helper macro to iterate over valid zones in a zonelist at or below a given zone index
+ * @zone - The current zone in the iterator
+ * @z - The current pointer within zonelist->zones being iterated

include/linux/mmzone.h extern struct zone *next_zone(struct zon · line 755

Quoted 10 lines
include/linux/mmzone.h +10 -0
+ * @zlist - The zonelist being iterated
+ * @highidx - The zone index of the highest zone to return
+ *
+ * This iterator iterates though all zones at or below a given zone index.
+ */
+#define for_each_zone_zonelist(zone, z, zlist, highidx) \
+	for (z = first_zones_zonelist(zlist, highidx), zone = *z++;	\
+		zone;							\
+		z = next_zones_zonelist(z, highidx), zone = *z++)
+

omygawd will that thing generate a lot of code!

It has four call sites in mm/oom_kill.c and the overall patchset increases mm/oom_kill.o's text section (x86_64 allmodconfig) from 3268 bytes to 3845.

vmscan.o and page_alloc.o also grew a lot. otoh total vmlinux bloat from the patchset is only around 700 bytes, so I expect that with a little less insanity we could actually get an aggregate improvement here.

Some of the inlining in mmzone.h is just comical. Some of it is obvious (first_zones_zonelist) and some of it is less obvious (pfn_present).

I applied these for testing but I really don't think we should be merging such easily-fixed regressions into mainline. Could someone please take a look at de-porking core MM?

Also, I switched all your Tested-by:s to Signed-off-by:s. You were on the delivery path, so s-o-b is the appropriate tag. I would like to believe that Signed-off-by: implies Tested-by: anyway (rofl).

--
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>

#11 lore raw

Re: [PATCH 4/6] Use two zonelist that are filtered by GFP mask

Quoted 48 lines · Andrew Morton
Quoted 29 lines · On Wed, 27 Feb 2008 16:47:34 -0500 Lee Schermerhorn <[email protected]>…
include/linux/mmzone.h +29 -0
+/* Returns the first zone at or below highest_zoneidx in a zonelist */
+static inline struct zone **first_zones_zonelist(struct zonelist *zonelist,
+					enum zone_type highest_zoneidx)
+{
+	struct zone **z;
+
+	/* Find the first suitable zone to use for the allocation */
+	z = zonelist->zones;
+	while (*z && zone_idx(*z) > highest_zoneidx)
+		z++;
+
+	return z;
+}
+
+/* Returns the next zone at or below highest_zoneidx in a zonelist */
+static inline struct zone **next_zones_zonelist(struct zone **z,
+					enum zone_type highest_zoneidx)
+{
+	/* Find the next suitable zone to use for the allocation */
+	while (*z && zone_idx(*z) > highest_zoneidx)
+		z++;
+
+	return z;
+}
+
+/**
+ * for_each_zone_zonelist - helper macro to iterate over valid zones in a zonelist at or below a given zone index
+ * @zone - The current zone in the iterator
+ * @z - The current pointer within zonelist->zones being iterated

include/linux/mmzone.h extern struct zone *next_zone(struct zon · line 755

Quoted 10 lines
include/linux/mmzone.h +10 -0
+ * @zlist - The zonelist being iterated
+ * @highidx - The zone index of the highest zone to return
+ *
+ * This iterator iterates though all zones at or below a given zone index.
+ */
+#define for_each_zone_zonelist(zone, z, zlist, highidx) \
+	for (z = first_zones_zonelist(zlist, highidx), zone = *z++;	\
+		zone;							\
+		z = next_zones_zonelist(z, highidx), zone = *z++)
+

omygawd will that thing generate a lot of code!

It has four call sites in mm/oom_kill.c and the overall patchset increases mm/oom_kill.o's text section (x86_64 allmodconfig) from 3268 bytes to 3845.

Quoted 6 lines

vmscan.o and page_alloc.o also grew a lot. otoh total vmlinux bloat from the patchset is only around 700 bytes, so I expect that with a little less insanity we could actually get an aggregate improvement here.

Some of the inlining in mmzone.h is just comical. Some of it is obvious (first_zones_zonelist) and some of it is less obvious (pfn_present).

Yeah, Mel said he was really reaching to avoid performance regression in this set.

Quoted 4 lines

I applied these for testing but I really don't think we should be merging such easily-fixed regressions into mainline. Could someone please take a look at de-porking core MM?

OK, Mel should be back real soon now, and I'll take a look as well. At this point, we just wanted to get some more testing in -mm.

Quoted 5 lines

Also, I switched all your Tested-by:s to Signed-off-by:s. You were on the delivery path, so s-o-b is the appropriate tag. I would like to believe that Signed-off-by: implies Tested-by: anyway (rofl).

Well, this is not the first time I've been tagged with 's-o-b' and probably not the last :-).

Lee

Quoted 1 line

--
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>

#12 lore raw

Re: [PATCH 4/6] Use two zonelist that are filtered by GFP mask

Quoted 16 lines · On Thu, 28 Feb 2008 16:53:58 -0500 Lee Schermerhorn <[email protected]>…
Quoted 11 lines

omygawd will that thing generate a lot of code!

It has four call sites in mm/oom_kill.c and the overall patchset increases mm/oom_kill.o's text section (x86_64 allmodconfig) from 3268 bytes to 3845.

vmscan.o and page_alloc.o also grew a lot. otoh total vmlinux bloat from the patchset is only around 700 bytes, so I expect that with a little less insanity we could actually get an aggregate improvement here.

Some of the inlining in mmzone.h is just comical. Some of it is obvious (first_zones_zonelist) and some of it is less obvious (pfn_present).

Yeah, Mel said he was really reaching to avoid performance regression in this set.

Quoted 1 line
Quoted 7 lines
Quoted 3 lines

I applied these for testing but I really don't think we should be merging such easily-fixed regressions into mainline. Could someone please take a look at de-porking core MM?

OK, Mel should be back real soon now, and I'll take a look as well. At this point, we just wanted to get some more testing in -mm.

maybe mm/mmzone.c can be candidate to put these into.

Thanks,
-Kame

--
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>

#13 lore raw

Re: [PATCH 4/6] Use two zonelist that are filtered by GFP mask

In reply to Andrew Morton

On (28/02/08 13:32), Andrew Morton didst pronounce:

Quoted 45 lines
Quoted 29 lines · On Wed, 27 Feb 2008 16:47:34 -0500 Lee Schermerhorn <[email protected]>…
include/linux/mmzone.h +29 -0
+/* Returns the first zone at or below highest_zoneidx in a zonelist */
+static inline struct zone **first_zones_zonelist(struct zonelist *zonelist,
+					enum zone_type highest_zoneidx)
+{
+	struct zone **z;
+
+	/* Find the first suitable zone to use for the allocation */
+	z = zonelist->zones;
+	while (*z && zone_idx(*z) > highest_zoneidx)
+		z++;
+
+	return z;
+}
+
+/* Returns the next zone at or below highest_zoneidx in a zonelist */
+static inline struct zone **next_zones_zonelist(struct zone **z,
+					enum zone_type highest_zoneidx)
+{
+	/* Find the next suitable zone to use for the allocation */
+	while (*z && zone_idx(*z) > highest_zoneidx)
+		z++;
+
+	return z;
+}
+
+/**
+ * for_each_zone_zonelist - helper macro to iterate over valid zones in a zonelist at or below a given zone index
+ * @zone - The current zone in the iterator
+ * @z - The current pointer within zonelist->zones being iterated

include/linux/mmzone.h extern struct zone *next_zone(struct zon · line 755

Quoted 10 lines
include/linux/mmzone.h +10 -0
+ * @zlist - The zonelist being iterated
+ * @highidx - The zone index of the highest zone to return
+ *
+ * This iterator iterates though all zones at or below a given zone index.
+ */
+#define for_each_zone_zonelist(zone, z, zlist, highidx) \
+	for (z = first_zones_zonelist(zlist, highidx), zone = *z++;	\
+		zone;							\
+		z = next_zones_zonelist(z, highidx), zone = *z++)
+

omygawd will that thing generate a lot of code!

Quoted 3 lines

It has four call sites in mm/oom_kill.c and the overall patchset increases mm/oom_kill.o's text section (x86_64 allmodconfig) from 3268 bytes to 3845.

Yeah... that's pretty bad. They were inlined to avoid function call overhead when trying to avoid any additional performance overhead but the text overhead is not helping either. I'll start looking at things to uninline and see what can be gained text-reduction wise without mucking performance.

Quoted 16 lines

vmscan.o and page_alloc.o also grew a lot. otoh total vmlinux bloat from the patchset is only around 700 bytes, so I expect that with a little less insanity we could actually get an aggregate improvement here.

Some of the inlining in mmzone.h is just comical. Some of it is obvious (first_zones_zonelist) and some of it is less obvious (pfn_present).

I applied these for testing but I really don't think we should be merging such easily-fixed regressions into mainline. Could someone please take a look at de-porking core MM?

Also, I switched all your Tested-by:s to Signed-off-by:s. You were on the delivery path, so s-o-b is the appropriate tag. I would like to believe that Signed-off-by: implies Tested-by: anyway (rofl).

Signature · 9 lines
Mel Gorman
Part-time Phd Student                          Linux Technology Center
University of Limerick                         IBM Dublin Software Lab

--
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>
#14 lore raw

Re: [PATCH 4/6] Use two zonelist that are filtered by GFP mask

Quoted 46 lines · Mel Gorman

On (28/02/08 13:32), Andrew Morton didst pronounce:

Quoted 45 lines
Quoted 29 lines · On Wed, 27 Feb 2008 16:47:34 -0500 Lee Schermerhorn <[email protected]>…
include/linux/mmzone.h +29 -0
+/* Returns the first zone at or below highest_zoneidx in a zonelist */
+static inline struct zone **first_zones_zonelist(struct zonelist *zonelist,
+					enum zone_type highest_zoneidx)
+{
+	struct zone **z;
+
+	/* Find the first suitable zone to use for the allocation */
+	z = zonelist->zones;
+	while (*z && zone_idx(*z) > highest_zoneidx)
+		z++;
+
+	return z;
+}
+
+/* Returns the next zone at or below highest_zoneidx in a zonelist */
+static inline struct zone **next_zones_zonelist(struct zone **z,
+					enum zone_type highest_zoneidx)
+{
+	/* Find the next suitable zone to use for the allocation */
+	while (*z && zone_idx(*z) > highest_zoneidx)
+		z++;
+
+	return z;
+}
+
+/**
+ * for_each_zone_zonelist - helper macro to iterate over valid zones in a zonelist at or below a given zone index
+ * @zone - The current zone in the iterator
+ * @z - The current pointer within zonelist->zones being iterated

include/linux/mmzone.h extern struct zone *next_zone(struct zon · line 755

Quoted 10 lines
include/linux/mmzone.h +10 -0
+ * @zlist - The zonelist being iterated
+ * @highidx - The zone index of the highest zone to return
+ *
+ * This iterator iterates though all zones at or below a given zone index.
+ */
+#define for_each_zone_zonelist(zone, z, zlist, highidx) \
+	for (z = first_zones_zonelist(zlist, highidx), zone = *z++;	\
+		zone;							\
+		z = next_zones_zonelist(z, highidx), zone = *z++)
+

omygawd will that thing generate a lot of code!

Quoted 12 lines
Quoted 3 lines

It has four call sites in mm/oom_kill.c and the overall patchset increases mm/oom_kill.o's text section (x86_64 allmodconfig) from 3268 bytes to 3845.

Yeah... that's pretty bad. They were inlined to avoid function call overhead when trying to avoid any additional performance overhead but the text overhead is not helping either. I'll start looking at things to uninline and see what can be gained text-reduction wise without mucking performance.

Quoted 3 lines

vmscan.o and page_alloc.o also grew a lot. otoh total vmlinux bloat from the patchset is only around 700 bytes, so I expect that with a little less insanity we could actually get an aggregate improvement here.

Mel:

Thinking about this:

for_each_zone_zonelist():

Seems like the call sites to this macro are not hot paths, so maybe these can call out to a zonelist iterator func in page_alloc.c or, as Kame-san suggested, mmzone.c.

+ oom_kill and vmscan call sites: if these are hot, we're already in, uh..., slow mode.

+ usage in slab.c and slub.c appears to be the fallback/slow path. Christoph can chime in, here, if he disagrees.

+ in page_alloc.c: waking up of kswapd and counting free zone pages [mostly for init code] don't appear to be fast paths.

+ The call site in hugetlb.c is in the huge-page allocation path, which is under a global spinlock. So, any slowdown here could result in longer lock hold time and higher contention. But, I have to believe that in the grand scheme of things, huge-page allocation is not that hot. [Someone faulting in terabytes of hugepages might contest that.]

That leaves the call to for_each_zone_zonelist_nodemask() in get_page_from_freelist(). This might be deserving of inlining?

If this works out, we could end up with these macros being inlined in only 2 places: get_page_from_freelist() and a to-be-designed zonelist iterator function. [In fact, I believe that such an iterator need not expose the details of zonelists outside of page_alloc/mmzone, but that would require more rework of the call sites, and additional helper functions. Maybe someday...]

Comments?

Right now, I've got to build/test the latest reclaim scalability patches that Rik posted, and clean up the issues already pointed out. If you don't get to this, I can look at it further next week.

Lee

Quoted 9 lines
Quoted 9 lines

Some of the inlining in mmzone.h is just comical. Some of it is obvious (first_zones_zonelist) and some of it is less obvious (pfn_present).

I applied these for testing but I really don't think we should be merging such easily-fixed regressions into mainline. Could someone please take a look at de-porking core MM?

Quoted 5 lines
Quoted 4 lines

Also, I switched all your Tested-by:s to Signed-off-by:s. You were on the delivery path, so s-o-b is the appropriate tag. I would like to believe that Signed-off-by: implies Tested-by: anyway (rofl).

--
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>

#15 lore raw

Re: [PATCH 4/6] Use two zonelist that are filtered by GFP mask

Quoted 2 lines · Lee Schermerhorn

+ usage in slab.c and slub.c appears to be the fallback/slow path. Christoph can chime in, here, if he disagrees.

Correct. And in 2.6.25 slub will start to buffer page allocator allocs in order to avoid that current issue with 4k allocs being slower than slab due to page allocator inefficiency.

I think we need a new fastpath for the page allocator! (No not me, I am already handing a gazillion patches).

--
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>

#16 lore raw

Re: [PATCH 4/6] Use two zonelist that are filtered by GFP mask

In reply to Lee Schermerhorn

On (29/02/08 10:48), Lee Schermerhorn didst pronounce:

Quoted 62 lines · Mel Gorman
Quoted 46 lines

On (28/02/08 13:32), Andrew Morton didst pronounce:

Quoted 45 lines
Quoted 29 lines · On Wed, 27 Feb 2008 16:47:34 -0500 Lee Schermerhorn <[email protected]>…
include/linux/mmzone.h +29 -0
+/* Returns the first zone at or below highest_zoneidx in a zonelist */
+static inline struct zone **first_zones_zonelist(struct zonelist *zonelist,
+					enum zone_type highest_zoneidx)
+{
+	struct zone **z;
+
+	/* Find the first suitable zone to use for the allocation */
+	z = zonelist->zones;
+	while (*z && zone_idx(*z) > highest_zoneidx)
+		z++;
+
+	return z;
+}
+
+/* Returns the next zone at or below highest_zoneidx in a zonelist */
+static inline struct zone **next_zones_zonelist(struct zone **z,
+					enum zone_type highest_zoneidx)
+{
+	/* Find the next suitable zone to use for the allocation */
+	while (*z && zone_idx(*z) > highest_zoneidx)
+		z++;
+
+	return z;
+}
+
+/**
+ * for_each_zone_zonelist - helper macro to iterate over valid zones in a zonelist at or below a given zone index
+ * @zone - The current zone in the iterator
+ * @z - The current pointer within zonelist->zones being iterated

include/linux/mmzone.h extern struct zone *next_zone(struct zon · line 755

Quoted 10 lines
include/linux/mmzone.h +10 -0
+ * @zlist - The zonelist being iterated
+ * @highidx - The zone index of the highest zone to return
+ *
+ * This iterator iterates though all zones at or below a given zone index.
+ */
+#define for_each_zone_zonelist(zone, z, zlist, highidx) \
+	for (z = first_zones_zonelist(zlist, highidx), zone = *z++;	\
+		zone;							\
+		z = next_zones_zonelist(z, highidx), zone = *z++)
+

omygawd will that thing generate a lot of code!

Quoted 12 lines
Quoted 3 lines

It has four call sites in mm/oom_kill.c and the overall patchset increases mm/oom_kill.o's text section (x86_64 allmodconfig) from 3268 bytes to 3845.

Yeah... that's pretty bad. They were inlined to avoid function call overhead when trying to avoid any additional performance overhead but the text overhead is not helping either. I'll start looking at things to uninline and see what can be gained text-reduction wise without mucking performance.

Quoted 3 lines

vmscan.o and page_alloc.o also grew a lot. otoh total vmlinux bloat from the patchset is only around 700 bytes, so I expect that with a little less insanity we could actually get an aggregate improvement here.

Mel:

Quoted 8 lines

Thinking about this:

for_each_zone_zonelist():

Seems like the call sites to this macro are not hot paths, so maybe these can call out to a zonelist iterator func in page_alloc.c or, as Kame-san suggested, mmzone.c.

I am trying an unlined version in mmzone.c to see what is looks like. As expected there is less text bloat and I'll know in another day whether it makes a performance difference or not. As you note below, the majority of call sites are not in hot-paths as such.

Quoted 48 lines

+ oom_kill and vmscan call sites: if these are hot, we're already in, uh..., slow mode.

+ usage in slab.c and slub.c appears to be the fallback/slow path. Christoph can chime in, here, if he disagrees.

+ in page_alloc.c: waking up of kswapd and counting free zone pages [mostly for init code] don't appear to be fast paths.

+ The call site in hugetlb.c is in the huge-page allocation path, which is under a global spinlock. So, any slowdown here could result in longer lock hold time and higher contention. But, I have to believe that in the grand scheme of things, huge-page allocation is not that hot. [Someone faulting in terabytes of hugepages might contest that.]

That leaves the call to for_each_zone_zonelist_nodemask() in get_page_from_freelist(). This might be deserving of inlining?

If this works out, we could end up with these macros being inlined in only 2 places: get_page_from_freelist() and a to-be-designed zonelist iterator function. [In fact, I believe that such an iterator need not expose the details of zonelists outside of page_alloc/mmzone, but that would require more rework of the call sites, and additional helper functions. Maybe someday...]

Comments?

Right now, I've got to build/test the latest reclaim scalability patches that Rik posted, and clean up the issues already pointed out. If you don't get to this, I can look at it further next week.

Lee

Quoted 14 lines
Quoted 13 lines

Some of the inlining in mmzone.h is just comical. Some of it is obvious (first_zones_zonelist) and some of it is less obvious (pfn_present).

I applied these for testing but I really don't think we should be merging such easily-fixed regressions into mainline. Could someone please take a look at de-porking core MM?

Also, I switched all your Tested-by:s to Signed-off-by:s. You were on the delivery path, so s-o-b is the appropriate tag. I would like to believe that Signed-off-by: implies Tested-by: anyway (rofl).

Signature · 9 lines
Mel Gorman
Part-time Phd Student                          Linux Technology Center
University of Limerick                         IBM Dublin Software Lab

--
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>
#17 lore raw

PATCH 1/1 2.6.25-rc3-mm1 - Mempolicy: make dequeue_huge_page_vma() obey MPOL_BIND nodemask

PATCH Mempolicy - make dequeue_huge_page_vma() obey MPOL_BIND nodemask

dequeue_huge_page_vma() is not obeying the MPOL_BIND nodemask with the zonelist rework. It needs to search only zones in the mempolicy nodemask for hugepages.

Use for_each_zone_zonelist_nodemask() instead of
for_each_zone_zonelist().

Note: this will bloat mm/hugetlb.o a bit until Mel reworks the inlining of the for_each_zone... macros and helpers.

Added mempolicy helper function mpol_bind_nodemask() to hide the details of mempolicy from hugetlb and to avoid #ifdef CONFIG_NUMA in dequeue_huge_page_vma().

 include/linux/mempolicy.h |   13 +++++++++++++
 mm/hugetlb.c              |    4 +++-
 2 files changed, 16 insertions(+), 1 deletion(-)
mm/hugetlb.c +3 -1
Index: linux-2.6.25-rc3-mm1/mm/hugetlb.c
===================================================================
@@ -99,8 +99,10 @@ static struct page *dequeue_huge_page_vm
 					htlb_alloc_mask, &mpol);
 	struct zone *zone;
 	struct zoneref *z;
+	nodemask_t *nodemask = mpol_bind_nodemask(mpol);
 
-	for_each_zone_zonelist(zone, z, zonelist, MAX_NR_ZONES - 1) {
+	for_each_zone_zonelist_nodemask(zone, z, zonelist,
+						MAX_NR_ZONES - 1, nodemask) {
 		nid = zone_to_nid(zone);
 		if (cpuset_zone_allowed_softwall(zone, htlb_alloc_mask) &&
 		    !list_empty(&hugepage_freelists[nid])) {
include/linux/mempolicy.h +13 -0
Index: linux-2.6.25-rc3-mm1/include/linux/mempolicy.h
===================================================================
@@ -163,6 +163,14 @@ static inline void check_highest_zone(en
 		policy_zone = k;
 }
 
+static inline nodemask_t *mpol_bind_nodemask(struct mempolicy *mpol)
+{
+	if (mpol->policy == MPOL_BIND)
+		return &mpol->v.nodes;
+	else
+		return NULL;
+}
+
 int do_migrate_pages(struct mm_struct *mm,
 	const nodemask_t *from_nodes, const nodemask_t *to_nodes, int flags);
 
@@ -255,6 +263,11 @@ static inline int do_migrate_pages(struc
 static inline void check_highest_zone(int k)
 {
 }
+
+static inline nodemask_t *mpol_bind_nodemask(struct mempolicy *mpol)
+{
+	return NULL;
+}
 #endif /* CONFIG_NUMA */
 #endif /* __KERNEL__ */
 

--
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>

#18 lore raw

Re: [PATCH] 2.6.25-rc3-mm1 - Mempolicy: make dequeue_huge_page_vma() obey MPOL_BIND nodemask

Quoted 27 lines · Lee Schermerhorn

PATCH Mempolicy - make dequeue_huge_page_vma() obey MPOL_BIND nodemask

dequeue_huge_page_vma() is not obeying the MPOL_BIND nodemask with the zonelist rework. It needs to search only zones in the mempolicy nodemask for hugepages.

Use for_each_zone_zonelist_nodemask() instead of
for_each_zone_zonelist().

Note: this will bloat mm/hugetlb.o a bit until Mel reworks the inlining of the for_each_zone... macros and helpers.

Added mempolicy helper function mpol_bind_nodemask() to hide the details of mempolicy from hugetlb and to avoid #ifdef CONFIG_NUMA in dequeue_huge_page_vma().

 include/linux/mempolicy.h |   13 +++++++++++++
 mm/hugetlb.c              |    4 +++-
 2 files changed, 16 insertions(+), 1 deletion(-)

Index: linux-2.6.25-rc3-mm1/mm/hugetlb.c
===================================================================

mm/hugetlb.c static struct page *dequeue_huge_page_vm · line 102

Quoted 4 lines
mm/hugetlb.c +1 -0
@@ -99,8 +99,10 @@ static struct page *dequeue_huge_page_vm
 					htlb_alloc_mask, &mpol);
 	struct zone *zone;
 	struct zoneref *z;
+	nodemask_t *nodemask = mpol_bind_nodemask(mpol);

We get this mpol from huge_zonelist(). Would it perhaps make sense to pass the nodemask as a parameter, too, to huge_zonelist(), rather than adding mpol_bind_nodemask()? This is the only user of it in-tree.

Thanks,
Nish

Signature · 8 lines
Nishanth Aravamudan <[email protected]>
IBM Linux Technology Center

--
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>
#19 lore raw

Re: [PATCH] 2.6.25-rc3-mm1 - Mempolicy: make dequeue_huge_page_vma() obey MPOL_BIND nodemask

Quoted 23 lines · Nishanth Aravamudan
Quoted 22 lines · Lee Schermerhorn

PATCH Mempolicy - make dequeue_huge_page_vma() obey MPOL_BIND nodemask

dequeue_huge_page_vma() is not obeying the MPOL_BIND nodemask with the zonelist rework. It needs to search only zones in the mempolicy nodemask for hugepages.

Use for_each_zone_zonelist_nodemask() instead of
for_each_zone_zonelist().

Note: this will bloat mm/hugetlb.o a bit until Mel reworks the inlining of the for_each_zone... macros and helpers.

Added mempolicy helper function mpol_bind_nodemask() to hide the details of mempolicy from hugetlb and to avoid #ifdef CONFIG_NUMA in dequeue_huge_page_vma().

 include/linux/mempolicy.h |   13 +++++++++++++
 mm/hugetlb.c              |    4 +++-
 2 files changed, 16 insertions(+), 1 deletion(-)
Quoted 13 lines

mm/hugetlb.c static struct page *dequeue_huge_page_vm · line 102

Quoted 9 lines

Index: linux-2.6.25-rc3-mm1/mm/hugetlb.c
===================================================================

mm/hugetlb.c +1 -0
@@ -99,8 +99,10 @@ static struct page *dequeue_huge_page_vm
 					htlb_alloc_mask, &mpol);
 	struct zone *zone;
 	struct zoneref *z;
+	nodemask_t *nodemask = mpol_bind_nodemask(mpol);

We get this mpol from huge_zonelist(). Would it perhaps make sense to pass the nodemask as a parameter, too, to huge_zonelist(), rather than adding mpol_bind_nodemask()? This is the only user of it in-tree.

Nish:

I thought of that. I didn't go that way because I'd either need to pass a [pointer to a pointer to] a nodemask in addition to the [pointer to a pointer to] the mpol, so that I can release the reference on the mpol after the allocation is finished; or I'd need to copy the nodemask [which can get pretty big] in the allocation path. I wanted to avoid both of those. I suppose I could be convinced that one or the other of those options is better than the single use helper function. What do you think?

Lee

--
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>

#20 lore raw

Re: [PATCH] 2.6.25-rc3-mm1 - Mempolicy: make dequeue_huge_page_vma() obey MPOL_BIND nodemask

Quoted 38 lines · Lee Schermerhorn
Quoted 23 lines · Nishanth Aravamudan
Quoted 22 lines · Lee Schermerhorn

PATCH Mempolicy - make dequeue_huge_page_vma() obey MPOL_BIND nodemask

dequeue_huge_page_vma() is not obeying the MPOL_BIND nodemask with the zonelist rework. It needs to search only zones in the mempolicy nodemask for hugepages.

Use for_each_zone_zonelist_nodemask() instead of
for_each_zone_zonelist().

Note: this will bloat mm/hugetlb.o a bit until Mel reworks the inlining of the for_each_zone... macros and helpers.

Added mempolicy helper function mpol_bind_nodemask() to hide the details of mempolicy from hugetlb and to avoid #ifdef CONFIG_NUMA in dequeue_huge_page_vma().

 include/linux/mempolicy.h |   13 +++++++++++++
 mm/hugetlb.c              |    4 +++-
 2 files changed, 16 insertions(+), 1 deletion(-)
Quoted 13 lines

mm/hugetlb.c static struct page *dequeue_huge_page_vm · line 102

Quoted 9 lines

Index: linux-2.6.25-rc3-mm1/mm/hugetlb.c
===================================================================

mm/hugetlb.c +1 -0
@@ -99,8 +99,10 @@ static struct page *dequeue_huge_page_vm
 					htlb_alloc_mask, &mpol);
 	struct zone *zone;
 	struct zoneref *z;
+	nodemask_t *nodemask = mpol_bind_nodemask(mpol);

We get this mpol from huge_zonelist(). Would it perhaps make sense to pass the nodemask as a parameter, too, to huge_zonelist(), rather than adding mpol_bind_nodemask()? This is the only user of it in-tree.

Quoted 6 lines

Nish:

I thought of that. I didn't go that way because I'd either need to pass a [pointer to a pointer to] a nodemask in addition to the [pointer to a pointer to] the mpol, so that I can release the reference on the mpol after the allocation is finished;

See I looked at that and thought: "We're already passing a pointer to a pointer to mpol, so a pointer to a pointer to a nodemask shouldn't be that big of deal. This is the one call-site, as well. The idea being, we've pushed as much of the zonelist/nodemask knowledge into huge_zonelist(), keeping hugetlb.c relatively clear of it. Maybe it doesn't matter, was really just a question. Not sure what other folks think.

Quoted 4 lines

or I'd need to copy the nodemask [which can get pretty big] in the allocation path. I wanted to avoid both of those. I suppose I could be convinced that one or the other of those options is better than the single use helper function. What do you think?

What you have is fine, I guess -- and has been picked up by Andrew.

Thanks,
Nish

--
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>

#21 lore raw

Re: [PATCH] 2.6.25-rc3-mm1 - Mempolicy: make dequeue_huge_page_vma() obey MPOL_BIND nodemask

Quoted 41 lines · Nishanth Aravamudan
Quoted 40 lines · Lee Schermerhorn
Quoted 23 lines · Nishanth Aravamudan
Quoted 22 lines · Lee Schermerhorn

PATCH Mempolicy - make dequeue_huge_page_vma() obey MPOL_BIND nodemask

dequeue_huge_page_vma() is not obeying the MPOL_BIND nodemask with the zonelist rework. It needs to search only zones in the mempolicy nodemask for hugepages.

Use for_each_zone_zonelist_nodemask() instead of
for_each_zone_zonelist().

Note: this will bloat mm/hugetlb.o a bit until Mel reworks the inlining of the for_each_zone... macros and helpers.

Added mempolicy helper function mpol_bind_nodemask() to hide the details of mempolicy from hugetlb and to avoid #ifdef CONFIG_NUMA in dequeue_huge_page_vma().

 include/linux/mempolicy.h |   13 +++++++++++++
 mm/hugetlb.c              |    4 +++-
 2 files changed, 16 insertions(+), 1 deletion(-)
Quoted 13 lines

mm/hugetlb.c static struct page *dequeue_huge_page_vm · line 102

Quoted 9 lines

Index: linux-2.6.25-rc3-mm1/mm/hugetlb.c
===================================================================

mm/hugetlb.c +1 -0
@@ -99,8 +99,10 @@ static struct page *dequeue_huge_page_vm
 					htlb_alloc_mask, &mpol);
 	struct zone *zone;
 	struct zoneref *z;
+	nodemask_t *nodemask = mpol_bind_nodemask(mpol);

We get this mpol from huge_zonelist(). Would it perhaps make sense to pass the nodemask as a parameter, too, to huge_zonelist(), rather than adding mpol_bind_nodemask()? This is the only user of it in-tree.

Nish:

Quoted 8 lines
Quoted 4 lines

I thought of that. I didn't go that way because I'd either need to pass a [pointer to a pointer to] a nodemask in addition to the [pointer to a pointer to] the mpol, so that I can release the reference on the mpol after the allocation is finished;

See I looked at that and thought: "We're already passing a pointer to a pointer to mpol, so a pointer to a pointer to a nodemask shouldn't be that big of deal.

:-) I looked at that and thought: "Yuck! we're already passing a pointer to a pointer, ... I don't want to add another one." Don't know why I have such an aversion to passing results back like that.

Quoted 4 lines

This is the one call-site, as well. The idea being, we've pushed as much of the zonelist/nodemask knowledge into huge_zonelist(), keeping hugetlb.c relatively clear of it. Maybe it doesn't matter, was really just a question.

No, I do see your point. As I say, I had the same thoughts.

Quoted 10 lines

Not sure what other folks
think.

Quoted 4 lines

or I'd need to copy the nodemask [which can get pretty big] in the allocation path. I wanted to avoid both of those. I suppose I could be convinced that one or the other of those options is better than the single use helper function. What do you think?

What you have is fine, I guess -- and has been picked up by Andrew.

We'll need more work in this area, I think. I can fix it up then. I'll try a patch and see how it "feels"...

Thanks for your attention,
Lee

--
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>

#22 lore raw

PATCH 1/1 Mempolicy: make dequeue_huge_page_vma() obey MPOL_BIND nodemask rework

In reply to Nishanth Aravamudan

Fix for earlier patch:
"mempolicy-make-dequeue_huge_page_vma-obey-bind-policy"

Against: 2.6.25-rc3-mm1 atop the above patch.

As suggested by Nish Aravamudan, remove the mpol_bind_nodemask() helper and return a pointer to the policy node mask from huge_zonelist for MPOL_BIND. This hides more of the mempolicy quirks from hugetlb.

In making this change, I noticed that the huge_zonelist() stub for !NUMA wasn't nulling out the mpol. Added that as well.

 include/linux/mempolicy.h |   21 ++++++---------------
 mm/hugetlb.c              |    4 ++--
 mm/mempolicy.c            |   18 ++++++++++++------
 3 files changed, 20 insertions(+), 23 deletions(-)
include/linux/mempolicy.h +6 -15
Index: linux-2.6.25-rc3-mm1/include/linux/mempolicy.h
===================================================================
@@ -152,7 +152,8 @@ extern void mpol_fix_fork_child_flag(str
 
 extern struct mempolicy default_policy;
 extern struct zonelist *huge_zonelist(struct vm_area_struct *vma,
-		unsigned long addr, gfp_t gfp_flags, struct mempolicy **mpol);
+				unsigned long addr, gfp_t gfp_flags,
+				struct mempolicy **mpol, nodemask_t **nodemask);
 extern unsigned slab_node(struct mempolicy *policy);
 
 extern enum zone_type policy_zone;
@@ -163,14 +164,6 @@ static inline void check_highest_zone(en
 		policy_zone = k;
 }
 
-static inline nodemask_t *mpol_bind_nodemask(struct mempolicy *mpol)
-{
-	if (mpol->policy == MPOL_BIND)
-		return &mpol->v.nodes;
-	else
-		return NULL;
-}
-
 int do_migrate_pages(struct mm_struct *mm,
 	const nodemask_t *from_nodes, const nodemask_t *to_nodes, int flags);
 
@@ -248,8 +241,11 @@ static inline void mpol_fix_fork_child_f
 }
 
 static inline struct zonelist *huge_zonelist(struct vm_area_struct *vma,
- 		unsigned long addr, gfp_t gfp_flags, struct mempolicy **mpol)
+				unsigned long addr, gfp_t gfp_flags,
+				struct mempolicy **mpol, nodemask_t **nodemask)
 {
+	*mpol = NULL;
+	*nodemask = NULL;
 	return node_zonelist(0, gfp_flags);
 }
 
@@ -263,11 +259,6 @@ static inline int do_migrate_pages(struc
 static inline void check_highest_zone(int k)
 {
 }
-
-static inline nodemask_t *mpol_bind_nodemask(struct mempolicy *mpol)
-{
-	return NULL;
-}
 #endif /* CONFIG_NUMA */
 #endif /* __KERNEL__ */
 
mm/hugetlb.c +2 -2
Index: linux-2.6.25-rc3-mm1/mm/hugetlb.c
===================================================================
@@ -95,11 +95,11 @@ static struct page *dequeue_huge_page_vm
 	int nid;
 	struct page *page = NULL;
 	struct mempolicy *mpol;
+	nodemask_t *nodemask;
 	struct zonelist *zonelist = huge_zonelist(vma, address,
-					htlb_alloc_mask, &mpol);
+					htlb_alloc_mask, &mpol, &nodemask);
 	struct zone *zone;
 	struct zoneref *z;
-	nodemask_t *nodemask = mpol_bind_nodemask(mpol);
 
 	for_each_zone_zonelist_nodemask(zone, z, zonelist,
 						MAX_NR_ZONES - 1, nodemask) {
mm/mempolicy.c +12 -6
Index: linux-2.6.25-rc3-mm1/mm/mempolicy.c
===================================================================
@@ -1276,25 +1276,31 @@ static inline unsigned interleave_nid(st
  * @vma = virtual memory area whose policy is sought
  * @addr = address in @vma for shared policy lookup and interleave policy
  * @gfp_flags = for requested zone
- * @mpol = pointer to mempolicy pointer for reference counted 'BIND policy
+ * @mpol = pointer to mempolicy pointer for reference counted mempolicy
+ * @nodemask = pointer to nodemask pointer for MPOL_BIND nodemask
  *
  * Returns a zonelist suitable for a huge page allocation.
- * If the effective policy is 'BIND, returns pointer to policy's zonelist.
+ * If the effective policy is 'BIND, returns pointer to local node's zonelist,
+ * and a pointer to the mempolicy's @nodemask for filtering the zonelist.
  * If it is also a policy for which get_vma_policy() returns an extra
- * reference, we must hold that reference until after allocation.
+ * reference, we must hold that reference until after the allocation.
  * In that case, return policy via @mpol so hugetlb allocation can drop
- * the reference.  For non-'BIND referenced policies, we can/do drop the
+ * the reference. For non-'BIND referenced policies, we can/do drop the
  * reference here, so the caller doesn't need to know about the special case
  * for default and current task policy.
  */
 struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
-				gfp_t gfp_flags, struct mempolicy **mpol)
+				gfp_t gfp_flags, struct mempolicy **mpol,
+				nodemask_t **nodemask)
 {
 	struct mempolicy *pol = get_vma_policy(current, vma, addr);
 	struct zonelist *zl;
 
 	*mpol = NULL;		/* probably no unref needed */
-	if (pol->policy == MPOL_INTERLEAVE) {
+	*nodemask = NULL;	/* assume !MPOL_BIND */
+	if (pol->policy == MPOL_BIND) {
+			*nodemask = &pol->v.nodes;
+	} else if (pol->policy == MPOL_INTERLEAVE) {
 		unsigned nid;
 
 		nid = interleave_nid(pol, vma, addr, HPAGE_SHIFT);

--
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>

#23 lore raw

Re: [PATCH] Mempolicy: make dequeue_huge_page_vma() obey MPOL_BIND nodemask rework

Quoted 6 lines · Lee Schermerhorn

Fix for earlier patch:
"mempolicy-make-dequeue_huge_page_vma-obey-bind-policy"

Against: 2.6.25-rc3-mm1 atop the above patch.

Quoted 7 lines

As suggested by Nish Aravamudan, remove the mpol_bind_nodemask() helper and return a pointer to the policy node mask from huge_zonelist for MPOL_BIND. This hides more of the mempolicy quirks from hugetlb.

In making this change, I noticed that the huge_zonelist() stub for !NUMA wasn't nulling out the mpol. Added that as well.

Hrm, I was thinking more of the following (on top of this patch):

mm/mempolicy.c +5 -5
index 4c5d41d..3790f5a 100644
@@ -1298,9 +1298,7 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
 
 	*mpol = NULL;		/* probably no unref needed */
 	*nodemask = NULL;	/* assume !MPOL_BIND */
-	if (pol->policy == MPOL_BIND) {
-			*nodemask = &pol->v.nodes;
-	} else if (pol->policy == MPOL_INTERLEAVE) {
+	if (pol->policy == MPOL_INTERLEAVE) {
 		unsigned nid;
 
 		nid = interleave_nid(pol, vma, addr, HPAGE_SHIFT);
@@ -1310,10 +1308,12 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
 
 	zl = zonelist_policy(GFP_HIGHUSER, pol);
 	if (unlikely(pol != &default_policy && pol != current->mempolicy)) {
-		if (pol->policy != MPOL_BIND)
+		if (pol->policy != MPOL_BIND) {
 			__mpol_free(pol);	/* finished with pol */
-		else
+		} else {
 			*mpol = pol;	/* unref needed after allocation */
+			*nodemask = &pol->v.nodes;
+		}
 	}
 	return zl;
 }

but perhaps that won't do the right thing if pol == current->mempolicy and pol->policy == MPOL_BIND. So something like:

mm/mempolicy.c +6 -7
index 4c5d41d..7eb77e0 100644
@@ -1298,9 +1298,7 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
 
 	*mpol = NULL;		/* probably no unref needed */
 	*nodemask = NULL;	/* assume !MPOL_BIND */
-	if (pol->policy == MPOL_BIND) {
-			*nodemask = &pol->v.nodes;
-	} else if (pol->policy == MPOL_INTERLEAVE) {
+	if (pol->policy == MPOL_INTERLEAVE) {
 		unsigned nid;
 
 		nid = interleave_nid(pol, vma, addr, HPAGE_SHIFT);
@@ -1309,11 +1307,12 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
 	}
 
 	zl = zonelist_policy(GFP_HIGHUSER, pol);
-	if (unlikely(pol != &default_policy && pol != current->mempolicy)) {
-		if (pol->policy != MPOL_BIND)
-			__mpol_free(pol);	/* finished with pol */
-		else
+	if (unlikely(pol != &default_policy && pol != current->mempolicy
+						&& pol->policy != MPOL_BIND))
+		__mpol_free(pol);	/* finished with pol */
+	if (pol->policy == MPOL_BIND) {
 			*mpol = pol;	/* unref needed after allocation */
+			*nodemask = &pol->v.nodes;
 	}
 	return zl;
 }

Still not quite as clean, but I think it's best to keep the *mpol and *nodemask assignments together, as if *mpol is being assigned, that's the only time we should need to set *nodemask, right?

Thanks,
Nish

Signature · 8 lines
Nishanth Aravamudan <[email protected]>
IBM Linux Technology Center

--
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>
#24 lore raw

Re: [PATCH] Mempolicy: make dequeue_huge_page_vma() obey MPOL_BIND nodemask rework

Quoted 48 lines · Nishanth Aravamudan
Quoted 6 lines · Lee Schermerhorn

Fix for earlier patch:
"mempolicy-make-dequeue_huge_page_vma-obey-bind-policy"

Against: 2.6.25-rc3-mm1 atop the above patch.

Quoted 7 lines

As suggested by Nish Aravamudan, remove the mpol_bind_nodemask() helper and return a pointer to the policy node mask from huge_zonelist for MPOL_BIND. This hides more of the mempolicy quirks from hugetlb.

In making this change, I noticed that the huge_zonelist() stub for !NUMA wasn't nulling out the mpol. Added that as well.

Hrm, I was thinking more of the following (on top of this patch):

mm/mempolicy.c +5 -5
index 4c5d41d..3790f5a 100644
@@ -1298,9 +1298,7 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
 
 	*mpol = NULL;		/* probably no unref needed */
 	*nodemask = NULL;	/* assume !MPOL_BIND */
-	if (pol->policy == MPOL_BIND) {
-			*nodemask = &pol->v.nodes;
-	} else if (pol->policy == MPOL_INTERLEAVE) {
+	if (pol->policy == MPOL_INTERLEAVE) {
 		unsigned nid;
 
 		nid = interleave_nid(pol, vma, addr, HPAGE_SHIFT);
@@ -1310,10 +1308,12 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
 
 	zl = zonelist_policy(GFP_HIGHUSER, pol);
 	if (unlikely(pol != &default_policy && pol != current->mempolicy)) {
-		if (pol->policy != MPOL_BIND)
+		if (pol->policy != MPOL_BIND) {
 			__mpol_free(pol);	/* finished with pol */
-		else
+		} else {
 			*mpol = pol;	/* unref needed after allocation */
+			*nodemask = &pol->v.nodes;
+		}
 	}
 	return zl;
 }
Quoted 2 lines

but perhaps that won't do the right thing if pol == current->mempolicy and pol->policy == MPOL_BIND.

Right, you won't return the nodemask for current task policy == MBIND.

Quoted 36 lines

So something like:

mm/mempolicy.c +6 -7
index 4c5d41d..7eb77e0 100644
@@ -1298,9 +1298,7 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
 
 	*mpol = NULL;		/* probably no unref needed */
 	*nodemask = NULL;	/* assume !MPOL_BIND */
-	if (pol->policy == MPOL_BIND) {
-			*nodemask = &pol->v.nodes;
-	} else if (pol->policy == MPOL_INTERLEAVE) {
+	if (pol->policy == MPOL_INTERLEAVE) {
 		unsigned nid;
 
 		nid = interleave_nid(pol, vma, addr, HPAGE_SHIFT);
@@ -1309,11 +1307,12 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
 	}
 
 	zl = zonelist_policy(GFP_HIGHUSER, pol);
-	if (unlikely(pol != &default_policy && pol != current->mempolicy)) {
-		if (pol->policy != MPOL_BIND)
-			__mpol_free(pol);	/* finished with pol */
-		else
+	if (unlikely(pol != &default_policy && pol != current->mempolicy
+						&& pol->policy != MPOL_BIND))
+		__mpol_free(pol);	/* finished with pol */
+	if (pol->policy == MPOL_BIND) {
 			*mpol = pol;	/* unref needed after allocation */
+			*nodemask = &pol->v.nodes;
 	}
 	return zl;
 }
Quoted 3 lines

Still not quite as clean, but I think it's best to keep the *mpol and *nodemask assignments together, as if *mpol is being assigned, that's the only time we should need to set *nodemask, right?

Well, as you've noted, we do have to test MPOL_BIND twice: once to return the nodemask for any 'BIND policy and once to return a non-NULL mpol ONLY if it's MPOL_BIND and we need an unref. However, I wanted to avoid checking the policies twice as well, or storing *nodemask 3rd time.

I think that your second change above is not quite right, either. You're unconditionally returning the policy when the 'mode' == MBIND, even if it does not need a deref. This could result in prematurely freeing the task policy, causing a "use after free" error on next allocation; or even decrementing the reference on the system_default policy, which is probably benign, but not "nice". [Also, check your parentheses...]

Anyway you slice it, it's pretty ugly.

So, for now, I'd like to keep it the way I have it. I'll be sending out a set of patches to rework the reference counting after mempolicy settles down--i.e., Mel's and David's patches, which I'm testing now. That will clean this area up quite a bit, IMO.

Lee

--
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>

#25 lore raw

Re: [PATCH] Mempolicy: make dequeue_huge_page_vma() obey MPOL_BIND nodemask rework

Quoted 108 lines · Lee Schermerhorn
Quoted 48 lines · Nishanth Aravamudan
Quoted 6 lines · Lee Schermerhorn

Fix for earlier patch:
"mempolicy-make-dequeue_huge_page_vma-obey-bind-policy"

Against: 2.6.25-rc3-mm1 atop the above patch.

Quoted 7 lines

As suggested by Nish Aravamudan, remove the mpol_bind_nodemask() helper and return a pointer to the policy node mask from huge_zonelist for MPOL_BIND. This hides more of the mempolicy quirks from hugetlb.

In making this change, I noticed that the huge_zonelist() stub for !NUMA wasn't nulling out the mpol. Added that as well.

Hrm, I was thinking more of the following (on top of this patch):

mm/mempolicy.c +5 -5
index 4c5d41d..3790f5a 100644
@@ -1298,9 +1298,7 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
 
 	*mpol = NULL;		/* probably no unref needed */
 	*nodemask = NULL;	/* assume !MPOL_BIND */
-	if (pol->policy == MPOL_BIND) {
-			*nodemask = &pol->v.nodes;
-	} else if (pol->policy == MPOL_INTERLEAVE) {
+	if (pol->policy == MPOL_INTERLEAVE) {
 		unsigned nid;
 
 		nid = interleave_nid(pol, vma, addr, HPAGE_SHIFT);
@@ -1310,10 +1308,12 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
 
 	zl = zonelist_policy(GFP_HIGHUSER, pol);
 	if (unlikely(pol != &default_policy && pol != current->mempolicy)) {
-		if (pol->policy != MPOL_BIND)
+		if (pol->policy != MPOL_BIND) {
 			__mpol_free(pol);	/* finished with pol */
-		else
+		} else {
 			*mpol = pol;	/* unref needed after allocation */
+			*nodemask = &pol->v.nodes;
+		}
 	}
 	return zl;
 }
Quoted 2 lines

but perhaps that won't do the right thing if pol == current->mempolicy and pol->policy == MPOL_BIND.

Right, you won't return the nodemask for current task policy == MBIND.

Quoted 36 lines

So something like:

mm/mempolicy.c +6 -7
index 4c5d41d..7eb77e0 100644
@@ -1298,9 +1298,7 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
 
 	*mpol = NULL;		/* probably no unref needed */
 	*nodemask = NULL;	/* assume !MPOL_BIND */
-	if (pol->policy == MPOL_BIND) {
-			*nodemask = &pol->v.nodes;
-	} else if (pol->policy == MPOL_INTERLEAVE) {
+	if (pol->policy == MPOL_INTERLEAVE) {
 		unsigned nid;
 
 		nid = interleave_nid(pol, vma, addr, HPAGE_SHIFT);
@@ -1309,11 +1307,12 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
 	}
 
 	zl = zonelist_policy(GFP_HIGHUSER, pol);
-	if (unlikely(pol != &default_policy && pol != current->mempolicy)) {
-		if (pol->policy != MPOL_BIND)
-			__mpol_free(pol);	/* finished with pol */
-		else
+	if (unlikely(pol != &default_policy && pol != current->mempolicy
+						&& pol->policy != MPOL_BIND))
+		__mpol_free(pol);	/* finished with pol */
+	if (pol->policy == MPOL_BIND) {
 			*mpol = pol;	/* unref needed after allocation */
+			*nodemask = &pol->v.nodes;
 	}
 	return zl;
 }
Quoted 3 lines

Still not quite as clean, but I think it's best to keep the *mpol and *nodemask assignments together, as if *mpol is being assigned, that's the only time we should need to set *nodemask, right?

Well, as you've noted, we do have to test MPOL_BIND twice: once to return the nodemask for any 'BIND policy and once to return a non-NULL mpol ONLY if it's MPOL_BIND and we need an unref. However, I wanted to avoid checking the policies twice as well, or storing *nodemask 3rd time.

I think that your second change above is not quite right, either. You're unconditionally returning the policy when the 'mode' == MBIND, even if it does not need a deref. This could result in prematurely freeing the task policy, causing a "use after free" error on next allocation; or even decrementing the reference on the system_default policy, which is probably benign, but not "nice". [Also, check your parentheses...]

Quoted 1 line

Anyway you slice it, it's pretty ugly.

Yep. You understand all this mempolicy code much better than I :) I was just trying to explain my aesthetic goal :)

Quoted 4 lines

So, for now, I'd like to keep it the way I have it. I'll be sending out a set of patches to rework the reference counting after mempolicy settles down--i.e., Mel's and David's patches, which I'm testing now. That will clean this area up quite a bit, IMO.

Sounds good. Looking forward to it.

-Nish

Signature · 8 lines
Nishanth Aravamudan <[email protected]>
IBM Linux Technology Center

--
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>
maintainer
#26 lore raw

Re: [PATCH] 2.6.25-rc3-mm1 - Mempolicy: make dequeue_huge_page_vma() obey MPOL_BIND nodemask

In reply to Lee Schermerhorn
Quoted 1 line · On Wed, 05 Mar 2008 11:06:34 -0500 Lee Schermerhorn <[email protected]>…

Subject: [PATCH] 2.6.25-rc3-mm1 - Mempolicy: make dequeue_huge_page_vma() obey MPOL_BIND nodemask

whine.

a) Please put the "2.6.25-rc3-mm1" stuff inside []. This tells the receiver that this is not-to-be-included metadata pertaining only to this email.

b) Sometimes (quite often) people send me patches advertised as being for "2.6.x-mm-y" whereas they actually fix problems which are in mainline (or even -stable).

So I tend to not believe it when people say that.

Quoted 13 lines

Date: Wed, 05 Mar 2008 11:06:34 -0500
Organization: HP/OSLO
X-Mailer: Evolution 2.6.1

PATCH Mempolicy - make dequeue_huge_page_vma() obey MPOL_BIND nodemask

dequeue_huge_page_vma() is not obeying the MPOL_BIND nodemask with the zonelist rework. It needs to search only zones in the mempolicy nodemask for hugepages.

Use for_each_zone_zonelist_nodemask() instead of
for_each_zone_zonelist().

Quoted 7 lines

Note: this will bloat mm/hugetlb.o a bit until Mel reworks the inlining of the for_each_zone... macros and helpers.

Added mempolicy helper function mpol_bind_nodemask() to hide the details of mempolicy from hugetlb and to avoid #ifdef CONFIG_NUMA in dequeue_huge_page_vma().

But this patch does indeed fix an only-in-mm problem. afacit it is a bugfix against mm-filter-based-on-a-nodemask-as-well-as-a-gfp_mask.patch. At least, that's how I've staged it.

If you have already worked that information out, please let me know.

Quoted 1 line

You're "Lee", not " Lee" ;)

--
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>

#27 lore raw

Re: [PATCH] 2.6.25-rc3-mm1 - Mempolicy: make dequeue_huge_page_vma() obey MPOL_BIND nodemask

Quoted 38 lines · Andrew Morton
Quoted 1 line · On Wed, 05 Mar 2008 11:06:34 -0500 Lee Schermerhorn <[email protected]>…

Subject: [PATCH] 2.6.25-rc3-mm1 - Mempolicy: make dequeue_huge_page_vma() obey MPOL_BIND nodemask

whine.

a) Please put the "2.6.25-rc3-mm1" stuff inside []. This tells the receiver that this is not-to-be-included metadata pertaining only to this email.

b) Sometimes (quite often) people send me patches advertised as being for "2.6.x-mm-y" whereas they actually fix problems which are in mainline (or even -stable).

So I tend to not believe it when people say that.

Quoted 13 lines

Date: Wed, 05 Mar 2008 11:06:34 -0500
Organization: HP/OSLO
X-Mailer: Evolution 2.6.1

PATCH Mempolicy - make dequeue_huge_page_vma() obey MPOL_BIND nodemask

dequeue_huge_page_vma() is not obeying the MPOL_BIND nodemask with the zonelist rework. It needs to search only zones in the mempolicy nodemask for hugepages.

Use for_each_zone_zonelist_nodemask() instead of
for_each_zone_zonelist().

Quoted 7 lines

Note: this will bloat mm/hugetlb.o a bit until Mel reworks the inlining of the for_each_zone... macros and helpers.

Added mempolicy helper function mpol_bind_nodemask() to hide the details of mempolicy from hugetlb and to avoid #ifdef CONFIG_NUMA in dequeue_huge_page_vma().

Quoted 5 lines

But this patch does indeed fix an only-in-mm problem. afacit it is a bugfix against mm-filter-based-on-a-nodemask-as-well-as-a-gfp_mask.patch. At least, that's how I've staged it.

If you have already worked that information out, please let me know.

ACK. Sorry, didn't mean to make more work for you.

And, I think you've told me this before--to mention when problem was introduced by another patch :-(.

Quoted 4 lines
Quoted 2 lines

You're "Lee", not " Lee" ;)

Ah... I didn't know the tools were that sensitive to whitespace.

Lee

--
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>

#28 lore raw

Re: [PATCH 4/6] Use two zonelist that are filtered by GFP mask

In reply to Andrew Morton

On (28/02/08 13:32), Andrew Morton didst pronounce:

Quoted 52 lines
Quoted 29 lines · On Wed, 27 Feb 2008 16:47:34 -0500 Lee Schermerhorn <[email protected]>…
(quoted diff) +29 -0
+/* Returns the first zone at or below highest_zoneidx in a zonelist */
+static inline struct zone **first_zones_zonelist(struct zonelist *zonelist,
+					enum zone_type highest_zoneidx)
+{
+	struct zone **z;
+
+	/* Find the first suitable zone to use for the allocation */
+	z = zonelist->zones;
+	while (*z && zone_idx(*z) > highest_zoneidx)
+		z++;
+
+	return z;
+}
+
+/* Returns the next zone at or below highest_zoneidx in a zonelist */
+static inline struct zone **next_zones_zonelist(struct zone **z,
+					enum zone_type highest_zoneidx)
+{
+	/* Find the next suitable zone to use for the allocation */
+	while (*z && zone_idx(*z) > highest_zoneidx)
+		z++;
+
+	return z;
+}
+
+/**
+ * for_each_zone_zonelist - helper macro to iterate over valid zones in a zonelist at or below a given zone index
+ * @zone - The current zone in the iterator
+ * @z - The current pointer within zonelist->zones being iterated

Review

Quoted 10 lines
(quoted diff) +10 -0
+ * @zlist - The zonelist being iterated
+ * @highidx - The zone index of the highest zone to return
+ *
+ * This iterator iterates though all zones at or below a given zone index.
+ */
+#define for_each_zone_zonelist(zone, z, zlist, highidx) \
+	for (z = first_zones_zonelist(zlist, highidx), zone = *z++;	\
+		zone;							\
+		z = next_zones_zonelist(z, highidx), zone = *z++)
+

omygawd will that thing generate a lot of code!

It has four call sites in mm/oom_kill.c and the overall patchset increases mm/oom_kill.o's text section (x86_64 allmodconfig) from 3268 bytes to 3845.

vmscan.o and page_alloc.o also grew a lot. otoh total vmlinux bloat from the patchset is only around 700 bytes, so I expect that with a little less insanity we could actually get an aggregate improvement here.

Quoted 7 lines

Some of the inlining in mmzone.h is just comical. Some of it is obvious (first_zones_zonelist) and some of it is less obvious (pfn_present).

I applied these for testing but I really don't think we should be merging such easily-fixed regressions into mainline. Could someone please take a look at de-porking core MM?

Here is the first attempt at uninlining the the zonelist walking. It doesn't use a callback iterator as that appeared at first glance as it would cause more bloat. With the version of patches posted, the increase in text size of vmlinux was 946 (x86_64 allmodconfig) and with this patch it's 613 so it's a bit of an improvement.

Performance is still good. It's not a definitive gain or loss in comparison to the two-zonelist as different machines show different results. In comparison to the vanilla, it's still generally a win so for the reduction in text size, it's likely worth it overall.

In comparison to two-zonelist the performance differences looks like

Kernbench Elapsed    time      -0.66      to 2.03
Kernbench Total  CPU           -0.73      to 0.94
Hackbench pipes-1              -8.23      to 29.65
Hackbench pipes-4              -8.10      to 10.98
Hackbench pipes-8              -17.55     to 8.66
Hackbench pipes-16             -12.15     to 3.16
Hackbench sockets-1            -6.90      to 11.78
Hackbench sockets-4            -1.75      to 3.89
Hackbench sockets-8            -7.53      to 5.78
Hackbench sockets-16           -3.78      to 5.89
TBench    clients-1            -1.98      to 12.01
TBench    clients-2            -3.83      to 11.02
TBench    clients-4            -4.49      to 6.32
TBench    clients-8            -5.65      to 5.76
DBench    clients-1-ext2       -5.13      to 0.98
DBench    clients-2-ext2       -2.42      to 8.07
DBench    clients-4-ext2       -4.64      to 1.92
DBench    clients-8-ext2       -5.57      to 9.90

DBench is the one I am most frowning at as another file-based microbench showed huge variances but it's not clear why it would be particularly sensitive.

Suggestions on how to improve the patch or alternatives?

==CUT HERE==
Subject: Uninline zonelist iterator helper functions

The order zones are accessed in is determined by a zonelist and the iterator for it is for_each_zone_zonelist_nodemask(). That uses a number of large inline functions that bloats the vmlinux file unnecessarily. This patch uninlines one helper function and reduces the size of another. Some iterator logic is then pushed to the uninlined function to reduce text duplication

---
 fs/buffer.c            |    4 ++-
 include/linux/mmzone.h |   62 +++++++------------------------------------------
 mm/mempolicy.c         |    4 ++-
 mm/mmzone.c            |   31 ++++++++++++++++++++++++
 mm/page_alloc.c        |   10 ++++++-
 5 files changed, 55 insertions(+), 56 deletions(-)
fs/buffer.c +3 -1
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.25-rc2-mm1-1010_update_documentation/fs/buffer.c linux-2.6.25-rc2-mm1-1020_uninline/fs/buffer.c
@@ -369,6 +369,7 @@ void invalidate_bdev(struct block_device
 static void free_more_memory(void)
 {
 	struct zoneref *zrefs;
+	struct zone *dummy;
 	int nid;
 
 	wakeup_pdflush(1024);
@@ -376,7 +377,8 @@ static void free_more_memory(void)
 
 	for_each_online_node(nid) {
 		zrefs = first_zones_zonelist(node_zonelist(nid, GFP_NOFS),
-						gfp_zone(GFP_NOFS), NULL);
+						gfp_zone(GFP_NOFS), NULL,
+						&dummy);
 		if (zrefs->zone)
 			try_to_free_pages(node_zonelist(nid, GFP_NOFS), 0,
 						GFP_NOFS);
include/linux/mmzone.h +10 -52

80 lines not on this page · raw message

mm/mempolicy.c +3 -1
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.25-rc2-mm1-1010_update_documentation/mm/mempolicy.c linux-2.6.25-rc2-mm1-1020_uninline/mm/mempolicy.c
@@ -1214,10 +1214,12 @@ unsigned slab_node(struct mempolicy *pol
 		 */
 		struct zonelist *zonelist;
 		struct zoneref *z;
+		struct zone *dummy;
 		enum zone_type highest_zoneidx = gfp_zone(GFP_KERNEL);
 		zonelist = &NODE_DATA(numa_node_id())->node_zonelists[0];
 		z = first_zones_zonelist(zonelist, highest_zoneidx,
-							&policy->v.nodes);
+							&policy->v.nodes,
+							&dummy);
 		return zonelist_node_idx(z);
 	}
 
mm/mmzone.c +31 -0
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.25-rc2-mm1-1010_update_documentation/mm/mmzone.c linux-2.6.25-rc2-mm1-1020_uninline/mm/mmzone.c
@@ -42,3 +42,34 @@ struct zone *next_zone(struct zone *zone
 	return zone;
 }
 
+static inline int zref_in_nodemask(struct zoneref *zref, nodemask_t *nodes)
+{
+#ifdef CONFIG_NUMA
+	return node_isset(zonelist_node_idx(zref), *nodes);
+#else
+	return 1;
+#endif /* CONFIG_NUMA */
+}
+
+
+/* Returns the next zone at or below highest_zoneidx in a zonelist */
+struct zoneref *next_zones_zonelist(struct zoneref *z,
+					enum zone_type highest_zoneidx,
+					nodemask_t *nodes,
+					struct zone **zone)
+{
+	/*
+	 * Find the next suitable zone to use for the allocation.
+	 * Only filter based on nodemask if it's set
+	 */
+	if (likely(nodes == NULL))
+		while (zonelist_zone_idx(z) > highest_zoneidx)
+			z++;
+	else
+		while (zonelist_zone_idx(z) > highest_zoneidx ||
+				(z->zone && !zref_in_nodemask(z, nodes)))
+			z++;
+
+	*zone = zonelist_zone(z++);
+	return z;
+}
mm/page_alloc.c +8 -2
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.25-rc2-mm1-1010_update_documentation/mm/page_alloc.c linux-2.6.25-rc2-mm1-1020_uninline/mm/page_alloc.c
@@ -1398,9 +1398,9 @@ get_page_from_freelist(gfp_t gfp_mask, n
 	int zlc_active = 0;		/* set if using zonelist_cache */
 	int did_zlc_setup = 0;		/* just call zlc_setup() one time */
 
-	z = first_zones_zonelist(zonelist, high_zoneidx, nodemask);
+	z = first_zones_zonelist(zonelist, high_zoneidx, nodemask,
+							&preferred_zone);
 	classzone_idx = zonelist_zone_idx(z);
-	preferred_zone = zonelist_zone(z);
 
 zonelist_scan:
 	/*
@@ -1966,6 +1966,12 @@ void show_free_areas(void)
 	show_swap_cache_info();
 }
 
+static void zoneref_set_zone(struct zone *zone, struct zoneref *zoneref)
+{
+	zoneref->zone = zone;
+	zoneref->zone_idx = zone_idx(zone);
+}
+
 /*
  * Builds allocation fallback zone lists.
  *

--
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>

#29 lore raw

PATCH 5/6 Have zonelist contains structs with both a zone pointer and zone_idx

In reply to Lee Schermerhorn

[PATCH 5/6] Have zonelist contains structs with both a zone pointer and zone_idx

V11r3 against 2.6.25-rc2-mm1

Filtering zonelists requires very frequent use of zone_idx(). This is costly as it involves a lookup of another structure and a substraction operation. As the zone_idx is often required, it should be quickly accessible. The node idx could also be stored here if it was found that accessing zone->node is significant which may be the case on workloads where nodemasks are heavily used.

This patch introduces a struct zoneref to store a zone pointer and a zone index. The zonelist then consists of an array of these struct zonerefs which are looked up as necessary. Helpers are given for accessing the zone index as well as the node index.

[[email protected]: Suggested struct zoneref instead of embedding information in pointers]

 arch/parisc/mm/init.c  |    2 -
 fs/buffer.c            |    6 ++--
 include/linux/mmzone.h |   64 ++++++++++++++++++++++++++++++++++++++--------
 include/linux/oom.h    |    4 +-
 kernel/cpuset.c        |    4 +-
 mm/hugetlb.c           |    3 +-
 mm/mempolicy.c         |   36 +++++++++++++++----------
 mm/oom_kill.c          |   45 +++++++++++++++-----------------
 mm/page_alloc.c        |   68 +++++++++++++++++++++++++++----------------------
 mm/slab.c              |    2 -
 mm/slub.c              |    2 -
 mm/vmscan.c            |    6 ++--
 12 files changed, 151 insertions(+), 91 deletions(-)
arch/parisc/mm/init.c +1 -1
Index: linux-2.6.25-rc2-mm1/arch/parisc/mm/init.c
===================================================================
@@ -608,7 +608,7 @@ void show_mem(void)
 		for (i = 0; i < npmem_ranges; i++) {
 			zl = node_zonelist(i);
 			for (j = 0; j < MAX_NR_ZONES; j++) {
-				struct zone **z;
+				struct zoneref *z;
 				struct zone *zone;
 
 				printk("Zone list for zone %d on node %d: ", j, i);
fs/buffer.c +3 -3
Index: linux-2.6.25-rc2-mm1/fs/buffer.c
===================================================================
@@ -368,16 +368,16 @@ void invalidate_bdev(struct block_device
  */
 static void free_more_memory(void)
 {
-	struct zone **zones;
+	struct zoneref *zrefs;
 	int nid;
 
 	wakeup_pdflush(1024);
 	yield();
 
 	for_each_online_node(nid) {
-		zones = first_zones_zonelist(node_zonelist(nid, GFP_NOFS),
+		zrefs = first_zones_zonelist(node_zonelist(nid, GFP_NOFS),
 						gfp_zone(GFP_NOFS));
-		if (*zones)
+		if (zrefs->zone)
 			try_to_free_pages(node_zonelist(nid, GFP_NOFS), 0,
 						GFP_NOFS);
 	}
include/linux/mmzone.h +54 -10

106 lines not on this page · raw message

include/linux/oom.h +2 -2
Index: linux-2.6.25-rc2-mm1/include/linux/oom.h
===================================================================
@@ -23,8 +23,8 @@ enum oom_constraint {
 	CONSTRAINT_MEMORY_POLICY,
 };
 
-extern int try_set_zone_oom(struct zonelist *zonelist);
-extern void clear_zonelist_oom(struct zonelist *zonelist);
+extern int try_set_zone_oom(struct zonelist *zonelist, gfp_t gfp_flags);
+extern void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags);
 
 extern void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order);
 extern int register_oom_notifier(struct notifier_block *nb);
kernel/cpuset.c +2 -2
Index: linux-2.6.25-rc2-mm1/kernel/cpuset.c
===================================================================
@@ -1915,8 +1915,8 @@ int cpuset_zonelist_valid_mems_allowed(s
 {
 	int i;
 
-	for (i = 0; zl->zones[i]; i++) {
-		int nid = zone_to_nid(zl->zones[i]);
+	for (i = 0; zl->_zonerefs[i].zone; i++) {
+		int nid = zonelist_node_idx(&zl->_zonerefs[i]);
 
 		if (node_isset(nid, current->mems_allowed))
 			return 1;
mm/hugetlb.c +2 -1
Index: linux-2.6.25-rc2-mm1/mm/hugetlb.c
===================================================================
@@ -79,7 +79,8 @@ static struct page *dequeue_huge_page(st
 	struct mempolicy *mpol;
 	struct zonelist *zonelist = huge_zonelist(vma, address,
 					htlb_alloc_mask, &mpol);
-	struct zone *zone, **z;
+	struct zone *zone;
+	struct zoneref *z;
 
 	for_each_zone_zonelist(zone, z, zonelist, MAX_NR_ZONES - 1) {
 		nid = zone_to_nid(zone);
mm/mempolicy.c +22 -14
Index: linux-2.6.25-rc2-mm1/mm/mempolicy.c
===================================================================
@@ -186,7 +186,7 @@ static struct zonelist *bind_zonelist(no
 		for_each_node_mask(nd, *nodes) { 
 			struct zone *z = &NODE_DATA(nd)->node_zones[k];
 			if (z->present_pages > 0) 
-				zl->zones[num++] = z;
+				zoneref_set_zone(z, &zl->_zonerefs[num++]);
 		}
 		if (k == 0)
 			break;
@@ -196,7 +196,8 @@ static struct zonelist *bind_zonelist(no
 		kfree(zl);
 		return ERR_PTR(-EINVAL);
 	}
-	zl->zones[num] = NULL;
+	zl->_zonerefs[num].zone = NULL;
+	zl->_zonerefs[num].zone_idx = 0;
 	return zl;
 }
 
@@ -504,9 +505,11 @@ static void get_zonemask(struct mempolic
 	nodes_clear(*nodes);
 	switch (p->policy) {
 	case MPOL_BIND:
-		for (i = 0; p->v.zonelist->zones[i]; i++)
-			node_set(zone_to_nid(p->v.zonelist->zones[i]),
-				*nodes);
+		for (i = 0; p->v.zonelist->_zonerefs[i].zone; i++) {
+			struct zoneref *zref;
+			zref = &p->v.zonelist->_zonerefs[i];
+			node_set(zonelist_node_idx(zref), *nodes);
+		}
 		break;
 	case MPOL_DEFAULT:
 		break;
@@ -1212,12 +1215,13 @@ unsigned slab_node(struct mempolicy *pol
 	case MPOL_INTERLEAVE:
 		return interleave_nodes(policy);
 
-	case MPOL_BIND:
+	case MPOL_BIND: {
 		/*
 		 * Follow bind policy behavior and start allocation at the
 		 * first node.
 		 */
-		return zone_to_nid(policy->v.zonelist->zones[0]);
+		return zonelist_node_idx(policy->v.zonelist->_zonerefs);
+	}
 
 	case MPOL_PREFERRED:
 		if (policy->v.preferred_node >= 0)
@@ -1321,7 +1325,7 @@ static struct page *alloc_page_interleav
 
 	zl = node_zonelist(nid, gfp);
 	page = __alloc_pages(gfp, order, zl);
-	if (page && page_zone(page) == zl->zones[0])
+	if (page && page_zone(page) == zonelist_zone(&zl->_zonerefs[0]))
 		inc_zone_page_state(page, NUMA_INTERLEAVE_HIT);
 	return page;
 }
@@ -1458,10 +1462,14 @@ int __mpol_equal(struct mempolicy *a, st
 		return a->v.preferred_node == b->v.preferred_node;
 	case MPOL_BIND: {
 		int i;
-		for (i = 0; a->v.zonelist->zones[i]; i++)
-			if (a->v.zonelist->zones[i] != b->v.zonelist->zones[i])
+		for (i = 0; a->v.zonelist->_zonerefs[i].zone; i++) {
+			struct zone *za, *zb;
+			za = zonelist_zone(&a->v.zonelist->_zonerefs[i]);
+			zb = zonelist_zone(&b->v.zonelist->_zonerefs[i]);
+			if (za != zb)
 				return 0;
-		return b->v.zonelist->zones[i] == NULL;
+		}
+		return b->v.zonelist->_zonerefs[i].zone == NULL;
 	}
 	default:
 		BUG();
@@ -1780,12 +1788,12 @@ static void mpol_rebind_policy(struct me
 		break;
 	case MPOL_BIND: {
 		nodemask_t nodes;
-		struct zone **z;
+		struct zoneref *z;
 		struct zonelist *zonelist;
 
 		nodes_clear(nodes);
-		for (z = pol->v.zonelist->zones; *z; z++)
-			node_set(zone_to_nid(*z), nodes);
+		for (z = pol->v.zonelist->_zonerefs; z->zone; z++)
+			node_set(zonelist_node_idx(z), nodes);
 		nodes_remap(tmp, nodes, *mpolmask, *newmask);
 		nodes = tmp;
 
mm/oom_kill.c +22 -23

74 lines not on this page · raw message

mm/page_alloc.c +38 -30

214 lines not on this page · raw message

mm/slab.c +1 -1
Index: linux-2.6.25-rc2-mm1/mm/slab.c
===================================================================
@@ -3244,7 +3244,7 @@ static void *fallback_alloc(struct kmem_
 {
 	struct zonelist *zonelist;
 	gfp_t local_flags;
-	struct zone **z;
+	struct zoneref *z;
 	struct zone *zone;
 	enum zone_type high_zoneidx = gfp_zone(flags);
 	void *obj = NULL;
mm/slub.c +1 -1
Index: linux-2.6.25-rc2-mm1/mm/slub.c
===================================================================
@@ -1299,7 +1299,7 @@ static struct page *get_any_partial(stru
 {
 #ifdef CONFIG_NUMA
 	struct zonelist *zonelist;
-	struct zone **z;
+	struct zoneref *z;
 	struct zone *zone;
 	enum zone_type high_zoneidx = gfp_zone(flags);
 	struct page *page;
mm/vmscan.c +3 -3
Index: linux-2.6.25-rc2-mm1/mm/vmscan.c
===================================================================
@@ -1273,7 +1273,7 @@ static unsigned long shrink_zones(int pr
 {
 	enum zone_type high_zoneidx = gfp_zone(sc->gfp_mask);
 	unsigned long nr_reclaimed = 0;
-	struct zone **z;
+	struct zoneref *z;
 	struct zone *zone;
 
 	sc->all_unreclaimable = 1;
@@ -1331,7 +1331,7 @@ static unsigned long do_try_to_free_page
 	unsigned long nr_reclaimed = 0;
 	struct reclaim_state *reclaim_state = current->reclaim_state;
 	unsigned long lru_pages = 0;
-	struct zone **z;
+	struct zoneref *z;
 	struct zone *zone;
 	enum zone_type high_zoneidx = gfp_zone(gfp_mask);
 
@@ -1453,7 +1453,7 @@ unsigned long try_to_free_mem_cgroup_pag
 		.isolate_pages = mem_cgroup_isolate_pages,
 	};
 	struct zonelist *zonelist;
-	int target_zone = gfp_zone(GFP_HIGHUSER_MOVABLE);
+	int target_zone = gfp_zonelist(GFP_HIGHUSER_MOVABLE);
 
 	zonelist = &NODE_DATA(numa_node_id())->node_zonelists[target_zone];
 	if (do_try_to_free_pages(zonelist, sc.gfp_mask, &sc))

--
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>

#30 lore raw

Re: [PATCH 5/6] Have zonelist contains structs with both a zone pointer and zone_idx

Hi

this is nitpick.

include/linux/mmzone.h extern struct zone *next_zone(struct zon · line 736

Quoted 4 lines
include/linux/mmzone.h +4 -0
+static inline struct zone *zonelist_zone(struct zoneref *zoneref)
+{
+	return zoneref->zone;
+}

this is zoneref operated function, not zonelist operation. I like zoneref_zone() :)

include/linux/mmzone.h extern struct zone *next_zone(struct zon · line 741

Quoted 4 lines
include/linux/mmzone.h +4 -0
+static inline int zonelist_zone_idx(struct zoneref *zoneref)
+{
+	return zoneref->zone_idx;
+}

ditto

include/linux/mmzone.h extern struct zone *next_zone(struct zon · line 751

Quoted 9 lines
include/linux/mmzone.h +9 -0
+static inline int zonelist_node_idx(struct zoneref *zoneref)
+{
+#ifdef CONFIG_NUMA
+	/* zone_to_nid not available in this context */
+	return zoneref->zone->node;
+#else
+	return 0;
+#endif /* CONFIG_NUMA */
+}

tritto

Quoted 3 lines
mm/oom_kill.c +1 -1
-int try_set_zone_oom(struct zonelist *zonelist)
+int try_set_zone_oom(struct zonelist *zonelist, gfp_t gfp_mask)
 {

(snip)

Review

Quoted 1 line
(quoted diff) +1 -0
+	for_each_zone_zonelist(zone, z, zonelist, gfp_zone(gfp_mask)) {

this function is no relation memory allocation and free. and gfp_mask argument is only used for calculate highest zone.

I think following argument is more descriptive, may be.

int try_set_zone_oom(struct zonelist *zonelist,
enum zone_type highest_zone_idx)

mm/oom_kill.c out: · line 494

Quoted 6 lines
mm/oom_kill.c +1 -1
@@ -491,16 +491,15 @@ out:
  * allocation attempts with zonelists containing them may now recall the OOM
  * killer, if necessary.
  */
-void clear_zonelist_oom(struct zonelist *zonelist)
+void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_mask)

ditto

Thanks.

- kosaki

--
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>

#31 lore raw

PATCH 6/6 Filter based on a nodemask as well as a gfp_mask

In reply to Lee Schermerhorn

[PATCH 6/6] Filter based on a nodemask as well as a gfp_mask

V11r3 against 2.6.25-rc2-mm1

The MPOL_BIND policy creates a zonelist that is used for allocations controlled by that mempolicy. As the per-node zonelist is already being filtered based on a zone id, this patch adds a version of __alloc_pages() that takes a nodemask for further filtering. This eliminates the need for MPOL_BIND to create a custom zonelist.

A positive benefit of this is that allocations using MPOL_BIND now use the local node's distance-ordered zonelist instead of a custom node-id-ordered zonelist. I.e., pages will be allocated from the closest allowed node with available memory.

 fs/buffer.c               |    2 
 include/linux/cpuset.h    |    4 -
 include/linux/gfp.h       |    4 +
 include/linux/mempolicy.h |    3 
 include/linux/mmzone.h    |   62 +++++++++++++----
 kernel/cpuset.c           |   18 +----
 mm/mempolicy.c            |  165 ++++++++++++++++------------------------------
 mm/page_alloc.c           |   40 +++++++----
 8 files changed, 151 insertions(+), 147 deletions(-)
fs/buffer.c +1 -1
Index: linux-2.6.25-rc2-mm1/fs/buffer.c
===================================================================
@@ -376,7 +376,7 @@ static void free_more_memory(void)
 
 	for_each_online_node(nid) {
 		zrefs = first_zones_zonelist(node_zonelist(nid, GFP_NOFS),
-						gfp_zone(GFP_NOFS));
+						gfp_zone(GFP_NOFS), NULL);
 		if (zrefs->zone)
 			try_to_free_pages(node_zonelist(nid, GFP_NOFS), 0,
 						GFP_NOFS);
include/linux/cpuset.h +2 -2
Index: linux-2.6.25-rc2-mm1/include/linux/cpuset.h
===================================================================
@@ -26,7 +26,7 @@ extern nodemask_t cpuset_mems_allowed(st
 #define cpuset_current_mems_allowed (current->mems_allowed)
 void cpuset_init_current_mems_allowed(void);
 void cpuset_update_task_memory_state(void);
-int cpuset_zonelist_valid_mems_allowed(struct zonelist *zl);
+int cpuset_nodemask_valid_mems_allowed(nodemask_t *nodemask);
 
 extern int __cpuset_zone_allowed_softwall(struct zone *z, gfp_t gfp_mask);
 extern int __cpuset_zone_allowed_hardwall(struct zone *z, gfp_t gfp_mask);
@@ -102,7 +102,7 @@ static inline nodemask_t cpuset_mems_all
 static inline void cpuset_init_current_mems_allowed(void) {}
 static inline void cpuset_update_task_memory_state(void) {}
 
-static inline int cpuset_zonelist_valid_mems_allowed(struct zonelist *zl)
+static inline int cpuset_nodemask_valid_mems_allowed(nodemask_t *nodemask)
 {
 	return 1;
 }
include/linux/gfp.h +4 -0
Index: linux-2.6.25-rc2-mm1/include/linux/gfp.h
===================================================================
@@ -182,6 +182,10 @@ static inline void arch_alloc_page(struc
 
 extern struct page *__alloc_pages(gfp_t, unsigned int, struct zonelist *);
 
+extern struct page *
+__alloc_pages_nodemask(gfp_t, unsigned int,
+				struct zonelist *, nodemask_t *nodemask);
+
 static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask,
 						unsigned int order)
 {
include/linux/mempolicy.h +1 -2
Index: linux-2.6.25-rc2-mm1/include/linux/mempolicy.h
===================================================================
@@ -64,9 +64,8 @@ struct mempolicy {
 	atomic_t refcnt;
 	short policy; 	/* See MPOL_* above */
 	union {
-		struct zonelist  *zonelist;	/* bind */
 		short 		 preferred_node; /* preferred */
-		nodemask_t	 nodes;		/* interleave */
+		nodemask_t	 nodes;		/* interleave/bind */
 		/* undefined for default */
 	} v;
 	nodemask_t cpuset_mems_allowed;	/* mempolicy relative to these nodes */
include/linux/mmzone.h +50 -12

97 lines not on this page · raw message

kernel/cpuset.c +5 -13
Index: linux-2.6.25-rc2-mm1/kernel/cpuset.c
===================================================================
@@ -1906,22 +1906,14 @@ nodemask_t cpuset_mems_allowed(struct ta
 }
 
 /**
- * cpuset_zonelist_valid_mems_allowed - check zonelist vs. curremt mems_allowed
- * @zl: the zonelist to be checked
+ * cpuset_nodemask_valid_mems_allowed - check nodemask vs. curremt mems_allowed
+ * @nodemask: the nodemask to be checked
  *
- * Are any of the nodes on zonelist zl allowed in current->mems_allowed?
+ * Are any of the nodes in the nodemask allowed in current->mems_allowed?
  */
-int cpuset_zonelist_valid_mems_allowed(struct zonelist *zl)
+int cpuset_nodemask_valid_mems_allowed(nodemask_t *nodemask)
 {
-	int i;
-
-	for (i = 0; zl->_zonerefs[i].zone; i++) {
-		int nid = zonelist_node_idx(&zl->_zonerefs[i]);
-
-		if (node_isset(nid, current->mems_allowed))
-			return 1;
-	}
-	return 0;
+	return nodes_intersects(*nodemask, current->mems_allowed);
 }
 
 /*
mm/mempolicy.c +60 -105

269 lines not on this page · raw message

mm/page_alloc.c +28 -12
Index: linux-2.6.25-rc2-mm1/mm/page_alloc.c
===================================================================
@@ -1387,7 +1387,7 @@ static void zlc_mark_zone_full(struct zo
  * a page.
  */
 static struct page *
-get_page_from_freelist(gfp_t gfp_mask, unsigned int order,
+get_page_from_freelist(gfp_t gfp_mask, nodemask_t *nodemask, unsigned int order,
 		struct zonelist *zonelist, int high_zoneidx, int alloc_flags)
 {
 	struct zoneref *z;
@@ -1398,7 +1398,7 @@ get_page_from_freelist(gfp_t gfp_mask, u
 	int zlc_active = 0;		/* set if using zonelist_cache */
 	int did_zlc_setup = 0;		/* just call zlc_setup() one time */
 
-	z = first_zones_zonelist(zonelist, high_zoneidx);
+	z = first_zones_zonelist(zonelist, high_zoneidx, nodemask);
 	classzone_idx = zonelist_zone_idx(z);
 	preferred_zone = zonelist_zone(z);
 
@@ -1407,7 +1407,8 @@ zonelist_scan:
 	 * Scan zonelist, looking for a zone with enough free.
 	 * See also cpuset_zone_allowed() comment in kernel/cpuset.c.
 	 */
-	for_each_zone_zonelist(zone, z, zonelist, high_zoneidx) {
+	for_each_zone_zonelist_nodemask(zone, z, zonelist,
+						high_zoneidx, nodemask) {
 		if (NUMA_BUILD && zlc_active &&
 			!zlc_zone_worth_trying(zonelist, z, allowednodes))
 				continue;
@@ -1513,9 +1514,9 @@ static void set_page_owner(struct page *
 /*
  * This is the 'heart' of the zoned buddy allocator.
  */
-struct page *
-__alloc_pages(gfp_t gfp_mask, unsigned int order,
-		struct zonelist *zonelist)
+static struct page *
+__alloc_pages_internal(gfp_t gfp_mask, unsigned int order,
+			struct zonelist *zonelist, nodemask_t *nodemask)
 {
 	const gfp_t wait = gfp_mask & __GFP_WAIT;
 	enum zone_type high_zoneidx = gfp_zone(gfp_mask);
@@ -1544,7 +1545,7 @@ restart:
 		return NULL;
 	}
 
-	page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, order,
+	page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, nodemask, order,
 			zonelist, high_zoneidx, ALLOC_WMARK_LOW|ALLOC_CPUSET);
 	if (page)
 		goto got_pg;
@@ -1589,7 +1590,7 @@ restart:
 	 * Ignore cpuset if GFP_ATOMIC (!wait) rather than fail alloc.
 	 * See also cpuset_zone_allowed() comment in kernel/cpuset.c.
 	 */
-	page = get_page_from_freelist(gfp_mask, order, zonelist,
+	page = get_page_from_freelist(gfp_mask, nodemask, order, zonelist,
 						high_zoneidx, alloc_flags);
 	if (page)
 		goto got_pg;
@@ -1602,7 +1603,7 @@ rebalance:
 		if (!(gfp_mask & __GFP_NOMEMALLOC)) {
 nofail_alloc:
 			/* go through the zonelist yet again, ignoring mins */
-			page = get_page_from_freelist(gfp_mask, order,
+			page = get_page_from_freelist(gfp_mask, nodemask, order,
 				zonelist, high_zoneidx, ALLOC_NO_WATERMARKS);
 			if (page)
 				goto got_pg;
@@ -1637,7 +1638,7 @@ nofail_alloc:
 		drain_all_pages();
 
 	if (likely(did_some_progress)) {
-		page = get_page_from_freelist(gfp_mask, order,
+		page = get_page_from_freelist(gfp_mask, nodemask, order,
 					zonelist, high_zoneidx, alloc_flags);
 		if (page)
 			goto got_pg;
@@ -1653,8 +1654,9 @@ nofail_alloc:
 		 * a parallel oom killing, we must fail if we're still
 		 * under heavy pressure.
 		 */
-		page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, order,
-			zonelist, high_zoneidx, ALLOC_WMARK_HIGH|ALLOC_CPUSET);
+		page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, nodemask,
+			order, zonelist, high_zoneidx,
+			ALLOC_WMARK_HIGH|ALLOC_CPUSET);
 		if (page) {
 			clear_zonelist_oom(zonelist, gfp_mask);
 			goto got_pg;
@@ -1707,6 +1709,20 @@ got_pg:
 	return page;
 }
 
+struct page *
+__alloc_pages(gfp_t gfp_mask, unsigned int order,
+		struct zonelist *zonelist)
+{
+	return __alloc_pages_internal(gfp_mask, order, zonelist, NULL);
+}
+
+struct page *
+__alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order,
+		struct zonelist *zonelist, nodemask_t *nodemask)
+{
+	return __alloc_pages_internal(gfp_mask, order, zonelist, nodemask);
+}
+
 EXPORT_SYMBOL(__alloc_pages);
 
 /*

--
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>

#32 lore raw

Re: [PATCH 6/6] Filter based on a nodemask as well as a gfp_mask

Quoted 10 lines · Mel Gorman

[PATCH 6/6] Filter based on a nodemask as well as a gfp_mask

V11r3 against 2.6.25-rc2-mm1

The MPOL_BIND policy creates a zonelist that is used for allocations controlled by that mempolicy. As the per-node zonelist is already being filtered based on a zone id, this patch adds a version of __alloc_pages() that takes a nodemask for further filtering. This eliminates the need for MPOL_BIND to create a custom zonelist.

Quoted 9 lines

A positive benefit of this is that allocations using MPOL_BIND now use the local node's distance-ordered zonelist instead of a custom node-id-ordered zonelist. I.e., pages will be allocated from the closest allowed node with available memory.

Thank you! I like this very much.
Next step is maybe to pass nodemask to try_to_free_pages().

BTW, cpuset memory limitation by nodemask has the same kind of feature. But it seems cpuset_zone_allowed_soft/hardwall() has extra checks for system sanity.

Could you import them ? maybe like this
==
void __alloc_pages_internal(gfp_t gfp_mask, unsigned int order,
+ struct zonelist *zonelist, nodemask_t *nodemask))

{
	if (nodemask) {
		if (unlikely(test_thread_flag(TIF_MEMDIE)))
	                nodemask = NULL;
		if ((gfp_mask & __GFP_HARDWALL)
                     && (unlikely(test_thread_flag(TIF_MEMDIE)))
			nodemask = NULL;
	}
}

==
(I don't think above is clean.)

Thanks,
-Kame

--
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>

#33 lore raw

Re: [PATCH 6/6] Filter based on a nodemask as well as a gfp_mask

On (29/02/08 11:59), KAMEZAWA Hiroyuki didst pronounce:

Quoted 18 lines
Quoted 15 lines · Mel Gorman

[PATCH 6/6] Filter based on a nodemask as well as a gfp_mask

V11r3 against 2.6.25-rc2-mm1

The MPOL_BIND policy creates a zonelist that is used for allocations controlled by that mempolicy. As the per-node zonelist is already being filtered based on a zone id, this patch adds a version of __alloc_pages() that takes a nodemask for further filtering. This eliminates the need for MPOL_BIND to create a custom zonelist.

A positive benefit of this is that allocations using MPOL_BIND now use the local node's distance-ordered zonelist instead of a custom node-id-ordered zonelist. I.e., pages will be allocated from the closest allowed node with available memory.

Quoted 8 lines
Quoted 4 lines

Thank you! I like this very much.
Next step is maybe to pass nodemask to try_to_free_pages().

Not a bad plan. I will visit it after the text bloat is reduced a bit. Currently each usage of for_each_zone_zonelist_nodemask() adds a bit too much.

Quoted 17 lines

BTW, cpuset memory limitation by nodemask has the same kind of feature. But it seems cpuset_zone_allowed_soft/hardwall() has extra checks for system sanity.

Could you import them ? maybe like this
==
void __alloc_pages_internal(gfp_t gfp_mask, unsigned int order,

(quoted diff) +1 -0
+			struct zonelist *zonelist, nodemask_t *nodemask))
{
	if (nodemask) {
		if (unlikely(test_thread_flag(TIF_MEMDIE)))
	                nodemask = NULL;
		if ((gfp_mask & __GFP_HARDWALL)
                     && (unlikely(test_thread_flag(TIF_MEMDIE)))
			nodemask = NULL;
	}
}
Quoted 2 lines

==
(I don't think above is clean.)

I get the idea, I'll check it out and see.

Thanks

Signature · 9 lines
Mel Gorman
Part-time Phd Student                          Linux Technology Center
University of Limerick                         IBM Dublin Software Lab

--
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>
#34 lore raw

Re: [PATCH 6/6] Filter based on a nodemask as well as a gfp_mask

In reply to Lee Schermerhorn

Hi

Quoted 10 lines

The MPOL_BIND policy creates a zonelist that is used for allocations controlled by that mempolicy. As the per-node zonelist is already being filtered based on a zone id, this patch adds a version of __alloc_pages() that takes a nodemask for further filtering. This eliminates the need for MPOL_BIND to create a custom zonelist.

A positive benefit of this is that allocations using MPOL_BIND now use the local node's distance-ordered zonelist instead of a custom node-id-ordered zonelist. I.e., pages will be allocated from the closest allowed node with available memory.

Great.
this is not only clean up, but also great mempolicy improvement.

Quoted 5 lines
mm/mempolicy.c +2 -2
-/* Generate a custom zonelist for the BIND policy. */
-static struct zonelist *bind_zonelist(nodemask_t *nodes)
+/* Check that the nodemask contains at least one populated zone */
+static int is_valid_nodemask(nodemask_t *nodemask)
 {

(snip)

mm/mempolicy.c static int mpol_check_policy(int mode, n · line 180

Quoted 7 lines
mm/mempolicy.c +7 -0
+	for_each_node_mask(nd, *nodemask) {
+		struct zone *z;
+
+		for (k = 0; k <= policy_zone; k++) {
+			z = &NODE_DATA(nd)->node_zones[k];
+			if (z->present_pages > 0)
+				return 1;

could we use populated_zone()?

-kosaki

--
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>

#35 lore raw
In reply to Lee Schermerhorn
Quoted 9 lines · Mel Gorman

[PATCH 0/6] Use two zonelists per node instead of multiple zonelists v11r3

This is a rebase of the two-zonelist patchset to 2.6.25-rc2-mm1.

Mel, still on vacation last I checked, asked me to repost these as I'd already rebased them and I've been testing them continually on each -mm tree for months, hoping to see them in -mm for wider testing.

Quoted 7 lines

I have a series of mempolicy cleanup patches, including a rework of the reference counting that depend on this series. David R. has a series of mempolicy enhancements out for review that, IMO, will benefit from this series. In both cases, the removal of the custom zonelist for MPOL_BIND is the important feature.

Lee

FYI: I reposted this series, at Mel's request, as I mentioned above. I see that the mail system blames Mel for the mail--probably because I placed his 'From: Mel..." in the body as original author. Hope this doesn't cause any confusion.

Lee

Quoted 117 lines

---

Changelog since V11r2
o Rebase to 2.6.25-rc2-mm1

Changelog since V10
o Rebase to 2.6.24-rc4-mm1
o Clear up warnings in fs/buffer.c early in the patchset

Changelog since V9
o Rebase to 2.6.24-rc2-mm1
o Lookup the nodemask for each allocator callsite in mempolicy.c
o Update NUMA statistics based on preferred zone, not first zonelist entry
o When __GFP_THISNODE is specified with MPOL_BIND and the current node is not in the allowed nodemask, the first node in the mask will be used
o Stick with using two zonelists instead of one because of excessive complexity with corner cases

Changelog since V8
o Rebase to 2.6.24-rc2
o Added ack for the OOM changes
o Behave correctly when GFP_THISNODE and a node ID are specified
o Clear up warning over type of nodes_intersects() function

Changelog since V7
o Rebase to 2.6.23-rc8-mm2

Changelog since V6
o Fix build bug in relation to memory controller combined with one-zonelist
o Use while() instead of a stupid looking for()
o Instead of encoding zone index information in a pointer, this version introduces a structure that stores a zone pointer and its index

Changelog since V5
o Rebase to 2.6.23-rc4-mm1
o Drop patch that replaces inline functions with macros

Changelog since V4
o Rebase to -mm kernel. Host of memoryless patches collisions dealt with
o Do not call wakeup_kswapd() for every zone in a zonelist
o Dropped the FASTCALL removal
o Have cursor in iterator advance earlier
o Use nodes_and in cpuset_nodes_valid_mems_allowed()
o Use defines instead of inlines, noticably better performance on gcc-3.4 No difference on later compilers such as gcc 4.1
o Dropped gfp_skip patch until it is proven to be of benefit. Tests are currently inconclusive but it definitly consumes at least one cache line

Changelog since V3
o Fix compile error in the parisc change
o Calculate gfp_zone only once in __alloc_pages
o Calculate classzone_idx properly in get_page_from_freelist
o Alter check so that zone id embedded may still be used on UP
o Use Kamezawa-sans suggestion for skipping zones in zonelist
o Add __alloc_pages_nodemask() to filter zonelist based on a nodemask. This removes the need for MPOL_BIND to have a custom zonelist
o Move zonelist iterators and helpers to mm.h
o Change _zones from struct zone * to unsigned long

Changelog since V2
o shrink_zones() uses zonelist instead of zonelist->zones
o hugetlb uses zonelist iterator
o zone_idx information is embedded in zonelist pointers
o replace NODE_DATA(nid)->node_zonelist with node_zonelist(nid)

Changelog since V1
o Break up the patch into 3 patches
o Introduce iterators for zonelists
o Performance regression test

The following patches replace multiple zonelists per node with two zonelists that are filtered based on the GFP flags. The patches as a set fix a bug with regard to the use of MPOL_BIND and ZONE_MOVABLE. With this patchset, the MPOL_BIND will apply to the two highest zones when the highest zone is ZONE_MOVABLE. This should be considered as an alternative fix for the MPOL_BIND+ZONE_MOVABLE in 2.6.23 to the previously discussed hack that filters only custom zonelists.

The first patch cleans up an inconsistency where direct reclaim uses zonelist->zones where other places use zonelist.

The second patch introduces a helper function node_zonelist() for looking up the appropriate zonelist for a GFP mask which simplifies patches later in the set.

The third patch defines/remembers the "preferred zone" for numa statistics, as it is no longer always the first zone in a zonelist.

The forth patch replaces multiple zonelists with two zonelists that are filtered. The two zonelists are due to the fact that the memoryless patchset introduces a second set of zonelists for __GFP_THISNODE.

The fifth patch introduces helper macros for retrieving the zone and node indices of entries in a zonelist.

The final patch introduces filtering of the zonelists based on a nodemask. Two zonelists exist per node, one for normal allocations and one for __GFP_THISNODE.

Performance results varied depending on the machine configuration. In real workloads the gain/loss will depend on how much the userspace portion of the benchmark benefits from having more cache available due to reduced referencing of zonelists.

These are the range of performance losses/gains when running against 2.6.24-rc4-mm1. The set and these machines are a mix of i386, x86_64 and ppc64 both NUMA and non-NUMA.

loss to gain

Total CPU time on Kernbench: -0.86% to  1.13%
Elapsed   time on Kernbench: -0.79% to  0.76%
page_test from aim9:         -4.37% to  0.79%
brk_test  from aim9:         -0.71% to  4.07%
fork_test from aim9:         -1.84% to  4.60%
exec_test from aim9:         -0.71% to  1.08%
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>

#36 lore raw
In reply to Lee Schermerhorn

On (27/02/08 16:47), Lee Schermerhorn didst pronounce:

Quoted 10 lines

From: Mel Gorman <[email protected]>
[PATCH 0/6] Use two zonelists per node instead of multiple zonelists v11r3

This is a rebase of the two-zonelist patchset to 2.6.25-rc2-mm1.

Mel, still on vacation last I checked, asked me to repost these as I'd already rebased them and I've been testing them continually on each -mm tree for months, hoping to see them in -mm for wider testing.

Thanks a lot, Lee. I tested this patchset against a slightly patched 2.6.25-rc2-mm1 (compile-failure fix and a memoryless-related bug that is fixed in git-x86#testing).

Quoted 4 lines

These are the range of performance losses/gains when running against 2.6.24-rc4-mm1. The set and these machines are a mix of i386, x86_64 and ppc64 both NUMA and non-NUMA.

Against 2.6.25-rc5-mm1, the results are
loss to gain
Total CPU time on Kernbench: -0.23% 1.04%
Elapsed time on Kernbench: -0.69% 3.86%
page_test from aim9: -3.74% 5.72%
brk_test from aim9: -7.37% 10.98%
fork_test from aim9: -3.52% 3.17%
exec_test from aim9: -2.78% 2.34%
TBench: -1.93% 2.96%

Hackbench was similarly variable but most machines showed little or no difference. As before, whether the changes are a performance win/loss depends on the machine but the majority of results showed little difference.

Quoted 8 lines

loss to gain

Total CPU time on Kernbench: -0.86% to  1.13%
Elapsed   time on Kernbench: -0.79% to  0.76%
page_test from aim9:         -4.37% to  0.79%
brk_test  from aim9:         -0.71% to  4.07%
fork_test from aim9:         -1.84% to  4.60%
exec_test from aim9:         -0.71% to  1.08%
Signature · 9 lines
Mel Gorman
Part-time Phd Student                          Linux Technology Center
University of Limerick                         IBM Dublin Software Lab

--
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>