Hello When a PUFFS filesystem uses the page cache, data enters the cache with no guarantee it will be flushed. If it cannot be flushed (bcause PUFFS write requests get EDQUOT or ENOSPC), then the kernel will loop forever trying to flush data from the cache, and the filesystem cannot be unmounted without -f (and data loss).
In the attached patch, I add in PUFFS: - support for the fallocate operation - a puffs_gop_alloe() function that use fallocate - when writing through the page cache we call first GOP_ALLOC to make sure backend storage is allocated for the data we cache. debug printf show a sane behavior, GOP_ALLOC calling puffs_gop_alloc only when required. If the filesystem does not implement fallocate, we keep the current behavior of filling the page cache with data we are not sure we can flush. Perhaps we can improve further: missing fallocate can be emulated by writing zeroed chuncks. I have implemented that in libperfuse, but we may want to have this in libpuffs, enabled by a mount option. Input welcome. -- Emmanuel Dreyfus m...@netbsd.org
Index: lib/libpuffs/dispatcher.c =================================================================== RCS file: /cvsroot/src/lib/libpuffs/dispatcher.c,v retrieving revision 1.46.4.1 diff -U4 -r1.46.4.1 dispatcher.c --- lib/libpuffs/dispatcher.c 24 Aug 2014 08:42:06 -0000 1.46.4.1 +++ lib/libpuffs/dispatcher.c 30 Sep 2014 09:40:28 -0000 @@ -1139,8 +1139,22 @@ auxt->pvnr_attrname, pcr); break; } + case PUFFS_VN_FALLOCATE: + { + struct puffs_vnmsg_fallocate *auxt = auxbuf; + + if (pops->puffs_node_fallocate == NULL) { + error = EOPNOTSUPP; + break; + } + + error = pops->puffs_node_fallocate(pu, + opcookie, auxt->pvnr_off, auxt->pvnr_len); + break; + } + default: printf("inval op %d\n", preq->preq_optype); error = EINVAL; break; Index: lib/libpuffs/opdump.c =================================================================== RCS file: /cvsroot/src/lib/libpuffs/opdump.c,v retrieving revision 1.36 diff -U4 -r1.36 opdump.c --- lib/libpuffs/opdump.c 15 Mar 2012 02:02:21 -0000 1.36 +++ lib/libpuffs/opdump.c 30 Sep 2014 09:40:28 -0000 @@ -116,8 +116,9 @@ "PUFFS_VN_OPENEXTATTR", "PUFFS_VN_DELETEEXTATTR", "PUFFS_VN_SETEXTATTR", "PUFFS_VN_CLOSEEXTATTR", + "PUFFS_VN_FALLOCATE", }; size_t puffsdump_vnop_count = __arraycount(puffsdump_vnop_revmap); /* XXX! */ Index: lib/libpuffs/puffs.c =================================================================== RCS file: /cvsroot/src/lib/libpuffs/puffs.c,v retrieving revision 1.117 diff -U4 -r1.117 puffs.c --- lib/libpuffs/puffs.c 14 Nov 2011 01:27:42 -0000 1.117 +++ lib/libpuffs/puffs.c 30 Sep 2014 09:40:28 -0000 @@ -105,8 +105,9 @@ FILLOP(getextattr, GETEXTATTR); FILLOP(setextattr, SETEXTATTR); FILLOP(listextattr, LISTEXTATTR); FILLOP(deleteextattr, DELETEEXTATTR); + FILLOP(fallocate, FALLOCATE); } #undef FILLOP /* Index: lib/libpuffs/puffs.h =================================================================== RCS file: /cvsroot/src/lib/libpuffs/puffs.h,v retrieving revision 1.124.10.1 diff -U4 -r1.124.10.1 puffs.h --- lib/libpuffs/puffs.h 24 Aug 2014 08:42:06 -0000 1.124.10.1 +++ lib/libpuffs/puffs.h 30 Sep 2014 09:40:28 -0000 @@ -251,10 +251,12 @@ int (*puffs_node_reclaim2)(struct puffs_usermount *, puffs_cookie_t, int); int (*puffs_node_open2)(struct puffs_usermount *, puffs_cookie_t, int, const struct puffs_cred *, int *); + int (*puffs_node_fallocate)(struct puffs_usermount *, + puffs_cookie_t, off_t, off_t); - void *puffs_ops_spare[28]; + void *puffs_ops_spare[27]; }; typedef int (*pu_pathbuild_fn)(struct puffs_usermount *, const struct puffs_pathobj *, @@ -413,9 +415,11 @@ const struct puffs_cred *, int, int); \ int fsname##_node_reclaim2(struct puffs_usermount *, \ puffs_cookie_t, int); \ int fsname##_node_open2(struct puffs_usermount *, \ - puffs_cookie_t, int, const struct puffs_cred *, int *); + puffs_cookie_t, int, const struct puffs_cred *, int *); \ + int fsname##_node_fallocate(struct puffs_usermount *, \ + puffs_cookie_t, int, off_t, off_t); #define PUFFSOP_INIT(ops) \ ops = malloc(sizeof(struct puffs_ops)); \ ? sys/fs/puffs/diff ? sys/fs/puffs/ndiff ? sys/fs/puffs/out Index: sys/fs/puffs/puffs_msgif.h =================================================================== RCS file: /cvsroot/src/sys/fs/puffs/puffs_msgif.h,v retrieving revision 1.80.14.1 diff -U 4 -r1.80.14.1 puffs_msgif.h --- sys/fs/puffs/puffs_msgif.h 26 Aug 2014 23:15:12 -0000 1.80.14.1 +++ sys/fs/puffs/puffs_msgif.h 30 Sep 2014 13:36:52 -0000 @@ -85,13 +85,13 @@ PUFFS_VN_PRINT, PUFFS_VN_ISLOCKED, PUFFS_VN_PATHCONF, PUFFS_VN_ADVLOCK, PUFFS_VN_LEASE, PUFFS_VN_WHITEOUT, PUFFS_VN_GETPAGES, PUFFS_VN_PUTPAGES, PUFFS_VN_GETEXTATTR, PUFFS_VN_LISTEXTATTR, PUFFS_VN_OPENEXTATTR, PUFFS_VN_DELETEEXTATTR, - PUFFS_VN_SETEXTATTR, PUFFS_VN_CLOSEEXTATTR + PUFFS_VN_SETEXTATTR, PUFFS_VN_CLOSEEXTATTR, PUFFS_VN_FALLOCATE, /* NOTE: If you add an op, decrement PUFFS_VN_SPARE accordingly */ }; #define PUFFS_VN_MAX PUFFS_VN_CLOSEEXTATTR -#define PUFFS_VN_SPARE 32 +#define PUFFS_VN_SPARE 31 /* * These signal invalid parameters the file system returned. */ @@ -665,8 +665,14 @@ struct puffs_kcred pvnr_cred; /* OUT */ }; +struct puffs_vnmsg_fallocate { + struct puffs_req pvn_pr; + off_t pvnr_off; /* OUT */ + off_t pvnr_len; /* OUT */ +}; + /* * For cache reports. Everything is always out-out-out, no replies */ Index: sys/fs/puffs/puffs_subr.c =================================================================== RCS file: /cvsroot/src/sys/fs/puffs/puffs_subr.c,v retrieving revision 1.66 diff -U 4 -r1.66 puffs_subr.c --- sys/fs/puffs/puffs_subr.c 16 Nov 2008 19:34:30 -0000 1.66 +++ sys/fs/puffs/puffs_subr.c 30 Sep 2014 13:36:52 -0000 @@ -197,8 +197,15 @@ puffs_updatenode(VPTOPP(vp), uflags, 0); } +int +puffs_gop_alloc(struct vnode *vp, off_t off, off_t len, + int flags, kauth_cred_t cred) +{ + return _puffs_vnop_fallocate(vp, off, len); +} + void puffs_senderr(struct puffs_mount *pmp, int type, int error, const char *str, puffs_cookie_t ck) { Index: sys/fs/puffs/puffs_sys.h =================================================================== RCS file: /cvsroot/src/sys/fs/puffs/puffs_sys.h,v retrieving revision 1.84.4.2 diff -U 4 -r1.84.4.2 puffs_sys.h --- sys/fs/puffs/puffs_sys.h 29 Aug 2014 11:55:34 -0000 1.84.4.2 +++ sys/fs/puffs/puffs_sys.h 30 Sep 2014 13:36:52 -0000 @@ -279,8 +279,10 @@ void puffs_mp_release(struct puffs_mount *); void puffs_gop_size(struct vnode *, off_t, off_t *, int); void puffs_gop_markupdate(struct vnode *, int); +int puffs_gop_alloc(struct vnode *, off_t, off_t, int, kauth_cred_t); +int _puffs_vnop_fallocate(struct vnode *, off_t, off_t); void puffs_senderr(struct puffs_mount *, int, int, const char *, puffs_cookie_t); Index: sys/fs/puffs/puffs_vfsops.c =================================================================== RCS file: /cvsroot/src/sys/fs/puffs/puffs_vfsops.c,v retrieving revision 1.113.2.1 diff -U 4 -r1.113.2.1 puffs_vfsops.c --- sys/fs/puffs/puffs_vfsops.c 29 Aug 2014 11:55:34 -0000 1.113.2.1 +++ sys/fs/puffs/puffs_vfsops.c 30 Sep 2014 13:36:52 -0000 @@ -73,11 +73,9 @@ static const struct genfs_ops puffs_genfsops = { .gop_size = puffs_gop_size, .gop_write = genfs_gop_write, .gop_markupdate = puffs_gop_markupdate, -#if 0 - .gop_alloc, should ask userspace -#endif + .gop_alloc = puffs_gop_alloc, }; /* * Try to ensure data structures used by the puffs protocol Index: sys/fs/puffs/puffs_vnops.c =================================================================== RCS file: /cvsroot/src/sys/fs/puffs/puffs_vnops.c,v retrieving revision 1.182.2.4 diff -U 4 -r1.182.2.4 puffs_vnops.c --- sys/fs/puffs/puffs_vnops.c 11 Sep 2014 14:00:54 -0000 1.182.2.4 +++ sys/fs/puffs/puffs_vnops.c 30 Sep 2014 13:36:53 -0000 @@ -72,8 +72,9 @@ int puffs_vnop_symlink(void *); int puffs_vnop_rename(void *); int puffs_vnop_read(void *); int puffs_vnop_write(void *); +int puffs_vnop_fallocate(void *); int puffs_vnop_fcntl(void *); int puffs_vnop_ioctl(void *); int puffs_vnop_inactive(void *); int puffs_vnop_print(void *); @@ -112,9 +113,9 @@ { &vop_getattr_desc, puffs_vnop_checkop }, /* getattr */ { &vop_setattr_desc, puffs_vnop_checkop }, /* setattr */ { &vop_read_desc, puffs_vnop_checkop }, /* read */ { &vop_write_desc, puffs_vnop_checkop }, /* write */ - { &vop_fallocate_desc, genfs_eopnotsupp }, /* fallocate */ + { &vop_fallocate_desc, puffs_vnop_fallocate }, /* fallocate */ { &vop_fdiscard_desc, genfs_eopnotsupp }, /* fdiscard */ { &vop_fsync_desc, puffs_vnop_fsync }, /* REAL fsync */ { &vop_seek_desc, puffs_vnop_checkop }, /* seek */ { &vop_remove_desc, puffs_vnop_checkop }, /* remove */ @@ -2330,8 +2331,24 @@ if (ap->a_ioflag & IO_APPEND) uio->uio_offset = vp->v_size; origoff = uio->uio_offset; + + /* + * Attempt to allocate storage so that we do not + * feed the page cache with data we cannot flush + * later. If we get EOPNOTSUPP it means FALLOCATE + * is unimplemented in the filesystem: in that + * case we cary on without any guarantee that the + * data we cache will be flushable + */ + error = GOP_ALLOC(vp, origoff, uio->uio_resid, + 0, curlwp->l_cred); + if (error == EOPNOTSUPP) + error = 0; + else + goto out; + while (uio->uio_resid > 0) { oldoff = uio->uio_offset; bytelen = uio->uio_resid; @@ -2444,9 +2461,62 @@ uflags |= PUFFS_UPDATECTIME; uflags |= PUFFS_UPDATEMTIME; puffs_updatenode(VPTOPP(vp), uflags, vp->v_size); +out: + mutex_exit(&pn->pn_sizemtx); + return error; +} + +int +_puffs_vnop_fallocate(struct vnode *vp, off_t pos, off_t len) +{ + PUFFS_MSG_VARS(vn, fallocate); + struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount); + int error; + + PUFFS_MSG_ALLOC(vn, fallocate); + fallocate_msg->pvnr_off = pos; + fallocate_msg->pvnr_len = len; + puffs_msg_setinfo(park_fallocate, PUFFSOP_VN, + PUFFS_VN_FALLOCATE, VPTOPNC(vp)); + + PUFFS_MSG_ENQUEUEWAIT2(pmp, park_fallocate, vp->v_data, + NULL, error); + error = checkerr(pmp, error, __func__); + PUFFS_MSG_RELEASE(fallocate); + + return error; +} + +int +puffs_vnop_fallocate(void *v) +{ + struct vop_fallocate_args /* { + const struct vnodeop_desc *a_desc; + struct vnode *a_vp; + off_t a_pos; + off_t a_len; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct puffs_node *pn = VPTOPP(vp); + int error; + + mutex_enter(&pn->pn_sizemtx); + error = _puffs_vnop_fallocate(v, ap->a_pos, ap->a_len); + if (error) { + if (error == EAGAIN) + error = EIO; + goto out; + } + + if (ap->a_pos + ap->a_len > vp->v_size) { + uvm_vnp_setsize(vp, ap->a_pos + ap->a_len); + puffs_updatenode(pn, PUFFS_UPDATESIZE, vp->v_size); + } +out: mutex_exit(&pn->pn_sizemtx); + return error; } int