18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/pagewalk.h> 38c2ecf20Sopenharmony_ci#include <linux/vmacache.h> 48c2ecf20Sopenharmony_ci#include <linux/mm_inline.h> 58c2ecf20Sopenharmony_ci#include <linux/hugetlb.h> 68c2ecf20Sopenharmony_ci#include <linux/huge_mm.h> 78c2ecf20Sopenharmony_ci#include <linux/mount.h> 88c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 98c2ecf20Sopenharmony_ci#include <linux/highmem.h> 108c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 138c2ecf20Sopenharmony_ci#include <linux/mempolicy.h> 148c2ecf20Sopenharmony_ci#include <linux/rmap.h> 158c2ecf20Sopenharmony_ci#include <linux/swap.h> 168c2ecf20Sopenharmony_ci#include <linux/sched/mm.h> 178c2ecf20Sopenharmony_ci#include <linux/swapops.h> 188c2ecf20Sopenharmony_ci#include <linux/mmu_notifier.h> 198c2ecf20Sopenharmony_ci#include <linux/page_idle.h> 208c2ecf20Sopenharmony_ci#include <linux/shmem_fs.h> 218c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 228c2ecf20Sopenharmony_ci#include <linux/pkeys.h> 238c2ecf20Sopenharmony_ci#ifdef CONFIG_MEM_PURGEABLE 248c2ecf20Sopenharmony_ci#include <linux/mm_purgeable.h> 258c2ecf20Sopenharmony_ci#endif 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <asm/elf.h> 288c2ecf20Sopenharmony_ci#include <asm/tlb.h> 298c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 308c2ecf20Sopenharmony_ci#include "internal.h" 318c2ecf20Sopenharmony_ci#include <linux/hck/lite_hck_hideaddr.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define SEQ_PUT_DEC(str, val) \ 348c2ecf20Sopenharmony_ci seq_put_decimal_ull_width(m, str, (val) << (PAGE_SHIFT-10), 8) 358c2ecf20Sopenharmony_civoid task_mem(struct seq_file *m, struct mm_struct *mm) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci unsigned long text, lib, swap, anon, file, shmem; 388c2ecf20Sopenharmony_ci unsigned long hiwater_vm, total_vm, hiwater_rss, total_rss; 398c2ecf20Sopenharmony_ci#ifdef CONFIG_MEM_PURGEABLE 408c2ecf20Sopenharmony_ci unsigned long nr_purg_sum = 0, nr_purg_pin = 0; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci mm_purg_pages_info(mm, &nr_purg_sum, &nr_purg_pin); 438c2ecf20Sopenharmony_ci#endif 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci anon = get_mm_counter(mm, MM_ANONPAGES); 468c2ecf20Sopenharmony_ci file = get_mm_counter(mm, MM_FILEPAGES); 478c2ecf20Sopenharmony_ci shmem = get_mm_counter(mm, MM_SHMEMPAGES); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci /* 508c2ecf20Sopenharmony_ci * Note: to minimize their overhead, mm maintains hiwater_vm and 518c2ecf20Sopenharmony_ci * hiwater_rss only when about to *lower* total_vm or rss. Any 528c2ecf20Sopenharmony_ci * collector of these hiwater stats must therefore get total_vm 538c2ecf20Sopenharmony_ci * and rss too, which will usually be the higher. Barriers? not 548c2ecf20Sopenharmony_ci * worth the effort, such snapshots can always be inconsistent. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci hiwater_vm = total_vm = mm->total_vm; 578c2ecf20Sopenharmony_ci if (hiwater_vm < mm->hiwater_vm) 588c2ecf20Sopenharmony_ci hiwater_vm = mm->hiwater_vm; 598c2ecf20Sopenharmony_ci hiwater_rss = total_rss = anon + file + shmem; 608c2ecf20Sopenharmony_ci if (hiwater_rss < mm->hiwater_rss) 618c2ecf20Sopenharmony_ci hiwater_rss = mm->hiwater_rss; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci /* split executable areas between text and lib */ 648c2ecf20Sopenharmony_ci text = PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK); 658c2ecf20Sopenharmony_ci text = min(text, mm->exec_vm << PAGE_SHIFT); 668c2ecf20Sopenharmony_ci lib = (mm->exec_vm << PAGE_SHIFT) - text; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci swap = get_mm_counter(mm, MM_SWAPENTS); 698c2ecf20Sopenharmony_ci SEQ_PUT_DEC("VmPeak:\t", hiwater_vm); 708c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nVmSize:\t", total_vm); 718c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nVmLck:\t", mm->locked_vm); 728c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nVmPin:\t", atomic64_read(&mm->pinned_vm)); 738c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nVmHWM:\t", hiwater_rss); 748c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nVmRSS:\t", total_rss); 758c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nRssAnon:\t", anon); 768c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nRssFile:\t", file); 778c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nRssShmem:\t", shmem); 788c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nVmData:\t", mm->data_vm); 798c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nVmStk:\t", mm->stack_vm); 808c2ecf20Sopenharmony_ci seq_put_decimal_ull_width(m, 818c2ecf20Sopenharmony_ci " kB\nVmExe:\t", text >> 10, 8); 828c2ecf20Sopenharmony_ci seq_put_decimal_ull_width(m, 838c2ecf20Sopenharmony_ci " kB\nVmLib:\t", lib >> 10, 8); 848c2ecf20Sopenharmony_ci seq_put_decimal_ull_width(m, 858c2ecf20Sopenharmony_ci " kB\nVmPTE:\t", mm_pgtables_bytes(mm) >> 10, 8); 868c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nVmSwap:\t", swap); 878c2ecf20Sopenharmony_ci#ifdef CONFIG_MEM_PURGEABLE 888c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nPurgSum:\t", nr_purg_sum); 898c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nPurgPin:\t", nr_purg_pin); 908c2ecf20Sopenharmony_ci#endif 918c2ecf20Sopenharmony_ci seq_puts(m, " kB\n"); 928c2ecf20Sopenharmony_ci hugetlb_report_usage(m, mm); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci#undef SEQ_PUT_DEC 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ciunsigned long task_vsize(struct mm_struct *mm) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci return PAGE_SIZE * mm->total_vm; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ciunsigned long task_statm(struct mm_struct *mm, 1028c2ecf20Sopenharmony_ci unsigned long *shared, unsigned long *text, 1038c2ecf20Sopenharmony_ci unsigned long *data, unsigned long *resident) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci *shared = get_mm_counter(mm, MM_FILEPAGES) + 1068c2ecf20Sopenharmony_ci get_mm_counter(mm, MM_SHMEMPAGES); 1078c2ecf20Sopenharmony_ci *text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) 1088c2ecf20Sopenharmony_ci >> PAGE_SHIFT; 1098c2ecf20Sopenharmony_ci *data = mm->data_vm + mm->stack_vm; 1108c2ecf20Sopenharmony_ci *resident = *shared + get_mm_counter(mm, MM_ANONPAGES); 1118c2ecf20Sopenharmony_ci return mm->total_vm; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#ifdef CONFIG_NUMA 1158c2ecf20Sopenharmony_ci/* 1168c2ecf20Sopenharmony_ci * Save get_task_policy() for show_numa_map(). 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_cistatic void hold_task_mempolicy(struct proc_maps_private *priv) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct task_struct *task = priv->task; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci task_lock(task); 1238c2ecf20Sopenharmony_ci priv->task_mempolicy = get_task_policy(task); 1248c2ecf20Sopenharmony_ci mpol_get(priv->task_mempolicy); 1258c2ecf20Sopenharmony_ci task_unlock(task); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_cistatic void release_task_mempolicy(struct proc_maps_private *priv) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci mpol_put(priv->task_mempolicy); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci#else 1328c2ecf20Sopenharmony_cistatic void hold_task_mempolicy(struct proc_maps_private *priv) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_cistatic void release_task_mempolicy(struct proc_maps_private *priv) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci#endif 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void *m_start(struct seq_file *m, loff_t *ppos) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct proc_maps_private *priv = m->private; 1438c2ecf20Sopenharmony_ci unsigned long last_addr = *ppos; 1448c2ecf20Sopenharmony_ci struct mm_struct *mm; 1458c2ecf20Sopenharmony_ci struct vm_area_struct *vma; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* See m_next(). Zero at the start or after lseek. */ 1488c2ecf20Sopenharmony_ci if (last_addr == -1UL) 1498c2ecf20Sopenharmony_ci return NULL; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci priv->task = get_proc_task(priv->inode); 1528c2ecf20Sopenharmony_ci if (!priv->task) 1538c2ecf20Sopenharmony_ci return ERR_PTR(-ESRCH); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci mm = priv->mm; 1568c2ecf20Sopenharmony_ci if (!mm || !mmget_not_zero(mm)) { 1578c2ecf20Sopenharmony_ci put_task_struct(priv->task); 1588c2ecf20Sopenharmony_ci priv->task = NULL; 1598c2ecf20Sopenharmony_ci return NULL; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (mmap_read_lock_killable(mm)) { 1638c2ecf20Sopenharmony_ci mmput(mm); 1648c2ecf20Sopenharmony_ci put_task_struct(priv->task); 1658c2ecf20Sopenharmony_ci priv->task = NULL; 1668c2ecf20Sopenharmony_ci return ERR_PTR(-EINTR); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci hold_task_mempolicy(priv); 1708c2ecf20Sopenharmony_ci priv->tail_vma = get_gate_vma(mm); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci vma = find_vma(mm, last_addr); 1738c2ecf20Sopenharmony_ci if (vma) 1748c2ecf20Sopenharmony_ci return vma; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return priv->tail_vma; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void *m_next(struct seq_file *m, void *v, loff_t *ppos) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct proc_maps_private *priv = m->private; 1828c2ecf20Sopenharmony_ci struct vm_area_struct *next, *vma = v; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (vma == priv->tail_vma) 1858c2ecf20Sopenharmony_ci next = NULL; 1868c2ecf20Sopenharmony_ci else if (vma->vm_next) 1878c2ecf20Sopenharmony_ci next = vma->vm_next; 1888c2ecf20Sopenharmony_ci else 1898c2ecf20Sopenharmony_ci next = priv->tail_vma; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci *ppos = next ? next->vm_start : -1UL; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return next; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void m_stop(struct seq_file *m, void *v) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct proc_maps_private *priv = m->private; 1998c2ecf20Sopenharmony_ci struct mm_struct *mm = priv->mm; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (!priv->task) 2028c2ecf20Sopenharmony_ci return; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci release_task_mempolicy(priv); 2058c2ecf20Sopenharmony_ci mmap_read_unlock(mm); 2068c2ecf20Sopenharmony_ci mmput(mm); 2078c2ecf20Sopenharmony_ci put_task_struct(priv->task); 2088c2ecf20Sopenharmony_ci priv->task = NULL; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int proc_maps_open(struct inode *inode, struct file *file, 2128c2ecf20Sopenharmony_ci const struct seq_operations *ops, int psize) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct proc_maps_private *priv = __seq_open_private(file, ops, psize); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (!priv) 2178c2ecf20Sopenharmony_ci return -ENOMEM; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci priv->inode = inode; 2208c2ecf20Sopenharmony_ci priv->mm = proc_mem_open(inode, PTRACE_MODE_READ); 2218c2ecf20Sopenharmony_ci if (IS_ERR(priv->mm)) { 2228c2ecf20Sopenharmony_ci int err = PTR_ERR(priv->mm); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci seq_release_private(inode, file); 2258c2ecf20Sopenharmony_ci return err; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic int proc_map_release(struct inode *inode, struct file *file) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct seq_file *seq = file->private_data; 2348c2ecf20Sopenharmony_ci struct proc_maps_private *priv = seq->private; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (priv->mm) 2378c2ecf20Sopenharmony_ci mmdrop(priv->mm); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return seq_release_private(inode, file); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int do_maps_open(struct inode *inode, struct file *file, 2438c2ecf20Sopenharmony_ci const struct seq_operations *ops) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci return proc_maps_open(inode, file, ops, 2468c2ecf20Sopenharmony_ci sizeof(struct proc_maps_private)); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/* 2508c2ecf20Sopenharmony_ci * Indicate if the VMA is a stack for the given task; for 2518c2ecf20Sopenharmony_ci * /proc/PID/maps that is the stack of the main task. 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_cistatic int is_stack(struct vm_area_struct *vma) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci /* 2568c2ecf20Sopenharmony_ci * We make no effort to guess what a given thread considers to be 2578c2ecf20Sopenharmony_ci * its "stack". It's not even well-defined for programs written 2588c2ecf20Sopenharmony_ci * languages like Go. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci return vma->vm_start <= vma->vm_mm->start_stack && 2618c2ecf20Sopenharmony_ci vma->vm_end >= vma->vm_mm->start_stack; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic void show_vma_header_prefix(struct seq_file *m, 2658c2ecf20Sopenharmony_ci unsigned long start, unsigned long end, 2668c2ecf20Sopenharmony_ci vm_flags_t flags, unsigned long long pgoff, 2678c2ecf20Sopenharmony_ci dev_t dev, unsigned long ino) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci seq_setwidth(m, 25 + sizeof(void *) * 6 - 1); 2708c2ecf20Sopenharmony_ci seq_put_hex_ll(m, NULL, start, 8); 2718c2ecf20Sopenharmony_ci seq_put_hex_ll(m, "-", end, 8); 2728c2ecf20Sopenharmony_ci seq_putc(m, ' '); 2738c2ecf20Sopenharmony_ci seq_putc(m, flags & VM_READ ? 'r' : '-'); 2748c2ecf20Sopenharmony_ci seq_putc(m, flags & VM_WRITE ? 'w' : '-'); 2758c2ecf20Sopenharmony_ci seq_putc(m, flags & VM_EXEC ? 'x' : '-'); 2768c2ecf20Sopenharmony_ci seq_putc(m, flags & VM_MAYSHARE ? 's' : 'p'); 2778c2ecf20Sopenharmony_ci seq_put_hex_ll(m, " ", pgoff, 8); 2788c2ecf20Sopenharmony_ci seq_put_hex_ll(m, " ", MAJOR(dev), 2); 2798c2ecf20Sopenharmony_ci seq_put_hex_ll(m, ":", MINOR(dev), 2); 2808c2ecf20Sopenharmony_ci seq_put_decimal_ull(m, " ", ino); 2818c2ecf20Sopenharmony_ci seq_putc(m, ' '); 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic void 2858c2ecf20Sopenharmony_cishow_map_vma(struct seq_file *m, struct vm_area_struct *vma) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 2888c2ecf20Sopenharmony_ci struct file *file = vma->vm_file; 2898c2ecf20Sopenharmony_ci vm_flags_t flags = vma->vm_flags; 2908c2ecf20Sopenharmony_ci unsigned long ino = 0; 2918c2ecf20Sopenharmony_ci unsigned long long pgoff = 0; 2928c2ecf20Sopenharmony_ci unsigned long start, end; 2938c2ecf20Sopenharmony_ci dev_t dev = 0; 2948c2ecf20Sopenharmony_ci const char *name = NULL; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (file) { 2978c2ecf20Sopenharmony_ci struct inode *inode = file_inode(vma->vm_file); 2988c2ecf20Sopenharmony_ci dev = inode->i_sb->s_dev; 2998c2ecf20Sopenharmony_ci ino = inode->i_ino; 3008c2ecf20Sopenharmony_ci pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci start = vma->vm_start; 3048c2ecf20Sopenharmony_ci end = vma->vm_end; 3058c2ecf20Sopenharmony_ci CALL_HCK_LITE_HOOK(hideaddr_header_prefix_lhck, &start, &end, &flags, m, vma); 3068c2ecf20Sopenharmony_ci show_vma_header_prefix(m, start, end, flags, pgoff, dev, ino); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* 3098c2ecf20Sopenharmony_ci * Print the dentry name for named mappings, and a 3108c2ecf20Sopenharmony_ci * special [heap] marker for the heap: 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci if (file) { 3138c2ecf20Sopenharmony_ci seq_pad(m, ' '); 3148c2ecf20Sopenharmony_ci seq_file_path(m, file, "\n"); 3158c2ecf20Sopenharmony_ci goto done; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (vma->vm_ops && vma->vm_ops->name) { 3198c2ecf20Sopenharmony_ci name = vma->vm_ops->name(vma); 3208c2ecf20Sopenharmony_ci if (name) 3218c2ecf20Sopenharmony_ci goto done; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci name = arch_vma_name(vma); 3258c2ecf20Sopenharmony_ci if (!name) { 3268c2ecf20Sopenharmony_ci struct anon_vma_name *anon_name; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (!mm) { 3298c2ecf20Sopenharmony_ci name = "[vdso]"; 3308c2ecf20Sopenharmony_ci goto done; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (vma->vm_start <= mm->brk && 3348c2ecf20Sopenharmony_ci vma->vm_end >= mm->start_brk) { 3358c2ecf20Sopenharmony_ci name = "[heap]"; 3368c2ecf20Sopenharmony_ci goto done; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (is_stack(vma)) { 3408c2ecf20Sopenharmony_ci name = "[stack]"; 3418c2ecf20Sopenharmony_ci goto done; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci anon_name = anon_vma_name(vma); 3458c2ecf20Sopenharmony_ci if (anon_name) { 3468c2ecf20Sopenharmony_ci seq_pad(m, ' '); 3478c2ecf20Sopenharmony_ci seq_printf(m, "[anon:%s]", anon_name->name); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cidone: 3528c2ecf20Sopenharmony_ci if (name) { 3538c2ecf20Sopenharmony_ci seq_pad(m, ' '); 3548c2ecf20Sopenharmony_ci seq_puts(m, name); 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci seq_putc(m, '\n'); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int show_map(struct seq_file *m, void *v) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci show_map_vma(m, v); 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic const struct seq_operations proc_pid_maps_op = { 3668c2ecf20Sopenharmony_ci .start = m_start, 3678c2ecf20Sopenharmony_ci .next = m_next, 3688c2ecf20Sopenharmony_ci .stop = m_stop, 3698c2ecf20Sopenharmony_ci .show = show_map 3708c2ecf20Sopenharmony_ci}; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic int pid_maps_open(struct inode *inode, struct file *file) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci return do_maps_open(inode, file, &proc_pid_maps_op); 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ciconst struct file_operations proc_pid_maps_operations = { 3788c2ecf20Sopenharmony_ci .open = pid_maps_open, 3798c2ecf20Sopenharmony_ci .read = seq_read, 3808c2ecf20Sopenharmony_ci .llseek = seq_lseek, 3818c2ecf20Sopenharmony_ci .release = proc_map_release, 3828c2ecf20Sopenharmony_ci}; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci/* 3858c2ecf20Sopenharmony_ci * Proportional Set Size(PSS): my share of RSS. 3868c2ecf20Sopenharmony_ci * 3878c2ecf20Sopenharmony_ci * PSS of a process is the count of pages it has in memory, where each 3888c2ecf20Sopenharmony_ci * page is divided by the number of processes sharing it. So if a 3898c2ecf20Sopenharmony_ci * process has 1000 pages all to itself, and 1000 shared with one other 3908c2ecf20Sopenharmony_ci * process, its PSS will be 1500. 3918c2ecf20Sopenharmony_ci * 3928c2ecf20Sopenharmony_ci * To keep (accumulated) division errors low, we adopt a 64bit 3938c2ecf20Sopenharmony_ci * fixed-point pss counter to minimize division errors. So (pss >> 3948c2ecf20Sopenharmony_ci * PSS_SHIFT) would be the real byte count. 3958c2ecf20Sopenharmony_ci * 3968c2ecf20Sopenharmony_ci * A shift of 12 before division means (assuming 4K page size): 3978c2ecf20Sopenharmony_ci * - 1M 3-user-pages add up to 8KB errors; 3988c2ecf20Sopenharmony_ci * - supports mapcount up to 2^24, or 16M; 3998c2ecf20Sopenharmony_ci * - supports PSS up to 2^52 bytes, or 4PB. 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_ci#define PSS_SHIFT 12 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_PAGE_MONITOR 4048c2ecf20Sopenharmony_cistruct mem_size_stats { 4058c2ecf20Sopenharmony_ci unsigned long resident; 4068c2ecf20Sopenharmony_ci unsigned long shared_clean; 4078c2ecf20Sopenharmony_ci unsigned long shared_dirty; 4088c2ecf20Sopenharmony_ci unsigned long private_clean; 4098c2ecf20Sopenharmony_ci unsigned long private_dirty; 4108c2ecf20Sopenharmony_ci unsigned long referenced; 4118c2ecf20Sopenharmony_ci unsigned long anonymous; 4128c2ecf20Sopenharmony_ci unsigned long lazyfree; 4138c2ecf20Sopenharmony_ci unsigned long anonymous_thp; 4148c2ecf20Sopenharmony_ci unsigned long shmem_thp; 4158c2ecf20Sopenharmony_ci unsigned long file_thp; 4168c2ecf20Sopenharmony_ci unsigned long swap; 4178c2ecf20Sopenharmony_ci unsigned long shared_hugetlb; 4188c2ecf20Sopenharmony_ci unsigned long private_hugetlb; 4198c2ecf20Sopenharmony_ci u64 pss; 4208c2ecf20Sopenharmony_ci u64 pss_anon; 4218c2ecf20Sopenharmony_ci u64 pss_file; 4228c2ecf20Sopenharmony_ci u64 pss_shmem; 4238c2ecf20Sopenharmony_ci u64 pss_locked; 4248c2ecf20Sopenharmony_ci u64 swap_pss; 4258c2ecf20Sopenharmony_ci bool check_shmem_swap; 4268c2ecf20Sopenharmony_ci}; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic void smaps_page_accumulate(struct mem_size_stats *mss, 4298c2ecf20Sopenharmony_ci struct page *page, unsigned long size, unsigned long pss, 4308c2ecf20Sopenharmony_ci bool dirty, bool locked, bool private) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci mss->pss += pss; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (PageAnon(page)) 4358c2ecf20Sopenharmony_ci mss->pss_anon += pss; 4368c2ecf20Sopenharmony_ci else if (PageSwapBacked(page)) 4378c2ecf20Sopenharmony_ci mss->pss_shmem += pss; 4388c2ecf20Sopenharmony_ci else 4398c2ecf20Sopenharmony_ci mss->pss_file += pss; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (locked) 4428c2ecf20Sopenharmony_ci mss->pss_locked += pss; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (dirty || PageDirty(page)) { 4458c2ecf20Sopenharmony_ci if (private) 4468c2ecf20Sopenharmony_ci mss->private_dirty += size; 4478c2ecf20Sopenharmony_ci else 4488c2ecf20Sopenharmony_ci mss->shared_dirty += size; 4498c2ecf20Sopenharmony_ci } else { 4508c2ecf20Sopenharmony_ci if (private) 4518c2ecf20Sopenharmony_ci mss->private_clean += size; 4528c2ecf20Sopenharmony_ci else 4538c2ecf20Sopenharmony_ci mss->shared_clean += size; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void smaps_account(struct mem_size_stats *mss, struct page *page, 4588c2ecf20Sopenharmony_ci bool compound, bool young, bool dirty, bool locked, 4598c2ecf20Sopenharmony_ci bool migration) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci int i, nr = compound ? compound_nr(page) : 1; 4628c2ecf20Sopenharmony_ci unsigned long size = nr * PAGE_SIZE; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci /* 4658c2ecf20Sopenharmony_ci * First accumulate quantities that depend only on |size| and the type 4668c2ecf20Sopenharmony_ci * of the compound page. 4678c2ecf20Sopenharmony_ci */ 4688c2ecf20Sopenharmony_ci if (PageAnon(page)) { 4698c2ecf20Sopenharmony_ci mss->anonymous += size; 4708c2ecf20Sopenharmony_ci if (!PageSwapBacked(page) && !dirty && !PageDirty(page)) 4718c2ecf20Sopenharmony_ci mss->lazyfree += size; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci mss->resident += size; 4758c2ecf20Sopenharmony_ci /* Accumulate the size in pages that have been accessed. */ 4768c2ecf20Sopenharmony_ci if (young || page_is_young(page) || PageReferenced(page)) 4778c2ecf20Sopenharmony_ci mss->referenced += size; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* 4808c2ecf20Sopenharmony_ci * Then accumulate quantities that may depend on sharing, or that may 4818c2ecf20Sopenharmony_ci * differ page-by-page. 4828c2ecf20Sopenharmony_ci * 4838c2ecf20Sopenharmony_ci * page_count(page) == 1 guarantees the page is mapped exactly once. 4848c2ecf20Sopenharmony_ci * If any subpage of the compound page mapped with PTE it would elevate 4858c2ecf20Sopenharmony_ci * page_count(). 4868c2ecf20Sopenharmony_ci * 4878c2ecf20Sopenharmony_ci * The page_mapcount() is called to get a snapshot of the mapcount. 4888c2ecf20Sopenharmony_ci * Without holding the page lock this snapshot can be slightly wrong as 4898c2ecf20Sopenharmony_ci * we cannot always read the mapcount atomically. It is not safe to 4908c2ecf20Sopenharmony_ci * call page_mapcount() even with PTL held if the page is not mapped, 4918c2ecf20Sopenharmony_ci * especially for migration entries. Treat regular migration entries 4928c2ecf20Sopenharmony_ci * as mapcount == 1. 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_ci if ((page_count(page) == 1) || migration) { 4958c2ecf20Sopenharmony_ci smaps_page_accumulate(mss, page, size, size << PSS_SHIFT, dirty, 4968c2ecf20Sopenharmony_ci locked, true); 4978c2ecf20Sopenharmony_ci return; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci for (i = 0; i < nr; i++, page++) { 5008c2ecf20Sopenharmony_ci int mapcount = page_mapcount(page); 5018c2ecf20Sopenharmony_ci unsigned long pss = PAGE_SIZE << PSS_SHIFT; 5028c2ecf20Sopenharmony_ci if (mapcount >= 2) 5038c2ecf20Sopenharmony_ci pss /= mapcount; 5048c2ecf20Sopenharmony_ci smaps_page_accumulate(mss, page, PAGE_SIZE, pss, dirty, locked, 5058c2ecf20Sopenharmony_ci mapcount < 2); 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci#ifdef CONFIG_SHMEM 5108c2ecf20Sopenharmony_cistatic int smaps_pte_hole(unsigned long addr, unsigned long end, 5118c2ecf20Sopenharmony_ci __always_unused int depth, struct mm_walk *walk) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct mem_size_stats *mss = walk->private; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci mss->swap += shmem_partial_swap_usage( 5168c2ecf20Sopenharmony_ci walk->vma->vm_file->f_mapping, addr, end); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return 0; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci#else 5218c2ecf20Sopenharmony_ci#define smaps_pte_hole NULL 5228c2ecf20Sopenharmony_ci#endif /* CONFIG_SHMEM */ 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic void smaps_pte_entry(pte_t *pte, unsigned long addr, 5258c2ecf20Sopenharmony_ci struct mm_walk *walk) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct mem_size_stats *mss = walk->private; 5288c2ecf20Sopenharmony_ci struct vm_area_struct *vma = walk->vma; 5298c2ecf20Sopenharmony_ci bool locked = !!(vma->vm_flags & VM_LOCKED); 5308c2ecf20Sopenharmony_ci struct page *page = NULL; 5318c2ecf20Sopenharmony_ci bool migration = false, young = false, dirty = false; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (pte_present(*pte)) { 5348c2ecf20Sopenharmony_ci page = vm_normal_page(vma, addr, *pte); 5358c2ecf20Sopenharmony_ci young = pte_young(*pte); 5368c2ecf20Sopenharmony_ci dirty = pte_dirty(*pte); 5378c2ecf20Sopenharmony_ci } else if (is_swap_pte(*pte)) { 5388c2ecf20Sopenharmony_ci swp_entry_t swpent = pte_to_swp_entry(*pte); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (!non_swap_entry(swpent)) { 5418c2ecf20Sopenharmony_ci int mapcount; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci mss->swap += PAGE_SIZE; 5448c2ecf20Sopenharmony_ci mapcount = swp_swapcount(swpent); 5458c2ecf20Sopenharmony_ci if (mapcount >= 2) { 5468c2ecf20Sopenharmony_ci u64 pss_delta = (u64)PAGE_SIZE << PSS_SHIFT; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci do_div(pss_delta, mapcount); 5498c2ecf20Sopenharmony_ci mss->swap_pss += pss_delta; 5508c2ecf20Sopenharmony_ci } else { 5518c2ecf20Sopenharmony_ci mss->swap_pss += (u64)PAGE_SIZE << PSS_SHIFT; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci } else if (is_migration_entry(swpent)) { 5548c2ecf20Sopenharmony_ci migration = true; 5558c2ecf20Sopenharmony_ci page = migration_entry_to_page(swpent); 5568c2ecf20Sopenharmony_ci } else if (is_device_private_entry(swpent)) 5578c2ecf20Sopenharmony_ci page = device_private_entry_to_page(swpent); 5588c2ecf20Sopenharmony_ci } else if (unlikely(IS_ENABLED(CONFIG_SHMEM) && mss->check_shmem_swap 5598c2ecf20Sopenharmony_ci && pte_none(*pte))) { 5608c2ecf20Sopenharmony_ci page = xa_load(&vma->vm_file->f_mapping->i_pages, 5618c2ecf20Sopenharmony_ci linear_page_index(vma, addr)); 5628c2ecf20Sopenharmony_ci if (xa_is_value(page)) 5638c2ecf20Sopenharmony_ci mss->swap += PAGE_SIZE; 5648c2ecf20Sopenharmony_ci return; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (!page) 5688c2ecf20Sopenharmony_ci return; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci smaps_account(mss, page, false, young, dirty, locked, migration); 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 5748c2ecf20Sopenharmony_cistatic void smaps_pmd_entry(pmd_t *pmd, unsigned long addr, 5758c2ecf20Sopenharmony_ci struct mm_walk *walk) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci struct mem_size_stats *mss = walk->private; 5788c2ecf20Sopenharmony_ci struct vm_area_struct *vma = walk->vma; 5798c2ecf20Sopenharmony_ci bool locked = !!(vma->vm_flags & VM_LOCKED); 5808c2ecf20Sopenharmony_ci struct page *page = NULL; 5818c2ecf20Sopenharmony_ci bool migration = false; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (pmd_present(*pmd)) { 5848c2ecf20Sopenharmony_ci /* FOLL_DUMP will return -EFAULT on huge zero page */ 5858c2ecf20Sopenharmony_ci page = follow_trans_huge_pmd(vma, addr, pmd, FOLL_DUMP); 5868c2ecf20Sopenharmony_ci } else if (unlikely(thp_migration_supported() && is_swap_pmd(*pmd))) { 5878c2ecf20Sopenharmony_ci swp_entry_t entry = pmd_to_swp_entry(*pmd); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (is_migration_entry(entry)) { 5908c2ecf20Sopenharmony_ci migration = true; 5918c2ecf20Sopenharmony_ci page = migration_entry_to_page(entry); 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(page)) 5958c2ecf20Sopenharmony_ci return; 5968c2ecf20Sopenharmony_ci if (PageAnon(page)) 5978c2ecf20Sopenharmony_ci mss->anonymous_thp += HPAGE_PMD_SIZE; 5988c2ecf20Sopenharmony_ci else if (PageSwapBacked(page)) 5998c2ecf20Sopenharmony_ci mss->shmem_thp += HPAGE_PMD_SIZE; 6008c2ecf20Sopenharmony_ci else if (is_zone_device_page(page)) 6018c2ecf20Sopenharmony_ci /* pass */; 6028c2ecf20Sopenharmony_ci else 6038c2ecf20Sopenharmony_ci mss->file_thp += HPAGE_PMD_SIZE; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci smaps_account(mss, page, true, pmd_young(*pmd), pmd_dirty(*pmd), 6068c2ecf20Sopenharmony_ci locked, migration); 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci#else 6098c2ecf20Sopenharmony_cistatic void smaps_pmd_entry(pmd_t *pmd, unsigned long addr, 6108c2ecf20Sopenharmony_ci struct mm_walk *walk) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci#endif 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, 6168c2ecf20Sopenharmony_ci struct mm_walk *walk) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct vm_area_struct *vma = walk->vma; 6198c2ecf20Sopenharmony_ci pte_t *pte; 6208c2ecf20Sopenharmony_ci spinlock_t *ptl; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci ptl = pmd_trans_huge_lock(pmd, vma); 6238c2ecf20Sopenharmony_ci if (ptl) { 6248c2ecf20Sopenharmony_ci smaps_pmd_entry(pmd, addr, walk); 6258c2ecf20Sopenharmony_ci spin_unlock(ptl); 6268c2ecf20Sopenharmony_ci goto out; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (pmd_trans_unstable(pmd)) 6308c2ecf20Sopenharmony_ci goto out; 6318c2ecf20Sopenharmony_ci /* 6328c2ecf20Sopenharmony_ci * The mmap_lock held all the way back in m_start() is what 6338c2ecf20Sopenharmony_ci * keeps khugepaged out of here and from collapsing things 6348c2ecf20Sopenharmony_ci * in here. 6358c2ecf20Sopenharmony_ci */ 6368c2ecf20Sopenharmony_ci pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); 6378c2ecf20Sopenharmony_ci for (; addr != end; pte++, addr += PAGE_SIZE) 6388c2ecf20Sopenharmony_ci smaps_pte_entry(pte, addr, walk); 6398c2ecf20Sopenharmony_ci pte_unmap_unlock(pte - 1, ptl); 6408c2ecf20Sopenharmony_ciout: 6418c2ecf20Sopenharmony_ci cond_resched(); 6428c2ecf20Sopenharmony_ci return 0; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic void show_smap_vma_flags(struct seq_file *m, struct vm_area_struct *vma) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci /* 6488c2ecf20Sopenharmony_ci * Don't forget to update Documentation/ on changes. 6498c2ecf20Sopenharmony_ci */ 6508c2ecf20Sopenharmony_ci static const char mnemonics[BITS_PER_LONG][2] = { 6518c2ecf20Sopenharmony_ci /* 6528c2ecf20Sopenharmony_ci * In case if we meet a flag we don't know about. 6538c2ecf20Sopenharmony_ci */ 6548c2ecf20Sopenharmony_ci [0 ... (BITS_PER_LONG-1)] = "??", 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci [ilog2(VM_READ)] = "rd", 6578c2ecf20Sopenharmony_ci [ilog2(VM_WRITE)] = "wr", 6588c2ecf20Sopenharmony_ci [ilog2(VM_EXEC)] = "ex", 6598c2ecf20Sopenharmony_ci [ilog2(VM_SHARED)] = "sh", 6608c2ecf20Sopenharmony_ci [ilog2(VM_MAYREAD)] = "mr", 6618c2ecf20Sopenharmony_ci [ilog2(VM_MAYWRITE)] = "mw", 6628c2ecf20Sopenharmony_ci [ilog2(VM_MAYEXEC)] = "me", 6638c2ecf20Sopenharmony_ci [ilog2(VM_MAYSHARE)] = "ms", 6648c2ecf20Sopenharmony_ci [ilog2(VM_GROWSDOWN)] = "gd", 6658c2ecf20Sopenharmony_ci [ilog2(VM_PFNMAP)] = "pf", 6668c2ecf20Sopenharmony_ci [ilog2(VM_DENYWRITE)] = "dw", 6678c2ecf20Sopenharmony_ci [ilog2(VM_LOCKED)] = "lo", 6688c2ecf20Sopenharmony_ci [ilog2(VM_IO)] = "io", 6698c2ecf20Sopenharmony_ci [ilog2(VM_SEQ_READ)] = "sr", 6708c2ecf20Sopenharmony_ci [ilog2(VM_RAND_READ)] = "rr", 6718c2ecf20Sopenharmony_ci [ilog2(VM_DONTCOPY)] = "dc", 6728c2ecf20Sopenharmony_ci [ilog2(VM_DONTEXPAND)] = "de", 6738c2ecf20Sopenharmony_ci [ilog2(VM_ACCOUNT)] = "ac", 6748c2ecf20Sopenharmony_ci [ilog2(VM_NORESERVE)] = "nr", 6758c2ecf20Sopenharmony_ci [ilog2(VM_HUGETLB)] = "ht", 6768c2ecf20Sopenharmony_ci [ilog2(VM_SYNC)] = "sf", 6778c2ecf20Sopenharmony_ci [ilog2(VM_ARCH_1)] = "ar", 6788c2ecf20Sopenharmony_ci [ilog2(VM_WIPEONFORK)] = "wf", 6798c2ecf20Sopenharmony_ci [ilog2(VM_DONTDUMP)] = "dd", 6808c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64_BTI 6818c2ecf20Sopenharmony_ci [ilog2(VM_ARM64_BTI)] = "bt", 6828c2ecf20Sopenharmony_ci#endif 6838c2ecf20Sopenharmony_ci#ifdef CONFIG_MEM_SOFT_DIRTY 6848c2ecf20Sopenharmony_ci [ilog2(VM_SOFTDIRTY)] = "sd", 6858c2ecf20Sopenharmony_ci#endif 6868c2ecf20Sopenharmony_ci [ilog2(VM_MIXEDMAP)] = "mm", 6878c2ecf20Sopenharmony_ci [ilog2(VM_HUGEPAGE)] = "hg", 6888c2ecf20Sopenharmony_ci [ilog2(VM_NOHUGEPAGE)] = "nh", 6898c2ecf20Sopenharmony_ci [ilog2(VM_MERGEABLE)] = "mg", 6908c2ecf20Sopenharmony_ci [ilog2(VM_UFFD_MISSING)]= "um", 6918c2ecf20Sopenharmony_ci [ilog2(VM_UFFD_WP)] = "uw", 6928c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64_MTE 6938c2ecf20Sopenharmony_ci [ilog2(VM_MTE)] = "mt", 6948c2ecf20Sopenharmony_ci [ilog2(VM_MTE_ALLOWED)] = "", 6958c2ecf20Sopenharmony_ci#endif 6968c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_HAS_PKEYS 6978c2ecf20Sopenharmony_ci /* These come out via ProtectionKey: */ 6988c2ecf20Sopenharmony_ci [ilog2(VM_PKEY_BIT0)] = "", 6998c2ecf20Sopenharmony_ci [ilog2(VM_PKEY_BIT1)] = "", 7008c2ecf20Sopenharmony_ci [ilog2(VM_PKEY_BIT2)] = "", 7018c2ecf20Sopenharmony_ci [ilog2(VM_PKEY_BIT3)] = "", 7028c2ecf20Sopenharmony_ci#if VM_PKEY_BIT4 7038c2ecf20Sopenharmony_ci [ilog2(VM_PKEY_BIT4)] = "", 7048c2ecf20Sopenharmony_ci#endif 7058c2ecf20Sopenharmony_ci#endif /* CONFIG_ARCH_HAS_PKEYS */ 7068c2ecf20Sopenharmony_ci }; 7078c2ecf20Sopenharmony_ci size_t i; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci seq_puts(m, "VmFlags: "); 7108c2ecf20Sopenharmony_ci for (i = 0; i < BITS_PER_LONG; i++) { 7118c2ecf20Sopenharmony_ci if (!mnemonics[i][0]) 7128c2ecf20Sopenharmony_ci continue; 7138c2ecf20Sopenharmony_ci if (vma->vm_flags & (1UL << i)) { 7148c2ecf20Sopenharmony_ci seq_putc(m, mnemonics[i][0]); 7158c2ecf20Sopenharmony_ci seq_putc(m, mnemonics[i][1]); 7168c2ecf20Sopenharmony_ci seq_putc(m, ' '); 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci seq_putc(m, '\n'); 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci#ifdef CONFIG_HUGETLB_PAGE 7238c2ecf20Sopenharmony_cistatic int smaps_hugetlb_range(pte_t *pte, unsigned long hmask, 7248c2ecf20Sopenharmony_ci unsigned long addr, unsigned long end, 7258c2ecf20Sopenharmony_ci struct mm_walk *walk) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci struct mem_size_stats *mss = walk->private; 7288c2ecf20Sopenharmony_ci struct vm_area_struct *vma = walk->vma; 7298c2ecf20Sopenharmony_ci struct page *page = NULL; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (pte_present(*pte)) { 7328c2ecf20Sopenharmony_ci page = vm_normal_page(vma, addr, *pte); 7338c2ecf20Sopenharmony_ci } else if (is_swap_pte(*pte)) { 7348c2ecf20Sopenharmony_ci swp_entry_t swpent = pte_to_swp_entry(*pte); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (is_migration_entry(swpent)) 7378c2ecf20Sopenharmony_ci page = migration_entry_to_page(swpent); 7388c2ecf20Sopenharmony_ci else if (is_device_private_entry(swpent)) 7398c2ecf20Sopenharmony_ci page = device_private_entry_to_page(swpent); 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci if (page) { 7428c2ecf20Sopenharmony_ci if (page_mapcount(page) >= 2 || hugetlb_pmd_shared(pte)) 7438c2ecf20Sopenharmony_ci mss->shared_hugetlb += huge_page_size(hstate_vma(vma)); 7448c2ecf20Sopenharmony_ci else 7458c2ecf20Sopenharmony_ci mss->private_hugetlb += huge_page_size(hstate_vma(vma)); 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci return 0; 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci#else 7508c2ecf20Sopenharmony_ci#define smaps_hugetlb_range NULL 7518c2ecf20Sopenharmony_ci#endif /* HUGETLB_PAGE */ 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cistatic const struct mm_walk_ops smaps_walk_ops = { 7548c2ecf20Sopenharmony_ci .pmd_entry = smaps_pte_range, 7558c2ecf20Sopenharmony_ci .hugetlb_entry = smaps_hugetlb_range, 7568c2ecf20Sopenharmony_ci}; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic const struct mm_walk_ops smaps_shmem_walk_ops = { 7598c2ecf20Sopenharmony_ci .pmd_entry = smaps_pte_range, 7608c2ecf20Sopenharmony_ci .hugetlb_entry = smaps_hugetlb_range, 7618c2ecf20Sopenharmony_ci .pte_hole = smaps_pte_hole, 7628c2ecf20Sopenharmony_ci}; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci/* 7658c2ecf20Sopenharmony_ci * Gather mem stats from @vma with the indicated beginning 7668c2ecf20Sopenharmony_ci * address @start, and keep them in @mss. 7678c2ecf20Sopenharmony_ci * 7688c2ecf20Sopenharmony_ci * Use vm_start of @vma as the beginning address if @start is 0. 7698c2ecf20Sopenharmony_ci */ 7708c2ecf20Sopenharmony_cistatic void smap_gather_stats(struct vm_area_struct *vma, 7718c2ecf20Sopenharmony_ci struct mem_size_stats *mss, unsigned long start) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci const struct mm_walk_ops *ops = &smaps_walk_ops; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci /* Invalid start */ 7768c2ecf20Sopenharmony_ci if (start >= vma->vm_end) 7778c2ecf20Sopenharmony_ci return; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci#ifdef CONFIG_SHMEM 7808c2ecf20Sopenharmony_ci /* In case of smaps_rollup, reset the value from previous vma */ 7818c2ecf20Sopenharmony_ci mss->check_shmem_swap = false; 7828c2ecf20Sopenharmony_ci if (vma->vm_file && shmem_mapping(vma->vm_file->f_mapping)) { 7838c2ecf20Sopenharmony_ci /* 7848c2ecf20Sopenharmony_ci * For shared or readonly shmem mappings we know that all 7858c2ecf20Sopenharmony_ci * swapped out pages belong to the shmem object, and we can 7868c2ecf20Sopenharmony_ci * obtain the swap value much more efficiently. For private 7878c2ecf20Sopenharmony_ci * writable mappings, we might have COW pages that are 7888c2ecf20Sopenharmony_ci * not affected by the parent swapped out pages of the shmem 7898c2ecf20Sopenharmony_ci * object, so we have to distinguish them during the page walk. 7908c2ecf20Sopenharmony_ci * Unless we know that the shmem object (or the part mapped by 7918c2ecf20Sopenharmony_ci * our VMA) has no swapped out pages at all. 7928c2ecf20Sopenharmony_ci */ 7938c2ecf20Sopenharmony_ci unsigned long shmem_swapped = shmem_swap_usage(vma); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (!start && (!shmem_swapped || (vma->vm_flags & VM_SHARED) || 7968c2ecf20Sopenharmony_ci !(vma->vm_flags & VM_WRITE))) { 7978c2ecf20Sopenharmony_ci mss->swap += shmem_swapped; 7988c2ecf20Sopenharmony_ci } else { 7998c2ecf20Sopenharmony_ci mss->check_shmem_swap = true; 8008c2ecf20Sopenharmony_ci ops = &smaps_shmem_walk_ops; 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci#endif 8048c2ecf20Sopenharmony_ci /* mmap_lock is held in m_start */ 8058c2ecf20Sopenharmony_ci if (!start) 8068c2ecf20Sopenharmony_ci walk_page_vma(vma, ops, mss); 8078c2ecf20Sopenharmony_ci else 8088c2ecf20Sopenharmony_ci walk_page_range(vma->vm_mm, start, vma->vm_end, ops, mss); 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci#define SEQ_PUT_DEC(str, val) \ 8128c2ecf20Sopenharmony_ci seq_put_decimal_ull_width(m, str, (val) >> 10, 8) 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci/* Show the contents common for smaps and smaps_rollup */ 8158c2ecf20Sopenharmony_cistatic void __show_smap(struct seq_file *m, const struct mem_size_stats *mss, 8168c2ecf20Sopenharmony_ci bool rollup_mode) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci SEQ_PUT_DEC("Rss: ", mss->resident); 8198c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nPss: ", mss->pss >> PSS_SHIFT); 8208c2ecf20Sopenharmony_ci if (rollup_mode) { 8218c2ecf20Sopenharmony_ci /* 8228c2ecf20Sopenharmony_ci * These are meaningful only for smaps_rollup, otherwise two of 8238c2ecf20Sopenharmony_ci * them are zero, and the other one is the same as Pss. 8248c2ecf20Sopenharmony_ci */ 8258c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nPss_Anon: ", 8268c2ecf20Sopenharmony_ci mss->pss_anon >> PSS_SHIFT); 8278c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nPss_File: ", 8288c2ecf20Sopenharmony_ci mss->pss_file >> PSS_SHIFT); 8298c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nPss_Shmem: ", 8308c2ecf20Sopenharmony_ci mss->pss_shmem >> PSS_SHIFT); 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nShared_Clean: ", mss->shared_clean); 8338c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nShared_Dirty: ", mss->shared_dirty); 8348c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nPrivate_Clean: ", mss->private_clean); 8358c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nPrivate_Dirty: ", mss->private_dirty); 8368c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nReferenced: ", mss->referenced); 8378c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nAnonymous: ", mss->anonymous); 8388c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nLazyFree: ", mss->lazyfree); 8398c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nAnonHugePages: ", mss->anonymous_thp); 8408c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nShmemPmdMapped: ", mss->shmem_thp); 8418c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nFilePmdMapped: ", mss->file_thp); 8428c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nShared_Hugetlb: ", mss->shared_hugetlb); 8438c2ecf20Sopenharmony_ci seq_put_decimal_ull_width(m, " kB\nPrivate_Hugetlb: ", 8448c2ecf20Sopenharmony_ci mss->private_hugetlb >> 10, 7); 8458c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nSwap: ", mss->swap); 8468c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nSwapPss: ", 8478c2ecf20Sopenharmony_ci mss->swap_pss >> PSS_SHIFT); 8488c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nLocked: ", 8498c2ecf20Sopenharmony_ci mss->pss_locked >> PSS_SHIFT); 8508c2ecf20Sopenharmony_ci seq_puts(m, " kB\n"); 8518c2ecf20Sopenharmony_ci} 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_cistatic int show_smap(struct seq_file *m, void *v) 8548c2ecf20Sopenharmony_ci{ 8558c2ecf20Sopenharmony_ci struct vm_area_struct *vma = v; 8568c2ecf20Sopenharmony_ci struct mem_size_stats mss; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci memset(&mss, 0, sizeof(mss)); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci smap_gather_stats(vma, &mss, 0); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci show_map_vma(m, vma); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci SEQ_PUT_DEC("Size: ", vma->vm_end - vma->vm_start); 8658c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nKernelPageSize: ", vma_kernel_pagesize(vma)); 8668c2ecf20Sopenharmony_ci SEQ_PUT_DEC(" kB\nMMUPageSize: ", vma_mmu_pagesize(vma)); 8678c2ecf20Sopenharmony_ci seq_puts(m, " kB\n"); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci __show_smap(m, &mss, false); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci seq_printf(m, "THPeligible: %d\n", 8728c2ecf20Sopenharmony_ci transparent_hugepage_active(vma)); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci if (arch_pkeys_enabled()) 8758c2ecf20Sopenharmony_ci seq_printf(m, "ProtectionKey: %8u\n", vma_pkey(vma)); 8768c2ecf20Sopenharmony_ci show_smap_vma_flags(m, vma); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci return 0; 8798c2ecf20Sopenharmony_ci} 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_cistatic int show_smaps_rollup(struct seq_file *m, void *v) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci struct proc_maps_private *priv = m->private; 8848c2ecf20Sopenharmony_ci struct mem_size_stats mss; 8858c2ecf20Sopenharmony_ci struct mm_struct *mm; 8868c2ecf20Sopenharmony_ci struct vm_area_struct *vma; 8878c2ecf20Sopenharmony_ci unsigned long last_vma_end = 0; 8888c2ecf20Sopenharmony_ci int ret = 0; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci priv->task = get_proc_task(priv->inode); 8918c2ecf20Sopenharmony_ci if (!priv->task) 8928c2ecf20Sopenharmony_ci return -ESRCH; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci mm = priv->mm; 8958c2ecf20Sopenharmony_ci if (!mm || !mmget_not_zero(mm)) { 8968c2ecf20Sopenharmony_ci ret = -ESRCH; 8978c2ecf20Sopenharmony_ci goto out_put_task; 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci memset(&mss, 0, sizeof(mss)); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci ret = mmap_read_lock_killable(mm); 9038c2ecf20Sopenharmony_ci if (ret) 9048c2ecf20Sopenharmony_ci goto out_put_mm; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci hold_task_mempolicy(priv); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci for (vma = priv->mm->mmap; vma;) { 9098c2ecf20Sopenharmony_ci smap_gather_stats(vma, &mss, 0); 9108c2ecf20Sopenharmony_ci last_vma_end = vma->vm_end; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci /* 9138c2ecf20Sopenharmony_ci * Release mmap_lock temporarily if someone wants to 9148c2ecf20Sopenharmony_ci * access it for write request. 9158c2ecf20Sopenharmony_ci */ 9168c2ecf20Sopenharmony_ci if (mmap_lock_is_contended(mm)) { 9178c2ecf20Sopenharmony_ci mmap_read_unlock(mm); 9188c2ecf20Sopenharmony_ci ret = mmap_read_lock_killable(mm); 9198c2ecf20Sopenharmony_ci if (ret) { 9208c2ecf20Sopenharmony_ci release_task_mempolicy(priv); 9218c2ecf20Sopenharmony_ci goto out_put_mm; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci /* 9258c2ecf20Sopenharmony_ci * After dropping the lock, there are four cases to 9268c2ecf20Sopenharmony_ci * consider. See the following example for explanation. 9278c2ecf20Sopenharmony_ci * 9288c2ecf20Sopenharmony_ci * +------+------+-----------+ 9298c2ecf20Sopenharmony_ci * | VMA1 | VMA2 | VMA3 | 9308c2ecf20Sopenharmony_ci * +------+------+-----------+ 9318c2ecf20Sopenharmony_ci * | | | | 9328c2ecf20Sopenharmony_ci * 4k 8k 16k 400k 9338c2ecf20Sopenharmony_ci * 9348c2ecf20Sopenharmony_ci * Suppose we drop the lock after reading VMA2 due to 9358c2ecf20Sopenharmony_ci * contention, then we get: 9368c2ecf20Sopenharmony_ci * 9378c2ecf20Sopenharmony_ci * last_vma_end = 16k 9388c2ecf20Sopenharmony_ci * 9398c2ecf20Sopenharmony_ci * 1) VMA2 is freed, but VMA3 exists: 9408c2ecf20Sopenharmony_ci * 9418c2ecf20Sopenharmony_ci * find_vma(mm, 16k - 1) will return VMA3. 9428c2ecf20Sopenharmony_ci * In this case, just continue from VMA3. 9438c2ecf20Sopenharmony_ci * 9448c2ecf20Sopenharmony_ci * 2) VMA2 still exists: 9458c2ecf20Sopenharmony_ci * 9468c2ecf20Sopenharmony_ci * find_vma(mm, 16k - 1) will return VMA2. 9478c2ecf20Sopenharmony_ci * Iterate the loop like the original one. 9488c2ecf20Sopenharmony_ci * 9498c2ecf20Sopenharmony_ci * 3) No more VMAs can be found: 9508c2ecf20Sopenharmony_ci * 9518c2ecf20Sopenharmony_ci * find_vma(mm, 16k - 1) will return NULL. 9528c2ecf20Sopenharmony_ci * No more things to do, just break. 9538c2ecf20Sopenharmony_ci * 9548c2ecf20Sopenharmony_ci * 4) (last_vma_end - 1) is the middle of a vma (VMA'): 9558c2ecf20Sopenharmony_ci * 9568c2ecf20Sopenharmony_ci * find_vma(mm, 16k - 1) will return VMA' whose range 9578c2ecf20Sopenharmony_ci * contains last_vma_end. 9588c2ecf20Sopenharmony_ci * Iterate VMA' from last_vma_end. 9598c2ecf20Sopenharmony_ci */ 9608c2ecf20Sopenharmony_ci vma = find_vma(mm, last_vma_end - 1); 9618c2ecf20Sopenharmony_ci /* Case 3 above */ 9628c2ecf20Sopenharmony_ci if (!vma) 9638c2ecf20Sopenharmony_ci break; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci /* Case 1 above */ 9668c2ecf20Sopenharmony_ci if (vma->vm_start >= last_vma_end) 9678c2ecf20Sopenharmony_ci continue; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci /* Case 4 above */ 9708c2ecf20Sopenharmony_ci if (vma->vm_end > last_vma_end) 9718c2ecf20Sopenharmony_ci smap_gather_stats(vma, &mss, last_vma_end); 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci /* Case 2 above */ 9748c2ecf20Sopenharmony_ci vma = vma->vm_next; 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci show_vma_header_prefix(m, priv->mm->mmap ? priv->mm->mmap->vm_start : 0, 9788c2ecf20Sopenharmony_ci last_vma_end, 0, 0, 0, 0); 9798c2ecf20Sopenharmony_ci seq_pad(m, ' '); 9808c2ecf20Sopenharmony_ci seq_puts(m, "[rollup]\n"); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci __show_smap(m, &mss, true); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci release_task_mempolicy(priv); 9858c2ecf20Sopenharmony_ci mmap_read_unlock(mm); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ciout_put_mm: 9888c2ecf20Sopenharmony_ci mmput(mm); 9898c2ecf20Sopenharmony_ciout_put_task: 9908c2ecf20Sopenharmony_ci put_task_struct(priv->task); 9918c2ecf20Sopenharmony_ci priv->task = NULL; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci return ret; 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci#undef SEQ_PUT_DEC 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cistatic const struct seq_operations proc_pid_smaps_op = { 9988c2ecf20Sopenharmony_ci .start = m_start, 9998c2ecf20Sopenharmony_ci .next = m_next, 10008c2ecf20Sopenharmony_ci .stop = m_stop, 10018c2ecf20Sopenharmony_ci .show = show_smap 10028c2ecf20Sopenharmony_ci}; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cistatic int pid_smaps_open(struct inode *inode, struct file *file) 10058c2ecf20Sopenharmony_ci{ 10068c2ecf20Sopenharmony_ci return do_maps_open(inode, file, &proc_pid_smaps_op); 10078c2ecf20Sopenharmony_ci} 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_cistatic int smaps_rollup_open(struct inode *inode, struct file *file) 10108c2ecf20Sopenharmony_ci{ 10118c2ecf20Sopenharmony_ci int ret; 10128c2ecf20Sopenharmony_ci struct proc_maps_private *priv; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL_ACCOUNT); 10158c2ecf20Sopenharmony_ci if (!priv) 10168c2ecf20Sopenharmony_ci return -ENOMEM; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci ret = single_open(file, show_smaps_rollup, priv); 10198c2ecf20Sopenharmony_ci if (ret) 10208c2ecf20Sopenharmony_ci goto out_free; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci priv->inode = inode; 10238c2ecf20Sopenharmony_ci priv->mm = proc_mem_open(inode, PTRACE_MODE_READ); 10248c2ecf20Sopenharmony_ci if (IS_ERR(priv->mm)) { 10258c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->mm); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci single_release(inode, file); 10288c2ecf20Sopenharmony_ci goto out_free; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci return 0; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ciout_free: 10348c2ecf20Sopenharmony_ci kfree(priv); 10358c2ecf20Sopenharmony_ci return ret; 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_cistatic int smaps_rollup_release(struct inode *inode, struct file *file) 10398c2ecf20Sopenharmony_ci{ 10408c2ecf20Sopenharmony_ci struct seq_file *seq = file->private_data; 10418c2ecf20Sopenharmony_ci struct proc_maps_private *priv = seq->private; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci if (priv->mm) 10448c2ecf20Sopenharmony_ci mmdrop(priv->mm); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci kfree(priv); 10478c2ecf20Sopenharmony_ci return single_release(inode, file); 10488c2ecf20Sopenharmony_ci} 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ciconst struct file_operations proc_pid_smaps_operations = { 10518c2ecf20Sopenharmony_ci .open = pid_smaps_open, 10528c2ecf20Sopenharmony_ci .read = seq_read, 10538c2ecf20Sopenharmony_ci .llseek = seq_lseek, 10548c2ecf20Sopenharmony_ci .release = proc_map_release, 10558c2ecf20Sopenharmony_ci}; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ciconst struct file_operations proc_pid_smaps_rollup_operations = { 10588c2ecf20Sopenharmony_ci .open = smaps_rollup_open, 10598c2ecf20Sopenharmony_ci .read = seq_read, 10608c2ecf20Sopenharmony_ci .llseek = seq_lseek, 10618c2ecf20Sopenharmony_ci .release = smaps_rollup_release, 10628c2ecf20Sopenharmony_ci}; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_cienum clear_refs_types { 10658c2ecf20Sopenharmony_ci CLEAR_REFS_ALL = 1, 10668c2ecf20Sopenharmony_ci CLEAR_REFS_ANON, 10678c2ecf20Sopenharmony_ci CLEAR_REFS_MAPPED, 10688c2ecf20Sopenharmony_ci CLEAR_REFS_SOFT_DIRTY, 10698c2ecf20Sopenharmony_ci CLEAR_REFS_MM_HIWATER_RSS, 10708c2ecf20Sopenharmony_ci CLEAR_REFS_LAST, 10718c2ecf20Sopenharmony_ci}; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistruct clear_refs_private { 10748c2ecf20Sopenharmony_ci enum clear_refs_types type; 10758c2ecf20Sopenharmony_ci}; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci#ifdef CONFIG_MEM_SOFT_DIRTY 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci#define is_cow_mapping(flags) (((flags) & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE) 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_cistatic inline bool pte_is_pinned(struct vm_area_struct *vma, unsigned long addr, pte_t pte) 10828c2ecf20Sopenharmony_ci{ 10838c2ecf20Sopenharmony_ci struct page *page; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci if (!pte_write(pte)) 10868c2ecf20Sopenharmony_ci return false; 10878c2ecf20Sopenharmony_ci if (!is_cow_mapping(vma->vm_flags)) 10888c2ecf20Sopenharmony_ci return false; 10898c2ecf20Sopenharmony_ci if (likely(!atomic_read(&vma->vm_mm->has_pinned))) 10908c2ecf20Sopenharmony_ci return false; 10918c2ecf20Sopenharmony_ci page = vm_normal_page(vma, addr, pte); 10928c2ecf20Sopenharmony_ci if (!page) 10938c2ecf20Sopenharmony_ci return false; 10948c2ecf20Sopenharmony_ci return page_maybe_dma_pinned(page); 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_cistatic inline void clear_soft_dirty(struct vm_area_struct *vma, 10988c2ecf20Sopenharmony_ci unsigned long addr, pte_t *pte) 10998c2ecf20Sopenharmony_ci{ 11008c2ecf20Sopenharmony_ci /* 11018c2ecf20Sopenharmony_ci * The soft-dirty tracker uses #PF-s to catch writes 11028c2ecf20Sopenharmony_ci * to pages, so write-protect the pte as well. See the 11038c2ecf20Sopenharmony_ci * Documentation/admin-guide/mm/soft-dirty.rst for full description 11048c2ecf20Sopenharmony_ci * of how soft-dirty works. 11058c2ecf20Sopenharmony_ci */ 11068c2ecf20Sopenharmony_ci pte_t ptent = *pte; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci if (pte_present(ptent)) { 11098c2ecf20Sopenharmony_ci pte_t old_pte; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if (pte_is_pinned(vma, addr, ptent)) 11128c2ecf20Sopenharmony_ci return; 11138c2ecf20Sopenharmony_ci old_pte = ptep_modify_prot_start(vma, addr, pte); 11148c2ecf20Sopenharmony_ci ptent = pte_wrprotect(old_pte); 11158c2ecf20Sopenharmony_ci ptent = pte_clear_soft_dirty(ptent); 11168c2ecf20Sopenharmony_ci ptep_modify_prot_commit(vma, addr, pte, old_pte, ptent); 11178c2ecf20Sopenharmony_ci } else if (is_swap_pte(ptent)) { 11188c2ecf20Sopenharmony_ci ptent = pte_swp_clear_soft_dirty(ptent); 11198c2ecf20Sopenharmony_ci set_pte_at(vma->vm_mm, addr, pte, ptent); 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci} 11228c2ecf20Sopenharmony_ci#else 11238c2ecf20Sopenharmony_cistatic inline void clear_soft_dirty(struct vm_area_struct *vma, 11248c2ecf20Sopenharmony_ci unsigned long addr, pte_t *pte) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci} 11278c2ecf20Sopenharmony_ci#endif 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci#if defined(CONFIG_MEM_SOFT_DIRTY) && defined(CONFIG_TRANSPARENT_HUGEPAGE) 11308c2ecf20Sopenharmony_cistatic inline void clear_soft_dirty_pmd(struct vm_area_struct *vma, 11318c2ecf20Sopenharmony_ci unsigned long addr, pmd_t *pmdp) 11328c2ecf20Sopenharmony_ci{ 11338c2ecf20Sopenharmony_ci pmd_t old, pmd = *pmdp; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (pmd_present(pmd)) { 11368c2ecf20Sopenharmony_ci /* See comment in change_huge_pmd() */ 11378c2ecf20Sopenharmony_ci old = pmdp_invalidate(vma, addr, pmdp); 11388c2ecf20Sopenharmony_ci if (pmd_dirty(old)) 11398c2ecf20Sopenharmony_ci pmd = pmd_mkdirty(pmd); 11408c2ecf20Sopenharmony_ci if (pmd_young(old)) 11418c2ecf20Sopenharmony_ci pmd = pmd_mkyoung(pmd); 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci pmd = pmd_wrprotect(pmd); 11448c2ecf20Sopenharmony_ci pmd = pmd_clear_soft_dirty(pmd); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci set_pmd_at(vma->vm_mm, addr, pmdp, pmd); 11478c2ecf20Sopenharmony_ci } else if (is_migration_entry(pmd_to_swp_entry(pmd))) { 11488c2ecf20Sopenharmony_ci pmd = pmd_swp_clear_soft_dirty(pmd); 11498c2ecf20Sopenharmony_ci set_pmd_at(vma->vm_mm, addr, pmdp, pmd); 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci} 11528c2ecf20Sopenharmony_ci#else 11538c2ecf20Sopenharmony_cistatic inline void clear_soft_dirty_pmd(struct vm_area_struct *vma, 11548c2ecf20Sopenharmony_ci unsigned long addr, pmd_t *pmdp) 11558c2ecf20Sopenharmony_ci{ 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci#endif 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_cistatic int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, 11608c2ecf20Sopenharmony_ci unsigned long end, struct mm_walk *walk) 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci struct clear_refs_private *cp = walk->private; 11638c2ecf20Sopenharmony_ci struct vm_area_struct *vma = walk->vma; 11648c2ecf20Sopenharmony_ci pte_t *pte, ptent; 11658c2ecf20Sopenharmony_ci spinlock_t *ptl; 11668c2ecf20Sopenharmony_ci struct page *page; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci ptl = pmd_trans_huge_lock(pmd, vma); 11698c2ecf20Sopenharmony_ci if (ptl) { 11708c2ecf20Sopenharmony_ci if (cp->type == CLEAR_REFS_SOFT_DIRTY) { 11718c2ecf20Sopenharmony_ci clear_soft_dirty_pmd(vma, addr, pmd); 11728c2ecf20Sopenharmony_ci goto out; 11738c2ecf20Sopenharmony_ci } 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci if (!pmd_present(*pmd)) 11768c2ecf20Sopenharmony_ci goto out; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci page = pmd_page(*pmd); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci /* Clear accessed and referenced bits. */ 11818c2ecf20Sopenharmony_ci pmdp_test_and_clear_young(vma, addr, pmd); 11828c2ecf20Sopenharmony_ci test_and_clear_page_young(page); 11838c2ecf20Sopenharmony_ci ClearPageReferenced(page); 11848c2ecf20Sopenharmony_ciout: 11858c2ecf20Sopenharmony_ci spin_unlock(ptl); 11868c2ecf20Sopenharmony_ci return 0; 11878c2ecf20Sopenharmony_ci } 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci if (pmd_trans_unstable(pmd)) 11908c2ecf20Sopenharmony_ci return 0; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); 11938c2ecf20Sopenharmony_ci for (; addr != end; pte++, addr += PAGE_SIZE) { 11948c2ecf20Sopenharmony_ci ptent = *pte; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci if (cp->type == CLEAR_REFS_SOFT_DIRTY) { 11978c2ecf20Sopenharmony_ci clear_soft_dirty(vma, addr, pte); 11988c2ecf20Sopenharmony_ci continue; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci if (!pte_present(ptent)) 12028c2ecf20Sopenharmony_ci continue; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci page = vm_normal_page(vma, addr, ptent); 12058c2ecf20Sopenharmony_ci if (!page) 12068c2ecf20Sopenharmony_ci continue; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci /* Clear accessed and referenced bits. */ 12098c2ecf20Sopenharmony_ci ptep_test_and_clear_young(vma, addr, pte); 12108c2ecf20Sopenharmony_ci test_and_clear_page_young(page); 12118c2ecf20Sopenharmony_ci ClearPageReferenced(page); 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci pte_unmap_unlock(pte - 1, ptl); 12148c2ecf20Sopenharmony_ci cond_resched(); 12158c2ecf20Sopenharmony_ci return 0; 12168c2ecf20Sopenharmony_ci} 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_cistatic int clear_refs_test_walk(unsigned long start, unsigned long end, 12198c2ecf20Sopenharmony_ci struct mm_walk *walk) 12208c2ecf20Sopenharmony_ci{ 12218c2ecf20Sopenharmony_ci struct clear_refs_private *cp = walk->private; 12228c2ecf20Sopenharmony_ci struct vm_area_struct *vma = walk->vma; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_PFNMAP) 12258c2ecf20Sopenharmony_ci return 1; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci /* 12288c2ecf20Sopenharmony_ci * Writing 1 to /proc/pid/clear_refs affects all pages. 12298c2ecf20Sopenharmony_ci * Writing 2 to /proc/pid/clear_refs only affects anonymous pages. 12308c2ecf20Sopenharmony_ci * Writing 3 to /proc/pid/clear_refs only affects file mapped pages. 12318c2ecf20Sopenharmony_ci * Writing 4 to /proc/pid/clear_refs affects all pages. 12328c2ecf20Sopenharmony_ci */ 12338c2ecf20Sopenharmony_ci if (cp->type == CLEAR_REFS_ANON && vma->vm_file) 12348c2ecf20Sopenharmony_ci return 1; 12358c2ecf20Sopenharmony_ci if (cp->type == CLEAR_REFS_MAPPED && !vma->vm_file) 12368c2ecf20Sopenharmony_ci return 1; 12378c2ecf20Sopenharmony_ci return 0; 12388c2ecf20Sopenharmony_ci} 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_cistatic const struct mm_walk_ops clear_refs_walk_ops = { 12418c2ecf20Sopenharmony_ci .pmd_entry = clear_refs_pte_range, 12428c2ecf20Sopenharmony_ci .test_walk = clear_refs_test_walk, 12438c2ecf20Sopenharmony_ci}; 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_cistatic ssize_t clear_refs_write(struct file *file, const char __user *buf, 12468c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 12478c2ecf20Sopenharmony_ci{ 12488c2ecf20Sopenharmony_ci struct task_struct *task; 12498c2ecf20Sopenharmony_ci char buffer[PROC_NUMBUF]; 12508c2ecf20Sopenharmony_ci struct mm_struct *mm; 12518c2ecf20Sopenharmony_ci struct vm_area_struct *vma; 12528c2ecf20Sopenharmony_ci enum clear_refs_types type; 12538c2ecf20Sopenharmony_ci int itype; 12548c2ecf20Sopenharmony_ci int rv; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci memset(buffer, 0, sizeof(buffer)); 12578c2ecf20Sopenharmony_ci if (count > sizeof(buffer) - 1) 12588c2ecf20Sopenharmony_ci count = sizeof(buffer) - 1; 12598c2ecf20Sopenharmony_ci if (copy_from_user(buffer, buf, count)) 12608c2ecf20Sopenharmony_ci return -EFAULT; 12618c2ecf20Sopenharmony_ci rv = kstrtoint(strstrip(buffer), 10, &itype); 12628c2ecf20Sopenharmony_ci if (rv < 0) 12638c2ecf20Sopenharmony_ci return rv; 12648c2ecf20Sopenharmony_ci type = (enum clear_refs_types)itype; 12658c2ecf20Sopenharmony_ci if (type < CLEAR_REFS_ALL || type >= CLEAR_REFS_LAST) 12668c2ecf20Sopenharmony_ci return -EINVAL; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci task = get_proc_task(file_inode(file)); 12698c2ecf20Sopenharmony_ci if (!task) 12708c2ecf20Sopenharmony_ci return -ESRCH; 12718c2ecf20Sopenharmony_ci mm = get_task_mm(task); 12728c2ecf20Sopenharmony_ci if (mm) { 12738c2ecf20Sopenharmony_ci struct mmu_notifier_range range; 12748c2ecf20Sopenharmony_ci struct clear_refs_private cp = { 12758c2ecf20Sopenharmony_ci .type = type, 12768c2ecf20Sopenharmony_ci }; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci if (mmap_write_lock_killable(mm)) { 12798c2ecf20Sopenharmony_ci count = -EINTR; 12808c2ecf20Sopenharmony_ci goto out_mm; 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci if (type == CLEAR_REFS_MM_HIWATER_RSS) { 12838c2ecf20Sopenharmony_ci /* 12848c2ecf20Sopenharmony_ci * Writing 5 to /proc/pid/clear_refs resets the peak 12858c2ecf20Sopenharmony_ci * resident set size to this mm's current rss value. 12868c2ecf20Sopenharmony_ci */ 12878c2ecf20Sopenharmony_ci reset_mm_hiwater_rss(mm); 12888c2ecf20Sopenharmony_ci goto out_unlock; 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci if (type == CLEAR_REFS_SOFT_DIRTY) { 12928c2ecf20Sopenharmony_ci for (vma = mm->mmap; vma; vma = vma->vm_next) { 12938c2ecf20Sopenharmony_ci if (!(vma->vm_flags & VM_SOFTDIRTY)) 12948c2ecf20Sopenharmony_ci continue; 12958c2ecf20Sopenharmony_ci vma->vm_flags &= ~VM_SOFTDIRTY; 12968c2ecf20Sopenharmony_ci vma_set_page_prot(vma); 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci inc_tlb_flush_pending(mm); 13008c2ecf20Sopenharmony_ci mmu_notifier_range_init(&range, MMU_NOTIFY_SOFT_DIRTY, 13018c2ecf20Sopenharmony_ci 0, NULL, mm, 0, -1UL); 13028c2ecf20Sopenharmony_ci mmu_notifier_invalidate_range_start(&range); 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci walk_page_range(mm, 0, mm->highest_vm_end, &clear_refs_walk_ops, 13058c2ecf20Sopenharmony_ci &cp); 13068c2ecf20Sopenharmony_ci if (type == CLEAR_REFS_SOFT_DIRTY) { 13078c2ecf20Sopenharmony_ci mmu_notifier_invalidate_range_end(&range); 13088c2ecf20Sopenharmony_ci flush_tlb_mm(mm); 13098c2ecf20Sopenharmony_ci dec_tlb_flush_pending(mm); 13108c2ecf20Sopenharmony_ci } 13118c2ecf20Sopenharmony_ciout_unlock: 13128c2ecf20Sopenharmony_ci mmap_write_unlock(mm); 13138c2ecf20Sopenharmony_ciout_mm: 13148c2ecf20Sopenharmony_ci mmput(mm); 13158c2ecf20Sopenharmony_ci } 13168c2ecf20Sopenharmony_ci put_task_struct(task); 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci return count; 13198c2ecf20Sopenharmony_ci} 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ciconst struct file_operations proc_clear_refs_operations = { 13228c2ecf20Sopenharmony_ci .write = clear_refs_write, 13238c2ecf20Sopenharmony_ci .llseek = noop_llseek, 13248c2ecf20Sopenharmony_ci}; 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_citypedef struct { 13278c2ecf20Sopenharmony_ci u64 pme; 13288c2ecf20Sopenharmony_ci} pagemap_entry_t; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_cistruct pagemapread { 13318c2ecf20Sopenharmony_ci int pos, len; /* units: PM_ENTRY_BYTES, not bytes */ 13328c2ecf20Sopenharmony_ci pagemap_entry_t *buffer; 13338c2ecf20Sopenharmony_ci bool show_pfn; 13348c2ecf20Sopenharmony_ci}; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci#define PAGEMAP_WALK_SIZE (PMD_SIZE) 13378c2ecf20Sopenharmony_ci#define PAGEMAP_WALK_MASK (PMD_MASK) 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci#define PM_ENTRY_BYTES sizeof(pagemap_entry_t) 13408c2ecf20Sopenharmony_ci#define PM_PFRAME_BITS 55 13418c2ecf20Sopenharmony_ci#define PM_PFRAME_MASK GENMASK_ULL(PM_PFRAME_BITS - 1, 0) 13428c2ecf20Sopenharmony_ci#define PM_SOFT_DIRTY BIT_ULL(55) 13438c2ecf20Sopenharmony_ci#define PM_MMAP_EXCLUSIVE BIT_ULL(56) 13448c2ecf20Sopenharmony_ci#define PM_FILE BIT_ULL(61) 13458c2ecf20Sopenharmony_ci#define PM_SWAP BIT_ULL(62) 13468c2ecf20Sopenharmony_ci#define PM_PRESENT BIT_ULL(63) 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci#define PM_END_OF_BUFFER 1 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_cistatic inline pagemap_entry_t make_pme(u64 frame, u64 flags) 13518c2ecf20Sopenharmony_ci{ 13528c2ecf20Sopenharmony_ci return (pagemap_entry_t) { .pme = (frame & PM_PFRAME_MASK) | flags }; 13538c2ecf20Sopenharmony_ci} 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_cistatic int add_to_pagemap(unsigned long addr, pagemap_entry_t *pme, 13568c2ecf20Sopenharmony_ci struct pagemapread *pm) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci pm->buffer[pm->pos++] = *pme; 13598c2ecf20Sopenharmony_ci if (pm->pos >= pm->len) 13608c2ecf20Sopenharmony_ci return PM_END_OF_BUFFER; 13618c2ecf20Sopenharmony_ci return 0; 13628c2ecf20Sopenharmony_ci} 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_cistatic int pagemap_pte_hole(unsigned long start, unsigned long end, 13658c2ecf20Sopenharmony_ci __always_unused int depth, struct mm_walk *walk) 13668c2ecf20Sopenharmony_ci{ 13678c2ecf20Sopenharmony_ci struct pagemapread *pm = walk->private; 13688c2ecf20Sopenharmony_ci unsigned long addr = start; 13698c2ecf20Sopenharmony_ci int err = 0; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci while (addr < end) { 13728c2ecf20Sopenharmony_ci struct vm_area_struct *vma = find_vma(walk->mm, addr); 13738c2ecf20Sopenharmony_ci pagemap_entry_t pme = make_pme(0, 0); 13748c2ecf20Sopenharmony_ci /* End of address space hole, which we mark as non-present. */ 13758c2ecf20Sopenharmony_ci unsigned long hole_end; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci if (vma) 13788c2ecf20Sopenharmony_ci hole_end = min(end, vma->vm_start); 13798c2ecf20Sopenharmony_ci else 13808c2ecf20Sopenharmony_ci hole_end = end; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci for (; addr < hole_end; addr += PAGE_SIZE) { 13838c2ecf20Sopenharmony_ci err = add_to_pagemap(addr, &pme, pm); 13848c2ecf20Sopenharmony_ci if (err) 13858c2ecf20Sopenharmony_ci goto out; 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci if (!vma) 13898c2ecf20Sopenharmony_ci break; 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci /* Addresses in the VMA. */ 13928c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_SOFTDIRTY) 13938c2ecf20Sopenharmony_ci pme = make_pme(0, PM_SOFT_DIRTY); 13948c2ecf20Sopenharmony_ci for (; addr < min(end, vma->vm_end); addr += PAGE_SIZE) { 13958c2ecf20Sopenharmony_ci err = add_to_pagemap(addr, &pme, pm); 13968c2ecf20Sopenharmony_ci if (err) 13978c2ecf20Sopenharmony_ci goto out; 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci } 14008c2ecf20Sopenharmony_ciout: 14018c2ecf20Sopenharmony_ci return err; 14028c2ecf20Sopenharmony_ci} 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_cistatic pagemap_entry_t pte_to_pagemap_entry(struct pagemapread *pm, 14058c2ecf20Sopenharmony_ci struct vm_area_struct *vma, unsigned long addr, pte_t pte) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci u64 frame = 0, flags = 0; 14088c2ecf20Sopenharmony_ci struct page *page = NULL; 14098c2ecf20Sopenharmony_ci bool migration = false; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci if (pte_present(pte)) { 14128c2ecf20Sopenharmony_ci if (pm->show_pfn) 14138c2ecf20Sopenharmony_ci frame = pte_pfn(pte); 14148c2ecf20Sopenharmony_ci flags |= PM_PRESENT; 14158c2ecf20Sopenharmony_ci page = vm_normal_page(vma, addr, pte); 14168c2ecf20Sopenharmony_ci if (pte_soft_dirty(pte)) 14178c2ecf20Sopenharmony_ci flags |= PM_SOFT_DIRTY; 14188c2ecf20Sopenharmony_ci } else if (is_swap_pte(pte)) { 14198c2ecf20Sopenharmony_ci swp_entry_t entry; 14208c2ecf20Sopenharmony_ci if (pte_swp_soft_dirty(pte)) 14218c2ecf20Sopenharmony_ci flags |= PM_SOFT_DIRTY; 14228c2ecf20Sopenharmony_ci entry = pte_to_swp_entry(pte); 14238c2ecf20Sopenharmony_ci if (pm->show_pfn) 14248c2ecf20Sopenharmony_ci frame = swp_type(entry) | 14258c2ecf20Sopenharmony_ci (swp_offset(entry) << MAX_SWAPFILES_SHIFT); 14268c2ecf20Sopenharmony_ci flags |= PM_SWAP; 14278c2ecf20Sopenharmony_ci if (is_migration_entry(entry)) { 14288c2ecf20Sopenharmony_ci migration = true; 14298c2ecf20Sopenharmony_ci page = migration_entry_to_page(entry); 14308c2ecf20Sopenharmony_ci } 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci if (is_device_private_entry(entry)) 14338c2ecf20Sopenharmony_ci page = device_private_entry_to_page(entry); 14348c2ecf20Sopenharmony_ci } 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci if (page && !PageAnon(page)) 14378c2ecf20Sopenharmony_ci flags |= PM_FILE; 14388c2ecf20Sopenharmony_ci if (page && !migration && page_mapcount(page) == 1) 14398c2ecf20Sopenharmony_ci flags |= PM_MMAP_EXCLUSIVE; 14408c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_SOFTDIRTY) 14418c2ecf20Sopenharmony_ci flags |= PM_SOFT_DIRTY; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci return make_pme(frame, flags); 14448c2ecf20Sopenharmony_ci} 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_cistatic int pagemap_pmd_range(pmd_t *pmdp, unsigned long addr, unsigned long end, 14478c2ecf20Sopenharmony_ci struct mm_walk *walk) 14488c2ecf20Sopenharmony_ci{ 14498c2ecf20Sopenharmony_ci struct vm_area_struct *vma = walk->vma; 14508c2ecf20Sopenharmony_ci struct pagemapread *pm = walk->private; 14518c2ecf20Sopenharmony_ci spinlock_t *ptl; 14528c2ecf20Sopenharmony_ci pte_t *pte, *orig_pte; 14538c2ecf20Sopenharmony_ci int err = 0; 14548c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 14558c2ecf20Sopenharmony_ci bool migration = false; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci ptl = pmd_trans_huge_lock(pmdp, vma); 14588c2ecf20Sopenharmony_ci if (ptl) { 14598c2ecf20Sopenharmony_ci u64 flags = 0, frame = 0; 14608c2ecf20Sopenharmony_ci pmd_t pmd = *pmdp; 14618c2ecf20Sopenharmony_ci struct page *page = NULL; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_SOFTDIRTY) 14648c2ecf20Sopenharmony_ci flags |= PM_SOFT_DIRTY; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci if (pmd_present(pmd)) { 14678c2ecf20Sopenharmony_ci page = pmd_page(pmd); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci flags |= PM_PRESENT; 14708c2ecf20Sopenharmony_ci if (pmd_soft_dirty(pmd)) 14718c2ecf20Sopenharmony_ci flags |= PM_SOFT_DIRTY; 14728c2ecf20Sopenharmony_ci if (pm->show_pfn) 14738c2ecf20Sopenharmony_ci frame = pmd_pfn(pmd) + 14748c2ecf20Sopenharmony_ci ((addr & ~PMD_MASK) >> PAGE_SHIFT); 14758c2ecf20Sopenharmony_ci } 14768c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_ENABLE_THP_MIGRATION 14778c2ecf20Sopenharmony_ci else if (is_swap_pmd(pmd)) { 14788c2ecf20Sopenharmony_ci swp_entry_t entry = pmd_to_swp_entry(pmd); 14798c2ecf20Sopenharmony_ci unsigned long offset; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci if (pm->show_pfn) { 14828c2ecf20Sopenharmony_ci offset = swp_offset(entry) + 14838c2ecf20Sopenharmony_ci ((addr & ~PMD_MASK) >> PAGE_SHIFT); 14848c2ecf20Sopenharmony_ci frame = swp_type(entry) | 14858c2ecf20Sopenharmony_ci (offset << MAX_SWAPFILES_SHIFT); 14868c2ecf20Sopenharmony_ci } 14878c2ecf20Sopenharmony_ci flags |= PM_SWAP; 14888c2ecf20Sopenharmony_ci if (pmd_swp_soft_dirty(pmd)) 14898c2ecf20Sopenharmony_ci flags |= PM_SOFT_DIRTY; 14908c2ecf20Sopenharmony_ci VM_BUG_ON(!is_pmd_migration_entry(pmd)); 14918c2ecf20Sopenharmony_ci migration = is_migration_entry(entry); 14928c2ecf20Sopenharmony_ci page = migration_entry_to_page(entry); 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci#endif 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci if (page && !migration && page_mapcount(page) == 1) 14978c2ecf20Sopenharmony_ci flags |= PM_MMAP_EXCLUSIVE; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci for (; addr != end; addr += PAGE_SIZE) { 15008c2ecf20Sopenharmony_ci pagemap_entry_t pme = make_pme(frame, flags); 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci err = add_to_pagemap(addr, &pme, pm); 15038c2ecf20Sopenharmony_ci if (err) 15048c2ecf20Sopenharmony_ci break; 15058c2ecf20Sopenharmony_ci if (pm->show_pfn) { 15068c2ecf20Sopenharmony_ci if (flags & PM_PRESENT) 15078c2ecf20Sopenharmony_ci frame++; 15088c2ecf20Sopenharmony_ci else if (flags & PM_SWAP) 15098c2ecf20Sopenharmony_ci frame += (1 << MAX_SWAPFILES_SHIFT); 15108c2ecf20Sopenharmony_ci } 15118c2ecf20Sopenharmony_ci } 15128c2ecf20Sopenharmony_ci spin_unlock(ptl); 15138c2ecf20Sopenharmony_ci return err; 15148c2ecf20Sopenharmony_ci } 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci if (pmd_trans_unstable(pmdp)) 15178c2ecf20Sopenharmony_ci return 0; 15188c2ecf20Sopenharmony_ci#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci /* 15218c2ecf20Sopenharmony_ci * We can assume that @vma always points to a valid one and @end never 15228c2ecf20Sopenharmony_ci * goes beyond vma->vm_end. 15238c2ecf20Sopenharmony_ci */ 15248c2ecf20Sopenharmony_ci orig_pte = pte = pte_offset_map_lock(walk->mm, pmdp, addr, &ptl); 15258c2ecf20Sopenharmony_ci for (; addr < end; pte++, addr += PAGE_SIZE) { 15268c2ecf20Sopenharmony_ci pagemap_entry_t pme; 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci pme = pte_to_pagemap_entry(pm, vma, addr, *pte); 15298c2ecf20Sopenharmony_ci err = add_to_pagemap(addr, &pme, pm); 15308c2ecf20Sopenharmony_ci if (err) 15318c2ecf20Sopenharmony_ci break; 15328c2ecf20Sopenharmony_ci } 15338c2ecf20Sopenharmony_ci pte_unmap_unlock(orig_pte, ptl); 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci cond_resched(); 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci return err; 15388c2ecf20Sopenharmony_ci} 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci#ifdef CONFIG_HUGETLB_PAGE 15418c2ecf20Sopenharmony_ci/* This function walks within one hugetlb entry in the single call */ 15428c2ecf20Sopenharmony_cistatic int pagemap_hugetlb_range(pte_t *ptep, unsigned long hmask, 15438c2ecf20Sopenharmony_ci unsigned long addr, unsigned long end, 15448c2ecf20Sopenharmony_ci struct mm_walk *walk) 15458c2ecf20Sopenharmony_ci{ 15468c2ecf20Sopenharmony_ci struct pagemapread *pm = walk->private; 15478c2ecf20Sopenharmony_ci struct vm_area_struct *vma = walk->vma; 15488c2ecf20Sopenharmony_ci u64 flags = 0, frame = 0; 15498c2ecf20Sopenharmony_ci int err = 0; 15508c2ecf20Sopenharmony_ci pte_t pte; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_SOFTDIRTY) 15538c2ecf20Sopenharmony_ci flags |= PM_SOFT_DIRTY; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci pte = huge_ptep_get(ptep); 15568c2ecf20Sopenharmony_ci if (pte_present(pte)) { 15578c2ecf20Sopenharmony_ci struct page *page = pte_page(pte); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci if (!PageAnon(page)) 15608c2ecf20Sopenharmony_ci flags |= PM_FILE; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci if (page_mapcount(page) == 1) 15638c2ecf20Sopenharmony_ci flags |= PM_MMAP_EXCLUSIVE; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci flags |= PM_PRESENT; 15668c2ecf20Sopenharmony_ci if (pm->show_pfn) 15678c2ecf20Sopenharmony_ci frame = pte_pfn(pte) + 15688c2ecf20Sopenharmony_ci ((addr & ~hmask) >> PAGE_SHIFT); 15698c2ecf20Sopenharmony_ci } 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci for (; addr != end; addr += PAGE_SIZE) { 15728c2ecf20Sopenharmony_ci pagemap_entry_t pme = make_pme(frame, flags); 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci err = add_to_pagemap(addr, &pme, pm); 15758c2ecf20Sopenharmony_ci if (err) 15768c2ecf20Sopenharmony_ci return err; 15778c2ecf20Sopenharmony_ci if (pm->show_pfn && (flags & PM_PRESENT)) 15788c2ecf20Sopenharmony_ci frame++; 15798c2ecf20Sopenharmony_ci } 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci cond_resched(); 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci return err; 15848c2ecf20Sopenharmony_ci} 15858c2ecf20Sopenharmony_ci#else 15868c2ecf20Sopenharmony_ci#define pagemap_hugetlb_range NULL 15878c2ecf20Sopenharmony_ci#endif /* HUGETLB_PAGE */ 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_cistatic const struct mm_walk_ops pagemap_ops = { 15908c2ecf20Sopenharmony_ci .pmd_entry = pagemap_pmd_range, 15918c2ecf20Sopenharmony_ci .pte_hole = pagemap_pte_hole, 15928c2ecf20Sopenharmony_ci .hugetlb_entry = pagemap_hugetlb_range, 15938c2ecf20Sopenharmony_ci}; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci/* 15968c2ecf20Sopenharmony_ci * /proc/pid/pagemap - an array mapping virtual pages to pfns 15978c2ecf20Sopenharmony_ci * 15988c2ecf20Sopenharmony_ci * For each page in the address space, this file contains one 64-bit entry 15998c2ecf20Sopenharmony_ci * consisting of the following: 16008c2ecf20Sopenharmony_ci * 16018c2ecf20Sopenharmony_ci * Bits 0-54 page frame number (PFN) if present 16028c2ecf20Sopenharmony_ci * Bits 0-4 swap type if swapped 16038c2ecf20Sopenharmony_ci * Bits 5-54 swap offset if swapped 16048c2ecf20Sopenharmony_ci * Bit 55 pte is soft-dirty (see Documentation/admin-guide/mm/soft-dirty.rst) 16058c2ecf20Sopenharmony_ci * Bit 56 page exclusively mapped 16068c2ecf20Sopenharmony_ci * Bits 57-60 zero 16078c2ecf20Sopenharmony_ci * Bit 61 page is file-page or shared-anon 16088c2ecf20Sopenharmony_ci * Bit 62 page swapped 16098c2ecf20Sopenharmony_ci * Bit 63 page present 16108c2ecf20Sopenharmony_ci * 16118c2ecf20Sopenharmony_ci * If the page is not present but in swap, then the PFN contains an 16128c2ecf20Sopenharmony_ci * encoding of the swap file number and the page's offset into the 16138c2ecf20Sopenharmony_ci * swap. Unmapped pages return a null PFN. This allows determining 16148c2ecf20Sopenharmony_ci * precisely which pages are mapped (or in swap) and comparing mapped 16158c2ecf20Sopenharmony_ci * pages between processes. 16168c2ecf20Sopenharmony_ci * 16178c2ecf20Sopenharmony_ci * Efficient users of this interface will use /proc/pid/maps to 16188c2ecf20Sopenharmony_ci * determine which areas of memory are actually mapped and llseek to 16198c2ecf20Sopenharmony_ci * skip over unmapped regions. 16208c2ecf20Sopenharmony_ci */ 16218c2ecf20Sopenharmony_cistatic ssize_t pagemap_read(struct file *file, char __user *buf, 16228c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 16238c2ecf20Sopenharmony_ci{ 16248c2ecf20Sopenharmony_ci struct mm_struct *mm = file->private_data; 16258c2ecf20Sopenharmony_ci struct pagemapread pm; 16268c2ecf20Sopenharmony_ci unsigned long src; 16278c2ecf20Sopenharmony_ci unsigned long svpfn; 16288c2ecf20Sopenharmony_ci unsigned long start_vaddr; 16298c2ecf20Sopenharmony_ci unsigned long end_vaddr; 16308c2ecf20Sopenharmony_ci int ret = 0, copied = 0; 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci if (!mm || !mmget_not_zero(mm)) 16338c2ecf20Sopenharmony_ci goto out; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci ret = -EINVAL; 16368c2ecf20Sopenharmony_ci /* file position must be aligned */ 16378c2ecf20Sopenharmony_ci if ((*ppos % PM_ENTRY_BYTES) || (count % PM_ENTRY_BYTES)) 16388c2ecf20Sopenharmony_ci goto out_mm; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci ret = 0; 16418c2ecf20Sopenharmony_ci if (!count) 16428c2ecf20Sopenharmony_ci goto out_mm; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci /* do not disclose physical addresses: attack vector */ 16458c2ecf20Sopenharmony_ci pm.show_pfn = file_ns_capable(file, &init_user_ns, CAP_SYS_ADMIN); 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci pm.len = (PAGEMAP_WALK_SIZE >> PAGE_SHIFT); 16488c2ecf20Sopenharmony_ci pm.buffer = kmalloc_array(pm.len, PM_ENTRY_BYTES, GFP_KERNEL); 16498c2ecf20Sopenharmony_ci ret = -ENOMEM; 16508c2ecf20Sopenharmony_ci if (!pm.buffer) 16518c2ecf20Sopenharmony_ci goto out_mm; 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci src = *ppos; 16548c2ecf20Sopenharmony_ci svpfn = src / PM_ENTRY_BYTES; 16558c2ecf20Sopenharmony_ci end_vaddr = mm->task_size; 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci /* watch out for wraparound */ 16588c2ecf20Sopenharmony_ci start_vaddr = end_vaddr; 16598c2ecf20Sopenharmony_ci if (svpfn <= (ULONG_MAX >> PAGE_SHIFT)) 16608c2ecf20Sopenharmony_ci start_vaddr = untagged_addr(svpfn << PAGE_SHIFT); 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci /* Ensure the address is inside the task */ 16638c2ecf20Sopenharmony_ci if (start_vaddr > mm->task_size) 16648c2ecf20Sopenharmony_ci start_vaddr = end_vaddr; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci /* 16678c2ecf20Sopenharmony_ci * The odds are that this will stop walking way 16688c2ecf20Sopenharmony_ci * before end_vaddr, because the length of the 16698c2ecf20Sopenharmony_ci * user buffer is tracked in "pm", and the walk 16708c2ecf20Sopenharmony_ci * will stop when we hit the end of the buffer. 16718c2ecf20Sopenharmony_ci */ 16728c2ecf20Sopenharmony_ci ret = 0; 16738c2ecf20Sopenharmony_ci while (count && (start_vaddr < end_vaddr)) { 16748c2ecf20Sopenharmony_ci int len; 16758c2ecf20Sopenharmony_ci unsigned long end; 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci pm.pos = 0; 16788c2ecf20Sopenharmony_ci end = (start_vaddr + PAGEMAP_WALK_SIZE) & PAGEMAP_WALK_MASK; 16798c2ecf20Sopenharmony_ci /* overflow ? */ 16808c2ecf20Sopenharmony_ci if (end < start_vaddr || end > end_vaddr) 16818c2ecf20Sopenharmony_ci end = end_vaddr; 16828c2ecf20Sopenharmony_ci ret = mmap_read_lock_killable(mm); 16838c2ecf20Sopenharmony_ci if (ret) 16848c2ecf20Sopenharmony_ci goto out_free; 16858c2ecf20Sopenharmony_ci ret = walk_page_range(mm, start_vaddr, end, &pagemap_ops, &pm); 16868c2ecf20Sopenharmony_ci mmap_read_unlock(mm); 16878c2ecf20Sopenharmony_ci start_vaddr = end; 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci len = min(count, PM_ENTRY_BYTES * pm.pos); 16908c2ecf20Sopenharmony_ci if (copy_to_user(buf, pm.buffer, len)) { 16918c2ecf20Sopenharmony_ci ret = -EFAULT; 16928c2ecf20Sopenharmony_ci goto out_free; 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci copied += len; 16958c2ecf20Sopenharmony_ci buf += len; 16968c2ecf20Sopenharmony_ci count -= len; 16978c2ecf20Sopenharmony_ci } 16988c2ecf20Sopenharmony_ci *ppos += copied; 16998c2ecf20Sopenharmony_ci if (!ret || ret == PM_END_OF_BUFFER) 17008c2ecf20Sopenharmony_ci ret = copied; 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ciout_free: 17038c2ecf20Sopenharmony_ci kfree(pm.buffer); 17048c2ecf20Sopenharmony_ciout_mm: 17058c2ecf20Sopenharmony_ci mmput(mm); 17068c2ecf20Sopenharmony_ciout: 17078c2ecf20Sopenharmony_ci return ret; 17088c2ecf20Sopenharmony_ci} 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_cistatic int pagemap_open(struct inode *inode, struct file *file) 17118c2ecf20Sopenharmony_ci{ 17128c2ecf20Sopenharmony_ci struct mm_struct *mm; 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci mm = proc_mem_open(inode, PTRACE_MODE_READ); 17158c2ecf20Sopenharmony_ci if (IS_ERR(mm)) 17168c2ecf20Sopenharmony_ci return PTR_ERR(mm); 17178c2ecf20Sopenharmony_ci file->private_data = mm; 17188c2ecf20Sopenharmony_ci return 0; 17198c2ecf20Sopenharmony_ci} 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_cistatic int pagemap_release(struct inode *inode, struct file *file) 17228c2ecf20Sopenharmony_ci{ 17238c2ecf20Sopenharmony_ci struct mm_struct *mm = file->private_data; 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci if (mm) 17268c2ecf20Sopenharmony_ci mmdrop(mm); 17278c2ecf20Sopenharmony_ci return 0; 17288c2ecf20Sopenharmony_ci} 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ciconst struct file_operations proc_pagemap_operations = { 17318c2ecf20Sopenharmony_ci .llseek = mem_lseek, /* borrow this */ 17328c2ecf20Sopenharmony_ci .read = pagemap_read, 17338c2ecf20Sopenharmony_ci .open = pagemap_open, 17348c2ecf20Sopenharmony_ci .release = pagemap_release, 17358c2ecf20Sopenharmony_ci}; 17368c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_PAGE_MONITOR */ 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci#ifdef CONFIG_NUMA 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_cistruct numa_maps { 17418c2ecf20Sopenharmony_ci unsigned long pages; 17428c2ecf20Sopenharmony_ci unsigned long anon; 17438c2ecf20Sopenharmony_ci unsigned long active; 17448c2ecf20Sopenharmony_ci unsigned long writeback; 17458c2ecf20Sopenharmony_ci unsigned long mapcount_max; 17468c2ecf20Sopenharmony_ci unsigned long dirty; 17478c2ecf20Sopenharmony_ci unsigned long swapcache; 17488c2ecf20Sopenharmony_ci unsigned long node[MAX_NUMNODES]; 17498c2ecf20Sopenharmony_ci}; 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_cistruct numa_maps_private { 17528c2ecf20Sopenharmony_ci struct proc_maps_private proc_maps; 17538c2ecf20Sopenharmony_ci struct numa_maps md; 17548c2ecf20Sopenharmony_ci}; 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_cistatic void gather_stats(struct page *page, struct numa_maps *md, int pte_dirty, 17578c2ecf20Sopenharmony_ci unsigned long nr_pages) 17588c2ecf20Sopenharmony_ci{ 17598c2ecf20Sopenharmony_ci int count = page_mapcount(page); 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci md->pages += nr_pages; 17628c2ecf20Sopenharmony_ci if (pte_dirty || PageDirty(page)) 17638c2ecf20Sopenharmony_ci md->dirty += nr_pages; 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci if (PageSwapCache(page)) 17668c2ecf20Sopenharmony_ci md->swapcache += nr_pages; 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci if (PageActive(page) || PageUnevictable(page)) 17698c2ecf20Sopenharmony_ci md->active += nr_pages; 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci if (PageWriteback(page)) 17728c2ecf20Sopenharmony_ci md->writeback += nr_pages; 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci if (PageAnon(page)) 17758c2ecf20Sopenharmony_ci md->anon += nr_pages; 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci if (count > md->mapcount_max) 17788c2ecf20Sopenharmony_ci md->mapcount_max = count; 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci md->node[page_to_nid(page)] += nr_pages; 17818c2ecf20Sopenharmony_ci} 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_cistatic struct page *can_gather_numa_stats(pte_t pte, struct vm_area_struct *vma, 17848c2ecf20Sopenharmony_ci unsigned long addr) 17858c2ecf20Sopenharmony_ci{ 17868c2ecf20Sopenharmony_ci struct page *page; 17878c2ecf20Sopenharmony_ci int nid; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci if (!pte_present(pte)) 17908c2ecf20Sopenharmony_ci return NULL; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci page = vm_normal_page(vma, addr, pte); 17938c2ecf20Sopenharmony_ci if (!page) 17948c2ecf20Sopenharmony_ci return NULL; 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci if (PageReserved(page)) 17978c2ecf20Sopenharmony_ci return NULL; 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci nid = page_to_nid(page); 18008c2ecf20Sopenharmony_ci if (!node_isset(nid, node_states[N_MEMORY])) 18018c2ecf20Sopenharmony_ci return NULL; 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci return page; 18048c2ecf20Sopenharmony_ci} 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 18078c2ecf20Sopenharmony_cistatic struct page *can_gather_numa_stats_pmd(pmd_t pmd, 18088c2ecf20Sopenharmony_ci struct vm_area_struct *vma, 18098c2ecf20Sopenharmony_ci unsigned long addr) 18108c2ecf20Sopenharmony_ci{ 18118c2ecf20Sopenharmony_ci struct page *page; 18128c2ecf20Sopenharmony_ci int nid; 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci if (!pmd_present(pmd)) 18158c2ecf20Sopenharmony_ci return NULL; 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci page = vm_normal_page_pmd(vma, addr, pmd); 18188c2ecf20Sopenharmony_ci if (!page) 18198c2ecf20Sopenharmony_ci return NULL; 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci if (PageReserved(page)) 18228c2ecf20Sopenharmony_ci return NULL; 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci nid = page_to_nid(page); 18258c2ecf20Sopenharmony_ci if (!node_isset(nid, node_states[N_MEMORY])) 18268c2ecf20Sopenharmony_ci return NULL; 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci return page; 18298c2ecf20Sopenharmony_ci} 18308c2ecf20Sopenharmony_ci#endif 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_cistatic int gather_pte_stats(pmd_t *pmd, unsigned long addr, 18338c2ecf20Sopenharmony_ci unsigned long end, struct mm_walk *walk) 18348c2ecf20Sopenharmony_ci{ 18358c2ecf20Sopenharmony_ci struct numa_maps *md = walk->private; 18368c2ecf20Sopenharmony_ci struct vm_area_struct *vma = walk->vma; 18378c2ecf20Sopenharmony_ci spinlock_t *ptl; 18388c2ecf20Sopenharmony_ci pte_t *orig_pte; 18398c2ecf20Sopenharmony_ci pte_t *pte; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 18428c2ecf20Sopenharmony_ci ptl = pmd_trans_huge_lock(pmd, vma); 18438c2ecf20Sopenharmony_ci if (ptl) { 18448c2ecf20Sopenharmony_ci struct page *page; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci page = can_gather_numa_stats_pmd(*pmd, vma, addr); 18478c2ecf20Sopenharmony_ci if (page) 18488c2ecf20Sopenharmony_ci gather_stats(page, md, pmd_dirty(*pmd), 18498c2ecf20Sopenharmony_ci HPAGE_PMD_SIZE/PAGE_SIZE); 18508c2ecf20Sopenharmony_ci spin_unlock(ptl); 18518c2ecf20Sopenharmony_ci return 0; 18528c2ecf20Sopenharmony_ci } 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci if (pmd_trans_unstable(pmd)) 18558c2ecf20Sopenharmony_ci return 0; 18568c2ecf20Sopenharmony_ci#endif 18578c2ecf20Sopenharmony_ci orig_pte = pte = pte_offset_map_lock(walk->mm, pmd, addr, &ptl); 18588c2ecf20Sopenharmony_ci do { 18598c2ecf20Sopenharmony_ci struct page *page = can_gather_numa_stats(*pte, vma, addr); 18608c2ecf20Sopenharmony_ci if (!page) 18618c2ecf20Sopenharmony_ci continue; 18628c2ecf20Sopenharmony_ci gather_stats(page, md, pte_dirty(*pte), 1); 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci } while (pte++, addr += PAGE_SIZE, addr != end); 18658c2ecf20Sopenharmony_ci pte_unmap_unlock(orig_pte, ptl); 18668c2ecf20Sopenharmony_ci cond_resched(); 18678c2ecf20Sopenharmony_ci return 0; 18688c2ecf20Sopenharmony_ci} 18698c2ecf20Sopenharmony_ci#ifdef CONFIG_HUGETLB_PAGE 18708c2ecf20Sopenharmony_cistatic int gather_hugetlb_stats(pte_t *pte, unsigned long hmask, 18718c2ecf20Sopenharmony_ci unsigned long addr, unsigned long end, struct mm_walk *walk) 18728c2ecf20Sopenharmony_ci{ 18738c2ecf20Sopenharmony_ci pte_t huge_pte = huge_ptep_get(pte); 18748c2ecf20Sopenharmony_ci struct numa_maps *md; 18758c2ecf20Sopenharmony_ci struct page *page; 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci if (!pte_present(huge_pte)) 18788c2ecf20Sopenharmony_ci return 0; 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci page = pte_page(huge_pte); 18818c2ecf20Sopenharmony_ci if (!page) 18828c2ecf20Sopenharmony_ci return 0; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci md = walk->private; 18858c2ecf20Sopenharmony_ci gather_stats(page, md, pte_dirty(huge_pte), 1); 18868c2ecf20Sopenharmony_ci return 0; 18878c2ecf20Sopenharmony_ci} 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci#else 18908c2ecf20Sopenharmony_cistatic int gather_hugetlb_stats(pte_t *pte, unsigned long hmask, 18918c2ecf20Sopenharmony_ci unsigned long addr, unsigned long end, struct mm_walk *walk) 18928c2ecf20Sopenharmony_ci{ 18938c2ecf20Sopenharmony_ci return 0; 18948c2ecf20Sopenharmony_ci} 18958c2ecf20Sopenharmony_ci#endif 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_cistatic const struct mm_walk_ops show_numa_ops = { 18988c2ecf20Sopenharmony_ci .hugetlb_entry = gather_hugetlb_stats, 18998c2ecf20Sopenharmony_ci .pmd_entry = gather_pte_stats, 19008c2ecf20Sopenharmony_ci}; 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci/* 19038c2ecf20Sopenharmony_ci * Display pages allocated per node and memory policy via /proc. 19048c2ecf20Sopenharmony_ci */ 19058c2ecf20Sopenharmony_cistatic int show_numa_map(struct seq_file *m, void *v) 19068c2ecf20Sopenharmony_ci{ 19078c2ecf20Sopenharmony_ci struct numa_maps_private *numa_priv = m->private; 19088c2ecf20Sopenharmony_ci struct proc_maps_private *proc_priv = &numa_priv->proc_maps; 19098c2ecf20Sopenharmony_ci struct vm_area_struct *vma = v; 19108c2ecf20Sopenharmony_ci struct numa_maps *md = &numa_priv->md; 19118c2ecf20Sopenharmony_ci struct file *file = vma->vm_file; 19128c2ecf20Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 19138c2ecf20Sopenharmony_ci struct mempolicy *pol; 19148c2ecf20Sopenharmony_ci char buffer[64]; 19158c2ecf20Sopenharmony_ci int nid; 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci if (!mm) 19188c2ecf20Sopenharmony_ci return 0; 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci /* Ensure we start with an empty set of numa_maps statistics. */ 19218c2ecf20Sopenharmony_ci memset(md, 0, sizeof(*md)); 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci pol = __get_vma_policy(vma, vma->vm_start); 19248c2ecf20Sopenharmony_ci if (pol) { 19258c2ecf20Sopenharmony_ci mpol_to_str(buffer, sizeof(buffer), pol); 19268c2ecf20Sopenharmony_ci mpol_cond_put(pol); 19278c2ecf20Sopenharmony_ci } else { 19288c2ecf20Sopenharmony_ci mpol_to_str(buffer, sizeof(buffer), proc_priv->task_mempolicy); 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci seq_printf(m, "%08lx %s", vma->vm_start, buffer); 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci if (file) { 19348c2ecf20Sopenharmony_ci seq_puts(m, " file="); 19358c2ecf20Sopenharmony_ci seq_file_path(m, file, "\n\t= "); 19368c2ecf20Sopenharmony_ci } else if (vma->vm_start <= mm->brk && vma->vm_end >= mm->start_brk) { 19378c2ecf20Sopenharmony_ci seq_puts(m, " heap"); 19388c2ecf20Sopenharmony_ci } else if (is_stack(vma)) { 19398c2ecf20Sopenharmony_ci seq_puts(m, " stack"); 19408c2ecf20Sopenharmony_ci } 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci if (is_vm_hugetlb_page(vma)) 19438c2ecf20Sopenharmony_ci seq_puts(m, " huge"); 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci /* mmap_lock is held by m_start */ 19468c2ecf20Sopenharmony_ci walk_page_vma(vma, &show_numa_ops, md); 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_ci if (!md->pages) 19498c2ecf20Sopenharmony_ci goto out; 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci if (md->anon) 19528c2ecf20Sopenharmony_ci seq_printf(m, " anon=%lu", md->anon); 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci if (md->dirty) 19558c2ecf20Sopenharmony_ci seq_printf(m, " dirty=%lu", md->dirty); 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci if (md->pages != md->anon && md->pages != md->dirty) 19588c2ecf20Sopenharmony_ci seq_printf(m, " mapped=%lu", md->pages); 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci if (md->mapcount_max > 1) 19618c2ecf20Sopenharmony_ci seq_printf(m, " mapmax=%lu", md->mapcount_max); 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci if (md->swapcache) 19648c2ecf20Sopenharmony_ci seq_printf(m, " swapcache=%lu", md->swapcache); 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci if (md->active < md->pages && !is_vm_hugetlb_page(vma)) 19678c2ecf20Sopenharmony_ci seq_printf(m, " active=%lu", md->active); 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci if (md->writeback) 19708c2ecf20Sopenharmony_ci seq_printf(m, " writeback=%lu", md->writeback); 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ci for_each_node_state(nid, N_MEMORY) 19738c2ecf20Sopenharmony_ci if (md->node[nid]) 19748c2ecf20Sopenharmony_ci seq_printf(m, " N%d=%lu", nid, md->node[nid]); 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci seq_printf(m, " kernelpagesize_kB=%lu", vma_kernel_pagesize(vma) >> 10); 19778c2ecf20Sopenharmony_ciout: 19788c2ecf20Sopenharmony_ci seq_putc(m, '\n'); 19798c2ecf20Sopenharmony_ci return 0; 19808c2ecf20Sopenharmony_ci} 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_cistatic const struct seq_operations proc_pid_numa_maps_op = { 19838c2ecf20Sopenharmony_ci .start = m_start, 19848c2ecf20Sopenharmony_ci .next = m_next, 19858c2ecf20Sopenharmony_ci .stop = m_stop, 19868c2ecf20Sopenharmony_ci .show = show_numa_map, 19878c2ecf20Sopenharmony_ci}; 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_cistatic int pid_numa_maps_open(struct inode *inode, struct file *file) 19908c2ecf20Sopenharmony_ci{ 19918c2ecf20Sopenharmony_ci return proc_maps_open(inode, file, &proc_pid_numa_maps_op, 19928c2ecf20Sopenharmony_ci sizeof(struct numa_maps_private)); 19938c2ecf20Sopenharmony_ci} 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ciconst struct file_operations proc_pid_numa_maps_operations = { 19968c2ecf20Sopenharmony_ci .open = pid_numa_maps_open, 19978c2ecf20Sopenharmony_ci .read = seq_read, 19988c2ecf20Sopenharmony_ci .llseek = seq_lseek, 19998c2ecf20Sopenharmony_ci .release = proc_map_release, 20008c2ecf20Sopenharmony_ci}; 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci#endif /* CONFIG_NUMA */ 2003