btrfs: alloc_chunk: fix DUP stripe size handling
[sfrench/cifs-2.6.git] / fs / btrfs / volumes.c
index 7d33bd2a0fb4f853a702c05302125a54785c798d..b2d05c6b1c5672a638ba8752333c10508e366885 100644 (file)
@@ -604,8 +604,16 @@ static void pending_bios_fn(struct btrfs_work *work)
        run_scheduled_bios(device);
 }
 
-
-static void btrfs_free_stale_devices(struct btrfs_device *skip_dev)
+/*
+ *  Search and remove all stale (devices which are not mounted) devices.
+ *  When both inputs are NULL, it will search and release all stale devices.
+ *  path:      Optional. When provided will it release all unmounted devices
+ *             matching this path only.
+ *  skip_dev:  Optional. Will skip this device when searching for the stale
+ *             devices.
+ */
+static void btrfs_free_stale_devices(const char *path,
+                                    struct btrfs_device *skip_dev)
 {
        struct btrfs_fs_devices *fs_devs, *tmp_fs_devs;
        struct btrfs_device *dev, *tmp_dev;
@@ -619,19 +627,15 @@ static void btrfs_free_stale_devices(struct btrfs_device *skip_dev)
                                         &fs_devs->devices, dev_list) {
                        int not_found = 0;
 
-                       if (skip_dev && (skip_dev == dev || !dev->name))
+                       if (skip_dev && skip_dev == dev)
+                               continue;
+                       if (path && !dev->name)
                                continue;
 
-                       /*
-                        * Todo: This won't be enough. What if the same device
-                        * comes back (with new uuid and) with its mapper path?
-                        * But for now, this does help as mostly an admin will
-                        * either use mapper or non mapper path throughout.
-                        */
                        rcu_read_lock();
-                       if (skip_dev)
+                       if (path)
                                not_found = strcmp(rcu_str_deref(dev->name),
-                                                  rcu_str_deref(skip_dev->name));
+                                                  path);
                        rcu_read_unlock();
                        if (not_found)
                                continue;
@@ -641,6 +645,7 @@ static void btrfs_free_stale_devices(struct btrfs_device *skip_dev)
                                btrfs_sysfs_remove_fsid(fs_devs);
                                list_del(&fs_devs->list);
                                free_fs_devices(fs_devs);
+                               break;
                        } else {
                                fs_devs->num_devices--;
                                list_del(&dev->dev_list);
@@ -720,25 +725,23 @@ error_brelse:
  * Add new device to list of registered devices
  *
  * Returns:
- * 1   - first time device is seen
- * 0   - device already known
- * < 0 - error
+ * device pointer which was just added or updated when successful
+ * error pointer when failed
  */
-static noinline int device_list_add(const char *path,
-                          struct btrfs_super_block *disk_super,
-                          u64 devid, struct btrfs_fs_devices **fs_devices_ret)
+static noinline struct btrfs_device *device_list_add(const char *path,
+                          struct btrfs_super_block *disk_super)
 {
        struct btrfs_device *device;
        struct btrfs_fs_devices *fs_devices;
        struct rcu_string *name;
-       int ret = 0;
        u64 found_transid = btrfs_super_generation(disk_super);
+       u64 devid = btrfs_stack_device_id(&disk_super->dev_item);
 
        fs_devices = find_fsid(disk_super->fsid);
        if (!fs_devices) {
                fs_devices = alloc_fs_devices(disk_super->fsid);
                if (IS_ERR(fs_devices))
-                       return PTR_ERR(fs_devices);
+                       return ERR_CAST(fs_devices);
 
                list_add(&fs_devices->list, &fs_uuids);
 
@@ -750,19 +753,19 @@ static noinline int device_list_add(const char *path,
 
        if (!device) {
                if (fs_devices->opened)
-                       return -EBUSY;
+                       return ERR_PTR(-EBUSY);
 
                device = btrfs_alloc_device(NULL, &devid,
                                            disk_super->dev_item.uuid);
                if (IS_ERR(device)) {
                        /* we can safely leave the fs_devices entry around */
-                       return PTR_ERR(device);
+                       return device;
                }
 
                name = rcu_string_strdup(path, GFP_NOFS);
                if (!name) {
                        free_device(device);
-                       return -ENOMEM;
+                       return ERR_PTR(-ENOMEM);
                }
                rcu_assign_pointer(device->name, name);
 
@@ -771,9 +774,16 @@ static noinline int device_list_add(const char *path,
                fs_devices->num_devices++;
                mutex_unlock(&fs_devices->device_list_mutex);
 
-               ret = 1;
                device->fs_devices = fs_devices;
-               btrfs_free_stale_devices(device);
+               btrfs_free_stale_devices(path, device);
+
+               if (disk_super->label[0])
+                       pr_info("BTRFS: device label %s devid %llu transid %llu %s\n",
+                               disk_super->label, devid, found_transid, path);
+               else
+                       pr_info("BTRFS: device fsid %pU devid %llu transid %llu %s\n",
+                               disk_super->fsid, devid, found_transid, path);
+
        } else if (!device->name || strcmp(device->name->str, path)) {
                /*
                 * When FS is already mounted.
@@ -809,12 +819,12 @@ static noinline int device_list_add(const char *path,
                         * with larger generation number or the last-in if
                         * generation are equal.
                         */
-                       return -EEXIST;
+                       return ERR_PTR(-EEXIST);
                }
 
                name = rcu_string_strdup(path, GFP_NOFS);
                if (!name)
-                       return -ENOMEM;
+                       return ERR_PTR(-ENOMEM);
                rcu_string_free(device->name);
                rcu_assign_pointer(device->name, name);
                if (test_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state)) {
@@ -832,9 +842,9 @@ static noinline int device_list_add(const char *path,
        if (!fs_devices->opened)
                device->generation = found_transid;
 
-       *fs_devices_ret = fs_devices;
+       fs_devices->total_devices = btrfs_super_num_devices(disk_super);
 
-       return ret;
+       return device;
 }
 
 static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig)
@@ -1169,12 +1179,10 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
                          struct btrfs_fs_devices **fs_devices_ret)
 {
        struct btrfs_super_block *disk_super;
+       struct btrfs_device *device;
        struct block_device *bdev;
        struct page *page;
-       int ret;
-       u64 devid;
-       u64 transid;
-       u64 total_devices;
+       int ret = 0;
        u64 bytenr;
 
        /*
@@ -1198,22 +1206,11 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
                goto error_bdev_put;
        }
 
-       devid = btrfs_stack_device_id(&disk_super->dev_item);
-       transid = btrfs_super_generation(disk_super);
-       total_devices = btrfs_super_num_devices(disk_super);
-
-       ret = device_list_add(path, disk_super, devid, fs_devices_ret);
-       if (ret > 0) {
-               if (disk_super->label[0])
-                       pr_info("BTRFS: device label %s ", disk_super->label);
-               else
-                       pr_info("BTRFS: device fsid %pU ", disk_super->fsid);
-
-               pr_cont("devid %llu transid %llu %s\n", devid, transid, path);
-               ret = 0;
-       }
-       if (!ret && fs_devices_ret)
-               (*fs_devices_ret)->total_devices = total_devices;
+       device = device_list_add(path, disk_super);
+       if (IS_ERR(device))
+               ret = PTR_ERR(device);
+       else
+               *fs_devices_ret = device->fs_devices;
 
        btrfs_release_disk_super(page);
 
@@ -4832,10 +4829,13 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
        ndevs = min(ndevs, devs_max);
 
        /*
-        * the primary goal is to maximize the number of stripes, so use as many
-        * devices as possible, even if the stripes are not maximum sized.
+        * The primary goal is to maximize the number of stripes, so use as
+        * many devices as possible, even if the stripes are not maximum sized.
+        *
+        * The DUP profile stores more than one stripe per device, the
+        * max_avail is the total size so we have to adjust.
         */
-       stripe_size = devices_info[ndevs-1].max_avail;
+       stripe_size = div_u64(devices_info[ndevs - 1].max_avail, dev_stripes);
        num_stripes = ndevs * dev_stripes;
 
        /*
@@ -4870,8 +4870,6 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
                        stripe_size = devices_info[ndevs-1].max_avail;
        }
 
-       stripe_size = div_u64(stripe_size, dev_stripes);
-
        /* align to BTRFS_STRIPE_LEN */
        stripe_size = round_down(stripe_size, BTRFS_STRIPE_LEN);