diff --git a/sys/fs/tmpfs/tmpfs.h b/sys/fs/tmpfs/tmpfs.h index ac8fe0c1c2d..5b966635c48 100644 --- a/sys/fs/tmpfs/tmpfs.h +++ b/sys/fs/tmpfs/tmpfs.h @@ -287,6 +287,7 @@ struct tmpfs_node { * a position within the file is accessed. */ vm_object_t tn_aobj; /* (c) */ + struct tmpfs_mount *tn_tmp; /* (c) */ } tn_reg; } tn_spec; /* (v) */ }; @@ -415,6 +416,7 @@ void tmpfs_ref_node(struct tmpfs_node *node); int tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount *, enum vtype, uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *, const char *, dev_t, struct tmpfs_node **); +int tmpfs_fo_close(struct file *fp, struct thread *td); void tmpfs_free_node(struct tmpfs_mount *, struct tmpfs_node *); bool tmpfs_free_node_locked(struct tmpfs_mount *, struct tmpfs_node *, bool); void tmpfs_free_tmp(struct tmpfs_mount *); @@ -558,6 +560,8 @@ tmpfs_update_getattr(struct vnode *vp) tmpfs_update(vp); } +extern struct fileops tmpfs_fnops; + #endif /* _KERNEL */ #endif /* _FS_TMPFS_TMPFS_H_ */ diff --git a/sys/fs/tmpfs/tmpfs_subr.c b/sys/fs/tmpfs/tmpfs_subr.c index d9844bc3c7c..726f6819b12 100644 --- a/sys/fs/tmpfs/tmpfs_subr.c +++ b/sys/fs/tmpfs/tmpfs_subr.c @@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -340,6 +341,7 @@ tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount *tmp, enum vtype type, /* OBJ_TMPFS is set together with the setting of vp->v_object */ vm_object_set_flag(obj, OBJ_TMPFS_NODE); VM_OBJECT_WUNLOCK(obj); + nnode->tn_reg.tn_tmp = tmp; break; default: @@ -697,6 +699,7 @@ loop: vp->v_object = object; object->un_pager.swp.swp_tmpfs = vp; vm_object_set_flag(object, OBJ_TMPFS); + vp->v_irflag |= VIRF_PGREAD; VI_UNLOCK(vp); VM_OBJECT_WUNLOCK(object); break; diff --git a/sys/fs/tmpfs/tmpfs_vfsops.c b/sys/fs/tmpfs/tmpfs_vfsops.c index c98b6126eec..c090cd5aa65 100644 --- a/sys/fs/tmpfs/tmpfs_vfsops.c +++ b/sys/fs/tmpfs/tmpfs_vfsops.c @@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -662,6 +663,8 @@ static int tmpfs_init(struct vfsconf *conf) { tmpfs_subr_init(); + memcpy(&tmpfs_fnops, &vnops, sizeof(struct fileops)); + tmpfs_fnops.fo_close = tmpfs_fo_close; return (0); } diff --git a/sys/fs/tmpfs/tmpfs_vnops.c b/sys/fs/tmpfs/tmpfs_vnops.c index c5d0702801e..150af5f9395 100644 --- a/sys/fs/tmpfs/tmpfs_vnops.c +++ b/sys/fs/tmpfs/tmpfs_vnops.c @@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -276,22 +277,25 @@ tmpfs_mknod(struct vop_mknod_args *v) return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); } +struct fileops tmpfs_fnops; + static int tmpfs_open(struct vop_open_args *v) { - struct vnode *vp = v->a_vp; - int mode = v->a_mode; - - int error; + struct vnode *vp; struct tmpfs_node *node; + struct file *fp; + int error, mode; - MPASS(VOP_ISLOCKED(vp)); - + vp = v->a_vp; + mode = v->a_mode; node = VP_TO_TMPFS_NODE(vp); - /* The file is still active but all its names have been removed + /* + * The file is still active but all its names have been removed * (e.g. by a "rmdir $(pwd)"). It cannot be opened any more as - * it is about to die. */ + * it is about to die. + */ if (node->tn_links < 1) return (ENOENT); @@ -306,8 +310,13 @@ tmpfs_open(struct vop_open_args *v) vnode_create_vobject(vp, node->tn_size, v->a_td); } - MPASS(VOP_ISLOCKED(vp)); - return error; + fp = v->a_fp; + if (error == 0 && fp != NULL && vp->v_type == VREG) { + tmpfs_ref_node(node); + finit_vnode(fp, mode, node, &tmpfs_fnops); + } + + return (error); } static int @@ -321,6 +330,19 @@ tmpfs_close(struct vop_close_args *v) return (0); } +int +tmpfs_fo_close(struct file *fp, struct thread *td) +{ + struct tmpfs_node *node; + + node = fp->f_data; + if (node != NULL) { + MPASS(node->tn_type == VREG); + tmpfs_free_node(node->tn_reg.tn_tmp, node); + } + return (vnops.fo_close(fp, td)); +} + /* * VOP_FPLOOKUP_VEXEC routines are subject to special circumstances, see * the comment above cache_fplookup for details. @@ -566,6 +588,47 @@ tmpfs_read(struct vop_read_args *v) return (uiomove_object(node->tn_reg.tn_aobj, node->tn_size, uio)); } +static int +tmpfs_read_pgcache(struct vop_read_pgcache_args *v) +{ + struct vnode *vp; + struct tmpfs_node *node; + vm_object_t object; + off_t size; + int error; + + vp = v->a_vp; + MPASS((vp->v_irflag & VIRF_PGREAD) != 0); + + if (v->a_uio->uio_offset < 0) + return (EINVAL); + + error = EJUSTRETURN; + vfs_smr_enter(); + + node = VP_TO_TMPFS_NODE_SMR(vp); + if (node == NULL) + goto out_smr; + MPASS(node->tn_type == VREG); + MPASS(node->tn_refcount >= 1); + object = node->tn_reg.tn_aobj; + if (object == NULL) + goto out_smr; + + MPASS((object->flags & (OBJ_ANON | OBJ_DEAD | OBJ_TMPFS_NODE)) == + OBJ_TMPFS_NODE); + if (!VN_IS_DOOMED(vp)) { + /* size cannot become shorter due to rangelock. */ + size = node->tn_size; + vfs_smr_exit(); + error = uiomove_object(object, size, v->a_uio); + return (error); + } +out_smr: + vfs_smr_exit(); + return (error); +} + static int tmpfs_write(struct vop_write_args *v) { @@ -1721,6 +1784,7 @@ struct vop_vector tmpfs_vnodeop_entries = { .vop_getattr = tmpfs_getattr, .vop_setattr = tmpfs_setattr, .vop_read = tmpfs_read, + .vop_read_pgcache = tmpfs_read_pgcache, .vop_write = tmpfs_write, .vop_fsync = tmpfs_fsync, .vop_remove = tmpfs_remove,