diff options
| -rw-r--r-- | fs/btrfs/ctree.h | 1 | ||||
| -rw-r--r-- | fs/btrfs/disk-io.c | 1 | ||||
| -rw-r--r-- | fs/btrfs/transaction.c | 119 | ||||
| -rw-r--r-- | fs/btrfs/transaction.h | 3 |
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 | */ | ||
| 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); |
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); | |||
| 108 | int btrfs_clean_old_snapshots(struct btrfs_root *root); | 108 | int btrfs_clean_old_snapshots(struct btrfs_root *root); |
| 109 | int btrfs_commit_transaction(struct btrfs_trans_handle *trans, | 109 | int btrfs_commit_transaction(struct btrfs_trans_handle *trans, |
| 110 | struct btrfs_root *root); | 110 | struct btrfs_root *root); |
| 111 | int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans, | ||
| 112 | struct btrfs_root *root, | ||
| 113 | int wait_for_unblock); | ||
| 111 | int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans, | 114 | int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans, |
| 112 | struct btrfs_root *root); | 115 | struct btrfs_root *root); |
| 113 | int btrfs_should_end_transaction(struct btrfs_trans_handle *trans, | 116 | int btrfs_should_end_transaction(struct btrfs_trans_handle *trans, |
