try a saner locking for pde_opener...
Al Viro [Thu, 4 Apr 2013 20:28:47 +0000 (16:28 -0400)]
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

fs/proc/inode.c
fs/proc/internal.h
include/linux/proc_fs.h

index b5b204d..3b14a45 100644 (file)
@@ -133,67 +133,48 @@ enum {BIAS = -1U<<31};
 
 static inline int use_pde(struct proc_dir_entry *pde)
 {
-       int res = 1;
-       spin_lock(&pde->pde_unload_lock);
-       if (unlikely(pde->pde_users < 0))
-               res = 0;
-       else
-               pde->pde_users++;
-       spin_unlock(&pde->pde_unload_lock);
-       return res;
-}
-
-static void __pde_users_dec(struct proc_dir_entry *pde)
-{
-       if (--pde->pde_users == BIAS)
-               complete(pde->pde_unload_completion);
+       return atomic_inc_unless_negative(&pde->in_use);
 }
 
 static void unuse_pde(struct proc_dir_entry *pde)
 {
-       spin_lock(&pde->pde_unload_lock);
-       __pde_users_dec(pde);
-       spin_unlock(&pde->pde_unload_lock);
+       if (atomic_dec_return(&pde->in_use) == BIAS)
+               complete(pde->pde_unload_completion);
 }
 
 /* pde is locked */
 static void close_pdeo(struct proc_dir_entry *pde, struct pde_opener *pdeo)
 {
-       pdeo->count++;
-       if (!mutex_trylock(&pdeo->mutex)) {
+       if (pdeo->closing) {
                /* somebody else is doing that, just wait */
+               DECLARE_COMPLETION_ONSTACK(c);
+               pdeo->c = &c;
                spin_unlock(&pde->pde_unload_lock);
-               mutex_lock(&pdeo->mutex);
+               wait_for_completion(&c);
                spin_lock(&pde->pde_unload_lock);
-               WARN_ON(!list_empty(&pdeo->lh));
        } else {
                struct file *file;
+               pdeo->closing = 1;
                spin_unlock(&pde->pde_unload_lock);
                file = pdeo->file;
                pde->proc_fops->release(file_inode(file), file);
                spin_lock(&pde->pde_unload_lock);
                list_del_init(&pdeo->lh);
-       }
-       mutex_unlock(&pdeo->mutex);
-       if (!--pdeo->count)
+               if (pdeo->c)
+                       complete(pdeo->c);
                kfree(pdeo);
+       }
 }
 
 void proc_entry_rundown(struct proc_dir_entry *de)
 {
-       spin_lock(&de->pde_unload_lock);
-       de->pde_users += BIAS;
+       DECLARE_COMPLETION_ONSTACK(c);
        /* Wait until all existing callers into module are done. */
-       if (de->pde_users != BIAS) {
-               DECLARE_COMPLETION_ONSTACK(c);
-               de->pde_unload_completion = &c;
-               spin_unlock(&de->pde_unload_lock);
-
-               wait_for_completion(de->pde_unload_completion);
-
-               spin_lock(&de->pde_unload_lock);
-       }
+       de->pde_unload_completion = &c;
+       if (atomic_add_return(BIAS, &de->in_use) != BIAS)
+               wait_for_completion(&c);
 
+       spin_lock(&de->pde_unload_lock);
        while (!list_empty(&de->pde_openers)) {
                struct pde_opener *pdeo;
                pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh);
@@ -356,7 +337,7 @@ static int proc_reg_open(struct inode *inode, struct file *file)
         * by hand in remove_proc_entry(). For this, save opener's credentials
         * for later.
         */
-       pdeo = kmalloc(sizeof(struct pde_opener), GFP_KERNEL);
+       pdeo = kzalloc(sizeof(struct pde_opener), GFP_KERNEL);
        if (!pdeo)
                return -ENOMEM;
 
@@ -370,18 +351,17 @@ static int proc_reg_open(struct inode *inode, struct file *file)
        if (open)
                rv = open(inode, file);
 
-       spin_lock(&pde->pde_unload_lock);
        if (rv == 0 && release) {
                /* To know what to release. */
-               mutex_init(&pdeo->mutex);
-               pdeo->count = 0;
                pdeo->file = file;
                /* Strictly for "too late" ->release in proc_reg_release(). */
+               spin_lock(&pde->pde_unload_lock);
                list_add(&pdeo->lh, &pde->pde_openers);
+               spin_unlock(&pde->pde_unload_lock);
        } else
                kfree(pdeo);
-       __pde_users_dec(pde);
-       spin_unlock(&pde->pde_unload_lock);
+
+       unuse_pde(pde);
        return rv;
 }
 
index e2fa934..46a7e2a 100644 (file)
@@ -153,8 +153,8 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent,
 struct pde_opener {
        struct file *file;
        struct list_head lh;
-       int count;      /* number of threads in close_pdeo() */
-       struct mutex mutex;
+       int closing;
+       struct completion *c;
 };
 
 ssize_t __proc_file_read(struct file *, char __user *, size_t, loff_t *);
index 947ae7e..2781e49 100644 (file)
@@ -65,7 +65,7 @@ struct proc_dir_entry {
        void *data;
        read_proc_t *read_proc;
        atomic_t count;         /* use count */
-       int pde_users;  /* number of callers into module in progress; */
+       atomic_t in_use;        /* number of callers into module in progress; */
                        /* negative -> it's going away RSN */
        struct completion *pde_unload_completion;
        struct list_head pde_openers;   /* who did ->open, but not ->release */