autofs4: cleanup active and expire lookup
[safe/jmp/linux-2.6] / fs / autofs4 / root.c
index b96a3c5..81700f4 100644 (file)
@@ -72,6 +72,46 @@ const struct inode_operations autofs4_dir_inode_operations = {
        .rmdir          = autofs4_dir_rmdir,
 };
 
+static void autofs4_add_active(struct dentry *dentry)
+{
+       struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
+       struct autofs_info *ino = autofs4_dentry_ino(dentry);
+       if (ino) {
+               spin_lock(&sbi->lookup_lock);
+               if (!ino->active_count) {
+                       if (list_empty(&ino->active))
+                               list_add(&ino->active, &sbi->active_list);
+               }
+               ino->active_count++;
+               spin_unlock(&sbi->lookup_lock);
+       }
+       return;
+}
+
+static void autofs4_del_active(struct dentry *dentry)
+{
+       struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
+       struct autofs_info *ino = autofs4_dentry_ino(dentry);
+       if (ino) {
+               spin_lock(&sbi->lookup_lock);
+               ino->active_count--;
+               if (!ino->active_count) {
+                       if (!list_empty(&ino->active))
+                               list_del_init(&ino->active);
+               }
+               spin_unlock(&sbi->lookup_lock);
+       }
+       return;
+}
+
+static unsigned int autofs4_need_mount(unsigned int flags)
+{
+       unsigned int res = 0;
+       if (flags & (TRIGGER_FLAGS | TRIGGER_INTENTS))
+               res = 1;
+       return res;
+}
+
 static int autofs4_dir_open(struct inode *inode, struct file *file)
 {
        struct dentry *dentry = file->f_path.dentry;
@@ -126,32 +166,32 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags)
 
                /* Turn this into a real negative dentry? */
                if (status == -ENOENT) {
-                       spin_lock(&dentry->d_lock);
-                       dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
-                       spin_unlock(&dentry->d_lock);
+                       spin_lock(&sbi->fs_lock);
+                       ino->flags &= ~AUTOFS_INF_PENDING;
+                       spin_unlock(&sbi->fs_lock);
                        return status;
                } else if (status) {
                        /* Return a negative dentry, but leave it "pending" */
                        return status;
                }
        /* Trigger mount for path component or follow link */
-       } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING ||
-                       flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) ||
+       } else if (ino->flags & AUTOFS_INF_PENDING ||
+                       autofs4_need_mount(flags) ||
                        current->link_count) {
                DPRINTK("waiting for mount name=%.*s",
                        dentry->d_name.len, dentry->d_name.name);
 
-               spin_lock(&dentry->d_lock);
-               dentry->d_flags |= DCACHE_AUTOFS_PENDING;
-               spin_unlock(&dentry->d_lock);
+               spin_lock(&sbi->fs_lock);
+               ino->flags |= AUTOFS_INF_PENDING;
+               spin_unlock(&sbi->fs_lock);
                status = autofs4_wait(sbi, dentry, NFY_MOUNT);
 
                DPRINTK("mount done status=%d", status);
 
                if (status) {
-                       spin_lock(&dentry->d_lock);
-                       dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
-                       spin_unlock(&dentry->d_lock);
+                       spin_lock(&sbi->fs_lock);
+                       ino->flags &= ~AUTOFS_INF_PENDING;
+                       spin_unlock(&sbi->fs_lock);
                        return status;
                }
        }
@@ -160,9 +200,9 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags)
        if (ino)
                ino->last_used = jiffies;
 
-       spin_lock(&dentry->d_lock);
-       dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
-       spin_unlock(&dentry->d_lock);
+       spin_lock(&sbi->fs_lock);
+       ino->flags &= ~AUTOFS_INF_PENDING;
+       spin_unlock(&sbi->fs_lock);
 
        return 0;
 }
@@ -202,19 +242,24 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
        autofs4_expire_wait(dentry);
 
        /* We trigger a mount for almost all flags */
-       lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS);
-       if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING))
+       lookup_type = autofs4_need_mount(nd->flags);
+       spin_lock(&sbi->fs_lock);
+       spin_lock(&dcache_lock);
+       if (!(lookup_type || ino->flags & AUTOFS_INF_PENDING)) {
+               spin_unlock(&dcache_lock);
+               spin_unlock(&sbi->fs_lock);
                goto follow;
+       }
 
        /*
         * If the dentry contains directories then it is an autofs
         * multi-mount with no root mount offset. So don't try to
         * mount it again.
         */
-       spin_lock(&dcache_lock);
-       if (dentry->d_flags & DCACHE_AUTOFS_PENDING ||
+       if (ino->flags & AUTOFS_INF_PENDING ||
            (!d_mountpoint(dentry) && __simple_empty(dentry))) {
                spin_unlock(&dcache_lock);
+               spin_unlock(&sbi->fs_lock);
 
                status = try_to_fill_dentry(dentry, 0);
                if (status)
@@ -223,6 +268,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
                goto follow;
        }
        spin_unlock(&dcache_lock);
+       spin_unlock(&sbi->fs_lock);
 follow:
        /*
         * If there is no root mount it must be an autofs
@@ -359,8 +405,11 @@ static const struct dentry_operations autofs4_dentry_operations = {
        .d_release      = autofs4_dentry_release,
 };
 
-static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name)
+static struct dentry *autofs4_lookup_active(struct dentry *dentry)
 {
+       struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
+       struct dentry *parent = dentry->d_parent;
+       struct qstr *name = &dentry->d_name;
        unsigned int len = name->len;
        unsigned int hash = name->hash;
        const unsigned char *str = name->name;
@@ -411,8 +460,11 @@ next:
        return NULL;
 }
 
-static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name)
+static struct dentry *autofs4_lookup_expiring(struct dentry *dentry)
 {
+       struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
+       struct dentry *parent = dentry->d_parent;
+       struct qstr *name = &dentry->d_name;
        unsigned int len = name->len;
        unsigned int hash = name->hash;
        const unsigned char *str = name->name;
@@ -468,7 +520,7 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
 {
        struct autofs_sb_info *sbi;
        struct autofs_info *ino;
-       struct dentry *expiring, *unhashed;
+       struct dentry *expiring, *active;
        int oz_mode;
 
        DPRINTK("name = %.*s",
@@ -484,10 +536,11 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
        DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d",
                 current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode);
 
-       unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name);
-       if (unhashed)
-               dentry = unhashed;
-       else {
+       active = autofs4_lookup_active(dentry);
+       if (active) {
+               dentry = active;
+               ino = autofs4_dentry_ino(dentry);
+       } else {
                /*
                 * Mark the dentry incomplete but don't hash it. We do this
                 * to serialize our inode creation operations (symlink and
@@ -513,36 +566,28 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
                dentry->d_fsdata = ino;
                ino->dentry = dentry;
 
-               spin_lock(&sbi->lookup_lock);
-               list_add(&ino->active, &sbi->active_list);
-               spin_unlock(&sbi->lookup_lock);
+               autofs4_add_active(dentry);
 
                d_instantiate(dentry, NULL);
        }
 
        if (!oz_mode) {
                mutex_unlock(&dir->i_mutex);
-               expiring = autofs4_lookup_expiring(sbi,
-                                                  dentry->d_parent,
-                                                  &dentry->d_name);
+               expiring = autofs4_lookup_expiring(dentry);
                if (expiring) {
                        /*
                         * If we are racing with expire the request might not
                         * be quite complete but the directory has been removed
                         * so it must have been successful, so just wait for it.
                         */
-                       ino = autofs4_dentry_ino(expiring);
                        autofs4_expire_wait(expiring);
-                       spin_lock(&sbi->lookup_lock);
-                       if (!list_empty(&ino->expiring))
-                               list_del_init(&ino->expiring);
-                       spin_unlock(&sbi->lookup_lock);
+                       autofs4_del_expiring(expiring);
                        dput(expiring);
                }
 
-               spin_lock(&dentry->d_lock);
-               dentry->d_flags |= DCACHE_AUTOFS_PENDING;
-               spin_unlock(&dentry->d_lock);
+               spin_lock(&sbi->fs_lock);
+               ino->flags |= AUTOFS_INF_PENDING;
+               spin_unlock(&sbi->fs_lock);
                if (dentry->d_op && dentry->d_op->d_revalidate)
                        (dentry->d_op->d_revalidate)(dentry, nd);
                mutex_lock(&dir->i_mutex);
@@ -552,22 +597,22 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
         * If we are still pending, check if we had to handle
         * a signal. If so we can force a restart..
         */
-       if (dentry->d_flags & DCACHE_AUTOFS_PENDING) {
+       if (ino->flags & AUTOFS_INF_PENDING) {
                /* See if we were interrupted */
                if (signal_pending(current)) {
                        sigset_t *sigset = &current->pending.signal;
                        if (sigismember (sigset, SIGKILL) ||
                            sigismember (sigset, SIGQUIT) ||
                            sigismember (sigset, SIGINT)) {
-                           if (unhashed)
-                               dput(unhashed);
+                           if (active)
+                               dput(active);
                            return ERR_PTR(-ERESTARTNOINTR);
                        }
                }
                if (!oz_mode) {
-                       spin_lock(&dentry->d_lock);
-                       dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
-                       spin_unlock(&dentry->d_lock);
+                       spin_lock(&sbi->fs_lock);
+                       ino->flags &= ~AUTOFS_INF_PENDING;
+                       spin_unlock(&sbi->fs_lock);
                }
        }
 
@@ -592,14 +637,14 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
                else
                        dentry = ERR_PTR(-ENOENT);
 
-               if (unhashed)
-                       dput(unhashed);
+               if (active)
+                       dput(active);
 
                return dentry;
        }
 
-       if (unhashed)
-               return unhashed;
+       if (active)
+               return active;
 
        return NULL;
 }
@@ -624,10 +669,7 @@ static int autofs4_dir_symlink(struct inode *dir,
        if (!ino)
                return -ENOMEM;
 
-       spin_lock(&sbi->lookup_lock);
-       if (!list_empty(&ino->active))
-               list_del_init(&ino->active);
-       spin_unlock(&sbi->lookup_lock);
+       autofs4_del_active(dentry);
 
        ino->size = strlen(symname);
        cp = kmalloc(ino->size + 1, GFP_KERNEL);
@@ -705,10 +747,7 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry)
        dir->i_mtime = CURRENT_TIME;
 
        spin_lock(&dcache_lock);
-       spin_lock(&sbi->lookup_lock);
-       if (list_empty(&ino->expiring))
-               list_add(&ino->expiring, &sbi->expiring_list);
-       spin_unlock(&sbi->lookup_lock);
+       autofs4_add_expiring(dentry);
        spin_lock(&dentry->d_lock);
        __d_drop(dentry);
        spin_unlock(&dentry->d_lock);
@@ -734,10 +773,7 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
                spin_unlock(&dcache_lock);
                return -ENOTEMPTY;
        }
-       spin_lock(&sbi->lookup_lock);
-       if (list_empty(&ino->expiring))
-               list_add(&ino->expiring, &sbi->expiring_list);
-       spin_unlock(&sbi->lookup_lock);
+       autofs4_add_expiring(dentry);
        spin_lock(&dentry->d_lock);
        __d_drop(dentry);
        spin_unlock(&dentry->d_lock);
@@ -775,10 +811,7 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        if (!ino)
                return -ENOMEM;
 
-       spin_lock(&sbi->lookup_lock);
-       if (!list_empty(&ino->active))
-               list_del_init(&ino->active);
-       spin_unlock(&sbi->lookup_lock);
+       autofs4_del_active(dentry);
 
        inode = autofs4_get_inode(dir->i_sb, ino);
        if (!inode) {