diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index d9c69e16d368..45127e4797c8 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -161,7 +161,8 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode, while(bio_index < bio->bi_vcnt) { offset = page_offset(bvec->bv_page) + bvec->bv_offset; - if (offset >= ordered->file_offset + ordered->len) { + if (offset >= ordered->file_offset + ordered->len || + offset < ordered->file_offset) { unsigned long bytes_left; sums->len = this_sum_bytes; this_sum_bytes = 0; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 08dbe738b512..50ee4befac8e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -128,7 +128,9 @@ static int cow_file_range(struct inode *inode, u64 start, u64 end) goto out; BUG_ON(num_bytes > btrfs_super_total_bytes(&root->fs_info->super_copy)); + mutex_lock(&BTRFS_I(inode)->extent_mutex); btrfs_drop_extent_cache(inode, start, start + num_bytes - 1); + mutex_unlock(&BTRFS_I(inode)->extent_mutex); while(num_bytes > 0) { cur_alloc_size = min(num_bytes, root->fs_info->max_extent); @@ -144,6 +146,7 @@ static int cow_file_range(struct inode *inode, u64 start, u64 end) em->len = ins.offset; em->block_start = ins.objectid; em->bdev = root->fs_info->fs_devices->latest_bdev; + mutex_lock(&BTRFS_I(inode)->extent_mutex); set_bit(EXTENT_FLAG_PINNED, &em->flags); while(1) { spin_lock(&em_tree->lock); @@ -156,6 +159,7 @@ static int cow_file_range(struct inode *inode, u64 start, u64 end) btrfs_drop_extent_cache(inode, start, start + ins.offset - 1); } + mutex_unlock(&BTRFS_I(inode)->extent_mutex); cur_alloc_size = ins.offset; ret = btrfs_add_ordered_extent(inode, start, ins.objectid, @@ -487,6 +491,8 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end) struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree; struct extent_map *em; u64 alloc_hint = 0; + u64 clear_start; + u64 clear_end; struct list_head list; struct btrfs_key ins; int ret; @@ -509,12 +515,14 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end) ins.objectid = ordered_extent->start; ins.offset = ordered_extent->len; ins.type = BTRFS_EXTENT_ITEM_KEY; + ret = btrfs_alloc_reserved_extent(trans, root, root->root_key.objectid, trans->transid, inode->i_ino, ordered_extent->file_offset, &ins); BUG_ON(ret); mutex_lock(&BTRFS_I(inode)->extent_mutex); + ret = btrfs_drop_extents(trans, root, inode, ordered_extent->file_offset, ordered_extent->file_offset + @@ -528,13 +536,19 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end) ordered_extent->len, 0); BUG_ON(ret); - spin_lock(&em_tree->lock); - em = lookup_extent_mapping(em_tree, ordered_extent->file_offset, - ordered_extent->len); - if (em) { - clear_bit(EXTENT_FLAG_PINNED, &em->flags); - free_extent_map(em); + clear_start = ordered_extent->file_offset; + clear_end = ordered_extent->file_offset + ordered_extent->len; + while(clear_start < clear_end) { + em = lookup_extent_mapping(em_tree, clear_start, + clear_end - clear_start); + if (em) { + clear_bit(EXTENT_FLAG_PINNED, &em->flags); + clear_start = em->start + em->len; + free_extent_map(em); + } else { + break; + } } spin_unlock(&em_tree->lock); diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 1ddb7bceea99..c2b4a9c4ddb6 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -324,22 +324,37 @@ void btrfs_start_ordered_extent(struct inode *inode, void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len) { u64 end; + u64 orig_end; + u64 wait_end; struct btrfs_ordered_extent *ordered; - int found; - int should_wait = 0; + u64 mask = BTRFS_I(inode)->root->sectorsize - 1; + + if (start + len < start) { + wait_end = (inode->i_size + mask) & ~mask; + orig_end = (u64)-1; + } else { + orig_end = start + len - 1; + wait_end = orig_end; + } + + /* start IO across the range first to instantiate any delalloc + * extents + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) + do_sync_file_range(file, start, wait_end, SYNC_FILE_RANGE_WRITE); +#else + do_sync_mapping_range(inode->i_mapping, start, wait_end, + SYNC_FILE_RANGE_WRITE); +#endif + end = orig_end; + wait_on_extent_writeback(&BTRFS_I(inode)->io_tree, start, orig_end); -again: - if (start + len < start) - end = (u64)-1; - else - end = start + len - 1; - found = 0; while(1) { ordered = btrfs_lookup_first_ordered_extent(inode, end); if (!ordered) { break; } - if (ordered->file_offset >= start + len) { + if (ordered->file_offset > orig_end) { btrfs_put_ordered_extent(ordered); break; } @@ -347,21 +362,15 @@ void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len) btrfs_put_ordered_extent(ordered); break; } - btrfs_start_ordered_extent(inode, ordered, should_wait); - found++; + btrfs_start_ordered_extent(inode, ordered, 1); end = ordered->file_offset; btrfs_put_ordered_extent(ordered); - if (end == 0) + if (end == 0 || end == start) break; end--; } - if (should_wait && found) { - should_wait = 0; - goto again; - } } - /* * find an ordered extent corresponding to file_offset. return NULL if * nothing is found, otherwise take a reference on the extent and return it