Btrfs: Shrink delay allocated space in a synchronized
authorYan, Zheng <zheng.yan@oracle.com>
Sun, 16 May 2010 14:46:25 +0000 (10:46 -0400)
committerChris Mason <chris.mason@oracle.com>
Tue, 25 May 2010 14:34:48 +0000 (10:34 -0400)
Shrink delayed allocation space in a synchronized manner is more
controllable than flushing all delay allocated space in an async
thread.

Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/ctree.h
fs/btrfs/disk-io.c
fs/btrfs/extent-tree.c
fs/btrfs/inode.c

index a68f34603b9dc9907994c9823510eb15cfd8a176..85c7b95dd2fe6d775e54559a0a61cf6f35431d95 100644 (file)
@@ -700,10 +700,6 @@ struct btrfs_space_info {
 
        struct list_head list;
 
-       /* for controlling how we free up space for allocations */
-       wait_queue_head_t flush_wait;
-       int flushing;
-
        /* for block groups in our same type */
        struct list_head block_groups[BTRFS_NR_RAID_TYPES];
        spinlock_t lock;
@@ -928,7 +924,6 @@ struct btrfs_fs_info {
        struct btrfs_workers endio_meta_write_workers;
        struct btrfs_workers endio_write_workers;
        struct btrfs_workers submit_workers;
-       struct btrfs_workers enospc_workers;
        /*
         * fixup workers take dirty pages that didn't properly go through
         * the cow mechanism and make them safe to write.  It happens
@@ -2312,6 +2307,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
                               u32 min_type);
 
 int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput);
+int btrfs_start_one_delalloc_inode(struct btrfs_root *root, int delay_iput);
 int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end,
                              struct extent_state **cached_state);
 int btrfs_writepages(struct address_space *mapping,
index feca04197d028e9d25a73dd177299319e1af351d..05f26acfd070b360d05f6871d3503d80ab233caa 100644 (file)
@@ -1759,9 +1759,6 @@ struct btrfs_root *open_ctree(struct super_block *sb,
                           min_t(u64, fs_devices->num_devices,
                           fs_info->thread_pool_size),
                           &fs_info->generic_worker);
-       btrfs_init_workers(&fs_info->enospc_workers, "enospc",
-                          fs_info->thread_pool_size,
-                          &fs_info->generic_worker);
 
        /* a higher idle thresh on the submit workers makes it much more
         * likely that bios will be send down in a sane order to the
@@ -1809,7 +1806,6 @@ struct btrfs_root *open_ctree(struct super_block *sb,
        btrfs_start_workers(&fs_info->endio_meta_workers, 1);
        btrfs_start_workers(&fs_info->endio_meta_write_workers, 1);
        btrfs_start_workers(&fs_info->endio_write_workers, 1);
-       btrfs_start_workers(&fs_info->enospc_workers, 1);
 
        fs_info->bdi.ra_pages *= btrfs_super_num_devices(disk_super);
        fs_info->bdi.ra_pages = max(fs_info->bdi.ra_pages,
@@ -2040,7 +2036,6 @@ fail_sb_buffer:
        btrfs_stop_workers(&fs_info->endio_meta_write_workers);
        btrfs_stop_workers(&fs_info->endio_write_workers);
        btrfs_stop_workers(&fs_info->submit_workers);
-       btrfs_stop_workers(&fs_info->enospc_workers);
 fail_iput:
        invalidate_inode_pages2(fs_info->btree_inode->i_mapping);
        iput(fs_info->btree_inode);
@@ -2473,7 +2468,6 @@ int close_ctree(struct btrfs_root *root)
        btrfs_stop_workers(&fs_info->endio_meta_write_workers);
        btrfs_stop_workers(&fs_info->endio_write_workers);
        btrfs_stop_workers(&fs_info->submit_workers);
-       btrfs_stop_workers(&fs_info->enospc_workers);
 
        btrfs_close_devices(fs_info->fs_devices);
        btrfs_mapping_tree_free(&fs_info->mapping_tree);
index 2c95507c9abbcc8418ede5d14be13d90ffcbc6a9..f32b1618ee6ddc4da960234a06941120b92cdf39 100644 (file)
@@ -74,6 +74,9 @@ static void dump_space_info(struct btrfs_space_info *info, u64 bytes,
 static int maybe_allocate_chunk(struct btrfs_trans_handle *trans,
                                struct btrfs_root *root,
                                struct btrfs_space_info *sinfo, u64 num_bytes);
+static int shrink_delalloc(struct btrfs_trans_handle *trans,
+                          struct btrfs_root *root,
+                          struct btrfs_space_info *sinfo, u64 to_reclaim);
 
 static noinline int
 block_group_cache_done(struct btrfs_block_group_cache *cache)
@@ -2693,7 +2696,6 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
        for (i = 0; i < BTRFS_NR_RAID_TYPES; i++)
                INIT_LIST_HEAD(&found->block_groups[i]);
        init_rwsem(&found->groups_sem);
-       init_waitqueue_head(&found->flush_wait);
        spin_lock_init(&found->lock);
        found->flags = flags & (BTRFS_BLOCK_GROUP_DATA |
                                BTRFS_BLOCK_GROUP_SYSTEM |
@@ -2907,105 +2909,6 @@ static void check_force_delalloc(struct btrfs_space_info *meta_sinfo)
                meta_sinfo->force_delalloc = 0;
 }
 
-struct async_flush {
-       struct btrfs_root *root;
-       struct btrfs_space_info *info;
-       struct btrfs_work work;
-};
-
-static noinline void flush_delalloc_async(struct btrfs_work *work)
-{
-       struct async_flush *async;
-       struct btrfs_root *root;
-       struct btrfs_space_info *info;
-
-       async = container_of(work, struct async_flush, work);
-       root = async->root;
-       info = async->info;
-
-       btrfs_start_delalloc_inodes(root, 0);
-       wake_up(&info->flush_wait);
-       btrfs_wait_ordered_extents(root, 0, 0);
-
-       spin_lock(&info->lock);
-       info->flushing = 0;
-       spin_unlock(&info->lock);
-       wake_up(&info->flush_wait);
-
-       kfree(async);
-}
-
-static void wait_on_flush(struct btrfs_space_info *info)
-{
-       DEFINE_WAIT(wait);
-       u64 used;
-
-       while (1) {
-               prepare_to_wait(&info->flush_wait, &wait,
-                               TASK_UNINTERRUPTIBLE);
-               spin_lock(&info->lock);
-               if (!info->flushing) {
-                       spin_unlock(&info->lock);
-                       break;
-               }
-
-               used = info->bytes_used + info->bytes_reserved +
-                       info->bytes_pinned + info->bytes_readonly +
-                       info->bytes_super + info->bytes_root +
-                       info->bytes_may_use + info->bytes_delalloc;
-               if (used < info->total_bytes) {
-                       spin_unlock(&info->lock);
-                       break;
-               }
-               spin_unlock(&info->lock);
-               schedule();
-       }
-       finish_wait(&info->flush_wait, &wait);
-}
-
-static void flush_delalloc(struct btrfs_root *root,
-                                struct btrfs_space_info *info)
-{
-       struct async_flush *async;
-       bool wait = false;
-
-       spin_lock(&info->lock);
-
-       if (!info->flushing)
-               info->flushing = 1;
-       else
-               wait = true;
-
-       spin_unlock(&info->lock);
-
-       if (wait) {
-               wait_on_flush(info);
-               return;
-       }
-
-       async = kzalloc(sizeof(*async), GFP_NOFS);
-       if (!async)
-               goto flush;
-
-       async->root = root;
-       async->info = info;
-       async->work.func = flush_delalloc_async;
-
-       btrfs_queue_worker(&root->fs_info->enospc_workers,
-                          &async->work);
-       wait_on_flush(info);
-       return;
-
-flush:
-       btrfs_start_delalloc_inodes(root, 0);
-       btrfs_wait_ordered_extents(root, 0, 0);
-
-       spin_lock(&info->lock);
-       info->flushing = 0;
-       spin_unlock(&info->lock);
-       wake_up(&info->flush_wait);
-}
-
 /*
  * Reserve metadata space for delalloc.
  */
@@ -3058,7 +2961,7 @@ again:
                        filemap_flush(inode->i_mapping);
                        goto again;
                } else if (flushed == 3) {
-                       flush_delalloc(root, meta_sinfo);
+                       shrink_delalloc(NULL, root, meta_sinfo, num_bytes);
                        goto again;
                }
                spin_lock(&meta_sinfo->lock);
@@ -3171,7 +3074,7 @@ again:
                }
 
                if (retries == 2) {
-                       flush_delalloc(root, meta_sinfo);
+                       shrink_delalloc(NULL, root, meta_sinfo, num_bytes);
                        goto again;
                }
                spin_lock(&meta_sinfo->lock);
@@ -3197,7 +3100,7 @@ int btrfs_check_data_free_space(struct btrfs_root *root, struct inode *inode,
 {
        struct btrfs_space_info *data_sinfo;
        u64 used;
-       int ret = 0, committed = 0, flushed = 0;
+       int ret = 0, committed = 0;
 
        /* make sure bytes are sectorsize aligned */
        bytes = (bytes + root->sectorsize - 1) & ~((u64)root->sectorsize - 1);
@@ -3217,13 +3120,6 @@ again:
        if (used + bytes > data_sinfo->total_bytes) {
                struct btrfs_trans_handle *trans;
 
-               if (!flushed) {
-                       spin_unlock(&data_sinfo->lock);
-                       flush_delalloc(root, data_sinfo);
-                       flushed = 1;
-                       goto again;
-               }
-
                /*
                 * if we don't have enough free bytes in this space then we need
                 * to alloc a new chunk.
@@ -3467,6 +3363,55 @@ static int maybe_allocate_chunk(struct btrfs_trans_handle *trans,
        return ret == 1 ? 1 : 0;
 }
 
+/*
+ * shrink metadata reservation for delalloc
+ */
+static int shrink_delalloc(struct btrfs_trans_handle *trans,
+                          struct btrfs_root *root,
+                          struct btrfs_space_info *sinfo, u64 to_reclaim)
+{
+       u64 reserved;
+       u64 max_reclaim;
+       u64 reclaimed = 0;
+       int pause = 1;
+       int ret;
+
+       spin_lock(&sinfo->lock);
+       reserved = sinfo->bytes_delalloc;
+       spin_unlock(&sinfo->lock);
+
+       if (reserved == 0)
+               return 0;
+
+       max_reclaim = min(reserved, to_reclaim);
+
+       while (1) {
+               ret = btrfs_start_one_delalloc_inode(root, trans ? 1 : 0);
+               if (!ret) {
+                       __set_current_state(TASK_INTERRUPTIBLE);
+                       schedule_timeout(pause);
+                       pause <<= 1;
+                       if (pause > HZ / 10)
+                               pause = HZ / 10;
+               } else {
+                       pause = 1;
+               }
+
+               spin_lock(&sinfo->lock);
+               if (reserved > sinfo->bytes_delalloc)
+                       reclaimed = reserved - sinfo->bytes_delalloc;
+               reserved = sinfo->bytes_delalloc;
+               spin_unlock(&sinfo->lock);
+
+               if (reserved == 0 || reclaimed >= max_reclaim)
+                       break;
+
+               if (trans && trans->transaction->blocked)
+                       return -EAGAIN;
+       }
+       return reclaimed >= to_reclaim;
+}
+
 static int update_block_group(struct btrfs_trans_handle *trans,
                              struct btrfs_root *root,
                              u64 bytenr, u64 num_bytes, int alloc,
index 2bfdc641d4e3ba046f3afd89400f362ec959e46b..d53cad1afe26a8e234e77c76e3a6bef2b8d0b7b0 100644 (file)
@@ -5611,6 +5611,38 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
        return 0;
 }
 
+int btrfs_start_one_delalloc_inode(struct btrfs_root *root, int delay_iput)
+{
+       struct btrfs_inode *binode;
+       struct inode *inode = NULL;
+
+       spin_lock(&root->fs_info->delalloc_lock);
+       while (!list_empty(&root->fs_info->delalloc_inodes)) {
+               binode = list_entry(root->fs_info->delalloc_inodes.next,
+                                   struct btrfs_inode, delalloc_inodes);
+               inode = igrab(&binode->vfs_inode);
+               if (inode) {
+                       list_move_tail(&binode->delalloc_inodes,
+                                      &root->fs_info->delalloc_inodes);
+                       break;
+               }
+
+               list_del_init(&binode->delalloc_inodes);
+               cond_resched_lock(&root->fs_info->delalloc_lock);
+       }
+       spin_unlock(&root->fs_info->delalloc_lock);
+
+       if (inode) {
+               write_inode_now(inode, 0);
+               if (delay_iput)
+                       btrfs_add_delayed_iput(inode);
+               else
+                       iput(inode);
+               return 1;
+       }
+       return 0;
+}
+
 static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
                         const char *symname)
 {