btrfs: introduce subpage metadata validation check
[sfrench/cifs-2.6.git] / fs / btrfs / disk-io.c
index e0d1b328397e80f120c276646389641f9be6bb43..d34c6d61928f8a640dfbc6f33d6bd4d50eb93122 100644 (file)
@@ -591,6 +591,59 @@ out:
        return ret;
 }
 
+static int validate_subpage_buffer(struct page *page, u64 start, u64 end,
+                                  int mirror)
+{
+       struct btrfs_fs_info *fs_info = btrfs_sb(page->mapping->host->i_sb);
+       struct extent_buffer *eb;
+       bool reads_done;
+       int ret = 0;
+
+       /*
+        * We don't allow bio merge for subpage metadata read, so we should
+        * only get one eb for each endio hook.
+        */
+       ASSERT(end == start + fs_info->nodesize - 1);
+       ASSERT(PagePrivate(page));
+
+       eb = find_extent_buffer(fs_info, start);
+       /*
+        * When we are reading one tree block, eb must have been inserted into
+        * the radix tree. If not, something is wrong.
+        */
+       ASSERT(eb);
+
+       reads_done = atomic_dec_and_test(&eb->io_pages);
+       /* Subpage read must finish in page read */
+       ASSERT(reads_done);
+
+       eb->read_mirror = mirror;
+       if (test_bit(EXTENT_BUFFER_READ_ERR, &eb->bflags)) {
+               ret = -EIO;
+               goto err;
+       }
+       ret = validate_extent_buffer(eb);
+       if (ret < 0)
+               goto err;
+
+       if (test_and_clear_bit(EXTENT_BUFFER_READAHEAD, &eb->bflags))
+               btree_readahead_hook(eb, ret);
+
+       set_extent_buffer_uptodate(eb);
+
+       free_extent_buffer(eb);
+       return ret;
+err:
+       /*
+        * end_bio_extent_readpage decrements io_pages in case of error,
+        * make sure it has something to decrement.
+        */
+       atomic_inc(&eb->io_pages);
+       clear_extent_buffer_uptodate(eb);
+       free_extent_buffer(eb);
+       return ret;
+}
+
 int btrfs_validate_metadata_buffer(struct btrfs_io_bio *io_bio,
                                   struct page *page, u64 start, u64 end,
                                   int mirror)
@@ -600,6 +653,10 @@ int btrfs_validate_metadata_buffer(struct btrfs_io_bio *io_bio,
        int reads_done;
 
        ASSERT(page->private);
+
+       if (btrfs_sb(page->mapping->host->i_sb)->sectorsize < PAGE_SIZE)
+               return validate_subpage_buffer(page, start, end, mirror);
+
        eb = (struct extent_buffer *)page->private;
 
        /*