3631 lines · 88.0 KB · C
RawDownload
// 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.