ml_iterator Add a mountlist iterator to prepare to remove the mountlist processing from vfs_busy()/vfs_unbusy(). diff -r dab39dcbcb29 -r ff3f89481f2e sys/kern/vfs_mount.c --- a/sys/kern/vfs_mount.c Thu Mar 30 11:17:56 2017 +0200 +++ b/sys/kern/vfs_mount.c Thu Mar 30 11:17:56 2017 +0200 @@ -94,6 +94,23 @@ #include #include +enum mountlist_type { + ME_MOUNT, + ME_MARKER, + ME_MARKER_REVERSE +}; +struct mountlist_entry { + TAILQ_ENTRY(mountlist_entry) me_list; /* Mount list. */ + struct mount *me_mount; /* Actual mount if ME_MOUNT. */ + enum mountlist_type me_type; /* Mount or marker. */ +}; +struct mount_iterator { + struct mountlist_entry mi_entry; +}; + +static TAILQ_HEAD(mountlist, mountlist_entry) mount_list; +static kmutex_t mount_list_lock; + static struct vnode *vfs_vnode_iterator_next1(struct vnode_iterator *, bool (*)(void *, struct vnode *), void *, bool); @@ -119,7 +136,9 @@ vfs_mount_sysinit(void) { + TAILQ_INIT(&mount_list); TAILQ_INIT(&mountlist); + mutex_init(&mount_list_lock, MUTEX_DEFAULT, IPL_NONE); mutex_init(&mountlist_lock, MUTEX_DEFAULT, IPL_NONE); mutex_init(&mntvnode_lock, MUTEX_DEFAULT, IPL_NONE); mutex_init(&vfs_list_lock, MUTEX_DEFAULT, IPL_NONE); @@ -769,9 +788,7 @@ cache_purge(vp); mp->mnt_iflag &= ~IMNT_WANTRDWR; - mutex_enter(&mountlist_lock); - TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); - mutex_exit(&mountlist_lock); + mountlist_append(mp); if ((mp->mnt_flag & (MNT_RDONLY | MNT_ASYNC)) == 0) vfs_syncer_add_to_worklist(mp); vp->v_mountedhere = mp; @@ -931,9 +948,7 @@ coveredvp->v_mountedhere = NULL; VOP_UNLOCK(coveredvp); } - mutex_enter(&mountlist_lock); - TAILQ_REMOVE(&mountlist, mp, mnt_list); - mutex_exit(&mountlist_lock); + mountlist_remove(mp); if (TAILQ_FIRST(&mp->mnt_vnodelist) != NULL) panic("unmount: dangling vnode"); if (used_syncer) @@ -1439,10 +1454,168 @@ return rv; } +static struct mountlist_entry * +mountlist_alloc(enum mountlist_type type, struct mount *mp) +{ + struct mountlist_entry *me; + + me = kmem_zalloc(sizeof(*me), KM_SLEEP); + me->me_mount = mp; + me->me_type = type; + + return me; +} + +static void +mountlist_free(struct mountlist_entry *me) +{ + + kmem_free(me, sizeof(*me)); +} + +void +mountlist_iterator_init(mount_iterator_t **mip) +{ + struct mountlist_entry *me; + + me = mountlist_alloc(ME_MARKER, NULL); + mutex_enter(&mount_list_lock); + TAILQ_INSERT_HEAD(&mount_list, me, me_list); + mutex_exit(&mount_list_lock); + *mip = (mount_iterator_t *)me; +} + +void +mountlist_iterator_init_reverse(mount_iterator_t **mip) +{ + struct mountlist_entry *me; + + me = mountlist_alloc(ME_MARKER_REVERSE, NULL); + mutex_enter(&mount_list_lock); + TAILQ_INSERT_TAIL(&mount_list, me, me_list); + mutex_exit(&mount_list_lock); + *mip = (mount_iterator_t *)me; +} + +void +mountlist_iterator_destroy(mount_iterator_t *mi) +{ + struct mountlist_entry *me = &mi->mi_entry; + + mutex_enter(&mount_list_lock); + TAILQ_REMOVE(&mount_list, me, me_list); + mutex_exit(&mount_list_lock); + + mountlist_free(me); + +} + +/* + * Return the next mount or NULL for this iterator. + * Mark it busy on success. + */ +struct mount * +mountlist_iterator_next(mount_iterator_t *mi, int flags) +{ + struct mountlist_entry *me, *marker = &mi->mi_entry; + struct mount *mp; + + KASSERT(marker->me_type == ME_MARKER || + marker->me_type == ME_MARKER_REVERSE); + KASSERT((flags & ~MNT_NOWAIT) == 0); + + mutex_enter(&mount_list_lock); + for (;;) { + if (marker->me_type == ME_MARKER) + me = TAILQ_NEXT(marker, me_list); + else + me = TAILQ_PREV(marker, mountlist, me_list); + if (me == NULL) { + /* End of list: keep marker and rewturn. */ + mutex_exit(&mount_list_lock); + return NULL; + } + TAILQ_REMOVE(&mount_list, marker, me_list); + if (marker->me_type == ME_MARKER) + TAILQ_INSERT_AFTER(&mount_list, me, marker, me_list); + else + TAILQ_INSERT_BEFORE(me, marker, me_list); + + /* Skip other markers. */ + if (me->me_type != ME_MOUNT) + continue; + + /* Take an initial reference for vfs_busy() below. */ + mp = me->me_mount; + KASSERT(mp != NULL); + atomic_inc_uint(&mp->mnt_refcnt); + mutex_exit(&mount_list_lock); + + /* Try to mark this mount busy and return on success. */ + if (vfs_busy(mp, NULL) == 0) { + vfs_destroy(mp); + return mp; + } + vfs_destroy(mp); + mutex_enter(&mount_list_lock); + } +} + void mountlist_append(struct mount *mp) { + struct mountlist_entry *me; + + me = mountlist_alloc(ME_MOUNT, mp); + mutex_enter(&mount_list_lock); + TAILQ_INSERT_TAIL(&mount_list, me, me_list); + mutex_exit(&mount_list_lock); + mutex_enter(&mountlist_lock); TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); mutex_exit(&mountlist_lock); } + +void +mountlist_remove(struct mount *mp) +{ + struct mountlist_entry *me; + + mutex_enter(&mount_list_lock); + TAILQ_FOREACH(me, &mount_list, me_list) + if (me->me_mount == mp) + break; + KASSERT(me != NULL); + TAILQ_REMOVE(&mount_list, me, me_list); + mutex_exit(&mount_list_lock); + mountlist_free(me); + + mutex_enter(&mountlist_lock); + TAILQ_REMOVE(&mountlist, mp, mnt_list); + mutex_exit(&mountlist_lock); +} + +/* + * Unlocked variant to traverse the mountlist. + * To be used from DDB only. + */ +struct mount * +_mountlist_next(struct mount *mp) +{ + struct mountlist_entry *me; + + if (mp == NULL) { + me = TAILQ_FIRST(&mount_list); + } else { + TAILQ_FOREACH(me, &mount_list, me_list) + if (me->me_type == ME_MOUNT && me->me_mount == mp) + break; + if (me != NULL) + me = TAILQ_NEXT(me, me_list); + } + + while (me != NULL && me->me_type != ME_MOUNT) + me = TAILQ_NEXT(me, me_list); + + return (me ? me->me_mount : NULL); +} diff -r dab39dcbcb29 -r ff3f89481f2e sys/sys/mount.h --- a/sys/sys/mount.h Thu Mar 30 11:17:56 2017 +0200 +++ b/sys/sys/mount.h Thu Mar 30 11:17:56 2017 +0200 @@ -494,7 +494,15 @@ void mount_setspecific(struct mount *, specificdata_key_t, void *); int usermount_common_policy(struct mount *, u_long); + +typedef struct mount_iterator mount_iterator_t; /* Opaque. */ +void mountlist_iterator_init(mount_iterator_t **); +void mountlist_iterator_init_reverse(mount_iterator_t **); +void mountlist_iterator_destroy(mount_iterator_t *); +struct mount *mountlist_iterator_next(mount_iterator_t *, int); +struct mount *_mountlist_next(struct mount *); void mountlist_append(struct mount *); +void mountlist_remove(struct mount *); LIST_HEAD(vfs_list_head, vfsops); extern struct vfs_list_head vfs_list;