aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--fs/proc/proc_misc.c14
-rw-r--r--include/linux/gfp.h12
-rw-r--r--include/linux/mmzone.h10
-rw-r--r--mm/page_alloc.c20
-rw-r--r--mm/vmstat.c286
5 files changed, 241 insertions, 101 deletions
diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c
index 0071939c0095..5de7f874d95c 100644
--- a/fs/proc/proc_misc.c
+++ b/fs/proc/proc_misc.c
@@ -229,6 +229,19 @@ static const struct file_operations fragmentation_file_operations = {
229 .release = seq_release, 229 .release = seq_release,
230}; 230};
231 231
232extern struct seq_operations pagetypeinfo_op;
233static int pagetypeinfo_open(struct inode *inode, struct file *file)
234{
235 return seq_open(file, &pagetypeinfo_op);
236}
237
238static const struct file_operations pagetypeinfo_file_ops = {
239 .open = pagetypeinfo_open,
240 .read = seq_read,
241 .llseek = seq_lseek,
242 .release = seq_release,
243};
244
232extern struct seq_operations zoneinfo_op; 245extern struct seq_operations zoneinfo_op;
233static int zoneinfo_open(struct inode *inode, struct file *file) 246static int zoneinfo_open(struct inode *inode, struct file *file)
234{ 247{
@@ -724,6 +737,7 @@ void __init proc_misc_init(void)
724#endif 737#endif
725#endif 738#endif
726 create_seq_entry("buddyinfo",S_IRUGO, &fragmentation_file_operations); 739 create_seq_entry("buddyinfo",S_IRUGO, &fragmentation_file_operations);
740 create_seq_entry("pagetypeinfo", S_IRUGO, &pagetypeinfo_file_ops);
727 create_seq_entry("vmstat",S_IRUGO, &proc_vmstat_file_operations); 741 create_seq_entry("vmstat",S_IRUGO, &proc_vmstat_file_operations);
728 create_seq_entry("zoneinfo",S_IRUGO, &proc_zoneinfo_file_operations); 742 create_seq_entry("zoneinfo",S_IRUGO, &proc_zoneinfo_file_operations);
729#ifdef CONFIG_BLOCK 743#ifdef CONFIG_BLOCK
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index f8ffcd401c5f..7e93a9ae7064 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -104,6 +104,18 @@ struct vm_area_struct;
104/* 4GB DMA on some platforms */ 104/* 4GB DMA on some platforms */
105#define GFP_DMA32 __GFP_DMA32 105#define GFP_DMA32 __GFP_DMA32
106 106
107/* Convert GFP flags to their corresponding migrate type */
108static inline int allocflags_to_migratetype(gfp_t gfp_flags)
109{
110 WARN_ON((gfp_flags & GFP_MOVABLE_MASK) == GFP_MOVABLE_MASK);
111
112 if (unlikely(page_group_by_mobility_disabled))
113 return MIGRATE_UNMOVABLE;
114
115 /* Group based on mobility */
116 return (((gfp_flags & __GFP_MOVABLE) != 0) << 1) |
117 ((gfp_flags & __GFP_RECLAIMABLE) != 0);
118}
107 119
108static inline enum zone_type gfp_zone(gfp_t flags) 120static inline enum zone_type gfp_zone(gfp_t flags)
109{ 121{
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 87a4045580f2..20ea42c45e4a 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -43,6 +43,16 @@
43 for (order = 0; order < MAX_ORDER; order++) \ 43 for (order = 0; order < MAX_ORDER; order++) \
44 for (type = 0; type < MIGRATE_TYPES; type++) 44 for (type = 0; type < MIGRATE_TYPES; type++)
45 45
46extern int page_group_by_mobility_disabled;
47
48static inline int get_pageblock_migratetype(struct page *page)
49{
50 if (unlikely(page_group_by_mobility_disabled))
51 return MIGRATE_UNMOVABLE;
52
53 return get_pageblock_flags_group(page, PB_migrate, PB_migrate_end);
54}
55
46struct free_area { 56struct free_area {
47 struct list_head free_list[MIGRATE_TYPES]; 57 struct list_head free_list[MIGRATE_TYPES];
48 unsigned long nr_free; 58 unsigned long nr_free;
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index b9bc7369c48d..17797d062c3b 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -164,32 +164,12 @@ EXPORT_SYMBOL(nr_node_ids);
164 164
165int page_group_by_mobility_disabled __read_mostly; 165int page_group_by_mobility_disabled __read_mostly;
166 166
167static inline int get_pageblock_migratetype(struct page *page)
168{
169 if (unlikely(page_group_by_mobility_disabled))
170 return MIGRATE_UNMOVABLE;
171
172 return get_pageblock_flags_group(page, PB_migrate, PB_migrate_end);
173}
174
175static void set_pageblock_migratetype(struct page *page, int migratetype) 167static void set_pageblock_migratetype(struct page *page, int migratetype)
176{ 168{
177 set_pageblock_flags_group(page, (unsigned long)migratetype, 169 set_pageblock_flags_group(page, (unsigned long)migratetype,
178 PB_migrate, PB_migrate_end); 170 PB_migrate, PB_migrate_end);
179} 171}
180 172
181static inline int allocflags_to_migratetype(gfp_t gfp_flags)
182{
183 WARN_ON((gfp_flags & GFP_MOVABLE_MASK) == GFP_MOVABLE_MASK);
184
185 if (unlikely(page_group_by_mobility_disabled))
186 return MIGRATE_UNMOVABLE;
187
188 /* Cluster based on mobility */
189 return (((gfp_flags & __GFP_MOVABLE) != 0) << 1) |
190 ((gfp_flags & __GFP_RECLAIMABLE) != 0);
191}
192
193#ifdef CONFIG_DEBUG_VM 173#ifdef CONFIG_DEBUG_VM
194static int page_outside_zone_boundaries(struct zone *zone, struct page *page) 174static int page_outside_zone_boundaries(struct zone *zone, struct page *page)
195{ 175{
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