Skip to content

Commit 59eab10

Browse files
authored
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
1 parent 1d901c3 commit 59eab10

File tree

1 file changed

+8
-0
lines changed

1 file changed

+8
-0
lines changed

module/zfs/zfs_vnops.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ zfs_read(struct znode *zp, zfs_uio_t *uio, int ioflag, cred_t *cr)
254254
}
255255

256256
ASSERT(zfs_uio_offset(uio) < zp->z_size);
257+
ssize_t start_offset = zfs_uio_offset(uio);
257258
ssize_t n = MIN(zfs_uio_resid(uio), zp->z_size - zfs_uio_offset(uio));
258259
ssize_t start_resid = n;
259260

@@ -276,6 +277,13 @@ zfs_read(struct znode *zp, zfs_uio_t *uio, int ioflag, cred_t *cr)
276277
/* convert checksum errors into IO errors */
277278
if (error == ECKSUM)
278279
error = SET_ERROR(EIO);
280+
/*
281+
* if we actually read some bytes, bubbling EFAULT
282+
* up to become EAGAIN isn't what we want here.
283+
*/
284+
if (error == EFAULT &&
285+
(zfs_uio_offset(uio) - start_offset) != 0)
286+
error = 0;
279287
break;
280288
}
281289

0 commit comments

Comments
 (0)