#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "internal.h" #include #include static ssize_t read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos) { unsigned long pfn; struct page *page; char *kbuf; int ret = 0; ssize_t num_written = 0; int blocktype = 0, pagetype = 0; page = NULL; pfn = min_low_pfn + *ppos; /* Find a valid PFN or the start of a MAX_ORDER_NR_PAGES area */ while (!pfn_valid(pfn) && (pfn & (MAX_ORDER_NR_PAGES - 1)) != 0) pfn++; //printk("pfn: %ld max_pfn: %ld\n", pfn, max_pfn); /* Find an allocated page */ for (; pfn < max_pfn; pfn++) { /* * If the new page is in a new MAX_ORDER_NR_PAGES area, * validate the area as existing, skip it if not */ if ((pfn & (MAX_ORDER_NR_PAGES - 1)) == 0 && !pfn_valid(pfn)) { pfn += MAX_ORDER_NR_PAGES - 1; continue; } /* Check for holes within a MAX_ORDER area */ if (!pfn_valid_within(pfn)) continue; page = pfn_to_page(pfn); /* Catch situations where free pages have a bad ->order */ if (page->order >= 0 && PageBuddy(page)) printk(KERN_WARNING "PageOwner info inaccurate for PFN %lu\n", pfn); /* Stop search if page is allocated and has trace info */ if (page->order >= 0 && page->trace.nr_entries) { //intk("stopped search at pfn: %ld\n", pfn); break; } } if (!pfn_valid(pfn)) return 0; /* * If memory does not end at a SECTION_SIZE boundary, then * we might have a pfn_valid() above max_pfn */ if (pfn >= max_pfn) return 0; /* Record the next PFN to read in the file offset */ *ppos = (pfn - min_low_pfn) + 1; kbuf = kmalloc(count, GFP_KERNEL); if (!kbuf) return -ENOMEM; //printk("page: %p\n", page); ret = snprintf(kbuf, count, "Page allocated via order %d, mask 0x%x\n", page->order, page->gfp_mask); if (ret >= count) { ret = -ENOMEM; goto out; } /* Print information relevant to grouping pages by mobility */ blocktype = get_pageblock_migratetype(page); pagetype = allocflags_to_migratetype(page->gfp_mask); ret += snprintf(kbuf+ret, count-ret, "PFN %lu Block %lu type %d %s " "Flags %s%s%s%s%s%s%s%s%s%s%s%s\n", pfn, pfn >> pageblock_order, blocktype, blocktype != pagetype ? "Fallback" : " ", PageLocked(page) ? "K" : " ", PageError(page) ? "E" : " ", PageReferenced(page) ? "R" : " ", PageUptodate(page) ? "U" : " ", PageDirty(page) ? "D" : " ", PageLRU(page) ? "L" : " ", PageActive(page) ? "A" : " ", PageSlab(page) ? "S" : " ", PageWriteback(page) ? "W" : " ", PageCompound(page) ? "C" : " ", PageSwapCache(page) ? "B" : " ", PageMappedToDisk(page) ? "M" : " "); if (ret >= count) { ret = -ENOMEM; goto out; } num_written = ret; ret = snprint_stack_trace(kbuf + num_written, count - num_written, &page->trace, 0); if (ret >= count - num_written) { ret = -ENOMEM; goto out; } num_written += ret; ret = snprintf(kbuf + num_written, count - num_written, "\n"); if (ret >= count - num_written) { ret = -ENOMEM; goto out; } num_written += ret; ret = num_written; if (copy_to_user(buf, kbuf, ret)) ret = -EFAULT; out: kfree(kbuf); return ret; } static struct file_operations proc_page_owner_operations = { .read = read_page_owner, }; static int __init pageowner_init(void) { struct dentry *dentry; dentry = debugfs_create_file("page_owner", S_IRUSR, NULL, NULL, &proc_page_owner_operations); if (IS_ERR(dentry)) return PTR_ERR(dentry); return 0; } module_init(pageowner_init)