Btrfs: groundwork for subvolume and snapshot roots
Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 20d84bd..83a0194 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -8,6 +8,8 @@
extern struct kmem_cache *btrfs_trans_handle_cachep;
extern struct kmem_cache *btrfs_transaction_cachep;
+#define BTRFS_ROOT_TRANS_TAG 0
+
#define TRANS_MAGIC 0xE1E10E
static void put_transaction(struct btrfs_transaction *transaction)
{
@@ -31,9 +33,10 @@
GFP_NOFS);
total_trans++;
BUG_ON(!cur_trans);
+ root->fs_info->generation++;
root->fs_info->running_transaction = cur_trans;
cur_trans->num_writers = 0;
- cur_trans->transid = root->root_key.offset + 1;
+ cur_trans->transid = root->fs_info->generation;
init_waitqueue_head(&cur_trans->writer_wait);
init_waitqueue_head(&cur_trans->commit_wait);
cur_trans->magic = TRANS_MAGIC;
@@ -51,13 +54,22 @@
struct btrfs_trans_handle *h =
kmem_cache_alloc(btrfs_trans_handle_cachep, GFP_NOFS);
int ret;
+ u64 running_trans_id;
- /* FIXME, use the right root */
- root = root->fs_info->fs_root;
mutex_lock(&root->fs_info->trans_mutex);
ret = join_transaction(root);
BUG_ON(ret);
- h->transid = root->fs_info->running_transaction->transid;
+ running_trans_id = root->fs_info->running_transaction->transid;
+
+ if (root != root->fs_info->tree_root && root->last_trans <
+ running_trans_id) {
+ radix_tree_tag_set(&root->fs_info->fs_roots_radix,
+ (unsigned long)root, BTRFS_ROOT_TRANS_TAG);
+ root->commit_root = root->node;
+ get_bh(root->node);
+ }
+ root->last_trans = running_trans_id;
+ h->transid = running_trans_id;
h->transaction = root->fs_info->running_transaction;
h->blocks_reserved = num_blocks;
h->blocks_used = 0;
@@ -72,9 +84,6 @@
{
struct btrfs_transaction *cur_trans;
- /* FIXME, use the right root */
- root = root->fs_info->fs_root;
-
WARN_ON(trans->magic != TRANS_MAGIC);
WARN_ON(trans->magic2 != TRANS_MAGIC);
mutex_lock(&root->fs_info->trans_mutex);
@@ -145,17 +154,96 @@
return 0;
}
+struct dirty_root {
+ struct list_head list;
+ struct btrfs_key snap_key;
+ struct buffer_head *commit_root;
+ struct btrfs_root *root;
+};
+
+int add_dirty_roots(struct btrfs_trans_handle *trans,
+ struct radix_tree_root *radix, struct list_head *list)
+{
+ struct dirty_root *dirty;
+ struct btrfs_root *gang[8];
+ struct btrfs_root *root;
+ int i;
+ int ret;
+ int err;
+printk("add dirty\n");
+ while(1) {
+ ret = radix_tree_gang_lookup_tag(radix, (void **)gang, 0,
+ ARRAY_SIZE(gang),
+ BTRFS_ROOT_TRANS_TAG);
+ if (ret == 0)
+ break;
+ for (i = 0; i < ret; i++) {
+ root = gang[i];
+ radix_tree_tag_clear(radix, (unsigned long)root,
+ BTRFS_ROOT_TRANS_TAG);
+ if (root->commit_root == root->node) {
+ WARN_ON(root->node->b_blocknr !=
+ btrfs_root_blocknr(&root->root_item));
+ brelse(root->commit_root);
+ root->commit_root = NULL;
+ continue;
+ }
+ dirty = kmalloc(sizeof(*dirty), GFP_NOFS);
+ BUG_ON(!dirty);
+ memcpy(&dirty->snap_key, &root->root_key,
+ sizeof(root->root_key));
+ dirty->commit_root = root->commit_root;
+ root->commit_root = NULL;
+ dirty->root = root;
+printk("adding dirty root %Lu gen %Lu blocknr %Lu\n", root->root_key.objectid, root->root_key.offset, dirty->commit_root->b_blocknr);
+ root->root_key.offset = root->fs_info->generation;
+ btrfs_set_root_blocknr(&root->root_item,
+ root->node->b_blocknr);
+ err = btrfs_insert_root(trans, root->fs_info->tree_root,
+ &root->root_key,
+ &root->root_item);
+ BUG_ON(err);
+ list_add(&dirty->list, list);
+ }
+ }
+printk("add dirty done\n");
+ return 0;
+}
+
+int drop_dirty_roots(struct btrfs_root *tree_root, struct list_head *list)
+{
+ struct dirty_root *dirty;
+ struct btrfs_trans_handle *trans;
+ int ret;
+
+ while(!list_empty(list)) {
+ dirty = list_entry(list->next, struct dirty_root, list);
+ list_del_init(&dirty->list);
+ trans = btrfs_start_transaction(tree_root, 1);
+printk("drop snapshot root %p, commit_root blocknr %Lu generation %Lu\n", dirty->root, dirty->commit_root->b_blocknr, dirty->snap_key.offset);
+ ret = btrfs_drop_snapshot(trans, dirty->root,
+ dirty->commit_root);
+ BUG_ON(ret);
+
+printk("del root objectid %Lu, offset %Lu\n", dirty->snap_key.objectid, dirty->snap_key.offset);
+ ret = btrfs_del_root(trans, tree_root, &dirty->snap_key);
+ BUG_ON(ret);
+ ret = btrfs_end_transaction(trans, tree_root);
+ BUG_ON(ret);
+ kfree(dirty);
+ }
+ return 0;
+}
+
int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
int ret = 0;
- struct buffer_head *snap;
- struct btrfs_key snap_key;
struct btrfs_transaction *cur_trans;
+ struct list_head dirty_fs_roots;
DEFINE_WAIT(wait);
- /* FIXME, use the right root */
- root = root->fs_info->fs_root;
+ INIT_LIST_HEAD(&dirty_fs_roots);
mutex_lock(&root->fs_info->trans_mutex);
if (trans->transaction->in_commit) {
@@ -184,22 +272,13 @@
}
finish_wait(&trans->transaction->writer_wait, &wait);
WARN_ON(cur_trans != trans->transaction);
- if (root->node != root->commit_root) {
- memcpy(&snap_key, &root->root_key, sizeof(snap_key));
- root->root_key.offset++;
- }
-
- if (btrfs_root_blocknr(&root->root_item) != root->node->b_blocknr) {
- btrfs_set_root_blocknr(&root->root_item, root->node->b_blocknr);
- ret = btrfs_insert_root(trans, root->fs_info->tree_root,
- &root->root_key, &root->root_item);
- BUG_ON(ret);
- }
-
+ add_dirty_roots(trans, &root->fs_info->fs_roots_radix, &dirty_fs_roots);
ret = btrfs_commit_tree_roots(trans, root);
BUG_ON(ret);
cur_trans = root->fs_info->running_transaction;
root->fs_info->running_transaction = NULL;
+ btrfs_set_super_generation(root->fs_info->disk_super,
+ root->fs_info->generation + 1);
mutex_unlock(&root->fs_info->trans_mutex);
ret = btrfs_write_and_wait_transaction(trans, root);
BUG_ON(ret);
@@ -213,21 +292,8 @@
put_transaction(cur_trans);
mutex_unlock(&root->fs_info->trans_mutex);
kmem_cache_free(btrfs_trans_handle_cachep, trans);
- if (root->node != root->commit_root) {
- trans = btrfs_start_transaction(root, 1);
- snap = root->commit_root;
- root->commit_root = root->node;
- get_bh(root->node);
- ret = btrfs_drop_snapshot(trans, root, snap);
- BUG_ON(ret);
- ret = btrfs_del_root(trans, root->fs_info->tree_root,
- &snap_key);
- BUG_ON(ret);
- root->fs_info->generation = root->root_key.offset + 1;
- ret = btrfs_end_transaction(trans, root);
- BUG_ON(ret);
- }
+ drop_dirty_roots(root->fs_info->tree_root, &dirty_fs_roots);
return ret;
}