netfs: Combine prepare and issue ops
Modify the way subrequests are generated in netfslib to simplify the way
things work. Currently, netfslib creates a subrequest and then calls
->prepare_xxx() to prepare it and to enquire about how big it can be and to
which source it sends a request (server or cache), then it progressively
loads up the buffer and some time later finally issues it.
This delay between prepare and issue, however, presents a couple of issues:
firstly, it may hold server resources for a significant length of time
(e.g. cifs SMB2+ credits), blocking other users; and secondly it holds
memory or other local resources that could otherwise be used to perform
some other task under low-memory conditions.
Fix this by accumulating buffers to be involved in the I/O and then calling
->issue_xxx() to do all the preparation work and issuing, including calling
back into netfslib to grab a slice of the so-far accumulated buffers.
For buffered reads, instead of ->prepare_read() setting limits on the
amount of bufferage to be accumulated, all read requests (including
readahead) build the buffer list up front and then just go through
dispatching slices of it. Each subreq bites off the slice it can handle.
In a previous patch, the cache acquired a ->query_occupancy() method that
allows netfs to find out where extents of holes or data are rather than
sending each subreq to the cache first.
For unbuffered/DIO read and writes, as for buffered reads, the buffer list
is built up front and then slices of it are dispatched.
For buffered writeback, ->estimate_write() is used to set a need-flush-by
point for each stream, and then buffers are added into the list until
enough is accumulated to flush a stream or a discontiguity is reached - at
which point ->issue_write() is called and the estimate refreshed repeatedly
until the remaining amount of accumulated buffers is less than the
estimate.
To this end, netfslib is changed in the following ways:
(1) ->prepare_xxx(), buffer selection and ->issue_xxx() are now collapsed
together such that one ->issue_xxx() call is made with the subrequest
defined to the maximum extent; the filesystem/cache then reduces the
length of the subrequest and calls back to netfslib to grab a slice of
the buffer, which may reduce the subrequest further if a maximum
segment limit is set. The filesystem/cache then dispatches the
operation.
(2) Retry buffer tracking is added to the netfs_io_request struct. This
is then selected by the subrequest retry counter being non-zero.
(3) The use of iov_iter is pushed down to the filesystem. Netfslib now
provides the filesystem with a bvecq holding the buffer rather than an
iov_iter. The bvecq can be duplicated and headers/trailers attached
to hold protocol and several duplicated bvecqs can be linked together
to create a compound operation.
(4) If the ->issue_xxx() functions terminate with -ENOMEM, a flag is set
on the request to abort further subrequest generation/retrying. If
it fails early with any other error, generally the subreq will be
extended as much as possible and failed with the error returned.
(5) During writeback, netfslib now builds up an accumulation of buffered
data before issuing writes on each stream (one server, one cache). It
asks each stream for an estimate of how much data to accumulate before
it next generates subrequests on the stream. The filesystem or cache
is not required to use up all the data accumulated on a stream at that
time unless the end of the pagecache is hit.
(6) During read-gaps, in which there are two gaps on either end of a dirty
streaming write page that need to be filled, a buffer is constructed
consisting of the two ends plus a sink page repeated to cover the
middle portion. This is passed to the server as a single write. For
something like Ceph, this should probably be done either as a
vectored/sparse read or as two separate reads (if different Ceph
objects are involved).
(7) During unbuffered/DIO read/write, there is a single contiguous file
region to be read or written as a single stream. The dispatching
function just creates subrequests and calls ->issue_xxx() repeatedly
to eat through the bufferage.
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Paulo Alcantara <pc@manguebit.org>
cc: Matthew Wilcox <willy@infradead.org>
cc: netfs@lists.linux.dev
cc: linux-fsdevel@vger.kernel.org
32 files changed