diff options
author | Alan Somers <asomers@FreeBSD.org> | 2021-10-02 18:17:36 +0000 |
---|---|---|
committer | Alan Somers <asomers@FreeBSD.org> | 2021-10-06 20:07:33 +0000 |
commit | 7430017b9978cae054ed99e5160f739e5ca021d5 (patch) | |
tree | 2d16872ddb5903a5a986ccdd766634822bcea7a2 /sys/fs/fuse | |
parent | 5d94aaacb5180798b2f698e33937f068386004eb (diff) | |
download | src-7430017b9978cae054ed99e5160f739e5ca021d5.tar.gz src-7430017b9978cae054ed99e5160f739e5ca021d5.zip |
fusefs: fix a recurse-on-non-recursive lockmgr panic
fuse_vnop_bmap needs to know the file's size in order to calculate the
optimum amount of readahead. If the file's size is unknown, it must ask
the FUSE server. But if the file's data was previously cached and the
server reports that its size has shrunk, fusefs must invalidate the
cached data. That's not possible during VOP_BMAP because the buffer
object is already locked.
Fix the panic by not querying the FUSE server for the file's size during
VOP_BMAP if we don't need it. That's also a a slight performance
optimization.
PR: 256937
Reported by: Agata <chogata@moosefs.pro>
Tested by: Agata <chogata@moosefs.pro>
MFC after: 2 weeks
Diffstat (limited to 'sys/fs/fuse')
-rw-r--r-- | sys/fs/fuse/fuse_vnops.c | 20 |
1 files changed, 16 insertions, 4 deletions
diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c index dd8ff0fcc45a..9aafbad990c5 100644 --- a/sys/fs/fuse/fuse_vnops.c +++ b/sys/fs/fuse/fuse_vnops.c @@ -516,8 +516,9 @@ fuse_vnop_bmap(struct vop_bmap_args *ap) struct fuse_bmap_in *fbi; struct fuse_bmap_out *fbo; struct fuse_data *data; + struct fuse_vnode_data *fvdat = VTOFUD(vp); uint64_t biosize; - off_t filesize; + off_t fsize; daddr_t lbn = ap->a_bn; daddr_t *pbn = ap->a_bnp; int *runp = ap->a_runp; @@ -550,10 +551,21 @@ fuse_vnop_bmap(struct vop_bmap_args *ap) */ if (runb != NULL) *runb = MIN(lbn, maxrun); - if (runp != NULL) { - error = fuse_vnode_size(vp, &filesize, td->td_ucred, td); + if (runp != NULL && maxrun == 0) + *runp = 0; + else if (runp != NULL) { + /* + * If the file's size is cached, use that value to calculate + * runp, even if the cache is expired. runp is only advisory, + * and the risk of getting it wrong is not worth the cost of + * another upcall. + */ + if (fvdat->cached_attrs.va_size != VNOVAL) + fsize = fvdat->cached_attrs.va_size; + else + error = fuse_vnode_size(vp, &fsize, td->td_ucred, td); if (error == 0) - *runp = MIN(MAX(0, filesize / (off_t)biosize - lbn - 1), + *runp = MIN(MAX(0, fsize / (off_t)biosize - lbn - 1), maxrun); else *runp = 0; |