|
@@ -1007,6 +1007,123 @@ int btrfs_transaction_blocked(struct btrfs_fs_info *info)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * wait for the current transaction commit to start and block subsequent
|
|
|
+ * transaction joins
|
|
|
+ */
|
|
|
+static void wait_current_trans_commit_start(struct btrfs_root *root,
|
|
|
+ struct btrfs_transaction *trans)
|
|
|
+{
|
|
|
+ DEFINE_WAIT(wait);
|
|
|
+
|
|
|
+ if (trans->in_commit)
|
|
|
+ return;
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ prepare_to_wait(&root->fs_info->transaction_blocked_wait, &wait,
|
|
|
+ TASK_UNINTERRUPTIBLE);
|
|
|
+ if (trans->in_commit) {
|
|
|
+ finish_wait(&root->fs_info->transaction_blocked_wait,
|
|
|
+ &wait);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
+ schedule();
|
|
|
+ mutex_lock(&root->fs_info->trans_mutex);
|
|
|
+ finish_wait(&root->fs_info->transaction_blocked_wait, &wait);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * wait for the current transaction to start and then become unblocked.
|
|
|
+ * caller holds ref.
|
|
|
+ */
|
|
|
+static void wait_current_trans_commit_start_and_unblock(struct btrfs_root *root,
|
|
|
+ struct btrfs_transaction *trans)
|
|
|
+{
|
|
|
+ DEFINE_WAIT(wait);
|
|
|
+
|
|
|
+ if (trans->commit_done || (trans->in_commit && !trans->blocked))
|
|
|
+ return;
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ prepare_to_wait(&root->fs_info->transaction_wait, &wait,
|
|
|
+ TASK_UNINTERRUPTIBLE);
|
|
|
+ if (trans->commit_done ||
|
|
|
+ (trans->in_commit && !trans->blocked)) {
|
|
|
+ finish_wait(&root->fs_info->transaction_wait,
|
|
|
+ &wait);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
+ schedule();
|
|
|
+ mutex_lock(&root->fs_info->trans_mutex);
|
|
|
+ finish_wait(&root->fs_info->transaction_wait,
|
|
|
+ &wait);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * commit transactions asynchronously. once btrfs_commit_transaction_async
|
|
|
+ * returns, any subsequent transaction will not be allowed to join.
|
|
|
+ */
|
|
|
+struct btrfs_async_commit {
|
|
|
+ struct btrfs_trans_handle *newtrans;
|
|
|
+ struct btrfs_root *root;
|
|
|
+ struct delayed_work work;
|
|
|
+};
|
|
|
+
|
|
|
+static void do_async_commit(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct btrfs_async_commit *ac =
|
|
|
+ container_of(work, struct btrfs_async_commit, work.work);
|
|
|
+
|
|
|
+ btrfs_commit_transaction(ac->newtrans, ac->root);
|
|
|
+ kfree(ac);
|
|
|
+}
|
|
|
+
|
|
|
+int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans,
|
|
|
+ struct btrfs_root *root,
|
|
|
+ int wait_for_unblock)
|
|
|
+{
|
|
|
+ struct btrfs_async_commit *ac;
|
|
|
+ struct btrfs_transaction *cur_trans;
|
|
|
+
|
|
|
+ ac = kmalloc(sizeof(*ac), GFP_NOFS);
|
|
|
+ BUG_ON(!ac);
|
|
|
+
|
|
|
+ INIT_DELAYED_WORK(&ac->work, do_async_commit);
|
|
|
+ ac->root = root;
|
|
|
+ ac->newtrans = btrfs_join_transaction(root, 0);
|
|
|
+
|
|
|
+ /* take transaction reference */
|
|
|
+ mutex_lock(&root->fs_info->trans_mutex);
|
|
|
+ cur_trans = trans->transaction;
|
|
|
+ cur_trans->use_count++;
|
|
|
+ mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
+
|
|
|
+ btrfs_end_transaction(trans, root);
|
|
|
+ schedule_delayed_work(&ac->work, 0);
|
|
|
+
|
|
|
+ /* wait for transaction to start and unblock */
|
|
|
+ mutex_lock(&root->fs_info->trans_mutex);
|
|
|
+ if (wait_for_unblock)
|
|
|
+ wait_current_trans_commit_start_and_unblock(root, cur_trans);
|
|
|
+ else
|
|
|
+ wait_current_trans_commit_start(root, cur_trans);
|
|
|
+ put_transaction(cur_trans);
|
|
|
+ mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * btrfs_transaction state sequence:
|
|
|
+ * in_commit = 0, blocked = 0 (initial)
|
|
|
+ * in_commit = 1, blocked = 1
|
|
|
+ * blocked = 0
|
|
|
+ * commit_done = 1
|
|
|
+ */
|
|
|
int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|
|
struct btrfs_root *root)
|
|
|
{
|
|
@@ -1057,6 +1174,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|
|
|
|
|
trans->transaction->in_commit = 1;
|
|
|
trans->transaction->blocked = 1;
|
|
|
+ wake_up(&root->fs_info->transaction_blocked_wait);
|
|
|
+
|
|
|
if (cur_trans->list.prev != &root->fs_info->trans_list) {
|
|
|
prev_trans = list_entry(cur_trans->list.prev,
|
|
|
struct btrfs_transaction, list);
|