diff options
| author | Meizu OpenSource <patchwork@meizu.com> | 2016-08-15 10:19:42 +0800 |
|---|---|---|
| committer | Meizu OpenSource <patchwork@meizu.com> | 2016-08-15 10:19:42 +0800 |
| commit | d2e1446d81725c351dc73a03b397ce043fb18452 (patch) | |
| tree | 4dbc616b7f92aea39cd697a9084205ddb805e344 /fs/proc/page.c | |
first commit
Diffstat (limited to 'fs/proc/page.c')
| -rw-r--r-- | fs/proc/page.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/fs/proc/page.c b/fs/proc/page.c new file mode 100644 index 000000000..1a10de230 --- /dev/null +++ b/fs/proc/page.c @@ -0,0 +1,294 @@ +#include <linux/bootmem.h> +#include <linux/compiler.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/ksm.h> +#include <linux/mm.h> +#include <linux/mmzone.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/hugetlb.h> +#include <linux/kernel-page-flags.h> +#include <linux/swap.h> +#include <linux/swapops.h> +#include <asm/uaccess.h> +#include "internal.h" + +#define KPMSIZE sizeof(u64) +#define KPMMASK (KPMSIZE - 1) + +/* /proc/kpagecount - an array exposing page counts + * + * Each entry is a u64 representing the corresponding + * physical page count. + */ +static ssize_t kpagecount_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + u64 __user *out = (u64 __user *)buf; + struct page *ppage; + unsigned long src = *ppos; + unsigned long pfn; + unsigned long max_pfn_kpmsize = max_pfn * KPMSIZE; + ssize_t ret = 0; + u64 pcount; + + pfn = src / KPMSIZE; + if(src != max_pfn_kpmsize){ + count = min_t(size_t, count, max_pfn_kpmsize - src); + } + + if (src & KPMMASK || count & KPMMASK) + return -EINVAL; + + while (count > 0) { + if (pfn_valid(pfn)) + ppage = pfn_to_page(pfn); + else + ppage = NULL; + if (!ppage || PageSlab(ppage)) + pcount = 0; + else + pcount = page_mapcount(ppage); + + if (put_user(pcount, out)) { + ret = -EFAULT; + break; + } + + pfn++; + out++; + count -= KPMSIZE; + } + + *ppos += (char __user *)out - buf; + if (!ret) + ret = (char __user *)out - buf; + return ret; +} + +static const struct file_operations proc_kpagecount_operations = { + .llseek = mem_lseek, + .read = kpagecount_read, +}; + +#ifdef CONFIG_SWAP +extern struct swap_info_struct *swap_info_get(swp_entry_t entry); +extern void swap_info_unlock(struct swap_info_struct *si); + +static inline unsigned char swap_count(unsigned char ent) +{ + return ent & ~SWAP_HAS_CACHE; /* may include SWAP_HAS_CONT flag */ +} + +/* /proc/kpageswapn - an array exposing page swap counts + * + * Each entry is a u64 representing the corresponding + * physical page swap count. + */ +static ssize_t kpageswapn_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + u64 __user *out = (u64 __user *)buf; + unsigned long src = *ppos, dst; + swp_entry_t swap_entry; + ssize_t ret = 0; + struct swap_info_struct *p; + + dst = src / KPMSIZE; + /* Format the swap entry from the corresponding pagemap value */ + swap_entry = swp_entry(dst >> (SWP_TYPE_SHIFT(swap_entry) + RADIX_TREE_EXCEPTIONAL_SHIFT), dst & SWP_OFFSET_MASK(swap_entry)); + + //printk(KERN_INFO "kpageswapn_read src: %lx\n", src); + //printk(KERN_INFO "kpageswapn_read swap entry: %lx\n", swap_entry.val); + + if (src & KPMMASK || count & KPMMASK) { + printk(KERN_INFO "kpageswapn_read return EINVAL\n"); + return -EINVAL; + } + + p = swap_info_get(swap_entry); + if (p) { + u64 swapcount = swap_count(p->swap_map[swp_offset(swap_entry)]); + if (put_user(swapcount, out)) { + printk(KERN_INFO "kpageswapn_read put user failed\n"); + ret = -EFAULT; + } + swap_info_unlock(p); + } else { + printk(KERN_INFO "kpageswapn_read swap_info_get failed\n"); + ret = -EFAULT; + } + + if (!ret) { + *ppos += KPMSIZE; + ret = KPMSIZE; + } + return ret; +} + +static const struct file_operations proc_kpageswapn_operations = { + .llseek = mem_lseek, + .read = kpageswapn_read, +}; +#endif // CONFIG_SWAP + +/* /proc/kpageflags - an array exposing page flags + * + * Each entry is a u64 representing the corresponding + * physical page flags. + */ + +static inline u64 kpf_copy_bit(u64 kflags, int ubit, int kbit) +{ + return ((kflags >> kbit) & 1) << ubit; +} + +u64 stable_page_flags(struct page *page) +{ + u64 k; + u64 u; + + /* + * pseudo flag: KPF_NOPAGE + * it differentiates a memory hole from a page with no flags + */ + if (!page) + return 1 << KPF_NOPAGE; + + k = page->flags; + u = 0; + + /* + * pseudo flags for the well known (anonymous) memory mapped pages + * + * Note that page->_mapcount is overloaded in SLOB/SLUB/SLQB, so the + * simple test in page_mapped() is not enough. + */ + if (!PageSlab(page) && page_mapped(page)) + u |= 1 << KPF_MMAP; + if (PageAnon(page)) + u |= 1 << KPF_ANON; + if (PageKsm(page)) + u |= 1 << KPF_KSM; + + /* + * compound pages: export both head/tail info + * they together define a compound page's start/end pos and order + */ + if (PageHead(page)) + u |= 1 << KPF_COMPOUND_HEAD; + if (PageTail(page)) + u |= 1 << KPF_COMPOUND_TAIL; + if (PageHuge(page)) + u |= 1 << KPF_HUGE; + /* + * PageTransCompound can be true for non-huge compound pages (slab + * pages or pages allocated by drivers with __GFP_COMP) because it + * just checks PG_head/PG_tail, so we need to check PageLRU to make + * sure a given page is a thp, not a non-huge compound page. + */ + else if (PageTransCompound(page) && PageLRU(compound_head(page))) + u |= 1 << KPF_THP; + + /* + * Caveats on high order pages: page->_count will only be set + * -1 on the head page; SLUB/SLQB do the same for PG_slab; + * SLOB won't set PG_slab at all on compound pages. + */ + if (PageBuddy(page)) + u |= 1 << KPF_BUDDY; + + u |= kpf_copy_bit(k, KPF_LOCKED, PG_locked); + + u |= kpf_copy_bit(k, KPF_SLAB, PG_slab); + + u |= kpf_copy_bit(k, KPF_ERROR, PG_error); + u |= kpf_copy_bit(k, KPF_DIRTY, PG_dirty); + u |= kpf_copy_bit(k, KPF_UPTODATE, PG_uptodate); + u |= kpf_copy_bit(k, KPF_WRITEBACK, PG_writeback); + + u |= kpf_copy_bit(k, KPF_LRU, PG_lru); + u |= kpf_copy_bit(k, KPF_REFERENCED, PG_referenced); + u |= kpf_copy_bit(k, KPF_ACTIVE, PG_active); + u |= kpf_copy_bit(k, KPF_RECLAIM, PG_reclaim); + + u |= kpf_copy_bit(k, KPF_SWAPCACHE, PG_swapcache); + u |= kpf_copy_bit(k, KPF_SWAPBACKED, PG_swapbacked); + + u |= kpf_copy_bit(k, KPF_UNEVICTABLE, PG_unevictable); + u |= kpf_copy_bit(k, KPF_MLOCKED, PG_mlocked); + +#ifdef CONFIG_MEMORY_FAILURE + u |= kpf_copy_bit(k, KPF_HWPOISON, PG_hwpoison); +#endif + +#ifdef CONFIG_ARCH_USES_PG_UNCACHED + u |= kpf_copy_bit(k, KPF_UNCACHED, PG_uncached); +#endif + + u |= kpf_copy_bit(k, KPF_RESERVED, PG_reserved); + u |= kpf_copy_bit(k, KPF_MAPPEDTODISK, PG_mappedtodisk); + u |= kpf_copy_bit(k, KPF_PRIVATE, PG_private); + u |= kpf_copy_bit(k, KPF_PRIVATE_2, PG_private_2); + u |= kpf_copy_bit(k, KPF_OWNER_PRIVATE, PG_owner_priv_1); + u |= kpf_copy_bit(k, KPF_ARCH, PG_arch_1); + + return u; +}; + +static ssize_t kpageflags_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + u64 __user *out = (u64 __user *)buf; + struct page *ppage; + unsigned long src = *ppos; + unsigned long pfn; + unsigned long max_pfn_kpmsize = max_pfn * KPMSIZE; + ssize_t ret = 0; + + pfn = src / KPMSIZE; + if(src != max_pfn_kpmsize){ + count = min_t(unsigned long, count, max_pfn_kpmsize - src); + } + + if (src & KPMMASK || count & KPMMASK) + return -EINVAL; + + while (count > 0) { + if (pfn_valid(pfn)) + ppage = pfn_to_page(pfn); + else + ppage = NULL; + + if (put_user(stable_page_flags(ppage), out)) { + ret = -EFAULT; + break; + } + + pfn++; + out++; + count -= KPMSIZE; + } + + *ppos += (char __user *)out - buf; + if (!ret) + ret = (char __user *)out - buf; + return ret; +} + +static const struct file_operations proc_kpageflags_operations = { + .llseek = mem_lseek, + .read = kpageflags_read, +}; + +static int __init proc_page_init(void) +{ + proc_create("kpagecount", S_IRUSR, NULL, &proc_kpagecount_operations); +#ifdef CONFIG_SWAP + proc_create("kpageswapn", S_IRUSR, NULL, &proc_kpageswapn_operations); +#endif // CONFIG_SWAP + proc_create("kpageflags", S_IRUSR, NULL, &proc_kpageflags_operations); + return 0; +} +module_init(proc_page_init); |
