diff options
author | Cody P Schafer <cody@linux.vnet.ibm.com> | 2013-07-03 18:01:31 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-03 19:07:27 -0400 |
commit | 8d7a8fa97abeb4fd6b3975d32c9f859875157770 (patch) | |
tree | 99e057e6b90bd514e645ed0539d1d1b8178046d3 /mm/page_alloc.c | |
parent | c8e251fadc6220261f6e0c6b8a4f1cdf27626165 (diff) |
mm/page_alloc: insert memory barriers to allow async update of pcp batch and high
Introduce pageset_update() to perform a safe transision from one set of
pcp->{batch,high} to a new set using memory barriers.
This ensures that batch is always set to a safe value (1) prior to
updating high, and ensure that high is fully updated before setting the
real value of batch. It avoids ->batch ever rising above ->high.
Suggested by Gilad Ben-Yossef in these threads:
https://lkml.org/lkml/2013/4/9/23
https://lkml.org/lkml/2013/4/10/49
Also reproduces his proposed comment.
Signed-off-by: Cody P Schafer <cody@linux.vnet.ibm.com>
Reviewed-by: Gilad Ben-Yossef <gilad@benyossef.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@gmail.com>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Pekka Enberg <penberg@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/page_alloc.c')
-rw-r--r-- | mm/page_alloc.c | 41 |
1 files changed, 32 insertions, 9 deletions
diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 8d4335779633..eaaef2a09424 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c | |||
@@ -4035,12 +4035,37 @@ static int __meminit zone_batchsize(struct zone *zone) | |||
4035 | #endif | 4035 | #endif |
4036 | } | 4036 | } |
4037 | 4037 | ||
4038 | /* | ||
4039 | * pcp->high and pcp->batch values are related and dependent on one another: | ||
4040 | * ->batch must never be higher then ->high. | ||
4041 | * The following function updates them in a safe manner without read side | ||
4042 | * locking. | ||
4043 | * | ||
4044 | * Any new users of pcp->batch and pcp->high should ensure they can cope with | ||
4045 | * those fields changing asynchronously (acording the the above rule). | ||
4046 | * | ||
4047 | * mutex_is_locked(&pcp_batch_high_lock) required when calling this function | ||
4048 | * outside of boot time (or some other assurance that no concurrent updaters | ||
4049 | * exist). | ||
4050 | */ | ||
4051 | static void pageset_update(struct per_cpu_pages *pcp, unsigned long high, | ||
4052 | unsigned long batch) | ||
4053 | { | ||
4054 | /* start with a fail safe value for batch */ | ||
4055 | pcp->batch = 1; | ||
4056 | smp_wmb(); | ||
4057 | |||
4058 | /* Update high, then batch, in order */ | ||
4059 | pcp->high = high; | ||
4060 | smp_wmb(); | ||
4061 | |||
4062 | pcp->batch = batch; | ||
4063 | } | ||
4064 | |||
4038 | /* a companion to setup_pagelist_highmark() */ | 4065 | /* a companion to setup_pagelist_highmark() */ |
4039 | static void pageset_set_batch(struct per_cpu_pageset *p, unsigned long batch) | 4066 | static void pageset_set_batch(struct per_cpu_pageset *p, unsigned long batch) |
4040 | { | 4067 | { |
4041 | struct per_cpu_pages *pcp = &p->pcp; | 4068 | pageset_update(&p->pcp, 6 * batch, max(1UL, 1 * batch)); |
4042 | pcp->high = 6 * batch; | ||
4043 | pcp->batch = max(1UL, 1 * batch); | ||
4044 | } | 4069 | } |
4045 | 4070 | ||
4046 | static void setup_pageset(struct per_cpu_pageset *p, unsigned long batch) | 4071 | static void setup_pageset(struct per_cpu_pageset *p, unsigned long batch) |
@@ -4064,13 +4089,11 @@ static void setup_pageset(struct per_cpu_pageset *p, unsigned long batch) | |||
4064 | static void setup_pagelist_highmark(struct per_cpu_pageset *p, | 4089 | static void setup_pagelist_highmark(struct per_cpu_pageset *p, |
4065 | unsigned long high) | 4090 | unsigned long high) |
4066 | { | 4091 | { |
4067 | struct per_cpu_pages *pcp; | 4092 | unsigned long batch = max(1UL, high / 4); |
4093 | if ((high / 4) > (PAGE_SHIFT * 8)) | ||
4094 | batch = PAGE_SHIFT * 8; | ||
4068 | 4095 | ||
4069 | pcp = &p->pcp; | 4096 | pageset_update(&p->pcp, high, batch); |
4070 | pcp->high = high; | ||
4071 | pcp->batch = max(1UL, high/4); | ||
4072 | if ((high/4) > (PAGE_SHIFT * 8)) | ||
4073 | pcp->batch = PAGE_SHIFT * 8; | ||
4074 | } | 4097 | } |
4075 | 4098 | ||
4076 | static void __meminit setup_zone_pageset(struct zone *zone) | 4099 | static void __meminit setup_zone_pageset(struct zone *zone) |