Btrfs: keep track of max_extent_size per space_info
authorJosef Bacik <jbacik@fb.com>
Tue, 29 Sep 2015 15:40:47 +0000 (11:40 -0400)
committerChris Mason <clm@fb.com>
Thu, 22 Oct 2015 01:55:19 +0000 (18:55 -0700)
When we are heavily fragmented we can induce a lot of latency trying to make an
allocation happen that is simply not going to happen.  Thankfully we keep track
of our max_extent_size when going through the allocator, so if we get to the
point where we are exiting find_free_extent with ENOSPC then set our
space_info->max_extent_size so we can keep future allocations from having to pay
this cost.  We reset the max_extent_size whenever we release pinned bytes back
into this space info so we can redo all the work.  Thanks,

Signed-off-by: Josef Bacik <jbacik@fb.com>
Signed-off-by: Chris Mason <clm@fb.com>
fs/btrfs/ctree.h
fs/btrfs/extent-tree.c

index 16384231db82d8fabf5f504a070238516c57420a..78a8f56c7e88b6953d7ee3f803a4209f7c51c8b5 100644 (file)
@@ -1154,6 +1154,10 @@ struct btrfs_space_info {
                                   delalloc/allocations */
        u64 bytes_readonly;     /* total bytes that are read only */
 
+       u64 max_extent_size;    /* This will hold the maximum extent size of
+                                  the space info if we had an ENOSPC in the
+                                  allocator. */
+
        unsigned int full:1;    /* indicates that we cannot allocate any more
                                   chunks for this space */
        unsigned int chunk_alloc:1;     /* set if we are allocating a chunk */
index d6b5ef4b4c72f5b83e1a6ed72e1abebf8949ae19..febb5bc35a6424c81f054dd902b298112d00987a 100644 (file)
@@ -3810,6 +3810,7 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
        found->bytes_readonly = 0;
        found->bytes_may_use = 0;
        found->full = 0;
+       found->max_extent_size = 0;
        found->force_alloc = CHUNK_ALLOC_NO_FORCE;
        found->chunk_alloc = 0;
        found->flush = 0;
@@ -6158,6 +6159,7 @@ static int unpin_extent_range(struct btrfs_root *root, u64 start, u64 end,
                spin_lock(&cache->lock);
                cache->pinned -= len;
                space_info->bytes_pinned -= len;
+               space_info->max_extent_size = 0;
                percpu_counter_add(&space_info->total_bytes_pinned, -len);
                if (cache->ro) {
                        space_info->bytes_readonly += len;
@@ -6914,6 +6916,29 @@ static noinline int find_free_extent(struct btrfs_root *orig_root,
                return -ENOSPC;
        }
 
+       /*
+        * If our free space is heavily fragmented we may not be able to make
+        * big contiguous allocations, so instead of doing the expensive search
+        * for free space, simply return ENOSPC with our max_extent_size so we
+        * can go ahead and search for a more manageable chunk.
+        *
+        * If our max_extent_size is large enough for our allocation simply
+        * disable clustering since we will likely not be able to find enough
+        * space to create a cluster and induce latency trying.
+        */
+       if (unlikely(space_info->max_extent_size)) {
+               spin_lock(&space_info->lock);
+               if (space_info->max_extent_size &&
+                   num_bytes > space_info->max_extent_size) {
+                       ins->offset = space_info->max_extent_size;
+                       spin_unlock(&space_info->lock);
+                       return -ENOSPC;
+               } else if (space_info->max_extent_size) {
+                       use_cluster = false;
+               }
+               spin_unlock(&space_info->lock);
+       }
+
        /*
         * If the space info is for both data and metadata it means we have a
         * small filesystem and we can't use the clustering stuff.
@@ -7287,8 +7312,12 @@ loop:
                ret = 0;
        }
 out:
-       if (ret == -ENOSPC)
+       if (ret == -ENOSPC) {
+               spin_lock(&space_info->lock);
+               space_info->max_extent_size = max_extent_size;
+               spin_unlock(&space_info->lock);
                ins->offset = max_extent_size;
+       }
        return ret;
 }