Fix btrfs_open_devices to deal with changes since the scan ioctls
authorChris Mason <chris.mason@oracle.com>
Tue, 13 May 2008 20:03:06 +0000 (16:03 -0400)
committerChris Mason <chris.mason@oracle.com>
Thu, 25 Sep 2008 15:04:03 +0000 (11:04 -0400)
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: Chris Mason <chris.mason@oracle.com>
fs/btrfs/disk-io.c
fs/btrfs/volumes.c
fs/btrfs/volumes.h

index 38b0d9ecda6a89cc16cf8e63811ebc0d60151383..264f297260f85c96909c11d55ac9a6731cda5226 100644 (file)
@@ -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 {
index 43f74d17bceae8466f092ab7453a87c73722c432..501d23d3ebfd2f0c0a23ad4672af2d445a9391a7 100644 (file)
@@ -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 @@ again:
        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);
index 454fe81033296d29495d7b0603208abfbb31e688..4df6b1608f91b13a54f59db973a81353ed57641f 100644 (file)
@@ -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 {