blob: 4903b47c9780c8046c54ec3095a9ef09da3183dd [file] [log] [blame]
Chris Mason79154b12007-03-22 15:59:16 -04001#include <linux/module.h>
2#include <linux/fs.h>
3#include "ctree.h"
4#include "disk-io.h"
5#include "transaction.h"
6
7
8static void put_transaction(struct btrfs_transaction *transaction)
9{
10 transaction->use_count--;
11 if (transaction->use_count == 0)
12 kfree(transaction);
13}
14
15static int join_transaction(struct btrfs_root *root)
16{
17 struct btrfs_transaction *cur_trans;
18 cur_trans = root->fs_info->running_transaction;
19 if (!cur_trans) {
20 cur_trans = kmalloc(sizeof(*cur_trans), GFP_NOFS);
21 BUG_ON(!cur_trans);
22 root->fs_info->running_transaction = cur_trans;
23 cur_trans->num_writers = 0;
24 cur_trans->transid = root->root_key.offset + 1;
25 init_waitqueue_head(&cur_trans->writer_wait);
26 init_waitqueue_head(&cur_trans->commit_wait);
27 cur_trans->in_commit = 0;
Chris Masond5719762007-03-23 10:01:08 -040028 cur_trans->use_count = 1;
Chris Mason79154b12007-03-22 15:59:16 -040029 cur_trans->commit_done = 0;
30 }
31 cur_trans->num_writers++;
32 return 0;
33}
34
35struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
36 int num_blocks)
37{
38 struct btrfs_trans_handle *h = kmalloc(sizeof(*h), GFP_NOFS);
39 int ret;
40
41 mutex_lock(&root->fs_info->trans_mutex);
42 ret = join_transaction(root);
43 BUG_ON(ret);
44 h->transid = root->fs_info->running_transaction->transid;
45 h->transaction = root->fs_info->running_transaction;
46 h->blocks_reserved = num_blocks;
47 h->blocks_used = 0;
48 root->fs_info->running_transaction->use_count++;
49 mutex_unlock(&root->fs_info->trans_mutex);
50 return h;
51}
52
53int btrfs_end_transaction(struct btrfs_trans_handle *trans,
54 struct btrfs_root *root)
55{
56 struct btrfs_transaction *cur_trans;
57 mutex_lock(&root->fs_info->trans_mutex);
58 cur_trans = root->fs_info->running_transaction;
Chris Masond5719762007-03-23 10:01:08 -040059 WARN_ON(cur_trans->num_writers < 1);
Chris Mason79154b12007-03-22 15:59:16 -040060 if (waitqueue_active(&cur_trans->writer_wait))
61 wake_up(&cur_trans->writer_wait);
62 cur_trans->num_writers--;
63 put_transaction(cur_trans);
64 mutex_unlock(&root->fs_info->trans_mutex);
65 kfree(trans);
66 return 0;
67}
68
69
70int btrfs_write_and_wait_transaction(struct btrfs_trans_handle *trans,
71 struct btrfs_root *root)
72{
73 filemap_write_and_wait(root->fs_info->sb->s_bdev->bd_inode->i_mapping);
74 return 0;
75}
76
77int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans,
78 struct btrfs_root *root)
79{
80 int ret;
81 u64 old_extent_block;
82 struct btrfs_fs_info *fs_info = root->fs_info;
83 struct btrfs_root *tree_root = fs_info->tree_root;
84 struct btrfs_root *extent_root = fs_info->extent_root;
85 struct btrfs_root *inode_root = fs_info->inode_root;
86
87 btrfs_set_root_blocknr(&inode_root->root_item,
88 inode_root->node->b_blocknr);
89 ret = btrfs_update_root(trans, tree_root,
90 &inode_root->root_key,
91 &inode_root->root_item);
92 BUG_ON(ret);
93 while(1) {
94 old_extent_block = btrfs_root_blocknr(&extent_root->root_item);
95 if (old_extent_block == extent_root->node->b_blocknr)
96 break;
97 btrfs_set_root_blocknr(&extent_root->root_item,
98 extent_root->node->b_blocknr);
99 ret = btrfs_update_root(trans, tree_root,
100 &extent_root->root_key,
101 &extent_root->root_item);
102 BUG_ON(ret);
103 }
104 return 0;
105}
106
107static int wait_for_commit(struct btrfs_root *root,
108 struct btrfs_transaction *commit)
109{
110 DEFINE_WAIT(wait);
111 commit->use_count++;
112 while(!commit->commit_done) {
113 prepare_to_wait(&commit->commit_wait, &wait,
114 TASK_UNINTERRUPTIBLE);
115 if (commit->commit_done)
116 break;
117 mutex_unlock(&root->fs_info->trans_mutex);
118 schedule();
119 mutex_lock(&root->fs_info->trans_mutex);
120 }
121 finish_wait(&commit->commit_wait, &wait);
122 return 0;
123}
124
125int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
126 struct btrfs_root *root)
127{
128 int ret = 0;
129 struct buffer_head *snap = root->commit_root;
130 struct btrfs_key snap_key;
131 struct btrfs_transaction *cur_trans;
132 DEFINE_WAIT(wait);
133
134 mutex_lock(&root->fs_info->trans_mutex);
135 if (trans->transaction->in_commit) {
136 cur_trans = trans->transaction;
137 trans->transaction->use_count++;
138 btrfs_end_transaction(trans, root);
139 ret = wait_for_commit(root, cur_trans);
140 BUG_ON(ret);
141 put_transaction(cur_trans);
142 mutex_unlock(&root->fs_info->trans_mutex);
143 return 0;
144 }
145 while (trans->transaction->num_writers > 1) {
146 prepare_to_wait(&trans->transaction->writer_wait, &wait,
147 TASK_UNINTERRUPTIBLE);
148 if (trans->transaction->num_writers <= 1)
149 break;
150 mutex_unlock(&root->fs_info->trans_mutex);
151 schedule();
152 mutex_lock(&root->fs_info->trans_mutex);
153 }
154 finish_wait(&trans->transaction->writer_wait, &wait);
155
156 cur_trans = root->fs_info->running_transaction;
157 root->fs_info->running_transaction = NULL;
Chris Mason79154b12007-03-22 15:59:16 -0400158
Chris Masond5719762007-03-23 10:01:08 -0400159 if (root->node != root->commit_root) {
160 memcpy(&snap_key, &root->root_key, sizeof(snap_key));
161 root->root_key.offset++;
162 }
163
164 mutex_unlock(&root->fs_info->trans_mutex);
Chris Mason79154b12007-03-22 15:59:16 -0400165
166 if (btrfs_root_blocknr(&root->root_item) != root->node->b_blocknr) {
167 btrfs_set_root_blocknr(&root->root_item, root->node->b_blocknr);
168 ret = btrfs_insert_root(trans, root->fs_info->tree_root,
169 &root->root_key, &root->root_item);
170 BUG_ON(ret);
171 }
172
173 ret = btrfs_commit_tree_roots(trans, root);
174 BUG_ON(ret);
175
176 ret = btrfs_write_and_wait_transaction(trans, root);
177 BUG_ON(ret);
178
179 write_ctree_super(trans, root);
180 btrfs_finish_extent_commit(trans, root->fs_info->extent_root);
181 btrfs_finish_extent_commit(trans, root->fs_info->tree_root);
182 put_transaction(cur_trans);
183 kfree(trans);
184
185 if (root->node != root->commit_root) {
186 trans = btrfs_start_transaction(root, 1);
187 root->commit_root = root->node;
188 get_bh(root->node);
189 ret = btrfs_drop_snapshot(trans, root, snap);
190 BUG_ON(ret);
191
192 ret = btrfs_del_root(trans, root->fs_info->tree_root,
193 &snap_key);
194 BUG_ON(ret);
195 root->fs_info->generation = root->root_key.offset + 1;
196 ret = btrfs_end_transaction(trans, root);
197 BUG_ON(ret);
198 }
199
200 return ret;
201}
202