Add tmpfs page cache read support.

Or it could be explained as lockless (for vnode lock) reads.  Reads
are performed from the node tn_obj object.  Tmpfs regular vnode object
lifecycle is significantly different from the normal OBJT_VNODE: it is
alive as far as ref_count > 0.

Ensure liveness of the tmpfs VREG node and consequently v_object
inside VOP_READ_PGCACHE by referencing tmpfs node in tmpfs_open().
Provide custom tmpfs fo_close() method on file, to ensure that close
is paired with open.

Add tmpfs VOP_READ_PGCACHE that takes advantage of all tmpfs quirks.
It is quite cheap in code size sense to support page-ins for read for
tmpfs even if we do not own tmpfs vnode lock.  Also, we can handle
holes in tmpfs node without additional efforts, and do not have
limitation of the transfer size.

Reviewed by:	markj
Discussed with and benchmarked by:	mjg (previous version)
Tested by:	pho
Sponsored by:	The FreeBSD Foundation
Differential revision:	https://reviews.freebsd.org/D26346
This commit is contained in:
Konstantin Belousov 2020-09-15 22:19:16 +00:00
parent 4601f5f5ee
commit 081e36e760
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=365787
4 changed files with 84 additions and 10 deletions

View File

@ -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_ */

View File

@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
#include <sys/random.h>
#include <sys/refcount.h>
#include <sys/rwlock.h>
#include <sys/smr.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/vnode.h>
@ -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 @@ tmpfs_alloc_vp(struct mount *mp, struct tmpfs_node *node, int lkflag,
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;

View File

@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/dirent.h>
#include <sys/file.h>
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/mount.h>
@ -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);
}

View File

@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/dirent.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/limits.h>
#include <sys/lockf.h>
#include <sys/lock.h>
@ -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,