Merge branch 'dev/pending-changes' of git://git.kernel.org/pub/scm/linux/kernel/git...
[cascardo/linux.git] / fs / btrfs / super.c
index 65c75d9..391ec44 100644 (file)
@@ -1652,8 +1652,20 @@ static int btrfs_calc_avail_data_space(struct btrfs_root *root, u64 *free_bytes)
        int i = 0, nr_devices;
        int ret;
 
+       /*
+        * We aren't under the device list lock, so this is racey-ish, but good
+        * enough for our purposes.
+        */
        nr_devices = fs_info->fs_devices->open_devices;
-       BUG_ON(!nr_devices);
+       if (!nr_devices) {
+               smp_mb();
+               nr_devices = fs_info->fs_devices->open_devices;
+               ASSERT(nr_devices);
+               if (!nr_devices) {
+                       *free_bytes = 0;
+                       return 0;
+               }
+       }
 
        devices_info = kmalloc_array(nr_devices, sizeof(*devices_info),
                               GFP_NOFS);
@@ -1678,11 +1690,17 @@ static int btrfs_calc_avail_data_space(struct btrfs_root *root, u64 *free_bytes)
        else
                min_stripe_size = BTRFS_STRIPE_LEN;
 
-       list_for_each_entry(device, &fs_devices->devices, dev_list) {
+       if (fs_info->alloc_start)
+               mutex_lock(&fs_devices->device_list_mutex);
+       rcu_read_lock();
+       list_for_each_entry_rcu(device, &fs_devices->devices, dev_list) {
                if (!device->in_fs_metadata || !device->bdev ||
                    device->is_tgtdev_for_dev_replace)
                        continue;
 
+               if (i >= nr_devices)
+                       break;
+
                avail_space = device->total_bytes - device->bytes_used;
 
                /* align with stripe_len */
@@ -1697,24 +1715,32 @@ static int btrfs_calc_avail_data_space(struct btrfs_root *root, u64 *free_bytes)
                skip_space = 1024 * 1024;
 
                /* user can set the offset in fs_info->alloc_start. */
-               if (fs_info->alloc_start + BTRFS_STRIPE_LEN <=
-                   device->total_bytes)
+               if (fs_info->alloc_start &&
+                   fs_info->alloc_start + BTRFS_STRIPE_LEN <=
+                   device->total_bytes) {
+                       rcu_read_unlock();
                        skip_space = max(fs_info->alloc_start, skip_space);
 
-               /*
-                * btrfs can not use the free space in [0, skip_space - 1],
-                * we must subtract it from the total. In order to implement
-                * it, we account the used space in this range first.
-                */
-               ret = btrfs_account_dev_extents_size(device, 0, skip_space - 1,
-                                                    &used_space);
-               if (ret) {
-                       kfree(devices_info);
-                       return ret;
-               }
+                       /*
+                        * btrfs can not use the free space in
+                        * [0, skip_space - 1], we must subtract it from the
+                        * total. In order to implement it, we account the used
+                        * space in this range first.
+                        */
+                       ret = btrfs_account_dev_extents_size(device, 0,
+                                                            skip_space - 1,
+                                                            &used_space);
+                       if (ret) {
+                               kfree(devices_info);
+                               mutex_unlock(&fs_devices->device_list_mutex);
+                               return ret;
+                       }
 
-               /* calc the free space in [0, skip_space - 1] */
-               skip_space -= used_space;
+                       rcu_read_lock();
+
+                       /* calc the free space in [0, skip_space - 1] */
+                       skip_space -= used_space;
+               }
 
                /*
                 * we can use the free space in [0, skip_space - 1], subtract
@@ -1733,6 +1759,9 @@ static int btrfs_calc_avail_data_space(struct btrfs_root *root, u64 *free_bytes)
 
                i++;
        }
+       rcu_read_unlock();
+       if (fs_info->alloc_start)
+               mutex_unlock(&fs_devices->device_list_mutex);
 
        nr_devices = i;
 
@@ -1795,8 +1824,6 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
         * holding chunk_muext to avoid allocating new chunks, holding
         * device_list_mutex to avoid the device being removed
         */
-       mutex_lock(&fs_info->fs_devices->device_list_mutex);
-       mutex_lock(&fs_info->chunk_mutex);
        rcu_read_lock();
        list_for_each_entry_rcu(found, head, list) {
                if (found->flags & BTRFS_BLOCK_GROUP_DATA) {
@@ -1832,17 +1859,12 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
        buf->f_bfree -= block_rsv->size >> bits;
        spin_unlock(&block_rsv->lock);
 
-       buf->f_bavail = total_free_data;
+       buf->f_bavail = div_u64(total_free_data, factor);
        ret = btrfs_calc_avail_data_space(fs_info->tree_root, &total_free_data);
-       if (ret) {
-               mutex_unlock(&fs_info->chunk_mutex);
-               mutex_unlock(&fs_info->fs_devices->device_list_mutex);
+       if (ret)
                return ret;
-       }
        buf->f_bavail += div_u64(total_free_data, factor);
        buf->f_bavail = buf->f_bavail >> bits;
-       mutex_unlock(&fs_info->chunk_mutex);
-       mutex_unlock(&fs_info->fs_devices->device_list_mutex);
 
        buf->f_type = BTRFS_SUPER_MAGIC;
        buf->f_bsize = dentry->d_sb->s_blocksize;