[PATCH v4 30/30] CHANGES
David Howells
dhowells at redhat.com
Tue Jun 16 03:08:19 PDT 2026
---
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 &&
More information about the linux-afs
mailing list