linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: "Matthew Wilcox (Oracle)" <willy@infradead.org>,
	linux-fsdevel@vger.kernel.org, linux-mm@kvack.org,
	linux-kernel@vger.kernel.org,
	Kent Overstreet <kent.overstreet@gmail.com>,
	Christoph Hellwig <hch@lst.de>
Subject: [PATCH v5 06/18] mm/filemap: Support readpage splitting a page
Date: Fri, 22 Jan 2021 16:01:28 +0000	[thread overview]
Message-ID: <20210122160140.223228-7-willy@infradead.org> (raw)
In-Reply-To: <20210122160140.223228-1-willy@infradead.org>

For page splitting to succeed, the thread asking to split the
page has to be the only one with a reference to the page.  Calling
wait_on_page_locked() while holding a reference to the page will
effectively prevent this from happening with sufficient threads waiting
on the same page.  Use put_and_wait_on_page_locked() to sleep without
holding a reference to the page, then retry the page lookup after the
page is unlocked.

Since we now get the page lock a little earlier in filemap_update_page(),
we can eliminate a number of duplicate checks.  The original intent
(commit ebded02788b5 ("avoid unnecessary calls to lock_page when waiting
for IO to complete during a read")) behind getting the page lock later
was to avoid re-locking the page after it has been brought uptodate by
another thread.  We still avoid that because we go through the normal
lookup path again after the winning thread has brought the page uptodate.

Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Kent Overstreet <kent.overstreet@gmail.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 mm/filemap.c | 76 ++++++++++++++++------------------------------------
 1 file changed, 23 insertions(+), 53 deletions(-)

diff --git a/mm/filemap.c b/mm/filemap.c
index c71cd95e5372b..88201506ad3ef 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1368,14 +1368,6 @@ static int __wait_on_page_locked_async(struct page *page,
 	return ret;
 }
 
-static int wait_on_page_locked_async(struct page *page,
-				     struct wait_page_queue *wait)
-{
-	if (!PageLocked(page))
-		return 0;
-	return __wait_on_page_locked_async(compound_head(page), wait, false);
-}
-
 /**
  * put_and_wait_on_page_locked - Drop a reference and wait for it to be unlocked
  * @page: The page to wait for.
@@ -2299,64 +2291,42 @@ static struct page *filemap_update_page(struct kiocb *iocb, struct file *filp,
 	struct inode *inode = mapping->host;
 	int error;
 
-	/*
-	 * See comment in do_read_cache_page on why
-	 * wait_on_page_locked is used to avoid unnecessarily
-	 * serialisations and why it's safe.
-	 */
 	if (iocb->ki_flags & IOCB_WAITQ) {
-		error = wait_on_page_locked_async(page,
-						iocb->ki_waitq);
+		error = lock_page_async(page, iocb->ki_waitq);
+		if (error) {
+			put_page(page);
+			return ERR_PTR(error);
+		}
 	} else {
-		error = wait_on_page_locked_killable(page);
-	}
-	if (unlikely(error)) {
-		put_page(page);
-		return ERR_PTR(error);
+		if (!trylock_page(page)) {
+			put_and_wait_on_page_locked(page, TASK_KILLABLE);
+			return NULL;
+		}
 	}
-	if (PageUptodate(page))
-		return page;
 
+	if (!page->mapping)
+		goto truncated;
+	if (PageUptodate(page))
+		goto uptodate;
 	if (inode->i_blkbits == PAGE_SHIFT ||
 			!mapping->a_ops->is_partially_uptodate)
-		goto page_not_up_to_date;
+		goto readpage;
 	/* pipes can't handle partially uptodate pages */
 	if (unlikely(iov_iter_is_pipe(iter)))
-		goto page_not_up_to_date;
-	if (!trylock_page(page))
-		goto page_not_up_to_date;
-	/* Did it get truncated before we got the lock? */
-	if (!page->mapping)
-		goto page_not_up_to_date_locked;
+		goto readpage;
 	if (!mapping->a_ops->is_partially_uptodate(page,
-				pos & ~PAGE_MASK, count))
-		goto page_not_up_to_date_locked;
+				pos & (thp_size(page) - 1), count))
+		goto readpage;
+uptodate:
 	unlock_page(page);
 	return page;
 
-page_not_up_to_date:
-	/* Get exclusive access to the page ... */
-	error = lock_page_for_iocb(iocb, page);
-	if (unlikely(error)) {
-		put_page(page);
-		return ERR_PTR(error);
-	}
-
-page_not_up_to_date_locked:
-	/* Did it get truncated before we got the lock? */
-	if (!page->mapping) {
-		unlock_page(page);
-		put_page(page);
-		return NULL;
-	}
-
-	/* Did somebody else fill it already? */
-	if (PageUptodate(page)) {
-		unlock_page(page);
-		return page;
-	}
-
+readpage:
 	return filemap_read_page(iocb, filp, mapping, page);
+truncated:
+	unlock_page(page);
+	put_page(page);
+	return NULL;
 }
 
 static struct page *filemap_create_page(struct kiocb *iocb,
-- 
2.29.2


  parent reply	other threads:[~2021-01-22 16:08 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-01-22 16:01 [PATCH v5 00/18] Refactor generic_file_buffered_read Matthew Wilcox (Oracle)
2021-01-22 16:01 ` [PATCH v5 01/18] mm/filemap: Rename generic_file_buffered_read subfunctions Matthew Wilcox (Oracle)
2021-01-22 16:01 ` [PATCH v5 02/18] mm/filemap: Remove dynamically allocated array from filemap_read Matthew Wilcox (Oracle)
2021-01-22 16:01 ` [PATCH v5 03/18] mm/filemap: Convert filemap_get_pages to take a pagevec Matthew Wilcox (Oracle)
2021-01-22 16:01 ` [PATCH v5 04/18] mm/filemap: Use head pages in generic_file_buffered_read Matthew Wilcox (Oracle)
2021-01-22 16:01 ` [PATCH v5 05/18] mm/filemap: Pass a sleep state to put_and_wait_on_page_locked Matthew Wilcox (Oracle)
2021-01-22 16:01 ` Matthew Wilcox (Oracle) [this message]
2021-01-22 16:01 ` [PATCH v5 07/18] mm/filemap: Inline __wait_on_page_locked_async into caller Matthew Wilcox (Oracle)
2021-01-22 16:01 ` [PATCH v5 08/18] mm/filemap: Don't call ->readpage if IOCB_WAITQ is set Matthew Wilcox (Oracle)
2021-01-22 16:01 ` [PATCH v5 09/18] mm/filemap: Change filemap_read_page calling conventions Matthew Wilcox (Oracle)
2021-01-22 16:01 ` [PATCH v5 10/18] mm/filemap: Change filemap_create_page " Matthew Wilcox (Oracle)
2021-01-22 16:01 ` [PATCH v5 11/18] mm/filemap: Convert filemap_update_page to return an errno Matthew Wilcox (Oracle)
2021-01-22 16:01 ` [PATCH v5 12/18] mm/filemap: Move the iocb checks into filemap_update_page Matthew Wilcox (Oracle)
2021-01-22 16:01 ` [PATCH v5 13/18] mm/filemap: Add filemap_range_uptodate Matthew Wilcox (Oracle)
2021-01-22 16:01 ` [PATCH v5 14/18] mm/filemap: Split filemap_readahead out of filemap_get_pages Matthew Wilcox (Oracle)
2021-01-22 16:01 ` [PATCH v5 15/18] mm/filemap: Restructure filemap_get_pages Matthew Wilcox (Oracle)
2021-01-22 16:01 ` [PATCH v5 16/18] mm/filemap: Don't relock the page after calling readpage Matthew Wilcox (Oracle)
2021-01-22 16:01 ` [PATCH v5 17/18] mm/filemap: Rename generic_file_buffered_read to filemap_read Matthew Wilcox (Oracle)
2021-01-22 16:01 ` [PATCH v5 18/18] mm/filemap: Simplify generic_file_read_iter Matthew Wilcox (Oracle)

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210122160140.223228-7-willy@infradead.org \
    --to=willy@infradead.org \
    --cc=akpm@linux-foundation.org \
    --cc=hch@lst.de \
    --cc=kent.overstreet@gmail.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).