diff options
Diffstat (limited to 'fs/btrfs/transaction.c')
-rw-r--r-- | fs/btrfs/transaction.c | 119 |
1 files changed, 119 insertions, 0 deletions
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 | */ | ||
1014 | static 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 | */ | ||
1041 | static 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 | */ | ||
1070 | struct btrfs_async_commit { | ||
1071 | struct btrfs_trans_handle *newtrans; | ||
1072 | struct btrfs_root *root; | ||
1073 | struct delayed_work work; | ||
1074 | }; | ||
1075 | |||
1076 | static 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 | |||
1085 | int 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 | */ | ||
1010 | int btrfs_commit_transaction(struct btrfs_trans_handle *trans, | 1127 | int 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); |