aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2013-04-04 16:28:47 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2013-04-09 15:16:52 -0400
commit05c0ae21c034a6f7c6f4c0c63a31167ebb4b061f (patch)
tree69c6b35347eeddc49e6ffa2188d4811ce9633c4f /fs
parentca469f35a8e9ef12571a4b80ac6d7fdc0260fb44 (diff)
try a saner locking for pde_opener...
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r--fs/proc/inode.c62
-rw-r--r--fs/proc/internal.h4
2 files changed, 23 insertions, 43 deletions
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index b5b204d6b07f..3b14a45870a9 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -133,67 +133,48 @@ enum {BIAS = -1U<<31};
133 133
134static inline int use_pde(struct proc_dir_entry *pde) 134static inline int use_pde(struct proc_dir_entry *pde)
135{ 135{
136 int res = 1; 136 return atomic_inc_unless_negative(&pde->in_use);
137 spin_lock(&pde->pde_unload_lock);
138 if (unlikely(pde->pde_users < 0))
139 res = 0;
140 else
141 pde->pde_users++;
142 spin_unlock(&pde->pde_unload_lock);
143 return res;
144}
145
146static void __pde_users_dec(struct proc_dir_entry *pde)
147{
148 if (--pde->pde_users == BIAS)
149 complete(pde->pde_unload_completion);
150} 137}
151 138
152static void unuse_pde(struct proc_dir_entry *pde) 139static void unuse_pde(struct proc_dir_entry *pde)
153{ 140{
154 spin_lock(&pde->pde_unload_lock); 141 if (atomic_dec_return(&pde->in_use) == BIAS)
155 __pde_users_dec(pde); 142 complete(pde->pde_unload_completion);
156 spin_unlock(&pde->pde_unload_lock);
157} 143}
158 144
159/* pde is locked */ 145/* pde is locked */
160static void close_pdeo(struct proc_dir_entry *pde, struct pde_opener *pdeo) 146static void close_pdeo(struct proc_dir_entry *pde, struct pde_opener *pdeo)
161{ 147{
162 pdeo->count++; 148 if (pdeo->closing) {
163 if (!mutex_trylock(&pdeo->mutex)) {
164 /* somebody else is doing that, just wait */ 149 /* somebody else is doing that, just wait */
150 DECLARE_COMPLETION_ONSTACK(c);
151 pdeo->c = &c;
165 spin_unlock(&pde->pde_unload_lock); 152 spin_unlock(&pde->pde_unload_lock);
166 mutex_lock(&pdeo->mutex); 153 wait_for_completion(&c);
167 spin_lock(&pde->pde_unload_lock); 154 spin_lock(&pde->pde_unload_lock);
168 WARN_ON(!list_empty(&pdeo->lh));
169 } else { 155 } else {
170 struct file *file; 156 struct file *file;
157 pdeo->closing = 1;
171 spin_unlock(&pde->pde_unload_lock); 158 spin_unlock(&pde->pde_unload_lock);
172 file = pdeo->file; 159 file = pdeo->file;
173 pde->proc_fops->release(file_inode(file), file); 160 pde->proc_fops->release(file_inode(file), file);
174 spin_lock(&pde->pde_unload_lock); 161 spin_lock(&pde->pde_unload_lock);
175 list_del_init(&pdeo->lh); 162 list_del_init(&pdeo->lh);
176 } 163 if (pdeo->c)
177 mutex_unlock(&pdeo->mutex); 164 complete(pdeo->c);
178 if (!--pdeo->count)
179 kfree(pdeo); 165 kfree(pdeo);
166 }
180} 167}
181 168
182void proc_entry_rundown(struct proc_dir_entry *de) 169void proc_entry_rundown(struct proc_dir_entry *de)
183{ 170{
184 spin_lock(&de->pde_unload_lock); 171 DECLARE_COMPLETION_ONSTACK(c);
185 de->pde_users += BIAS;
186 /* Wait until all existing callers into module are done. */ 172 /* Wait until all existing callers into module are done. */
187 if (de->pde_users != BIAS) { 173 de->pde_unload_completion = &c;
188 DECLARE_COMPLETION_ONSTACK(c); 174 if (atomic_add_return(BIAS, &de->in_use) != BIAS)
189 de->pde_unload_completion = &c; 175 wait_for_completion(&c);
190 spin_unlock(&de->pde_unload_lock);
191
192 wait_for_completion(de->pde_unload_completion);
193
194 spin_lock(&de->pde_unload_lock);
195 }
196 176
177 spin_lock(&de->pde_unload_lock);
197 while (!list_empty(&de->pde_openers)) { 178 while (!list_empty(&de->pde_openers)) {
198 struct pde_opener *pdeo; 179 struct pde_opener *pdeo;
199 pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh); 180 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 * by hand in remove_proc_entry(). For this, save opener's credentials 337 * by hand in remove_proc_entry(). For this, save opener's credentials
357 * for later. 338 * for later.
358 */ 339 */
359 pdeo = kmalloc(sizeof(struct pde_opener), GFP_KERNEL); 340 pdeo = kzalloc(sizeof(struct pde_opener), GFP_KERNEL);
360 if (!pdeo) 341 if (!pdeo)
361 return -ENOMEM; 342 return -ENOMEM;
362 343
@@ -370,18 +351,17 @@ static int proc_reg_open(struct inode *inode, struct file *file)
370 if (open) 351 if (open)
371 rv = open(inode, file); 352 rv = open(inode, file);
372 353
373 spin_lock(&pde->pde_unload_lock);
374 if (rv == 0 && release) { 354 if (rv == 0 && release) {
375 /* To know what to release. */ 355 /* To know what to release. */
376 mutex_init(&pdeo->mutex);
377 pdeo->count = 0;
378 pdeo->file = file; 356 pdeo->file = file;
379 /* Strictly for "too late" ->release in proc_reg_release(). */ 357 /* Strictly for "too late" ->release in proc_reg_release(). */
358 spin_lock(&pde->pde_unload_lock);
380 list_add(&pdeo->lh, &pde->pde_openers); 359 list_add(&pdeo->lh, &pde->pde_openers);
360 spin_unlock(&pde->pde_unload_lock);
381 } else 361 } else
382 kfree(pdeo); 362 kfree(pdeo);
383 __pde_users_dec(pde); 363
384 spin_unlock(&pde->pde_unload_lock); 364 unuse_pde(pde);
385 return rv; 365 return rv;
386} 366}
387 367
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index e2fa9345a9a8..46a7e2a7b904 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -153,8 +153,8 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent,
153struct pde_opener { 153struct pde_opener {
154 struct file *file; 154 struct file *file;
155 struct list_head lh; 155 struct list_head lh;
156 int count; /* number of threads in close_pdeo() */ 156 int closing;
157 struct mutex mutex; 157 struct completion *c;
158}; 158};
159 159
160ssize_t __proc_file_read(struct file *, char __user *, size_t, loff_t *); 160ssize_t __proc_file_read(struct file *, char __user *, size_t, loff_t *);