// SPDX-License-Identifier: GPL-2.0#include <linux/pagewalk.h>#include <linux/mm_inline.h>#include <linux/hugetlb.h>#include <linux/huge_mm.h>#include <linux/mount.h>#include <linux/ksm.h>#include <linux/seq_file.h>#include <linux/highmem.h>#include <linux/ptrace.h>#include <linux/slab.h>#include <linux/pagemap.h>#include <linux/mempolicy.h>#include <linux/rmap.h>#include <linux/swap.h>#include <linux/sched/mm.h>#include <linux/leafops.h>#include <linux/mmu_notifier.h>#include <linux/page_idle.h>#include <linux/shmem_fs.h>#include <linux/uaccess.h>#include <linux/pkeys.h>#include <linux/minmax.h>#include <linux/overflow.h>#include <linux/buildid.h> #include <asm/elf.h>#include <asm/tlb.h>#include <asm/tlbflush.h>#include "internal.h" #define SENTINEL_VMA_END -1#define SENTINEL_VMA_GATE -2 #define SEQ_PUT_DEC(str, val) \ seq_put_decimal_ull_width(m, str, (val) << (PAGE_SHIFT-10), 8)void task_mem(struct seq_file *m, struct mm_struct *mm){ unsigned long text, lib, swap, anon, file, shmem; unsigned long hiwater_vm, total_vm, hiwater_rss, total_rss; anon = get_mm_counter_sum(mm, MM_ANONPAGES); file = get_mm_counter_sum(mm, MM_FILEPAGES); shmem = get_mm_counter_sum(mm, MM_SHMEMPAGES); /* * Note: to minimize their overhead, mm maintains hiwater_vm and * hiwater_rss only when about to *lower* total_vm or rss. Any * collector of these hiwater stats must therefore get total_vm * and rss too, which will usually be the higher. Barriers? not * worth the effort, such snapshots can always be inconsistent. */ hiwater_vm = total_vm = mm->total_vm; if (hiwater_vm < mm->hiwater_vm) hiwater_vm = mm->hiwater_vm; hiwater_rss = total_rss = anon + file + shmem; if (hiwater_rss < mm->hiwater_rss) hiwater_rss = mm->hiwater_rss; /* split executable areas between text and lib */ text = PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK); text = min(text, mm->exec_vm << PAGE_SHIFT); lib = (mm->exec_vm << PAGE_SHIFT) - text; swap = get_mm_counter_sum(mm, MM_SWAPENTS); SEQ_PUT_DEC("VmPeak:\t", hiwater_vm); SEQ_PUT_DEC(" kB\nVmSize:\t", total_vm); SEQ_PUT_DEC(" kB\nVmLck:\t", mm->locked_vm); SEQ_PUT_DEC(" kB\nVmPin:\t", atomic64_read(&mm->pinned_vm)); SEQ_PUT_DEC(" kB\nVmHWM:\t", hiwater_rss); SEQ_PUT_DEC(" kB\nVmRSS:\t", total_rss); SEQ_PUT_DEC(" kB\nRssAnon:\t", anon); SEQ_PUT_DEC(" kB\nRssFile:\t", file); SEQ_PUT_DEC(" kB\nRssShmem:\t", shmem); SEQ_PUT_DEC(" kB\nVmData:\t", mm->data_vm); SEQ_PUT_DEC(" kB\nVmStk:\t", mm->stack_vm); seq_put_decimal_ull_width(m, " kB\nVmExe:\t", text >> 10, 8); seq_put_decimal_ull_width(m, " kB\nVmLib:\t", lib >> 10, 8); seq_put_decimal_ull_width(m, " kB\nVmPTE:\t", mm_pgtables_bytes(mm) >> 10, 8); SEQ_PUT_DEC(" kB\nVmSwap:\t", swap); seq_puts(m, " kB\n"); hugetlb_report_usage(m, mm);}#undef SEQ_PUT_DEC unsigned long task_vsize(struct mm_struct *mm){ return PAGE_SIZE * mm->total_vm;} unsigned long task_statm(struct mm_struct *mm, unsigned long *shared, unsigned long *text, unsigned long *data, unsigned long *resident){ *shared = get_mm_counter_sum(mm, MM_FILEPAGES) + get_mm_counter_sum(mm, MM_SHMEMPAGES); *text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) >> PAGE_SHIFT; *data = mm->data_vm + mm->stack_vm; *resident = *shared + get_mm_counter_sum(mm, MM_ANONPAGES); return mm->total_vm;} #ifdef CONFIG_NUMA/* * Save get_task_policy() for show_numa_map(). */static void hold_task_mempolicy(struct proc_maps_private *priv){ struct task_struct *task = priv->task; task_lock(task); priv->task_mempolicy = get_task_policy(task); mpol_get(priv->task_mempolicy); task_unlock(task);}static void release_task_mempolicy(struct proc_maps_private *priv){ mpol_put(priv->task_mempolicy);}#elsestatic void hold_task_mempolicy(struct proc_maps_private *priv){}static void release_task_mempolicy(struct proc_maps_private *priv){}#endif #ifdef CONFIG_PER_VMA_LOCK static inline int lock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx){ int ret = mmap_read_lock_killable(lock_ctx->mm); if (!ret) lock_ctx->mmap_locked = true; return ret;} static inline void unlock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx){ mmap_read_unlock(lock_ctx->mm); lock_ctx->mmap_locked = false;} static void reset_lock_ctx(struct proc_maps_locking_ctx *lock_ctx){ lock_ctx->locked_vma = NULL; lock_ctx->mmap_locked = false;} static void unlock_ctx_vma(struct proc_maps_locking_ctx *lock_ctx){ if (lock_ctx->locked_vma) { vma_end_read(lock_ctx->locked_vma); lock_ctx->locked_vma = NULL; }} static inline bool lock_vma_range(struct seq_file *m, struct proc_maps_locking_ctx *lock_ctx){ rcu_read_lock(); reset_lock_ctx(lock_ctx); return true;} static inline void unlock_vma_range(struct proc_maps_locking_ctx *lock_ctx){ if (lock_ctx->mmap_locked) { unlock_ctx_mm(lock_ctx); } else { unlock_ctx_vma(lock_ctx); rcu_read_unlock(); }} static struct vm_area_struct *get_next_vma(struct proc_maps_private *priv, loff_t last_pos){ struct proc_maps_locking_ctx *lock_ctx = &priv->lock_ctx; struct vm_area_struct *vma; if (lock_ctx->mmap_locked) return vma_next(&priv->iter); unlock_ctx_vma(lock_ctx); vma = lock_next_vma(lock_ctx->mm, &priv->iter, last_pos); if (!IS_ERR_OR_NULL(vma)) lock_ctx->locked_vma = vma; return vma;} static inline bool fallback_to_mmap_lock(struct proc_maps_private *priv, loff_t pos){ struct proc_maps_locking_ctx *lock_ctx = &priv->lock_ctx; if (lock_ctx->mmap_locked) return false; rcu_read_unlock(); mmap_read_lock(lock_ctx->mm); /* Reinitialize the iterator after taking mmap_lock */ vma_iter_set(&priv->iter, pos); lock_ctx->mmap_locked = true; return true;} static inline void drop_rcu(struct proc_maps_private *priv){ if (priv->lock_ctx.mmap_locked) return; rcu_read_unlock();} static inline void reacquire_rcu(struct proc_maps_private *priv){ if (priv->lock_ctx.mmap_locked) return; rcu_read_lock(); /* Reinitialize the iterator. */ vma_iter_set(&priv->iter, priv->lock_ctx.locked_vma->vm_end);} #else /* CONFIG_PER_VMA_LOCK */ static inline int lock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx){ return mmap_read_lock_killable(lock_ctx->mm);} static inline void unlock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx){ mmap_read_unlock(lock_ctx->mm);} static inline bool lock_vma_range(struct seq_file *m, struct proc_maps_locking_ctx *lock_ctx){ return lock_ctx_mm(lock_ctx) == 0;} static inline void unlock_vma_range(struct proc_maps_locking_ctx *lock_ctx){ unlock_ctx_mm(lock_ctx);} static struct vm_area_struct *get_next_vma(struct proc_maps_private *priv, loff_t last_pos){ return vma_next(&priv->iter);} static inline bool fallback_to_mmap_lock(struct proc_maps_private *priv, loff_t pos){ return false;} static inline void drop_rcu(struct proc_maps_private *priv) {}static inline void reacquire_rcu(struct proc_maps_private *priv) {} #endif /* CONFIG_PER_VMA_LOCK */ static struct vm_area_struct *proc_get_vma(struct seq_file *m, loff_t *ppos){ struct proc_maps_private *priv = m->private; struct vm_area_struct *vma; retry: vma = get_next_vma(priv, *ppos); /* EINTR of EAGAIN is possible */ if (IS_ERR(vma)) { if (PTR_ERR(vma) == -EAGAIN && fallback_to_mmap_lock(priv, *ppos)) goto retry; return vma; } /* Store previous position to be able to restart if needed */ priv->last_pos = *ppos; if (vma) { /* * Track the end of the reported vma to ensure position changes * even if previous vma was merged with the next vma and we * found the extended vma with the same vm_start. */ *ppos = vma->vm_end; } else { *ppos = SENTINEL_VMA_GATE; vma = get_gate_vma(priv->lock_ctx.mm); } return vma;} static void *m_start(struct seq_file *m, loff_t *ppos){ struct proc_maps_private *priv = m->private; struct proc_maps_locking_ctx *lock_ctx; loff_t last_addr = *ppos; struct mm_struct *mm; /* See m_next(). Zero at the start or after lseek. */ if (last_addr == SENTINEL_VMA_END) return NULL; priv->task = get_proc_task(priv->inode); if (!priv->task) return ERR_PTR(-ESRCH); lock_ctx = &priv->lock_ctx; mm = lock_ctx->mm; if (!mm || !mmget_not_zero(mm)) { put_task_struct(priv->task); priv->task = NULL; return NULL; } if (!lock_vma_range(m, lock_ctx)) { mmput(mm); put_task_struct(priv->task); priv->task = NULL; return ERR_PTR(-EINTR); } /* * Reset current position if last_addr was set before * and it's not a sentinel. */ if (last_addr > 0) *ppos = last_addr = priv->last_pos; vma_iter_init(&priv->iter, mm, (unsigned long)last_addr); hold_task_mempolicy(priv); if (last_addr == SENTINEL_VMA_GATE) return get_gate_vma(mm); return proc_get_vma(m, ppos);} static void *m_next(struct seq_file *m, void *v, loff_t *ppos){ if (*ppos == SENTINEL_VMA_GATE) { *ppos = SENTINEL_VMA_END; return NULL; } return proc_get_vma(m, ppos);} static void m_stop(struct seq_file *m, void *v){ struct proc_maps_private *priv = m->private; struct mm_struct *mm = priv->lock_ctx.mm; if (!priv->task) return; release_task_mempolicy(priv); unlock_vma_range(&priv->lock_ctx); mmput(mm); put_task_struct(priv->task); priv->task = NULL;} static int proc_maps_open(struct inode *inode, struct file *file, const struct seq_operations *ops, int psize){ struct proc_maps_private *priv = __seq_open_private(file, ops, psize); if (!priv) return -ENOMEM; priv->inode = inode; priv->lock_ctx.mm = proc_mem_open(inode, PTRACE_MODE_READ); if (IS_ERR(priv->lock_ctx.mm)) { int err = PTR_ERR(priv->lock_ctx.mm); seq_release_private(inode, file); return err; } return 0;} static int proc_map_release(struct inode *inode, struct file *file){ struct seq_file *seq = file->private_data; struct proc_maps_private *priv = seq->private; if (priv->lock_ctx.mm) mmdrop(priv->lock_ctx.mm); return seq_release_private(inode, file);} static int do_maps_open(struct inode *inode, struct file *file, const struct seq_operations *ops){ return proc_maps_open(inode, file, ops, sizeof(struct proc_maps_private));} static void get_vma_name(struct vm_area_struct *vma, const struct path **path, const char **name, const char **name_fmt){ struct anon_vma_name *anon_name = vma->vm_mm ? anon_vma_name(vma) : NULL; *name = NULL; *path = NULL; *name_fmt = NULL; /* * Print the dentry name for named mappings, and a * special [heap] marker for the heap: */ if (vma->vm_file) { /* * If user named this anon shared memory via * prctl(PR_SET_VMA ..., use the provided name. */ if (anon_name) { *name_fmt = "[anon_shmem:%s]"; *name = anon_name->name; } else { *path = file_user_path(vma->vm_file); } return; } if (vma->vm_ops && vma->vm_ops->name) { *name = vma->vm_ops->name(vma); if (*name) return; } *name = arch_vma_name(vma); if (*name) return; if (!vma->vm_mm) { *name = "[vdso]"; return; } if (vma_is_initial_heap(vma)) { *name = "[heap]"; return; } if (vma_is_initial_stack(vma)) { *name = "[stack]"; return; } if (anon_name) { *name_fmt = "[anon:%s]"; *name = anon_name->name; return; }} static void show_vma_header_prefix(struct seq_file *m, unsigned long start, unsigned long end, vm_flags_t flags, unsigned long long pgoff, dev_t dev, u64 ino){ seq_setwidth(m, 25 + sizeof(void *) * 6 - 1); seq_put_hex_ll(m, NULL, start, 8); seq_put_hex_ll(m, "-", end, 8); seq_putc(m, ' '); seq_putc(m, flags & VM_READ ? 'r' : '-'); seq_putc(m, flags & VM_WRITE ? 'w' : '-'); seq_putc(m, flags & VM_EXEC ? 'x' : '-'); seq_putc(m, flags & VM_MAYSHARE ? 's' : 'p'); seq_put_hex_ll(m, " ", pgoff, 8); seq_put_hex_ll(m, " ", MAJOR(dev), 2); seq_put_hex_ll(m, ":", MINOR(dev), 2); seq_put_decimal_ull(m, " ", ino); seq_putc(m, ' ');} static voidshow_map_vma(struct seq_file *m, struct vm_area_struct *vma){ const struct path *path; const char *name_fmt, *name; vm_flags_t flags = vma->vm_flags;Lines 1–500 of 3631. Load more
// SPDX-License-Identifier: GPL-2.0 #include <linux/pagewalk.h> #include <linux/mm_inline.h> #include <linux/hugetlb.h> #include <linux/huge_mm.h> #include <linux/mount.h> #include <linux/ksm.h> #include <linux/seq_file.h> #include <linux/highmem.h> #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/pagemap.h> #include <linux/mempolicy.h> #include <linux/rmap.h> #include <linux/swap.h> #include <linux/sched/mm.h> #include <linux/leafops.h> #include <linux/mmu_notifier.h> #include <linux/page_idle.h> #include <linux/shmem_fs.h> #include <linux/uaccess.h> #include <linux/pkeys.h> #include <linux/minmax.h> #include <linux/overflow.h> #include <linux/buildid.h> #include <asm/elf.h> #include <asm/tlb.h> #include <asm/tlbflush.h> #include "internal.h" #define SENTINEL_VMA_END -1 #define SENTINEL_VMA_GATE -2 #define SEQ_PUT_DEC(str, val) \ seq_put_decimal_ull_width(m, str, (val) << (PAGE_SHIFT-10), 8) void task_mem(struct seq_file *m, struct mm_struct *mm) { unsigned long text, lib, swap, anon, file, shmem; unsigned long hiwater_vm, total_vm, hiwater_rss, total_rss; anon = get_mm_counter_sum(mm, MM_ANONPAGES); file = get_mm_counter_sum(mm, MM_FILEPAGES); shmem = get_mm_counter_sum(mm, MM_SHMEMPAGES); /* * Note: to minimize their overhead, mm maintains hiwater_vm and * hiwater_rss only when about to *lower* total_vm or rss. Any * collector of these hiwater stats must therefore get total_vm * and rss too, which will usually be the higher. Barriers? not * worth the effort, such snapshots can always be inconsistent. */ hiwater_vm = total_vm = mm->total_vm; if (hiwater_vm < mm->hiwater_vm) hiwater_vm = mm->hiwater_vm; hiwater_rss = total_rss = anon + file + shmem; if (hiwater_rss < mm->hiwater_rss) hiwater_rss = mm->hiwater_rss; /* split executable areas between text and lib */ text = PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK); text = min(text, mm->exec_vm << PAGE_SHIFT); lib = (mm->exec_vm << PAGE_SHIFT) - text; swap = get_mm_counter_sum(mm, MM_SWAPENTS); SEQ_PUT_DEC("VmPeak:\t", hiwater_vm); SEQ_PUT_DEC(" kB\nVmSize:\t", total_vm); SEQ_PUT_DEC(" kB\nVmLck:\t", mm->locked_vm); SEQ_PUT_DEC(" kB\nVmPin:\t", atomic64_read(&mm->pinned_vm)); SEQ_PUT_DEC(" kB\nVmHWM:\t", hiwater_rss); SEQ_PUT_DEC(" kB\nVmRSS:\t", total_rss); SEQ_PUT_DEC(" kB\nRssAnon:\t", anon); SEQ_PUT_DEC(" kB\nRssFile:\t", file); SEQ_PUT_DEC(" kB\nRssShmem:\t", shmem); SEQ_PUT_DEC(" kB\nVmData:\t", mm->data_vm); SEQ_PUT_DEC(" kB\nVmStk:\t", mm->stack_vm); seq_put_decimal_ull_width(m, " kB\nVmExe:\t", text >> 10, 8); seq_put_decimal_ull_width(m, " kB\nVmLib:\t", lib >> 10, 8); seq_put_decimal_ull_width(m, " kB\nVmPTE:\t", mm_pgtables_bytes(mm) >> 10, 8); SEQ_PUT_DEC(" kB\nVmSwap:\t", swap); seq_puts(m, " kB\n"); hugetlb_report_usage(m, mm); } #undef SEQ_PUT_DEC unsigned long task_vsize(struct mm_struct *mm) { return PAGE_SIZE * mm->total_vm; } unsigned long task_statm(struct mm_struct *mm, unsigned long *shared, unsigned long *text, unsigned long *data, unsigned long *resident) { *shared = get_mm_counter_sum(mm, MM_FILEPAGES) + get_mm_counter_sum(mm, MM_SHMEMPAGES); *text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) >> PAGE_SHIFT; *data = mm->data_vm + mm->stack_vm; *resident = *shared + get_mm_counter_sum(mm, MM_ANONPAGES); return mm->total_vm; } #ifdef CONFIG_NUMA /* * Save get_task_policy() for show_numa_map(). */ static void hold_task_mempolicy(struct proc_maps_private *priv) { struct task_struct *task = priv->task; task_lock(task); priv->task_mempolicy = get_task_policy(task); mpol_get(priv->task_mempolicy); task_unlock(task); } static void release_task_mempolicy(struct proc_maps_private *priv) { mpol_put(priv->task_mempolicy); } #else static void hold_task_mempolicy(struct proc_maps_private *priv) { } static void release_task_mempolicy(struct proc_maps_private *priv) { } #endif #ifdef CONFIG_PER_VMA_LOCK static inline int lock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx) { int ret = mmap_read_lock_killable(lock_ctx->mm); if (!ret) lock_ctx->mmap_locked = true; return ret; } static inline void unlock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx) { mmap_read_unlock(lock_ctx->mm); lock_ctx->mmap_locked = false; } static void reset_lock_ctx(struct proc_maps_locking_ctx *lock_ctx) { lock_ctx->locked_vma = NULL; lock_ctx->mmap_locked = false; } static void unlock_ctx_vma(struct proc_maps_locking_ctx *lock_ctx) { if (lock_ctx->locked_vma) { vma_end_read(lock_ctx->locked_vma); lock_ctx->locked_vma = NULL; } } static inline bool lock_vma_range(struct seq_file *m, struct proc_maps_locking_ctx *lock_ctx) { rcu_read_lock(); reset_lock_ctx(lock_ctx); return true; } static inline void unlock_vma_range(struct proc_maps_locking_ctx *lock_ctx) { if (lock_ctx->mmap_locked) { unlock_ctx_mm(lock_ctx); } else { unlock_ctx_vma(lock_ctx); rcu_read_unlock(); } } static struct vm_area_struct *get_next_vma(struct proc_maps_private *priv, loff_t last_pos) { struct proc_maps_locking_ctx *lock_ctx = &priv->lock_ctx; struct vm_area_struct *vma; if (lock_ctx->mmap_locked) return vma_next(&priv->iter); unlock_ctx_vma(lock_ctx); vma = lock_next_vma(lock_ctx->mm, &priv->iter, last_pos); if (!IS_ERR_OR_NULL(vma)) lock_ctx->locked_vma = vma; return vma; } static inline bool fallback_to_mmap_lock(struct proc_maps_private *priv, loff_t pos) { struct proc_maps_locking_ctx *lock_ctx = &priv->lock_ctx; if (lock_ctx->mmap_locked) return false; rcu_read_unlock(); mmap_read_lock(lock_ctx->mm); /* Reinitialize the iterator after taking mmap_lock */ vma_iter_set(&priv->iter, pos); lock_ctx->mmap_locked = true; return true; } static inline void drop_rcu(struct proc_maps_private *priv) { if (priv->lock_ctx.mmap_locked) return; rcu_read_unlock(); } static inline void reacquire_rcu(struct proc_maps_private *priv) { if (priv->lock_ctx.mmap_locked) return; rcu_read_lock(); /* Reinitialize the iterator. */ vma_iter_set(&priv->iter, priv->lock_ctx.locked_vma->vm_end); } #else /* CONFIG_PER_VMA_LOCK */ static inline int lock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx) { return mmap_read_lock_killable(lock_ctx->mm); } static inline void unlock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx) { mmap_read_unlock(lock_ctx->mm); } static inline bool lock_vma_range(struct seq_file *m, struct proc_maps_locking_ctx *lock_ctx) { return lock_ctx_mm(lock_ctx) == 0; } static inline void unlock_vma_range(struct proc_maps_locking_ctx *lock_ctx) { unlock_ctx_mm(lock_ctx); } static struct vm_area_struct *get_next_vma(struct proc_maps_private *priv, loff_t last_pos) { return vma_next(&priv->iter); } static inline bool fallback_to_mmap_lock(struct proc_maps_private *priv, loff_t pos) { return false; } static inline void drop_rcu(struct proc_maps_private *priv) {} static inline void reacquire_rcu(struct proc_maps_private *priv) {} #endif /* CONFIG_PER_VMA_LOCK */ static struct vm_area_struct *proc_get_vma(struct seq_file *m, loff_t *ppos) { struct proc_maps_private *priv = m->private; struct vm_area_struct *vma; retry: vma = get_next_vma(priv, *ppos); /* EINTR of EAGAIN is possible */ if (IS_ERR(vma)) { if (PTR_ERR(vma) == -EAGAIN && fallback_to_mmap_lock(priv, *ppos)) goto retry; return vma; } /* Store previous position to be able to restart if needed */ priv->last_pos = *ppos; if (vma) { /* * Track the end of the reported vma to ensure position changes * even if previous vma was merged with the next vma and we * found the extended vma with the same vm_start. */ *ppos = vma->vm_end; } else { *ppos = SENTINEL_VMA_GATE; vma = get_gate_vma(priv->lock_ctx.mm); } return vma; } static void *m_start(struct seq_file *m, loff_t *ppos) { struct proc_maps_private *priv = m->private; struct proc_maps_locking_ctx *lock_ctx; loff_t last_addr = *ppos; struct mm_struct *mm; /* See m_next(). Zero at the start or after lseek. */ if (last_addr == SENTINEL_VMA_END) return NULL; priv->task = get_proc_task(priv->inode); if (!priv->task) return ERR_PTR(-ESRCH); lock_ctx = &priv->lock_ctx; mm = lock_ctx->mm; if (!mm || !mmget_not_zero(mm)) { put_task_struct(priv->task); priv->task = NULL; return NULL; } if (!lock_vma_range(m, lock_ctx)) { mmput(mm); put_task_struct(priv->task); priv->task = NULL; return ERR_PTR(-EINTR); } /* * Reset current position if last_addr was set before * and it's not a sentinel. */ if (last_addr > 0) *ppos = last_addr = priv->last_pos; vma_iter_init(&priv->iter, mm, (unsigned long)last_addr); hold_task_mempolicy(priv); if (last_addr == SENTINEL_VMA_GATE) return get_gate_vma(mm); return proc_get_vma(m, ppos); } static void *m_next(struct seq_file *m, void *v, loff_t *ppos) { if (*ppos == SENTINEL_VMA_GATE) { *ppos = SENTINEL_VMA_END; return NULL; } return proc_get_vma(m, ppos); } static void m_stop(struct seq_file *m, void *v) { struct proc_maps_private *priv = m->private; struct mm_struct *mm = priv->lock_ctx.mm; if (!priv->task) return; release_task_mempolicy(priv); unlock_vma_range(&priv->lock_ctx); mmput(mm); put_task_struct(priv->task); priv->task = NULL; } static int proc_maps_open(struct inode *inode, struct file *file, const struct seq_operations *ops, int psize) { struct proc_maps_private *priv = __seq_open_private(file, ops, psize); if (!priv) return -ENOMEM; priv->inode = inode; priv->lock_ctx.mm = proc_mem_open(inode, PTRACE_MODE_READ); if (IS_ERR(priv->lock_ctx.mm)) { int err = PTR_ERR(priv->lock_ctx.mm); seq_release_private(inode, file); return err; } return 0; } static int proc_map_release(struct inode *inode, struct file *file) { struct seq_file *seq = file->private_data; struct proc_maps_private *priv = seq->private; if (priv->lock_ctx.mm) mmdrop(priv->lock_ctx.mm); return seq_release_private(inode, file); } static int do_maps_open(struct inode *inode, struct file *file, const struct seq_operations *ops) { return proc_maps_open(inode, file, ops, sizeof(struct proc_maps_private)); } static void get_vma_name(struct vm_area_struct *vma, const struct path **path, const char **name, const char **name_fmt) { struct anon_vma_name *anon_name = vma->vm_mm ? anon_vma_name(vma) : NULL; *name = NULL; *path = NULL; *name_fmt = NULL; /* * Print the dentry name for named mappings, and a * special [heap] marker for the heap: */ if (vma->vm_file) { /* * If user named this anon shared memory via * prctl(PR_SET_VMA ..., use the provided name. */ if (anon_name) { *name_fmt = "[anon_shmem:%s]"; *name = anon_name->name; } else { *path = file_user_path(vma->vm_file); } return; } if (vma->vm_ops && vma->vm_ops->name) { *name = vma->vm_ops->name(vma); if (*name) return; } *name = arch_vma_name(vma); if (*name) return; if (!vma->vm_mm) { *name = "[vdso]"; return; } if (vma_is_initial_heap(vma)) { *name = "[heap]"; return; } if (vma_is_initial_stack(vma)) { *name = "[stack]"; return; } if (anon_name) { *name_fmt = "[anon:%s]"; *name = anon_name->name; return; } } static void show_vma_header_prefix(struct seq_file *m, unsigned long start, unsigned long end, vm_flags_t flags, unsigned long long pgoff, dev_t dev, u64 ino) { seq_setwidth(m, 25 + sizeof(void *) * 6 - 1); seq_put_hex_ll(m, NULL, start, 8); seq_put_hex_ll(m, "-", end, 8); seq_putc(m, ' '); seq_putc(m, flags & VM_READ ? 'r' : '-'); seq_putc(m, flags & VM_WRITE ? 'w' : '-'); seq_putc(m, flags & VM_EXEC ? 'x' : '-'); seq_putc(m, flags & VM_MAYSHARE ? 's' : 'p'); seq_put_hex_ll(m, " ", pgoff, 8); seq_put_hex_ll(m, " ", MAJOR(dev), 2); seq_put_hex_ll(m, ":", MINOR(dev), 2); seq_put_decimal_ull(m, " ", ino); seq_putc(m, ' '); } static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma) { const struct path *path; const char *name_fmt, *name; vm_flags_t flags = vma->vm_flags;