summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/autofs4/autofs_i.h1
-rw-r--r--fs/autofs4/expire.c34
-rw-r--r--fs/autofs4/inode.c12
-rw-r--r--fs/autofs4/root.c23
4 files changed, 59 insertions, 11 deletions
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
index eea2593..00da71d 100644
--- a/fs/autofs4/autofs_i.h
+++ b/fs/autofs4/autofs_i.h
@@ -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;
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
index 165fe9e..053d92a 100644
--- a/fs/autofs4/expire.c
+++ b/fs/autofs4/expire.c
@@ -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;
}
diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c
index 1ad98d4..2335b1d 100644
--- a/fs/autofs4/inode.c
+++ b/fs/autofs4/inode.c
@@ -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)
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index c7ff357..d196712 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -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;
OpenPOWER on IntegriCloud