aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexey Dobriyan <adobriyan@gmail.com>2008-07-25 04:48:29 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-07-25 13:53:44 -0400
commit881adb85358309ea9c6f707394002719982ec607 (patch)
treee4ffc2f6ca6013bab97bdb77b80e98b46a8d01e1
parent6e644c3126149b65460610fe5a00d8a162092abe (diff)
proc: always do ->release
Current two-stage scheme of removing PDE emphasizes one bug in proc: open rmmod remove_proc_entry close ->release won't be called because ->proc_fops were cleared. In simple cases it's small memory leak. For every ->open, ->release has to be done. List of openers is introduced which is traversed at remove_proc_entry() if neeeded. Discussions with Al long ago (sigh). Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/proc/generic.c14
-rw-r--r--fs/proc/inode.c74
-rw-r--r--fs/proc/internal.h7
-rw-r--r--include/linux/proc_fs.h1
4 files changed, 92 insertions, 4 deletions
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 43e54e86cefd..bc0a0dd2d844 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -597,6 +597,7 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
597 ent->pde_users = 0; 597 ent->pde_users = 0;
598 spin_lock_init(&ent->pde_unload_lock); 598 spin_lock_init(&ent->pde_unload_lock);
599 ent->pde_unload_completion = NULL; 599 ent->pde_unload_completion = NULL;
600 INIT_LIST_HEAD(&ent->pde_openers);
600 out: 601 out:
601 return ent; 602 return ent;
602} 603}
@@ -789,6 +790,19 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
789 spin_unlock(&de->pde_unload_lock); 790 spin_unlock(&de->pde_unload_lock);
790 791
791continue_removing: 792continue_removing:
793 spin_lock(&de->pde_unload_lock);
794 while (!list_empty(&de->pde_openers)) {
795 struct pde_opener *pdeo;
796
797 pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh);
798 list_del(&pdeo->lh);
799 spin_unlock(&de->pde_unload_lock);
800 pdeo->release(pdeo->inode, pdeo->file);
801 kfree(pdeo);
802 spin_lock(&de->pde_unload_lock);
803 }
804 spin_unlock(&de->pde_unload_lock);
805
792 if (S_ISDIR(de->mode)) 806 if (S_ISDIR(de->mode))
793 parent->nlink--; 807 parent->nlink--;
794 de->nlink = 0; 808 de->nlink = 0;
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index b08d10017911..354c08485825 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -126,12 +126,17 @@ static const struct super_operations proc_sops = {
126 .remount_fs = proc_remount, 126 .remount_fs = proc_remount,
127}; 127};
128 128
129static void pde_users_dec(struct proc_dir_entry *pde) 129static void __pde_users_dec(struct proc_dir_entry *pde)
130{ 130{
131 spin_lock(&pde->pde_unload_lock);
132 pde->pde_users--; 131 pde->pde_users--;
133 if (pde->pde_unload_completion && pde->pde_users == 0) 132 if (pde->pde_unload_completion && pde->pde_users == 0)
134 complete(pde->pde_unload_completion); 133 complete(pde->pde_unload_completion);
134}
135
136static void pde_users_dec(struct proc_dir_entry *pde)
137{
138 spin_lock(&pde->pde_unload_lock);
139 __pde_users_dec(pde);
135 spin_unlock(&pde->pde_unload_lock); 140 spin_unlock(&pde->pde_unload_lock);
136} 141}
137 142
@@ -318,36 +323,97 @@ static int proc_reg_open(struct inode *inode, struct file *file)
318 struct proc_dir_entry *pde = PDE(inode); 323 struct proc_dir_entry *pde = PDE(inode);
319 int rv = 0; 324 int rv = 0;
320 int (*open)(struct inode *, struct file *); 325 int (*open)(struct inode *, struct file *);
326 int (*release)(struct inode *, struct file *);
327 struct pde_opener *pdeo;
328
329 /*
330 * What for, you ask? Well, we can have open, rmmod, remove_proc_entry
331 * sequence. ->release won't be called because ->proc_fops will be
332 * cleared. Depending on complexity of ->release, consequences vary.
333 *
334 * We can't wait for mercy when close will be done for real, it's
335 * deadlockable: rmmod foo </proc/foo . So, we're going to do ->release
336 * by hand in remove_proc_entry(). For this, save opener's credentials
337 * for later.
338 */
339 pdeo = kmalloc(sizeof(struct pde_opener), GFP_KERNEL);
340 if (!pdeo)
341 return -ENOMEM;
321 342
322 spin_lock(&pde->pde_unload_lock); 343 spin_lock(&pde->pde_unload_lock);
323 if (!pde->proc_fops) { 344 if (!pde->proc_fops) {
324 spin_unlock(&pde->pde_unload_lock); 345 spin_unlock(&pde->pde_unload_lock);
346 kfree(pdeo);
325 return rv; 347 return rv;
326 } 348 }
327 pde->pde_users++; 349 pde->pde_users++;
328 open = pde->proc_fops->open; 350 open = pde->proc_fops->open;
351 release = pde->proc_fops->release;
329 spin_unlock(&pde->pde_unload_lock); 352 spin_unlock(&pde->pde_unload_lock);
330 353
331 if (open) 354 if (open)
332 rv = open(inode, file); 355 rv = open(inode, file);
333 356
334 pde_users_dec(pde); 357 spin_lock(&pde->pde_unload_lock);
358 if (rv == 0 && release) {
359 /* To know what to release. */
360 pdeo->inode = inode;
361 pdeo->file = file;
362 /* Strictly for "too late" ->release in proc_reg_release(). */
363 pdeo->release = release;
364 list_add(&pdeo->lh, &pde->pde_openers);
365 } else
366 kfree(pdeo);
367 __pde_users_dec(pde);
368 spin_unlock(&pde->pde_unload_lock);
335 return rv; 369 return rv;
336} 370}
337 371
372static struct pde_opener *find_pde_opener(struct proc_dir_entry *pde,
373 struct inode *inode, struct file *file)
374{
375 struct pde_opener *pdeo;
376
377 list_for_each_entry(pdeo, &pde->pde_openers, lh) {
378 if (pdeo->inode == inode && pdeo->file == file)
379 return pdeo;
380 }
381 return NULL;
382}
383
338static int proc_reg_release(struct inode *inode, struct file *file) 384static int proc_reg_release(struct inode *inode, struct file *file)
339{ 385{
340 struct proc_dir_entry *pde = PDE(inode); 386 struct proc_dir_entry *pde = PDE(inode);
341 int rv = 0; 387 int rv = 0;
342 int (*release)(struct inode *, struct file *); 388 int (*release)(struct inode *, struct file *);
389 struct pde_opener *pdeo;
343 390
344 spin_lock(&pde->pde_unload_lock); 391 spin_lock(&pde->pde_unload_lock);
392 pdeo = find_pde_opener(pde, inode, file);
345 if (!pde->proc_fops) { 393 if (!pde->proc_fops) {
346 spin_unlock(&pde->pde_unload_lock); 394 /*
395 * Can't simply exit, __fput() will think that everything is OK,
396 * and move on to freeing struct file. remove_proc_entry() will
397 * find slacker in opener's list and will try to do non-trivial
398 * things with struct file. Therefore, remove opener from list.
399 *
400 * But if opener is removed from list, who will ->release it?
401 */
402 if (pdeo) {
403 list_del(&pdeo->lh);
404 spin_unlock(&pde->pde_unload_lock);
405 rv = pdeo->release(inode, file);
406 kfree(pdeo);
407 } else
408 spin_unlock(&pde->pde_unload_lock);
347 return rv; 409 return rv;
348 } 410 }
349 pde->pde_users++; 411 pde->pde_users++;
350 release = pde->proc_fops->release; 412 release = pde->proc_fops->release;
413 if (pdeo) {
414 list_del(&pdeo->lh);
415 kfree(pdeo);
416 }
351 spin_unlock(&pde->pde_unload_lock); 417 spin_unlock(&pde->pde_unload_lock);
352 418
353 if (release) 419 if (release)
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 8d67616e7bb0..442202314d53 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -89,3 +89,10 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *ino,
89 struct dentry *dentry); 89 struct dentry *dentry);
90int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent, 90int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent,
91 filldir_t filldir); 91 filldir_t filldir);
92
93struct pde_opener {
94 struct inode *inode;
95 struct file *file;
96 int (*release)(struct inode *, struct file *);
97 struct list_head lh;
98};
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index cdabc2fc02f7..f560d1705afe 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -79,6 +79,7 @@ struct proc_dir_entry {
79 int pde_users; /* number of callers into module in progress */ 79 int pde_users; /* number of callers into module in progress */
80 spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */ 80 spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
81 struct completion *pde_unload_completion; 81 struct completion *pde_unload_completion;
82 struct list_head pde_openers; /* who did ->open, but not ->release */
82}; 83};
83 84
84struct kcore_list { 85struct kcore_list {