Skip to content

Commit 16e7549

Browse files
Josef Bacikmasoncl
authored andcommitted
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 <[email protected]> Signed-off-by: Chris Mason <[email protected]>
1 parent d8ec26d commit 16e7549

File tree

6 files changed

+373
-56
lines changed

6 files changed

+373
-56
lines changed

fs/btrfs/ctree.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ static void del_ptr(struct btrfs_root *root, struct btrfs_path *path,
4141
int level, int slot);
4242
static void tree_mod_log_free_eb(struct btrfs_fs_info *fs_info,
4343
struct extent_buffer *eb);
44-
static int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path);
4544

4645
struct btrfs_path *btrfs_alloc_path(void)
4746
{
@@ -4817,7 +4816,7 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
48174816
* This may release the path, and so you may lose any locks held at the
48184817
* time you call it.
48194818
*/
4820-
static int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path)
4819+
int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path)
48214820
{
48224821
struct btrfs_key key;
48234822
struct btrfs_disk_key found_key;

fs/btrfs/ctree.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@ struct btrfs_super_block {
521521
#define BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF (1ULL << 6)
522522
#define BTRFS_FEATURE_INCOMPAT_RAID56 (1ULL << 7)
523523
#define BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA (1ULL << 8)
524+
#define BTRFS_FEATURE_INCOMPAT_NO_HOLES (1ULL << 9)
524525

525526
#define BTRFS_FEATURE_COMPAT_SUPP 0ULL
526527
#define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL
@@ -532,7 +533,8 @@ struct btrfs_super_block {
532533
BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO | \
533534
BTRFS_FEATURE_INCOMPAT_RAID56 | \
534535
BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF | \
535-
BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)
536+
BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA | \
537+
BTRFS_FEATURE_INCOMPAT_NO_HOLES)
536538

537539
/*
538540
* A leaf is full of items. offset and size tell us where to find
@@ -3399,6 +3401,7 @@ static inline int btrfs_insert_empty_item(struct btrfs_trans_handle *trans,
33993401
}
34003402

34013403
int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path);
3404+
int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path);
34023405
int btrfs_next_old_leaf(struct btrfs_root *root, struct btrfs_path *path,
34033406
u64 time_seq);
34043407
static inline int btrfs_next_old_item(struct btrfs_root *root,

fs/btrfs/file.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1963,11 +1963,13 @@ static int fill_holes(struct btrfs_trans_handle *trans, struct inode *inode,
19631963
struct btrfs_key key;
19641964
int ret;
19651965

1966+
if (btrfs_fs_incompat(root->fs_info, NO_HOLES))
1967+
goto out;
1968+
19661969
key.objectid = btrfs_ino(inode);
19671970
key.type = BTRFS_EXTENT_DATA_KEY;
19681971
key.offset = offset;
19691972

1970-
19711973
ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
19721974
if (ret < 0)
19731975
return ret;
@@ -2064,8 +2066,10 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
20642066
u64 drop_end;
20652067
int ret = 0;
20662068
int err = 0;
2069+
int rsv_count;
20672070
bool same_page = ((offset >> PAGE_CACHE_SHIFT) ==
20682071
((offset + len - 1) >> PAGE_CACHE_SHIFT));
2072+
bool no_holes = btrfs_fs_incompat(root->fs_info, NO_HOLES);
20692073

20702074
ret = btrfs_wait_ordered_range(inode, offset, len);
20712075
if (ret)
@@ -2163,9 +2167,10 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
21632167
/*
21642168
* 1 - update the inode
21652169
* 1 - removing the extents in the range
2166-
* 1 - adding the hole extent
2170+
* 1 - adding the hole extent if no_holes isn't set
21672171
*/
2168-
trans = btrfs_start_transaction(root, 3);
2172+
rsv_count = no_holes ? 2 : 3;
2173+
trans = btrfs_start_transaction(root, rsv_count);
21692174
if (IS_ERR(trans)) {
21702175
err = PTR_ERR(trans);
21712176
goto out_free;
@@ -2202,7 +2207,7 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
22022207
btrfs_end_transaction(trans, root);
22032208
btrfs_btree_balance_dirty(root);
22042209

2205-
trans = btrfs_start_transaction(root, 3);
2210+
trans = btrfs_start_transaction(root, rsv_count);
22062211
if (IS_ERR(trans)) {
22072212
ret = PTR_ERR(trans);
22082213
trans = NULL;

fs/btrfs/inode.c

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4203,6 +4203,49 @@ int btrfs_truncate_page(struct inode *inode, loff_t from, loff_t len,
42034203
return ret;
42044204
}
42054205

4206+
static int maybe_insert_hole(struct btrfs_root *root, struct inode *inode,
4207+
u64 offset, u64 len)
4208+
{
4209+
struct btrfs_trans_handle *trans;
4210+
int ret;
4211+
4212+
/*
4213+
* Still need to make sure the inode looks like it's been updated so
4214+
* that any holes get logged if we fsync.
4215+
*/
4216+
if (btrfs_fs_incompat(root->fs_info, NO_HOLES)) {
4217+
BTRFS_I(inode)->last_trans = root->fs_info->generation;
4218+
BTRFS_I(inode)->last_sub_trans = root->log_transid;
4219+
BTRFS_I(inode)->last_log_commit = root->last_log_commit;
4220+
return 0;
4221+
}
4222+
4223+
/*
4224+
* 1 - for the one we're dropping
4225+
* 1 - for the one we're adding
4226+
* 1 - for updating the inode.
4227+
*/
4228+
trans = btrfs_start_transaction(root, 3);
4229+
if (IS_ERR(trans))
4230+
return PTR_ERR(trans);
4231+
4232+
ret = btrfs_drop_extents(trans, root, inode, offset, offset + len, 1);
4233+
if (ret) {
4234+
btrfs_abort_transaction(trans, root, ret);
4235+
btrfs_end_transaction(trans, root);
4236+
return ret;
4237+
}
4238+
4239+
ret = btrfs_insert_file_extent(trans, root, btrfs_ino(inode), offset,
4240+
0, 0, len, 0, len, 0, 0, 0);
4241+
if (ret)
4242+
btrfs_abort_transaction(trans, root, ret);
4243+
else
4244+
btrfs_update_inode(trans, root, inode);
4245+
btrfs_end_transaction(trans, root);
4246+
return ret;
4247+
}
4248+
42064249
/*
42074250
* This function puts in dummy file extents for the area we're creating a hole
42084251
* for. So if we are truncating this file to a larger size we need to insert
@@ -4211,7 +4254,6 @@ int btrfs_truncate_page(struct inode *inode, loff_t from, loff_t len,
42114254
*/
42124255
int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
42134256
{
4214-
struct btrfs_trans_handle *trans;
42154257
struct btrfs_root *root = BTRFS_I(inode)->root;
42164258
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
42174259
struct extent_map *em = NULL;
@@ -4266,31 +4308,10 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
42664308
struct extent_map *hole_em;
42674309
hole_size = last_byte - cur_offset;
42684310

4269-
trans = btrfs_start_transaction(root, 3);
4270-
if (IS_ERR(trans)) {
4271-
err = PTR_ERR(trans);
4272-
break;
4273-
}
4274-
4275-
err = btrfs_drop_extents(trans, root, inode,
4276-
cur_offset,
4277-
cur_offset + hole_size, 1);
4278-
if (err) {
4279-
btrfs_abort_transaction(trans, root, err);
4280-
btrfs_end_transaction(trans, root);
4281-
break;
4282-
}
4283-
4284-
err = btrfs_insert_file_extent(trans, root,
4285-
btrfs_ino(inode), cur_offset, 0,
4286-
0, hole_size, 0, hole_size,
4287-
0, 0, 0);
4288-
if (err) {
4289-
btrfs_abort_transaction(trans, root, err);
4290-
btrfs_end_transaction(trans, root);
4311+
err = maybe_insert_hole(root, inode, cur_offset,
4312+
hole_size);
4313+
if (err)
42914314
break;
4292-
}
4293-
42944315
btrfs_drop_extent_cache(inode, cur_offset,
42954316
cur_offset + hole_size - 1, 0);
42964317
hole_em = alloc_extent_map();
@@ -4309,7 +4330,7 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
43094330
hole_em->ram_bytes = hole_size;
43104331
hole_em->bdev = root->fs_info->fs_devices->latest_bdev;
43114332
hole_em->compress_type = BTRFS_COMPRESS_NONE;
4312-
hole_em->generation = trans->transid;
4333+
hole_em->generation = root->fs_info->generation;
43134334

43144335
while (1) {
43154336
write_lock(&em_tree->lock);
@@ -4322,17 +4343,14 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
43224343
hole_size - 1, 0);
43234344
}
43244345
free_extent_map(hole_em);
4325-
next:
4326-
btrfs_update_inode(trans, root, inode);
4327-
btrfs_end_transaction(trans, root);
43284346
}
4347+
next:
43294348
free_extent_map(em);
43304349
em = NULL;
43314350
cur_offset = last_byte;
43324351
if (cur_offset >= block_end)
43334352
break;
43344353
}
4335-
43364354
free_extent_map(em);
43374355
unlock_extent_cached(io_tree, hole_start, block_end - 1, &cached_state,
43384356
GFP_NOFS);

0 commit comments

Comments
 (0)