fusefs: fix a deadlock in VOP_PUTPAGES

As of r346162 fuse now invalidates the cache during writes.  But it can't do
that when writing from VOP_PUTPAGES, because the write is coming _from_ the
cache.  Trying to invalidate the cache in that situation causes a deadlock
in vm_object_page_remove, because the pages in question have already been
busied by the same thread.

PR:		235774
Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Alan Somers 2019-04-26 19:47:43 +00:00
parent 75d5cb29cb
commit 9c7ec33162
3 changed files with 14 additions and 7 deletions

View File

@ -122,7 +122,7 @@ fuse_write_biobackend(struct vnode *vp, struct uio *uio,
SDT_PROBE_DEFINE5(fusefs, , io, io_dispatch, "struct vnode*", "struct uio*",
"int", "struct ucred*", "struct fuse_filehandle*");
int
fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag,
fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag, bool pages,
struct ucred *cred, pid_t pid)
{
struct fuse_filehandle *fufh;
@ -172,13 +172,20 @@ fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag,
* cached.
*/
if (directio || fuse_data_cache_mode == FUSE_CACHE_WT) {
const int iosize = fuse_iosize(vp);
off_t start, end;
SDT_PROBE2(fusefs, , io, trace, 1,
"direct write of vnode");
start = uio->uio_offset;
end = start + uio->uio_resid;
v_inval_buf_range(vp, start, end, fuse_iosize(vp));
/*
* Invalidate the write cache unless we're coming from
* VOP_PUTPAGES, in which case we're writing _from_ the
* write cache
*/
if (!pages )
v_inval_buf_range(vp, start, end, iosize);
err = fuse_write_directbackend(vp, uio, cred, fufh,
ioflag);
} else {

View File

@ -60,7 +60,7 @@
#ifndef _FUSE_IO_H_
#define _FUSE_IO_H_
int fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag,
int fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag, bool pages,
struct ucred *cred, pid_t pid);
int fuse_io_strategy(struct vnode *vp, struct buf *bp);
int fuse_io_flushbuf(struct vnode *vp, int waitfor, struct thread *td);

View File

@ -1222,7 +1222,7 @@ fuse_vnop_read(struct vop_read_args *ap)
ioflag |= IO_DIRECT;
}
return fuse_io_dispatch(vp, uio, ioflag, cred, pid);
return fuse_io_dispatch(vp, uio, ioflag, false, cred, pid);
}
/*
@ -1729,7 +1729,7 @@ fuse_vnop_write(struct vop_write_args *ap)
ioflag |= IO_DIRECT;
}
return fuse_io_dispatch(vp, uio, ioflag, cred, pid);
return fuse_io_dispatch(vp, uio, ioflag, false, cred, pid);
}
SDT_PROBE_DEFINE1(fusefs, , vnops, vnop_getpages_error, "int");
@ -1803,7 +1803,7 @@ fuse_vnop_getpages(struct vop_getpages_args *ap)
uio.uio_rw = UIO_READ;
uio.uio_td = td;
error = fuse_io_dispatch(vp, &uio, IO_DIRECT, cred, pid);
error = fuse_io_dispatch(vp, &uio, IO_DIRECT, true, cred, pid);
pmap_qremove(kva, npages);
uma_zfree(fuse_pbuf_zone, bp);
@ -1936,7 +1936,7 @@ fuse_vnop_putpages(struct vop_putpages_args *ap)
uio.uio_rw = UIO_WRITE;
uio.uio_td = td;
error = fuse_io_dispatch(vp, &uio, IO_DIRECT, cred, pid);
error = fuse_io_dispatch(vp, &uio, IO_DIRECT, true, cred, pid);
pmap_qremove(kva, npages);
uma_zfree(fuse_pbuf_zone, bp);