--- 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, ¶ms); - if (error < 0) + if (error < 0) { + writeback_iter_cancel(mapping, wbc, folio, &error, false); break; + } error = netfs_issue_streams(wreq, ¶ms); - if (error < 0) + if (error < 0) { + writeback_iter_cancel(mapping, wbc, folio, &error, true); break; + } bvecq_pos_step(¶ms.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 &&