16d80e53fSAlexey Dobriyan #include <linux/bootmem.h> 26d80e53fSAlexey Dobriyan #include <linux/compiler.h> 36d80e53fSAlexey Dobriyan #include <linux/fs.h> 46d80e53fSAlexey Dobriyan #include <linux/init.h> 59a840895SHugh Dickins #include <linux/ksm.h> 66d80e53fSAlexey Dobriyan #include <linux/mm.h> 76d80e53fSAlexey Dobriyan #include <linux/mmzone.h> 86d80e53fSAlexey Dobriyan #include <linux/proc_fs.h> 96d80e53fSAlexey Dobriyan #include <linux/seq_file.h> 1020a0307cSWu Fengguang #include <linux/hugetlb.h> 111a9b5b7fSWu Fengguang #include <linux/kernel-page-flags.h> 126d80e53fSAlexey Dobriyan #include <asm/uaccess.h> 136d80e53fSAlexey Dobriyan #include "internal.h" 146d80e53fSAlexey Dobriyan 156d80e53fSAlexey Dobriyan #define KPMSIZE sizeof(u64) 166d80e53fSAlexey Dobriyan #define KPMMASK (KPMSIZE - 1) 17ed7ce0f1SWu Fengguang 186d80e53fSAlexey Dobriyan /* /proc/kpagecount - an array exposing page counts 196d80e53fSAlexey Dobriyan * 206d80e53fSAlexey Dobriyan * Each entry is a u64 representing the corresponding 216d80e53fSAlexey Dobriyan * physical page count. 226d80e53fSAlexey Dobriyan */ 236d80e53fSAlexey Dobriyan static ssize_t kpagecount_read(struct file *file, char __user *buf, 246d80e53fSAlexey Dobriyan size_t count, loff_t *ppos) 256d80e53fSAlexey Dobriyan { 266d80e53fSAlexey Dobriyan u64 __user *out = (u64 __user *)buf; 276d80e53fSAlexey Dobriyan struct page *ppage; 286d80e53fSAlexey Dobriyan unsigned long src = *ppos; 296d80e53fSAlexey Dobriyan unsigned long pfn; 306d80e53fSAlexey Dobriyan ssize_t ret = 0; 316d80e53fSAlexey Dobriyan u64 pcount; 326d80e53fSAlexey Dobriyan 336d80e53fSAlexey Dobriyan pfn = src / KPMSIZE; 346d80e53fSAlexey Dobriyan count = min_t(size_t, count, (max_pfn * KPMSIZE) - src); 356d80e53fSAlexey Dobriyan if (src & KPMMASK || count & KPMMASK) 366d80e53fSAlexey Dobriyan return -EINVAL; 376d80e53fSAlexey Dobriyan 386d80e53fSAlexey Dobriyan while (count > 0) { 396d80e53fSAlexey Dobriyan if (pfn_valid(pfn)) 406d80e53fSAlexey Dobriyan ppage = pfn_to_page(pfn); 41ed7ce0f1SWu Fengguang else 42ed7ce0f1SWu Fengguang ppage = NULL; 43a6fc86d2SPetr Holasek if (!ppage || PageSlab(ppage)) 446d80e53fSAlexey Dobriyan pcount = 0; 456d80e53fSAlexey Dobriyan else 466d80e53fSAlexey Dobriyan pcount = page_mapcount(ppage); 476d80e53fSAlexey Dobriyan 48ed7ce0f1SWu Fengguang if (put_user(pcount, out)) { 496d80e53fSAlexey Dobriyan ret = -EFAULT; 506d80e53fSAlexey Dobriyan break; 516d80e53fSAlexey Dobriyan } 526d80e53fSAlexey Dobriyan 53ed7ce0f1SWu Fengguang pfn++; 54ed7ce0f1SWu Fengguang out++; 556d80e53fSAlexey Dobriyan count -= KPMSIZE; 566d80e53fSAlexey Dobriyan } 576d80e53fSAlexey Dobriyan 586d80e53fSAlexey Dobriyan *ppos += (char __user *)out - buf; 596d80e53fSAlexey Dobriyan if (!ret) 606d80e53fSAlexey Dobriyan ret = (char __user *)out - buf; 616d80e53fSAlexey Dobriyan return ret; 626d80e53fSAlexey Dobriyan } 636d80e53fSAlexey Dobriyan 646d80e53fSAlexey Dobriyan static const struct file_operations proc_kpagecount_operations = { 656d80e53fSAlexey Dobriyan .llseek = mem_lseek, 666d80e53fSAlexey Dobriyan .read = kpagecount_read, 676d80e53fSAlexey Dobriyan }; 686d80e53fSAlexey Dobriyan 696d80e53fSAlexey Dobriyan /* /proc/kpageflags - an array exposing page flags 706d80e53fSAlexey Dobriyan * 716d80e53fSAlexey Dobriyan * Each entry is a u64 representing the corresponding 726d80e53fSAlexey Dobriyan * physical page flags. 736d80e53fSAlexey Dobriyan */ 746d80e53fSAlexey Dobriyan 7517797549SWu Fengguang static inline u64 kpf_copy_bit(u64 kflags, int ubit, int kbit) 7617797549SWu Fengguang { 7717797549SWu Fengguang return ((kflags >> kbit) & 1) << ubit; 7817797549SWu Fengguang } 7917797549SWu Fengguang 801a9b5b7fSWu Fengguang u64 stable_page_flags(struct page *page) 8117797549SWu Fengguang { 8217797549SWu Fengguang u64 k; 8317797549SWu Fengguang u64 u; 8417797549SWu Fengguang 8517797549SWu Fengguang /* 8617797549SWu Fengguang * pseudo flag: KPF_NOPAGE 8717797549SWu Fengguang * it differentiates a memory hole from a page with no flags 8817797549SWu Fengguang */ 8917797549SWu Fengguang if (!page) 9017797549SWu Fengguang return 1 << KPF_NOPAGE; 9117797549SWu Fengguang 9217797549SWu Fengguang k = page->flags; 9317797549SWu Fengguang u = 0; 9417797549SWu Fengguang 9517797549SWu Fengguang /* 9617797549SWu Fengguang * pseudo flags for the well known (anonymous) memory mapped pages 9717797549SWu Fengguang * 9817797549SWu Fengguang * Note that page->_mapcount is overloaded in SLOB/SLUB/SLQB, so the 9917797549SWu Fengguang * simple test in page_mapped() is not enough. 10017797549SWu Fengguang */ 10117797549SWu Fengguang if (!PageSlab(page) && page_mapped(page)) 10217797549SWu Fengguang u |= 1 << KPF_MMAP; 10317797549SWu Fengguang if (PageAnon(page)) 10417797549SWu Fengguang u |= 1 << KPF_ANON; 1059a840895SHugh Dickins if (PageKsm(page)) 1069a840895SHugh Dickins u |= 1 << KPF_KSM; 10717797549SWu Fengguang 10817797549SWu Fengguang /* 10917797549SWu Fengguang * compound pages: export both head/tail info 11017797549SWu Fengguang * they together define a compound page's start/end pos and order 11117797549SWu Fengguang */ 11217797549SWu Fengguang if (PageHead(page)) 11317797549SWu Fengguang u |= 1 << KPF_COMPOUND_HEAD; 11417797549SWu Fengguang if (PageTail(page)) 11517797549SWu Fengguang u |= 1 << KPF_COMPOUND_TAIL; 11617797549SWu Fengguang if (PageHuge(page)) 11717797549SWu Fengguang u |= 1 << KPF_HUGE; 118*e873c49fSNaoya Horiguchi else if (PageTransCompound(page)) 119*e873c49fSNaoya Horiguchi u |= 1 << KPF_THP; 12017797549SWu Fengguang 1215f24ce5fSAndrea Arcangeli /* 1225f24ce5fSAndrea Arcangeli * Caveats on high order pages: page->_count will only be set 1235f24ce5fSAndrea Arcangeli * -1 on the head page; SLUB/SLQB do the same for PG_slab; 1245f24ce5fSAndrea Arcangeli * SLOB won't set PG_slab at all on compound pages. 1255f24ce5fSAndrea Arcangeli */ 1265f24ce5fSAndrea Arcangeli if (PageBuddy(page)) 1275f24ce5fSAndrea Arcangeli u |= 1 << KPF_BUDDY; 1285f24ce5fSAndrea Arcangeli 12917797549SWu Fengguang u |= kpf_copy_bit(k, KPF_LOCKED, PG_locked); 13017797549SWu Fengguang 13117797549SWu Fengguang u |= kpf_copy_bit(k, KPF_SLAB, PG_slab); 13217797549SWu Fengguang 13317797549SWu Fengguang u |= kpf_copy_bit(k, KPF_ERROR, PG_error); 13417797549SWu Fengguang u |= kpf_copy_bit(k, KPF_DIRTY, PG_dirty); 13517797549SWu Fengguang u |= kpf_copy_bit(k, KPF_UPTODATE, PG_uptodate); 13617797549SWu Fengguang u |= kpf_copy_bit(k, KPF_WRITEBACK, PG_writeback); 13717797549SWu Fengguang 13817797549SWu Fengguang u |= kpf_copy_bit(k, KPF_LRU, PG_lru); 13917797549SWu Fengguang u |= kpf_copy_bit(k, KPF_REFERENCED, PG_referenced); 14017797549SWu Fengguang u |= kpf_copy_bit(k, KPF_ACTIVE, PG_active); 14117797549SWu Fengguang u |= kpf_copy_bit(k, KPF_RECLAIM, PG_reclaim); 14217797549SWu Fengguang 14317797549SWu Fengguang u |= kpf_copy_bit(k, KPF_SWAPCACHE, PG_swapcache); 14417797549SWu Fengguang u |= kpf_copy_bit(k, KPF_SWAPBACKED, PG_swapbacked); 14517797549SWu Fengguang 14617797549SWu Fengguang u |= kpf_copy_bit(k, KPF_UNEVICTABLE, PG_unevictable); 14717797549SWu Fengguang u |= kpf_copy_bit(k, KPF_MLOCKED, PG_mlocked); 14817797549SWu Fengguang 149253fb02dSWu Fengguang #ifdef CONFIG_MEMORY_FAILURE 150253fb02dSWu Fengguang u |= kpf_copy_bit(k, KPF_HWPOISON, PG_hwpoison); 151253fb02dSWu Fengguang #endif 152253fb02dSWu Fengguang 153ed430fecSTakashi Iwai #ifdef CONFIG_ARCH_USES_PG_UNCACHED 15417797549SWu Fengguang u |= kpf_copy_bit(k, KPF_UNCACHED, PG_uncached); 15517797549SWu Fengguang #endif 15617797549SWu Fengguang 15717797549SWu Fengguang u |= kpf_copy_bit(k, KPF_RESERVED, PG_reserved); 15817797549SWu Fengguang u |= kpf_copy_bit(k, KPF_MAPPEDTODISK, PG_mappedtodisk); 15917797549SWu Fengguang u |= kpf_copy_bit(k, KPF_PRIVATE, PG_private); 16017797549SWu Fengguang u |= kpf_copy_bit(k, KPF_PRIVATE_2, PG_private_2); 16117797549SWu Fengguang u |= kpf_copy_bit(k, KPF_OWNER_PRIVATE, PG_owner_priv_1); 16217797549SWu Fengguang u |= kpf_copy_bit(k, KPF_ARCH, PG_arch_1); 16317797549SWu Fengguang 16417797549SWu Fengguang return u; 16517797549SWu Fengguang }; 1666d80e53fSAlexey Dobriyan 1676d80e53fSAlexey Dobriyan static ssize_t kpageflags_read(struct file *file, char __user *buf, 1686d80e53fSAlexey Dobriyan size_t count, loff_t *ppos) 1696d80e53fSAlexey Dobriyan { 1706d80e53fSAlexey Dobriyan u64 __user *out = (u64 __user *)buf; 1716d80e53fSAlexey Dobriyan struct page *ppage; 1726d80e53fSAlexey Dobriyan unsigned long src = *ppos; 1736d80e53fSAlexey Dobriyan unsigned long pfn; 1746d80e53fSAlexey Dobriyan ssize_t ret = 0; 1756d80e53fSAlexey Dobriyan 1766d80e53fSAlexey Dobriyan pfn = src / KPMSIZE; 1776d80e53fSAlexey Dobriyan count = min_t(unsigned long, count, (max_pfn * KPMSIZE) - src); 1786d80e53fSAlexey Dobriyan if (src & KPMMASK || count & KPMMASK) 1796d80e53fSAlexey Dobriyan return -EINVAL; 1806d80e53fSAlexey Dobriyan 1816d80e53fSAlexey Dobriyan while (count > 0) { 1826d80e53fSAlexey Dobriyan if (pfn_valid(pfn)) 1836d80e53fSAlexey Dobriyan ppage = pfn_to_page(pfn); 184ed7ce0f1SWu Fengguang else 185ed7ce0f1SWu Fengguang ppage = NULL; 1866d80e53fSAlexey Dobriyan 1871a9b5b7fSWu Fengguang if (put_user(stable_page_flags(ppage), out)) { 1886d80e53fSAlexey Dobriyan ret = -EFAULT; 1896d80e53fSAlexey Dobriyan break; 1906d80e53fSAlexey Dobriyan } 1916d80e53fSAlexey Dobriyan 192ed7ce0f1SWu Fengguang pfn++; 193ed7ce0f1SWu Fengguang out++; 1946d80e53fSAlexey Dobriyan count -= KPMSIZE; 1956d80e53fSAlexey Dobriyan } 1966d80e53fSAlexey Dobriyan 1976d80e53fSAlexey Dobriyan *ppos += (char __user *)out - buf; 1986d80e53fSAlexey Dobriyan if (!ret) 1996d80e53fSAlexey Dobriyan ret = (char __user *)out - buf; 2006d80e53fSAlexey Dobriyan return ret; 2016d80e53fSAlexey Dobriyan } 2026d80e53fSAlexey Dobriyan 2036d80e53fSAlexey Dobriyan static const struct file_operations proc_kpageflags_operations = { 2046d80e53fSAlexey Dobriyan .llseek = mem_lseek, 2056d80e53fSAlexey Dobriyan .read = kpageflags_read, 2066d80e53fSAlexey Dobriyan }; 2076d80e53fSAlexey Dobriyan 2086d80e53fSAlexey Dobriyan static int __init proc_page_init(void) 2096d80e53fSAlexey Dobriyan { 2106d80e53fSAlexey Dobriyan proc_create("kpagecount", S_IRUSR, NULL, &proc_kpagecount_operations); 2116d80e53fSAlexey Dobriyan proc_create("kpageflags", S_IRUSR, NULL, &proc_kpageflags_operations); 2126d80e53fSAlexey Dobriyan return 0; 2136d80e53fSAlexey Dobriyan } 2146d80e53fSAlexey Dobriyan module_init(proc_page_init); 215