Date: Tue, 03 Sep 2019 14:07:00 -0000 From: Konstantin Belousov <kib@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org Subject: svn commit: r346285 - stable/12/sys/fs/tmpfs Message-ID: <201904161733.x3GHX9RA034239@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: kib Date: Tue Apr 16 17:33:08 2019 New Revision: 346285 URL: https://svnweb.freebsd.org/changeset/base/346285 Log: MFC r345425, r345514, r345799, r345800, r345803, r346157: Enable tmpfs rw->ro remounts. Modified: stable/12/sys/fs/tmpfs/tmpfs.h stable/12/sys/fs/tmpfs/tmpfs_fifoops.c stable/12/sys/fs/tmpfs/tmpfs_subr.c stable/12/sys/fs/tmpfs/tmpfs_vfsops.c stable/12/sys/fs/tmpfs/tmpfs_vnops.c Directory Properties: stable/12/ (props changed) Modified: stable/12/sys/fs/tmpfs/tmpfs.h ============================================================================== --- stable/12/sys/fs/tmpfs/tmpfs.h Tue Apr 16 17:16:18 2019 (r346284) +++ stable/12/sys/fs/tmpfs/tmpfs.h Tue Apr 16 17:33:08 2019 (r346285) @@ -325,6 +325,11 @@ LIST_HEAD(tmpfs_node_list, tmpfs_node); */ struct tmpfs_mount { /* + * Original value of the "size" parameter, for reference purposes, + * mostly. + */ + off_t tm_size_max; + /* * Maximum number of memory pages available for use by the file * system, set during mount time. This variable must never be * used directly as it may be bigger than the current amount of @@ -432,8 +437,8 @@ void tmpfs_dir_destroy(struct tmpfs_mount *, struct tm struct tmpfs_dirent * tmpfs_dir_lookup(struct tmpfs_node *node, struct tmpfs_node *f, struct componentname *cnp); -int tmpfs_dir_getdents(struct tmpfs_node *, struct uio *, int, - u_long *, int *); +int tmpfs_dir_getdents(struct tmpfs_mount *, struct tmpfs_node *, + struct uio *, int, u_long *, int *); int tmpfs_dir_whiteout_add(struct vnode *, struct componentname *); void tmpfs_dir_whiteout_remove(struct vnode *, struct componentname *); int tmpfs_reg_resize(struct vnode *, off_t, boolean_t); @@ -447,7 +452,8 @@ int tmpfs_chtimes(struct vnode *, struct vattr *, stru void tmpfs_itimes(struct vnode *, const struct timespec *, const struct timespec *); -void tmpfs_set_status(struct tmpfs_node *node, int status); +void tmpfs_set_status(struct tmpfs_mount *tm, struct tmpfs_node *node, + int status); void tmpfs_update(struct vnode *); int tmpfs_truncate(struct vnode *, off_t); struct tmpfs_dirent *tmpfs_dir_first(struct tmpfs_node *dnode, Modified: stable/12/sys/fs/tmpfs/tmpfs_fifoops.c ============================================================================== --- stable/12/sys/fs/tmpfs/tmpfs_fifoops.c Tue Apr 16 17:16:18 2019 (r346284) +++ stable/12/sys/fs/tmpfs/tmpfs_fifoops.c Tue Apr 16 17:33:08 2019 (r346285) @@ -56,7 +56,8 @@ tmpfs_fifo_close(struct vop_close_args *v) struct tmpfs_node *node; node = VP_TO_TMPFS_NODE(v->a_vp); - tmpfs_set_status(node, TMPFS_NODE_ACCESSED); + tmpfs_set_status(VFS_TO_TMPFS(v->a_vp->v_mount), node, + TMPFS_NODE_ACCESSED); tmpfs_update(v->a_vp); return (fifo_specops.vop_close(v)); } Modified: stable/12/sys/fs/tmpfs/tmpfs_subr.c ============================================================================== --- stable/12/sys/fs/tmpfs/tmpfs_subr.c Tue Apr 16 17:16:18 2019 (r346284) +++ stable/12/sys/fs/tmpfs/tmpfs_subr.c Tue Apr 16 17:33:08 2019 (r346285) @@ -218,6 +218,8 @@ tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount */ return (EBUSY); } + if ((mp->mnt_kern_flag & MNT_RDONLY) != 0) + return (EROFS); nnode = (struct tmpfs_node *)uma_zalloc_arg(tmp->tm_node_pool, tmp, M_WAITOK); @@ -1108,7 +1110,8 @@ tmpfs_dir_destroy(struct tmpfs_mount *tmp, struct tmpf * error happens. */ static int -tmpfs_dir_getdotdent(struct tmpfs_node *node, struct uio *uio) +tmpfs_dir_getdotdent(struct tmpfs_mount *tm, struct tmpfs_node *node, + struct uio *uio) { int error; struct dirent dent; @@ -1128,7 +1131,7 @@ tmpfs_dir_getdotdent(struct tmpfs_node *node, struct u else error = uiomove(&dent, dent.d_reclen, uio); - tmpfs_set_status(node, TMPFS_NODE_ACCESSED); + tmpfs_set_status(tm, node, TMPFS_NODE_ACCESSED); return (error); } @@ -1141,7 +1144,8 @@ tmpfs_dir_getdotdent(struct tmpfs_node *node, struct u * error happens. */ static int -tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struct uio *uio) +tmpfs_dir_getdotdotdent(struct tmpfs_mount *tm, struct tmpfs_node *node, + struct uio *uio) { int error; struct dirent dent; @@ -1172,7 +1176,7 @@ tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struc else error = uiomove(&dent, dent.d_reclen, uio); - tmpfs_set_status(node, TMPFS_NODE_ACCESSED); + tmpfs_set_status(tm, node, TMPFS_NODE_ACCESSED); return (error); } @@ -1185,8 +1189,8 @@ tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struc * error code if another error happens. */ int -tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio, int maxcookies, - u_long *cookies, int *ncookies) +tmpfs_dir_getdents(struct tmpfs_mount *tm, struct tmpfs_node *node, + struct uio *uio, int maxcookies, u_long *cookies, int *ncookies) { struct tmpfs_dir_cursor dc; struct tmpfs_dirent *de; @@ -1207,7 +1211,7 @@ tmpfs_dir_getdents(struct tmpfs_node *node, struct uio */ switch (uio->uio_offset) { case TMPFS_DIRCOOKIE_DOT: - error = tmpfs_dir_getdotdent(node, uio); + error = tmpfs_dir_getdotdent(tm, node, uio); if (error != 0) return (error); uio->uio_offset = TMPFS_DIRCOOKIE_DOTDOT; @@ -1215,7 +1219,7 @@ tmpfs_dir_getdents(struct tmpfs_node *node, struct uio cookies[(*ncookies)++] = off = uio->uio_offset; /* FALLTHROUGH */ case TMPFS_DIRCOOKIE_DOTDOT: - error = tmpfs_dir_getdotdotdent(node, uio); + error = tmpfs_dir_getdotdotdent(tm, node, uio); if (error != 0) return (error); de = tmpfs_dir_first(node, &dc); @@ -1317,7 +1321,7 @@ tmpfs_dir_getdents(struct tmpfs_node *node, struct uio node->tn_dir.tn_readdir_lastn = off; node->tn_dir.tn_readdir_lastp = de; - tmpfs_set_status(node, TMPFS_NODE_ACCESSED); + tmpfs_set_status(tm, node, TMPFS_NODE_ACCESSED); return error; } @@ -1772,10 +1776,10 @@ tmpfs_chtimes(struct vnode *vp, struct vattr *vap, } void -tmpfs_set_status(struct tmpfs_node *node, int status) +tmpfs_set_status(struct tmpfs_mount *tm, struct tmpfs_node *node, int status) { - if ((node->tn_status & status) == status) + if ((node->tn_status & status) == status || tm->tm_ronly) return; TMPFS_NODE_LOCK(node); node->tn_status |= status; Modified: stable/12/sys/fs/tmpfs/tmpfs_vfsops.c ============================================================================== --- stable/12/sys/fs/tmpfs/tmpfs_vfsops.c Tue Apr 16 17:16:18 2019 (r346284) +++ stable/12/sys/fs/tmpfs/tmpfs_vfsops.c Tue Apr 16 17:33:08 2019 (r346285) @@ -57,10 +57,15 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/rwlock.h> #include <sys/stat.h> +#include <sys/sx.h> #include <sys/sysctl.h> #include <sys/vnode.h> #include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/pmap.h> +#include <vm/vm_extern.h> +#include <vm/vm_map.h> #include <vm/vm_object.h> #include <vm/vm_param.h> @@ -88,7 +93,7 @@ static const char *tmpfs_opts[] = { }; static const char *tmpfs_updateopts[] = { - "from", "export", NULL + "from", "export", "size", NULL }; static int @@ -134,7 +139,228 @@ tmpfs_node_fini(void *mem, int size) mtx_destroy(&node->tn_interlock); } +/* + * Handle updates of time from writes to mmaped regions. Use + * MNT_VNODE_FOREACH_ALL instead of MNT_VNODE_FOREACH_ACTIVE, since + * unmap of the tmpfs-backed vnode does not call vinactive(), due to + * vm object type is OBJT_SWAP. + * If lazy, only handle delayed update of mtime due to the writes to + * mapped files. + */ +static void +tmpfs_update_mtime(struct mount *mp, bool lazy) +{ + struct vnode *vp, *mvp; + struct vm_object *obj; + + MNT_VNODE_FOREACH_ALL(vp, mp, mvp) { + if (vp->v_type != VREG) { + VI_UNLOCK(vp); + continue; + } + obj = vp->v_object; + KASSERT((obj->flags & (OBJ_TMPFS_NODE | OBJ_TMPFS)) == + (OBJ_TMPFS_NODE | OBJ_TMPFS), ("non-tmpfs obj")); + + /* + * In lazy case, do unlocked read, avoid taking vnode + * lock if not needed. Lost update will be handled on + * the next call. + * For non-lazy case, we must flush all pending + * metadata changes now. + */ + if (!lazy || (obj->flags & OBJ_TMPFS_DIRTY) != 0) { + if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, + curthread) != 0) + continue; + tmpfs_check_mtime(vp); + if (!lazy) + tmpfs_update(vp); + vput(vp); + } else { + VI_UNLOCK(vp); + continue; + } + } +} + +struct tmpfs_check_rw_maps_arg { + bool found; +}; + +static bool +tmpfs_check_rw_maps_cb(struct mount *mp __unused, vm_map_t map __unused, + vm_map_entry_t entry __unused, void *arg) +{ + struct tmpfs_check_rw_maps_arg *a; + + a = arg; + a->found = true; + return (true); +} + +/* + * Revoke write permissions from all mappings of regular files + * belonging to the specified tmpfs mount. + */ +static bool +tmpfs_revoke_rw_maps_cb(struct mount *mp __unused, vm_map_t map, + vm_map_entry_t entry, void *arg __unused) +{ + + /* + * XXXKIB: might be invalidate the mapping + * instead ? The process is not going to be + * happy in any case. + */ + entry->max_protection &= ~VM_PROT_WRITE; + if ((entry->protection & VM_PROT_WRITE) != 0) { + entry->protection &= ~VM_PROT_WRITE; + pmap_protect(map->pmap, entry->start, entry->end, + entry->protection); + } + return (false); +} + +static void +tmpfs_all_rw_maps(struct mount *mp, bool (*cb)(struct mount *mp, vm_map_t, + vm_map_entry_t, void *), void *cb_arg) +{ + struct proc *p; + struct vmspace *vm; + vm_map_t map; + vm_map_entry_t entry; + vm_object_t object; + struct vnode *vp; + int gen; + bool terminate; + + terminate = false; + sx_slock(&allproc_lock); +again: + gen = allproc_gen; + FOREACH_PROC_IN_SYSTEM(p) { + PROC_LOCK(p); + if (p->p_state != PRS_NORMAL || (p->p_flag & (P_INEXEC | + P_SYSTEM | P_WEXIT)) != 0) { + PROC_UNLOCK(p); + continue; + } + vm = vmspace_acquire_ref(p); + _PHOLD_LITE(p); + PROC_UNLOCK(p); + if (vm == NULL) { + PRELE(p); + continue; + } + sx_sunlock(&allproc_lock); + map = &vm->vm_map; + + vm_map_lock(map); + if (map->busy) + vm_map_wait_busy(map); + for (entry = map->header.next; entry != &map->header; + entry = entry->next) { + if ((entry->eflags & (MAP_ENTRY_GUARD | + MAP_ENTRY_IS_SUB_MAP | MAP_ENTRY_COW)) != 0 || + (entry->max_protection & VM_PROT_WRITE) == 0) + continue; + object = entry->object.vm_object; + if (object == NULL || object->type != OBJT_SWAP || + (object->flags & OBJ_TMPFS_NODE) == 0) + continue; + /* + * No need to dig into shadow chain, mapping + * of the object not at top is readonly. + */ + + VM_OBJECT_RLOCK(object); + if (object->type == OBJT_DEAD) { + VM_OBJECT_RUNLOCK(object); + continue; + } + MPASS(object->ref_count > 1); + if ((object->flags & (OBJ_TMPFS_NODE | OBJ_TMPFS)) != + (OBJ_TMPFS_NODE | OBJ_TMPFS)) { + VM_OBJECT_RUNLOCK(object); + continue; + } + vp = object->un_pager.swp.swp_tmpfs; + if (vp->v_mount != mp) { + VM_OBJECT_RUNLOCK(object); + continue; + } + + terminate = cb(mp, map, entry, cb_arg); + VM_OBJECT_RUNLOCK(object); + if (terminate) + break; + } + vm_map_unlock(map); + + vmspace_free(vm); + sx_slock(&allproc_lock); + PRELE(p); + if (terminate) + break; + } + if (!terminate && gen != allproc_gen) + goto again; + sx_sunlock(&allproc_lock); +} + +static bool +tmpfs_check_rw_maps(struct mount *mp) +{ + struct tmpfs_check_rw_maps_arg ca; + + ca.found = false; + tmpfs_all_rw_maps(mp, tmpfs_check_rw_maps_cb, &ca); + return (ca.found); +} + static int +tmpfs_rw_to_ro(struct mount *mp) +{ + int error, flags; + bool forced; + + forced = (mp->mnt_flag & MNT_FORCE) != 0; + flags = WRITECLOSE | (forced ? FORCECLOSE : 0); + + if ((error = vn_start_write(NULL, &mp, V_WAIT)) != 0) + return (error); + error = vfs_write_suspend_umnt(mp); + if (error != 0) + return (error); + if (!forced && tmpfs_check_rw_maps(mp)) { + error = EBUSY; + goto out; + } + VFS_TO_TMPFS(mp)->tm_ronly = 1; + MNT_ILOCK(mp); + mp->mnt_flag |= MNT_RDONLY; + MNT_IUNLOCK(mp); + for (;;) { + tmpfs_all_rw_maps(mp, tmpfs_revoke_rw_maps_cb, NULL); + tmpfs_update_mtime(mp, false); + error = vflush(mp, 0, flags, curthread); + if (error != 0) { + VFS_TO_TMPFS(mp)->tm_ronly = 0; + MNT_ILOCK(mp); + mp->mnt_flag &= ~MNT_RDONLY; + MNT_IUNLOCK(mp); + goto out; + } + if (!tmpfs_check_rw_maps(mp)) + break; + } +out: + vfs_write_resume(mp, 0); + return (error); +} + +static int tmpfs_mount(struct mount *mp) { const size_t nodes_per_page = howmany(PAGE_SIZE, @@ -161,9 +387,29 @@ tmpfs_mount(struct mount *mp) /* Only support update mounts for certain options. */ if (vfs_filteropt(mp->mnt_optnew, tmpfs_updateopts) != 0) return (EOPNOTSUPP); - if (vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0) != - ((struct tmpfs_mount *)mp->mnt_data)->tm_ronly) - return (EOPNOTSUPP); + if (vfs_getopt_size(mp->mnt_optnew, "size", &size_max) == 0) { + /* + * On-the-fly resizing is not supported (yet). We still + * need to have "size" listed as "supported", otherwise + * trying to update fs that is listed in fstab with size + * parameter, say trying to change rw to ro or vice + * versa, would cause vfs_filteropt() to bail. + */ + if (size_max != VFS_TO_TMPFS(mp)->tm_size_max) + return (EOPNOTSUPP); + } + if (vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0) && + !(VFS_TO_TMPFS(mp)->tm_ronly)) { + /* RW -> RO */ + return (tmpfs_rw_to_ro(mp)); + } else if (!vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0) && + VFS_TO_TMPFS(mp)->tm_ronly) { + /* RO -> RW */ + VFS_TO_TMPFS(mp)->tm_ronly = 0; + MNT_ILOCK(mp); + mp->mnt_flag &= ~MNT_RDONLY; + MNT_IUNLOCK(mp); + } return (0); } @@ -229,6 +475,7 @@ tmpfs_mount(struct mount *mp) tmp->tm_maxfilesize = maxfilesize > 0 ? maxfilesize : OFF_MAX; LIST_INIT(&tmp->tm_nodes_used); + tmp->tm_size_max = size_max; tmp->tm_pages_max = pages; tmp->tm_pages_used = 0; new_unrhdr64(&tmp->tm_ino_unr, 2); @@ -433,45 +680,13 @@ tmpfs_statfs(struct mount *mp, struct statfs *sbp) static int tmpfs_sync(struct mount *mp, int waitfor) { - struct vnode *vp, *mvp; - struct vm_object *obj; if (waitfor == MNT_SUSPEND) { MNT_ILOCK(mp); mp->mnt_kern_flag |= MNTK_SUSPEND2 | MNTK_SUSPENDED; MNT_IUNLOCK(mp); } else if (waitfor == MNT_LAZY) { - /* - * Handle lazy updates of mtime from writes to mmaped - * regions. Use MNT_VNODE_FOREACH_ALL instead of - * MNT_VNODE_FOREACH_ACTIVE, since unmap of the - * tmpfs-backed vnode does not call vinactive(), due - * to vm object type is OBJT_SWAP. - */ - MNT_VNODE_FOREACH_ALL(vp, mp, mvp) { - if (vp->v_type != VREG) { - VI_UNLOCK(vp); - continue; - } - obj = vp->v_object; - KASSERT((obj->flags & (OBJ_TMPFS_NODE | OBJ_TMPFS)) == - (OBJ_TMPFS_NODE | OBJ_TMPFS), ("non-tmpfs obj")); - - /* - * Unlocked read, avoid taking vnode lock if - * not needed. Lost update will be handled on - * the next call. - */ - if ((obj->flags & OBJ_TMPFS_DIRTY) == 0) { - VI_UNLOCK(vp); - continue; - } - if (vget(vp, LK_EXCLUSIVE | LK_RETRY | LK_INTERLOCK, - curthread) != 0) - continue; - tmpfs_check_mtime(vp); - vput(vp); - } + tmpfs_update_mtime(mp, true); } return (0); } Modified: stable/12/sys/fs/tmpfs/tmpfs_vnops.c ============================================================================== --- stable/12/sys/fs/tmpfs/tmpfs_vnops.c Tue Apr 16 17:16:18 2019 (r346284) +++ stable/12/sys/fs/tmpfs/tmpfs_vnops.c Tue Apr 16 17:33:08 2019 (r346285) @@ -481,7 +481,7 @@ tmpfs_read(struct vop_read_args *v) if (uio->uio_offset < 0) return (EINVAL); node = VP_TO_TMPFS_NODE(vp); - tmpfs_set_status(node, TMPFS_NODE_ACCESSED); + tmpfs_set_status(VFS_TO_TMPFS(vp->v_mount), node, TMPFS_NODE_ACCESSED); return (uiomove_object(node->tn_reg.tn_aobj, node->tn_size, uio)); } @@ -1188,25 +1188,30 @@ tmpfs_symlink(struct vop_symlink_args *v) } static int -tmpfs_readdir(struct vop_readdir_args *v) +tmpfs_readdir(struct vop_readdir_args *va) { - struct vnode *vp = v->a_vp; - struct uio *uio = v->a_uio; - int *eofflag = v->a_eofflag; - u_long **cookies = v->a_cookies; - int *ncookies = v->a_ncookies; - - int error; - ssize_t startresid; - int maxcookies; + struct vnode *vp; + struct uio *uio; + struct tmpfs_mount *tm; struct tmpfs_node *node; + u_long **cookies; + int *eofflag, *ncookies; + ssize_t startresid; + int error, maxcookies; + vp = va->a_vp; + uio = va->a_uio; + eofflag = va->a_eofflag; + cookies = va->a_cookies; + ncookies = va->a_ncookies; + /* This operation only makes sense on directory nodes. */ if (vp->v_type != VDIR) return ENOTDIR; maxcookies = 0; node = VP_TO_TMPFS_DIR(vp); + tm = VFS_TO_TMPFS(vp->v_mount); startresid = uio->uio_resid; @@ -1220,9 +1225,9 @@ tmpfs_readdir(struct vop_readdir_args *v) } if (cookies == NULL) - error = tmpfs_dir_getdents(node, uio, 0, NULL, NULL); + error = tmpfs_dir_getdents(tm, node, uio, 0, NULL, NULL); else - error = tmpfs_dir_getdents(node, uio, maxcookies, *cookies, + error = tmpfs_dir_getdents(tm, node, uio, maxcookies, *cookies, ncookies); /* Buffer was filled without hitting EOF. */ @@ -1258,7 +1263,7 @@ tmpfs_readlink(struct vop_readlink_args *v) error = uiomove(node->tn_link, MIN(node->tn_size, uio->uio_resid), uio); - tmpfs_set_status(node, TMPFS_NODE_ACCESSED); + tmpfs_set_status(VFS_TO_TMPFS(vp->v_mount), node, TMPFS_NODE_ACCESSED); return (error); }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201904161733.x3GHX9RA034239>