aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@gmail.com>2014-06-08 22:48:05 -0400
committerChris Mason <clm@fb.com>2014-06-09 20:21:16 -0400
commit7ffbb598a059b73487909619d73150f99b50337a (patch)
treeba975f1810eb340b07143eea3a284861cad0ff4b /fs/btrfs
parentcd857dd6bc2ae9ecea14e75a34e8a8fdc158e307 (diff)
Btrfs: make fsync work after cloning into a file
When cloning into a file, we were correctly replacing the extent items in the target range and removing the extent maps. However we weren't replacing the extent maps with new ones that point to the new extents - as a consequence, an incremental fsync (when the inode doesn't have the full sync flag) was a NOOP, since it relies on the existence of extent maps in the modified list of the inode's extent map tree, which was empty. Therefore add new extent maps to reflect the target clone range. A test case for xfstests follows. Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com> Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/ctree.h6
-rw-r--r--fs/btrfs/file-item.c76
-rw-r--r--fs/btrfs/inode.c42
-rw-r--r--fs/btrfs/ioctl.c69
4 files changed, 155 insertions, 38 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index af523d695432..b7e2c1c1ef36 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -3749,6 +3749,12 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode,
3749 struct bio *bio, u64 file_start, int contig); 3749 struct bio *bio, u64 file_start, int contig);
3750int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end, 3750int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
3751 struct list_head *list, int search_commit); 3751 struct list_head *list, int search_commit);
3752void btrfs_extent_item_to_extent_map(struct inode *inode,
3753 const struct btrfs_path *path,
3754 struct btrfs_file_extent_item *fi,
3755 const bool new_inline,
3756 struct extent_map *em);
3757
3752/* inode.c */ 3758/* inode.c */
3753struct btrfs_delalloc_work { 3759struct btrfs_delalloc_work {
3754 struct inode *inode; 3760 struct inode *inode;
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index 609d56b9fd8e..f46cfe45d686 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -885,3 +885,79 @@ out:
885fail_unlock: 885fail_unlock:
886 goto out; 886 goto out;
887} 887}
888
889void btrfs_extent_item_to_extent_map(struct inode *inode,
890 const struct btrfs_path *path,
891 struct btrfs_file_extent_item *fi,
892 const bool new_inline,
893 struct extent_map *em)
894{
895 struct btrfs_root *root = BTRFS_I(inode)->root;
896 struct extent_buffer *leaf = path->nodes[0];
897 const int slot = path->slots[0];
898 struct btrfs_key key;
899 u64 extent_start, extent_end;
900 u64 bytenr;
901 u8 type = btrfs_file_extent_type(leaf, fi);
902 int compress_type = btrfs_file_extent_compression(leaf, fi);
903
904 em->bdev = root->fs_info->fs_devices->latest_bdev;
905 btrfs_item_key_to_cpu(leaf, &key, slot);
906 extent_start = key.offset;
907
908 if (type == BTRFS_FILE_EXTENT_REG ||
909 type == BTRFS_FILE_EXTENT_PREALLOC) {
910 extent_end = extent_start +
911 btrfs_file_extent_num_bytes(leaf, fi);
912 } else if (type == BTRFS_FILE_EXTENT_INLINE) {
913 size_t size;
914 size = btrfs_file_extent_inline_len(leaf, slot, fi);
915 extent_end = ALIGN(extent_start + size, root->sectorsize);
916 }
917
918 em->ram_bytes = btrfs_file_extent_ram_bytes(leaf, fi);
919 if (type == BTRFS_FILE_EXTENT_REG ||
920 type == BTRFS_FILE_EXTENT_PREALLOC) {
921 em->start = extent_start;
922 em->len = extent_end - extent_start;
923 em->orig_start = extent_start -
924 btrfs_file_extent_offset(leaf, fi);
925 em->orig_block_len = btrfs_file_extent_disk_num_bytes(leaf, fi);
926 bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
927 if (bytenr == 0) {
928 em->block_start = EXTENT_MAP_HOLE;
929 return;
930 }
931 if (compress_type != BTRFS_COMPRESS_NONE) {
932 set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
933 em->compress_type = compress_type;
934 em->block_start = bytenr;
935 em->block_len = em->orig_block_len;
936 } else {
937 bytenr += btrfs_file_extent_offset(leaf, fi);
938 em->block_start = bytenr;
939 em->block_len = em->len;
940 if (type == BTRFS_FILE_EXTENT_PREALLOC)
941 set_bit(EXTENT_FLAG_PREALLOC, &em->flags);
942 }
943 } else if (type == BTRFS_FILE_EXTENT_INLINE) {
944 em->block_start = EXTENT_MAP_INLINE;
945 em->start = extent_start;
946 em->len = extent_end - extent_start;
947 /*
948 * Initialize orig_start and block_len with the same values
949 * as in inode.c:btrfs_get_extent().
950 */
951 em->orig_start = EXTENT_MAP_HOLE;
952 em->block_len = (u64)-1;
953 if (!new_inline && compress_type != BTRFS_COMPRESS_NONE) {
954 set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
955 em->compress_type = compress_type;
956 }
957 } else {
958 btrfs_err(root->fs_info,
959 "unknown file extent item type %d, inode %llu, offset %llu, root %llu",
960 type, btrfs_ino(inode), extent_start,
961 root->root_key.objectid);
962 }
963}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 477e64a1146f..372b2cb2b297 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -6129,7 +6129,6 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
6129{ 6129{
6130 int ret; 6130 int ret;
6131 int err = 0; 6131 int err = 0;
6132 u64 bytenr;
6133 u64 extent_start = 0; 6132 u64 extent_start = 0;
6134 u64 extent_end = 0; 6133 u64 extent_end = 0;
6135 u64 objectid = btrfs_ino(inode); 6134 u64 objectid = btrfs_ino(inode);
@@ -6143,7 +6142,7 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
6143 struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree; 6142 struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
6144 struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; 6143 struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
6145 struct btrfs_trans_handle *trans = NULL; 6144 struct btrfs_trans_handle *trans = NULL;
6146 int compress_type; 6145 const bool new_inline = !page || create;
6147 6146
6148again: 6147again:
6149 read_lock(&em_tree->lock); 6148 read_lock(&em_tree->lock);
@@ -6217,7 +6216,6 @@ again:
6217 6216
6218 found_type = btrfs_file_extent_type(leaf, item); 6217 found_type = btrfs_file_extent_type(leaf, item);
6219 extent_start = found_key.offset; 6218 extent_start = found_key.offset;
6220 compress_type = btrfs_file_extent_compression(leaf, item);
6221 if (found_type == BTRFS_FILE_EXTENT_REG || 6219 if (found_type == BTRFS_FILE_EXTENT_REG ||
6222 found_type == BTRFS_FILE_EXTENT_PREALLOC) { 6220 found_type == BTRFS_FILE_EXTENT_PREALLOC) {
6223 extent_end = extent_start + 6221 extent_end = extent_start +
@@ -6252,32 +6250,10 @@ next:
6252 goto not_found_em; 6250 goto not_found_em;
6253 } 6251 }
6254 6252
6255 em->ram_bytes = btrfs_file_extent_ram_bytes(leaf, item); 6253 btrfs_extent_item_to_extent_map(inode, path, item, new_inline, em);
6254
6256 if (found_type == BTRFS_FILE_EXTENT_REG || 6255 if (found_type == BTRFS_FILE_EXTENT_REG ||
6257 found_type == BTRFS_FILE_EXTENT_PREALLOC) { 6256 found_type == BTRFS_FILE_EXTENT_PREALLOC) {
6258 em->start = extent_start;
6259 em->len = extent_end - extent_start;
6260 em->orig_start = extent_start -
6261 btrfs_file_extent_offset(leaf, item);
6262 em->orig_block_len = btrfs_file_extent_disk_num_bytes(leaf,
6263 item);
6264 bytenr = btrfs_file_extent_disk_bytenr(leaf, item);
6265 if (bytenr == 0) {
6266 em->block_start = EXTENT_MAP_HOLE;
6267 goto insert;
6268 }
6269 if (compress_type != BTRFS_COMPRESS_NONE) {
6270 set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
6271 em->compress_type = compress_type;
6272 em->block_start = bytenr;
6273 em->block_len = em->orig_block_len;
6274 } else {
6275 bytenr += btrfs_file_extent_offset(leaf, item);
6276 em->block_start = bytenr;
6277 em->block_len = em->len;
6278 if (found_type == BTRFS_FILE_EXTENT_PREALLOC)
6279 set_bit(EXTENT_FLAG_PREALLOC, &em->flags);
6280 }
6281 goto insert; 6257 goto insert;
6282 } else if (found_type == BTRFS_FILE_EXTENT_INLINE) { 6258 } else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
6283 unsigned long ptr; 6259 unsigned long ptr;
@@ -6286,12 +6262,8 @@ next:
6286 size_t extent_offset; 6262 size_t extent_offset;
6287 size_t copy_size; 6263 size_t copy_size;
6288 6264
6289 em->block_start = EXTENT_MAP_INLINE; 6265 if (new_inline)
6290 if (!page || create) {
6291 em->start = extent_start;
6292 em->len = extent_end - extent_start;
6293 goto out; 6266 goto out;
6294 }
6295 6267
6296 size = btrfs_file_extent_inline_len(leaf, path->slots[0], item); 6268 size = btrfs_file_extent_inline_len(leaf, path->slots[0], item);
6297 extent_offset = page_offset(page) + pg_offset - extent_start; 6269 extent_offset = page_offset(page) + pg_offset - extent_start;
@@ -6301,10 +6273,6 @@ next:
6301 em->len = ALIGN(copy_size, root->sectorsize); 6273 em->len = ALIGN(copy_size, root->sectorsize);
6302 em->orig_block_len = em->len; 6274 em->orig_block_len = em->len;
6303 em->orig_start = em->start; 6275 em->orig_start = em->start;
6304 if (compress_type) {
6305 set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
6306 em->compress_type = compress_type;
6307 }
6308 ptr = btrfs_file_extent_inline_start(item) + extent_offset; 6276 ptr = btrfs_file_extent_inline_start(item) + extent_offset;
6309 if (create == 0 && !PageUptodate(page)) { 6277 if (create == 0 && !PageUptodate(page)) {
6310 if (btrfs_file_extent_compression(leaf, item) != 6278 if (btrfs_file_extent_compression(leaf, item) !=
@@ -6351,8 +6319,6 @@ next:
6351 set_extent_uptodate(io_tree, em->start, 6319 set_extent_uptodate(io_tree, em->start,
6352 extent_map_end(em) - 1, NULL, GFP_NOFS); 6320 extent_map_end(em) - 1, NULL, GFP_NOFS);
6353 goto insert; 6321 goto insert;
6354 } else {
6355 WARN(1, KERN_ERR "btrfs unknown found_type %d\n", found_type);
6356 } 6322 }
6357not_found: 6323not_found:
6358 em->start = start; 6324 em->start = start;
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index dd9a02a53c7b..c2e796b664c1 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3043,6 +3043,60 @@ out:
3043 return ret; 3043 return ret;
3044} 3044}
3045 3045
3046static void clone_update_extent_map(struct inode *inode,
3047 const struct btrfs_trans_handle *trans,
3048 const struct btrfs_path *path,
3049 struct btrfs_file_extent_item *fi,
3050 const u64 hole_offset,
3051 const u64 hole_len)
3052{
3053 struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
3054 struct extent_map *em;
3055 int ret;
3056
3057 em = alloc_extent_map();
3058 if (!em) {
3059 set_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
3060 &BTRFS_I(inode)->runtime_flags);
3061 return;
3062 }
3063
3064 if (fi) {
3065 btrfs_extent_item_to_extent_map(inode, path, fi, false, em);
3066 em->generation = -1;
3067 if (btrfs_file_extent_type(path->nodes[0], fi) ==
3068 BTRFS_FILE_EXTENT_INLINE)
3069 set_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
3070 &BTRFS_I(inode)->runtime_flags);
3071 } else {
3072 em->start = hole_offset;
3073 em->len = hole_len;
3074 em->ram_bytes = em->len;
3075 em->orig_start = hole_offset;
3076 em->block_start = EXTENT_MAP_HOLE;
3077 em->block_len = 0;
3078 em->orig_block_len = 0;
3079 em->compress_type = BTRFS_COMPRESS_NONE;
3080 em->generation = trans->transid;
3081 }
3082
3083 while (1) {
3084 write_lock(&em_tree->lock);
3085 ret = add_extent_mapping(em_tree, em, 1);
3086 write_unlock(&em_tree->lock);
3087 if (ret != -EEXIST) {
3088 free_extent_map(em);
3089 break;
3090 }
3091 btrfs_drop_extent_cache(inode, em->start,
3092 em->start + em->len - 1, 0);
3093 }
3094
3095 if (unlikely(ret))
3096 set_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
3097 &BTRFS_I(inode)->runtime_flags);
3098}
3099
3046/** 3100/**
3047 * btrfs_clone() - clone a range from inode file to another 3101 * btrfs_clone() - clone a range from inode file to another
3048 * 3102 *
@@ -3361,8 +3415,19 @@ process_slot:
3361 btrfs_item_ptr_offset(leaf, slot), 3415 btrfs_item_ptr_offset(leaf, slot),
3362 size); 3416 size);
3363 inode_add_bytes(inode, datal); 3417 inode_add_bytes(inode, datal);
3418 extent = btrfs_item_ptr(leaf, slot,
3419 struct btrfs_file_extent_item);
3364 } 3420 }
3365 3421
3422 /* If we have an implicit hole (NO_HOLES feature). */
3423 if (drop_start < new_key.offset)
3424 clone_update_extent_map(inode, trans,
3425 path, NULL, drop_start,
3426 new_key.offset - drop_start);
3427
3428 clone_update_extent_map(inode, trans, path,
3429 extent, 0, 0);
3430
3366 btrfs_mark_buffer_dirty(leaf); 3431 btrfs_mark_buffer_dirty(leaf);
3367 btrfs_release_path(path); 3432 btrfs_release_path(path);
3368 3433
@@ -3406,6 +3471,10 @@ process_slot:
3406 } 3471 }
3407 ret = clone_finish_inode_update(trans, inode, destoff + len, 3472 ret = clone_finish_inode_update(trans, inode, destoff + len,
3408 destoff, olen); 3473 destoff, olen);
3474 if (ret)
3475 goto out;
3476 clone_update_extent_map(inode, trans, path, NULL, last_dest_end,
3477 destoff + len - last_dest_end);
3409 } 3478 }
3410 3479
3411out: 3480out: