Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 19 Mar 2009 21:49:55 +0000 (14:49 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 19 Mar 2009 21:49:55 +0000 (14:49 -0700)
* git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable:
  Btrfs: Clear space_info full when adding new devices
  Btrfs: Fix locking around adding new space_info

fs/btrfs/ctree.h
fs/btrfs/extent-tree.c
fs/btrfs/volumes.c

index 82491ba8fa40a451d0396f7f0b13c7a625414548..5e1d4e30e9d863a6c66c36549136bb9abd088738 100644 (file)
@@ -784,7 +784,14 @@ struct btrfs_fs_info {
        struct list_head dirty_cowonly_roots;
 
        struct btrfs_fs_devices *fs_devices;
+
+       /*
+        * the space_info list is almost entirely read only.  It only changes
+        * when we add a new raid type to the FS, and that happens
+        * very rarely.  RCU is used to protect it.
+        */
        struct list_head space_info;
+
        spinlock_t delalloc_lock;
        spinlock_t new_trans_lock;
        u64 delalloc_bytes;
@@ -1797,6 +1804,8 @@ int btrfs_cleanup_reloc_trees(struct btrfs_root *root);
 int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len);
 u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags);
 void btrfs_set_inode_space_info(struct btrfs_root *root, struct inode *ionde);
+void btrfs_clear_space_info_full(struct btrfs_fs_info *info);
+
 int btrfs_check_metadata_free_space(struct btrfs_root *root);
 int btrfs_check_data_free_space(struct btrfs_root *root, struct inode *inode,
                                u64 bytes);
index 9abf81f71c46582db63b89c26d4e952651e3a1ee..fefe83ad20595eba078f6480781eefb0fe81c659 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/writeback.h>
 #include <linux/blkdev.h>
 #include <linux/sort.h>
+#include <linux/rcupdate.h>
 #include "compat.h"
 #include "hash.h"
 #include "crc32c.h"
@@ -330,13 +331,33 @@ static struct btrfs_space_info *__find_space_info(struct btrfs_fs_info *info,
 {
        struct list_head *head = &info->space_info;
        struct btrfs_space_info *found;
-       list_for_each_entry(found, head, list) {
-               if (found->flags == flags)
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(found, head, list) {
+               if (found->flags == flags) {
+                       rcu_read_unlock();
                        return found;
+               }
        }
+       rcu_read_unlock();
        return NULL;
 }
 
+/*
+ * after adding space to the filesystem, we need to clear the full flags
+ * on all the space infos.
+ */
+void btrfs_clear_space_info_full(struct btrfs_fs_info *info)
+{
+       struct list_head *head = &info->space_info;
+       struct btrfs_space_info *found;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(found, head, list)
+               found->full = 0;
+       rcu_read_unlock();
+}
+
 static u64 div_factor(u64 num, int factor)
 {
        if (factor == 10)
@@ -1903,7 +1924,6 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
        if (!found)
                return -ENOMEM;
 
-       list_add(&found->list, &info->space_info);
        INIT_LIST_HEAD(&found->block_groups);
        init_rwsem(&found->groups_sem);
        spin_lock_init(&found->lock);
@@ -1917,6 +1937,7 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
        found->full = 0;
        found->force_alloc = 0;
        *space_info = found;
+       list_add_rcu(&found->list, &info->space_info);
        return 0;
 }
 
@@ -6320,6 +6341,7 @@ out:
 int btrfs_free_block_groups(struct btrfs_fs_info *info)
 {
        struct btrfs_block_group_cache *block_group;
+       struct btrfs_space_info *space_info;
        struct rb_node *n;
 
        spin_lock(&info->block_group_cache_lock);
@@ -6341,6 +6363,23 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
                spin_lock(&info->block_group_cache_lock);
        }
        spin_unlock(&info->block_group_cache_lock);
+
+       /* now that all the block groups are freed, go through and
+        * free all the space_info structs.  This is only called during
+        * the final stages of unmount, and so we know nobody is
+        * using them.  We call synchronize_rcu() once before we start,
+        * just to be on the safe side.
+        */
+       synchronize_rcu();
+
+       while(!list_empty(&info->space_info)) {
+               space_info = list_entry(info->space_info.next,
+                                       struct btrfs_space_info,
+                                       list);
+
+               list_del(&space_info->list);
+               kfree(space_info);
+       }
        return 0;
 }
 
index 1316139bf9e8209750ba671edd042df4976307ab..dd06e18e5aac7518191e02791073345e6a0f51cd 100644 (file)
@@ -1374,6 +1374,12 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
                ret = btrfs_add_device(trans, root, device);
        }
 
+       /*
+        * we've got more storage, clear any full flags on the space
+        * infos
+        */
+       btrfs_clear_space_info_full(root->fs_info);
+
        unlock_chunks(root);
        btrfs_commit_transaction(trans, root);
 
@@ -1459,6 +1465,8 @@ static int __btrfs_grow_device(struct btrfs_trans_handle *trans,
        device->fs_devices->total_rw_bytes += diff;
 
        device->total_bytes = new_size;
+       btrfs_clear_space_info_full(device->dev_root->fs_info);
+
        return btrfs_update_device(trans, device);
 }