bcc63abbf3
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>
200 lines
6.8 KiB
C
200 lines
6.8 KiB
C
/*
|
|
* Copyright (C) 2007 Oracle. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public
|
|
* License v2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this program; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 021110-1307, USA.
|
|
*/
|
|
|
|
#include "ctree.h"
|
|
#include "disk-io.h"
|
|
#include "print-tree.h"
|
|
|
|
static void print_chunk(struct extent_buffer *eb, struct btrfs_chunk *chunk)
|
|
{
|
|
int num_stripes = btrfs_chunk_num_stripes(eb, chunk);
|
|
int i;
|
|
printk("\t\tchunk length %llu owner %llu type %llu num_stripes %d\n",
|
|
(unsigned long long)btrfs_chunk_length(eb, chunk),
|
|
(unsigned long long)btrfs_chunk_owner(eb, chunk),
|
|
(unsigned long long)btrfs_chunk_type(eb, chunk),
|
|
num_stripes);
|
|
for (i = 0 ; i < num_stripes ; i++) {
|
|
printk("\t\t\tstripe %d devid %llu offset %llu\n", i,
|
|
(unsigned long long)btrfs_stripe_devid_nr(eb, chunk, i),
|
|
(unsigned long long)btrfs_stripe_offset_nr(eb, chunk, i));
|
|
}
|
|
}
|
|
static void print_dev_item(struct extent_buffer *eb,
|
|
struct btrfs_dev_item *dev_item)
|
|
{
|
|
printk("\t\tdev item devid %llu "
|
|
"total_bytes %llu bytes used %Lu\n",
|
|
(unsigned long long)btrfs_device_id(eb, dev_item),
|
|
(unsigned long long)btrfs_device_total_bytes(eb, dev_item),
|
|
(unsigned long long)btrfs_device_bytes_used(eb, dev_item));
|
|
}
|
|
void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
|
|
{
|
|
int i;
|
|
u32 nr = btrfs_header_nritems(l);
|
|
struct btrfs_item *item;
|
|
struct btrfs_extent_item *ei;
|
|
struct btrfs_root_item *ri;
|
|
struct btrfs_dir_item *di;
|
|
struct btrfs_inode_item *ii;
|
|
struct btrfs_block_group_item *bi;
|
|
struct btrfs_file_extent_item *fi;
|
|
struct btrfs_key key;
|
|
struct btrfs_key found_key;
|
|
struct btrfs_extent_ref *ref;
|
|
struct btrfs_dev_extent *dev_extent;
|
|
u32 type;
|
|
|
|
printk("leaf %llu total ptrs %d free space %d\n",
|
|
(unsigned long long)btrfs_header_bytenr(l), nr,
|
|
btrfs_leaf_free_space(root, l));
|
|
for (i = 0 ; i < nr ; i++) {
|
|
item = btrfs_item_nr(l, i);
|
|
btrfs_item_key_to_cpu(l, &key, i);
|
|
type = btrfs_key_type(&key);
|
|
printk("\titem %d key (%llu %x %llu) itemoff %d itemsize %d\n",
|
|
i,
|
|
(unsigned long long)key.objectid, type,
|
|
(unsigned long long)key.offset,
|
|
btrfs_item_offset(l, item), btrfs_item_size(l, item));
|
|
switch (type) {
|
|
case BTRFS_INODE_ITEM_KEY:
|
|
ii = btrfs_item_ptr(l, i, struct btrfs_inode_item);
|
|
printk("\t\tinode generation %llu size %llu mode %o\n",
|
|
(unsigned long long)btrfs_inode_generation(l, ii),
|
|
(unsigned long long)btrfs_inode_size(l, ii),
|
|
btrfs_inode_mode(l, ii));
|
|
break;
|
|
case BTRFS_DIR_ITEM_KEY:
|
|
di = btrfs_item_ptr(l, i, struct btrfs_dir_item);
|
|
btrfs_dir_item_key_to_cpu(l, di, &found_key);
|
|
printk("\t\tdir oid %llu type %u\n",
|
|
(unsigned long long)found_key.objectid,
|
|
btrfs_dir_type(l, di));
|
|
break;
|
|
case BTRFS_ROOT_ITEM_KEY:
|
|
ri = btrfs_item_ptr(l, i, struct btrfs_root_item);
|
|
printk("\t\troot data bytenr %llu refs %u\n",
|
|
(unsigned long long)btrfs_disk_root_bytenr(l, ri),
|
|
btrfs_disk_root_refs(l, ri));
|
|
break;
|
|
case BTRFS_EXTENT_ITEM_KEY:
|
|
ei = btrfs_item_ptr(l, i, struct btrfs_extent_item);
|
|
printk("\t\textent data refs %u\n",
|
|
btrfs_extent_refs(l, ei));
|
|
break;
|
|
case BTRFS_EXTENT_REF_KEY:
|
|
ref = btrfs_item_ptr(l, i, struct btrfs_extent_ref);
|
|
printk("\t\textent back ref root %llu gen %llu "
|
|
"owner %llu offset %llu\n",
|
|
(unsigned long long)btrfs_ref_root(l, ref),
|
|
(unsigned long long)btrfs_ref_generation(l, ref),
|
|
(unsigned long long)btrfs_ref_objectid(l, ref),
|
|
(unsigned long long)btrfs_ref_offset(l, ref));
|
|
break;
|
|
|
|
case BTRFS_EXTENT_DATA_KEY:
|
|
fi = btrfs_item_ptr(l, i,
|
|
struct btrfs_file_extent_item);
|
|
if (btrfs_file_extent_type(l, fi) ==
|
|
BTRFS_FILE_EXTENT_INLINE) {
|
|
printk("\t\tinline extent data size %u\n",
|
|
btrfs_file_extent_inline_len(l, item));
|
|
break;
|
|
}
|
|
printk("\t\textent data disk bytenr %llu nr %llu\n",
|
|
(unsigned long long)btrfs_file_extent_disk_bytenr(l, fi),
|
|
(unsigned long long)btrfs_file_extent_disk_num_bytes(l, fi));
|
|
printk("\t\textent data offset %llu nr %llu\n",
|
|
(unsigned long long)btrfs_file_extent_offset(l, fi),
|
|
(unsigned long long)btrfs_file_extent_num_bytes(l, fi));
|
|
break;
|
|
case BTRFS_BLOCK_GROUP_ITEM_KEY:
|
|
bi = btrfs_item_ptr(l, i,
|
|
struct btrfs_block_group_item);
|
|
printk("\t\tblock group used %llu\n",
|
|
(unsigned long long)btrfs_disk_block_group_used(l, bi));
|
|
break;
|
|
case BTRFS_CHUNK_ITEM_KEY:
|
|
print_chunk(l, btrfs_item_ptr(l, i, struct btrfs_chunk));
|
|
break;
|
|
case BTRFS_DEV_ITEM_KEY:
|
|
print_dev_item(l, btrfs_item_ptr(l, i,
|
|
struct btrfs_dev_item));
|
|
break;
|
|
case BTRFS_DEV_EXTENT_KEY:
|
|
dev_extent = btrfs_item_ptr(l, i,
|
|
struct btrfs_dev_extent);
|
|
printk("\t\tdev extent chunk_tree %llu\n"
|
|
"\t\tchunk objectid %llu chunk offset %llu "
|
|
"length %llu\n",
|
|
(unsigned long long)
|
|
btrfs_dev_extent_chunk_tree(l, dev_extent),
|
|
(unsigned long long)
|
|
btrfs_dev_extent_chunk_objectid(l, dev_extent),
|
|
(unsigned long long)
|
|
btrfs_dev_extent_chunk_offset(l, dev_extent),
|
|
(unsigned long long)
|
|
btrfs_dev_extent_length(l, dev_extent));
|
|
};
|
|
}
|
|
}
|
|
|
|
void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *c)
|
|
{
|
|
int i; u32 nr;
|
|
struct btrfs_key key;
|
|
int level;
|
|
|
|
if (!c)
|
|
return;
|
|
nr = btrfs_header_nritems(c);
|
|
level = btrfs_header_level(c);
|
|
if (level == 0) {
|
|
btrfs_print_leaf(root, c);
|
|
return;
|
|
}
|
|
printk("node %llu level %d total ptrs %d free spc %u\n",
|
|
(unsigned long long)btrfs_header_bytenr(c),
|
|
btrfs_header_level(c), nr,
|
|
(u32)BTRFS_NODEPTRS_PER_BLOCK(root) - nr);
|
|
for (i = 0; i < nr; i++) {
|
|
btrfs_node_key_to_cpu(c, &key, i);
|
|
printk("\tkey %d (%llu %u %llu) block %llu\n",
|
|
i,
|
|
(unsigned long long)key.objectid,
|
|
key.type,
|
|
(unsigned long long)key.offset,
|
|
(unsigned long long)btrfs_node_blockptr(c, i));
|
|
}
|
|
for (i = 0; i < nr; i++) {
|
|
struct extent_buffer *next = read_tree_block(root,
|
|
btrfs_node_blockptr(c, i),
|
|
btrfs_level_size(root, level - 1),
|
|
btrfs_node_ptr_generation(c, i));
|
|
if (btrfs_is_leaf(next) &&
|
|
btrfs_header_level(c) != 1)
|
|
BUG();
|
|
if (btrfs_header_level(next) !=
|
|
btrfs_header_level(c) - 1)
|
|
BUG();
|
|
btrfs_print_tree(root, next);
|
|
free_extent_buffer(next);
|
|
}
|
|
}
|