diff options
Diffstat (limited to 'mm/vmstat.c')
-rw-r--r-- | mm/vmstat.c | 155 |
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) | |||
167 | void __mod_zone_page_state(struct zone *zone, enum zone_stat_item item, | 167 | void __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 | } |
183 | EXPORT_SYMBOL(__mod_zone_page_state); | 185 | EXPORT_SYMBOL(__mod_zone_page_state); |
184 | 186 | ||
185 | /* | 187 | /* |
186 | * For an unknown interrupt state | ||
187 | */ | ||
188 | void 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 | } | ||
197 | EXPORT_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 | */ |
222 | void __inc_zone_state(struct zone *zone, enum zone_stat_item item) | 210 | void __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 | ||
243 | void __dec_zone_state(struct zone *zone, enum zone_stat_item item) | 232 | void __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 | } |
262 | EXPORT_SYMBOL(__dec_zone_page_state); | 252 | EXPORT_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 | */ | ||
267 | static 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 | |||
302 | void mod_zone_page_state(struct zone *zone, enum zone_stat_item item, | ||
303 | int delta) | ||
304 | { | ||
305 | mod_state(zone, item, delta, 0); | ||
306 | } | ||
307 | EXPORT_SYMBOL(mod_zone_page_state); | ||
308 | |||
309 | void inc_zone_state(struct zone *zone, enum zone_stat_item item) | ||
310 | { | ||
311 | mod_state(zone, item, 1, 1); | ||
312 | } | ||
313 | |||
314 | void inc_zone_page_state(struct page *page, enum zone_stat_item item) | ||
315 | { | ||
316 | mod_state(page_zone(page), item, 1, 1); | ||
317 | } | ||
318 | EXPORT_SYMBOL(inc_zone_page_state); | ||
319 | |||
320 | void dec_zone_page_state(struct page *page, enum zone_stat_item item) | ||
321 | { | ||
322 | mod_state(page_zone(page), item, -1, -1); | ||
323 | } | ||
324 | EXPORT_SYMBOL(dec_zone_page_state); | ||
325 | #else | ||
326 | /* | ||
327 | * Use interrupt disable to serialize counter updates | ||
328 | */ | ||
329 | void 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 | } | ||
338 | EXPORT_SYMBOL(mod_zone_page_state); | ||
339 | |||
264 | void inc_zone_state(struct zone *zone, enum zone_stat_item item) | 340 | void 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 | } |
293 | EXPORT_SYMBOL(dec_zone_page_state); | 369 | EXPORT_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: |