aboutsummaryrefslogtreecommitdiffstats
path: root/fs/autofs4/waitq.c
diff options
context:
space:
mode:
authorIan Kent <raven@themaw.net>2008-07-24 00:30:21 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-07-24 13:47:32 -0400
commit296f7bf78bc5c7a4d772aea580ce800d14040d1a (patch)
tree794e3ae197d75b1a03511fefb4f87ca89caa54ac /fs/autofs4/waitq.c
parente64be33ccaceaca67c84237dff8805b861398eab (diff)
autofs4: fix waitq memory leak
If an autofs mount becomes catatonic before autofs4_wait_release() is called the wait queue counter will not be decremented down to zero and the entry will never be freed. There are also races decrementing the wait counter in the wait release function. To deal with this the counter needs to be updated while holding the wait queue mutex and waiters need to be woken up unconditionally when the wait is removed from the queue to ensure we eventually free the wait. Signed-off-by: Ian Kent <raven@themaw.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/autofs4/waitq.c')
-rw-r--r--fs/autofs4/waitq.c18
1 files changed, 9 insertions, 9 deletions
diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c
index dd2914d7ad7f..3458dbc8fff0 100644
--- a/fs/autofs4/waitq.c
+++ b/fs/autofs4/waitq.c
@@ -46,6 +46,7 @@ void autofs4_catatonic_mode(struct autofs_sb_info *sbi)
46 kfree(wq->name.name); 46 kfree(wq->name.name);
47 wq->name.name = NULL; 47 wq->name.name = NULL;
48 } 48 }
49 wq->wait_ctr--;
49 wake_up_interruptible(&wq->queue); 50 wake_up_interruptible(&wq->queue);
50 wq = nwq; 51 wq = nwq;
51 } 52 }
@@ -380,7 +381,7 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry,
380 wq->pid = current->pid; 381 wq->pid = current->pid;
381 wq->tgid = current->tgid; 382 wq->tgid = current->tgid;
382 wq->status = -EINTR; /* Status return if interrupted */ 383 wq->status = -EINTR; /* Status return if interrupted */
383 atomic_set(&wq->wait_ctr, 2); 384 wq->wait_ctr = 2;
384 mutex_unlock(&sbi->wq_mutex); 385 mutex_unlock(&sbi->wq_mutex);
385 386
386 if (sbi->version < 5) { 387 if (sbi->version < 5) {
@@ -406,7 +407,7 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry,
406 /* autofs4_notify_daemon() may block */ 407 /* autofs4_notify_daemon() may block */
407 autofs4_notify_daemon(sbi, wq, type); 408 autofs4_notify_daemon(sbi, wq, type);
408 } else { 409 } else {
409 atomic_inc(&wq->wait_ctr); 410 wq->wait_ctr++;
410 mutex_unlock(&sbi->wq_mutex); 411 mutex_unlock(&sbi->wq_mutex);
411 kfree(qstr.name); 412 kfree(qstr.name);
412 DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", 413 DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d",
@@ -442,8 +443,10 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry,
442 status = wq->status; 443 status = wq->status;
443 444
444 /* Are we the last process to need status? */ 445 /* Are we the last process to need status? */
445 if (atomic_dec_and_test(&wq->wait_ctr)) 446 mutex_lock(&sbi->wq_mutex);
447 if (!--wq->wait_ctr)
446 kfree(wq); 448 kfree(wq);
449 mutex_unlock(&sbi->wq_mutex);
447 450
448 return status; 451 return status;
449} 452}
@@ -467,14 +470,11 @@ int autofs4_wait_release(struct autofs_sb_info *sbi, autofs_wqt_t wait_queue_tok
467 *wql = wq->next; /* Unlink from chain */ 470 *wql = wq->next; /* Unlink from chain */
468 kfree(wq->name.name); 471 kfree(wq->name.name);
469 wq->name.name = NULL; /* Do not wait on this queue */ 472 wq->name.name = NULL; /* Do not wait on this queue */
470 mutex_unlock(&sbi->wq_mutex);
471
472 wq->status = status; 473 wq->status = status;
473 474 wake_up_interruptible(&wq->queue);
474 if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ 475 if (!--wq->wait_ctr)
475 kfree(wq); 476 kfree(wq);
476 else 477 mutex_unlock(&sbi->wq_mutex);
477 wake_up_interruptible(&wq->queue);
478 478
479 return 0; 479 return 0;
480} 480}