summaryrefslogtreecommitdiffstats
path: root/fs/btrfs/volumes.c
diff options
context:
space:
mode:
authorChris Mason2008-07-08 20:19:17 +0200
committerChris Mason2008-09-25 17:04:04 +0200
commit7d9eb12c8739e7dc80c78c6b3596f912ecd8f941 (patch)
tree000608285b44920f22e0888753b36299bc762cef /fs/btrfs/volumes.c
parentBtrfs: Fix deadlock while searching for dead roots on mount (diff)
downloadkernel-qcow2-linux-7d9eb12c8739e7dc80c78c6b3596f912ecd8f941.tar.gz
kernel-qcow2-linux-7d9eb12c8739e7dc80c78c6b3596f912ecd8f941.tar.xz
kernel-qcow2-linux-7d9eb12c8739e7dc80c78c6b3596f912ecd8f941.zip
Btrfs: Add locking around volume management (device add/remove/balance)
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/volumes.c')
-rw-r--r--fs/btrfs/volumes.c58
1 files changed, 44 insertions, 14 deletions
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 4e7cee27aab5..5e6ee7a6f738 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -56,6 +56,18 @@ void btrfs_unlock_volumes(void)
mutex_unlock(&uuid_mutex);
}
+static void lock_chunks(struct btrfs_root *root)
+{
+ mutex_lock(&root->fs_info->alloc_mutex);
+ mutex_lock(&root->fs_info->chunk_mutex);
+}
+
+static void unlock_chunks(struct btrfs_root *root)
+{
+ mutex_unlock(&root->fs_info->alloc_mutex);
+ mutex_unlock(&root->fs_info->chunk_mutex);
+}
+
int btrfs_cleanup_fs_uuids(void)
{
struct btrfs_fs_devices *fs_devices;
@@ -822,6 +834,7 @@ static int btrfs_rm_dev_item(struct btrfs_root *root,
key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
key.type = BTRFS_DEV_ITEM_KEY;
key.offset = device->devid;
+ lock_chunks(root);
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
if (ret < 0)
@@ -856,6 +869,7 @@ static int btrfs_rm_dev_item(struct btrfs_root *root,
total_bytes - 1);
out:
btrfs_free_path(path);
+ unlock_chunks(root);
btrfs_commit_transaction(trans, root);
return ret;
}
@@ -870,9 +884,8 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
u64 devid;
int ret = 0;
- mutex_lock(&root->fs_info->alloc_mutex);
- mutex_lock(&root->fs_info->chunk_mutex);
mutex_lock(&uuid_mutex);
+ mutex_lock(&root->fs_info->volume_mutex);
all_avail = root->fs_info->avail_data_alloc_bits |
root->fs_info->avail_system_alloc_bits |
@@ -988,9 +1001,8 @@ error_close:
if (bdev)
close_bdev_excl(bdev);
out:
+ mutex_unlock(&root->fs_info->volume_mutex);
mutex_unlock(&uuid_mutex);
- mutex_unlock(&root->fs_info->chunk_mutex);
- mutex_unlock(&root->fs_info->alloc_mutex);
return ret;
}
@@ -1010,10 +1022,10 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
return -EIO;
}
- mutex_lock(&root->fs_info->alloc_mutex);
- mutex_lock(&root->fs_info->chunk_mutex);
+ mutex_lock(&root->fs_info->volume_mutex);
trans = btrfs_start_transaction(root, 1);
+ lock_chunks(root);
devices = &root->fs_info->fs_devices->devices;
list_for_each(cur, devices) {
device = list_entry(cur, struct btrfs_device, dev_list);
@@ -1065,9 +1077,9 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
root->fs_info->fs_devices->num_devices++;
root->fs_info->fs_devices->open_devices++;
out:
+ unlock_chunks(root);
btrfs_end_transaction(trans, root);
- mutex_unlock(&root->fs_info->chunk_mutex);
- mutex_unlock(&root->fs_info->alloc_mutex);
+ mutex_unlock(&root->fs_info->volume_mutex);
return ret;
@@ -1122,7 +1134,7 @@ out:
return ret;
}
-int btrfs_grow_device(struct btrfs_trans_handle *trans,
+static int __btrfs_grow_device(struct btrfs_trans_handle *trans,
struct btrfs_device *device, u64 new_size)
{
struct btrfs_super_block *super_copy =
@@ -1134,6 +1146,16 @@ int btrfs_grow_device(struct btrfs_trans_handle *trans,
return btrfs_update_device(trans, device);
}
+int btrfs_grow_device(struct btrfs_trans_handle *trans,
+ struct btrfs_device *device, u64 new_size)
+{
+ int ret;
+ lock_chunks(device->dev_root);
+ ret = __btrfs_grow_device(trans, device, new_size);
+ unlock_chunks(device->dev_root);
+ return ret;
+}
+
static int btrfs_free_chunk(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 chunk_tree, u64 chunk_objectid,
@@ -1234,6 +1256,8 @@ int btrfs_relocate_chunk(struct btrfs_root *root,
trans = btrfs_start_transaction(root, 1);
BUG_ON(!trans);
+ lock_chunks(root);
+
/*
* step two, delete the device extents and the
* chunk tree entries
@@ -1278,6 +1302,7 @@ int btrfs_relocate_chunk(struct btrfs_root *root,
/* once for us */
free_extent_map(em);
+ unlock_chunks(root);
btrfs_end_transaction(trans, root);
return 0;
}
@@ -1308,8 +1333,7 @@ int btrfs_balance(struct btrfs_root *dev_root)
struct btrfs_key found_key;
- BUG(); /* FIXME, needs locking */
-
+ mutex_lock(&dev_root->fs_info->volume_mutex);
dev_root = dev_root->fs_info->dev_root;
/* step one make some room on all the devices */
@@ -1355,13 +1379,14 @@ int btrfs_balance(struct btrfs_root *dev_root)
ret = btrfs_previous_item(chunk_root, path, 0,
BTRFS_CHUNK_ITEM_KEY);
- if (ret) {
+ if (ret)
break;
- }
+
btrfs_item_key_to_cpu(path->nodes[0], &found_key,
path->slots[0]);
if (found_key.objectid != key.objectid)
break;
+
chunk = btrfs_item_ptr(path->nodes[0],
path->slots[0],
struct btrfs_chunk);
@@ -1370,16 +1395,17 @@ int btrfs_balance(struct btrfs_root *dev_root)
if (key.offset == 0)
break;
+ btrfs_release_path(chunk_root, path);
ret = btrfs_relocate_chunk(chunk_root,
chunk_root->root_key.objectid,
found_key.objectid,
found_key.offset);
BUG_ON(ret);
- btrfs_release_path(chunk_root, path);
}
ret = 0;
error:
btrfs_free_path(path);
+ mutex_unlock(&dev_root->fs_info->volume_mutex);
return ret;
}
@@ -1419,14 +1445,18 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
path->reada = 2;
+ lock_chunks(root);
+
device->total_bytes = new_size;
ret = btrfs_update_device(trans, device);
if (ret) {
+ unlock_chunks(root);
btrfs_end_transaction(trans, root);
goto done;
}
WARN_ON(diff > old_total);
btrfs_set_super_total_bytes(super_copy, old_total - diff);
+ unlock_chunks(root);
btrfs_end_transaction(trans, root);
key.objectid = device->devid;