Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a0af469b authored by Chris Mason's avatar Chris Mason
Browse files

Fix btrfs_open_devices to deal with changes since the scan ioctls



Devices can change after the scan ioctls are done, and btrfs_open_devices
needs to be able to verify them as they are opened and used by the FS.

Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent dfe25020
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1266,10 +1266,10 @@ struct btrfs_root *open_ctree(struct super_block *sb,

	btrfs_parse_options(options, tree_root, NULL);

	if (btrfs_super_num_devices(disk_super) > fs_devices->num_devices) {
	if (btrfs_super_num_devices(disk_super) > fs_devices->open_devices) {
		printk("Btrfs: wanted %llu devices, but found %llu\n",
		       (unsigned long long)btrfs_super_num_devices(disk_super),
		       (unsigned long long)fs_devices->num_devices);
		       (unsigned long long)fs_devices->open_devices);
		if (btrfs_test_opt(tree_root, DEGRADED))
			printk("continuing in degraded mode\n");
		else {
+59 −11
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ int btrfs_cleanup_fs_uuids(void)
					 dev_list);
			if (dev->bdev) {
				close_bdev_excl(dev->bdev);
				fs_devices->open_devices--;
			}
			list_del(&dev->dev_list);
			kfree(dev->name);
@@ -174,9 +175,10 @@ int btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices)
	list_for_each(cur, head) {
		device = list_entry(cur, struct btrfs_device, dev_list);
		if (!device->in_fs_metadata) {
printk("getting rid of extra dev %s\n", device->name);
			if (device->bdev)
			if (device->bdev) {
				close_bdev_excl(device->bdev);
				fs_devices->open_devices--;
			}
			list_del(&device->dev_list);
			list_del(&device->dev_alloc_list);
			fs_devices->num_devices--;
@@ -188,6 +190,7 @@ printk("getting rid of extra dev %s\n", device->name);
	mutex_unlock(&uuid_mutex);
	return 0;
}

int btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
{
	struct list_head *head = &fs_devices->devices;
@@ -199,10 +202,12 @@ int btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
		device = list_entry(cur, struct btrfs_device, dev_list);
		if (device->bdev) {
			close_bdev_excl(device->bdev);
			fs_devices->open_devices--;
		}
		device->bdev = NULL;
		device->in_fs_metadata = 0;
	}
	fs_devices->mounted = 0;
	mutex_unlock(&uuid_mutex);
	return 0;
}
@@ -214,9 +219,19 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
	struct list_head *head = &fs_devices->devices;
	struct list_head *cur;
	struct btrfs_device *device;
	int ret;
	struct block_device *latest_bdev = NULL;
	struct buffer_head *bh;
	struct btrfs_super_block *disk_super;
	u64 latest_devid = 0;
	u64 latest_transid = 0;
	u64 transid;
	u64 devid;
	int ret = 0;

	mutex_lock(&uuid_mutex);
	if (fs_devices->mounted)
		goto out;

	list_for_each(cur, head) {
		device = list_entry(cur, struct btrfs_device, dev_list);
		if (device->bdev)
@@ -229,21 +244,52 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,

		if (IS_ERR(bdev)) {
			printk("open %s failed\n", device->name);
			ret = PTR_ERR(bdev);
			goto fail;
			goto error;
		}
		set_blocksize(bdev, 4096);
		if (device->devid == fs_devices->latest_devid)
			fs_devices->latest_bdev = bdev;

		bh = __bread(bdev, BTRFS_SUPER_INFO_OFFSET / 4096, 4096);
		if (!bh)
			goto error_close;

		disk_super = (struct btrfs_super_block *)bh->b_data;
		if (strncmp((char *)(&disk_super->magic), BTRFS_MAGIC,
		    sizeof(disk_super->magic)))
			goto error_brelse;

		devid = le64_to_cpu(disk_super->dev_item.devid);
		if (devid != device->devid)
			goto error_brelse;

		transid = btrfs_super_generation(disk_super);
		if (transid > latest_transid) {
			latest_devid = devid;
			latest_transid = transid;
			latest_bdev = bdev;
		}

		device->bdev = bdev;
		device->in_fs_metadata = 0;
		fs_devices->open_devices++;
		continue;

error_brelse:
		brelse(bh);
error_close:
		close_bdev_excl(bdev);
error:
		continue;
	}
	if (fs_devices->open_devices == 0) {
		ret = -EIO;
		goto out;
	}
	fs_devices->mounted = 1;
	fs_devices->latest_bdev = latest_bdev;
	fs_devices->latest_devid = latest_devid;
	fs_devices->latest_trans = latest_transid;
out:
	mutex_unlock(&uuid_mutex);
	return 0;
fail:
	mutex_unlock(&uuid_mutex);
	btrfs_close_devices(fs_devices);
	return ret;
}

@@ -828,6 +874,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
	if (device->bdev) {
		/* one close for the device struct or super_block */
		close_bdev_excl(device->bdev);
		root->fs_info->fs_devices->open_devices--;
	}
	if (bdev) {
		/* one close for us */
@@ -914,6 +961,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
	list_add(&device->dev_alloc_list,
		 &root->fs_info->fs_devices->alloc_list);
	root->fs_info->fs_devices->num_devices++;
	root->fs_info->fs_devices->open_devices++;
out:
	btrfs_end_transaction(trans, root);
	mutex_unlock(&root->fs_info->fs_mutex);
+2 −2
Original line number Diff line number Diff line
@@ -71,16 +71,16 @@ struct btrfs_fs_devices {
	/* the device with this id has the most recent coyp of the super */
	u64 latest_devid;
	u64 latest_trans;
	u64 lowest_devid;
	u64 num_devices;
	u64 open_devices;
	struct block_device *latest_bdev;
	struct block_device *lowest_bdev;
	/* all of the devices in the FS */
	struct list_head devices;

	/* devices not currently being allocated */
	struct list_head alloc_list;
	struct list_head list;
	int mounted;
};

struct btrfs_bio_stripe {