diff options
Diffstat (limited to 'fs/proc/base.c')
| -rw-r--r-- | fs/proc/base.c | 199 | 
1 files changed, 87 insertions, 112 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index 2d696b0c93bf..baf852b648ad 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -105,7 +105,7 @@   */  struct pid_entry { -	char *name; +	const char *name;  	int len;  	umode_t mode;  	const struct inode_operations *iop; @@ -130,10 +130,6 @@ struct pid_entry {  		{ .proc_get_link = get_link } )  #define REG(NAME, MODE, fops)				\  	NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {}) -#define INF(NAME, MODE, read)				\ -	NOD(NAME, (S_IFREG|(MODE)), 			\ -		NULL, &proc_info_file_operations,	\ -		{ .proc_read = read } )  #define ONE(NAME, MODE, show)				\  	NOD(NAME, (S_IFREG|(MODE)), 			\  		NULL, &proc_single_file_operations,	\ @@ -200,27 +196,32 @@ static int proc_root_link(struct dentry *dentry, struct path *path)  	return result;  } -static int proc_pid_cmdline(struct task_struct *task, char *buffer) +static int proc_pid_cmdline(struct seq_file *m, struct pid_namespace *ns, +			    struct pid *pid, struct task_struct *task)  { -	return get_cmdline(task, buffer, PAGE_SIZE); +	/* +	 * Rely on struct seq_operations::show() being called once +	 * per internal buffer allocation. See single_open(), traverse(). +	 */ +	BUG_ON(m->size < PAGE_SIZE); +	m->count += get_cmdline(task, m->buf, PAGE_SIZE); +	return 0;  } -static int proc_pid_auxv(struct task_struct *task, char *buffer) +static int proc_pid_auxv(struct seq_file *m, struct pid_namespace *ns, +			 struct pid *pid, struct task_struct *task)  {  	struct mm_struct *mm = mm_access(task, PTRACE_MODE_READ); -	int res = PTR_ERR(mm);  	if (mm && !IS_ERR(mm)) {  		unsigned int nwords = 0;  		do {  			nwords += 2;  		} while (mm->saved_auxv[nwords - 2] != 0); /* AT_NULL */ -		res = nwords * sizeof(mm->saved_auxv[0]); -		if (res > PAGE_SIZE) -			res = PAGE_SIZE; -		memcpy(buffer, mm->saved_auxv, res); +		seq_write(m, mm->saved_auxv, nwords * sizeof(mm->saved_auxv[0]));  		mmput(mm); -	} -	return res; +		return 0; +	} else +		return PTR_ERR(mm);  } @@ -229,7 +230,8 @@ static int proc_pid_auxv(struct task_struct *task, char *buffer)   * 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 task_struct *task, char *buffer) +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]; @@ -240,9 +242,9 @@ static int proc_pid_wchan(struct task_struct *task, char *buffer)  		if (!ptrace_may_access(task, PTRACE_MODE_READ))  			return 0;  		else -			return sprintf(buffer, "%lu", wchan); +			return seq_printf(m, "%lu", wchan);  	else -		return sprintf(buffer, "%s", symname); +		return seq_printf(m, "%s", symname);  }  #endif /* CONFIG_KALLSYMS */ @@ -304,9 +306,10 @@ static int proc_pid_stack(struct seq_file *m, struct pid_namespace *ns,  /*   * Provides /proc/PID/schedstat   */ -static int proc_pid_schedstat(struct task_struct *task, char *buffer) +static int proc_pid_schedstat(struct seq_file *m, struct pid_namespace *ns, +			      struct pid *pid, struct task_struct *task)  { -	return sprintf(buffer, "%llu %llu %lu\n", +	return 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); @@ -404,7 +407,8 @@ static const struct file_operations proc_cpuset_operations = {  };  #endif -static int proc_oom_score(struct task_struct *task, char *buffer) +static int proc_oom_score(struct seq_file *m, struct pid_namespace *ns, +			  struct pid *pid, struct task_struct *task)  {  	unsigned long totalpages = totalram_pages + total_swap_pages;  	unsigned long points = 0; @@ -414,12 +418,12 @@ static int proc_oom_score(struct task_struct *task, char *buffer)  		points = oom_badness(task, NULL, NULL, totalpages) *  						1000 / totalpages;  	read_unlock(&tasklist_lock); -	return sprintf(buffer, "%lu\n", points); +	return seq_printf(m, "%lu\n", points);  }  struct limit_names { -	char *name; -	char *unit; +	const char *name; +	const char *unit;  };  static const struct limit_names lnames[RLIM_NLIMITS] = { @@ -442,12 +446,11 @@ static const struct limit_names lnames[RLIM_NLIMITS] = {  };  /* Display limits for a process */ -static int proc_pid_limits(struct task_struct *task, char *buffer) +static int proc_pid_limits(struct seq_file *m, struct pid_namespace *ns, +			   struct pid *pid, struct task_struct *task)  {  	unsigned int i; -	int count = 0;  	unsigned long flags; -	char *bufptr = buffer;  	struct rlimit rlim[RLIM_NLIMITS]; @@ -459,35 +462,34 @@ static int proc_pid_limits(struct task_struct *task, char *buffer)  	/*  	 * print the file header  	 */ -	count += sprintf(&bufptr[count], "%-25s %-20s %-20s %-10s\n", +       seq_printf(m, "%-25s %-20s %-20s %-10s\n",  			"Limit", "Soft Limit", "Hard Limit", "Units");  	for (i = 0; i < RLIM_NLIMITS; i++) {  		if (rlim[i].rlim_cur == RLIM_INFINITY) -			count += sprintf(&bufptr[count], "%-25s %-20s ", +			seq_printf(m, "%-25s %-20s ",  					 lnames[i].name, "unlimited");  		else -			count += sprintf(&bufptr[count], "%-25s %-20lu ", +			seq_printf(m, "%-25s %-20lu ",  					 lnames[i].name, rlim[i].rlim_cur);  		if (rlim[i].rlim_max == RLIM_INFINITY) -			count += sprintf(&bufptr[count], "%-20s ", "unlimited"); +			seq_printf(m, "%-20s ", "unlimited");  		else -			count += sprintf(&bufptr[count], "%-20lu ", -					 rlim[i].rlim_max); +			seq_printf(m, "%-20lu ", rlim[i].rlim_max);  		if (lnames[i].unit) -			count += sprintf(&bufptr[count], "%-10s\n", -					 lnames[i].unit); +			seq_printf(m, "%-10s\n", lnames[i].unit);  		else -			count += sprintf(&bufptr[count], "\n"); +			seq_putc(m, '\n');  	} -	return count; +	return 0;  }  #ifdef CONFIG_HAVE_ARCH_TRACEHOOK -static int proc_pid_syscall(struct task_struct *task, char *buffer) +static int proc_pid_syscall(struct seq_file *m, struct pid_namespace *ns, +			    struct pid *pid, struct task_struct *task)  {  	long nr;  	unsigned long args[6], sp, pc; @@ -496,11 +498,11 @@ static int proc_pid_syscall(struct task_struct *task, char *buffer)  		return res;  	if (task_current_syscall(task, &nr, args, 6, &sp, &pc)) -		res = sprintf(buffer, "running\n"); +		seq_puts(m, "running\n");  	else if (nr < 0) -		res = sprintf(buffer, "%ld 0x%lx 0x%lx\n", nr, sp, pc); +		seq_printf(m, "%ld 0x%lx 0x%lx\n", nr, sp, pc);  	else -		res = sprintf(buffer, +		seq_printf(m,  		       "%ld 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n",  		       nr,  		       args[0], args[1], args[2], args[3], args[4], args[5], @@ -598,43 +600,6 @@ static const struct inode_operations proc_def_inode_operations = {  	.setattr	= proc_setattr,  }; -#define PROC_BLOCK_SIZE	(3*1024)		/* 4K page size but our output routines use some slack for overruns */ - -static ssize_t proc_info_read(struct file * file, char __user * buf, -			  size_t count, loff_t *ppos) -{ -	struct inode * inode = file_inode(file); -	unsigned long page; -	ssize_t length; -	struct task_struct *task = get_proc_task(inode); - -	length = -ESRCH; -	if (!task) -		goto out_no_task; - -	if (count > PROC_BLOCK_SIZE) -		count = PROC_BLOCK_SIZE; - -	length = -ENOMEM; -	if (!(page = __get_free_page(GFP_TEMPORARY))) -		goto out; - -	length = PROC_I(inode)->op.proc_read(task, (char*)page); - -	if (length >= 0) -		length = simple_read_from_buffer(buf, count, ppos, (char *)page, length); -	free_page(page); -out: -	put_task_struct(task); -out_no_task: -	return length; -} - -static const struct file_operations proc_info_file_operations = { -	.read		= proc_info_read, -	.llseek		= generic_file_llseek, -}; -  static int proc_single_show(struct seq_file *m, void *v)  {  	struct inode *inode = m->private; @@ -2056,7 +2021,7 @@ static int show_timer(struct seq_file *m, void *v)  	struct k_itimer *timer;  	struct timers_private *tp = m->private;  	int notify; -	static char *nstr[] = { +	static const char * const nstr[] = {  		[SIGEV_SIGNAL] = "signal",  		[SIGEV_NONE] = "none",  		[SIGEV_THREAD] = "thread", @@ -2392,7 +2357,7 @@ static const struct file_operations proc_coredump_filter_operations = {  #endif  #ifdef CONFIG_TASK_IO_ACCOUNTING -static int do_io_accounting(struct task_struct *task, char *buffer, int whole) +static int do_io_accounting(struct task_struct *task, struct seq_file *m, int whole)  {  	struct task_io_accounting acct = task->ioac;  	unsigned long flags; @@ -2416,7 +2381,7 @@ static int do_io_accounting(struct task_struct *task, char *buffer, int whole)  		unlock_task_sighand(task, &flags);  	} -	result = sprintf(buffer, +	result = seq_printf(m,  			"rchar: %llu\n"  			"wchar: %llu\n"  			"syscr: %llu\n" @@ -2436,20 +2401,22 @@ out_unlock:  	return result;  } -static int proc_tid_io_accounting(struct task_struct *task, char *buffer) +static int proc_tid_io_accounting(struct seq_file *m, struct pid_namespace *ns, +				  struct pid *pid, struct task_struct *task)  { -	return do_io_accounting(task, buffer, 0); +	return do_io_accounting(task, m, 0);  } -static int proc_tgid_io_accounting(struct task_struct *task, char *buffer) +static int proc_tgid_io_accounting(struct seq_file *m, struct pid_namespace *ns, +				   struct pid *pid, struct task_struct *task)  { -	return do_io_accounting(task, buffer, 1); +	return do_io_accounting(task, m, 1);  }  #endif /* CONFIG_TASK_IO_ACCOUNTING */  #ifdef CONFIG_USER_NS  static int proc_id_map_open(struct inode *inode, struct file *file, -	struct seq_operations *seq_ops) +	const struct seq_operations *seq_ops)  {  	struct user_namespace *ns = NULL;  	struct task_struct *task; @@ -2557,10 +2524,10 @@ static const struct pid_entry tgid_base_stuff[] = {  	DIR("net",        S_IRUGO|S_IXUGO, proc_net_inode_operations, proc_net_operations),  #endif  	REG("environ",    S_IRUSR, proc_environ_operations), -	INF("auxv",       S_IRUSR, proc_pid_auxv), +	ONE("auxv",       S_IRUSR, proc_pid_auxv),  	ONE("status",     S_IRUGO, proc_pid_status),  	ONE("personality", S_IRUSR, proc_pid_personality), -	INF("limits",	  S_IRUGO, proc_pid_limits), +	ONE("limits",	  S_IRUGO, proc_pid_limits),  #ifdef CONFIG_SCHED_DEBUG  	REG("sched",      S_IRUGO|S_IWUSR, proc_pid_sched_operations),  #endif @@ -2569,9 +2536,9 @@ static const struct pid_entry tgid_base_stuff[] = {  #endif  	REG("comm",      S_IRUGO|S_IWUSR, proc_pid_set_comm_operations),  #ifdef CONFIG_HAVE_ARCH_TRACEHOOK -	INF("syscall",    S_IRUSR, proc_pid_syscall), +	ONE("syscall",    S_IRUSR, proc_pid_syscall),  #endif -	INF("cmdline",    S_IRUGO, proc_pid_cmdline), +	ONE("cmdline",    S_IRUGO, proc_pid_cmdline),  	ONE("stat",       S_IRUGO, proc_tgid_stat),  	ONE("statm",      S_IRUGO, proc_pid_statm),  	REG("maps",       S_IRUGO, proc_pid_maps_operations), @@ -2594,13 +2561,13 @@ static const struct pid_entry tgid_base_stuff[] = {  	DIR("attr",       S_IRUGO|S_IXUGO, proc_attr_dir_inode_operations, proc_attr_dir_operations),  #endif  #ifdef CONFIG_KALLSYMS -	INF("wchan",      S_IRUGO, proc_pid_wchan), +	ONE("wchan",      S_IRUGO, proc_pid_wchan),  #endif  #ifdef CONFIG_STACKTRACE  	ONE("stack",      S_IRUSR, proc_pid_stack),  #endif  #ifdef CONFIG_SCHEDSTATS -	INF("schedstat",  S_IRUGO, proc_pid_schedstat), +	ONE("schedstat",  S_IRUGO, proc_pid_schedstat),  #endif  #ifdef CONFIG_LATENCYTOP  	REG("latency",  S_IRUGO, proc_lstats_operations), @@ -2611,7 +2578,7 @@ static const struct pid_entry tgid_base_stuff[] = {  #ifdef CONFIG_CGROUPS  	REG("cgroup",  S_IRUGO, proc_cgroup_operations),  #endif -	INF("oom_score",  S_IRUGO, proc_oom_score), +	ONE("oom_score",  S_IRUGO, proc_oom_score),  	REG("oom_adj",    S_IRUGO|S_IWUSR, proc_oom_adj_operations),  	REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations),  #ifdef CONFIG_AUDITSYSCALL @@ -2625,10 +2592,10 @@ static const struct pid_entry tgid_base_stuff[] = {  	REG("coredump_filter", S_IRUGO|S_IWUSR, proc_coredump_filter_operations),  #endif  #ifdef CONFIG_TASK_IO_ACCOUNTING -	INF("io",	S_IRUSR, proc_tgid_io_accounting), +	ONE("io",	S_IRUSR, proc_tgid_io_accounting),  #endif  #ifdef CONFIG_HARDWALL -	INF("hardwall",   S_IRUGO, proc_pid_hardwall), +	ONE("hardwall",   S_IRUGO, proc_pid_hardwall),  #endif  #ifdef CONFIG_USER_NS  	REG("uid_map",    S_IRUGO|S_IWUSR, proc_uid_map_operations), @@ -2780,12 +2747,12 @@ out:  struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags)  { -	int result = 0; +	int result = -ENOENT;  	struct task_struct *task;  	unsigned tgid;  	struct pid_namespace *ns; -	tgid = name_to_int(dentry); +	tgid = name_to_int(&dentry->d_name);  	if (tgid == ~0U)  		goto out; @@ -2847,7 +2814,7 @@ retry:  	return iter;  } -#define TGID_OFFSET (FIRST_PROCESS_ENTRY + 1) +#define TGID_OFFSET (FIRST_PROCESS_ENTRY + 2)  /* for the /proc/ directory itself, after non-process stuff has been done */  int proc_pid_readdir(struct file *file, struct dir_context *ctx) @@ -2859,14 +2826,19 @@ int proc_pid_readdir(struct file *file, struct dir_context *ctx)  	if (pos >= PID_MAX_LIMIT + TGID_OFFSET)  		return 0; -	if (pos == TGID_OFFSET - 1) { +	if (pos == TGID_OFFSET - 2) {  		struct inode *inode = ns->proc_self->d_inode;  		if (!dir_emit(ctx, "self", 4, inode->i_ino, DT_LNK))  			return 0; -		iter.tgid = 0; -	} else { -		iter.tgid = pos - TGID_OFFSET; +		ctx->pos = pos = pos + 1;  	} +	if (pos == TGID_OFFSET - 1) { +		struct inode *inode = ns->proc_thread_self->d_inode; +		if (!dir_emit(ctx, "thread-self", 11, inode->i_ino, DT_LNK)) +			return 0; +		ctx->pos = pos = pos + 1; +	} +	iter.tgid = pos - TGID_OFFSET;  	iter.task = NULL;  	for (iter = next_tgid(ns, iter);  	     iter.task; @@ -2895,19 +2867,22 @@ static const struct pid_entry tid_base_stuff[] = {  	DIR("fd",        S_IRUSR|S_IXUSR, proc_fd_inode_operations, proc_fd_operations),  	DIR("fdinfo",    S_IRUSR|S_IXUSR, proc_fdinfo_inode_operations, proc_fdinfo_operations),  	DIR("ns",	 S_IRUSR|S_IXUGO, proc_ns_dir_inode_operations, proc_ns_dir_operations), +#ifdef CONFIG_NET +	DIR("net",        S_IRUGO|S_IXUGO, proc_net_inode_operations, proc_net_operations), +#endif  	REG("environ",   S_IRUSR, proc_environ_operations), -	INF("auxv",      S_IRUSR, proc_pid_auxv), +	ONE("auxv",      S_IRUSR, proc_pid_auxv),  	ONE("status",    S_IRUGO, proc_pid_status),  	ONE("personality", S_IRUSR, proc_pid_personality), -	INF("limits",	 S_IRUGO, proc_pid_limits), +	ONE("limits",	 S_IRUGO, proc_pid_limits),  #ifdef CONFIG_SCHED_DEBUG  	REG("sched",     S_IRUGO|S_IWUSR, proc_pid_sched_operations),  #endif  	REG("comm",      S_IRUGO|S_IWUSR, proc_pid_set_comm_operations),  #ifdef CONFIG_HAVE_ARCH_TRACEHOOK -	INF("syscall",   S_IRUSR, proc_pid_syscall), +	ONE("syscall",   S_IRUSR, proc_pid_syscall),  #endif -	INF("cmdline",   S_IRUGO, proc_pid_cmdline), +	ONE("cmdline",   S_IRUGO, proc_pid_cmdline),  	ONE("stat",      S_IRUGO, proc_tid_stat),  	ONE("statm",     S_IRUGO, proc_pid_statm),  	REG("maps",      S_IRUGO, proc_tid_maps_operations), @@ -2932,13 +2907,13 @@ static const struct pid_entry tid_base_stuff[] = {  	DIR("attr",      S_IRUGO|S_IXUGO, proc_attr_dir_inode_operations, proc_attr_dir_operations),  #endif  #ifdef CONFIG_KALLSYMS -	INF("wchan",     S_IRUGO, proc_pid_wchan), +	ONE("wchan",     S_IRUGO, proc_pid_wchan),  #endif  #ifdef CONFIG_STACKTRACE  	ONE("stack",      S_IRUSR, proc_pid_stack),  #endif  #ifdef CONFIG_SCHEDSTATS -	INF("schedstat", S_IRUGO, proc_pid_schedstat), +	ONE("schedstat", S_IRUGO, proc_pid_schedstat),  #endif  #ifdef CONFIG_LATENCYTOP  	REG("latency",  S_IRUGO, proc_lstats_operations), @@ -2949,7 +2924,7 @@ static const struct pid_entry tid_base_stuff[] = {  #ifdef CONFIG_CGROUPS  	REG("cgroup",  S_IRUGO, proc_cgroup_operations),  #endif -	INF("oom_score", S_IRUGO, proc_oom_score), +	ONE("oom_score", S_IRUGO, proc_oom_score),  	REG("oom_adj",   S_IRUGO|S_IWUSR, proc_oom_adj_operations),  	REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations),  #ifdef CONFIG_AUDITSYSCALL @@ -2960,10 +2935,10 @@ static const struct pid_entry tid_base_stuff[] = {  	REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations),  #endif  #ifdef CONFIG_TASK_IO_ACCOUNTING -	INF("io",	S_IRUSR, proc_tid_io_accounting), +	ONE("io",	S_IRUSR, proc_tid_io_accounting),  #endif  #ifdef CONFIG_HARDWALL -	INF("hardwall",   S_IRUGO, proc_pid_hardwall), +	ONE("hardwall",   S_IRUGO, proc_pid_hardwall),  #endif  #ifdef CONFIG_USER_NS  	REG("uid_map",    S_IRUGO|S_IWUSR, proc_uid_map_operations), @@ -3033,7 +3008,7 @@ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry  	if (!leader)  		goto out_no_task; -	tid = name_to_int(dentry); +	tid = name_to_int(&dentry->d_name);  	if (tid == ~0U)  		goto out;  |