aboutsummaryrefslogtreecommitdiff
path: root/sys/fs
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2020-09-15 22:19:16 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2020-09-15 22:19:16 +0000
commit081e36e760cac7f8869d8bae1472373b1320df3f (patch)
tree798d1584fe690ed1fa95d4d442e79f16e41b67e3 /sys/fs
parent4601f5f5ee10e6ea093c6c7289a7157ed527d9d8 (diff)
downloadsrc-081e36e760cac7f8869d8bae1472373b1320df3f.tar.gz
src-081e36e760cac7f8869d8bae1472373b1320df3f.zip
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
Notes
Notes: svn path=/head/; revision=365787
Diffstat (limited to 'sys/fs')
-rw-r--r--sys/fs/tmpfs/tmpfs.h4
-rw-r--r--sys/fs/tmpfs/tmpfs_subr.c3
-rw-r--r--sys/fs/tmpfs/tmpfs_vfsops.c3
-rw-r--r--sys/fs/tmpfs/tmpfs_vnops.c84
4 files changed, 84 insertions, 10 deletions
diff --git a/sys/fs/tmpfs/tmpfs.h b/sys/fs/tmpfs/tmpfs.h
index ac8fe0c1c2d6..5b966635c486 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 d9844bc3c7c9..726f6819b126 100644
--- a/sys/fs/tmpfs/tmpfs_subr.c
+++ b/sys/fs/tmpfs/tmpfs_subr.c
@@ -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 @@ 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 c98b6126eecd..c090cd5aa658 100644
--- a/sys/fs/tmpfs/tmpfs_vfsops.c
+++ b/sys/fs/tmpfs/tmpfs_vfsops.c
@@ -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);
}
diff --git a/sys/fs/tmpfs/tmpfs_vnops.c b/sys/fs/tmpfs/tmpfs_vnops.c
index c5d0702801e9..150af5f93953 100644
--- a/sys/fs/tmpfs/tmpfs_vnops.c
+++ b/sys/fs/tmpfs/tmpfs_vnops.c
@@ -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.
@@ -567,6 +589,47 @@ tmpfs_read(struct vop_read_args *v)
}
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)
{
struct vnode *vp;
@@ -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,