[PATCH] autofs4: fix false negative return from expire
authorIan Kent <raven@themaw.net>
Mon, 27 Mar 2006 09:14:46 +0000 (01:14 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 27 Mar 2006 16:44:39 +0000 (08:44 -0800)
Fix the case where an expire returns busy on a tree mount when it is in fact
not busy.  This case was overlooked when the patch to prevent the expiring
away of "scaffolding" directories for tree mounts was applied.

The problem arises when a tree of mounts is a member of a map with other keys.
 The current logic will not expire the tree if any other mount in the map is
busy.  The solution is to maintain a "minimum" use count for each autofs
dentry and compare this to the actual dentry usage count during expire.

Signed-off-by: Ian Kent <raven@themaw.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/autofs4/autofs_i.h
fs/autofs4/expire.c
fs/autofs4/inode.c
fs/autofs4/root.c

index eea25934da6299431ebf62d62cd11fb2b94d15fc..00da71d0f32a7463c74b0603907d04e3aca71cbd 100644 (file)
@@ -55,6 +55,7 @@ struct autofs_info {
 
        struct autofs_sb_info *sbi;
        unsigned long last_used;
+       atomic_t count;
 
        mode_t  mode;
        size_t  size;
index 165fe9e2d5705916689029d2b04e80b90b90c8a2..053d92a745b91c9624cc585f54b718bb8dc22c63 100644 (file)
@@ -101,6 +101,7 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
                             unsigned long timeout,
                             int do_now)
 {
+       struct autofs_info *ino;
        struct dentry *p;
 
        DPRINTK("top %p %.*s",
@@ -110,14 +111,6 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
        if (!simple_positive(top))
                return 1;
 
-       /* Timeout of a tree mount is determined by its top dentry */
-       if (!autofs4_can_expire(top, timeout, do_now))
-               return 1;
-
-       /* Is someone visiting anywhere in the tree ? */
-       if (may_umount_tree(mnt))
-               return 1;
-
        spin_lock(&dcache_lock);
        for (p = top; p; p = next_dentry(p, top)) {
                /* Negative dentry - give up */
@@ -130,17 +123,40 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
                p = dget(p);
                spin_unlock(&dcache_lock);
 
+               /*
+                * Is someone visiting anywhere in the subtree ?
+                * If there's no mount we need to check the usage
+                * count for the autofs dentry.
+                */
+               ino = autofs4_dentry_ino(p);
                if (d_mountpoint(p)) {
-                       /* First busy => tree busy */
                        if (autofs4_mount_busy(mnt, p)) {
                                dput(p);
                                return 1;
                        }
+               } else {
+                       unsigned int ino_count = atomic_read(&ino->count);
+
+                       /* allow for dget above and top is already dgot */
+                       if (p == top)
+                               ino_count += 2;
+                       else
+                               ino_count++;
+
+                       if (atomic_read(&p->d_count) > ino_count) {
+                               dput(p);
+                               return 1;
+                       }
                }
                dput(p);
                spin_lock(&dcache_lock);
        }
        spin_unlock(&dcache_lock);
+
+       /* Timeout of a tree mount is ultimately determined by its top dentry */
+       if (!autofs4_can_expire(top, timeout, do_now))
+               return 1;
+
        return 0;
 }
 
index 1ad98d48e550dc2c79b2a976172fa1bde1dc4ec0..2335b1d6490f0fd3f86294ab42e569293e521455 100644 (file)
@@ -46,6 +46,7 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino,
        ino->size = 0;
 
        ino->last_used = jiffies;
+       atomic_set(&ino->count, 0);
 
        ino->sbi = sbi;
 
@@ -64,10 +65,19 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino,
 
 void autofs4_free_ino(struct autofs_info *ino)
 {
+       struct autofs_info *p_ino;
+
        if (ino->dentry) {
                ino->dentry->d_fsdata = NULL;
-               if (ino->dentry->d_inode)
+               if (ino->dentry->d_inode) {
+                       struct dentry *parent = ino->dentry->d_parent;
+                       if (atomic_dec_and_test(&ino->count)) {
+                               p_ino = autofs4_dentry_ino(parent);
+                               if (p_ino && parent != ino->dentry)
+                                       atomic_dec(&p_ino->count);
+                       }
                        dput(ino->dentry);
+               }
                ino->dentry = NULL;
        }
        if (ino->free)
index c7ff35774344e1dc805fd40e62b06ddd5ca1d11d..d196712c4b9449b16ae9438c9282f09e163c5ad0 100644 (file)
@@ -490,6 +490,7 @@ static int autofs4_dir_symlink(struct inode *dir,
 {
        struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
        struct autofs_info *ino = autofs4_dentry_ino(dentry);
+       struct autofs_info *p_ino;
        struct inode *inode;
        char *cp;
 
@@ -523,6 +524,10 @@ static int autofs4_dir_symlink(struct inode *dir,
 
        dentry->d_fsdata = ino;
        ino->dentry = dget(dentry);
+       atomic_inc(&ino->count);
+       p_ino = autofs4_dentry_ino(dentry->d_parent);
+       if (p_ino && dentry->d_parent != dentry)
+               atomic_inc(&p_ino->count);
        ino->inode = inode;
 
        dir->i_mtime = CURRENT_TIME;
@@ -549,11 +554,17 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry)
 {
        struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
        struct autofs_info *ino = autofs4_dentry_ino(dentry);
+       struct autofs_info *p_ino;
        
        /* This allows root to remove symlinks */
        if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) )
                return -EACCES;
 
+       if (atomic_dec_and_test(&ino->count)) {
+               p_ino = autofs4_dentry_ino(dentry->d_parent);
+               if (p_ino && dentry->d_parent != dentry)
+                       atomic_dec(&p_ino->count);
+       }
        dput(ino->dentry);
 
        dentry->d_inode->i_size = 0;
@@ -570,6 +581,7 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
 {
        struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
        struct autofs_info *ino = autofs4_dentry_ino(dentry);
+       struct autofs_info *p_ino;
        
        if (!autofs4_oz_mode(sbi))
                return -EACCES;
@@ -584,8 +596,12 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
        spin_unlock(&dentry->d_lock);
        spin_unlock(&dcache_lock);
 
+       if (atomic_dec_and_test(&ino->count)) {
+               p_ino = autofs4_dentry_ino(dentry->d_parent);
+               if (p_ino && dentry->d_parent != dentry)
+                       atomic_dec(&p_ino->count);
+       }
        dput(ino->dentry);
-
        dentry->d_inode->i_size = 0;
        dentry->d_inode->i_nlink = 0;
 
@@ -599,6 +615,7 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 {
        struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
        struct autofs_info *ino = autofs4_dentry_ino(dentry);
+       struct autofs_info *p_ino;
        struct inode *inode;
 
        if ( !autofs4_oz_mode(sbi) )
@@ -621,6 +638,10 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 
        dentry->d_fsdata = ino;
        ino->dentry = dget(dentry);
+       atomic_inc(&ino->count);
+       p_ino = autofs4_dentry_ino(dentry->d_parent);
+       if (p_ino && dentry->d_parent != dentry)
+               atomic_inc(&p_ino->count);
        ino->inode = inode;
        dir->i_nlink++;
        dir->i_mtime = CURRENT_TIME;