summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYan Zheng <zheng.yan@oracle.com>2008-11-18 10:40:06 -0500
committerChris Mason <chris.mason@oracle.com>2008-11-18 10:40:06 -0500
commit4d1d3a59d6debe80ce1e2da60e82233ab8f1f886 (patch)
tree896a25411082e009b2699ef63a7dc9ae5d286bc3
parent95470dfaf12dc8aa59bcb6829c4a7f759d2c5ff6 (diff)
update btrfs-progs for seed device support
This patch does the following: 1) Update device management code to match the kernel code. 2) Allocator fixes. 3) Add a program called btrfstune to set/clear the SEEDING super block flags.
-rw-r--r--Makefile5
-rw-r--r--btrfstune.c126
-rw-r--r--ctree.c8
-rw-r--r--ctree.h18
-rw-r--r--disk-io.c29
-rw-r--r--extent-tree.c81
-rw-r--r--utils.c8
-rw-r--r--volumes.c112
-rw-r--r--volumes.h7
9 files changed, 342 insertions, 52 deletions
diff --git a/Makefile b/Makefile
index d7a1667d..840fca63 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,7 @@ prefix ?= /usr/local
bindir = $(prefix)/bin
LIBS=-luuid
-progs = btrfsctl btrfsck mkfs.btrfs debug-tree btrfs-show btrfs-vol
+progs = btrfsctl btrfsck mkfs.btrfs debug-tree btrfs-show btrfs-vol btrfstune
# make C=1 to enable sparse
ifdef C
@@ -52,6 +52,9 @@ mkfs.btrfs: $(objects) mkfs.o
debug-tree: $(objects) debug-tree.o
gcc $(CFLAGS) -o debug-tree $(objects) debug-tree.o $(LDFLAGS) $(LIBS)
+btrfstune: $(objects) btrfstune.o
+ gcc $(CFLAGS) -o btrfstune $(objects) btrfstune.o $(LDFLAGS) $(LIBS)
+
dir-test: $(objects) dir-test.o
gcc $(CFLAGS) -o dir-test $(objects) dir-test.o $(LDFLAGS) $(LIBS)
diff --git a/btrfstune.c b/btrfstune.c
new file mode 100644
index 00000000..47830c5a
--- /dev/null
+++ b/btrfstune.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#define _XOPEN_SOURCE 500
+#define _GNU_SOURCE 1
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include "kerncompat.h"
+#include "ctree.h"
+#include "disk-io.h"
+#include "transaction.h"
+#include "utils.h"
+#include "version.h"
+
+static char *device;
+
+int update_seeding_flag(struct btrfs_root *root, int set_flag)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_super_block *disk_super;
+ u64 super_flags;
+
+ disk_super = &root->fs_info->super_copy;
+ super_flags = btrfs_super_flags(disk_super);
+ if (set_flag) {
+ if (super_flags & BTRFS_SUPER_FLAG_SEEDING) {
+ fprintf(stderr, "seeding flag is already set on %s\n",
+ device);
+ return 1;
+ }
+ super_flags |= BTRFS_SUPER_FLAG_SEEDING;
+ } else {
+ if (!(super_flags & BTRFS_SUPER_FLAG_SEEDING)) {
+ fprintf(stderr, "seeding flag is not set on %s\n",
+ device);
+ return 1;
+ }
+ super_flags &= ~BTRFS_SUPER_FLAG_SEEDING;
+ }
+
+ trans = btrfs_start_transaction(root, 1);
+ btrfs_set_super_flags(disk_super, super_flags);
+ btrfs_commit_transaction(trans, root);
+
+ return 0;
+}
+
+static void print_usage(void)
+{
+ fprintf(stderr, "usage: btrfstune [options] device\n");
+ fprintf(stderr, "\t-S value\tenable/disable seeding\n");
+}
+
+int main(int argc, char *argv[])
+{
+ struct btrfs_root *root;
+ int success = 0;
+ int seeding_flag = 0;
+ int seeding_value = 0;
+ int ret;
+
+ while(1) {
+ int c = getopt(argc, argv, "S:");
+ if (c < 0)
+ break;
+ switch(c) {
+ case 'S':
+ seeding_flag = 1;
+ seeding_value = atoi(optarg);
+ break;
+ default:
+ print_usage();
+ return 1;
+ }
+ }
+
+ argc = argc - optind;
+ device = argv[optind];
+ if (argc != 1) {
+ print_usage();
+ return 1;
+ }
+
+ if (check_mounted(device)) {
+ fprintf(stderr, "%s is mounted\n", device);
+ return 1;
+ }
+
+ root = open_ctree(device, 0, 1);
+
+ if (seeding_flag) {
+ ret = update_seeding_flag(root, seeding_value);
+ if (!ret)
+ success++;
+ }
+
+ if (success > 0) {
+ ret = 0;
+ } else {
+ root->fs_info->readonly = 1;
+ ret = 1;
+ }
+ close_ctree(root);
+
+ return ret;
+}
diff --git a/ctree.c b/ctree.c
index 84ebf2b9..0d9797e5 100644
--- a/ctree.c
+++ b/ctree.c
@@ -112,6 +112,10 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
btrfs_set_header_owner(cow, new_root_objectid);
btrfs_clear_header_flag(cow, BTRFS_HEADER_FLAG_WRITTEN);
+ write_extent_buffer(cow, root->fs_info->fsid,
+ (unsigned long)btrfs_header_fsid(cow),
+ BTRFS_FSID_SIZE);
+
WARN_ON(btrfs_header_generation(buf) > trans->transid);
ret = btrfs_inc_ref(trans, new_root, buf, cow, NULL);
kfree(new_root);
@@ -161,6 +165,10 @@ int __btrfs_cow_block(struct btrfs_trans_handle *trans,
btrfs_set_header_owner(cow, root->root_key.objectid);
btrfs_clear_header_flag(cow, BTRFS_HEADER_FLAG_WRITTEN);
+ write_extent_buffer(cow, root->fs_info->fsid,
+ (unsigned long)btrfs_header_fsid(cow),
+ BTRFS_FSID_SIZE);
+
WARN_ON(btrfs_header_generation(buf) > trans->transid);
if (btrfs_header_generation(buf) != trans->transid) {
different_trans = 1;
diff --git a/ctree.h b/ctree.h
index c605abdf..94a102d3 100644
--- a/ctree.h
+++ b/ctree.h
@@ -160,6 +160,9 @@ struct btrfs_dev_item {
/* type and info about this device */
__le64 type;
+ /* expected generation for this device */
+ __le64 generation;
+
/* grouping information for allocation decisions */
__le32 dev_group;
@@ -171,6 +174,9 @@ struct btrfs_dev_item {
/* btrfs generated uuid for this device */
u8 uuid[BTRFS_UUID_SIZE];
+
+ /* uuid of FS who owns this device */
+ u8 fsid[BTRFS_UUID_SIZE];
} __attribute__ ((__packed__));
struct btrfs_stripe {
@@ -246,6 +252,8 @@ struct btrfs_header {
sizeof(struct btrfs_item) - \
sizeof(struct btrfs_file_extent_item))
+#define BTRFS_SUPER_FLAG_SEEDING (1ULL << 32)
+
/*
* this is a very generous portion of the super block, giving us
* room to translate 14 chunks with 3 stripes each.
@@ -524,6 +532,7 @@ struct btrfs_block_group_cache {
u64 pinned;
u64 flags;
int cached;
+ int ro;
};
struct btrfs_extent_ops {
@@ -744,6 +753,7 @@ BTRFS_SETGET_FUNCS(device_id, struct btrfs_dev_item, devid, 64);
BTRFS_SETGET_FUNCS(device_group, struct btrfs_dev_item, dev_group, 32);
BTRFS_SETGET_FUNCS(device_seek_speed, struct btrfs_dev_item, seek_speed, 8);
BTRFS_SETGET_FUNCS(device_bandwidth, struct btrfs_dev_item, bandwidth, 8);
+BTRFS_SETGET_FUNCS(device_generation, struct btrfs_dev_item, generation, 64);
BTRFS_SETGET_STACK_FUNCS(stack_device_type, struct btrfs_dev_item, type, 64);
BTRFS_SETGET_STACK_FUNCS(stack_device_total_bytes, struct btrfs_dev_item,
@@ -763,12 +773,19 @@ BTRFS_SETGET_STACK_FUNCS(stack_device_seek_speed, struct btrfs_dev_item,
seek_speed, 8);
BTRFS_SETGET_STACK_FUNCS(stack_device_bandwidth, struct btrfs_dev_item,
bandwidth, 8);
+BTRFS_SETGET_STACK_FUNCS(stack_device_generation, struct btrfs_dev_item,
+ generation, 64);
static inline char *btrfs_device_uuid(struct btrfs_dev_item *d)
{
return (char *)d + offsetof(struct btrfs_dev_item, uuid);
}
+static inline char *btrfs_device_fsid(struct btrfs_dev_item *d)
+{
+ return (char *)d + offsetof(struct btrfs_dev_item, fsid);
+}
+
BTRFS_SETGET_FUNCS(chunk_length, struct btrfs_chunk, length, 64);
BTRFS_SETGET_FUNCS(chunk_owner, struct btrfs_chunk, owner, 64);
BTRFS_SETGET_FUNCS(chunk_stripe_len, struct btrfs_chunk, stripe_len, 64);
@@ -1259,6 +1276,7 @@ BTRFS_SETGET_STACK_FUNCS(root_last_snapshot, struct btrfs_root_item,
/* struct btrfs_super_block */
BTRFS_SETGET_STACK_FUNCS(super_bytenr, struct btrfs_super_block, bytenr, 64);
+BTRFS_SETGET_STACK_FUNCS(super_flags, struct btrfs_super_block, flags, 64);
BTRFS_SETGET_STACK_FUNCS(super_generation, struct btrfs_super_block,
generation, 64);
BTRFS_SETGET_STACK_FUNCS(super_root, struct btrfs_super_block, root, 64);
diff --git a/disk-io.c b/disk-io.c
index 3caf3a03..d2e1bdd6 100644
--- a/disk-io.c
+++ b/disk-io.c
@@ -37,14 +37,24 @@
static int check_tree_block(struct btrfs_root *root, struct extent_buffer *buf)
{
+
+ struct btrfs_fs_devices *fs_devices;
+ int ret = 1;
+
if (buf->start != btrfs_header_bytenr(buf))
- return 1;
+ return ret;
- if (memcmp_extent_buffer(buf, root->fs_info->fsid,
- (unsigned long)btrfs_header_fsid(buf),
- BTRFS_FSID_SIZE))
- return 1;
- return 0;
+ fs_devices = root->fs_info->fs_devices;
+ while (fs_devices) {
+ if (!memcmp_extent_buffer(buf, fs_devices->fsid,
+ (unsigned long)btrfs_header_fsid(buf),
+ BTRFS_FSID_SIZE)) {
+ ret = 0;
+ break;
+ }
+ fs_devices = fs_devices->seed;
+ }
+ return ret;
}
u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len)
@@ -685,6 +695,10 @@ int write_all_supers(struct btrfs_root *root)
dev_item);
list_for_each(cur, head) {
dev = list_entry(cur, struct btrfs_device, dev_list);
+ if (!dev->writeable)
+ continue;
+
+ btrfs_set_device_generation(sb, dev_item, 0);
btrfs_set_device_type(sb, dev_item, dev->type);
btrfs_set_device_id(sb, dev_item, dev->devid);
btrfs_set_device_total_bytes(sb, dev_item, dev->total_bytes);
@@ -695,6 +709,9 @@ int write_all_supers(struct btrfs_root *root)
write_extent_buffer(sb, dev->uuid,
(unsigned long)btrfs_device_uuid(dev_item),
BTRFS_UUID_SIZE);
+ write_extent_buffer(sb, dev->fs_devices->fsid,
+ (unsigned long)btrfs_device_fsid(dev_item),
+ BTRFS_UUID_SIZE);
sb->fd = dev->fd;
sb->dev_bytenr = sb->start;
btrfs_set_header_flag(sb, BTRFS_HEADER_FLAG_WRITTEN);
diff --git a/extent-tree.c b/extent-tree.c
index e5c304ca..6a5194c5 100644
--- a/extent-tree.c
+++ b/extent-tree.c
@@ -159,6 +159,35 @@ err:
return 0;
}
+struct btrfs_block_group_cache *btrfs_lookup_first_block_group(struct
+ btrfs_fs_info *info,
+ u64 bytenr)
+{
+ struct extent_io_tree *block_group_cache;
+ struct btrfs_block_group_cache *block_group = NULL;
+ u64 ptr;
+ u64 start;
+ u64 end;
+ int ret;
+
+ bytenr = max_t(u64, bytenr,
+ BTRFS_SUPER_INFO_OFFSET + BTRFS_SUPER_INFO_SIZE);
+ block_group_cache = &info->block_group_cache;
+ ret = find_first_extent_bit(block_group_cache,
+ bytenr, &start, &end,
+ BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA |
+ BLOCK_GROUP_SYSTEM);
+ if (ret) {
+ return NULL;
+ }
+ ret = get_state_private(block_group_cache, start, &ptr);
+ if (ret)
+ return NULL;
+
+ block_group = (struct btrfs_block_group_cache *)(unsigned long)ptr;
+ return block_group;
+}
+
struct btrfs_block_group_cache *btrfs_lookup_block_group(struct
btrfs_fs_info *info,
u64 bytenr)
@@ -203,7 +232,6 @@ static int noinline find_search_start(struct btrfs_root *root,
u64 last;
u64 start = 0;
u64 end = 0;
- u64 cache_miss = 0;
u64 search_start = *start_ret;
int wrapped = 0;
@@ -216,7 +244,7 @@ again:
goto out;
last = max(search_start, cache->key.objectid);
- if (!block_group_bits(cache, data)) {
+ if (cache->ro || !block_group_bits(cache, data)) {
goto new_group;
}
@@ -224,20 +252,17 @@ again:
ret = find_first_extent_bit(&root->fs_info->free_space_cache,
last, &start, &end, EXTENT_DIRTY);
if (ret) {
- if (!cache_miss)
- cache_miss = last;
goto new_group;
}
start = max(last, start);
last = end + 1;
if (last - start < num) {
- if (last == cache->key.objectid + cache->key.offset)
- cache_miss = start;
continue;
}
- if (start + num > cache->key.objectid + cache->key.offset)
+ if (start + num > cache->key.objectid + cache->key.offset) {
goto new_group;
+ }
*start_ret = start;
return 0;
}
@@ -253,7 +278,7 @@ out:
new_group:
last = cache->key.objectid + cache->key.offset;
wrapped:
- cache = btrfs_lookup_block_group(root->fs_info, last);
+ cache = btrfs_lookup_first_block_group(root->fs_info, last);
if (!cache) {
no_cache:
if (!wrapped) {
@@ -263,16 +288,12 @@ no_cache:
}
goto out;
}
- if (cache_miss && !cache->cached) {
- cache_block_group(root, cache);
- last = cache_miss;
- cache = btrfs_lookup_block_group(root->fs_info, last);
- }
+ cache = btrfs_find_block_group(root, cache, last, data, 0);
cache = btrfs_find_block_group(root, cache, last, data, 0);
if (!cache)
goto no_cache;
+
*cache_ret = cache;
- cache_miss = 0;
goto again;
}
@@ -328,7 +349,7 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
if (search_start) {
struct btrfs_block_group_cache *shint;
shint = btrfs_lookup_block_group(info, search_start);
- if (shint && block_group_bits(shint, data)) {
+ if (shint && !shint->ro && block_group_bits(shint, data)) {
used = btrfs_block_group_used(&shint->item);
if (used + shint->pinned <
div_factor(shint->key.offset, factor)) {
@@ -336,7 +357,7 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
}
}
}
- if (hint && block_group_bits(hint, data)) {
+ if (hint && !hint->ro && block_group_bits(hint, data)) {
used = btrfs_block_group_used(&hint->item);
if (used + hint->pinned <
div_factor(hint->key.offset, factor)) {
@@ -367,7 +388,7 @@ again:
last = cache->key.objectid + cache->key.offset;
used = btrfs_block_group_used(&cache->item);
- if (block_group_bits(cache, data)) {
+ if (!cache->ro && block_group_bits(cache, data)) {
if (full_search)
free_check = cache->key.offset;
else
@@ -1705,11 +1726,8 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans,
WARN_ON(num_bytes < root->sectorsize);
btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);
- if (search_end == (u64)-1)
- search_end = btrfs_super_total_bytes(&info->super_copy);
-
if (hint_byte) {
- block_group = btrfs_lookup_block_group(info, hint_byte);
+ block_group = btrfs_lookup_first_block_group(info, hint_byte);
if (!block_group)
hint_byte = search_start;
block_group = btrfs_find_block_group(root, block_group,
@@ -1724,9 +1742,10 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans,
check_failed:
if (!block_group) {
- block_group = btrfs_lookup_block_group(info, search_start);
+ block_group = btrfs_lookup_first_block_group(info,
+ search_start);
if (!block_group)
- block_group = btrfs_lookup_block_group(info,
+ block_group = btrfs_lookup_first_block_group(info,
orig_search_start);
}
ret = find_search_start(root, &block_group, &search_start,
@@ -1738,9 +1757,6 @@ check_failed:
ins->objectid = search_start;
ins->offset = num_bytes;
- if (ins->objectid + num_bytes >= search_end)
- goto enospc;
-
if (ins->objectid + num_bytes >
block_group->key.objectid + block_group->key.offset) {
search_start = block_group->key.objectid +
@@ -1775,8 +1791,8 @@ check_failed:
return 0;
new_group:
- if (search_start + num_bytes >= search_end) {
-enospc:
+ block_group = btrfs_lookup_first_block_group(info, search_start);
+ if (!block_group) {
search_start = orig_search_start;
if (full_scan) {
ret = -ENOSPC;
@@ -1789,7 +1805,6 @@ enospc:
} else
wrapped = 1;
}
- block_group = btrfs_lookup_block_group(info, search_start);
cond_resched();
block_group = btrfs_find_block_group(root, block_group,
search_start, data, 0);
@@ -2414,7 +2429,7 @@ int btrfs_read_block_groups(struct btrfs_root *root)
}
leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
- cache = kmalloc(sizeof(*cache), GFP_NOFS);
+ cache = kzalloc(sizeof(*cache), GFP_NOFS);
if (!cache) {
ret = -ENOMEM;
break;
@@ -2438,6 +2453,8 @@ int btrfs_read_block_groups(struct btrfs_root *root)
bit = BLOCK_GROUP_METADATA;
}
set_avail_alloc_bits(info, cache->flags);
+ if (btrfs_chunk_readonly(root, cache->key.objectid))
+ cache->ro = 1;
ret = update_space_info(info, cache->flags, found_key.offset,
btrfs_block_group_used(&cache->item),
@@ -2451,10 +2468,6 @@ int btrfs_read_block_groups(struct btrfs_root *root)
bit | EXTENT_LOCKED, GFP_NOFS);
set_state_private(block_group_cache, found_key.objectid,
(unsigned long)cache);
-
- if (key.objectid >=
- btrfs_super_total_bytes(&info->super_copy))
- break;
}
ret = 0;
error:
diff --git a/utils.c b/utils.c
index fa3cef46..4eb9046e 100644
--- a/utils.c
+++ b/utils.c
@@ -96,7 +96,7 @@ int make_btrfs(int fd, const char *device, const char *label,
btrfs_set_super_root(&super, blocks[1]);
btrfs_set_super_chunk_root(&super, blocks[3]);
btrfs_set_super_total_bytes(&super, num_bytes);
- btrfs_set_super_bytes_used(&super, first_free + 5 * leafsize);
+ btrfs_set_super_bytes_used(&super, 5 * leafsize);
btrfs_set_super_sectorsize(&super, sectorsize);
btrfs_set_super_leafsize(&super, leafsize);
btrfs_set_super_nodesize(&super, nodesize);
@@ -252,6 +252,7 @@ int make_btrfs(int fd, const char *device, const char *label,
dev_item = btrfs_item_ptr(buf, nritems, struct btrfs_dev_item);
btrfs_set_device_id(buf, dev_item, 1);
+ btrfs_set_device_generation(buf, dev_item, 0);
btrfs_set_device_total_bytes(buf, dev_item, num_bytes);
btrfs_set_device_bytes_used(buf, dev_item,
BTRFS_MKFS_SYSTEM_GROUP_SIZE);
@@ -263,6 +264,9 @@ int make_btrfs(int fd, const char *device, const char *label,
write_extent_buffer(buf, super.dev_item.uuid,
(unsigned long)btrfs_device_uuid(dev_item),
BTRFS_UUID_SIZE);
+ write_extent_buffer(buf, super.fsid,
+ (unsigned long)btrfs_device_fsid(dev_item),
+ BTRFS_UUID_SIZE);
read_extent_buffer(buf, &super.dev_item, (unsigned long)dev_item,
sizeof(*dev_item));
@@ -456,6 +460,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
device->io_align = io_align;
device->sector_size = sectorsize;
device->fd = fd;
+ device->writeable = 1;
device->total_bytes = block_count;
device->bytes_used = 0;
device->total_ios = 0;
@@ -489,6 +494,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
kfree(buf);
list_add(&device->dev_list, &root->fs_info->fs_devices->devices);
+ device->fs_devices = root->fs_info->fs_devices;
ret = btrfs_bootstrap_super_map(&root->fs_info->mapping_tree,
root->fs_info->fs_devices);
BUG_ON(ret);
diff --git a/volumes.c b/volumes.c
index c94f73c1..249cdc72 100644
--- a/volumes.c
+++ b/volumes.c
@@ -91,7 +91,7 @@ static int device_list_add(const char *path,
fs_devices = find_fsid(disk_super->fsid);
if (!fs_devices) {
- fs_devices = kmalloc(sizeof(*fs_devices), GFP_NOFS);
+ fs_devices = kzalloc(sizeof(*fs_devices), GFP_NOFS);
if (!fs_devices)
return -ENOMEM;
INIT_LIST_HEAD(&fs_devices->devices);
@@ -127,6 +127,7 @@ static int device_list_add(const char *path,
device->bytes_used =
btrfs_stack_device_bytes_used(&disk_super->dev_item);
list_add(&device->dev_list, &fs_devices->devices);
+ device->fs_devices = fs_devices;
}
if (found_transid > fs_devices->latest_trans) {
@@ -142,15 +143,24 @@ static int device_list_add(const char *path,
int btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
{
- struct list_head *head = &fs_devices->devices;
+ struct btrfs_fs_devices *seed_devices;
struct list_head *cur;
struct btrfs_device *device;
-
- list_for_each(cur, head) {
+again:
+ list_for_each(cur, &fs_devices->devices) {
device = list_entry(cur, struct btrfs_device, dev_list);
close(device->fd);
device->fd = -1;
+ device->writeable = 0;
+ }
+
+ seed_devices = fs_devices->seed;
+ fs_devices->seed = NULL;
+ if (seed_devices) {
+ fs_devices = seed_devices;
+ goto again;
}
+
return 0;
}
@@ -176,6 +186,8 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, int flags)
if (device->devid == fs_devices->lowest_devid)
fs_devices->lowest_bdev = fd;
device->fd = fd;
+ if (flags == O_RDWR)
+ device->writeable = 1;
}
return 0;
fail:
@@ -504,6 +516,7 @@ int btrfs_add_device(struct btrfs_trans_handle *trans,
device->devid = free_devid;
btrfs_set_device_id(leaf, dev_item, device->devid);
+ btrfs_set_device_generation(leaf, dev_item, 0);
btrfs_set_device_type(leaf, dev_item, device->type);
btrfs_set_device_io_align(leaf, dev_item, device->io_align);
btrfs_set_device_io_width(leaf, dev_item, device->io_width);
@@ -516,6 +529,8 @@ int btrfs_add_device(struct btrfs_trans_handle *trans,
ptr = (unsigned long)btrfs_device_uuid(dev_item);
write_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE);
+ ptr = (unsigned long)btrfs_device_fsid(dev_item);
+ write_extent_buffer(leaf, root->fs_info->fsid, ptr, BTRFS_UUID_SIZE);
btrfs_mark_buffer_dirty(leaf);
ret = 0;
@@ -996,11 +1011,23 @@ out:
}
struct btrfs_device *btrfs_find_device(struct btrfs_root *root, u64 devid,
- u8 *uuid)
+ u8 *uuid, u8 *fsid)
{
- struct list_head *head = &root->fs_info->fs_devices->devices;
-
- return __find_device(head, devid, uuid);
+ struct btrfs_device *device;
+ struct btrfs_fs_devices *cur_devices;
+
+ cur_devices = root->fs_info->fs_devices;
+ while (cur_devices) {
+ if (!fsid ||
+ !memcmp(cur_devices->fsid, fsid, BTRFS_UUID_SIZE)) {
+ device = __find_device(&cur_devices->devices,
+ devid, uuid);
+ if (device)
+ return device;
+ }
+ cur_devices = cur_devices->seed;
+ }
+ return NULL;
}
int btrfs_bootstrap_super_map(struct btrfs_mapping_tree *map_tree,
@@ -1056,6 +1083,28 @@ int btrfs_bootstrap_super_map(struct btrfs_mapping_tree *map_tree,
return 0;
}
+int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset)
+{
+ struct cache_extent *ce;
+ struct map_lookup *map;
+ struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree;
+ int readonly = 0;
+ int i;
+
+ ce = find_first_cache_extent(&map_tree->cache_tree, chunk_offset);
+ BUG_ON(!ce);
+
+ map = container_of(ce, struct map_lookup, ce);
+ for (i = 0; i < map->num_stripes; i++) {
+ if (!map->stripes[i].dev->writeable) {
+ readonly = 1;
+ break;
+ }
+ }
+
+ return readonly;
+}
+
static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
struct extent_buffer *leaf,
struct btrfs_chunk *chunk)
@@ -1111,7 +1160,8 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
read_extent_buffer(leaf, uuid, (unsigned long)
btrfs_stripe_dev_uuid_nr(chunk, i),
BTRFS_UUID_SIZE);
- map->stripes[i].dev = btrfs_find_device(root, devid, uuid);
+ map->stripes[i].dev = btrfs_find_device(root, devid, uuid,
+ NULL);
if (!map->stripes[i].dev) {
kfree(map);
return -EIO;
@@ -1144,6 +1194,36 @@ static int fill_device_from_item(struct extent_buffer *leaf,
return 0;
}
+static int open_seed_devices(struct btrfs_root *root, u8 *fsid)
+{
+ struct btrfs_fs_devices *fs_devices;
+ int ret;
+
+ fs_devices = root->fs_info->fs_devices->seed;
+ while (fs_devices) {
+ if (!memcmp(fs_devices->fsid, fsid, BTRFS_UUID_SIZE)) {
+ ret = 0;
+ goto out;
+ }
+ fs_devices = fs_devices->seed;
+ }
+
+ fs_devices = find_fsid(fsid);
+ if (!fs_devices) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ ret = btrfs_open_devices(fs_devices, O_RDONLY);
+ if (ret)
+ goto out;
+
+ fs_devices->seed = root->fs_info->fs_devices->seed;
+ root->fs_info->fs_devices->seed = fs_devices;
+out:
+ return ret;
+}
+
static int read_one_dev(struct btrfs_root *root,
struct extent_buffer *leaf,
struct btrfs_dev_item *dev_item)
@@ -1151,13 +1231,24 @@ static int read_one_dev(struct btrfs_root *root,
struct btrfs_device *device;
u64 devid;
int ret = 0;
+ u8 fs_uuid[BTRFS_UUID_SIZE];
u8 dev_uuid[BTRFS_UUID_SIZE];
devid = btrfs_device_id(leaf, dev_item);
read_extent_buffer(leaf, dev_uuid,
(unsigned long)btrfs_device_uuid(dev_item),
BTRFS_UUID_SIZE);
- device = btrfs_find_device(root, devid, dev_uuid);
+ read_extent_buffer(leaf, fs_uuid,
+ (unsigned long)btrfs_device_fsid(dev_item),
+ BTRFS_UUID_SIZE);
+
+ if (memcmp(fs_uuid, root->fs_info->fsid, BTRFS_UUID_SIZE)) {
+ ret = open_seed_devices(root, fs_uuid);
+ if (ret)
+ return ret;
+ }
+
+ device = btrfs_find_device(root, devid, dev_uuid, fs_uuid);
if (!device) {
printk("warning devid %llu not found already\n",
(unsigned long long)devid);
@@ -1284,6 +1375,7 @@ again:
struct btrfs_chunk *chunk;
chunk = btrfs_item_ptr(leaf, slot, struct btrfs_chunk);
ret = read_one_chunk(root, &found_key, leaf, chunk);
+ BUG_ON(ret);
}
path->slots[0]++;
}
diff --git a/volumes.h b/volumes.h
index 7ccd0eed..ad8cfaf3 100644
--- a/volumes.h
+++ b/volumes.h
@@ -21,11 +21,14 @@
struct btrfs_device {
struct list_head dev_list;
struct btrfs_root *dev_root;
+ struct btrfs_fs_devices *fs_devices;
u64 total_ios;
int fd;
+ int writeable;
+
char *name;
/* these are read off the super block, only in the progs */
@@ -69,6 +72,9 @@ struct btrfs_fs_devices {
int lowest_bdev;
struct list_head devices;
struct list_head list;
+
+ int seeding;
+ struct btrfs_fs_devices *seed;
};
struct btrfs_bio_stripe {
@@ -120,4 +126,5 @@ struct list_head *btrfs_scanned_uuids(void);
int btrfs_add_system_chunk(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_key *key,
struct btrfs_chunk *chunk, int item_size);
+int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset);
#endif