summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Pierre André <jpandre@users.sourceforge.net>2012-08-22 10:00:23 +0200
committerJean-Pierre André <jpandre@users.sourceforge.net>2012-08-22 10:00:23 +0200
commit493e91e42c43acce62b1cbb04db4ca81f12b504d (patch)
tree76d1530149546f9d8591fd023d10de831040fa8d
parent2d01280099a10c1d188763a93be0743aa06427d8 (diff)
downloadillumos-fusefs-493e91e42c43acce62b1cbb04db4ca81f12b504d.tar.gz
Avoided sending partial pages to user space
-rw-r--r--kernel/fuse.h2
-rw-r--r--kernel/fuse_vnops.c100
2 files changed, 96 insertions, 6 deletions
diff --git a/kernel/fuse.h b/kernel/fuse.h
index c26d6b6..5150366 100644
--- a/kernel/fuse.h
+++ b/kernel/fuse.h
@@ -58,6 +58,7 @@ extern "C" {
#define FSIZE_UPDATED 0x1 /* File size modifed by write */
#define FSIZE_NOT_RELIABLE 0x2 /* Cached file size might be invalid */
+#define FSIZE_UNSENT 0x4 /* File size update not started */
#define FUSE_FORCE_FH_RELEASE 0x1 /* force release of filehandle */
@@ -117,6 +118,7 @@ typedef struct fuse_vnode_data {
#endif
size_t fsize; /* temp place holder when file size gets modified */
int file_size_status; /* indicates if cached file size is valid */
+ offset_t offset; /* offset of (single) unsent page */
kmutex_t f_lock; /* serializes write/setattr requests */
long f_mapcnt; /* mappings to file pages */
diff --git a/kernel/fuse_vnops.c b/kernel/fuse_vnops.c
index 5824218..6da6407 100644
--- a/kernel/fuse_vnops.c
+++ b/kernel/fuse_vnops.c
@@ -866,6 +866,28 @@ static int fuse_discard_name(struct vnode *vp, fuse_session_t *sep)
return (0);
}
+/*
+ * Flush any unsent data not sent to putpage()
+ *
+ * This is required at close() and truncate()
+ */
+
+static int flush_unsent_data(struct vnode *vp, struct cred *credp,
+ caller_context_t *ct)
+{
+ fuse_vnode_data_t *vdata;
+ int err;
+
+ err = 0;
+ vdata = (fuse_vnode_data_t*)vp->v_data;
+ if (vdata && (vdata->file_size_status & FSIZE_UNSENT)) {
+ vdata->file_size_status &= ~FSIZE_UNSENT;
+ err = VOP_PUTPAGE(vp, vdata->offset,
+ (size_t)0, 0, credp, ct);
+ }
+ return (err);
+}
+
/* ARGSUSED */
static int fuse_close(struct vnode *vp, int flags, int count,
offset_t offset, struct cred *credp, caller_context_t *ct)
@@ -877,6 +899,13 @@ static int fuse_close(struct vnode *vp, int flags, int count,
enum fuse_opcode op = (vp->v_type == VDIR) ?
FUSE_RELEASEDIR : FUSE_RELEASE;
+ /* First, send unsent data */
+ if (vp && vp->v_data) {
+ err = flush_unsent_data(vp, credp, ct);
+ if (err)
+ return (err);
+ }
+
sep = fuse_minor_get_session(getminor(vp->v_rdev));
if (sep == NULL) {
DTRACE_PROBE2(fuse_close_err_session,
@@ -1209,6 +1238,8 @@ wrfuse(struct vnode *vp, struct uio *uiop, int ioflag,
caddr_t base; /* base of segmap */
ssize_t bytes;
int err;
+ offset_t unsent_offset; /* offset of a single unsent page */
+ fuse_vnode_data_t *vdata;
int pagecreate, newpage;
ssize_t premove_resid;
uint_t flags;
@@ -1222,6 +1253,22 @@ wrfuse(struct vnode *vp, struct uio *uiop, int ioflag,
if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
limit = MAXOFFSET_T;
+ unsent_offset = start_off & PAGEMASK;
+ /*
+ * If there is unsent data in a page not covered by
+ * the current write, sent it first.
+ * Maybe this is not needed (depends on the meaning
+ * of offset in putpage()), anyway it is safer.
+ */
+ vdata = (fuse_vnode_data_t*)vp->v_data;
+ if (vdata && (vdata->file_size_status & FSIZE_UNSENT)) {
+ if ((vdata->offset < (offset_t)(uiop->uio_loffset & PAGEMASK))
+ || (vdata->offset
+ > (offset_t)((uiop->uio_loffset + uiop->uio_resid - 1)
+ & PAGEMASK)))
+ flush_unsent_data(vp, credp, ct);
+ vdata->file_size_status &= ~FSIZE_UNSENT;
+ }
if ((rlim64_t)uiop->uio_loffset >= limit) {
proc_t *p = ttoproc(curthread);
@@ -1277,9 +1324,29 @@ wrfuse(struct vnode *vp, struct uio *uiop, int ioflag,
premove_resid = uiop->uio_resid;
if (vpm_enable) {
- err = vpm_data_copy(vp, uoff, bytes, uiop, !pagecreate,
- &newpage, pagecreate && pageoff, S_WRITE);
+ /*
+ * If we are about to copy a partial page
+ * and there are ready pages, flush them now
+ */
+ if ((unsent_offset < 0)
+ && ((uoff + bytes) & PAGEOFFSET)) {
+ err = VOP_PUTPAGE(vp, (offset_t)(start_off & PAGEMASK),
+ (size_t)0, 0, credp, ct);
+ /* next page need not be sent */
+ unsent_offset = (uoff + bytes) & PAGEMASK;
+ }
+ /* copy the page, unless we go an error */
+ if (!err) {
+ err = vpm_data_copy(vp, uoff, bytes, uiop,
+ !pagecreate, &newpage,
+ pagecreate && pageoff, S_WRITE);
+ /* flush if this is a full page */
+ if (!((uoff + bytes) & PAGEOFFSET)) {
+ unsent_offset = -1;
+ }
+ }
} else {
+ unsent_offset = -1;
segmap_offset = (uoff & PAGEMASK) & MAXBOFFSET;
base = segmap_getmapflt(segkmap, vp,
uoff & MAXBMASK, PAGESIZE, !pagecreate, S_WRITE);
@@ -1401,10 +1468,16 @@ wrfuse(struct vnode *vp, struct uio *uiop, int ioflag,
}
} while (uiop->uio_resid > 0 && err == 0 && bytes != 0);
out:
- /* if we did write any data, then try flushing it out to daemon */
+ /* if we did write any data, then flush it out to daemon if needed */
if (start_resid != uiop->uio_resid) {
- err = VOP_PUTPAGE(vp, (offset_t)(start_off & PAGEMASK),
- (size_t)0, 0, credp, ct);
+ if (unsent_offset < 0)
+ err = VOP_PUTPAGE(vp, (offset_t)(start_off & PAGEMASK),
+ (size_t)0, 0, credp, ct);
+ else {
+ VTOFD(vp)->offset = unsent_offset;
+ fsize_change_notify(vp, fsize,
+ FSIZE_UNSENT | FSIZE_UPDATED);
+ }
}
return (err);
@@ -1436,7 +1509,11 @@ static void fuse_set_getattr(struct vnode *vp, struct vattr *vap,
struct fuse_attr *attr)
{
vap->va_mode = attr->mode & MODEMASK;
- vap->va_size = attr->size;
+ /* return cached size if some data were not flushed */
+ if (vp->v_data && (VTOFD(vp)->file_size_status & FSIZE_UPDATED))
+ vap->va_size = VTOFD(vp)->fsize;
+ else
+ vap->va_size = attr->size;
vap->va_atime.tv_sec = attr->atime;
vap->va_atime.tv_nsec = attr->atimensec;
vap->va_mtime.tv_sec = attr->mtime;
@@ -1597,6 +1674,17 @@ fuse_setattr(
if (mask & AT_SIZE) {
/*
+ * Write unsent data before truncating, only
+ * needed for data visible after the truncation
+ */
+ if (vap->va_size) {
+ err = flush_unsent_data(vp, credp, ct);
+ if (err)
+ return (err);
+ }
+ if (vp->v_data)
+ fsize_change_notify(vp, 0, FSIZE_NOT_RELIABLE);
+ /*
* Mark all pages beyond the truncation as invalid
* otherwise old data may show in holes which
* are left in rewritten parts.