diff options
Diffstat (limited to 'fs/proc')
-rw-r--r-- | fs/proc/array.c | 15 | ||||
-rw-r--r-- | fs/proc/base.c | 83 | ||||
-rw-r--r-- | fs/proc/generic.c | 52 | ||||
-rw-r--r-- | fs/proc/inode.c | 254 | ||||
-rw-r--r-- | fs/proc/proc_misc.c | 7 | ||||
-rw-r--r-- | fs/proc/proc_tty.c | 15 |
6 files changed, 318 insertions, 108 deletions
diff --git a/fs/proc/array.c b/fs/proc/array.c index 98e78e2f18d6..9cbab7e93557 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -289,6 +289,15 @@ static inline char *task_cap(struct task_struct *p, char *buffer) cap_t(p->cap_effective)); } +static inline char *task_context_switch_counts(struct task_struct *p, + char *buffer) +{ + return buffer + sprintf(buffer, "voluntary_ctxt_switches:\t%lu\n" + "nonvoluntary_ctxt_switches:\t%lu\n", + p->nvcsw, + p->nivcsw); +} + int proc_pid_status(struct task_struct *task, char * buffer) { char * orig = buffer; @@ -307,6 +316,7 @@ int proc_pid_status(struct task_struct *task, char * buffer) #if defined(CONFIG_S390) buffer = task_show_regs(task, buffer); #endif + buffer = task_context_switch_counts(task, buffer); return buffer - orig; } @@ -440,8 +450,9 @@ static int do_task_stat(struct task_struct *task, char * buffer, int whole) /* Temporary variable needed for gcc-2.96 */ /* convert timespec -> nsec*/ - start_time = (unsigned long long)task->start_time.tv_sec * NSEC_PER_SEC - + task->start_time.tv_nsec; + start_time = + (unsigned long long)task->real_start_time.tv_sec * NSEC_PER_SEC + + task->real_start_time.tv_nsec; /* convert nsec -> ticks */ start_time = nsec_to_clock_t(start_time); diff --git a/fs/proc/base.c b/fs/proc/base.c index 46ea5d56e1bb..ae3627337a92 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -67,7 +67,6 @@ #include <linux/mount.h> #include <linux/security.h> #include <linux/ptrace.h> -#include <linux/seccomp.h> #include <linux/cpuset.h> #include <linux/audit.h> #include <linux/poll.h> @@ -204,12 +203,17 @@ static int proc_pid_environ(struct task_struct *task, char * buffer) int res = 0; struct mm_struct *mm = get_task_mm(task); if (mm) { - unsigned int len = mm->env_end - mm->env_start; + unsigned int len; + + res = -ESRCH; + if (!ptrace_may_attach(task)) + goto out; + + len = mm->env_end - mm->env_start; if (len > PAGE_SIZE) len = PAGE_SIZE; res = access_process_vm(task, mm->env_start, buffer, len, 0); - if (!ptrace_may_attach(task)) - res = -ESRCH; +out: mmput(mm); } return res; @@ -812,71 +816,6 @@ static const struct file_operations proc_loginuid_operations = { }; #endif -#ifdef CONFIG_SECCOMP -static ssize_t seccomp_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct task_struct *tsk = get_proc_task(file->f_dentry->d_inode); - char __buf[20]; - size_t len; - - if (!tsk) - return -ESRCH; - /* no need to print the trailing zero, so use only len */ - len = sprintf(__buf, "%u\n", tsk->seccomp.mode); - put_task_struct(tsk); - - return simple_read_from_buffer(buf, count, ppos, __buf, len); -} - -static ssize_t seccomp_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct task_struct *tsk = get_proc_task(file->f_dentry->d_inode); - char __buf[20], *end; - unsigned int seccomp_mode; - ssize_t result; - - result = -ESRCH; - if (!tsk) - goto out_no_task; - - /* can set it only once to be even more secure */ - result = -EPERM; - if (unlikely(tsk->seccomp.mode)) - goto out; - - result = -EFAULT; - memset(__buf, 0, sizeof(__buf)); - count = min(count, sizeof(__buf) - 1); - if (copy_from_user(__buf, buf, count)) - goto out; - - seccomp_mode = simple_strtoul(__buf, &end, 0); - if (*end == '\n') - end++; - result = -EINVAL; - if (seccomp_mode && seccomp_mode <= NR_SECCOMP_MODES) { - tsk->seccomp.mode = seccomp_mode; - set_tsk_thread_flag(tsk, TIF_SECCOMP); - } else - goto out; - result = -EIO; - if (unlikely(!(end - __buf))) - goto out; - result = end - __buf; -out: - put_task_struct(tsk); -out_no_task: - return result; -} - -static const struct file_operations proc_seccomp_operations = { - .read = seccomp_read, - .write = seccomp_write, -}; -#endif /* CONFIG_SECCOMP */ - #ifdef CONFIG_FAULT_INJECTION static ssize_t proc_fault_inject_read(struct file * file, char __user * buf, size_t count, loff_t *ppos) @@ -2037,9 +1976,6 @@ static const struct pid_entry tgid_base_stuff[] = { REG("numa_maps", S_IRUGO, numa_maps), #endif REG("mem", S_IRUSR|S_IWUSR, mem), -#ifdef CONFIG_SECCOMP - REG("seccomp", S_IRUSR|S_IWUSR, seccomp), -#endif LNK("cwd", cwd), LNK("root", root), LNK("exe", exe), @@ -2324,9 +2260,6 @@ static const struct pid_entry tid_base_stuff[] = { REG("numa_maps", S_IRUGO, numa_maps), #endif REG("mem", S_IRUSR|S_IWUSR, mem), -#ifdef CONFIG_SECCOMP - REG("seccomp", S_IRUSR|S_IWUSR, seccomp), -#endif LNK("cwd", cwd), LNK("root", root), LNK("exe", exe), diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 8a40e15f5ecb..b5e7155d30d8 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -20,6 +20,7 @@ #include <linux/namei.h> #include <linux/bitops.h> #include <linux/spinlock.h> +#include <linux/completion.h> #include <asm/uaccess.h> #include "internal.h" @@ -529,12 +530,6 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp return -EAGAIN; dp->low_ino = i; - spin_lock(&proc_subdir_lock); - dp->next = dir->subdir; - dp->parent = dir; - dir->subdir = dp; - spin_unlock(&proc_subdir_lock); - if (S_ISDIR(dp->mode)) { if (dp->proc_iops == NULL) { dp->proc_fops = &proc_dir_operations; @@ -550,6 +545,13 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp if (dp->proc_iops == NULL) dp->proc_iops = &proc_file_inode_operations; } + + spin_lock(&proc_subdir_lock); + dp->next = dir->subdir; + dp->parent = dir; + dir->subdir = dp; + spin_unlock(&proc_subdir_lock); + return 0; } @@ -613,6 +615,9 @@ static struct proc_dir_entry *proc_create(struct proc_dir_entry **parent, ent->namelen = len; ent->mode = mode; ent->nlink = nlink; + ent->pde_users = 0; + spin_lock_init(&ent->pde_unload_lock); + ent->pde_unload_completion = NULL; out: return ent; } @@ -649,9 +654,6 @@ struct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode, ent = proc_create(&parent, name, S_IFDIR | mode, 2); if (ent) { - ent->proc_fops = &proc_dir_operations; - ent->proc_iops = &proc_dir_inode_operations; - if (proc_register(parent, ent) < 0) { kfree(ent); ent = NULL; @@ -686,10 +688,6 @@ struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, ent = proc_create(&parent,name,mode,nlink); if (ent) { - if (S_ISDIR(mode)) { - ent->proc_fops = &proc_dir_operations; - ent->proc_iops = &proc_dir_inode_operations; - } if (proc_register(parent, ent) < 0) { kfree(ent); ent = NULL; @@ -734,9 +732,35 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) de = *p; *p = de->next; de->next = NULL; + + spin_lock(&de->pde_unload_lock); + /* + * Stop accepting new callers into module. If you're + * dynamically allocating ->proc_fops, save a pointer somewhere. + */ + de->proc_fops = NULL; + /* Wait until all existing callers into module are done. */ + if (de->pde_users > 0) { + DECLARE_COMPLETION_ONSTACK(c); + + if (!de->pde_unload_completion) + de->pde_unload_completion = &c; + + spin_unlock(&de->pde_unload_lock); + spin_unlock(&proc_subdir_lock); + + wait_for_completion(de->pde_unload_completion); + + spin_lock(&proc_subdir_lock); + goto continue_removing; + } + spin_unlock(&de->pde_unload_lock); + +continue_removing: if (S_ISDIR(de->mode)) parent->nlink--; - proc_kill_inodes(de); + if (!S_ISREG(de->mode)) + proc_kill_inodes(de); de->nlink = 0; WARN_ON(de->subdir); if (!atomic_read(&de->count)) diff --git a/fs/proc/inode.c b/fs/proc/inode.c index d5ce65c68d7b..dd28e86ab422 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -10,6 +10,7 @@ #include <linux/mm.h> #include <linux/string.h> #include <linux/stat.h> +#include <linux/completion.h> #include <linux/file.h> #include <linux/limits.h> #include <linux/init.h> @@ -140,6 +141,251 @@ static const struct super_operations proc_sops = { .remount_fs = proc_remount, }; +static void pde_users_dec(struct proc_dir_entry *pde) +{ + spin_lock(&pde->pde_unload_lock); + pde->pde_users--; + if (pde->pde_unload_completion && pde->pde_users == 0) + complete(pde->pde_unload_completion); + spin_unlock(&pde->pde_unload_lock); +} + +static loff_t proc_reg_llseek(struct file *file, loff_t offset, int whence) +{ + struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); + loff_t rv = -EINVAL; + loff_t (*llseek)(struct file *, loff_t, int); + + spin_lock(&pde->pde_unload_lock); + /* + * remove_proc_entry() is going to delete PDE (as part of module + * cleanup sequence). No new callers into module allowed. + */ + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + /* + * Bump refcount so that remove_proc_entry will wail for ->llseek to + * complete. + */ + pde->pde_users++; + /* + * Save function pointer under lock, to protect against ->proc_fops + * NULL'ifying right after ->pde_unload_lock is dropped. + */ + llseek = pde->proc_fops->llseek; + spin_unlock(&pde->pde_unload_lock); + + if (!llseek) + llseek = default_llseek; + rv = llseek(file, offset, whence); + + pde_users_dec(pde); + return rv; +} + +static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); + ssize_t rv = -EIO; + ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); + + spin_lock(&pde->pde_unload_lock); + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + pde->pde_users++; + read = pde->proc_fops->read; + spin_unlock(&pde->pde_unload_lock); + + if (read) + rv = read(file, buf, count, ppos); + + pde_users_dec(pde); + return rv; +} + +static ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +{ + struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); + ssize_t rv = -EIO; + ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *); + + spin_lock(&pde->pde_unload_lock); + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + pde->pde_users++; + write = pde->proc_fops->write; + spin_unlock(&pde->pde_unload_lock); + + if (write) + rv = write(file, buf, count, ppos); + + pde_users_dec(pde); + return rv; +} + +static unsigned int proc_reg_poll(struct file *file, struct poll_table_struct *pts) +{ + struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); + unsigned int rv = 0; + unsigned int (*poll)(struct file *, struct poll_table_struct *); + + spin_lock(&pde->pde_unload_lock); + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + pde->pde_users++; + poll = pde->proc_fops->poll; + spin_unlock(&pde->pde_unload_lock); + + if (poll) + rv = poll(file, pts); + + pde_users_dec(pde); + return rv; +} + +static long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); + long rv = -ENOTTY; + long (*unlocked_ioctl)(struct file *, unsigned int, unsigned long); + int (*ioctl)(struct inode *, struct file *, unsigned int, unsigned long); + + spin_lock(&pde->pde_unload_lock); + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + pde->pde_users++; + unlocked_ioctl = pde->proc_fops->unlocked_ioctl; + ioctl = pde->proc_fops->ioctl; + spin_unlock(&pde->pde_unload_lock); + + if (unlocked_ioctl) { + rv = unlocked_ioctl(file, cmd, arg); + if (rv == -ENOIOCTLCMD) + rv = -EINVAL; + } else if (ioctl) { + lock_kernel(); + rv = ioctl(file->f_path.dentry->d_inode, file, cmd, arg); + unlock_kernel(); + } + + pde_users_dec(pde); + return rv; +} + +#ifdef CONFIG_COMPAT +static long proc_reg_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); + long rv = -ENOTTY; + long (*compat_ioctl)(struct file *, unsigned int, unsigned long); + + spin_lock(&pde->pde_unload_lock); + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + pde->pde_users++; + compat_ioctl = pde->proc_fops->compat_ioctl; + spin_unlock(&pde->pde_unload_lock); + + if (compat_ioctl) + rv = compat_ioctl(file, cmd, arg); + + pde_users_dec(pde); + return rv; +} +#endif + +static int proc_reg_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); + int rv = -EIO; + int (*mmap)(struct file *, struct vm_area_struct *); + + spin_lock(&pde->pde_unload_lock); + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + pde->pde_users++; + mmap = pde->proc_fops->mmap; + spin_unlock(&pde->pde_unload_lock); + + if (mmap) + rv = mmap(file, vma); + + pde_users_dec(pde); + return rv; +} + +static int proc_reg_open(struct inode *inode, struct file *file) +{ + struct proc_dir_entry *pde = PDE(inode); + int rv = 0; + int (*open)(struct inode *, struct file *); + + spin_lock(&pde->pde_unload_lock); + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + pde->pde_users++; + open = pde->proc_fops->open; + spin_unlock(&pde->pde_unload_lock); + + if (open) + rv = open(inode, file); + + pde_users_dec(pde); + return rv; +} + +static int proc_reg_release(struct inode *inode, struct file *file) +{ + struct proc_dir_entry *pde = PDE(inode); + int rv = 0; + int (*release)(struct inode *, struct file *); + + spin_lock(&pde->pde_unload_lock); + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + pde->pde_users++; + release = pde->proc_fops->release; + spin_unlock(&pde->pde_unload_lock); + + if (release) + rv = release(inode, file); + + pde_users_dec(pde); + return rv; +} + +static const struct file_operations proc_reg_file_ops = { + .llseek = proc_reg_llseek, + .read = proc_reg_read, + .write = proc_reg_write, + .poll = proc_reg_poll, + .unlocked_ioctl = proc_reg_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = proc_reg_compat_ioctl, +#endif + .mmap = proc_reg_mmap, + .open = proc_reg_open, + .release = proc_reg_release, +}; + struct inode *proc_get_inode(struct super_block *sb, unsigned int ino, struct proc_dir_entry *de) { @@ -166,8 +412,12 @@ struct inode *proc_get_inode(struct super_block *sb, unsigned int ino, inode->i_nlink = de->nlink; if (de->proc_iops) inode->i_op = de->proc_iops; - if (de->proc_fops) - inode->i_fop = de->proc_fops; + if (de->proc_fops) { + if (S_ISREG(inode->i_mode)) + inode->i_fop = &proc_reg_file_ops; + else + inode->i_fop = de->proc_fops; + } } return inode; diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 5fd49e47f83a..d24b8d46059a 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -105,6 +105,7 @@ static int uptime_read_proc(char *page, char **start, off_t off, cputime_t idletime = cputime_add(init_task.utime, init_task.stime); do_posix_clock_monotonic_gettime(&uptime); + monotonic_to_bootbased(&uptime); cputime_to_timespec(idletime, &idle); len = sprintf(page,"%lu.%02lu %lu.%02lu\n", (unsigned long) uptime.tv_sec, @@ -443,12 +444,12 @@ static int show_stat(struct seq_file *p, void *v) unsigned long jif; cputime64_t user, nice, system, idle, iowait, irq, softirq, steal; u64 sum = 0; + struct timespec boottime; user = nice = system = idle = iowait = irq = softirq = steal = cputime64_zero; - jif = - wall_to_monotonic.tv_sec; - if (wall_to_monotonic.tv_nsec) - --jif; + getboottime(&boottime); + jif = boottime.tv_sec; for_each_possible_cpu(i) { int j; diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c index b3a473b0a191..22846225acfa 100644 --- a/fs/proc/proc_tty.c +++ b/fs/proc/proc_tty.c @@ -69,7 +69,7 @@ static void show_tty_range(struct seq_file *m, struct tty_driver *p, static int show_tty_driver(struct seq_file *m, void *v) { - struct tty_driver *p = v; + struct tty_driver *p = list_entry(v, struct tty_driver, tty_drivers); dev_t from = MKDEV(p->major, p->minor_start); dev_t to = from + p->num; @@ -106,22 +106,13 @@ static int show_tty_driver(struct seq_file *m, void *v) /* iterator */ static void *t_start(struct seq_file *m, loff_t *pos) { - struct list_head *p; - loff_t l = *pos; - mutex_lock(&tty_mutex); - list_for_each(p, &tty_drivers) - if (!l--) - return list_entry(p, struct tty_driver, tty_drivers); - return NULL; + return seq_list_start(&tty_drivers, *pos); } static void *t_next(struct seq_file *m, void *v, loff_t *pos) { - struct list_head *p = ((struct tty_driver *)v)->tty_drivers.next; - (*pos)++; - return p==&tty_drivers ? NULL : - list_entry(p, struct tty_driver, tty_drivers); + return seq_list_next(v, &tty_drivers, pos); } static void t_stop(struct seq_file *m, void *v) |