aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
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
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')
-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 88c0fb7e12d2..e5d66b13c175 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 e163424c7fce..b40dfe48017b 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 700dc4b34ada..9f40bfc9c45c 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 15f83e1c1ef7..e1908e6872fe 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,