aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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,