aboutsummaryrefslogtreecommitdiffstats
path: root/mm/vmstat.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/vmstat.c')
-rw-r--r--mm/vmstat.c155
1 files changed, 116 insertions, 39 deletions
diff --git a/mm/vmstat.c b/mm/vmstat.c
index 42eac4d33216..312d728976f1 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -167,36 +167,24 @@ static void refresh_zone_stat_thresholds(void)
167void __mod_zone_page_state(struct zone *zone, enum zone_stat_item item, 167void __mod_zone_page_state(struct zone *zone, enum zone_stat_item item,
168 int delta) 168 int delta)
169{ 169{
170 struct per_cpu_pageset *pcp = this_cpu_ptr(zone->pageset); 170 struct per_cpu_pageset __percpu *pcp = zone->pageset;
171 171 s8 __percpu *p = pcp->vm_stat_diff + item;
172 s8 *p = pcp->vm_stat_diff + item;
173 long x; 172 long x;
173 long t;
174
175 x = delta + __this_cpu_read(*p);
174 176
175 x = delta + *p; 177 t = __this_cpu_read(pcp->stat_threshold);
176 178
177 if (unlikely(x > pcp->stat_threshold || x < -pcp->stat_threshold)) { 179 if (unlikely(x > t || x < -t)) {
178 zone_page_state_add(x, zone, item); 180 zone_page_state_add(x, zone, item);
179 x = 0; 181 x = 0;
180 } 182 }
181 *p = x; 183 __this_cpu_write(*p, x);
182} 184}
183EXPORT_SYMBOL(__mod_zone_page_state); 185EXPORT_SYMBOL(__mod_zone_page_state);
184 186
185/* 187/*
186 * For an unknown interrupt state
187 */
188void mod_zone_page_state(struct zone *zone, enum zone_stat_item item,
189 int delta)
190{
191 unsigned long flags;
192
193 local_irq_save(flags);
194 __mod_zone_page_state(zone, item, delta);
195 local_irq_restore(flags);
196}
197EXPORT_SYMBOL(mod_zone_page_state);
198
199/*
200 * Optimized increment and decrement functions. 188 * Optimized increment and decrement functions.
201 * 189 *
202 * These are only for a single page and therefore can take a struct page * 190 * These are only for a single page and therefore can take a struct page *
@@ -221,16 +209,17 @@ EXPORT_SYMBOL(mod_zone_page_state);
221 */ 209 */
222void __inc_zone_state(struct zone *zone, enum zone_stat_item item) 210void __inc_zone_state(struct zone *zone, enum zone_stat_item item)
223{ 211{
224 struct per_cpu_pageset *pcp = this_cpu_ptr(zone->pageset); 212 struct per_cpu_pageset __percpu *pcp = zone->pageset;
225 s8 *p = pcp->vm_stat_diff + item; 213 s8 __percpu *p = pcp->vm_stat_diff + item;
226 214 s8 v, t;
227 (*p)++;
228 215
229 if (unlikely(*p > pcp->stat_threshold)) { 216 v = __this_cpu_inc_return(*p);
230 int overstep = pcp->stat_threshold / 2; 217 t = __this_cpu_read(pcp->stat_threshold);
218 if (unlikely(v > t)) {
219 s8 overstep = t >> 1;
231 220
232 zone_page_state_add(*p + overstep, zone, item); 221 zone_page_state_add(v + overstep, zone, item);
233 *p = -overstep; 222 __this_cpu_write(*p, -overstep);
234 } 223 }
235} 224}
236 225
@@ -242,16 +231,17 @@ EXPORT_SYMBOL(__inc_zone_page_state);
242 231
243void __dec_zone_state(struct zone *zone, enum zone_stat_item item) 232void __dec_zone_state(struct zone *zone, enum zone_stat_item item)
244{ 233{
245 struct per_cpu_pageset *pcp = this_cpu_ptr(zone->pageset); 234 struct per_cpu_pageset __percpu *pcp = zone->pageset;
246 s8 *p = pcp->vm_stat_diff + item; 235 s8 __percpu *p = pcp->vm_stat_diff + item;
236 s8 v, t;
247 237
248 (*p)--; 238 v = __this_cpu_dec_return(*p);
239 t = __this_cpu_read(pcp->stat_threshold);
240 if (unlikely(v < - t)) {
241 s8 overstep = t >> 1;
249 242
250 if (unlikely(*p < - pcp->stat_threshold)) { 243 zone_page_state_add(v - overstep, zone, item);
251 int overstep = pcp->stat_threshold / 2; 244 __this_cpu_write(*p, overstep);
252
253 zone_page_state_add(*p - overstep, zone, item);
254 *p = overstep;
255 } 245 }
256} 246}
257 247
@@ -261,6 +251,92 @@ void __dec_zone_page_state(struct page *page, enum zone_stat_item item)
261} 251}
262EXPORT_SYMBOL(__dec_zone_page_state); 252EXPORT_SYMBOL(__dec_zone_page_state);
263 253
254#ifdef CONFIG_CMPXCHG_LOCAL
255/*
256 * If we have cmpxchg_local support then we do not need to incur the overhead
257 * that comes with local_irq_save/restore if we use this_cpu_cmpxchg.
258 *
259 * mod_state() modifies the zone counter state through atomic per cpu
260 * operations.
261 *
262 * Overstep mode specifies how overstep should handled:
263 * 0 No overstepping
264 * 1 Overstepping half of threshold
265 * -1 Overstepping minus half of threshold
266*/
267static inline void mod_state(struct zone *zone,
268 enum zone_stat_item item, int delta, int overstep_mode)
269{
270 struct per_cpu_pageset __percpu *pcp = zone->pageset;
271 s8 __percpu *p = pcp->vm_stat_diff + item;
272 long o, n, t, z;
273
274 do {
275 z = 0; /* overflow to zone counters */
276
277 /*
278 * The fetching of the stat_threshold is racy. We may apply
279 * a counter threshold to the wrong the cpu if we get
280 * rescheduled while executing here. However, the following
281 * will apply the threshold again and therefore bring the
282 * counter under the threshold.
283 */
284 t = this_cpu_read(pcp->stat_threshold);
285
286 o = this_cpu_read(*p);
287 n = delta + o;
288
289 if (n > t || n < -t) {
290 int os = overstep_mode * (t >> 1) ;
291
292 /* Overflow must be added to zone counters */
293 z = n + os;
294 n = -os;
295 }
296 } while (this_cpu_cmpxchg(*p, o, n) != o);
297
298 if (z)
299 zone_page_state_add(z, zone, item);
300}
301
302void mod_zone_page_state(struct zone *zone, enum zone_stat_item item,
303 int delta)
304{
305 mod_state(zone, item, delta, 0);
306}
307EXPORT_SYMBOL(mod_zone_page_state);
308
309void inc_zone_state(struct zone *zone, enum zone_stat_item item)
310{
311 mod_state(zone, item, 1, 1);
312}
313
314void inc_zone_page_state(struct page *page, enum zone_stat_item item)
315{
316 mod_state(page_zone(page), item, 1, 1);
317}
318EXPORT_SYMBOL(inc_zone_page_state);
319
320void dec_zone_page_state(struct page *page, enum zone_stat_item item)
321{
322 mod_state(page_zone(page), item, -1, -1);
323}
324EXPORT_SYMBOL(dec_zone_page_state);
325#else
326/*
327 * Use interrupt disable to serialize counter updates
328 */
329void mod_zone_page_state(struct zone *zone, enum zone_stat_item item,
330 int delta)
331{
332 unsigned long flags;
333
334 local_irq_save(flags);
335 __mod_zone_page_state(zone, item, delta);
336 local_irq_restore(flags);
337}
338EXPORT_SYMBOL(mod_zone_page_state);
339
264void inc_zone_state(struct zone *zone, enum zone_stat_item item) 340void inc_zone_state(struct zone *zone, enum zone_stat_item item)
265{ 341{
266 unsigned long flags; 342 unsigned long flags;
@@ -291,6 +367,7 @@ void dec_zone_page_state(struct page *page, enum zone_stat_item item)
291 local_irq_restore(flags); 367 local_irq_restore(flags);
292} 368}
293EXPORT_SYMBOL(dec_zone_page_state); 369EXPORT_SYMBOL(dec_zone_page_state);
370#endif
294 371
295/* 372/*
296 * Update the zone counters for one cpu. 373 * Update the zone counters for one cpu.
@@ -750,8 +827,6 @@ static const char * const vmstat_text[] = {
750 "nr_shmem", 827 "nr_shmem",
751 "nr_dirtied", 828 "nr_dirtied",
752 "nr_written", 829 "nr_written",
753 "nr_dirty_threshold",
754 "nr_dirty_background_threshold",
755 830
756#ifdef CONFIG_NUMA 831#ifdef CONFIG_NUMA
757 "numa_hit", 832 "numa_hit",
@@ -761,6 +836,8 @@ static const char * const vmstat_text[] = {
761 "numa_local", 836 "numa_local",
762 "numa_other", 837 "numa_other",
763#endif 838#endif
839 "nr_dirty_threshold",
840 "nr_dirty_background_threshold",
764 841
765#ifdef CONFIG_VM_EVENT_COUNTERS 842#ifdef CONFIG_VM_EVENT_COUNTERS
766 "pgpgin", 843 "pgpgin",
@@ -1033,7 +1110,7 @@ static int __cpuinit vmstat_cpuup_callback(struct notifier_block *nfb,
1033 break; 1110 break;
1034 case CPU_DOWN_PREPARE: 1111 case CPU_DOWN_PREPARE:
1035 case CPU_DOWN_PREPARE_FROZEN: 1112 case CPU_DOWN_PREPARE_FROZEN:
1036 cancel_rearming_delayed_work(&per_cpu(vmstat_work, cpu)); 1113 cancel_delayed_work_sync(&per_cpu(vmstat_work, cpu));
1037 per_cpu(vmstat_work, cpu).work.func = NULL; 1114 per_cpu(vmstat_work, cpu).work.func = NULL;
1038 break; 1115 break;
1039 case CPU_DOWN_FAILED: 1116 case CPU_DOWN_FAILED: