diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/fs/tmpfs/tmpfs.h | 2 | ||||
-rw-r--r-- | sys/fs/tmpfs/tmpfs_subr.c | 25 | ||||
-rw-r--r-- | sys/fs/tmpfs/tmpfs_vfsops.c | 74 |
3 files changed, 74 insertions, 27 deletions
diff --git a/sys/fs/tmpfs/tmpfs.h b/sys/fs/tmpfs/tmpfs.h index 644f5a9..445bf61 100644 --- a/sys/fs/tmpfs/tmpfs.h +++ b/sys/fs/tmpfs/tmpfs.h @@ -384,7 +384,7 @@ struct tmpfs_fid { * Prototypes for tmpfs_subr.c. */ -int tmpfs_alloc_node(struct tmpfs_mount *, enum vtype, +int tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount *, enum vtype, uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *, char *, dev_t, struct tmpfs_node **); void tmpfs_free_node(struct tmpfs_mount *, struct tmpfs_node *); diff --git a/sys/fs/tmpfs/tmpfs_subr.c b/sys/fs/tmpfs/tmpfs_subr.c index 47dd613..d0d9a15 100644 --- a/sys/fs/tmpfs/tmpfs_subr.c +++ b/sys/fs/tmpfs/tmpfs_subr.c @@ -159,7 +159,7 @@ tmpfs_pages_check_avail(struct tmpfs_mount *tmp, size_t req_pages) * Returns zero on success or an appropriate error code on failure. */ int -tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type, +tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount *tmp, enum vtype type, uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *parent, char *target, dev_t rdev, struct tmpfs_node **node) { @@ -169,6 +169,8 @@ tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type, /* If the root directory of the 'tmp' file system is not yet * allocated, this must be the request to do it. */ MPASS(IMPLIES(tmp->tm_root == NULL, parent == NULL && type == VDIR)); + KASSERT(tmp->tm_root == NULL || mp->mnt_writeopcount > 0, + ("creating node not under vn_start_write")); MPASS(IFF(type == VLNK, target != NULL)); MPASS(IFF(type == VBLK || type == VCHR, rdev != VNOVAL)); @@ -178,6 +180,24 @@ tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type, if (tmpfs_pages_check_avail(tmp, 1) == 0) return (ENOSPC); + if ((mp->mnt_kern_flag & MNTK_UNMOUNT) != 0) { + /* + * When a new tmpfs node is created for fully + * constructed mount point, there must be a parent + * node, which vnode is locked exclusively. As + * consequence, if the unmount is executing in + * parallel, vflush() cannot reclaim the parent vnode. + * Due to this, the check for MNTK_UNMOUNT flag is not + * racy: if we did not see MNTK_UNMOUNT flag, then tmp + * cannot be destroyed until node construction is + * finished and the parent vnode unlocked. + * + * Tmpfs does not need to instantiate new nodes during + * unmount. + */ + return (EBUSY); + } + nnode = (struct tmpfs_node *)uma_zalloc_arg( tmp->tm_node_pool, tmp, M_WAITOK); @@ -687,7 +707,8 @@ tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap, parent = NULL; /* Allocate a node that represents the new file. */ - error = tmpfs_alloc_node(tmp, vap->va_type, cnp->cn_cred->cr_uid, + error = tmpfs_alloc_node(dvp->v_mount, tmp, vap->va_type, + cnp->cn_cred->cr_uid, dnode->tn_gid, vap->va_mode, parent, target, vap->va_rdev, &node); if (error != 0) return (error); diff --git a/sys/fs/tmpfs/tmpfs_vfsops.c b/sys/fs/tmpfs/tmpfs_vfsops.c index 1773f71..cb4a32b 100644 --- a/sys/fs/tmpfs/tmpfs_vfsops.c +++ b/sys/fs/tmpfs/tmpfs_vfsops.c @@ -238,7 +238,7 @@ tmpfs_mount(struct mount *mp) tmp->tm_ronly = (mp->mnt_flag & MNT_RDONLY) != 0; /* Allocate the root node. */ - error = tmpfs_alloc_node(tmp, VDIR, root_uid, + error = tmpfs_alloc_node(mp, tmp, VDIR, root_uid, root_gid, root_mode & ALLPERMS, NULL, NULL, VNOVAL, &root); @@ -269,38 +269,49 @@ tmpfs_mount(struct mount *mp) static int tmpfs_unmount(struct mount *mp, int mntflags) { - int error; - int flags = 0; struct tmpfs_mount *tmp; struct tmpfs_node *node; + int error, flags; - /* Handle forced unmounts. */ - if (mntflags & MNT_FORCE) - flags |= FORCECLOSE; - - /* Finalize all pending I/O. */ - error = vflush(mp, 0, flags, curthread); - if (error != 0) - return error; - + flags = (mntflags & MNT_FORCE) != 0 ? FORCECLOSE : 0; tmp = VFS_TO_TMPFS(mp); - /* Free all associated data. The loop iterates over the linked list - * we have containing all used nodes. For each of them that is - * a directory, we free all its directory entries. Note that after - * freeing a node, it will automatically go to the available list, - * so we will later have to iterate over it to release its items. */ - node = LIST_FIRST(&tmp->tm_nodes_used); - while (node != NULL) { - struct tmpfs_node *next; + /* Stop writers */ + error = vfs_write_suspend_umnt(mp); + if (error != 0) + return (error); + /* + * At this point, nodes cannot be destroyed by any other + * thread because write suspension is started. + */ + + for (;;) { + error = vflush(mp, 0, flags, curthread); + if (error != 0) { + vfs_write_resume(mp, VR_START_WRITE); + return (error); + } + MNT_ILOCK(mp); + if (mp->mnt_nvnodelistsize == 0) { + MNT_IUNLOCK(mp); + break; + } + MNT_IUNLOCK(mp); + if ((mntflags & MNT_FORCE) == 0) { + vfs_write_resume(mp, VR_START_WRITE); + return (EBUSY); + } + } + TMPFS_LOCK(tmp); + while ((node = LIST_FIRST(&tmp->tm_nodes_used)) != NULL) { + TMPFS_UNLOCK(tmp); if (node->tn_type == VDIR) tmpfs_dir_destroy(tmp, node); - - next = LIST_NEXT(node, tn_entries); tmpfs_free_node(tmp, node); - node = next; + TMPFS_LOCK(tmp); } + TMPFS_UNLOCK(tmp); uma_zdestroy(tmp->tm_dirent_pool); uma_zdestroy(tmp->tm_node_pool); @@ -313,11 +324,13 @@ tmpfs_unmount(struct mount *mp, int mntflags) /* Throw away the tmpfs_mount structure. */ free(mp->mnt_data, M_TMPFSMNT); mp->mnt_data = NULL; + vfs_write_resume(mp, VR_START_WRITE); MNT_ILOCK(mp); mp->mnt_flag &= ~MNT_LOCAL; MNT_IUNLOCK(mp); - return 0; + + return (0); } static int @@ -401,6 +414,18 @@ tmpfs_statfs(struct mount *mp, struct statfs *sbp) return 0; } +static int +tmpfs_sync(struct mount *mp, int waitfor) +{ + + if (waitfor == MNT_SUSPEND) { + MNT_ILOCK(mp); + mp->mnt_kern_flag |= MNTK_SUSPEND2 | MNTK_SUSPENDED; + MNT_IUNLOCK(mp); + } + return (0); +} + /* * tmpfs vfs operations. */ @@ -411,5 +436,6 @@ struct vfsops tmpfs_vfsops = { .vfs_root = tmpfs_root, .vfs_statfs = tmpfs_statfs, .vfs_fhtovp = tmpfs_fhtovp, + .vfs_sync = tmpfs_sync, }; VFS_SET(tmpfs_vfsops, tmpfs, VFCF_JAIL); |