]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
NFS/localio: Stop further I/O upon hitting an error
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Fri, 2 Jan 2026 21:01:06 +0000 (16:01 -0500)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Wed, 7 Jan 2026 17:28:26 +0000 (12:28 -0500)
If the call into the filesystem results in an I/O error, then the next
chunk of data won't be contiguous with the end of the last successful
chunk. So break out of the I/O loop and report the results.
Currently the localio code will do this for a short read/write, but not
for an error.

Fixes: 6a218b9c3183 ("nfs/localio: do not issue misaligned DIO out-of-order")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Reviewed-by: Mike Snitzer <snitzer@kernel.org>
fs/nfs/localio.c

index a113bfdacfd61ea6dd6b8760c9394466eff850fb..c884245e8fb87643847a82972fdfdcb689ede989 100644 (file)
@@ -618,7 +618,6 @@ static void nfs_local_call_read(struct work_struct *work)
        struct nfs_local_kiocb *iocb =
                container_of(work, struct nfs_local_kiocb, work);
        struct file *filp = iocb->kiocb.ki_filp;
-       bool force_done = false;
        ssize_t status;
        int n_iters;
 
@@ -637,13 +636,13 @@ static void nfs_local_call_read(struct work_struct *work)
                scoped_with_creds(filp->f_cred)
                        status = filp->f_op->read_iter(&iocb->kiocb, &iocb->iters[i]);
 
-               if (status != -EIOCBQUEUED) {
-                       if (unlikely(status >= 0 && status < iocb->iters[i].count))
-                               force_done = true; /* Partial read */
-                       if (nfs_local_pgio_done(iocb, status, force_done)) {
-                               nfs_local_read_iocb_done(iocb);
-                               break;
-                       }
+               if (status == -EIOCBQUEUED)
+                       continue;
+               /* Break on completion, errors, or short reads */
+               if (nfs_local_pgio_done(iocb, status, false) || status < 0 ||
+                   (size_t)status < iov_iter_count(&iocb->iters[i])) {
+                       nfs_local_read_iocb_done(iocb);
+                       break;
                }
        }
 }
@@ -821,7 +820,6 @@ static void nfs_local_call_write(struct work_struct *work)
                container_of(work, struct nfs_local_kiocb, work);
        struct file *filp = iocb->kiocb.ki_filp;
        unsigned long old_flags = current->flags;
-       bool force_done = false;
        ssize_t status;
        int n_iters;
 
@@ -843,13 +841,13 @@ static void nfs_local_call_write(struct work_struct *work)
                scoped_with_creds(filp->f_cred)
                        status = filp->f_op->write_iter(&iocb->kiocb, &iocb->iters[i]);
 
-               if (status != -EIOCBQUEUED) {
-                       if (unlikely(status >= 0 && status < iocb->iters[i].count))
-                               force_done = true; /* Partial write */
-                       if (nfs_local_pgio_done(iocb, status, force_done)) {
-                               nfs_local_write_iocb_done(iocb);
-                               break;
-                       }
+               if (status == -EIOCBQUEUED)
+                       continue;
+               /* Break on completion, errors, or short writes */
+               if (nfs_local_pgio_done(iocb, status, false) || status < 0 ||
+                   (size_t)status < iov_iter_count(&iocb->iters[i])) {
+                       nfs_local_write_iocb_done(iocb);
+                       break;
                }
        }
        file_end_write(filp);