[PATCH v4 30/30] CHANGES

David Howells <[email protected]>
Newsgroups gmane.linux.file-systems,gmane.linux.kernel.cifs,gmane.linux.nfs,gmane.comp.file-systems.ceph.devel,gmane.linux.kernel
Message-ID <[email protected]>
---
 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 &&
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.