3609 lines · 85.0 KB · C
RawDownload
/* *  linux/fs/proc/base.c * *  Copyright (C) 1991, 1992 Linus Torvalds * *  proc base directory handling functions * *  1999, Al Viro. Rewritten. Now it covers the whole per-process part. *  Instead of using magical inumbers to determine the kind of object *  we allocate and fill in-core inodes upon lookup. They don't even *  go into icache. We cache the reference to task_struct upon lookup too. *  Eventually it should become a filesystem in its own. We don't use the *  rest of procfs anymore. * * *  Changelog: *  17-Jan-2005 *  Allan Bezerra *  Bruna Moreira <bruna.moreira@indt.org.br> *  Edjard Mota <edjard.mota@indt.org.br> *  Ilias Biris <ilias.biris@indt.org.br> *  Mauricio Lin <mauricio.lin@indt.org.br> * *  Embedded Linux Lab - 10LE Instituto Nokia de Tecnologia - INdT * *  A new process specific entry (smaps) included in /proc. It shows the *  size of rss for each memory area. The maps entry lacks information *  about physical memory size (rss) for each mapped file, i.e., *  rss information for executables and library files. *  This additional information is useful for any tools that need to know *  about physical memory consumption for a process specific library. * *  Changelog: *  21-Feb-2005 *  Embedded Linux Lab - 10LE Instituto Nokia de Tecnologia - INdT *  Pud inclusion in the page table walking. * *  ChangeLog: *  10-Mar-2005 *  10LE Instituto Nokia de Tecnologia - INdT: *  A better way to walks through the page table as suggested by Hugh Dickins. * *  Simo Piiroinen <simo.piiroinen@nokia.com>: *  Smaps information related to shared, private, clean and dirty pages. * *  Paul Mundt <paul.mundt@nokia.com>: *  Overall revision about smaps. */ #include <linux/uaccess.h> #include <linux/errno.h>#include <linux/time.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <linux/task_io_accounting_ops.h>#include <linux/init.h>#include <linux/capability.h>#include <linux/file.h>#include <linux/fdtable.h>#include <linux/string.h>#include <linux/seq_file.h>#include <linux/namei.h>#include <linux/mnt_namespace.h>#include <linux/mm.h>#include <linux/swap.h>#include <linux/rcupdate.h>#include <linux/kallsyms.h>#include <linux/stacktrace.h>#include <linux/resource.h>#include <linux/module.h>#include <linux/mount.h>#include <linux/security.h>#include <linux/ptrace.h>#include <linux/tracehook.h>#include <linux/printk.h>#include <linux/cgroup.h>#include <linux/cpuset.h>#include <linux/audit.h>#include <linux/poll.h>#include <linux/nsproxy.h>#include <linux/oom.h>#include <linux/elf.h>#include <linux/pid_namespace.h>#include <linux/user_namespace.h>#include <linux/fs_struct.h>#include <linux/slab.h>#include <linux/sched/autogroup.h>#include <linux/sched/mm.h>#include <linux/sched/coredump.h>#include <linux/sched/debug.h>#include <linux/sched/stat.h>#include <linux/flex_array.h>#include <linux/posix-timers.h>#ifdef CONFIG_HARDWALL#include <asm/hardwall.h>#endif#include <trace/events/oom.h>#include "internal.h"#include "fd.h" /* NOTE: *	Implementing inode permission operations in /proc is almost *	certainly an error.  Permission checks need to happen during *	each system call not at open time.  The reason is that most of *	what we wish to check for permissions in /proc varies at runtime. * *	The classic example of a problem is opening file descriptors *	in /proc for a task before it execs a suid executable. */ static u8 nlink_tid;static u8 nlink_tgid; struct pid_entry {	const char *name;	unsigned int len;	umode_t mode;	const struct inode_operations *iop;	const struct file_operations *fop;	union proc_op op;}; #define NOD(NAME, MODE, IOP, FOP, OP) {			\	.name = (NAME),					\	.len  = sizeof(NAME) - 1,			\	.mode = MODE,					\	.iop  = IOP,					\	.fop  = FOP,					\	.op   = OP,					\} #define DIR(NAME, MODE, iops, fops)	\	NOD(NAME, (S_IFDIR|(MODE)), &iops, &fops, {} )#define LNK(NAME, get_link)					\	NOD(NAME, (S_IFLNK|S_IRWXUGO),				\		&proc_pid_link_inode_operations, NULL,		\		{ .proc_get_link = get_link } )#define REG(NAME, MODE, fops)				\	NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {})#define ONE(NAME, MODE, show)				\	NOD(NAME, (S_IFREG|(MODE)), 			\		NULL, &proc_single_file_operations,	\		{ .proc_show = show } ) /* * Count the number of hardlinks for the pid_entry table, excluding the . * and .. links. */static unsigned int __init pid_entry_nlink(const struct pid_entry *entries,	unsigned int n){	unsigned int i;	unsigned int count; 	count = 2;	for (i = 0; i < n; ++i) {		if (S_ISDIR(entries[i].mode))			++count;	} 	return count;} static int get_task_root(struct task_struct *task, struct path *root){	int result = -ENOENT; 	task_lock(task);	if (task->fs) {		get_fs_root(task->fs, root);		result = 0;	}	task_unlock(task);	return result;} static int proc_cwd_link(struct dentry *dentry, struct path *path){	struct task_struct *task = get_proc_task(d_inode(dentry));	int result = -ENOENT; 	if (task) {		task_lock(task);		if (task->fs) {			get_fs_pwd(task->fs, path);			result = 0;		}		task_unlock(task);		put_task_struct(task);	}	return result;} static int proc_root_link(struct dentry *dentry, struct path *path){	struct task_struct *task = get_proc_task(d_inode(dentry));	int result = -ENOENT; 	if (task) {		result = get_task_root(task, path);		put_task_struct(task);	}	return result;} static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,				     size_t _count, loff_t *pos){	struct task_struct *tsk;	struct mm_struct *mm;	char *page;	unsigned long count = _count;	unsigned long arg_start, arg_end, env_start, env_end;	unsigned long len1, len2, len;	unsigned long p;	char c;	ssize_t rv; 	BUG_ON(*pos < 0); 	tsk = get_proc_task(file_inode(file));	if (!tsk)		return -ESRCH;	mm = get_task_mm(tsk);	put_task_struct(tsk);	if (!mm)		return 0;	/* Check if process spawned far enough to have cmdline. */	if (!mm->env_end) {		rv = 0;		goto out_mmput;	} 	page = (char *)__get_free_page(GFP_TEMPORARY);	if (!page) {		rv = -ENOMEM;		goto out_mmput;	} 	down_read(&mm->mmap_sem);	arg_start = mm->arg_start;	arg_end = mm->arg_end;	env_start = mm->env_start;	env_end = mm->env_end;	up_read(&mm->mmap_sem); 	BUG_ON(arg_start > arg_end);	BUG_ON(env_start > env_end); 	len1 = arg_end - arg_start;	len2 = env_end - env_start; 	/* Empty ARGV. */	if (len1 == 0) {		rv = 0;		goto out_free_page;	}	/*	 * Inherently racy -- command line shares address space	 * with code and data.	 */	rv = access_remote_vm(mm, arg_end - 1, &c, 1, 0);	if (rv <= 0)		goto out_free_page; 	rv = 0; 	if (c == '\0') {		/* Command line (set of strings) occupies whole ARGV. */		if (len1 <= *pos)			goto out_free_page; 		p = arg_start + *pos;		len = len1 - *pos;		while (count > 0 && len > 0) {			unsigned int _count;			int nr_read; 			_count = min3(count, len, PAGE_SIZE);			nr_read = access_remote_vm(mm, p, page, _count, 0);			if (nr_read < 0)				rv = nr_read;			if (nr_read <= 0)				goto out_free_page; 			if (copy_to_user(buf, page, nr_read)) {				rv = -EFAULT;				goto out_free_page;			} 			p	+= nr_read;			len	-= nr_read;			buf	+= nr_read;			count	-= nr_read;			rv	+= nr_read;		}	} else {		/*		 * Command line (1 string) occupies ARGV and		 * extends into ENVP.		 */		struct {			unsigned long p;			unsigned long len;		} cmdline[2] = {			{ .p = arg_start, .len = len1 },			{ .p = env_start, .len = len2 },		};		loff_t pos1 = *pos;		unsigned int i; 		i = 0;		while (i < 2 && pos1 >= cmdline[i].len) {			pos1 -= cmdline[i].len;			i++;		}		while (i < 2) {			p = cmdline[i].p + pos1;			len = cmdline[i].len - pos1;			while (count > 0 && len > 0) {				unsigned int _count, l;				int nr_read;				bool final; 				_count = min3(count, len, PAGE_SIZE);				nr_read = access_remote_vm(mm, p, page, _count, 0);				if (nr_read < 0)					rv = nr_read;				if (nr_read <= 0)					goto out_free_page; 				/*				 * Command line can be shorter than whole ARGV				 * even if last "marker" byte says it is not.				 */				final = false;				l = strnlen(page, nr_read);				if (l < nr_read) {					nr_read = l;					final = true;				} 				if (copy_to_user(buf, page, nr_read)) {					rv = -EFAULT;					goto out_free_page;				} 				p	+= nr_read;				len	-= nr_read;				buf	+= nr_read;				count	-= nr_read;				rv	+= nr_read; 				if (final)					goto out_free_page;			} 			/* Only first chunk can be read partially. */			pos1 = 0;			i++;		}	} out_free_page:	free_page((unsigned long)page);out_mmput:	mmput(mm);	if (rv > 0)		*pos += rv;	return rv;} static const struct file_operations proc_pid_cmdline_ops = {	.read	= proc_pid_cmdline_read,	.llseek	= generic_file_llseek,}; #ifdef CONFIG_KALLSYMS/* * Provides a wchan file via kallsyms in a proper one-value-per-file format. * Returns the resolved symbol.  If that fails, simply return the address. */static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns,			  struct pid *pid, struct task_struct *task){	unsigned long wchan;	char symname[KSYM_NAME_LEN]; 	wchan = get_wchan(task); 	if (wchan && ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)			&& !lookup_symbol_name(wchan, symname))		seq_printf(m, "%s", symname);	else		seq_putc(m, '0'); 	return 0;}#endif /* CONFIG_KALLSYMS */ static int lock_trace(struct task_struct *task){	int err = mutex_lock_killable(&task->signal->cred_guard_mutex);	if (err)		return err;	if (!ptrace_may_access(task, PTRACE_MODE_ATTACH_FSCREDS)) {		mutex_unlock(&task->signal->cred_guard_mutex);		return -EPERM;	}	return 0;} static void unlock_trace(struct task_struct *task){	mutex_unlock(&task->signal->cred_guard_mutex);} #ifdef CONFIG_STACKTRACE #define MAX_STACK_TRACE_DEPTH	64 static int proc_pid_stack(struct seq_file *m, struct pid_namespace *ns,			  struct pid *pid, struct task_struct *task){	struct stack_trace trace;	unsigned long *entries;	int err;	int i; 	entries = kmalloc(MAX_STACK_TRACE_DEPTH * sizeof(*entries), GFP_KERNEL);	if (!entries)		return -ENOMEM; 	trace.nr_entries	= 0;	trace.max_entries	= MAX_STACK_TRACE_DEPTH;	trace.entries		= entries;	trace.skip		= 0; 	err = lock_trace(task);	if (!err) {		save_stack_trace_tsk(task, &trace); 		for (i = 0; i < trace.nr_entries; i++) {			seq_printf(m, "[<%pK>] %pB\n",				   (void *)entries[i], (void *)entries[i]);		}		unlock_trace(task);	}	kfree(entries); 	return err;}#endif #ifdef CONFIG_SCHED_INFO/* * Provides /proc/PID/schedstat */static int proc_pid_schedstat(struct seq_file *m, struct pid_namespace *ns,			      struct pid *pid, struct task_struct *task){	if (unlikely(!sched_info_on()))		seq_printf(m, "0 0 0\n");	else		seq_printf(m, "%llu %llu %lu\n",		   (unsigned long long)task->se.sum_exec_runtime,		   (unsigned long long)task->sched_info.run_delay,		   task->sched_info.pcount); 	return 0;}#endif #ifdef CONFIG_LATENCYTOPstatic int lstats_show_proc(struct seq_file *m, void *v){	int i;	struct inode *inode = m->private;	struct task_struct *task = get_proc_task(inode); 	if (!task)		return -ESRCH;	seq_puts(m, "Latency Top version : v0.1\n");	for (i = 0; i < 32; i++) {		struct latency_record *lr = &task->latency_record[i];		if (lr->backtrace[0]) {			int q;			seq_printf(m, "%i %li %li",				   lr->count, lr->time, lr->max);			for (q = 0; q < LT_BACKTRACEDEPTH; q++) {				unsigned long bt = lr->backtrace[q];				if (!bt)					break;				if (bt == ULONG_MAX)					break;				seq_printf(m, " %ps", (void *)bt);			}			seq_putc(m, '\n');		}

Lines 1–500 of 3609.