diff options
Diffstat (limited to 'fs/btrfs/transaction.c')
| -rw-r--r-- | fs/btrfs/transaction.c | 190 | 
1 files changed, 155 insertions, 35 deletions
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index fb5cd5a4adb..5a4999aa8fe 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -31,7 +31,7 @@  #define BTRFS_ROOT_TRANS_TAG 0 -static noinline void put_transaction(struct btrfs_transaction *transaction) +void put_transaction(struct btrfs_transaction *transaction)  {  	WARN_ON(atomic_read(&transaction->use_count) == 0);  	if (atomic_dec_and_test(&transaction->use_count)) { @@ -58,6 +58,12 @@ static noinline int join_transaction(struct btrfs_root *root, int nofail)  	spin_lock(&root->fs_info->trans_lock);  loop: +	/* The file system has been taken offline. No new transactions. */ +	if (root->fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) { +		spin_unlock(&root->fs_info->trans_lock); +		return -EROFS; +	} +  	if (root->fs_info->trans_no_join) {  		if (!nofail) {  			spin_unlock(&root->fs_info->trans_lock); @@ -67,6 +73,8 @@ loop:  	cur_trans = root->fs_info->running_transaction;  	if (cur_trans) { +		if (cur_trans->aborted) +			return cur_trans->aborted;  		atomic_inc(&cur_trans->use_count);  		atomic_inc(&cur_trans->num_writers);  		cur_trans->num_joined++; @@ -123,6 +131,7 @@ loop:  	root->fs_info->generation++;  	cur_trans->transid = root->fs_info->generation;  	root->fs_info->running_transaction = cur_trans; +	cur_trans->aborted = 0;  	spin_unlock(&root->fs_info->trans_lock);  	return 0; @@ -318,6 +327,7 @@ again:  	h->use_count = 1;  	h->block_rsv = NULL;  	h->orig_rsv = NULL; +	h->aborted = 0;  	smp_mb();  	if (cur_trans->blocked && may_wait_transaction(root, type)) { @@ -440,6 +450,7 @@ int btrfs_should_end_transaction(struct btrfs_trans_handle *trans,  	struct btrfs_transaction *cur_trans = trans->transaction;  	struct btrfs_block_rsv *rsv = trans->block_rsv;  	int updates; +	int err;  	smp_mb();  	if (cur_trans->blocked || cur_trans->delayed_refs.flushing) @@ -453,8 +464,11 @@ int btrfs_should_end_transaction(struct btrfs_trans_handle *trans,  	updates = trans->delayed_ref_updates;  	trans->delayed_ref_updates = 0; -	if (updates) -		btrfs_run_delayed_refs(trans, root, updates); +	if (updates) { +		err = btrfs_run_delayed_refs(trans, root, updates); +		if (err) /* Error code will also eval true */ +			return err; +	}  	trans->block_rsv = rsv; @@ -525,6 +539,11 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,  	if (throttle)  		btrfs_run_delayed_iputs(root); +	if (trans->aborted || +	    root->fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) { +		return -EIO; +	} +  	return 0;  } @@ -690,11 +709,13 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,  		ret = btrfs_update_root(trans, tree_root,  					&root->root_key,  					&root->root_item); -		BUG_ON(ret); +		if (ret) +			return ret;  		old_root_used = btrfs_root_used(&root->root_item);  		ret = btrfs_write_dirty_block_groups(trans, root); -		BUG_ON(ret); +		if (ret) +			return ret;  	}  	if (root != root->fs_info->extent_root) @@ -705,6 +726,10 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,  /*   * update all the cowonly tree roots on disk + * + * The error handling in this function may not be obvious. Any of the + * failures will cause the file system to go offline. We still need + * to clean up the delayed refs.   */  static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,  					 struct btrfs_root *root) @@ -715,22 +740,30 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,  	int ret;  	ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1); -	BUG_ON(ret); +	if (ret) +		return ret;  	eb = btrfs_lock_root_node(fs_info->tree_root); -	btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb); +	ret = btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, +			      0, &eb);  	btrfs_tree_unlock(eb);  	free_extent_buffer(eb); +	if (ret) +		return ret; +  	ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1); -	BUG_ON(ret); +	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); -		update_cowonly_root(trans, root); +		ret = update_cowonly_root(trans, root); +		if (ret) +			return ret;  	}  	down_write(&fs_info->extent_commit_sem); @@ -874,7 +907,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,  	new_root_item = kmalloc(sizeof(*new_root_item), GFP_NOFS);  	if (!new_root_item) { -		pending->error = -ENOMEM; +		ret = pending->error = -ENOMEM;  		goto fail;  	} @@ -911,7 +944,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,  	 * insert the directory item  	 */  	ret = btrfs_set_inode_index(parent_inode, &index); -	BUG_ON(ret); +	BUG_ON(ret); /* -ENOMEM */  	ret = btrfs_insert_dir_item(trans, parent_root,  				dentry->d_name.name, dentry->d_name.len,  				parent_inode, &key, @@ -920,12 +953,14 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,  		pending->error = -EEXIST;  		dput(parent);  		goto fail; -	} +	} else if (ret) +		goto abort_trans;  	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); +	if (ret) +		goto abort_trans;  	/*  	 * pull in the delayed directory update @@ -934,7 +969,8 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,  	 * snapshot  	 */  	ret = btrfs_run_delayed_items(trans, root); -	BUG_ON(ret); +	if (ret) /* Transaction aborted */ +		goto fail;  	record_root_in_trans(trans, root);  	btrfs_set_root_last_snapshot(&root->root_item, trans->transid); @@ -949,10 +985,16 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,  	btrfs_set_root_flags(new_root_item, root_flags);  	old = btrfs_lock_root_node(root); -	btrfs_cow_block(trans, root, old, NULL, 0, &old); +	ret = btrfs_cow_block(trans, root, old, NULL, 0, &old); +	if (ret) +		goto abort_trans; +  	btrfs_set_lock_blocking(old); -	btrfs_copy_root(trans, root, old, &tmp, objectid); +	ret = btrfs_copy_root(trans, root, old, &tmp, objectid); +	if (ret) +		goto abort_trans; +  	btrfs_tree_unlock(old);  	free_extent_buffer(old); @@ -966,7 +1008,8 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,  	ret = btrfs_insert_root(trans, tree_root, &key, new_root_item);  	btrfs_tree_unlock(tmp);  	free_extent_buffer(tmp); -	BUG_ON(ret); +	if (ret) +		goto abort_trans;  	/*  	 * insert root back/forward references @@ -975,19 +1018,28 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,  				 parent_root->root_key.objectid,  				 btrfs_ino(parent_inode), index,  				 dentry->d_name.name, dentry->d_name.len); -	BUG_ON(ret); +	if (ret) +		goto fail;  	dput(parent);  	key.offset = (u64)-1;  	pending->snap = btrfs_read_fs_root_no_name(root->fs_info, &key); -	BUG_ON(IS_ERR(pending->snap)); +	if (IS_ERR(pending->snap)) +		goto abort_trans; -	btrfs_reloc_post_snapshot(trans, pending); +	ret = btrfs_reloc_post_snapshot(trans, pending); +	if (ret) +		goto abort_trans; +	ret = 0;  fail:  	kfree(new_root_item);  	trans->block_rsv = rsv;  	btrfs_block_rsv_release(root, &pending->block_rsv, (u64)-1); -	return 0; +	return ret; + +abort_trans: +	btrfs_abort_transaction(trans, root, ret); +	goto fail;  }  /* @@ -1124,6 +1176,33 @@ int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans,  	return 0;  } + +static void cleanup_transaction(struct btrfs_trans_handle *trans, +				struct btrfs_root *root) +{ +	struct btrfs_transaction *cur_trans = trans->transaction; + +	WARN_ON(trans->use_count > 1); + +	spin_lock(&root->fs_info->trans_lock); +	list_del_init(&cur_trans->list); +	spin_unlock(&root->fs_info->trans_lock); + +	btrfs_cleanup_one_transaction(trans->transaction, root); + +	put_transaction(cur_trans); +	put_transaction(cur_trans); + +	trace_btrfs_transaction_commit(root); + +	btrfs_scrub_continue(root); + +	if (current->journal_info == trans) +		current->journal_info = NULL; + +	kmem_cache_free(btrfs_trans_handle_cachep, trans); +} +  /*   * btrfs_transaction state sequence:   *    in_commit = 0, blocked = 0  (initial) @@ -1135,10 +1214,10 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,  			     struct btrfs_root *root)  {  	unsigned long joined = 0; -	struct btrfs_transaction *cur_trans; +	struct btrfs_transaction *cur_trans = trans->transaction;  	struct btrfs_transaction *prev_trans = NULL;  	DEFINE_WAIT(wait); -	int ret; +	int ret = -EIO;  	int should_grow = 0;  	unsigned long now = get_seconds();  	int flush_on_commit = btrfs_test_opt(root, FLUSHONCOMMIT); @@ -1148,13 +1227,18 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,  	btrfs_trans_release_metadata(trans, root);  	trans->block_rsv = NULL; +	if (cur_trans->aborted) +		goto cleanup_transaction; +  	/* make a pass through all the delayed refs we have so far  	 * any runnings procs may add more while we are here  	 */  	ret = btrfs_run_delayed_refs(trans, root, 0); -	BUG_ON(ret); +	if (ret) +		goto cleanup_transaction;  	cur_trans = trans->transaction; +  	/*  	 * set the flushing flag so procs in this transaction have to  	 * start sending their work down. @@ -1162,19 +1246,20 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,  	cur_trans->delayed_refs.flushing = 1;  	ret = btrfs_run_delayed_refs(trans, root, 0); -	BUG_ON(ret); +	if (ret) +		goto cleanup_transaction;  	spin_lock(&cur_trans->commit_lock);  	if (cur_trans->in_commit) {  		spin_unlock(&cur_trans->commit_lock);  		atomic_inc(&cur_trans->use_count); -		btrfs_end_transaction(trans, root); +		ret = btrfs_end_transaction(trans, root);  		wait_for_commit(root, cur_trans);  		put_transaction(cur_trans); -		return 0; +		return ret;  	}  	trans->transaction->in_commit = 1; @@ -1218,7 +1303,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,  		}  		ret = btrfs_run_delayed_items(trans, root); -		BUG_ON(ret); +		if (ret) +			goto cleanup_transaction;  		/*  		 * rename don't use btrfs_join_transaction, so, once we @@ -1260,13 +1346,22 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,  	mutex_lock(&root->fs_info->reloc_mutex);  	ret = btrfs_run_delayed_items(trans, root); -	BUG_ON(ret); +	if (ret) { +		mutex_unlock(&root->fs_info->reloc_mutex); +		goto cleanup_transaction; +	}  	ret = create_pending_snapshots(trans, root->fs_info); -	BUG_ON(ret); +	if (ret) { +		mutex_unlock(&root->fs_info->reloc_mutex); +		goto cleanup_transaction; +	}  	ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1); -	BUG_ON(ret); +	if (ret) { +		mutex_unlock(&root->fs_info->reloc_mutex); +		goto cleanup_transaction; +	}  	/*  	 * make sure none of the code above managed to slip in a @@ -1293,7 +1388,10 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,  	mutex_lock(&root->fs_info->tree_log_mutex);  	ret = commit_fs_roots(trans, root); -	BUG_ON(ret); +	if (ret) { +		mutex_unlock(&root->fs_info->tree_log_mutex); +		goto cleanup_transaction; +	}  	/* commit_fs_roots gets rid of all the tree log roots, it is now  	 * safe to free the root of tree log roots @@ -1301,7 +1399,10 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,  	btrfs_free_log_root_tree(trans, root->fs_info);  	ret = commit_cowonly_roots(trans, root); -	BUG_ON(ret); +	if (ret) { +		mutex_unlock(&root->fs_info->tree_log_mutex); +		goto cleanup_transaction; +	}  	btrfs_prepare_extent_commit(trans, root); @@ -1335,8 +1436,18 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,  	wake_up(&root->fs_info->transaction_wait);  	ret = btrfs_write_and_wait_transaction(trans, root); -	BUG_ON(ret); -	write_ctree_super(trans, root, 0); +	if (ret) { +		btrfs_error(root->fs_info, ret, +			    "Error while writing out transaction."); +		mutex_unlock(&root->fs_info->tree_log_mutex); +		goto cleanup_transaction; +	} + +	ret = write_ctree_super(trans, root, 0); +	if (ret) { +		mutex_unlock(&root->fs_info->tree_log_mutex); +		goto cleanup_transaction; +	}  	/*  	 * the super is written, we can safely allow the tree-loggers @@ -1372,6 +1483,15 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,  		btrfs_run_delayed_iputs(root);  	return ret; + +cleanup_transaction: +	btrfs_printk(root->fs_info, "Skipping commit of aborted transaction.\n"); +//	WARN_ON(1); +	if (current->journal_info == trans) +		current->journal_info = NULL; +	cleanup_transaction(trans, root); + +	return ret;  }  /*  |