[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, &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 &&




More information about the linux-afs mailing list