From 0c67dd51efcf68721cb3f19cadffd40ee8b4c60b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 7 Nov 2016 17:56:52 -0800 Subject: [PATCH] Forget freed btree blocks When the btree stops referencing a block and frees it we can also forget it so that it isn't uselessly written to disk. Callers who forget are careful to only unlock and release the block ref after freeing it. They won't be confused if something allocates the block and starts using it. Signed-off-by: Zach Brown Reviewed-by: Mark Fasheh --- kmod/src/btree.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/kmod/src/btree.c b/kmod/src/btree.c index 829dca63..2e0427b1 100644 --- a/kmod/src/btree.c +++ b/kmod/src/btree.c @@ -530,13 +530,19 @@ static struct scoutfs_block *alloc_tree_block(struct super_block *sb) } /* the caller has ensured that the free must succeed */ -static void free_tree_block(struct super_block *sb, __le64 blkno) +static void free_tree_block(struct super_block *sb, struct scoutfs_block *bl) { struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb); + struct scoutfs_super_block *super = &sbi->super; + struct scoutfs_btree_block *bt = scoutfs_block_data(bl); + int err; - int err = scoutfs_buddy_free(sb, sbi->super.hdr.seq, - le64_to_cpu(blkno), 0); - WARN_ON_ONCE(err); + BUG_ON(bt->hdr.seq != super->hdr.seq); + + scoutfs_block_forget(bl); + err = scoutfs_buddy_free(sb, bt->hdr.seq, + le64_to_cpu(bt->hdr.blkno), 0); + BUG_ON(err); } /* @@ -657,7 +663,7 @@ static struct scoutfs_block *try_split(struct super_block *sb, if (!parent) { par_bl = grow_tree(sb, root); if (IS_ERR(par_bl)) { - free_tree_block(sb, left->hdr.blkno); + free_tree_block(sb, left_bl); scoutfs_block_put(left_bl); unlock_tree_block(sb, root, right_bl, true); scoutfs_block_put(right_bl); @@ -717,10 +723,11 @@ static struct scoutfs_block *try_split(struct super_block *sb, */ static struct scoutfs_block *try_merge(struct super_block *sb, struct scoutfs_btree_root *root, - struct scoutfs_btree_block *parent, + struct scoutfs_block *par_bl, int level, unsigned int pos, struct scoutfs_block *bl) { + struct scoutfs_btree_block *parent = scoutfs_block_data(par_bl); struct scoutfs_btree_block *bt = scoutfs_block_data(bl); struct scoutfs_btree_item *sib_item; struct scoutfs_btree_block *sib_bt; @@ -791,7 +798,7 @@ static struct scoutfs_block *try_merge(struct super_block *sb, /* delete an empty sib or update if we changed its greatest key */ if (le16_to_cpu(sib_bt->nr_items) == 0) { delete_item(parent, sib_pos); - free_tree_block(sb, sib_bt->hdr.blkno); + free_tree_block(sb, sib_bl); } else if (move_right) { sib_item->key = *greatest_key(sib_bt); } @@ -801,7 +808,8 @@ static struct scoutfs_block *try_merge(struct super_block *sb, root->height--; root->ref.blkno = bt->hdr.blkno; root->ref.seq = bt->hdr.seq; - free_tree_block(sb, parent->hdr.blkno); + free_tree_block(sb, par_bl); + /* caller just unlocks and drops parent */ } unlock_tree_block(sb, root, sib_bl, true); @@ -1049,7 +1057,7 @@ static struct scoutfs_block *btree_walk(struct super_block *sb, bl = try_split(sb, root, level, key, val_len, parent, pos, bl); if ((op == WALK_DELETE) && parent) - bl = try_merge(sb, root, parent, level, pos, bl); + bl = try_merge(sb, root, par_bl, level, pos, bl); if (IS_ERR(bl)) break; @@ -1239,7 +1247,7 @@ int scoutfs_btree_delete(struct super_block *sb, root->ref.blkno = 0; root->ref.seq = 0; - free_tree_block(sb, bt->hdr.blkno); + free_tree_block(sb, bl); } } else { ret = -ENOENT;