diff --git a/CHANGELOG b/CHANGELOG index 3e801e2..866ae0a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,6 +19,8 @@ patches. - tidy up directory cleanup and add validation check to rmdir_path. - remove SIGCHLD handler. +- alter expire locking of multi-mounts to lock sub-tree instead of + entire tree. 13/7/2006 autofs-5.0.1 rc1 -------------------------- diff --git a/autofs.spec b/autofs.spec index 7532b80..cc1ee93 100644 --- a/autofs.spec +++ b/autofs.spec @@ -59,7 +59,7 @@ inkludera nätfilsystem, CD-ROM, floppydi echo %{version}-%{release} > .version %build -CFLAGS="$RPM_OPT_FLAGS -Wall" ./configure --libdir=%{_libdir} --enable-ignore-busy +CFLAGS="$RPM_OPT_FLAGS -Wall" ./configure --libdir=%{_libdir} --disable-mount-locking --enable-ignore-busy CFLAGS="$RPM_OPT_FLAGS -Wall" make initdir=/etc/rc.d/init.d DONTSTRIP=1 %install diff --git a/daemon/automount.c b/daemon/automount.c index 5217347..5fba783 100644 --- a/daemon/automount.c +++ b/daemon/automount.c @@ -370,6 +370,7 @@ static void update_map_cache(struct auto it also tries to umount path itself */ int umount_multi(struct autofs_point *ap, const char *path, int incl) { + struct mapent_cache *mc; struct mapent *me; struct statfs fs; int is_autofs_fs; @@ -400,16 +401,20 @@ int umount_multi(struct autofs_point *ap } } + cache_multi_lock(me->parent); + + mc = me->source->mc; + left = 0; - if (me && me->multi) { + if (me->multi) { struct autofs_point *oap = ap; char *root, *base; if (ap->submount) oap = ap->parent; - if (me == me->multi && !strchr(me->key, '/')) { + if (me == me->multi && !strchr(me->key, '/')) { /* Indirect multi-mount root */ root = alloca(strlen(ap->path) + strlen(me->key) + 2); strcpy(root, ap->path); @@ -427,10 +432,12 @@ int umount_multi(struct autofs_point *ap left++; } } - cache_unlock(me->source->mc); - if (left || is_autofs_fs) + if (left || is_autofs_fs) { + cache_multi_unlock(me->parent); + cache_unlock(mc); return left; + } /* * If this is the root of a multi-mount we've had to umount @@ -451,6 +458,9 @@ int umount_multi(struct autofs_point *ap check_rm_dirs(ap, path, incl); } + cache_multi_unlock(me->parent); + cache_unlock(mc); + return left; } diff --git a/daemon/direct.c b/daemon/direct.c index e6c1f35..5a528fc 100644 --- a/daemon/direct.c +++ b/daemon/direct.c @@ -725,29 +725,10 @@ out_err: static int expire_direct(int ioctlfd, const char *path, unsigned int when, unsigned int logopt) { - char *estr, buf[MAX_ERR_BUF]; int ret, retries = EXPIRE_RETRIES; while (retries--) { struct timespec tm = {0, 100000000}; - int busy = 0; - - ret = ioctl(ioctlfd, AUTOFS_IOC_ASKUMOUNT, &busy); - if (ret == -1) { - /* Mount has gone away */ - if (errno == EBADF || errno == EINVAL) - return 1; - - estr = strerror_r(errno, buf, MAX_ERR_BUF); - error(logopt, "ioctl failed: %s", estr); - return 0; - } - - /* No need to go further */ - if (busy) - return 0; - - sched_yield(); /* Ggenerate expire message for the mount. */ ret = ioctl(ioctlfd, AUTOFS_IOC_EXPIRE_DIRECT, &when); @@ -756,7 +737,7 @@ static int expire_direct(int ioctlfd, co if (errno == EBADF || errno == EINVAL) return 1; - /* Need to wait for the kernel ? */ + /* Other than need to wait for the kernel ? */ if (errno != EAGAIN) return 0; } diff --git a/daemon/indirect.c b/daemon/indirect.c index 970215c..766ec08 100644 --- a/daemon/indirect.c +++ b/daemon/indirect.c @@ -366,29 +366,10 @@ force_umount: static int expire_indirect(int ioctlfd, const char *path, unsigned int when, unsigned int count, unsigned int logopt) { - char *estr, buf[MAX_ERR_BUF]; int ret, retries = count; while (retries--) { struct timespec tm = {0, 100000000}; - int busy = 0; - - ret = ioctl(ioctlfd, AUTOFS_IOC_ASKUMOUNT, &busy); - if (ret == -1) { - /* Mount has gone away */ - if (errno == EBADF || errno == EINVAL) - return 1; - - estr = strerror_r(errno, buf, MAX_ERR_BUF); - error(logopt, "ioctl failed: %s", estr); - return 0; - } - - /* No need to go further */ - if (busy) - return 0; - - sched_yield(); /* Ggenerate expire message for the mount. */ ret = ioctl(ioctlfd, AUTOFS_IOC_EXPIRE_DIRECT, &when); @@ -397,7 +378,7 @@ static int expire_indirect(int ioctlfd, if (errno == EBADF || errno == EINVAL) return 1; - /* Need to wait for the kernel ? */ + /* Other than need to wait for the kernel ? */ if (errno != EAGAIN) return 0; } @@ -480,11 +461,10 @@ void *expire_proc_indirect(void *arg) if (*me->key == '/') { ioctlfd = me->ioctlfd; - cache_unlock(me->source->mc); } else { ioctlfd = ap->ioctlfd; - cache_unlock(me->source->mc); } + cache_unlock(me->source->mc); debug(ap->logopt, "expire %s", next->path); @@ -502,7 +482,7 @@ void *expire_proc_indirect(void *arg) * have some offset mounts with no '/' offset so we need to * umount them here. */ - limit = count_mounts(ap, ap->path); + limit = count_mounts(ap, ap->path) * 2; ret = expire_indirect(ap->ioctlfd, ap->path, now, limit, ap->logopt); if (!ret) { debug(ap->logopt, @@ -525,28 +505,22 @@ void *expire_proc_indirect(void *arg) } free_mnt_list(mnts); - if (submnts) { + if (submnts) debug(ap->logopt, "%d submounts remaining in %s", submnts, ap->path); - ea->status = 1; - pthread_exit(NULL); - } /* * EXPIRE_MULTI is synchronous, so we can be sure (famous last * words) the umounts are done by the time we reach here */ - if (count) { + if (count) debug(ap->logopt, "%d remaining in %s", count, ap->path); - ea->status = 1; - pthread_exit(NULL); - } /* If we are trying to shutdown make sure we can umount */ if (!ioctl(ap->ioctlfd, AUTOFS_IOC_ASKUMOUNT, &ret)) { if (!ret) { warn(ap->logopt, "mount still busy %s", ap->path); - ea->status = 1; + ea->status++; } } diff --git a/include/automount.h b/include/automount.h index 54a7180..90a9c7a 100644 --- a/include/automount.h +++ b/include/automount.h @@ -123,12 +123,11 @@ #define CHE_DUPLICATE 0x0020 #define HASHSIZE 77 #define NEGATIVE_TIMEOUT 10 -#define UMOUNT_RETRIES 25 -#define EXPIRE_RETRIES 15 +#define UMOUNT_RETRIES 50 +#define EXPIRE_RETRIES 25 struct mapent_cache { pthread_rwlock_t rwlock; - pthread_mutex_t multi_mutex; unsigned int size; struct list_head *ino_index; struct mapent **hash; @@ -137,11 +136,14 @@ struct mapent_cache { struct mapent { struct mapent *next; struct list_head ino_index; + pthread_mutex_t multi_mutex; struct list_head multi_list; /* Map source of the cache entry */ struct map_source *source; - /* Need to know owner if we're a multi mount */ + /* Need to know owner if we're a multi-mount */ struct mapent *multi; + /* Parent nesting point within multi-mount */ + struct mapent *parent; char *key; char *mapent; time_t age; @@ -173,11 +175,12 @@ struct mapent *cache_partial_match(struc int cache_add(struct mapent_cache *mc, struct map_source *source, const char *key, const char *mapent, time_t age); int cache_add_offset(struct mapent_cache *mc, const char *mkey, const char *key, const char *mapent, time_t age); +int cache_set_parents(struct mapent *mm); int cache_update(struct mapent_cache *mc, struct map_source *source, const char *key, const char *mapent, time_t age); int cache_delete(struct mapent_cache *mc, const char *key); -void cache_multi_lock(struct mapent_cache *mc); -void cache_multi_unlock(struct mapent_cache *mc); +void cache_multi_lock(struct mapent *me); +void cache_multi_unlock(struct mapent *me); int cache_delete_offset_list(struct mapent_cache *mc, const char *key); void cache_release(struct map_source *map); struct mapent *cache_enumerate(struct mapent_cache *mc, struct mapent *me); diff --git a/lib/cache.c b/lib/cache.c index a047320..432270b 100644 --- a/lib/cache.c +++ b/lib/cache.c @@ -98,11 +98,14 @@ void cache_lock_cleanup(void *arg) cache_unlock(mc); } -void cache_multi_lock(struct mapent_cache *mc) +void cache_multi_lock(struct mapent *me) { int status; - status = pthread_mutex_lock(&mc->multi_mutex); + if (!me) + return; + + status = pthread_mutex_lock(&me->multi_mutex); if (status) { error(LOGOPT_ANY, "mapent cache multi mutex lock failed"); fatal(status); @@ -110,17 +113,21 @@ void cache_multi_lock(struct mapent_cach return; } -void cache_multi_unlock(struct mapent_cache *mc) +void cache_multi_unlock(struct mapent *me) { int status; - status = pthread_mutex_unlock(&mc->multi_mutex); + if (!me) + return; + + status = pthread_mutex_unlock(&me->multi_mutex); if (status) { error(LOGOPT_ANY, "mapent cache multi mutex unlock failed"); fatal(status); } return; } + struct mapent_cache *cache_init(struct map_source *map) { struct mapent_cache *mc; @@ -153,10 +160,6 @@ struct mapent_cache *cache_init(struct m if (status) fatal(status); - status = pthread_mutex_init(&mc->multi_mutex, NULL); - if (status) - fatal(status); - cache_writelock(mc); for (i = 0; i < mc->size; i++) { @@ -411,6 +414,7 @@ int cache_add(struct mapent_cache *mc, s struct mapent *me, *existing = NULL; char *pkey, *pent; unsigned int hashval = hash(key); + int status; me = (struct mapent *) malloc(sizeof(struct mapent)); if (!me) @@ -440,10 +444,15 @@ int cache_add(struct mapent_cache *mc, s INIT_LIST_HEAD(&me->ino_index); INIT_LIST_HEAD(&me->multi_list); me->multi = NULL; + me->parent = NULL; me->ioctlfd = -1; me->dev = (dev_t) -1; me->ino = (ino_t) -1; + status = pthread_mutex_init(&me->multi_mutex, NULL); + if (status) + fatal(status); + /* * We need to add to the end if values exist in order to * preserve the order in which the map was read on lookup. @@ -526,6 +535,56 @@ done: return ret; } +static struct mapent *get_parent(const char *key, struct list_head *head, struct list_head **pos) +{ + struct list_head *next; + struct mapent *this, *last; + int eq; + + last = NULL; + next = *pos ? (*pos)->next : head->next; + + list_for_each(next, head) { + this = list_entry(next, struct mapent, multi_list); + + if (!strcmp(this->key, key)) + break; + + eq = strncmp(this->key, key, strlen(this->key)); + if (eq == 0) { + *pos = next; + last = this; + continue; + } + } + + return last; +} + +int cache_set_parents(struct mapent *mm) +{ + struct list_head *multi_head, *p, *pos; + struct mapent *this; + + if (!mm->multi) + return 0; + + pos = NULL; + multi_head = &mm->multi->multi_list; + + list_for_each(p, multi_head) { + struct mapent *parent; + this = list_entry(p, struct mapent, multi_list); + parent = get_parent(this->key, multi_head, &pos); + if (parent) + this->parent = parent; + else + this->parent = mm->multi; + } + + return 1; +} + /* cache must be write locked by caller */ int cache_update(struct mapent_cache *mc, struct map_source *source, const char *key, const char *mapent, time_t age) @@ -568,7 +627,7 @@ int cache_delete(struct mapent_cache *mc { struct mapent *me = NULL, *pred; unsigned int hashval = hash(key); - int ret = CHE_OK; + int status, ret = CHE_OK; me = mc->hash[hashval]; if (!me) { @@ -585,6 +644,9 @@ int cache_delete(struct mapent_cache *mc goto done; } pred->next = me->next; + status = pthread_mutex_destroy(&me->multi_mutex); + if (status) + fatal(status); if (!list_empty(&me->ino_index)) list_del(&me->ino_index); free(me->key); @@ -605,6 +667,9 @@ int cache_delete(struct mapent_cache *mc goto done; } mc->hash[hashval] = me->next; + status = pthread_mutex_destroy(&me->multi_mutex); + if (status) + fatal(status); if (!list_empty(&me->ino_index)) list_del(&me->ino_index); free(me->key); @@ -713,10 +778,6 @@ void cache_release(struct map_source *ma if (status) fatal(status); - status = pthread_mutex_destroy(&mc->multi_mutex); - if (status) - fatal(status); - free(mc->hash); free(mc->ino_index); free(mc); diff --git a/lib/parse_subs.c b/lib/parse_subs.c index 6fb9895..a24e6da 100644 --- a/lib/parse_subs.c +++ b/lib/parse_subs.c @@ -472,9 +472,7 @@ cont: } /* We're done - clean out the offsets */ - cache_multi_lock(mc); status = cache_delete_offset_list(mc, me->key); - cache_multi_unlock(mc); if (status != CHE_OK) warn(ap->logopt, "couldn't delete offset list"); } diff --git a/modules/parse_sun.c b/modules/parse_sun.c index 25d2eda..7dc8339 100644 --- a/modules/parse_sun.c +++ b/modules/parse_sun.c @@ -645,12 +645,7 @@ add_offset_entry(struct autofs_point *ap } else strcpy(m_mapent, loc); - cache_readlock(mc); - cache_multi_lock(mc); ret = cache_add_offset(mc, name, m_key, m_mapent, age); - cache_multi_unlock(mc); - cache_unlock(mc); - if (ret == CHE_OK) debug(ap->logopt, MODPREFIX "added multi-mount offset %s -> %s", path, m_mapent); @@ -956,22 +951,25 @@ int parse_mount(struct autofs_point *ap, return 1; } } + cache_unlock(mc); + cache_readlock(mc); me = cache_lookup_distinct(mc, name); if (me) { /* So we know we're the multi-mount root */ if (!me->multi) me->multi = me; } - cache_unlock(mc); if (!me) { error(ap->logopt, MODPREFIX "can't find multi root %s", name); free(options); + cache_unlock(mc); return 1; } + cache_multi_lock(me); /* It's a multi-mount; deal with it */ do { char *path, *myoptions, *loc; @@ -991,10 +989,8 @@ int parse_mount(struct autofs_point *ap, if (!path) { warn(ap->logopt, MODPREFIX "null path or out of memory"); - cache_readlock(mc); - cache_multi_lock(mc); cache_delete_offset_list(mc, name); - cache_multi_unlock(mc); + cache_multi_unlock(me); cache_unlock(mc); free(options); return 1; @@ -1005,10 +1001,8 @@ int parse_mount(struct autofs_point *ap, l = parse_mapent(p, options, &myoptions, &loc, ap->logopt); if (!l) { - cache_readlock(mc); - cache_multi_lock(mc); cache_delete_offset_list(mc, name); - cache_multi_unlock(mc); + cache_multi_unlock(me); cache_unlock(mc); free(path); free(options); @@ -1027,10 +1021,8 @@ int parse_mount(struct autofs_point *ap, if (status != CHE_OK) { warn(ap->logopt, MODPREFIX "error adding multi-mount"); - cache_readlock(mc); - cache_multi_lock(mc); cache_delete_offset_list(mc, name); - cache_multi_unlock(mc); + cache_multi_unlock(me); cache_unlock(mc); free(path); free(options); @@ -1044,19 +1036,11 @@ int parse_mount(struct autofs_point *ap, free(myoptions); } while (*p == '/'); - cache_readlock(mc); - me = cache_lookup_distinct(mc, name); - if (!me) { - error(ap->logopt, - MODPREFIX - "failed to find cache entry for %s", name); - cache_multi_lock(mc); - cache_delete_offset_list(mc, name); - cache_multi_unlock(mc); - cache_unlock(mc); - free(options); - return 1; - } + /* + * We've got the ordered list of multi-mount entries so go + * through and set the parent entry of each + */ + cache_set_parents(me); /* Mount root offset if it exists */ ro = cache_lookup_offset("/", "/", strlen(m_root), &me->multi_list); @@ -1068,9 +1052,8 @@ int parse_mount(struct autofs_point *ap, if (!rv) { warn(ap->logopt, MODPREFIX "failed to mount root offset"); - cache_multi_lock(mc); cache_delete_offset_list(mc, name); - cache_multi_unlock(mc); + cache_multi_unlock(me); cache_unlock(mc); free(options); return 1; @@ -1086,9 +1069,8 @@ int parse_mount(struct autofs_point *ap, warn(ap->logopt, MODPREFIX "mount multi-mount root %s failed", name); - cache_multi_lock(mc); cache_delete_offset_list(mc, name); - cache_multi_unlock(mc); + cache_multi_unlock(me); cache_unlock(mc); free(options); return rv; @@ -1098,9 +1080,12 @@ int parse_mount(struct autofs_point *ap, if (!mount_multi_triggers(ap, m_root, me, "/")) { warn(ap->logopt, MODPREFIX "failed to mount offset triggers"); + cache_multi_unlock(me); + cache_unlock(mc); free(options); return 1; } + cache_multi_unlock(me); cache_unlock(mc); free(options); @@ -1217,11 +1202,13 @@ int parse_mount(struct autofs_point *ap, me = lookup_source_mapent(oap, name, LKP_DISTINCT); if (me) { + struct mapent *mm; char *m_key; int start; char *base, *m_root; mc = me->source->mc; + mm = me->multi; if (!me->multi) { cache_unlock(mc); @@ -1253,13 +1240,13 @@ int parse_mount(struct autofs_point *ap, base = &me->key[start]; - cache_multi_lock(mc); + cache_multi_lock(mm); if (!mount_multi_triggers(oap, m_root, me->multi, base)) { error(ap->logopt, MODPREFIX "failed to mount offset triggers"); rv = 1; } - cache_multi_unlock(mc); + cache_multi_unlock(mm); cache_unlock(mc); } }