summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2008-07-18 14:17:13 +0400
committerChris Mason <chris.mason@oracle.com>2008-09-25 19:04:05 +0400
commit3edf7d33f4edb1e4a9bb0a4c0a84d95fb4d22a09 (patch)
tree8698a6dd1d0d038f1b56f79c92783518d36f2145
parenteb84ae039e10f1f80443d846ba1350122bbdc753 (diff)
downloadlinux-3edf7d33f4edb1e4a9bb0a4c0a84d95fb4d22a09.tar.xz
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: Chris Mason <chris.mason@oracle.com>
-rw-r--r--fs/btrfs/ctree.h4
-rw-r--r--fs/btrfs/file-item.c43
-rw-r--r--fs/btrfs/inode.c6
-rw-r--r--fs/btrfs/ordered-data.c36
-rw-r--r--fs/btrfs/ordered-data.h11
5 files changed, 69 insertions, 31 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index acbce542d291..96ab2797c09a 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -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,
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index 345caf8ff516..e02f1e5acb0a 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -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;
}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index f37e09e724f7..4d729d90d2b8 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -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);
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c
index 230fd3ca6b2c..1ddb7bceea99 100644
--- a/fs/btrfs/ordered-data.c
+++ b/fs/btrfs/ordered-data.c
@@ -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,14 +531,17 @@ 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;
- ret = 0;
- goto out;
+ 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:
diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h
index 98f491d1022b..1794efd13ca3 100644
--- a/fs/btrfs/ordered-data.h
+++ b/fs/btrfs/ordered-data.h
@@ -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,