blob: aa1e4f8d46ab36fde2fe7b6d218f77749e8e5027 [file] [edit]
// 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)
*/
#include <linux/export.h>
#include <linux/task_io_accounting_ops.h>
#include "internal.h"
static void netfs_cache_expand_readahead(struct netfs_io_request *rreq,
uoff_t *_start,
uoff_t *_len,
uoff_t i_size)
{
struct netfs_cache_resources *cres = &rreq->cache_resources;
if (cres->ops && cres->ops->expand_readahead)
cres->ops->expand_readahead(cres, _start, _len, i_size);
}
static void 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);
trace_netfs_read(rreq, readahead_pos(ractl), readahead_length(ractl),
netfs_read_trace_expanded);
}
}
/*
* Drop the folio refs acquired from the readahead API.
*/
static void netfs_bulk_drop_ra_refs(struct netfs_io_request *rreq)
{
struct folio_batch fbatch;
struct folio *folio;
pgoff_t nr_pages = DIV_ROUND_UP(rreq->len, PAGE_SIZE);
pgoff_t first = rreq->start / PAGE_SIZE;
XA_STATE(xas, &rreq->mapping->i_pages, first);
folio_batch_init(&fbatch);
rcu_read_lock();
xas_for_each(&xas, folio, first + nr_pages - 1) {
if (xas_retry(&xas, folio))
continue;
if (!folio_batch_add(&fbatch, folio))
folio_batch_release(&fbatch);
}
rcu_read_unlock();
folio_batch_release(&fbatch);
trace_netfs_rreq(rreq, netfs_rreq_trace_ra_put_ref);
clear_bit_unlock(NETFS_RREQ_NEED_PUT_RA_REFS, &rreq->flags);
wake_up(&rreq->waitq);
}
static void netfs_maybe_bulk_drop_ra_refs(struct netfs_io_request *rreq)
{
if (test_bit(NETFS_RREQ_NEED_PUT_RA_REFS, &rreq->flags))
netfs_bulk_drop_ra_refs(rreq);
}
/*
* Begin an operation, and fetch the stored zero point value from the cookie if
* available.
*/
static int 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.
*/
static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq)
{
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);
subreq->len = rsize;
if (unlikely(rreq->io_streams[0].sreq_max_segs)) {
size_t limit = netfs_limit_iter(&rreq->buffer.iter, 0, rsize,
rreq->io_streams[0].sreq_max_segs);
if (limit < rsize) {
subreq->len = limit;
trace_netfs_sreq(subreq, netfs_sreq_trace_limited);
}
}
subreq->io_iter = rreq->buffer.iter;
iov_iter_truncate(&subreq->io_iter, subreq->len);
rolling_buffer_advance(&rreq->buffer, subreq->len);
return subreq->len;
}
/*
* Issue a read against the cache.
* - Eats the caller's ref on subreq.
*/
static void netfs_read_cache_to_pagecache(struct netfs_io_request *rreq,
struct netfs_io_subrequest *subreq)
{
struct netfs_cache_resources *cres = &rreq->cache_resources;
netfs_stat(&netfs_n_rh_read);
cres->ops->read(cres, subreq->start, &subreq->io_iter, NETFS_READ_HOLE_IGNORE,
netfs_cache_read_terminated, subreq);
}
int netfs_read_query_cache(struct netfs_io_request *rreq, struct fscache_occupancy *occ)
{
struct netfs_cache_resources *cres = &rreq->cache_resources;
occ->granularity = PAGE_SIZE;
if (occ->query_from >= occ->query_to)
return 0;
if (!cres->ops)
return 0;
occ->query_from = round_up(occ->query_from, occ->granularity);
return cres->ops->query_occupancy(cres, occ);
}
void netfs_queue_read(struct netfs_io_request *rreq,
struct netfs_io_subrequest *subreq)
{
struct netfs_io_stream *stream = &rreq->io_streams[0];
__set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags);
/* 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);
/* Write IN_PROGRESS before pointer to new subreq */
list_add_tail_release(&subreq->rreq_link, &stream->subrequests);
if (list_is_first(&subreq->rreq_link, &stream->subrequests)) {
if (!stream->active) {
stream->collected_to = subreq->start;
/* Store list pointers before active flag */
smp_store_release(&stream->active, true);
}
}
spin_unlock(&rreq->lock);
}
static void netfs_issue_read(struct netfs_io_request *rreq,
struct netfs_io_subrequest *subreq)
{
switch (subreq->source) {
case NETFS_DOWNLOAD_FROM_SERVER:
rreq->netfs_ops->issue_read(subreq);
break;
case NETFS_READ_FROM_CACHE:
netfs_read_cache_to_pagecache(rreq, subreq);
break;
default:
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
subreq->error = 0;
iov_iter_zero(subreq->len, &subreq->io_iter);
subreq->transferred = subreq->len;
netfs_read_subreq_terminated(subreq);
break;
}
}
/*
* Mark folios that we want to copy to the cache. For filesystems that use
* netfslib fully, we set folio->private to NETFS_FOLIO_COPY_TO_CACHE;
* otherwise we set the deprecated PG_private_2.
*/
static void netfs_mark_copy_to_cache(struct netfs_io_request *rreq,
struct bvecq **bq,
unsigned int *offset,
int *slot,
size_t len,
bool copy)
{
while (len > 0) {
struct folio *folio;
size_t fsize, overlap;
if (!*bq)
break;
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 = (*bq)->bv[*slot].bv_len;
overlap = min(len, fsize - *offset);
if (overlap > 0 && copy) {
folio = bvec_folio(&(*bq)->bv[*slot]);
if (netfs_using_pgpriv2(rreq)) {
if (!folio_test_private_2(folio))
folio_start_private_2(folio);
} else {
if (!folio_get_private(folio))
folio_attach_private(folio, NETFS_FOLIO_COPY_TO_CACHE);
}
trace_netfs_folio(folio, netfs_folio_trace_mark_copy);
}
len -= overlap;
*offset += overlap;
if (*offset >= fsize) {
*slot += 1;
*offset = 0;
}
}
}
/*
* 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.
*/
static void netfs_read_to_pagecache(struct netfs_io_request *rreq)
{
struct fscache_occupancy _occ = {
.query_from = rreq->start,
.query_to = rreq->start + rreq->len,
.cached_from[0] = 0,
.cached_to[0] = 0,
.cached_from[1] = ULLONG_MAX,
.cached_to[1] = ULLONG_MAX,
};
struct fscache_occupancy *occ = &_occ;
struct bvecq *bq = rreq->buffer.tail;
unsigned int offset = 0;
ssize_t size = rreq->len;
uoff_t start = rreq->start;
int ret = 0, slot = 0;
do {
int (*prepare_read)(struct netfs_io_subrequest *subreq) = NULL;
struct netfs_io_subrequest *subreq;
enum netfs_io_source source;
ssize_t slice;
uoff_t hole_to, cache_to;
size_t len = size;
bool copy = false;
/* If we don't have any, find out the next couple of data
* extents from the cache, containing of following the
* specified start offset. Holes have to be fetched from the
* server; data regions from the cache.
*/
hole_to = occ->cached_from[0];
cache_to = occ->cached_to[0];
if (start >= cache_to) {
/* Extent exhausted; shuffle down. */
int i;
for (i = 0; i < ARRAY_SIZE(occ->cached_from) - 1; i++) {
occ->cached_from[i] = occ->cached_from[i + 1];
occ->cached_to[i] = occ->cached_to[i + 1];
occ->cached_type[i] = occ->cached_type[i + 1];
}
occ->cached_from[i] = ULLONG_MAX;
occ->cached_to[i] = ULLONG_MAX;
if (occ->cached_from[0] != ULLONG_MAX)
continue;
/* Get new extents */
ret = netfs_read_query_cache(rreq, occ);
if (ret < 0)
break;
continue;
}
uoff_t zero_point = netfs_read_zero_point(rreq->inode);
uoff_t zlimit = umin(zero_point, rreq->i_size);
_debug("rsub %llx %llx-%llx", start, hole_to, cache_to);
if (start >= hole_to && start < cache_to) {
/* Overlap with a cached region, where the cache may
* record a block of zeroes.
*/
_debug("cached s=%llx c=%llx l=%zx", start, cache_to, size);
len = umin(cache_to - start, size);
len = round_up(len, occ->granularity);
if (occ->cached_type[0] == FSCACHE_EXTENT_ZERO) {
source = NETFS_FILL_WITH_ZEROES;
netfs_stat(&netfs_n_rh_zero);
} else {
source = NETFS_READ_FROM_CACHE;
prepare_read = rreq->cache_resources.ops->prepare_read;
}
} else if (start >= zlimit && size > 0) {
/* If this range lies beyond the zero-point, that part
* can just be cleared locally.
*/
_debug("zero %llx-%llx", start, start + size);
len = size;
source = NETFS_FILL_WITH_ZEROES;
if (rreq->cache_resources.ops)
copy = true;
netfs_stat(&netfs_n_rh_zero);
} else {
/* Read a cache hole from the server. If any part of
* this range lies beyond the zero-point or the EOF,
* that part can just be cleared locally.
*/
uoff_t limit = min3(zlimit, start + size, hole_to);
_debug("limit %llx %llx", rreq->i_size, zero_point);
_debug("download %llx-%llx", start, start + size);
len = umin(limit - start, ULONG_MAX);
source = NETFS_DOWNLOAD_FROM_SERVER;
prepare_read = rreq->netfs_ops->prepare_read;
if (rreq->cache_resources.ops)
copy = true;
netfs_stat(&netfs_n_rh_download);
}
if (len == 0) {
pr_err("ZERO-LEN READ: R=%08x l=%zx/%zx s=%llx z=%llx i=%llx",
rreq->debug_id, len, size,
start, zero_point, rreq->i_size);
break;
}
subreq = netfs_alloc_subrequest(rreq, source);
if (!subreq) {
ret = -ENOMEM;
break;
}
subreq->start = start;
subreq->len = size;
if (copy)
__set_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags);
netfs_queue_read(rreq, subreq);
rreq->io_streams[0].sreq_max_len = MAX_RW_COUNT;
rreq->io_streams[0].sreq_max_segs = INT_MAX;
if (prepare_read) {
ret = prepare_read(subreq);
if (ret < 0) {
netfs_cancel_read(subreq, ret);
break;
}
trace_netfs_sreq(subreq, netfs_sreq_trace_prepare);
}
slice = netfs_prepare_read_iterator(subreq);
if (slice < 0) {
ret = slice;
netfs_cancel_read(subreq, ret);
break;
}
start += slice;
size -= slice;
if (size <= 0)
netfs_all_subreqs_queued(rreq);
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, &bq, &slot, &offset, slice, copy);
}
trace_netfs_sreq(subreq, netfs_sreq_trace_submit);
netfs_issue_read(rreq, subreq);
netfs_maybe_bulk_drop_ra_refs(rreq);
if (test_bit(NETFS_RREQ_PAUSE, &rreq->flags))
netfs_wait_for_paused_read(rreq);
if (test_bit(NETFS_RREQ_FAILED, &rreq->flags))
break;
cond_resched();
} while (size > 0);
if (unlikely(size > 0)) {
netfs_all_subreqs_queued(rreq);
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);
ssize_t added;
uoff_t 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;
__set_bit(NETFS_RREQ_OFFLOAD_COLLECTION, &rreq->flags);
ret = netfs_begin_cache_read(rreq, ictx);
if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS)
goto cleanup_free;
netfs_stat(&netfs_n_rh_readahead);
trace_netfs_read(rreq, readahead_pos(ractl), readahead_length(ractl),
netfs_read_trace_readahead);
netfs_rreq_expand(rreq, ractl);
/* Load the folios to be read into a bvecq chain. Note that this
* 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->gfp);
if (added < 0) {
ret = added;
goto cleanup_free;
}
__set_bit(NETFS_RREQ_NEED_PUT_RA_REFS, &rreq->flags);
rreq->submitted = rreq->start + added;
rreq->cleaned_to = rreq->start;
netfs_read_set_unlock_at(rreq);
netfs_read_to_pagecache(rreq);
netfs_maybe_bulk_drop_ra_refs(rreq);
return netfs_put_request(rreq, netfs_rreq_trace_put_return);
cleanup_free:
return netfs_put_failed_request(rreq);
}
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)
{
ssize_t added;
if (rolling_buffer_init(&rreq->buffer, ITER_DEST, rreq->gfp, false) < 0)
return -ENOMEM;
added = rolling_buffer_append(&rreq->buffer, folio, rreq->gfp);
if (added < 0)
return added;
rreq->submitted = rreq->start + added;
rreq->progress_at = added;
return 0;
}
/*
* Read into gaps in a folio partially filled by a streaming write.
*/
static int netfs_read_gaps(struct file *file, struct folio *folio)
{
struct netfs_io_request *rreq;
struct address_space *mapping = folio->mapping;
struct netfs_group *group = netfs_folio_group(folio);
struct netfs_folio *finfo = netfs_folio_info(folio);
struct netfs_inode *ctx = netfs_inode(mapping->host);
struct bio_vec *bvec = NULL;
unsigned int from = finfo->dirty_offset;
unsigned int to = from + finfo->dirty_len;
unsigned int off = 0;
size_t flen = folio_size(folio);
size_t nr_bvec = flen / PAGE_SIZE + 2;
size_t part;
int ret, i = 0, sink_from = -1, sink_to = -1;
_enter("%lx", folio->index);
rreq = netfs_alloc_request(mapping, file, folio_pos(folio), flen, NETFS_READ_GAPS);
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;
netfs_stat(&netfs_n_rh_read_folio);
trace_netfs_read(rreq, rreq->start, rreq->len, netfs_read_trace_read_gaps);
/* 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_objs(*bvec, nr_bvec);
if (!bvec)
goto discard;
trace_netfs_folio(folio, netfs_folio_trace_read_gaps);
if (from > 0) {
bvec_set_folio(&bvec[i++], folio, from, 0);
off = from;
}
sink_from = i;
while (off < to) {
struct folio *sink = folio_alloc(GFP_KERNEL, 0);
if (!sink)
goto discard;
part = min_t(size_t, to - off, PAGE_SIZE);
bvec_set_folio(&bvec[i], sink, part, 0);
off += part;
sink_to = i;
i++;
}
if (to < flen)
bvec_set_folio(&bvec[i++], folio, flen - to, to);
iov_iter_bvec(&rreq->buffer.iter, ITER_DEST, bvec, i, rreq->len);
rreq->submitted = rreq->start + flen;
netfs_read_to_pagecache(rreq);
ret = netfs_wait_for_read(rreq);
if (ret >= 0) {
if (group)
folio_change_private(folio, group);
else
folio_detach_private(folio);
kfree(finfo);
trace_netfs_folio(folio, netfs_folio_trace_filled_gaps);
flush_dcache_folio(folio);
folio_mark_uptodate(folio);
}
if (sink_to >= 0)
for (; sink_from <= sink_to; sink_from++)
folio_put(bvec_folio(&bvec[sink_from]));
kfree(bvec);
folio_unlock(folio);
netfs_put_request(rreq, netfs_rreq_trace_put_return);
return ret < 0 ? ret : 0;
discard:
netfs_put_failed_request(rreq);
alloc_error:
folio_unlock(folio);
if (sink_to >= 0)
for (; sink_from <= sink_to; sink_from++)
folio_put(bvec_folio(&bvec[sink_from]));
kfree(bvec);
return ret;
}
/**
* 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;
folio_wait_writeback(folio);
if (folio_test_dirty(folio))
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;
netfs_stat(&netfs_n_rh_read_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);
if (ret < 0)
goto discard;
netfs_read_to_pagecache(rreq);
ret = netfs_wait_for_read(rreq);
netfs_put_request(rreq, netfs_rreq_trace_put_return);
return ret < 0 ? ret : 0;
discard:
netfs_put_failed_request(rreq);
alloc_error:
folio_unlock(folio);
return ret;
}
EXPORT_SYMBOL(netfs_read_folio);
/*
* 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.
*/
static bool netfs_skip_folio_read(struct folio *folio, uoff_t pos, size_t len,
bool always_fill)
{
struct inode *inode = folio_inode(folio);
uoff_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)
return false; /* Page entirely before EOF */
folio_zero_segment(folio, 0, plen);
folio_mark_uptodate(folio);
return true;
}
/* Full folio write */
if (offset == 0 && len >= plen)
return true;
/* 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;
return false;
zero_out:
folio_zero_segments(folio, 0, offset, offset + len, plen);
return true;
}
/**
* 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,
uoff_t pos, unsigned int len, struct folio **_folio,
void **_fsdata)
{
struct netfs_io_request *rreq;
struct folio *folio;
pgoff_t index = pos >> PAGE_SHIFT;
int ret;
retry:
folio = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN,
mapping_gfp_mask(mapping));
if (IS_ERR(folio))
return PTR_ERR(folio);
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_maybe_enabled(ctx) &&
netfs_skip_folio_read(folio, pos, len, false)) {
netfs_stat(&netfs_n_rh_write_zskip);
goto have_folio_no_wait;
}
rreq = netfs_alloc_request(mapping, file,
folio_pos(folio), folio_size(folio),
NETFS_READ_FOR_WRITE);
if (IS_ERR(rreq)) {
ret = PTR_ERR(rreq);
goto error;
}
rreq->no_unlock_folio = folio;
__set_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags);
ret = netfs_begin_cache_read(rreq, ctx);
if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS)
goto error_put;
netfs_stat(&netfs_n_rh_write_begin);
trace_netfs_read(rreq, pos, len, netfs_read_trace_write_begin);
/* Set up the output buffer */
ret = netfs_create_singular_buffer(rreq, folio);
if (ret < 0)
goto error_put;
netfs_read_to_pagecache(rreq);
ret = netfs_wait_for_read(rreq);
netfs_put_request(rreq, netfs_rreq_trace_put_return);
if (ret < 0)
goto error;
have_folio:
ret = folio_wait_private_2_killable(folio);
if (ret < 0)
goto error;
have_folio_no_wait:
*_folio = folio;
_leave(" = 0");
return 0;
error_put:
netfs_put_failed_request(rreq);
error:
if (folio) {
folio_unlock(folio);
folio_put(folio);
}
_leave(" = %d", ret);
return ret;
}
EXPORT_SYMBOL(netfs_write_begin);
/*
* Preload the data into a folio we're proposing to write into.
*/
int netfs_prefetch_for_write(struct file *file, struct folio *folio,
size_t offset, size_t len)
{
struct netfs_io_request *rreq;
struct address_space *mapping = folio->mapping;
struct netfs_inode *ctx = netfs_inode(mapping->host);
uoff_t start = folio_pos(folio);
size_t flen = folio_size(folio);
int ret;
_enter("%zx @%llx", flen, start);
ret = -ENOMEM;
rreq = netfs_alloc_request(mapping, file, start, flen,
NETFS_READ_FOR_WRITE);
if (IS_ERR(rreq)) {
ret = PTR_ERR(rreq);
goto error;
}
rreq->no_unlock_folio = folio;
__set_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags);
ret = netfs_begin_cache_read(rreq, ctx);
if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS)
goto error_put;
netfs_stat(&netfs_n_rh_write_begin);
trace_netfs_read(rreq, start, flen, netfs_read_trace_prefetch_for_write);
/* Set up the output buffer */
ret = netfs_create_singular_buffer(rreq, folio);
if (ret < 0)
goto error_put;
netfs_read_to_pagecache(rreq);
ret = netfs_wait_for_read(rreq);
netfs_put_request(rreq, netfs_rreq_trace_put_return);
return ret < 0 ? ret : 0;
error_put:
netfs_put_failed_request(rreq);
error:
_leave(" = %d", ret);
return ret;
}
/**
* 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);
return netfs_buffered_read_iter(iocb, iter);
}
EXPORT_SYMBOL(netfs_file_read_iter);