---
 fs/netfs/iterator.c    | 22 ++++++++++++++--------
 fs/netfs/read_retry.c  | 12 +++++++++---
 fs/netfs/write_issue.c | 24 ++++++++++++++++++++++--
 fs/netfs/write_retry.c | 23 ++++++++++++++---------
 fs/nfs/fscache.c       |  3 ++-
 fs/smb/client/file.c   |  2 +-
 6 files changed, 62 insertions(+), 24 deletions(-)

diff --git a/fs/netfs/iterator.c b/fs/netfs/iterator.c
index d464e1784b8a..5e8e816eeff3 100644
--- a/fs/netfs/iterator.c
+++ b/fs/netfs/iterator.c
@@ -33,8 +33,8 @@
  * the original iterator will have been advanced by the amount extracted.
  *
  * If an error occurs and no pages are extracted, an error will be returned and
- * any allocated bvecq will be freed.  The allocated bvecq will also be freed
- * if no pages are extracted, but no error is recorded.
+ * any allocated bvecq will be freed.  If there is no data to be extracted (or
+ * @max_len or @max_pages are zero), a single empty bvecq will be returned.
  *
  * The bvecq segments are marked with indications on how to get clean up the
  * extracted fragments.
@@ -43,7 +43,7 @@ ssize_t netfs_extract_iter(struct iov_iter *orig, size_t 
max_len, size_t max_pag
                           unsigned long long fpos, struct bvecq **_bvecq_head,
                           iov_iter_extraction_t extraction_flags)
 {
-       struct bvecq *bq_tail = NULL;
+       struct bvecq *bq_tail = NULL, *bq;
        ssize_t ret = 0;
        size_t extracted = 0;
 
@@ -53,15 +53,13 @@ ssize_t netfs_extract_iter(struct iov_iter *orig, size_t 
max_len, size_t max_pag
        if (max_len > orig->count)
                max_len = orig->count;
        if (WARN_ON_ONCE(!max_len || !max_pages))
-               return 0;
+               goto alloc_empty;
 
        max_pages = iov_iter_npages(orig, max_pages);
        if (!max_pages)
-               return 0;
+               goto alloc_empty;
 
        do {
-               struct bvecq *bq;
-
                bq = bvecq_alloc_one(max_pages, GFP_NOFS);
                if (!bq) {
                        ret = -ENOMEM;
@@ -142,10 +140,18 @@ ssize_t netfs_extract_iter(struct iov_iter *orig, size_t 
max_len, size_t max_pag
        } while (max_len > 0 && max_pages > 0);
 
 out:
-       if (extracted)
+       if (extracted || ret == 0)
                return extracted;
        bvecq_put(*_bvecq_head);
        *_bvecq_head = NULL;
        return ret;
+
+alloc_empty:
+       bq = bvecq_alloc_one(1, GFP_NOFS);
+       if (!bq)
+               return -ENOMEM;
+       *_bvecq_head = bq;
+       return 0;
+
 }
 EXPORT_SYMBOL_GPL(netfs_extract_iter);
diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c
index a5cd6e20cae1..0f8ff53fe703 100644
--- a/fs/netfs/read_retry.c
+++ b/fs/netfs/read_retry.c
@@ -79,11 +79,12 @@ static void netfs_retry_read_subrequests(struct 
netfs_io_request *rreq)
        if (rreq->netfs_ops->retry_request)
                rreq->netfs_ops->retry_request(rreq, NULL);
 
+       /* Read pointer to subreq before reading subreq state. */
+       next = smp_load_acquire(&stream->subrequests.next);
+
        /* Renegotiate all the download requests and flip any failed cache
         * reads over to being download requests and negotiate those also.
         */
-       next = stream->subrequests.next;
-
        do {
                struct netfs_io_subrequest *from, *to, *tmp;
                unsigned long long start;
@@ -110,7 +111,12 @@ static void netfs_retry_read_subrequests(struct 
netfs_io_request *rreq)
                        goto abandon;
                }
 
-               list_for_each_continue(next, &stream->subrequests) {
+               for (;;) {
+                       /* Read pointer to subreq before reading subreq state. 
*/
+                       next = smp_load_acquire(&next->next);
+                       if (next == &stream->subrequests)
+                               break;
+
                        subreq = list_entry(next, struct netfs_io_subrequest, 
rreq_link);
                        if (subreq->start != start + len ||
                            subreq->transferred > 0 ||
diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c
index e2e35d619119..37e5b5ee1cea 100644
--- a/fs/netfs/write_issue.c
+++ b/fs/netfs/write_issue.c
@@ -722,6 +722,20 @@ static int netfs_queue_wb_folio(struct netfs_io_request 
*wreq,
        goto out;
 }
 
+static void writeback_iter_cancel(struct address_space *mapping,
+                                 struct writeback_control *wbc,
+                                 struct folio *folio, int *error,
+                                 bool unlocked)
+{
+       do {
+               if (!unlocked) {
+                       folio_redirty_for_writepage(wbc, folio);
+                       folio_unlock(folio);
+                       unlocked = false;
+               }
+       } while ((folio = writeback_iter(mapping, wbc, folio, error)));
+}
+
 /*
  * Write some of the pending data back to the server
  */
@@ -776,11 +790,15 @@ int netfs_writepages(struct address_space *mapping,
 
                params.notes &= NOTES__KEEP_MASK;
                error = netfs_queue_wb_folio(wreq, wbc, folio, &params);
-               if (error < 0)
+               if (error < 0) {
+                       writeback_iter_cancel(mapping, wbc, folio, &error, 
false);
                        break;
+               }
                error = netfs_issue_streams(wreq, &params);
-               if (error < 0)
+               if (error < 0) {
+                       writeback_iter_cancel(mapping, wbc, folio, &error, 
true);
                        break;
+               }
 
                bvecq_pos_step(&params.dispatch_cursor);
        } while ((folio = writeback_iter(mapping, wbc, folio, &error)));
@@ -924,6 +942,7 @@ int netfs_advance_writethrough(struct netfs_writethrough 
*wthru,
        folio_put(wthru->in_progress);
        wthru->in_progress = NULL;
        wreq->submitted = wreq->len;
+       bvecq_pos_step(&wthru->params.dispatch_cursor);
        return ret;
 }
 
@@ -945,6 +964,7 @@ ssize_t netfs_end_writethrough(struct netfs_writethrough 
*wthru,
                ret = netfs_queue_wb_folio(wreq, wbc, folio, &wthru->params);
                if (ret == 0)
                        ret = netfs_issue_streams(wreq, &wthru->params);
+               bvecq_pos_step(&wthru->params.dispatch_cursor);
                folio_put(folio);
                wthru->in_progress = NULL;
                wreq->submitted = wreq->len;
diff --git a/fs/netfs/write_retry.c b/fs/netfs/write_retry.c
index e7955cc707e0..d9cc49f21346 100644
--- a/fs/netfs/write_retry.c
+++ b/fs/netfs/write_retry.c
@@ -23,7 +23,6 @@ int netfs_prepare_write_retry_buffer(struct 
netfs_io_subrequest *subreq,
                                     unsigned int max_segs)
 {
        struct netfs_io_request *wreq = subreq->rreq;
-       struct netfs_io_stream *stream = &wreq->io_streams[subreq->stream_nr];
        size_t len;
 
        bvecq_pos_set(&subreq->dispatch_pos, &wreq->retry_cursor);
@@ -35,9 +34,9 @@ int netfs_prepare_write_retry_buffer(struct 
netfs_io_subrequest *subreq,
                trace_netfs_sreq(subreq, netfs_sreq_trace_limited);
        }
 
-       stream->issue_from += len;
-       stream->buffered   -= len;
-       if (stream->buffered == 0)
+       wreq->retry_start += len;
+       wreq->retry_buffered   -= len;
+       if (wreq->retry_buffered == 0)
                bvecq_pos_unset(&wreq->retry_cursor);
        return 0;
 }
@@ -63,7 +62,8 @@ static void netfs_retry_write_stream(struct netfs_io_request 
*wreq,
        if (unlikely(stream->failed))
                return;
 
-       next = stream->subrequests.next;
+       /* Read pointer to subreq before reading subreq state. */
+       next = smp_load_acquire(&stream->subrequests.next);
 
        do {
                struct netfs_io_subrequest *subreq = NULL, *from, *to, *tmp;
@@ -84,7 +84,12 @@ static void netfs_retry_write_stream(struct netfs_io_request 
*wreq,
                    !test_bit(NETFS_SREQ_NEED_RETRY, &from->flags))
                        goto out;
 
-               list_for_each_continue(next, &stream->subrequests) {
+               for (;;) {
+                       /* Read pointer to subreq before reading subreq state. 
*/
+                       next = smp_load_acquire(&next->next);
+                       if (next == &stream->subrequests)
+                               break;
+
                        subreq = list_entry(next, struct netfs_io_subrequest, 
rreq_link);
                        if (subreq->start != start + len ||
                            subreq->transferred > 0 ||
@@ -135,7 +140,7 @@ static void netfs_retry_write_stream(struct 
netfs_io_request *wreq,
                /* If we managed to use fewer subreqs, we can discard the
                 * excess; if we used the same number, then we're done.
                 */
-               if (!len) {
+               if (!wreq->retry_buffered) {
                        if (subreq == to)
                                continue;
                        list_for_each_entry_safe_from(subreq, tmp,
@@ -158,7 +163,7 @@ static void netfs_retry_write_stream(struct 
netfs_io_request *wreq,
                        subreq = netfs_alloc_subrequest(wreq);
                        subreq->source          = to->source;
                        subreq->start           = start;
-                       subreq->len             = len;
+                       subreq->len             = wreq->retry_buffered;
                        subreq->stream_nr       = to->stream_nr;
                        subreq->retry_count     = 1;
 
@@ -188,7 +193,7 @@ static void netfs_retry_write_stream(struct 
netfs_io_request *wreq,
 
                        trace_netfs_sreq(subreq, netfs_sreq_trace_retry);
                        stream->issue_write(subreq);
-               } while (len);
+               } while (wreq->retry_buffered > 0);
 
        } while (!list_is_head(next, &stream->subrequests));
 
diff --git a/fs/nfs/fscache.c b/fs/nfs/fscache.c
index cf750faaec6a..f39a351c566d 100644
--- a/fs/nfs/fscache.c
+++ b/fs/nfs/fscache.c
@@ -324,7 +324,7 @@ static void nfs_netfs_issue_read(struct netfs_io_subrequest 
*sreq)
 
        netfs = nfs_netfs_alloc(sreq);
        if (!netfs) {
-               sreq->error = err;
+               sreq->error = -ENOMEM;
                goto term;
        }
 
@@ -343,6 +343,7 @@ static void nfs_netfs_issue_read(struct netfs_io_subrequest 
*sreq)
 out:
        nfs_pageio_complete_read(&pgio);
        nfs_netfs_put(netfs);
+       return;
 term:
        return netfs_read_subreq_terminated(sreq);
 }
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index d3a9041786ac..b770c349137a 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -222,7 +222,7 @@ static void cifs_issue_read(struct netfs_io_subrequest 
*subreq)
                        rc = cifs_reopen_file(req->cfile, true);
                } while (rc == -EAGAIN);
                if (rc)
-                       goto failed;
+                       goto fail_with_credits;
        }
 
        if (subreq->rreq->origin != NETFS_UNBUFFERED_READ &&


Reply via email to