aboutsummaryrefslogtreecommitdiffstats
path: root/mm/vmstat.c
diff options
context:
space:
mode:
authorMel Gorman <mel@csn.ul.ie>2007-10-16 04:26:02 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-16 12:43:00 -0400
commit467c996c1e1910633fa8e7adc9b052aa3ed5f97c (patch)
tree09e0e70160386be1bdaa12801afddf287e12c8a1 /mm/vmstat.c
parentd9c2340052278d8eb2ffb16b0484f8f794def4de (diff)
Print out statistics in relation to fragmentation avoidance to /proc/pagetypeinfo
This patch provides fragmentation avoidance statistics via /proc/pagetypeinfo. The information is collected only on request so there is no runtime overhead. The statistics are in three parts: The first part prints information on the size of blocks that pages are being grouped on and looks like Page block order: 10 Pages per block: 1024 The second part is a more detailed version of /proc/buddyinfo and looks like Free pages count per migrate type at order 0 1 2 3 4 5 6 7 8 9 10 Node 0, zone DMA, type Unmovable 0 0 0 0 0 0 0 0 0 0 0 Node 0, zone DMA, type Reclaimable 1 0 0 0 0 0 0 0 0 0 0 Node 0, zone DMA, type Movable 0 0 0 0 0 0 0 0 0 0 0 Node 0, zone DMA, type Reserve 0 4 4 0 0 0 0 1 0 1 0 Node 0, zone Normal, type Unmovable 111 8 4 4 2 3 1 0 0 0 0 Node 0, zone Normal, type Reclaimable 293 89 8 0 0 0 0 0 0 0 0 Node 0, zone Normal, type Movable 1 6 13 9 7 6 3 0 0 0 0 Node 0, zone Normal, type Reserve 0 0 0 0 0 0 0 0 0 0 4 The third part looks like Number of blocks type Unmovable Reclaimable Movable Reserve Node 0, zone DMA 0 1 2 1 Node 0, zone Normal 3 17 94 4 To walk the zones within a node with interrupts disabled, walk_zones_in_node() is introduced and shared between /proc/buddyinfo, /proc/zoneinfo and /proc/pagetypeinfo to reduce code duplication. It seems specific to what vmstat.c requires but could be broken out as a general utility function in mmzone.c if there were other other potential users. Signed-off-by: Mel Gorman <mel@csn.ul.ie> Acked-by: Andy Whitcroft <apw@shadowen.org> Acked-by: Christoph Lameter <clameter@sgi.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/vmstat.c')
-rw-r--r--mm/vmstat.c286
1 files changed, 205 insertions, 81 deletions
diff --git a/mm/vmstat.c b/mm/vmstat.c
index c64d169537bf..2f19e4f867c1 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -398,6 +398,13 @@ void zone_statistics(struct zonelist *zonelist, struct zone *z)
398 398
399#include <linux/seq_file.h> 399#include <linux/seq_file.h>
400 400
401static char * const migratetype_names[MIGRATE_TYPES] = {
402 "Unmovable",
403 "Reclaimable",
404 "Movable",
405 "Reserve",
406};
407
401static void *frag_start(struct seq_file *m, loff_t *pos) 408static void *frag_start(struct seq_file *m, loff_t *pos)
402{ 409{
403 pg_data_t *pgdat; 410 pg_data_t *pgdat;
@@ -422,28 +429,144 @@ static void frag_stop(struct seq_file *m, void *arg)
422{ 429{
423} 430}
424 431
425/* 432/* Walk all the zones in a node and print using a callback */
426 * This walks the free areas for each zone. 433static void walk_zones_in_node(struct seq_file *m, pg_data_t *pgdat,
427 */ 434 void (*print)(struct seq_file *m, pg_data_t *, struct zone *))
428static int frag_show(struct seq_file *m, void *arg)
429{ 435{
430 pg_data_t *pgdat = (pg_data_t *)arg;
431 struct zone *zone; 436 struct zone *zone;
432 struct zone *node_zones = pgdat->node_zones; 437 struct zone *node_zones = pgdat->node_zones;
433 unsigned long flags; 438 unsigned long flags;
434 int order;
435 439
436 for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) { 440 for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
437 if (!populated_zone(zone)) 441 if (!populated_zone(zone))
438 continue; 442 continue;
439 443
440 spin_lock_irqsave(&zone->lock, flags); 444 spin_lock_irqsave(&zone->lock, flags);
441 seq_printf(m, "Node %d, zone %8s ", pgdat->node_id, zone->name); 445 print(m, pgdat, zone);
442 for (order = 0; order < MAX_ORDER; ++order)
443 seq_printf(m, "%6lu ", zone->free_area[order].nr_free);
444 spin_unlock_irqrestore(&zone->lock, flags); 446 spin_unlock_irqrestore(&zone->lock, flags);
447 }
448}
449
450static void frag_show_print(struct seq_file *m, pg_data_t *pgdat,
451 struct zone *zone)
452{
453 int order;
454
455 seq_printf(m, "Node %d, zone %8s ", pgdat->node_id, zone->name);
456 for (order = 0; order < MAX_ORDER; ++order)
457 seq_printf(m, "%6lu ", zone->free_area[order].nr_free);
458 seq_putc(m, '\n');
459}
460
461/*
462 * This walks the free areas for each zone.
463 */
464static int frag_show(struct seq_file *m, void *arg)
465{
466 pg_data_t *pgdat = (pg_data_t *)arg;
467 walk_zones_in_node(m, pgdat, frag_show_print);
468 return 0;
469}
470
471static void pagetypeinfo_showfree_print(struct seq_file *m,
472 pg_data_t *pgdat, struct zone *zone)
473{
474 int order, mtype;
475
476 for (mtype = 0; mtype < MIGRATE_TYPES; mtype++) {
477 seq_printf(m, "Node %4d, zone %8s, type %12s ",
478 pgdat->node_id,
479 zone->name,
480 migratetype_names[mtype]);
481 for (order = 0; order < MAX_ORDER; ++order) {
482 unsigned long freecount = 0;
483 struct free_area *area;
484 struct list_head *curr;
485
486 area = &(zone->free_area[order]);
487
488 list_for_each(curr, &area->free_list[mtype])
489 freecount++;
490 seq_printf(m, "%6lu ", freecount);
491 }
445 seq_putc(m, '\n'); 492 seq_putc(m, '\n');
446 } 493 }
494}
495
496/* Print out the free pages at each order for each migatetype */
497static int pagetypeinfo_showfree(struct seq_file *m, void *arg)
498{
499 int order;
500 pg_data_t *pgdat = (pg_data_t *)arg;
501
502 /* Print header */
503 seq_printf(m, "%-43s ", "Free pages count per migrate type at order");
504 for (order = 0; order < MAX_ORDER; ++order)
505 seq_printf(m, "%6d ", order);
506 seq_putc(m, '\n');
507
508 walk_zones_in_node(m, pgdat, pagetypeinfo_showfree_print);
509
510 return 0;
511}
512
513static void pagetypeinfo_showblockcount_print(struct seq_file *m,
514 pg_data_t *pgdat, struct zone *zone)
515{
516 int mtype;
517 unsigned long pfn;
518 unsigned long start_pfn = zone->zone_start_pfn;
519 unsigned long end_pfn = start_pfn + zone->spanned_pages;
520 unsigned long count[MIGRATE_TYPES] = { 0, };
521
522 for (pfn = start_pfn; pfn < end_pfn; pfn += pageblock_nr_pages) {
523 struct page *page;
524
525 if (!pfn_valid(pfn))
526 continue;
527
528 page = pfn_to_page(pfn);
529 mtype = get_pageblock_migratetype(page);
530
531 count[mtype]++;
532 }
533
534 /* Print counts */
535 seq_printf(m, "Node %d, zone %8s ", pgdat->node_id, zone->name);
536 for (mtype = 0; mtype < MIGRATE_TYPES; mtype++)
537 seq_printf(m, "%12lu ", count[mtype]);
538 seq_putc(m, '\n');
539}
540
541/* Print out the free pages at each order for each migratetype */
542static int pagetypeinfo_showblockcount(struct seq_file *m, void *arg)
543{
544 int mtype;
545 pg_data_t *pgdat = (pg_data_t *)arg;
546
547 seq_printf(m, "\n%-23s", "Number of blocks type ");
548 for (mtype = 0; mtype < MIGRATE_TYPES; mtype++)
549 seq_printf(m, "%12s ", migratetype_names[mtype]);
550 seq_putc(m, '\n');
551 walk_zones_in_node(m, pgdat, pagetypeinfo_showblockcount_print);
552
553 return 0;
554}
555
556/*
557 * This prints out statistics in relation to grouping pages by mobility.
558 * It is expensive to collect so do not constantly read the file.
559 */
560static int pagetypeinfo_show(struct seq_file *m, void *arg)
561{
562 pg_data_t *pgdat = (pg_data_t *)arg;
563
564 seq_printf(m, "Page block order: %d\n", pageblock_order);
565 seq_printf(m, "Pages per block: %lu\n", pageblock_nr_pages);
566 seq_putc(m, '\n');
567 pagetypeinfo_showfree(m, pgdat);
568 pagetypeinfo_showblockcount(m, pgdat);
569
447 return 0; 570 return 0;
448} 571}
449 572
@@ -454,6 +577,13 @@ const struct seq_operations fragmentation_op = {
454 .show = frag_show, 577 .show = frag_show,
455}; 578};
456 579
580const struct seq_operations pagetypeinfo_op = {
581 .start = frag_start,
582 .next = frag_next,
583 .stop = frag_stop,
584 .show = pagetypeinfo_show,
585};
586
457#ifdef CONFIG_ZONE_DMA 587#ifdef CONFIG_ZONE_DMA
458#define TEXT_FOR_DMA(xx) xx "_dma", 588#define TEXT_FOR_DMA(xx) xx "_dma",
459#else 589#else
@@ -532,84 +662,78 @@ static const char * const vmstat_text[] = {
532#endif 662#endif
533}; 663};
534 664
535/* 665static void zoneinfo_show_print(struct seq_file *m, pg_data_t *pgdat,
536 * Output information about zones in @pgdat. 666 struct zone *zone)
537 */
538static int zoneinfo_show(struct seq_file *m, void *arg)
539{ 667{
540 pg_data_t *pgdat = arg; 668 int i;
541 struct zone *zone; 669 seq_printf(m, "Node %d, zone %8s", pgdat->node_id, zone->name);
542 struct zone *node_zones = pgdat->node_zones; 670 seq_printf(m,
543 unsigned long flags; 671 "\n pages free %lu"
544 672 "\n min %lu"
545 for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; zone++) { 673 "\n low %lu"
546 int i; 674 "\n high %lu"
547 675 "\n scanned %lu (a: %lu i: %lu)"
548 if (!populated_zone(zone)) 676 "\n spanned %lu"
549 continue; 677 "\n present %lu",
550 678 zone_page_state(zone, NR_FREE_PAGES),
551 spin_lock_irqsave(&zone->lock, flags); 679 zone->pages_min,
552 seq_printf(m, "Node %d, zone %8s", pgdat->node_id, zone->name); 680 zone->pages_low,
553 seq_printf(m, 681 zone->pages_high,
554 "\n pages free %lu" 682 zone->pages_scanned,
555 "\n min %lu" 683 zone->nr_scan_active, zone->nr_scan_inactive,
556 "\n low %lu" 684 zone->spanned_pages,
557 "\n high %lu" 685 zone->present_pages);
558 "\n scanned %lu (a: %lu i: %lu)"
559 "\n spanned %lu"
560 "\n present %lu",
561 zone_page_state(zone, NR_FREE_PAGES),
562 zone->pages_min,
563 zone->pages_low,
564 zone->pages_high,
565 zone->pages_scanned,
566 zone->nr_scan_active, zone->nr_scan_inactive,
567 zone->spanned_pages,
568 zone->present_pages);
569 686
570 for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) 687 for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
571 seq_printf(m, "\n %-12s %lu", vmstat_text[i], 688 seq_printf(m, "\n %-12s %lu", vmstat_text[i],
572 zone_page_state(zone, i)); 689 zone_page_state(zone, i));
573 690
574 seq_printf(m, 691 seq_printf(m,
575 "\n protection: (%lu", 692 "\n protection: (%lu",
576 zone->lowmem_reserve[0]); 693 zone->lowmem_reserve[0]);
577 for (i = 1; i < ARRAY_SIZE(zone->lowmem_reserve); i++) 694 for (i = 1; i < ARRAY_SIZE(zone->lowmem_reserve); i++)
578 seq_printf(m, ", %lu", zone->lowmem_reserve[i]); 695 seq_printf(m, ", %lu", zone->lowmem_reserve[i]);
579 seq_printf(m, 696 seq_printf(m,
580 ")" 697 ")"
581 "\n pagesets"); 698 "\n pagesets");
582 for_each_online_cpu(i) { 699 for_each_online_cpu(i) {
583 struct per_cpu_pageset *pageset; 700 struct per_cpu_pageset *pageset;
584 int j; 701 int j;
585 702
586 pageset = zone_pcp(zone, i); 703 pageset = zone_pcp(zone, i);
587 for (j = 0; j < ARRAY_SIZE(pageset->pcp); j++) { 704 for (j = 0; j < ARRAY_SIZE(pageset->pcp); j++) {
588 seq_printf(m, 705 seq_printf(m,
589 "\n cpu: %i pcp: %i" 706 "\n cpu: %i pcp: %i"
590 "\n count: %i" 707 "\n count: %i"
591 "\n high: %i" 708 "\n high: %i"
592 "\n batch: %i", 709 "\n batch: %i",
593 i, j, 710 i, j,
594 pageset->pcp[j].count, 711 pageset->pcp[j].count,
595 pageset->pcp[j].high, 712 pageset->pcp[j].high,
596 pageset->pcp[j].batch); 713 pageset->pcp[j].batch);
597 } 714 }
598#ifdef CONFIG_SMP 715#ifdef CONFIG_SMP
599 seq_printf(m, "\n vm stats threshold: %d", 716 seq_printf(m, "\n vm stats threshold: %d",
600 pageset->stat_threshold); 717 pageset->stat_threshold);
601#endif 718#endif
602 }
603 seq_printf(m,
604 "\n all_unreclaimable: %u"
605 "\n prev_priority: %i"
606 "\n start_pfn: %lu",
607 zone->all_unreclaimable,
608 zone->prev_priority,
609 zone->zone_start_pfn);
610 spin_unlock_irqrestore(&zone->lock, flags);
611 seq_putc(m, '\n');
612 } 719 }
720 seq_printf(m,
721 "\n all_unreclaimable: %u"
722 "\n prev_priority: %i"
723 "\n start_pfn: %lu",
724 zone->all_unreclaimable,
725 zone->prev_priority,
726 zone->zone_start_pfn);
727 seq_putc(m, '\n');
728}
729
730/*
731 * Output information about zones in @pgdat.
732 */
733static int zoneinfo_show(struct seq_file *m, void *arg)
734{
735 pg_data_t *pgdat = (pg_data_t *)arg;
736 walk_zones_in_node(m, pgdat, zoneinfo_show_print);
613 return 0; 737 return 0;
614} 738}
615 739