Btrfs: fix data space leak fix
[deliverable/linux.git] / fs / btrfs / free-space-cache.c
index ab8cad8b46c926ce61df9cb8777f03d39838ae23..5c2caad76212d2b5bfcc4329e255978525d6f5ed 100644 (file)
@@ -238,6 +238,7 @@ static void unlink_free_space(struct btrfs_block_group_cache *block_group,
 {
        rb_erase(&info->offset_index, &block_group->free_space_offset);
        block_group->free_extents--;
+       block_group->free_space -= info->bytes;
 }
 
 static int link_free_space(struct btrfs_block_group_cache *block_group,
@@ -251,13 +252,16 @@ static int link_free_space(struct btrfs_block_group_cache *block_group,
        if (ret)
                return ret;
 
+       block_group->free_space += info->bytes;
        block_group->free_extents++;
        return ret;
 }
 
 static void recalculate_thresholds(struct btrfs_block_group_cache *block_group)
 {
-       u64 max_bytes, possible_bytes;
+       u64 max_bytes;
+       u64 bitmap_bytes;
+       u64 extent_bytes;
 
        /*
         * The goal is to keep the total amount of memory used per 1gb of space
@@ -267,54 +271,63 @@ static void recalculate_thresholds(struct btrfs_block_group_cache *block_group)
        max_bytes = MAX_CACHE_BYTES_PER_GIG *
                (div64_u64(block_group->key.offset, 1024 * 1024 * 1024));
 
-       possible_bytes = (block_group->total_bitmaps * PAGE_CACHE_SIZE) +
-               (sizeof(struct btrfs_free_space) *
-                block_group->extents_thresh);
+       /*
+        * we want to account for 1 more bitmap than what we have so we can make
+        * sure we don't go over our overall goal of MAX_CACHE_BYTES_PER_GIG as
+        * we add more bitmaps.
+        */
+       bitmap_bytes = (block_group->total_bitmaps + 1) * PAGE_CACHE_SIZE;
 
-       if (possible_bytes > max_bytes) {
-               int extent_bytes = max_bytes -
-                       (block_group->total_bitmaps * PAGE_CACHE_SIZE);
+       if (bitmap_bytes >= max_bytes) {
+               block_group->extents_thresh = 0;
+               return;
+       }
 
-               if (extent_bytes <= 0) {
-                       block_group->extents_thresh = 0;
-                       return;
-               }
+       /*
+        * we want the extent entry threshold to always be at most 1/2 the maxw
+        * bytes we can have, or whatever is less than that.
+        */
+       extent_bytes = max_bytes - bitmap_bytes;
+       extent_bytes = min_t(u64, extent_bytes, div64_u64(max_bytes, 2));
 
-               block_group->extents_thresh = extent_bytes /
-                       (sizeof(struct btrfs_free_space));
-       }
+       block_group->extents_thresh =
+               div64_u64(extent_bytes, (sizeof(struct btrfs_free_space)));
 }
 
-static void bitmap_clear_bits(struct btrfs_free_space *info, u64 offset, u64 bytes,
-                             u64 sectorsize)
+static void bitmap_clear_bits(struct btrfs_block_group_cache *block_group,
+                             struct btrfs_free_space *info, u64 offset,
+                             u64 bytes)
 {
        unsigned long start, end;
        unsigned long i;
 
-       start = offset_to_bit(info->offset, sectorsize, offset);
-       end = start + bytes_to_bits(bytes, sectorsize);
+       start = offset_to_bit(info->offset, block_group->sectorsize, offset);
+       end = start + bytes_to_bits(bytes, block_group->sectorsize);
        BUG_ON(end > BITS_PER_BITMAP);
 
        for (i = start; i < end; i++)
                clear_bit(i, info->bitmap);
 
        info->bytes -= bytes;
+       block_group->free_space -= bytes;
 }
 
-static void bitmap_set_bits(struct btrfs_free_space *info, u64 offset, u64 bytes,
-                           u64 sectorsize)
+static void bitmap_set_bits(struct btrfs_block_group_cache *block_group,
+                           struct btrfs_free_space *info, u64 offset,
+                           u64 bytes)
 {
        unsigned long start, end;
        unsigned long i;
 
-       start = offset_to_bit(info->offset, sectorsize, offset);
-       end = start + bytes_to_bits(bytes, sectorsize);
+       start = offset_to_bit(info->offset, block_group->sectorsize, offset);
+       end = start + bytes_to_bits(bytes, block_group->sectorsize);
        BUG_ON(end > BITS_PER_BITMAP);
 
        for (i = start; i < end; i++)
                set_bit(i, info->bitmap);
 
        info->bytes += bytes;
+       block_group->free_space += bytes;
 }
 
 static int search_bitmap(struct btrfs_block_group_cache *block_group,
@@ -397,6 +410,7 @@ static void add_new_bitmap(struct btrfs_block_group_cache *block_group,
        BUG_ON(block_group->total_bitmaps >= max_bitmaps);
 
        info->offset = offset_to_bitmap(block_group, offset);
+       info->bytes = 0;
        link_free_space(block_group, info);
        block_group->total_bitmaps++;
 
@@ -408,23 +422,41 @@ static noinline int remove_from_bitmap(struct btrfs_block_group_cache *block_gro
                              u64 *offset, u64 *bytes)
 {
        u64 end;
+       u64 search_start, search_bytes;
+       int ret;
 
 again:
        end = bitmap_info->offset +
                (u64)(BITS_PER_BITMAP * block_group->sectorsize) - 1;
 
+       /*
+        * XXX - this can go away after a few releases.
+        *
+        * since the only user of btrfs_remove_free_space is the tree logging
+        * stuff, and the only way to test that is under crash conditions, we
+        * want to have this debug stuff here just in case somethings not
+        * working.  Search the bitmap for the space we are trying to use to
+        * make sure its actually there.  If its not there then we need to stop
+        * because something has gone wrong.
+        */
+       search_start = *offset;
+       search_bytes = *bytes;
+       ret = search_bitmap(block_group, bitmap_info, &search_start,
+                           &search_bytes);
+       BUG_ON(ret < 0 || search_start != *offset);
+
        if (*offset > bitmap_info->offset && *offset + *bytes > end) {
-               bitmap_clear_bits(bitmap_info, *offset,
-                                 end - *offset + 1, block_group->sectorsize);
+               bitmap_clear_bits(block_group, bitmap_info, *offset,
+                                 end - *offset + 1);
                *bytes -= end - *offset + 1;
                *offset = end + 1;
        } else if (*offset >= bitmap_info->offset && *offset + *bytes <= end) {
-               bitmap_clear_bits(bitmap_info, *offset,
-                                 *bytes, block_group->sectorsize);
+               bitmap_clear_bits(block_group, bitmap_info, *offset, *bytes);
                *bytes = 0;
        }
 
        if (*bytes) {
+               struct rb_node *next = rb_next(&bitmap_info->offset_index);
                if (!bitmap_info->bytes) {
                        unlink_free_space(block_group, bitmap_info);
                        kfree(bitmap_info->bitmap);
@@ -433,16 +465,36 @@ again:
                        recalculate_thresholds(block_group);
                }
 
-               bitmap_info = tree_search_offset(block_group,
-                                                offset_to_bitmap(block_group,
-                                                                 *offset),
-                                                1, 0);
-               if (!bitmap_info)
+               /*
+                * no entry after this bitmap, but we still have bytes to
+                * remove, so something has gone wrong.
+                */
+               if (!next)
                        return -EINVAL;
 
+               bitmap_info = rb_entry(next, struct btrfs_free_space,
+                                      offset_index);
+
+               /*
+                * if the next entry isn't a bitmap we need to return to let the
+                * extent stuff do its work.
+                */
                if (!bitmap_info->bitmap)
                        return -EAGAIN;
 
+               /*
+                * Ok the next item is a bitmap, but it may not actually hold
+                * the information for the rest of this free space stuff, so
+                * look for it, and if we don't find it return so we can try
+                * everything over again.
+                */
+               search_start = *offset;
+               search_bytes = *bytes;
+               ret = search_bitmap(block_group, bitmap_info, &search_start,
+                                   &search_bytes);
+               if (ret < 0 || search_start != *offset)
+                       return -EAGAIN;
+
                goto again;
        } else if (!bitmap_info->bytes) {
                unlink_free_space(block_group, bitmap_info);
@@ -495,14 +547,13 @@ again:
                (u64)(BITS_PER_BITMAP * block_group->sectorsize);
 
        if (offset >= bitmap_info->offset && offset + bytes > end) {
-               bitmap_set_bits(bitmap_info, offset, end - offset,
-                               block_group->sectorsize);
+               bitmap_set_bits(block_group, bitmap_info, offset,
+                               end - offset);
                bytes -= end - offset;
                offset = end;
                added = 0;
        } else if (offset >= bitmap_info->offset && offset + bytes <= end) {
-               bitmap_set_bits(bitmap_info, offset, bytes,
-                               block_group->sectorsize);
+               bitmap_set_bits(block_group, bitmap_info, offset, bytes);
                bytes = 0;
        } else {
                BUG();
@@ -640,8 +691,17 @@ int btrfs_remove_free_space(struct btrfs_block_group_cache *block_group,
 again:
        info = tree_search_offset(block_group, offset, 0, 0);
        if (!info) {
-               WARN_ON(1);
-               goto out_lock;
+               /*
+                * oops didn't find an extent that matched the space we wanted
+                * to remove, look for a bitmap instead
+                */
+               info = tree_search_offset(block_group,
+                                         offset_to_bitmap(block_group, offset),
+                                         1, 0);
+               if (!info) {
+                       WARN_ON(1);
+                       goto out_lock;
+               }
        }
 
        if (info->bytes < bytes && rb_next(&info->offset_index)) {
@@ -870,8 +930,7 @@ u64 btrfs_find_space_for_alloc(struct btrfs_block_group_cache *block_group,
 
        ret = offset;
        if (entry->bitmap) {
-               bitmap_clear_bits(entry, offset, bytes,
-                                 block_group->sectorsize);
+               bitmap_clear_bits(block_group, entry, offset, bytes);
                if (!entry->bytes) {
                        unlink_free_space(block_group, entry);
                        kfree(entry->bitmap);
@@ -891,6 +950,7 @@ u64 btrfs_find_space_for_alloc(struct btrfs_block_group_cache *block_group,
 
 out:
        spin_unlock(&block_group->tree_lock);
+
        return ret;
 }
 
@@ -953,8 +1013,15 @@ static u64 btrfs_alloc_from_bitmap(struct btrfs_block_group_cache *block_group,
        if (cluster->block_group != block_group)
                goto out;
 
-       entry = tree_search_offset(block_group, search_start, 0, 0);
-
+       /*
+        * search_start is the beginning of the bitmap, but at some point it may
+        * be a good idea to point to the actual start of the free area in the
+        * bitmap, so do the offset_to_bitmap trick anyway, and set bitmap_only
+        * to 1 to make sure we get the bitmap entry
+        */
+       entry = tree_search_offset(block_group,
+                                  offset_to_bitmap(block_group, search_start),
+                                  1, 0);
        if (!entry || !entry->bitmap)
                goto out;
 
@@ -967,7 +1034,7 @@ static u64 btrfs_alloc_from_bitmap(struct btrfs_block_group_cache *block_group,
                goto out;
 
        ret = search_start;
-       bitmap_clear_bits(entry, ret, bytes, block_group->sectorsize);
+       bitmap_clear_bits(block_group, entry, ret, bytes);
 out:
        spin_unlock(&cluster->lock);
        spin_unlock(&block_group->tree_lock);
This page took 0.030632 seconds and 5 git commands to generate.