*
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
* Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
+ * Copyright 2001-2006 Ian Kent <raven@themaw.net>
*
* This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License, version 2, or at your
* ------------------------------------------------------------------------- */
#include "autofs_i.h"
-#include <linux/mount.h>
-/*
- * Determine if a subtree of the namespace is busy.
- *
- * mnt is the mount tree under the autofs mountpoint
- */
-static inline int is_vfsmnt_tree_busy(struct vfsmount *mnt)
+static unsigned long now;
+
+/* Check if a dentry can be expired */
+static inline int autofs4_can_expire(struct dentry *dentry,
+ unsigned long timeout, int do_now)
{
- struct vfsmount *this_parent = mnt;
- struct list_head *next;
- int count;
+ struct autofs_info *ino = autofs4_dentry_ino(dentry);
+
+ /* dentry in the process of being deleted */
+ if (ino == NULL)
+ return 0;
+
+ /* No point expiring a pending mount */
+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING)
+ return 0;
+
+ if (!do_now) {
+ /* Too young to die */
+ if (time_after(ino->last_used + timeout, now))
+ return 0;
+
+ /* update last_used here :-
+ - obviously makes sense if it is in use now
+ - less obviously, prevents rapid-fire expire
+ attempts if expire fails the first time */
+ ino->last_used = now;
+ }
+ return 1;
+}
- count = atomic_read(&mnt->mnt_count) - 1;
+/* Check a mount point for busyness */
+static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry)
+{
+ struct dentry *top = dentry;
+ int status = 1;
-repeat:
- next = this_parent->mnt_mounts.next;
- DPRINTK(("is_vfsmnt_tree_busy: mnt=%p, this_parent=%p, next=%p\n",
- mnt, this_parent, next));
-resume:
- for( ; next != &this_parent->mnt_mounts; next = next->next) {
- struct vfsmount *p = list_entry(next, struct vfsmount,
- mnt_child);
+ DPRINTK("dentry %p %.*s",
+ dentry, (int)dentry->d_name.len, dentry->d_name.name);
- /* -1 for struct vfs_mount's normal count,
- -1 to compensate for child's reference to parent */
- count += atomic_read(&p->mnt_count) - 1 - 1;
+ mntget(mnt);
+ dget(dentry);
- DPRINTK(("is_vfsmnt_tree_busy: p=%p, count now %d\n",
- p, count));
+ if (!autofs4_follow_mount(&mnt, &dentry))
+ goto done;
- if (!list_empty(&p->mnt_mounts)) {
- this_parent = p;
- goto repeat;
- }
- /* root is busy if any leaf is busy */
- if (atomic_read(&p->mnt_count) > 1)
- return 1;
- }
+ /* This is an autofs submount, we can't expire it */
+ if (is_autofs4_dentry(dentry))
+ goto done;
- /* All done at this level ... ascend and resume the search. */
- if (this_parent != mnt) {
- next = this_parent->mnt_child.next;
- this_parent = this_parent->mnt_parent;
- goto resume;
+ /* Update the expiry counter if fs is busy */
+ if (!may_umount_tree(mnt)) {
+ struct autofs_info *ino = autofs4_dentry_ino(top);
+ ino->last_used = jiffies;
+ goto done;
}
- DPRINTK(("is_vfsmnt_tree_busy: count=%d\n", count));
- return count != 0; /* remaining users? */
+ status = 0;
+done:
+ DPRINTK("returning = %d", status);
+ mntput(mnt);
+ dput(dentry);
+ return status;
}
-/* Traverse a dentry's list of vfsmounts and return the number of
- non-busy mounts */
-static int check_vfsmnt(struct vfsmount *mnt, struct dentry *dentry)
+/*
+ * Calculate next entry in top down tree traversal.
+ * From next_mnt in namespace.c - elegant.
+ */
+static struct dentry *next_dentry(struct dentry *p, struct dentry *root)
{
- int ret = dentry->d_mounted;
- struct vfsmount *vfs = lookup_mnt(mnt, dentry);
+ struct list_head *next = p->d_subdirs.next;
+
+ if (next == &p->d_subdirs) {
+ while (1) {
+ if (p == root)
+ return NULL;
+ next = p->d_u.d_child.next;
+ if (next != &p->d_parent->d_subdirs)
+ break;
+ p = p->d_parent;
+ }
+ }
+ return list_entry(next, struct dentry, d_u.d_child);
+}
- if (vfs) {
- mntput(vfs);
- if (is_vfsmnt_tree_busy(vfs))
- ret--;
+/*
+ * Check a direct mount point for busyness.
+ * Direct mounts have similar expiry semantics to tree mounts.
+ * The tree is not busy iff no mountpoints are busy and there are no
+ * autofs submounts.
+ */
+static int autofs4_direct_busy(struct vfsmount *mnt,
+ struct dentry *top,
+ unsigned long timeout,
+ int do_now)
+{
+ DPRINTK("top %p %.*s",
+ top, (int) top->d_name.len, top->d_name.name);
+
+ /* If it's busy update the expiry counters */
+ if (!may_umount_tree(mnt)) {
+ struct autofs_info *ino = autofs4_dentry_ino(top);
+ if (ino)
+ ino->last_used = jiffies;
+ return 1;
}
- DPRINTK(("check_vfsmnt: ret=%d\n", ret));
- return ret;
+
+ /* Timeout of a direct mount is determined by its top dentry */
+ if (!autofs4_can_expire(top, timeout, do_now))
+ return 1;
+
+ return 0;
}
-/* Check dentry tree for busyness. If a dentry appears to be busy
- because it is a mountpoint, check to see if the mounted
- filesystem is busy. */
-static int is_tree_busy(struct vfsmount *topmnt, struct dentry *top)
+/* Check a directory tree of mount points for busyness
+ * The tree is not busy iff no mountpoints are busy
+ */
+static int autofs4_tree_busy(struct vfsmount *mnt,
+ struct dentry *top,
+ unsigned long timeout,
+ int do_now)
{
- struct dentry *this_parent;
- struct list_head *next;
- int count;
+ struct autofs_info *top_ino = autofs4_dentry_ino(top);
+ struct dentry *p;
- count = atomic_read(&top->d_count);
-
- DPRINTK(("is_tree_busy: top=%p initial count=%d\n",
- top, count));
- this_parent = top;
+ DPRINTK("top %p %.*s",
+ top, (int)top->d_name.len, top->d_name.name);
- if (is_autofs4_dentry(top)) {
- count--;
- DPRINTK(("is_tree_busy: autofs; count=%d\n", count));
+ /* Negative dentry - give up */
+ if (!simple_positive(top))
+ return 1;
+
+ spin_lock(&dcache_lock);
+ for (p = top; p; p = next_dentry(p, top)) {
+ /* Negative dentry - give up */
+ if (!simple_positive(p))
+ continue;
+
+ DPRINTK("dentry %p %.*s",
+ p, (int) p->d_name.len, p->d_name.name);
+
+ 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.
+ * If the fs is busy update the expiry counter.
+ */
+ if (d_mountpoint(p)) {
+ if (autofs4_mount_busy(mnt, p)) {
+ top_ino->last_used = jiffies;
+ dput(p);
+ return 1;
+ }
+ } else {
+ struct autofs_info *ino = autofs4_dentry_ino(p);
+ unsigned int ino_count = atomic_read(&ino->count);
+
+ /*
+ * Clean stale dentries below that have not been
+ * invalidated after a mount fail during lookup
+ */
+ d_invalidate(p);
+
+ /* 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) {
+ top_ino->last_used = jiffies;
+ dput(p);
+ return 1;
+ }
+ }
+ dput(p);
+ spin_lock(&dcache_lock);
}
+ spin_unlock(&dcache_lock);
- if (d_mountpoint(top))
- count -= check_vfsmnt(topmnt, top);
+ /* Timeout of a tree mount is ultimately determined by its top dentry */
+ if (!autofs4_can_expire(top, timeout, do_now))
+ return 1;
- repeat:
- next = this_parent->d_subdirs.next;
- resume:
- while (next != &this_parent->d_subdirs) {
- int adj = 0;
- struct dentry *dentry = list_entry(next, struct dentry,
- d_child);
- next = next->next;
+ return 0;
+}
- count += atomic_read(&dentry->d_count) - 1;
+static struct dentry *autofs4_check_leaves(struct vfsmount *mnt,
+ struct dentry *parent,
+ unsigned long timeout,
+ int do_now)
+{
+ struct dentry *p;
- if (d_mountpoint(dentry))
- adj += check_vfsmnt(topmnt, dentry);
+ DPRINTK("parent %p %.*s",
+ parent, (int)parent->d_name.len, parent->d_name.name);
- if (is_autofs4_dentry(dentry)) {
- adj++;
- DPRINTK(("is_tree_busy: autofs; adj=%d\n",
- adj));
- }
+ spin_lock(&dcache_lock);
+ for (p = parent; p; p = next_dentry(p, parent)) {
+ /* Negative dentry - give up */
+ if (!simple_positive(p))
+ continue;
- count -= adj;
+ DPRINTK("dentry %p %.*s",
+ p, (int) p->d_name.len, p->d_name.name);
- if (!list_empty(&dentry->d_subdirs)) {
- this_parent = dentry;
- goto repeat;
- }
+ p = dget(p);
+ spin_unlock(&dcache_lock);
+
+ if (d_mountpoint(p)) {
+ /* Can we umount this guy */
+ if (autofs4_mount_busy(mnt, p))
+ goto cont;
- if (atomic_read(&dentry->d_count) != adj) {
- DPRINTK(("is_tree_busy: busy leaf (d_count=%d adj=%d)\n",
- atomic_read(&dentry->d_count), adj));
- return 1;
+ /* Can we expire this guy */
+ if (autofs4_can_expire(p, timeout, do_now))
+ return p;
}
+cont:
+ dput(p);
+ spin_lock(&dcache_lock);
}
+ spin_unlock(&dcache_lock);
+ return NULL;
+}
+
+/* Check if we can expire a direct mount (possibly a tree) */
+static struct dentry *autofs4_expire_direct(struct super_block *sb,
+ struct vfsmount *mnt,
+ struct autofs_sb_info *sbi,
+ int how)
+{
+ unsigned long timeout;
+ struct dentry *root = dget(sb->s_root);
+ int do_now = how & AUTOFS_EXP_IMMEDIATE;
- /* All done at this level ... ascend and resume the search. */
- if (this_parent != top) {
- next = this_parent->d_child.next;
- this_parent = this_parent->d_parent;
- goto resume;
+ if (!sbi->exp_timeout || !root)
+ return NULL;
+
+ now = jiffies;
+ timeout = sbi->exp_timeout;
+
+ /* Lock the tree as we must expire as a whole */
+ spin_lock(&sbi->fs_lock);
+ if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
+ struct autofs_info *ino = autofs4_dentry_ino(root);
+
+ /* Set this flag early to catch sys_chdir and the like */
+ ino->flags |= AUTOFS_INF_EXPIRING;
+ spin_unlock(&sbi->fs_lock);
+ return root;
}
+ spin_unlock(&sbi->fs_lock);
+ dput(root);
- DPRINTK(("is_tree_busy: count=%d\n", count));
- return count != 0; /* remaining users? */
+ return NULL;
}
/*
* - it is unused by any user process
* - it has been unused for exp_timeout time
*/
-static struct dentry *autofs4_expire(struct super_block *sb,
- struct vfsmount *mnt,
- struct autofs_sb_info *sbi,
- int do_now)
+static struct dentry *autofs4_expire_indirect(struct super_block *sb,
+ struct vfsmount *mnt,
+ struct autofs_sb_info *sbi,
+ int how)
{
- unsigned long now = jiffies;
unsigned long timeout;
struct dentry *root = sb->s_root;
- struct list_head *tmp;
+ struct dentry *expired = NULL;
+ struct list_head *next;
+ int do_now = how & AUTOFS_EXP_IMMEDIATE;
+ int exp_leaves = how & AUTOFS_EXP_LEAVES;
- if (!sbi->exp_timeout || !root)
+ if ( !sbi->exp_timeout || !root )
return NULL;
+ now = jiffies;
timeout = sbi->exp_timeout;
spin_lock(&dcache_lock);
- for(tmp = root->d_subdirs.next;
- tmp != &root->d_subdirs;
- tmp = tmp->next) {
- struct autofs_info *ino;
- struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
-
- if (dentry->d_inode == NULL)
- continue;
+ next = root->d_subdirs.next;
- ino = autofs4_dentry_ino(dentry);
+ /* On exit from the loop expire is set to a dgot dentry
+ * to expire or it's NULL */
+ while ( next != &root->d_subdirs ) {
+ struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child);
- if (ino == NULL) {
- /* dentry in the process of being deleted */
+ /* Negative dentry - give up */
+ if (!simple_positive(dentry)) {
+ next = next->next;
continue;
}
- /* No point expiring a pending mount */
- if (dentry->d_flags & DCACHE_AUTOFS_PENDING)
- continue;
-
- if (!do_now) {
- /* Too young to die */
- if (time_after(ino->last_used + timeout, now))
- continue;
-
- /* update last_used here :-
- - obviously makes sense if it is in use now
- - less obviously, prevents rapid-fire expire
- attempts if expire fails the first time */
- ino->last_used = now;
+ dentry = dget(dentry);
+ spin_unlock(&dcache_lock);
+
+ /*
+ * Case 1: (i) indirect mount or top level pseudo direct mount
+ * (autofs-4.1).
+ * (ii) indirect mount with offset mount, check the "/"
+ * offset (autofs-5.0+).
+ */
+ if (d_mountpoint(dentry)) {
+ DPRINTK("checking mountpoint %p %.*s",
+ dentry, (int)dentry->d_name.len, dentry->d_name.name);
+
+ /* Can we umount this guy */
+ if (autofs4_mount_busy(mnt, dentry))
+ goto next;
+
+ /* Can we expire this guy */
+ if (autofs4_can_expire(dentry, timeout, do_now)) {
+ expired = dentry;
+ break;
+ }
+ goto next;
}
- if (!is_tree_busy(mnt, dentry)) {
- DPRINTK(("autofs_expire: returning %p %.*s\n",
- dentry, (int)dentry->d_name.len, dentry->d_name.name));
- /* Start from here next time */
- list_del(&root->d_subdirs);
- list_add(&root->d_subdirs, &dentry->d_child);
- dget(dentry);
- spin_unlock(&dcache_lock);
-
- return dentry;
+
+ if (simple_empty(dentry))
+ goto next;
+
+ /* Case 2: tree mount, expire iff entire tree is not busy */
+ if (!exp_leaves) {
+ /* Lock the tree as we must expire as a whole */
+ spin_lock(&sbi->fs_lock);
+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) {
+ struct autofs_info *inf = autofs4_dentry_ino(dentry);
+
+ /* Set this flag early to catch sys_chdir and the like */
+ inf->flags |= AUTOFS_INF_EXPIRING;
+ spin_unlock(&sbi->fs_lock);
+ expired = dentry;
+ break;
+ }
+ spin_unlock(&sbi->fs_lock);
+ /*
+ * Case 3: pseudo direct mount, expire individual leaves
+ * (autofs-4.1).
+ */
+ } else {
+ expired = autofs4_check_leaves(mnt, dentry, timeout, do_now);
+ if (expired) {
+ dput(dentry);
+ break;
+ }
}
+next:
+ dput(dentry);
+ spin_lock(&dcache_lock);
+ next = next->next;
+ }
+
+ if (expired) {
+ DPRINTK("returning %p %.*s",
+ expired, (int)expired->d_name.len, expired->d_name.name);
+ spin_lock(&dcache_lock);
+ list_del(&expired->d_parent->d_subdirs);
+ list_add(&expired->d_parent->d_subdirs, &expired->d_u.d_child);
+ spin_unlock(&dcache_lock);
+ return expired;
}
spin_unlock(&dcache_lock);
int autofs4_expire_run(struct super_block *sb,
struct vfsmount *mnt,
struct autofs_sb_info *sbi,
- struct autofs_packet_expire *pkt_p)
+ struct autofs_packet_expire __user *pkt_p)
{
struct autofs_packet_expire pkt;
struct dentry *dentry;
pkt.hdr.proto_version = sbi->version;
pkt.hdr.type = autofs_ptype_expire;
- if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL)
+ if ((dentry = autofs4_expire_indirect(sb, mnt, sbi, 0)) == NULL)
return -EAGAIN;
pkt.len = dentry->d_name.len;
/* Call repeatedly until it returns -EAGAIN, meaning there's nothing
more to be done */
int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt,
- struct autofs_sb_info *sbi, int *arg)
+ struct autofs_sb_info *sbi, int __user *arg)
{
struct dentry *dentry;
int ret = -EAGAIN;
if (arg && get_user(do_now, arg))
return -EFAULT;
- if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) {
- struct autofs_info *de_info = autofs4_dentry_ino(dentry);
+ if (sbi->type & AUTOFS_TYPE_DIRECT)
+ dentry = autofs4_expire_direct(sb, mnt, sbi, do_now);
+ else
+ dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now);
+
+ if (dentry) {
+ struct autofs_info *ino = autofs4_dentry_ino(dentry);
/* This is synchronous because it makes the daemon a
little easier */
- de_info->flags |= AUTOFS_INF_EXPIRING;
- ret = autofs4_wait(sbi, &dentry->d_name, NFY_EXPIRE);
- de_info->flags &= ~AUTOFS_INF_EXPIRING;
+ ino->flags |= AUTOFS_INF_EXPIRING;
+ ret = autofs4_wait(sbi, dentry, NFY_EXPIRE);
+ ino->flags &= ~AUTOFS_INF_EXPIRING;
dput(dentry);
}
-
+
return ret;
}