btrfs: add members to fs_devices to track fsid changes
[sfrench/cifs-2.6.git] / fs / btrfs / volumes.c
index 29fc8a09dd2ea9622be49f4305856d46f37afe21..13bb44194c564459e2114032e524ed606af2d6d5 100644 (file)
@@ -245,13 +245,15 @@ struct list_head *btrfs_get_fs_uuids(void)
 
 /*
  * alloc_fs_devices - allocate struct btrfs_fs_devices
- * @fsid:      if not NULL, copy the uuid to fs_devices::fsid
+ * @fsid:              if not NULL, copy the UUID to fs_devices::fsid
+ * @metadata_fsid:     if not NULL, copy the UUID to fs_devices::metadata_fsid
  *
  * Return a pointer to a new struct btrfs_fs_devices on success, or ERR_PTR().
  * The returned struct is not linked onto any lists and can be destroyed with
  * kfree() right away.
  */
-static struct btrfs_fs_devices *alloc_fs_devices(const u8 *fsid)
+static struct btrfs_fs_devices *alloc_fs_devices(const u8 *fsid,
+                                                const u8 *metadata_fsid)
 {
        struct btrfs_fs_devices *fs_devs;
 
@@ -268,6 +270,11 @@ static struct btrfs_fs_devices *alloc_fs_devices(const u8 *fsid)
        if (fsid)
                memcpy(fs_devs->fsid, fsid, BTRFS_FSID_SIZE);
 
+       if (metadata_fsid)
+               memcpy(fs_devs->metadata_uuid, metadata_fsid, BTRFS_FSID_SIZE);
+       else if (fsid)
+               memcpy(fs_devs->metadata_uuid, fsid, BTRFS_FSID_SIZE);
+
        return fs_devs;
 }
 
@@ -375,13 +382,23 @@ static struct btrfs_device *find_device(struct btrfs_fs_devices *fs_devices,
        return NULL;
 }
 
-static noinline struct btrfs_fs_devices *find_fsid(u8 *fsid)
+static noinline struct btrfs_fs_devices *find_fsid(
+               const u8 *fsid, const u8 *metadata_fsid)
 {
        struct btrfs_fs_devices *fs_devices;
 
+       ASSERT(fsid);
+
        list_for_each_entry(fs_devices, &fs_uuids, fs_list) {
-               if (memcmp(fsid, fs_devices->fsid, BTRFS_FSID_SIZE) == 0)
-                       return fs_devices;
+               if (metadata_fsid) {
+                       if (memcmp(fsid, fs_devices->fsid, BTRFS_FSID_SIZE) == 0
+                           && memcmp(metadata_fsid, fs_devices->metadata_uuid,
+                                     BTRFS_FSID_SIZE) == 0)
+                               return fs_devices;
+               } else {
+                       if (memcmp(fsid, fs_devices->fsid, BTRFS_FSID_SIZE) == 0)
+                               return fs_devices;
+               }
        }
        return NULL;
 }
@@ -716,6 +733,13 @@ static int btrfs_open_one_device(struct btrfs_fs_devices *fs_devices,
        device->generation = btrfs_super_generation(disk_super);
 
        if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_SEEDING) {
+               if (btrfs_super_incompat_flags(disk_super) &
+                   BTRFS_FEATURE_INCOMPAT_METADATA_UUID) {
+                       pr_err(
+               "BTRFS: Invalid seeding and uuid-changed device detected\n");
+                       goto error_brelse;
+               }
+
                clear_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state);
                fs_devices->seeding = 1;
        } else {
@@ -766,10 +790,25 @@ static noinline struct btrfs_device *device_list_add(const char *path,
        struct rcu_string *name;
        u64 found_transid = btrfs_super_generation(disk_super);
        u64 devid = btrfs_stack_device_id(&disk_super->dev_item);
+       bool has_metadata_uuid = (btrfs_super_incompat_flags(disk_super) &
+               BTRFS_FEATURE_INCOMPAT_METADATA_UUID);
+       bool fsid_change_in_progress = (btrfs_super_flags(disk_super) &
+                                       BTRFS_SUPER_FLAG_CHANGING_FSID_V2);
+
+       if (has_metadata_uuid)
+               fs_devices = find_fsid(disk_super->fsid, disk_super->metadata_uuid);
+       else
+               fs_devices = find_fsid(disk_super->fsid, NULL);
 
-       fs_devices = find_fsid(disk_super->fsid);
        if (!fs_devices) {
-               fs_devices = alloc_fs_devices(disk_super->fsid);
+               if (has_metadata_uuid)
+                       fs_devices = alloc_fs_devices(disk_super->fsid,
+                                                     disk_super->metadata_uuid);
+               else
+                       fs_devices = alloc_fs_devices(disk_super->fsid, NULL);
+
+               fs_devices->fsid_change = fsid_change_in_progress;
+
                if (IS_ERR(fs_devices))
                        return ERR_CAST(fs_devices);
 
@@ -857,6 +896,35 @@ static noinline struct btrfs_device *device_list_add(const char *path,
                        return ERR_PTR(-EEXIST);
                }
 
+               /*
+                * We are going to replace the device path for a given devid,
+                * make sure it's the same device if the device is mounted
+                */
+               if (device->bdev) {
+                       struct block_device *path_bdev;
+
+                       path_bdev = lookup_bdev(path);
+                       if (IS_ERR(path_bdev)) {
+                               mutex_unlock(&fs_devices->device_list_mutex);
+                               return ERR_CAST(path_bdev);
+                       }
+
+                       if (device->bdev != path_bdev) {
+                               bdput(path_bdev);
+                               mutex_unlock(&fs_devices->device_list_mutex);
+                               btrfs_warn_in_rcu(device->fs_info,
+                       "duplicate device fsid:devid for %pU:%llu old:%s new:%s",
+                                       disk_super->fsid, devid,
+                                       rcu_str_deref(device->name), path);
+                               return ERR_PTR(-EEXIST);
+                       }
+                       bdput(path_bdev);
+                       btrfs_info_in_rcu(device->fs_info,
+                               "device fsid %pU devid %llu moved old:%s new:%s",
+                               disk_super->fsid, devid,
+                               rcu_str_deref(device->name), path);
+               }
+
                name = rcu_string_strdup(path, GFP_NOFS);
                if (!name) {
                        mutex_unlock(&fs_devices->device_list_mutex);
@@ -876,8 +944,11 @@ static noinline struct btrfs_device *device_list_add(const char *path,
         * it back. We need it to pick the disk with largest generation
         * (as above).
         */
-       if (!fs_devices->opened)
+       if (!fs_devices->opened) {
                device->generation = found_transid;
+               fs_devices->latest_generation = max_t(u64, found_transid,
+                                               fs_devices->latest_generation);
+       }
 
        fs_devices->total_devices = btrfs_super_num_devices(disk_super);
 
@@ -891,7 +962,7 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig)
        struct btrfs_device *device;
        struct btrfs_device *orig_dev;
 
-       fs_devices = alloc_fs_devices(orig->fsid);
+       fs_devices = alloc_fs_devices(orig->fsid, NULL);
        if (IS_ERR(fs_devices))
                return fs_devices;
 
@@ -1716,7 +1787,8 @@ static int btrfs_add_dev_item(struct btrfs_trans_handle *trans,
        ptr = btrfs_device_uuid(dev_item);
        write_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE);
        ptr = btrfs_device_fsid(dev_item);
-       write_extent_buffer(leaf, trans->fs_info->fsid, ptr, BTRFS_FSID_SIZE);
+       write_extent_buffer(leaf, trans->fs_info->fs_devices->metadata_uuid,
+                           ptr, BTRFS_FSID_SIZE);
        btrfs_mark_buffer_dirty(leaf);
 
        ret = 0;
@@ -2147,7 +2219,13 @@ static struct btrfs_device *btrfs_find_device_by_path(
        disk_super = (struct btrfs_super_block *)bh->b_data;
        devid = btrfs_stack_device_id(&disk_super->dev_item);
        dev_uuid = disk_super->dev_item.uuid;
-       device = btrfs_find_device(fs_info, devid, dev_uuid, disk_super->fsid);
+       if (btrfs_fs_incompat(fs_info, METADATA_UUID))
+               device = btrfs_find_device(fs_info, devid, dev_uuid,
+                               disk_super->metadata_uuid);
+       else
+               device = btrfs_find_device(fs_info, devid,
+                               dev_uuid, disk_super->fsid);
+
        brelse(bh);
        if (!device)
                device = ERR_PTR(-ENOENT);
@@ -2217,7 +2295,7 @@ static int btrfs_prepare_sprout(struct btrfs_fs_info *fs_info)
        if (!fs_devices->seeding)
                return -EINVAL;
 
-       seed_devices = alloc_fs_devices(NULL);
+       seed_devices = alloc_fs_devices(NULL, NULL);
        if (IS_ERR(seed_devices))
                return PTR_ERR(seed_devices);
 
@@ -2253,7 +2331,7 @@ static int btrfs_prepare_sprout(struct btrfs_fs_info *fs_info)
        fs_devices->seed = seed_devices;
 
        generate_random_uuid(fs_devices->fsid);
-       memcpy(fs_info->fsid, fs_devices->fsid, BTRFS_FSID_SIZE);
+       memcpy(fs_devices->metadata_uuid, fs_devices->fsid, BTRFS_FSID_SIZE);
        memcpy(disk_super->fsid, fs_devices->fsid, BTRFS_FSID_SIZE);
        mutex_unlock(&fs_devices->device_list_mutex);
 
@@ -2495,7 +2573,7 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
                 * so rename the fsid on the sysfs
                 */
                snprintf(fsid_buf, BTRFS_UUID_UNPARSED_SIZE, "%pU",
-                                               fs_info->fsid);
+                                               fs_info->fs_devices->fsid);
                if (kobject_rename(&fs_devices->fsid_kobj, fsid_buf))
                        btrfs_warn(fs_info,
                                   "sysfs: failed to create fsid for sprout");
@@ -2819,13 +2897,11 @@ int btrfs_remove_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset)
                        mutex_unlock(&fs_info->chunk_mutex);
                }
 
-               if (map->stripes[i].dev) {
-                       ret = btrfs_update_device(trans, map->stripes[i].dev);
-                       if (ret) {
-                               mutex_unlock(&fs_devices->device_list_mutex);
-                               btrfs_abort_transaction(trans, ret);
-                               goto out;
-                       }
+               ret = btrfs_update_device(trans, device);
+               if (ret) {
+                       mutex_unlock(&fs_devices->device_list_mutex);
+                       btrfs_abort_transaction(trans, ret);
+                       goto out;
                }
        }
        mutex_unlock(&fs_devices->device_list_mutex);
@@ -6267,7 +6343,7 @@ struct btrfs_device *btrfs_find_device(struct btrfs_fs_info *fs_info, u64 devid,
        cur_devices = fs_info->fs_devices;
        while (cur_devices) {
                if (!fsid ||
-                   !memcmp(cur_devices->fsid, fsid, BTRFS_FSID_SIZE)) {
+                   !memcmp(cur_devices->metadata_uuid, fsid, BTRFS_FSID_SIZE)) {
                        device = find_device(cur_devices, devid, uuid);
                        if (device)
                                return device;
@@ -6596,12 +6672,12 @@ static struct btrfs_fs_devices *open_seed_devices(struct btrfs_fs_info *fs_info,
                fs_devices = fs_devices->seed;
        }
 
-       fs_devices = find_fsid(fsid);
+       fs_devices = find_fsid(fsid, NULL);
        if (!fs_devices) {
                if (!btrfs_test_opt(fs_info, DEGRADED))
                        return ERR_PTR(-ENOENT);
 
-               fs_devices = alloc_fs_devices(fsid);
+               fs_devices = alloc_fs_devices(fsid, NULL);
                if (IS_ERR(fs_devices))
                        return fs_devices;
 
@@ -6651,7 +6727,7 @@ static int read_one_dev(struct btrfs_fs_info *fs_info,
        read_extent_buffer(leaf, fs_uuid, btrfs_device_fsid(dev_item),
                           BTRFS_FSID_SIZE);
 
-       if (memcmp(fs_uuid, fs_info->fsid, BTRFS_FSID_SIZE)) {
+       if (memcmp(fs_uuid, fs_devices->metadata_uuid, BTRFS_FSID_SIZE)) {
                fs_devices = open_seed_devices(fs_info, fs_uuid);
                if (IS_ERR(fs_devices))
                        return PTR_ERR(fs_devices);