|
@@ -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;
|
|
|
}
|
|
|
|
|
|
/*
|