diff options
Diffstat (limited to 'fs/btrfs/transaction.c')
-rw-r--r-- | fs/btrfs/transaction.c | 105 |
1 files changed, 87 insertions, 18 deletions
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 498626470a04..338a7199363b 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c | |||
@@ -29,6 +29,7 @@ extern struct kmem_cache *btrfs_transaction_cachep; | |||
29 | static struct workqueue_struct *trans_wq; | 29 | static struct workqueue_struct *trans_wq; |
30 | 30 | ||
31 | #define BTRFS_ROOT_TRANS_TAG 0 | 31 | #define BTRFS_ROOT_TRANS_TAG 0 |
32 | #define BTRFS_ROOT_DEFRAG_TAG 1 | ||
32 | 33 | ||
33 | static void put_transaction(struct btrfs_transaction *transaction) | 34 | static void put_transaction(struct btrfs_transaction *transaction) |
34 | { | 35 | { |
@@ -69,35 +70,41 @@ static int join_transaction(struct btrfs_root *root) | |||
69 | return 0; | 70 | return 0; |
70 | } | 71 | } |
71 | 72 | ||
73 | static int record_root_in_trans(struct btrfs_root *root) | ||
74 | { | ||
75 | u64 running_trans_id = root->fs_info->running_transaction->transid; | ||
76 | if (root->ref_cows && root->last_trans < running_trans_id) { | ||
77 | WARN_ON(root == root->fs_info->extent_root); | ||
78 | if (root->root_item.refs != 0) { | ||
79 | radix_tree_tag_set(&root->fs_info->fs_roots_radix, | ||
80 | (unsigned long)root->root_key.objectid, | ||
81 | BTRFS_ROOT_TRANS_TAG); | ||
82 | radix_tree_tag_set(&root->fs_info->fs_roots_radix, | ||
83 | (unsigned long)root->root_key.objectid, | ||
84 | BTRFS_ROOT_DEFRAG_TAG); | ||
85 | root->commit_root = root->node; | ||
86 | get_bh(root->node); | ||
87 | } else { | ||
88 | WARN_ON(1); | ||
89 | } | ||
90 | root->last_trans = running_trans_id; | ||
91 | } | ||
92 | return 0; | ||
93 | } | ||
94 | |||
72 | struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root, | 95 | struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root, |
73 | int num_blocks) | 96 | int num_blocks) |
74 | { | 97 | { |
75 | struct btrfs_trans_handle *h = | 98 | struct btrfs_trans_handle *h = |
76 | kmem_cache_alloc(btrfs_trans_handle_cachep, GFP_NOFS); | 99 | kmem_cache_alloc(btrfs_trans_handle_cachep, GFP_NOFS); |
77 | int ret; | 100 | int ret; |
78 | u64 running_trans_id; | ||
79 | 101 | ||
80 | mutex_lock(&root->fs_info->trans_mutex); | 102 | mutex_lock(&root->fs_info->trans_mutex); |
81 | ret = join_transaction(root); | 103 | ret = join_transaction(root); |
82 | BUG_ON(ret); | 104 | BUG_ON(ret); |
83 | running_trans_id = root->fs_info->running_transaction->transid; | ||
84 | 105 | ||
85 | if (root != root->fs_info->tree_root && root->last_trans < | 106 | record_root_in_trans(root); |
86 | running_trans_id) { | 107 | h->transid = root->fs_info->running_transaction->transid; |
87 | WARN_ON(root == root->fs_info->extent_root); | ||
88 | WARN_ON(root->ref_cows != 1); | ||
89 | if (root->root_item.refs != 0) { | ||
90 | radix_tree_tag_set(&root->fs_info->fs_roots_radix, | ||
91 | (unsigned long)root->root_key.objectid, | ||
92 | BTRFS_ROOT_TRANS_TAG); | ||
93 | root->commit_root = root->node; | ||
94 | get_bh(root->node); | ||
95 | } else { | ||
96 | WARN_ON(1); | ||
97 | } | ||
98 | } | ||
99 | root->last_trans = running_trans_id; | ||
100 | h->transid = running_trans_id; | ||
101 | h->transaction = root->fs_info->running_transaction; | 108 | h->transaction = root->fs_info->running_transaction; |
102 | h->blocks_reserved = num_blocks; | 109 | h->blocks_reserved = num_blocks; |
103 | h->blocks_used = 0; | 110 | h->blocks_used = 0; |
@@ -155,6 +162,15 @@ int btrfs_write_and_wait_transaction(struct btrfs_trans_handle *trans, | |||
155 | gang[i]); | 162 | gang[i]); |
156 | if (!page) | 163 | if (!page) |
157 | continue; | 164 | continue; |
165 | if (PageWriteback(page)) { | ||
166 | if (PageDirty(page)) | ||
167 | wait_on_page_writeback(page); | ||
168 | else { | ||
169 | unlock_page(page); | ||
170 | page_cache_release(page); | ||
171 | continue; | ||
172 | } | ||
173 | } | ||
158 | err = write_one_page(page, 0); | 174 | err = write_one_page(page, 0); |
159 | if (err) | 175 | if (err) |
160 | werr = err; | 176 | werr = err; |
@@ -299,6 +315,58 @@ static int add_dirty_roots(struct btrfs_trans_handle *trans, | |||
299 | return err; | 315 | return err; |
300 | } | 316 | } |
301 | 317 | ||
318 | int btrfs_defrag_dirty_roots(struct btrfs_fs_info *info) | ||
319 | { | ||
320 | struct btrfs_root *gang[1]; | ||
321 | struct btrfs_root *root; | ||
322 | struct btrfs_root *tree_root = info->tree_root; | ||
323 | struct btrfs_trans_handle *trans; | ||
324 | int i; | ||
325 | int ret; | ||
326 | int err = 0; | ||
327 | u64 last = 0; | ||
328 | |||
329 | trans = btrfs_start_transaction(tree_root, 1); | ||
330 | while(1) { | ||
331 | ret = radix_tree_gang_lookup_tag(&info->fs_roots_radix, | ||
332 | (void **)gang, last, | ||
333 | ARRAY_SIZE(gang), | ||
334 | BTRFS_ROOT_DEFRAG_TAG); | ||
335 | if (ret == 0) | ||
336 | break; | ||
337 | for (i = 0; i < ret; i++) { | ||
338 | root = gang[i]; | ||
339 | last = root->root_key.objectid + 1; | ||
340 | radix_tree_tag_clear(&info->fs_roots_radix, | ||
341 | (unsigned long)root->root_key.objectid, | ||
342 | BTRFS_ROOT_DEFRAG_TAG); | ||
343 | if (root->defrag_running) | ||
344 | continue; | ||
345 | |||
346 | while (1) { | ||
347 | mutex_lock(&root->fs_info->trans_mutex); | ||
348 | record_root_in_trans(root); | ||
349 | mutex_unlock(&root->fs_info->trans_mutex); | ||
350 | |||
351 | root->defrag_running = 1; | ||
352 | err = btrfs_defrag_leaves(trans, root, 1); | ||
353 | btrfs_end_transaction(trans, tree_root); | ||
354 | mutex_unlock(&info->fs_mutex); | ||
355 | |||
356 | btrfs_btree_balance_dirty(root); | ||
357 | |||
358 | mutex_lock(&info->fs_mutex); | ||
359 | trans = btrfs_start_transaction(tree_root, 1); | ||
360 | if (err != -EAGAIN) | ||
361 | break; | ||
362 | } | ||
363 | root->defrag_running = 0; | ||
364 | } | ||
365 | } | ||
366 | btrfs_end_transaction(trans, tree_root); | ||
367 | return err; | ||
368 | } | ||
369 | |||
302 | static int drop_dirty_roots(struct btrfs_root *tree_root, | 370 | static int drop_dirty_roots(struct btrfs_root *tree_root, |
303 | struct list_head *list) | 371 | struct list_head *list) |
304 | { | 372 | { |
@@ -475,6 +543,7 @@ void btrfs_transaction_cleaner(struct work_struct *work) | |||
475 | goto out; | 543 | goto out; |
476 | } | 544 | } |
477 | mutex_unlock(&root->fs_info->trans_mutex); | 545 | mutex_unlock(&root->fs_info->trans_mutex); |
546 | btrfs_defrag_dirty_roots(root->fs_info); | ||
478 | trans = btrfs_start_transaction(root, 1); | 547 | trans = btrfs_start_transaction(root, 1); |
479 | ret = btrfs_commit_transaction(trans, root); | 548 | ret = btrfs_commit_transaction(trans, root); |
480 | out: | 549 | out: |