From 881adb85358309ea9c6f707394002719982ec607 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 25 Jul 2008 01:48:29 -0700 Subject: 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 Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/generic.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'fs/proc/generic.c') diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 43e54e86cef..bc0a0dd2d84 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, ent->pde_users = 0; spin_lock_init(&ent->pde_unload_lock); ent->pde_unload_completion = NULL; + INIT_LIST_HEAD(&ent->pde_openers); out: return ent; } @@ -789,6 +790,19 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) spin_unlock(&de->pde_unload_lock); continue_removing: + 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); + 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); + if (S_ISDIR(de->mode)) parent->nlink--; de->nlink = 0; -- cgit v1.2.3-70-g09d2 From 267e2a9c71b8e088ac307f9549f71468e86e26c1 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Fri, 25 Jul 2008 19:45:41 -0700 Subject: Use WARN() in fs/proc/ Use WARN() instead of a printk+WARN_ON() pair; this way the message becomes part of the warning section for better reporting/collection. This way, the entire if() {} section can collapse into the WARN() as well. Signed-off-by: Arjan van de Ven Acked-by: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/generic.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'fs/proc/generic.c') diff --git a/fs/proc/generic.c b/fs/proc/generic.c index bc0a0dd2d84..cb4096cc3fb 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -806,12 +806,9 @@ continue_removing: if (S_ISDIR(de->mode)) parent->nlink--; de->nlink = 0; - if (de->subdir) { - printk(KERN_WARNING "%s: removing non-empty directory " + WARN(de->subdir, KERN_WARNING "%s: removing non-empty directory " "'%s/%s', leaking at least '%s'\n", __func__, de->parent->name, de->name, de->subdir->name); - WARN_ON(1); - } if (atomic_dec_and_test(&de->count)) free_proc_entry(de); } -- cgit v1.2.3-70-g09d2 From 67935df49dae836fa86621861979fafdfd37ae59 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sat, 26 Jul 2008 11:18:28 +0400 Subject: [PATCH 1/2] proc: fix inode number bogorithmetic Id which proc gets from IDR for inode number and id which proc removes from IDR do not match. E.g. 0x11a transforms into 0x8000011a. Which stayed unnoticed for a long time because, surprise, idr_remove() masks out that high bit before doing anything. All of this due to "| ~MAX_ID_MASK" in release_inode_number(). I still don't understand how it's supposed to work, because "| ~MASK" is not an inversion for "& MAX" operation. So, use just one nice, working addition. Make start offset unsigned int, while I'm at it. It's longness is not used anywhere. Signed-off-by: Alexey Dobriyan Signed-off-by: Al Viro --- fs/proc/generic.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'fs/proc/generic.c') diff --git a/fs/proc/generic.c b/fs/proc/generic.c index cb4096cc3fb..b85e36e153b 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -303,7 +303,7 @@ out: static DEFINE_IDR(proc_inum_idr); static DEFINE_SPINLOCK(proc_inum_lock); /* protects the above */ -#define PROC_DYNAMIC_FIRST 0xF0000000UL +#define PROC_DYNAMIC_FIRST 0xF0000000U /* * Return an inode number between PROC_DYNAMIC_FIRST and @@ -311,7 +311,7 @@ static DEFINE_SPINLOCK(proc_inum_lock); /* protects the above */ */ static unsigned int get_inode_number(void) { - int i, inum = 0; + unsigned int i; int error; retry: @@ -326,21 +326,18 @@ retry: else if (error) return 0; - inum = (i & MAX_ID_MASK) + PROC_DYNAMIC_FIRST; - - /* inum will never be more than 0xf0ffffff, so no check - * for overflow. - */ - - return inum; + if (i > UINT_MAX - PROC_DYNAMIC_FIRST) { + spin_lock(&proc_inum_lock); + idr_remove(&proc_inum_idr, i); + spin_unlock(&proc_inum_lock); + } + return PROC_DYNAMIC_FIRST + i; } static void release_inode_number(unsigned int inum) { - int id = (inum - PROC_DYNAMIC_FIRST) | ~MAX_ID_MASK; - spin_lock(&proc_inum_lock); - idr_remove(&proc_inum_idr, id); + idr_remove(&proc_inum_idr, inum - PROC_DYNAMIC_FIRST); spin_unlock(&proc_inum_lock); } -- cgit v1.2.3-70-g09d2 From 9a18540915faaaadd7f71c16fa877a0c19675923 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sat, 26 Jul 2008 11:21:37 +0400 Subject: [PATCH 2/2] proc: switch inode number allocation to IDA proc doesn't use "associate pointer with id" feature of IDR, so switch to IDA. NOTE, NOTE, NOTE: Do not apply if release_inode_number() still mantions MAX_ID_MASK! Signed-off-by: Alexey Dobriyan Signed-off-by: Al Viro --- fs/proc/generic.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'fs/proc/generic.c') diff --git a/fs/proc/generic.c b/fs/proc/generic.c index b85e36e153b..4fb81e9c94e 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -300,7 +300,7 @@ out: return rtn; } -static DEFINE_IDR(proc_inum_idr); +static DEFINE_IDA(proc_inum_ida); static DEFINE_SPINLOCK(proc_inum_lock); /* protects the above */ #define PROC_DYNAMIC_FIRST 0xF0000000U @@ -315,11 +315,11 @@ static unsigned int get_inode_number(void) int error; retry: - if (idr_pre_get(&proc_inum_idr, GFP_KERNEL) == 0) + if (ida_pre_get(&proc_inum_ida, GFP_KERNEL) == 0) return 0; spin_lock(&proc_inum_lock); - error = idr_get_new(&proc_inum_idr, NULL, &i); + error = ida_get_new(&proc_inum_ida, &i); spin_unlock(&proc_inum_lock); if (error == -EAGAIN) goto retry; @@ -328,7 +328,7 @@ retry: if (i > UINT_MAX - PROC_DYNAMIC_FIRST) { spin_lock(&proc_inum_lock); - idr_remove(&proc_inum_idr, i); + ida_remove(&proc_inum_ida, i); spin_unlock(&proc_inum_lock); } return PROC_DYNAMIC_FIRST + i; @@ -337,7 +337,7 @@ retry: static void release_inode_number(unsigned int inum) { spin_lock(&proc_inum_lock); - idr_remove(&proc_inum_idr, inum - PROC_DYNAMIC_FIRST); + ida_remove(&proc_inum_ida, inum - PROC_DYNAMIC_FIRST); spin_unlock(&proc_inum_lock); } -- cgit v1.2.3-70-g09d2