From: Goldwyn Rodrigues <rgold...@suse.com>

We cannot perform a readpage in iomap_apply after
iomap_begin() because we have our extents locked. So,
we perform a readpage and make sure we unlock it, but
increase the page count.

Question: How do we deal with -EAGAIN return from
prepare_uptodate_page()? Under what scenario's would this occur?

Signed-off-by: Goldwyn Rodrigues <rgold...@suse.com>
---
 fs/btrfs/file.c  | 116 ++++++++++++++++++++++---------------------------------
 fs/btrfs/iomap.h |   1 +
 2 files changed, 47 insertions(+), 70 deletions(-)

diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index b7390214ef3a..b34ec493fe4b 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1252,84 +1252,36 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle 
*trans,
        return 0;
 }
 
-/*
- * on error we return an unlocked page and the error value
- * on success we return a locked page and 0
- */
-static int prepare_uptodate_page(struct inode *inode,
-                                struct page *page, u64 pos,
-                                bool force_uptodate)
+static int prepare_uptodate_page(struct inode *inode, u64 pos, struct page 
**pagep)
 {
+       struct page *page = NULL;
        int ret = 0;
+       int index = pos >> PAGE_SHIFT;
+
+       if (!(pos & (PAGE_SIZE - 1)))
+               goto out;
+
+       page = grab_cache_page_write_begin(inode->i_mapping, index,
+                       AOP_FLAG_NOFS);
 
-       if (((pos & (PAGE_SIZE - 1)) || force_uptodate) &&
-           !PageUptodate(page)) {
+       if (!PageUptodate(page)) {
                ret = btrfs_readpage(NULL, page);
                if (ret)
-                       return ret;
-               lock_page(page);
+                       goto out;
                if (!PageUptodate(page)) {
-                       unlock_page(page);
-                       return -EIO;
+                       ret = -EIO;
+                       goto out;
                }
                if (page->mapping != inode->i_mapping) {
-                       unlock_page(page);
-                       return -EAGAIN;
-               }
-       }
-       return 0;
-}
-
-/*
- * this just gets pages into the page cache and locks them down.
- */
-static noinline int prepare_pages(struct inode *inode, struct page **pages,
-                                 size_t num_pages, loff_t pos,
-                                 size_t write_bytes, bool force_uptodate)
-{
-       int i;
-       unsigned long index = pos >> PAGE_SHIFT;
-       gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping);
-       int err = 0;
-       int faili;
-
-       for (i = 0; i < num_pages; i++) {
-again:
-               pages[i] = find_or_create_page(inode->i_mapping, index + i,
-                                              mask | __GFP_WRITE);
-               if (!pages[i]) {
-                       faili = i - 1;
-                       err = -ENOMEM;
-                       goto fail;
-               }
-
-               if (i == 0)
-                       err = prepare_uptodate_page(inode, pages[i], pos,
-                                                   force_uptodate);
-               if (!err && i == num_pages - 1)
-                       err = prepare_uptodate_page(inode, pages[i],
-                                                   pos + write_bytes, false);
-               if (err) {
-                       put_page(pages[i]);
-                       if (err == -EAGAIN) {
-                               err = 0;
-                               goto again;
-                       }
-                       faili = i - 1;
-                       goto fail;
+                       ret = -EAGAIN;
+                       goto out;
                }
-               wait_on_page_writeback(pages[i]);
        }
-
-       return 0;
-fail:
-       while (faili >= 0) {
-               unlock_page(pages[faili]);
-               put_page(pages[faili]);
-               faili--;
-       }
-       return err;
-
+out:
+       if (page)
+               unlock_page(page);
+       *pagep = page;
+       return ret;
 }
 
 static int btrfs_find_new_delalloc_bytes(struct btrfs_inode *inode,
@@ -1502,7 +1454,7 @@ int btrfs_file_iomap_begin(struct inode *inode, loff_t 
pos, loff_t length,
                         fs_info->sectorsize);
         bim->extent_locked = false;
         iomap->type = IOMAP_DELALLOC;
-        iomap->flags = IOMAP_F_NEW;
+        iomap->flags = IOMAP_F_NEW | IOMAP_F_NOBH;
 
        extent_changeset_release(bim->data_reserved);
         /* Reserve data/quota space */
@@ -1526,7 +1478,7 @@ int btrfs_file_iomap_begin(struct inode *inode, loff_t 
pos, loff_t length,
                                         sector_offset,
                                         fs_info->sectorsize);
                         iomap->type = IOMAP_UNWRITTEN;
-                        iomap->flags = 0;
+                        iomap->flags &= ~IOMAP_F_NEW;
                 } else {
                         return ret;
                 }
@@ -1543,6 +1495,20 @@ int btrfs_file_iomap_begin(struct inode *inode, loff_t 
pos, loff_t length,
         }
 
        bim->extent_locked = 0;
+
+       if (pos < inode->i_size) {
+               ret = prepare_uptodate_page(inode, pos, &bim->first_page);
+               if (ret)
+                       goto release;
+       }
+
+       if ((length > PAGE_SIZE) &&
+                       (round_down(length + pos, PAGE_SIZE) < inode->i_size)) {
+               ret = prepare_uptodate_page(inode, pos + length, 
&bim->last_page);
+               if (ret)
+                       goto release;
+       }
+
 again:
         bim->extent_locked = lock_and_cleanup_extent(BTRFS_I(inode),
                         pos, write_bytes, &bim->lockstart,
@@ -1597,6 +1563,16 @@ int btrfs_file_iomap_end(struct inode *inode, loff_t 
pos, loff_t length,
 
        dirty_sectors = BTRFS_BYTES_TO_BLKS(fs_info, dirty_sectors);
 
+       if (bim->first_page) {
+               put_page(bim->first_page);
+               bim->first_page = NULL;
+       }
+
+       if (bim->last_page) {
+               put_page(bim->last_page);
+               bim->last_page = NULL;
+       }
+
        if (unlikely(copied == 0))
                dirty_sectors = 0;
        else
diff --git a/fs/btrfs/iomap.h b/fs/btrfs/iomap.h
index ac34b3412f64..f62e3ee6d4de 100644
--- a/fs/btrfs/iomap.h
+++ b/fs/btrfs/iomap.h
@@ -14,6 +14,7 @@ struct btrfs_iomap {
        int extent_locked;
        struct extent_state *cached_state;
        struct extent_changeset *data_reserved;
+       struct page *first_page, *last_page;
 };
 
 #endif
-- 
2.14.2

--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to