aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/btrfs/ctree.h1
-rw-r--r--fs/btrfs/disk-io.c1
-rw-r--r--fs/btrfs/transaction.c119
-rw-r--r--fs/btrfs/transaction.h3
4 files changed, 124 insertions, 0 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 88c0fb7e12d..e5d66b13c17 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -901,6 +901,7 @@ struct btrfs_fs_info {
901 struct btrfs_transaction *running_transaction; 901 struct btrfs_transaction *running_transaction;
902 wait_queue_head_t transaction_throttle; 902 wait_queue_head_t transaction_throttle;
903 wait_queue_head_t transaction_wait; 903 wait_queue_head_t transaction_wait;
904 wait_queue_head_t transaction_blocked_wait;
904 wait_queue_head_t async_submit_wait; 905 wait_queue_head_t async_submit_wait;
905 906
906 struct btrfs_super_block super_copy; 907 struct btrfs_super_block super_copy;
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index e163424c7fc..b40dfe48017 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1679,6 +1679,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
1679 1679
1680 init_waitqueue_head(&fs_info->transaction_throttle); 1680 init_waitqueue_head(&fs_info->transaction_throttle);
1681 init_waitqueue_head(&fs_info->transaction_wait); 1681 init_waitqueue_head(&fs_info->transaction_wait);
1682 init_waitqueue_head(&fs_info->transaction_blocked_wait);
1682 init_waitqueue_head(&fs_info->async_submit_wait); 1683 init_waitqueue_head(&fs_info->async_submit_wait);
1683 1684
1684 __setup_root(4096, 4096, 4096, 4096, tree_root, 1685 __setup_root(4096, 4096, 4096, 4096, tree_root,
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 700dc4b34ad..9f40bfc9c45 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -1007,6 +1007,123 @@ int btrfs_transaction_blocked(struct btrfs_fs_info *info)
1007 return ret; 1007 return ret;
1008} 1008}
1009 1009
1010/*
1011 * wait for the current transaction commit to start and block subsequent
1012 * transaction joins
1013 */
1014static void wait_current_trans_commit_start(struct btrfs_root *root,
1015 struct btrfs_transaction *trans)
1016{
1017 DEFINE_WAIT(wait);
1018
1019 if (trans->in_commit)
1020 return;
1021
1022 while (1) {
1023 prepare_to_wait(&root->fs_info->transaction_blocked_wait, &wait,
1024 TASK_UNINTERRUPTIBLE);
1025 if (trans->in_commit) {
1026 finish_wait(&root->fs_info->transaction_blocked_wait,
1027 &wait);
1028 break;
1029 }
1030 mutex_unlock(&root->fs_info->trans_mutex);
1031 schedule();
1032 mutex_lock(&root->fs_info->trans_mutex);
1033 finish_wait(&root->fs_info->transaction_blocked_wait, &wait);
1034 }
1035}
1036
1037/*
1038 * wait for the current transaction to start and then become unblocked.
1039 * caller holds ref.
1040 */
1041static void wait_current_trans_commit_start_and_unblock(struct btrfs_root *root,
1042 struct btrfs_transaction *trans)
1043{
1044 DEFINE_WAIT(wait);
1045
1046 if (trans->commit_done || (trans->in_commit && !trans->blocked))
1047 return;
1048
1049 while (1) {
1050 prepare_to_wait(&root->fs_info->transaction_wait, &wait,
1051 TASK_UNINTERRUPTIBLE);
1052 if (trans->commit_done ||
1053 (trans->in_commit && !trans->blocked)) {
1054 finish_wait(&root->fs_info->transaction_wait,
1055 &wait);
1056 break;
1057 }
1058 mutex_unlock(&root->fs_info->trans_mutex);
1059 schedule();
1060 mutex_lock(&root->fs_info->trans_mutex);
1061 finish_wait(&root->fs_info->transaction_wait,
1062 &wait);
1063 }
1064}
1065
1066/*
1067 * commit transactions asynchronously. once btrfs_commit_transaction_async
1068 * returns, any subsequent transaction will not be allowed to join.
1069 */
1070struct btrfs_async_commit {
1071 struct btrfs_trans_handle *newtrans;
1072 struct btrfs_root *root;
1073 struct delayed_work work;
1074};
1075
1076static void do_async_commit(struct work_struct *work)
1077{
1078 struct btrfs_async_commit *ac =
1079 container_of(work, struct btrfs_async_commit, work.work);
1080
1081 btrfs_commit_transaction(ac->newtrans, ac->root);
1082 kfree(ac);
1083}
1084
1085int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans,
1086 struct btrfs_root *root,
1087 int wait_for_unblock)
1088{
1089 struct btrfs_async_commit *ac;
1090 struct btrfs_transaction *cur_trans;
1091
1092 ac = kmalloc(sizeof(*ac), GFP_NOFS);
1093 BUG_ON(!ac);
1094
1095 INIT_DELAYED_WORK(&ac->work, do_async_commit);
1096 ac->root = root;
1097 ac->newtrans = btrfs_join_transaction(root, 0);
1098
1099 /* take transaction reference */
1100 mutex_lock(&root->fs_info->trans_mutex);
1101 cur_trans = trans->transaction;
1102 cur_trans->use_count++;
1103 mutex_unlock(&root->fs_info->trans_mutex);
1104
1105 btrfs_end_transaction(trans, root);
1106 schedule_delayed_work(&ac->work, 0);
1107
1108 /* wait for transaction to start and unblock */
1109 mutex_lock(&root->fs_info->trans_mutex);
1110 if (wait_for_unblock)
1111 wait_current_trans_commit_start_and_unblock(root, cur_trans);
1112 else
1113 wait_current_trans_commit_start(root, cur_trans);
1114 put_transaction(cur_trans);
1115 mutex_unlock(&root->fs_info->trans_mutex);
1116
1117 return 0;
1118}
1119
1120/*
1121 * btrfs_transaction state sequence:
1122 * in_commit = 0, blocked = 0 (initial)
1123 * in_commit = 1, blocked = 1
1124 * blocked = 0
1125 * commit_done = 1
1126 */
1010int btrfs_commit_transaction(struct btrfs_trans_handle *trans, 1127int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
1011 struct btrfs_root *root) 1128 struct btrfs_root *root)
1012{ 1129{
@@ -1057,6 +1174,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
1057 1174
1058 trans->transaction->in_commit = 1; 1175 trans->transaction->in_commit = 1;
1059 trans->transaction->blocked = 1; 1176 trans->transaction->blocked = 1;
1177 wake_up(&root->fs_info->transaction_blocked_wait);
1178
1060 if (cur_trans->list.prev != &root->fs_info->trans_list) { 1179 if (cur_trans->list.prev != &root->fs_info->trans_list) {
1061 prev_trans = list_entry(cur_trans->list.prev, 1180 prev_trans = list_entry(cur_trans->list.prev,
1062 struct btrfs_transaction, list); 1181 struct btrfs_transaction, list);
diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h
index 15f83e1c1ef..e1908e6872f 100644
--- a/fs/btrfs/transaction.h
+++ b/fs/btrfs/transaction.h
@@ -108,6 +108,9 @@ int btrfs_defrag_root(struct btrfs_root *root, int cacheonly);
108int btrfs_clean_old_snapshots(struct btrfs_root *root); 108int btrfs_clean_old_snapshots(struct btrfs_root *root);
109int btrfs_commit_transaction(struct btrfs_trans_handle *trans, 109int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
110 struct btrfs_root *root); 110 struct btrfs_root *root);
111int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans,
112 struct btrfs_root *root,
113 int wait_for_unblock);
111int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans, 114int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans,
112 struct btrfs_root *root); 115 struct btrfs_root *root);
113int btrfs_should_end_transaction(struct btrfs_trans_handle *trans, 116int btrfs_should_end_transaction(struct btrfs_trans_handle *trans,