]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
Btrfs: fix unexpected cow in run_delalloc_nocow
authorLiu Bo <bo.li.liu@oracle.com>
Thu, 1 Feb 2018 00:09:13 +0000 (17:09 -0700)
committerThadeu Lima de Souza Cascardo <cascardo@canonical.com>
Mon, 9 Apr 2018 13:42:00 +0000 (10:42 -0300)
BugLink: http://bugs.launchpad.net/bugs/1762370
commit 5811375325420052fcadd944792a416a43072b7f upstream.

Fstests generic/475 provides a way to fail metadata reads while
checking if checksum exists for the inode inside run_delalloc_nocow(),
and csum_exist_in_range() interprets error (-EIO) as inode having
checksum and makes its caller enter the cow path.

In case of free space inode, this ends up with a warning in
cow_file_range().

The same problem applies to btrfs_cross_ref_exist() since it may also
read metadata in between.

With this, run_delalloc_nocow() bails out when errors occur at the two
places.

cc: <stable@vger.kernel.org> v2.6.28+
Fixes: 17d217fe970d ("Btrfs: fix nodatasum handling in balancing code")
Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
fs/btrfs/inode.c

index c04183cc21178d63f1217df0e28c438068184a81..ae07b3a2fb5d48f5dddd277226933dce50c370bc 100644 (file)
@@ -1257,6 +1257,8 @@ static noinline int csum_exist_in_range(struct btrfs_fs_info *fs_info,
                list_del(&sums->list);
                kfree(sums);
        }
+       if (ret < 0)
+               return ret;
        return 1;
 }
 
@@ -1389,10 +1391,23 @@ next_slot:
                                goto out_check;
                        if (btrfs_extent_readonly(fs_info, disk_bytenr))
                                goto out_check;
-                       if (btrfs_cross_ref_exist(root, ino,
-                                                 found_key.offset -
-                                                 extent_offset, disk_bytenr))
+                       ret = btrfs_cross_ref_exist(root, ino,
+                                                   found_key.offset -
+                                                   extent_offset, disk_bytenr);
+                       if (ret) {
+                               /*
+                                * ret could be -EIO if the above fails to read
+                                * metadata.
+                                */
+                               if (ret < 0) {
+                                       if (cow_start != (u64)-1)
+                                               cur_offset = cow_start;
+                                       goto error;
+                               }
+
+                               WARN_ON_ONCE(nolock);
                                goto out_check;
+                       }
                        disk_bytenr += extent_offset;
                        disk_bytenr += cur_offset - found_key.offset;
                        num_bytes = min(end + 1, extent_end) - cur_offset;
@@ -1410,10 +1425,22 @@ next_slot:
                         * this ensure that csum for a given extent are
                         * either valid or do not exist.
                         */
-                       if (csum_exist_in_range(fs_info, disk_bytenr,
-                                               num_bytes)) {
+                       ret = csum_exist_in_range(fs_info, disk_bytenr,
+                                                 num_bytes);
+                       if (ret) {
                                if (!nolock)
                                        btrfs_end_write_no_snapshotting(root);
+
+                               /*
+                                * ret could be -EIO if the above fails to read
+                                * metadata.
+                                */
+                               if (ret < 0) {
+                                       if (cow_start != (u64)-1)
+                                               cur_offset = cow_start;
+                                       goto error;
+                               }
+                               WARN_ON_ONCE(nolock);
                                goto out_check;
                        }
                        if (!btrfs_inc_nocow_writers(fs_info, disk_bytenr)) {