|
@@ -398,6 +398,13 @@ void zone_statistics(struct zonelist *zonelist, struct zone *z)
|
|
|
|
|
|
#include <linux/seq_file.h>
|
|
|
|
|
|
+static char * const migratetype_names[MIGRATE_TYPES] = {
|
|
|
+ "Unmovable",
|
|
|
+ "Reclaimable",
|
|
|
+ "Movable",
|
|
|
+ "Reserve",
|
|
|
+};
|
|
|
+
|
|
|
static void *frag_start(struct seq_file *m, loff_t *pos)
|
|
|
{
|
|
|
pg_data_t *pgdat;
|
|
@@ -422,28 +429,144 @@ static void frag_stop(struct seq_file *m, void *arg)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * This walks the free areas for each zone.
|
|
|
- */
|
|
|
-static int frag_show(struct seq_file *m, void *arg)
|
|
|
+/* Walk all the zones in a node and print using a callback */
|
|
|
+static void walk_zones_in_node(struct seq_file *m, pg_data_t *pgdat,
|
|
|
+ void (*print)(struct seq_file *m, pg_data_t *, struct zone *))
|
|
|
{
|
|
|
- pg_data_t *pgdat = (pg_data_t *)arg;
|
|
|
struct zone *zone;
|
|
|
struct zone *node_zones = pgdat->node_zones;
|
|
|
unsigned long flags;
|
|
|
- int order;
|
|
|
|
|
|
for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
|
|
|
if (!populated_zone(zone))
|
|
|
continue;
|
|
|
|
|
|
spin_lock_irqsave(&zone->lock, flags);
|
|
|
- seq_printf(m, "Node %d, zone %8s ", pgdat->node_id, zone->name);
|
|
|
- for (order = 0; order < MAX_ORDER; ++order)
|
|
|
- seq_printf(m, "%6lu ", zone->free_area[order].nr_free);
|
|
|
+ print(m, pgdat, zone);
|
|
|
spin_unlock_irqrestore(&zone->lock, flags);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void frag_show_print(struct seq_file *m, pg_data_t *pgdat,
|
|
|
+ struct zone *zone)
|
|
|
+{
|
|
|
+ int order;
|
|
|
+
|
|
|
+ seq_printf(m, "Node %d, zone %8s ", pgdat->node_id, zone->name);
|
|
|
+ for (order = 0; order < MAX_ORDER; ++order)
|
|
|
+ seq_printf(m, "%6lu ", zone->free_area[order].nr_free);
|
|
|
+ seq_putc(m, '\n');
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * This walks the free areas for each zone.
|
|
|
+ */
|
|
|
+static int frag_show(struct seq_file *m, void *arg)
|
|
|
+{
|
|
|
+ pg_data_t *pgdat = (pg_data_t *)arg;
|
|
|
+ walk_zones_in_node(m, pgdat, frag_show_print);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void pagetypeinfo_showfree_print(struct seq_file *m,
|
|
|
+ pg_data_t *pgdat, struct zone *zone)
|
|
|
+{
|
|
|
+ int order, mtype;
|
|
|
+
|
|
|
+ for (mtype = 0; mtype < MIGRATE_TYPES; mtype++) {
|
|
|
+ seq_printf(m, "Node %4d, zone %8s, type %12s ",
|
|
|
+ pgdat->node_id,
|
|
|
+ zone->name,
|
|
|
+ migratetype_names[mtype]);
|
|
|
+ for (order = 0; order < MAX_ORDER; ++order) {
|
|
|
+ unsigned long freecount = 0;
|
|
|
+ struct free_area *area;
|
|
|
+ struct list_head *curr;
|
|
|
+
|
|
|
+ area = &(zone->free_area[order]);
|
|
|
+
|
|
|
+ list_for_each(curr, &area->free_list[mtype])
|
|
|
+ freecount++;
|
|
|
+ seq_printf(m, "%6lu ", freecount);
|
|
|
+ }
|
|
|
seq_putc(m, '\n');
|
|
|
}
|
|
|
+}
|
|
|
+
|
|
|
+/* Print out the free pages at each order for each migatetype */
|
|
|
+static int pagetypeinfo_showfree(struct seq_file *m, void *arg)
|
|
|
+{
|
|
|
+ int order;
|
|
|
+ pg_data_t *pgdat = (pg_data_t *)arg;
|
|
|
+
|
|
|
+ /* Print header */
|
|
|
+ seq_printf(m, "%-43s ", "Free pages count per migrate type at order");
|
|
|
+ for (order = 0; order < MAX_ORDER; ++order)
|
|
|
+ seq_printf(m, "%6d ", order);
|
|
|
+ seq_putc(m, '\n');
|
|
|
+
|
|
|
+ walk_zones_in_node(m, pgdat, pagetypeinfo_showfree_print);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void pagetypeinfo_showblockcount_print(struct seq_file *m,
|
|
|
+ pg_data_t *pgdat, struct zone *zone)
|
|
|
+{
|
|
|
+ int mtype;
|
|
|
+ unsigned long pfn;
|
|
|
+ unsigned long start_pfn = zone->zone_start_pfn;
|
|
|
+ unsigned long end_pfn = start_pfn + zone->spanned_pages;
|
|
|
+ unsigned long count[MIGRATE_TYPES] = { 0, };
|
|
|
+
|
|
|
+ for (pfn = start_pfn; pfn < end_pfn; pfn += pageblock_nr_pages) {
|
|
|
+ struct page *page;
|
|
|
+
|
|
|
+ if (!pfn_valid(pfn))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ page = pfn_to_page(pfn);
|
|
|
+ mtype = get_pageblock_migratetype(page);
|
|
|
+
|
|
|
+ count[mtype]++;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Print counts */
|
|
|
+ seq_printf(m, "Node %d, zone %8s ", pgdat->node_id, zone->name);
|
|
|
+ for (mtype = 0; mtype < MIGRATE_TYPES; mtype++)
|
|
|
+ seq_printf(m, "%12lu ", count[mtype]);
|
|
|
+ seq_putc(m, '\n');
|
|
|
+}
|
|
|
+
|
|
|
+/* Print out the free pages at each order for each migratetype */
|
|
|
+static int pagetypeinfo_showblockcount(struct seq_file *m, void *arg)
|
|
|
+{
|
|
|
+ int mtype;
|
|
|
+ pg_data_t *pgdat = (pg_data_t *)arg;
|
|
|
+
|
|
|
+ seq_printf(m, "\n%-23s", "Number of blocks type ");
|
|
|
+ for (mtype = 0; mtype < MIGRATE_TYPES; mtype++)
|
|
|
+ seq_printf(m, "%12s ", migratetype_names[mtype]);
|
|
|
+ seq_putc(m, '\n');
|
|
|
+ walk_zones_in_node(m, pgdat, pagetypeinfo_showblockcount_print);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * This prints out statistics in relation to grouping pages by mobility.
|
|
|
+ * It is expensive to collect so do not constantly read the file.
|
|
|
+ */
|
|
|
+static int pagetypeinfo_show(struct seq_file *m, void *arg)
|
|
|
+{
|
|
|
+ pg_data_t *pgdat = (pg_data_t *)arg;
|
|
|
+
|
|
|
+ seq_printf(m, "Page block order: %d\n", pageblock_order);
|
|
|
+ seq_printf(m, "Pages per block: %lu\n", pageblock_nr_pages);
|
|
|
+ seq_putc(m, '\n');
|
|
|
+ pagetypeinfo_showfree(m, pgdat);
|
|
|
+ pagetypeinfo_showblockcount(m, pgdat);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -454,6 +577,13 @@ const struct seq_operations fragmentation_op = {
|
|
|
.show = frag_show,
|
|
|
};
|
|
|
|
|
|
+const struct seq_operations pagetypeinfo_op = {
|
|
|
+ .start = frag_start,
|
|
|
+ .next = frag_next,
|
|
|
+ .stop = frag_stop,
|
|
|
+ .show = pagetypeinfo_show,
|
|
|
+};
|
|
|
+
|
|
|
#ifdef CONFIG_ZONE_DMA
|
|
|
#define TEXT_FOR_DMA(xx) xx "_dma",
|
|
|
#else
|
|
@@ -532,84 +662,78 @@ static const char * const vmstat_text[] = {
|
|
|
#endif
|
|
|
};
|
|
|
|
|
|
-/*
|
|
|
- * Output information about zones in @pgdat.
|
|
|
- */
|
|
|
-static int zoneinfo_show(struct seq_file *m, void *arg)
|
|
|
+static void zoneinfo_show_print(struct seq_file *m, pg_data_t *pgdat,
|
|
|
+ struct zone *zone)
|
|
|
{
|
|
|
- pg_data_t *pgdat = arg;
|
|
|
- struct zone *zone;
|
|
|
- struct zone *node_zones = pgdat->node_zones;
|
|
|
- unsigned long flags;
|
|
|
-
|
|
|
- for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; zone++) {
|
|
|
- int i;
|
|
|
-
|
|
|
- if (!populated_zone(zone))
|
|
|
- continue;
|
|
|
-
|
|
|
- spin_lock_irqsave(&zone->lock, flags);
|
|
|
- seq_printf(m, "Node %d, zone %8s", pgdat->node_id, zone->name);
|
|
|
- seq_printf(m,
|
|
|
- "\n pages free %lu"
|
|
|
- "\n min %lu"
|
|
|
- "\n low %lu"
|
|
|
- "\n high %lu"
|
|
|
- "\n scanned %lu (a: %lu i: %lu)"
|
|
|
- "\n spanned %lu"
|
|
|
- "\n present %lu",
|
|
|
- zone_page_state(zone, NR_FREE_PAGES),
|
|
|
- zone->pages_min,
|
|
|
- zone->pages_low,
|
|
|
- zone->pages_high,
|
|
|
- zone->pages_scanned,
|
|
|
- zone->nr_scan_active, zone->nr_scan_inactive,
|
|
|
- zone->spanned_pages,
|
|
|
- zone->present_pages);
|
|
|
+ int i;
|
|
|
+ seq_printf(m, "Node %d, zone %8s", pgdat->node_id, zone->name);
|
|
|
+ seq_printf(m,
|
|
|
+ "\n pages free %lu"
|
|
|
+ "\n min %lu"
|
|
|
+ "\n low %lu"
|
|
|
+ "\n high %lu"
|
|
|
+ "\n scanned %lu (a: %lu i: %lu)"
|
|
|
+ "\n spanned %lu"
|
|
|
+ "\n present %lu",
|
|
|
+ zone_page_state(zone, NR_FREE_PAGES),
|
|
|
+ zone->pages_min,
|
|
|
+ zone->pages_low,
|
|
|
+ zone->pages_high,
|
|
|
+ zone->pages_scanned,
|
|
|
+ zone->nr_scan_active, zone->nr_scan_inactive,
|
|
|
+ zone->spanned_pages,
|
|
|
+ zone->present_pages);
|
|
|
|
|
|
- for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
|
|
|
- seq_printf(m, "\n %-12s %lu", vmstat_text[i],
|
|
|
- zone_page_state(zone, i));
|
|
|
-
|
|
|
- seq_printf(m,
|
|
|
- "\n protection: (%lu",
|
|
|
- zone->lowmem_reserve[0]);
|
|
|
- for (i = 1; i < ARRAY_SIZE(zone->lowmem_reserve); i++)
|
|
|
- seq_printf(m, ", %lu", zone->lowmem_reserve[i]);
|
|
|
- seq_printf(m,
|
|
|
- ")"
|
|
|
- "\n pagesets");
|
|
|
- for_each_online_cpu(i) {
|
|
|
- struct per_cpu_pageset *pageset;
|
|
|
- int j;
|
|
|
-
|
|
|
- pageset = zone_pcp(zone, i);
|
|
|
- for (j = 0; j < ARRAY_SIZE(pageset->pcp); j++) {
|
|
|
- seq_printf(m,
|
|
|
- "\n cpu: %i pcp: %i"
|
|
|
- "\n count: %i"
|
|
|
- "\n high: %i"
|
|
|
- "\n batch: %i",
|
|
|
- i, j,
|
|
|
- pageset->pcp[j].count,
|
|
|
- pageset->pcp[j].high,
|
|
|
- pageset->pcp[j].batch);
|
|
|
+ for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
|
|
|
+ seq_printf(m, "\n %-12s %lu", vmstat_text[i],
|
|
|
+ zone_page_state(zone, i));
|
|
|
+
|
|
|
+ seq_printf(m,
|
|
|
+ "\n protection: (%lu",
|
|
|
+ zone->lowmem_reserve[0]);
|
|
|
+ for (i = 1; i < ARRAY_SIZE(zone->lowmem_reserve); i++)
|
|
|
+ seq_printf(m, ", %lu", zone->lowmem_reserve[i]);
|
|
|
+ seq_printf(m,
|
|
|
+ ")"
|
|
|
+ "\n pagesets");
|
|
|
+ for_each_online_cpu(i) {
|
|
|
+ struct per_cpu_pageset *pageset;
|
|
|
+ int j;
|
|
|
+
|
|
|
+ pageset = zone_pcp(zone, i);
|
|
|
+ for (j = 0; j < ARRAY_SIZE(pageset->pcp); j++) {
|
|
|
+ seq_printf(m,
|
|
|
+ "\n cpu: %i pcp: %i"
|
|
|
+ "\n count: %i"
|
|
|
+ "\n high: %i"
|
|
|
+ "\n batch: %i",
|
|
|
+ i, j,
|
|
|
+ pageset->pcp[j].count,
|
|
|
+ pageset->pcp[j].high,
|
|
|
+ pageset->pcp[j].batch);
|
|
|
}
|
|
|
#ifdef CONFIG_SMP
|
|
|
- seq_printf(m, "\n vm stats threshold: %d",
|
|
|
- pageset->stat_threshold);
|
|
|
+ seq_printf(m, "\n vm stats threshold: %d",
|
|
|
+ pageset->stat_threshold);
|
|
|
#endif
|
|
|
- }
|
|
|
- seq_printf(m,
|
|
|
- "\n all_unreclaimable: %u"
|
|
|
- "\n prev_priority: %i"
|
|
|
- "\n start_pfn: %lu",
|
|
|
- zone->all_unreclaimable,
|
|
|
- zone->prev_priority,
|
|
|
- zone->zone_start_pfn);
|
|
|
- spin_unlock_irqrestore(&zone->lock, flags);
|
|
|
- seq_putc(m, '\n');
|
|
|
}
|
|
|
+ seq_printf(m,
|
|
|
+ "\n all_unreclaimable: %u"
|
|
|
+ "\n prev_priority: %i"
|
|
|
+ "\n start_pfn: %lu",
|
|
|
+ zone->all_unreclaimable,
|
|
|
+ zone->prev_priority,
|
|
|
+ zone->zone_start_pfn);
|
|
|
+ seq_putc(m, '\n');
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Output information about zones in @pgdat.
|
|
|
+ */
|
|
|
+static int zoneinfo_show(struct seq_file *m, void *arg)
|
|
|
+{
|
|
|
+ pg_data_t *pgdat = (pg_data_t *)arg;
|
|
|
+ walk_zones_in_node(m, pgdat, zoneinfo_show_print);
|
|
|
return 0;
|
|
|
}
|
|
|
|