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:
parent
341346b039
commit
3d15b234a4
@ -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 @@ out:
|
||||
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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 @@ out:
|
||||
|
||||
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 @@ retry:
|
||||
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 @@ again:
|
||||
* 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 @@ again:
|
||||
* 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))) {
|
||||
|
@ -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 @@ out:
|
||||
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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user