summaryrefslogtreecommitdiff
path: root/free-space-cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'free-space-cache.c')
-rw-r--r--free-space-cache.c35
1 files changed, 25 insertions, 10 deletions
diff --git a/free-space-cache.c b/free-space-cache.c
index 50356d04..9b83a71c 100644
--- a/free-space-cache.c
+++ b/free-space-cache.c
@@ -54,8 +54,7 @@ static int io_ctl_init(struct io_ctl *io_ctl, u64 size, u64 ino,
struct btrfs_root *root)
{
memset(io_ctl, 0, sizeof(struct io_ctl));
- io_ctl->num_pages = (size + root->fs_info->sectorsize - 1) /
- root->fs_info->sectorsize;
+ io_ctl->num_pages = DIV_ROUND_UP(size, root->fs_info->sectorsize);
io_ctl->buffer = kzalloc(size, GFP_NOFS);
if (!io_ctl->buffer)
return -ENOMEM;
@@ -439,7 +438,8 @@ int load_free_space_cache(struct btrfs_fs_info *fs_info,
struct btrfs_path *path;
u64 used = btrfs_block_group_used(&block_group->item);
int ret = 0;
- int matched;
+ u64 bg_free;
+ s64 diff;
path = btrfs_alloc_path();
if (!path)
@@ -449,18 +449,33 @@ int load_free_space_cache(struct btrfs_fs_info *fs_info,
block_group->key.objectid);
btrfs_free_path(path);
- matched = (ctl->free_space == (block_group->key.offset - used -
- block_group->bytes_super));
- if (ret == 1 && !matched) {
- __btrfs_remove_free_space_cache(ctl);
+ bg_free = block_group->key.offset - used - block_group->bytes_super;
+ diff = ctl->free_space - bg_free;
+ if (ret == 1 && diff) {
fprintf(stderr,
- "block group %llu has wrong amount of free space\n",
- block_group->key.objectid);
+ "block group %llu has wrong amount of free space, free space cache has %llu block group has %llu\n",
+ block_group->key.objectid, ctl->free_space, bg_free);
+ __btrfs_remove_free_space_cache(ctl);
+ /*
+ * Due to btrfs_reserve_extent() can happen out of a
+ * transaction, but all btrfs_release_extent() happens inside
+ * a transaction, so under heavy race it's possible that free
+ * space cache has less free space, and both kernel just discard
+ * such cache. But if we find some case where free space cache
+ * has more free space, this means under certain case such
+ * cache can be loaded and cause double allocate.
+ *
+ * Detect such possibility here.
+ */
+ if (diff > 0)
+ error(
+"free space cache has more free space than block group item, this could leads to serious corruption, please contact btrfs developers");
ret = -1;
}
if (ret < 0) {
- ret = 0;
+ if (diff <= 0)
+ ret = 0;
fprintf(stderr,
"failed to load free space cache for block group %llu\n",