Index: sys/fs/fuse/fuse_io.c =================================================================== --- sys/fs/fuse/fuse_io.c +++ sys/fs/fuse/fuse_io.c @@ -155,7 +155,13 @@ } break; case UIO_WRITE: - if (directio) { + /* + * Kludge: simulate write-through caching via write-around + * caching. Same effect, as far as never caching dirty data, + * but slightly pessimal in that newly written data is not + * cached. + */ + if (directio || fuse_data_cache_mode == FUSE_CACHE_WT) { FS_DEBUG("direct write of vnode %ju via file handle %ju\n", (uintmax_t)VTOILLU(vp), (uintmax_t)fufh->fh_id); err = fuse_write_directbackend(vp, uio, cred, fufh, ioflag); @@ -363,7 +369,7 @@ uio->uio_resid += diff; uio->uio_offset -= diff; if (uio->uio_offset > fvdat->filesize && - fuse_data_cache_enable) { + fuse_data_cache_mode != FUSE_CACHE_UC) { fuse_vnode_setsize(vp, cred, uio->uio_offset); fvdat->flag &= ~FN_SIZECHANGE; } Index: sys/fs/fuse/fuse_ipc.h =================================================================== --- sys/fs/fuse/fuse_ipc.h +++ sys/fs/fuse/fuse_ipc.h @@ -214,7 +214,13 @@ #define FSESS_NO_MMAP 0x0800 /* disable mmap */ #define FSESS_BROKENIO 0x1000 /* fix broken io */ -extern int fuse_data_cache_enable; +enum fuse_data_cache_mode { + FUSE_CACHE_UC, + FUSE_CACHE_WT, + FUSE_CACHE_WB, +}; + +extern int fuse_data_cache_mode; extern int fuse_data_cache_invalidate; extern int fuse_mmap_enable; extern int fuse_sync_resize; @@ -248,7 +254,7 @@ { struct fuse_data *data = fuse_get_mpdata(mp); - return (fuse_data_cache_enable || + return (fuse_data_cache_mode != FUSE_CACHE_UC && (data->dataflags & FSESS_NO_DATACACHE) == 0); } @@ -257,7 +263,7 @@ { struct fuse_data *data = fuse_get_mpdata(mp); - if (!(fuse_mmap_enable && fuse_data_cache_enable)) + if (!fuse_mmap_enable || fuse_data_cache_mode == FUSE_CACHE_UC) return 0; return ((data->dataflags & (FSESS_NO_DATACACHE | FSESS_NO_MMAP)) == 0); } Index: sys/fs/fuse/fuse_node.c =================================================================== --- sys/fs/fuse/fuse_node.c +++ sys/fs/fuse/fuse_node.c @@ -94,16 +94,19 @@ MALLOC_DEFINE(M_FUSEVN, "fuse_vnode", "fuse vnode private data"); +static int sysctl_fuse_cache_mode(SYSCTL_HANDLER_ARGS); + static int fuse_node_count = 0; SYSCTL_INT(_vfs_fuse, OID_AUTO, node_count, CTLFLAG_RD, &fuse_node_count, 0, "Count of FUSE vnodes"); -int fuse_data_cache_enable = 1; +int fuse_data_cache_mode = FUSE_CACHE_WT; -SYSCTL_INT(_vfs_fuse, OID_AUTO, data_cache_enable, CTLFLAG_RW, - &fuse_data_cache_enable, 0, - "enable caching of FUSE file data (including dirty data)"); +SYSCTL_PROC(_vfs_fuse, OID_AUTO, data_cache_mode, CTLTYPE_INT|CTLFLAG_RW, + &fuse_data_cache_mode, 0, sysctl_fuse_cache_mode, "I", + "Zero: disable caching of FUSE file data; One: write-through caching " + "(default); Two: write-back caching (generally unsafe)"); int fuse_data_cache_invalidate = 0; @@ -116,7 +119,7 @@ SYSCTL_INT(_vfs_fuse, OID_AUTO, mmap_enable, CTLFLAG_RW, &fuse_mmap_enable, 0, - "If non-zero, and data_cache_enable is also non-zero, enable mmap(2) of " + "If non-zero, and data_cache_mode is also non-zero, enable mmap(2) of " "FUSE files"); int fuse_refresh_size = 0; @@ -140,6 +143,28 @@ "If non-zero, print a diagnostic warning if a userspace filesystem returns" " EIO on reads of recently extended portions of files"); +static int +sysctl_fuse_cache_mode(SYSCTL_HANDLER_ARGS) +{ + int val, error; + + val = *(int *)arg1; + error = sysctl_handle_int(oidp, &val, 0, req); + if (error || !req->newptr) + return (error); + + switch (val) { + case FUSE_CACHE_UC: + case FUSE_CACHE_WT: + case FUSE_CACHE_WB: + *(int *)arg1 = val; + break; + default: + return (EDOM); + } + return (0); +} + static void fuse_vnode_init(struct vnode *vp, struct fuse_vnode_data *fvdat, uint64_t nodeid, enum vtype vtyp) @@ -375,7 +400,7 @@ struct vattr va; if ((fvdat->flag & FN_SIZECHANGE) != 0 || - fuse_data_cache_enable == 0 || + fuse_data_cache_mode == FUSE_CACHE_UC || (fuse_refresh_size == 0 && fvdat->filesize != 0)) return;