mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-04 18:09:57 +00:00
Btrfs: break up __btrfs_write_out_cache to cut down stack usage
__btrfs_write_out_cache was one of our stack pigs. This breaks it up into helper functions and slims it down to 194 bytes. Signed-off-by: Chris Mason <clm@fb.com>
This commit is contained in:
parent
2a10840945
commit
d4452bc526
1 changed files with 194 additions and 120 deletions
|
@ -851,90 +851,44 @@ int load_free_space_cache(struct btrfs_fs_info *fs_info,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static noinline_for_stack
|
||||||
* __btrfs_write_out_cache - write out cached info to an inode
|
int write_cache_extent_entries(struct io_ctl *io_ctl,
|
||||||
* @root - the root the inode belongs to
|
struct btrfs_free_space_ctl *ctl,
|
||||||
* @ctl - the free space cache we are going to write out
|
struct btrfs_block_group_cache *block_group,
|
||||||
* @block_group - the block_group for this cache if it belongs to a block_group
|
int *entries, int *bitmaps,
|
||||||
* @trans - the trans handle
|
struct list_head *bitmap_list)
|
||||||
* @path - the path to use
|
|
||||||
* @offset - the offset for the key we'll insert
|
|
||||||
*
|
|
||||||
* This function writes out a free space cache struct to disk for quick recovery
|
|
||||||
* on mount. This will return 0 if it was successfull in writing the cache out,
|
|
||||||
* and -1 if it was not.
|
|
||||||
*/
|
|
||||||
static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
|
|
||||||
struct btrfs_free_space_ctl *ctl,
|
|
||||||
struct btrfs_block_group_cache *block_group,
|
|
||||||
struct btrfs_trans_handle *trans,
|
|
||||||
struct btrfs_path *path, u64 offset)
|
|
||||||
{
|
{
|
||||||
struct btrfs_free_space_header *header;
|
|
||||||
struct extent_buffer *leaf;
|
|
||||||
struct rb_node *node;
|
|
||||||
struct list_head *pos, *n;
|
|
||||||
struct extent_state *cached_state = NULL;
|
|
||||||
struct btrfs_free_cluster *cluster = NULL;
|
|
||||||
struct extent_io_tree *unpin = NULL;
|
|
||||||
struct io_ctl io_ctl;
|
|
||||||
struct list_head bitmap_list;
|
|
||||||
struct btrfs_key key;
|
|
||||||
u64 start, extent_start, extent_end, len;
|
|
||||||
int entries = 0;
|
|
||||||
int bitmaps = 0;
|
|
||||||
int ret;
|
int ret;
|
||||||
int err = -1;
|
struct btrfs_free_cluster *cluster = NULL;
|
||||||
|
struct rb_node *node = rb_first(&ctl->free_space_offset);
|
||||||
INIT_LIST_HEAD(&bitmap_list);
|
|
||||||
|
|
||||||
if (!i_size_read(inode))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
ret = io_ctl_init(&io_ctl, inode, root);
|
|
||||||
if (ret)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* Get the cluster for this block_group if it exists */
|
/* Get the cluster for this block_group if it exists */
|
||||||
if (block_group && !list_empty(&block_group->cluster_list))
|
if (block_group && !list_empty(&block_group->cluster_list)) {
|
||||||
cluster = list_entry(block_group->cluster_list.next,
|
cluster = list_entry(block_group->cluster_list.next,
|
||||||
struct btrfs_free_cluster,
|
struct btrfs_free_cluster,
|
||||||
block_group_list);
|
block_group_list);
|
||||||
|
}
|
||||||
|
|
||||||
/* Lock all pages first so we can lock the extent safely. */
|
|
||||||
io_ctl_prepare_pages(&io_ctl, inode, 0);
|
|
||||||
|
|
||||||
lock_extent_bits(&BTRFS_I(inode)->io_tree, 0, i_size_read(inode) - 1,
|
|
||||||
0, &cached_state);
|
|
||||||
|
|
||||||
node = rb_first(&ctl->free_space_offset);
|
|
||||||
if (!node && cluster) {
|
if (!node && cluster) {
|
||||||
node = rb_first(&cluster->root);
|
node = rb_first(&cluster->root);
|
||||||
cluster = NULL;
|
cluster = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure we can fit our crcs into the first page */
|
|
||||||
if (io_ctl.check_crcs &&
|
|
||||||
(io_ctl.num_pages * sizeof(u32)) >= PAGE_CACHE_SIZE)
|
|
||||||
goto out_nospc;
|
|
||||||
|
|
||||||
io_ctl_set_generation(&io_ctl, trans->transid);
|
|
||||||
|
|
||||||
/* Write out the extent entries */
|
/* Write out the extent entries */
|
||||||
while (node) {
|
while (node) {
|
||||||
struct btrfs_free_space *e;
|
struct btrfs_free_space *e;
|
||||||
|
|
||||||
e = rb_entry(node, struct btrfs_free_space, offset_index);
|
e = rb_entry(node, struct btrfs_free_space, offset_index);
|
||||||
entries++;
|
*entries += 1;
|
||||||
|
|
||||||
ret = io_ctl_add_entry(&io_ctl, e->offset, e->bytes,
|
ret = io_ctl_add_entry(io_ctl, e->offset, e->bytes,
|
||||||
e->bitmap);
|
e->bitmap);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_nospc;
|
goto fail;
|
||||||
|
|
||||||
if (e->bitmap) {
|
if (e->bitmap) {
|
||||||
list_add_tail(&e->list, &bitmap_list);
|
list_add_tail(&e->list, bitmap_list);
|
||||||
bitmaps++;
|
*bitmaps += 1;
|
||||||
}
|
}
|
||||||
node = rb_next(node);
|
node = rb_next(node);
|
||||||
if (!node && cluster) {
|
if (!node && cluster) {
|
||||||
|
@ -942,13 +896,84 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
|
||||||
cluster = NULL;
|
cluster = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
fail:
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
static noinline_for_stack int
|
||||||
|
update_cache_item(struct btrfs_trans_handle *trans,
|
||||||
|
struct btrfs_root *root,
|
||||||
|
struct inode *inode,
|
||||||
|
struct btrfs_path *path, u64 offset,
|
||||||
|
int entries, int bitmaps)
|
||||||
|
{
|
||||||
|
struct btrfs_key key;
|
||||||
|
struct btrfs_free_space_header *header;
|
||||||
|
struct extent_buffer *leaf;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
key.objectid = BTRFS_FREE_SPACE_OBJECTID;
|
||||||
|
key.offset = offset;
|
||||||
|
key.type = 0;
|
||||||
|
|
||||||
|
ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
|
||||||
|
if (ret < 0) {
|
||||||
|
clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, inode->i_size - 1,
|
||||||
|
EXTENT_DIRTY | EXTENT_DELALLOC, 0, 0, NULL,
|
||||||
|
GFP_NOFS);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
leaf = path->nodes[0];
|
||||||
|
if (ret > 0) {
|
||||||
|
struct btrfs_key found_key;
|
||||||
|
ASSERT(path->slots[0]);
|
||||||
|
path->slots[0]--;
|
||||||
|
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
|
||||||
|
if (found_key.objectid != BTRFS_FREE_SPACE_OBJECTID ||
|
||||||
|
found_key.offset != offset) {
|
||||||
|
clear_extent_bit(&BTRFS_I(inode)->io_tree, 0,
|
||||||
|
inode->i_size - 1,
|
||||||
|
EXTENT_DIRTY | EXTENT_DELALLOC, 0, 0,
|
||||||
|
NULL, GFP_NOFS);
|
||||||
|
btrfs_release_path(path);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BTRFS_I(inode)->generation = trans->transid;
|
||||||
|
header = btrfs_item_ptr(leaf, path->slots[0],
|
||||||
|
struct btrfs_free_space_header);
|
||||||
|
btrfs_set_free_space_entries(leaf, header, entries);
|
||||||
|
btrfs_set_free_space_bitmaps(leaf, header, bitmaps);
|
||||||
|
btrfs_set_free_space_generation(leaf, header, trans->transid);
|
||||||
|
btrfs_mark_buffer_dirty(leaf);
|
||||||
|
btrfs_release_path(path);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static noinline_for_stack int
|
||||||
|
add_ioctl_entries(struct btrfs_root *root,
|
||||||
|
struct inode *inode,
|
||||||
|
struct btrfs_block_group_cache *block_group,
|
||||||
|
struct io_ctl *io_ctl,
|
||||||
|
struct extent_state **cached_state,
|
||||||
|
struct list_head *bitmap_list,
|
||||||
|
int *entries)
|
||||||
|
{
|
||||||
|
u64 start, extent_start, extent_end, len;
|
||||||
|
struct list_head *pos, *n;
|
||||||
|
struct extent_io_tree *unpin = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We want to add any pinned extents to our free space cache
|
* We want to add any pinned extents to our free space cache
|
||||||
* so we don't leak the space
|
* so we don't leak the space
|
||||||
*/
|
*
|
||||||
|
|
||||||
/*
|
|
||||||
* We shouldn't have switched the pinned extents yet so this is the
|
* We shouldn't have switched the pinned extents yet so this is the
|
||||||
* right one
|
* right one
|
||||||
*/
|
*/
|
||||||
|
@ -977,8 +1002,8 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
|
||||||
block_group->key.offset, extent_end + 1);
|
block_group->key.offset, extent_end + 1);
|
||||||
len = extent_end - extent_start;
|
len = extent_end - extent_start;
|
||||||
|
|
||||||
entries++;
|
*entries += 1;
|
||||||
ret = io_ctl_add_entry(&io_ctl, extent_start, len, NULL);
|
ret = io_ctl_add_entry(io_ctl, extent_start, len, NULL);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_nospc;
|
goto out_nospc;
|
||||||
|
|
||||||
|
@ -986,74 +1011,129 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write out the bitmaps */
|
/* Write out the bitmaps */
|
||||||
list_for_each_safe(pos, n, &bitmap_list) {
|
list_for_each_safe(pos, n, bitmap_list) {
|
||||||
struct btrfs_free_space *entry =
|
struct btrfs_free_space *entry =
|
||||||
list_entry(pos, struct btrfs_free_space, list);
|
list_entry(pos, struct btrfs_free_space, list);
|
||||||
|
|
||||||
ret = io_ctl_add_bitmap(&io_ctl, entry->bitmap);
|
ret = io_ctl_add_bitmap(io_ctl, entry->bitmap);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_nospc;
|
goto out_nospc;
|
||||||
list_del_init(&entry->list);
|
list_del_init(&entry->list);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Zero out the rest of the pages just to make sure */
|
/* Zero out the rest of the pages just to make sure */
|
||||||
io_ctl_zero_remaining_pages(&io_ctl);
|
io_ctl_zero_remaining_pages(io_ctl);
|
||||||
|
|
||||||
ret = btrfs_dirty_pages(root, inode, io_ctl.pages, io_ctl.num_pages,
|
ret = btrfs_dirty_pages(root, inode, io_ctl->pages, io_ctl->num_pages,
|
||||||
0, i_size_read(inode), &cached_state);
|
0, i_size_read(inode), cached_state);
|
||||||
io_ctl_drop_pages(&io_ctl);
|
io_ctl_drop_pages(io_ctl);
|
||||||
unlock_extent_cached(&BTRFS_I(inode)->io_tree, 0,
|
unlock_extent_cached(&BTRFS_I(inode)->io_tree, 0,
|
||||||
i_size_read(inode) - 1, &cached_state, GFP_NOFS);
|
i_size_read(inode) - 1, cached_state, GFP_NOFS);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto fail;
|
||||||
|
|
||||||
ret = btrfs_wait_ordered_range(inode, 0, (u64)-1);
|
ret = btrfs_wait_ordered_range(inode, 0, (u64)-1);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, inode->i_size - 1,
|
clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, inode->i_size - 1,
|
||||||
EXTENT_DIRTY | EXTENT_DELALLOC, 0, 0, NULL,
|
EXTENT_DIRTY | EXTENT_DELALLOC, 0, 0, NULL,
|
||||||
GFP_NOFS);
|
GFP_NOFS);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
out_nospc:
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void noinline_for_stack
|
||||||
|
cleanup_write_cache_enospc(struct inode *inode,
|
||||||
|
struct io_ctl *io_ctl,
|
||||||
|
struct extent_state **cached_state,
|
||||||
|
struct list_head *bitmap_list)
|
||||||
|
{
|
||||||
|
struct list_head *pos, *n;
|
||||||
|
list_for_each_safe(pos, n, bitmap_list) {
|
||||||
|
struct btrfs_free_space *entry =
|
||||||
|
list_entry(pos, struct btrfs_free_space, list);
|
||||||
|
list_del_init(&entry->list);
|
||||||
|
}
|
||||||
|
io_ctl_drop_pages(io_ctl);
|
||||||
|
unlock_extent_cached(&BTRFS_I(inode)->io_tree, 0,
|
||||||
|
i_size_read(inode) - 1, cached_state,
|
||||||
|
GFP_NOFS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __btrfs_write_out_cache - write out cached info to an inode
|
||||||
|
* @root - the root the inode belongs to
|
||||||
|
* @ctl - the free space cache we are going to write out
|
||||||
|
* @block_group - the block_group for this cache if it belongs to a block_group
|
||||||
|
* @trans - the trans handle
|
||||||
|
* @path - the path to use
|
||||||
|
* @offset - the offset for the key we'll insert
|
||||||
|
*
|
||||||
|
* This function writes out a free space cache struct to disk for quick recovery
|
||||||
|
* on mount. This will return 0 if it was successfull in writing the cache out,
|
||||||
|
* and -1 if it was not.
|
||||||
|
*/
|
||||||
|
static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
|
||||||
|
struct btrfs_free_space_ctl *ctl,
|
||||||
|
struct btrfs_block_group_cache *block_group,
|
||||||
|
struct btrfs_trans_handle *trans,
|
||||||
|
struct btrfs_path *path, u64 offset)
|
||||||
|
{
|
||||||
|
struct extent_state *cached_state = NULL;
|
||||||
|
struct io_ctl io_ctl;
|
||||||
|
struct list_head bitmap_list;
|
||||||
|
int entries = 0;
|
||||||
|
int bitmaps = 0;
|
||||||
|
int ret;
|
||||||
|
int err = -1;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&bitmap_list);
|
||||||
|
|
||||||
|
if (!i_size_read(inode))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ret = io_ctl_init(&io_ctl, inode, root);
|
||||||
|
if (ret)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Lock all pages first so we can lock the extent safely. */
|
||||||
|
io_ctl_prepare_pages(&io_ctl, inode, 0);
|
||||||
|
|
||||||
|
lock_extent_bits(&BTRFS_I(inode)->io_tree, 0, i_size_read(inode) - 1,
|
||||||
|
0, &cached_state);
|
||||||
|
|
||||||
|
|
||||||
|
/* Make sure we can fit our crcs into the first page */
|
||||||
|
if (io_ctl.check_crcs &&
|
||||||
|
(io_ctl.num_pages * sizeof(u32)) >= PAGE_CACHE_SIZE)
|
||||||
|
goto out_nospc;
|
||||||
|
|
||||||
|
io_ctl_set_generation(&io_ctl, trans->transid);
|
||||||
|
|
||||||
|
ret = write_cache_extent_entries(&io_ctl, ctl,
|
||||||
|
block_group, &entries, &bitmaps,
|
||||||
|
&bitmap_list);
|
||||||
|
if (ret)
|
||||||
|
goto out_nospc;
|
||||||
|
|
||||||
|
ret = add_ioctl_entries(root, inode, block_group, &io_ctl,
|
||||||
|
&cached_state, &bitmap_list, &entries);
|
||||||
|
|
||||||
|
if (ret == -ENOSPC)
|
||||||
|
goto out_nospc;
|
||||||
|
else if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
key.objectid = BTRFS_FREE_SPACE_OBJECTID;
|
err = update_cache_item(trans, root, inode, path, offset,
|
||||||
key.offset = offset;
|
entries, bitmaps);
|
||||||
key.type = 0;
|
|
||||||
|
|
||||||
ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
|
|
||||||
if (ret < 0) {
|
|
||||||
clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, inode->i_size - 1,
|
|
||||||
EXTENT_DIRTY | EXTENT_DELALLOC, 0, 0, NULL,
|
|
||||||
GFP_NOFS);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
leaf = path->nodes[0];
|
|
||||||
if (ret > 0) {
|
|
||||||
struct btrfs_key found_key;
|
|
||||||
ASSERT(path->slots[0]);
|
|
||||||
path->slots[0]--;
|
|
||||||
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
|
|
||||||
if (found_key.objectid != BTRFS_FREE_SPACE_OBJECTID ||
|
|
||||||
found_key.offset != offset) {
|
|
||||||
clear_extent_bit(&BTRFS_I(inode)->io_tree, 0,
|
|
||||||
inode->i_size - 1,
|
|
||||||
EXTENT_DIRTY | EXTENT_DELALLOC, 0, 0,
|
|
||||||
NULL, GFP_NOFS);
|
|
||||||
btrfs_release_path(path);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BTRFS_I(inode)->generation = trans->transid;
|
|
||||||
header = btrfs_item_ptr(leaf, path->slots[0],
|
|
||||||
struct btrfs_free_space_header);
|
|
||||||
btrfs_set_free_space_entries(leaf, header, entries);
|
|
||||||
btrfs_set_free_space_bitmaps(leaf, header, bitmaps);
|
|
||||||
btrfs_set_free_space_generation(leaf, header, trans->transid);
|
|
||||||
btrfs_mark_buffer_dirty(leaf);
|
|
||||||
btrfs_release_path(path);
|
|
||||||
|
|
||||||
err = 0;
|
|
||||||
out:
|
out:
|
||||||
io_ctl_free(&io_ctl);
|
io_ctl_free(&io_ctl);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -1064,14 +1144,8 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
out_nospc:
|
out_nospc:
|
||||||
list_for_each_safe(pos, n, &bitmap_list) {
|
|
||||||
struct btrfs_free_space *entry =
|
cleanup_write_cache_enospc(inode, &io_ctl, &cached_state, &bitmap_list);
|
||||||
list_entry(pos, struct btrfs_free_space, list);
|
|
||||||
list_del_init(&entry->list);
|
|
||||||
}
|
|
||||||
io_ctl_drop_pages(&io_ctl);
|
|
||||||
unlock_extent_cached(&BTRFS_I(inode)->io_tree, 0,
|
|
||||||
i_size_read(inode) - 1, &cached_state, GFP_NOFS);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue