aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/send.c
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@fusionio.com>2013-10-22 12:18:51 -0400
committerChris Mason <clm@fb.com>2014-01-28 16:19:21 -0500
commit16e7549f045d33b0c5b0ebf19d08439e9221d40c (patch)
treea7f3c3bbe5fa0cf6bf821f3a26d8969647dfd20d /fs/btrfs/send.c
parentd8ec26d7f8287f5788a494f56e8814210f0e64be (diff)
Btrfs: incompatible format change to remove hole extents
Btrfs has always had these filler extent data items for holes in inodes. This has made somethings very easy, like logging hole punches and sending hole punches. However for large holey files these extent data items are pure overhead. So add an incompatible feature to no longer add hole extents to reduce the amount of metadata used by these sort of files. This has a few changes for logging and send obviously since they will need to detect holes and log/send the holes if there are any. I've tested this thoroughly with xfstests and it doesn't cause any issues with and without the incompat format set. Thanks, Signed-off-by: Josef Bacik <jbacik@fusionio.com> Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs/btrfs/send.c')
-rw-r--r--fs/btrfs/send.c158
1 files changed, 149 insertions, 9 deletions
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 945d1db98f26..29803b4129fc 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -111,6 +111,7 @@ struct send_ctx {
111 int cur_inode_deleted; 111 int cur_inode_deleted;
112 u64 cur_inode_size; 112 u64 cur_inode_size;
113 u64 cur_inode_mode; 113 u64 cur_inode_mode;
114 u64 cur_inode_last_extent;
114 115
115 u64 send_progress; 116 u64 send_progress;
116 117
@@ -145,6 +146,13 @@ struct name_cache_entry {
145 char name[]; 146 char name[];
146}; 147};
147 148
149static int need_send_hole(struct send_ctx *sctx)
150{
151 return (sctx->parent_root && !sctx->cur_inode_new &&
152 !sctx->cur_inode_new_gen && !sctx->cur_inode_deleted &&
153 S_ISREG(sctx->cur_inode_mode));
154}
155
148static void fs_path_reset(struct fs_path *p) 156static void fs_path_reset(struct fs_path *p)
149{ 157{
150 if (p->reversed) { 158 if (p->reversed) {
@@ -3752,6 +3760,39 @@ out:
3752 return ret; 3760 return ret;
3753} 3761}
3754 3762
3763static int send_hole(struct send_ctx *sctx, u64 end)
3764{
3765 struct fs_path *p = NULL;
3766 u64 offset = sctx->cur_inode_last_extent;
3767 u64 len;
3768 int ret = 0;
3769
3770 p = fs_path_alloc();
3771 if (!p)
3772 return -ENOMEM;
3773 memset(sctx->read_buf, 0, BTRFS_SEND_READ_SIZE);
3774 while (offset < end) {
3775 len = min_t(u64, end - offset, BTRFS_SEND_READ_SIZE);
3776
3777 ret = begin_cmd(sctx, BTRFS_SEND_C_WRITE);
3778 if (ret < 0)
3779 break;
3780 ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen, p);
3781 if (ret < 0)
3782 break;
3783 TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p);
3784 TLV_PUT_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, offset);
3785 TLV_PUT(sctx, BTRFS_SEND_A_DATA, sctx->read_buf, len);
3786 ret = send_cmd(sctx);
3787 if (ret < 0)
3788 break;
3789 offset += len;
3790 }
3791tlv_put_failure:
3792 fs_path_free(p);
3793 return ret;
3794}
3795
3755static int send_write_or_clone(struct send_ctx *sctx, 3796static int send_write_or_clone(struct send_ctx *sctx,
3756 struct btrfs_path *path, 3797 struct btrfs_path *path,
3757 struct btrfs_key *key, 3798 struct btrfs_key *key,
@@ -3979,6 +4020,84 @@ out:
3979 return ret; 4020 return ret;
3980} 4021}
3981 4022
4023static int get_last_extent(struct send_ctx *sctx, u64 offset)
4024{
4025 struct btrfs_path *path;
4026 struct btrfs_root *root = sctx->send_root;
4027 struct btrfs_file_extent_item *fi;
4028 struct btrfs_key key;
4029 u64 extent_end;
4030 u8 type;
4031 int ret;
4032
4033 path = alloc_path_for_send();
4034 if (!path)
4035 return -ENOMEM;
4036
4037 sctx->cur_inode_last_extent = 0;
4038
4039 key.objectid = sctx->cur_ino;
4040 key.type = BTRFS_EXTENT_DATA_KEY;
4041 key.offset = offset;
4042 ret = btrfs_search_slot_for_read(root, &key, path, 0, 1);
4043 if (ret < 0)
4044 goto out;
4045 ret = 0;
4046 btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
4047 if (key.objectid != sctx->cur_ino || key.type != BTRFS_EXTENT_DATA_KEY)
4048 goto out;
4049
4050 fi = btrfs_item_ptr(path->nodes[0], path->slots[0],
4051 struct btrfs_file_extent_item);
4052 type = btrfs_file_extent_type(path->nodes[0], fi);
4053 if (type == BTRFS_FILE_EXTENT_INLINE) {
4054 u64 size = btrfs_file_extent_inline_len(path->nodes[0], fi);
4055 extent_end = ALIGN(key.offset + size,
4056 sctx->send_root->sectorsize);
4057 } else {
4058 extent_end = key.offset +
4059 btrfs_file_extent_num_bytes(path->nodes[0], fi);
4060 }
4061 sctx->cur_inode_last_extent = extent_end;
4062out:
4063 btrfs_free_path(path);
4064 return ret;
4065}
4066
4067static int maybe_send_hole(struct send_ctx *sctx, struct btrfs_path *path,
4068 struct btrfs_key *key)
4069{
4070 struct btrfs_file_extent_item *fi;
4071 u64 extent_end;
4072 u8 type;
4073 int ret = 0;
4074
4075 if (sctx->cur_ino != key->objectid || !need_send_hole(sctx))
4076 return 0;
4077
4078 if (sctx->cur_inode_last_extent == (u64)-1) {
4079 ret = get_last_extent(sctx, key->offset - 1);
4080 if (ret)
4081 return ret;
4082 }
4083
4084 fi = btrfs_item_ptr(path->nodes[0], path->slots[0],
4085 struct btrfs_file_extent_item);
4086 type = btrfs_file_extent_type(path->nodes[0], fi);
4087 if (type == BTRFS_FILE_EXTENT_INLINE) {
4088 u64 size = btrfs_file_extent_inline_len(path->nodes[0], fi);
4089 extent_end = ALIGN(key->offset + size,
4090 sctx->send_root->sectorsize);
4091 } else {
4092 extent_end = key->offset +
4093 btrfs_file_extent_num_bytes(path->nodes[0], fi);
4094 }
4095 if (sctx->cur_inode_last_extent < key->offset)
4096 ret = send_hole(sctx, key->offset);
4097 sctx->cur_inode_last_extent = extent_end;
4098 return ret;
4099}
4100
3982static int process_extent(struct send_ctx *sctx, 4101static int process_extent(struct send_ctx *sctx,
3983 struct btrfs_path *path, 4102 struct btrfs_path *path,
3984 struct btrfs_key *key) 4103 struct btrfs_key *key)
@@ -3995,7 +4114,7 @@ static int process_extent(struct send_ctx *sctx,
3995 goto out; 4114 goto out;
3996 if (ret) { 4115 if (ret) {
3997 ret = 0; 4116 ret = 0;
3998 goto out; 4117 goto out_hole;
3999 } 4118 }
4000 } else { 4119 } else {
4001 struct btrfs_file_extent_item *ei; 4120 struct btrfs_file_extent_item *ei;
@@ -4031,7 +4150,10 @@ static int process_extent(struct send_ctx *sctx,
4031 goto out; 4150 goto out;
4032 4151
4033 ret = send_write_or_clone(sctx, path, key, found_clone); 4152 ret = send_write_or_clone(sctx, path, key, found_clone);
4034 4153 if (ret)
4154 goto out;
4155out_hole:
4156 ret = maybe_send_hole(sctx, path, key);
4035out: 4157out:
4036 return ret; 4158 return ret;
4037} 4159}
@@ -4157,6 +4279,19 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
4157 } 4279 }
4158 4280
4159 if (S_ISREG(sctx->cur_inode_mode)) { 4281 if (S_ISREG(sctx->cur_inode_mode)) {
4282 if (need_send_hole(sctx)) {
4283 if (sctx->cur_inode_last_extent == (u64)-1) {
4284 ret = get_last_extent(sctx, (u64)-1);
4285 if (ret)
4286 goto out;
4287 }
4288 if (sctx->cur_inode_last_extent <
4289 sctx->cur_inode_size) {
4290 ret = send_hole(sctx, sctx->cur_inode_size);
4291 if (ret)
4292 goto out;
4293 }
4294 }
4160 ret = send_truncate(sctx, sctx->cur_ino, sctx->cur_inode_gen, 4295 ret = send_truncate(sctx, sctx->cur_ino, sctx->cur_inode_gen,
4161 sctx->cur_inode_size); 4296 sctx->cur_inode_size);
4162 if (ret < 0) 4297 if (ret < 0)
@@ -4200,6 +4335,7 @@ static int changed_inode(struct send_ctx *sctx,
4200 4335
4201 sctx->cur_ino = key->objectid; 4336 sctx->cur_ino = key->objectid;
4202 sctx->cur_inode_new_gen = 0; 4337 sctx->cur_inode_new_gen = 0;
4338 sctx->cur_inode_last_extent = (u64)-1;
4203 4339
4204 /* 4340 /*
4205 * Set send_progress to current inode. This will tell all get_cur_xxx 4341 * Set send_progress to current inode. This will tell all get_cur_xxx
@@ -4480,14 +4616,18 @@ static int changed_cb(struct btrfs_root *left_root,
4480 struct send_ctx *sctx = ctx; 4616 struct send_ctx *sctx = ctx;
4481 4617
4482 if (result == BTRFS_COMPARE_TREE_SAME) { 4618 if (result == BTRFS_COMPARE_TREE_SAME) {
4483 if (key->type != BTRFS_INODE_REF_KEY && 4619 if (key->type == BTRFS_INODE_REF_KEY ||
4484 key->type != BTRFS_INODE_EXTREF_KEY) 4620 key->type == BTRFS_INODE_EXTREF_KEY) {
4485 return 0; 4621 ret = compare_refs(sctx, left_path, key);
4486 ret = compare_refs(sctx, left_path, key); 4622 if (!ret)
4487 if (!ret) 4623 return 0;
4624 if (ret < 0)
4625 return ret;
4626 } else if (key->type == BTRFS_EXTENT_DATA_KEY) {
4627 return maybe_send_hole(sctx, left_path, key);
4628 } else {
4488 return 0; 4629 return 0;
4489 if (ret < 0) 4630 }
4490 return ret;
4491 result = BTRFS_COMPARE_TREE_CHANGED; 4631 result = BTRFS_COMPARE_TREE_CHANGED;
4492 ret = 0; 4632 ret = 0;
4493 } 4633 }