mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 23:09:29 +02:00
For really big read RPC ops that span multiple folios, netfslib allows the
filesystem to give progress notifications to wake up the collector thread
to do a collection of folios that have now been fetched, even if the RPC is
still ongoing, thereby allowing the application to make progress.
This works by taking the current rreq->cleaned_to value (which indicates
which folios have been unlocked) and adding the stashed size of the next
folio to it. cleaned_to, however, is subject to 64-bit tearing on a 32-bit
arch.
Fix this by stashing the next progress notification point as a size_t
(which won't tear) to be added to rreq->start (which won't change), with
the collector thread calculating that from cleaned_to plus the next folio
size.
Further, however, if the folios are small, the collector thread gets
constantly woken up - which has a negative performance impact on the
system.
Fix that too by setting a minimum trigger of 256KiB or the size of the
folio at the front of the queue, whichever is larger. Note that this has
an issue that different subreqs have different need-to-be-cached
properties; this is solved by a preceding patch that marks the property on
the folios whilst issuing subreqs rather than when collecting them.
Also, make sure rreq->cleaned_to is initialised up front, along with
rreq->collected_to and stream->collected_to.
Fixes: e2d46f2ec3 ("netfs: Change the read result collector to only use one work item")
Link: https://sashiko.dev/#/patchset/20260804100224.2748935-1-dhowells%40redhat.com
Signed-off-by: David Howells <dhowells@redhat.com>
Link: https://patch.msgid.link/20260827134304.2075713-10-dhowells@redhat.com
Acked-by: Paulo Alcantara <pc@manguebit.org>
cc: Paulo Alcantara <pc@manguebit.org>
cc: netfs@lists.linux.dev
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
194 lines
5.6 KiB
C
194 lines
5.6 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/* Single, monolithic object support (e.g. AFS directory).
|
|
*
|
|
* Copyright (C) 2024 Red Hat, Inc. All Rights Reserved.
|
|
* Written by David Howells (dhowells@redhat.com)
|
|
*/
|
|
|
|
#include <linux/export.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/uio.h>
|
|
#include <linux/sched/mm.h>
|
|
#include <linux/task_io_accounting_ops.h>
|
|
#include <linux/netfs.h>
|
|
#include "internal.h"
|
|
|
|
/**
|
|
* netfs_single_mark_inode_dirty - Mark a single, monolithic object inode dirty
|
|
* @inode: The inode to mark
|
|
*
|
|
* Mark an inode that contains a single, monolithic object as dirty so that its
|
|
* writepages op will get called. If set, the SINGLE_NO_UPLOAD flag indicates
|
|
* that the object will only be written to the cache and not uploaded (e.g. AFS
|
|
* directory contents).
|
|
*/
|
|
void netfs_single_mark_inode_dirty(struct inode *inode)
|
|
{
|
|
struct netfs_inode *ictx = netfs_inode(inode);
|
|
bool cache_only = test_bit(NETFS_ICTX_SINGLE_NO_UPLOAD, &ictx->flags);
|
|
bool caching = fscache_cookie_enabled(netfs_i_cookie(netfs_inode(inode)));
|
|
|
|
if (cache_only && !caching)
|
|
return;
|
|
|
|
mark_inode_dirty(inode);
|
|
|
|
if (caching && !(inode_state_read_once(inode) & I_PINNING_NETFS_WB)) {
|
|
bool need_use = false;
|
|
|
|
spin_lock(&inode->i_lock);
|
|
if (!(inode_state_read(inode) & I_PINNING_NETFS_WB)) {
|
|
inode_state_set(inode, I_PINNING_NETFS_WB);
|
|
need_use = true;
|
|
}
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
if (need_use)
|
|
fscache_use_cookie(netfs_i_cookie(ictx), true);
|
|
}
|
|
|
|
}
|
|
EXPORT_SYMBOL(netfs_single_mark_inode_dirty);
|
|
|
|
static int netfs_single_begin_cache_read(struct netfs_io_request *rreq, struct netfs_inode *ctx)
|
|
{
|
|
return fscache_begin_read_operation(&rreq->cache_resources, netfs_i_cookie(ctx));
|
|
}
|
|
|
|
static void netfs_single_cache_prepare_read(struct netfs_io_request *rreq,
|
|
struct netfs_io_subrequest *subreq)
|
|
{
|
|
struct netfs_cache_resources *cres = &rreq->cache_resources;
|
|
|
|
if (!cres->ops) {
|
|
subreq->source = NETFS_DOWNLOAD_FROM_SERVER;
|
|
return;
|
|
}
|
|
subreq->source = cres->ops->prepare_read(subreq, rreq->i_size);
|
|
trace_netfs_sreq(subreq, netfs_sreq_trace_prepare);
|
|
|
|
}
|
|
|
|
static void netfs_single_read_cache(struct netfs_io_request *rreq,
|
|
struct netfs_io_subrequest *subreq)
|
|
{
|
|
struct netfs_cache_resources *cres = &rreq->cache_resources;
|
|
|
|
_enter("R=%08x[%x]", rreq->debug_id, subreq->debug_index);
|
|
netfs_stat(&netfs_n_rh_read);
|
|
cres->ops->read(cres, subreq->start, &subreq->io_iter, NETFS_READ_HOLE_FAIL,
|
|
netfs_cache_read_terminated, subreq);
|
|
}
|
|
|
|
/*
|
|
* Perform a read to a buffer from the cache or the server. Only a single
|
|
* subreq is permitted as the object must be fetched in a single transaction.
|
|
*/
|
|
static int netfs_single_dispatch_read(struct netfs_io_request *rreq)
|
|
{
|
|
struct netfs_io_subrequest *subreq;
|
|
int ret = 0;
|
|
|
|
subreq = netfs_alloc_subrequest(rreq);
|
|
if (!subreq)
|
|
return -ENOMEM;
|
|
|
|
subreq->source = NETFS_SOURCE_UNKNOWN;
|
|
subreq->start = 0;
|
|
subreq->len = rreq->len;
|
|
subreq->io_iter = rreq->buffer.iter;
|
|
|
|
netfs_queue_read(rreq, subreq);
|
|
|
|
netfs_single_cache_prepare_read(rreq, subreq);
|
|
switch (subreq->source) {
|
|
case NETFS_DOWNLOAD_FROM_SERVER:
|
|
netfs_stat(&netfs_n_rh_download);
|
|
if (rreq->netfs_ops->prepare_read) {
|
|
ret = rreq->netfs_ops->prepare_read(subreq);
|
|
if (ret < 0)
|
|
goto cancel;
|
|
}
|
|
|
|
smp_wmb(); /* Write lists before ALL_QUEUED. */
|
|
set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags);
|
|
rreq->netfs_ops->issue_read(subreq);
|
|
rreq->submitted += subreq->len;
|
|
break;
|
|
case NETFS_READ_FROM_CACHE:
|
|
smp_wmb(); /* Write lists before ALL_QUEUED. */
|
|
set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags);
|
|
trace_netfs_sreq(subreq, netfs_sreq_trace_submit);
|
|
netfs_single_read_cache(rreq, subreq);
|
|
rreq->submitted += subreq->len;
|
|
ret = 0;
|
|
break;
|
|
default:
|
|
pr_warn("Unexpected single-read source %u\n", subreq->source);
|
|
WARN_ON_ONCE(true);
|
|
ret = -EIO;
|
|
goto cancel;
|
|
}
|
|
|
|
return ret;
|
|
cancel:
|
|
netfs_cancel_read(subreq, ret);
|
|
smp_wmb(); /* Write lists before ALL_QUEUED. */
|
|
set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags);
|
|
netfs_wake_collector(rreq);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* netfs_read_single - Synchronously read a single blob of pages.
|
|
* @inode: The inode to read from.
|
|
* @file: The file we're using to read or NULL.
|
|
* @iter: The buffer we're reading into.
|
|
*
|
|
* Fulfil a read request for a single monolithic object by drawing data from
|
|
* the cache if possible, or the netfs if not. The buffer may be larger than
|
|
* the file content; unused beyond the EOF will be zero-filled. The content
|
|
* will be read with a single I/O request (though this may 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. If caching is enabled,
|
|
* the data will be stored as a single object into the cache.
|
|
*/
|
|
ssize_t netfs_read_single(struct inode *inode, struct file *file, struct iov_iter *iter)
|
|
{
|
|
struct netfs_io_request *rreq;
|
|
struct netfs_inode *ictx = netfs_inode(inode);
|
|
ssize_t ret;
|
|
|
|
rreq = netfs_alloc_request(inode->i_mapping, file, 0, iov_iter_count(iter),
|
|
NETFS_READ_SINGLE);
|
|
if (IS_ERR(rreq))
|
|
return PTR_ERR(rreq);
|
|
|
|
rreq->progress_at = rreq->len;
|
|
|
|
ret = netfs_single_begin_cache_read(rreq, ictx);
|
|
if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS)
|
|
goto cleanup_free;
|
|
|
|
netfs_stat(&netfs_n_rh_read_single);
|
|
trace_netfs_read(rreq, 0, rreq->len, netfs_read_trace_read_single);
|
|
|
|
rreq->buffer.iter = *iter;
|
|
netfs_single_dispatch_read(rreq);
|
|
|
|
ret = netfs_wait_for_read(rreq);
|
|
netfs_put_request(rreq, netfs_rreq_trace_put_return);
|
|
return ret;
|
|
|
|
cleanup_free:
|
|
netfs_put_failed_request(rreq);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(netfs_read_single);
|