Skip to content

Commit

Permalink
Btrfs: implement memory reclaim for leaf reference cache
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Yan authored and Chris Mason committed Sep 25, 2008
1 parent 33958dc commit bcc63ab
Show file tree
Hide file tree
Showing 15 changed files with 86 additions and 69 deletions.
1 change: 0 additions & 1 deletion fs/btrfs/ctree.c
Original file line number Diff line number Diff line change
Expand Up @@ -3275,4 +3275,3 @@ int btrfs_previous_item(struct btrfs_root *root,
}
return 1;
}

3 changes: 2 additions & 1 deletion fs/btrfs/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,8 @@ struct btrfs_root {
/* the dirty list is only used by non-reference counted roots */
struct list_head dirty_list;

spinlock_t orphan_lock;
spinlock_t list_lock;
struct list_head dead_list;
struct list_head orphan_list;
};

Expand Down
1 change: 0 additions & 1 deletion fs/btrfs/dir-item.c
Original file line number Diff line number Diff line change
Expand Up @@ -340,4 +340,3 @@ int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
}
return 0;
}

5 changes: 3 additions & 2 deletions fs/btrfs/disk-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -735,8 +735,9 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,

INIT_LIST_HEAD(&root->dirty_list);
INIT_LIST_HEAD(&root->orphan_list);
INIT_LIST_HEAD(&root->dead_list);
spin_lock_init(&root->node_lock);
spin_lock_init(&root->orphan_lock);
spin_lock_init(&root->list_lock);
mutex_init(&root->objectid_mutex);

btrfs_leaf_ref_tree_init(&root->ref_tree_struct);
Expand Down Expand Up @@ -1717,7 +1718,7 @@ int close_ctree(struct btrfs_root *root)
printk("btrfs: at umount reference cache size %Lu\n",
fs_info->total_ref_cache_size);
}

if (fs_info->extent_root->node)
free_extent_buffer(fs_info->extent_root->node);

Expand Down
18 changes: 9 additions & 9 deletions fs/btrfs/extent-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -867,8 +867,8 @@ static int get_reference_status(struct btrfs_root *root, u64 bytenr,
/*
* For (parent_gen > 0 && parent_gen > ref_gen):
*
* we reach here through the oldest root, therefore
* all other reference from same snapshot should have
* we reach here through the oldest root, therefore
* all other reference from same snapshot should have
* a larger generation.
*/
if ((root_objectid != btrfs_ref_root(leaf, ref_item)) ||
Expand Down Expand Up @@ -954,7 +954,7 @@ int btrfs_cross_ref_exists(struct btrfs_root *root,
if (!eb)
continue;
extent_start = eb->start;
} else
} else
extent_start = bytenr;

ret = get_reference_status(root, extent_start, ref_generation,
Expand Down Expand Up @@ -1048,7 +1048,7 @@ int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct btrfs_leaf_ref *ref;
struct btrfs_extent_info *info;

ref = btrfs_alloc_leaf_ref(nr_file_extents);
ref = btrfs_alloc_leaf_ref(root, nr_file_extents);
if (!ref) {
WARN_ON(1);
goto out;
Expand All @@ -1059,7 +1059,7 @@ int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
ref->generation = btrfs_header_generation(buf);
ref->nritems = nr_file_extents;
info = ref->extents;

for (i = 0; nr_file_extents > 0 && i < nritems; i++) {
u64 disk_bytenr;
btrfs_item_key_to_cpu(buf, &key, i);
Expand All @@ -1085,7 +1085,7 @@ int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
BUG_ON(!root->ref_tree);
ret = btrfs_add_leaf_ref(root, ref);
WARN_ON(ret);
btrfs_free_leaf_ref(ref);
btrfs_free_leaf_ref(root, ref);
}
out:
return 0;
Expand Down Expand Up @@ -2316,7 +2316,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
}

static int noinline drop_leaf_ref_no_cache(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_root *root,
struct extent_buffer *leaf)
{
u64 leaf_owner;
Expand Down Expand Up @@ -2367,7 +2367,7 @@ static int noinline drop_leaf_ref_no_cache(struct btrfs_trans_handle *trans,
}

static int noinline drop_leaf_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_root *root,
struct btrfs_leaf_ref *ref)
{
int i;
Expand Down Expand Up @@ -2521,7 +2521,7 @@ static int noinline walk_down_tree(struct btrfs_trans_handle *trans,
ret = drop_leaf_ref(trans, root, ref);
BUG_ON(ret);
btrfs_remove_leaf_ref(root, ref);
btrfs_free_leaf_ref(ref);
btrfs_free_leaf_ref(root, ref);
*level = 0;
break;
}
Expand Down
1 change: 0 additions & 1 deletion fs/btrfs/extent_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -3497,4 +3497,3 @@ int try_release_extent_buffer(struct extent_io_tree *tree, struct page *page)
return ret;
}
EXPORT_SYMBOL(try_release_extent_buffer);

1 change: 0 additions & 1 deletion fs/btrfs/file-item.c
Original file line number Diff line number Diff line change
Expand Up @@ -422,4 +422,3 @@ int btrfs_csum_truncate(struct btrfs_trans_handle *trans,
BUG_ON(ret);
return ret;
}

1 change: 0 additions & 1 deletion fs/btrfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -1095,4 +1095,3 @@ struct file_operations btrfs_file_operations = {
.compat_ioctl = btrfs_ioctl,
#endif
};

22 changes: 11 additions & 11 deletions fs/btrfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -835,17 +835,17 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode)
struct btrfs_root *root = BTRFS_I(inode)->root;
int ret = 0;

spin_lock(&root->orphan_lock);
spin_lock(&root->list_lock);

/* already on the orphan list, we're good */
if (!list_empty(&BTRFS_I(inode)->i_orphan)) {
spin_unlock(&root->orphan_lock);
spin_unlock(&root->list_lock);
return 0;
}

list_add(&BTRFS_I(inode)->i_orphan, &root->orphan_list);

spin_unlock(&root->orphan_lock);
spin_unlock(&root->list_lock);

/*
* insert an orphan item to track this unlinked/truncated file
Expand All @@ -864,20 +864,20 @@ int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode)
struct btrfs_root *root = BTRFS_I(inode)->root;
int ret = 0;

spin_lock(&root->orphan_lock);
spin_lock(&root->list_lock);

if (list_empty(&BTRFS_I(inode)->i_orphan)) {
spin_unlock(&root->orphan_lock);
spin_unlock(&root->list_lock);
return 0;
}

list_del_init(&BTRFS_I(inode)->i_orphan);
if (!trans) {
spin_unlock(&root->orphan_lock);
spin_unlock(&root->list_lock);
return 0;
}

spin_unlock(&root->orphan_lock);
spin_unlock(&root->list_lock);

ret = btrfs_del_orphan_item(trans, root, inode->i_ino);

Expand Down Expand Up @@ -973,9 +973,9 @@ void btrfs_orphan_cleanup(struct btrfs_root *root)
* add this inode to the orphan list so btrfs_orphan_del does
* the proper thing when we hit it
*/
spin_lock(&root->orphan_lock);
spin_lock(&root->list_lock);
list_add(&BTRFS_I(inode)->i_orphan, &root->orphan_list);
spin_unlock(&root->orphan_lock);
spin_unlock(&root->list_lock);

/*
* if this is a bad inode, means we actually succeeded in
Expand Down Expand Up @@ -3269,13 +3269,13 @@ void btrfs_destroy_inode(struct inode *inode)
BTRFS_I(inode)->i_default_acl != BTRFS_ACL_NOT_CACHED)
posix_acl_release(BTRFS_I(inode)->i_default_acl);

spin_lock(&BTRFS_I(inode)->root->orphan_lock);
spin_lock(&BTRFS_I(inode)->root->list_lock);
if (!list_empty(&BTRFS_I(inode)->i_orphan)) {
printk(KERN_ERR "BTRFS: inode %lu: inode still on the orphan"
" list\n", inode->i_ino);
dump_stack();
}
spin_unlock(&BTRFS_I(inode)->root->orphan_lock);
spin_unlock(&BTRFS_I(inode)->root->list_lock);

while(1) {
ordered = btrfs_lookup_first_ordered_extent(inode, (u64)-1);
Expand Down
1 change: 0 additions & 1 deletion fs/btrfs/locking.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,3 @@ int btrfs_tree_locked(struct extent_buffer *eb)
{
return mutex_is_locked(&eb->mutex);
}

1 change: 0 additions & 1 deletion fs/btrfs/print-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,3 @@ void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *c)
free_extent_buffer(next);
}
}

48 changes: 25 additions & 23 deletions fs/btrfs/ref-cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,39 @@
#include "ref-cache.h"
#include "transaction.h"

struct btrfs_leaf_ref *btrfs_alloc_leaf_ref(int nr_extents)
struct btrfs_leaf_ref *btrfs_alloc_leaf_ref(struct btrfs_root *root,
int nr_extents)
{
struct btrfs_leaf_ref *ref;
size_t size = btrfs_leaf_ref_size(nr_extents);

ref = kmalloc(btrfs_leaf_ref_size(nr_extents), GFP_NOFS);
ref = kmalloc(size, GFP_NOFS);
if (ref) {
spin_lock(&root->fs_info->ref_cache_lock);
root->fs_info->total_ref_cache_size += size;
spin_unlock(&root->fs_info->ref_cache_lock);

memset(ref, 0, sizeof(*ref));
atomic_set(&ref->usage, 1);
INIT_LIST_HEAD(&ref->list);
}
return ref;
}

void btrfs_free_leaf_ref(struct btrfs_leaf_ref *ref)
void btrfs_free_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref)
{
if (!ref)
return;
WARN_ON(atomic_read(&ref->usage) == 0);
if (atomic_dec_and_test(&ref->usage)) {
size_t size = btrfs_leaf_ref_size(ref->nritems);

BUG_ON(ref->in_tree);
kfree(ref);

spin_lock(&root->fs_info->ref_cache_lock);
root->fs_info->total_ref_cache_size -= size;
spin_unlock(&root->fs_info->ref_cache_lock);
}
}

Expand All @@ -64,7 +76,7 @@ static struct rb_node *tree_insert(struct rb_root *root, u64 bytenr,
else
return parent;
}

entry = rb_entry(node, struct btrfs_leaf_ref, rb_node);
entry->in_tree = 1;
rb_link_node(node, parent, p);
Expand All @@ -91,27 +103,27 @@ static struct rb_node *tree_search(struct rb_root *root, u64 bytenr)
return NULL;
}

int btrfs_remove_leaf_refs(struct btrfs_root *root)
int btrfs_remove_leaf_refs(struct btrfs_root *root, u64 max_root_gen)
{
struct rb_node *rb;
struct btrfs_leaf_ref *ref = NULL;
struct btrfs_leaf_ref_tree *tree = root->ref_tree;

if (!tree)
return 0;

spin_lock(&tree->lock);
while(!btrfs_leaf_ref_tree_empty(tree)) {
rb = rb_first(&tree->root);
ref = rb_entry(rb, struct btrfs_leaf_ref, rb_node);
while(!list_empty(&tree->list)) {
ref = list_entry(tree->list.next, struct btrfs_leaf_ref, list);
BUG_ON(!ref->in_tree);
if (ref->root_gen > max_root_gen)
break;

rb_erase(&ref->rb_node, &tree->root);
ref->in_tree = 0;
list_del_init(&ref->list);

spin_unlock(&tree->lock);

btrfs_free_leaf_ref(ref);

btrfs_free_leaf_ref(root, ref);
cond_resched();
spin_lock(&tree->lock);
}
Expand Down Expand Up @@ -143,17 +155,13 @@ int btrfs_add_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref)
{
int ret = 0;
struct rb_node *rb;
size_t size = btrfs_leaf_ref_size(ref->nritems);
struct btrfs_leaf_ref_tree *tree = root->ref_tree;

spin_lock(&tree->lock);
rb = tree_insert(&tree->root, ref->bytenr, &ref->rb_node);
if (rb) {
ret = -EEXIST;
} else {
spin_lock(&root->fs_info->ref_cache_lock);
root->fs_info->total_ref_cache_size += size;
spin_unlock(&root->fs_info->ref_cache_lock);
atomic_inc(&ref->usage);
list_add_tail(&ref->list, &tree->list);
}
Expand All @@ -163,23 +171,17 @@ int btrfs_add_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref)

int btrfs_remove_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref)
{
size_t size = btrfs_leaf_ref_size(ref->nritems);
struct btrfs_leaf_ref_tree *tree = root->ref_tree;

BUG_ON(!ref->in_tree);
spin_lock(&tree->lock);

spin_lock(&root->fs_info->ref_cache_lock);
root->fs_info->total_ref_cache_size -= size;
spin_unlock(&root->fs_info->ref_cache_lock);

rb_erase(&ref->rb_node, &tree->root);
ref->in_tree = 0;
list_del_init(&ref->list);

spin_unlock(&tree->lock);

btrfs_free_leaf_ref(ref);
btrfs_free_leaf_ref(root, ref);
return 0;
}

11 changes: 6 additions & 5 deletions fs/btrfs/ref-cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct btrfs_leaf_ref {
int in_tree;
atomic_t usage;

u64 root_gen;
u64 bytenr;
u64 owner;
u64 generation;
Expand All @@ -41,14 +42,13 @@ struct btrfs_leaf_ref {

static inline size_t btrfs_leaf_ref_size(int nr_extents)
{
return sizeof(struct btrfs_leaf_ref) +
return sizeof(struct btrfs_leaf_ref) +
sizeof(struct btrfs_extent_info) * nr_extents;
}

static inline void btrfs_leaf_ref_tree_init(struct btrfs_leaf_ref_tree *tree)
{
tree->root.rb_node = NULL;
tree->last = NULL;
INIT_LIST_HEAD(&tree->list);
spin_lock_init(&tree->lock);
}
Expand All @@ -59,12 +59,13 @@ static inline int btrfs_leaf_ref_tree_empty(struct btrfs_leaf_ref_tree *tree)
}

void btrfs_leaf_ref_tree_init(struct btrfs_leaf_ref_tree *tree);
struct btrfs_leaf_ref *btrfs_alloc_leaf_ref(int nr_extents);
void btrfs_free_leaf_ref(struct btrfs_leaf_ref *ref);
struct btrfs_leaf_ref *btrfs_alloc_leaf_ref(struct btrfs_root *root,
int nr_extents);
void btrfs_free_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref);
struct btrfs_leaf_ref *btrfs_lookup_leaf_ref(struct btrfs_root *root,
u64 bytenr);
int btrfs_add_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref);
int btrfs_remove_leaf_refs(struct btrfs_root *root);
int btrfs_remove_leaf_refs(struct btrfs_root *root, u64 max_root_gen);
int btrfs_remove_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref);

#endif
Loading

0 comments on commit bcc63ab

Please sign in to comment.