All of lore.kernel.org
 help / color / mirror / Atom feed
From: Miklos Szeredi <mszeredi@redhat.com>
To: linux-fsdevel@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, Al Viro <viro@zeniv.linux.org.uk>
Subject: [PATCH 09/11] splice: use get_page_for_read()
Date: Wed, 14 Sep 2016 10:37:14 +0200	[thread overview]
Message-ID: <1473842236-28655-10-git-send-email-mszeredi@redhat.com> (raw)
In-Reply-To: <1473842236-28655-1-git-send-email-mszeredi@redhat.com>

What __generic_file_splice_read() does is get a series of uptodate pages
and put them into the pipe buffer.

The get_page_for_read() helper can now be used to get the pages,
simplifying the code and making sure the splice(2) stays in sync with
read(2).

For example get_page_for_read() can handle partially uptodate pages and now
splice can take advantage of these as well.

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
---
 fs/splice.c | 169 +++++-------------------------------------------------------
 1 file changed, 12 insertions(+), 157 deletions(-)

diff --git a/fs/splice.c b/fs/splice.c
index dc4648ba6e8d..e7757b363b6c 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -265,14 +265,12 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
 			   struct pipe_inode_info *pipe, size_t len,
 			   unsigned int flags)
 {
-	struct address_space *mapping = in->f_mapping;
 	unsigned int loff, nr_pages, req_pages;
 	struct page *pages[PIPE_DEF_BUFFERS];
 	struct partial_page partial[PIPE_DEF_BUFFERS];
 	struct page *page;
-	pgoff_t index, end_index;
-	loff_t isize;
-	int error, page_nr;
+	pgoff_t index;
+	int error;
 	struct splice_pipe_desc spd = {
 		.pages = pages,
 		.partial = partial,
@@ -290,168 +288,25 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
 	req_pages = (len + loff + PAGE_SIZE - 1) >> PAGE_SHIFT;
 	nr_pages = min(req_pages, spd.nr_pages_max);
 
-	/*
-	 * Lookup the (hopefully) full range of pages we need.
-	 */
-	spd.nr_pages = find_get_pages_contig(mapping, index, nr_pages, spd.pages);
-	index += spd.nr_pages;
-
-	/*
-	 * If find_get_pages_contig() returned fewer pages than we needed,
-	 * readahead/allocate the rest and fill in the holes.
-	 */
-	if (spd.nr_pages < nr_pages)
-		page_cache_sync_readahead(mapping, &in->f_ra, in,
-				index, req_pages - spd.nr_pages);
-
 	error = 0;
-	while (spd.nr_pages < nr_pages) {
-		/*
-		 * Page could be there, find_get_pages_contig() breaks on
-		 * the first hole.
-		 */
-		page = find_get_page(mapping, index);
-		if (!page) {
-			/*
-			 * page didn't exist, allocate one.
-			 */
-			page = page_cache_alloc_cold(mapping);
-			if (!page)
-				break;
+	while (spd.nr_pages < nr_pages && len) {
+		long ret;
 
-			error = add_to_page_cache_lru(page, mapping, index,
-				   mapping_gfp_constraint(mapping, GFP_KERNEL));
-			if (unlikely(error)) {
-				put_page(page);
-				if (error == -EEXIST)
-					continue;
-				break;
-			}
-			/*
-			 * add_to_page_cache() locks the page, unlock it
-			 * to avoid convoluting the logic below even more.
-			 */
-			unlock_page(page);
-		}
-
-		spd.pages[spd.nr_pages++] = page;
-		index++;
-	}
-
-	/*
-	 * Now loop over the map and see if we need to start IO on any
-	 * pages, fill in the partial map, etc.
-	 */
-	index = *ppos >> PAGE_SHIFT;
-	nr_pages = spd.nr_pages;
-	spd.nr_pages = 0;
-	for (page_nr = 0; page_nr < nr_pages; page_nr++) {
-		unsigned int this_len;
-
-		if (!len)
+		ret = get_page_for_read(in, loff, len, index, &page);
+		if (ret <= 0) {
+			error = ret;
 			break;
-
-		/*
-		 * this_len is the max we'll use from this page
-		 */
-		this_len = min_t(unsigned long, len, PAGE_SIZE - loff);
-		page = spd.pages[page_nr];
-
-		if (PageReadahead(page))
-			page_cache_async_readahead(mapping, &in->f_ra, in,
-					page, index, req_pages - page_nr);
-
-		/*
-		 * If the page isn't uptodate, we may need to start io on it
-		 */
-		if (!PageUptodate(page)) {
-			lock_page(page);
-
-			/*
-			 * Page was truncated, or invalidated by the
-			 * filesystem.  Redo the find/create, but this time the
-			 * page is kept locked, so there's no chance of another
-			 * race with truncate/invalidate.
-			 */
-			if (!page->mapping) {
-				unlock_page(page);
-retry_lookup:
-				page = find_or_create_page(mapping, index,
-						mapping_gfp_mask(mapping));
-
-				if (!page) {
-					error = -ENOMEM;
-					break;
-				}
-				put_page(spd.pages[page_nr]);
-				spd.pages[page_nr] = page;
-			}
-			/*
-			 * page was already under io and is now done, great
-			 */
-			if (PageUptodate(page)) {
-				unlock_page(page);
-				goto fill_it;
-			}
-
-			/*
-			 * need to read in the page
-			 */
-			error = mapping->a_ops->readpage(in, page);
-			if (unlikely(error)) {
-				/*
-				 * Re-lookup the page
-				 */
-				if (error == AOP_TRUNCATED_PAGE)
-					goto retry_lookup;
-
-				break;
-			}
-		}
-fill_it:
-		/*
-		 * i_size must be checked after PageUptodate.
-		 */
-		isize = i_size_read(mapping->host);
-		end_index = (isize - 1) >> PAGE_SHIFT;
-		if (unlikely(!isize || index > end_index))
-			break;
-
-		/*
-		 * if this is the last page, see if we need to shrink
-		 * the length and stop
-		 */
-		if (end_index == index) {
-			unsigned int plen;
-
-			/*
-			 * max good bytes in this page
-			 */
-			plen = ((isize - 1) & ~PAGE_MASK) + 1;
-			if (plen <= loff)
-				break;
-
-			/*
-			 * force quit after adding this page
-			 */
-			this_len = min(this_len, plen - loff);
-			len = this_len;
 		}
 
-		spd.partial[page_nr].offset = loff;
-		spd.partial[page_nr].len = this_len;
-		len -= this_len;
-		loff = 0;
+		spd.pages[spd.nr_pages] = page;
+		spd.partial[spd.nr_pages].offset = loff;
+		spd.partial[spd.nr_pages].len = ret;
 		spd.nr_pages++;
 		index++;
+		len -= ret;
+		loff = 0;
 	}
 
-	/*
-	 * Release any pages at the end, if we quit early. 'page_nr' is how far
-	 * we got, 'nr_pages' is how many pages are in the map.
-	 */
-	while (page_nr < nr_pages)
-		put_page(spd.pages[page_nr++]);
 	in->f_ra.prev_pos = (loff_t)index << PAGE_SHIFT;
 
 	if (spd.nr_pages)
-- 
2.5.5

  parent reply	other threads:[~2016-09-14  8:38 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-09-14  8:37 [PATCH 00/11] splice cleanups Miklos Szeredi
2016-09-14  8:37 ` [PATCH 01/11] pipe: add pipe_buf_get() helper Miklos Szeredi
2016-09-14  8:37 ` [PATCH 02/11] pipe: add pipe_buf_release() helper Miklos Szeredi
2016-09-14  8:37 ` [PATCH 03/11] pipe: add pipe_buf_confirm() helper Miklos Szeredi
2016-09-14  8:37 ` [PATCH 04/11] pipe: add pipe_buf_steal() helper Miklos Szeredi
2016-09-14  8:37 ` [PATCH 05/11] pipe: fix comment in pipe_buf_operations Miklos Szeredi
2016-09-14  8:37 ` [PATCH 06/11] pipe: no need to confirm page cache buf Miklos Szeredi
2016-09-27  3:40   ` Al Viro
2016-09-27  7:34     ` Miklos Szeredi
2016-09-14  8:37 ` [PATCH 07/11] pipe: remove generic_pipe_buf_confirm() Miklos Szeredi
2016-09-16 11:23   ` Christoph Hellwig
2016-09-14  8:37 ` [PATCH 08/11] filemap: add get_page_for_read() helper Miklos Szeredi
2016-09-27  3:43   ` Al Viro
2016-09-14  8:37 ` Miklos Szeredi [this message]
2016-09-27  3:45   ` [PATCH 09/11] splice: use get_page_for_read() Al Viro
2016-09-14  8:37 ` [PATCH 10/11] splice: don't check i_size in generic_file_splice_read() Miklos Szeredi
2016-09-14  8:37 ` [PATCH 11/11] splice: fold __generic_file_splice_read() into caller Miklos Szeredi
2016-09-14  8:55 ` [PATCH 00/11] splice cleanups Cedric Blancher
2016-09-14  9:30   ` Miklos Szeredi
2016-09-16 11:24 ` Christoph Hellwig
2016-09-27  3:55 ` Al Viro

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=1473842236-28655-10-git-send-email-mszeredi@redhat.com \
    --to=mszeredi@redhat.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=viro@zeniv.linux.org.uk \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.