// SPDX-License-Identifier: GPL-2.0-or-later /* Network filesystem high-level buffered read support. * * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com)
*/
if (cres->ops && cres->ops->expand_readahead)
cres->ops->expand_readahead(cres, _start, _len, i_size);
}
staticvoid netfs_rreq_expand(struct netfs_io_request *rreq, struct readahead_control *ractl)
{ /* Give the cache a chance to change the request parameters. The * resultant request must contain the original region.
*/
netfs_cache_expand_readahead(rreq, &rreq->start, &rreq->len, rreq->i_size);
/* Give the netfs a chance to change the request parameters. The * resultant request must contain the original region.
*/ if (rreq->netfs_ops->expand_readahead)
rreq->netfs_ops->expand_readahead(rreq);
/* Expand the request if the cache wants it to start earlier. Note * that the expansion may get further extended if the VM wishes to * insert THPs and the preferred start and/or end wind up in the middle * of THPs. * * If this is the case, however, the THP size should be an integer * multiple of the cache granule size, so we get a whole number of * granules to deal with.
*/ if (rreq->start != readahead_pos(ractl) ||
rreq->len != readahead_length(ractl)) {
readahead_expand(ractl, rreq->start, rreq->len);
rreq->start = readahead_pos(ractl);
rreq->len = readahead_length(ractl);
/* * Begin an operation, and fetch the stored zero point value from the cookie if * available.
*/ staticint netfs_begin_cache_read(struct netfs_io_request *rreq, struct netfs_inode *ctx)
{ return fscache_begin_read_operation(&rreq->cache_resources, netfs_i_cookie(ctx));
}
/* * netfs_prepare_read_iterator - Prepare the subreq iterator for I/O * @subreq: The subrequest to be set up * * Prepare the I/O iterator representing the read buffer on a subrequest for * the filesystem to use for I/O (it can be passed directly to a socket). This * is intended to be called from the ->issue_read() method once the filesystem * has trimmed the request to the size it wants. * * Returns the limited size if successful and -ENOMEM if insufficient memory * available. * * [!] NOTE: This must be run in the same thread as ->issue_read() was called * in as we access the readahead_control struct.
*/ static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq, struct readahead_control *ractl)
{ struct netfs_io_request *rreq = subreq->rreq;
size_t rsize = subreq->len;
if (subreq->source == NETFS_DOWNLOAD_FROM_SERVER)
rsize = umin(rsize, rreq->io_streams[0].sreq_max_len);
if (ractl) { /* If we don't have sufficient folios in the rolling buffer, * extract a folioq's worth from the readahead region at a time * into the buffer. Note that this acquires a ref on each page * that we will need to release later - but we don't want to do * that until after we've started the I/O.
*/ struct folio_batch put_batch;
folio_batch_init(&put_batch); while (rreq->submitted < subreq->start + rsize) {
ssize_t added;
/* We add to the end of the list whilst the collector may be walking * the list. The collector only goes nextwards and uses the lock to * remove entries off of the front.
*/
spin_lock(&rreq->lock);
list_add_tail(&subreq->rreq_link, &stream->subrequests); if (list_is_first(&subreq->rreq_link, &stream->subrequests)) {
stream->front = subreq; if (!stream->active) {
stream->collected_to = stream->front->start; /* Store list pointers before active flag */
smp_store_release(&stream->active, true);
}
}
if (last_subreq) {
smp_wmb(); /* Write lists before ALL_QUEUED. */
set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags);
}
/* * Perform a read to the pagecache from a series of sources of different types, * slicing up the region to be read according to available cache blocks and * network rsize.
*/ staticvoid netfs_read_to_pagecache(struct netfs_io_request *rreq, struct readahead_control *ractl)
{ struct netfs_inode *ictx = netfs_inode(rreq->inode); unsignedlonglong start = rreq->start;
ssize_t size = rreq->len; int ret = 0;
if (unlikely(size > 0)) {
smp_wmb(); /* Write lists before ALL_QUEUED. */
set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags);
netfs_wake_collector(rreq);
}
/* Defer error return as we may need to wait for outstanding I/O. */
cmpxchg(&rreq->error, 0, ret);
}
/** * netfs_readahead - Helper to manage a read request * @ractl: The description of the readahead request * * Fulfil a readahead request by drawing data from the cache if possible, or * the netfs if not. Space beyond the EOF is zero-filled. Multiple I/O * requests from different sources will get munged together. If necessary, the * readahead window can be expanded in either direction to a more convenient * alighment for RPC efficiency or to make storage in the cache feasible. * * The calling netfs must initialise a netfs context contiguous to the vfs * inode before calling this. * * This is usable whether or not caching is enabled.
*/ void netfs_readahead(struct readahead_control *ractl)
{ struct netfs_io_request *rreq; struct netfs_inode *ictx = netfs_inode(ractl->mapping->host); unsignedlonglong start = readahead_pos(ractl);
size_t size = readahead_length(ractl); int ret;
rreq = netfs_alloc_request(ractl->mapping, ractl->file, start, size,
NETFS_READAHEAD); if (IS_ERR(rreq)) return;
/* * Create a rolling buffer with a single occupying folio.
*/ staticint netfs_create_singular_buffer(struct netfs_io_request *rreq, struct folio *folio, unsignedint rollbuf_flags)
{
ssize_t added;
if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST) < 0) return -ENOMEM;
/* Fiddle the buffer so that a gap at the beginning and/or a gap at the * end get copied to, but the middle is discarded.
*/
ret = -ENOMEM;
bvec = kmalloc_array(nr_bvec, sizeof(*bvec), GFP_KERNEL); if (!bvec) goto discard;
/** * netfs_read_folio - Helper to manage a read_folio request * @file: The file to read from * @folio: The folio to read * * Fulfil a read_folio request by drawing data from the cache if * possible, or the netfs if not. Space beyond the EOF is zero-filled. * Multiple I/O requests from different sources will get munged together. * * The calling netfs must initialise a netfs context contiguous to the vfs * inode before calling this. * * This is usable whether or not caching is enabled.
*/ int netfs_read_folio(struct file *file, struct folio *folio)
{ struct address_space *mapping = folio->mapping; struct netfs_io_request *rreq; struct netfs_inode *ctx = netfs_inode(mapping->host); int ret;
if (folio_test_dirty(folio)) {
trace_netfs_folio(folio, netfs_folio_trace_read_gaps); return netfs_read_gaps(file, folio);
}
_enter("%lx", folio->index);
rreq = netfs_alloc_request(mapping, file,
folio_pos(folio), folio_size(folio),
NETFS_READPAGE); if (IS_ERR(rreq)) {
ret = PTR_ERR(rreq); goto alloc_error;
}
ret = netfs_begin_cache_read(rreq, ctx); if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS) goto discard;
/* * Prepare a folio for writing without reading first * @folio: The folio being prepared * @pos: starting position for the write * @len: length of write * @always_fill: T if the folio should always be completely filled/cleared * * In some cases, write_begin doesn't need to read at all: * - full folio write * - write that lies in a folio that is completely beyond EOF * - write that covers the folio from start to EOF or beyond it * * If any of these criteria are met, then zero out the unwritten parts * of the folio and return true. Otherwise, return false.
*/ staticbool netfs_skip_folio_read(struct folio *folio, loff_t pos, size_t len, bool always_fill)
{ struct inode *inode = folio_inode(folio);
loff_t i_size = i_size_read(inode);
size_t offset = offset_in_folio(folio, pos);
size_t plen = folio_size(folio);
if (unlikely(always_fill)) { if (pos - offset + len <= i_size) returnfalse; /* Page entirely before EOF */
folio_zero_segment(folio, 0, plen);
folio_mark_uptodate(folio); returntrue;
}
/* Full folio write */ if (offset == 0 && len >= plen) returntrue;
/* Page entirely beyond the end of the file */ if (pos - offset >= i_size) goto zero_out;
/* Write that covers from the start of the folio to EOF or beyond */ if (offset == 0 && (pos + len) >= i_size) goto zero_out;
/** * netfs_write_begin - Helper to prepare for writing [DEPRECATED] * @ctx: The netfs context * @file: The file to read from * @mapping: The mapping to read from * @pos: File position at which the write will begin * @len: The length of the write (may extend beyond the end of the folio chosen) * @_folio: Where to put the resultant folio * @_fsdata: Place for the netfs to store a cookie * * Pre-read data for a write-begin request by drawing data from the cache if * possible, or the netfs if not. Space beyond the EOF is zero-filled. * Multiple I/O requests from different sources will get munged together. * * The calling netfs must provide a table of operations, only one of which, * issue_read, is mandatory. * * The check_write_begin() operation can be provided to check for and flush * conflicting writes once the folio is grabbed and locked. It is passed a * pointer to the fsdata cookie that gets returned to the VM to be passed to * write_end. It is permitted to sleep. It should return 0 if the request * should go ahead or it may return an error. It may also unlock and put the * folio, provided it sets ``*foliop`` to NULL, in which case a return of 0 * will cause the folio to be re-got and the process to be retried. * * The calling netfs must initialise a netfs context contiguous to the vfs * inode before calling this. * * This is usable whether or not caching is enabled. * * Note that this should be considered deprecated and netfs_perform_write() * used instead.
*/ int netfs_write_begin(struct netfs_inode *ctx, struct file *file, struct address_space *mapping,
loff_t pos, unsignedint len, struct folio **_folio, void **_fsdata)
{ struct netfs_io_request *rreq; struct folio *folio;
pgoff_t index = pos >> PAGE_SHIFT; int ret;
if (ctx->ops->check_write_begin) { /* Allow the netfs (eg. ceph) to flush conflicts. */
ret = ctx->ops->check_write_begin(file, pos, len, &folio, _fsdata); if (ret < 0) {
trace_netfs_failure(NULL, NULL, ret, netfs_fail_check_write_begin); goto error;
} if (!folio) goto retry;
}
if (folio_test_uptodate(folio)) goto have_folio;
/* If the folio is beyond the EOF, we want to clear it - unless it's * within the cache granule containing the EOF, in which case we need * to preload the granule.
*/ if (!netfs_is_cache_enabled(ctx) &&
netfs_skip_folio_read(folio, pos, len, false)) {
netfs_stat(&netfs_n_rh_write_zskip); goto have_folio_no_wait;
}
/** * netfs_buffered_read_iter - Filesystem buffered I/O read routine * @iocb: kernel I/O control block * @iter: destination for the data read * * This is the ->read_iter() routine for all filesystems that can use the page * cache directly. * * The IOCB_NOWAIT flag in iocb->ki_flags indicates that -EAGAIN shall be * returned when no data can be read without waiting for I/O requests to * complete; it doesn't prevent readahead. * * The IOCB_NOIO flag in iocb->ki_flags indicates that no new I/O requests * shall be made for the read or for readahead. When no data can be read, * -EAGAIN shall be returned. When readahead would be triggered, a partial, * possibly empty read shall be returned. * * Return: * * number of bytes copied, even for partial reads * * negative error code (or 0 if IOCB_NOIO) if nothing was read
*/
ssize_t netfs_buffered_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{ struct inode *inode = file_inode(iocb->ki_filp); struct netfs_inode *ictx = netfs_inode(inode);
ssize_t ret;
if (WARN_ON_ONCE((iocb->ki_flags & IOCB_DIRECT) ||
test_bit(NETFS_ICTX_UNBUFFERED, &ictx->flags))) return -EINVAL;
ret = netfs_start_io_read(inode); if (ret == 0) {
ret = filemap_read(iocb, iter, 0);
netfs_end_io_read(inode);
} return ret;
}
EXPORT_SYMBOL(netfs_buffered_read_iter);
/** * netfs_file_read_iter - Generic filesystem read routine * @iocb: kernel I/O control block * @iter: destination for the data read * * This is the ->read_iter() routine for all filesystems that can use the page * cache directly. * * The IOCB_NOWAIT flag in iocb->ki_flags indicates that -EAGAIN shall be * returned when no data can be read without waiting for I/O requests to * complete; it doesn't prevent readahead. * * The IOCB_NOIO flag in iocb->ki_flags indicates that no new I/O requests * shall be made for the read or for readahead. When no data can be read, * -EAGAIN shall be returned. When readahead would be triggered, a partial, * possibly empty read shall be returned. * * Return: * * number of bytes copied, even for partial reads * * negative error code (or 0 if IOCB_NOIO) if nothing was read
*/
ssize_t netfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{ struct netfs_inode *ictx = netfs_inode(iocb->ki_filp->f_mapping->host);
if ((iocb->ki_flags & IOCB_DIRECT) ||
test_bit(NETFS_ICTX_UNBUFFERED, &ictx->flags)) return netfs_unbuffered_read_iter(iocb, iter);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.