diff options
Diffstat (limited to 'mm/vmstat.c')
-rw-r--r-- | mm/vmstat.c | 218 |
1 files changed, 214 insertions, 4 deletions
diff --git a/mm/vmstat.c b/mm/vmstat.c index ad456202ff1a..210f9bbbb04f 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c | |||
@@ -3,10 +3,15 @@ | |||
3 | * | 3 | * |
4 | * Manages VM statistics | 4 | * Manages VM statistics |
5 | * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds | 5 | * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds |
6 | * | ||
7 | * zoned VM statistics | ||
8 | * Copyright (C) 2006 Silicon Graphics, Inc., | ||
9 | * Christoph Lameter <christoph@lameter.com> | ||
6 | */ | 10 | */ |
7 | 11 | ||
8 | #include <linux/config.h> | 12 | #include <linux/config.h> |
9 | #include <linux/mm.h> | 13 | #include <linux/mm.h> |
14 | #include <linux/module.h> | ||
10 | 15 | ||
11 | /* | 16 | /* |
12 | * Accumulate the page_state information across all CPUs. | 17 | * Accumulate the page_state information across all CPUs. |
@@ -143,6 +148,197 @@ void get_zone_counts(unsigned long *active, | |||
143 | } | 148 | } |
144 | } | 149 | } |
145 | 150 | ||
151 | /* | ||
152 | * Manage combined zone based / global counters | ||
153 | * | ||
154 | * vm_stat contains the global counters | ||
155 | */ | ||
156 | atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS]; | ||
157 | EXPORT_SYMBOL(vm_stat); | ||
158 | |||
159 | #ifdef CONFIG_SMP | ||
160 | |||
161 | #define STAT_THRESHOLD 32 | ||
162 | |||
163 | /* | ||
164 | * Determine pointer to currently valid differential byte given a zone and | ||
165 | * the item number. | ||
166 | * | ||
167 | * Preemption must be off | ||
168 | */ | ||
169 | static inline s8 *diff_pointer(struct zone *zone, enum zone_stat_item item) | ||
170 | { | ||
171 | return &zone_pcp(zone, smp_processor_id())->vm_stat_diff[item]; | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | * For use when we know that interrupts are disabled. | ||
176 | */ | ||
177 | void __mod_zone_page_state(struct zone *zone, enum zone_stat_item item, | ||
178 | int delta) | ||
179 | { | ||
180 | s8 *p; | ||
181 | long x; | ||
182 | |||
183 | p = diff_pointer(zone, item); | ||
184 | x = delta + *p; | ||
185 | |||
186 | if (unlikely(x > STAT_THRESHOLD || x < -STAT_THRESHOLD)) { | ||
187 | zone_page_state_add(x, zone, item); | ||
188 | x = 0; | ||
189 | } | ||
190 | |||
191 | *p = x; | ||
192 | } | ||
193 | EXPORT_SYMBOL(__mod_zone_page_state); | ||
194 | |||
195 | /* | ||
196 | * For an unknown interrupt state | ||
197 | */ | ||
198 | void mod_zone_page_state(struct zone *zone, enum zone_stat_item item, | ||
199 | int delta) | ||
200 | { | ||
201 | unsigned long flags; | ||
202 | |||
203 | local_irq_save(flags); | ||
204 | __mod_zone_page_state(zone, item, delta); | ||
205 | local_irq_restore(flags); | ||
206 | } | ||
207 | EXPORT_SYMBOL(mod_zone_page_state); | ||
208 | |||
209 | /* | ||
210 | * Optimized increment and decrement functions. | ||
211 | * | ||
212 | * These are only for a single page and therefore can take a struct page * | ||
213 | * argument instead of struct zone *. This allows the inclusion of the code | ||
214 | * generated for page_zone(page) into the optimized functions. | ||
215 | * | ||
216 | * No overflow check is necessary and therefore the differential can be | ||
217 | * incremented or decremented in place which may allow the compilers to | ||
218 | * generate better code. | ||
219 | * | ||
220 | * The increment or decrement is known and therefore one boundary check can | ||
221 | * be omitted. | ||
222 | * | ||
223 | * Some processors have inc/dec instructions that are atomic vs an interrupt. | ||
224 | * However, the code must first determine the differential location in a zone | ||
225 | * based on the processor number and then inc/dec the counter. There is no | ||
226 | * guarantee without disabling preemption that the processor will not change | ||
227 | * in between and therefore the atomicity vs. interrupt cannot be exploited | ||
228 | * in a useful way here. | ||
229 | */ | ||
230 | void __inc_zone_page_state(struct page *page, enum zone_stat_item item) | ||
231 | { | ||
232 | struct zone *zone = page_zone(page); | ||
233 | s8 *p = diff_pointer(zone, item); | ||
234 | |||
235 | (*p)++; | ||
236 | |||
237 | if (unlikely(*p > STAT_THRESHOLD)) { | ||
238 | zone_page_state_add(*p, zone, item); | ||
239 | *p = 0; | ||
240 | } | ||
241 | } | ||
242 | EXPORT_SYMBOL(__inc_zone_page_state); | ||
243 | |||
244 | void __dec_zone_page_state(struct page *page, enum zone_stat_item item) | ||
245 | { | ||
246 | struct zone *zone = page_zone(page); | ||
247 | s8 *p = diff_pointer(zone, item); | ||
248 | |||
249 | (*p)--; | ||
250 | |||
251 | if (unlikely(*p < -STAT_THRESHOLD)) { | ||
252 | zone_page_state_add(*p, zone, item); | ||
253 | *p = 0; | ||
254 | } | ||
255 | } | ||
256 | EXPORT_SYMBOL(__dec_zone_page_state); | ||
257 | |||
258 | void inc_zone_page_state(struct page *page, enum zone_stat_item item) | ||
259 | { | ||
260 | unsigned long flags; | ||
261 | struct zone *zone; | ||
262 | s8 *p; | ||
263 | |||
264 | zone = page_zone(page); | ||
265 | local_irq_save(flags); | ||
266 | p = diff_pointer(zone, item); | ||
267 | |||
268 | (*p)++; | ||
269 | |||
270 | if (unlikely(*p > STAT_THRESHOLD)) { | ||
271 | zone_page_state_add(*p, zone, item); | ||
272 | *p = 0; | ||
273 | } | ||
274 | local_irq_restore(flags); | ||
275 | } | ||
276 | EXPORT_SYMBOL(inc_zone_page_state); | ||
277 | |||
278 | void dec_zone_page_state(struct page *page, enum zone_stat_item item) | ||
279 | { | ||
280 | unsigned long flags; | ||
281 | struct zone *zone; | ||
282 | s8 *p; | ||
283 | |||
284 | zone = page_zone(page); | ||
285 | local_irq_save(flags); | ||
286 | p = diff_pointer(zone, item); | ||
287 | |||
288 | (*p)--; | ||
289 | |||
290 | if (unlikely(*p < -STAT_THRESHOLD)) { | ||
291 | zone_page_state_add(*p, zone, item); | ||
292 | *p = 0; | ||
293 | } | ||
294 | local_irq_restore(flags); | ||
295 | } | ||
296 | EXPORT_SYMBOL(dec_zone_page_state); | ||
297 | |||
298 | /* | ||
299 | * Update the zone counters for one cpu. | ||
300 | */ | ||
301 | void refresh_cpu_vm_stats(int cpu) | ||
302 | { | ||
303 | struct zone *zone; | ||
304 | int i; | ||
305 | unsigned long flags; | ||
306 | |||
307 | for_each_zone(zone) { | ||
308 | struct per_cpu_pageset *pcp; | ||
309 | |||
310 | pcp = zone_pcp(zone, cpu); | ||
311 | |||
312 | for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) | ||
313 | if (pcp->vm_stat_diff[i]) { | ||
314 | local_irq_save(flags); | ||
315 | zone_page_state_add(pcp->vm_stat_diff[i], | ||
316 | zone, i); | ||
317 | pcp->vm_stat_diff[i] = 0; | ||
318 | local_irq_restore(flags); | ||
319 | } | ||
320 | } | ||
321 | } | ||
322 | |||
323 | static void __refresh_cpu_vm_stats(void *dummy) | ||
324 | { | ||
325 | refresh_cpu_vm_stats(smp_processor_id()); | ||
326 | } | ||
327 | |||
328 | /* | ||
329 | * Consolidate all counters. | ||
330 | * | ||
331 | * Note that the result is less inaccurate but still inaccurate | ||
332 | * if concurrent processes are allowed to run. | ||
333 | */ | ||
334 | void refresh_vm_stats(void) | ||
335 | { | ||
336 | on_each_cpu(__refresh_cpu_vm_stats, NULL, 0, 1); | ||
337 | } | ||
338 | EXPORT_SYMBOL(refresh_vm_stats); | ||
339 | |||
340 | #endif | ||
341 | |||
146 | #ifdef CONFIG_PROC_FS | 342 | #ifdef CONFIG_PROC_FS |
147 | 343 | ||
148 | #include <linux/seq_file.h> | 344 | #include <linux/seq_file.h> |
@@ -204,6 +400,9 @@ struct seq_operations fragmentation_op = { | |||
204 | }; | 400 | }; |
205 | 401 | ||
206 | static char *vmstat_text[] = { | 402 | static char *vmstat_text[] = { |
403 | /* Zoned VM counters */ | ||
404 | |||
405 | /* Page state */ | ||
207 | "nr_dirty", | 406 | "nr_dirty", |
208 | "nr_writeback", | 407 | "nr_writeback", |
209 | "nr_unstable", | 408 | "nr_unstable", |
@@ -297,6 +496,11 @@ static int zoneinfo_show(struct seq_file *m, void *arg) | |||
297 | zone->nr_scan_active, zone->nr_scan_inactive, | 496 | zone->nr_scan_active, zone->nr_scan_inactive, |
298 | zone->spanned_pages, | 497 | zone->spanned_pages, |
299 | zone->present_pages); | 498 | zone->present_pages); |
499 | |||
500 | for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) | ||
501 | seq_printf(m, "\n %-12s %lu", vmstat_text[i], | ||
502 | zone_page_state(zone, i)); | ||
503 | |||
300 | seq_printf(m, | 504 | seq_printf(m, |
301 | "\n protection: (%lu", | 505 | "\n protection: (%lu", |
302 | zone->lowmem_reserve[0]); | 506 | zone->lowmem_reserve[0]); |
@@ -368,19 +572,25 @@ struct seq_operations zoneinfo_op = { | |||
368 | 572 | ||
369 | static void *vmstat_start(struct seq_file *m, loff_t *pos) | 573 | static void *vmstat_start(struct seq_file *m, loff_t *pos) |
370 | { | 574 | { |
575 | unsigned long *v; | ||
371 | struct page_state *ps; | 576 | struct page_state *ps; |
577 | int i; | ||
372 | 578 | ||
373 | if (*pos >= ARRAY_SIZE(vmstat_text)) | 579 | if (*pos >= ARRAY_SIZE(vmstat_text)) |
374 | return NULL; | 580 | return NULL; |
375 | 581 | ||
376 | ps = kmalloc(sizeof(*ps), GFP_KERNEL); | 582 | v = kmalloc(NR_VM_ZONE_STAT_ITEMS * sizeof(unsigned long) |
377 | m->private = ps; | 583 | + sizeof(*ps), GFP_KERNEL); |
378 | if (!ps) | 584 | m->private = v; |
585 | if (!v) | ||
379 | return ERR_PTR(-ENOMEM); | 586 | return ERR_PTR(-ENOMEM); |
587 | for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) | ||
588 | v[i] = global_page_state(i); | ||
589 | ps = (struct page_state *)(v + NR_VM_ZONE_STAT_ITEMS); | ||
380 | get_full_page_state(ps); | 590 | get_full_page_state(ps); |
381 | ps->pgpgin /= 2; /* sectors -> kbytes */ | 591 | ps->pgpgin /= 2; /* sectors -> kbytes */ |
382 | ps->pgpgout /= 2; | 592 | ps->pgpgout /= 2; |
383 | return (unsigned long *)ps + *pos; | 593 | return v + *pos; |
384 | } | 594 | } |
385 | 595 | ||
386 | static void *vmstat_next(struct seq_file *m, void *arg, loff_t *pos) | 596 | static void *vmstat_next(struct seq_file *m, void *arg, loff_t *pos) |