Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Filipe Manana and committed by
Chris Mason
7ffbb598 cd857dd6

+155 -38
+6
fs/btrfs/ctree.h
··· 3749 3749 struct bio *bio, u64 file_start, int contig); 3750 3750 int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end, 3751 3751 struct list_head *list, int search_commit); 3752 + void 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 3758 /* inode.c */ 3753 3759 struct btrfs_delalloc_work { 3754 3760 struct inode *inode;
+76
fs/btrfs/file-item.c
··· 885 885 fail_unlock: 886 886 goto out; 887 887 } 888 + 889 + void 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 + }
+4 -38
fs/btrfs/inode.c
··· 6129 6129 { 6130 6130 int ret; 6131 6131 int err = 0; 6132 - u64 bytenr; 6133 6132 u64 extent_start = 0; 6134 6133 u64 extent_end = 0; 6135 6134 u64 objectid = btrfs_ino(inode); ··· 6142 6143 struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree; 6143 6144 struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; 6144 6145 struct btrfs_trans_handle *trans = NULL; 6145 - int compress_type; 6146 + const bool new_inline = !page || create; 6146 6147 6147 6148 again: 6148 6149 read_lock(&em_tree->lock); ··· 6216 6217 6217 6218 found_type = btrfs_file_extent_type(leaf, item); 6218 6219 extent_start = found_key.offset; 6219 - compress_type = btrfs_file_extent_compression(leaf, item); 6220 6220 if (found_type == BTRFS_FILE_EXTENT_REG || 6221 6221 found_type == BTRFS_FILE_EXTENT_PREALLOC) { 6222 6222 extent_end = extent_start + ··· 6250 6252 goto not_found_em; 6251 6253 } 6252 6254 6253 - em->ram_bytes = btrfs_file_extent_ram_bytes(leaf, item); 6255 + btrfs_extent_item_to_extent_map(inode, path, item, new_inline, em); 6256 + 6254 6257 if (found_type == BTRFS_FILE_EXTENT_REG || 6255 6258 found_type == BTRFS_FILE_EXTENT_PREALLOC) { 6256 - em->start = extent_start; 6257 - em->len = extent_end - extent_start; 6258 - em->orig_start = extent_start - 6259 - btrfs_file_extent_offset(leaf, item); 6260 - em->orig_block_len = btrfs_file_extent_disk_num_bytes(leaf, 6261 - item); 6262 - bytenr = btrfs_file_extent_disk_bytenr(leaf, item); 6263 - if (bytenr == 0) { 6264 - em->block_start = EXTENT_MAP_HOLE; 6265 - goto insert; 6266 - } 6267 - if (compress_type != BTRFS_COMPRESS_NONE) { 6268 - set_bit(EXTENT_FLAG_COMPRESSED, &em->flags); 6269 - em->compress_type = compress_type; 6270 - em->block_start = bytenr; 6271 - em->block_len = em->orig_block_len; 6272 - } else { 6273 - bytenr += btrfs_file_extent_offset(leaf, item); 6274 - em->block_start = bytenr; 6275 - em->block_len = em->len; 6276 - if (found_type == BTRFS_FILE_EXTENT_PREALLOC) 6277 - set_bit(EXTENT_FLAG_PREALLOC, &em->flags); 6278 - } 6279 6259 goto insert; 6280 6260 } else if (found_type == BTRFS_FILE_EXTENT_INLINE) { 6281 6261 unsigned long ptr; ··· 6262 6286 size_t extent_offset; 6263 6287 size_t copy_size; 6264 6288 6265 - em->block_start = EXTENT_MAP_INLINE; 6266 - if (!page || create) { 6267 - em->start = extent_start; 6268 - em->len = extent_end - extent_start; 6289 + if (new_inline) 6269 6290 goto out; 6270 - } 6271 6291 6272 6292 size = btrfs_file_extent_inline_len(leaf, path->slots[0], item); 6273 6293 extent_offset = page_offset(page) + pg_offset - extent_start; ··· 6273 6301 em->len = ALIGN(copy_size, root->sectorsize); 6274 6302 em->orig_block_len = em->len; 6275 6303 em->orig_start = em->start; 6276 - if (compress_type) { 6277 - set_bit(EXTENT_FLAG_COMPRESSED, &em->flags); 6278 - em->compress_type = compress_type; 6279 - } 6280 6304 ptr = btrfs_file_extent_inline_start(item) + extent_offset; 6281 6305 if (create == 0 && !PageUptodate(page)) { 6282 6306 if (btrfs_file_extent_compression(leaf, item) != ··· 6319 6351 set_extent_uptodate(io_tree, em->start, 6320 6352 extent_map_end(em) - 1, NULL, GFP_NOFS); 6321 6353 goto insert; 6322 - } else { 6323 - WARN(1, KERN_ERR "btrfs unknown found_type %d\n", found_type); 6324 6354 } 6325 6355 not_found: 6326 6356 em->start = start;
+69
fs/btrfs/ioctl.c
··· 3043 3043 return ret; 3044 3044 } 3045 3045 3046 + static 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 3101 * btrfs_clone() - clone a range from inode file to another 3048 3102 * ··· 3415 3361 btrfs_item_ptr_offset(leaf, slot), 3416 3362 size); 3417 3363 inode_add_bytes(inode, datal); 3364 + extent = btrfs_item_ptr(leaf, slot, 3365 + struct btrfs_file_extent_item); 3418 3366 } 3367 + 3368 + /* If we have an implicit hole (NO_HOLES feature). */ 3369 + if (drop_start < new_key.offset) 3370 + clone_update_extent_map(inode, trans, 3371 + path, NULL, drop_start, 3372 + new_key.offset - drop_start); 3373 + 3374 + clone_update_extent_map(inode, trans, path, 3375 + extent, 0, 0); 3419 3376 3420 3377 btrfs_mark_buffer_dirty(leaf); 3421 3378 btrfs_release_path(path); ··· 3471 3406 } 3472 3407 ret = clone_finish_inode_update(trans, inode, destoff + len, 3473 3408 destoff, olen); 3409 + if (ret) 3410 + goto out; 3411 + clone_update_extent_map(inode, trans, path, NULL, last_dest_end, 3412 + destoff + len - last_dest_end); 3474 3413 } 3475 3414 3476 3415 out: