aboutsummaryrefslogtreecommitdiffstats
path: root/module/os
diff options
context:
space:
mode:
authorMark Johnston <[email protected]>2020-06-30 15:54:42 -0400
committerBrian Behlendorf <[email protected]>2020-07-06 11:53:57 -0700
commitcd32b4f5b79c97b293f7be3fe9ddfc9024f7d734 (patch)
tree3b2eb31b6c5b0dc21d3848a194e50fe277d4f493 /module/os
parent6e0056171234b84450af2afbb6594bd3b09422b5 (diff)
Fix a deadlock in the FreeBSD getpages VOP
FreeBSD has a per-page "busy" lock which is held when handling a page fault on a mapped file. This lock is also acquired when copying data from the DMU to the page cache in zfs_write(). File range locks are also acquired in both of these paths, in the opposite order with respect to the busy lock. In the getpages VOP, the range lock is only used to determine the extent of optional read-ahead and read-behind operations. To resolve the lock order reversal, modify the getpages VOP to avoid blocking on the range lock. Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Ryan Moeller <[email protected]> Signed-off-by: Mark Johnston <[email protected]> Closes #10519
Diffstat (limited to 'module/os')
-rw-r--r--module/os/freebsd/zfs/zfs_vnops.c13
1 files changed, 12 insertions, 1 deletions
diff --git a/module/os/freebsd/zfs/zfs_vnops.c b/module/os/freebsd/zfs/zfs_vnops.c
index 6698e3655..1f1c0cb93 100644
--- a/module/os/freebsd/zfs/zfs_vnops.c
+++ b/module/os/freebsd/zfs/zfs_vnops.c
@@ -4809,9 +4809,20 @@ zfs_getpages(struct vnode *vp, vm_page_t *ma, int count, int *rbehind,
*/
for (;;) {
blksz = zp->z_blksz;
- lr = zfs_rangelock_enter(&zp->z_rangelock,
+ lr = zfs_rangelock_tryenter(&zp->z_rangelock,
rounddown(start, blksz),
roundup(end, blksz) - rounddown(start, blksz), RL_READER);
+ if (lr == NULL) {
+ if (rahead != NULL) {
+ *rahead = 0;
+ rahead = NULL;
+ }
+ if (rbehind != NULL) {
+ *rbehind = 0;
+ rbehind = NULL;
+ }
+ break;
+ }
if (blksz == zp->z_blksz)
break;
zfs_rangelock_exit(lr);