From: David Howells <dhowells@redhat.com>
To: Christian Brauner <christian@brauner.io>
Cc: David Howells <dhowells@redhat.com>,
Paulo Alcantara <pc@manguebit.org>,
Matthew Wilcox <willy@infradead.org>,
Namjae Jeon <linkinjeon@kernel.org>,
Marc Dionne <marc.dionne@auristor.com>,
Stefan Metzmacher <metze@samba.org>,
Eric Van Hensbergen <ericvh@kernel.org>,
Dominique Martinet <asmadeus@codewreck.org>,
Ilya Dryomov <idryomov@gmail.com>,
netfs@lists.linux.dev, linux-afs@lists.infradead.org,
linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org,
ceph-devel@vger.kernel.org, v9fs@lists.linux.dev,
linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
Shyam Prasad N <sprasad@microsoft.com>,
Tom Talpey <tom@talpey.com>,
Christoph Hellwig <hch@infradead.org>
Subject: [PATCH v13 07/10] netfs: Switch folioq to bvecq
Date: Tue, 29 Sep 2026 11:34:07 +0100 [thread overview]
Message-ID: <20260929103412.2807494-8-dhowells@redhat.com> (raw)
In-Reply-To: <20260929103412.2807494-1-dhowells@redhat.com>
In netfslib, perform more or less a straight switch from using folio_queue
to using bvecq to hold the lists of folios involved in various sorts of
buffered read and in buffered writeback.
It's not quite a straight swap, however, because:
(1) The bveq_alloc_*() routines want to know if the callers is doing
writeback for emergency pool use.
(2) folio_queue includes a fixed capacity folio_batch, but bvecq has a
variable size bio_vec array.
(3) folio_queue has a set of per-folio marks that are mostly unused; the
one exception is for netfs_prefetch_for_write() - but the marked folio
is then ignored because no_unlock_folio points to it (and the bvecq
mem cleanup type can handle that anyway). This will become an issue
if/when netfs_prefetch_for_write() expands the read to cope with large
cache granularity.
(4) The bvecq API doesn't have functions to get a folio's length or to
clear a folio pointer by slot, instead accessing the bio_vec array
directly. Also bvecq stores the folio length in bv_len instead of
storing the folio orders.
(5) rolling_buffer_bulk_load_from_ra() needs to work a bit differently as
the folio_queue contains a folio_batch and bvecq doesn't.
(6) rolling_buffer_delete_spent() has to clear the bvecq->next pointer
before putting the bvecq to avoid rolling up the entire list. On the
other hand, rolling_buffer_clear() just needs to put the tail.
Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Paulo Alcantara <pc@manguebit.org>
cc: Matthew Wilcox <willy@infradead.org>
cc: Namjae Jeon <linkinjeon@kernel.org>
cc: Shyam Prasad N <sprasad@microsoft.com>
cc: Tom Talpey <tom@talpey.com>
cc: Christoph Hellwig <hch@infradead.org>
cc: linux-cifs@vger.kernel.org
cc: netfs@lists.linux.dev
cc: linux-fsdevel@vger.kernel.org
---
fs/netfs/buffered_read.c | 34 ++++----
fs/netfs/iterator.c | 33 ++++----
fs/netfs/read_collect.c | 47 ++++++-----
fs/netfs/read_pgpriv2.c | 24 +++---
fs/netfs/read_retry.c | 29 +++----
fs/netfs/rolling_buffer.c | 143 ++++++++++++++-------------------
fs/netfs/write_collect.c | 26 +++---
fs/netfs/write_issue.c | 16 ++--
include/linux/rolling_buffer.h | 42 ++++------
include/trace/events/netfs.h | 2 +-
10 files changed, 181 insertions(+), 215 deletions(-)
diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c
index e30bde80276a..aa1e4f8d46ab 100644
--- a/fs/netfs/buffered_read.c
+++ b/fs/netfs/buffered_read.c
@@ -215,7 +215,7 @@ static void netfs_issue_read(struct netfs_io_request *rreq,
* otherwise we set the deprecated PG_private_2.
*/
static void netfs_mark_copy_to_cache(struct netfs_io_request *rreq,
- struct folio_queue **fq,
+ struct bvecq **bq,
unsigned int *offset,
int *slot,
size_t len,
@@ -225,21 +225,21 @@ static void netfs_mark_copy_to_cache(struct netfs_io_request *rreq,
struct folio *folio;
size_t fsize, overlap;
- if (!*fq)
+ if (!*bq)
break;
- if (*slot >= folioq_count(*fq)) {
- *fq = (*fq)->next;
+ if (!bvecq_acquire_slot(*bq, *slot)) {
+ *bq = bvecq_next(*bq);
*slot = 0;
*offset = 0;
continue;
}
/* Determine how much the subreq overlaps the folio, if at all. */
- fsize = folioq_folio_size(*fq, *slot);
+ fsize = (*bq)->bv[*slot].bv_len;
overlap = min(len, fsize - *offset);
if (overlap > 0 && copy) {
- folio = folioq_folio(*fq, *slot);
+ folio = bvec_folio(&(*bq)->bv[*slot]);
if (netfs_using_pgpriv2(rreq)) {
if (!folio_test_private_2(folio))
folio_start_private_2(folio);
@@ -275,7 +275,7 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq)
.cached_to[1] = ULLONG_MAX,
};
struct fscache_occupancy *occ = &_occ;
- struct folio_queue *fq = rreq->buffer.tail;
+ struct bvecq *bq = rreq->buffer.tail;
unsigned int offset = 0;
ssize_t size = rreq->len;
uoff_t start = rreq->start;
@@ -408,10 +408,10 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq)
if (size <= 0)
netfs_all_subreqs_queued(rreq);
- if (fq) {
+ if (bq) {
/* See if the cache indicated this should be cached. */
copy = test_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags);
- netfs_mark_copy_to_cache(rreq, &fq, &slot, &offset, slice, copy);
+ netfs_mark_copy_to_cache(rreq, &bq, &slot, &offset, slice, copy);
}
trace_netfs_sreq(subreq, netfs_sreq_trace_submit);
@@ -479,8 +479,7 @@ void netfs_readahead(struct readahead_control *ractl)
* acquires a ref on each folio that we will need to release later -
* but we don't want to do that until after we've started the I/O.
*/
- added = rolling_buffer_bulk_load_from_ra(&rreq->buffer, ractl,
- rreq->debug_id, rreq->gfp);
+ added = rolling_buffer_bulk_load_from_ra(&rreq->buffer, ractl, rreq->gfp);
if (added < 0) {
ret = added;
goto cleanup_free;
@@ -503,15 +502,14 @@ EXPORT_SYMBOL(netfs_readahead);
/*
* Create a rolling buffer with a single occupying folio.
*/
-static int netfs_create_singular_buffer(struct netfs_io_request *rreq, struct folio *folio,
- unsigned int rollbuf_flags)
+static int netfs_create_singular_buffer(struct netfs_io_request *rreq, struct folio *folio)
{
ssize_t added;
- if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST, rreq->gfp) < 0)
+ if (rolling_buffer_init(&rreq->buffer, ITER_DEST, rreq->gfp, false) < 0)
return -ENOMEM;
- added = rolling_buffer_append(&rreq->buffer, folio, rollbuf_flags, rreq->gfp);
+ added = rolling_buffer_append(&rreq->buffer, folio, rreq->gfp);
if (added < 0)
return added;
rreq->submitted = rreq->start + added;
@@ -661,7 +659,7 @@ int netfs_read_folio(struct file *file, struct folio *folio)
trace_netfs_read(rreq, rreq->start, rreq->len, netfs_read_trace_readpage);
/* Set up the output buffer */
- ret = netfs_create_singular_buffer(rreq, folio, 0);
+ ret = netfs_create_singular_buffer(rreq, folio);
if (ret < 0)
goto discard;
@@ -818,7 +816,7 @@ int netfs_write_begin(struct netfs_inode *ctx,
trace_netfs_read(rreq, pos, len, netfs_read_trace_write_begin);
/* Set up the output buffer */
- ret = netfs_create_singular_buffer(rreq, folio, 0);
+ ret = netfs_create_singular_buffer(rreq, folio);
if (ret < 0)
goto error_put;
@@ -883,7 +881,7 @@ int netfs_prefetch_for_write(struct file *file, struct folio *folio,
trace_netfs_read(rreq, start, flen, netfs_read_trace_prefetch_for_write);
/* Set up the output buffer */
- ret = netfs_create_singular_buffer(rreq, folio, NETFS_ROLLBUF_PAGECACHE_MARK);
+ ret = netfs_create_singular_buffer(rreq, folio);
if (ret < 0)
goto error_put;
diff --git a/fs/netfs/iterator.c b/fs/netfs/iterator.c
index eb1efb17f53a..31748526d568 100644
--- a/fs/netfs/iterator.c
+++ b/fs/netfs/iterator.c
@@ -245,33 +245,34 @@ static size_t netfs_limit_xarray(const struct iov_iter *iter, size_t start_offse
}
/*
- * Select the span of a folio queue iterator we're going to use. Limit it by
- * both maximum size and maximum number of segments. Returns the size of the
- * span in bytes.
+ * Select the span of a bvecq iterator we're going to use. Limit it by both
+ * maximum size and maximum number of segments. Returns the size of the span
+ * in bytes.
*/
-static size_t netfs_limit_folioq(const struct iov_iter *iter, size_t start_offset,
- size_t max_size, size_t max_segs)
+static size_t netfs_limit_bvecq(const struct iov_iter *iter, size_t start_offset,
+ size_t max_size, size_t max_segs)
{
- const struct folio_queue *folioq = iter->folioq;
+ const struct bvecq *bq = iter->bvecq;
unsigned int nsegs = 0;
- unsigned int slot = iter->folioq_slot;
+ unsigned int slot = iter->bvecq_slot;
size_t span = 0, n = iter->count;
- if (WARN_ON(!iov_iter_is_folioq(iter)) ||
+ if (WARN_ON(!iov_iter_is_bvecq(iter)) ||
WARN_ON(start_offset > n) ||
n == 0)
return 0;
max_size = umin(max_size, n - start_offset);
- if (slot >= folioq_nr_slots(folioq)) {
- folioq = folioq->next;
+ if (!bvecq_acquire_slot(bq, slot)) {
+ bq = bvecq_next(bq);
slot = 0;
}
start_offset += iter->iov_offset;
do {
- size_t flen = folioq_folio_size(folioq, slot);
+ size_t flen;
+ flen = bq->bv[slot].bv_len;
if (start_offset < flen) {
span += flen - start_offset;
nsegs++;
@@ -283,11 +284,11 @@ static size_t netfs_limit_folioq(const struct iov_iter *iter, size_t start_offse
break;
slot++;
- if (slot >= folioq_nr_slots(folioq)) {
- folioq = folioq->next;
+ if (!bvecq_acquire_slot(bq, slot)) {
+ bq = bvecq_next(bq);
slot = 0;
}
- } while (folioq);
+ } while (bq);
return umin(span, max_size);
}
@@ -295,8 +296,8 @@ static size_t netfs_limit_folioq(const struct iov_iter *iter, size_t start_offse
size_t netfs_limit_iter(const struct iov_iter *iter, size_t start_offset,
size_t max_size, size_t max_segs)
{
- if (iov_iter_is_folioq(iter))
- return netfs_limit_folioq(iter, start_offset, max_size, max_segs);
+ if (iov_iter_is_bvecq(iter))
+ return netfs_limit_bvecq(iter, start_offset, max_size, max_segs);
if (iov_iter_is_bvec(iter))
return netfs_limit_bvec(iter, start_offset, max_size, max_segs);
if (iov_iter_is_xarray(iter))
diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c
index 2625efd48a9b..6bbaaea69354 100644
--- a/fs/netfs/read_collect.c
+++ b/fs/netfs/read_collect.c
@@ -63,11 +63,11 @@ void netfs_cancel_copy_to_cache(struct netfs_io_request *rreq, struct folio *fol
* dirty and let writeback handle it.
*/
static void netfs_unlock_read_folio(struct netfs_io_request *rreq,
- struct folio_queue *folioq,
+ struct bvecq *bq,
int slot)
{
struct netfs_folio *finfo;
- struct folio *folio = folioq_folio(folioq, slot);
+ struct folio *folio = bvec_folio(&bq->bv[slot]);
if (unlikely(folio_pos(folio) < rreq->abandon_to)) {
trace_netfs_folio(folio, netfs_folio_trace_abandon);
@@ -98,7 +98,7 @@ static void netfs_unlock_read_folio(struct netfs_io_request *rreq,
trace_netfs_folio(folio, netfs_folio_trace_read_done);
}
- folioq_clear(folioq, slot);
+ bq->bv[slot].bv_page = NULL;
} else {
// TODO: Use of PG_private_2 is deprecated.
if (folio_test_private_2(folio))
@@ -114,7 +114,7 @@ static void netfs_unlock_read_folio(struct netfs_io_request *rreq,
folio_unlock(folio);
}
- folioq_clear(folioq, slot);
+ bq->bv[slot].bv_page = NULL;
}
/*
@@ -122,21 +122,21 @@ static void netfs_unlock_read_folio(struct netfs_io_request *rreq,
*/
void netfs_read_set_unlock_at(struct netfs_io_request *rreq)
{
- struct folio_queue *folioq = rreq->buffer.tail;
+ const struct bvecq *bq = rreq->buffer.tail;
unsigned int slot = rreq->buffer.first_tail_slot;
size_t cleaned_to = rreq->cleaned_to - rreq->start;
size_t progress_at = cleaned_to;
size_t minimum = 256 * 1024;
while (progress_at < rreq->len) {
- if (slot >= folioq_count(folioq)) {
- folioq = folioq->next;
- if (!folioq)
+ if (!bvecq_acquire_slot(bq, slot)) {
+ bq = bvecq_next(bq);
+ if (!bq)
break;
slot = 0;
}
- progress_at += folioq_folio_size(folioq, slot);
+ progress_at += bq->bv[slot].bv_len;
if (progress_at - cleaned_to >= minimum)
break;
slot++;
@@ -152,7 +152,7 @@ void netfs_read_set_unlock_at(struct netfs_io_request *rreq)
static void netfs_read_unlock_folios(struct netfs_io_request *rreq,
unsigned int *notes)
{
- struct folio_queue *folioq = rreq->buffer.tail;
+ struct bvecq *bq = rreq->buffer.tail;
unsigned int slot = rreq->buffer.first_tail_slot;
uoff_t collected_to = rreq->collected_to;
@@ -161,9 +161,9 @@ static void netfs_read_unlock_folios(struct netfs_io_request *rreq,
// TODO: Begin decryption
- if (slot >= folioq_nr_slots(folioq)) {
- folioq = rolling_buffer_delete_spent(&rreq->buffer);
- if (!folioq) {
+ if (!bvecq_acquire_slot(bq, slot)) {
+ bq = rolling_buffer_delete_spent(&rreq->buffer);
+ if (!bq) {
WRITE_ONCE(rreq->progress_at, rreq->len);
return;
}
@@ -182,13 +182,13 @@ static void netfs_read_unlock_folios(struct netfs_io_request *rreq,
uoff_t fpos, fend;
size_t fsize;
- folio = folioq_folio(folioq, slot);
+ folio = bvec_folio(&bq->bv[slot]);
if (WARN_ONCE(!folio_test_locked(folio),
"R=%08x: folio %lx is not locked\n",
rreq->debug_id, folio->index))
trace_netfs_folio(folio, netfs_folio_trace_not_locked);
- fsize = folioq_folio_size(folioq, slot);
+ fsize = bq->bv[slot].bv_len;
fpos = folio_pos(folio);
fend = fpos + fsize;
@@ -198,29 +198,28 @@ static void netfs_read_unlock_folios(struct netfs_io_request *rreq,
if (collected_to < fend)
break;
- netfs_unlock_read_folio(rreq, folioq, slot);
+ netfs_unlock_read_folio(rreq, bq, slot);
WRITE_ONCE(rreq->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
+ /* Clean up the head bq. If we clear an entire bq, then
+ * we can get rid of it provided it's not also the tail bq
* being filled by the issuer.
*/
- folioq_clear(folioq, slot);
+ bq->bv[slot].bv_page = NULL;
slot++;
- if (slot >= folioq_nr_slots(folioq)) {
- folioq = rolling_buffer_delete_spent(&rreq->buffer);
- if (!folioq)
+ if (!bvecq_acquire_slot(bq, slot)) {
+ bq = rolling_buffer_delete_spent(&rreq->buffer);
+ if (!bq)
goto done;
slot = 0;
- trace_netfs_folioq(folioq, netfs_trace_folioq_read_progress);
}
if (fpos + fsize >= collected_to)
break;
}
- rreq->buffer.tail = folioq;
+ rreq->buffer.tail = bq;
done:
rreq->buffer.first_tail_slot = slot;
diff --git a/fs/netfs/read_pgpriv2.c b/fs/netfs/read_pgpriv2.c
index 5280b606fda4..16d623ca9df3 100644
--- a/fs/netfs/read_pgpriv2.c
+++ b/fs/netfs/read_pgpriv2.c
@@ -53,7 +53,7 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_request *creq, struct folio
trace_netfs_folio(folio, netfs_folio_trace_store_copy);
/* Attach the folio to the rolling buffer. */
- if (rolling_buffer_append(&creq->buffer, folio, 0, creq->gfp) < 0) {
+ if (rolling_buffer_append(&creq->buffer, folio, creq->gfp) < 0) {
set_bit(NETFS_RREQ_CANCEL_CACHING, &creq->flags);
folio_end_private_2(folio);
return;
@@ -173,13 +173,13 @@ void netfs_pgpriv2_end_copy_to_cache(struct netfs_io_request *rreq)
*/
bool netfs_pgpriv2_unlock_copied_folios(struct netfs_io_request *creq)
{
- struct folio_queue *folioq = creq->buffer.tail;
+ struct bvecq *bq = creq->buffer.tail;
unsigned int slot = creq->buffer.first_tail_slot;
uoff_t collected_to = creq->collected_to;
bool made_progress = false;
- if (slot >= folioq_nr_slots(folioq)) {
- folioq = rolling_buffer_delete_spent(&creq->buffer);
+ if (!bvecq_acquire_slot(bq, slot)) {
+ bq = rolling_buffer_delete_spent(&creq->buffer);
slot = 0;
}
@@ -188,7 +188,7 @@ bool netfs_pgpriv2_unlock_copied_folios(struct netfs_io_request *creq)
uoff_t fpos, fend;
size_t fsize, flen;
- folio = folioq_folio(folioq, slot);
+ folio = bvec_folio(&bq->bv[slot]);
if (WARN_ONCE(!folio_test_private_2(folio),
"R=%08x: folio %lx is not marked private_2\n",
creq->debug_id, folio->index))
@@ -211,15 +211,15 @@ bool netfs_pgpriv2_unlock_copied_folios(struct netfs_io_request *creq)
creq->cleaned_to = fpos + fsize;
made_progress = true;
- /* 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
+ /* Clean up the head bq. If we clear an entire bq, then
+ * we can get rid of it provided it's not also the tail bq
* being filled by the issuer.
*/
- folioq_clear(folioq, slot);
+ bq->bv[slot].bv_page = NULL;
slot++;
- if (slot >= folioq_nr_slots(folioq)) {
- folioq = rolling_buffer_delete_spent(&creq->buffer);
- if (!folioq)
+ if (!bvecq_acquire_slot(bq, slot)) {
+ bq = rolling_buffer_delete_spent(&creq->buffer);
+ if (!bq)
goto done;
slot = 0;
}
@@ -228,7 +228,7 @@ bool netfs_pgpriv2_unlock_copied_folios(struct netfs_io_request *creq)
break;
}
- creq->buffer.tail = folioq;
+ creq->buffer.tail = bq;
done:
creq->buffer.first_tail_slot = slot;
return made_progress;
diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c
index 5bd8dee5a834..142c3fb8dab1 100644
--- a/fs/netfs/read_retry.c
+++ b/fs/netfs/read_retry.c
@@ -291,7 +291,7 @@ void netfs_retry_reads(struct netfs_io_request *rreq)
*/
void netfs_unlock_abandoned_read_pages(struct netfs_io_request *rreq)
{
- struct folio_queue *p;
+ struct bvecq *p;
/* We have to wait for readahead refs to have been released before we
* can unlock any folios as the ref-dropper walks i_pages and the only
@@ -301,24 +301,25 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io_request *rreq)
netfs_wait_for_put_ra_refs(rreq);
for (p = rreq->buffer.tail; p; p = p->next) {
- for (int slot = 0; slot < folioq_count(p); slot++) {
- struct folio *folio = folioq_folio(p, slot);
+ for (int slot = rreq->buffer.first_tail_slot;
+ bvecq_acquire_slot(p, slot);
+ slot++) {
+ struct folio *folio;
- if (!folio)
+ if (!p->bv[slot].bv_page)
continue;
+
+ folio = bvec_folio(&p->bv[slot]);
netfs_cancel_copy_to_cache(rreq, folio);
- if (!folioq_is_marked2(p, slot)) {
- if (folio == rreq->no_unlock_folio &&
- test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO,
- &rreq->flags)) {
- _debug("no unlock");
- } else {
- trace_netfs_folio(folio,
- netfs_folio_trace_abandon);
- folio_unlock(folio);
- }
+ if (folio == rreq->no_unlock_folio &&
+ test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags)) {
+ _debug("no unlock");
+ } else {
+ trace_netfs_folio(folio, netfs_folio_trace_abandon);
+ folio_unlock(folio);
}
}
+ rreq->buffer.first_tail_slot = 0;
}
}
diff --git a/fs/netfs/rolling_buffer.c b/fs/netfs/rolling_buffer.c
index d30d5ef6d86e..76429fbb6920 100644
--- a/fs/netfs/rolling_buffer.c
+++ b/fs/netfs/rolling_buffer.c
@@ -63,45 +63,46 @@ EXPORT_SYMBOL(netfs_folioq_free);
* that the pointers can be independently driven by the producer and the
* consumer.
*/
-int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id,
- unsigned int direction, gfp_t gfp)
+int rolling_buffer_init(struct rolling_buffer *roll, unsigned int direction,
+ gfp_t gfp, bool for_writeback)
{
- struct folio_queue *fq;
+ struct bvecq *bq;
+
+ roll->for_writeback = for_writeback;
- fq = netfs_folioq_alloc(rreq_id, gfp, netfs_trace_folioq_rollbuf_init);
- if (!fq)
+ bq = bvecq_alloc_one(BVECQ_POOL_SLOTS, gfp, for_writeback);
+ if (!bq)
return -ENOMEM;
- roll->head = fq;
- roll->tail = fq;
- iov_iter_folio_queue(&roll->iter, direction, fq, 0, 0, 0);
+ roll->head = bq;
+ roll->tail = bq;
+ iov_iter_bvec_queue(&roll->iter, direction, bq, 0, 0, 0);
return 0;
}
/*
- * Add another folio_queue to a rolling buffer if there's no space left.
+ * Add another bvecq to a rolling buffer if there's no space left.
*/
int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp)
{
- struct folio_queue *fq, *head = roll->head;
+ struct bvecq *bq, *head = roll->head;
- if (!folioq_full(head))
+ if (!bvecq_is_full(head))
return 0;
- fq = netfs_folioq_alloc(head->rreq_id, gfp, netfs_trace_folioq_make_space);
- if (!fq)
+ bq = bvecq_alloc_one(BVECQ_POOL_SLOTS, gfp, roll->for_writeback);
+ if (!bq)
return -ENOMEM;
- fq->prev = head;
- roll->head = fq;
- if (folioq_full(head)) {
+ roll->head = bq;
+ if (bvecq_is_full(head)) {
/* Make sure we don't leave the master iterator pointing to a
* block that might get immediately consumed.
*/
- if (roll->iter.folioq == head &&
- roll->iter.folioq_slot == folioq_nr_slots(head)) {
- roll->iter.folioq = fq;
- roll->iter.folioq_slot = 0;
+ if (roll->iter.bvecq == head &&
+ roll->iter.bvecq_slot == head->nr_slots) {
+ roll->iter.bvecq = bq;
+ roll->iter.bvecq_slot = 0;
}
}
@@ -110,7 +111,7 @@ int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp)
* [!] NOTE: After we set head->next, the consumer is at liberty to
* immediately delete the old head.
*/
- smp_store_release(&head->next, fq);
+ bvecq_append(head, bq);
return 0;
}
@@ -119,55 +120,59 @@ int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp)
*/
ssize_t rolling_buffer_bulk_load_from_ra(struct rolling_buffer *roll,
struct readahead_control *ractl,
- unsigned int rreq_id, gfp_t gfp)
+ gfp_t gfp)
{
- struct folio_queue *fq;
- ssize_t loaded = 0;
+ struct bvecq *bq;
+ size_t loaded = 0;
while (ractl->_nr_pages - ractl->_batch_count > 0) {
+ struct page **pages;
unsigned int nr;
- /* Allocate a folioq to put some folios into and attach it to
+ /* Allocate a bvecq to put some folios into and attach it to
* the rolling buffer.
*/
- fq = netfs_folioq_alloc(rreq_id, gfp,
- netfs_trace_folioq_make_space);
- if (!fq)
+ bq = bvecq_alloc_one(BVECQ_POOL_SLOTS, gfp, false);
+ if (!bq)
goto nomem_unlock;
- fq->prev = roll->head;
+ bq->mem_type = BVECQ_MEM_EXTERNAL; /* Folio cleanup handled separately. */
+
if (!roll->tail)
- roll->tail = fq;
+ roll->tail = bq;
else
- roll->head->next = fq;
- roll->head = fq;
+ bvecq_append(roll->head, bq);
+ roll->head = bq;
- /* Get a batch of folios and note their orders. */
- nr = __readahead_batch(ractl, (struct page **)fq->vec.folios,
- folioq_nr_slots(fq));
+ /* Get a bunch of folios and note their sizes. */
+ pages = (struct page **)(bq->bv + bq->max_slots);
+ pages -= bq->max_slots;
+ nr = __readahead_batch(ractl, pages, bq->max_slots);
if (WARN_ON_ONCE(!nr))
break;
- fq->vec.nr = nr;
for (int slot = 0; slot < nr; slot++) {
- struct folio *folio = folioq_folio(fq, slot);
- unsigned int order;
+ struct folio *folio = page_folio(pages[slot]);
+ size_t len = folio_size(folio);
- order = folio_order(folio);
- fq->orders[slot] = order;
- loaded += PAGE_SIZE << order;
+ bvec_set_folio(&bq->bv[slot], folio, len, 0);
+ loaded += len;
trace_netfs_folio(folio, netfs_folio_trace_read);
}
+
+ bvecq_filled_to(bq, nr);
}
WRITE_ONCE(roll->iter.count, loaded);
- iov_iter_folio_queue(&roll->iter, ITER_DEST, roll->tail, 0, 0, loaded);
+ iov_iter_bvec_queue(&roll->iter, ITER_DEST, roll->tail, 0, 0, loaded);
return loaded;
nomem_unlock:
- for (fq = roll->tail; fq; fq = fq->next) {
- for (int slot = 0; slot < folioq_count(fq); slot++) {
- folio_unlock(fq->vec.folios[slot]);
- folioq_mark(fq, slot);
+ for (bq = roll->tail; bq; bq = bq->next) {
+ for (int slot = 0; slot < bq->nr_slots; slot++) {
+ struct folio *folio = bvec_folio(&bq->bv[slot]);
+
+ folio_unlock(folio);
+ folio_put(folio);
}
}
rolling_buffer_clear(roll);
@@ -180,7 +185,7 @@ ssize_t rolling_buffer_bulk_load_from_ra(struct rolling_buffer *roll,
* Append a folio to the rolling buffer.
*/
ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio,
- unsigned int flags, gfp_t gfp)
+ gfp_t gfp)
{
ssize_t size = folio_size(folio);
int slot;
@@ -188,16 +193,11 @@ ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio,
if (rolling_buffer_make_space(roll, gfp) < 0)
return -ENOMEM;
- slot = folioq_append(roll->head, folio);
- if (flags & ROLLBUF_MARK_1)
- folioq_mark(roll->head, slot);
- if (flags & ROLLBUF_MARK_2)
- folioq_mark2(roll->head, slot);
+ slot = roll->head->nr_slots;
+ bvec_set_folio(&roll->head->bv[slot], folio, size, 0);
+ bvecq_filled_to(roll->head, slot + 1);
WRITE_ONCE(roll->iter.count, roll->iter.count + size);
-
- /* Store the counter after setting the slot. */
- smp_store_release(&roll->next_head_slot, slot);
return size;
}
@@ -206,44 +206,23 @@ ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio,
* don't return the last buffer to keep the pointers independent, but return
* NULL instead.
*/
-struct folio_queue *rolling_buffer_delete_spent(struct rolling_buffer *roll)
+struct bvecq *rolling_buffer_delete_spent(struct rolling_buffer *roll)
{
- struct folio_queue *spent = roll->tail, *next = READ_ONCE(spent->next);
+ struct bvecq *spent = roll->tail, *next = bvecq_next(spent);
if (!next)
return NULL;
next->prev = NULL;
- netfs_folioq_free(spent, netfs_trace_folioq_delete);
roll->tail = next;
+ spent->next = NULL;
+ bvecq_put(spent);
return next;
}
/*
- * Clear out a rolling queue. Folios that have mark 1 set are put.
+ * Clear out a rolling queue.
*/
void rolling_buffer_clear(struct rolling_buffer *roll)
{
- struct folio_batch fbatch;
- struct folio_queue *p;
-
- folio_batch_init(&fbatch);
-
- while ((p = roll->tail)) {
- roll->tail = p->next;
- for (int slot = 0; slot < folioq_count(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);
- if (!folio_batch_add(&fbatch, folio))
- folio_batch_release(&fbatch);
- }
- }
-
- netfs_folioq_free(p, netfs_trace_folioq_clear);
- }
-
- folio_batch_release(&fbatch);
+ bvecq_put(roll->tail);
}
diff --git a/fs/netfs/write_collect.c b/fs/netfs/write_collect.c
index 6e8ea534230d..80e6a3194a60 100644
--- a/fs/netfs/write_collect.c
+++ b/fs/netfs/write_collect.c
@@ -114,11 +114,11 @@ int netfs_folio_written_back(struct folio *folio)
static void netfs_writeback_unlock_folios(struct netfs_io_request *wreq,
unsigned int *notes)
{
- struct folio_queue *folioq = wreq->buffer.tail;
+ struct bvecq *bq = wreq->buffer.tail;
unsigned int slot = wreq->buffer.first_tail_slot;
uoff_t collected_to = wreq->collected_to;
- if (WARN_ON_ONCE(!folioq)) {
+ if (WARN_ON_ONCE(!bq)) {
pr_err("[!] Writeback unlock found empty rolling buffer!\n");
netfs_dump_request(wreq);
return;
@@ -130,9 +130,9 @@ static void netfs_writeback_unlock_folios(struct netfs_io_request *wreq,
return;
}
- if (slot >= folioq_nr_slots(folioq)) {
- folioq = rolling_buffer_delete_spent(&wreq->buffer);
- if (!folioq)
+ if (!bvecq_acquire_slot(bq, slot)) {
+ bq = rolling_buffer_delete_spent(&wreq->buffer);
+ if (!bq)
return;
slot = 0;
}
@@ -143,7 +143,7 @@ static void netfs_writeback_unlock_folios(struct netfs_io_request *wreq,
uoff_t fpos, fend;
size_t fsize, flen;
- folio = folioq_folio(folioq, slot);
+ folio = bvec_folio(&bq->bv[slot]);
if (WARN_ONCE(!folio_test_writeback(folio),
"R=%08x: folio %lx is not under writeback\n",
wreq->debug_id, folio->index))
@@ -166,15 +166,15 @@ 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
+ /* Clean up the head bq. If we clear an entire bq, then
+ * we can get rid of it provided it's not also the tail bq
* being filled by the issuer.
*/
- folioq_clear(folioq, slot);
+ bq->bv[slot].bv_page = NULL;
slot++;
- if (slot >= folioq_nr_slots(folioq)) {
- folioq = rolling_buffer_delete_spent(&wreq->buffer);
- if (!folioq)
+ if (!bvecq_acquire_slot(bq, slot)) {
+ bq = rolling_buffer_delete_spent(&wreq->buffer);
+ if (!bq)
goto done;
slot = 0;
}
@@ -183,7 +183,7 @@ static void netfs_writeback_unlock_folios(struct netfs_io_request *wreq,
break;
}
- wreq->buffer.tail = folioq;
+ wreq->buffer.tail = bq;
done:
wreq->buffer.first_tail_slot = slot;
}
diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c
index 121f28268cf0..9de73c0edad2 100644
--- a/fs/netfs/write_issue.c
+++ b/fs/netfs/write_issue.c
@@ -107,7 +107,9 @@ struct netfs_io_request *netfs_create_write_req(struct address_space *mapping,
ictx = netfs_inode(wreq->inode);
if (is_cacheable)
fscache_begin_write_operation(&wreq->cache_resources, netfs_i_cookie(ictx));
- if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE, wreq->gfp) < 0)
+ if (rolling_buffer_init(&wreq->buffer, ITER_SOURCE, wreq->gfp,
+ (origin == NETFS_WRITEBACK ||
+ origin == NETFS_WRITEBACK_SINGLE)) < 0)
goto nomem;
wreq->cleaned_to = wreq->start;
@@ -162,12 +164,12 @@ void netfs_prepare_write(struct netfs_io_request *wreq,
struct netfs_io_subrequest *subreq;
struct iov_iter *wreq_iter = &wreq->buffer.iter;
- /* Make sure we don't point the iterator at a used-up folio_queue
- * struct being used as a placeholder to prevent the queue from
- * collapsing. In such a case, extend the queue.
+ /* Make sure we don't point the iterator at a used-up bvecq struct
+ * being used as a placeholder to prevent the queue from collapsing.
+ * In such a case, extend the queue.
*/
- if (iov_iter_is_folioq(wreq_iter) &&
- wreq_iter->folioq_slot >= folioq_nr_slots(wreq_iter->folioq))
+ if (iov_iter_is_bvecq(wreq_iter) &&
+ !bvecq_acquire_slot(wreq_iter->bvecq, wreq_iter->bvecq_slot))
rolling_buffer_make_space(&wreq->buffer, wreq->gfp);
subreq = netfs_alloc_subrequest(wreq, stream->source);
@@ -450,7 +452,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq,
}
/* Attach the folio to the rolling buffer. */
- rolling_buffer_append(&wreq->buffer, folio, 0, wreq->gfp);
+ rolling_buffer_append(&wreq->buffer, folio, wreq->gfp);
/* Move the submission point forward to allow for write-streaming data
* not starting at the front of the page. We don't do write-streaming
diff --git a/include/linux/rolling_buffer.h b/include/linux/rolling_buffer.h
index a97f7cfaacaa..5c0bc4221f01 100644
--- a/include/linux/rolling_buffer.h
+++ b/include/linux/rolling_buffer.h
@@ -8,49 +8,35 @@
#ifndef _ROLLING_BUFFER_H
#define _ROLLING_BUFFER_H
-#include <linux/folio_queue.h>
+#include <linux/bvecq.h>
#include <linux/uio.h>
/*
- * Rolling buffer. Whilst the buffer is live and in use, folios and folio
- * queue segments can be added to one end by one thread and removed from the
- * other end by another thread. The buffer isn't allowed to be empty; it must
- * always have at least one folio_queue in it so that neither side has to
- * modify both queue pointers.
+ * Rolling buffer. Whilst the buffer is live and in use, folios and bvecq
+ * segments can be added to one end by one thread and removed from the other
+ * end by another thread. The buffer isn't allowed to be empty; it must always
+ * have at least one bvecq in it so that neither side has to modify both queue
+ * pointers.
*
* The iterator in the buffer is extended as buffers are inserted. It can be
* snapshotted to use a segment of the buffer.
*/
struct rolling_buffer {
- struct folio_queue *head; /* Producer's insertion point */
- struct folio_queue *tail; /* Consumer's removal point */
+ struct bvecq *head; /* Producer's insertion point */
+ struct bvecq *tail; /* Consumer's removal point */
struct iov_iter iter; /* Iterator tracking what's left in the buffer */
- u8 next_head_slot; /* Next slot in ->head */
u8 first_tail_slot; /* First slot in ->tail */
+ bool for_writeback; /* T if being used for writeback */
};
-/*
- * Snapshot of a rolling buffer.
- */
-struct rolling_buffer_snapshot {
- struct folio_queue *curr_folioq; /* Queue segment in which current folio resides */
- unsigned char curr_slot; /* Folio currently being read */
- unsigned char curr_order; /* Order of folio */
-};
-
-/* Marks to store per-folio in the internal folio_queue structs. */
-#define ROLLBUF_MARK_1 BIT(0)
-#define ROLLBUF_MARK_2 BIT(1)
-
-int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id,
- unsigned int direction, gfp_t gfp);
+int rolling_buffer_init(struct rolling_buffer *roll, unsigned int direction,
+ gfp_t gfp, bool for_writeback);
int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp);
ssize_t rolling_buffer_bulk_load_from_ra(struct rolling_buffer *roll,
struct readahead_control *ractl,
- unsigned int rreq_id, gfp_t gfp);
-ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio,
- unsigned int flags, gfp_t gfp);
-struct folio_queue *rolling_buffer_delete_spent(struct rolling_buffer *roll);
+ gfp_t gfp);
+ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio, gfp_t gfp);
+struct bvecq *rolling_buffer_delete_spent(struct rolling_buffer *roll);
void rolling_buffer_clear(struct rolling_buffer *roll);
static inline void rolling_buffer_advance(struct rolling_buffer *roll, size_t amount)
diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h
index bf1e1f185b05..41f7f0733bfa 100644
--- a/include/trace/events/netfs.h
+++ b/include/trace/events/netfs.h
@@ -398,7 +398,7 @@ TRACE_EVENT(netfs_sreq,
__entry->len = sreq->len;
__entry->transferred = sreq->transferred;
__entry->start = sreq->start;
- __entry->slot = sreq->io_iter.folioq_slot;
+ __entry->slot = sreq->io_iter.bvecq_slot;
),
TP_printk("R=%08x[%x] %s %s f=%03x s=%llx %zx/%zx s=%u e=%d",
next prev parent reply other threads:[~2026-09-29 10:35 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-29 10:34 [PATCH v13 00/10] netfs, iov_iter: Use a chain of bio_vec arrays instead of folio_queue David Howells
2026-09-29 10:34 ` [PATCH v13 01/10] Add a function to kmap one page of a multipage bio_vec David Howells
2026-09-29 10:34 ` [PATCH v13 02/10] iov_iter: Add a segmented queue of bio_vec[] David Howells
2026-09-29 10:34 ` [PATCH v13 03/10] netfs: Add some tools for managing bvecq chains David Howells
2026-09-29 10:34 ` [PATCH v13 04/10] afs: Use a bvecq to hold dir content rather than folioq David Howells
2026-09-29 10:34 ` [PATCH v13 05/10] cifs: Use a bvecq for buffering instead of a folioq David Howells
2026-09-29 10:34 ` [PATCH v13 06/10] smbdirect: Support ITER_BVECQ in smbdirect_map_sges_from_iter() David Howells
2026-09-29 10:34 ` David Howells [this message]
2026-09-29 10:34 ` [PATCH v13 08/10] smbdirect: Remove support for ITER_FOLIOQ from smbdirect_map_sges_from_iter() David Howells
2026-09-29 10:34 ` [PATCH v13 09/10] iov_iter: Remove ITER_FOLIOQ David Howells
2026-09-29 10:34 ` [PATCH v13 10/10] netfs: Remove folio_queue David Howells
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260929103412.2807494-8-dhowells@redhat.com \
--to=dhowells@redhat.com \
--cc=asmadeus@codewreck.org \
--cc=ceph-devel@vger.kernel.org \
--cc=christian@brauner.io \
--cc=ericvh@kernel.org \
--cc=hch@infradead.org \
--cc=idryomov@gmail.com \
--cc=linkinjeon@kernel.org \
--cc=linux-afs@lists.infradead.org \
--cc=linux-cifs@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=marc.dionne@auristor.com \
--cc=metze@samba.org \
--cc=netfs@lists.linux.dev \
--cc=pc@manguebit.org \
--cc=sprasad@microsoft.com \
--cc=tom@talpey.com \
--cc=v9fs@lists.linux.dev \
--cc=willy@infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®