Btrfs: implement memory reclaim for leaf reference cache
The memory reclaiming issue happens when snapshot exists. In that
case, some cache entries may not be used during old snapshot dropping,
so they will remain in the cache until umount.
The patch adds a field to struct btrfs_leaf_ref to record create time. Besides,
the patch makes all dead roots of a given snapshot linked together in order of
create time. After a old snapshot was completely dropped, we check the dead
root list and remove all cache entries created before the oldest dead root in
the list.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 216f315..52c5524 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -98,20 +98,24 @@
BUG_ON(!dirty);
dirty->root = kmalloc(sizeof(*dirty->root), GFP_NOFS);
BUG_ON(!dirty->root);
-
dirty->latest_root = root;
INIT_LIST_HEAD(&dirty->list);
root->commit_root = btrfs_root_node(root);
- root->dirty_root = dirty;
memcpy(dirty->root, root, sizeof(*root));
- dirty->root->ref_tree = &root->ref_tree_struct;
-
spin_lock_init(&dirty->root->node_lock);
+ spin_lock_init(&dirty->root->list_lock);
mutex_init(&dirty->root->objectid_mutex);
+ INIT_LIST_HEAD(&dirty->root->dead_list);
dirty->root->node = root->commit_root;
dirty->root->commit_root = NULL;
+
+ spin_lock(&root->list_lock);
+ list_add(&dirty->root->dead_list, &root->dead_list);
+ spin_unlock(&root->list_lock);
+
+ root->dirty_root = dirty;
} else {
WARN_ON(1);
}
@@ -356,8 +360,6 @@
list_del_init(next);
root = list_entry(next, struct btrfs_root, dirty_list);
update_cowonly_root(trans, root);
- if (root->fs_info->closing)
- btrfs_remove_leaf_refs(root);
}
return 0;
}
@@ -410,7 +412,11 @@
free_extent_buffer(root->commit_root);
root->commit_root = NULL;
-
+
+ spin_lock(&root->list_lock);
+ list_del_init(&dirty->root->dead_list);
+ spin_unlock(&root->list_lock);
+
kfree(dirty->root);
kfree(dirty);
@@ -497,6 +503,7 @@
unsigned long nr;
u64 num_bytes;
u64 bytes_used;
+ u64 max_useless;
int ret = 0;
int err;
@@ -554,10 +561,25 @@
}
mutex_unlock(&root->fs_info->drop_mutex);
+ spin_lock(&root->list_lock);
+ list_del_init(&dirty->root->dead_list);
+ if (!list_empty(&root->dead_list)) {
+ struct btrfs_root *oldest;
+ oldest = list_entry(root->dead_list.prev,
+ struct btrfs_root, dead_list);
+ max_useless = oldest->root_key.offset - 1;
+ } else {
+ max_useless = root->root_key.offset - 1;
+ }
+ spin_unlock(&root->list_lock);
+
nr = trans->blocks_used;
ret = btrfs_end_transaction(trans, tree_root);
BUG_ON(ret);
+ ret = btrfs_remove_leaf_refs(root, max_useless);
+ BUG_ON(ret);
+
free_extent_buffer(dirty->root->node);
kfree(dirty->root);
kfree(dirty);
@@ -785,10 +807,9 @@
put_transaction(cur_trans);
put_transaction(cur_trans);
+ list_splice_init(&dirty_fs_roots, &root->fs_info->dead_roots);
if (root->fs_info->closing)
list_splice_init(&root->fs_info->dead_roots, &dirty_fs_roots);
- else
- list_splice_init(&dirty_fs_roots, &root->fs_info->dead_roots);
mutex_unlock(&root->fs_info->trans_mutex);
kmem_cache_free(btrfs_trans_handle_cachep, trans);
@@ -814,4 +835,3 @@
}
return 0;
}
-