summaryrefslogtreecommitdiff
path: root/fs/nfs
diff options
context:
space:
mode:
authorMike Snitzer <snitzer@kernel.org>2025-10-27 09:08:33 -0400
committerAnna Schumaker <anna.schumaker@oracle.com>2025-11-10 10:32:28 -0500
commitd0497dd27452c79a48414df813a16cd12d274b3b (patch)
treeb89c2a31f0cf8c93f673f207de1967f386ae3e6e /fs/nfs
parentf2060bdc21d70f3d8a4753a9fd3b0b02cb48c0bc (diff)
nfs/localio: backfill missing partial read support for misaligned DIO
Misaligned DIO read can be split into 3 IOs, must handle potential for short read from each component IO (follows same pattern used for handling partial writes, except upper layer read code handles advancing offset before retry). Fixes: c817248fc831 ("nfs/localio: add proper O_DIRECT support for READ and WRITE") Signed-off-by: Mike Snitzer <snitzer@kernel.org> Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/localio.c24
1 files changed, 20 insertions, 4 deletions
diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
index 647fa19b0479..9c205f8b5e59 100644
--- a/fs/nfs/localio.c
+++ b/fs/nfs/localio.c
@@ -414,7 +414,7 @@ nfs_local_iters_setup_dio(struct nfs_local_kiocb *iocb, int rw,
/* Setup misaligned end?
* If so, the end is purposely setup to be issued using buffered IO
* before the middle (which will use DIO, if DIO-aligned, with AIO).
- * This creates problems if/when the end results in a partial write.
+ * This creates problems if/when the end results in short read or write.
* So must save index and length of end to handle this corner case.
*/
if (local_dio->end_len) {
@@ -580,8 +580,9 @@ static void nfs_local_read_done(struct nfs_local_kiocb *iocb)
*/
hdr->res.replen = 0;
- if (hdr->res.count != hdr->args.count ||
- hdr->args.offset + hdr->res.count >= i_size_read(file_inode(filp)))
+ /* nfs_readpage_result() handles short read */
+
+ if (hdr->args.offset + hdr->res.count >= i_size_read(file_inode(filp)))
hdr->res.eof = true;
dprintk("%s: read %ld bytes eof %d.\n", __func__,
@@ -620,6 +621,7 @@ static void nfs_local_call_read(struct work_struct *work)
container_of(work, struct nfs_local_kiocb, work);
struct file *filp = iocb->kiocb.ki_filp;
const struct cred *save_cred;
+ bool force_done = false;
ssize_t status;
int n_iters;
@@ -637,7 +639,21 @@ static void nfs_local_call_read(struct work_struct *work)
iocb->kiocb.ki_pos = iocb->offset[i];
status = filp->f_op->read_iter(&iocb->kiocb, &iocb->iters[i]);
if (status != -EIOCBQUEUED) {
- if (nfs_local_pgio_done(iocb, status, false)) {
+ if (unlikely(status >= 0 && status < iocb->iters[i].count)) {
+ /* partial read */
+ if (i == iocb->end_iter_index) {
+ /* Must not account DIO partial end, otherwise (due
+ * to end being issued before middle): the partial
+ * read accounting in nfs_local_read_done()
+ * would incorrectly advance hdr->args.offset
+ */
+ status = 0;
+ } else {
+ /* Partial read at start or middle, force done */
+ force_done = true;
+ }
+ }
+ if (nfs_local_pgio_done(iocb, status, force_done)) {
nfs_local_read_iocb_done(iocb);
break;
}