* [PATCH v9 00/25] Change readahead API
@ 2020-03-20 14:22 Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 01/25] mm: Move readahead prototypes from mm.h Matthew Wilcox
` (24 more replies)
0 siblings, 25 replies; 46+ messages in thread
From: Matthew Wilcox @ 2020-03-20 14:22 UTC (permalink / raw)
To: Andrew Morton
Cc: Matthew Wilcox (Oracle), linux-fsdevel, linux-mm, linux-kernel,
linux-btrfs, linux-erofs, linux-ext4, linux-f2fs-devel,
cluster-devel, ocfs2-devel, linux-xfs
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
This series adds a readahead address_space operation to replace the
readpages operation. The key difference is that pages are added to the
page cache as they are allocated (and then looked up by the filesystem)
instead of passing them on a list to the readpages operation and having
the filesystem add them to the page cache. It's a net reduction in
code for each implementation, more efficient than walking a list, and
solves the direct-write vs buffered-read problem reported by yu kuai at
https://lore.kernel.org/linux-fsdevel/20200116063601.39201-1-yukuai3@huawei.com/
The only unconverted filesystems are those which use fscache. Their
conversion is pending Dave Howells' rewrite which will make the conversion
substantially easier. This should be completed by the end of the year.
I want to thank the reviewers/testers; Dave Chinner, John Hubbard,
Eric Biggers, Johannes Thumshirn, Dave Sterba, Zi Yan and Christoph
Hellwig have done a marvellous job of providing constructive criticism.
These patches pass an xfstests run on ext4, xfs & btrfs with no
regressions that I can tell (some of the tests seem a little flaky before
and remain flaky afterwards).
This series can also be found at
http://git.infradead.org/users/willy/linux-dax.git/shortlog/refs/tags/readahead_v9
v9: No code changes. Fixed a changelog and added some reviewed-by tags.
v8:
- btrfs, ext4 and xfs all survive an xfstests run (thanks to Kent Overstreet
for providing the ktest framework)
- iomap restructuring dropped due to Christoph's opposition and the
redesign of readahead_page() meaning it wasn't needed any more.
- f2fs_mpage_readpages() made static again
- Made iomap_readahead() comment more useful
- Added kernel-doc for the entire readahead_control API
- Conditionally zero batch_count in readahead_page() (requested by John)
- Hold RCU read lock while iterating over the xarray in readahead_page_batch()
- Iterate over the correct pages in readahead_page_batch()
- Correct the return type of readahead_index() (spotted by Zi Yan)
- Added a 'skip_page' parameter to read_pages for better documentation
purposes and so we can reuse the readahead_control higher in the call
chain in future.
- Removed the use_list bool (requested by Christoph)
- Removed the explicit initialisation of _nr_pages to 0 (requested by
Christoph & John)
- Add comments explaining why nr_to_read is being capped (requested by John)
- Reshuffled some of the patches:
- Split out adding the readahead_control API from the three patches which
added it piecemeal
- Shift the final two mm patches to be with the other mm patches
- Split the f2fs "pass the inode" patch from the "convert to readahead"
patch, like ext4
v7:
- Now passes an xfstests run on ext4!
- Documentation improvements
- Move the readahead prototypes out of mm.h (new patch)
- readahead_for_each* iterators are gone; replaced with readahead_page()
and readahead_page_batch()
- page_cache_readahead_limit() renamed to page_cache_readahead_unbounded()
and arguments changed
- iomap_readahead_actor() restructured differently
- The readahead code no longer uses the word 'offset' to reduce ambiguity
- read_pages() now maintains the rac so we can just call it and continue
instead of mucking around with branches
- More assertions
- More readahead functions return void
v6:
- Name the private members of readahead_control with a leading underscore
(suggested by Christoph Hellwig)
- Fix whitespace in rst file
- Remove misleading comment in btrfs patch
- Add readahead_next() API and use it in iomap
- Add iomap_readahead kerneldoc.
- Fix the mpage_readahead kerneldoc
- Make various readahead functions return void
- Keep readahead_index() and readahead_offset() pointing to the start of
this batch through the body. No current user requires this, but it's
less surprising.
- Add kerneldoc for page_cache_readahead_limit
- Make page_idx an unsigned long, and rename it to just 'i'
- Get rid of page_offset local variable
- Add patch to call memalloc_nofs_save() before allocating pages (suggested
by Michal Hocko)
- Resplit a lot of patches for more logical progression and easier review
(suggested by John Hubbard)
- Added sign-offs where received, and I deemed still relevant
v5 switched to passing a readahead_control struct (mirroring the
writepages_control struct passed to writepages). This has a number of
advantages:
- It fixes a number of bugs in various implementations, eg forgetting to
increment 'start', an off-by-one error in 'nr_pages' or treating 'start'
as a byte offset instead of a page offset.
- It allows us to change the arguments without changing all the
implementations of ->readahead which just call mpage_readahead() or
iomap_readahead()
- Figuring out which pages haven't been attempted by the implementation
is more natural this way.
- There's less code in each implementation.
Matthew Wilcox (Oracle) (25):
mm: Move readahead prototypes from mm.h
mm: Return void from various readahead functions
mm: Ignore return value of ->readpages
mm: Move readahead nr_pages check into read_pages
mm: Add new readahead_control API
mm: Use readahead_control to pass arguments
mm: Rename various 'offset' parameters to 'index'
mm: rename readahead loop variable to 'i'
mm: Remove 'page_offset' from readahead loop
mm: Put readahead pages in cache earlier
mm: Add readahead address space operation
mm: Move end_index check out of readahead loop
mm: Add page_cache_readahead_unbounded
mm: Document why we don't set PageReadahead
mm: Use memalloc_nofs_save in readahead path
fs: Convert mpage_readpages to mpage_readahead
btrfs: Convert from readpages to readahead
erofs: Convert uncompressed files from readpages to readahead
erofs: Convert compressed files from readpages to readahead
ext4: Convert from readpages to readahead
ext4: Pass the inode to ext4_mpage_readpages
f2fs: Convert from readpages to readahead
f2fs: Pass the inode to f2fs_mpage_readpages
fuse: Convert from readpages to readahead
iomap: Convert from readpages to readahead
Documentation/filesystems/locking.rst | 6 +-
Documentation/filesystems/vfs.rst | 15 ++
block/blk-core.c | 1 +
drivers/staging/exfat/exfat_super.c | 7 +-
fs/block_dev.c | 7 +-
fs/btrfs/extent_io.c | 46 ++---
fs/btrfs/extent_io.h | 3 +-
fs/btrfs/inode.c | 16 +-
fs/erofs/data.c | 39 ++--
fs/erofs/zdata.c | 29 +--
fs/ext2/inode.c | 10 +-
fs/ext4/ext4.h | 5 +-
fs/ext4/inode.c | 21 +-
fs/ext4/readpage.c | 25 +--
fs/ext4/verity.c | 35 +---
fs/f2fs/data.c | 50 ++---
fs/f2fs/f2fs.h | 3 -
fs/f2fs/verity.c | 35 +---
fs/fat/inode.c | 7 +-
fs/fuse/file.c | 46 ++---
fs/gfs2/aops.c | 23 +--
fs/hpfs/file.c | 7 +-
fs/iomap/buffered-io.c | 92 +++------
fs/iomap/trace.h | 2 +-
fs/isofs/inode.c | 7 +-
fs/jfs/inode.c | 7 +-
fs/mpage.c | 38 +---
fs/nilfs2/inode.c | 15 +-
fs/ocfs2/aops.c | 34 ++--
fs/omfs/file.c | 7 +-
fs/qnx6/inode.c | 7 +-
fs/reiserfs/inode.c | 8 +-
fs/udf/inode.c | 7 +-
fs/xfs/xfs_aops.c | 13 +-
fs/zonefs/super.c | 7 +-
include/linux/fs.h | 2 +
include/linux/iomap.h | 3 +-
include/linux/mm.h | 19 --
include/linux/mpage.h | 4 +-
include/linux/pagemap.h | 151 ++++++++++++++
include/trace/events/erofs.h | 6 +-
include/trace/events/f2fs.h | 6 +-
mm/fadvise.c | 6 +-
mm/internal.h | 12 +-
mm/migrate.c | 2 +-
mm/readahead.c | 278 ++++++++++++++++----------
46 files changed, 580 insertions(+), 589 deletions(-)
base-commit: 11a48a5a18c63fd7621bb050228cebf13566e4d8
--
2.25.1
^ permalink raw reply [flat|nested] 46+ messages in thread
* [PATCH v9 01/25] mm: Move readahead prototypes from mm.h
2020-03-20 14:22 [PATCH v9 00/25] Change readahead API Matthew Wilcox
@ 2020-03-20 14:22 ` Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 02/25] mm: Return void from various readahead functions Matthew Wilcox
` (23 subsequent siblings)
24 siblings, 0 replies; 46+ messages in thread
From: Matthew Wilcox @ 2020-03-20 14:22 UTC (permalink / raw)
To: Andrew Morton
Cc: Matthew Wilcox (Oracle), linux-fsdevel, linux-mm, linux-kernel,
linux-btrfs, linux-erofs, linux-ext4, linux-f2fs-devel,
cluster-devel, ocfs2-devel, linux-xfs, John Hubbard,
Christoph Hellwig, William Kucharski
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
The readahead code is part of the page cache so should be found in the
pagemap.h file. force_page_cache_readahead is only used within mm,
so move it to mm/internal.h instead. Remove the parameter names where
they add no value, and rename the ones which were actively misleading.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: John Hubbard <jhubbard@nvidia.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: William Kucharski <william.kucharski@oracle.com>
---
block/blk-core.c | 1 +
include/linux/mm.h | 19 -------------------
include/linux/pagemap.h | 8 ++++++++
mm/fadvise.c | 2 ++
mm/internal.h | 2 ++
5 files changed, 13 insertions(+), 19 deletions(-)
diff --git a/block/blk-core.c b/block/blk-core.c
index 089e890ab208..41417bb93634 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -20,6 +20,7 @@
#include <linux/blk-mq.h>
#include <linux/highmem.h>
#include <linux/mm.h>
+#include <linux/pagemap.h>
#include <linux/kernel_stat.h>
#include <linux/string.h>
#include <linux/init.h>
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 52269e56c514..68dcda9a2112 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2401,25 +2401,6 @@ extern vm_fault_t filemap_page_mkwrite(struct vm_fault *vmf);
int __must_check write_one_page(struct page *page);
void task_dirty_inc(struct task_struct *tsk);
-/* readahead.c */
-#define VM_READAHEAD_PAGES (SZ_128K / PAGE_SIZE)
-
-int force_page_cache_readahead(struct address_space *mapping, struct file *filp,
- pgoff_t offset, unsigned long nr_to_read);
-
-void page_cache_sync_readahead(struct address_space *mapping,
- struct file_ra_state *ra,
- struct file *filp,
- pgoff_t offset,
- unsigned long size);
-
-void page_cache_async_readahead(struct address_space *mapping,
- struct file_ra_state *ra,
- struct file *filp,
- struct page *pg,
- pgoff_t offset,
- unsigned long size);
-
extern unsigned long stack_guard_gap;
/* Generic expand stack which grows the stack according to GROWS{UP,DOWN} */
extern int expand_stack(struct vm_area_struct *vma, unsigned long address);
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index ccb14b6a16b5..24894b9b90c9 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -614,6 +614,14 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask);
void delete_from_page_cache_batch(struct address_space *mapping,
struct pagevec *pvec);
+#define VM_READAHEAD_PAGES (SZ_128K / PAGE_SIZE)
+
+void page_cache_sync_readahead(struct address_space *, struct file_ra_state *,
+ struct file *, pgoff_t index, unsigned long req_count);
+void page_cache_async_readahead(struct address_space *, struct file_ra_state *,
+ struct file *, struct page *, pgoff_t index,
+ unsigned long req_count);
+
/*
* Like add_to_page_cache_locked, but used to add newly allocated pages:
* the page is new, so we can just run __SetPageLocked() against it.
diff --git a/mm/fadvise.c b/mm/fadvise.c
index 4f17c83db575..3efebfb9952c 100644
--- a/mm/fadvise.c
+++ b/mm/fadvise.c
@@ -22,6 +22,8 @@
#include <asm/unistd.h>
+#include "internal.h"
+
/*
* POSIX_FADV_WILLNEED could set PG_Referenced, and POSIX_FADV_NOREUSE could
* deactivate the pages and clear PG_Referenced.
diff --git a/mm/internal.h b/mm/internal.h
index 3cf20ab3ca01..83f353e74654 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -49,6 +49,8 @@ void unmap_page_range(struct mmu_gather *tlb,
unsigned long addr, unsigned long end,
struct zap_details *details);
+int force_page_cache_readahead(struct address_space *, struct file *,
+ pgoff_t index, unsigned long nr_to_read);
extern unsigned int __do_page_cache_readahead(struct address_space *mapping,
struct file *filp, pgoff_t offset, unsigned long nr_to_read,
unsigned long lookahead_size);
--
2.25.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v9 02/25] mm: Return void from various readahead functions
2020-03-20 14:22 [PATCH v9 00/25] Change readahead API Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 01/25] mm: Move readahead prototypes from mm.h Matthew Wilcox
@ 2020-03-20 14:22 ` Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 03/25] mm: Ignore return value of ->readpages Matthew Wilcox
` (22 subsequent siblings)
24 siblings, 0 replies; 46+ messages in thread
From: Matthew Wilcox @ 2020-03-20 14:22 UTC (permalink / raw)
To: Andrew Morton
Cc: Matthew Wilcox (Oracle), linux-fsdevel, linux-mm, linux-kernel,
linux-btrfs, linux-erofs, linux-ext4, linux-f2fs-devel,
cluster-devel, ocfs2-devel, linux-xfs, Dave Chinner, John Hubbard,
Christoph Hellwig, William Kucharski
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
ondemand_readahead has two callers, neither of which use the return value.
That means that both ra_submit and __do_page_cache_readahead() can return
void, and we don't need to worry that a present page in the readahead
window causes us to return a smaller nr_pages than we ought to have.
Similarly, no caller uses the return value from force_page_cache_readahead().
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: John Hubbard <jhubbard@nvidia.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: William Kucharski <william.kucharski@oracle.com>
---
mm/fadvise.c | 4 ----
mm/internal.h | 12 ++++++------
mm/readahead.c | 31 +++++++++++++------------------
3 files changed, 19 insertions(+), 28 deletions(-)
diff --git a/mm/fadvise.c b/mm/fadvise.c
index 3efebfb9952c..0e66f2aaeea3 100644
--- a/mm/fadvise.c
+++ b/mm/fadvise.c
@@ -104,10 +104,6 @@ int generic_fadvise(struct file *file, loff_t offset, loff_t len, int advice)
if (!nrpages)
nrpages = ~0UL;
- /*
- * Ignore return value because fadvise() shall return
- * success even if filesystem can't retrieve a hint,
- */
force_page_cache_readahead(mapping, file, start_index, nrpages);
break;
case POSIX_FADV_NOREUSE:
diff --git a/mm/internal.h b/mm/internal.h
index 83f353e74654..15aaebebd768 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -49,20 +49,20 @@ void unmap_page_range(struct mmu_gather *tlb,
unsigned long addr, unsigned long end,
struct zap_details *details);
-int force_page_cache_readahead(struct address_space *, struct file *,
+void force_page_cache_readahead(struct address_space *, struct file *,
pgoff_t index, unsigned long nr_to_read);
-extern unsigned int __do_page_cache_readahead(struct address_space *mapping,
- struct file *filp, pgoff_t offset, unsigned long nr_to_read,
+void __do_page_cache_readahead(struct address_space *, struct file *,
+ pgoff_t index, unsigned long nr_to_read,
unsigned long lookahead_size);
/*
* Submit IO for the read-ahead request in file_ra_state.
*/
-static inline unsigned long ra_submit(struct file_ra_state *ra,
+static inline void ra_submit(struct file_ra_state *ra,
struct address_space *mapping, struct file *filp)
{
- return __do_page_cache_readahead(mapping, filp,
- ra->start, ra->size, ra->async_size);
+ __do_page_cache_readahead(mapping, filp,
+ ra->start, ra->size, ra->async_size);
}
/*
diff --git a/mm/readahead.c b/mm/readahead.c
index 2fe72cd29b47..41a592886da7 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -149,10 +149,8 @@ static int read_pages(struct address_space *mapping, struct file *filp,
* the pages first, then submits them for I/O. This avoids the very bad
* behaviour which would occur if page allocations are causing VM writeback.
* We really don't want to intermingle reads and writes like that.
- *
- * Returns the number of pages requested, or the maximum amount of I/O allowed.
*/
-unsigned int __do_page_cache_readahead(struct address_space *mapping,
+void __do_page_cache_readahead(struct address_space *mapping,
struct file *filp, pgoff_t offset, unsigned long nr_to_read,
unsigned long lookahead_size)
{
@@ -166,7 +164,7 @@ unsigned int __do_page_cache_readahead(struct address_space *mapping,
gfp_t gfp_mask = readahead_gfp_mask(mapping);
if (isize == 0)
- goto out;
+ return;
end_index = ((isize - 1) >> PAGE_SHIFT);
@@ -211,23 +209,21 @@ unsigned int __do_page_cache_readahead(struct address_space *mapping,
if (nr_pages)
read_pages(mapping, filp, &page_pool, nr_pages, gfp_mask);
BUG_ON(!list_empty(&page_pool));
-out:
- return nr_pages;
}
/*
* Chunk the readahead into 2 megabyte units, so that we don't pin too much
* memory at once.
*/
-int force_page_cache_readahead(struct address_space *mapping, struct file *filp,
- pgoff_t offset, unsigned long nr_to_read)
+void force_page_cache_readahead(struct address_space *mapping,
+ struct file *filp, pgoff_t offset, unsigned long nr_to_read)
{
struct backing_dev_info *bdi = inode_to_bdi(mapping->host);
struct file_ra_state *ra = &filp->f_ra;
unsigned long max_pages;
if (unlikely(!mapping->a_ops->readpage && !mapping->a_ops->readpages))
- return -EINVAL;
+ return;
/*
* If the request exceeds the readahead window, allow the read to
@@ -245,7 +241,6 @@ int force_page_cache_readahead(struct address_space *mapping, struct file *filp,
offset += this_chunk;
nr_to_read -= this_chunk;
}
- return 0;
}
/*
@@ -378,11 +373,10 @@ static int try_context_readahead(struct address_space *mapping,
/*
* A minimal readahead algorithm for trivial sequential/random reads.
*/
-static unsigned long
-ondemand_readahead(struct address_space *mapping,
- struct file_ra_state *ra, struct file *filp,
- bool hit_readahead_marker, pgoff_t offset,
- unsigned long req_size)
+static void ondemand_readahead(struct address_space *mapping,
+ struct file_ra_state *ra, struct file *filp,
+ bool hit_readahead_marker, pgoff_t offset,
+ unsigned long req_size)
{
struct backing_dev_info *bdi = inode_to_bdi(mapping->host);
unsigned long max_pages = ra->ra_pages;
@@ -428,7 +422,7 @@ ondemand_readahead(struct address_space *mapping,
rcu_read_unlock();
if (!start || start - offset > max_pages)
- return 0;
+ return;
ra->start = start;
ra->size = start - offset; /* old async_size */
@@ -464,7 +458,8 @@ ondemand_readahead(struct address_space *mapping,
* standalone, small random read
* Read as is, and do not pollute the readahead state.
*/
- return __do_page_cache_readahead(mapping, filp, offset, req_size, 0);
+ __do_page_cache_readahead(mapping, filp, offset, req_size, 0);
+ return;
initial_readahead:
ra->start = offset;
@@ -489,7 +484,7 @@ ondemand_readahead(struct address_space *mapping,
}
}
- return ra_submit(ra, mapping, filp);
+ ra_submit(ra, mapping, filp);
}
/**
--
2.25.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v9 03/25] mm: Ignore return value of ->readpages
2020-03-20 14:22 [PATCH v9 00/25] Change readahead API Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 01/25] mm: Move readahead prototypes from mm.h Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 02/25] mm: Return void from various readahead functions Matthew Wilcox
@ 2020-03-20 14:22 ` Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 04/25] mm: Move readahead nr_pages check into read_pages Matthew Wilcox
` (21 subsequent siblings)
24 siblings, 0 replies; 46+ messages in thread
From: Matthew Wilcox @ 2020-03-20 14:22 UTC (permalink / raw)
To: Andrew Morton
Cc: Matthew Wilcox (Oracle), linux-fsdevel, linux-mm, linux-kernel,
linux-btrfs, linux-erofs, linux-ext4, linux-f2fs-devel,
cluster-devel, ocfs2-devel, linux-xfs, Christoph Hellwig,
Dave Chinner, John Hubbard, William Kucharski
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
We used to assign the return value to a variable, which we then ignored.
Remove the pretence of caring.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: John Hubbard <jhubbard@nvidia.com>
Reviewed-by: William Kucharski <william.kucharski@oracle.com>
---
mm/readahead.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/mm/readahead.c b/mm/readahead.c
index 41a592886da7..61b15b6b9e72 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -113,17 +113,16 @@ int read_cache_pages(struct address_space *mapping, struct list_head *pages,
EXPORT_SYMBOL(read_cache_pages);
-static int read_pages(struct address_space *mapping, struct file *filp,
+static void read_pages(struct address_space *mapping, struct file *filp,
struct list_head *pages, unsigned int nr_pages, gfp_t gfp)
{
struct blk_plug plug;
unsigned page_idx;
- int ret;
blk_start_plug(&plug);
if (mapping->a_ops->readpages) {
- ret = mapping->a_ops->readpages(filp, mapping, pages, nr_pages);
+ mapping->a_ops->readpages(filp, mapping, pages, nr_pages);
/* Clean up the remaining pages */
put_pages_list(pages);
goto out;
@@ -136,12 +135,9 @@ static int read_pages(struct address_space *mapping, struct file *filp,
mapping->a_ops->readpage(filp, page);
put_page(page);
}
- ret = 0;
out:
blk_finish_plug(&plug);
-
- return ret;
}
/*
--
2.25.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v9 04/25] mm: Move readahead nr_pages check into read_pages
2020-03-20 14:22 [PATCH v9 00/25] Change readahead API Matthew Wilcox
` (2 preceding siblings ...)
2020-03-20 14:22 ` [PATCH v9 03/25] mm: Ignore return value of ->readpages Matthew Wilcox
@ 2020-03-20 14:22 ` Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 05/25] mm: Add new readahead_control API Matthew Wilcox
` (20 subsequent siblings)
24 siblings, 0 replies; 46+ messages in thread
From: Matthew Wilcox @ 2020-03-20 14:22 UTC (permalink / raw)
To: Andrew Morton
Cc: Matthew Wilcox (Oracle), linux-fsdevel, linux-mm, linux-kernel,
linux-btrfs, linux-erofs, linux-ext4, linux-f2fs-devel,
cluster-devel, ocfs2-devel, linux-xfs, Zi Yan, John Hubbard,
Christoph Hellwig, William Kucharski
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
Simplify the callers by moving the check for nr_pages and the BUG_ON
into read_pages().
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Zi Yan <ziy@nvidia.com>
Reviewed-by: John Hubbard <jhubbard@nvidia.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: William Kucharski <william.kucharski@oracle.com>
---
mm/readahead.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/mm/readahead.c b/mm/readahead.c
index 61b15b6b9e72..9fcd4e32b62d 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -119,6 +119,9 @@ static void read_pages(struct address_space *mapping, struct file *filp,
struct blk_plug plug;
unsigned page_idx;
+ if (!nr_pages)
+ return;
+
blk_start_plug(&plug);
if (mapping->a_ops->readpages) {
@@ -138,6 +141,8 @@ static void read_pages(struct address_space *mapping, struct file *filp,
out:
blk_finish_plug(&plug);
+
+ BUG_ON(!list_empty(pages));
}
/*
@@ -180,8 +185,7 @@ void __do_page_cache_readahead(struct address_space *mapping,
* contiguous pages before continuing with the next
* batch.
*/
- if (nr_pages)
- read_pages(mapping, filp, &page_pool, nr_pages,
+ read_pages(mapping, filp, &page_pool, nr_pages,
gfp_mask);
nr_pages = 0;
continue;
@@ -202,9 +206,7 @@ void __do_page_cache_readahead(struct address_space *mapping,
* uptodate then the caller will launch readpage again, and
* will then handle the error.
*/
- if (nr_pages)
- read_pages(mapping, filp, &page_pool, nr_pages, gfp_mask);
- BUG_ON(!list_empty(&page_pool));
+ read_pages(mapping, filp, &page_pool, nr_pages, gfp_mask);
}
/*
--
2.25.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v9 05/25] mm: Add new readahead_control API
2020-03-20 14:22 [PATCH v9 00/25] Change readahead API Matthew Wilcox
` (3 preceding siblings ...)
2020-03-20 14:22 ` [PATCH v9 04/25] mm: Move readahead nr_pages check into read_pages Matthew Wilcox
@ 2020-03-20 14:22 ` Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 06/25] mm: Use readahead_control to pass arguments Matthew Wilcox
` (19 subsequent siblings)
24 siblings, 0 replies; 46+ messages in thread
From: Matthew Wilcox @ 2020-03-20 14:22 UTC (permalink / raw)
To: Andrew Morton
Cc: Matthew Wilcox (Oracle), linux-fsdevel, linux-mm, linux-kernel,
linux-btrfs, linux-erofs, linux-ext4, linux-f2fs-devel,
cluster-devel, ocfs2-devel, linux-xfs, Christoph Hellwig,
William Kucharski
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
Filesystems which implement the upcoming ->readahead method will get
their pages by calling readahead_page() or readahead_page_batch().
These functions support large pages, even though none of the filesystems
to be converted do yet.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: William Kucharski <william.kucharski@oracle.com>
---
include/linux/pagemap.h | 140 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 140 insertions(+)
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 24894b9b90c9..232892d37071 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -638,6 +638,146 @@ static inline int add_to_page_cache(struct page *page,
return error;
}
+/**
+ * struct readahead_control - Describes a readahead request.
+ *
+ * A readahead request is for consecutive pages. Filesystems which
+ * implement the ->readahead method should call readahead_page() or
+ * readahead_page_batch() in a loop and attempt to start I/O against
+ * each page in the request.
+ *
+ * Most of the fields in this struct are private and should be accessed
+ * by the functions below.
+ *
+ * @file: The file, used primarily by network filesystems for authentication.
+ * May be NULL if invoked internally by the filesystem.
+ * @mapping: Readahead this filesystem object.
+ */
+struct readahead_control {
+ struct file *file;
+ struct address_space *mapping;
+/* private: use the readahead_* accessors instead */
+ pgoff_t _index;
+ unsigned int _nr_pages;
+ unsigned int _batch_count;
+};
+
+/**
+ * readahead_page - Get the next page to read.
+ * @rac: The current readahead request.
+ *
+ * Context: The page is locked and has an elevated refcount. The caller
+ * should decreases the refcount once the page has been submitted for I/O
+ * and unlock the page once all I/O to that page has completed.
+ * Return: A pointer to the next page, or %NULL if we are done.
+ */
+static inline struct page *readahead_page(struct readahead_control *rac)
+{
+ struct page *page;
+
+ BUG_ON(rac->_batch_count > rac->_nr_pages);
+ rac->_nr_pages -= rac->_batch_count;
+ rac->_index += rac->_batch_count;
+
+ if (!rac->_nr_pages) {
+ rac->_batch_count = 0;
+ return NULL;
+ }
+
+ page = xa_load(&rac->mapping->i_pages, rac->_index);
+ VM_BUG_ON_PAGE(!PageLocked(page), page);
+ rac->_batch_count = hpage_nr_pages(page);
+
+ return page;
+}
+
+static inline unsigned int __readahead_batch(struct readahead_control *rac,
+ struct page **array, unsigned int array_sz)
+{
+ unsigned int i = 0;
+ XA_STATE(xas, &rac->mapping->i_pages, 0);
+ struct page *page;
+
+ BUG_ON(rac->_batch_count > rac->_nr_pages);
+ rac->_nr_pages -= rac->_batch_count;
+ rac->_index += rac->_batch_count;
+ rac->_batch_count = 0;
+
+ xas_set(&xas, rac->_index);
+ rcu_read_lock();
+ xas_for_each(&xas, page, rac->_index + rac->_nr_pages - 1) {
+ VM_BUG_ON_PAGE(!PageLocked(page), page);
+ VM_BUG_ON_PAGE(PageTail(page), page);
+ array[i++] = page;
+ rac->_batch_count += hpage_nr_pages(page);
+
+ /*
+ * The page cache isn't using multi-index entries yet,
+ * so the xas cursor needs to be manually moved to the
+ * next index. This can be removed once the page cache
+ * is converted.
+ */
+ if (PageHead(page))
+ xas_set(&xas, rac->_index + rac->_batch_count);
+
+ if (i == array_sz)
+ break;
+ }
+ rcu_read_unlock();
+
+ return i;
+}
+
+/**
+ * readahead_page_batch - Get a batch of pages to read.
+ * @rac: The current readahead request.
+ * @array: An array of pointers to struct page.
+ *
+ * Context: The pages are locked and have an elevated refcount. The caller
+ * should decreases the refcount once the page has been submitted for I/O
+ * and unlock the page once all I/O to that page has completed.
+ * Return: The number of pages placed in the array. 0 indicates the request
+ * is complete.
+ */
+#define readahead_page_batch(rac, array) \
+ __readahead_batch(rac, array, ARRAY_SIZE(array))
+
+/**
+ * readahead_pos - The byte offset into the file of this readahead request.
+ * @rac: The readahead request.
+ */
+static inline loff_t readahead_pos(struct readahead_control *rac)
+{
+ return (loff_t)rac->_index * PAGE_SIZE;
+}
+
+/**
+ * readahead_length - The number of bytes in this readahead request.
+ * @rac: The readahead request.
+ */
+static inline loff_t readahead_length(struct readahead_control *rac)
+{
+ return (loff_t)rac->_nr_pages * PAGE_SIZE;
+}
+
+/**
+ * readahead_index - The index of the first page in this readahead request.
+ * @rac: The readahead request.
+ */
+static inline pgoff_t readahead_index(struct readahead_control *rac)
+{
+ return rac->_index;
+}
+
+/**
+ * readahead_count - The number of pages in this readahead request.
+ * @rac: The readahead request.
+ */
+static inline unsigned int readahead_count(struct readahead_control *rac)
+{
+ return rac->_nr_pages;
+}
+
static inline unsigned long dir_pages(struct inode *inode)
{
return (unsigned long)(inode->i_size + PAGE_SIZE - 1) >>
--
2.25.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v9 06/25] mm: Use readahead_control to pass arguments
2020-03-20 14:22 [PATCH v9 00/25] Change readahead API Matthew Wilcox
` (4 preceding siblings ...)
2020-03-20 14:22 ` [PATCH v9 05/25] mm: Add new readahead_control API Matthew Wilcox
@ 2020-03-20 14:22 ` Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 07/25] mm: Rename various 'offset' parameters to 'index' Matthew Wilcox
` (18 subsequent siblings)
24 siblings, 0 replies; 46+ messages in thread
From: Matthew Wilcox @ 2020-03-20 14:22 UTC (permalink / raw)
To: Andrew Morton
Cc: Matthew Wilcox (Oracle), linux-fsdevel, linux-mm, linux-kernel,
linux-btrfs, linux-erofs, linux-ext4, linux-f2fs-devel,
cluster-devel, ocfs2-devel, linux-xfs, John Hubbard,
Christoph Hellwig, William Kucharski
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
In this patch, only between __do_page_cache_readahead() and read_pages(),
but it will be extended in upcoming patches. The read_pages() function
becomes aops centric, as this makes the most sense by the end of the
patchset.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: John Hubbard <jhubbard@nvidia.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: William Kucharski <william.kucharski@oracle.com>
---
mm/readahead.c | 33 +++++++++++++++++++--------------
1 file changed, 19 insertions(+), 14 deletions(-)
diff --git a/mm/readahead.c b/mm/readahead.c
index 9fcd4e32b62d..9d9aa4ffc7d4 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -113,29 +113,32 @@ int read_cache_pages(struct address_space *mapping, struct list_head *pages,
EXPORT_SYMBOL(read_cache_pages);
-static void read_pages(struct address_space *mapping, struct file *filp,
- struct list_head *pages, unsigned int nr_pages, gfp_t gfp)
+static void read_pages(struct readahead_control *rac, struct list_head *pages,
+ gfp_t gfp)
{
+ const struct address_space_operations *aops = rac->mapping->a_ops;
struct blk_plug plug;
unsigned page_idx;
- if (!nr_pages)
+ if (!readahead_count(rac))
return;
blk_start_plug(&plug);
- if (mapping->a_ops->readpages) {
- mapping->a_ops->readpages(filp, mapping, pages, nr_pages);
+ if (aops->readpages) {
+ aops->readpages(rac->file, rac->mapping, pages,
+ readahead_count(rac));
/* Clean up the remaining pages */
put_pages_list(pages);
goto out;
}
- for (page_idx = 0; page_idx < nr_pages; page_idx++) {
+ for (page_idx = 0; page_idx < readahead_count(rac); page_idx++) {
struct page *page = lru_to_page(pages);
list_del(&page->lru);
- if (!add_to_page_cache_lru(page, mapping, page->index, gfp))
- mapping->a_ops->readpage(filp, page);
+ if (!add_to_page_cache_lru(page, rac->mapping, page->index,
+ gfp))
+ aops->readpage(rac->file, page);
put_page(page);
}
@@ -143,6 +146,7 @@ static void read_pages(struct address_space *mapping, struct file *filp,
blk_finish_plug(&plug);
BUG_ON(!list_empty(pages));
+ rac->_nr_pages = 0;
}
/*
@@ -160,9 +164,12 @@ void __do_page_cache_readahead(struct address_space *mapping,
unsigned long end_index; /* The last page we want to read */
LIST_HEAD(page_pool);
int page_idx;
- unsigned int nr_pages = 0;
loff_t isize = i_size_read(inode);
gfp_t gfp_mask = readahead_gfp_mask(mapping);
+ struct readahead_control rac = {
+ .mapping = mapping,
+ .file = filp,
+ };
if (isize == 0)
return;
@@ -185,9 +192,7 @@ void __do_page_cache_readahead(struct address_space *mapping,
* contiguous pages before continuing with the next
* batch.
*/
- read_pages(mapping, filp, &page_pool, nr_pages,
- gfp_mask);
- nr_pages = 0;
+ read_pages(&rac, &page_pool, gfp_mask);
continue;
}
@@ -198,7 +203,7 @@ void __do_page_cache_readahead(struct address_space *mapping,
list_add(&page->lru, &page_pool);
if (page_idx == nr_to_read - lookahead_size)
SetPageReadahead(page);
- nr_pages++;
+ rac._nr_pages++;
}
/*
@@ -206,7 +211,7 @@ void __do_page_cache_readahead(struct address_space *mapping,
* uptodate then the caller will launch readpage again, and
* will then handle the error.
*/
- read_pages(mapping, filp, &page_pool, nr_pages, gfp_mask);
+ read_pages(&rac, &page_pool, gfp_mask);
}
/*
--
2.25.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v9 07/25] mm: Rename various 'offset' parameters to 'index'
2020-03-20 14:22 [PATCH v9 00/25] Change readahead API Matthew Wilcox
` (5 preceding siblings ...)
2020-03-20 14:22 ` [PATCH v9 06/25] mm: Use readahead_control to pass arguments Matthew Wilcox
@ 2020-03-20 14:22 ` Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 08/25] mm: rename readahead loop variable to 'i' Matthew Wilcox
` (17 subsequent siblings)
24 siblings, 0 replies; 46+ messages in thread
From: Matthew Wilcox @ 2020-03-20 14:22 UTC (permalink / raw)
To: Andrew Morton
Cc: Matthew Wilcox (Oracle), linux-fsdevel, linux-mm, linux-kernel,
linux-btrfs, linux-erofs, linux-ext4, linux-f2fs-devel,
cluster-devel, ocfs2-devel, linux-xfs, Zi Yan, William Kucharski
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
The word 'offset' is used ambiguously to mean 'byte offset within
a page', 'byte offset from the start of the file' and 'page offset
from the start of the file'. Use 'index' to mean 'page offset
from the start of the file' throughout the readahead code.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Zi Yan <ziy@nvidia.com>
Reviewed-by: William Kucharski <william.kucharski@oracle.com>
---
mm/readahead.c | 86 ++++++++++++++++++++++++--------------------------
1 file changed, 42 insertions(+), 44 deletions(-)
diff --git a/mm/readahead.c b/mm/readahead.c
index 9d9aa4ffc7d4..8a65d6bd97e0 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -156,7 +156,7 @@ static void read_pages(struct readahead_control *rac, struct list_head *pages,
* We really don't want to intermingle reads and writes like that.
*/
void __do_page_cache_readahead(struct address_space *mapping,
- struct file *filp, pgoff_t offset, unsigned long nr_to_read,
+ struct file *filp, pgoff_t index, unsigned long nr_to_read,
unsigned long lookahead_size)
{
struct inode *inode = mapping->host;
@@ -180,7 +180,7 @@ void __do_page_cache_readahead(struct address_space *mapping,
* Preallocate as many pages as we will need.
*/
for (page_idx = 0; page_idx < nr_to_read; page_idx++) {
- pgoff_t page_offset = offset + page_idx;
+ pgoff_t page_offset = index + page_idx;
if (page_offset > end_index)
break;
@@ -219,7 +219,7 @@ void __do_page_cache_readahead(struct address_space *mapping,
* memory at once.
*/
void force_page_cache_readahead(struct address_space *mapping,
- struct file *filp, pgoff_t offset, unsigned long nr_to_read)
+ struct file *filp, pgoff_t index, unsigned long nr_to_read)
{
struct backing_dev_info *bdi = inode_to_bdi(mapping->host);
struct file_ra_state *ra = &filp->f_ra;
@@ -239,9 +239,9 @@ void force_page_cache_readahead(struct address_space *mapping,
if (this_chunk > nr_to_read)
this_chunk = nr_to_read;
- __do_page_cache_readahead(mapping, filp, offset, this_chunk, 0);
+ __do_page_cache_readahead(mapping, filp, index, this_chunk, 0);
- offset += this_chunk;
+ index += this_chunk;
nr_to_read -= this_chunk;
}
}
@@ -322,21 +322,21 @@ static unsigned long get_next_ra_size(struct file_ra_state *ra,
*/
/*
- * Count contiguously cached pages from @offset-1 to @offset-@max,
+ * Count contiguously cached pages from @index-1 to @index-@max,
* this count is a conservative estimation of
* - length of the sequential read sequence, or
* - thrashing threshold in memory tight systems
*/
static pgoff_t count_history_pages(struct address_space *mapping,
- pgoff_t offset, unsigned long max)
+ pgoff_t index, unsigned long max)
{
pgoff_t head;
rcu_read_lock();
- head = page_cache_prev_miss(mapping, offset - 1, max);
+ head = page_cache_prev_miss(mapping, index - 1, max);
rcu_read_unlock();
- return offset - 1 - head;
+ return index - 1 - head;
}
/*
@@ -344,13 +344,13 @@ static pgoff_t count_history_pages(struct address_space *mapping,
*/
static int try_context_readahead(struct address_space *mapping,
struct file_ra_state *ra,
- pgoff_t offset,
+ pgoff_t index,
unsigned long req_size,
unsigned long max)
{
pgoff_t size;
- size = count_history_pages(mapping, offset, max);
+ size = count_history_pages(mapping, index, max);
/*
* not enough history pages:
@@ -363,10 +363,10 @@ static int try_context_readahead(struct address_space *mapping,
* starts from beginning of file:
* it is a strong indication of long-run stream (or whole-file-read)
*/
- if (size >= offset)
+ if (size >= index)
size *= 2;
- ra->start = offset;
+ ra->start = index;
ra->size = min(size + req_size, max);
ra->async_size = 1;
@@ -378,13 +378,13 @@ static int try_context_readahead(struct address_space *mapping,
*/
static void ondemand_readahead(struct address_space *mapping,
struct file_ra_state *ra, struct file *filp,
- bool hit_readahead_marker, pgoff_t offset,
+ bool hit_readahead_marker, pgoff_t index,
unsigned long req_size)
{
struct backing_dev_info *bdi = inode_to_bdi(mapping->host);
unsigned long max_pages = ra->ra_pages;
unsigned long add_pages;
- pgoff_t prev_offset;
+ pgoff_t prev_index;
/*
* If the request exceeds the readahead window, allow the read to
@@ -396,15 +396,15 @@ static void ondemand_readahead(struct address_space *mapping,
/*
* start of file
*/
- if (!offset)
+ if (!index)
goto initial_readahead;
/*
- * It's the expected callback offset, assume sequential access.
+ * It's the expected callback index, assume sequential access.
* Ramp up sizes, and push forward the readahead window.
*/
- if ((offset == (ra->start + ra->size - ra->async_size) ||
- offset == (ra->start + ra->size))) {
+ if ((index == (ra->start + ra->size - ra->async_size) ||
+ index == (ra->start + ra->size))) {
ra->start += ra->size;
ra->size = get_next_ra_size(ra, max_pages);
ra->async_size = ra->size;
@@ -421,14 +421,14 @@ static void ondemand_readahead(struct address_space *mapping,
pgoff_t start;
rcu_read_lock();
- start = page_cache_next_miss(mapping, offset + 1, max_pages);
+ start = page_cache_next_miss(mapping, index + 1, max_pages);
rcu_read_unlock();
- if (!start || start - offset > max_pages)
+ if (!start || start - index > max_pages)
return;
ra->start = start;
- ra->size = start - offset; /* old async_size */
+ ra->size = start - index; /* old async_size */
ra->size += req_size;
ra->size = get_next_ra_size(ra, max_pages);
ra->async_size = ra->size;
@@ -443,29 +443,29 @@ static void ondemand_readahead(struct address_space *mapping,
/*
* sequential cache miss
- * trivial case: (offset - prev_offset) == 1
- * unaligned reads: (offset - prev_offset) == 0
+ * trivial case: (index - prev_index) == 1
+ * unaligned reads: (index - prev_index) == 0
*/
- prev_offset = (unsigned long long)ra->prev_pos >> PAGE_SHIFT;
- if (offset - prev_offset <= 1UL)
+ prev_index = (unsigned long long)ra->prev_pos >> PAGE_SHIFT;
+ if (index - prev_index <= 1UL)
goto initial_readahead;
/*
* Query the page cache and look for the traces(cached history pages)
* that a sequential stream would leave behind.
*/
- if (try_context_readahead(mapping, ra, offset, req_size, max_pages))
+ if (try_context_readahead(mapping, ra, index, req_size, max_pages))
goto readit;
/*
* standalone, small random read
* Read as is, and do not pollute the readahead state.
*/
- __do_page_cache_readahead(mapping, filp, offset, req_size, 0);
+ __do_page_cache_readahead(mapping, filp, index, req_size, 0);
return;
initial_readahead:
- ra->start = offset;
+ ra->start = index;
ra->size = get_init_ra_size(req_size, max_pages);
ra->async_size = ra->size > req_size ? ra->size - req_size : ra->size;
@@ -476,7 +476,7 @@ static void ondemand_readahead(struct address_space *mapping,
* the resulted next readahead window into the current one.
* Take care of maximum IO pages as above.
*/
- if (offset == ra->start && ra->size == ra->async_size) {
+ if (index == ra->start && ra->size == ra->async_size) {
add_pages = get_next_ra_size(ra, max_pages);
if (ra->size + add_pages <= max_pages) {
ra->async_size = add_pages;
@@ -495,9 +495,8 @@ static void ondemand_readahead(struct address_space *mapping,
* @mapping: address_space which holds the pagecache and I/O vectors
* @ra: file_ra_state which holds the readahead state
* @filp: passed on to ->readpage() and ->readpages()
- * @offset: start offset into @mapping, in pagecache page-sized units
- * @req_size: hint: total size of the read which the caller is performing in
- * pagecache pages
+ * @index: Index of first page to be read.
+ * @req_count: Total number of pages being read by the caller.
*
* page_cache_sync_readahead() should be called when a cache miss happened:
* it will submit the read. The readahead logic may decide to piggyback more
@@ -506,7 +505,7 @@ static void ondemand_readahead(struct address_space *mapping,
*/
void page_cache_sync_readahead(struct address_space *mapping,
struct file_ra_state *ra, struct file *filp,
- pgoff_t offset, unsigned long req_size)
+ pgoff_t index, unsigned long req_count)
{
/* no read-ahead */
if (!ra->ra_pages)
@@ -517,12 +516,12 @@ void page_cache_sync_readahead(struct address_space *mapping,
/* be dumb */
if (filp && (filp->f_mode & FMODE_RANDOM)) {
- force_page_cache_readahead(mapping, filp, offset, req_size);
+ force_page_cache_readahead(mapping, filp, index, req_count);
return;
}
/* do read-ahead */
- ondemand_readahead(mapping, ra, filp, false, offset, req_size);
+ ondemand_readahead(mapping, ra, filp, false, index, req_count);
}
EXPORT_SYMBOL_GPL(page_cache_sync_readahead);
@@ -531,21 +530,20 @@ EXPORT_SYMBOL_GPL(page_cache_sync_readahead);
* @mapping: address_space which holds the pagecache and I/O vectors
* @ra: file_ra_state which holds the readahead state
* @filp: passed on to ->readpage() and ->readpages()
- * @page: the page at @offset which has the PG_readahead flag set
- * @offset: start offset into @mapping, in pagecache page-sized units
- * @req_size: hint: total size of the read which the caller is performing in
- * pagecache pages
+ * @page: The page at @index which triggered the readahead call.
+ * @index: Index of first page to be read.
+ * @req_count: Total number of pages being read by the caller.
*
* page_cache_async_readahead() should be called when a page is used which
- * has the PG_readahead flag; this is a marker to suggest that the application
+ * is marked as PageReadahead; this is a marker to suggest that the application
* has used up enough of the readahead window that we should start pulling in
* more pages.
*/
void
page_cache_async_readahead(struct address_space *mapping,
struct file_ra_state *ra, struct file *filp,
- struct page *page, pgoff_t offset,
- unsigned long req_size)
+ struct page *page, pgoff_t index,
+ unsigned long req_count)
{
/* no read-ahead */
if (!ra->ra_pages)
@@ -569,7 +567,7 @@ page_cache_async_readahead(struct address_space *mapping,
return;
/* do read-ahead */
- ondemand_readahead(mapping, ra, filp, true, offset, req_size);
+ ondemand_readahead(mapping, ra, filp, true, index, req_count);
}
EXPORT_SYMBOL_GPL(page_cache_async_readahead);
--
2.25.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v9 08/25] mm: rename readahead loop variable to 'i'
2020-03-20 14:22 [PATCH v9 00/25] Change readahead API Matthew Wilcox
` (6 preceding siblings ...)
2020-03-20 14:22 ` [PATCH v9 07/25] mm: Rename various 'offset' parameters to 'index' Matthew Wilcox
@ 2020-03-20 14:22 ` Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 09/25] mm: Remove 'page_offset' from readahead loop Matthew Wilcox
` (16 subsequent siblings)
24 siblings, 0 replies; 46+ messages in thread
From: Matthew Wilcox @ 2020-03-20 14:22 UTC (permalink / raw)
To: Andrew Morton
Cc: Matthew Wilcox (Oracle), linux-fsdevel, linux-mm, linux-kernel,
linux-btrfs, linux-erofs, linux-ext4, linux-f2fs-devel,
cluster-devel, ocfs2-devel, linux-xfs, John Hubbard, Dave Chinner,
William Kucharski
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
Change the type of page_idx to unsigned long, and rename it -- it's
just a loop counter, not a page index.
Suggested-by: John Hubbard <jhubbard@nvidia.com>
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: William Kucharski <william.kucharski@oracle.com>
---
mm/readahead.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/mm/readahead.c b/mm/readahead.c
index 8a65d6bd97e0..7ce320854bad 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -163,13 +163,13 @@ void __do_page_cache_readahead(struct address_space *mapping,
struct page *page;
unsigned long end_index; /* The last page we want to read */
LIST_HEAD(page_pool);
- int page_idx;
loff_t isize = i_size_read(inode);
gfp_t gfp_mask = readahead_gfp_mask(mapping);
struct readahead_control rac = {
.mapping = mapping,
.file = filp,
};
+ unsigned long i;
if (isize == 0)
return;
@@ -179,8 +179,8 @@ void __do_page_cache_readahead(struct address_space *mapping,
/*
* Preallocate as many pages as we will need.
*/
- for (page_idx = 0; page_idx < nr_to_read; page_idx++) {
- pgoff_t page_offset = index + page_idx;
+ for (i = 0; i < nr_to_read; i++) {
+ pgoff_t page_offset = index + i;
if (page_offset > end_index)
break;
@@ -201,7 +201,7 @@ void __do_page_cache_readahead(struct address_space *mapping,
break;
page->index = page_offset;
list_add(&page->lru, &page_pool);
- if (page_idx == nr_to_read - lookahead_size)
+ if (i == nr_to_read - lookahead_size)
SetPageReadahead(page);
rac._nr_pages++;
}
--
2.25.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v9 09/25] mm: Remove 'page_offset' from readahead loop
2020-03-20 14:22 [PATCH v9 00/25] Change readahead API Matthew Wilcox
` (7 preceding siblings ...)
2020-03-20 14:22 ` [PATCH v9 08/25] mm: rename readahead loop variable to 'i' Matthew Wilcox
@ 2020-03-20 14:22 ` Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 10/25] mm: Put readahead pages in cache earlier Matthew Wilcox
` (15 subsequent siblings)
24 siblings, 0 replies; 46+ messages in thread
From: Matthew Wilcox @ 2020-03-20 14:22 UTC (permalink / raw)
To: Andrew Morton
Cc: Matthew Wilcox (Oracle), linux-fsdevel, linux-mm, linux-kernel,
linux-btrfs, linux-erofs, linux-ext4, linux-f2fs-devel,
cluster-devel, ocfs2-devel, linux-xfs, John Hubbard,
Christoph Hellwig, William Kucharski
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
Replace the page_offset variable with 'index + i'.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: John Hubbard <jhubbard@nvidia.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: William Kucharski <william.kucharski@oracle.com>
---
mm/readahead.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/mm/readahead.c b/mm/readahead.c
index 7ce320854bad..ddc63d3b07b8 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -180,12 +180,10 @@ void __do_page_cache_readahead(struct address_space *mapping,
* Preallocate as many pages as we will need.
*/
for (i = 0; i < nr_to_read; i++) {
- pgoff_t page_offset = index + i;
-
- if (page_offset > end_index)
+ if (index + i > end_index)
break;
- page = xa_load(&mapping->i_pages, page_offset);
+ page = xa_load(&mapping->i_pages, index + i);
if (page && !xa_is_value(page)) {
/*
* Page already present? Kick off the current batch of
@@ -199,7 +197,7 @@ void __do_page_cache_readahead(struct address_space *mapping,
page = __page_cache_alloc(gfp_mask);
if (!page)
break;
- page->index = page_offset;
+ page->index = index + i;
list_add(&page->lru, &page_pool);
if (i == nr_to_read - lookahead_size)
SetPageReadahead(page);
--
2.25.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v9 10/25] mm: Put readahead pages in cache earlier
2020-03-20 14:22 [PATCH v9 00/25] Change readahead API Matthew Wilcox
` (8 preceding siblings ...)
2020-03-20 14:22 ` [PATCH v9 09/25] mm: Remove 'page_offset' from readahead loop Matthew Wilcox
@ 2020-03-20 14:22 ` Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 11/25] mm: Add readahead address space operation Matthew Wilcox
` (14 subsequent siblings)
24 siblings, 0 replies; 46+ messages in thread
From: Matthew Wilcox @ 2020-03-20 14:22 UTC (permalink / raw)
To: Andrew Morton
Cc: Matthew Wilcox (Oracle), linux-fsdevel, linux-mm, linux-kernel,
linux-btrfs, linux-erofs, linux-ext4, linux-f2fs-devel,
cluster-devel, ocfs2-devel, linux-xfs, Christoph Hellwig,
William Kucharski
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
When populating the page cache for readahead, mappings that use
->readpages must populate the page cache themselves as the pages are
passed on a linked list which would normally be used for the page cache's
LRU. For mappings that use ->readpage or the upcoming ->readahead method,
we can put the pages into the page cache as soon as they're allocated,
which solves a race between readahead and direct IO. It also lets us
remove the gfp argument from read_pages().
Use the new readahead_page() API to implement the repeated calls to
->readpage(), just like most filesystems will.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: William Kucharski <william.kucharski@oracle.com>
---
mm/readahead.c | 46 ++++++++++++++++++++++++++++------------------
1 file changed, 28 insertions(+), 18 deletions(-)
diff --git a/mm/readahead.c b/mm/readahead.c
index ddc63d3b07b8..e52b3a7b9da5 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -114,14 +114,14 @@ int read_cache_pages(struct address_space *mapping, struct list_head *pages,
EXPORT_SYMBOL(read_cache_pages);
static void read_pages(struct readahead_control *rac, struct list_head *pages,
- gfp_t gfp)
+ bool skip_page)
{
const struct address_space_operations *aops = rac->mapping->a_ops;
+ struct page *page;
struct blk_plug plug;
- unsigned page_idx;
if (!readahead_count(rac))
- return;
+ goto out;
blk_start_plug(&plug);
@@ -130,23 +130,23 @@ static void read_pages(struct readahead_control *rac, struct list_head *pages,
readahead_count(rac));
/* Clean up the remaining pages */
put_pages_list(pages);
- goto out;
- }
-
- for (page_idx = 0; page_idx < readahead_count(rac); page_idx++) {
- struct page *page = lru_to_page(pages);
- list_del(&page->lru);
- if (!add_to_page_cache_lru(page, rac->mapping, page->index,
- gfp))
+ rac->_index += rac->_nr_pages;
+ rac->_nr_pages = 0;
+ } else {
+ while ((page = readahead_page(rac))) {
aops->readpage(rac->file, page);
- put_page(page);
+ put_page(page);
+ }
}
-out:
blk_finish_plug(&plug);
BUG_ON(!list_empty(pages));
- rac->_nr_pages = 0;
+ BUG_ON(readahead_count(rac));
+
+out:
+ if (skip_page)
+ rac->_index++;
}
/*
@@ -168,6 +168,7 @@ void __do_page_cache_readahead(struct address_space *mapping,
struct readahead_control rac = {
.mapping = mapping,
.file = filp,
+ ._index = index,
};
unsigned long i;
@@ -183,6 +184,8 @@ void __do_page_cache_readahead(struct address_space *mapping,
if (index + i > end_index)
break;
+ BUG_ON(index + i != rac._index + rac._nr_pages);
+
page = xa_load(&mapping->i_pages, index + i);
if (page && !xa_is_value(page)) {
/*
@@ -190,15 +193,22 @@ void __do_page_cache_readahead(struct address_space *mapping,
* contiguous pages before continuing with the next
* batch.
*/
- read_pages(&rac, &page_pool, gfp_mask);
+ read_pages(&rac, &page_pool, true);
continue;
}
page = __page_cache_alloc(gfp_mask);
if (!page)
break;
- page->index = index + i;
- list_add(&page->lru, &page_pool);
+ if (mapping->a_ops->readpages) {
+ page->index = index + i;
+ list_add(&page->lru, &page_pool);
+ } else if (add_to_page_cache_lru(page, mapping, index + i,
+ gfp_mask) < 0) {
+ put_page(page);
+ read_pages(&rac, &page_pool, true);
+ continue;
+ }
if (i == nr_to_read - lookahead_size)
SetPageReadahead(page);
rac._nr_pages++;
@@ -209,7 +219,7 @@ void __do_page_cache_readahead(struct address_space *mapping,
* uptodate then the caller will launch readpage again, and
* will then handle the error.
*/
- read_pages(&rac, &page_pool, gfp_mask);
+ read_pages(&rac, &page_pool, false);
}
/*
--
2.25.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v9 11/25] mm: Add readahead address space operation
2020-03-20 14:22 [PATCH v9 00/25] Change readahead API Matthew Wilcox
` (9 preceding siblings ...)
2020-03-20 14:22 ` [PATCH v9 10/25] mm: Put readahead pages in cache earlier Matthew Wilcox
@ 2020-03-20 14:22 ` Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 12/25] mm: Move end_index check out of readahead loop Matthew Wilcox
` (13 subsequent siblings)
24 siblings, 0 replies; 46+ messages in thread
From: Matthew Wilcox @ 2020-03-20 14:22 UTC (permalink / raw)
To: Andrew Morton
Cc: Matthew Wilcox (Oracle), linux-fsdevel, linux-mm, linux-kernel,
linux-btrfs, linux-erofs, linux-ext4, linux-f2fs-devel,
cluster-devel, ocfs2-devel, linux-xfs, John Hubbard,
Christoph Hellwig, William Kucharski
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
This replaces ->readpages with a saner interface:
- Return void instead of an ignored error code.
- Page cache is already populated with locked pages when ->readahead
is called.
- New arguments can be passed to the implementation without changing
all the filesystems that use a common helper function like
mpage_readahead().
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: John Hubbard <jhubbard@nvidia.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: William Kucharski <william.kucharski@oracle.com>
---
Documentation/filesystems/locking.rst | 6 +++++-
Documentation/filesystems/vfs.rst | 15 +++++++++++++++
include/linux/fs.h | 2 ++
mm/readahead.c | 12 ++++++++++--
4 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/Documentation/filesystems/locking.rst b/Documentation/filesystems/locking.rst
index 5057e4d9dcd1..0af2e0e11461 100644
--- a/Documentation/filesystems/locking.rst
+++ b/Documentation/filesystems/locking.rst
@@ -239,6 +239,7 @@ prototypes::
int (*readpage)(struct file *, struct page *);
int (*writepages)(struct address_space *, struct writeback_control *);
int (*set_page_dirty)(struct page *page);
+ void (*readahead)(struct readahead_control *);
int (*readpages)(struct file *filp, struct address_space *mapping,
struct list_head *pages, unsigned nr_pages);
int (*write_begin)(struct file *, struct address_space *mapping,
@@ -271,7 +272,8 @@ writepage: yes, unlocks (see below)
readpage: yes, unlocks
writepages:
set_page_dirty no
-readpages:
+readahead: yes, unlocks
+readpages: no
write_begin: locks the page exclusive
write_end: yes, unlocks exclusive
bmap:
@@ -295,6 +297,8 @@ the request handler (/dev/loop).
->readpage() unlocks the page, either synchronously or via I/O
completion.
+->readahead() unlocks the pages that I/O is attempted on like ->readpage().
+
->readpages() populates the pagecache with the passed pages and starts
I/O against them. They come unlocked upon I/O completion.
diff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst
index 7d4d09dd5e6d..ed17771c212b 100644
--- a/Documentation/filesystems/vfs.rst
+++ b/Documentation/filesystems/vfs.rst
@@ -706,6 +706,7 @@ cache in your filesystem. The following members are defined:
int (*readpage)(struct file *, struct page *);
int (*writepages)(struct address_space *, struct writeback_control *);
int (*set_page_dirty)(struct page *page);
+ void (*readahead)(struct readahead_control *);
int (*readpages)(struct file *filp, struct address_space *mapping,
struct list_head *pages, unsigned nr_pages);
int (*write_begin)(struct file *, struct address_space *mapping,
@@ -781,12 +782,26 @@ cache in your filesystem. The following members are defined:
If defined, it should set the PageDirty flag, and the
PAGECACHE_TAG_DIRTY tag in the radix tree.
+``readahead``
+ Called by the VM to read pages associated with the address_space
+ object. The pages are consecutive in the page cache and are
+ locked. The implementation should decrement the page refcount
+ after starting I/O on each page. Usually the page will be
+ unlocked by the I/O completion handler. If the filesystem decides
+ to stop attempting I/O before reaching the end of the readahead
+ window, it can simply return. The caller will decrement the page
+ refcount and unlock the remaining pages for you. Set PageUptodate
+ if the I/O completes successfully. Setting PageError on any page
+ will be ignored; simply unlock the page if an I/O error occurs.
+
``readpages``
called by the VM to read pages associated with the address_space
object. This is essentially just a vector version of readpage.
Instead of just one page, several pages are requested.
readpages is only used for read-ahead, so read errors are
ignored. If anything goes wrong, feel free to give up.
+ This interface is deprecated and will be removed by the end of
+ 2020; implement readahead instead.
``write_begin``
Called by the generic buffered write code to ask the filesystem
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 3cd4fe6b845e..d4e2d2964346 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -292,6 +292,7 @@ enum positive_aop_returns {
struct page;
struct address_space;
struct writeback_control;
+struct readahead_control;
/*
* Write life time hint values.
@@ -375,6 +376,7 @@ struct address_space_operations {
*/
int (*readpages)(struct file *filp, struct address_space *mapping,
struct list_head *pages, unsigned nr_pages);
+ void (*readahead)(struct readahead_control *);
int (*write_begin)(struct file *, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
diff --git a/mm/readahead.c b/mm/readahead.c
index e52b3a7b9da5..d01531ef9f3c 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -125,7 +125,14 @@ static void read_pages(struct readahead_control *rac, struct list_head *pages,
blk_start_plug(&plug);
- if (aops->readpages) {
+ if (aops->readahead) {
+ aops->readahead(rac);
+ /* Clean up the remaining pages */
+ while ((page = readahead_page(rac))) {
+ unlock_page(page);
+ put_page(page);
+ }
+ } else if (aops->readpages) {
aops->readpages(rac->file, rac->mapping, pages,
readahead_count(rac));
/* Clean up the remaining pages */
@@ -233,7 +240,8 @@ void force_page_cache_readahead(struct address_space *mapping,
struct file_ra_state *ra = &filp->f_ra;
unsigned long max_pages;
- if (unlikely(!mapping->a_ops->readpage && !mapping->a_ops->readpages))
+ if (unlikely(!mapping->a_ops->readpage && !mapping->a_ops->readpages &&
+ !mapping->a_ops->readahead))
return;
/*
--
2.25.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v9 12/25] mm: Move end_index check out of readahead loop
2020-03-20 14:22 [PATCH v9 00/25] Change readahead API Matthew Wilcox
` (10 preceding siblings ...)
2020-03-20 14:22 ` [PATCH v9 11/25] mm: Add readahead address space operation Matthew Wilcox
@ 2020-03-20 14:22 ` Matthew Wilcox
2020-03-20 16:58 ` Eric Biggers
2020-03-20 14:22 ` [PATCH v9 13/25] mm: Add page_cache_readahead_unbounded Matthew Wilcox
` (12 subsequent siblings)
24 siblings, 1 reply; 46+ messages in thread
From: Matthew Wilcox @ 2020-03-20 14:22 UTC (permalink / raw)
To: Andrew Morton
Cc: Matthew Wilcox (Oracle), linux-fsdevel, linux-mm, linux-kernel,
linux-btrfs, linux-erofs, linux-ext4, linux-f2fs-devel,
cluster-devel, ocfs2-devel, linux-xfs, John Hubbard,
William Kucharski
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
By reducing nr_to_read, we can eliminate this check from inside the loop.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: John Hubbard <jhubbard@nvidia.com>
Reviewed-by: William Kucharski <william.kucharski@oracle.com>
---
mm/readahead.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/mm/readahead.c b/mm/readahead.c
index d01531ef9f3c..a37b68f66233 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -167,8 +167,6 @@ void __do_page_cache_readahead(struct address_space *mapping,
unsigned long lookahead_size)
{
struct inode *inode = mapping->host;
- struct page *page;
- unsigned long end_index; /* The last page we want to read */
LIST_HEAD(page_pool);
loff_t isize = i_size_read(inode);
gfp_t gfp_mask = readahead_gfp_mask(mapping);
@@ -178,22 +176,29 @@ void __do_page_cache_readahead(struct address_space *mapping,
._index = index,
};
unsigned long i;
+ pgoff_t end_index; /* The last page we want to read */
if (isize == 0)
return;
- end_index = ((isize - 1) >> PAGE_SHIFT);
+ end_index = (isize - 1) >> PAGE_SHIFT;
+ if (index > end_index)
+ return;
+ /* Avoid wrapping to the beginning of the file */
+ if (index + nr_to_read < index)
+ nr_to_read = ULONG_MAX - index + 1;
+ /* Don't read past the page containing the last byte of the file */
+ if (index + nr_to_read >= end_index)
+ nr_to_read = end_index - index + 1;
/*
* Preallocate as many pages as we will need.
*/
for (i = 0; i < nr_to_read; i++) {
- if (index + i > end_index)
- break;
+ struct page *page = xa_load(&mapping->i_pages, index + i);
BUG_ON(index + i != rac._index + rac._nr_pages);
- page = xa_load(&mapping->i_pages, index + i);
if (page && !xa_is_value(page)) {
/*
* Page already present? Kick off the current batch of
--
2.25.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v9 13/25] mm: Add page_cache_readahead_unbounded
2020-03-20 14:22 [PATCH v9 00/25] Change readahead API Matthew Wilcox
` (11 preceding siblings ...)
2020-03-20 14:22 ` [PATCH v9 12/25] mm: Move end_index check out of readahead loop Matthew Wilcox
@ 2020-03-20 14:22 ` Matthew Wilcox
2020-03-20 17:27 ` Eric Biggers
2020-03-20 14:22 ` [PATCH v9 14/25] mm: Document why we don't set PageReadahead Matthew Wilcox
` (11 subsequent siblings)
24 siblings, 1 reply; 46+ messages in thread
From: Matthew Wilcox @ 2020-03-20 14:22 UTC (permalink / raw)
To: Andrew Morton
Cc: Matthew Wilcox (Oracle), linux-fsdevel, linux-mm, linux-kernel,
linux-btrfs, linux-erofs, linux-ext4, linux-f2fs-devel,
cluster-devel, ocfs2-devel, linux-xfs, Christoph Hellwig,
William Kucharski
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
ext4 and f2fs have duplicated the guts of the readahead code so
they can read past i_size. Instead, separate out the guts of the
readahead code so they can call it directly.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: William Kucharski <william.kucharski@oracle.com>
---
fs/ext4/verity.c | 35 ++-----------------
fs/f2fs/data.c | 2 +-
fs/f2fs/f2fs.h | 3 --
fs/f2fs/verity.c | 35 ++-----------------
include/linux/pagemap.h | 3 ++
mm/readahead.c | 74 ++++++++++++++++++++++++++++-------------
6 files changed, 58 insertions(+), 94 deletions(-)
diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c
index dc5ec724d889..dec1244dd062 100644
--- a/fs/ext4/verity.c
+++ b/fs/ext4/verity.c
@@ -342,37 +342,6 @@ static int ext4_get_verity_descriptor(struct inode *inode, void *buf,
return desc_size;
}
-/*
- * Prefetch some pages from the file's Merkle tree.
- *
- * This is basically a stripped-down version of __do_page_cache_readahead()
- * which works on pages past i_size.
- */
-static void ext4_merkle_tree_readahead(struct address_space *mapping,
- pgoff_t start_index, unsigned long count)
-{
- LIST_HEAD(pages);
- unsigned int nr_pages = 0;
- struct page *page;
- pgoff_t index;
- struct blk_plug plug;
-
- for (index = start_index; index < start_index + count; index++) {
- page = xa_load(&mapping->i_pages, index);
- if (!page || xa_is_value(page)) {
- page = __page_cache_alloc(readahead_gfp_mask(mapping));
- if (!page)
- break;
- page->index = index;
- list_add(&page->lru, &pages);
- nr_pages++;
- }
- }
- blk_start_plug(&plug);
- ext4_mpage_readpages(mapping, &pages, NULL, nr_pages, true);
- blk_finish_plug(&plug);
-}
-
static struct page *ext4_read_merkle_tree_page(struct inode *inode,
pgoff_t index,
unsigned long num_ra_pages)
@@ -386,8 +355,8 @@ static struct page *ext4_read_merkle_tree_page(struct inode *inode,
if (page)
put_page(page);
else if (num_ra_pages > 1)
- ext4_merkle_tree_readahead(inode->i_mapping, index,
- num_ra_pages);
+ page_cache_readahead_unbounded(inode->i_mapping, NULL,
+ index, num_ra_pages, 0);
page = read_mapping_page(inode->i_mapping, index, NULL);
}
return page;
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index b27b72107911..8e9aa2254490 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -2159,7 +2159,7 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
* use ->readpage() or do the necessary surgery to decouple ->readpages()
* from read-ahead.
*/
-int f2fs_mpage_readpages(struct address_space *mapping,
+static int f2fs_mpage_readpages(struct address_space *mapping,
struct list_head *pages, struct page *page,
unsigned nr_pages, bool is_readahead)
{
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 5355be6b6755..4a414e06a8af 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -3344,9 +3344,6 @@ int f2fs_reserve_new_block(struct dnode_of_data *dn);
int f2fs_get_block(struct dnode_of_data *dn, pgoff_t index);
int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *from);
int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index);
-int f2fs_mpage_readpages(struct address_space *mapping,
- struct list_head *pages, struct page *page,
- unsigned nr_pages, bool is_readahead);
struct page *f2fs_get_read_data_page(struct inode *inode, pgoff_t index,
int op_flags, bool for_write);
struct page *f2fs_find_data_page(struct inode *inode, pgoff_t index);
diff --git a/fs/f2fs/verity.c b/fs/f2fs/verity.c
index d7d430a6f130..865c9fb774fb 100644
--- a/fs/f2fs/verity.c
+++ b/fs/f2fs/verity.c
@@ -222,37 +222,6 @@ static int f2fs_get_verity_descriptor(struct inode *inode, void *buf,
return size;
}
-/*
- * Prefetch some pages from the file's Merkle tree.
- *
- * This is basically a stripped-down version of __do_page_cache_readahead()
- * which works on pages past i_size.
- */
-static void f2fs_merkle_tree_readahead(struct address_space *mapping,
- pgoff_t start_index, unsigned long count)
-{
- LIST_HEAD(pages);
- unsigned int nr_pages = 0;
- struct page *page;
- pgoff_t index;
- struct blk_plug plug;
-
- for (index = start_index; index < start_index + count; index++) {
- page = xa_load(&mapping->i_pages, index);
- if (!page || xa_is_value(page)) {
- page = __page_cache_alloc(readahead_gfp_mask(mapping));
- if (!page)
- break;
- page->index = index;
- list_add(&page->lru, &pages);
- nr_pages++;
- }
- }
- blk_start_plug(&plug);
- f2fs_mpage_readpages(mapping, &pages, NULL, nr_pages, true);
- blk_finish_plug(&plug);
-}
-
static struct page *f2fs_read_merkle_tree_page(struct inode *inode,
pgoff_t index,
unsigned long num_ra_pages)
@@ -266,8 +235,8 @@ static struct page *f2fs_read_merkle_tree_page(struct inode *inode,
if (page)
put_page(page);
else if (num_ra_pages > 1)
- f2fs_merkle_tree_readahead(inode->i_mapping, index,
- num_ra_pages);
+ page_cache_readahead_unbounded(inode->i_mapping, NULL,
+ index, num_ra_pages, 0);
page = read_mapping_page(inode->i_mapping, index, NULL);
}
return page;
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 232892d37071..0c25625ed27d 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -621,6 +621,9 @@ void page_cache_sync_readahead(struct address_space *, struct file_ra_state *,
void page_cache_async_readahead(struct address_space *, struct file_ra_state *,
struct file *, struct page *, pgoff_t index,
unsigned long req_count);
+void page_cache_readahead_unbounded(struct address_space *, struct file *,
+ pgoff_t index, unsigned long nr_to_read,
+ unsigned long lookahead_count);
/*
* Like add_to_page_cache_locked, but used to add newly allocated pages:
diff --git a/mm/readahead.c b/mm/readahead.c
index a37b68f66233..8ee9036fd681 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -156,40 +156,34 @@ static void read_pages(struct readahead_control *rac, struct list_head *pages,
rac->_index++;
}
-/*
- * __do_page_cache_readahead() actually reads a chunk of disk. It allocates
- * the pages first, then submits them for I/O. This avoids the very bad
- * behaviour which would occur if page allocations are causing VM writeback.
- * We really don't want to intermingle reads and writes like that.
+/**
+ * page_cache_readahead_unbounded - Start unchecked readahead.
+ * @mapping: File address space.
+ * @file: This instance of the open file; used for authentication.
+ * @index: First page index to read.
+ * @nr_to_read: The number of pages to read.
+ * @lookahead_size: Where to start the next readahead.
+ *
+ * This function is for filesystems to call when they want to start
+ * readahead beyond a file's stated i_size. This is almost certainly
+ * not the function you want to call. Use page_cache_async_readahead()
+ * or page_cache_sync_readahead() instead.
+ *
+ * Context: File is referenced by caller. Mutexes may be held by caller.
+ * May sleep, but will not reenter filesystem to reclaim memory.
*/
-void __do_page_cache_readahead(struct address_space *mapping,
- struct file *filp, pgoff_t index, unsigned long nr_to_read,
+void page_cache_readahead_unbounded(struct address_space *mapping,
+ struct file *file, pgoff_t index, unsigned long nr_to_read,
unsigned long lookahead_size)
{
- struct inode *inode = mapping->host;
LIST_HEAD(page_pool);
- loff_t isize = i_size_read(inode);
gfp_t gfp_mask = readahead_gfp_mask(mapping);
struct readahead_control rac = {
.mapping = mapping,
- .file = filp,
+ .file = file,
._index = index,
};
unsigned long i;
- pgoff_t end_index; /* The last page we want to read */
-
- if (isize == 0)
- return;
-
- end_index = (isize - 1) >> PAGE_SHIFT;
- if (index > end_index)
- return;
- /* Avoid wrapping to the beginning of the file */
- if (index + nr_to_read < index)
- nr_to_read = ULONG_MAX - index + 1;
- /* Don't read past the page containing the last byte of the file */
- if (index + nr_to_read >= end_index)
- nr_to_read = end_index - index + 1;
/*
* Preallocate as many pages as we will need.
@@ -233,6 +227,38 @@ void __do_page_cache_readahead(struct address_space *mapping,
*/
read_pages(&rac, &page_pool, false);
}
+EXPORT_SYMBOL_GPL(page_cache_readahead_unbounded);
+
+/*
+ * __do_page_cache_readahead() actually reads a chunk of disk. It allocates
+ * the pages first, then submits them for I/O. This avoids the very bad
+ * behaviour which would occur if page allocations are causing VM writeback.
+ * We really don't want to intermingle reads and writes like that.
+ */
+void __do_page_cache_readahead(struct address_space *mapping,
+ struct file *file, pgoff_t index, unsigned long nr_to_read,
+ unsigned long lookahead_size)
+{
+ struct inode *inode = mapping->host;
+ loff_t isize = i_size_read(inode);
+ pgoff_t end_index; /* The last page we want to read */
+
+ if (isize == 0)
+ return;
+
+ end_index = (isize - 1) >> PAGE_SHIFT;
+ if (index > end_index)
+ return;
+ /* Avoid wrapping to the beginning of the file */
+ if (index + nr_to_read < index)
+ nr_to_read = ULONG_MAX - index + 1;
+ /* Don't read past the page containing the last byte of the file */
+ if (index + nr_to_read >= end_index)
+ nr_to_read = end_index - index + 1;
+
+ page_cache_readahead_unbounded(mapping, file, index, nr_to_read,
+ lookahead_size);
+}
/*
* Chunk the readahead into 2 megabyte units, so that we don't pin too much
--
2.25.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v9 14/25] mm: Document why we don't set PageReadahead
2020-03-20 14:22 [PATCH v9 00/25] Change readahead API Matthew Wilcox
` (12 preceding siblings ...)
2020-03-20 14:22 ` [PATCH v9 13/25] mm: Add page_cache_readahead_unbounded Matthew Wilcox
@ 2020-03-20 14:22 ` Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 15/25] mm: Use memalloc_nofs_save in readahead path Matthew Wilcox
` (10 subsequent siblings)
24 siblings, 0 replies; 46+ messages in thread
From: Matthew Wilcox @ 2020-03-20 14:22 UTC (permalink / raw)
To: Andrew Morton
Cc: Matthew Wilcox (Oracle), linux-fsdevel, linux-mm, linux-kernel,
linux-btrfs, linux-erofs, linux-ext4, linux-f2fs-devel,
cluster-devel, ocfs2-devel, linux-xfs, Christoph Hellwig,
William Kucharski
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
If the page is already in cache, we don't set PageReadahead on it.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: William Kucharski <william.kucharski@oracle.com>
---
mm/readahead.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/mm/readahead.c b/mm/readahead.c
index 8ee9036fd681..0afb55a49909 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -195,9 +195,12 @@ void page_cache_readahead_unbounded(struct address_space *mapping,
if (page && !xa_is_value(page)) {
/*
- * Page already present? Kick off the current batch of
- * contiguous pages before continuing with the next
- * batch.
+ * Page already present? Kick off the current batch
+ * of contiguous pages before continuing with the
+ * next batch. This page may be the one we would
+ * have intended to mark as Readahead, but we don't
+ * have a stable reference to this page, and it's
+ * not worth getting one just for that.
*/
read_pages(&rac, &page_pool, true);
continue;
--
2.25.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v9 15/25] mm: Use memalloc_nofs_save in readahead path
2020-03-20 14:22 [PATCH v9 00/25] Change readahead API Matthew Wilcox
` (13 preceding siblings ...)
2020-03-20 14:22 ` [PATCH v9 14/25] mm: Document why we don't set PageReadahead Matthew Wilcox
@ 2020-03-20 14:22 ` Matthew Wilcox
2020-03-20 14:22 ` [PATCH v9 16/25] fs: Convert mpage_readpages to mpage_readahead Matthew Wilcox
` (9 subsequent siblings)
24 siblings, 0 replies; 46+ messages in thread
From: Matthew Wilcox @ 2020-03-20 14:22 UTC (permalink / raw)
To: Andrew Morton
Cc: Matthew Wilcox (Oracle), linux-fsdevel, linux-mm, linux-kernel,
linux-btrfs, linux-erofs, linux-ext4, linux-f2fs-devel,
cluster-devel, ocfs2-devel, linux-xfs, Cong Wang, Michal Hocko,
William Kucharski
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
Ensure that memory allocations in the readahead path do not attempt to
reclaim file-backed pages, which could lead to a deadlock. It is
possible, though unlikely this is the root cause of a problem observed
by Cong Wang.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reported-by: Cong Wang <xiyou.wangcong@gmail.com>
Suggested-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: William Kucharski <william.kucharski@oracle.com>
---
mm/readahead.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/mm/readahead.c b/mm/readahead.c
index 0afb55a49909..7f2d54fb1691 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -22,6 +22,7 @@
#include <linux/mm_inline.h>
#include <linux/blk-cgroup.h>
#include <linux/fadvise.h>
+#include <linux/sched/mm.h>
#include "internal.h"
@@ -185,6 +186,18 @@ void page_cache_readahead_unbounded(struct address_space *mapping,
};
unsigned long i;
+ /*
+ * Partway through the readahead operation, we will have added
+ * locked pages to the page cache, but will not yet have submitted
+ * them for I/O. Adding another page may need to allocate memory,
+ * which can trigger memory reclaim. Telling the VM we're in
+ * the middle of a filesystem operation will cause it to not
+ * touch file-backed pages, preventing a deadlock. Most (all?)
+ * filesystems already specify __GFP_NOFS in their mapping's
+ * gfp_mask, but le