diff mbox series

[11/17] mm/filemap: Add filemap_range_uptodate

Message ID 20201102184312.25926-12-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
Move the complicated condition and the calculations out of
filemap_update_page() into its own function.
---
 mm/filemap.c | 49 +++++++++++++++++++++++++++++++++----------------
 1 file changed, 33 insertions(+), 16 deletions(-)

Comments

Kent Overstreet Nov. 2, 2020, 7:50 p.m. UTC | #1
On Mon, Nov 02, 2020 at 06:43:06PM +0000, Matthew Wilcox (Oracle) wrote:
> Move the complicated condition and the calculations out of
> filemap_update_page() into its own function.

You're missing a signed-off-by

Reviewed-by: Kent Overstreet <kent.overstreet@gmail.com>
Matthew Wilcox Nov. 2, 2020, 8:09 p.m. UTC | #2
On Mon, Nov 02, 2020 at 02:50:56PM -0500, Kent Overstreet wrote:
> On Mon, Nov 02, 2020 at 06:43:06PM +0000, Matthew Wilcox (Oracle) wrote:
> > Move the complicated condition and the calculations out of
> > filemap_update_page() into its own function.
> 
> You're missing a signed-off-by
> 
> Reviewed-by: Kent Overstreet <kent.overstreet@gmail.com>

Oops.

Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Christoph Hellwig Nov. 3, 2020, 7:49 a.m. UTC | #3
On Mon, Nov 02, 2020 at 06:43:06PM +0000, Matthew Wilcox (Oracle) wrote:
> Move the complicated condition and the calculations out of
> filemap_update_page() into its own function.

The logic looks good, but the flow inside of filemap_update_page looks
odd.  The patch below relative to this commit shows how I'd do it:

diff --git a/mm/filemap.c b/mm/filemap.c
index 81b569d818a3f8..304180c022d38a 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -2266,40 +2266,39 @@ static int filemap_update_page(struct kiocb *iocb,
 	if (!trylock_page(page)) {
 		if (iocb->ki_flags & (IOCB_NOWAIT | IOCB_NOIO))
 			goto error;
-		if (iocb->ki_flags & IOCB_WAITQ) {
-			if (!first)
-				goto error;
-			error = __lock_page_async(page, iocb->ki_waitq);
-			if (error)
-				goto error;
-		} else {
+		if (!(iocb->ki_flags & IOCB_WAITQ)) {
 			put_and_wait_on_page_locked(page, TASK_KILLABLE);
 			return AOP_TRUNCATED_PAGE;
 		}
+		if (!first)
+			goto error;
+		error = __lock_page_async(page, iocb->ki_waitq);
+		if (error)
+			goto error;
 	}
 
+	error = AOP_TRUNCATED_PAGE;
 	if (!page->mapping)
-		goto truncated;
-	if (filemap_range_uptodate(iocb, mapping, iter, page)) {
-		unlock_page(page);
-		return 0;
-	}
+		goto error_unlock_page;
 
-	if (iocb->ki_flags & (IOCB_NOIO | IOCB_NOWAIT | IOCB_WAITQ)) {
-		unlock_page(page);
+	if (!filemap_range_uptodate(iocb, mapping, iter, page)) {
 		error = -EAGAIN;
-	} else {
+		if (iocb->ki_flags & (IOCB_NOIO | IOCB_NOWAIT | IOCB_WAITQ))
+			goto error_unlock_page;
 		error = filemap_read_page(iocb->ki_filp, mapping, page);
-		if (!error)
-			return 0;
+		if (error)
+			goto error;
+		return 0; /* filemap_read_page unlocks the page */
 	}
+
+	unlock_page(page);
+	return 0;
+
+error_unlock_page:
+	unlock_page(page);
 error:
 	put_page(page);
 	return error;
-truncated:
-	unlock_page(page);
-	put_page(page);
-	return AOP_TRUNCATED_PAGE;
 }
 
 static struct page *filemap_create_page(struct file *file,
Matthew Wilcox Nov. 3, 2020, 3:18 p.m. UTC | #4
On Tue, Nov 03, 2020 at 08:49:44AM +0100, Christoph Hellwig wrote:
> On Mon, Nov 02, 2020 at 06:43:06PM +0000, Matthew Wilcox (Oracle) wrote:
> > Move the complicated condition and the calculations out of
> > filemap_update_page() into its own function.
> 
> The logic looks good, but the flow inside of filemap_update_page looks
> odd.  The patch below relative to this commit shows how I'd do it:

I have a simplification in mind that gets rid of the awkward 'first'
parameter.  In filemap_get_pages(), do:

                if ((iocb->ki_flags & IOCB_WAITQ) && (pagevec_count(pvec) > 1))
                        iocb->ki_flags |= IOCB_NOWAIT;

before calling filemap_update_page().  That matches what Kent did in
filemap_read():

                if ((iocb->ki_flags & IOCB_WAITQ) && already_read)
                        iocb->ki_flags |= IOCB_NOWAIT;

> diff --git a/mm/filemap.c b/mm/filemap.c
> index 81b569d818a3f8..304180c022d38a 100644
> --- a/mm/filemap.c
> +++ b/mm/filemap.c
> @@ -2266,40 +2266,39 @@ static int filemap_update_page(struct kiocb *iocb,
>  	if (!trylock_page(page)) {
>  		if (iocb->ki_flags & (IOCB_NOWAIT | IOCB_NOIO))
>  			goto error;
> -		if (iocb->ki_flags & IOCB_WAITQ) {
> -			if (!first)
> -				goto error;
> -			error = __lock_page_async(page, iocb->ki_waitq);
> -			if (error)
> -				goto error;
> -		} else {
> +		if (!(iocb->ki_flags & IOCB_WAITQ)) {
>  			put_and_wait_on_page_locked(page, TASK_KILLABLE);
>  			return AOP_TRUNCATED_PAGE;
>  		}
> +		if (!first)
> +			goto error;
> +		error = __lock_page_async(page, iocb->ki_waitq);
> +		if (error)
> +			goto error;
>  	}

I see where you're going there.  OK.

>  
> +	error = AOP_TRUNCATED_PAGE;
>  	if (!page->mapping)
> -		goto truncated;
> -	if (filemap_range_uptodate(iocb, mapping, iter, page)) {
> -		unlock_page(page);
> -		return 0;
> -	}
> +		goto error_unlock_page;
>  
> -	if (iocb->ki_flags & (IOCB_NOIO | IOCB_NOWAIT | IOCB_WAITQ)) {
> -		unlock_page(page);
> +	if (!filemap_range_uptodate(iocb, mapping, iter, page)) {
>  		error = -EAGAIN;
> -	} else {
> +		if (iocb->ki_flags & (IOCB_NOIO | IOCB_NOWAIT | IOCB_WAITQ))
> +			goto error_unlock_page;
>  		error = filemap_read_page(iocb->ki_filp, mapping, page);
> -		if (!error)
> -			return 0;
> +		if (error)
> +			goto error;
> +		return 0; /* filemap_read_page unlocks the page */
>  	}
> +
> +	unlock_page(page);
> +	return 0;
> +
> +error_unlock_page:
> +	unlock_page(page);
>  error:
>  	put_page(page);
>  	return error;
> -truncated:
> -	unlock_page(page);
> -	put_page(page);
> -	return AOP_TRUNCATED_PAGE;
>  }

There's something niggling at me about this change ... I'll play with
it a bit.
Christoph Hellwig Nov. 3, 2020, 3:31 p.m. UTC | #5
On Tue, Nov 03, 2020 at 03:18:47PM +0000, Matthew Wilcox wrote:
> I have a simplification in mind that gets rid of the awkward 'first'
> parameter.  In filemap_get_pages(), do:
> 
>                 if ((iocb->ki_flags & IOCB_WAITQ) && (pagevec_count(pvec) > 1))
>                         iocb->ki_flags |= IOCB_NOWAIT;
> 
> before calling filemap_update_page().  That matches what Kent did in
> filemap_read():

Yes, that makes a lot of sense.  No need for the second pair of inner
braces, though :)
Matthew Wilcox Nov. 3, 2020, 3:42 p.m. UTC | #6
On Tue, Nov 03, 2020 at 04:31:18PM +0100, Christoph Hellwig wrote:
> On Tue, Nov 03, 2020 at 03:18:47PM +0000, Matthew Wilcox wrote:
> > I have a simplification in mind that gets rid of the awkward 'first'
> > parameter.  In filemap_get_pages(), do:
> > 
> >                 if ((iocb->ki_flags & IOCB_WAITQ) && (pagevec_count(pvec) > 1))
> >                         iocb->ki_flags |= IOCB_NOWAIT;
> > 
> > before calling filemap_update_page().  That matches what Kent did in
> > filemap_read():
> 
> Yes, that makes a lot of sense.  No need for the second pair of inner
> braces, though :)

I wrote it as
		if ((iocb->ki_flags & IOCB_WAITQ) && pagevec_count(pvec) > 1)
originally, and then talked myself into putting the unnecessary brackets
in to make it look more symmetric ;-)
diff mbox series

Patch

diff --git a/mm/filemap.c b/mm/filemap.c
index 41b90243f4ee..81b569d818a3 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -2228,11 +2228,39 @@  static int filemap_read_page(struct file *file, struct address_space *mapping,
 	return error;
 }
 
+static bool filemap_range_uptodate(struct kiocb *iocb,
+		struct address_space *mapping, struct iov_iter *iter,
+		struct page *page)
+{
+	loff_t pos;
+	int count;
+
+	if (PageUptodate(page))
+		return true;
+	/* pipes can't handle partially uptodate pages */
+	if (iov_iter_is_pipe(iter))
+		return false;
+	if (!mapping->a_ops->is_partially_uptodate)
+		return false;
+	if (mapping->host->i_blkbits >= (PAGE_SHIFT + thp_order(page)))
+		return false;
+
+	pos = (loff_t) page->index << PAGE_SHIFT;
+	if (pos > iocb->ki_pos) {
+		count = iocb->ki_pos + iter->count - pos;
+		pos = 0;
+	} else {
+		count = iter->count;
+		pos = iocb->ki_pos & (thp_size(page) - 1);
+	}
+
+	return mapping->a_ops->is_partially_uptodate(page, pos, count);
+}
+
 static int filemap_update_page(struct kiocb *iocb,
 		struct address_space *mapping, struct iov_iter *iter,
 		struct page *page, loff_t pos, loff_t count, bool first)
 {
-	struct inode *inode = mapping->host;
 	int error = -EAGAIN;
 
 	if (!trylock_page(page)) {
@@ -2252,22 +2280,11 @@  static int filemap_update_page(struct kiocb *iocb,
 
 	if (!page->mapping)
 		goto truncated;
-	if (PageUptodate(page))
-		goto uptodate;
-	if (inode->i_blkbits == PAGE_SHIFT ||
-			!mapping->a_ops->is_partially_uptodate)
-		goto readpage;
-	/* pipes can't handle partially uptodate pages */
-	if (unlikely(iov_iter_is_pipe(iter)))
-		goto readpage;
-	if (!mapping->a_ops->is_partially_uptodate(page,
-				pos & (thp_size(page) - 1), count))
-		goto readpage;
-uptodate:
-	unlock_page(page);
-	return 0;
+	if (filemap_range_uptodate(iocb, mapping, iter, page)) {
+		unlock_page(page);
+		return 0;
+	}
 
-readpage:
 	if (iocb->ki_flags & (IOCB_NOIO | IOCB_NOWAIT | IOCB_WAITQ)) {
 		unlock_page(page);
 		error = -EAGAIN;