autofs-5.0.3 - try not to block on expire From: Ian Kent 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(-) diff --git a/CHANGELOG b/CHANGELOG index ff04985..ff44cc7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -27,6 +27,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 ----------------------- diff --git a/daemon/automount.c b/daemon/automount.c index 68bf1d3..5bd5f6d 100644 --- a/daemon/automount.c +++ b/daemon/automount.c @@ -247,9 +247,17 @@ static int walk_tree(const char *base, int (*fn) (unsigned logopt, 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, int (*fn) (unsigned logopt, 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 logopt, const char *file, const struct stat * 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, const char *file, const struct stat *st, { 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 autofs_point *ap, const char *path, unsi 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, const char *path, int incl) } 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; diff --git a/daemon/direct.c b/daemon/direct.c index 334a4b6..7fb78a3 100644 --- a/daemon/direct.c +++ b/daemon/direct.c @@ -152,7 +152,7 @@ int do_umount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, stru 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_point *ap, struct mapent *me) 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_point *ap, struct mapent *me) * 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; } diff --git a/daemon/indirect.c b/daemon/indirect.c index 17bed3e..e832cd4 100644 --- a/daemon/indirect.c +++ b/daemon/indirect.c @@ -285,7 +285,7 @@ int umount_autofs_indirect(struct autofs_point *ap) 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); diff --git a/daemon/spawn.c b/daemon/spawn.c index 78d69c6..e3c355e 100644 --- a/daemon/spawn.c +++ b/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, unsigned int options, const char *prog, con 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, unsigned int options, const char *prog, con } } 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, unsigned int options, const char *prog, con 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 *prog, ...) 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 diff --git a/include/master.h b/include/master.h index 86ae045..a397a75 100644 --- a/include/master.h +++ b/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 *); diff --git a/lib/master.c b/lib/master.c index 522b919..71ba04a 100644 --- a/lib/master.c +++ b/lib/master.c @@ -602,6 +602,29 @@ struct master_mapent *master_find_mapent(struct master *master, const char *path 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; diff --git a/lib/mounts.c b/lib/mounts.c index a4bf86c..d77a6b0 100644 --- a/lib/mounts.c +++ b/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);