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 v17 14/17] netfs: Switch folioq to bvecq
Date: Wed, 11 Nov 2026 20:30:27 +0000 [thread overview]
Message-ID: <20261111203034.2126314-15-dhowells@redhat.com> (raw)
In-Reply-To: <20261111203034.2126314-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.
(7) The bvecq chain needs to be pinned by the subrequests to prevent the
retry reverting the iterator and hitting a spent link in the chain
that has been removed. (Note that the intention is to remove the
iterator from the netfs_io_subrequest struct and push the use of
iterators down into the filesystem in a later patchset).
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 | 36 ++++-----
fs/netfs/iterator.c | 42 +++++-----
fs/netfs/objects.c | 1 +
fs/netfs/read_collect.c | 48 +++++------
fs/netfs/read_pgpriv2.c | 26 +++---
fs/netfs/read_retry.c | 53 ++++++++----
fs/netfs/rolling_buffer.c | 144 ++++++++++++++-------------------
fs/netfs/write_collect.c | 26 +++---
fs/netfs/write_issue.c | 26 ++++--
fs/netfs/write_retry.c | 15 +++-
include/linux/netfs.h | 1 +
include/linux/rolling_buffer.h | 42 ++++------
include/trace/events/netfs.h | 2 +-
13 files changed, 239 insertions(+), 223 deletions(-)
diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c
index 69e68ef0652a..c6c850ffd56e 100644
--- a/fs/netfs/buffered_read.c
+++ b/fs/netfs/buffered_read.c
@@ -131,6 +131,8 @@ static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq)
}
subreq->io_iter = rreq->buffer.iter;
+ if (iov_iter_is_bvecq(&subreq->io_iter))
+ subreq->bvecq_pin = bvecq_get((struct bvecq *)subreq->io_iter.bvecq);
iov_iter_truncate(&subreq->io_iter, subreq->len);
rolling_buffer_advance(&rreq->buffer, subreq->len);
@@ -215,7 +217,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 +227,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 +277,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 +410,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 +481,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 +504,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;
@@ -668,7 +668,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;
@@ -825,7 +825,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;
@@ -890,7 +890,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..983d3e45c698 100644
--- a/fs/netfs/iterator.c
+++ b/fs/netfs/iterator.c
@@ -245,33 +245,37 @@ 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;
- slot = 0;
- }
-
start_offset += iter->iov_offset;
do {
- size_t flen = folioq_folio_size(folioq, slot);
+ size_t flen;
+ if (!bvecq_acquire_slot(bq, slot)) {
+ bq = bvecq_next(bq);
+ if (!bq)
+ break;
+ slot = 0;
+ continue;
+ }
+
+ flen = bq->bv[slot].bv_len;
if (start_offset < flen) {
span += flen - start_offset;
nsegs++;
@@ -283,11 +287,7 @@ 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;
- slot = 0;
- }
- } while (folioq);
+ } while (bq);
return umin(span, max_size);
}
@@ -295,8 +295,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/objects.c b/fs/netfs/objects.c
index 9895cf662bca..0ce5d528b7fe 100644
--- a/fs/netfs/objects.c
+++ b/fs/netfs/objects.c
@@ -254,6 +254,7 @@ static void netfs_free_subrequest(struct netfs_io_subrequest *subreq)
trace_netfs_sreq(subreq, netfs_sreq_trace_free);
if (rreq->netfs_ops->free_subrequest)
rreq->netfs_ops->free_subrequest(subreq);
+ bvecq_put(subreq->bvecq_pin);
mempool_free(subreq, rreq->netfs_ops->subrequest_pool ?: &netfs_subrequest_pool);
netfs_stat_d(&netfs_n_rh_sreq);
netfs_put_request(rreq, netfs_rreq_trace_put_subreq);
diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c
index be0f9d2f7e10..75c8874ec459 100644
--- a/fs/netfs/read_collect.c
+++ b/fs/netfs/read_collect.c
@@ -79,11 +79,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);
@@ -114,7 +114,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))
@@ -130,7 +130,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;
}
/*
@@ -138,21 +138,22 @@ 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;
+ continue;
}
- progress_at += folioq_folio_size(folioq, slot);
+ progress_at += bq->bv[slot].bv_len;
if (progress_at - cleaned_to >= minimum)
break;
slot++;
@@ -168,7 +169,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;
@@ -177,9 +178,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) {
+ while (!bvecq_acquire_slot(bq, slot)) {
+ bq = rolling_buffer_delete_spent(&rreq->buffer);
+ if (!bq) {
WRITE_ONCE(rreq->progress_at, rreq->len);
return;
}
@@ -198,13 +199,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;
@@ -214,29 +215,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)
+ while (!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 85084aad9e31..037fa80037c7 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,15 @@ 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);
+ while (!bvecq_acquire_slot(bq, slot)) {
+ bq = rolling_buffer_delete_spent(&creq->buffer);
+ if (!bq)
+ return false;
slot = 0;
}
@@ -188,7 +190,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 +213,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)
+ while (!bvecq_acquire_slot(bq, slot)) {
+ bq = rolling_buffer_delete_spent(&creq->buffer);
+ if (!bq)
goto done;
slot = 0;
}
@@ -228,7 +230,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..adbb6cdedb59 100644
--- a/fs/netfs/read_retry.c
+++ b/fs/netfs/read_retry.c
@@ -12,6 +12,14 @@
static void netfs_reissue_read(struct netfs_io_request *rreq,
struct netfs_io_subrequest *subreq)
{
+ if (iov_iter_is_bvecq(&subreq->io_iter) &&
+ subreq->bvecq_pin != subreq->io_iter.bvecq) {
+ struct bvecq *old = subreq->bvecq_pin;
+
+ subreq->bvecq_pin = bvecq_get((struct bvecq *)subreq->io_iter.bvecq);
+ bvecq_put(old);
+ }
+
subreq->error = 0;
__clear_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags);
__set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags);
@@ -28,6 +36,7 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
struct netfs_io_subrequest *subreq;
struct netfs_io_stream *stream = &rreq->io_streams[0];
struct list_head *next;
+ struct bvecq *pin = NULL;
_enter("R=%x", rreq->debug_id);
@@ -79,6 +88,9 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
size_t part;
bool boundary = false, subreq_superfluous = false;
+ bvecq_put(pin);
+ pin = NULL;
+
/* Go through the subreqs and find the next span of contiguous
* buffer that we then rejig (cifs, for example, needs the
* rsize renegotiating) and reissue.
@@ -115,6 +127,13 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
_debug(" - range: %llx-%llx %llx", start, start + len - 1, len);
+ /* Pin start of the bvecq from where the first subreq in the
+ * sequence started. We will advance the subreqs' pins to
+ * their new starting points and then drop this pin.
+ */
+ if (from->bvecq_pin)
+ pin = bvecq_get(from->bvecq_pin);
+
/* Determine the set of buffers we're going to use. Each
* subreq gets a subset of a single overall contiguous buffer.
*/
@@ -245,12 +264,14 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
} while (!list_is_head(next, &stream->subrequests));
+out:
+ bvecq_put(pin);
return;
/* If we hit an error, fail all remaining incomplete subrequests */
abandon_after:
if (list_is_last(&subreq->rreq_link, &stream->subrequests))
- return;
+ goto out;
subreq = list_next_entry(subreq, rreq_link);
abandon:
list_for_each_entry_from(subreq, &stream->subrequests, rreq_link) {
@@ -261,6 +282,7 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
__set_bit(NETFS_SREQ_FAILED, &subreq->flags);
__clear_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags);
}
+ goto out;
}
/*
@@ -291,7 +313,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 +323,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 4347c99452d8..cedab0496eba 100644
--- a/fs/netfs/rolling_buffer.c
+++ b/fs/netfs/rolling_buffer.c
@@ -62,45 +62,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;
}
}
@@ -109,7 +110,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;
}
@@ -118,55 +119,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);
@@ -179,7 +184,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;
@@ -187,16 +192,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;
}
@@ -205,44 +205,22 @@ 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;
+ roll->tail = bvecq_get(next);
+ 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..91b42820c892 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)
+ while (!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)
+ while (!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 4c1e597a6d03..e7b1d5c3baa6 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;
@@ -163,12 +165,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);
@@ -177,6 +179,8 @@ void netfs_prepare_write(struct netfs_io_request *wreq,
subreq->start = start;
subreq->stream_nr = stream->stream_nr;
subreq->io_iter = *wreq_iter;
+ if (iov_iter_is_bvecq(wreq_iter))
+ subreq->bvecq_pin = bvecq_get((struct bvecq *)wreq->buffer.iter.bvecq);
_enter("R=%x[%x]", wreq->debug_id, subreq->debug_index);
@@ -267,6 +271,14 @@ void netfs_reissue_write(struct netfs_io_stream *stream,
iov_iter_advance(source, size);
iov_iter_truncate(&subreq->io_iter, size);
+ if (iov_iter_is_bvecq(&subreq->io_iter) &&
+ subreq->bvecq_pin != subreq->io_iter.bvecq) {
+ struct bvecq *old = subreq->bvecq_pin;
+
+ subreq->bvecq_pin = bvecq_get((struct bvecq *)subreq->io_iter.bvecq);
+ bvecq_put(old);
+ }
+
subreq->retry_count++;
subreq->error = 0;
__clear_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags);
@@ -451,7 +463,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/fs/netfs/write_retry.c b/fs/netfs/write_retry.c
index 2f20577563e1..81c106ef37f7 100644
--- a/fs/netfs/write_retry.c
+++ b/fs/netfs/write_retry.c
@@ -18,6 +18,7 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq,
struct netfs_io_stream *stream)
{
struct list_head *next;
+ struct bvecq *pin = NULL;
_enter("R=%x[%x:]", wreq->debug_id, stream->stream_nr);
@@ -59,6 +60,9 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq,
size_t part;
bool boundary = false;
+ bvecq_put(pin);
+ pin = NULL;
+
/* Go through the stream and find the next span of contiguous
* data that we then rejig (cifs, for example, needs the wsize
* renegotiating) and reissue.
@@ -70,7 +74,7 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq,
if (test_bit(NETFS_SREQ_FAILED, &from->flags) ||
!test_bit(NETFS_SREQ_NEED_RETRY, &from->flags))
- return;
+ break;
for (;;) {
/* Read pointer to subreq before reading subreq state. */
@@ -87,6 +91,13 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq,
len += to->len;
}
+ /* Pin start of the bvecq from where the first subreq in the
+ * sequence started. We will advance the subreqs' pins to
+ * their new starting points and then drop this pin.
+ */
+ if (from->bvecq_pin)
+ pin = bvecq_get(from->bvecq_pin);
+
/* Determine the set of buffers we're going to use. Each
* subreq gets a subset of a single overall contiguous buffer.
*/
@@ -197,6 +208,8 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq,
} while (len);
} while (!list_is_head(next, &stream->subrequests));
+
+ bvecq_put(pin);
}
/*
diff --git a/include/linux/netfs.h b/include/linux/netfs.h
index 375fd415b4e5..6f3276e8eeb0 100644
--- a/include/linux/netfs.h
+++ b/include/linux/netfs.h
@@ -188,6 +188,7 @@ struct netfs_io_subrequest {
struct netfs_io_request *rreq; /* Supervising I/O request */
struct work_struct work;
struct list_head rreq_link; /* Link in rreq->subrequests */
+ struct bvecq *bvecq_pin; /* Pin in request's bvecq chain (holds ref) */
struct iov_iter io_iter; /* Iterator for this subrequest */
uoff_t start; /* Where to start the I/O */
size_t len; /* Size of the I/O */
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-11-11 20:32 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 20:30 [PATCH v17 00/17] netfs, iov_iter: Use a chain of bio_vec arrays instead of folio_queue David Howells
2026-11-11 20:30 ` [PATCH v17 01/17] iov_iter: Fix kunit test to remove __init and __refdata marks David Howells
2026-11-11 20:30 ` [PATCH v17 02/17] Add a function to kmap one page of a multipage bio_vec David Howells
2026-11-11 20:30 ` [PATCH v17 03/17] iov_iter: Add a segmented queue of bio_vec[] David Howells
2026-11-11 20:30 ` [PATCH v17 04/17] netfs: Add some tools for managing bvecq chains David Howells
2026-11-11 20:30 ` [PATCH v17 05/17] iov_iter: Add an API document for the bvecq structure David Howells
2026-11-11 20:30 ` [PATCH v17 06/17] iov_iter: Add a bvecq kunit test David Howells
2026-11-11 20:30 ` [PATCH v17 07/17] afs: Fix an inconsistency in the symlink code David Howells
2026-11-11 20:30 ` [PATCH v17 08/17] netfs: Explicitly pass tail padding to netfs_writeback_single() David Howells
2026-11-11 20:30 ` [PATCH v17 09/17] netfs: Change netfs_writeback_single() to not look inside the iterator David Howells
2026-11-11 20:30 ` [PATCH v17 10/17] netfs: Pass the write length to request creation where appropriate David Howells
2026-11-11 20:30 ` [PATCH v17 11/17] afs: Use a bvecq to hold dir content rather than folioq David Howells
2026-11-11 20:30 ` [PATCH v17 12/17] cifs: Use a bvecq for buffering instead of a folioq David Howells
2026-11-11 20:30 ` [PATCH v17 13/17] smbdirect: Support ITER_BVECQ in smbdirect_map_sges_from_iter() David Howells
2026-11-11 20:30 ` David Howells [this message]
2026-11-11 20:30 ` [PATCH v17 15/17] smbdirect: Remove support for ITER_FOLIOQ from smbdirect_map_sges_from_iter() David Howells
2026-11-11 20:30 ` [PATCH v17 16/17] iov_iter: Remove ITER_FOLIOQ David Howells
2026-11-11 20:30 ` [PATCH v17 17/17] 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=20261111203034.2126314-15-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®