diff options
-rw-r--r-- | fs/btrfs/ctree.h | 4 | ||||
-rw-r--r-- | fs/btrfs/disk-io.c | 18 | ||||
-rw-r--r-- | fs/btrfs/transaction.c | 3 | ||||
-rw-r--r-- | fs/btrfs/uuid-tree.c | 123 | ||||
-rw-r--r-- | fs/btrfs/volumes.c | 83 | ||||
-rw-r--r-- | fs/btrfs/volumes.h | 1 |
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, | |||
3511 | int btrfs_uuid_tree_rem(struct btrfs_trans_handle *trans, | 3512 | int 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); |
3515 | int 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 */ |
3516 | int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir, | 3520 | int 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 | ||
1386 | int btrfs_transaction_in_commit(struct btrfs_fs_info *info) | 1387 | int 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 | |||
237 | static 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 | |||
253 | out: | ||
254 | return ret; | ||
255 | } | ||
256 | |||
257 | int 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 | |||
284 | again_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 | |||
344 | skip: | ||
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 | |||
353 | out: | ||
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 | */ | ||
3575 | static 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 | |||
3609 | out: | ||
3610 | return ret; | ||
3611 | } | ||
3612 | |||
3613 | static 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 | |||
3566 | int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info) | 3632 | int 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 | ||
3674 | int 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); | |||
323 | int btrfs_pause_balance(struct btrfs_fs_info *fs_info); | 323 | int btrfs_pause_balance(struct btrfs_fs_info *fs_info); |
324 | int btrfs_cancel_balance(struct btrfs_fs_info *fs_info); | 324 | int btrfs_cancel_balance(struct btrfs_fs_info *fs_info); |
325 | int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info); | 325 | int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info); |
326 | int btrfs_check_uuid_tree(struct btrfs_fs_info *fs_info); | ||
326 | int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset); | 327 | int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset); |
327 | int find_free_dev_extent(struct btrfs_trans_handle *trans, | 328 | int 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, |