fusefs: quiet some cache-related warnings
If the FUSE server does something that would make our cache incoherent, we should print a warning to the user. However, we previously warned in some situations when we shouldn't, such as if the file's size changed on the server _after_ our own attribute cache had expired. This change suppresses the warning in cases like that. It also moves the warning logic to a single place within the code. PR: 256936 Reported by: Agata <chogata@moosefs.pro> Tested by: Agata <chogata@moosefs.pro>, jSML4ThWwBID69YC@protonmail.com MFC after: 2 weeks
This commit is contained in:
parent
a76de17715
commit
5d94aaacb5
@ -255,7 +255,8 @@ fuse_internal_access(struct vnode *vp,
|
||||
*/
|
||||
void
|
||||
fuse_internal_cache_attrs(struct vnode *vp, struct fuse_attr *attr,
|
||||
uint64_t attr_valid, uint32_t attr_valid_nsec, struct vattr *vap)
|
||||
uint64_t attr_valid, uint32_t attr_valid_nsec, struct vattr *vap,
|
||||
bool from_server)
|
||||
{
|
||||
struct mount *mp;
|
||||
struct fuse_vnode_data *fvdat;
|
||||
@ -271,9 +272,54 @@ fuse_internal_cache_attrs(struct vnode *vp, struct fuse_attr *attr,
|
||||
fuse_validity_2_bintime(attr_valid, attr_valid_nsec,
|
||||
&fvdat->attr_cache_timeout);
|
||||
|
||||
if (vnode_isreg(vp) &&
|
||||
fvdat->cached_attrs.va_size != VNOVAL &&
|
||||
attr->size != fvdat->cached_attrs.va_size)
|
||||
{
|
||||
if ( data->cache_mode == FUSE_CACHE_WB &&
|
||||
fvdat->flag & FN_SIZECHANGE)
|
||||
{
|
||||
const char *msg;
|
||||
|
||||
/*
|
||||
* The server changed the file's size even though we're
|
||||
* using writeback cacheing and and we have outstanding
|
||||
* dirty writes! That's a server bug.
|
||||
*/
|
||||
if (fuse_libabi_geq(data, 7, 23)) {
|
||||
msg = "writeback cache incoherent!."
|
||||
"To prevent data corruption, disable "
|
||||
"the writeback cache according to your "
|
||||
"FUSE server's documentation.";
|
||||
} else {
|
||||
msg = "writeback cache incoherent!."
|
||||
"To prevent data corruption, disable "
|
||||
"the writeback cache by setting "
|
||||
"vfs.fusefs.data_cache_mode to 0 or 1.";
|
||||
}
|
||||
fuse_warn(data, FSESS_WARN_WB_CACHE_INCOHERENT, msg);
|
||||
}
|
||||
if (fuse_vnode_attr_cache_valid(vp) &&
|
||||
data->cache_mode != FUSE_CACHE_UC)
|
||||
{
|
||||
/*
|
||||
* The server changed the file's size even though we
|
||||
* have it cached and our cache has not yet expired.
|
||||
* That's a bug.
|
||||
*/
|
||||
fuse_warn(data, FSESS_WARN_CACHE_INCOHERENT,
|
||||
"cache incoherent! "
|
||||
"To prevent "
|
||||
"data corruption, disable the data cache "
|
||||
"by mounting with -o direct_io, or as "
|
||||
"directed otherwise by your FUSE server's "
|
||||
"documentation.");
|
||||
}
|
||||
}
|
||||
|
||||
/* Fix our buffers if the filesize changed without us knowing */
|
||||
if (vnode_isreg(vp) && attr->size != fvdat->cached_attrs.va_size) {
|
||||
(void)fuse_vnode_setsize(vp, attr->size);
|
||||
(void)fuse_vnode_setsize(vp, attr->size, from_server);
|
||||
fvdat->cached_attrs.va_size = attr->size;
|
||||
}
|
||||
|
||||
@ -806,7 +852,7 @@ fuse_internal_newentry_core(struct vnode *dvp,
|
||||
fuse_vnode_clear_attr_cache(dvp);
|
||||
|
||||
fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
|
||||
feo->attr_valid_nsec, NULL);
|
||||
feo->attr_valid_nsec, NULL, true);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -912,26 +958,8 @@ fuse_internal_do_getattr(struct vnode *vp, struct vattr *vap,
|
||||
fao->attr.mtime = old_mtime.tv_sec;
|
||||
fao->attr.mtimensec = old_mtime.tv_nsec;
|
||||
}
|
||||
if (vnode_isreg(vp) &&
|
||||
fvdat->cached_attrs.va_size != VNOVAL &&
|
||||
fao->attr.size != fvdat->cached_attrs.va_size) {
|
||||
/*
|
||||
* The server changed the file's size even though we had it
|
||||
* cached! That's a server bug.
|
||||
*/
|
||||
struct mount *mp = vnode_mount(vp);
|
||||
struct fuse_data *data = fuse_get_mpdata(mp);
|
||||
|
||||
fuse_warn(data, FSESS_WARN_CACHE_INCOHERENT,
|
||||
"cache incoherent! "
|
||||
"To prevent data corruption, disable the data cache "
|
||||
"by mounting with -o direct_io, or as directed "
|
||||
"otherwise by your FUSE server's documentation.");
|
||||
int iosize = fuse_iosize(vp);
|
||||
v_inval_buf_range(vp, 0, INT64_MAX, iosize);
|
||||
}
|
||||
fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid,
|
||||
fao->attr_valid_nsec, vap);
|
||||
fao->attr_valid_nsec, vap, true);
|
||||
if (vtyp != vnode_vtype(vp)) {
|
||||
fuse_internal_vnode_disappear(vp);
|
||||
err = ENOENT;
|
||||
@ -1231,7 +1259,7 @@ int fuse_internal_setattr(struct vnode *vp, struct vattr *vap,
|
||||
struct fuse_attr_out *fao = (struct fuse_attr_out*)fdi.answ;
|
||||
fuse_vnode_undirty_cached_timestamps(vp);
|
||||
fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid,
|
||||
fao->attr_valid_nsec, NULL);
|
||||
fao->attr_valid_nsec, NULL, false);
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -223,7 +223,8 @@ int fuse_internal_access(struct vnode *vp, accmode_t mode,
|
||||
|
||||
/* attributes */
|
||||
void fuse_internal_cache_attrs(struct vnode *vp, struct fuse_attr *attr,
|
||||
uint64_t attr_valid, uint32_t attr_valid_nsec, struct vattr *vap);
|
||||
uint64_t attr_valid, uint32_t attr_valid_nsec, struct vattr *vap,
|
||||
bool from_server);
|
||||
|
||||
/* fsync */
|
||||
|
||||
|
@ -571,7 +571,7 @@ retry:
|
||||
as_written_offset = uio->uio_offset - diff;
|
||||
|
||||
if (as_written_offset - diff > filesize)
|
||||
fuse_vnode_setsize(vp, as_written_offset);
|
||||
fuse_vnode_setsize(vp, as_written_offset, false);
|
||||
if (as_written_offset - diff >= filesize)
|
||||
fvdat->flag &= ~FN_SIZECHANGE;
|
||||
|
||||
@ -715,7 +715,7 @@ again:
|
||||
* Extend file _after_ locking buffer so we won't race
|
||||
* with other readers
|
||||
*/
|
||||
err = fuse_vnode_setsize(vp, uio->uio_offset + n);
|
||||
err = fuse_vnode_setsize(vp, uio->uio_offset + n, false);
|
||||
filesize = uio->uio_offset + n;
|
||||
fvdat->flag |= FN_SIZECHANGE;
|
||||
if (err) {
|
||||
|
@ -389,11 +389,14 @@ fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid)
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the vnode's size to a new value, such as that provided by
|
||||
* FUSE_GETATTR.
|
||||
* Adjust the vnode's size to a new value.
|
||||
*
|
||||
* If the new value came from the server, such as from a FUSE_GETATTR
|
||||
* operation, set `from_server` true. But if it came from a local operation,
|
||||
* such as write(2) or truncate(2), set `from_server` false.
|
||||
*/
|
||||
int
|
||||
fuse_vnode_setsize(struct vnode *vp, off_t newsize)
|
||||
fuse_vnode_setsize(struct vnode *vp, off_t newsize, bool from_server)
|
||||
{
|
||||
struct fuse_vnode_data *fvdat = VTOFUD(vp);
|
||||
struct vattr *attrs;
|
||||
@ -434,6 +437,16 @@ fuse_vnode_setsize(struct vnode *vp, off_t newsize)
|
||||
MPASS(bp->b_flags & B_VMIO);
|
||||
vfs_bio_clrbuf(bp);
|
||||
bp->b_dirtyend = MIN(bp->b_dirtyend, newsize - lbn * iosize);
|
||||
} else if (from_server && newsize > oldsize && oldsize != VNOVAL) {
|
||||
/*
|
||||
* The FUSE server changed the file size behind our back. We
|
||||
* should invalidate the entire cache.
|
||||
*/
|
||||
daddr_t left_lbn, end_lbn;
|
||||
|
||||
left_lbn = oldsize / iosize;
|
||||
end_lbn = howmany(newsize, iosize);
|
||||
v_inval_buf_range(vp, 0, end_lbn, iosize);
|
||||
}
|
||||
out:
|
||||
if (bp)
|
||||
|
@ -201,7 +201,7 @@ void fuse_vnode_open(struct vnode *vp, int32_t fuse_open_flags,
|
||||
|
||||
int fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid);
|
||||
|
||||
int fuse_vnode_setsize(struct vnode *vp, off_t newsize);
|
||||
int fuse_vnode_setsize(struct vnode *vp, off_t newsize, bool from_server);
|
||||
|
||||
void fuse_vnode_undirty_cached_timestamps(struct vnode *vp);
|
||||
|
||||
|
@ -539,7 +539,6 @@ fuse_vfsop_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
|
||||
struct fuse_entry_out *feo;
|
||||
struct fuse_vnode_data *fvdat;
|
||||
const char dot[] = ".";
|
||||
off_t filesize;
|
||||
enum vtype vtyp;
|
||||
int error;
|
||||
|
||||
@ -576,47 +575,10 @@ fuse_vfsop_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
|
||||
error = fuse_vnode_get(mp, feo, nodeid, NULL, vpp, NULL, vtyp);
|
||||
if (error)
|
||||
goto out;
|
||||
filesize = feo->attr.size;
|
||||
|
||||
/*
|
||||
* In the case where we are looking up a FUSE node represented by an
|
||||
* existing cached vnode, and the true size reported by FUSE_LOOKUP
|
||||
* doesn't match the vnode's cached size, then any cached writes beyond
|
||||
* the file's current size are lost.
|
||||
*
|
||||
* We can get here:
|
||||
* * following attribute cache expiration, or
|
||||
* * due a bug in the daemon, or
|
||||
*/
|
||||
fvdat = VTOFUD(*vpp);
|
||||
if (vnode_isreg(*vpp) &&
|
||||
filesize != fvdat->cached_attrs.va_size &&
|
||||
fvdat->flag & FN_SIZECHANGE) {
|
||||
if (data->cache_mode == fuse_data_cache_mode) {
|
||||
const char *msg;
|
||||
|
||||
if (fuse_libabi_geq(data, 7, 23)) {
|
||||
msg = "writeback cache incoherent!."
|
||||
"To prevent data corruption, disable "
|
||||
"the writeback cache according to your "
|
||||
"FUSE server's documentation.";
|
||||
} else {
|
||||
msg = "writeback cache incoherent!."
|
||||
"To prevent data corruption, disable "
|
||||
"the writeback cache by setting "
|
||||
"vfs.fusefs.data_cache_mode to 0 or 1.";
|
||||
}
|
||||
fuse_warn(data, FSESS_WARN_WB_CACHE_INCOHERENT, msg);
|
||||
} else {
|
||||
/* If we get here, it's likely a fusefs kernel bug */
|
||||
printf("%s: WB cache incoherent on %s!\n", __func__,
|
||||
vnode_mount(*vpp)->mnt_stat.f_mntonname);
|
||||
}
|
||||
fvdat->flag &= ~FN_SIZECHANGE;
|
||||
}
|
||||
|
||||
fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
|
||||
feo->attr_valid_nsec, NULL);
|
||||
feo->attr_valid_nsec, NULL, true);
|
||||
fuse_validity_2_bintime(feo->entry_valid, feo->entry_valid_nsec,
|
||||
&fvdat->entry_cache_timeout);
|
||||
out:
|
||||
|
@ -895,7 +895,7 @@ fuse_vnop_create(struct vop_create_args *ap)
|
||||
}
|
||||
ASSERT_VOP_ELOCKED(*vpp, "fuse_vnop_create");
|
||||
fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
|
||||
feo->attr_valid_nsec, NULL);
|
||||
feo->attr_valid_nsec, NULL, true);
|
||||
|
||||
fuse_filehandle_init(*vpp, FUFH_RDWR, NULL, td, cred, foo);
|
||||
fuse_vnode_open(*vpp, foo->open_flags, td);
|
||||
@ -1151,7 +1151,7 @@ fuse_vnop_link(struct vop_link_args *ap)
|
||||
*/
|
||||
fuse_vnode_clear_attr_cache(tdvp);
|
||||
fuse_internal_cache_attrs(vp, &feo->attr, feo->attr_valid,
|
||||
feo->attr_valid_nsec, NULL);
|
||||
feo->attr_valid_nsec, NULL, true);
|
||||
}
|
||||
out:
|
||||
fdisp_destroy(&fdi);
|
||||
@ -1360,52 +1360,17 @@ fuse_vnop_lookup(struct vop_lookup_args *ap)
|
||||
*vpp = dvp;
|
||||
} else {
|
||||
struct fuse_vnode_data *fvdat;
|
||||
struct vattr *vap;
|
||||
|
||||
err = fuse_vnode_get(vnode_mount(dvp), feo, nid, dvp,
|
||||
&vp, cnp, vtyp);
|
||||
if (err)
|
||||
goto out;
|
||||
*vpp = vp;
|
||||
|
||||
/*
|
||||
* In the case where we are looking up a FUSE node
|
||||
* represented by an existing cached vnode, and the
|
||||
* true size reported by FUSE_LOOKUP doesn't match
|
||||
* the vnode's cached size, then any cached writes
|
||||
* beyond the file's current size are lost.
|
||||
*
|
||||
* We can get here:
|
||||
* * following attribute cache expiration, or
|
||||
* * due a bug in the daemon, or
|
||||
*/
|
||||
fvdat = VTOFUD(vp);
|
||||
if (vnode_isreg(vp) &&
|
||||
((filesize != fvdat->cached_attrs.va_size &&
|
||||
fvdat->flag & FN_SIZECHANGE) ||
|
||||
((vap = VTOVA(vp)) &&
|
||||
filesize != vap->va_size)))
|
||||
{
|
||||
fvdat->flag &= ~FN_SIZECHANGE;
|
||||
/*
|
||||
* The server changed the file's size even
|
||||
* though we had it cached, or had dirty writes
|
||||
* in the WB cache!
|
||||
*/
|
||||
fuse_warn(data, FSESS_WARN_CACHE_INCOHERENT,
|
||||
"cache incoherent! "
|
||||
"To prevent "
|
||||
"data corruption, disable the data cache "
|
||||
"by mounting with -o direct_io, or as "
|
||||
"directed otherwise by your FUSE server's "
|
||||
"documentation.");
|
||||
int iosize = fuse_iosize(vp);
|
||||
v_inval_buf_range(vp, 0, INT64_MAX, iosize);
|
||||
}
|
||||
|
||||
MPASS(feo != NULL);
|
||||
fuse_internal_cache_attrs(*vpp, &feo->attr,
|
||||
feo->attr_valid, feo->attr_valid_nsec, NULL);
|
||||
feo->attr_valid, feo->attr_valid_nsec, NULL, true);
|
||||
fuse_validity_2_bintime(feo->entry_valid,
|
||||
feo->entry_valid_nsec,
|
||||
&fvdat->entry_cache_timeout);
|
||||
|
Loading…
x
Reference in New Issue
Block a user