When the device is added or if the device pointer is retrieved
for writing, make sure that device_list_mutex is held. Also
make sure it is held when we check fs_devices::opened and
fs_devices::total_devices is updated.

Signed-off-by: Anand Jain <anand.j...@oracle.com>
---
 fs/btrfs/volumes.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 4edba7c5c050..0fdb9b717398 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -765,21 +765,26 @@ static noinline struct btrfs_device 
*device_list_add(const char *path,
                if (IS_ERR(fs_devices))
                        return ERR_CAST(fs_devices);
 
+               mutex_lock(&fs_devices->device_list_mutex);
                list_add(&fs_devices->fs_list, &fs_uuids);
 
                device = NULL;
        } else {
+               mutex_lock(&fs_devices->device_list_mutex);
                device = find_device(fs_devices, devid,
                                disk_super->dev_item.uuid);
        }
 
        if (!device) {
-               if (fs_devices->opened)
+               if (fs_devices->opened) {
+                       mutex_unlock(&fs_devices->device_list_mutex);
                        return ERR_PTR(-EBUSY);
+               }
 
                device = btrfs_alloc_device(NULL, &devid,
                                            disk_super->dev_item.uuid);
                if (IS_ERR(device)) {
+                       mutex_unlock(&fs_devices->device_list_mutex);
                        /* we can safely leave the fs_devices entry around */
                        return device;
                }
@@ -787,14 +792,13 @@ static noinline struct btrfs_device 
*device_list_add(const char *path,
                name = rcu_string_strdup(path, GFP_NOFS);
                if (!name) {
                        btrfs_free_device(device);
+                       mutex_unlock(&fs_devices->device_list_mutex);
                        return ERR_PTR(-ENOMEM);
                }
                rcu_assign_pointer(device->name, name);
 
-               mutex_lock(&fs_devices->device_list_mutex);
                list_add_rcu(&device->dev_list, &fs_devices->devices);
                fs_devices->num_devices++;
-               mutex_unlock(&fs_devices->device_list_mutex);
 
                device->fs_devices = fs_devices;
                *new_device_added = true;
@@ -841,12 +845,15 @@ static noinline struct btrfs_device 
*device_list_add(const char *path,
                         * with larger generation number or the last-in if
                         * generation are equal.
                         */
+                       mutex_unlock(&fs_devices->device_list_mutex);
                        return ERR_PTR(-EEXIST);
                }
 
                name = rcu_string_strdup(path, GFP_NOFS);
-               if (!name)
+               if (!name) {
+                       mutex_unlock(&fs_devices->device_list_mutex);
                        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)) {
@@ -866,6 +873,7 @@ static noinline struct btrfs_device *device_list_add(const 
char *path,
 
        fs_devices->total_devices = btrfs_super_num_devices(disk_super);
 
+       mutex_unlock(&fs_devices->device_list_mutex);
        return device;
 }
 
-- 
2.7.0

--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to