|
@@ -74,8 +74,9 @@ enum {
|
|
|
RESERVE_ALLOC_NO_ACCOUNT = 2,
|
|
|
};
|
|
|
|
|
|
-static int update_block_group(struct btrfs_root *root,
|
|
|
- u64 bytenr, u64 num_bytes, int alloc);
|
|
|
+static int update_block_group(struct btrfs_trans_handle *trans,
|
|
|
+ struct btrfs_root *root, u64 bytenr,
|
|
|
+ u64 num_bytes, int alloc);
|
|
|
static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
|
|
|
struct btrfs_root *root,
|
|
|
u64 bytenr, u64 num_bytes, u64 parent,
|
|
@@ -3315,120 +3316,42 @@ int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
|
|
|
struct btrfs_root *root)
|
|
|
{
|
|
|
struct btrfs_block_group_cache *cache;
|
|
|
- int err = 0;
|
|
|
+ struct btrfs_transaction *cur_trans = trans->transaction;
|
|
|
+ int ret = 0;
|
|
|
struct btrfs_path *path;
|
|
|
- u64 last = 0;
|
|
|
+
|
|
|
+ if (list_empty(&cur_trans->dirty_bgs))
|
|
|
+ return 0;
|
|
|
|
|
|
path = btrfs_alloc_path();
|
|
|
if (!path)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
-again:
|
|
|
- while (1) {
|
|
|
- cache = btrfs_lookup_first_block_group(root->fs_info, last);
|
|
|
- while (cache) {
|
|
|
- if (cache->disk_cache_state == BTRFS_DC_CLEAR)
|
|
|
- break;
|
|
|
- cache = next_block_group(root, cache);
|
|
|
- }
|
|
|
- if (!cache) {
|
|
|
- if (last == 0)
|
|
|
- break;
|
|
|
- last = 0;
|
|
|
- continue;
|
|
|
- }
|
|
|
- err = cache_save_setup(cache, trans, path);
|
|
|
- last = cache->key.objectid + cache->key.offset;
|
|
|
- btrfs_put_block_group(cache);
|
|
|
- }
|
|
|
-
|
|
|
- while (1) {
|
|
|
- if (last == 0) {
|
|
|
- err = btrfs_run_delayed_refs(trans, root,
|
|
|
- (unsigned long)-1);
|
|
|
- if (err) /* File system offline */
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- cache = btrfs_lookup_first_block_group(root->fs_info, last);
|
|
|
- while (cache) {
|
|
|
- if (cache->disk_cache_state == BTRFS_DC_CLEAR) {
|
|
|
- btrfs_put_block_group(cache);
|
|
|
- goto again;
|
|
|
- }
|
|
|
-
|
|
|
- if (cache->dirty)
|
|
|
- break;
|
|
|
- cache = next_block_group(root, cache);
|
|
|
- }
|
|
|
- if (!cache) {
|
|
|
- if (last == 0)
|
|
|
- break;
|
|
|
- last = 0;
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- if (cache->disk_cache_state == BTRFS_DC_SETUP)
|
|
|
- cache->disk_cache_state = BTRFS_DC_NEED_WRITE;
|
|
|
- cache->dirty = 0;
|
|
|
- last = cache->key.objectid + cache->key.offset;
|
|
|
-
|
|
|
- err = write_one_cache_group(trans, root, path, cache);
|
|
|
- btrfs_put_block_group(cache);
|
|
|
- if (err) /* File system offline */
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- while (1) {
|
|
|
- /*
|
|
|
- * I don't think this is needed since we're just marking our
|
|
|
- * preallocated extent as written, but just in case it can't
|
|
|
- * hurt.
|
|
|
- */
|
|
|
- if (last == 0) {
|
|
|
- err = btrfs_run_delayed_refs(trans, root,
|
|
|
- (unsigned long)-1);
|
|
|
- if (err) /* File system offline */
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- cache = btrfs_lookup_first_block_group(root->fs_info, last);
|
|
|
- while (cache) {
|
|
|
- /*
|
|
|
- * Really this shouldn't happen, but it could if we
|
|
|
- * couldn't write the entire preallocated extent and
|
|
|
- * splitting the extent resulted in a new block.
|
|
|
- */
|
|
|
- if (cache->dirty) {
|
|
|
- btrfs_put_block_group(cache);
|
|
|
- goto again;
|
|
|
- }
|
|
|
- if (cache->disk_cache_state == BTRFS_DC_NEED_WRITE)
|
|
|
- break;
|
|
|
- cache = next_block_group(root, cache);
|
|
|
- }
|
|
|
- if (!cache) {
|
|
|
- if (last == 0)
|
|
|
- break;
|
|
|
- last = 0;
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- err = btrfs_write_out_cache(root, trans, cache, path);
|
|
|
-
|
|
|
- /*
|
|
|
- * If we didn't have an error then the cache state is still
|
|
|
- * NEED_WRITE, so we can set it to WRITTEN.
|
|
|
- */
|
|
|
- if (!err && cache->disk_cache_state == BTRFS_DC_NEED_WRITE)
|
|
|
- cache->disk_cache_state = BTRFS_DC_WRITTEN;
|
|
|
- last = cache->key.objectid + cache->key.offset;
|
|
|
+ /*
|
|
|
+ * We don't need the lock here since we are protected by the transaction
|
|
|
+ * commit. We want to do the cache_save_setup first and then run the
|
|
|
+ * delayed refs to make sure we have the best chance at doing this all
|
|
|
+ * in one shot.
|
|
|
+ */
|
|
|
+ while (!list_empty(&cur_trans->dirty_bgs)) {
|
|
|
+ cache = list_first_entry(&cur_trans->dirty_bgs,
|
|
|
+ struct btrfs_block_group_cache,
|
|
|
+ dirty_list);
|
|
|
+ list_del_init(&cache->dirty_list);
|
|
|
+ if (cache->disk_cache_state == BTRFS_DC_CLEAR)
|
|
|
+ cache_save_setup(cache, trans, path);
|
|
|
+ if (!ret)
|
|
|
+ ret = btrfs_run_delayed_refs(trans, root,
|
|
|
+ (unsigned long) -1);
|
|
|
+ if (!ret && cache->disk_cache_state == BTRFS_DC_SETUP)
|
|
|
+ btrfs_write_out_cache(root, trans, cache, path);
|
|
|
+ if (!ret)
|
|
|
+ ret = write_one_cache_group(trans, root, path, cache);
|
|
|
btrfs_put_block_group(cache);
|
|
|
}
|
|
|
-out:
|
|
|
|
|
|
btrfs_free_path(path);
|
|
|
- return err;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
int btrfs_extent_readonly(struct btrfs_root *root, u64 bytenr)
|
|
@@ -5375,8 +5298,9 @@ void btrfs_delalloc_release_space(struct inode *inode, u64 num_bytes)
|
|
|
btrfs_free_reserved_data_space(inode, num_bytes);
|
|
|
}
|
|
|
|
|
|
-static int update_block_group(struct btrfs_root *root,
|
|
|
- u64 bytenr, u64 num_bytes, int alloc)
|
|
|
+static int update_block_group(struct btrfs_trans_handle *trans,
|
|
|
+ struct btrfs_root *root, u64 bytenr,
|
|
|
+ u64 num_bytes, int alloc)
|
|
|
{
|
|
|
struct btrfs_block_group_cache *cache = NULL;
|
|
|
struct btrfs_fs_info *info = root->fs_info;
|
|
@@ -5414,6 +5338,14 @@ static int update_block_group(struct btrfs_root *root,
|
|
|
if (!alloc && cache->cached == BTRFS_CACHE_NO)
|
|
|
cache_block_group(cache, 1);
|
|
|
|
|
|
+ spin_lock(&trans->transaction->dirty_bgs_lock);
|
|
|
+ if (list_empty(&cache->dirty_list)) {
|
|
|
+ list_add_tail(&cache->dirty_list,
|
|
|
+ &trans->transaction->dirty_bgs);
|
|
|
+ btrfs_get_block_group(cache);
|
|
|
+ }
|
|
|
+ spin_unlock(&trans->transaction->dirty_bgs_lock);
|
|
|
+
|
|
|
byte_in_group = bytenr - cache->key.objectid;
|
|
|
WARN_ON(byte_in_group > cache->key.offset);
|
|
|
|
|
@@ -5424,7 +5356,6 @@ static int update_block_group(struct btrfs_root *root,
|
|
|
cache->disk_cache_state < BTRFS_DC_CLEAR)
|
|
|
cache->disk_cache_state = BTRFS_DC_CLEAR;
|
|
|
|
|
|
- cache->dirty = 1;
|
|
|
old_val = btrfs_block_group_used(&cache->item);
|
|
|
num_bytes = min(total, cache->key.offset - byte_in_group);
|
|
|
if (alloc) {
|
|
@@ -6103,7 +6034,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- ret = update_block_group(root, bytenr, num_bytes, 0);
|
|
|
+ ret = update_block_group(trans, root, bytenr, num_bytes, 0);
|
|
|
if (ret) {
|
|
|
btrfs_abort_transaction(trans, extent_root, ret);
|
|
|
goto out;
|
|
@@ -7063,7 +6994,7 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
- ret = update_block_group(root, ins->objectid, ins->offset, 1);
|
|
|
+ ret = update_block_group(trans, root, ins->objectid, ins->offset, 1);
|
|
|
if (ret) { /* -ENOENT, logic error */
|
|
|
btrfs_err(fs_info, "update block group failed for %llu %llu",
|
|
|
ins->objectid, ins->offset);
|
|
@@ -7152,7 +7083,8 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- ret = update_block_group(root, ins->objectid, root->nodesize, 1);
|
|
|
+ ret = update_block_group(trans, root, ins->objectid, root->nodesize,
|
|
|
+ 1);
|
|
|
if (ret) { /* -ENOENT, logic error */
|
|
|
btrfs_err(fs_info, "update block group failed for %llu %llu",
|
|
|
ins->objectid, ins->offset);
|
|
@@ -9005,6 +8937,7 @@ btrfs_create_block_group_cache(struct btrfs_root *root, u64 start, u64 size)
|
|
|
INIT_LIST_HEAD(&cache->cluster_list);
|
|
|
INIT_LIST_HEAD(&cache->bg_list);
|
|
|
INIT_LIST_HEAD(&cache->ro_list);
|
|
|
+ INIT_LIST_HEAD(&cache->dirty_list);
|
|
|
btrfs_init_free_space_ctl(cache);
|
|
|
atomic_set(&cache->trimming, 0);
|
|
|
|
|
@@ -9068,9 +9001,8 @@ int btrfs_read_block_groups(struct btrfs_root *root)
|
|
|
* b) Setting 'dirty flag' makes sure that we flush
|
|
|
* the new space cache info onto disk.
|
|
|
*/
|
|
|
- cache->disk_cache_state = BTRFS_DC_CLEAR;
|
|
|
if (btrfs_test_opt(root, SPACE_CACHE))
|
|
|
- cache->dirty = 1;
|
|
|
+ cache->disk_cache_state = BTRFS_DC_CLEAR;
|
|
|
}
|
|
|
|
|
|
read_extent_buffer(leaf, &cache->item,
|
|
@@ -9461,6 +9393,13 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ spin_lock(&trans->transaction->dirty_bgs_lock);
|
|
|
+ if (!list_empty(&block_group->dirty_list)) {
|
|
|
+ list_del_init(&block_group->dirty_list);
|
|
|
+ btrfs_put_block_group(block_group);
|
|
|
+ }
|
|
|
+ spin_unlock(&trans->transaction->dirty_bgs_lock);
|
|
|
+
|
|
|
btrfs_remove_free_space_cache(block_group);
|
|
|
|
|
|
spin_lock(&block_group->space_info->lock);
|