From e246a5f2b9765059b88a86e968f219c6283cb4a9 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 14 May 2004 20:15:15 -0700 Subject: [PATCH] [PATCH] autofs4 race fix From: Ian Kent The case where two process similtaneously trigger a mount in autofs4 can cause multiple requests to the daemon for the same mount. The daemon handles this OK but it's possible an incorrect error to be returned. For this reason I believe it is better to change the spin lock to a semaphore in waitq.c. This makes the second and subsequent request wait on the q as ther supposed to. --- fs/autofs4/autofs_i.h | 1 + fs/autofs4/inode.c | 1 + fs/autofs4/waitq.c | 27 +++++++++++++++------------ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index afb3456ccd2df..3d869a10ab205 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -100,6 +100,7 @@ struct autofs_sb_info { int reghost_enabled; int needs_reghost; struct super_block *sb; + struct semaphore wq_sem; struct autofs_wait_queue *queues; /* Wait queue pointer */ }; diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 3698f1908bff5..bdb88a5050120 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -205,6 +205,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) sbi->sb = s; sbi->version = 0; sbi->sub_version = 0; + init_MUTEX(&sbi->wq_sem); sbi->queues = NULL; s->s_blocksize = 1024; s->s_blocksize_bits = 10; diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index 58a524641f783..ad671ffacfdd8 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -17,8 +17,6 @@ #include #include "autofs_i.h" -static spinlock_t waitq_lock = SPIN_LOCK_UNLOCKED; - /* We make this a static variable rather than a part of the superblock; it is better if we don't reassign numbers easily even across filesystems */ static autofs_wqt_t autofs4_next_wait_queue = 1; @@ -180,40 +178,43 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, return -ENOENT; } - spin_lock(&waitq_lock); + if (down_interruptible(&sbi->wq_sem)) { + kfree(name); + return -EINTR; + } + for (wq = sbi->queues ; wq ; wq = wq->next) { if (wq->hash == dentry->d_name.hash && wq->len == len && wq->name && !memcmp(wq->name, name, len)) break; } - spin_unlock(&waitq_lock); - + if ( !wq ) { /* Create a new wait queue */ wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); if ( !wq ) { kfree(name); + up(&sbi->wq_sem); return -ENOMEM; } - spin_lock(&waitq_lock); wq->wait_queue_token = autofs4_next_wait_queue; if (++autofs4_next_wait_queue == 0) autofs4_next_wait_queue = 1; wq->next = sbi->queues; sbi->queues = wq; - spin_unlock(&waitq_lock); init_waitqueue_head(&wq->queue); wq->hash = dentry->d_name.hash; wq->name = name; wq->len = len; wq->status = -EINTR; /* Status return if interrupted */ + atomic_set(&wq->wait_ctr, 2); + up(&sbi->wq_sem); DPRINTK(("autofs4_wait: new wait id = 0x%08lx, name = %.*s, nfy=%d\n", (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify)); /* autofs4_notify_daemon() may block */ - atomic_set(&wq->wait_ctr, 2); if (notify != NFY_NONE) { autofs4_notify_daemon(sbi,wq, notify == NFY_MOUNT ? @@ -222,6 +223,7 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, } } else { atomic_inc(&wq->wait_ctr); + up(&sbi->wq_sem); DPRINTK(("autofs4_wait: existing wait id = 0x%08lx, name = %.*s, nfy=%d\n", (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify)); } @@ -260,7 +262,8 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, status = wq->status; - if (atomic_dec_and_test(&wq->wait_ctr)) /* Are we the last process to need status? */ + /* Are we the last process to need status? */ + if (atomic_dec_and_test(&wq->wait_ctr)) kfree(wq); return status; @@ -271,19 +274,19 @@ int autofs4_wait_release(struct autofs_sb_info *sbi, autofs_wqt_t wait_queue_tok { struct autofs_wait_queue *wq, **wql; - spin_lock(&waitq_lock); + down(&sbi->wq_sem); for ( wql = &sbi->queues ; (wq = *wql) ; wql = &wq->next ) { if ( wq->wait_queue_token == wait_queue_token ) break; } if ( !wq ) { - spin_unlock(&waitq_lock); + up(&sbi->wq_sem); return -EINVAL; } *wql = wq->next; /* Unlink from chain */ - spin_unlock(&waitq_lock); + up(&sbi->wq_sem); kfree(wq->name); wq->name = NULL; /* Do not wait on this queue */ -- 2.53.0