|
@@ -8,6 +8,7 @@
|
|
|
#include <linux/jump_label.h>
|
|
|
#include <linux/migrate.h>
|
|
|
#include <linux/stackdepot.h>
|
|
|
+#include <linux/seq_file.h>
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
|
@@ -214,6 +215,82 @@ void __copy_page_owner(struct page *oldpage, struct page *newpage)
|
|
|
__set_bit(PAGE_EXT_OWNER, &new_ext->flags);
|
|
|
}
|
|
|
|
|
|
+void pagetypeinfo_showmixedcount_print(struct seq_file *m,
|
|
|
+ pg_data_t *pgdat, struct zone *zone)
|
|
|
+{
|
|
|
+ struct page *page;
|
|
|
+ struct page_ext *page_ext;
|
|
|
+ unsigned long pfn = zone->zone_start_pfn, block_end_pfn;
|
|
|
+ unsigned long end_pfn = pfn + zone->spanned_pages;
|
|
|
+ unsigned long count[MIGRATE_TYPES] = { 0, };
|
|
|
+ int pageblock_mt, page_mt;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* Scan block by block. First and last block may be incomplete */
|
|
|
+ pfn = zone->zone_start_pfn;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Walk the zone in pageblock_nr_pages steps. If a page block spans
|
|
|
+ * a zone boundary, it will be double counted between zones. This does
|
|
|
+ * not matter as the mixed block count will still be correct
|
|
|
+ */
|
|
|
+ for (; pfn < end_pfn; ) {
|
|
|
+ if (!pfn_valid(pfn)) {
|
|
|
+ pfn = ALIGN(pfn + 1, MAX_ORDER_NR_PAGES);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ block_end_pfn = ALIGN(pfn + 1, pageblock_nr_pages);
|
|
|
+ block_end_pfn = min(block_end_pfn, end_pfn);
|
|
|
+
|
|
|
+ page = pfn_to_page(pfn);
|
|
|
+ pageblock_mt = get_pageblock_migratetype(page);
|
|
|
+
|
|
|
+ for (; pfn < block_end_pfn; pfn++) {
|
|
|
+ if (!pfn_valid_within(pfn))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ page = pfn_to_page(pfn);
|
|
|
+
|
|
|
+ if (page_zone(page) != zone)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (PageBuddy(page)) {
|
|
|
+ pfn += (1UL << page_order(page)) - 1;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (PageReserved(page))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ page_ext = lookup_page_ext(page);
|
|
|
+ if (unlikely(!page_ext))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ page_mt = gfpflags_to_migratetype(page_ext->gfp_mask);
|
|
|
+ if (pageblock_mt != page_mt) {
|
|
|
+ if (is_migrate_cma(pageblock_mt))
|
|
|
+ count[MIGRATE_MOVABLE]++;
|
|
|
+ else
|
|
|
+ count[pageblock_mt]++;
|
|
|
+
|
|
|
+ pfn = block_end_pfn;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ pfn += (1UL << page_ext->order) - 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Print counts */
|
|
|
+ seq_printf(m, "Node %d, zone %8s ", pgdat->node_id, zone->name);
|
|
|
+ for (i = 0; i < MIGRATE_TYPES; i++)
|
|
|
+ seq_printf(m, "%12lu ", count[i]);
|
|
|
+ seq_putc(m, '\n');
|
|
|
+}
|
|
|
+
|
|
|
static ssize_t
|
|
|
print_page_owner(char __user *buf, size_t count, unsigned long pfn,
|
|
|
struct page *page, struct page_ext *page_ext,
|