autofs-5.0.3 - try not to block on expire
From: Ian Kent <raven@themaw.net>
When a server is not available umount and any stat statfs and related
function calls may block for a significant amount of time. This effects
expire timeouts a lot due to their synchronous nature. This patch limits
the time we wait on spawned umounts and elininates calls to functions
that would block. This allows us to retry the umount on the next expire
event and continue the expire of remaining mounts.
---
CHANGELOG | 2 +
daemon/automount.c | 34 +++++++++++++++++-------------
daemon/direct.c | 29 +++++---------------------
daemon/indirect.c | 26 ++++++++---------------
daemon/spawn.c | 59 +++++++++++++++++++++++++++++++++++++++++++----------
include/master.h | 1
lib/master.c | 23 ++++++++++++++++++++
lib/mounts.c | 48 +------------------------------------------
8 files changed, 112 insertions(+), 110 deletions(-)
--- autofs-5.0.3.orig/CHANGELOG
+++ autofs-5.0.3/CHANGELOG
@@ -25,6 +25,8 @@
- don't use proc file system when checking if the daemon is running.
- make handle_mounts startup condition distinct.
- fix submount shutdown recovery handling.
+- avoid stat of possibly dead mount points and limit time to wait for
+ umount during expire.
14/01/2008 autofs-5.0.3
-----------------------
--- autofs-5.0.3.orig/daemon/automount.c
+++ autofs-5.0.3/daemon/automount.c
@@ -247,9 +247,17 @@ static int walk_tree(const char *base, i
int, void *), int incl, unsigned logopt, void *arg)
{
char buf[PATH_MAX + 1];
- struct stat st;
+ struct stat st, *pst = &st;
+ int ret;
+
+ if (!is_mounted(_PATH_MOUNTED, base, MNTS_REAL))
+ ret = lstat(base, pst);
+ else {
+ pst = NULL;
+ ret = 0;
+ }
- if (lstat(base, &st) != -1 && (fn) (logopt, base, &st, 0, arg)) {
+ if (ret != -1 && (fn) (logopt, base, pst, 0, arg)) {
if (S_ISDIR(st.st_mode)) {
struct dirent **de;
int n;
@@ -283,7 +291,7 @@ static int walk_tree(const char *base, i
free(de);
}
if (incl)
- (fn) (logopt, base, &st, 1, arg);
+ (fn) (logopt, base, pst, 1, arg);
}
return 0;
}
@@ -294,6 +302,9 @@ static int rm_unwanted_fn(unsigned logop
char buf[MAX_ERR_BUF];
struct stat newst;
+ if (!st)
+ return 0;
+
if (when == 0) {
if (st->st_dev != dev)
return 0;
@@ -344,8 +355,8 @@ static int counter_fn(unsigned logopt, c
{
struct counter_args *counter = (struct counter_args *) arg;
- if (S_ISLNK(st->st_mode) || (S_ISDIR(st->st_mode)
- && st->st_dev != counter->dev)) {
+ if (!st || (S_ISLNK(st->st_mode) || (S_ISDIR(st->st_mode)
+ && st->st_dev != counter->dev))) {
counter->count++;
return 0;
}
@@ -512,9 +523,8 @@ static int umount_subtree_mounts(struct
int umount_multi(struct autofs_point *ap, const char *path, int incl)
{
struct mapent_cache *nc;
- struct statfs fs;
int is_autofs_fs;
- int ret, left;
+ int left;
debug(ap->logopt, "path %s incl %d", path, incl);
@@ -526,13 +536,9 @@ int umount_multi(struct autofs_point *ap
}
cache_unlock(nc);
- ret = statfs(path, &fs);
- if (ret == -1) {
- error(ap->logopt, "could not stat fs of %s", path);
- return 1;
- }
-
- is_autofs_fs = fs.f_type == (__SWORD_TYPE) AUTOFS_SUPER_MAGIC ? 1 : 0;
+ is_autofs_fs = 0;
+ if (master_find_submount(ap, path))
+ is_autofs_fs = 1;
left = 0;
--- autofs-5.0.3.orig/daemon/direct.c
+++ autofs-5.0.3/daemon/direct.c
@@ -152,7 +152,7 @@ int do_umount_autofs_direct(struct autof
retries = UMOUNT_RETRIES;
while ((rv = umount(me->key)) == -1 && retries--) {
- struct timespec tm = {0, 100000000};
+ struct timespec tm = {0, 200000000};
if (errno != EBUSY)
break;
nanosleep(&tm, NULL);
@@ -604,7 +604,7 @@ int umount_autofs_offset(struct autofs_p
retries = UMOUNT_RETRIES;
while ((rv = umount(me->key)) == -1 && retries--) {
- struct timespec tm = {0, 100000000};
+ struct timespec tm = {0, 200000000};
if (errno != EBUSY)
break;
nanosleep(&tm, NULL);
@@ -705,7 +705,7 @@ int mount_autofs_offset(struct autofs_po
* the kernel NFS client.
*/
if (me->multi != me &&
- is_mounted(_PROC_MOUNTS, me->key, MNTS_REAL))
+ is_mounted(_PATH_MOUNTED, me->key, MNTS_REAL))
return MOUNT_OFFSET_IGNORE;
/*
@@ -807,17 +807,7 @@ out_err:
static int expire_direct(int ioctlfd, const char *path, unsigned int when, unsigned int logopt)
{
- char buf[MAX_ERR_BUF];
- int ret, retries;
- struct stat st;
-
- if (fstat(ioctlfd, &st) == -1) {
- char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
- debug(logopt, "fstat failed: %s", estr);
- return 0;
- }
-
- retries = (count_mounts(logopt, path, st.st_dev) + 1) * EXPIRE_RETRIES;
+ int ret, retries = EXPIRE_RETRIES;
while (retries--) {
struct timespec tm = {0, 100000000};
@@ -911,7 +901,6 @@ void *expire_proc_direct(void *arg)
if (!strcmp(next->fs_type, "autofs")) {
struct stat st;
- struct statfs fs;
int ioctlfd;
cache_unlock(me->mc);
@@ -932,14 +921,8 @@ void *expire_proc_direct(void *arg)
continue;
}
- if (statfs(next->path, &fs) == -1) {
- pthread_setcancelstate(cur_state, NULL);
- warn(ap->logopt,
- "fstatfs failed for %s", next->path);
- continue;
- }
-
- if (fs.f_type != (__SWORD_TYPE) AUTOFS_SUPER_MAGIC) {
+ /* It's got a mount, deal with in the outer loop */
+ if (tree_is_mounted(mnts, me->key, MNTS_REAL)) {
pthread_setcancelstate(cur_state, NULL);
continue;
}
--- autofs-5.0.3.orig/daemon/indirect.c
+++ autofs-5.0.3/daemon/indirect.c
@@ -285,7 +285,7 @@ int umount_autofs_indirect(struct autofs
retries = UMOUNT_RETRIES;
while ((rv = umount(ap->path)) == -1 && retries--) {
- struct timespec tm = {0, 100000000};
+ struct timespec tm = {0, 200000000};
if (errno != EBUSY)
break;
nanosleep(&tm, NULL);
@@ -368,17 +368,7 @@ force_umount:
static int expire_indirect(struct autofs_point *ap, int ioctlfd, const char *path, unsigned int when)
{
- char buf[MAX_ERR_BUF];
- int ret, retries;
- struct stat st;
-
- if (fstat(ioctlfd, &st) == -1) {
- char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
- debug(ap->logopt, "fstat failed: %s", estr);
- return 0;
- }
-
- retries = (count_mounts(ap->logopt, path, st.st_dev) + 1) * EXPIRE_RETRIES;
+ int ret, retries = EXPIRE_RETRIES;
while (retries--) {
struct timespec tm = {0, 100000000};
@@ -512,7 +502,6 @@ void *expire_proc_indirect(void *arg)
left++;
pthread_setcancelstate(cur_state, NULL);
}
- pthread_cleanup_pop(1);
/*
* If there are no more real mounts left we could still
@@ -520,12 +509,17 @@ void *expire_proc_indirect(void *arg)
* umount them here.
*/
if (mnts) {
+ int retries;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
- ret = expire_indirect(ap, ap->ioctlfd, ap->path, now);
- if (!ret)
- left++;
+ retries = (count_mounts(ap->logopt, ap->path, ap->dev) + 1);
+ while (retries--) {
+ ret = expire_indirect(ap, ap->ioctlfd, ap->path, now);
+ if (!ret)
+ left++;
+ }
pthread_setcancelstate(cur_state, NULL);
}
+ pthread_cleanup_pop(1);
count = offsets = submnts = 0;
mnts = get_mnt_list(_PROC_MOUNTS, ap->path, 0);
--- autofs-5.0.3.orig/daemon/spawn.c
+++ autofs-5.0.3/daemon/spawn.c
@@ -89,13 +89,43 @@ void reset_signals(void)
#define ERRBUFSIZ 2047 /* Max length of error string excl \0 */
-static int do_spawn(unsigned logopt, unsigned int options, const char *prog, const char *const *argv)
+static int timed_read(int pipe, char *buf, size_t len, int time)
+{
+ struct timeval timeout = { 0, 0 };
+ struct timeval *tout = NULL;
+ fd_set wset, rset;
+ int ret;
+
+ FD_ZERO(&rset);
+ FD_SET(pipe, &rset);
+ wset = rset;
+
+ if (time != -1) {
+ timeout.tv_sec = time;
+ tout = &timeout;
+ }
+
+ ret = select(pipe + 1, &rset, &wset, NULL, tout);
+ if (ret <= 0) {
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+ return ret;
+ }
+
+ while ((ret = read(pipe, buf, len)) == -1 && errno == EINTR);
+
+ return ret;
+}
+
+static int do_spawn(unsigned logopt, unsigned int wait,
+ unsigned int options, const char *prog,
+ const char *const *argv)
{
pid_t f;
int ret, status, pipefd[2];
char errbuf[ERRBUFSIZ + 1], *p, *sp;
int errp, errn;
- int cancel_state;
+ int flags, cancel_state;
unsigned int use_lock = options & SPAWN_OPT_LOCK;
unsigned int use_access = options & SPAWN_OPT_ACCESS;
sigset_t allsigs, tmpsig, oldsig;
@@ -183,12 +213,15 @@ static int do_spawn(unsigned logopt, uns
return -1;
}
+ if ((flags = fcntl(pipefd[0], F_GETFD, 0)) != -1) {
+ flags |= FD_CLOEXEC;
+ fcntl(pipefd[0], F_SETFD, flags);
+ }
+
errp = 0;
do {
- while ((errn =
- read(pipefd[0], errbuf + errp, ERRBUFSIZ - errp)) == -1
- && errno == EINTR);
-
+ errn = timed_read(pipefd[0],
+ errbuf + errp, ERRBUFSIZ - errp, wait);
if (errn > 0) {
errp += errn;
@@ -213,6 +246,9 @@ static int do_spawn(unsigned logopt, uns
}
} while (errn > 0);
+ if (errn == -ETIMEDOUT)
+ kill(f, SIGTERM);
+
close(pipefd[0]);
if (errp > 0) {
@@ -238,7 +274,7 @@ static int do_spawn(unsigned logopt, uns
int spawnv(unsigned logopt, const char *prog, const char *const *argv)
{
- return do_spawn(logopt, SPAWN_OPT_NONE, prog, argv);
+ return do_spawn(logopt, -1, SPAWN_OPT_NONE, prog, argv);
}
int spawnl(unsigned logopt, const char *prog, ...)
@@ -259,7 +295,7 @@ int spawnl(unsigned logopt, const char *
while ((*p++ = va_arg(arg, char *)));
va_end(arg);
- return do_spawn(logopt, SPAWN_OPT_NONE, prog, (const char **) argv);
+ return do_spawn(logopt, -1, SPAWN_OPT_NONE, prog, (const char **) argv);
}
int spawn_mount(unsigned logopt, ...)
@@ -307,7 +343,7 @@ int spawn_mount(unsigned logopt, ...)
va_end(arg);
while (retries--) {
- ret = do_spawn(logopt, options, prog, (const char **) argv);
+ ret = do_spawn(logopt, -1, options, prog, (const char **) argv);
if (ret & MTAB_NOTUPDATED) {
struct timespec tm = {3, 0};
@@ -406,7 +442,7 @@ int spawn_bind_mount(unsigned logopt, ..
va_end(arg);
while (retries--) {
- ret = do_spawn(logopt, options, prog, (const char **) argv);
+ ret = do_spawn(logopt, -1, options, prog, (const char **) argv);
if (ret & MTAB_NOTUPDATED) {
struct timespec tm = {3, 0};
@@ -466,6 +502,7 @@ int spawn_umount(unsigned logopt, ...)
unsigned int options;
unsigned int retries = MTAB_LOCK_RETRIES;
int ret, printed = 0;
+ unsigned int wait = 12;
#ifdef ENABLE_MOUNT_LOCKING
options = SPAWN_OPT_LOCK;
@@ -488,7 +525,7 @@ int spawn_umount(unsigned logopt, ...)
va_end(arg);
while (retries--) {
- ret = do_spawn(logopt, options, prog, (const char **) argv);
+ ret = do_spawn(logopt, wait, options, prog, (const char **) argv);
if (ret & MTAB_NOTUPDATED) {
/*
* If the mount succeeded but the mtab was not
--- autofs-5.0.3.orig/include/master.h
+++ autofs-5.0.3/include/master.h
@@ -91,6 +91,7 @@ void master_source_lock_cleanup(void *);
void master_source_current_wait(struct master_mapent *);
void master_source_current_signal(struct master_mapent *);
struct master_mapent *master_find_mapent(struct master *, const char *);
+struct autofs_point *master_find_submount(struct autofs_point *, const char *);
struct master_mapent *master_new_mapent(struct master *, const char *, time_t);
void master_add_mapent(struct master *, struct master_mapent *);
void master_remove_mapent(struct master_mapent *);
--- autofs-5.0.3.orig/lib/master.c
+++ autofs-5.0.3/lib/master.c
@@ -602,6 +602,29 @@ struct master_mapent *master_find_mapent
return NULL;
}
+struct autofs_point *master_find_submount(struct autofs_point *ap, const char *path)
+{
+ struct list_head *head, *p;
+
+ mounts_mutex_lock(ap);
+
+ head = &ap->submounts;
+ list_for_each(p, head) {
+ struct autofs_point *submount;
+
+ submount = list_entry(p, struct autofs_point, mounts);
+
+ if (!strcmp(submount->path, path)) {
+ mounts_mutex_unlock(ap);
+ return submount;
+ }
+ }
+
+ mounts_mutex_unlock(ap);
+
+ return NULL;
+}
+
struct master_mapent *master_new_mapent(struct master *master, const char *path, time_t age)
{
struct master_mapent *entry;
--- autofs-5.0.3.orig/lib/mounts.c
+++ autofs-5.0.3/lib/mounts.c
@@ -1073,55 +1073,11 @@ free_tsv:
int umount_ent(struct autofs_point *ap, const char *path)
{
- struct stat st;
- struct statfs fs;
- int sav_errno;
- int status, is_smbfs = 0;
- int ret, rv = 1;
-
- ret = statfs(path, &fs);
- if (ret == -1) {
- warn(ap->logopt, "could not stat fs of %s", path);
- is_smbfs = 0;
- } else {
- int cifsfs = fs.f_type == (__SWORD_TYPE) CIFS_MAGIC_NUMBER;
- int smbfs = fs.f_type == (__SWORD_TYPE) SMB_SUPER_MAGIC;
- is_smbfs = (cifsfs | smbfs) ? 1 : 0;
- }
-
- status = lstat(path, &st);
- sav_errno = errno;
-
- if (status < 0)
- warn(ap->logopt, "lstat of %s failed with %d", path, status);
-
- /*
- * lstat failed and we're an smbfs fs returning an error that is not
- * EIO or EBADSLT or the lstat failed so it's a bad path. Return
- * a fail.
- *
- * EIO appears to correspond to an smb mount that has gone away
- * and EBADSLT relates to CD changer not responding.
- */
- if (!status && (S_ISDIR(st.st_mode) && st.st_dev != ap->dev)) {
- rv = spawn_umount(ap->logopt, path, NULL);
- } else if (is_smbfs && (sav_errno == EIO || sav_errno == EBADSLT)) {
- rv = spawn_umount(ap->logopt, path, NULL);
- }
+ int rv;
+ rv = spawn_umount(ap->logopt, path, NULL);
/* We are doing a forced shutcwdown down so unlink busy mounts */
if (rv && (ap->state == ST_SHUTDOWN_FORCE || ap->state == ST_SHUTDOWN)) {
- ret = stat(path, &st);
- if (ret == -1 && errno == ENOENT) {
- warn(ap->logopt, "mount point does not exist");
- return 0;
- }
-
- if (ret == 0 && !S_ISDIR(st.st_mode)) {
- warn(ap->logopt, "mount point is not a directory");
- return 0;
- }
-
if (ap->state == ST_SHUTDOWN_FORCE) {
info(ap->logopt, "forcing umount of %s", path);
rv = spawn_umount(ap->logopt, "-l", path, NULL);