summaryrefslogtreecommitdiffstats
path: root/module/zfs
diff options
context:
space:
mode:
authorRich Ercolani <[email protected]>2021-09-20 13:30:50 -0400
committerBrian Behlendorf <[email protected]>2022-08-02 10:05:14 -0700
commit5c56591b57bd4540cdab4cd02d7c07f8544ca967 (patch)
treeca5a52148979bdb4b25279e7585d44f5e86c89d0 /module/zfs
parent17512aba0c33f73b74e5bd10f11e6d41c10f709f (diff)
Handle partial reads in zfs_read
Currently, dmu_read_uio_dnode can read 64K of a requested 1M in one loop, get EFAULT back from zfs_uiomove() (because the iovec only holds 64k), and return EFAULT, which turns into EAGAIN on the way out. EAGAIN gets interpreted as "I didn't read anything", the caller tries again without consuming the 64k we already read, and we're stuck. This apparently works on newer kernels because the caller which breaks on older Linux kernels by happily passing along a 1M read request and a 64k iovec just requests 64k at a time. With this, we now won't return EFAULT if we got a partial read. Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Rich Ercolani <[email protected]> Closes #12370 Closes #12509 Closes #12516
Diffstat (limited to 'module/zfs')
-rw-r--r--module/zfs/zfs_vnops.c8
1 files changed, 8 insertions, 0 deletions
diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c
index 1b32d3876..b9e93791d 100644
--- a/module/zfs/zfs_vnops.c
+++ b/module/zfs/zfs_vnops.c
@@ -261,6 +261,7 @@ zfs_read(struct znode *zp, zfs_uio_t *uio, int ioflag, cred_t *cr)
}
ASSERT(zfs_uio_offset(uio) < zp->z_size);
+ ssize_t start_offset = zfs_uio_offset(uio);
ssize_t n = MIN(zfs_uio_resid(uio), zp->z_size - zfs_uio_offset(uio));
ssize_t start_resid = n;
@@ -283,6 +284,13 @@ zfs_read(struct znode *zp, zfs_uio_t *uio, int ioflag, cred_t *cr)
/* convert checksum errors into IO errors */
if (error == ECKSUM)
error = SET_ERROR(EIO);
+ /*
+ * if we actually read some bytes, bubbling EFAULT
+ * up to become EAGAIN isn't what we want here.
+ */
+ if (error == EFAULT &&
+ (zfs_uio_offset(uio) - start_offset) != 0)
+ error = 0;
break;
}