Skip to content

Commit

Permalink
Btrfs: fix data enospc check overflow
Browse files Browse the repository at this point in the history
Because we account for reserved space we get from the allocator before we
actually account for allocating delalloc space, we can have a small window where
the amount of "used" space in a space_info is more than the total amount of
space in the space_info.  This will cause a overflow in our check, so it will
seem like we have _tons_ of free space, and we'll allow reservations to occur
that will end up larger than the amount of space we have.  I've seen users
report ENOSPC panic's in cow_file_range a few times recently, so I tried to
reproduce this problem and found I could reproduce it if I ran one of my tests
in a loop for like 20 minutes.  With this patch my test ran all night without
issues.  Thanks,

Signed-off-by: Josef Bacik <josef@redhat.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
  • Loading branch information
Josef Bacik authored and Chris Mason committed Apr 5, 2010
1 parent 109f6ae commit ab6e241
Showing 1 changed file with 15 additions and 5 deletions.
20 changes: 15 additions & 5 deletions fs/btrfs/extent-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -3234,7 +3234,8 @@ int btrfs_check_data_free_space(struct btrfs_root *root, struct inode *inode,
u64 bytes)
{
struct btrfs_space_info *data_sinfo;
int ret = 0, committed = 0;
u64 used;
int ret = 0, committed = 0, flushed = 0;

/* make sure bytes are sectorsize aligned */
bytes = (bytes + root->sectorsize - 1) & ~((u64)root->sectorsize - 1);
Expand All @@ -3246,12 +3247,21 @@ int btrfs_check_data_free_space(struct btrfs_root *root, struct inode *inode,
again:
/* make sure we have enough space to handle the data first */
spin_lock(&data_sinfo->lock);
if (data_sinfo->total_bytes - data_sinfo->bytes_used -
data_sinfo->bytes_delalloc - data_sinfo->bytes_reserved -
data_sinfo->bytes_pinned - data_sinfo->bytes_readonly -
data_sinfo->bytes_may_use - data_sinfo->bytes_super < bytes) {
used = data_sinfo->bytes_used + data_sinfo->bytes_delalloc +
data_sinfo->bytes_reserved + data_sinfo->bytes_pinned +
data_sinfo->bytes_readonly + data_sinfo->bytes_may_use +
data_sinfo->bytes_super;

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.
Expand Down

0 comments on commit ab6e241

Please sign in to comment.