Commit 3edf7d33 authored by Chris Mason's avatar Chris Mason
Browse files

Btrfs: Handle data checksumming on bios that span multiple ordered extents



Data checksumming is done right before the bio is sent down the IO stack,
which means a single bio might span more than one ordered extent.  In
this case, the checksumming data is split between two ordered extents.

Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent eb84ae03
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1579,8 +1579,8 @@ int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans,
int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
			   struct btrfs_root *root, struct inode *inode,
			   struct btrfs_ordered_sum *sums);
int btrfs_csum_one_bio(struct btrfs_root *root,
		       struct bio *bio, struct btrfs_ordered_sum **sums_ret);
int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode,
		       struct bio *bio);
struct btrfs_csum_item *btrfs_lookup_csum(struct btrfs_trans_handle *trans,
					  struct btrfs_root *root,
					  struct btrfs_path *path,
+39 −4
Original line number Diff line number Diff line
@@ -134,26 +134,53 @@ int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans,
	return ret;
}

int btrfs_csum_one_bio(struct btrfs_root *root,
		       struct bio *bio, struct btrfs_ordered_sum **sums_ret)
int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode,
		       struct bio *bio)
{
	struct btrfs_ordered_sum *sums;
	struct btrfs_sector_sum *sector_sum;
	struct btrfs_ordered_extent *ordered;
	char *data;
	struct bio_vec *bvec = bio->bi_io_vec;
	int bio_index = 0;
	unsigned long total_bytes = 0;
	unsigned long this_sum_bytes = 0;
	u64 offset;

	WARN_ON(bio->bi_vcnt <= 0);
	sums = kzalloc(btrfs_ordered_sum_size(root, bio->bi_size), GFP_NOFS);
	if (!sums)
		return -ENOMEM;
	*sums_ret = sums;

	sector_sum = &sums->sums;
	sums->file_offset = page_offset(bvec->bv_page);
	sums->file_offset = page_offset(bvec->bv_page) + bvec->bv_offset;
	sums->len = bio->bi_size;
	INIT_LIST_HEAD(&sums->list);
	ordered = btrfs_lookup_ordered_extent(inode, sums->file_offset);
	BUG_ON(!ordered);

	while(bio_index < bio->bi_vcnt) {
		offset = page_offset(bvec->bv_page) + bvec->bv_offset;
		if (offset >= ordered->file_offset + ordered->len) {
			unsigned long bytes_left;
			sums->len = this_sum_bytes;
			this_sum_bytes = 0;
			btrfs_add_ordered_sum(inode, ordered, sums);
			btrfs_put_ordered_extent(ordered);

			bytes_left = bio->bi_size - total_bytes;

			sums = kzalloc(btrfs_ordered_sum_size(root, bytes_left),
				       GFP_NOFS);
			BUG_ON(!sums);
			sector_sum = &sums->sums;
			sums->len = bytes_left;
			sums->file_offset = offset;
			ordered = btrfs_lookup_ordered_extent(inode,
						      sums->file_offset);
			BUG_ON(!ordered);
		}

		data = kmap_atomic(bvec->bv_page, KM_USER0);
		sector_sum->sum = ~(u32)0;
		sector_sum->sum = btrfs_csum_data(root,
@@ -165,10 +192,18 @@ int btrfs_csum_one_bio(struct btrfs_root *root,
				 (char *)&sector_sum->sum);
		sector_sum->offset = page_offset(bvec->bv_page) +
			bvec->bv_offset;

		sector_sum++;
		bio_index++;
		total_bytes += bvec->bv_len;
		this_sum_bytes += bvec->bv_len;
		bvec++;
	}
	btrfs_add_ordered_sum(inode, ordered, sums);
	btrfs_put_ordered_extent(ordered);
	if (total_bytes != bio->bi_size) {
printk("warning, total bytes %lu bio size %u\n", total_bytes, bio->bi_size);
	}
	return 0;
}

+1 −5
Original line number Diff line number Diff line
@@ -351,12 +351,8 @@ int __btrfs_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
{
	struct btrfs_root *root = BTRFS_I(inode)->root;
	int ret = 0;
	struct btrfs_ordered_sum *sums;

	ret = btrfs_csum_one_bio(root, bio, &sums);
	BUG_ON(ret);

	ret = btrfs_add_ordered_sum(inode, sums);
	ret = btrfs_csum_one_bio(root, inode, bio);
	BUG_ON(ret);

	return btrfs_map_bio(root, rw, bio, mirror_num, 1);
+18 −18
Original line number Diff line number Diff line
@@ -186,22 +186,17 @@ int btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,

/*
 * Add a struct btrfs_ordered_sum into the list of checksums to be inserted
 * when an ordered extent is finished.
 * when an ordered extent is finished.  If the list covers more than one
 * ordered extent, it is split across multiples.
 */
int btrfs_add_ordered_sum(struct inode *inode, struct btrfs_ordered_sum *sum)
int btrfs_add_ordered_sum(struct inode *inode,
			  struct btrfs_ordered_extent *entry,
			  struct btrfs_ordered_sum *sum)
{
	struct btrfs_ordered_inode_tree *tree;
	struct rb_node *node;
	struct btrfs_ordered_extent *entry;

	tree = &BTRFS_I(inode)->ordered_tree;
	mutex_lock(&tree->mutex);
	node = tree_search(tree, sum->file_offset);
	BUG_ON(!node);

	entry = rb_entry(node, struct btrfs_ordered_extent, rb_node);
	BUG_ON(!offset_in_entry(entry, sum->file_offset));

	list_add_tail(&sum->list, &entry->list);
	mutex_unlock(&tree->mutex);
	return 0;
@@ -524,8 +519,10 @@ int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u32 *sum)
	struct btrfs_ordered_extent *ordered;
	struct btrfs_ordered_inode_tree *tree = &BTRFS_I(inode)->ordered_tree;
	struct list_head *cur;
	unsigned long num_sectors;
	unsigned long i;
	u32 sectorsize = BTRFS_I(inode)->root->sectorsize;
	int ret = 1;
	int index;

	ordered = btrfs_lookup_ordered_extent(inode, offset);
	if (!ordered)
@@ -534,16 +531,19 @@ int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u32 *sum)
	mutex_lock(&tree->mutex);
	list_for_each_prev(cur, &ordered->list) {
		ordered_sum = list_entry(cur, struct btrfs_ordered_sum, list);
		if (offset >= ordered_sum->file_offset &&
		    offset < ordered_sum->file_offset + ordered_sum->len) {
			index = (offset - ordered_sum->file_offset) /
				BTRFS_I(inode)->root->sectorsize;;
		if (offset >= ordered_sum->file_offset) {
			num_sectors = ordered_sum->len / sectorsize;
			sector_sums = &ordered_sum->sums;
			*sum = sector_sums[index].sum;
			for (i = 0; i < num_sectors; i++) {
				if (sector_sums[i].offset == offset) {
printk("find ordered sum inode %lu offset %Lu\n", inode->i_ino, offset);
					*sum = sector_sums[i].sum;
					ret = 0;
					goto out;
				}
			}
		}
	}
out:
	mutex_unlock(&tree->mutex);
	return ret;
+9 −2
Original line number Diff line number Diff line
@@ -39,7 +39,11 @@ struct btrfs_sector_sum {

struct btrfs_ordered_sum {
	u64 file_offset;
	u64 len;
	/*
	 * this is the length in bytes covered by the sums array below.
	 * But, the sums array may not be contiguous in the file.
	 */
	unsigned long len;
	struct list_head list;
	/* last field is a variable length array of btrfs_sector_sums */
	struct btrfs_sector_sum sums;
@@ -95,6 +99,7 @@ static inline int btrfs_ordered_sum_size(struct btrfs_root *root, u64 bytes)
{
	unsigned long num_sectors = (bytes + root->sectorsize - 1) /
		root->sectorsize;
	num_sectors++;
	return sizeof(struct btrfs_ordered_sum) +
		num_sectors * sizeof(struct btrfs_sector_sum);
}
@@ -114,7 +119,9 @@ int btrfs_dec_test_ordered_pending(struct inode *inode,
				       u64 file_offset, u64 io_size);
int btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
			     u64 start, u64 len);
int btrfs_add_ordered_sum(struct inode *inode, struct btrfs_ordered_sum *sum);
int btrfs_add_ordered_sum(struct inode *inode,
			  struct btrfs_ordered_extent *entry,
			  struct btrfs_ordered_sum *sum);
struct btrfs_ordered_extent *btrfs_lookup_ordered_extent(struct inode *inode,
							 u64 file_offset);
void btrfs_start_ordered_extent(struct inode *inode,