Getting the page for reading is pretty complicated.  This functionality is
also duplicated between generic_file_read() generic_file_splice_read().

So first extract the common code from do_generic_file_read() into a
separate helper function that takes a filp, the page index, the offset into
the page and the byte count and returns the page ready for reading (or an
error).

This makes do_generic_file_read() much easier to read as well.

Signed-off-by: Miklos Szeredi <mszer...@redhat.com>
---
 include/linux/pagemap.h |   3 +
 mm/filemap.c            | 339 +++++++++++++++++++++++++-----------------------
 2 files changed, 177 insertions(+), 165 deletions(-)

diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 66a1260b33de..f0369ded606c 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -653,4 +653,7 @@ static inline unsigned long dir_pages(struct inode *inode)
                               PAGE_SHIFT;
 }
 
+int get_page_for_read(struct file *filp, unsigned long offset, size_t count,
+                     pgoff_t index, struct page **pagep);
+
 #endif /* _LINUX_PAGEMAP_H */
diff --git a/mm/filemap.c b/mm/filemap.c
index 8a287dfc5372..288e0ee803b8 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1648,6 +1648,170 @@ static void shrink_readahead_size_eio(struct file *filp,
        ra->ra_pages /= 4;
 }
 
+int get_page_for_read(struct file *filp, unsigned long offset, size_t count,
+                     pgoff_t index, struct page **pagep)
+{
+       struct address_space *mapping = filp->f_mapping;
+       struct inode *inode = mapping->host;
+       struct file_ra_state *ra = &filp->f_ra;
+       int error = 0;
+       struct page *page;
+       pgoff_t end_index;
+       loff_t isize;
+       unsigned long nr;
+       pgoff_t pg_count = (offset + count + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+find_page:
+       page = find_get_page(mapping, index);
+       if (!page) {
+               page_cache_sync_readahead(mapping, ra, filp, index, pg_count);
+               page = find_get_page(mapping, index);
+               if (unlikely(page == NULL))
+                       goto no_cached_page;
+       }
+       if (PageReadahead(page)) {
+               page_cache_async_readahead(mapping, ra, filp, page,
+                                          index, pg_count);
+       }
+       if (!PageUptodate(page)) {
+               /*
+                * See comment in do_read_cache_page on why
+                * wait_on_page_locked is used to avoid unnecessarily
+                * serialisations and why it's safe.
+                */
+               wait_on_page_locked_killable(page);
+               if (PageUptodate(page))
+                       goto page_ok;
+
+               if (inode->i_blkbits == PAGE_SHIFT ||
+                   !mapping->a_ops->is_partially_uptodate)
+                       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;
+               if (!mapping->a_ops->is_partially_uptodate(page, offset, count))
+                       goto page_not_up_to_date_locked;
+               unlock_page(page);
+       }
+page_ok:
+       /*
+        * i_size must be checked after we know the page is Uptodate.
+        *
+        * Checking i_size after the check allows us to calculate
+        * the correct value for "nr", which means the zero-filled
+        * part of the page is not copied back to userspace (unless
+        * another truncate extends the file - this is desired though).
+        */
+
+       isize = i_size_read(inode);
+       end_index = (isize - 1) >> PAGE_SHIFT;
+       if (unlikely(!isize || index > end_index))
+               goto out_put_page;
+
+       /* nr is the maximum number of bytes to copy from this page */
+       nr = PAGE_SIZE;
+       if (index == end_index) {
+               nr = ((isize - 1) & ~PAGE_MASK) + 1;
+               if (nr <= offset)
+                       goto out_put_page;
+       }
+       nr = nr - offset;
+
+       *pagep = page;
+       return nr;
+
+page_not_up_to_date:
+       /* Get exclusive access to the page ... */
+       error = lock_page_killable(page);
+       if (unlikely(error))
+               goto out_put_page;
+
+page_not_up_to_date_locked:
+       /* Did it get truncated before we got the lock? */
+       if (!page->mapping) {
+               unlock_page(page);
+               put_page(page);
+               cond_resched();
+               goto find_page;
+       }
+
+       /* Did somebody else fill it already? */
+       if (PageUptodate(page)) {
+               unlock_page(page);
+               goto page_ok;
+       }
+
+readpage:
+       /*
+        * A previous I/O error may have been due to temporary
+        * failures, eg. multipath errors.
+        * PG_error will be set again if readpage fails.
+        */
+       ClearPageError(page);
+       /* Start the actual read. The read will unlock the page. */
+       error = mapping->a_ops->readpage(filp, page);
+
+       if (unlikely(error)) {
+               if (error == AOP_TRUNCATED_PAGE) {
+                       put_page(page);
+                       error = 0;
+                       goto find_page;
+               }
+               goto out_put_page;
+       }
+
+       if (!PageUptodate(page)) {
+               error = lock_page_killable(page);
+               if (unlikely(error))
+                       goto out_put_page;
+               if (!PageUptodate(page)) {
+                       if (page->mapping == NULL) {
+                               /*
+                                * invalidate_mapping_pages got it
+                                */
+                               unlock_page(page);
+                               put_page(page);
+                               goto find_page;
+                       }
+                       unlock_page(page);
+                       shrink_readahead_size_eio(filp, ra);
+                       error = -EIO;
+                       goto out_put_page;
+               }
+               unlock_page(page);
+       }
+       goto page_ok;
+
+no_cached_page:
+       /*
+        * Ok, it wasn't cached, so we need to create a new
+        * page..
+        */
+       page = page_cache_alloc_cold(mapping);
+       if (!page) {
+               error = -ENOMEM;
+               goto out;
+       }
+       error = add_to_page_cache_lru(page, mapping, index,
+                                     mapping_gfp_constraint(mapping, 
GFP_KERNEL));
+       if (!error)
+               goto readpage;
+
+       if (error == -EEXIST) {
+               put_page(page);
+               error = 0;
+               goto find_page;
+       }
+
+out_put_page:
+       put_page(page);
+out:
+       return error;
+
+}
+
 /**
  * do_generic_file_read - generic file read routine
  * @filp:      the file to read
@@ -1664,11 +1828,8 @@ static void shrink_readahead_size_eio(struct file *filp,
 static ssize_t do_generic_file_read(struct file *filp, loff_t *ppos,
                struct iov_iter *iter, ssize_t written)
 {
-       struct address_space *mapping = filp->f_mapping;
-       struct inode *inode = mapping->host;
        struct file_ra_state *ra = &filp->f_ra;
        pgoff_t index;
-       pgoff_t last_index;
        pgoff_t prev_index;
        unsigned long offset;      /* offset into pagecache page */
        unsigned int prev_offset;
@@ -1677,87 +1838,26 @@ static ssize_t do_generic_file_read(struct file *filp, 
loff_t *ppos,
        index = *ppos >> PAGE_SHIFT;
        prev_index = ra->prev_pos >> PAGE_SHIFT;
        prev_offset = ra->prev_pos & (PAGE_SIZE-1);
-       last_index = (*ppos + iter->count + PAGE_SIZE-1) >> PAGE_SHIFT;
        offset = *ppos & ~PAGE_MASK;
 
        for (;;) {
                struct page *page;
-               pgoff_t end_index;
-               loff_t isize;
-               unsigned long nr, ret;
+               long nr, ret;
 
                cond_resched();
-find_page:
-               page = find_get_page(mapping, index);
-               if (!page) {
-                       page_cache_sync_readahead(mapping,
-                                       ra, filp,
-                                       index, last_index - index);
-                       page = find_get_page(mapping, index);
-                       if (unlikely(page == NULL))
-                               goto no_cached_page;
-               }
-               if (PageReadahead(page)) {
-                       page_cache_async_readahead(mapping,
-                                       ra, filp, page,
-                                       index, last_index - index);
-               }
-               if (!PageUptodate(page)) {
-                       /*
-                        * See comment in do_read_cache_page on why
-                        * wait_on_page_locked is used to avoid unnecessarily
-                        * serialisations and why it's safe.
-                        */
-                       wait_on_page_locked_killable(page);
-                       if (PageUptodate(page))
-                               goto page_ok;
-
-                       if (inode->i_blkbits == PAGE_SHIFT ||
-                                       !mapping->a_ops->is_partially_uptodate)
-                               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;
-                       if (!mapping->a_ops->is_partially_uptodate(page,
-                                                       offset, iter->count))
-                               goto page_not_up_to_date_locked;
-                       unlock_page(page);
-               }
-page_ok:
-               /*
-                * i_size must be checked after we know the page is Uptodate.
-                *
-                * Checking i_size after the check allows us to calculate
-                * the correct value for "nr", which means the zero-filled
-                * part of the page is not copied back to userspace (unless
-                * another truncate extends the file - this is desired though).
-                */
-
-               isize = i_size_read(inode);
-               end_index = (isize - 1) >> PAGE_SHIFT;
-               if (unlikely(!isize || index > end_index)) {
-                       put_page(page);
-                       goto out;
-               }
-
-               /* nr is the maximum number of bytes to copy from this page */
-               nr = PAGE_SIZE;
-               if (index == end_index) {
-                       nr = ((isize - 1) & ~PAGE_MASK) + 1;
-                       if (nr <= offset) {
-                               put_page(page);
-                               goto out;
-                       }
+               ret = get_page_for_read(filp, offset, iter->count, index,
+                                       &page);
+               if (ret <= 0) {
+                       error = ret;
+                       break;
                }
-               nr = nr - offset;
+               nr = ret;
 
                /* If users can be writing to this page using arbitrary
                 * virtual addresses, take care about potential aliasing
                 * before reading the page on the kernel side.
                 */
-               if (mapping_writably_mapped(mapping))
+               if (mapping_writably_mapped(filp->f_mapping))
                        flush_dcache_page(page);
 
                /*
@@ -1782,104 +1882,13 @@ page_ok:
                put_page(page);
                written += ret;
                if (!iov_iter_count(iter))
-                       goto out;
+                       break;
                if (ret < nr) {
                        error = -EFAULT;
-                       goto out;
-               }
-               continue;
-
-page_not_up_to_date:
-               /* Get exclusive access to the page ... */
-               error = lock_page_killable(page);
-               if (unlikely(error))
-                       goto readpage_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);
-                       continue;
-               }
-
-               /* Did somebody else fill it already? */
-               if (PageUptodate(page)) {
-                       unlock_page(page);
-                       goto page_ok;
-               }
-
-readpage:
-               /*
-                * A previous I/O error may have been due to temporary
-                * failures, eg. multipath errors.
-                * PG_error will be set again if readpage fails.
-                */
-               ClearPageError(page);
-               /* Start the actual read. The read will unlock the page. */
-               error = mapping->a_ops->readpage(filp, page);
-
-               if (unlikely(error)) {
-                       if (error == AOP_TRUNCATED_PAGE) {
-                               put_page(page);
-                               error = 0;
-                               goto find_page;
-                       }
-                       goto readpage_error;
-               }
-
-               if (!PageUptodate(page)) {
-                       error = lock_page_killable(page);
-                       if (unlikely(error))
-                               goto readpage_error;
-                       if (!PageUptodate(page)) {
-                               if (page->mapping == NULL) {
-                                       /*
-                                        * invalidate_mapping_pages got it
-                                        */
-                                       unlock_page(page);
-                                       put_page(page);
-                                       goto find_page;
-                               }
-                               unlock_page(page);
-                               shrink_readahead_size_eio(filp, ra);
-                               error = -EIO;
-                               goto readpage_error;
-                       }
-                       unlock_page(page);
-               }
-
-               goto page_ok;
-
-readpage_error:
-               /* UHHUH! A synchronous read error occurred. Report it */
-               put_page(page);
-               goto out;
-
-no_cached_page:
-               /*
-                * Ok, it wasn't cached, so we need to create a new
-                * page..
-                */
-               page = page_cache_alloc_cold(mapping);
-               if (!page) {
-                       error = -ENOMEM;
-                       goto out;
-               }
-               error = add_to_page_cache_lru(page, mapping, index,
-                               mapping_gfp_constraint(mapping, GFP_KERNEL));
-               if (error) {
-                       put_page(page);
-                       if (error == -EEXIST) {
-                               error = 0;
-                               goto find_page;
-                       }
-                       goto out;
+                       break;
                }
-               goto readpage;
        }
 
-out:
        ra->prev_pos = prev_index;
        ra->prev_pos <<= PAGE_SHIFT;
        ra->prev_pos |= prev_offset;
-- 
2.5.5

Reply via email to