Commit 58113753 authored by Liu Bo's avatar Liu Bo Committed by David Sterba
Browse files

Btrfs: fix unexpected cow in run_delalloc_nocow



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: 17d217fe ("Btrfs: fix nodatasum handling in balancing code")
Signed-off-by: default avatarLiu Bo <bo.li.liu@oracle.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 9678c543
Loading
Loading
Loading
Loading
+32 −5
Original line number Diff line number Diff line
@@ -1256,6 +1256,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;
}

@@ -1388,10 +1390,23 @@ next_slot:
				goto out_check;
			if (btrfs_extent_readonly(fs_info, disk_bytenr))
				goto out_check;
			if (btrfs_cross_ref_exist(root, ino,
			ret = btrfs_cross_ref_exist(root, ino,
						    found_key.offset -
						  extent_offset, disk_bytenr))
						    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;
@@ -1409,10 +1424,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)) {