Skip to content

Commit

Permalink
Handle partial reads in zfs_read
Browse files Browse the repository at this point in the history
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 <behlendorf1@llnl.gov>
Signed-off-by: Rich Ercolani <rincebrain@gmail.com>
Closes openzfs#12370 
Closes openzfs#12509
Closes openzfs#12516
  • Loading branch information
rincebrain committed Sep 20, 2021
1 parent 1d901c3 commit 59eab10
Showing 1 changed file with 8 additions and 0 deletions.
8 changes: 8 additions & 0 deletions module/zfs/zfs_vnops.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,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;

Expand All @@ -276,6 +277,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;
}

Expand Down

0 comments on commit 59eab10

Please sign in to comment.