/* * 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 <asm/uaccess.h> #include <linux/errno.h>#include <linux/time.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <linux/init.h>#include <linux/capability.h>#include <linux/file.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/smp_lock.h>#include <linux/rcupdate.h>#include <linux/kallsyms.h>#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>#include <linux/nsproxy.h>#include <linux/oom.h>#include "internal.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. */ /* Worst case buffer size needed for holding an integer. */#define PROC_NUMBUF 13 struct pid_entry { int len; char *name; mode_t mode; const struct inode_operations *iop; const struct file_operations *fop; union proc_op op;}; #define NOD(NAME, MODE, IOP, FOP, OP) { \ .len = sizeof(NAME) - 1, \ .name = (NAME), \ .mode = MODE, \ .iop = IOP, \ .fop = FOP, \ .op = OP, \} #define DIR(NAME, MODE, OTYPE) \ NOD(NAME, (S_IFDIR|(MODE)), \ &proc_##OTYPE##_inode_operations, &proc_##OTYPE##_operations, \ {} )#define LNK(NAME, OTYPE) \ NOD(NAME, (S_IFLNK|S_IRWXUGO), \ &proc_pid_link_inode_operations, NULL, \ { .proc_get_link = &proc_##OTYPE##_link } )#define REG(NAME, MODE, OTYPE) \ NOD(NAME, (S_IFREG|(MODE)), NULL, \ &proc_##OTYPE##_operations, {})#define INF(NAME, MODE, OTYPE) \ NOD(NAME, (S_IFREG|(MODE)), \ NULL, &proc_info_file_operations, \ { .proc_read = &proc_##OTYPE } ) static struct fs_struct *get_fs_struct(struct task_struct *task){ struct fs_struct *fs; task_lock(task); fs = task->fs; if(fs) atomic_inc(&fs->count); task_unlock(task); return fs;} static int get_nr_threads(struct task_struct *tsk){ /* Must be called with the rcu_read_lock held */ unsigned long flags; int count = 0; if (lock_task_sighand(tsk, &flags)) { count = atomic_read(&tsk->signal->count); unlock_task_sighand(tsk, &flags); } return count;} static int proc_cwd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt){ struct task_struct *task = get_proc_task(inode); struct fs_struct *fs = NULL; int result = -ENOENT; if (task) { fs = get_fs_struct(task); put_task_struct(task); } if (fs) { read_lock(&fs->lock); *mnt = mntget(fs->pwdmnt); *dentry = dget(fs->pwd); read_unlock(&fs->lock); result = 0; put_fs_struct(fs); } return result;} static int proc_root_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt){ struct task_struct *task = get_proc_task(inode); struct fs_struct *fs = NULL; int result = -ENOENT; if (task) { fs = get_fs_struct(task); put_task_struct(task); } if (fs) { read_lock(&fs->lock); *mnt = mntget(fs->rootmnt); *dentry = dget(fs->root); read_unlock(&fs->lock); result = 0; put_fs_struct(fs); } return result;} #define MAY_PTRACE(task) \ (task == current || \ (task->parent == current && \ (task->ptrace & PT_PTRACED) && \ (task->state == TASK_STOPPED || task->state == TASK_TRACED) && \ security_ptrace(current,task) == 0)) 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; 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; mmput(mm); } return res;} static int proc_pid_cmdline(struct task_struct *task, char * buffer){ int res = 0; unsigned int len; struct mm_struct *mm = get_task_mm(task); if (!mm) goto out; if (!mm->arg_end) goto out_mm; /* Shh! No looking before we're done */ len = mm->arg_end - mm->arg_start; if (len > PAGE_SIZE) len = PAGE_SIZE; res = access_process_vm(task, mm->arg_start, buffer, len, 0); // If the nul at the end of args has been overwritten, then // assume application is using setproctitle(3). if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) { len = strnlen(buffer, res); if (len < res) { res = len; } else { len = mm->env_end - mm->env_start; if (len > PAGE_SIZE - res) len = PAGE_SIZE - res; res += access_process_vm(task, mm->env_start, buffer+res, len, 0); res = strnlen(buffer, res); } }out_mm: mmput(mm);out: return res;} static int proc_pid_auxv(struct task_struct *task, char *buffer){ int res = 0; struct mm_struct *mm = get_task_mm(task); if (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); mmput(mm); } return res;} #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 task_struct *task, char *buffer){ char *modname; const char *sym_name; unsigned long wchan, size, offset; char namebuf[KSYM_NAME_LEN+1]; wchan = get_wchan(task); sym_name = kallsyms_lookup(wchan, &size, &offset, &modname, namebuf); if (sym_name) return sprintf(buffer, "%s", sym_name); return sprintf(buffer, "%lu", wchan);}#endif /* CONFIG_KALLSYMS */ #ifdef CONFIG_SCHEDSTATS/* * Provides /proc/PID/schedstat */static int proc_pid_schedstat(struct task_struct *task, char *buffer){ return sprintf(buffer, "%lu %lu %lu\n", task->sched_info.cpu_time, task->sched_info.run_delay, task->sched_info.pcnt);}#endif /* The badness from the OOM killer */unsigned long badness(struct task_struct *p, unsigned long uptime);static int proc_oom_score(struct task_struct *task, char *buffer){ unsigned long points; struct timespec uptime; do_posix_clock_monotonic_gettime(&uptime); points = badness(task, uptime.tv_sec); return sprintf(buffer, "%lu\n", points);} /************************************************************************//* Here the fs part begins *//************************************************************************/ /* permission checks */static int proc_fd_access_allowed(struct inode *inode){ struct task_struct *task; int allowed = 0; /* Allow access to a task's file descriptors if it is us or we * may use ptrace attach to the process and find out that * information. */ task = get_proc_task(inode); if (task) { allowed = ptrace_may_attach(task); put_task_struct(task); } return allowed;} static int proc_setattr(struct dentry *dentry, struct iattr *attr){ int error; struct inode *inode = dentry->d_inode; if (attr->ia_valid & ATTR_MODE) return -EPERM; error = inode_change_ok(inode, attr); if (!error) { error = security_inode_setattr(dentry, attr); if (!error) error = inode_setattr(inode, attr); } return error;} static const struct inode_operations proc_def_inode_operations = { .setattr = proc_setattr,}; extern struct seq_operations mounts_op;struct proc_mounts { struct seq_file m; int event;}; static int mounts_open(struct inode *inode, struct file *file){ struct task_struct *task = get_proc_task(inode); struct mnt_namespace *ns = NULL; struct proc_mounts *p; int ret = -EINVAL; if (task) { task_lock(task); if (task->nsproxy) { ns = task->nsproxy->mnt_ns; if (ns) get_mnt_ns(ns); } task_unlock(task); put_task_struct(task); } if (ns) { ret = -ENOMEM; p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL); if (p) { file->private_data = &p->m; ret = seq_open(file, &mounts_op); if (!ret) { p->m.private = ns; p->event = ns->event; return 0; } kfree(p); } put_mnt_ns(ns); } return ret;} static int mounts_release(struct inode *inode, struct file *file){ struct seq_file *m = file->private_data; struct mnt_namespace *ns = m->private; put_mnt_ns(ns); return seq_release(inode, file);} static unsigned mounts_poll(struct file *file, poll_table *wait){ struct proc_mounts *p = file->private_data; struct mnt_namespace *ns = p->m.private; unsigned res = 0; poll_wait(file, &ns->poll, wait); spin_lock(&vfsmount_lock); if (p->event != ns->event) { p->event = ns->event; res = POLLERR; } spin_unlock(&vfsmount_lock); return res;} static const struct file_operations proc_mounts_operations = { .open = mounts_open, .read = seq_read, .llseek = seq_lseek, .release = mounts_release, .poll = mounts_poll,}; extern struct seq_operations mountstats_op;static int mountstats_open(struct inode *inode, struct file *file){ int ret = seq_open(file, &mountstats_op); if (!ret) { struct seq_file *m = file->private_data; struct mnt_namespace *mnt_ns = NULL; struct task_struct *task = get_proc_task(inode); if (task) { task_lock(task); if (task->nsproxy) mnt_ns = task->nsproxy->mnt_ns; if (mnt_ns) get_mnt_ns(mnt_ns); task_unlock(task); put_task_struct(task); } if (mnt_ns) m->private = mnt_ns; else { seq_release(inode, file); ret = -EINVAL; } } return ret;} static const struct file_operations proc_mountstats_operations = { .open = mountstats_open, .read = seq_read, .llseek = seq_lseek, .release = mounts_release,}; #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->f_path.dentry->d_inode; 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_KERNEL))) 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:Lines 1–500 of 2411. Load more
/* * 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 <asm/uaccess.h> #include <linux/errno.h> #include <linux/time.h> #include <linux/proc_fs.h> #include <linux/stat.h> #include <linux/init.h> #include <linux/capability.h> #include <linux/file.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/smp_lock.h> #include <linux/rcupdate.h> #include <linux/kallsyms.h> #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> #include <linux/nsproxy.h> #include <linux/oom.h> #include "internal.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. */ /* Worst case buffer size needed for holding an integer. */ #define PROC_NUMBUF 13 struct pid_entry { int len; char *name; mode_t mode; const struct inode_operations *iop; const struct file_operations *fop; union proc_op op; }; #define NOD(NAME, MODE, IOP, FOP, OP) { \ .len = sizeof(NAME) - 1, \ .name = (NAME), \ .mode = MODE, \ .iop = IOP, \ .fop = FOP, \ .op = OP, \ } #define DIR(NAME, MODE, OTYPE) \ NOD(NAME, (S_IFDIR|(MODE)), \ &proc_##OTYPE##_inode_operations, &proc_##OTYPE##_operations, \ {} ) #define LNK(NAME, OTYPE) \ NOD(NAME, (S_IFLNK|S_IRWXUGO), \ &proc_pid_link_inode_operations, NULL, \ { .proc_get_link = &proc_##OTYPE##_link } ) #define REG(NAME, MODE, OTYPE) \ NOD(NAME, (S_IFREG|(MODE)), NULL, \ &proc_##OTYPE##_operations, {}) #define INF(NAME, MODE, OTYPE) \ NOD(NAME, (S_IFREG|(MODE)), \ NULL, &proc_info_file_operations, \ { .proc_read = &proc_##OTYPE } ) static struct fs_struct *get_fs_struct(struct task_struct *task) { struct fs_struct *fs; task_lock(task); fs = task->fs; if(fs) atomic_inc(&fs->count); task_unlock(task); return fs; } static int get_nr_threads(struct task_struct *tsk) { /* Must be called with the rcu_read_lock held */ unsigned long flags; int count = 0; if (lock_task_sighand(tsk, &flags)) { count = atomic_read(&tsk->signal->count); unlock_task_sighand(tsk, &flags); } return count; } static int proc_cwd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt) { struct task_struct *task = get_proc_task(inode); struct fs_struct *fs = NULL; int result = -ENOENT; if (task) { fs = get_fs_struct(task); put_task_struct(task); } if (fs) { read_lock(&fs->lock); *mnt = mntget(fs->pwdmnt); *dentry = dget(fs->pwd); read_unlock(&fs->lock); result = 0; put_fs_struct(fs); } return result; } static int proc_root_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt) { struct task_struct *task = get_proc_task(inode); struct fs_struct *fs = NULL; int result = -ENOENT; if (task) { fs = get_fs_struct(task); put_task_struct(task); } if (fs) { read_lock(&fs->lock); *mnt = mntget(fs->rootmnt); *dentry = dget(fs->root); read_unlock(&fs->lock); result = 0; put_fs_struct(fs); } return result; } #define MAY_PTRACE(task) \ (task == current || \ (task->parent == current && \ (task->ptrace & PT_PTRACED) && \ (task->state == TASK_STOPPED || task->state == TASK_TRACED) && \ security_ptrace(current,task) == 0)) 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; 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; mmput(mm); } return res; } static int proc_pid_cmdline(struct task_struct *task, char * buffer) { int res = 0; unsigned int len; struct mm_struct *mm = get_task_mm(task); if (!mm) goto out; if (!mm->arg_end) goto out_mm; /* Shh! No looking before we're done */ len = mm->arg_end - mm->arg_start; if (len > PAGE_SIZE) len = PAGE_SIZE; res = access_process_vm(task, mm->arg_start, buffer, len, 0); // If the nul at the end of args has been overwritten, then // assume application is using setproctitle(3). if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) { len = strnlen(buffer, res); if (len < res) { res = len; } else { len = mm->env_end - mm->env_start; if (len > PAGE_SIZE - res) len = PAGE_SIZE - res; res += access_process_vm(task, mm->env_start, buffer+res, len, 0); res = strnlen(buffer, res); } } out_mm: mmput(mm); out: return res; } static int proc_pid_auxv(struct task_struct *task, char *buffer) { int res = 0; struct mm_struct *mm = get_task_mm(task); if (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); mmput(mm); } return res; } #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 task_struct *task, char *buffer) { char *modname; const char *sym_name; unsigned long wchan, size, offset; char namebuf[KSYM_NAME_LEN+1]; wchan = get_wchan(task); sym_name = kallsyms_lookup(wchan, &size, &offset, &modname, namebuf); if (sym_name) return sprintf(buffer, "%s", sym_name); return sprintf(buffer, "%lu", wchan); } #endif /* CONFIG_KALLSYMS */ #ifdef CONFIG_SCHEDSTATS /* * Provides /proc/PID/schedstat */ static int proc_pid_schedstat(struct task_struct *task, char *buffer) { return sprintf(buffer, "%lu %lu %lu\n", task->sched_info.cpu_time, task->sched_info.run_delay, task->sched_info.pcnt); } #endif /* The badness from the OOM killer */ unsigned long badness(struct task_struct *p, unsigned long uptime); static int proc_oom_score(struct task_struct *task, char *buffer) { unsigned long points; struct timespec uptime; do_posix_clock_monotonic_gettime(&uptime); points = badness(task, uptime.tv_sec); return sprintf(buffer, "%lu\n", points); } /************************************************************************/ /* Here the fs part begins */ /************************************************************************/ /* permission checks */ static int proc_fd_access_allowed(struct inode *inode) { struct task_struct *task; int allowed = 0; /* Allow access to a task's file descriptors if it is us or we * may use ptrace attach to the process and find out that * information. */ task = get_proc_task(inode); if (task) { allowed = ptrace_may_attach(task); put_task_struct(task); } return allowed; } static int proc_setattr(struct dentry *dentry, struct iattr *attr) { int error; struct inode *inode = dentry->d_inode; if (attr->ia_valid & ATTR_MODE) return -EPERM; error = inode_change_ok(inode, attr); if (!error) { error = security_inode_setattr(dentry, attr); if (!error) error = inode_setattr(inode, attr); } return error; } static const struct inode_operations proc_def_inode_operations = { .setattr = proc_setattr, }; extern struct seq_operations mounts_op; struct proc_mounts { struct seq_file m; int event; }; static int mounts_open(struct inode *inode, struct file *file) { struct task_struct *task = get_proc_task(inode); struct mnt_namespace *ns = NULL; struct proc_mounts *p; int ret = -EINVAL; if (task) { task_lock(task); if (task->nsproxy) { ns = task->nsproxy->mnt_ns; if (ns) get_mnt_ns(ns); } task_unlock(task); put_task_struct(task); } if (ns) { ret = -ENOMEM; p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL); if (p) { file->private_data = &p->m; ret = seq_open(file, &mounts_op); if (!ret) { p->m.private = ns; p->event = ns->event; return 0; } kfree(p); } put_mnt_ns(ns); } return ret; } static int mounts_release(struct inode *inode, struct file *file) { struct seq_file *m = file->private_data; struct mnt_namespace *ns = m->private; put_mnt_ns(ns); return seq_release(inode, file); } static unsigned mounts_poll(struct file *file, poll_table *wait) { struct proc_mounts *p = file->private_data; struct mnt_namespace *ns = p->m.private; unsigned res = 0; poll_wait(file, &ns->poll, wait); spin_lock(&vfsmount_lock); if (p->event != ns->event) { p->event = ns->event; res = POLLERR; } spin_unlock(&vfsmount_lock); return res; } static const struct file_operations proc_mounts_operations = { .open = mounts_open, .read = seq_read, .llseek = seq_lseek, .release = mounts_release, .poll = mounts_poll, }; extern struct seq_operations mountstats_op; static int mountstats_open(struct inode *inode, struct file *file) { int ret = seq_open(file, &mountstats_op); if (!ret) { struct seq_file *m = file->private_data; struct mnt_namespace *mnt_ns = NULL; struct task_struct *task = get_proc_task(inode); if (task) { task_lock(task); if (task->nsproxy) mnt_ns = task->nsproxy->mnt_ns; if (mnt_ns) get_mnt_ns(mnt_ns); task_unlock(task); put_task_struct(task); } if (mnt_ns) m->private = mnt_ns; else { seq_release(inode, file); ret = -EINVAL; } } return ret; } static const struct file_operations proc_mountstats_operations = { .open = mountstats_open, .read = seq_read, .llseek = seq_lseek, .release = mounts_release, }; #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->f_path.dentry->d_inode; 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_KERNEL))) 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: