fusefs: don't track a file's size in two places

fuse_vnode_data.filesize was mostly redundant with
fuse_vnode_data.cached_attrs.st_size, but didn't have exactly the same
meaning.  It was very confusing.  This commit eliminates the former.  It
also eliminates fuse_vnode_refreshsize, which ignored the cache timeout
value.

Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Alan Somers 2019-05-15 00:38:52 +00:00
parent 341346b039
commit 3d15b234a4
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=347603
6 changed files with 111 additions and 91 deletions

View File

@ -620,55 +620,44 @@ fuse_internal_forget_send(struct mount *mp,
fdisp_destroy(&fdi);
}
/* Read a vnode's attributes from cache or fetch them from the fuse daemon */
/* Fetch the vnode's attributes from the daemon*/
int
fuse_internal_getattr(struct vnode *vp, struct vattr *vap, struct ucred *cred,
struct thread *td)
fuse_internal_do_getattr(struct vnode *vp, struct vattr *vap,
struct ucred *cred, struct thread *td)
{
struct fuse_dispatcher fdi;
struct fuse_vnode_data *fvdat = VTOFUD(vp);
struct vattr *attrs;
struct fuse_attr_out *fao;
int err = 0;
if ((attrs = VTOVA(vp)) != NULL) {
/* struct copy */
*vap = *attrs;
if ((fvdat->flag & FN_SIZECHANGE) != 0)
vap->va_size = fvdat->filesize;
return 0;
}
off_t old_filesize = fvdat->cached_attrs.va_size;
enum vtype vtyp;
int err;
fdisp_init(&fdi, 0);
if ((err = fdisp_simple_putget_vp(&fdi, FUSE_GETATTR, vp, td, cred))) {
if (err == ENOENT) {
if (err == ENOENT)
fuse_internal_vnode_disappear(vp);
}
goto out;
}
fao = (struct fuse_attr_out *)fdi.answ;
vtyp = IFTOVT(fao->attr.mode);
fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid,
fao->attr_valid_nsec, vap);
if (vap->va_type != vnode_vtype(vp)) {
if (vtyp != vnode_vtype(vp)) {
fuse_internal_vnode_disappear(vp);
err = ENOENT;
goto out;
}
if ((fvdat->flag & FN_SIZECHANGE) != 0)
vap->va_size = fvdat->filesize;
fvdat->cached_attrs.va_size = old_filesize;
if (vnode_isreg(vp) && (fvdat->flag & FN_SIZECHANGE) == 0) {
/*
* This is for those cases when the file size changed without us
* knowing, and we want to catch up.
*/
off_t new_filesize = fao->attr.size;
if (fvdat->filesize != new_filesize) {
fuse_vnode_setsize(vp, cred, new_filesize);
fvdat->flag &= ~FN_SIZECHANGE;
}
if (old_filesize != fao->attr.size)
fuse_vnode_setsize(vp, cred, fao->attr.size);
}
out:
@ -676,6 +665,25 @@ fuse_internal_getattr(struct vnode *vp, struct vattr *vap, struct ucred *cred,
return err;
}
/* Read a vnode's attributes from cache or fetch them from the fuse daemon */
int
fuse_internal_getattr(struct vnode *vp, struct vattr *vap, struct ucred *cred,
struct thread *td)
{
struct fuse_vnode_data *fvdat = VTOFUD(vp);
struct vattr *attrs;
off_t old_filesize = vap->va_size;
if ((attrs = VTOVA(vp)) != NULL) {
*vap = *attrs; /* struct copy */
if ((fvdat->flag & FN_SIZECHANGE) != 0)
vap->va_size = old_filesize;
return 0;
}
return fuse_internal_do_getattr(vp, vap, cred, td);
}
void
fuse_internal_vnode_disappear(struct vnode *vp)
{

View File

@ -219,6 +219,8 @@ int fuse_internal_fsync(struct vnode *vp, struct thread *td, int waitfor,
int fuse_internal_fsync_callback(struct fuse_ticket *tick, struct uio *uio);
/* getattr */
int fuse_internal_do_getattr(struct vnode *vp, struct vattr *vap,
struct ucred *cred, struct thread *td);
int fuse_internal_getattr(struct vnode *vp, struct vattr *vap,
struct ucred *cred, struct thread *td);

View File

@ -118,7 +118,8 @@ fuse_read_biobackend(struct vnode *vp, struct uio *uio, int ioflag,
struct ucred *cred, struct fuse_filehandle *fufh, pid_t pid);
static int
fuse_write_directbackend(struct vnode *vp, struct uio *uio,
struct ucred *cred, struct fuse_filehandle *fufh, int ioflag);
struct ucred *cred, struct fuse_filehandle *fufh, off_t filesize,
int ioflag);
static int
fuse_write_biobackend(struct vnode *vp, struct uio *uio,
struct ucred *cred, struct fuse_filehandle *fufh, int ioflag, pid_t pid);
@ -214,10 +215,15 @@ fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag, bool pages,
*/
if (directio || fuse_data_cache_mode == FUSE_CACHE_WT) {
const int iosize = fuse_iosize(vp);
off_t start, end;
off_t start, end, filesize;
SDT_PROBE2(fusefs, , io, trace, 1,
"direct write of vnode");
err = fuse_vnode_size(vp, &filesize, cred, curthread);
if (err)
return err;
start = uio->uio_offset;
end = start + uio->uio_resid;
/*
@ -228,7 +234,7 @@ fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag, bool pages,
if (!pages )
v_inval_buf_range(vp, start, end, iosize);
err = fuse_write_directbackend(vp, uio, cred, fufh,
ioflag);
filesize, ioflag);
} else {
SDT_PROBE2(fusefs, , io, trace, 1,
"buffered write of vnode");
@ -262,7 +268,9 @@ fuse_read_biobackend(struct vnode *vp, struct uio *uio, int ioflag,
if (uio->uio_offset < 0)
return (EINVAL);
filesize = VTOFUD(vp)->filesize;
err = fuse_vnode_size(vp, &filesize, cred, curthread);
if (err)
return err;
for (err = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) {
if (fuse_isdeadfs(vp)) {
@ -373,7 +381,8 @@ fuse_read_directbackend(struct vnode *vp, struct uio *uio,
static int
fuse_write_directbackend(struct vnode *vp, struct uio *uio,
struct ucred *cred, struct fuse_filehandle *fufh, int ioflag)
struct ucred *cred, struct fuse_filehandle *fufh, off_t filesize,
int ioflag)
{
struct fuse_vnode_data *fvdat = VTOFUD(vp);
struct fuse_write_in *fwi;
@ -388,8 +397,9 @@ fuse_write_directbackend(struct vnode *vp, struct uio *uio,
if (uio->uio_resid == 0)
return (0);
if (ioflag & IO_APPEND)
uio_setoffset(uio, fvdat->filesize);
uio_setoffset(uio, filesize);
fdisp_init(&fdi, 0);
@ -436,7 +446,7 @@ fuse_write_directbackend(struct vnode *vp, struct uio *uio,
diff = fwi->size - fwo->size;
as_written_offset = uio->uio_offset - diff;
if (as_written_offset - diff > fvdat->filesize &&
if (as_written_offset - diff > filesize &&
fuse_data_cache_mode != FUSE_CACHE_UC) {
fuse_vnode_setsize(vp, cred, as_written_offset);
fvdat->flag &= ~FN_SIZECHANGE;
@ -495,6 +505,7 @@ fuse_write_biobackend(struct vnode *vp, struct uio *uio,
struct fuse_vnode_data *fvdat = VTOFUD(vp);
struct buf *bp;
daddr_t lbn;
off_t filesize;
int bcount;
int n, on, err = 0;
@ -507,8 +518,13 @@ fuse_write_biobackend(struct vnode *vp, struct uio *uio,
return (EINVAL);
if (uio->uio_resid == 0)
return (0);
err = fuse_vnode_size(vp, &filesize, cred, curthread);
if (err)
return err;
if (ioflag & IO_APPEND)
uio_setoffset(uio, fvdat->filesize);
uio_setoffset(uio, filesize);
/*
* Find all of this file's B_NEEDCOMMIT buffers. If our writes
@ -532,7 +548,7 @@ fuse_write_biobackend(struct vnode *vp, struct uio *uio,
* Handle direct append and file extension cases, calculate
* unaligned buffer size.
*/
if (uio->uio_offset == fvdat->filesize && n) {
if (uio->uio_offset == filesize && n) {
/*
* Get the buffer (in its pre-append state to maintain
* B_CACHE if it was previously set). Resize the
@ -564,17 +580,16 @@ fuse_write_biobackend(struct vnode *vp, struct uio *uio,
* adjust the file's size as appropriate.
*/
bcount = on + n;
if ((off_t)lbn * biosize + bcount < fvdat->filesize) {
if ((off_t)(lbn + 1) * biosize < fvdat->filesize)
if ((off_t)lbn * biosize + bcount < filesize) {
if ((off_t)(lbn + 1) * biosize < filesize)
bcount = biosize;
else
bcount = fvdat->filesize -
(off_t)lbn *biosize;
bcount = filesize - (off_t)lbn *biosize;
}
SDT_PROBE6(fusefs, , io, write_biobackend_start,
lbn, on, n, uio, bcount, false);
bp = getblk(vp, lbn, bcount, PCATCH, 0, 0);
if (bp && uio->uio_offset + n > fvdat->filesize) {
if (bp && uio->uio_offset + n > filesize) {
err = fuse_vnode_setsize(vp, cred,
uio->uio_offset + n);
if (err) {
@ -719,11 +734,11 @@ int
fuse_io_strategy(struct vnode *vp, struct buf *bp)
{
struct fuse_filehandle *fufh;
struct fuse_vnode_data *fvdat = VTOFUD(vp);
struct ucred *cred;
struct uio *uiop;
struct uio uio;
struct iovec io;
off_t filesize;
int error = 0;
int fflag;
/* We don't know the true pid when we're dealing with the cache */
@ -807,9 +822,16 @@ fuse_io_strategy(struct vnode *vp, struct buf *bp)
/*
* Setup for actual write
*/
if ((off_t)bp->b_blkno * biosize + bp->b_dirtyend >
fvdat->filesize)
bp->b_dirtyend = fvdat->filesize -
error = fuse_vnode_size(vp, &filesize, cred, curthread);
if (error) {
bp->b_ioflags |= BIO_ERROR;
bp->b_error = error;
bufdone(bp);
return (error);
}
if ((off_t)bp->b_blkno * biosize + bp->b_dirtyend > filesize)
bp->b_dirtyend = filesize -
(off_t)bp->b_blkno * biosize;
if (bp->b_dirtyend > bp->b_dirtyoff) {
@ -820,7 +842,8 @@ fuse_io_strategy(struct vnode *vp, struct buf *bp)
io.iov_base = (char *)bp->b_data + bp->b_dirtyoff;
uiop->uio_rw = UIO_WRITE;
error = fuse_write_directbackend(vp, uiop, cred, fufh, 0);
error = fuse_write_directbackend(vp, uiop, cred, fufh,
filesize, 0);
if (error == EINTR || error == ETIMEDOUT
|| (!error && (bp->b_flags & B_NEEDCOMMIT))) {

View File

@ -143,7 +143,6 @@ fuse_vnode_init(struct vnode *vp, struct fuse_vnode_data *fvdat,
fvdat->nid = nodeid;
LIST_INIT(&fvdat->handles);
vattr_null(&fvdat->cached_attrs);
fvdat->filesize = FUSE_FILESIZE_UNINITIALIZED;
if (nodeid == FUSE_ROOT_ID) {
vp->v_vflag |= VV_ROOT;
}
@ -363,7 +362,8 @@ fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid)
fsai->valid = 0;
/* Truncate to a new value. */
fsai->size = fvdat->filesize;
MPASS((fvdat->flag & FN_SIZECHANGE) != 0);
fsai->size = fvdat->cached_attrs.va_size;
fsai->valid |= FATTR_SIZE;
fuse_filehandle_getrw(vp, FWRITE, &fufh, cred, pid);
@ -379,24 +379,10 @@ fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid)
return err;
}
int
fuse_vnode_refreshsize(struct vnode *vp, struct ucred *cred)
{
struct fuse_vnode_data *fvdat = VTOFUD(vp);
struct vattr va;
int err;
if ((fvdat->flag & FN_SIZECHANGE) != 0 ||
fuse_data_cache_mode == FUSE_CACHE_UC ||
fvdat->filesize != FUSE_FILESIZE_UNINITIALIZED)
return 0;
err = fuse_internal_getattr(vp, &va, cred, curthread);
SDT_PROBE2(fusefs, , node, trace, 1, "refreshed file size");
return err;
}
/*
* Adjust the vnode's size to a new value, such as that provided by
* FUSE_GETATTR.
*/
int
fuse_vnode_setsize(struct vnode *vp, struct ucred *cred, off_t newsize)
{
@ -410,8 +396,8 @@ fuse_vnode_setsize(struct vnode *vp, struct ucred *cred, off_t newsize)
ASSERT_VOP_ELOCKED(vp, "fuse_vnode_setsize");
iosize = fuse_iosize(vp);
oldsize = fvdat->filesize;
fvdat->filesize = newsize;
oldsize = fvdat->cached_attrs.va_size;
fvdat->cached_attrs.va_size = newsize;
if ((attrs = VTOVA(vp)) != NULL)
attrs->va_size = newsize;
fvdat->flag |= FN_SIZECHANGE;
@ -446,3 +432,21 @@ fuse_vnode_setsize(struct vnode *vp, struct ucred *cred, off_t newsize)
vnode_pager_setsize(vp, newsize);
return err;
}
/* Get the current, possibly dirty, size of the file */
int
fuse_vnode_size(struct vnode *vp, off_t *filesize, struct ucred *cred,
struct thread *td)
{
struct fuse_vnode_data *fvdat = VTOFUD(vp);
int error = 0;
if (!(fvdat->flag & FN_SIZECHANGE) &&
(VTOVA(vp) == NULL || fvdat->cached_attrs.va_size == VNOVAL))
error = fuse_internal_do_getattr(vp, NULL, cred, td);
if (!error)
*filesize = fvdat->cached_attrs.va_size;
return error;
}

View File

@ -68,11 +68,14 @@
#define FN_REVOKED 0x00000020
#define FN_FLUSHINPROG 0x00000040
#define FN_FLUSHWANT 0x00000080
/*
* Indicates that the file's size is dirty; the kernel has changed it but not
* yet send the change to the daemon. When this bit is set, the
* cache_attrs.va_size field does not time out
*/
#define FN_SIZECHANGE 0x00000100
#define FN_DIRECTIO 0x00000200
#define FUSE_FILESIZE_UNINITIALIZED -1
struct fuse_vnode_data {
/** self **/
uint64_t nid;
@ -91,12 +94,6 @@ struct fuse_vnode_data {
/* The monotonic time after which the attr cache is invalid */
struct bintime attr_cache_timeout;
struct vattr cached_attrs;
/*
* File size according to the kernel, not the daemon.
* May differ from cached_attrs.st_size due to write caching. Unlike
* cached_attrs.st_size, filesize never expires.
*/
off_t filesize;
uint64_t nlookup;
enum vtype vtype;
};
@ -138,6 +135,9 @@ fuse_vnode_setparent(struct vnode *vp, struct vnode *dvp)
}
}
int fuse_vnode_size(struct vnode *vp, off_t *filesize, struct ucred *cred,
struct thread *td);
void fuse_vnode_destroy(struct vnode *vp);
int fuse_vnode_get(struct mount *mp, struct fuse_entry_out *feo,
@ -147,8 +147,6 @@ int fuse_vnode_get(struct mount *mp, struct fuse_entry_out *feo,
void fuse_vnode_open(struct vnode *vp, int32_t fuse_open_flags,
struct thread *td);
int fuse_vnode_refreshsize(struct vnode *vp, struct ucred *cred);
int fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid);
int fuse_vnode_setsize(struct vnode *vp, struct ucred *cred, off_t newsize);

View File

@ -1052,7 +1052,7 @@ fuse_vnop_lookup(struct vop_lookup_args *ap)
*/
fvdat = VTOFUD(vp);
if (vnode_isreg(vp) &&
filesize != fvdat->filesize) {
filesize != fvdat->cached_attrs.va_size) {
/*
* The FN_SIZECHANGE flag reflects a dirty
* append. If userspace lets us know our cache
@ -1704,17 +1704,6 @@ fuse_vnop_strategy(struct vop_strategy_args *ap)
bufdone(bp);
return 0;
}
if (bp->b_iocmd == BIO_WRITE) {
int err;
err = fuse_vnode_refreshsize(vp, NOCRED);
if (err) {
bp->b_ioflags |= BIO_ERROR;
bp->b_error = err;
bufdone(bp);
return 0;
}
}
/*
* VOP_STRATEGY always returns zero and signals error via bp->b_ioflags.
@ -1788,14 +1777,10 @@ fuse_vnop_write(struct vop_write_args *ap)
int ioflag = ap->a_ioflag;
struct ucred *cred = ap->a_cred;
pid_t pid = curthread->td_proc->p_pid;
int err;
if (fuse_isdeadfs(vp)) {
return ENXIO;
}
err = fuse_vnode_refreshsize(vp, cred);
if (err)
return err;
if (VTOFUD(vp)->flag & FN_DIRECTIO) {
ioflag |= IO_DIRECT;