Commit 05c0ae21 authored by Al Viro's avatar Al Viro

try a saner locking for pde_opener...

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent ca469f35
...@@ -133,67 +133,48 @@ enum {BIAS = -1U<<31}; ...@@ -133,67 +133,48 @@ enum {BIAS = -1U<<31};
static inline int use_pde(struct proc_dir_entry *pde) static inline int use_pde(struct proc_dir_entry *pde)
{ {
int res = 1; return atomic_inc_unless_negative(&pde->in_use);
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);
} }
static void unuse_pde(struct proc_dir_entry *pde) static void unuse_pde(struct proc_dir_entry *pde)
{ {
spin_lock(&pde->pde_unload_lock); if (atomic_dec_return(&pde->in_use) == BIAS)
__pde_users_dec(pde); complete(pde->pde_unload_completion);
spin_unlock(&pde->pde_unload_lock);
} }
/* pde is locked */ /* pde is locked */
static void close_pdeo(struct proc_dir_entry *pde, struct pde_opener *pdeo) static void close_pdeo(struct proc_dir_entry *pde, struct pde_opener *pdeo)
{ {
pdeo->count++; if (pdeo->closing) {
if (!mutex_trylock(&pdeo->mutex)) {
/* somebody else is doing that, just wait */ /* somebody else is doing that, just wait */
DECLARE_COMPLETION_ONSTACK(c);
pdeo->c = &c;
spin_unlock(&pde->pde_unload_lock); spin_unlock(&pde->pde_unload_lock);
mutex_lock(&pdeo->mutex); wait_for_completion(&c);
spin_lock(&pde->pde_unload_lock); spin_lock(&pde->pde_unload_lock);
WARN_ON(!list_empty(&pdeo->lh));
} else { } else {
struct file *file; struct file *file;
pdeo->closing = 1;
spin_unlock(&pde->pde_unload_lock); spin_unlock(&pde->pde_unload_lock);
file = pdeo->file; file = pdeo->file;
pde->proc_fops->release(file_inode(file), file); pde->proc_fops->release(file_inode(file), file);
spin_lock(&pde->pde_unload_lock); spin_lock(&pde->pde_unload_lock);
list_del_init(&pdeo->lh); list_del_init(&pdeo->lh);
} if (pdeo->c)
mutex_unlock(&pdeo->mutex); complete(pdeo->c);
if (!--pdeo->count)
kfree(pdeo); kfree(pdeo);
}
} }
void proc_entry_rundown(struct proc_dir_entry *de) void proc_entry_rundown(struct proc_dir_entry *de)
{ {
spin_lock(&de->pde_unload_lock);
de->pde_users += BIAS;
/* Wait until all existing callers into module are done. */
if (de->pde_users != BIAS) {
DECLARE_COMPLETION_ONSTACK(c); DECLARE_COMPLETION_ONSTACK(c);
/* Wait until all existing callers into module are done. */
de->pde_unload_completion = &c; de->pde_unload_completion = &c;
spin_unlock(&de->pde_unload_lock); if (atomic_add_return(BIAS, &de->in_use) != BIAS)
wait_for_completion(&c);
wait_for_completion(de->pde_unload_completion);
spin_lock(&de->pde_unload_lock); spin_lock(&de->pde_unload_lock);
}
while (!list_empty(&de->pde_openers)) { while (!list_empty(&de->pde_openers)) {
struct pde_opener *pdeo; struct pde_opener *pdeo;
pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh); 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) ...@@ -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 * by hand in remove_proc_entry(). For this, save opener's credentials
* for later. * for later.
*/ */
pdeo = kmalloc(sizeof(struct pde_opener), GFP_KERNEL); pdeo = kzalloc(sizeof(struct pde_opener), GFP_KERNEL);
if (!pdeo) if (!pdeo)
return -ENOMEM; return -ENOMEM;
...@@ -370,18 +351,17 @@ static int proc_reg_open(struct inode *inode, struct file *file) ...@@ -370,18 +351,17 @@ static int proc_reg_open(struct inode *inode, struct file *file)
if (open) if (open)
rv = open(inode, file); rv = open(inode, file);
spin_lock(&pde->pde_unload_lock);
if (rv == 0 && release) { if (rv == 0 && release) {
/* To know what to release. */ /* To know what to release. */
mutex_init(&pdeo->mutex);
pdeo->count = 0;
pdeo->file = file; pdeo->file = file;
/* Strictly for "too late" ->release in proc_reg_release(). */ /* Strictly for "too late" ->release in proc_reg_release(). */
spin_lock(&pde->pde_unload_lock);
list_add(&pdeo->lh, &pde->pde_openers); list_add(&pdeo->lh, &pde->pde_openers);
spin_unlock(&pde->pde_unload_lock);
} else } else
kfree(pdeo); kfree(pdeo);
__pde_users_dec(pde);
spin_unlock(&pde->pde_unload_lock); unuse_pde(pde);
return rv; return rv;
} }
......
...@@ -153,8 +153,8 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent, ...@@ -153,8 +153,8 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent,
struct pde_opener { struct pde_opener {
struct file *file; struct file *file;
struct list_head lh; struct list_head lh;
int count; /* number of threads in close_pdeo() */ int closing;
struct mutex mutex; struct completion *c;
}; };
ssize_t __proc_file_read(struct file *, char __user *, size_t, loff_t *); ssize_t __proc_file_read(struct file *, char __user *, size_t, loff_t *);
......
...@@ -65,7 +65,7 @@ struct proc_dir_entry { ...@@ -65,7 +65,7 @@ struct proc_dir_entry {
void *data; void *data;
read_proc_t *read_proc; read_proc_t *read_proc;
atomic_t count; /* use count */ 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 */ /* negative -> it's going away RSN */
struct completion *pde_unload_completion; struct completion *pde_unload_completion;
struct list_head pde_openers; /* who did ->open, but not ->release */ struct list_head pde_openers; /* who did ->open, but not ->release */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment