/* * this is used to update the root pointer in the tree of tree roots. * * But, in the case of the extent allocation tree, updating the root * pointer may allocate blocks which may change the root of the extent * allocation tree. * * So, this loops and repeats and makes sure the cowonly root didn't * change while the root pointer was being updated in the metadata. */ static int update_cowonly_root(struct btrfs_trans_handle *trans, struct btrfs_root *root) { int ret; u64 old_root_bytenr; struct btrfs_root *tree_root = root->fs_info->tree_root; btrfs_write_dirty_block_groups(trans, root); while (1) { old_root_bytenr = btrfs_root_bytenr(&root->root_item); if (old_root_bytenr == root->node->start) break; btrfs_set_root_node(&root->root_item, root->node); ret = btrfs_update_root(trans, tree_root, &root->root_key, &root->root_item); BUG_ON(ret); ret = btrfs_write_dirty_block_groups(trans, root); BUG_ON(ret); } if (root != root->fs_info->extent_root) switch_commit_root(root); return 0; }
/* * new snapshots need to be created at a very specific time in the * transaction commit. This does the actual creation */ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, struct btrfs_pending_snapshot *pending) { struct btrfs_key key; struct btrfs_root_item *new_root_item; struct btrfs_root *tree_root = fs_info->tree_root; struct btrfs_root *root = pending->root; struct extent_buffer *tmp; struct extent_buffer *old; int ret; u64 objectid; new_root_item = kmalloc(sizeof(*new_root_item), GFP_NOFS); if (!new_root_item) { ret = -ENOMEM; goto fail; } ret = btrfs_find_free_objectid(trans, tree_root, 0, &objectid); if (ret) goto fail; record_root_in_trans(trans, root); btrfs_set_root_last_snapshot(&root->root_item, trans->transid); memcpy(new_root_item, &root->root_item, sizeof(*new_root_item)); key.objectid = objectid; /* record when the snapshot was created in key.offset */ key.offset = trans->transid; btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); old = btrfs_lock_root_node(root); btrfs_cow_block(trans, root, old, NULL, 0, &old); btrfs_set_lock_blocking(old); btrfs_copy_root(trans, root, old, &tmp, objectid); btrfs_tree_unlock(old); free_extent_buffer(old); btrfs_set_root_node(new_root_item, tmp); ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key, new_root_item); btrfs_tree_unlock(tmp); free_extent_buffer(tmp); if (ret) goto fail; key.offset = (u64)-1; memcpy(&pending->root_key, &key, sizeof(key)); fail: kfree(new_root_item); btrfs_unreserve_metadata_space(root, 6); return ret; }
static noinline int commit_fs_roots(struct btrfs_trans_handle *trans, struct btrfs_root *root) { struct btrfs_root *gang[8]; struct btrfs_fs_info *fs_info = root->fs_info; int i; int ret; int err = 0; while (1) { ret = radix_tree_gang_lookup_tag(&fs_info->fs_roots_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(&fs_info->fs_roots_radix, (unsigned long)root->root_key.objectid, BTRFS_ROOT_TRANS_TAG); btrfs_free_log(trans, root); btrfs_update_reloc_root(trans, root); btrfs_orphan_commit_root(trans, root); if (root->commit_root != root->node) { switch_commit_root(root); btrfs_set_root_node(&root->root_item, root->node); } err = btrfs_update_root(trans, fs_info->tree_root, &root->root_key, &root->root_item); if (err) break; } } return err; }
static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, struct btrfs_pending_snapshot *pending) { struct btrfs_key key; struct btrfs_root_item *new_root_item; struct btrfs_root *tree_root = fs_info->tree_root; struct btrfs_root *root = pending->root; struct btrfs_root *parent_root; struct inode *parent_inode; struct dentry *dentry; struct extent_buffer *tmp; struct extent_buffer *old; int ret; int retries = 0; u64 to_reserve = 0; u64 index = 0; u64 objectid; new_root_item = kmalloc(sizeof(*new_root_item), GFP_NOFS); if (!new_root_item) { pending->error = -ENOMEM; goto fail; } ret = btrfs_find_free_objectid(trans, tree_root, 0, &objectid); if (ret) { pending->error = ret; goto fail; } btrfs_reloc_pre_snapshot(trans, pending, &to_reserve); btrfs_orphan_pre_snapshot(trans, pending, &to_reserve); if (to_reserve > 0) { ret = btrfs_block_rsv_add(trans, root, &pending->block_rsv, to_reserve, &retries); if (ret) { pending->error = ret; goto fail; } } key.objectid = objectid; key.offset = (u64)-1; key.type = BTRFS_ROOT_ITEM_KEY; trans->block_rsv = &pending->block_rsv; dentry = pending->dentry; parent_inode = dentry->d_parent->d_inode; parent_root = BTRFS_I(parent_inode)->root; record_root_in_trans(trans, parent_root); /* * insert the directory item */ ret = btrfs_set_inode_index(parent_inode, &index); BUG_ON(ret); ret = btrfs_insert_dir_item(trans, parent_root, dentry->d_name.name, dentry->d_name.len, parent_inode->i_ino, &key, BTRFS_FT_DIR, index); BUG_ON(ret); btrfs_i_size_write(parent_inode, parent_inode->i_size + dentry->d_name.len * 2); ret = btrfs_update_inode(trans, parent_root, parent_inode); BUG_ON(ret); record_root_in_trans(trans, root); btrfs_set_root_last_snapshot(&root->root_item, trans->transid); memcpy(new_root_item, &root->root_item, sizeof(*new_root_item)); old = btrfs_lock_root_node(root); btrfs_cow_block(trans, root, old, NULL, 0, &old); btrfs_set_lock_blocking(old); btrfs_copy_root(trans, root, old, &tmp, objectid); btrfs_tree_unlock(old); free_extent_buffer(old); btrfs_set_root_node(new_root_item, tmp); /* record when the snapshot was created in key.offset */ key.offset = trans->transid; ret = btrfs_insert_root(trans, tree_root, &key, new_root_item); btrfs_tree_unlock(tmp); free_extent_buffer(tmp); BUG_ON(ret); /* * insert root back/forward references */ ret = btrfs_add_root_ref(trans, tree_root, objectid, parent_root->root_key.objectid, parent_inode->i_ino, index, dentry->d_name.name, dentry->d_name.len); BUG_ON(ret); key.offset = (u64)-1; pending->snap = btrfs_read_fs_root_no_name(root->fs_info, &key); BUG_ON(IS_ERR(pending->snap)); btrfs_reloc_post_snapshot(trans, pending); btrfs_orphan_post_snapshot(trans, pending); fail: kfree(new_root_item); btrfs_block_rsv_release(root, &pending->block_rsv, (u64)-1); return 0; }