aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2009-03-10 12:39:20 -0400
committerChris Mason <chris.mason@oracle.com>2009-03-10 12:39:20 -0400
commit4184ea7f908d95f329febc3665cf66da8568b467 (patch)
treeef16aabf4604c9c1aba23e222ff7fb4510052ebb /fs
parentb9447ef80bd301b932ac4d85c9622e929de5fd62 (diff)
Btrfs: Fix locking around adding new space_info
Storage allocated to different raid levels in btrfs is tracked by a btrfs_space_info structure, and all of the current space_infos are collected into a list_head. Most filesystems have 3 or 4 of these structs total, and the list is only changed when new raid levels are added or at unmount time. This commit adds rcu locking on the list head, and properly frees things at unmount time. It also clears the space_info->full flag whenever new space is added to the FS. The locking for the space info list goes like this: reads: protected by rcu_read_lock() writes: protected by the chunk_mutex At unmount time we don't need special locking because all the readers are gone. Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/ctree.h9
-rw-r--r--fs/btrfs/extent-tree.c45
-rw-r--r--fs/btrfs/volumes.c2
3 files changed, 53 insertions, 3 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 82491ba8fa40..5e1d4e30e9d8 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -784,7 +784,14 @@ struct btrfs_fs_info {
784 struct list_head dirty_cowonly_roots; 784 struct list_head dirty_cowonly_roots;
785 785
786 struct btrfs_fs_devices *fs_devices; 786 struct btrfs_fs_devices *fs_devices;
787
788 /*
789 * the space_info list is almost entirely read only. It only changes
790 * when we add a new raid type to the FS, and that happens
791 * very rarely. RCU is used to protect it.
792 */
787 struct list_head space_info; 793 struct list_head space_info;
794
788 spinlock_t delalloc_lock; 795 spinlock_t delalloc_lock;
789 spinlock_t new_trans_lock; 796 spinlock_t new_trans_lock;
790 u64 delalloc_bytes; 797 u64 delalloc_bytes;
@@ -1797,6 +1804,8 @@ int btrfs_cleanup_reloc_trees(struct btrfs_root *root);
1797int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len); 1804int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len);
1798u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags); 1805u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags);
1799void btrfs_set_inode_space_info(struct btrfs_root *root, struct inode *ionde); 1806void btrfs_set_inode_space_info(struct btrfs_root *root, struct inode *ionde);
1807void btrfs_clear_space_info_full(struct btrfs_fs_info *info);
1808
1800int btrfs_check_metadata_free_space(struct btrfs_root *root); 1809int btrfs_check_metadata_free_space(struct btrfs_root *root);
1801int btrfs_check_data_free_space(struct btrfs_root *root, struct inode *inode, 1810int btrfs_check_data_free_space(struct btrfs_root *root, struct inode *inode,
1802 u64 bytes); 1811 u64 bytes);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 9abf81f71c46..fefe83ad2059 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -20,6 +20,7 @@
20#include <linux/writeback.h> 20#include <linux/writeback.h>
21#include <linux/blkdev.h> 21#include <linux/blkdev.h>
22#include <linux/sort.h> 22#include <linux/sort.h>
23#include <linux/rcupdate.h>
23#include "compat.h" 24#include "compat.h"
24#include "hash.h" 25#include "hash.h"
25#include "crc32c.h" 26#include "crc32c.h"
@@ -330,13 +331,33 @@ static struct btrfs_space_info *__find_space_info(struct btrfs_fs_info *info,
330{ 331{
331 struct list_head *head = &info->space_info; 332 struct list_head *head = &info->space_info;
332 struct btrfs_space_info *found; 333 struct btrfs_space_info *found;
333 list_for_each_entry(found, head, list) { 334
334 if (found->flags == flags) 335 rcu_read_lock();
336 list_for_each_entry_rcu(found, head, list) {
337 if (found->flags == flags) {
338 rcu_read_unlock();
335 return found; 339 return found;
340 }
336 } 341 }
342 rcu_read_unlock();
337 return NULL; 343 return NULL;
338} 344}
339 345
346/*
347 * after adding space to the filesystem, we need to clear the full flags
348 * on all the space infos.
349 */
350void btrfs_clear_space_info_full(struct btrfs_fs_info *info)
351{
352 struct list_head *head = &info->space_info;
353 struct btrfs_space_info *found;
354
355 rcu_read_lock();
356 list_for_each_entry_rcu(found, head, list)
357 found->full = 0;
358 rcu_read_unlock();
359}
360
340static u64 div_factor(u64 num, int factor) 361static u64 div_factor(u64 num, int factor)
341{ 362{
342 if (factor == 10) 363 if (factor == 10)
@@ -1903,7 +1924,6 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
1903 if (!found) 1924 if (!found)
1904 return -ENOMEM; 1925 return -ENOMEM;
1905 1926
1906 list_add(&found->list, &info->space_info);
1907 INIT_LIST_HEAD(&found->block_groups); 1927 INIT_LIST_HEAD(&found->block_groups);
1908 init_rwsem(&found->groups_sem); 1928 init_rwsem(&found->groups_sem);
1909 spin_lock_init(&found->lock); 1929 spin_lock_init(&found->lock);
@@ -1917,6 +1937,7 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
1917 found->full = 0; 1937 found->full = 0;
1918 found->force_alloc = 0; 1938 found->force_alloc = 0;
1919 *space_info = found; 1939 *space_info = found;
1940 list_add_rcu(&found->list, &info->space_info);
1920 return 0; 1941 return 0;
1921} 1942}
1922 1943
@@ -6320,6 +6341,7 @@ out:
6320int btrfs_free_block_groups(struct btrfs_fs_info *info) 6341int btrfs_free_block_groups(struct btrfs_fs_info *info)
6321{ 6342{
6322 struct btrfs_block_group_cache *block_group; 6343 struct btrfs_block_group_cache *block_group;
6344 struct btrfs_space_info *space_info;
6323 struct rb_node *n; 6345 struct rb_node *n;
6324 6346
6325 spin_lock(&info->block_group_cache_lock); 6347 spin_lock(&info->block_group_cache_lock);
@@ -6341,6 +6363,23 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
6341 spin_lock(&info->block_group_cache_lock); 6363 spin_lock(&info->block_group_cache_lock);
6342 } 6364 }
6343 spin_unlock(&info->block_group_cache_lock); 6365 spin_unlock(&info->block_group_cache_lock);
6366
6367 /* now that all the block groups are freed, go through and
6368 * free all the space_info structs. This is only called during
6369 * the final stages of unmount, and so we know nobody is
6370 * using them. We call synchronize_rcu() once before we start,
6371 * just to be on the safe side.
6372 */
6373 synchronize_rcu();
6374
6375 while(!list_empty(&info->space_info)) {
6376 space_info = list_entry(info->space_info.next,
6377 struct btrfs_space_info,
6378 list);
6379
6380 list_del(&space_info->list);
6381 kfree(space_info);
6382 }
6344 return 0; 6383 return 0;
6345} 6384}
6346 6385
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 1316139bf9e8..7aa3810d7f69 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -1459,6 +1459,8 @@ static int __btrfs_grow_device(struct btrfs_trans_handle *trans,
1459 device->fs_devices->total_rw_bytes += diff; 1459 device->fs_devices->total_rw_bytes += diff;
1460 1460
1461 device->total_bytes = new_size; 1461 device->total_bytes = new_size;
1462 btrfs_clear_space_info_full(device->dev_root->fs_info);
1463
1462 return btrfs_update_device(trans, device); 1464 return btrfs_update_device(trans, device);
1463} 1465}
1464 1466