1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 257c8a661SMike Rapoport #include <linux/memblock.h> 36d80e53fSAlexey Dobriyan #include <linux/compiler.h> 46d80e53fSAlexey Dobriyan #include <linux/fs.h> 56d80e53fSAlexey Dobriyan #include <linux/init.h> 69a840895SHugh Dickins #include <linux/ksm.h> 76d80e53fSAlexey Dobriyan #include <linux/mm.h> 86d80e53fSAlexey Dobriyan #include <linux/mmzone.h> 956873f43SWang, Yalin #include <linux/huge_mm.h> 106d80e53fSAlexey Dobriyan #include <linux/proc_fs.h> 116d80e53fSAlexey Dobriyan #include <linux/seq_file.h> 1220a0307cSWu Fengguang #include <linux/hugetlb.h> 1380ae2fdcSVladimir Davydov #include <linux/memcontrol.h> 1433c3fc71SVladimir Davydov #include <linux/mmu_notifier.h> 1533c3fc71SVladimir Davydov #include <linux/page_idle.h> 161a9b5b7fSWu Fengguang #include <linux/kernel-page-flags.h> 177c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 186d80e53fSAlexey Dobriyan #include "internal.h" 196d80e53fSAlexey Dobriyan 206d80e53fSAlexey Dobriyan #define KPMSIZE sizeof(u64) 216d80e53fSAlexey Dobriyan #define KPMMASK (KPMSIZE - 1) 2233c3fc71SVladimir Davydov #define KPMBITS (KPMSIZE * BITS_PER_BYTE) 23ed7ce0f1SWu Fengguang 246d80e53fSAlexey Dobriyan /* /proc/kpagecount - an array exposing page counts 256d80e53fSAlexey Dobriyan * 266d80e53fSAlexey Dobriyan * Each entry is a u64 representing the corresponding 276d80e53fSAlexey Dobriyan * physical page count. 286d80e53fSAlexey Dobriyan */ 296d80e53fSAlexey Dobriyan static ssize_t kpagecount_read(struct file *file, char __user *buf, 306d80e53fSAlexey Dobriyan size_t count, loff_t *ppos) 316d80e53fSAlexey Dobriyan { 326d80e53fSAlexey Dobriyan u64 __user *out = (u64 __user *)buf; 336d80e53fSAlexey Dobriyan struct page *ppage; 346d80e53fSAlexey Dobriyan unsigned long src = *ppos; 356d80e53fSAlexey Dobriyan unsigned long pfn; 366d80e53fSAlexey Dobriyan ssize_t ret = 0; 376d80e53fSAlexey Dobriyan u64 pcount; 386d80e53fSAlexey Dobriyan 396d80e53fSAlexey Dobriyan pfn = src / KPMSIZE; 406d80e53fSAlexey Dobriyan count = min_t(size_t, count, (max_pfn * KPMSIZE) - src); 416d80e53fSAlexey Dobriyan if (src & KPMMASK || count & KPMMASK) 426d80e53fSAlexey Dobriyan return -EINVAL; 436d80e53fSAlexey Dobriyan 446d80e53fSAlexey Dobriyan while (count > 0) { 456d80e53fSAlexey Dobriyan if (pfn_valid(pfn)) 466d80e53fSAlexey Dobriyan ppage = pfn_to_page(pfn); 47ed7ce0f1SWu Fengguang else 48ed7ce0f1SWu Fengguang ppage = NULL; 49144552ffSAnthony Yznaga if (!ppage || PageSlab(ppage) || page_has_type(ppage)) 506d80e53fSAlexey Dobriyan pcount = 0; 516d80e53fSAlexey Dobriyan else 526d80e53fSAlexey Dobriyan pcount = page_mapcount(ppage); 536d80e53fSAlexey Dobriyan 54ed7ce0f1SWu Fengguang if (put_user(pcount, out)) { 556d80e53fSAlexey Dobriyan ret = -EFAULT; 566d80e53fSAlexey Dobriyan break; 576d80e53fSAlexey Dobriyan } 586d80e53fSAlexey Dobriyan 59ed7ce0f1SWu Fengguang pfn++; 60ed7ce0f1SWu Fengguang out++; 616d80e53fSAlexey Dobriyan count -= KPMSIZE; 62d3691d2cSVladimir Davydov 63d3691d2cSVladimir Davydov cond_resched(); 646d80e53fSAlexey Dobriyan } 656d80e53fSAlexey Dobriyan 666d80e53fSAlexey Dobriyan *ppos += (char __user *)out - buf; 676d80e53fSAlexey Dobriyan if (!ret) 686d80e53fSAlexey Dobriyan ret = (char __user *)out - buf; 696d80e53fSAlexey Dobriyan return ret; 706d80e53fSAlexey Dobriyan } 716d80e53fSAlexey Dobriyan 726d80e53fSAlexey Dobriyan static const struct file_operations proc_kpagecount_operations = { 736d80e53fSAlexey Dobriyan .llseek = mem_lseek, 746d80e53fSAlexey Dobriyan .read = kpagecount_read, 756d80e53fSAlexey Dobriyan }; 766d80e53fSAlexey Dobriyan 776d80e53fSAlexey Dobriyan /* /proc/kpageflags - an array exposing page flags 786d80e53fSAlexey Dobriyan * 796d80e53fSAlexey Dobriyan * Each entry is a u64 representing the corresponding 806d80e53fSAlexey Dobriyan * physical page flags. 816d80e53fSAlexey Dobriyan */ 826d80e53fSAlexey Dobriyan 8317797549SWu Fengguang static inline u64 kpf_copy_bit(u64 kflags, int ubit, int kbit) 8417797549SWu Fengguang { 8517797549SWu Fengguang return ((kflags >> kbit) & 1) << ubit; 8617797549SWu Fengguang } 8717797549SWu Fengguang 881a9b5b7fSWu Fengguang u64 stable_page_flags(struct page *page) 8917797549SWu Fengguang { 9017797549SWu Fengguang u64 k; 9117797549SWu Fengguang u64 u; 9217797549SWu Fengguang 9317797549SWu Fengguang /* 9417797549SWu Fengguang * pseudo flag: KPF_NOPAGE 9517797549SWu Fengguang * it differentiates a memory hole from a page with no flags 9617797549SWu Fengguang */ 9717797549SWu Fengguang if (!page) 9817797549SWu Fengguang return 1 << KPF_NOPAGE; 9917797549SWu Fengguang 10017797549SWu Fengguang k = page->flags; 10117797549SWu Fengguang u = 0; 10217797549SWu Fengguang 10317797549SWu Fengguang /* 10417797549SWu Fengguang * pseudo flags for the well known (anonymous) memory mapped pages 10517797549SWu Fengguang * 10617797549SWu Fengguang * Note that page->_mapcount is overloaded in SLOB/SLUB/SLQB, so the 107832fc1deSNaoya Horiguchi * simple test in page_mapped() is not enough. 10817797549SWu Fengguang */ 109832fc1deSNaoya Horiguchi if (!PageSlab(page) && page_mapped(page)) 11017797549SWu Fengguang u |= 1 << KPF_MMAP; 11117797549SWu Fengguang if (PageAnon(page)) 11217797549SWu Fengguang u |= 1 << KPF_ANON; 1139a840895SHugh Dickins if (PageKsm(page)) 1149a840895SHugh Dickins u |= 1 << KPF_KSM; 11517797549SWu Fengguang 11617797549SWu Fengguang /* 11717797549SWu Fengguang * compound pages: export both head/tail info 11817797549SWu Fengguang * they together define a compound page's start/end pos and order 11917797549SWu Fengguang */ 12017797549SWu Fengguang if (PageHead(page)) 12117797549SWu Fengguang u |= 1 << KPF_COMPOUND_HEAD; 12217797549SWu Fengguang if (PageTail(page)) 12317797549SWu Fengguang u |= 1 << KPF_COMPOUND_TAIL; 12417797549SWu Fengguang if (PageHuge(page)) 12517797549SWu Fengguang u |= 1 << KPF_HUGE; 1267a71932dSNaoya Horiguchi /* 1277a71932dSNaoya Horiguchi * PageTransCompound can be true for non-huge compound pages (slab 1287a71932dSNaoya Horiguchi * pages or pages allocated by drivers with __GFP_COMP) because it 129e3bba3c3SNaoya Horiguchi * just checks PG_head/PG_tail, so we need to check PageLRU/PageAnon 130e3bba3c3SNaoya Horiguchi * to make sure a given page is a thp, not a non-huge compound page. 1317a71932dSNaoya Horiguchi */ 13256873f43SWang, Yalin else if (PageTransCompound(page)) { 13356873f43SWang, Yalin struct page *head = compound_head(page); 13456873f43SWang, Yalin 13556873f43SWang, Yalin if (PageLRU(head) || PageAnon(head)) 136e873c49fSNaoya Horiguchi u |= 1 << KPF_THP; 13756873f43SWang, Yalin else if (is_huge_zero_page(head)) { 13856873f43SWang, Yalin u |= 1 << KPF_ZERO_PAGE; 13956873f43SWang, Yalin u |= 1 << KPF_THP; 14056873f43SWang, Yalin } 14156873f43SWang, Yalin } else if (is_zero_pfn(page_to_pfn(page))) 14256873f43SWang, Yalin u |= 1 << KPF_ZERO_PAGE; 14356873f43SWang, Yalin 14417797549SWu Fengguang 1455f24ce5fSAndrea Arcangeli /* 1460139aa7bSJoonsoo Kim * Caveats on high order pages: page->_refcount will only be set 1475f24ce5fSAndrea Arcangeli * -1 on the head page; SLUB/SLQB do the same for PG_slab; 1485f24ce5fSAndrea Arcangeli * SLOB won't set PG_slab at all on compound pages. 1495f24ce5fSAndrea Arcangeli */ 1505f24ce5fSAndrea Arcangeli if (PageBuddy(page)) 1515f24ce5fSAndrea Arcangeli u |= 1 << KPF_BUDDY; 152832fc1deSNaoya Horiguchi else if (page_count(page) == 0 && is_free_buddy_page(page)) 153832fc1deSNaoya Horiguchi u |= 1 << KPF_BUDDY; 1545f24ce5fSAndrea Arcangeli 155*ca215086SDavid Hildenbrand if (PageOffline(page)) 156*ca215086SDavid Hildenbrand u |= 1 << KPF_OFFLINE; 1571d40a5eaSMatthew Wilcox if (PageTable(page)) 1581d40a5eaSMatthew Wilcox u |= 1 << KPF_PGTABLE; 15909316c09SKonstantin Khlebnikov 160f074a8f4SVladimir Davydov if (page_is_idle(page)) 161f074a8f4SVladimir Davydov u |= 1 << KPF_IDLE; 162f074a8f4SVladimir Davydov 16317797549SWu Fengguang u |= kpf_copy_bit(k, KPF_LOCKED, PG_locked); 16417797549SWu Fengguang 16517797549SWu Fengguang u |= kpf_copy_bit(k, KPF_SLAB, PG_slab); 1660a71649cSNaoya Horiguchi if (PageTail(page) && PageSlab(compound_head(page))) 1670a71649cSNaoya Horiguchi u |= 1 << KPF_SLAB; 16817797549SWu Fengguang 16917797549SWu Fengguang u |= kpf_copy_bit(k, KPF_ERROR, PG_error); 17017797549SWu Fengguang u |= kpf_copy_bit(k, KPF_DIRTY, PG_dirty); 17117797549SWu Fengguang u |= kpf_copy_bit(k, KPF_UPTODATE, PG_uptodate); 17217797549SWu Fengguang u |= kpf_copy_bit(k, KPF_WRITEBACK, PG_writeback); 17317797549SWu Fengguang 17417797549SWu Fengguang u |= kpf_copy_bit(k, KPF_LRU, PG_lru); 17517797549SWu Fengguang u |= kpf_copy_bit(k, KPF_REFERENCED, PG_referenced); 17617797549SWu Fengguang u |= kpf_copy_bit(k, KPF_ACTIVE, PG_active); 17717797549SWu Fengguang u |= kpf_copy_bit(k, KPF_RECLAIM, PG_reclaim); 17817797549SWu Fengguang 179b6789123SHugh Dickins if (PageSwapCache(page)) 180b6789123SHugh Dickins u |= 1 << KPF_SWAPCACHE; 18117797549SWu Fengguang u |= kpf_copy_bit(k, KPF_SWAPBACKED, PG_swapbacked); 18217797549SWu Fengguang 18317797549SWu Fengguang u |= kpf_copy_bit(k, KPF_UNEVICTABLE, PG_unevictable); 18417797549SWu Fengguang u |= kpf_copy_bit(k, KPF_MLOCKED, PG_mlocked); 18517797549SWu Fengguang 186253fb02dSWu Fengguang #ifdef CONFIG_MEMORY_FAILURE 187253fb02dSWu Fengguang u |= kpf_copy_bit(k, KPF_HWPOISON, PG_hwpoison); 188253fb02dSWu Fengguang #endif 189253fb02dSWu Fengguang 190ed430fecSTakashi Iwai #ifdef CONFIG_ARCH_USES_PG_UNCACHED 19117797549SWu Fengguang u |= kpf_copy_bit(k, KPF_UNCACHED, PG_uncached); 19217797549SWu Fengguang #endif 19317797549SWu Fengguang 19417797549SWu Fengguang u |= kpf_copy_bit(k, KPF_RESERVED, PG_reserved); 19517797549SWu Fengguang u |= kpf_copy_bit(k, KPF_MAPPEDTODISK, PG_mappedtodisk); 19617797549SWu Fengguang u |= kpf_copy_bit(k, KPF_PRIVATE, PG_private); 19717797549SWu Fengguang u |= kpf_copy_bit(k, KPF_PRIVATE_2, PG_private_2); 19817797549SWu Fengguang u |= kpf_copy_bit(k, KPF_OWNER_PRIVATE, PG_owner_priv_1); 19917797549SWu Fengguang u |= kpf_copy_bit(k, KPF_ARCH, PG_arch_1); 20017797549SWu Fengguang 20117797549SWu Fengguang return u; 20217797549SWu Fengguang }; 2036d80e53fSAlexey Dobriyan 2046d80e53fSAlexey Dobriyan static ssize_t kpageflags_read(struct file *file, char __user *buf, 2056d80e53fSAlexey Dobriyan size_t count, loff_t *ppos) 2066d80e53fSAlexey Dobriyan { 2076d80e53fSAlexey Dobriyan u64 __user *out = (u64 __user *)buf; 2086d80e53fSAlexey Dobriyan struct page *ppage; 2096d80e53fSAlexey Dobriyan unsigned long src = *ppos; 2106d80e53fSAlexey Dobriyan unsigned long pfn; 2116d80e53fSAlexey Dobriyan ssize_t ret = 0; 2126d80e53fSAlexey Dobriyan 2136d80e53fSAlexey Dobriyan pfn = src / KPMSIZE; 2146d80e53fSAlexey Dobriyan count = min_t(unsigned long, count, (max_pfn * KPMSIZE) - src); 2156d80e53fSAlexey Dobriyan if (src & KPMMASK || count & KPMMASK) 2166d80e53fSAlexey Dobriyan return -EINVAL; 2176d80e53fSAlexey Dobriyan 2186d80e53fSAlexey Dobriyan while (count > 0) { 2196d80e53fSAlexey Dobriyan if (pfn_valid(pfn)) 2206d80e53fSAlexey Dobriyan ppage = pfn_to_page(pfn); 221ed7ce0f1SWu Fengguang else 222ed7ce0f1SWu Fengguang ppage = NULL; 2236d80e53fSAlexey Dobriyan 2241a9b5b7fSWu Fengguang if (put_user(stable_page_flags(ppage), out)) { 2256d80e53fSAlexey Dobriyan ret = -EFAULT; 2266d80e53fSAlexey Dobriyan break; 2276d80e53fSAlexey Dobriyan } 2286d80e53fSAlexey Dobriyan 229ed7ce0f1SWu Fengguang pfn++; 230ed7ce0f1SWu Fengguang out++; 2316d80e53fSAlexey Dobriyan count -= KPMSIZE; 232d3691d2cSVladimir Davydov 233d3691d2cSVladimir Davydov cond_resched(); 2346d80e53fSAlexey Dobriyan } 2356d80e53fSAlexey Dobriyan 2366d80e53fSAlexey Dobriyan *ppos += (char __user *)out - buf; 2376d80e53fSAlexey Dobriyan if (!ret) 2386d80e53fSAlexey Dobriyan ret = (char __user *)out - buf; 2396d80e53fSAlexey Dobriyan return ret; 2406d80e53fSAlexey Dobriyan } 2416d80e53fSAlexey Dobriyan 2426d80e53fSAlexey Dobriyan static const struct file_operations proc_kpageflags_operations = { 2436d80e53fSAlexey Dobriyan .llseek = mem_lseek, 2446d80e53fSAlexey Dobriyan .read = kpageflags_read, 2456d80e53fSAlexey Dobriyan }; 2466d80e53fSAlexey Dobriyan 24780ae2fdcSVladimir Davydov #ifdef CONFIG_MEMCG 24880ae2fdcSVladimir Davydov static ssize_t kpagecgroup_read(struct file *file, char __user *buf, 24980ae2fdcSVladimir Davydov size_t count, loff_t *ppos) 25080ae2fdcSVladimir Davydov { 25180ae2fdcSVladimir Davydov u64 __user *out = (u64 __user *)buf; 25280ae2fdcSVladimir Davydov struct page *ppage; 25380ae2fdcSVladimir Davydov unsigned long src = *ppos; 25480ae2fdcSVladimir Davydov unsigned long pfn; 25580ae2fdcSVladimir Davydov ssize_t ret = 0; 25680ae2fdcSVladimir Davydov u64 ino; 25780ae2fdcSVladimir Davydov 25880ae2fdcSVladimir Davydov pfn = src / KPMSIZE; 25980ae2fdcSVladimir Davydov count = min_t(unsigned long, count, (max_pfn * KPMSIZE) - src); 26080ae2fdcSVladimir Davydov if (src & KPMMASK || count & KPMMASK) 26180ae2fdcSVladimir Davydov return -EINVAL; 26280ae2fdcSVladimir Davydov 26380ae2fdcSVladimir Davydov while (count > 0) { 26480ae2fdcSVladimir Davydov if (pfn_valid(pfn)) 26580ae2fdcSVladimir Davydov ppage = pfn_to_page(pfn); 26680ae2fdcSVladimir Davydov else 26780ae2fdcSVladimir Davydov ppage = NULL; 26880ae2fdcSVladimir Davydov 26980ae2fdcSVladimir Davydov if (ppage) 27080ae2fdcSVladimir Davydov ino = page_cgroup_ino(ppage); 27180ae2fdcSVladimir Davydov else 27280ae2fdcSVladimir Davydov ino = 0; 27380ae2fdcSVladimir Davydov 27480ae2fdcSVladimir Davydov if (put_user(ino, out)) { 27580ae2fdcSVladimir Davydov ret = -EFAULT; 27680ae2fdcSVladimir Davydov break; 27780ae2fdcSVladimir Davydov } 27880ae2fdcSVladimir Davydov 27980ae2fdcSVladimir Davydov pfn++; 28080ae2fdcSVladimir Davydov out++; 28180ae2fdcSVladimir Davydov count -= KPMSIZE; 282d3691d2cSVladimir Davydov 283d3691d2cSVladimir Davydov cond_resched(); 28480ae2fdcSVladimir Davydov } 28580ae2fdcSVladimir Davydov 28680ae2fdcSVladimir Davydov *ppos += (char __user *)out - buf; 28780ae2fdcSVladimir Davydov if (!ret) 28880ae2fdcSVladimir Davydov ret = (char __user *)out - buf; 28980ae2fdcSVladimir Davydov return ret; 29080ae2fdcSVladimir Davydov } 29180ae2fdcSVladimir Davydov 29280ae2fdcSVladimir Davydov static const struct file_operations proc_kpagecgroup_operations = { 29380ae2fdcSVladimir Davydov .llseek = mem_lseek, 29480ae2fdcSVladimir Davydov .read = kpagecgroup_read, 29580ae2fdcSVladimir Davydov }; 29680ae2fdcSVladimir Davydov #endif /* CONFIG_MEMCG */ 29780ae2fdcSVladimir Davydov 2986d80e53fSAlexey Dobriyan static int __init proc_page_init(void) 2996d80e53fSAlexey Dobriyan { 3006d80e53fSAlexey Dobriyan proc_create("kpagecount", S_IRUSR, NULL, &proc_kpagecount_operations); 3016d80e53fSAlexey Dobriyan proc_create("kpageflags", S_IRUSR, NULL, &proc_kpageflags_operations); 30280ae2fdcSVladimir Davydov #ifdef CONFIG_MEMCG 30380ae2fdcSVladimir Davydov proc_create("kpagecgroup", S_IRUSR, NULL, &proc_kpagecgroup_operations); 30480ae2fdcSVladimir Davydov #endif 3056d80e53fSAlexey Dobriyan return 0; 3066d80e53fSAlexey Dobriyan } 307abaf3787SPaul Gortmaker fs_initcall(proc_page_init); 308