/* * 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); ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1); BUG_ON(ret); 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_level(&root->root_item, btrfs_header_level(root->node)); btrfs_set_root_generation(&root->root_item, trans->transid); ret = btrfs_update_root(trans, tree_root, &root->root_key, &root->root_item); BUG_ON(ret); btrfs_write_dirty_block_groups(trans, root); ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1); BUG_ON(ret); } return 0; }
/* * update all the cowonly tree roots on disk */ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans, struct btrfs_root *root) { struct btrfs_fs_info *fs_info = root->fs_info; struct list_head *next; struct extent_buffer *eb; int ret; ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1); BUG_ON(ret); eb = btrfs_lock_root_node(fs_info->tree_root); btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb); btrfs_tree_unlock(eb); free_extent_buffer(eb); ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1); BUG_ON(ret); while (!list_empty(&fs_info->dirty_cowonly_roots)) { next = fs_info->dirty_cowonly_roots.next; list_del_init(next); root = list_entry(next, struct btrfs_root, dirty_list); update_cowonly_root(trans, root); } down_write(&fs_info->extent_commit_sem); switch_commit_root(fs_info->extent_root); up_write(&fs_info->extent_commit_sem); return 0; }
static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, struct btrfs_root *root, int throttle) { struct btrfs_transaction *cur_trans = trans->transaction; struct btrfs_fs_info *info = root->fs_info; int count = 0; while (count < 4) { unsigned long cur = trans->delayed_ref_updates; trans->delayed_ref_updates = 0; if (cur && trans->transaction->delayed_refs.num_heads_ready > 64) { trans->delayed_ref_updates = 0; /* * do a full flush if the transaction is trying * to close */ if (trans->transaction->delayed_refs.flushing) cur = 0; btrfs_run_delayed_refs(trans, root, cur); } else { break; } count++; } btrfs_trans_release_metadata(trans, root); if (!root->fs_info->open_ioctl_trans && should_end_transaction(trans, root)) trans->transaction->blocked = 1; if (cur_trans->blocked && !cur_trans->in_commit) { if (throttle) return btrfs_commit_transaction(trans, root); else wake_up_process(info->transaction_kthread); } mutex_lock(&info->trans_mutex); WARN_ON(cur_trans != info->running_transaction); WARN_ON(cur_trans->num_writers < 1); cur_trans->num_writers--; if (waitqueue_active(&cur_trans->writer_wait)) wake_up(&cur_trans->writer_wait); put_transaction(cur_trans); mutex_unlock(&info->trans_mutex); if (current->journal_info == trans) current->journal_info = NULL; memset(trans, 0, sizeof(*trans)); kmem_cache_free(btrfs_trans_handle_cachep, trans); if (throttle) btrfs_run_delayed_iputs(root); return 0; }
int btrfs_should_end_transaction(struct btrfs_trans_handle *trans, struct btrfs_root *root) { struct btrfs_transaction *cur_trans = trans->transaction; int updates; if (cur_trans->blocked || cur_trans->delayed_refs.flushing) return 1; updates = trans->delayed_ref_updates; trans->delayed_ref_updates = 0; if (updates) btrfs_run_delayed_refs(trans, root, updates); return should_end_transaction(trans, root); }
static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, struct btrfs_root *root, int throttle) { struct btrfs_transaction *cur_trans; struct btrfs_fs_info *info = root->fs_info; int count = 0; while (count < 4) { unsigned long cur = trans->delayed_ref_updates; trans->delayed_ref_updates = 0; if (cur && trans->transaction->delayed_refs.num_heads_ready > 64) { trans->delayed_ref_updates = 0; /* * do a full flush if the transaction is trying * to close */ if (trans->transaction->delayed_refs.flushing) cur = 0; btrfs_run_delayed_refs(trans, root, cur); } else { break; } count++; } mutex_lock(&info->trans_mutex); cur_trans = info->running_transaction; WARN_ON(cur_trans != trans->transaction); WARN_ON(cur_trans->num_writers < 1); cur_trans->num_writers--; if (waitqueue_active(&cur_trans->writer_wait)) wake_up(&cur_trans->writer_wait); put_transaction(cur_trans); mutex_unlock(&info->trans_mutex); if (current->journal_info == trans) current->journal_info = NULL; memset(trans, 0, sizeof(*trans)); kmem_cache_free(btrfs_trans_handle_cachep, trans); return 0; }
int commit_tree_roots(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info) { struct btrfs_root *root; struct list_head *next; struct extent_buffer *eb; int ret; if (fs_info->readonly) return 0; eb = fs_info->tree_root->node; extent_buffer_get(eb); ret = btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb); free_extent_buffer(eb); if (ret) return ret; /* * If the above CoW is the first one to dirty the current tree_root, * delayed refs for it won't be run until after this function has * finished executing, meaning we won't process the extent tree root, * which will have been added to ->dirty_cowonly_roots. So run * delayed refs here as well. */ ret = btrfs_run_delayed_refs(trans, -1); if (ret) return ret; while(!list_empty(&fs_info->dirty_cowonly_roots)) { next = fs_info->dirty_cowonly_roots.next; list_del_init(next); root = list_entry(next, struct btrfs_root, dirty_list); ret = update_cowonly_root(trans, root); free_extent_buffer(root->commit_root); root->commit_root = NULL; if (ret < 0) return ret; } 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; }