aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/transaction.c
diff options
context:
space:
mode:
authorSage Weil <sage@newdream.net>2010-10-29 15:37:34 -0400
committerChris Mason <chris.mason@oracle.com>2010-10-29 15:37:34 -0400
commitbb9c12c945cbd1b0eaa1589546dde772ccabeeba (patch)
tree92f0bb01dce5125547c590d7c06b3e02d5c9d4fa /fs/btrfs/transaction.c
parent99d16cbcaf694c803a1b6bf7e851694ffe1d255d (diff)
Btrfs: async transaction commit
Add support for an async transaction commit that is ordered such that any subsequent operations will join the following transaction, but does not wait until the current commit is fully on disk. This avoids much of the latency associated with the btrfs_commit_transaction for callers concerned with serialization and not safety. The wait_for_unblock flag controls whether we wait for the 'middle' portion of commit_transaction to complete, which is necessary if the caller expects some of the modifications contained in the commit to be available (this is the case for subvol/snapshot creation). Signed-off-by: Sage Weil <sage@newdream.net> Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/transaction.c')
-rw-r--r--fs/btrfs/transaction.c119
1 files changed, 119 insertions, 0 deletions
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);