diff options
author | Josef Bacik <josef@redhat.com> | 2010-03-19 10:38:13 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2010-04-05 16:04:50 -0400 |
commit | ab6e24103cbd215e922938a4f58c75194761a60e (patch) | |
tree | d54479feddd7fe625888dd62084621d4d0871c4b | |
parent | 109f6aef5fc436f355ad027f4d97bd696df2049a (diff) |
Btrfs: fix data enospc check overflow
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>
-rw-r--r-- | fs/btrfs/extent-tree.c | 20 |
1 files changed, 15 insertions, 5 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 101041d4d2b2..786899da3eb9 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -3234,7 +3234,8 @@ int btrfs_check_data_free_space(struct btrfs_root *root, struct inode *inode, | |||
3234 | u64 bytes) | 3234 | u64 bytes) |
3235 | { | 3235 | { |
3236 | struct btrfs_space_info *data_sinfo; | 3236 | struct btrfs_space_info *data_sinfo; |
3237 | int ret = 0, committed = 0; | 3237 | u64 used; |
3238 | int ret = 0, committed = 0, flushed = 0; | ||
3238 | 3239 | ||
3239 | /* make sure bytes are sectorsize aligned */ | 3240 | /* make sure bytes are sectorsize aligned */ |
3240 | bytes = (bytes + root->sectorsize - 1) & ~((u64)root->sectorsize - 1); | 3241 | bytes = (bytes + root->sectorsize - 1) & ~((u64)root->sectorsize - 1); |
@@ -3246,12 +3247,21 @@ int btrfs_check_data_free_space(struct btrfs_root *root, struct inode *inode, | |||
3246 | again: | 3247 | again: |
3247 | /* make sure we have enough space to handle the data first */ | 3248 | /* make sure we have enough space to handle the data first */ |
3248 | spin_lock(&data_sinfo->lock); | 3249 | spin_lock(&data_sinfo->lock); |
3249 | if (data_sinfo->total_bytes - data_sinfo->bytes_used - | 3250 | used = data_sinfo->bytes_used + data_sinfo->bytes_delalloc + |
3250 | data_sinfo->bytes_delalloc - data_sinfo->bytes_reserved - | 3251 | data_sinfo->bytes_reserved + data_sinfo->bytes_pinned + |
3251 | data_sinfo->bytes_pinned - data_sinfo->bytes_readonly - | 3252 | data_sinfo->bytes_readonly + data_sinfo->bytes_may_use + |
3252 | data_sinfo->bytes_may_use - data_sinfo->bytes_super < bytes) { | 3253 | data_sinfo->bytes_super; |
3254 | |||
3255 | if (used + bytes > data_sinfo->total_bytes) { | ||
3253 | struct btrfs_trans_handle *trans; | 3256 | struct btrfs_trans_handle *trans; |
3254 | 3257 | ||
3258 | if (!flushed) { | ||
3259 | spin_unlock(&data_sinfo->lock); | ||
3260 | flush_delalloc(root, data_sinfo); | ||
3261 | flushed = 1; | ||
3262 | goto again; | ||
3263 | } | ||
3264 | |||
3255 | /* | 3265 | /* |
3256 | * if we don't have enough free bytes in this space then we need | 3266 | * if we don't have enough free bytes in this space then we need |
3257 | * to alloc a new chunk. | 3267 | * to alloc a new chunk. |