aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorStefan Behrens <sbehrens@giantdisaster.de>2013-08-15 11:11:23 -0400
committerChris Mason <chris.mason@fusionio.com>2013-09-01 08:15:58 -0400
commit70f801754728017ebc909d603c69255dc1e6f06f (patch)
treef4af464776a17b130686cb85a98adbeb6083d43c /fs/btrfs
parent26432799c902b76e87f68f5c88f2146a78ba84af (diff)
Btrfs: check UUID tree during mount if required
If the filesystem was mounted with an old kernel that was not aware of the UUID tree, this is detected by looking at the uuid_tree_generation field of the superblock (similar to how the free space cache is doing it). If a mismatch is detected at mount time, a thread is started that does two things: 1. Iterate through the UUID tree, check each entry, delete those entries that are not valid anymore (i.e., the subvol does not exist anymore or the value changed). 2. Iterate through the root tree, for each found subvolume, add the UUID tree entries for the subvolume (if they are not already there). This mechanism is also used to handle and repair errors that happened during the initial creation and filling of the tree. The update of the uuid_tree_generation field (which indicates that the state of the UUID tree is up to date) is blocked until all create and repair operations are successfully completed. Signed-off-by: Stefan Behrens <sbehrens@giantdisaster.de> Signed-off-by: Josef Bacik <jbacik@fusionio.com> Signed-off-by: Chris Mason <chris.mason@fusionio.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/ctree.h4
-rw-r--r--fs/btrfs/disk-io.c18
-rw-r--r--fs/btrfs/transaction.c3
-rw-r--r--fs/btrfs/uuid-tree.c123
-rw-r--r--fs/btrfs/volumes.c83
-rw-r--r--fs/btrfs/volumes.h1
6 files changed, 230 insertions, 2 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 02b1cefbc308..8c954766db93 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1658,6 +1658,7 @@ struct btrfs_fs_info {
1658 atomic_t mutually_exclusive_operation_running; 1658 atomic_t mutually_exclusive_operation_running;
1659 1659
1660 struct semaphore uuid_tree_rescan_sem; 1660 struct semaphore uuid_tree_rescan_sem;
1661 unsigned int update_uuid_tree_gen:1;
1661}; 1662};
1662 1663
1663/* 1664/*
@@ -3511,6 +3512,9 @@ int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans,
3511int btrfs_uuid_tree_rem(struct btrfs_trans_handle *trans, 3512int btrfs_uuid_tree_rem(struct btrfs_trans_handle *trans,
3512 struct btrfs_root *uuid_root, u8 *uuid, u8 type, 3513 struct btrfs_root *uuid_root, u8 *uuid, u8 type,
3513 u64 subid); 3514 u64 subid);
3515int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info,
3516 int (*check_func)(struct btrfs_fs_info *, u8 *, u8,
3517 u64));
3514 3518
3515/* dir-item.c */ 3519/* dir-item.c */
3516int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir, 3520int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index fa49e900216a..e7ef82ad0d26 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -2120,7 +2120,8 @@ int open_ctree(struct super_block *sb,
2120 int err = -EINVAL; 2120 int err = -EINVAL;
2121 int num_backups_tried = 0; 2121 int num_backups_tried = 0;
2122 int backup_index = 0; 2122 int backup_index = 0;
2123 bool create_uuid_tree = false; 2123 bool create_uuid_tree;
2124 bool check_uuid_tree;
2124 2125
2125 tree_root = fs_info->tree_root = btrfs_alloc_root(fs_info); 2126 tree_root = fs_info->tree_root = btrfs_alloc_root(fs_info);
2126 chunk_root = fs_info->chunk_root = btrfs_alloc_root(fs_info); 2127 chunk_root = fs_info->chunk_root = btrfs_alloc_root(fs_info);
@@ -2724,9 +2725,13 @@ retry_root_backup:
2724 if (ret != -ENOENT) 2725 if (ret != -ENOENT)
2725 goto recovery_tree_root; 2726 goto recovery_tree_root;
2726 create_uuid_tree = true; 2727 create_uuid_tree = true;
2728 check_uuid_tree = false;
2727 } else { 2729 } else {
2728 uuid_root->track_dirty = 1; 2730 uuid_root->track_dirty = 1;
2729 fs_info->uuid_root = uuid_root; 2731 fs_info->uuid_root = uuid_root;
2732 create_uuid_tree = false;
2733 check_uuid_tree =
2734 generation != btrfs_super_uuid_tree_generation(disk_super);
2730 } 2735 }
2731 2736
2732 fs_info->generation = generation; 2737 fs_info->generation = generation;
@@ -2924,6 +2929,17 @@ retry_root_backup:
2924 close_ctree(tree_root); 2929 close_ctree(tree_root);
2925 return ret; 2930 return ret;
2926 } 2931 }
2932 } else if (check_uuid_tree) {
2933 pr_info("btrfs: checking UUID tree\n");
2934 ret = btrfs_check_uuid_tree(fs_info);
2935 if (ret) {
2936 pr_warn("btrfs: failed to check the UUID tree %d\n",
2937 ret);
2938 close_ctree(tree_root);
2939 return ret;
2940 }
2941 } else {
2942 fs_info->update_uuid_tree_gen = 1;
2927 } 2943 }
2928 2944
2929 return 0; 2945 return 0;
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index e76237c8802a..e0336b9a4385 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -1380,7 +1380,8 @@ static void update_super_roots(struct btrfs_root *root)
1380 super->root_level = root_item->level; 1380 super->root_level = root_item->level;
1381 if (btrfs_test_opt(root, SPACE_CACHE)) 1381 if (btrfs_test_opt(root, SPACE_CACHE))
1382 super->cache_generation = root_item->generation; 1382 super->cache_generation = root_item->generation;
1383 super->uuid_tree_generation = root_item->generation; 1383 if (root->fs_info->update_uuid_tree_gen)
1384 super->uuid_tree_generation = root_item->generation;
1384} 1385}
1385 1386
1386int btrfs_transaction_in_commit(struct btrfs_fs_info *info) 1387int btrfs_transaction_in_commit(struct btrfs_fs_info *info)
diff --git a/fs/btrfs/uuid-tree.c b/fs/btrfs/uuid-tree.c
index 04a04fa0f7c4..dd0dea3766f7 100644
--- a/fs/btrfs/uuid-tree.c
+++ b/fs/btrfs/uuid-tree.c
@@ -233,3 +233,126 @@ out:
233 btrfs_free_path(path); 233 btrfs_free_path(path);
234 return ret; 234 return ret;
235} 235}
236
237static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type,
238 u64 subid)
239{
240 struct btrfs_trans_handle *trans;
241 int ret;
242
243 /* 1 - for the uuid item */
244 trans = btrfs_start_transaction(uuid_root, 1);
245 if (IS_ERR(trans)) {
246 ret = PTR_ERR(trans);
247 goto out;
248 }
249
250 ret = btrfs_uuid_tree_rem(trans, uuid_root, uuid, type, subid);
251 btrfs_end_transaction(trans, uuid_root);
252
253out:
254 return ret;
255}
256
257int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info,
258 int (*check_func)(struct btrfs_fs_info *, u8 *, u8,
259 u64))
260{
261 struct btrfs_root *root = fs_info->uuid_root;
262 struct btrfs_key key;
263 struct btrfs_key max_key;
264 struct btrfs_path *path;
265 int ret = 0;
266 struct extent_buffer *leaf;
267 int slot;
268 u32 item_size;
269 unsigned long offset;
270
271 path = btrfs_alloc_path();
272 if (!path) {
273 ret = -ENOMEM;
274 goto out;
275 }
276
277 key.objectid = 0;
278 key.type = 0;
279 key.offset = 0;
280 max_key.objectid = (u64)-1;
281 max_key.type = (u8)-1;
282 max_key.offset = (u64)-1;
283
284again_search_slot:
285 path->keep_locks = 1;
286 ret = btrfs_search_forward(root, &key, &max_key, path, 0);
287 if (ret) {
288 if (ret > 0)
289 ret = 0;
290 goto out;
291 }
292
293 while (1) {
294 cond_resched();
295 leaf = path->nodes[0];
296 slot = path->slots[0];
297 btrfs_item_key_to_cpu(leaf, &key, slot);
298
299 if (key.type != BTRFS_UUID_KEY_SUBVOL &&
300 key.type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)
301 goto skip;
302
303 offset = btrfs_item_ptr_offset(leaf, slot);
304 item_size = btrfs_item_size_nr(leaf, slot);
305 if (!IS_ALIGNED(item_size, sizeof(u64))) {
306 pr_warn("btrfs: uuid item with illegal size %lu!\n",
307 (unsigned long)item_size);
308 goto skip;
309 }
310 while (item_size) {
311 u8 uuid[BTRFS_UUID_SIZE];
312 __le64 subid_le;
313 u64 subid_cpu;
314
315 put_unaligned_le64(key.objectid, uuid);
316 put_unaligned_le64(key.offset, uuid + sizeof(u64));
317 read_extent_buffer(leaf, &subid_le, offset,
318 sizeof(subid_le));
319 subid_cpu = le64_to_cpu(subid_le);
320 ret = check_func(fs_info, uuid, key.type, subid_cpu);
321 if (ret < 0)
322 goto out;
323 if (ret > 0) {
324 btrfs_release_path(path);
325 ret = btrfs_uuid_iter_rem(root, uuid, key.type,
326 subid_cpu);
327 if (ret == 0) {
328 /*
329 * this might look inefficient, but the
330 * justification is that it is an
331 * exception that check_func returns 1,
332 * and that in the regular case only one
333 * entry per UUID exists.
334 */
335 goto again_search_slot;
336 }
337 if (ret < 0 && ret != -ENOENT)
338 goto out;
339 }
340 item_size -= sizeof(subid_le);
341 offset += sizeof(subid_le);
342 }
343
344skip:
345 ret = btrfs_next_item(root, path);
346 if (ret == 0)
347 continue;
348 else if (ret > 0)
349 ret = 0;
350 break;
351 }
352
353out:
354 btrfs_free_path(path);
355 if (ret)
356 pr_warn("btrfs: btrfs_uuid_tree_iterate failed %d\n", ret);
357 return 0;
358}
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 4066803fe765..75bdea6bf188 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -3559,10 +3559,76 @@ out:
3559 btrfs_free_path(path); 3559 btrfs_free_path(path);
3560 if (ret) 3560 if (ret)
3561 pr_warn("btrfs: btrfs_uuid_scan_kthread failed %d\n", ret); 3561 pr_warn("btrfs: btrfs_uuid_scan_kthread failed %d\n", ret);
3562 else
3563 fs_info->update_uuid_tree_gen = 1;
3562 up(&fs_info->uuid_tree_rescan_sem); 3564 up(&fs_info->uuid_tree_rescan_sem);
3563 return 0; 3565 return 0;
3564} 3566}
3565 3567
3568/*
3569 * Callback for btrfs_uuid_tree_iterate().
3570 * returns:
3571 * 0 check succeeded, the entry is not outdated.
3572 * < 0 if an error occured.
3573 * > 0 if the check failed, which means the caller shall remove the entry.
3574 */
3575static int btrfs_check_uuid_tree_entry(struct btrfs_fs_info *fs_info,
3576 u8 *uuid, u8 type, u64 subid)
3577{
3578 struct btrfs_key key;
3579 int ret = 0;
3580 struct btrfs_root *subvol_root;
3581
3582 if (type != BTRFS_UUID_KEY_SUBVOL &&
3583 type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)
3584 goto out;
3585
3586 key.objectid = subid;
3587 key.type = BTRFS_ROOT_ITEM_KEY;
3588 key.offset = (u64)-1;
3589 subvol_root = btrfs_read_fs_root_no_name(fs_info, &key);
3590 if (IS_ERR(subvol_root)) {
3591 ret = PTR_ERR(subvol_root);
3592 if (ret == -ENOENT)
3593 ret = 1;
3594 goto out;
3595 }
3596
3597 switch (type) {
3598 case BTRFS_UUID_KEY_SUBVOL:
3599 if (memcmp(uuid, subvol_root->root_item.uuid, BTRFS_UUID_SIZE))
3600 ret = 1;
3601 break;
3602 case BTRFS_UUID_KEY_RECEIVED_SUBVOL:
3603 if (memcmp(uuid, subvol_root->root_item.received_uuid,
3604 BTRFS_UUID_SIZE))
3605 ret = 1;
3606 break;
3607 }
3608
3609out:
3610 return ret;
3611}
3612
3613static int btrfs_uuid_rescan_kthread(void *data)
3614{
3615 struct btrfs_fs_info *fs_info = (struct btrfs_fs_info *)data;
3616 int ret;
3617
3618 /*
3619 * 1st step is to iterate through the existing UUID tree and
3620 * to delete all entries that contain outdated data.
3621 * 2nd step is to add all missing entries to the UUID tree.
3622 */
3623 ret = btrfs_uuid_tree_iterate(fs_info, btrfs_check_uuid_tree_entry);
3624 if (ret < 0) {
3625 pr_warn("btrfs: iterating uuid_tree failed %d\n", ret);
3626 up(&fs_info->uuid_tree_rescan_sem);
3627 return ret;
3628 }
3629 return btrfs_uuid_scan_kthread(data);
3630}
3631
3566int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info) 3632int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info)
3567{ 3633{
3568 struct btrfs_trans_handle *trans; 3634 struct btrfs_trans_handle *trans;
@@ -3596,6 +3662,7 @@ int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info)
3596 down(&fs_info->uuid_tree_rescan_sem); 3662 down(&fs_info->uuid_tree_rescan_sem);
3597 task = kthread_run(btrfs_uuid_scan_kthread, fs_info, "btrfs-uuid"); 3663 task = kthread_run(btrfs_uuid_scan_kthread, fs_info, "btrfs-uuid");
3598 if (IS_ERR(task)) { 3664 if (IS_ERR(task)) {
3665 /* fs_info->update_uuid_tree_gen remains 0 in all error case */
3599 pr_warn("btrfs: failed to start uuid_scan task\n"); 3666 pr_warn("btrfs: failed to start uuid_scan task\n");
3600 up(&fs_info->uuid_tree_rescan_sem); 3667 up(&fs_info->uuid_tree_rescan_sem);
3601 return PTR_ERR(task); 3668 return PTR_ERR(task);
@@ -3604,6 +3671,22 @@ int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info)
3604 return 0; 3671 return 0;
3605} 3672}
3606 3673
3674int btrfs_check_uuid_tree(struct btrfs_fs_info *fs_info)
3675{
3676 struct task_struct *task;
3677
3678 down(&fs_info->uuid_tree_rescan_sem);
3679 task = kthread_run(btrfs_uuid_rescan_kthread, fs_info, "btrfs-uuid");
3680 if (IS_ERR(task)) {
3681 /* fs_info->update_uuid_tree_gen remains 0 in all error case */
3682 pr_warn("btrfs: failed to start uuid_rescan task\n");
3683 up(&fs_info->uuid_tree_rescan_sem);
3684 return PTR_ERR(task);
3685 }
3686
3687 return 0;
3688}
3689
3607/* 3690/*
3608 * shrinking a device means finding all of the device extents past 3691 * shrinking a device means finding all of the device extents past
3609 * the new size, and then following the back refs to the chunks. 3692 * the new size, and then following the back refs to the chunks.
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 7071b2988305..d98b942c3896 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -323,6 +323,7 @@ int btrfs_recover_balance(struct btrfs_fs_info *fs_info);
323int btrfs_pause_balance(struct btrfs_fs_info *fs_info); 323int btrfs_pause_balance(struct btrfs_fs_info *fs_info);
324int btrfs_cancel_balance(struct btrfs_fs_info *fs_info); 324int btrfs_cancel_balance(struct btrfs_fs_info *fs_info);
325int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info); 325int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info);
326int btrfs_check_uuid_tree(struct btrfs_fs_info *fs_info);
326int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset); 327int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset);
327int find_free_dev_extent(struct btrfs_trans_handle *trans, 328int find_free_dev_extent(struct btrfs_trans_handle *trans,
328 struct btrfs_device *device, u64 num_bytes, 329 struct btrfs_device *device, u64 num_bytes,