Commit e570fd27 authored by Miao Xie's avatar Miao Xie Committed by Chris Mason

Btrfs: fix broken free space cache after the system crashed

When we mounted the filesystem after the crash, we got the following
message:
  BTRFS error (device xxx): block group xxxx has wrong amount of free space
  BTRFS error (device xxx): failed to load free space cache for block group xxx

It is because we didn't update the metadata of the allocated space (in extent
tree) until the file data was written into the disk. During this time, there was
no information about the allocated spaces in either the extent tree nor the
free space cache. when we wrote out the free space cache at this time (commit
transaction), those spaces were lost. In fact, only the free space that is
used to store the file data had this problem, the others didn't because
the metadata of them is updated in the same transaction context.

There are many methods which can fix the above problem
- track the allocated space, and write it out when we write out the free
  space cache
- account the size of the allocated space that is used to store the file
  data, if the size is not zero, don't write out the free space cache.

The first one is complex and may make the performance drop down.
This patch chose the second method, we use a per-block-group variant to
account the size of that allocated space. Besides that, we also introduce
a per-block-group read-write semaphore to avoid the race between
the allocation and the free space cache write out.
Signed-off-by: default avatarMiao Xie <miaox@cn.fujitsu.com>
Signed-off-by: default avatarChris Mason <clm@fb.com>
parent 5349d6c3
...@@ -1259,11 +1259,19 @@ struct btrfs_block_group_cache { ...@@ -1259,11 +1259,19 @@ struct btrfs_block_group_cache {
spinlock_t lock; spinlock_t lock;
u64 pinned; u64 pinned;
u64 reserved; u64 reserved;
u64 delalloc_bytes;
u64 bytes_super; u64 bytes_super;
u64 flags; u64 flags;
u64 sectorsize; u64 sectorsize;
u64 cache_generation; u64 cache_generation;
/*
* It is just used for the delayed data space allocation because
* only the data space allocation and the relative metadata update
* can be done cross the transaction.
*/
struct rw_semaphore data_rwsem;
/* for raid56, this is a full stripe, without parity */ /* for raid56, this is a full stripe, without parity */
unsigned long full_stripe_len; unsigned long full_stripe_len;
...@@ -3316,7 +3324,7 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans, ...@@ -3316,7 +3324,7 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
struct btrfs_key *ins); struct btrfs_key *ins);
int btrfs_reserve_extent(struct btrfs_root *root, u64 num_bytes, int btrfs_reserve_extent(struct btrfs_root *root, u64 num_bytes,
u64 min_alloc_size, u64 empty_size, u64 hint_byte, u64 min_alloc_size, u64 empty_size, u64 hint_byte,
struct btrfs_key *ins, int is_data); struct btrfs_key *ins, int is_data, int delalloc);
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct extent_buffer *buf, int full_backref, int no_quota); struct extent_buffer *buf, int full_backref, int no_quota);
int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
...@@ -3330,7 +3338,8 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans, ...@@ -3330,7 +3338,8 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes, u64 parent, u64 root_objectid, u64 bytenr, u64 num_bytes, u64 parent, u64 root_objectid,
u64 owner, u64 offset, int no_quota); u64 owner, u64 offset, int no_quota);
int btrfs_free_reserved_extent(struct btrfs_root *root, u64 start, u64 len); int btrfs_free_reserved_extent(struct btrfs_root *root, u64 start, u64 len,
int delalloc);
int btrfs_free_and_pin_reserved_extent(struct btrfs_root *root, int btrfs_free_and_pin_reserved_extent(struct btrfs_root *root,
u64 start, u64 len); u64 start, u64 len);
void btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans, void btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans,
......
...@@ -105,7 +105,8 @@ static int find_next_key(struct btrfs_path *path, int level, ...@@ -105,7 +105,8 @@ static int find_next_key(struct btrfs_path *path, int level,
static void dump_space_info(struct btrfs_space_info *info, u64 bytes, static void dump_space_info(struct btrfs_space_info *info, u64 bytes,
int dump_block_groups); int dump_block_groups);
static int btrfs_update_reserved_bytes(struct btrfs_block_group_cache *cache, static int btrfs_update_reserved_bytes(struct btrfs_block_group_cache *cache,
u64 num_bytes, int reserve); u64 num_bytes, int reserve,
int delalloc);
static int block_rsv_use_bytes(struct btrfs_block_rsv *block_rsv, static int block_rsv_use_bytes(struct btrfs_block_rsv *block_rsv,
u64 num_bytes); u64 num_bytes);
int btrfs_pin_extent(struct btrfs_root *root, int btrfs_pin_extent(struct btrfs_root *root,
...@@ -3260,7 +3261,8 @@ static int cache_save_setup(struct btrfs_block_group_cache *block_group, ...@@ -3260,7 +3261,8 @@ static int cache_save_setup(struct btrfs_block_group_cache *block_group,
spin_lock(&block_group->lock); spin_lock(&block_group->lock);
if (block_group->cached != BTRFS_CACHE_FINISHED || if (block_group->cached != BTRFS_CACHE_FINISHED ||
!btrfs_test_opt(root, SPACE_CACHE)) { !btrfs_test_opt(root, SPACE_CACHE) ||
block_group->delalloc_bytes) {
/* /*
* don't bother trying to write stuff out _if_ * don't bother trying to write stuff out _if_
* a) we're not cached, * a) we're not cached,
...@@ -5613,6 +5615,7 @@ int btrfs_exclude_logged_extents(struct btrfs_root *log, ...@@ -5613,6 +5615,7 @@ int btrfs_exclude_logged_extents(struct btrfs_root *log,
* @cache: The cache we are manipulating * @cache: The cache we are manipulating
* @num_bytes: The number of bytes in question * @num_bytes: The number of bytes in question
* @reserve: One of the reservation enums * @reserve: One of the reservation enums
* @delalloc: The blocks are allocated for the delalloc write
* *
* This is called by the allocator when it reserves space, or by somebody who is * This is called by the allocator when it reserves space, or by somebody who is
* freeing space that was never actually used on disk. For example if you * freeing space that was never actually used on disk. For example if you
...@@ -5631,7 +5634,7 @@ int btrfs_exclude_logged_extents(struct btrfs_root *log, ...@@ -5631,7 +5634,7 @@ int btrfs_exclude_logged_extents(struct btrfs_root *log,
* succeeds. * succeeds.
*/ */
static int btrfs_update_reserved_bytes(struct btrfs_block_group_cache *cache, static int btrfs_update_reserved_bytes(struct btrfs_block_group_cache *cache,
u64 num_bytes, int reserve) u64 num_bytes, int reserve, int delalloc)
{ {
struct btrfs_space_info *space_info = cache->space_info; struct btrfs_space_info *space_info = cache->space_info;
int ret = 0; int ret = 0;
...@@ -5650,12 +5653,18 @@ static int btrfs_update_reserved_bytes(struct btrfs_block_group_cache *cache, ...@@ -5650,12 +5653,18 @@ static int btrfs_update_reserved_bytes(struct btrfs_block_group_cache *cache,
num_bytes, 0); num_bytes, 0);
space_info->bytes_may_use -= num_bytes; space_info->bytes_may_use -= num_bytes;
} }
if (delalloc)
cache->delalloc_bytes += num_bytes;
} }
} else { } else {
if (cache->ro) if (cache->ro)
space_info->bytes_readonly += num_bytes; space_info->bytes_readonly += num_bytes;
cache->reserved -= num_bytes; cache->reserved -= num_bytes;
space_info->bytes_reserved -= num_bytes; space_info->bytes_reserved -= num_bytes;
if (delalloc)
cache->delalloc_bytes -= num_bytes;
} }
spin_unlock(&cache->lock); spin_unlock(&cache->lock);
spin_unlock(&space_info->lock); spin_unlock(&space_info->lock);
...@@ -6206,7 +6215,7 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans, ...@@ -6206,7 +6215,7 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
WARN_ON(test_bit(EXTENT_BUFFER_DIRTY, &buf->bflags)); WARN_ON(test_bit(EXTENT_BUFFER_DIRTY, &buf->bflags));
btrfs_add_free_space(cache, buf->start, buf->len); btrfs_add_free_space(cache, buf->start, buf->len);
btrfs_update_reserved_bytes(cache, buf->len, RESERVE_FREE); btrfs_update_reserved_bytes(cache, buf->len, RESERVE_FREE, 0);
trace_btrfs_reserved_extent_free(root, buf->start, buf->len); trace_btrfs_reserved_extent_free(root, buf->start, buf->len);
pin = 0; pin = 0;
} }
...@@ -6365,6 +6374,70 @@ enum btrfs_loop_type { ...@@ -6365,6 +6374,70 @@ enum btrfs_loop_type {
LOOP_NO_EMPTY_SIZE = 3, LOOP_NO_EMPTY_SIZE = 3,
}; };
static inline void
btrfs_lock_block_group(struct btrfs_block_group_cache *cache,
int delalloc)
{
if (delalloc)
down_read(&cache->data_rwsem);
}
static inline void
btrfs_grab_block_group(struct btrfs_block_group_cache *cache,
int delalloc)
{
btrfs_get_block_group(cache);
if (delalloc)
down_read(&cache->data_rwsem);
}
static struct btrfs_block_group_cache *
btrfs_lock_cluster(struct btrfs_block_group_cache *block_group,
struct btrfs_free_cluster *cluster,
int delalloc)
{
struct btrfs_block_group_cache *used_bg;
bool locked = false;
again:
spin_lock(&cluster->refill_lock);
if (locked) {
if (used_bg == cluster->block_group)
return used_bg;
up_read(&used_bg->data_rwsem);
btrfs_put_block_group(used_bg);
}
used_bg = cluster->block_group;
if (!used_bg)
return NULL;
if (used_bg == block_group)
return used_bg;
btrfs_get_block_group(used_bg);
if (!delalloc)
return used_bg;
if (down_read_trylock(&used_bg->data_rwsem))
return used_bg;
spin_unlock(&cluster->refill_lock);
down_read(&used_bg->data_rwsem);
locked = true;
goto again;
}
static inline void
btrfs_release_block_group(struct btrfs_block_group_cache *cache,
int delalloc)
{
if (delalloc)
up_read(&cache->data_rwsem);
btrfs_put_block_group(cache);
}
/* /*
* walks the btree of allocated extents and find a hole of a given size. * walks the btree of allocated extents and find a hole of a given size.
* The key ins is changed to record the hole: * The key ins is changed to record the hole:
...@@ -6379,7 +6452,7 @@ enum btrfs_loop_type { ...@@ -6379,7 +6452,7 @@ enum btrfs_loop_type {
static noinline int find_free_extent(struct btrfs_root *orig_root, static noinline int find_free_extent(struct btrfs_root *orig_root,
u64 num_bytes, u64 empty_size, u64 num_bytes, u64 empty_size,
u64 hint_byte, struct btrfs_key *ins, u64 hint_byte, struct btrfs_key *ins,
u64 flags) u64 flags, int delalloc)
{ {
int ret = 0; int ret = 0;
struct btrfs_root *root = orig_root->fs_info->extent_root; struct btrfs_root *root = orig_root->fs_info->extent_root;
...@@ -6467,6 +6540,7 @@ static noinline int find_free_extent(struct btrfs_root *orig_root, ...@@ -6467,6 +6540,7 @@ static noinline int find_free_extent(struct btrfs_root *orig_root,
up_read(&space_info->groups_sem); up_read(&space_info->groups_sem);
} else { } else {
index = get_block_group_index(block_group); index = get_block_group_index(block_group);
btrfs_lock_block_group(block_group, delalloc);
goto have_block_group; goto have_block_group;
} }
} else if (block_group) { } else if (block_group) {
...@@ -6481,7 +6555,7 @@ static noinline int find_free_extent(struct btrfs_root *orig_root, ...@@ -6481,7 +6555,7 @@ static noinline int find_free_extent(struct btrfs_root *orig_root,
u64 offset; u64 offset;
int cached; int cached;
btrfs_get_block_group(block_group); btrfs_grab_block_group(block_group, delalloc);
search_start = block_group->key.objectid; search_start = block_group->key.objectid;
/* /*
...@@ -6529,16 +6603,16 @@ static noinline int find_free_extent(struct btrfs_root *orig_root, ...@@ -6529,16 +6603,16 @@ static noinline int find_free_extent(struct btrfs_root *orig_root,
* the refill lock keeps out other * the refill lock keeps out other
* people trying to start a new cluster * people trying to start a new cluster
*/ */
spin_lock(&last_ptr->refill_lock); used_block_group = btrfs_lock_cluster(block_group,
used_block_group = last_ptr->block_group; last_ptr,
if (used_block_group != block_group && delalloc);
(!used_block_group || if (!used_block_group)
used_block_group->ro ||
!block_group_bits(used_block_group, flags)))
goto refill_cluster; goto refill_cluster;
if (used_block_group != block_group) if (used_block_group != block_group &&
btrfs_get_block_group(used_block_group); (used_block_group->ro ||
!block_group_bits(used_block_group, flags)))
goto release_cluster;
offset = btrfs_alloc_from_cluster(used_block_group, offset = btrfs_alloc_from_cluster(used_block_group,
last_ptr, last_ptr,
...@@ -6552,16 +6626,15 @@ static noinline int find_free_extent(struct btrfs_root *orig_root, ...@@ -6552,16 +6626,15 @@ static noinline int find_free_extent(struct btrfs_root *orig_root,
used_block_group, used_block_group,
search_start, num_bytes); search_start, num_bytes);
if (used_block_group != block_group) { if (used_block_group != block_group) {
btrfs_put_block_group(block_group); btrfs_release_block_group(block_group,
delalloc);
block_group = used_block_group; block_group = used_block_group;
} }
goto checks; goto checks;
} }
WARN_ON(last_ptr->block_group != used_block_group); WARN_ON(last_ptr->block_group != used_block_group);
if (used_block_group != block_group) release_cluster:
btrfs_put_block_group(used_block_group);
refill_cluster:
/* If we are on LOOP_NO_EMPTY_SIZE, we can't /* If we are on LOOP_NO_EMPTY_SIZE, we can't
* set up a new clusters, so lets just skip it * set up a new clusters, so lets just skip it
* and let the allocator find whatever block * and let the allocator find whatever block
...@@ -6578,8 +6651,10 @@ static noinline int find_free_extent(struct btrfs_root *orig_root, ...@@ -6578,8 +6651,10 @@ static noinline int find_free_extent(struct btrfs_root *orig_root,
* succeeding in the unclustered * succeeding in the unclustered
* allocation. */ * allocation. */
if (loop >= LOOP_NO_EMPTY_SIZE && if (loop >= LOOP_NO_EMPTY_SIZE &&
last_ptr->block_group != block_group) { used_block_group != block_group) {
spin_unlock(&last_ptr->refill_lock); spin_unlock(&last_ptr->refill_lock);
btrfs_release_block_group(used_block_group,
delalloc);
goto unclustered_alloc; goto unclustered_alloc;
} }
...@@ -6589,6 +6664,10 @@ static noinline int find_free_extent(struct btrfs_root *orig_root, ...@@ -6589,6 +6664,10 @@ static noinline int find_free_extent(struct btrfs_root *orig_root,
*/ */
btrfs_return_cluster_to_free_space(NULL, last_ptr); btrfs_return_cluster_to_free_space(NULL, last_ptr);
if (used_block_group != block_group)
btrfs_release_block_group(used_block_group,
delalloc);
refill_cluster:
if (loop >= LOOP_NO_EMPTY_SIZE) { if (loop >= LOOP_NO_EMPTY_SIZE) {
spin_unlock(&last_ptr->refill_lock); spin_unlock(&last_ptr->refill_lock);
goto unclustered_alloc; goto unclustered_alloc;
...@@ -6696,7 +6775,7 @@ static noinline int find_free_extent(struct btrfs_root *orig_root, ...@@ -6696,7 +6775,7 @@ static noinline int find_free_extent(struct btrfs_root *orig_root,
BUG_ON(offset > search_start); BUG_ON(offset > search_start);
ret = btrfs_update_reserved_bytes(block_group, num_bytes, ret = btrfs_update_reserved_bytes(block_group, num_bytes,
alloc_type); alloc_type, delalloc);
if (ret == -EAGAIN) { if (ret == -EAGAIN) {
btrfs_add_free_space(block_group, offset, num_bytes); btrfs_add_free_space(block_group, offset, num_bytes);
goto loop; goto loop;
...@@ -6708,13 +6787,13 @@ static noinline int find_free_extent(struct btrfs_root *orig_root, ...@@ -6708,13 +6787,13 @@ static noinline int find_free_extent(struct btrfs_root *orig_root,
trace_btrfs_reserve_extent(orig_root, block_group, trace_btrfs_reserve_extent(orig_root, block_group,
search_start, num_bytes); search_start, num_bytes);
btrfs_put_block_group(block_group); btrfs_release_block_group(block_group, delalloc);
break; break;
loop: loop:
failed_cluster_refill = false; failed_cluster_refill = false;
failed_alloc = false; failed_alloc = false;
BUG_ON(index != get_block_group_index(block_group)); BUG_ON(index != get_block_group_index(block_group));
btrfs_put_block_group(block_group); btrfs_release_block_group(block_group, delalloc);
} }
up_read(&space_info->groups_sem); up_read(&space_info->groups_sem);
...@@ -6827,7 +6906,7 @@ static void dump_space_info(struct btrfs_space_info *info, u64 bytes, ...@@ -6827,7 +6906,7 @@ static void dump_space_info(struct btrfs_space_info *info, u64 bytes,
int btrfs_reserve_extent(struct btrfs_root *root, int btrfs_reserve_extent(struct btrfs_root *root,
u64 num_bytes, u64 min_alloc_size, u64 num_bytes, u64 min_alloc_size,
u64 empty_size, u64 hint_byte, u64 empty_size, u64 hint_byte,
struct btrfs_key *ins, int is_data) struct btrfs_key *ins, int is_data, int delalloc)
{ {
bool final_tried = false; bool final_tried = false;
u64 flags; u64 flags;
...@@ -6837,7 +6916,7 @@ int btrfs_reserve_extent(struct btrfs_root *root, ...@@ -6837,7 +6916,7 @@ int btrfs_reserve_extent(struct btrfs_root *root,
again: again:
WARN_ON(num_bytes < root->sectorsize); WARN_ON(num_bytes < root->sectorsize);
ret = find_free_extent(root, num_bytes, empty_size, hint_byte, ins, ret = find_free_extent(root, num_bytes, empty_size, hint_byte, ins,
flags); flags, delalloc);
if (ret == -ENOSPC) { if (ret == -ENOSPC) {
if (!final_tried && ins->offset) { if (!final_tried && ins->offset) {
...@@ -6862,7 +6941,8 @@ int btrfs_reserve_extent(struct btrfs_root *root, ...@@ -6862,7 +6941,8 @@ int btrfs_reserve_extent(struct btrfs_root *root,
} }
static int __btrfs_free_reserved_extent(struct btrfs_root *root, static int __btrfs_free_reserved_extent(struct btrfs_root *root,
u64 start, u64 len, int pin) u64 start, u64 len,
int pin, int delalloc)
{ {
struct btrfs_block_group_cache *cache; struct btrfs_block_group_cache *cache;
int ret = 0; int ret = 0;
...@@ -6881,7 +6961,7 @@ static int __btrfs_free_reserved_extent(struct btrfs_root *root, ...@@ -6881,7 +6961,7 @@ static int __btrfs_free_reserved_extent(struct btrfs_root *root,
pin_down_extent(root, cache, start, len, 1); pin_down_extent(root, cache, start, len, 1);
else { else {
btrfs_add_free_space(cache, start, len); btrfs_add_free_space(cache, start, len);
btrfs_update_reserved_bytes(cache, len, RESERVE_FREE); btrfs_update_reserved_bytes(cache, len, RESERVE_FREE, delalloc);
} }
btrfs_put_block_group(cache); btrfs_put_block_group(cache);
...@@ -6891,15 +6971,15 @@ static int __btrfs_free_reserved_extent(struct btrfs_root *root, ...@@ -6891,15 +6971,15 @@ static int __btrfs_free_reserved_extent(struct btrfs_root *root,
} }
int btrfs_free_reserved_extent(struct btrfs_root *root, int btrfs_free_reserved_extent(struct btrfs_root *root,
u64 start, u64 len) u64 start, u64 len, int delalloc)
{ {
return __btrfs_free_reserved_extent(root, start, len, 0); return __btrfs_free_reserved_extent(root, start, len, 0, delalloc);
} }
int btrfs_free_and_pin_reserved_extent(struct btrfs_root *root, int btrfs_free_and_pin_reserved_extent(struct btrfs_root *root,
u64 start, u64 len) u64 start, u64 len)
{ {
return __btrfs_free_reserved_extent(root, start, len, 1); return __btrfs_free_reserved_extent(root, start, len, 1, 0);
} }
static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans, static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
...@@ -7114,7 +7194,7 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans, ...@@ -7114,7 +7194,7 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
return -EINVAL; return -EINVAL;
ret = btrfs_update_reserved_bytes(block_group, ins->offset, ret = btrfs_update_reserved_bytes(block_group, ins->offset,
RESERVE_ALLOC_NO_ACCOUNT); RESERVE_ALLOC_NO_ACCOUNT, 0);
BUG_ON(ret); /* logic error */ BUG_ON(ret); /* logic error */
ret = alloc_reserved_file_extent(trans, root, 0, root_objectid, ret = alloc_reserved_file_extent(trans, root, 0, root_objectid,
0, owner, offset, ins, 1); 0, owner, offset, ins, 1);
...@@ -7256,7 +7336,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, ...@@ -7256,7 +7336,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
return ERR_CAST(block_rsv); return ERR_CAST(block_rsv);
ret = btrfs_reserve_extent(root, blocksize, blocksize, ret = btrfs_reserve_extent(root, blocksize, blocksize,
empty_size, hint, &ins, 0); empty_size, hint, &ins, 0, 0);
if (ret) { if (ret) {
unuse_block_rsv(root->fs_info, block_rsv, blocksize); unuse_block_rsv(root->fs_info, block_rsv, blocksize);
return ERR_PTR(ret); return ERR_PTR(ret);
...@@ -8659,6 +8739,7 @@ btrfs_create_block_group_cache(struct btrfs_root *root, u64 start, u64 size) ...@@ -8659,6 +8739,7 @@ btrfs_create_block_group_cache(struct btrfs_root *root, u64 start, u64 size)
start); start);
atomic_set(&cache->count, 1); atomic_set(&cache->count, 1);
spin_lock_init(&cache->lock); spin_lock_init(&cache->lock);
init_rwsem(&cache->data_rwsem);
INIT_LIST_HEAD(&cache->list); INIT_LIST_HEAD(&cache->list);
INIT_LIST_HEAD(&cache->cluster_list); INIT_LIST_HEAD(&cache->cluster_list);
INIT_LIST_HEAD(&cache->new_bg_list); INIT_LIST_HEAD(&cache->new_bg_list);
......
...@@ -680,6 +680,13 @@ static int __load_free_space_cache(struct btrfs_root *root, struct inode *inode, ...@@ -680,6 +680,13 @@ static int __load_free_space_cache(struct btrfs_root *root, struct inode *inode,
generation = btrfs_free_space_generation(leaf, header); generation = btrfs_free_space_generation(leaf, header);
btrfs_release_path(path); btrfs_release_path(path);
if (!BTRFS_I(inode)->generation) {
btrfs_info(root->fs_info,
"The free space cache file (%llu) is invalid. skip it\n",
offset);
return 0;
}
if (BTRFS_I(inode)->generation != generation) { if (BTRFS_I(inode)->generation != generation) {
btrfs_err(root->fs_info, btrfs_err(root->fs_info,
"free space inode generation (%llu) " "free space inode generation (%llu) "
...@@ -1107,6 +1114,20 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode, ...@@ -1107,6 +1114,20 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
if (ret) if (ret)
return -1; return -1;
if (block_group && (block_group->flags & BTRFS_BLOCK_GROUP_DATA)) {
down_write(&block_group->data_rwsem);
spin_lock(&block_group->lock);
if (block_group->delalloc_bytes) {
block_group->disk_cache_state = BTRFS_DC_WRITTEN;
spin_unlock(&block_group->lock);
up_write(&block_group->data_rwsem);
BTRFS_I(inode)->generation = 0;
ret = 0;
goto out;
}
spin_unlock(&block_group->lock);
}
/* Lock all pages first so we can lock the extent safely. */ /* Lock all pages first so we can lock the extent safely. */
io_ctl_prepare_pages(&io_ctl, inode, 0); io_ctl_prepare_pages(&io_ctl, inode, 0);
...@@ -1145,6 +1166,8 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode, ...@@ -1145,6 +1166,8 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
if (ret) if (ret)
goto out_nospc; goto out_nospc;
if (block_group && (block_group->flags & BTRFS_BLOCK_GROUP_DATA))
up_write(&block_group->data_rwsem);
/* /*
* Release the pages and unlock the extent, we will flush * Release the pages and unlock the extent, we will flush
* them out later * them out later
...@@ -1173,6 +1196,10 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode, ...@@ -1173,6 +1196,10 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
out_nospc: out_nospc:
cleanup_write_cache_enospc(inode, &io_ctl, &cached_state, &bitmap_list); cleanup_write_cache_enospc(inode, &io_ctl, &cached_state, &bitmap_list);
if (block_group && (block_group->flags & BTRFS_BLOCK_GROUP_DATA))
up_write(&block_group->data_rwsem);
goto out; goto out;
} }
...@@ -1192,6 +1219,12 @@ int btrfs_write_out_cache(struct btrfs_root *root, ...@@ -1192,6 +1219,12 @@ int btrfs_write_out_cache(struct btrfs_root *root,
spin_unlock(&block_group->lock); spin_unlock(&block_group->lock);
return 0; return 0;
} }
if (block_group->delalloc_bytes) {
block_group->disk_cache_state = BTRFS_DC_WRITTEN;
spin_unlock(&block_group->lock);
return 0;
}
spin_unlock(&block_group->lock); spin_unlock(&block_group->lock);
inode = lookup_free_space_inode(root, block_group, path); inode = lookup_free_space_inode(root, block_group, path);
......
...@@ -693,7 +693,7 @@ static noinline int submit_compressed_extents(struct inode *inode, ...@@ -693,7 +693,7 @@ static noinline int submit_compressed_extents(struct inode *inode,
ret = btrfs_reserve_extent(root, ret = btrfs_reserve_extent(root,
async_extent->compressed_size, async_extent->compressed_size,
async_extent->compressed_size, async_extent->compressed_size,
0, alloc_hint, &ins, 1); 0, alloc_hint, &ins, 1, 1);
if (ret) { if (ret) {
int i; int i;
...@@ -794,7 +794,7 @@ static noinline int submit_compressed_extents(struct inode *inode, ...@@ -794,7 +794,7 @@ static noinline int submit_compressed_extents(struct inode *inode,
out: out:
return ret; return ret;
out_free_reserve: out_free_reserve:
btrfs_free_reserved_extent(root, ins.objectid, ins.offset); btrfs_free_reserved_extent(root, ins.objectid, ins.offset, 1);
out_free: out_free:
extent_clear_unlock_delalloc(inode, async_extent->start, extent_clear_unlock_delalloc(inode, async_extent->start,
async_extent->start + async_extent->start +
...@@ -917,7 +917,7 @@ static noinline int cow_file_range(struct inode *inode, ...@@ -917,7 +917,7 @@ static noinline int cow_file_range(struct inode *inode,
cur_alloc_size = disk_num_bytes; cur_alloc_size = disk_num_bytes;
ret = btrfs_reserve_extent(root, cur_alloc_size, ret = btrfs_reserve_extent(root, cur_alloc_size,
root->sectorsize, 0, alloc_hint, root->sectorsize, 0, alloc_hint,
&ins, 1); &ins, 1, 1);
if (ret < 0) if (ret < 0)
goto out_unlock; goto out_unlock;
...@@ -995,7 +995,7 @@ static noinline int cow_file_range(struct inode *inode, ...@@ -995,7 +995,7 @@ static noinline int cow_file_range(struct inode *inode,
return ret; return ret;
out_reserve: out_reserve:
btrfs_free_reserved_extent(root, ins.objectid, ins.offset); btrfs_free_reserved_extent(root, ins.objectid, ins.offset, 1);
out_unlock: out_unlock:
extent_clear_unlock_delalloc(inode, start, end, locked_page, extent_clear_unlock_delalloc(inode, start, end, locked_page,
EXTENT_LOCKED | EXTENT_DO_ACCOUNTING | EXTENT_LOCKED | EXTENT_DO_ACCOUNTING |
...@@ -2599,6 +2599,21 @@ record_old_file_extents(struct inode *inode, ...@@ -2599,6 +2599,21 @@ record_old_file_extents(struct inode *inode,
return NULL; return NULL;
} }
static void btrfs_release_delalloc_bytes(struct btrfs_root *root,
u64 start, u64 len)
{
struct btrfs_block_group_cache *cache;
cache = btrfs_lookup_block_group(root->fs_info, start);
ASSERT(cache);
spin_lock(&cache->lock);
cache->delalloc_bytes -= len;
spin_unlock(&cache->lock);
btrfs_put_block_group(cache);
}
/* as ordered data IO finishes, this gets called so we can finish /* as ordered data IO finishes, this gets called so we can finish
* an ordered extent if the range of bytes in the file it covers are * an ordered extent if the range of bytes in the file it covers are
* fully written. * fully written.
...@@ -2698,6 +2713,10 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent) ...@@ -2698,6 +2713,10 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
logical_len, logical_len, logical_len, logical_len,
compress_type, 0, 0, compress_type, 0, 0,
BTRFS_FILE_EXTENT_REG); BTRFS_FILE_EXTENT_REG);
if (!ret)
btrfs_release_delalloc_bytes(root,
ordered_extent->start,
ordered_extent->disk_len);
} }
unpin_extent_cache(&BTRFS_I(inode)->extent_tree, unpin_extent_cache(&BTRFS_I(inode)->extent_tree,
ordered_extent->file_offset, ordered_extent->len, ordered_extent->file_offset, ordered_extent->len,
...@@ -2750,7 +2769,7 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent) ...@@ -2750,7 +2769,7 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
!test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags) && !test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags) &&
!test_bit(BTRFS_ORDERED_PREALLOC, &ordered_extent->flags)) !test_bit(BTRFS_ORDERED_PREALLOC, &ordered_extent->flags))
btrfs_free_reserved_extent(root, ordered_extent->start, btrfs_free_reserved_extent(root, ordered_extent->start,
ordered_extent->disk_len); ordered_extent->disk_len, 1);
} }
...@@ -6535,21 +6554,21 @@ static struct extent_map *btrfs_new_extent_direct(struct inode *inode, ...@@ -6535,21 +6554,21 @@ static struct extent_map *btrfs_new_extent_direct(struct inode *inode,
alloc_hint = get_extent_allocation_hint(inode, start, len); alloc_hint = get_extent_allocation_hint(inode, start, len);
ret = btrfs_reserve_extent(root, len, root->sectorsize, 0, ret = btrfs_reserve_extent(root, len, root->sectorsize, 0,
alloc_hint, &ins, 1); alloc_hint, &ins, 1, 1);
if (ret) if (ret)
return ERR_PTR(ret); return ERR_PTR(ret);
em = create_pinned_em(inode, start, ins.offset, start, ins.objectid, em = create_pinned_em(inode, start, ins.offset, start, ins.objectid,
ins.offset, ins.offset, ins.offset, 0); ins.offset, ins.offset, ins.offset, 0);
if (IS_ERR(em)) { if (IS_ERR(em)) {
btrfs_free_reserved_extent(root, ins.objectid, ins.offset); btrfs_free_reserved_extent(root, ins.objectid, ins.offset, 1);
return em; return em;
} }
ret = btrfs_add_ordered_extent_dio(inode, start, ins.objectid, ret = btrfs_add_ordered_extent_dio(inode, start, ins.objectid,
ins.offset, ins.offset, 0); ins.offset, ins.offset, 0);
if (ret) { if (ret) {
btrfs_free_reserved_extent(root, ins.objectid, ins.offset); btrfs_free_reserved_extent(root, ins.objectid, ins.offset, 1);
free_extent_map(em); free_extent_map(em);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
...@@ -7437,7 +7456,7 @@ static void btrfs_submit_direct(int rw, struct bio *dio_bio, ...@@ -7437,7 +7456,7 @@ static void btrfs_submit_direct(int rw, struct bio *dio_bio,
if (!test_bit(BTRFS_ORDERED_PREALLOC, &ordered->flags) && if (!test_bit(BTRFS_ORDERED_PREALLOC, &ordered->flags) &&
!test_bit(BTRFS_ORDERED_NOCOW, &ordered->flags)) !test_bit(BTRFS_ORDERED_NOCOW, &ordered->flags))
btrfs_free_reserved_extent(root, ordered->start, btrfs_free_reserved_extent(root, ordered->start,
ordered->disk_len); ordered->disk_len, 1);
btrfs_put_ordered_extent(ordered); btrfs_put_ordered_extent(ordered);
btrfs_put_ordered_extent(ordered); btrfs_put_ordered_extent(ordered);
} }
...@@ -8819,7 +8838,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, ...@@ -8819,7 +8838,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
cur_bytes = min(num_bytes, 256ULL * 1024 * 1024); cur_bytes = min(num_bytes, 256ULL * 1024 * 1024);
cur_bytes = max(cur_bytes, min_size); cur_bytes = max(cur_bytes, min_size);
ret = btrfs_reserve_extent(root, cur_bytes, min_size, 0, ret = btrfs_reserve_extent(root, cur_bytes, min_size, 0,
*alloc_hint, &ins, 1); *alloc_hint, &ins, 1, 0);
if (ret) { if (ret) {
if (own_trans) if (own_trans)
btrfs_end_transaction(trans, root); btrfs_end_transaction(trans, root);
...@@ -8833,7 +8852,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, ...@@ -8833,7 +8852,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
BTRFS_FILE_EXTENT_PREALLOC); BTRFS_FILE_EXTENT_PREALLOC);
if (ret) { if (ret) {
btrfs_free_reserved_extent(root, ins.objectid, btrfs_free_reserved_extent(root, ins.objectid,
ins.offset); ins.offset, 0);
btrfs_abort_transaction(trans, root, ret); btrfs_abort_transaction(trans, root, ret);
if (own_trans) if (own_trans)
btrfs_end_transaction(trans, root); btrfs_end_transaction(trans, root);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment