Group short-lived and reclaimable kernel allocations
[sfrench/cifs-2.6.git] / fs / proc / generic.c
index 0cdc00d9d97e5feb9e9e5872029e067527c8dea7..1bdb624357587ae23f31ec3b6f84ef42827cc6f4 100644 (file)
@@ -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"
@@ -32,7 +33,7 @@ static loff_t proc_file_lseek(struct file *, loff_t, int);
 
 DEFINE_SPINLOCK(proc_subdir_lock);
 
-int proc_match(int len, const char *name, struct proc_dir_entry *de)
+static int proc_match(int len, const char *name, struct proc_dir_entry *de)
 {
        if (de->namelen != len)
                return 0;
@@ -73,7 +74,7 @@ proc_file_read(struct file *file, char __user *buf, size_t nbytes,
                nbytes = MAX_NON_LFS - pos;
 
        dp = PDE(inode);
-       if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+       if (!(page = (char*) __get_free_page(GFP_TEMPORARY)))
                return -ENOMEM;
 
        while ((nbytes > 0) && !eof) {
@@ -398,6 +399,7 @@ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry, struct nam
                        if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
                                unsigned int ino = de->low_ino;
 
+                               de_get(de);
                                spin_unlock(&proc_subdir_lock);
                                error = -EINVAL;
                                inode = proc_get_inode(dir->i_sb, ino, de);
@@ -414,6 +416,7 @@ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry, struct nam
                d_add(dentry, inode);
                return NULL;
        }
+       de_put(de);
        return ERR_PTR(error);
 }
 
@@ -476,14 +479,21 @@ int proc_readdir(struct file * filp,
                        }
 
                        do {
+                               struct proc_dir_entry *next;
+
                                /* filldir passes info to user space */
+                               de_get(de);
                                spin_unlock(&proc_subdir_lock);
                                if (filldir(dirent, de->name, de->namelen, filp->f_pos,
-                                           de->low_ino, de->mode >> 12) < 0)
+                                           de->low_ino, de->mode >> 12) < 0) {
+                                       de_put(de);
                                        goto out;
+                               }
                                spin_lock(&proc_subdir_lock);
                                filp->f_pos++;
-                               de = de->next;
+                               next = de->next;
+                               de_put(de);
+                               de = next;
                        } while (de);
                        spin_unlock(&proc_subdir_lock);
        }
@@ -520,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;
@@ -541,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;
 }
 
@@ -604,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;
 }
@@ -640,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;
@@ -677,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;
@@ -725,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))