Unverified Commit cd0277ed authored by David Howells's avatar David Howells Committed by Christian Brauner
Browse files

netfs: Use new folio_queue data type and iterator instead of xarray iter



Make the netfs write-side routines use the new folio_queue struct to hold a
rolling buffer of folios, with the issuer adding folios at the tail and the
collector removing them from the head as they're processed instead of using
an xarray.

This will allow a subsequent patch to simplify the write collector.

The primary mark (as tested by folioq_is_marked()) is used to note if the
corresponding folio needs putting.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
cc: Jeff Layton <jlayton@kernel.org>
cc: netfs@lists.linux.dev
cc: linux-fsdevel@vger.kernel.org
Link: https://lore.kernel.org/r/20240814203850.2240469-16-dhowells@redhat.com/

 # v2
Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent c45ebd63
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@

#include <linux/slab.h>
#include <linux/seq_file.h>
#include <linux/folio_queue.h>
#include <linux/netfs.h>
#include <linux/fscache.h>
#include <linux/fscache-cache.h>
@@ -64,6 +65,10 @@ static inline void netfs_proc_del_rreq(struct netfs_io_request *rreq) {}
/*
 * misc.c
 */
int netfs_buffer_append_folio(struct netfs_io_request *rreq, struct folio *folio,
			      bool needs_put);
struct folio_queue *netfs_delete_buffer_head(struct netfs_io_request *wreq);
void netfs_clear_buffer(struct netfs_io_request *rreq);

/*
 * objects.c
@@ -120,6 +125,7 @@ extern atomic_t netfs_n_wh_write_done;
extern atomic_t netfs_n_wh_write_failed;
extern atomic_t netfs_n_wb_lock_skip;
extern atomic_t netfs_n_wb_lock_wait;
extern atomic_t netfs_n_folioq;

int netfs_stats_show(struct seq_file *m, void *v);

@@ -153,7 +159,8 @@ struct netfs_io_request *netfs_create_write_req(struct address_space *mapping,
						loff_t start,
						enum netfs_io_origin origin);
void netfs_reissue_write(struct netfs_io_stream *stream,
			 struct netfs_io_subrequest *subreq);
			 struct netfs_io_subrequest *subreq,
			 struct iov_iter *source);
int netfs_advance_write(struct netfs_io_request *wreq,
			struct netfs_io_stream *stream,
			loff_t start, size_t len, bool to_eof);
+76 −0
Original line number Diff line number Diff line
@@ -8,6 +8,82 @@
#include <linux/swap.h>
#include "internal.h"

/*
 * Append a folio to the rolling queue.
 */
int netfs_buffer_append_folio(struct netfs_io_request *rreq, struct folio *folio,
			      bool needs_put)
{
	struct folio_queue *tail = rreq->buffer_tail;
	unsigned int slot, order = folio_order(folio);

	if (WARN_ON_ONCE(!rreq->buffer && tail) ||
	    WARN_ON_ONCE(rreq->buffer && !tail))
		return -EIO;

	if (!tail || folioq_full(tail)) {
		tail = kmalloc(sizeof(*tail), GFP_NOFS);
		if (!tail)
			return -ENOMEM;
		netfs_stat(&netfs_n_folioq);
		folioq_init(tail);
		tail->prev = rreq->buffer_tail;
		if (tail->prev)
			tail->prev->next = tail;
		rreq->buffer_tail = tail;
		if (!rreq->buffer) {
			rreq->buffer = tail;
			iov_iter_folio_queue(&rreq->io_iter, ITER_SOURCE, tail, 0, 0, 0);
		}
		rreq->buffer_tail_slot = 0;
	}

	rreq->io_iter.count += PAGE_SIZE << order;

	slot = folioq_append(tail, folio);
	/* Store the counter after setting the slot. */
	smp_store_release(&rreq->buffer_tail_slot, slot);
	return 0;
}

/*
 * Delete the head of a rolling queue.
 */
struct folio_queue *netfs_delete_buffer_head(struct netfs_io_request *wreq)
{
	struct folio_queue *head = wreq->buffer, *next = head->next;

	if (next)
		next->prev = NULL;
	netfs_stat_d(&netfs_n_folioq);
	kfree(head);
	wreq->buffer = next;
	return next;
}

/*
 * Clear out a rolling queue.
 */
void netfs_clear_buffer(struct netfs_io_request *rreq)
{
	struct folio_queue *p;

	while ((p = rreq->buffer)) {
		rreq->buffer = p->next;
		for (int slot = 0; slot < folioq_nr_slots(p); slot++) {
			struct folio *folio = folioq_folio(p, slot);
			if (!folio)
				continue;
			if (folioq_is_marked(p, slot)) {
				trace_netfs_folio(folio, netfs_folio_trace_put);
				folio_put(folio);
			}
		}
		netfs_stat_d(&netfs_n_folioq);
		kfree(p);
	}
}

/**
 * netfs_dirty_folio - Mark folio dirty and pin a cache object for writeback
 * @mapping: The mapping the folio belongs to.
+1 −0
Original line number Diff line number Diff line
@@ -141,6 +141,7 @@ static void netfs_free_request(struct work_struct *work)
		}
		kvfree(rreq->direct_bv);
	}
	netfs_clear_buffer(rreq);

	if (atomic_dec_and_test(&ictx->io_count))
		wake_up_var(&ictx->io_count);
+3 −1
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ atomic_t netfs_n_wh_write_done;
atomic_t netfs_n_wh_write_failed;
atomic_t netfs_n_wb_lock_skip;
atomic_t netfs_n_wb_lock_wait;
atomic_t netfs_n_folioq;

int netfs_stats_show(struct seq_file *m, void *v)
{
@@ -76,9 +77,10 @@ int netfs_stats_show(struct seq_file *m, void *v)
		   atomic_read(&netfs_n_wh_write),
		   atomic_read(&netfs_n_wh_write_done),
		   atomic_read(&netfs_n_wh_write_failed));
	seq_printf(m, "Objs   : rr=%u sr=%u wsc=%u\n",
	seq_printf(m, "Objs   : rr=%u sr=%u foq=%u wsc=%u\n",
		   atomic_read(&netfs_n_rh_rreq),
		   atomic_read(&netfs_n_rh_sreq),
		   atomic_read(&netfs_n_folioq),
		   atomic_read(&netfs_n_wh_wstream_conflict));
	seq_printf(m, "WbLock : skip=%u wait=%u\n",
		   atomic_read(&netfs_n_wb_lock_skip),
+44 −40
Original line number Diff line number Diff line
@@ -81,42 +81,6 @@ int netfs_folio_written_back(struct folio *folio)
	return gcount;
}

/*
 * Get hold of a folio we have under writeback.  We don't want to get the
 * refcount on it.
 */
static struct folio *netfs_writeback_lookup_folio(struct netfs_io_request *wreq, loff_t pos)
{
	XA_STATE(xas, &wreq->mapping->i_pages, pos / PAGE_SIZE);
	struct folio *folio;

	rcu_read_lock();

	for (;;) {
		xas_reset(&xas);
		folio = xas_load(&xas);
		if (xas_retry(&xas, folio))
			continue;

		if (!folio || xa_is_value(folio))
			kdebug("R=%08x: folio %lx (%llx) not present",
			       wreq->debug_id, xas.xa_index, pos / PAGE_SIZE);
		BUG_ON(!folio || xa_is_value(folio));

		if (folio == xas_reload(&xas))
			break;
	}

	rcu_read_unlock();

	if (WARN_ONCE(!folio_test_writeback(folio),
		      "R=%08x: folio %lx is not under writeback\n",
		      wreq->debug_id, folio->index)) {
		trace_netfs_folio(folio, netfs_folio_trace_not_under_wback);
	}
	return folio;
}

/*
 * Unlock any folios we've finished with.
 */
@@ -124,13 +88,25 @@ static void netfs_writeback_unlock_folios(struct netfs_io_request *wreq,
					  unsigned long long collected_to,
					  unsigned int *notes)
{
	struct folio_queue *folioq = wreq->buffer;
	unsigned int slot = wreq->buffer_head_slot;

	if (slot >= folioq_nr_slots(folioq)) {
		folioq = netfs_delete_buffer_head(wreq);
		slot = 0;
	}

	for (;;) {
		struct folio *folio;
		struct netfs_folio *finfo;
		unsigned long long fpos, fend;
		size_t fsize, flen;

		folio = netfs_writeback_lookup_folio(wreq, wreq->cleaned_to);
		folio = folioq_folio(folioq, slot);
		if (WARN_ONCE(!folio_test_writeback(folio),
			      "R=%08x: folio %lx is not under writeback\n",
			      wreq->debug_id, folio->index))
			trace_netfs_folio(folio, netfs_folio_trace_not_under_wback);

		fpos = folio_pos(folio);
		fsize = folio_size(folio);
@@ -155,9 +131,25 @@ static void netfs_writeback_unlock_folios(struct netfs_io_request *wreq,
		wreq->cleaned_to = fpos + fsize;
		*notes |= MADE_PROGRESS;

		/* Clean up the head folioq.  If we clear an entire folioq, then
		 * we can get rid of it provided it's not also the tail folioq
		 * being filled by the issuer.
		 */
		folioq_clear(folioq, slot);
		slot++;
		if (slot >= folioq_nr_slots(folioq)) {
			if (READ_ONCE(wreq->buffer_tail) == folioq)
				break;
			folioq = netfs_delete_buffer_head(wreq);
			slot = 0;
		}

		if (fpos + fsize >= collected_to)
			break;
	}

	wreq->buffer = folioq;
	wreq->buffer_head_slot = slot;
}

/*
@@ -188,9 +180,12 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq,
			if (test_bit(NETFS_SREQ_FAILED, &subreq->flags))
				break;
			if (__test_and_clear_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags)) {
				struct iov_iter source = subreq->io_iter;

				iov_iter_revert(&source, subreq->len - source.count);
				__set_bit(NETFS_SREQ_RETRYING, &subreq->flags);
				netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit);
				netfs_reissue_write(stream, subreq);
				netfs_reissue_write(stream, subreq, &source);
			}
		}
		return;
@@ -200,6 +195,7 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq,

	do {
		struct netfs_io_subrequest *subreq = NULL, *from, *to, *tmp;
		struct iov_iter source;
		unsigned long long start, len;
		size_t part;
		bool boundary = false;
@@ -227,6 +223,14 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq,
			len += to->len;
		}

		/* Determine the set of buffers we're going to use.  Each
		 * subreq gets a subset of a single overall contiguous buffer.
		 */
		source = from->io_iter;
		iov_iter_revert(&source, subreq->len - source.count);
		iov_iter_advance(&source, from->transferred);
		source.count = len;

		/* Work through the sublist. */
		subreq = from;
		list_for_each_entry_from(subreq, &stream->subrequests, rreq_link) {
@@ -249,7 +253,7 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq,
				boundary = true;

			netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit);
			netfs_reissue_write(stream, subreq);
			netfs_reissue_write(stream, subreq, &source);
			if (subreq == to)
				break;
		}
@@ -316,7 +320,7 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq,
				boundary = false;
			}

			netfs_reissue_write(stream, subreq);
			netfs_reissue_write(stream, subreq, &source);
			if (!len)
				break;

Loading