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_bytenr(&root->root_item, root->node->start); btrfs_set_root_generation(&root->root_item, trans->transid); root->root_item.level = btrfs_header_level(root->node); ret = btrfs_update_root(trans, tree_root, &root->root_key, &root->root_item); BUG_ON(ret); btrfs_write_dirty_block_groups(trans, root); } return 0; }
/* * 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; }
int btrfs_commit_transaction(struct btrfs_trans_handle *trans, struct btrfs_root *root) { u64 transid = trans->transid; int ret = 0; struct btrfs_fs_info *fs_info = root->fs_info; if (trans->fs_info->transaction_aborted) return -EROFS; /* * Flush all accumulated delayed refs so that root-tree updates are * consistent */ ret = btrfs_run_delayed_refs(trans, -1); if (ret < 0) goto error; if (root->commit_root == root->node) goto commit_tree; if (root == root->fs_info->tree_root) goto commit_tree; if (root == root->fs_info->chunk_root) goto commit_tree; free_extent_buffer(root->commit_root); root->commit_root = NULL; btrfs_set_root_bytenr(&root->root_item, root->node->start); btrfs_set_root_generation(&root->root_item, trans->transid); root->root_item.level = btrfs_header_level(root->node); ret = btrfs_update_root(trans, root->fs_info->tree_root, &root->root_key, &root->root_item); if (ret < 0) goto error; commit_tree: ret = commit_tree_roots(trans, fs_info); if (ret < 0) goto error; /* * Ensure that all committed roots are properly accounted in the * extent tree */ ret = btrfs_run_delayed_refs(trans, -1); if (ret < 0) goto error; btrfs_write_dirty_block_groups(trans); __commit_transaction(trans, root); if (ret < 0) goto error; ret = write_ctree_super(trans); btrfs_finish_extent_commit(trans); kfree(trans); free_extent_buffer(root->commit_root); root->commit_root = NULL; fs_info->running_transaction = NULL; fs_info->last_trans_committed = transid; return ret; error: btrfs_destroy_delayed_refs(trans); free(trans); return ret; }