]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/proc/generic.c
procfs: improve scaling in proc
[karo-tx-linux.git] / fs / proc / generic.c
index 4b3b3ffb52f14e234aa3ec99abde281ca41e1817..c5450183ca781711f4dff7189aaac1d19a7af621 100644 (file)
@@ -181,14 +181,16 @@ proc_file_read(struct file *file, char __user *buf, size_t nbytes,
 {
        struct proc_dir_entry *pde = PDE(file_inode(file));
        ssize_t rv = -EIO;
+       const struct file_operations *fops;
 
-       spin_lock(&pde->pde_unload_lock);
-       if (!pde->proc_fops) {
-               spin_unlock(&pde->pde_unload_lock);
+       rcu_read_lock();
+       fops = rcu_dereference(pde->proc_fops);
+       if (!fops) {
+               rcu_read_unlock();
                return rv;
        }
-       pde->pde_users++;
-       spin_unlock(&pde->pde_unload_lock);
+       atomic_inc(&pde->pde_users);
+       rcu_read_unlock();
 
        rv = __proc_file_read(file, buf, nbytes, ppos);
 
@@ -204,13 +206,16 @@ proc_file_write(struct file *file, const char __user *buffer,
        ssize_t rv = -EIO;
 
        if (pde->write_proc) {
-               spin_lock(&pde->pde_unload_lock);
-               if (!pde->proc_fops) {
-                       spin_unlock(&pde->pde_unload_lock);
+               const struct file_operations *fops;
+
+               rcu_read_lock();
+               fops = rcu_dereference(pde->proc_fops);
+               if (!fops) {
+                       rcu_read_unlock();
                        return rv;
                }
-               pde->pde_users++;
-               spin_unlock(&pde->pde_unload_lock);
+               atomic_inc(&pde->pde_users);
+               rcu_read_unlock();
 
                /* FIXME: does this routine need ppos?  probably... */
                rv = pde->write_proc(file, buffer, count, pde->data);
@@ -542,7 +547,7 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp
 
        if (S_ISDIR(dp->mode)) {
                if (dp->proc_iops == NULL) {
-                       dp->proc_fops = &proc_dir_operations;
+                       RCU_INIT_POINTER(dp->proc_fops, &proc_dir_operations);
                        dp->proc_iops = &proc_dir_inode_operations;
                }
                dir->nlink++;
@@ -551,7 +556,7 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp
                        dp->proc_iops = &proc_link_inode_operations;
        } else if (S_ISREG(dp->mode)) {
                if (dp->proc_fops == NULL)
-                       dp->proc_fops = &proc_file_operations;
+                       RCU_INIT_POINTER(dp->proc_fops, &proc_file_operations);
                if (dp->proc_iops == NULL)
                        dp->proc_iops = &proc_file_inode_operations;
        }
@@ -604,7 +609,8 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
        ent->mode = mode;
        ent->nlink = nlink;
        atomic_set(&ent->count, 1);
-       spin_lock_init(&ent->pde_unload_lock);
+       atomic_set(&ent->pde_users, 1);
+       spin_lock_init(&ent->pde_openers_lock);
        INIT_LIST_HEAD(&ent->pde_openers);
 out:
        return ent;
@@ -728,7 +734,7 @@ struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
        pde = __proc_create(&parent, name, mode, nlink);
        if (!pde)
                goto out;
-       pde->proc_fops = proc_fops;
+       rcu_assign_pointer(pde->proc_fops, proc_fops);
        pde->data = data;
        if (proc_register(parent, pde) < 0)
                goto out_free;
@@ -764,6 +770,7 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
        struct proc_dir_entry *de = NULL;
        const char *fn = name;
        unsigned int len;
+       DECLARE_COMPLETION_ONSTACK(c);
 
        spin_lock(&proc_subdir_lock);
        if (__xlate_proc_name(name, &parent, &fn) != 0) {
@@ -786,37 +793,30 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
                return;
        }
 
-       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);
-
+       rcu_assign_pointer(de->proc_fops, NULL);
+       synchronize_rcu();
+       /*
+        * Wait until all existing callers into module are done.
+        * Once pde_users hits zero we are free to clean out pde_openers.
+        */
+       de->pde_unload_completion = &c;
+       if (!atomic_dec_and_test(&de->pde_users))
                wait_for_completion(de->pde_unload_completion);
 
-               spin_lock(&de->pde_unload_lock);
-       }
-
+       spin_lock(&de->pde_openers_lock);
        while (!list_empty(&de->pde_openers)) {
                struct pde_opener *pdeo;
 
                pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh);
                list_del(&pdeo->lh);
-               spin_unlock(&de->pde_unload_lock);
                pdeo->release(pdeo->inode, pdeo->file);
                kfree(pdeo);
-               spin_lock(&de->pde_unload_lock);
        }
-       spin_unlock(&de->pde_unload_lock);
+       spin_unlock(&de->pde_openers_lock);
 
        if (S_ISDIR(de->mode))
                parent->nlink--;