Commit 820c4bae authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'netfs-lib-20210426' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs

Pull network filesystem helper library updates from David Howells:
 "Here's a set of patches for 5.13 to begin the process of overhauling
  the local caching API for network filesystems. This set consists of
  two parts:

  (1) Add a helper library to handle the new VM readahead interface.

      This is intended to be used unconditionally by the filesystem
      (whether or not caching is enabled) and provides a common
      framework for doing caching, transparent huge pages and, in the
      future, possibly fscrypt and read bandwidth maximisation. It also
      allows the netfs and the cache to align, expand and slice up a
      read request from the VM in various ways; the netfs need only
      provide a function to read a stretch of data to the pagecache and
      the helper takes care of the rest.

  (2) Add an alternative fscache/cachfiles I/O API that uses the kiocb
      facility to do async DIO to transfer data to/from the netfs's
      pages, rather than using readpage with wait queue snooping on one
      side and vfs_write() on the other. It also uses less memory, since
      it doesn't do buffered I/O on the backing file.

      Note that this uses SEEK_HOLE/SEEK_DATA to locate the data
      available to be read from the cache. Whilst this is an improvement
      from the bmap interface, it still has a problem with regard to a
      modern extent-based filesystem inserting or removing bridging
      blocks of zeros. Fixing that requires a much greater overhaul.

  This is a step towards overhauling the fscache API. The change is
  opt-in on the part of the network filesystem. A netfs should not try
  to mix the old and the new API because of conflicting ways of handling
  pages and the PG_fscache page flag and because it would be mixing DIO
  with buffered I/O. Further, the helper library can't be used with the
  old API.

  This does not change any of the fscache cookie handling APIs or the
  way invalidation is done at this time.

  In the near term, I intend to deprecate and remove the old I/O API
  (fscache_allocate_page{,s}(), fscache_read_or_alloc_page{,s}(),
  fscache_write_page() and fscache_uncache_page()) and eventually
  replace most of fscache/cachefiles with something simpler and easier
  to follow.

  This patchset contains the following parts:

   - Some helper patches, including provision of an ITER_XARRAY iov
     iterator and a function to do readahead expansion.

   - Patches to add the netfs helper library.

   - A patch to add the fscache/cachefiles kiocb API.

   - A pair of patches to fix some review issues in the ITER_XARRAY and
     read helpers as spotted by Al and Willy.

  Jeff Layton has patches to add support in Ceph for this that he
  intends for this merge window. I have a set of patches to support AFS
  that I will post a separate pull request for.

  With this, AFS without a cache passes all expected xfstests; with a
  cache, there's an extra failure, but that's also there before these
  patches. Fixing that probably requires a greater overhaul. Ceph also
  passes the expected tests.

  I also have patches in a separate branch to tidy up the handling of
  PG_fscache/PG_private_2 and their contribution to page refcounting in
  the core kernel here, but I haven't included them in this set and will
  route them separately"

Link: https://lore.kernel.org/lkml/3779937.1619478404@warthog.procyon.org.uk/

* tag 'netfs-lib-20210426' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  netfs: Miscellaneous fixes
  iov_iter: Four fixes for ITER_XARRAY
  fscache, cachefiles: Add alternate API to use kiocb for read/write to cache
  netfs: Add a tracepoint to log failures that would be otherwise unseen
  netfs: Define an interface to talk to a cache
  netfs: Add write_begin helper
  netfs: Gather stats
  netfs: Add tracepoints
  netfs: Provide readahead and readpage netfs helpers
  netfs, mm: Add set/end/wait_on_page_fscache() aliases
  netfs, mm: Move PG_fscache helper funcs to linux/netfs.h
  netfs: Documentation for helper library
  netfs: Make a netfs helper module
  mm: Implement readahead_control pageset expansion
  mm/readahead: Handle ractl nr_pages being modified
  fs: Document file_ra_state
  mm/filemap: Pass the file_ra_state in the ractl
  mm: Add set/end/wait functions for PG_private_2
  iov_iter: Add ITER_XARRAY
parents 34a456eb 53b776c7
......@@ -53,6 +53,7 @@ filesystem implementations.
journalling
fscrypt
fsverity
netfs_library
Filesystems
===========
......
This diff is collapsed.
......@@ -125,6 +125,7 @@ source "fs/overlayfs/Kconfig"
menu "Caches"
source "fs/netfs/Kconfig"
source "fs/fscache/Kconfig"
source "fs/cachefiles/Kconfig"
......
......@@ -67,6 +67,7 @@ obj-y += devpts/
obj-$(CONFIG_DLM) += dlm/
# Do not add any filesystems before this line
obj-$(CONFIG_NETFS_SUPPORT) += netfs/
obj-$(CONFIG_FSCACHE) += fscache/
obj-$(CONFIG_REISERFS_FS) += reiserfs/
obj-$(CONFIG_EXT4_FS) += ext4/
......
......@@ -7,6 +7,7 @@ cachefiles-y := \
bind.o \
daemon.o \
interface.o \
io.o \
key.o \
main.o \
namei.o \
......
......@@ -319,8 +319,8 @@ static void cachefiles_drop_object(struct fscache_object *_object)
/*
* dispose of a reference to an object
*/
static void cachefiles_put_object(struct fscache_object *_object,
enum fscache_obj_ref_trace why)
void cachefiles_put_object(struct fscache_object *_object,
enum fscache_obj_ref_trace why)
{
struct cachefiles_object *object;
struct fscache_cache *cache;
......@@ -568,4 +568,5 @@ const struct fscache_cache_ops cachefiles_cache_ops = {
.uncache_page = cachefiles_uncache_page,
.dissociate_pages = cachefiles_dissociate_pages,
.check_consistency = cachefiles_check_consistency,
.begin_read_operation = cachefiles_begin_read_operation,
};
......@@ -150,6 +150,9 @@ extern int cachefiles_has_space(struct cachefiles_cache *cache,
*/
extern const struct fscache_cache_ops cachefiles_cache_ops;
void cachefiles_put_object(struct fscache_object *_object,
enum fscache_obj_ref_trace why);
/*
* key.c
*/
......@@ -217,6 +220,12 @@ extern int cachefiles_allocate_pages(struct fscache_retrieval *,
extern int cachefiles_write_page(struct fscache_storage *, struct page *);
extern void cachefiles_uncache_page(struct fscache_object *, struct page *);
/*
* rdwr2.c
*/
extern int cachefiles_begin_read_operation(struct netfs_read_request *,
struct fscache_retrieval *);
/*
* security.c
*/
......
This diff is collapsed.
......@@ -370,7 +370,7 @@ static struct page *ext4_read_merkle_tree_page(struct inode *inode,
pgoff_t index,
unsigned long num_ra_pages)
{
DEFINE_READAHEAD(ractl, NULL, inode->i_mapping, index);
DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, index);
struct page *page;
index += ext4_verity_metadata_pos(inode) >> PAGE_SHIFT;
......
......@@ -3927,7 +3927,7 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len)
{
DEFINE_READAHEAD(ractl, NULL, inode->i_mapping, page_idx);
DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, page_idx);
struct address_space *mapping = inode->i_mapping;
struct page *page;
pgoff_t redirty_idx = page_idx;
......
......@@ -228,7 +228,7 @@ static struct page *f2fs_read_merkle_tree_page(struct inode *inode,
pgoff_t index,
unsigned long num_ra_pages)
{
DEFINE_READAHEAD(ractl, NULL, inode->i_mapping, index);
DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, index);
struct page *page;
index += f2fs_verity_metadata_pos(inode) >> PAGE_SHIFT;
......
......@@ -2,6 +2,7 @@
config FSCACHE
tristate "General filesystem local caching manager"
select NETFS_SUPPORT
help
This option enables a generic filesystem caching manager that can be
used by various network and other filesystems to cache data locally.
......
......@@ -7,6 +7,7 @@ fscache-y := \
cache.o \
cookie.o \
fsdef.o \
io.o \
main.o \
netfs.o \
object.o \
......
......@@ -142,6 +142,10 @@ extern int fscache_wait_for_operation_activation(struct fscache_object *,
atomic_t *,
atomic_t *);
extern void fscache_invalidate_writes(struct fscache_cookie *);
struct fscache_retrieval *fscache_alloc_retrieval(struct fscache_cookie *cookie,
struct address_space *mapping,
fscache_rw_complete_t end_io_func,
void *context);
/*
* proc.c
......
// SPDX-License-Identifier: GPL-2.0-or-later
/* Cache data I/O routines
*
* Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#define FSCACHE_DEBUG_LEVEL PAGE
#include <linux/module.h>
#define FSCACHE_USE_NEW_IO_API
#include <linux/fscache-cache.h>
#include <linux/slab.h>
#include <linux/netfs.h>
#include "internal.h"
/*
* Start a cache read operation.
* - we return:
* -ENOMEM - out of memory, some pages may be being read
* -ERESTARTSYS - interrupted, some pages may be being read
* -ENOBUFS - no backing object or space available in which to cache any
* pages not being read
* -ENODATA - no data available in the backing object for some or all of
* the pages
* 0 - dispatched a read on all pages
*/
int __fscache_begin_read_operation(struct netfs_read_request *rreq,
struct fscache_cookie *cookie)
{
struct fscache_retrieval *op;
struct fscache_object *object;
bool wake_cookie = false;
int ret;
_enter("rr=%08x", rreq->debug_id);
fscache_stat(&fscache_n_retrievals);
if (hlist_empty(&cookie->backing_objects))
goto nobufs;
if (test_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) {
_leave(" = -ENOBUFS [invalidating]");
return -ENOBUFS;
}
ASSERTCMP(cookie->def->type, !=, FSCACHE_COOKIE_TYPE_INDEX);
if (fscache_wait_for_deferred_lookup(cookie) < 0)
return -ERESTARTSYS;
op = fscache_alloc_retrieval(cookie, NULL, NULL, NULL);
if (!op)
return -ENOMEM;
trace_fscache_page_op(cookie, NULL, &op->op, fscache_page_op_retr_multi);
spin_lock(&cookie->lock);
if (!fscache_cookie_enabled(cookie) ||
hlist_empty(&cookie->backing_objects))
goto nobufs_unlock;
object = hlist_entry(cookie->backing_objects.first,
struct fscache_object, cookie_link);
__fscache_use_cookie(cookie);
atomic_inc(&object->n_reads);
__set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags);
if (fscache_submit_op(object, &op->op) < 0)
goto nobufs_unlock_dec;
spin_unlock(&cookie->lock);
fscache_stat(&fscache_n_retrieval_ops);
/* we wait for the operation to become active, and then process it
* *here*, in this thread, and not in the thread pool */
ret = fscache_wait_for_operation_activation(
object, &op->op,
__fscache_stat(&fscache_n_retrieval_op_waits),
__fscache_stat(&fscache_n_retrievals_object_dead));
if (ret < 0)
goto error;
/* ask the cache to honour the operation */
ret = object->cache->ops->begin_read_operation(rreq, op);
error:
if (ret == -ENOMEM)
fscache_stat(&fscache_n_retrievals_nomem);
else if (ret == -ERESTARTSYS)
fscache_stat(&fscache_n_retrievals_intr);
else if (ret == -ENODATA)
fscache_stat(&fscache_n_retrievals_nodata);
else if (ret < 0)
fscache_stat(&fscache_n_retrievals_nobufs);
else
fscache_stat(&fscache_n_retrievals_ok);
fscache_put_retrieval(op);
_leave(" = %d", ret);
return ret;
nobufs_unlock_dec:
atomic_dec(&object->n_reads);
wake_cookie = __fscache_unuse_cookie(cookie);
nobufs_unlock:
spin_unlock(&cookie->lock);
fscache_put_retrieval(op);
if (wake_cookie)
__fscache_wake_unused_cookie(cookie);
nobufs:
fscache_stat(&fscache_n_retrievals_nobufs);
_leave(" = -ENOBUFS");
return -ENOBUFS;
}
EXPORT_SYMBOL(__fscache_begin_read_operation);
......@@ -299,7 +299,7 @@ static void fscache_release_retrieval_op(struct fscache_operation *_op)
/*
* allocate a retrieval op
*/
static struct fscache_retrieval *fscache_alloc_retrieval(
struct fscache_retrieval *fscache_alloc_retrieval(
struct fscache_cookie *cookie,
struct address_space *mapping,
fscache_rw_complete_t end_io_func,
......
......@@ -278,5 +278,6 @@ int fscache_stats_show(struct seq_file *m, void *v)
atomic_read(&fscache_n_cache_stale_objects),
atomic_read(&fscache_n_cache_retired_objects),
atomic_read(&fscache_n_cache_culled_objects));
netfs_stats_show(m);
return 0;
}
# SPDX-License-Identifier: GPL-2.0-only
config NETFS_SUPPORT
tristate "Support for network filesystem high-level I/O"
help
This option enables support for network filesystems, including
helpers for high-level buffered I/O, abstracting out read
segmentation, local caching and transparent huge page support.
config NETFS_STATS
bool "Gather statistical information on local caching"
depends on NETFS_SUPPORT && PROC_FS
help
This option causes statistical information to be gathered on local
caching and exported through file:
/proc/fs/fscache/stats
The gathering of statistics adds a certain amount of overhead to
execution as there are a quite a few stats gathered, and on a
multi-CPU system these may be on cachelines that keep bouncing
between CPUs. On the other hand, the stats are very useful for
debugging purposes. Saying 'Y' here is recommended.
# SPDX-License-Identifier: GPL-2.0
netfs-y := read_helper.o stats.o
obj-$(CONFIG_NETFS_SUPPORT) := netfs.o
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Internal definitions for network filesystem support
*
* Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) "netfs: " fmt
/*
* read_helper.c
*/
extern unsigned int netfs_debug;
/*
* stats.c
*/
#ifdef CONFIG_NETFS_STATS
extern atomic_t netfs_n_rh_readahead;
extern atomic_t netfs_n_rh_readpage;
extern atomic_t netfs_n_rh_rreq;
extern atomic_t netfs_n_rh_sreq;
extern atomic_t netfs_n_rh_download;
extern atomic_t netfs_n_rh_download_done;
extern atomic_t netfs_n_rh_download_failed;
extern atomic_t netfs_n_rh_download_instead;
extern atomic_t netfs_n_rh_read;
extern atomic_t netfs_n_rh_read_done;
extern atomic_t netfs_n_rh_read_failed;
extern atomic_t netfs_n_rh_zero;
extern atomic_t netfs_n_rh_short_read;
extern atomic_t netfs_n_rh_write;
extern atomic_t netfs_n_rh_write_begin;
extern atomic_t netfs_n_rh_write_done;
extern atomic_t netfs_n_rh_write_failed;
extern atomic_t netfs_n_rh_write_zskip;
static inline void netfs_stat(atomic_t *stat)
{
atomic_inc(stat);
}
static inline void netfs_stat_d(atomic_t *stat)
{
atomic_dec(stat);
}
#else
#define netfs_stat(x) do {} while(0)
#define netfs_stat_d(x) do {} while(0)
#endif
/*****************************************************************************/
/*
* debug tracing
*/
#define dbgprintk(FMT, ...) \
printk("[%-6.6s] "FMT"\n", current->comm, ##__VA_ARGS__)
#define kenter(FMT, ...) dbgprintk("==> %s("FMT")", __func__, ##__VA_ARGS__)
#define kleave(FMT, ...) dbgprintk("<== %s()"FMT"", __func__, ##__VA_ARGS__)
#define kdebug(FMT, ...) dbgprintk(FMT, ##__VA_ARGS__)
#ifdef __KDEBUG
#define _enter(FMT, ...) kenter(FMT, ##__VA_ARGS__)
#define _leave(FMT, ...) kleave(FMT, ##__VA_ARGS__)
#define _debug(FMT, ...) kdebug(FMT, ##__VA_ARGS__)
#elif defined(CONFIG_NETFS_DEBUG)
#define _enter(FMT, ...) \
do { \
if (netfs_debug) \
kenter(FMT, ##__VA_ARGS__); \
} while (0)
#define _leave(FMT, ...) \
do { \
if (netfs_debug) \
kleave(FMT, ##__VA_ARGS__); \
} while (0)
#define _debug(FMT, ...) \
do { \
if (netfs_debug) \
kdebug(FMT, ##__VA_ARGS__); \
} while (0)
#else
#define _enter(FMT, ...) no_printk("==> %s("FMT")", __func__, ##__VA_ARGS__)
#define _leave(FMT, ...) no_printk("<== %s()"FMT"", __func__, ##__VA_ARGS__)
#define _debug(FMT, ...) no_printk(FMT, ##__VA_ARGS__)
#endif
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-or-later
/* Netfs support statistics
*
* Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#include <linux/export.h>
#include <linux/seq_file.h>
#include <linux/netfs.h>
#include "internal.h"
atomic_t netfs_n_rh_readahead;
atomic_t netfs_n_rh_readpage;
atomic_t netfs_n_rh_rreq;
atomic_t netfs_n_rh_sreq;
atomic_t netfs_n_rh_download;
atomic_t netfs_n_rh_download_done;
atomic_t netfs_n_rh_download_failed;
atomic_t netfs_n_rh_download_instead;
atomic_t netfs_n_rh_read;
atomic_t netfs_n_rh_read_done;
atomic_t netfs_n_rh_read_failed;
atomic_t netfs_n_rh_zero;
atomic_t netfs_n_rh_short_read;
atomic_t netfs_n_rh_write;
atomic_t netfs_n_rh_write_begin;
atomic_t netfs_n_rh_write_done;
atomic_t netfs_n_rh_write_failed;
atomic_t netfs_n_rh_write_zskip;
void netfs_stats_show(struct seq_file *m)
{
seq_printf(m, "RdHelp : RA=%u RP=%u WB=%u WBZ=%u rr=%u sr=%u\n",
atomic_read(&netfs_n_rh_readahead),
atomic_read(&netfs_n_rh_readpage),
atomic_read(&netfs_n_rh_write_begin),
atomic_read(&netfs_n_rh_write_zskip),
atomic_read(&netfs_n_rh_rreq),
atomic_read(&netfs_n_rh_sreq));
seq_printf(m, "RdHelp : ZR=%u sh=%u sk=%u\n",
atomic_read(&netfs_n_rh_zero),
atomic_read(&netfs_n_rh_short_read),
atomic_read(&netfs_n_rh_write_zskip));
seq_printf(m, "RdHelp : DL=%u ds=%u df=%u di=%u\n",
atomic_read(&netfs_n_rh_download),
atomic_read(&netfs_n_rh_download_done),
atomic_read(&netfs_n_rh_download_failed),
atomic_read(&netfs_n_rh_download_instead));
seq_printf(m, "RdHelp : RD=%u rs=%u rf=%u\n",
atomic_read(&netfs_n_rh_read),
atomic_read(&netfs_n_rh_read_done),
atomic_read(&netfs_n_rh_read_failed));
seq_printf(m, "RdHelp : WR=%u ws=%u wf=%u\n",
atomic_read(&netfs_n_rh_write),
atomic_read(&netfs_n_rh_write_done),
atomic_read(&netfs_n_rh_write_failed));
}
EXPORT_SYMBOL(netfs_stats_show);
......@@ -892,18 +892,22 @@ struct fown_struct {
int signum; /* posix.1b rt signal to be delivered on IO */
};
/*
* Track a single file's readahead state
/**
* struct file_ra_state - Track a file's readahead state.
* @start: Where the most recent readahead started.
* @size: Number of pages read in the most recent readahead.
* @async_size: Start next readahead when this many pages are left.
* @ra_pages: Maximum size of a readahead request.
* @mmap_miss: How many mmap accesses missed in the page cache.
* @prev_pos: The last byte in the most recent read request.
*/
struct file_ra_state {
pgoff_t start; /* where readahead started */
unsigned int size; /* # of readahead pages */
unsigned int async_size; /* do asynchronous readahead when
there are only # of pages ahead */
unsigned int ra_pages; /* Maximum readahead window */
unsigned int mmap_miss; /* Cache miss stat for mmap accesses */
loff_t prev_pos; /* Cache last read() position */
pgoff_t start;
unsigned int size;
unsigned int async_size;
unsigned int ra_pages;
unsigned int mmap_miss;
loff_t prev_pos;
};
/*
......
......@@ -304,6 +304,10 @@ struct fscache_cache_ops {
/* dissociate a cache from all the pages it was backing */
void (*dissociate_pages)(struct fscache_cache *cache);
/* Begin a read operation for the netfs lib */
int (*begin_read_operation)(struct netfs_read_request *rreq,
struct fscache_retrieval *op);
};
extern struct fscache_cookie fscache_fsdef_index;
......
......@@ -19,6 +19,7 @@
#include <linux/pagemap.h>
#include <linux/pagevec.h>
#include <linux/list_bl.h>
#include <linux/netfs.h>
#if defined(CONFIG_FSCACHE) || defined(CONFIG_FSCACHE_MODULE)
#define fscache_available() (1)
......@@ -29,16 +30,6 @@
#endif
/*
* overload PG_private_2 to give us PG_fscache - this is used to indicate that
* a page is currently backed by a local disk cache
*/
#define PageFsCache(page) PagePrivate2((page))
#define SetPageFsCache(page) SetPagePrivate2((page))
#define ClearPageFsCache(page) ClearPagePrivate2((page))
#define TestSetPageFsCache(page) TestSetPagePrivate2((page))
#define TestClearPageFsCache(page) TestClearPagePrivate2((page))
/* pattern used to fill dead space in an index entry */
#define FSCACHE_INDEX_DEADFILL_PATTERN 0x79
......@@ -46,6 +37,7 @@ struct pagevec;
struct fscache_cache_tag;
struct fscache_cookie;
struct fscache_netfs;
struct netfs_read_request;
typedef void (*fscache_rw_complete_t)(struct page *page,
void *context,
......@@ -200,6 +192,10 @@ extern void __fscache_update_cookie(struct fscache_cookie *, const void *);
extern int __fscache_attr_changed(struct fscache_cookie *);
extern void __fscache_invalidate(struct fscache_cookie *);
extern void __fscache_wait_on_invalidate(struct fscache_cookie *);
#ifdef FSCACHE_USE_NEW_IO_API
extern int __fscache_begin_read_operation(struct netfs_read_request *, struct fscache_cookie *);
#else
extern int __fscache_read_or_alloc_page(struct fscache_cookie *,
struct page *,
fscache_rw_complete_t,
......@@ -223,6 +219,8 @@ extern void __fscache_uncache_all_inode_pages(struct fscache_cookie *,
struct inode *);
extern void __fscache_readpages_cancel(struct fscache_cookie *cookie,
struct list_head *pages);
#endif /* FSCACHE_USE_NEW_IO_API */
extern void __fscache_disable_cookie(struct fscache_cookie *, const void *, bool);
extern void __fscache_enable_cookie(struct fscache_cookie *, const void *, loff_t,
bool (*)(void *), void *);
......@@ -507,6 +505,36 @@ int fscache_reserve_space(struct fscache_cookie *cookie, loff_t size)
return -ENOBUFS;
}
#ifdef FSCACHE_USE_NEW_IO_API
/**
* fscache_begin_read_operation - Begin a read operation for the netfs lib
* @rreq: The read request being undertaken
* @cookie: The cookie representing the cache object
*
* Begin a read operation on behalf of the netfs helper library. @rreq
* indicates the read request to which the operation state should be attached;
* @cookie indicates the cache object that will be accessed.
*
* This is intended to be called from the ->begin_cache_operation() netfs lib
* operation as implemented by the network filesystem.
*
* Returns:
* * 0 - Success
* * -ENOBUFS - No caching available
* * Other error code from the cache, such as -ENOMEM.
*/
static inline
int fscache_begin_read_operation(struct netfs_read_request *rreq,
struct fscache_cookie *cookie)
{
if (fscache_cookie_valid(cookie) && fscache_cookie_enabled(cookie))
return __fscache_begin_read_operation(rreq, cookie);
return -ENOBUFS;
}
#else /* FSCACHE_USE_NEW_IO_API */
/**
* fscache_read_or_alloc_page - Read a page from the cache or allocate a block
* in which to store it
......@@ -786,6 +814,8 @@ void fscache_uncache_all_inode_pages(struct fscache_cookie *cookie,
__fscache_uncache_all_inode_pages(cookie, inode);
}
#endif /* FSCACHE_USE_NEW_IO_API */
/**
* fscache_disable_cookie - Disable a cookie
* @cookie: The cookie representing the cache object
......
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Network filesystem support services.
*
* Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* See:
*
* Documentation/filesystems/netfs_library.rst
*
* for a description of the network filesystem interface declared here.
*/
#ifndef _LINUX_NETFS_H
#define _LINUX_NETFS_H
#include <linux/workqueue.h>
#include <linux/fs.h>
#include <linux/pagemap.h>
/*
* Overload PG_private_2 to give us PG_fscache - this is used to indicate that
* a page is currently backed by a local disk cache
*/
#define PageFsCache(page) PagePrivate2((page))
#define SetPageFsCache(page) SetPagePrivate2((page))
#define ClearPageFsCache(page) ClearPagePrivate2((page))
#define TestSetPageFsCache(page) TestSetPagePrivate2((page))
#define TestClearPageFsCache(page) TestClearPagePrivate2((page))
/**
* set_page_fscache - Set PG_fscache on a page and take a ref
* @page: The page.
*
* Set the PG_fscache (PG_private_2) flag on a page and take the reference
* needed for the VM to handle its lifetime correctly. This sets the flag and
* takes the reference unconditionally, so care must be taken not to set the
* flag again if it's already set.
*/
static inline void set_page_fscache(struct page *page)
{
set_page_private_2(page);
}
/**
* end_page_fscache - Clear PG_fscache and release any waiters
* @page: The page
*
* Clear the PG_fscache (PG_private_2) bit on a page and wake up any sleepers
* waiting for this. The page ref held for PG_private_2 being set is released.
*
* This is, for example, used when a netfs page is being written to a local
* disk cache, thereby allowing writes to the cache for the same page to be
* serialised.
*/
static inline void end_page_fscache(struct page *page)
{
end_page_private_2(page);
}
/**
* wait_on_page_fscache - Wait for PG_fscache to be cleared on a page
* @page: The page to wait on
*
* Wait for PG_fscache (aka PG_private_2) to be cleared on a page.
*/
static inline void wait_on_page_fscache(struct page *page)
{
wait_on_page_private_2(page);
}
/**
* wait_on_page_fscache_killable - Wait for PG_fscache to be cleared on a page
* @page: The page to wait on
*
* Wait for PG_fscache (aka PG_private_2) to be cleared on a page or until a
* fatal signal is received by the calling task.
*
* Return:
* - 0 if successful.
* - -EINTR if a fatal signal was encountered.
*/
static inline int wait_on_page_fscache_killable(struct page *page)
{
return wait_on_page_private_2_killable(page);
}
enum netfs_read_source {
NETFS_FILL_WITH_ZEROES,
NETFS_DOWNLOAD_FROM_SERVER,
NETFS_READ_FROM_CACHE,
NETFS_INVALID_READ,
} __mode(byte);
typedef void (*netfs_io_terminated_t)(void *priv, ssize_t transferred_or_error,
bool was_async);
/*
* Resources required to do operations on a cache.
*/
struct netfs_cache_resources {
const struct netfs_cache_ops *ops;
void *cache_priv;
void *cache_priv2;
};
/*
* Descriptor for a single component subrequest.
*/
struct netfs_read_subrequest {
struct netfs_read_request *rreq; /* Supervising read request */
struct list_head rreq_link; /* Link in rreq->subrequests */
loff_t start; /* Where to start the I/O */
size_t len; /* Size of the I/O */
size_t transferred; /* Amount of data transferred */
refcount_t usage;
short error; /* 0 or error that occurred */
unsigned short debug_index; /* Index in list (for debugging output) */
enum netfs_read_source source; /* Where to read from */
unsigned long flags;
#define NETFS_SREQ_WRITE_TO_CACHE 0 /* Set if should write to cache */
#define NETFS_SREQ_CLEAR_TAIL 1 /* Set if the rest of the read should be cleared */
#define NETFS_SREQ_SHORT_READ 2 /* Set if there was a short read from the cache */
#define NETFS_SREQ_SEEK_DATA_READ 3 /* Set if ->read() should SEEK_DATA first */
#define NETFS_SREQ_NO_PROGRESS 4 /* Set if we didn't manage to read any data */
};
/*
* Descriptor for a read helper request. This is used to make multiple I/O
* requests on a variety of sources and then stitch the result together.
*/
struct netfs_read_request {
struct work_struct work;
struct inode *inode; /* The file being accessed */
struct address_space *mapping; /* The mapping being accessed */
struct netfs_cache_resources cache_resources;
struct list_head subrequests; /* Requests to fetch I/O from disk or net */
void *netfs_priv; /* Private data for the netfs */
unsigned int debug_id;
unsigned int cookie_debug_id;
atomic_t nr_rd_ops; /* Number of read ops in progress */
atomic_t nr_wr_ops; /* Number of write ops in progress */
size_t submitted; /* Amount submitted for I/O so far */
size_t len; /* Length of the request */
short error; /* 0 or error that occurred */
loff_t i_size; /* Size of the file */
loff_t start; /* Start position */
pgoff_t no_unlock_page; /* Don't unlock this page after read */
refcount_t usage;
unsigned long flags;
#define NETFS_RREQ_INCOMPLETE_IO 0 /* Some ioreqs terminated short or with error */
#define NETFS_RREQ_WRITE_TO_CACHE 1 /* Need to write to the cache */
#define NETFS_RREQ_NO_UNLOCK_PAGE 2 /* Don't unlock no_unlock_page on completion */
#define NETFS_RREQ_DONT_UNLOCK_PAGES 3 /* Don't unlock the pages on completion */
#define NETFS_RREQ_FAILED 4 /* The request failed */
#define NETFS_RREQ_IN_PROGRESS 5 /* Unlocked when the request completes */
const struct netfs_read_request_ops *netfs_ops;
};
/*
* Operations the network filesystem can/must provide to the helpers.
*/
struct netfs_read_request_ops {
bool (*is_cache_enabled)(struct inode *inode);
void (*init_rreq)(struct netfs_read_request *rreq, struct file *file);
int (*begin_cache_operation)(struct netfs_read_request *rreq);
void (*expand_readahead)(struct netfs_read_request *rreq);
bool (*clamp_length)(struct netfs_read_subrequest *subreq);
void (*issue_op)(struct netfs_read_subrequest *subreq);
bool (*is_still_valid)(struct netfs_read_request *rreq);
int (*check_write_begin)(struct file *file, loff_t pos, unsigned len,
struct page *page, void **_fsdata);
void (*done)(struct netfs_read_request *rreq);
void (*cleanup)(struct address_space *mapping, void *netfs_priv);
};
/*
* Table of operations for access to a cache. This is obtained by
* rreq->ops->begin_cache_operation().
*/
struct netfs_cache_ops {
/* End an operation */
void (*end_operation)(struct netfs_cache_resources *cres);
/* Read data from the cache */
int (*read)(struct netfs_cache_resources *cres,
loff_t start_pos,
struct iov_iter *iter,
bool seek_data,
netfs_io_terminated_t term_func,
void *term_func_priv);
/* Write data to the cache */
int (*write)(struct netfs_cache_resources *cres,
loff_t start_pos,
struct iov_iter *iter,
netfs_io_terminated_t term_func,
void *term_func_priv);
/* Expand readahead request */
void (*expand_readahead)(struct netfs_cache_resources *cres,
loff_t *_start, size_t *_len, loff_t i_size);
/* Prepare a read operation, shortening it to a cached/uncached
* boundary as appropriate.
*/
enum netfs_read_source (*prepare_read)(struct netfs_read_subrequest *subreq,
loff_t i_size);
/* Prepare a write operation, working out what part of the write we can
* actually do.
*/
int (*prepare_write)(struct netfs_cache_resources *cres,
loff_t *_start, size_t *_len, loff_t i_size);
};
struct readahead_control;
extern void netfs_readahead(struct readahead_control *,
const struct netfs_read_request_ops *,
void *);
extern int netfs_readpage(struct file *,
struct page *,
const struct netfs_read_request_ops *,
void *);
extern int netfs_write_begin(struct file *, struct address_space *,
loff_t, unsigned int, unsigned int, struct page **,
void **,
const struct netfs_read_request_ops *,
void *);
extern void netfs_subreq_terminated(struct netfs_read_subrequest *, ssize_t, bool);
extern void netfs_stats_show(struct seq_file *);
#endif /* _LINUX_NETFS_H */
......@@ -688,6 +688,26 @@ void wait_for_stable_page(struct page *page);
void page_endio(struct page *page, bool is_write, int err);
/**
* set_page_private_2 - Set PG_private_2 on a page and take a ref
* @page: The page.
*
* Set the PG_private_2 flag on a page and take the reference needed for the VM
* to handle its lifetime correctly. This sets the flag and takes the
* reference unconditionally, so care must be taken not to set the flag again
* if it's already set.
*/
static inline void set_page_private_2(struct page *page)
{
page = compound_head(page);
get_page(page);
SetPagePrivate2(page);
}
void end_page_private_2(struct page *page);
void wait_on_page_private_2(struct page *page);
int wait_on_page_private_2_killable(struct page *page);
/*
* Add an arbitrary waiter to a page's wait queue
*/
......@@ -792,20 +812,23 @@ static inline int add_to_page_cache(struct page *page,
* @file: The file, used primarily by network filesystems for authentication.
* May be NULL if invoked internally by the filesystem.
* @mapping: Readahead this filesystem object.
* @ra: File readahead state. May be NULL.
*/
struct readahead_control {
struct file *file;
struct address_space *mapping;
struct file_ra_state *ra;
/* private: use the readahead_* accessors instead */
pgoff_t _index;
unsigned int _nr_pages;
unsigned int _batch_count;
};
#define DEFINE_READAHEAD(rac, f, m, i) \
struct readahead_control rac = { \
#define DEFINE_READAHEAD(ractl, f, r, m, i) \
struct readahead_control ractl = { \
.file = f, \
.mapping = m, \
.ra = r, \
._index = i, \
}
......@@ -813,10 +836,11 @@ struct readahead_control {
void page_cache_ra_unbounded(struct readahead_control *,
unsigned long nr_to_read, unsigned long lookahead_count);
void page_cache_sync_ra(struct readahead_control *, struct file_ra_state *,
void page_cache_sync_ra(struct readahead_control *, unsigned long req_count);
void page_cache_async_ra(struct readahead_control *, struct page *,
unsigned long req_count);
void page_cache_async_ra(struct readahead_control *, struct file_ra_state *,
struct page *, unsigned long req_count);
void readahead_expand(struct readahead_control *ractl,
loff_t new_start, size_t new_len);
/**
* page_cache_sync_readahead - generic file readahead
......@@ -836,8 +860,8 @@ void page_cache_sync_readahead(struct address_space *mapping,
struct file_ra_state *ra, struct file *file, pgoff_t index,
unsigned long req_count)
{
DEFINE_READAHEAD(ractl, file, mapping, index);
page_cache_sync_ra(&ractl, ra, req_count);
DEFINE_READAHEAD(ractl, file, ra, mapping, index);
page_cache_sync_ra(&ractl, req_count);
}
/**
......@@ -859,8 +883,8 @@ void page_cache_async_readahead(struct address_space *mapping,
struct file_ra_state *ra, struct file *file,
struct page *page, pgoff_t index, unsigned long req_count)
{
DEFINE_READAHEAD(ractl, file, mapping, index);
page_cache_async_ra(&ractl, ra, page, req_count);
DEFINE_READAHEAD(ractl, file, ra, mapping, index);
page_cache_async_ra(&ractl, page, req_count);
}
/**
......
......@@ -24,6 +24,7 @@ enum iter_type {
ITER_BVEC = 16,
ITER_PIPE = 32,
ITER_DISCARD = 64,
ITER_XARRAY = 128,
};
struct iov_iter {
......@@ -39,6 +40,7 @@ struct iov_iter {
const struct iovec *iov;
const struct kvec *kvec;
const struct bio_vec *bvec;
struct xarray *xarray;
struct pipe_inode_info *pipe;
};
union {
......@@ -47,6 +49,7 @@ struct iov_iter {
unsigned int head;
unsigned int start_head;
};
loff_t xarray_start;
};
};
......@@ -80,6 +83,11 @@ static inline bool iov_iter_is_discard(const struct iov_iter *i)
return iov_iter_type(i) == ITER_DISCARD;
}
static inline bool iov_iter_is_xarray(const struct iov_iter *i)
{
return iov_iter_type(i) == ITER_XARRAY;
}
static inline unsigned char iov_iter_rw(const struct iov_iter *i)
{
return i->type & (READ | WRITE);
......@@ -221,6 +229,8 @@ void iov_iter_bvec(struct iov_iter *i, unsigned int direction, const struct bio_
void iov_iter_pipe(struct iov_iter *i, unsigned int direction, struct pipe_inode_info *pipe,
size_t count);
void iov_iter_discard(struct iov_iter *i, unsigned int direction, size_t count);
void iov_iter_xarray(struct iov_iter *i, unsigned int direction, struct xarray *xarray,
loff_t start, size_t count);
ssize_t iov_iter_get_pages(struct iov_iter *i, struct page **pages,
size_t maxsize, unsigned maxpages, size_t *start);
ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, struct page ***pages,
......
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Network filesystem support module tracepoints
*
* Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM netfs
#if !defined(_TRACE_NETFS_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_NETFS_H
#include <linux/tracepoint.h>
/*
* Define enums for tracing information.
*/
#ifndef __NETFS_DECLARE_TRACE_ENUMS_ONCE_ONLY
#define __NETFS_DECLARE_TRACE_ENUMS_ONCE_ONLY
enum netfs_read_trace {
netfs_read_trace_expanded,
netfs_read_trace_readahead,
netfs_read_trace_readpage,
netfs_read_trace_write_begin,
};
enum netfs_rreq_trace {
netfs_rreq_trace_assess,
netfs_rreq_trace_done,
netfs_rreq_trace_free,
netfs_rreq_trace_resubmit,
netfs_rreq_trace_unlock,
netfs_rreq_trace_unmark,
netfs_rreq_trace_write,
};
enum netfs_sreq_trace {
netfs_sreq_trace_download_instead,
netfs_sreq_trace_free,
netfs_sreq_trace_prepare,
netfs_sreq_trace_resubmit_short,
netfs_sreq_trace_submit,
netfs_sreq_trace_terminated,
netfs_sreq_trace_write,
netfs_sreq_trace_write_skip,
netfs_sreq_trace_write_term,
};
enum netfs_failure {
netfs_fail_check_write_begin,
netfs_fail_copy_to_cache,
netfs_fail_read,
netfs_fail_short_readpage,
netfs_fail_short_write_begin,
netfs_fail_prepare_write,
};
#endif
#define netfs_read_traces \
EM(netfs_read_trace_expanded, "EXPANDED ") \
EM(netfs_read_trace_readahead, "READAHEAD") \
EM(netfs_read_trace_readpage, "READPAGE ") \
E_(netfs_read_trace_write_begin, "WRITEBEGN")
#define netfs_rreq_traces \
EM(netfs_rreq_trace_assess, "ASSESS") \
EM(netfs_rreq_trace_done, "DONE ") \
EM(netfs_rreq_trace_free, "FREE ") \
EM(netfs_rreq_trace_resubmit, "RESUBM") \
EM(netfs_rreq_trace_unlock, "UNLOCK") \
EM(netfs_rreq_trace_unmark, "UNMARK") \
E_(netfs_rreq_trace_write, "WRITE ")
#define netfs_sreq_sources \
EM(NETFS_FILL_WITH_ZEROES, "ZERO") \
EM(NETFS_DOWNLOAD_FROM_SERVER, "DOWN") \
EM(NETFS_READ_FROM_CACHE, "READ") \
E_(NETFS_INVALID_READ, "INVL") \
#define netfs_sreq_traces \
EM(netfs_sreq_trace_download_instead, "RDOWN") \
EM(netfs_sreq_trace_free, "FREE ") \
EM(netfs_sreq_trace_prepare, "PREP ") \
EM(netfs_sreq_trace_resubmit_short, "SHORT") \
EM(netfs_sreq_trace_submit, "SUBMT") \
EM(netfs_sreq_trace_terminated, "TERM ") \
EM(netfs_sreq_trace_write, "WRITE") \
EM(netfs_sreq_trace_write_skip, "SKIP ") \
E_(netfs_sreq_trace_write_term, "WTERM")
#define netfs_failures \
EM(netfs_fail_check_write_begin, "check-write-begin") \
EM(netfs_fail_copy_to_cache, "copy-to-cache") \
EM(netfs_fail_read, "read") \
EM(netfs_fail_short_readpage, "short-readpage") \
EM(netfs_fail_short_write_begin, "short-write-begin") \
E_(netfs_fail_prepare_write, "prep-write")
/*
* Export enum symbols via userspace.
*/
#undef EM
#undef E_
#define EM(a, b) TRACE_DEFINE_ENUM(a);
#define E_(a, b) TRACE_DEFINE_ENUM(a);
netfs_read_traces;
netfs_rreq_traces;
netfs_sreq_sources;
netfs_sreq_traces;
netfs_failures;
/*
* Now redefine the EM() and E_() macros to map the enums to the strings that
* will be printed in the output.
*/
#undef EM
#undef E_
#define EM(a, b) { a, b },
#define E_(a, b) { a, b }
TRACE_EVENT(netfs_read,
TP_PROTO(struct netfs_read_request *rreq,
loff_t start, size_t len,
enum netfs_read_trace what),
TP_ARGS(rreq, start, len, what),
TP_STRUCT__entry(
__field(unsigned int, rreq )
__field(unsigned int, cookie )
__field(loff_t, start )
__field(size_t, len )
__field(enum netfs_read_trace, what )
),
TP_fast_assign(
__entry->rreq = rreq->debug_id;
__entry->cookie = rreq->cookie_debug_id;
__entry->start = start;
__entry->len = len;
__entry->what = what;
),
TP_printk("R=%08x %s c=%08x s=%llx %zx",
__entry->rreq,
__print_symbolic(__entry->what, netfs_read_traces),
__entry->cookie,
__entry->start, __entry->len)
);
TRACE_EVENT(netfs_rreq,
TP_PROTO(struct netfs_read_request *rreq,
enum netfs_rreq_trace what),
TP_ARGS(rreq, what),
TP_STRUCT__entry(
__field(unsigned int, rreq )
__field(unsigned short, flags )
__field(enum netfs_rreq_trace, what )
),
TP_fast_assign(
__entry->rreq = rreq->debug_id;
__entry->flags = rreq->flags;
__entry->what = what;
),
TP_printk("R=%08x %s f=%02x",
__entry->rreq,
__print_symbolic(__entry->what, netfs_rreq_traces),
__entry->flags)
);
TRACE_EVENT(netfs_sreq,
TP_PROTO(struct netfs_read_subrequest *sreq,
enum netfs_sreq_trace what),
TP_ARGS(sreq, what),
TP_STRUCT__entry(
__field(unsigned int, rreq )
__field(unsigned short, index )
__field(short, error )
__field(unsigned short, flags )
__field(enum netfs_read_source, source )
__field(enum netfs_sreq_trace, what )
__field(size_t, len )
__field(size_t, transferred )
__field(loff_t, start )
),
TP_fast_assign(
__entry->rreq = sreq->rreq->debug_id;
__entry->index = sreq->debug_index;
__entry->error = sreq->error;
__entry->flags = sreq->flags;
__entry->source = sreq->source;
__entry->what = what;
__entry->len = sreq->len;
__entry->transferred = sreq->transferred;
__entry->start = sreq->start;
),
TP_printk("R=%08x[%u] %s %s f=%02x s=%llx %zx/%zx e=%d",
__entry->rreq, __entry->index,
__print_symbolic(__entry->what, netfs_sreq_traces),
__print_symbolic(__entry->source, netfs_sreq_sources),
__entry->flags,
__entry->start, __entry->transferred, __entry->len,
__entry->error)
);
TRACE_EVENT(netfs_failure,
TP_PROTO(struct netfs_read_request *rreq,
struct netfs_read_subrequest *sreq,
int error, enum netfs_failure what),
TP_ARGS(rreq, sreq, error, what),
TP_STRUCT__entry(
__field(unsigned int, rreq )
__field(unsigned short, index )
__field(short, error )
__field(unsigned short, flags )
__field(enum netfs_read_source, source )
__field(enum netfs_failure, what )
__field(size_t, len )
__field(size_t, transferred )
__field(loff_t, start )
),
TP_fast_assign(
__entry->rreq = rreq->debug_id;
__entry->index = sreq ? sreq->debug_index : 0;
__entry->error = error;
__entry->flags = sreq ? sreq->flags : 0;
__entry->source = sreq ? sreq->source : NETFS_INVALID_READ;
__entry->what = what;
__entry->len = sreq ? sreq->len : 0;
__entry->transferred = sreq ? sreq->transferred : 0;
__entry->start = sreq ? sreq->start : 0;
),
TP_printk("R=%08x[%u] %s f=%02x s=%llx %zx/%zx %s e=%d",
__entry->rreq, __entry->index,
__print_symbolic(__entry->source, netfs_sreq_sources),
__entry->flags,
__entry->start, __entry->transferred, __entry->len,
__print_symbolic(__entry->what, netfs_failures),
__entry->error)
);
#endif /* _TRACE_NETFS_H */
/* This part must be outside protection */
#include <trace/define_trace.h>
This diff is collapsed.
......@@ -1432,6 +1432,67 @@ void unlock_page(struct page *page)
}
EXPORT_SYMBOL(unlock_page);
/**
* end_page_private_2 - Clear PG_private_2 and release any waiters
* @page: The page
*
* Clear the PG_private_2 bit on a page and wake up any sleepers waiting for
* this. The page ref held for PG_private_2 being set is released.
*
* This is, for example, used when a netfs page is being written to a local
* disk cache, thereby allowing writes to the cache for the same page to be
* serialised.
*/
void end_page_private_2(struct page *page)
{
page = compound_head(page);
VM_BUG_ON_PAGE(!PagePrivate2(page), page);
clear_bit_unlock(PG_private_2, &page->flags);
wake_up_page_bit(page, PG_private_2);
put_page(page);
}
EXPORT_SYMBOL(end_page_private_2);
/**
* wait_on_page_private_2 - Wait for PG_private_2 to be cleared on a page
* @page: The page to wait on
*
* Wait for PG_private_2 (aka PG_fscache) to be cleared on a page.
*/
void wait_on_page_private_2(struct page *page)
{
page = compound_head(page);
while (PagePrivate2(page))
wait_on_page_bit(page, PG_private_2);
}
EXPORT_SYMBOL(wait_on_page_private_2);
/**
* wait_on_page_private_2_killable - Wait for PG_private_2 to be cleared on a page
* @page: The page to wait on
*
* Wait for PG_private_2 (aka PG_fscache) to be cleared on a page or until a
* fatal signal is received by the calling task.
*
* Return:
* - 0 if successful.
* - -EINTR if a fatal signal was encountered.
*/
int wait_on_page_private_2_killable(struct page *page)
{
int ret = 0;
page = compound_head(page);
while (PagePrivate2(page)) {
ret = wait_on_page_bit_killable(page, PG_private_2);
if (ret < 0)
break;
}
return ret;
}
EXPORT_SYMBOL(wait_on_page_private_2_killable);
/**
* end_page_writeback - end writeback against a page
* @page: the page
......@@ -2778,7 +2839,7 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf)
struct file *file = vmf->vma->vm_file;
struct file_ra_state *ra = &file->f_ra;
struct address_space *mapping = file->f_mapping;
DEFINE_READAHEAD(ractl, file, mapping, vmf->pgoff);
DEFINE_READAHEAD(ractl, file, ra, mapping, vmf->pgoff);
struct file *fpin = NULL;
unsigned int mmap_miss;
......@@ -2790,7 +2851,7 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf)
if (vmf->vma->vm_flags & VM_SEQ_READ) {
fpin = maybe_unlock_mmap_for_io(vmf, fpin);
page_cache_sync_ra(&ractl, ra, ra->ra_pages);
page_cache_sync_ra(&ractl, ra->ra_pages);
return fpin;
}
......
......@@ -51,13 +51,12 @@ void unmap_page_range(struct mmu_gather *tlb,
void do_page_cache_ra(struct readahead_control *, unsigned long nr_to_read,
unsigned long lookahead_size);
void force_page_cache_ra(struct readahead_control *, struct file_ra_state *,
unsigned long nr);
void force_page_cache_ra(struct readahead_control *, unsigned long nr);
static inline void force_page_cache_readahead(struct address_space *mapping,
struct file *file, pgoff_t index, unsigned long nr_to_read)
{
DEFINE_READAHEAD(ractl, file, mapping, index);
force_page_cache_ra(&ractl, &file->f_ra, nr_to_read);
DEFINE_READAHEAD(ractl, file, &file->f_ra, mapping, index);
force_page_cache_ra(&ractl, nr_to_read);
}
unsigned find_lock_entries(struct address_space *mapping, pgoff_t start,
......
......@@ -198,8 +198,6 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
for (i = 0; i < nr_to_read; i++) {
struct page *page = xa_load(&mapping->i_pages, index + i);
BUG_ON(index + i != ractl->_index + ractl->_nr_pages);
if (page && !xa_is_value(page)) {
/*
* Page already present? Kick off the current batch
......@@ -210,6 +208,7 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
* not worth getting one just for that.
*/
read_pages(ractl, &page_pool, true);
i = ractl->_index + ractl->_nr_pages - index - 1;
continue;
}
......@@ -223,6 +222,7 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
gfp_mask) < 0) {
put_page(page);
read_pages(ractl, &page_pool, true);
i = ractl->_index + ractl->_nr_pages - index - 1;
continue;
}
if (i == nr_to_read - lookahead_size)
......@@ -272,9 +272,10 @@ void do_page_cache_ra(struct readahead_control *ractl,
* memory at once.
*/
void force_page_cache_ra(struct readahead_control *ractl,
struct file_ra_state *ra, unsigned long nr_to_read)
unsigned long nr_to_read)
{
struct address_space *mapping = ractl->mapping;
struct file_ra_state *ra = ractl->ra;
struct backing_dev_info *bdi = inode_to_bdi(mapping->host);
unsigned long max_pages, index;
......@@ -433,10 +434,10 @@ static int try_context_readahead(struct address_space *mapping,
* A minimal readahead algorithm for trivial sequential/random reads.
*/
static void ondemand_readahead(struct readahead_control *ractl,
struct file_ra_state *ra, bool hit_readahead_marker,
unsigned long req_size)
bool hit_readahead_marker, unsigned long req_size)
{
struct backing_dev_info *bdi = inode_to_bdi(ractl->mapping->host);
struct file_ra_state *ra = ractl->ra;
unsigned long max_pages = ra->ra_pages;
unsigned long add_pages;
unsigned long index = readahead_index(ractl);
......@@ -550,7 +551,7 @@ static void ondemand_readahead(struct readahead_control *ractl,
}
void page_cache_sync_ra(struct readahead_control *ractl,
struct file_ra_state *ra, unsigned long req_count)
unsigned long req_count)
{
bool do_forced_ra = ractl->file && (ractl->file->f_mode & FMODE_RANDOM);
......@@ -560,7 +561,7 @@ void page_cache_sync_ra(struct readahead_control *ractl,
* read-ahead will do the right thing and limit the read to just the
* requested range, which we'll set to 1 page for this case.
*/
if (!ra->ra_pages || blk_cgroup_congested()) {
if (!ractl->ra->ra_pages || blk_cgroup_congested()) {
if (!ractl->file)
return;
req_count = 1;
......@@ -569,21 +570,20 @@ void page_cache_sync_ra(struct readahead_control *ractl,
/* be dumb */
if (do_forced_ra) {
force_page_cache_ra(ractl, ra, req_count);
force_page_cache_ra(ractl, req_count);
return;
}
/* do read-ahead */
ondemand_readahead(ractl, ra, false, req_count);
ondemand_readahead(ractl, false, req_count);
}
EXPORT_SYMBOL_GPL(page_cache_sync_ra);
void page_cache_async_ra(struct readahead_control *ractl,
struct file_ra_state *ra, struct page *page,
unsigned long req_count)
struct page *page, unsigned long req_count)
{
/* no read-ahead */
if (!ra->ra_pages)
if (!ractl->ra->ra_pages)
return;
/*
......@@ -604,7 +604,7 @@ void page_cache_async_ra(struct readahead_control *ractl,
return;
/* do read-ahead */
ondemand_readahead(ractl, ra, true, req_count);
ondemand_readahead(ractl, true, req_count);
}
EXPORT_SYMBOL_GPL(page_cache_async_ra);
......@@ -638,3 +638,78 @@ SYSCALL_DEFINE3(readahead, int, fd, loff_t, offset, size_t, count)
{
return ksys_readahead(fd, offset, count);
}
/**
* readahead_expand - Expand a readahead request
* @ractl: The request to be expanded
* @new_start: The revised start
* @new_len: The revised size of the request
*
* Attempt to expand a readahead request outwards from the current size to the
* specified size by inserting locked pages before and after the current window
* to increase the size to the new window. This may involve the insertion of
* THPs, in which case the window may get expanded even beyond what was
* requested.
*
* The algorithm will stop if it encounters a conflicting page already in the
* pagecache and leave a smaller expansion than requested.
*
* The caller must check for this by examining the revised @ractl object for a
* different expansion than was requested.
*/
void readahead_expand(struct readahead_control *ractl,
loff_t new_start, size_t new_len)
{
struct address_space *mapping = ractl->mapping;
struct file_ra_state *ra = ractl->ra;
pgoff_t new_index, new_nr_pages;
gfp_t gfp_mask = readahead_gfp_mask(mapping);
new_index = new_start / PAGE_SIZE;
/* Expand the leading edge downwards */
while (ractl->_index > new_index) {
unsigned long index = ractl->_index - 1;
struct page *page = xa_load(&mapping->i_pages, index);
if (page && !xa_is_value(page))
return; /* Page apparently present */
page = __page_cache_alloc(gfp_mask);
if (!page)
return;
if (add_to_page_cache_lru(page, mapping, index, gfp_mask) < 0) {
put_page(page);
return;
}
ractl->_nr_pages++;
ractl->_index = page->index;
}
new_len += new_start - readahead_pos(ractl);
new_nr_pages = DIV_ROUND_UP(new_len, PAGE_SIZE);
/* Expand the trailing edge upwards */
while (ractl->_nr_pages < new_nr_pages) {
unsigned long index = ractl->_index + ractl->_nr_pages;
struct page *page = xa_load(&mapping->i_pages, index);
if (page && !xa_is_value(page))
return; /* Page apparently present */
page = __page_cache_alloc(gfp_mask);
if (!page)
return;
if (add_to_page_cache_lru(page, mapping, index, gfp_mask) < 0) {
put_page(page);
return;
}
ractl->_nr_pages++;
if (ra) {
ra->size++;
ra->async_size++;
}
}
}
EXPORT_SYMBOL(readahead_expand);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment