|
@@ -3559,10 +3559,76 @@ out:
|
|
btrfs_free_path(path);
|
|
btrfs_free_path(path);
|
|
if (ret)
|
|
if (ret)
|
|
pr_warn("btrfs: btrfs_uuid_scan_kthread failed %d\n", ret);
|
|
pr_warn("btrfs: btrfs_uuid_scan_kthread failed %d\n", ret);
|
|
|
|
+ else
|
|
|
|
+ fs_info->update_uuid_tree_gen = 1;
|
|
up(&fs_info->uuid_tree_rescan_sem);
|
|
up(&fs_info->uuid_tree_rescan_sem);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Callback for btrfs_uuid_tree_iterate().
|
|
|
|
+ * returns:
|
|
|
|
+ * 0 check succeeded, the entry is not outdated.
|
|
|
|
+ * < 0 if an error occured.
|
|
|
|
+ * > 0 if the check failed, which means the caller shall remove the entry.
|
|
|
|
+ */
|
|
|
|
+static int btrfs_check_uuid_tree_entry(struct btrfs_fs_info *fs_info,
|
|
|
|
+ u8 *uuid, u8 type, u64 subid)
|
|
|
|
+{
|
|
|
|
+ struct btrfs_key key;
|
|
|
|
+ int ret = 0;
|
|
|
|
+ struct btrfs_root *subvol_root;
|
|
|
|
+
|
|
|
|
+ if (type != BTRFS_UUID_KEY_SUBVOL &&
|
|
|
|
+ type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ key.objectid = subid;
|
|
|
|
+ key.type = BTRFS_ROOT_ITEM_KEY;
|
|
|
|
+ key.offset = (u64)-1;
|
|
|
|
+ subvol_root = btrfs_read_fs_root_no_name(fs_info, &key);
|
|
|
|
+ if (IS_ERR(subvol_root)) {
|
|
|
|
+ ret = PTR_ERR(subvol_root);
|
|
|
|
+ if (ret == -ENOENT)
|
|
|
|
+ ret = 1;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (type) {
|
|
|
|
+ case BTRFS_UUID_KEY_SUBVOL:
|
|
|
|
+ if (memcmp(uuid, subvol_root->root_item.uuid, BTRFS_UUID_SIZE))
|
|
|
|
+ ret = 1;
|
|
|
|
+ break;
|
|
|
|
+ case BTRFS_UUID_KEY_RECEIVED_SUBVOL:
|
|
|
|
+ if (memcmp(uuid, subvol_root->root_item.received_uuid,
|
|
|
|
+ BTRFS_UUID_SIZE))
|
|
|
|
+ ret = 1;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int btrfs_uuid_rescan_kthread(void *data)
|
|
|
|
+{
|
|
|
|
+ struct btrfs_fs_info *fs_info = (struct btrfs_fs_info *)data;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * 1st step is to iterate through the existing UUID tree and
|
|
|
|
+ * to delete all entries that contain outdated data.
|
|
|
|
+ * 2nd step is to add all missing entries to the UUID tree.
|
|
|
|
+ */
|
|
|
|
+ ret = btrfs_uuid_tree_iterate(fs_info, btrfs_check_uuid_tree_entry);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ pr_warn("btrfs: iterating uuid_tree failed %d\n", ret);
|
|
|
|
+ up(&fs_info->uuid_tree_rescan_sem);
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+ return btrfs_uuid_scan_kthread(data);
|
|
|
|
+}
|
|
|
|
+
|
|
int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info)
|
|
int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info)
|
|
{
|
|
{
|
|
struct btrfs_trans_handle *trans;
|
|
struct btrfs_trans_handle *trans;
|
|
@@ -3596,6 +3662,7 @@ int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info)
|
|
down(&fs_info->uuid_tree_rescan_sem);
|
|
down(&fs_info->uuid_tree_rescan_sem);
|
|
task = kthread_run(btrfs_uuid_scan_kthread, fs_info, "btrfs-uuid");
|
|
task = kthread_run(btrfs_uuid_scan_kthread, fs_info, "btrfs-uuid");
|
|
if (IS_ERR(task)) {
|
|
if (IS_ERR(task)) {
|
|
|
|
+ /* fs_info->update_uuid_tree_gen remains 0 in all error case */
|
|
pr_warn("btrfs: failed to start uuid_scan task\n");
|
|
pr_warn("btrfs: failed to start uuid_scan task\n");
|
|
up(&fs_info->uuid_tree_rescan_sem);
|
|
up(&fs_info->uuid_tree_rescan_sem);
|
|
return PTR_ERR(task);
|
|
return PTR_ERR(task);
|
|
@@ -3604,6 +3671,22 @@ int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+int btrfs_check_uuid_tree(struct btrfs_fs_info *fs_info)
|
|
|
|
+{
|
|
|
|
+ struct task_struct *task;
|
|
|
|
+
|
|
|
|
+ down(&fs_info->uuid_tree_rescan_sem);
|
|
|
|
+ task = kthread_run(btrfs_uuid_rescan_kthread, fs_info, "btrfs-uuid");
|
|
|
|
+ if (IS_ERR(task)) {
|
|
|
|
+ /* fs_info->update_uuid_tree_gen remains 0 in all error case */
|
|
|
|
+ pr_warn("btrfs: failed to start uuid_rescan task\n");
|
|
|
|
+ up(&fs_info->uuid_tree_rescan_sem);
|
|
|
|
+ return PTR_ERR(task);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* shrinking a device means finding all of the device extents past
|
|
* shrinking a device means finding all of the device extents past
|
|
* the new size, and then following the back refs to the chunks.
|
|
* the new size, and then following the back refs to the chunks.
|