On Fri, 2008-11-21 at 07:54 -0800, Joe Pruett wrote:
> i have now changed my setup to be totally straightforward. /home has
> normal direct mounts for everything, like:
>
> shipper hyperion:/disk/0/home/shipper
>
> auto.master has:
>
> /home auto.home --timeout=900
>
> on a very lightly loaded system i am seeing the following error 1-2 times
> per day:
>
> Nov 20 22:30:01 titan automount[1743]: do_mount_indirect: indirect trigger
> not valid or already mounted /home/shipper
>
> shipper has 4 cron jobs that fires off every 30 minutes. when this
> problem happens, just the first cron job fails, the rest run fine.
>
> i have just turned on automount debugging logging, but from past
> experience that seems to mask most problems.
>
> this is on centos 5 with stock kernel and autofs rpms:
> autofs-5.0.1-0.rc2.88
> kernel-2.6.18-92.1.18.el5
>
> i have also enabled the nis netid feature so that i'm not using large
> amounts of privileged ports.
>
> is there anything else i should be looking at?
Looks like a known problem.
Your only choice id to build a custom kernel.
Try the patch below.
It should fix the problem your and several other problems.
I'm not willing to pull out the specific patch(es) that address this
because of possible unexpected dependencies.
Ian
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
index e474a9e..d599c69 100644
--- a/fs/autofs4/autofs_i.h
+++ b/fs/autofs4/autofs_i.h
@@ -21,6 +21,8 @@
#define AUTOFS_IOC_FIRST AUTOFS_IOC_READY
#define AUTOFS_IOC_COUNT 32
+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET)
+
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/time.h>
@@ -54,7 +56,10 @@ struct autofs_info {
int flags;
- struct list_head rehash;
+ struct completion expire_complete;
+
+ struct list_head active;
+ struct list_head expiring;
struct autofs_sb_info *sbi;
unsigned long last_used;
@@ -70,15 +75,14 @@ struct autofs_info {
};
#define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring
*/
+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */
struct autofs_wait_queue {
wait_queue_head_t queue;
struct autofs_wait_queue *next;
autofs_wqt_t wait_queue_token;
/* We use the following to see what we are waiting for */
- unsigned int hash;
- unsigned int len;
- char *name;
+ struct qstr name;
u32 dev;
u64 ino;
uid_t uid;
@@ -87,15 +91,11 @@ struct autofs_wait_queue {
pid_t tgid;
/* This is for status reporting upon return */
int status;
- atomic_t wait_ctr;
+ unsigned int wait_ctr;
};
#define AUTOFS_SBI_MAGIC 0x6d4a556d
-#define AUTOFS_TYPE_INDIRECT 0x0001
-#define AUTOFS_TYPE_DIRECT 0x0002
-#define AUTOFS_TYPE_OFFSET 0x0004
-
struct autofs_sb_info {
u32 magic;
int pipefd;
@@ -114,8 +114,9 @@ struct autofs_sb_info {
struct mutex wq_mutex;
spinlock_t fs_lock;
struct autofs_wait_queue *queues; /* Wait queue pointer */
- spinlock_t rehash_lock;
- struct list_head rehash_list;
+ spinlock_t lookup_lock;
+ struct list_head active_list;
+ struct list_head expiring_list;
};
static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb)
@@ -140,18 +141,14 @@ static inline int autofs4_oz_mode(struct autofs_sb_info
*sbi) {
static inline int autofs4_ispending(struct dentry *dentry)
{
struct autofs_info *inf = autofs4_dentry_ino(dentry);
- int pending = 0;
if (dentry->d_flags & DCACHE_AUTOFS_PENDING)
return 1;
- if (inf) {
- spin_lock(&inf->sbi->fs_lock);
- pending = inf->flags & AUTOFS_INF_EXPIRING;
- spin_unlock(&inf->sbi->fs_lock);
- }
+ if (inf->flags & AUTOFS_INF_EXPIRING)
+ return 1;
- return pending;
+ return 0;
}
static inline void autofs4_copy_atime(struct file *src, struct file *dst)
@@ -165,6 +162,7 @@ void autofs4_free_ino(struct autofs_info *);
/* Expiration */
int is_autofs4_dentry(struct dentry *);
+int autofs4_expire_wait(struct dentry *dentry);
int autofs4_expire_run(struct super_block *, struct vfsmount *,
struct autofs_sb_info *,
struct autofs_packet_expire __user *);
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
index d96e5c1..e96e81b 100644
--- a/fs/autofs4/expire.c
+++ b/fs/autofs4/expire.c
@@ -56,12 +56,23 @@ static int autofs4_mount_busy(struct vfsmount *mnt, struct
dentry *dentry)
mntget(mnt);
dget(dentry);
- if (!autofs4_follow_mount(&mnt, &dentry))
+ if (!follow_down(&mnt, &dentry))
goto done;
- /* This is an autofs submount, we can't expire it */
- if (is_autofs4_dentry(dentry))
- goto done;
+ if (is_autofs4_dentry(dentry)) {
+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
+
+ /* This is an autofs submount, we can't expire it */
+ if (sbi->type == AUTOFS_TYPE_INDIRECT)
+ goto done;
+
+ /*
+ * Otherwise it's an offset mount and we need to check
+ * if we can umount its mount, if there is one.
+ */
+ if (!d_mountpoint(dentry))
+ goto done;
+ }
/* Update the expiry counter if fs is busy */
if (!may_umount_tree(mnt)) {
@@ -73,8 +84,8 @@ static int autofs4_mount_busy(struct vfsmount *mnt, struct
dentry *dentry)
status = 0;
done:
DPRINTK("returning = %d", status);
- mntput(mnt);
dput(dentry);
+ mntput(mnt);
return status;
}
@@ -259,13 +270,15 @@ static struct dentry *autofs4_expire_direct(struct
super_block *sb,
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 */
+ if (d_mountpoint(root)) {
+ ino->flags |= AUTOFS_INF_MOUNTPOINT;
+ root->d_mounted--;
+ }
ino->flags |= AUTOFS_INF_EXPIRING;
+ init_completion(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
return root;
}
@@ -292,6 +305,8 @@ static struct dentry *autofs4_expire_indirect(struct
super_block *sb,
struct list_head *next;
int do_now = how & AUTOFS_EXP_IMMEDIATE;
int exp_leaves = how & AUTOFS_EXP_LEAVES;
+ struct autofs_info *ino;
+ unsigned int ino_count;
if (!root)
return NULL;
@@ -316,6 +331,9 @@ static struct dentry *autofs4_expire_indirect(struct
super_block *sb,
dentry = dget(dentry);
spin_unlock(&dcache_lock);
+ spin_lock(&sbi->fs_lock);
+ ino = autofs4_dentry_ino(dentry);
+
/*
* Case 1: (i) indirect mount or top level pseudo direct mount
* (autofs-4.1).
@@ -326,6 +344,11 @@ static struct dentry *autofs4_expire_indirect(struct
super_block *sb,
DPRINTK("checking mountpoint %p %.*s",
dentry, (int)dentry->d_name.len,
dentry->d_name.name);
+ /* Path walk currently on this dentry? */
+ ino_count = atomic_read(&ino->count) + 2;
+ if (atomic_read(&dentry->d_count) > ino_count)
+ goto next;
+
/* Can we umount this guy */
if (autofs4_mount_busy(mnt, dentry))
goto next;
@@ -333,7 +356,7 @@ static struct dentry *autofs4_expire_indirect(struct
super_block *sb,
/* Can we expire this guy */
if (autofs4_can_expire(dentry, timeout, do_now)) {
expired = dentry;
- break;
+ goto found;
}
goto next;
}
@@ -343,46 +366,80 @@ static struct dentry *autofs4_expire_indirect(struct
super_block *sb,
/* 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);
+ /* Path walk currently on this dentry? */
+ ino_count = atomic_read(&ino->count) + 1;
+ if (atomic_read(&dentry->d_count) > ino_count)
+ goto next;
- /* Set this flag early to catch sys_chdir and
the like */
- inf->flags |= AUTOFS_INF_EXPIRING;
- spin_unlock(&sbi->fs_lock);
+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) {
expired = dentry;
- break;
+ goto found;
}
- spin_unlock(&sbi->fs_lock);
/*
* Case 3: pseudo direct mount, expire individual leaves
* (autofs-4.1).
*/
} else {
+ /* Path walk currently on this dentry? */
+ ino_count = atomic_read(&ino->count) + 1;
+ if (atomic_read(&dentry->d_count) > ino_count)
+ goto next;
+
expired = autofs4_check_leaves(mnt, dentry, timeout,
do_now);
if (expired) {
dput(dentry);
- break;
+ goto found;
}
}
next:
+ spin_unlock(&sbi->fs_lock);
dput(dentry);
spin_lock(&dcache_lock);
next = next->next;
}
+ spin_unlock(&dcache_lock);
+ return NULL;
- if (expired) {
- DPRINTK("returning %p %.*s",
- expired, (int)expired->d_name.len,
expired->d_name.name);
- spin_lock(&dcache_lock);
- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child);
- spin_unlock(&dcache_lock);
- return expired;
- }
+found:
+ DPRINTK("returning %p %.*s",
+ expired, (int)expired->d_name.len, expired->d_name.name);
+ ino = autofs4_dentry_ino(expired);
+ ino->flags |= AUTOFS_INF_EXPIRING;
+ init_completion(&ino->expire_complete);
+ spin_unlock(&sbi->fs_lock);
+ spin_lock(&dcache_lock);
+ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child);
spin_unlock(&dcache_lock);
+ return expired;
+}
- return NULL;
+int autofs4_expire_wait(struct dentry *dentry)
+{
+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
+ struct autofs_info *ino = autofs4_dentry_ino(dentry);
+ int status;
+
+ /* Block on any pending expire */
+ spin_lock(&sbi->fs_lock);
+ if (ino->flags & AUTOFS_INF_EXPIRING) {
+ spin_unlock(&sbi->fs_lock);
+
+ DPRINTK("waiting for expire %p name=%.*s",
+ dentry, dentry->d_name.len, dentry->d_name.name);
+
+ status = autofs4_wait(sbi, dentry, NFY_NONE);
+ wait_for_completion(&ino->expire_complete);
+
+ DPRINTK("expire done status=%d", status);
+
+ if (d_unhashed(dentry))
+ return -EAGAIN;
+
+ return status;
+ }
+ spin_unlock(&sbi->fs_lock);
+
+ return 0;
}
/* Perform an expiry operation */
@@ -392,7 +449,9 @@ int autofs4_expire_run(struct super_block *sb,
struct autofs_packet_expire __user *pkt_p)
{
struct autofs_packet_expire pkt;
+ struct autofs_info *ino;
struct dentry *dentry;
+ int ret = 0;
memset(&pkt,0,sizeof pkt);
@@ -408,9 +467,15 @@ int autofs4_expire_run(struct super_block *sb,
dput(dentry);
if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) )
- return -EFAULT;
+ ret = -EFAULT;
- return 0;
+ spin_lock(&sbi->fs_lock);
+ ino = autofs4_dentry_ino(dentry);
+ ino->flags &= ~AUTOFS_INF_EXPIRING;
+ complete_all(&ino->expire_complete);
+ spin_unlock(&sbi->fs_lock);
+
+ return ret;
}
/* Call repeatedly until it returns -EAGAIN, meaning there's nothing
@@ -425,7 +490,7 @@ int autofs4_expire_multi(struct super_block *sb, struct
vfsmount *mnt,
if (arg && get_user(do_now, arg))
return -EFAULT;
- if (sbi->type & AUTOFS_TYPE_DIRECT)
+ if (sbi->type & AUTOFS_TYPE_TRIGGER)
dentry = autofs4_expire_direct(sb, mnt, sbi, do_now);
else
dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now);
@@ -435,9 +500,16 @@ int autofs4_expire_multi(struct super_block *sb, struct
vfsmount *mnt,
/* This is synchronous because it makes the daemon a
little easier */
- ino->flags |= AUTOFS_INF_EXPIRING;
ret = autofs4_wait(sbi, dentry, NFY_EXPIRE);
+
+ spin_lock(&sbi->fs_lock);
+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) {
+ sb->s_root->d_mounted++;
+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT;
+ }
ino->flags &= ~AUTOFS_INF_EXPIRING;
+ complete_all(&ino->expire_complete);
+ spin_unlock(&sbi->fs_lock);
dput(dentry);
}
diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c
index a444577..1b700d1 100644
--- a/fs/autofs4/inode.c
+++ b/fs/autofs4/inode.c
@@ -24,8 +24,10 @@
static void ino_lnkfree(struct autofs_info *ino)
{
- kfree(ino->u.symlink);
- ino->u.symlink = NULL;
+ if (ino->u.symlink) {
+ kfree(ino->u.symlink);
+ ino->u.symlink = NULL;
+ }
}
struct autofs_info *autofs4_init_ino(struct autofs_info *ino,
@@ -41,16 +43,18 @@ struct autofs_info *autofs4_init_ino(struct autofs_info
*ino,
if (ino == NULL)
return NULL;
- ino->flags = 0;
- ino->mode = mode;
- ino->inode = NULL;
- ino->dentry = NULL;
- ino->size = 0;
-
- INIT_LIST_HEAD(&ino->rehash);
+ if (!reinit) {
+ ino->flags = 0;
+ ino->inode = NULL;
+ ino->dentry = NULL;
+ ino->size = 0;
+ INIT_LIST_HEAD(&ino->active);
+ INIT_LIST_HEAD(&ino->expiring);
+ atomic_set(&ino->count, 0);
+ }
+ ino->mode = mode;
ino->last_used = jiffies;
- atomic_set(&ino->count, 0);
ino->sbi = sbi;
@@ -159,8 +163,8 @@ void autofs4_kill_sb(struct super_block *sb)
if (!sbi)
goto out_kill_sb;
- if (!sbi->catatonic)
- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */
+ /* Free wait queues, close pipe */
+ autofs4_catatonic_mode(sbi);
/* Clean up and release dangling references */
autofs4_force_release(sbi);
@@ -280,7 +284,7 @@ static int parse_options(char *options, int *pipefd, uid_t
*uid, gid_t *gid,
*type = AUTOFS_TYPE_DIRECT;
break;
case Opt_offset:
- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET;
+ *type = AUTOFS_TYPE_OFFSET;
break;
default:
return 1;
@@ -330,14 +334,15 @@ int autofs4_fill_super(struct super_block *s, void *data,
int silent)
sbi->sb = s;
sbi->version = 0;
sbi->sub_version = 0;
- sbi->type = 0;
+ sbi->type = AUTOFS_TYPE_INDIRECT;
sbi->min_proto = 0;
sbi->max_proto = 0;
mutex_init(&sbi->wq_mutex);
spin_lock_init(&sbi->fs_lock);
sbi->queues = NULL;
- spin_lock_init(&sbi->rehash_lock);
- INIT_LIST_HEAD(&sbi->rehash_list);
+ spin_lock_init(&sbi->lookup_lock);
+ INIT_LIST_HEAD(&sbi->active_list);
+ INIT_LIST_HEAD(&sbi->expiring_list);
s->s_blocksize = 1024;
s->s_blocksize_bits = 10;
s->s_magic = AUTOFS_SUPER_MAGIC;
@@ -372,7 +377,7 @@ int autofs4_fill_super(struct super_block *s, void *data,
int silent)
}
root_inode->i_fop = &autofs4_root_operations;
- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ?
+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ?
&autofs4_direct_root_inode_operations :
&autofs4_indirect_root_inode_operations;
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index d2e23bf..d474338 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -26,25 +26,25 @@ static int autofs4_dir_rmdir(struct inode *,struct dentry
*);
static int autofs4_dir_mkdir(struct inode *,struct dentry *,int);
static int autofs4_root_ioctl(struct inode *, struct file *,unsigned
int,unsigned long);
static int autofs4_dir_open(struct inode *inode, struct file *file);
-static int autofs4_dir_close(struct inode *inode, struct file *file);
-static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t
filldir);
-static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t
filldir);
static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct
nameidata *);
static void *autofs4_follow_link(struct dentry *, struct nameidata *);
+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY)
+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE)
+
const struct file_operations autofs4_root_operations = {
.open = dcache_dir_open,
.release = dcache_dir_close,
.read = generic_read_dir,
- .readdir = autofs4_root_readdir,
+ .readdir = dcache_readdir,
.ioctl = autofs4_root_ioctl,
};
const struct file_operations autofs4_dir_operations = {
.open = autofs4_dir_open,
- .release = autofs4_dir_close,
+ .release = dcache_dir_close,
.read = generic_read_dir,
- .readdir = autofs4_dir_readdir,
+ .readdir = dcache_readdir,
};
struct inode_operations autofs4_indirect_root_inode_operations = {
@@ -71,42 +71,10 @@ struct inode_operations autofs4_dir_inode_operations = {
.rmdir = autofs4_dir_rmdir,
};
-static int autofs4_root_readdir(struct file *file, void *dirent,
- filldir_t filldir)
-{
- struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb);
- int oz_mode = autofs4_oz_mode(sbi);
-
- DPRINTK("called, filp->f_pos = %lld", file->f_pos);
-
- /*
- * Don't set reghost flag if:
- * 1) f_pos is larger than zero -- we've already been here.
- * 2) we haven't even enabled reghosting in the 1st place.
- * 3) this is the daemon doing a readdir
- */
- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled)
- sbi->needs_reghost = 1;
-
- DPRINTK("needs_reghost = %d", sbi->needs_reghost);
-
- return dcache_readdir(file, dirent, filldir);
-}
-
static int autofs4_dir_open(struct inode *inode, struct file *file)
{
struct dentry *dentry = file->f_dentry;
- struct vfsmount *mnt = file->f_vfsmnt;
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
- struct dentry *cursor;
- int status;
-
- status = dcache_dir_open(inode, file);
- if (status)
- goto out;
-
- cursor = file->private_data;
- cursor->d_fsdata = NULL;
DPRINTK("file=%p dentry=%p %.*s",
file, dentry, dentry->d_name.len, dentry->d_name.name);
@@ -114,157 +82,31 @@ static int autofs4_dir_open(struct inode *inode, struct
file *file)
if (autofs4_oz_mode(sbi))
goto out;
- if (autofs4_ispending(dentry)) {
- DPRINTK("dentry busy");
- dcache_dir_close(inode, file);
- status = -EBUSY;
- goto out;
- }
-
- status = -ENOENT;
- if (!d_mountpoint(dentry) && dentry->d_op &&
dentry->d_op->d_revalidate) {
- struct nameidata nd;
- int empty, ret;
-
- /* In case there are stale directory dentrys from a failed
mount */
- spin_lock(&dcache_lock);
- empty = list_empty(&dentry->d_subdirs);
+ /*
+ * An empty directory in an autofs file system is always a
+ * mount point. The daemon must have failed to mount this
+ * during lookup so it doesn't exist. This can happen, for
+ * example, if user space returns an incorrect status for a
+ * mount request. Otherwise we're doing a readdir on the
+ * autofs file system so just let the libfs routines handle
+ * it.
+ */
+ spin_lock(&dcache_lock);
+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) {
spin_unlock(&dcache_lock);
-
- if (!empty)
- d_invalidate(dentry);
-
- nd.flags = LOOKUP_DIRECTORY;
- ret = (dentry->d_op->d_revalidate)(dentry, &nd);
-
- if (ret <= 0) {
- if (ret < 0)
- status = ret;
- dcache_dir_close(inode, file);
- goto out;
- }
- }
-
- if (d_mountpoint(dentry)) {
- struct file *fp = NULL;
- struct vfsmount *fp_mnt = mntget(mnt);
- struct dentry *fp_dentry = dget(dentry);
-
- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) {
- dput(fp_dentry);
- mntput(fp_mnt);
- dcache_dir_close(inode, file);
- goto out;
- }
-
- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags);
- status = PTR_ERR(fp);
- if (IS_ERR(fp)) {
- dcache_dir_close(inode, file);
- goto out;
- }
- cursor->d_fsdata = fp;
- }
- return 0;
-out:
- return status;
-}
-
-static int autofs4_dir_close(struct inode *inode, struct file *file)
-{
- struct dentry *dentry = file->f_dentry;
- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
- struct dentry *cursor = file->private_data;
- int status = 0;
-
- DPRINTK("file=%p dentry=%p %.*s",
- file, dentry, dentry->d_name.len, dentry->d_name.name);
-
- if (autofs4_oz_mode(sbi))
- goto out;
-
- if (autofs4_ispending(dentry)) {
- DPRINTK("dentry busy");
- status = -EBUSY;
- goto out;
- }
-
- if (d_mountpoint(dentry)) {
- struct file *fp = cursor->d_fsdata;
- if (!fp) {
- status = -ENOENT;
- goto out;
- }
- filp_close(fp, current->files);
+ return -ENOENT;
}
-out:
- dcache_dir_close(inode, file);
- return status;
-}
-
-static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t
filldir)
-{
- struct dentry *dentry = file->f_dentry;
- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
- struct dentry *cursor = file->private_data;
- int status;
-
- DPRINTK("file=%p dentry=%p %.*s",
- file, dentry, dentry->d_name.len, dentry->d_name.name);
-
- if (autofs4_oz_mode(sbi))
- goto out;
-
- if (autofs4_ispending(dentry)) {
- DPRINTK("dentry busy");
- return -EBUSY;
- }
-
- if (d_mountpoint(dentry)) {
- struct file *fp = cursor->d_fsdata;
-
- if (!fp)
- return -ENOENT;
-
- if (!fp->f_op || !fp->f_op->readdir)
- goto out;
+ spin_unlock(&dcache_lock);
- status = vfs_readdir(fp, filldir, dirent);
- file->f_pos = fp->f_pos;
- if (status)
- autofs4_copy_atime(file, fp);
- return status;
- }
out:
- return dcache_readdir(file, dirent, filldir);
+ return dcache_dir_open(inode, file);
}
static int try_to_fill_dentry(struct dentry *dentry, int flags)
{
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
- int status = 0;
-
- /* Block on any pending expiry here; invalidate the dentry
- when expiration is done to trigger mount request with a new
- dentry */
- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) {
- DPRINTK("waiting for expire %p name=%.*s",
- dentry, dentry->d_name.len, dentry->d_name.name);
-
- status = autofs4_wait(sbi, dentry, NFY_NONE);
-
- DPRINTK("expire done status=%d", status);
-
- /*
- * If the directory still exists the mount request must
- * continue otherwise it can't be followed at the right
- * time during the walk.
- */
- status = d_invalidate(dentry);
- if (status != -EBUSY)
- return -EAGAIN;
- }
+ int status;
DPRINTK("dentry=%p %.*s ino=%p",
dentry, dentry->d_name.len, dentry->d_name.name,
dentry->d_inode);
@@ -292,7 +134,8 @@ static int try_to_fill_dentry(struct dentry *dentry, int
flags)
return status;
}
/* Trigger mount for path component or follow link */
- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ||
+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING ||
+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) ||
current->link_count) {
DPRINTK("waiting for mount name=%.*s",
dentry->d_name.len, dentry->d_name.name);
@@ -319,7 +162,7 @@ static int try_to_fill_dentry(struct dentry *dentry, int
flags)
spin_lock(&dentry->d_lock);
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
spin_unlock(&dentry->d_lock);
- return status;
+ return 0;
}
/* For autofs direct mounts the follow link triggers the mount */
@@ -334,50 +177,62 @@ static void *autofs4_follow_link(struct dentry *dentry,
struct nameidata *nd)
DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d",
dentry, dentry->d_name.len, dentry->d_name.name, oz_mode,
nd->flags);
-
- /* If it's our master or we shouldn't trigger a mount we're done */
- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY);
- if (oz_mode || !lookup_type)
+ /*
+ * For an expire of a covered direct or offset mount we need
+ * to beeak out of follow_down() at the autofs mount trigger
+ * (d_mounted--), so we can see the expiring flag, and manage
+ * the blocking and following here until the expire is completed.
+ */
+ if (oz_mode) {
+ spin_lock(&sbi->fs_lock);
+ if (ino->flags & AUTOFS_INF_EXPIRING) {
+ spin_unlock(&sbi->fs_lock);
+ /* Follow down to our covering mount. */
+ if (!follow_down(&nd->mnt, &nd->dentry))
+ goto done;
+ goto follow;
+ }
+ spin_unlock(&sbi->fs_lock);
goto done;
+ }
- /* If an expire request is pending wait for it. */
- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) {
- DPRINTK("waiting for active request %p name=%.*s",
- dentry, dentry->d_name.len, dentry->d_name.name);
+ /* If an expire request is pending everyone must wait. */
+ autofs4_expire_wait(dentry);
- status = autofs4_wait(sbi, dentry, NFY_NONE);
-
- DPRINTK("request done status=%d", status);
- }
+ /* 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))
+ 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.
+ * 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 (!d_mountpoint(dentry) && __simple_empty(dentry)) {
+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING ||
+ (!d_mountpoint(dentry) && __simple_empty(dentry))) {
spin_unlock(&dcache_lock);
status = try_to_fill_dentry(dentry, 0);
if (status)
goto out_error;
- /*
- * The mount succeeded but if there is no root mount
- * it must be an autofs multi-mount with no root offset
- * so we don't need to follow the mount.
- */
- if (d_mountpoint(dentry)) {
- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) {
- status = -ENOENT;
- goto out_error;
- }
- }
-
- goto done;
+ goto follow;
}
spin_unlock(&dcache_lock);
+follow:
+ /*
+ * If there is no root mount it must be an autofs
+ * multi-mount with no root offset so we don't need
+ * to follow it.
+ */
+ if (d_mountpoint(dentry)) {
+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) {
+ status = -ENOENT;
+ goto out_error;
+ }
+ }
done:
return NULL;
@@ -402,12 +257,23 @@ static int autofs4_revalidate(struct dentry *dentry,
struct nameidata *nd)
int status;
/* Pending dentry */
+ spin_lock(&sbi->fs_lock);
if (autofs4_ispending(dentry)) {
/* The daemon never causes a mount to trigger */
+ spin_unlock(&sbi->fs_lock);
+
if (oz_mode)
return 1;
/*
+ * If the directory has gone away due to an expire
+ * we have been called as ->d_revalidate() and so
+ * we need to return false and proceed to ->lookup().
+ */
+ if (autofs4_expire_wait(dentry) == -EAGAIN)
+ return 0;
+
+ /*
* A zero status is success otherwise we have a
* negative error code.
*/
@@ -415,17 +281,9 @@ static int autofs4_revalidate(struct dentry *dentry,
struct nameidata *nd)
if (status == 0)
return 1;
- /*
- * A status of EAGAIN here means that the dentry has gone
- * away while waiting for an expire to complete. If we are
- * racing with expire lookup will wait for it so this must
- * be a revalidate and we need to send it to lookup.
- */
- if (status == -EAGAIN)
- return 0;
-
return status;
}
+ spin_unlock(&sbi->fs_lock);
/* Negative dentry.. invalidate if "old" */
if (dentry->d_inode == NULL)
@@ -439,6 +297,7 @@ static int autofs4_revalidate(struct dentry *dentry, struct
nameidata *nd)
DPRINTK("dentry=%p %.*s, emptydir",
dentry, dentry->d_name.len, dentry->d_name.name);
spin_unlock(&dcache_lock);
+
/* The daemon never causes a mount to trigger */
if (oz_mode)
return 1;
@@ -471,10 +330,12 @@ void autofs4_dentry_release(struct dentry *de)
struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb);
if (sbi) {
- spin_lock(&sbi->rehash_lock);
- if (!list_empty(&inf->rehash))
- list_del(&inf->rehash);
- spin_unlock(&sbi->rehash_lock);
+ spin_lock(&sbi->lookup_lock);
+ if (!list_empty(&inf->active))
+ list_del(&inf->active);
+ if (!list_empty(&inf->expiring))
+ list_del(&inf->expiring);
+ spin_unlock(&sbi->lookup_lock);
}
inf->dentry = NULL;
@@ -496,7 +357,59 @@ static struct dentry_operations autofs4_dentry_operations
= {
.d_release = autofs4_dentry_release,
};
-static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi,
struct dentry *parent, struct qstr *name)
+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct
dentry *parent, struct qstr *name)
+{
+ unsigned int len = name->len;
+ unsigned int hash = name->hash;
+ const unsigned char *str = name->name;
+ struct list_head *p, *head;
+
+ spin_lock(&dcache_lock);
+ spin_lock(&sbi->lookup_lock);
+ head = &sbi->active_list;
+ list_for_each(p, head) {
+ struct autofs_info *ino;
+ struct dentry *dentry;
+ struct qstr *qstr;
+
+ ino = list_entry(p, struct autofs_info, active);
+ dentry = ino->dentry;
+
+ spin_lock(&dentry->d_lock);
+
+ /* Already gone? */
+ if (atomic_read(&dentry->d_count) == 0)
+ goto next;
+
+ qstr = &dentry->d_name;
+
+ if (dentry->d_name.hash != hash)
+ goto next;
+ if (dentry->d_parent != parent)
+ goto next;
+
+ if (qstr->len != len)
+ goto next;
+ if (memcmp(qstr->name, str, len))
+ goto next;
+
+ if (d_unhashed(dentry)) {
+ dget(dentry);
+ spin_unlock(&dentry->d_lock);
+ spin_unlock(&sbi->lookup_lock);
+ spin_unlock(&dcache_lock);
+ return dentry;
+ }
+next:
+ spin_unlock(&dentry->d_lock);
+ }
+ spin_unlock(&sbi->lookup_lock);
+ spin_unlock(&dcache_lock);
+
+ return NULL;
+}
+
+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi,
struct dentry *parent, struct qstr *name)
{
unsigned int len = name->len;
unsigned int hash = name->hash;
@@ -504,14 +417,14 @@ static struct dentry *autofs4_lookup_unhashed(struct
autofs_sb_info *sbi, struct
struct list_head *p, *head;
spin_lock(&dcache_lock);
- spin_lock(&sbi->rehash_lock);
- head = &sbi->rehash_list;
+ spin_lock(&sbi->lookup_lock);
+ head = &sbi->expiring_list;
list_for_each(p, head) {
struct autofs_info *ino;
struct dentry *dentry;
struct qstr *qstr;
- ino = list_entry(p, struct autofs_info, rehash);
+ ino = list_entry(p, struct autofs_info, expiring);
dentry = ino->dentry;
spin_lock(&dentry->d_lock);
@@ -533,33 +446,16 @@ static struct dentry *autofs4_lookup_unhashed(struct
autofs_sb_info *sbi, struct
goto next;
if (d_unhashed(dentry)) {
- struct autofs_info *ino = autofs4_dentry_ino(dentry);
- struct inode *inode = dentry->d_inode;
-
- list_del_init(&ino->rehash);
dget(dentry);
- /*
- * Make the rehashed dentry negative so the VFS
- * behaves as it should.
- */
- if (inode) {
- dentry->d_inode = NULL;
- list_del_init(&dentry->d_alias);
- spin_unlock(&dentry->d_lock);
- spin_unlock(&sbi->rehash_lock);
- spin_unlock(&dcache_lock);
- iput(inode);
- return dentry;
- }
spin_unlock(&dentry->d_lock);
- spin_unlock(&sbi->rehash_lock);
+ spin_unlock(&sbi->lookup_lock);
spin_unlock(&dcache_lock);
return dentry;
}
next:
spin_unlock(&dentry->d_lock);
}
- spin_unlock(&sbi->rehash_lock);
+ spin_unlock(&sbi->lookup_lock);
spin_unlock(&dcache_lock);
return NULL;
@@ -569,7 +465,8 @@ next:
static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry,
struct nameidata *nd)
{
struct autofs_sb_info *sbi;
- struct dentry *unhashed;
+ struct autofs_info *ino;
+ struct dentry *expiring, *unhashed;
int oz_mode;
DPRINTK("name = %.*s",
@@ -585,10 +482,28 @@ static struct dentry *autofs4_lookup(struct inode *dir,
struct dentry *dentry, s
DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d",
current->pid, process_group(current), sbi->catatonic, oz_mode);
- unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent,
&dentry->d_name);
- if (!unhashed) {
+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent,
&dentry->d_name);
+ if (expiring) {
/*
- * Mark the dentry incomplete but don't hash it. We do this
+ * 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);
+ dput(expiring);
+ }
+
+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent,
&dentry->d_name);
+ if (unhashed)
+ dentry = unhashed;
+ else {
+ /*
+ * Mark the dentry incomplete but don't hash it. We do this
* to serialize our inode creation operations (symlink and
* mkdir) which prevents deadlock during the callback to
* the daemon. Subsequent user space lookups for the same
@@ -600,39 +515,34 @@ static struct dentry *autofs4_lookup(struct inode *dir,
struct dentry *dentry, s
*/
dentry->d_op = &autofs4_root_dentry_operations;
- dentry->d_fsdata = NULL;
- d_instantiate(dentry, NULL);
- } else {
- struct autofs_info *ino = autofs4_dentry_ino(unhashed);
- DPRINTK("rehash %p with %p", dentry, unhashed);
/*
- * 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.
- * We need to ensure the AUTOFS_INF_EXPIRING flag is clear
- * before continuing as revalidate may fail when calling
- * try_to_fill_dentry (returning EAGAIN) if we don't.
+ * And we need to ensure that the same dentry is used for
+ * all following lookup calls until it is hashed so that
+ * the dentry flags are persistent throughout the request.
*/
- while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) {
- DPRINTK("wait for incomplete expire %p name=%.*s",
- unhashed, unhashed->d_name.len,
- unhashed->d_name.name);
- autofs4_wait(sbi, unhashed, NFY_NONE);
- DPRINTK("request completed");
- }
- dentry = unhashed;
+ ino = autofs4_init_ino(NULL, sbi, 0555);
+ if (!ino)
+ return ERR_PTR(-ENOMEM);
+
+ dentry->d_fsdata = ino;
+ ino->dentry = dentry;
+
+ spin_lock(&sbi->lookup_lock);
+ list_add(&ino->active, &sbi->active_list);
+ spin_unlock(&sbi->lookup_lock);
+
+ d_instantiate(dentry, NULL);
}
if (!oz_mode) {
spin_lock(&dentry->d_lock);
dentry->d_flags |= DCACHE_AUTOFS_PENDING;
spin_unlock(&dentry->d_lock);
- }
-
- if (dentry->d_op && dentry->d_op->d_revalidate) {
- mutex_unlock(&dir->i_mutex);
- (dentry->d_op->d_revalidate)(dentry, nd);
- mutex_lock(&dir->i_mutex);
+ if (dentry->d_op && dentry->d_op->d_revalidate) {
+ mutex_unlock(&dir->i_mutex);
+ (dentry->d_op->d_revalidate)(dentry, nd);
+ mutex_lock(&dir->i_mutex);
+ }
}
/*
@@ -651,9 +561,11 @@ static struct dentry *autofs4_lookup(struct inode *dir,
struct dentry *dentry, s
return ERR_PTR(-ERESTARTNOINTR);
}
}
- spin_lock(&dentry->d_lock);
- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
- spin_unlock(&dentry->d_lock);
+ if (!oz_mode) {
+ spin_lock(&dentry->d_lock);
+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
+ spin_unlock(&dentry->d_lock);
+ }
}
/*
@@ -684,7 +596,7 @@ static struct dentry *autofs4_lookup(struct inode *dir,
struct dentry *dentry, s
}
if (unhashed)
- return dentry;
+ return unhashed;
return NULL;
}
@@ -706,20 +618,31 @@ static int autofs4_dir_symlink(struct inode *dir,
return -EACCES;
ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555);
- if (ino == NULL)
- return -ENOSPC;
+ if (!ino)
+ return -ENOMEM;
- ino->size = strlen(symname);
- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL);
+ spin_lock(&sbi->lookup_lock);
+ if (!list_empty(&ino->active))
+ list_del_init(&ino->active);
+ spin_unlock(&sbi->lookup_lock);
- if (cp == NULL) {
- kfree(ino);
- return -ENOSPC;
+ ino->size = strlen(symname);
+ cp = kmalloc(ino->size + 1, GFP_KERNEL);
+ if (!cp) {
+ if (!dentry->d_fsdata)
+ kfree(ino);
+ return -ENOMEM;
}
strcpy(cp, symname);
inode = autofs4_get_inode(dir->i_sb, ino);
+ if (!inode) {
+ kfree(cp);
+ if (!dentry->d_fsdata)
+ kfree(ino);
+ return -ENOMEM;
+ }
d_add(dentry, inode);
if (dir == dir->i_sb->s_root->d_inode)
@@ -735,6 +658,7 @@ static int autofs4_dir_symlink(struct inode *dir,
atomic_inc(&p_ino->count);
ino->inode = inode;
+ ino->u.symlink = cp;
dir->i_mtime = CURRENT_TIME;
return 0;
@@ -747,9 +671,8 @@ static int autofs4_dir_symlink(struct inode *dir,
* that the file no longer exists. However, doing that means that the
* VFS layer can turn the dentry into a negative dentry. We don't want
* this, because the unlink is probably the result of an expire.
- * We simply d_drop it and add it to a rehash candidates list in the
- * super block, which allows the dentry lookup to reuse it retaining
- * the flags, such as expire in progress, in case we're racing with expire.
+ * We simply d_drop it and add it to a expiring list in the super block,
+ * which allows the dentry lookup to check for an incomplete expire.
*
* If a process is blocked on the dentry waiting for the expire to finish,
* it will invalidate the dentry and try to mount with a new one.
@@ -779,9 +702,10 @@ static int autofs4_dir_unlink(struct inode *dir, struct
dentry *dentry)
dir->i_mtime = CURRENT_TIME;
spin_lock(&dcache_lock);
- spin_lock(&sbi->rehash_lock);
- list_add(&ino->rehash, &sbi->rehash_list);
- spin_unlock(&sbi->rehash_lock);
+ spin_lock(&sbi->lookup_lock);
+ if (list_empty(&ino->expiring))
+ list_add(&ino->expiring, &sbi->expiring_list);
+ spin_unlock(&sbi->lookup_lock);
spin_lock(&dentry->d_lock);
__d_drop(dentry);
spin_unlock(&dentry->d_lock);
@@ -807,9 +731,10 @@ static int autofs4_dir_rmdir(struct inode *dir, struct
dentry *dentry)
spin_unlock(&dcache_lock);
return -ENOTEMPTY;
}
- spin_lock(&sbi->rehash_lock);
- list_add(&ino->rehash, &sbi->rehash_list);
- spin_unlock(&sbi->rehash_lock);
+ spin_lock(&sbi->lookup_lock);
+ if (list_empty(&ino->expiring))
+ list_add(&ino->expiring, &sbi->expiring_list);
+ spin_unlock(&sbi->lookup_lock);
spin_lock(&dentry->d_lock);
__d_drop(dentry);
spin_unlock(&dentry->d_lock);
@@ -844,10 +769,20 @@ static int autofs4_dir_mkdir(struct inode *dir, struct
dentry *dentry, int mode)
dentry, dentry->d_name.len, dentry->d_name.name);
ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555);
- if (ino == NULL)
- return -ENOSPC;
+ if (!ino)
+ return -ENOMEM;
+
+ spin_lock(&sbi->lookup_lock);
+ if (!list_empty(&ino->active))
+ list_del_init(&ino->active);
+ spin_unlock(&sbi->lookup_lock);
inode = autofs4_get_inode(dir->i_sb, ino);
+ if (!inode) {
+ if (!dentry->d_fsdata)
+ kfree(ino);
+ return -ENOMEM;
+ }
d_add(dentry, inode);
if (dir == dir->i_sb->s_root->d_inode)
@@ -900,44 +835,6 @@ static inline int autofs4_get_protosubver(struct
autofs_sb_info *sbi, int __user
}
/*
- * Tells the daemon whether we need to reghost or not. Also, clears
- * the reghost_needed flag.
- */
-static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user
*p)
-{
- int status;
-
- DPRINTK("returning %d", sbi->needs_reghost);
-
- status = put_user(sbi->needs_reghost, p);
- if ( status )
- return status;
-
- sbi->needs_reghost = 0;
- return 0;
-}
-
-/*
- * Enable / Disable reghosting ioctl() operation
- */
-static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int
__user *p)
-{
- int status;
- int val;
-
- status = get_user(val, p);
-
- DPRINTK("reghost = %d", val);
-
- if (status)
- return status;
-
- /* turn on/off reghosting, with the val */
- sbi->reghost_enabled = val;
- return 0;
-}
-
-/*
* Tells the daemon whether it can umount the autofs mount.
*/
static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p)
@@ -1001,11 +898,6 @@ static int autofs4_root_ioctl(struct inode *inode, struct
file *filp,
case AUTOFS_IOC_SETTIMEOUT:
return autofs4_get_set_timeout(sbi, p);
- case AUTOFS_IOC_TOGGLEREGHOST:
- return autofs4_toggle_reghost(sbi, p);
- case AUTOFS_IOC_ASKREGHOST:
- return autofs4_ask_reghost(sbi, p);
-
case AUTOFS_IOC_ASKUMOUNT:
return autofs4_ask_umount(filp->f_vfsmnt, p);
diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c
index 78c62b6..73ce14b 100644
--- a/fs/autofs4/waitq.c
+++ b/fs/autofs4/waitq.c
@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autofs_sb_info *sbi)
{
struct autofs_wait_queue *wq, *nwq;
+ mutex_lock(&sbi->wq_mutex);
+ if (sbi->catatonic) {
+ mutex_unlock(&sbi->wq_mutex);
+ return;
+ }
+
DPRINTK("entering catatonic mode");
sbi->catatonic = 1;
@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autofs_sb_info *sbi)
while (wq) {
nwq = wq->next;
wq->status = -ENOENT; /* Magic is gone - report failure */
- kfree(wq->name);
- wq->name = NULL;
+ if (wq->name.name) {
+ kfree(wq->name.name);
+ wq->name.name = NULL;
+ }
+ wq->wait_ctr--;
wake_up_interruptible(&wq->queue);
wq = nwq;
}
fput(sbi->pipe); /* Close the pipe */
sbi->pipe = NULL;
+ sbi->pipefd = -1;
+ mutex_unlock(&sbi->wq_mutex);
}
static int autofs4_write(struct file *file, const void *addr, int bytes)
@@ -86,9 +97,10 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi,
{
union autofs_packet_union pkt;
size_t pktsz;
+ struct file *pipe = NULL;
DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d",
- wq->wait_queue_token, wq->len, wq->name, type);
+ wq->wait_queue_token, wq->name.len, wq->name.name, type);
memset(&pkt,0,sizeof pkt); /* For security reasons */
@@ -103,9 +115,9 @@ static void autofs4_notify_daemon(struct autofs_sb_info
*sbi,
pktsz = sizeof(*mp);
mp->wait_queue_token = wq->wait_queue_token;
- mp->len = wq->len;
- memcpy(mp->name, wq->name, wq->len);
- mp->name[wq->len] = '\0';
+ mp->len = wq->name.len;
+ memcpy(mp->name, wq->name.name, wq->name.len);
+ mp->name[wq->name.len] = '\0';
break;
}
case autofs_ptype_expire_multi:
@@ -115,9 +127,9 @@ static void autofs4_notify_daemon(struct autofs_sb_info
*sbi,
pktsz = sizeof(*ep);
ep->wait_queue_token = wq->wait_queue_token;
- ep->len = wq->len;
- memcpy(ep->name, wq->name, wq->len);
- ep->name[wq->len] = '\0';
+ ep->len = wq->name.len;
+ memcpy(ep->name, wq->name.name, wq->name.len);
+ ep->name[wq->name.len] = '\0';
break;
}
/*
@@ -134,9 +146,9 @@ static void autofs4_notify_daemon(struct autofs_sb_info
*sbi,
pktsz = sizeof(*packet);
packet->wait_queue_token = wq->wait_queue_token;
- packet->len = wq->len;
- memcpy(packet->name, wq->name, wq->len);
- packet->name[wq->len] = '\0';
+ packet->len = wq->name.len;
+ memcpy(packet->name, wq->name.name, wq->name.len);
+ packet->name[wq->name.len] = '\0';
packet->dev = wq->dev;
packet->ino = wq->ino;
packet->uid = wq->uid;
@@ -150,8 +162,19 @@ static void autofs4_notify_daemon(struct autofs_sb_info
*sbi,
return;
}
- if (autofs4_write(sbi->pipe, &pkt, pktsz))
- autofs4_catatonic_mode(sbi);
+ /* Check if we have become catatonic */
+ mutex_lock(&sbi->wq_mutex);
+ if (!sbi->catatonic) {
+ pipe = sbi->pipe;
+ get_file(pipe);
+ }
+ mutex_unlock(&sbi->wq_mutex);
+
+ if (pipe) {
+ if (autofs4_write(pipe, &pkt, pktsz))
+ autofs4_catatonic_mode(sbi);
+ fput(pipe);
+ }
}
static int autofs4_getpath(struct autofs_sb_info *sbi,
@@ -187,58 +210,55 @@ static int autofs4_getpath(struct autofs_sb_info *sbi,
}
static struct autofs_wait_queue *
-autofs4_find_wait(struct autofs_sb_info *sbi,
- char *name, unsigned int hash, unsigned int len)
+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr)
{
struct autofs_wait_queue *wq;
for (wq = sbi->queues; wq; wq = wq->next) {
- if (wq->hash == hash &&
- wq->len == len &&
- wq->name && !memcmp(wq->name, name, len))
+ if (wq->name.hash == qstr->hash &&
+ wq->name.len == qstr->len &&
+ wq->name.name &&
+ !memcmp(wq->name.name, qstr->name, qstr->len))
break;
}
return wq;
}
-int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry,
- enum autofs_notify notify)
+/*
+ * Check if we have a valid request.
+ * Returns
+ * 1 if the request should continue.
+ * In this case we can return an autofs_wait_queue entry if one is
+ * found or NULL to idicate a new wait needs to be created.
+ * 0 or a negative errno if the request shouldn't continue.
+ */
+static int validate_request(struct autofs_wait_queue **wait,
+ struct autofs_sb_info *sbi,
+ struct qstr *qstr,
+ struct dentry*dentry, enum autofs_notify notify)
{
- struct autofs_info *ino;
struct autofs_wait_queue *wq;
- char *name;
- unsigned int len = 0;
- unsigned int hash = 0;
- int status, type;
-
- /* In catatonic mode, we don't wait for nobody */
- if (sbi->catatonic)
- return -ENOENT;
-
- name = kmalloc(NAME_MAX + 1, GFP_KERNEL);
- if (!name)
- return -ENOMEM;
+ struct autofs_info *ino;
- /* If this is a direct mount request create a dummy name */
- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT))
- len = sprintf(name, "%p", dentry);
- else {
- len = autofs4_getpath(sbi, dentry, &name);
- if (!len) {
- kfree(name);
- return -ENOENT;
- }
+ /* Wait in progress, continue; */
+ wq = autofs4_find_wait(sbi, qstr);
+ if (wq) {
+ *wait = wq;
+ return 1;
}
- hash = full_name_hash(name, len);
- if (mutex_lock_interruptible(&sbi->wq_mutex)) {
- kfree(name);
- return -EINTR;
- }
+ *wait = NULL;
- wq = autofs4_find_wait(sbi, name, hash, len);
+ /* If we don't yet have any info this is a new request */
ino = autofs4_dentry_ino(dentry);
- if (!wq && ino && notify == NFY_NONE) {
+ if (!ino)
+ return 1;
+
+ /*
+ * If we've been asked to wait on an existing expire (NFY_NONE)
+ * but there is no wait in the queue ...
+ */
+ if (notify == NFY_NONE) {
/*
* Either we've betean the pending expire to post it's
* wait or it finished while we waited on the mutex.
@@ -249,13 +269,14 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct
dentry *dentry,
while (ino->flags & AUTOFS_INF_EXPIRING) {
mutex_unlock(&sbi->wq_mutex);
schedule_timeout_interruptible(HZ/10);
- if (mutex_lock_interruptible(&sbi->wq_mutex)) {
- kfree(name);
+ if (mutex_lock_interruptible(&sbi->wq_mutex))
return -EINTR;
+
+ wq = autofs4_find_wait(sbi, qstr);
+ if (wq) {
+ *wait = wq;
+ return 1;
}
- wq = autofs4_find_wait(sbi, name, hash, len);
- if (wq)
- break;
}
/*
@@ -263,18 +284,96 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct
dentry *dentry,
* cases where we wait on NFY_NONE neither depend on the
* return status of the wait.
*/
- if (!wq) {
+ return 0;
+ }
+
+ /*
+ * If we've been asked to trigger a mount and the request
+ * completed while we waited on the mutex ...
+ */
+ if (notify == NFY_MOUNT) {
+ /*
+ * If the dentry isn't hashed just go ahead and try the
+ * mount again with a new wait (not much else we can do).
+ */
+ if (!d_unhashed(dentry)) {
+ /*
+ * But if the dentry is hashed, that means that we
+ * got here through the revalidate path. Thus, we
+ * need to check if the dentry has been mounted
+ * while we waited on the wq_mutex. If it has,
+ * simply return success.
+ */
+ if (d_mountpoint(dentry))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry,
+ enum autofs_notify notify)
+{
+ struct autofs_wait_queue *wq;
+ struct qstr qstr;
+ char *name;
+ int status, ret, type;
+
+ /* In catatonic mode, we don't wait for nobody */
+ if (sbi->catatonic)
+ return -ENOENT;
+
+ if (!dentry->d_inode) {
+ /*
+ * A wait for a negative dentry is invalid for certain
+ * cases. A direct or offset mount "always" has its mount
+ * point directory created and so the request dentry must
+ * be positive or the map key doesn't exist. The situation
+ * is very similar for indirect mounts except only dentrys
+ * in the root of the autofs file system may be negative.
+ */
+ if (sbi->type & AUTOFS_TYPE_TRIGGER)
+ return -ENOENT;
+ else if (!IS_ROOT(dentry->d_parent))
+ return -ENOENT;
+ }
+
+ name = kmalloc(NAME_MAX + 1, GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+
+ /* If this is a direct mount request create a dummy name */
+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER)
+ qstr.len = sprintf(name, "%p", dentry);
+ else {
+ qstr.len = autofs4_getpath(sbi, dentry, &name);
+ if (!qstr.len) {
kfree(name);
- mutex_unlock(&sbi->wq_mutex);
- return 0;
+ return -ENOENT;
}
}
+ qstr.name = name;
+ qstr.hash = full_name_hash(name, qstr.len);
+
+ if (mutex_lock_interruptible(&sbi->wq_mutex)) {
+ kfree(qstr.name);
+ return -EINTR;
+ }
+
+ ret = validate_request(&wq, sbi, &qstr, dentry, notify);
+ if (ret <= 0) {
+ if (ret == 0)
+ mutex_unlock(&sbi->wq_mutex);
+ kfree(qstr.name);
+ return ret;
+ }
if (!wq) {
/* Create a new wait queue */
wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL);
if (!wq) {
- kfree(name);
+ kfree(qstr.name);
mutex_unlock(&sbi->wq_mutex);
return -ENOMEM;
}
@@ -285,9 +384,7 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry
*dentry,
wq->next = sbi->queues;
sbi->queues = wq;
init_waitqueue_head(&wq->queue);
- wq->hash = hash;
- wq->name = name;
- wq->len = len;
+ memcpy(&wq->name, &qstr, sizeof(struct qstr));
wq->dev = autofs4_get_dev(sbi);
wq->ino = autofs4_get_ino(sbi);
wq->uid = current->uid;
@@ -295,7 +392,7 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry
*dentry,
wq->pid = current->pid;
wq->tgid = current->tgid;
wq->status = -EINTR; /* Status return if interrupted */
- atomic_set(&wq->wait_ctr, 2);
+ wq->wait_ctr = 2;
mutex_unlock(&sbi->wq_mutex);
if (sbi->version < 5) {
@@ -305,38 +402,35 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct
dentry *dentry,
type = autofs_ptype_expire_multi;
} else {
if (notify == NFY_MOUNT)
- type = (sbi->type & AUTOFS_TYPE_DIRECT) ?
+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ?
autofs_ptype_missing_direct :
autofs_ptype_missing_indirect;
else
- type = (sbi->type & AUTOFS_TYPE_DIRECT) ?
+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ?
autofs_ptype_expire_direct :
autofs_ptype_expire_indirect;
}
DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n",
- (unsigned long) wq->wait_queue_token, wq->len,
wq->name, notify);
+ (unsigned long) wq->wait_queue_token, wq->name.len,
+ wq->name.name, notify);
/* autofs4_notify_daemon() may block */
autofs4_notify_daemon(sbi, wq, type);
} else {
- atomic_inc(&wq->wait_ctr);
+ wq->wait_ctr++;
mutex_unlock(&sbi->wq_mutex);
- kfree(name);
+ kfree(qstr.name);
DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d",
- (unsigned long) wq->wait_queue_token, wq->len,
wq->name, notify);
+ (unsigned long) wq->wait_queue_token, wq->name.len,
+ wq->name.name, notify);
}
- /* wq->name is NULL if and only if the lock is already released */
-
- if (sbi->catatonic) {
- /* We might have slept, so check again for catatonic mode */
- wq->status = -ENOENT;
- kfree(wq->name);
- wq->name = NULL;
- }
-
- if (wq->name) {
+ /*
+ * wq->name.name is NULL iff the lock is already released
+ * or the mount has been made catatonic.
+ */
+ if (wq->name.name) {
/* Block all but "shutdown" signals while waiting */
sigset_t oldset;
unsigned long irqflags;
@@ -347,7 +441,7 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry
*dentry,
recalc_sigpending();
spin_unlock_irqrestore(¤t->sighand->siglock, irqflags);
- wait_event_interruptible(wq->queue, wq->name == NULL);
+ wait_event_interruptible(wq->queue, wq->name.name == NULL);
spin_lock_irqsave(¤t->sighand->siglock, irqflags);
current->blocked = oldset;
@@ -360,8 +454,10 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry
*dentry,
status = wq->status;
/* Are we the last process to need status? */
- if (atomic_dec_and_test(&wq->wait_ctr))
+ mutex_lock(&sbi->wq_mutex);
+ if (!--wq->wait_ctr)
kfree(wq);
+ mutex_unlock(&sbi->wq_mutex);
return status;
}
@@ -383,16 +479,13 @@ int autofs4_wait_release(struct autofs_sb_info *sbi,
autofs_wqt_t wait_queue_tok
}
*wql = wq->next; /* Unlink from chain */
- mutex_unlock(&sbi->wq_mutex);
- kfree(wq->name);
- wq->name = NULL; /* Do not wait on this queue */
-
+ kfree(wq->name.name);
+ wq->name.name = NULL; /* Do not wait on this queue */
wq->status = status;
-
- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for
this guy? */
+ wake_up_interruptible(&wq->queue);
+ if (!--wq->wait_ctr)
kfree(wq);
- else
- wake_up_interruptible(&wq->queue);
+ mutex_unlock(&sbi->wq_mutex);
return 0;
}
diff --git a/include/linux/auto_fs4.h b/include/linux/auto_fs4.h
index 0a6bc52..48dd618 100644
--- a/include/linux/auto_fs4.h
+++ b/include/linux/auto_fs4.h
@@ -29,6 +29,11 @@
#define AUTOFS_EXP_IMMEDIATE 1
#define AUTOFS_EXP_LEAVES 2
+#define AUTOFS_TYPE_ANY 0x0000
+#define AUTOFS_TYPE_INDIRECT 0x0001
+#define AUTOFS_TYPE_DIRECT 0x0002
+#define AUTOFS_TYPE_OFFSET 0x0004
+
/* Daemon notification packet types */
enum autofs_notify {
NFY_NONE,
@@ -90,8 +95,6 @@ union autofs_packet_union {
#define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI
#define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI
#define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int)
-#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int)
-#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int)
#define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int)
diff --git a/include/linux/compat_ioctl.h b/include/linux/compat_ioctl.h
index a044316..533f651 100644
--- a/include/linux/compat_ioctl.h
+++ b/include/linux/compat_ioctl.h
@@ -567,8 +567,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER)
COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE)
COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI)
COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER)
-COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST)
-COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST)
COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT)
/* Raw devices */
COMPATIBLE_IOCTL(RAW_SETBIND)
_______________________________________________
autofs mailing list
[email protected]
http://linux.kernel.org/mailman/listinfo/autofs