diff mbox series

[15/17] mm/filemap: Don't relock the page after calling readpage

Message ID 20201102184312.25926-16-willy@infradead.org (mailing list archive)
State New, archived
Headers show
Series Refactor generic_file_buffered_read | expand

Commit Message

Matthew Wilcox Nov. 2, 2020, 6:43 p.m. UTC
We don't need to get the page lock again; we just need to wait for
the I/O to finish, so use wait_on_page_locked_killable() like the
other callers of ->readpage.

Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
 mm/filemap.c | 21 +++++++--------------
 1 file changed, 7 insertions(+), 14 deletions(-)

Comments

Kent Overstreet Nov. 2, 2020, 8:06 p.m. UTC | #1
On Mon, Nov 02, 2020 at 06:43:10PM +0000, Matthew Wilcox (Oracle) wrote:
> We don't need to get the page lock again; we just need to wait for
> the I/O to finish, so use wait_on_page_locked_killable() like the
> other callers of ->readpage.
> 
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>

Reviewed-by: Kent Overstreet <kent.overstreet@gmail.com>
Christoph Hellwig Nov. 3, 2020, 8 a.m. UTC | #2
On Mon, Nov 02, 2020 at 06:43:10PM +0000, Matthew Wilcox (Oracle) wrote:
> We don't need to get the page lock again; we just need to wait for
> the I/O to finish, so use wait_on_page_locked_killable() like the
> other callers of ->readpage.

As that isn't entirely obvious, what about adding a comment next to
the wait_on_page_locked_killable call to document it?

>  
> +	error = wait_on_page_locked_killable(page);
>  	if (error)
>  		return error;
> +	if (PageUptodate(page))
> +		return 0;
> +	if (!page->mapping)	/* page truncated */
> +		return AOP_TRUNCATED_PAGE;
> +	shrink_readahead_size_eio(&file->f_ra);
> +	return -EIO;

You might notice a theme here, but I hate having the fast path exit
early without a good reason, so I'd be much happier with:

	if (!PageUptodate(page)) {
		if (!page->mapping)	/* page truncated */
			return AOP_TRUNCATED_PAGE;
		shrink_readahead_size_eio(&file->f_ra);
		return -EIO;
	}
	return 0;
Matthew Wilcox Nov. 3, 2020, 3:24 p.m. UTC | #3
On Tue, Nov 03, 2020 at 09:00:45AM +0100, Christoph Hellwig wrote:
> On Mon, Nov 02, 2020 at 06:43:10PM +0000, Matthew Wilcox (Oracle) wrote:
> > We don't need to get the page lock again; we just need to wait for
> > the I/O to finish, so use wait_on_page_locked_killable() like the
> > other callers of ->readpage.
> 
> As that isn't entirely obvious, what about adding a comment next to
> the wait_on_page_locked_killable call to document it?

The other callers of ->readpage don't document that, so not sure why
we should here?

> > +	error = wait_on_page_locked_killable(page);
> >  	if (error)
> >  		return error;
> > +	if (PageUptodate(page))
> > +		return 0;
> > +	if (!page->mapping)	/* page truncated */
> > +		return AOP_TRUNCATED_PAGE;
> > +	shrink_readahead_size_eio(&file->f_ra);
> > +	return -EIO;
> 
> You might notice a theme here, but I hate having the fast path exit
> early without a good reason, so I'd be much happier with:
> 
> 	if (!PageUptodate(page)) {
> 		if (!page->mapping)	/* page truncated */
> 			return AOP_TRUNCATED_PAGE;
> 		shrink_readahead_size_eio(&file->f_ra);
> 		return -EIO;
> 	}
> 	return 0;

I'm just not a fan of unnecessary indentation.
Christoph Hellwig Nov. 3, 2020, 5:13 p.m. UTC | #4
On Tue, Nov 03, 2020 at 03:24:36PM +0000, Matthew Wilcox wrote:
> On Tue, Nov 03, 2020 at 09:00:45AM +0100, Christoph Hellwig wrote:
> > On Mon, Nov 02, 2020 at 06:43:10PM +0000, Matthew Wilcox (Oracle) wrote:
> > > We don't need to get the page lock again; we just need to wait for
> > > the I/O to finish, so use wait_on_page_locked_killable() like the
> > > other callers of ->readpage.
> > 
> > As that isn't entirely obvious, what about adding a comment next to
> > the wait_on_page_locked_killable call to document it?
> 
> The other callers of ->readpage don't document that, so not sure why
> we should here?

The callers of ->readpage are a mess :(

Many use lock_page or trylock_page, one uses wait_on_page_locked directly,
another one uses the wait_on_page_read helper.  I think we need a good
helper here, and I think it looks a lot like filemap_read_page in your
tree..
Matthew Wilcox Nov. 3, 2020, 6:55 p.m. UTC | #5
On Tue, Nov 03, 2020 at 06:13:56PM +0100, Christoph Hellwig wrote:
> On Tue, Nov 03, 2020 at 03:24:36PM +0000, Matthew Wilcox wrote:
> > On Tue, Nov 03, 2020 at 09:00:45AM +0100, Christoph Hellwig wrote:
> > > On Mon, Nov 02, 2020 at 06:43:10PM +0000, Matthew Wilcox (Oracle) wrote:
> > > > We don't need to get the page lock again; we just need to wait for
> > > > the I/O to finish, so use wait_on_page_locked_killable() like the
> > > > other callers of ->readpage.
> > > 
> > > As that isn't entirely obvious, what about adding a comment next to
> > > the wait_on_page_locked_killable call to document it?
> > 
> > The other callers of ->readpage don't document that, so not sure why
> > we should here?
> 
> The callers of ->readpage are a mess :(
> 
> Many use lock_page or trylock_page, one uses wait_on_page_locked directly,
> another one uses the wait_on_page_read helper.  I think we need a good
> helper here, and I think it looks a lot like filemap_read_page in your
> tree..

Oh, heh.  Turns out I wrote this in a patch series the other day ...

static int mapping_readpage(struct file *file, struct address_space *mapping,
                struct page *page, bool synchronous)
{
        struct readahead_control ractl = {
                .file = file,
                .mapping = mapping,
                ._index = page->index,
                ._nr_pages = 1,
        };
        int ret;

        if (!synchronous && mapping->a_ops->readahead) {
                mapping->a_ops->readahead(&ractl);
                return 0;
        }

        ret = mapping->a_ops->readpage(file, page);
        if (ret != AOP_UPDATED_PAGE)
                return ret;
        unlock_page(page);
        return 0;
}

(it's for swap_readpage, which is generally called in an async manner ...
and really doesn't handle errors at all well)

This does illustrate that we need the mapping argument.  Some of the
callers of ->readpage need to pass a NULL file pointer, so we can't
get it from file->f_mapping.
diff mbox series

Patch

diff --git a/mm/filemap.c b/mm/filemap.c
index f16b1eb03bca..f2de97d51441 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -2209,23 +2209,16 @@  static int filemap_read_page(struct file *file, struct address_space *mapping,
 	error = mapping->a_ops->readpage(file, page);
 	if (error)
 		return error;
-	if (PageUptodate(page))
-		return 0;
 
-	error = lock_page_killable(page);
+	error = wait_on_page_locked_killable(page);
 	if (error)
 		return error;
-	if (!PageUptodate(page)) {
-		if (page->mapping == NULL) {
-			/* page truncated */
-			error = AOP_TRUNCATED_PAGE;
-		} else {
-			shrink_readahead_size_eio(&file->f_ra);
-			error = -EIO;
-		}
-	}
-	unlock_page(page);
-	return error;
+	if (PageUptodate(page))
+		return 0;
+	if (!page->mapping)	/* page truncated */
+		return AOP_TRUNCATED_PAGE;
+	shrink_readahead_size_eio(&file->f_ra);
+	return -EIO;
 }
 
 static bool filemap_range_uptodate(struct kiocb *iocb,