]> www.infradead.org Git - linux-platform-drivers-x86.git/commitdiff
btrfs: introduce btrfs_subpage for data inodes
authorQu Wenruo <wqu@suse.com>
Tue, 26 Jan 2021 08:34:00 +0000 (16:34 +0800)
committerDavid Sterba <dsterba@suse.com>
Mon, 8 Feb 2021 21:59:03 +0000 (22:59 +0100)
To support subpage sector size, data also need extra info to make sure
which sectors in a page are uptodate/dirty/...

This patch will make pages for data inodes get btrfs_subpage structure
attached, and detached when the page is freed.

This patch also slightly changes the timing when
set_page_extent_mapped() is called to make sure:

- We have page->mapping set
  page->mapping->host is used to grab btrfs_fs_info, thus we can only
  call this function after page is mapped to an inode.

  One call site attaches pages to inode manually, thus we have to modify
  the timing of set_page_extent_mapped() a bit.

- As soon as possible, before other operations
  Since memory allocation can fail, we have to do extra error handling.
  Calling set_page_extent_mapped() as soon as possible can simply the
  error handling for several call sites.

The idea is pretty much the same as iomap_page, but with more bitmaps
for btrfs specific cases.

Currently the plan is to switch iomap if iomap can provide sector
aligned write back (only write back dirty sectors, but not the full
page, data balance require this feature).

So we will stick to btrfs specific bitmap for now.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/compression.c
fs/btrfs/extent_io.c
fs/btrfs/extent_io.h
fs/btrfs/file.c
fs/btrfs/free-space-cache.c
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/btrfs/reflink.c
fs/btrfs/relocation.c

index 5ae3fa0386b769faf048d18f7ea928294e4f81a6..6d203acfdeb35810329184b973e56f7c75f066cd 100644 (file)
@@ -542,13 +542,19 @@ static noinline int add_ra_bio_pages(struct inode *inode,
                        goto next;
                }
 
-               end = last_offset + PAGE_SIZE - 1;
                /*
                 * at this point, we have a locked page in the page cache
                 * for these bytes in the file.  But, we have to make
                 * sure they map to this compressed extent on disk.
                 */
-               set_page_extent_mapped(page);
+               ret = set_page_extent_mapped(page);
+               if (ret < 0) {
+                       unlock_page(page);
+                       put_page(page);
+                       break;
+               }
+
+               end = last_offset + PAGE_SIZE - 1;
                lock_extent(tree, last_offset, end);
                read_lock(&em_tree->lock);
                em = lookup_extent_mapping(em_tree, last_offset,
index 491fc01146723ef2411c5bbe57e86068f7d669a3..5ff85ecc6c3fa07c70d673610017440aff097a49 100644 (file)
@@ -3191,10 +3191,38 @@ static int attach_extent_buffer_page(struct extent_buffer *eb,
        return ret;
 }
 
-void set_page_extent_mapped(struct page *page)
+int set_page_extent_mapped(struct page *page)
 {
+       struct btrfs_fs_info *fs_info;
+
+       ASSERT(page->mapping);
+
+       if (PagePrivate(page))
+               return 0;
+
+       fs_info = btrfs_sb(page->mapping->host->i_sb);
+
+       if (fs_info->sectorsize < PAGE_SIZE)
+               return btrfs_attach_subpage(fs_info, page, BTRFS_SUBPAGE_DATA);
+
+       attach_page_private(page, (void *)EXTENT_PAGE_PRIVATE);
+       return 0;
+}
+
+void clear_page_extent_mapped(struct page *page)
+{
+       struct btrfs_fs_info *fs_info;
+
+       ASSERT(page->mapping);
+
        if (!PagePrivate(page))
-               attach_page_private(page, (void *)EXTENT_PAGE_PRIVATE);
+               return;
+
+       fs_info = btrfs_sb(page->mapping->host->i_sb);
+       if (fs_info->sectorsize < PAGE_SIZE)
+               return btrfs_detach_subpage(fs_info, page);
+
+       detach_page_private(page);
 }
 
 static struct extent_map *
@@ -3251,7 +3279,12 @@ int btrfs_do_readpage(struct page *page, struct extent_map **em_cached,
        unsigned long this_bio_flag = 0;
        struct extent_io_tree *tree = &BTRFS_I(inode)->io_tree;
 
-       set_page_extent_mapped(page);
+       ret = set_page_extent_mapped(page);
+       if (ret < 0) {
+               unlock_extent(tree, start, end);
+               SetPageError(page);
+               goto out;
+       }
 
        if (!PageUptodate(page)) {
                if (cleancache_get_page(page) == 0) {
@@ -3691,7 +3724,11 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
                flush_dcache_page(page);
        }
 
-       set_page_extent_mapped(page);
+       ret = set_page_extent_mapped(page);
+       if (ret < 0) {
+               SetPageError(page);
+               goto done;
+       }
 
        if (!epd->extent_locked) {
                ret = writepage_delalloc(BTRFS_I(inode), page, wbc, start,
index 2d8187c84812861afbf07494d70191a06dd83396..047b3e66897fd239c511bc6f9f15aecc758f205e 100644 (file)
@@ -178,7 +178,8 @@ int btree_write_cache_pages(struct address_space *mapping,
 void extent_readahead(struct readahead_control *rac);
 int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,
                  u64 start, u64 len);
-void set_page_extent_mapped(struct page *page);
+int set_page_extent_mapped(struct page *page);
+void clear_page_extent_mapped(struct page *page);
 
 struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info,
                                          u64 start, u64 owner_root, int level);
index be5350f5bedf0f7cc4ddaab5e472cfed59f963f9..bf52d7e85914407dd0b4fc371097a585f2b181b5 100644 (file)
@@ -1369,6 +1369,12 @@ again:
                        goto fail;
                }
 
+               err = set_page_extent_mapped(pages[i]);
+               if (err < 0) {
+                       faili = i;
+                       goto fail;
+               }
+
                if (i == 0)
                        err = prepare_uptodate_page(inode, pages[i], pos,
                                                    force_uptodate);
@@ -1453,23 +1459,11 @@ lock_and_cleanup_extent_if_need(struct btrfs_inode *inode, struct page **pages,
        }
 
        /*
-        * It's possible the pages are dirty right now, but we don't want
-        * to clean them yet because copy_from_user may catch a page fault
-        * and we might have to fall back to one page at a time.  If that
-        * happens, we'll unlock these pages and we'd have a window where
-        * reclaim could sneak in and drop the once-dirty page on the floor
-        * without writing it.
-        *
-        * We have the pages locked and the extent range locked, so there's
-        * no way someone can start IO on any dirty pages in this range.
-        *
-        * We'll call btrfs_dirty_pages() later on, and that will flip around
-        * delalloc bits and dirty the pages as required.
+        * We should be called after prepare_pages() which should have locked
+        * all pages in the range.
         */
-       for (i = 0; i < num_pages; i++) {
-               set_page_extent_mapped(pages[i]);
+       for (i = 0; i < num_pages; i++)
                WARN_ON(!PageLocked(pages[i]));
-       }
 
        return ret;
 }
index 0d6dcb5ff963c7fe6aba48b5ebd9f363cc379d27..6134e10a6e7fcf563ab12425a5db58e1dadbe802 100644 (file)
@@ -431,11 +431,22 @@ static int io_ctl_prepare_pages(struct btrfs_io_ctl *io_ctl, bool uptodate)
        int i;
 
        for (i = 0; i < io_ctl->num_pages; i++) {
+               int ret;
+
                page = find_or_create_page(inode->i_mapping, i, mask);
                if (!page) {
                        io_ctl_drop_pages(io_ctl);
                        return -ENOMEM;
                }
+
+               ret = set_page_extent_mapped(page);
+               if (ret < 0) {
+                       unlock_page(page);
+                       put_page(page);
+                       io_ctl_drop_pages(io_ctl);
+                       return ret;
+               }
+
                io_ctl->pages[i] = page;
                if (uptodate && !PageUptodate(page)) {
                        btrfs_readpage(NULL, page);
@@ -455,10 +466,8 @@ static int io_ctl_prepare_pages(struct btrfs_io_ctl *io_ctl, bool uptodate)
                }
        }
 
-       for (i = 0; i < io_ctl->num_pages; i++) {
+       for (i = 0; i < io_ctl->num_pages; i++)
                clear_page_dirty_for_io(io_ctl->pages[i]);
-               set_page_extent_mapped(io_ctl->pages[i]);
-       }
 
        return 0;
 }
index 3337c8ee79280793bfe0cb2cca5c1e8b773762fb..5522e9d09c8a0c7c518e8e813b1881d5dda7f386 100644 (file)
@@ -4720,6 +4720,9 @@ again:
                ret = -ENOMEM;
                goto out;
        }
+       ret = set_page_extent_mapped(page);
+       if (ret < 0)
+               goto out_unlock;
 
        if (!PageUptodate(page)) {
                ret = btrfs_readpage(NULL, page);
@@ -4737,7 +4740,6 @@ again:
        wait_on_page_writeback(page);
 
        lock_extent_bits(io_tree, block_start, block_end, &cached_state);
-       set_page_extent_mapped(page);
 
        ordered = btrfs_lookup_ordered_extent(inode, block_start);
        if (ordered) {
@@ -8125,7 +8127,7 @@ static int __btrfs_releasepage(struct page *page, gfp_t gfp_flags)
 {
        int ret = try_release_extent_mapping(page, gfp_flags);
        if (ret == 1)
-               detach_page_private(page);
+               clear_page_extent_mapped(page);
        return ret;
 }
 
@@ -8285,7 +8287,7 @@ again:
        }
 
        ClearPageChecked(page);
-       detach_page_private(page);
+       clear_page_extent_mapped(page);
 }
 
 /*
@@ -8364,7 +8366,12 @@ again:
        wait_on_page_writeback(page);
 
        lock_extent_bits(io_tree, page_start, page_end, &cached_state);
-       set_page_extent_mapped(page);
+       ret2 = set_page_extent_mapped(page);
+       if (ret2 < 0) {
+               ret = vmf_error(ret2);
+               unlock_extent_cached(io_tree, page_start, page_end, &cached_state);
+               goto out_unlock;
+       }
 
        /*
         * we can't set the delalloc bits if there are pending ordered
index 7f2935ea8d3aba5888b02ba5fc282a211ad09d44..e6a63f652235cf06aca1219b1cd8e28182106a52 100644 (file)
@@ -1314,6 +1314,13 @@ again:
                if (!page)
                        break;
 
+               ret = set_page_extent_mapped(page);
+               if (ret < 0) {
+                       unlock_page(page);
+                       put_page(page);
+                       break;
+               }
+
                page_start = page_offset(page);
                page_end = page_start + PAGE_SIZE - 1;
                while (1) {
@@ -1435,7 +1442,6 @@ again:
        for (i = 0; i < i_done; i++) {
                clear_page_dirty_for_io(pages[i]);
                ClearPageChecked(pages[i]);
-               set_page_extent_mapped(pages[i]);
                set_page_dirty(pages[i]);
                unlock_page(pages[i]);
                put_page(pages[i]);
index b03e7891394e36c838f83a7219beb065177f5ae0..b24396cf2f99fd38a9bfa1664d3877aa94596daf 100644 (file)
@@ -81,7 +81,10 @@ static int copy_inline_to_page(struct btrfs_inode *inode,
                goto out_unlock;
        }
 
-       set_page_extent_mapped(page);
+       ret = set_page_extent_mapped(page);
+       if (ret < 0)
+               goto out_unlock;
+
        clear_extent_bit(&inode->io_tree, file_offset, range_end,
                         EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG,
                         0, 0, NULL);
index d29baf3822a7c89982f7ed0c7c07d7467a1f59e5..473b788748441590df0b9776c31bac221595170d 100644 (file)
@@ -2679,6 +2679,15 @@ static int relocate_file_extent_cluster(struct inode *inode,
                                goto out;
                        }
                }
+               ret = set_page_extent_mapped(page);
+               if (ret < 0) {
+                       btrfs_delalloc_release_metadata(BTRFS_I(inode),
+                                                       PAGE_SIZE, true);
+                       btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE);
+                       unlock_page(page);
+                       put_page(page);
+                       goto out;
+               }
 
                if (PageReadahead(page)) {
                        page_cache_async_readahead(inode->i_mapping,
@@ -2706,8 +2715,6 @@ static int relocate_file_extent_cluster(struct inode *inode,
 
                lock_extent(&BTRFS_I(inode)->io_tree, page_start, page_end);
 
-               set_page_extent_mapped(page);
-
                if (nr < cluster->nr &&
                    page_start + offset == cluster->boundary[nr]) {
                        set_extent_bits(&BTRFS_I(inode)->io_tree,