Skip to content

Commit

Permalink
Btrfs: Add support for mirroring across drives
Browse files Browse the repository at this point in the history
Signed-off-by: Chris Mason <chris.mason@oracle.com>
  • Loading branch information
Chris Mason committed Sep 25, 2008
1 parent 0ef8b24 commit 8790d50
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 93 deletions.
9 changes: 7 additions & 2 deletions fs/btrfs/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ struct btrfs_csum_item {
#define BTRFS_BLOCK_GROUP_SYSTEM (1 << 1)
#define BTRFS_BLOCK_GROUP_METADATA (1 << 2)
#define BTRFS_BLOCK_GROUP_RAID0 (1 << 3)
#define BTRFS_BLOCK_GROUP_RAID1 (1 << 4)


struct btrfs_block_group_item {
Expand Down Expand Up @@ -504,8 +505,12 @@ struct btrfs_fs_info {
u64 delalloc_bytes;
u64 last_alloc;
u64 last_data_alloc;
int extra_data_alloc_bits;
int extra_alloc_bits;
int avail_data_alloc_bits;
int avail_metadata_alloc_bits;
int avail_system_alloc_bits;
int data_alloc_profile;
int metadata_alloc_profile;
int system_alloc_profile;
};

/*
Expand Down
20 changes: 7 additions & 13 deletions fs/btrfs/disk-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
GFP_NOFS);
struct btrfs_root *tree_root = kmalloc(sizeof(struct btrfs_root),
GFP_NOFS);
struct btrfs_fs_info *fs_info = kmalloc(sizeof(*fs_info),
struct btrfs_fs_info *fs_info = kzalloc(sizeof(*fs_info),
GFP_NOFS);
struct btrfs_root *chunk_root = kmalloc(sizeof(struct btrfs_root),
GFP_NOFS);
Expand All @@ -744,6 +744,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
int ret;
int err = -EINVAL;
struct btrfs_super_block *disk_super;

if (!extent_root || !tree_root || !fs_info) {
err = -ENOMEM;
goto fail;
Expand All @@ -756,11 +757,8 @@ struct btrfs_root *open_ctree(struct super_block *sb,
spin_lock_init(&fs_info->delalloc_lock);
spin_lock_init(&fs_info->new_trans_lock);

memset(&fs_info->super_kobj, 0, sizeof(fs_info->super_kobj));
init_completion(&fs_info->kobj_unregister);
sb_set_blocksize(sb, 4096);
fs_info->running_transaction = NULL;
fs_info->last_trans_committed = 0;
fs_info->tree_root = tree_root;
fs_info->extent_root = extent_root;
fs_info->chunk_root = chunk_root;
Expand All @@ -770,11 +768,8 @@ struct btrfs_root *open_ctree(struct super_block *sb,
INIT_LIST_HEAD(&fs_info->space_info);
btrfs_mapping_init(&fs_info->mapping_tree);
fs_info->sb = sb;
fs_info->throttles = 0;
fs_info->mount_opt = 0;
fs_info->max_extent = (u64)-1;
fs_info->max_inline = 8192 * 1024;
fs_info->delalloc_bytes = 0;
setup_bdi(fs_info, &fs_info->bdi);
fs_info->btree_inode = new_inode(sb);
fs_info->btree_inode->i_ino = 1;
Expand Down Expand Up @@ -802,12 +797,6 @@ struct btrfs_root *open_ctree(struct super_block *sb,
extent_io_tree_init(&fs_info->extent_ins,
fs_info->btree_inode->i_mapping, GFP_NOFS);
fs_info->do_barriers = 1;
fs_info->closing = 0;
fs_info->total_pinned = 0;
fs_info->last_alloc = 0;
fs_info->last_data_alloc = 0;
fs_info->extra_alloc_bits = 0;
fs_info->extra_data_alloc_bits = 0;

#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,18)
INIT_WORK(&fs_info->trans_work, btrfs_transaction_cleaner, fs_info);
Expand Down Expand Up @@ -923,6 +912,11 @@ struct btrfs_root *open_ctree(struct super_block *sb,
btrfs_read_block_groups(extent_root);

fs_info->generation = btrfs_super_generation(disk_super) + 1;
if (btrfs_super_num_devices(disk_super) > 0) {
fs_info->data_alloc_profile = BTRFS_BLOCK_GROUP_RAID0;
fs_info->metadata_alloc_profile = BTRFS_BLOCK_GROUP_RAID1;
fs_info->system_alloc_profile = BTRFS_BLOCK_GROUP_RAID0;
}
mutex_unlock(&fs_info->fs_mutex);
return tree_root;

Expand Down
93 changes: 49 additions & 44 deletions fs/btrfs/extent-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,13 @@ static int noinline find_search_start(struct btrfs_root *root,
goto new_group;
if (start + num > total_fs_bytes)
goto new_group;
if (!block_group_bits(cache, data)) {
printk("block group bits don't match %Lu %Lu\n", cache->flags, data);
}
*start_ret = start;
return 0;
} out:
}
out:
cache = btrfs_lookup_block_group(root->fs_info, search_start);
if (!cache) {
printk("Unable to find block group for %Lu\n", search_start);
Expand Down Expand Up @@ -365,14 +369,17 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
if (cache->key.objectid > total_fs_bytes)
break;

if (full_search)
free_check = cache->key.offset;
else
free_check = div_factor(cache->key.offset, factor);
if (block_group_bits(cache, data)) {
if (full_search)
free_check = cache->key.offset;
else
free_check = div_factor(cache->key.offset,
factor);

if (used + cache->pinned < free_check) {
found_group = cache;
goto found;
if (used + cache->pinned < free_check) {
found_group = cache;
goto found;
}
}
cond_resched();
}
Expand Down Expand Up @@ -1038,6 +1045,19 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
return 0;
}

static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags)
{
u64 extra_flags = flags & (BTRFS_BLOCK_GROUP_RAID0 |
BTRFS_BLOCK_GROUP_RAID1);
if (extra_flags) {
if (flags & BTRFS_BLOCK_GROUP_DATA)
fs_info->avail_data_alloc_bits |= extra_flags;
if (flags & BTRFS_BLOCK_GROUP_METADATA)
fs_info->avail_metadata_alloc_bits |= extra_flags;
if (flags & BTRFS_BLOCK_GROUP_SYSTEM)
fs_info->avail_system_alloc_bits |= extra_flags;
}
}

static int do_chunk_alloc(struct btrfs_trans_handle *trans,
struct btrfs_root *extent_root, u64 alloc_bytes,
Expand All @@ -1060,7 +1080,7 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
if (space_info->full)
return 0;

thresh = div_factor(space_info->total_bytes, 7);
thresh = div_factor(space_info->total_bytes, 6);
if ((space_info->bytes_used + space_info->bytes_pinned + alloc_bytes) <
thresh)
return 0;
Expand All @@ -1079,16 +1099,7 @@ printk("space info full %Lu\n", flags);
start, num_bytes);
BUG_ON(ret);

if (flags & BTRFS_BLOCK_GROUP_RAID0) {
if (flags & BTRFS_BLOCK_GROUP_DATA) {
extent_root->fs_info->extra_data_alloc_bits =
BTRFS_BLOCK_GROUP_RAID0;
}
if (flags & BTRFS_BLOCK_GROUP_METADATA) {
extent_root->fs_info->extra_alloc_bits =
BTRFS_BLOCK_GROUP_RAID0;
}
}
set_avail_alloc_bits(extent_root->fs_info, flags);
return 0;
}

Expand Down Expand Up @@ -1529,6 +1540,7 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans,

if (data & BTRFS_BLOCK_GROUP_METADATA) {
last_ptr = &root->fs_info->last_alloc;
empty_cluster = 256 * 1024;
}

if ((data & BTRFS_BLOCK_GROUP_DATA) && btrfs_test_opt(root, SSD)) {
Expand Down Expand Up @@ -1693,38 +1705,40 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
u64 root_used;
u64 search_start = 0;
u64 new_hint;
u64 alloc_profile;
u32 sizes[2];
struct btrfs_fs_info *info = root->fs_info;
struct btrfs_root *extent_root = info->extent_root;
struct btrfs_extent_item *extent_item;
struct btrfs_extent_ref *ref;
struct btrfs_path *path;
struct btrfs_key keys[2];
int extra_chunk_alloc_bits = 0;

if (data) {
data = BTRFS_BLOCK_GROUP_DATA | info->extra_data_alloc_bits;
alloc_profile = info->avail_data_alloc_bits &
info->data_alloc_profile;
data = BTRFS_BLOCK_GROUP_DATA | alloc_profile;
} else if (root == root->fs_info->chunk_root) {
data = BTRFS_BLOCK_GROUP_SYSTEM;
alloc_profile = info->avail_system_alloc_bits &
info->system_alloc_profile;
data = BTRFS_BLOCK_GROUP_SYSTEM | alloc_profile;
} else {
data = BTRFS_BLOCK_GROUP_METADATA | info->extra_alloc_bits;
alloc_profile = info->avail_metadata_alloc_bits &
info->metadata_alloc_profile;
data = BTRFS_BLOCK_GROUP_METADATA | alloc_profile;
}
if (btrfs_super_num_devices(&info->super_copy) > 1 &&
!(data & BTRFS_BLOCK_GROUP_SYSTEM))
extra_chunk_alloc_bits = BTRFS_BLOCK_GROUP_RAID0;

if (root->ref_cows) {
if (!(data & BTRFS_BLOCK_GROUP_METADATA)) {
ret = do_chunk_alloc(trans, root->fs_info->extent_root,
2 * 1024 * 1024,
BTRFS_BLOCK_GROUP_METADATA |
info->extra_alloc_bits |
extra_chunk_alloc_bits);
(info->metadata_alloc_profile &
info->avail_metadata_alloc_bits));
BUG_ON(ret);
}
ret = do_chunk_alloc(trans, root->fs_info->extent_root,
num_bytes + 2 * 1024 * 1024, data |
extra_chunk_alloc_bits);
num_bytes + 2 * 1024 * 1024, data);
BUG_ON(ret);
}

Expand Down Expand Up @@ -2046,12 +2060,12 @@ static int noinline walk_down_tree(struct btrfs_trans_handle *trans,
if (!next || !btrfs_buffer_uptodate(next)) {
free_extent_buffer(next);
reada_walk_down(root, cur, path->slots[*level]);

mutex_unlock(&root->fs_info->fs_mutex);
next = read_tree_block(root, bytenr, blocksize);
mutex_lock(&root->fs_info->fs_mutex);

/* we used to drop the lock above, keep the
* code to double check so that we won't forget
* when we drop the lock again in the future
*/
/* we've dropped the lock, double check */
ret = lookup_extent_ref(trans, root, bytenr,
blocksize, &refs);
BUG_ON(ret);
Expand Down Expand Up @@ -2739,16 +2753,7 @@ int btrfs_read_block_groups(struct btrfs_root *root)
} else if (cache->flags & BTRFS_BLOCK_GROUP_METADATA) {
bit = BLOCK_GROUP_METADATA;
}
if (cache->flags & BTRFS_BLOCK_GROUP_RAID0) {
if (cache->flags & BTRFS_BLOCK_GROUP_DATA) {
info->extra_data_alloc_bits =
BTRFS_BLOCK_GROUP_RAID0;
}
if (cache->flags & BTRFS_BLOCK_GROUP_METADATA) {
info->extra_alloc_bits =
BTRFS_BLOCK_GROUP_RAID0;
}
}
set_avail_alloc_bits(info, cache->flags);

ret = update_space_info(info, cache->flags, found_key.offset,
btrfs_block_group_used(&cache->item),
Expand Down
4 changes: 3 additions & 1 deletion fs/btrfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
u64 physical;
u64 length = 0;
u64 map_length;
int total_devs;
struct bio_vec *bvec;
int i;
int ret;
Expand All @@ -315,7 +316,8 @@ int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
}
map_tree = &root->fs_info->mapping_tree;
map_length = length;
ret = btrfs_map_block(map_tree, logical, &physical, &map_length, &dev);
ret = btrfs_map_block(map_tree, READ, 0, logical, &physical,
&map_length, &dev, &total_devs);
if (map_length < length + size) {
return 1;
}
Expand Down
Loading

0 comments on commit 8790d50

Please sign in to comment.