mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-10 15:58:47 +00:00
Btrfs: avoid starting a transaction in the write path
I noticed while looking at a deadlock that we are always starting a transaction in cow_file_range(). This isn't really needed since we only need a transaction if we are doing an inline extent, or if the allocator needs to allocate a chunk. So push down all the transaction start stuff to be closer to where we actually need a transaction in all of these cases. This will hopefully reduce our write latency when we are committing often. Thanks, Signed-off-by: Josef Bacik <jbacik@fusionio.com> Signed-off-by: Chris Mason <chris.mason@fusionio.com>
This commit is contained in:
parent
9ffba8cda9
commit
00361589d2
@ -3165,11 +3165,9 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
|
|||||||
struct btrfs_root *root,
|
struct btrfs_root *root,
|
||||||
u64 root_objectid, u64 owner, u64 offset,
|
u64 root_objectid, u64 owner, u64 offset,
|
||||||
struct btrfs_key *ins);
|
struct btrfs_key *ins);
|
||||||
int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
|
int btrfs_reserve_extent(struct btrfs_root *root, u64 num_bytes,
|
||||||
struct btrfs_root *root,
|
u64 min_alloc_size, u64 empty_size, u64 hint_byte,
|
||||||
u64 num_bytes, u64 min_alloc_size,
|
struct btrfs_key *ins, int is_data);
|
||||||
u64 empty_size, u64 hint_byte,
|
|
||||||
struct btrfs_key *ins, int is_data);
|
|
||||||
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 for_cow);
|
struct extent_buffer *buf, int full_backref, int for_cow);
|
||||||
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,
|
||||||
@ -3612,8 +3610,7 @@ void btrfs_wait_and_free_delalloc_work(struct btrfs_delalloc_work *work);
|
|||||||
struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *page,
|
struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *page,
|
||||||
size_t pg_offset, u64 start, u64 len,
|
size_t pg_offset, u64 start, u64 len,
|
||||||
int create);
|
int create);
|
||||||
noinline int can_nocow_extent(struct btrfs_trans_handle *trans,
|
noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len,
|
||||||
struct inode *inode, u64 offset, u64 *len,
|
|
||||||
u64 *orig_start, u64 *orig_block_len,
|
u64 *orig_start, u64 *orig_block_len,
|
||||||
u64 *ram_bytes);
|
u64 *ram_bytes);
|
||||||
|
|
||||||
|
@ -6121,8 +6121,7 @@ enum btrfs_loop_type {
|
|||||||
* ins->offset == number of blocks
|
* ins->offset == number of blocks
|
||||||
* Any available blocks before search_start are skipped.
|
* Any available blocks before search_start are skipped.
|
||||||
*/
|
*/
|
||||||
static noinline int find_free_extent(struct btrfs_trans_handle *trans,
|
static noinline int find_free_extent(struct btrfs_root *orig_root,
|
||||||
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)
|
||||||
@ -6345,10 +6344,10 @@ refill_cluster:
|
|||||||
block_group->full_stripe_len);
|
block_group->full_stripe_len);
|
||||||
|
|
||||||
/* allocate a cluster in this block group */
|
/* allocate a cluster in this block group */
|
||||||
ret = btrfs_find_space_cluster(trans, root,
|
ret = btrfs_find_space_cluster(root, block_group,
|
||||||
block_group, last_ptr,
|
last_ptr, search_start,
|
||||||
search_start, num_bytes,
|
num_bytes,
|
||||||
aligned_cluster);
|
aligned_cluster);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
/*
|
/*
|
||||||
* now pull our allocation out of this
|
* now pull our allocation out of this
|
||||||
@ -6479,17 +6478,28 @@ loop:
|
|||||||
index = 0;
|
index = 0;
|
||||||
loop++;
|
loop++;
|
||||||
if (loop == LOOP_ALLOC_CHUNK) {
|
if (loop == LOOP_ALLOC_CHUNK) {
|
||||||
|
struct btrfs_trans_handle *trans;
|
||||||
|
|
||||||
|
trans = btrfs_join_transaction(root);
|
||||||
|
if (IS_ERR(trans)) {
|
||||||
|
ret = PTR_ERR(trans);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
ret = do_chunk_alloc(trans, root, flags,
|
ret = do_chunk_alloc(trans, root, flags,
|
||||||
CHUNK_ALLOC_FORCE);
|
CHUNK_ALLOC_FORCE);
|
||||||
/*
|
/*
|
||||||
* Do not bail out on ENOSPC since we
|
* Do not bail out on ENOSPC since we
|
||||||
* can do more things.
|
* can do more things.
|
||||||
*/
|
*/
|
||||||
if (ret < 0 && ret != -ENOSPC) {
|
if (ret < 0 && ret != -ENOSPC)
|
||||||
btrfs_abort_transaction(trans,
|
btrfs_abort_transaction(trans,
|
||||||
root, ret);
|
root, ret);
|
||||||
|
else
|
||||||
|
ret = 0;
|
||||||
|
btrfs_end_transaction(trans, root);
|
||||||
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loop == LOOP_NO_EMPTY_SIZE) {
|
if (loop == LOOP_NO_EMPTY_SIZE) {
|
||||||
@ -6553,8 +6563,7 @@ again:
|
|||||||
up_read(&info->groups_sem);
|
up_read(&info->groups_sem);
|
||||||
}
|
}
|
||||||
|
|
||||||
int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
|
int btrfs_reserve_extent(struct btrfs_root *root,
|
||||||
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)
|
||||||
@ -6566,8 +6575,8 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
|
|||||||
flags = btrfs_get_alloc_profile(root, is_data);
|
flags = btrfs_get_alloc_profile(root, is_data);
|
||||||
again:
|
again:
|
||||||
WARN_ON(num_bytes < root->sectorsize);
|
WARN_ON(num_bytes < root->sectorsize);
|
||||||
ret = find_free_extent(trans, root, num_bytes, empty_size,
|
ret = find_free_extent(root, num_bytes, empty_size, hint_byte, ins,
|
||||||
hint_byte, ins, flags);
|
flags);
|
||||||
|
|
||||||
if (ret == -ENOSPC) {
|
if (ret == -ENOSPC) {
|
||||||
if (!final_tried) {
|
if (!final_tried) {
|
||||||
@ -6955,7 +6964,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
|
|||||||
if (IS_ERR(block_rsv))
|
if (IS_ERR(block_rsv))
|
||||||
return ERR_CAST(block_rsv);
|
return ERR_CAST(block_rsv);
|
||||||
|
|
||||||
ret = btrfs_reserve_extent(trans, root, blocksize, blocksize,
|
ret = btrfs_reserve_extent(root, blocksize, blocksize,
|
||||||
empty_size, hint, &ins, 0);
|
empty_size, hint, &ins, 0);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
unuse_block_rsv(root->fs_info, block_rsv, blocksize);
|
unuse_block_rsv(root->fs_info, block_rsv, blocksize);
|
||||||
|
@ -1339,7 +1339,6 @@ fail:
|
|||||||
static noinline int check_can_nocow(struct inode *inode, loff_t pos,
|
static noinline int check_can_nocow(struct inode *inode, loff_t pos,
|
||||||
size_t *write_bytes)
|
size_t *write_bytes)
|
||||||
{
|
{
|
||||||
struct btrfs_trans_handle *trans;
|
|
||||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||||
struct btrfs_ordered_extent *ordered;
|
struct btrfs_ordered_extent *ordered;
|
||||||
u64 lockstart, lockend;
|
u64 lockstart, lockend;
|
||||||
@ -1361,16 +1360,8 @@ static noinline int check_can_nocow(struct inode *inode, loff_t pos,
|
|||||||
btrfs_put_ordered_extent(ordered);
|
btrfs_put_ordered_extent(ordered);
|
||||||
}
|
}
|
||||||
|
|
||||||
trans = btrfs_join_transaction(root);
|
|
||||||
if (IS_ERR(trans)) {
|
|
||||||
unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend);
|
|
||||||
return PTR_ERR(trans);
|
|
||||||
}
|
|
||||||
|
|
||||||
num_bytes = lockend - lockstart + 1;
|
num_bytes = lockend - lockstart + 1;
|
||||||
ret = can_nocow_extent(trans, inode, lockstart, &num_bytes, NULL, NULL,
|
ret = can_nocow_extent(inode, lockstart, &num_bytes, NULL, NULL, NULL);
|
||||||
NULL);
|
|
||||||
btrfs_end_transaction(trans, root);
|
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
ret = 0;
|
ret = 0;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2525,8 +2525,7 @@ setup_cluster_bitmap(struct btrfs_block_group_cache *block_group,
|
|||||||
* returns zero and sets up cluster if things worked out, otherwise
|
* returns zero and sets up cluster if things worked out, otherwise
|
||||||
* it returns -enospc
|
* it returns -enospc
|
||||||
*/
|
*/
|
||||||
int btrfs_find_space_cluster(struct btrfs_trans_handle *trans,
|
int btrfs_find_space_cluster(struct btrfs_root *root,
|
||||||
struct btrfs_root *root,
|
|
||||||
struct btrfs_block_group_cache *block_group,
|
struct btrfs_block_group_cache *block_group,
|
||||||
struct btrfs_free_cluster *cluster,
|
struct btrfs_free_cluster *cluster,
|
||||||
u64 offset, u64 bytes, u64 empty_size)
|
u64 offset, u64 bytes, u64 empty_size)
|
||||||
|
@ -98,8 +98,7 @@ u64 btrfs_find_space_for_alloc(struct btrfs_block_group_cache *block_group,
|
|||||||
u64 btrfs_find_ino_for_alloc(struct btrfs_root *fs_root);
|
u64 btrfs_find_ino_for_alloc(struct btrfs_root *fs_root);
|
||||||
void btrfs_dump_free_space(struct btrfs_block_group_cache *block_group,
|
void btrfs_dump_free_space(struct btrfs_block_group_cache *block_group,
|
||||||
u64 bytes);
|
u64 bytes);
|
||||||
int btrfs_find_space_cluster(struct btrfs_trans_handle *trans,
|
int btrfs_find_space_cluster(struct btrfs_root *root,
|
||||||
struct btrfs_root *root,
|
|
||||||
struct btrfs_block_group_cache *block_group,
|
struct btrfs_block_group_cache *block_group,
|
||||||
struct btrfs_free_cluster *cluster,
|
struct btrfs_free_cluster *cluster,
|
||||||
u64 offset, u64 bytes, u64 empty_size);
|
u64 offset, u64 bytes, u64 empty_size);
|
||||||
|
217
fs/btrfs/inode.c
217
fs/btrfs/inode.c
@ -230,12 +230,13 @@ fail:
|
|||||||
* does the checks required to make sure the data is small enough
|
* does the checks required to make sure the data is small enough
|
||||||
* to fit as an inline extent.
|
* to fit as an inline extent.
|
||||||
*/
|
*/
|
||||||
static noinline int cow_file_range_inline(struct btrfs_trans_handle *trans,
|
static noinline int cow_file_range_inline(struct btrfs_root *root,
|
||||||
struct btrfs_root *root,
|
struct inode *inode, u64 start,
|
||||||
struct inode *inode, u64 start, u64 end,
|
u64 end, size_t compressed_size,
|
||||||
size_t compressed_size, int compress_type,
|
int compress_type,
|
||||||
struct page **compressed_pages)
|
struct page **compressed_pages)
|
||||||
{
|
{
|
||||||
|
struct btrfs_trans_handle *trans;
|
||||||
u64 isize = i_size_read(inode);
|
u64 isize = i_size_read(inode);
|
||||||
u64 actual_end = min(end + 1, isize);
|
u64 actual_end = min(end + 1, isize);
|
||||||
u64 inline_len = actual_end - start;
|
u64 inline_len = actual_end - start;
|
||||||
@ -256,9 +257,16 @@ static noinline int cow_file_range_inline(struct btrfs_trans_handle *trans,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trans = btrfs_join_transaction(root);
|
||||||
|
if (IS_ERR(trans))
|
||||||
|
return PTR_ERR(trans);
|
||||||
|
trans->block_rsv = &root->fs_info->delalloc_block_rsv;
|
||||||
|
|
||||||
ret = btrfs_drop_extents(trans, root, inode, start, aligned_end, 1);
|
ret = btrfs_drop_extents(trans, root, inode, start, aligned_end, 1);
|
||||||
if (ret)
|
if (ret) {
|
||||||
return ret;
|
btrfs_abort_transaction(trans, root, ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
if (isize > actual_end)
|
if (isize > actual_end)
|
||||||
inline_len = min_t(u64, isize, actual_end);
|
inline_len = min_t(u64, isize, actual_end);
|
||||||
@ -267,15 +275,18 @@ static noinline int cow_file_range_inline(struct btrfs_trans_handle *trans,
|
|||||||
compress_type, compressed_pages);
|
compress_type, compressed_pages);
|
||||||
if (ret && ret != -ENOSPC) {
|
if (ret && ret != -ENOSPC) {
|
||||||
btrfs_abort_transaction(trans, root, ret);
|
btrfs_abort_transaction(trans, root, ret);
|
||||||
return ret;
|
goto out;
|
||||||
} else if (ret == -ENOSPC) {
|
} else if (ret == -ENOSPC) {
|
||||||
return 1;
|
ret = 1;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &BTRFS_I(inode)->runtime_flags);
|
set_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &BTRFS_I(inode)->runtime_flags);
|
||||||
btrfs_delalloc_release_metadata(inode, end + 1 - start);
|
btrfs_delalloc_release_metadata(inode, end + 1 - start);
|
||||||
btrfs_drop_extent_cache(inode, start, aligned_end - 1, 0);
|
btrfs_drop_extent_cache(inode, start, aligned_end - 1, 0);
|
||||||
return 0;
|
out:
|
||||||
|
btrfs_end_transaction(trans, root);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct async_extent {
|
struct async_extent {
|
||||||
@ -343,7 +354,6 @@ static noinline int compress_file_range(struct inode *inode,
|
|||||||
int *num_added)
|
int *num_added)
|
||||||
{
|
{
|
||||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||||
struct btrfs_trans_handle *trans;
|
|
||||||
u64 num_bytes;
|
u64 num_bytes;
|
||||||
u64 blocksize = root->sectorsize;
|
u64 blocksize = root->sectorsize;
|
||||||
u64 actual_end;
|
u64 actual_end;
|
||||||
@ -461,25 +471,16 @@ again:
|
|||||||
}
|
}
|
||||||
cont:
|
cont:
|
||||||
if (start == 0) {
|
if (start == 0) {
|
||||||
trans = btrfs_join_transaction(root);
|
|
||||||
if (IS_ERR(trans)) {
|
|
||||||
ret = PTR_ERR(trans);
|
|
||||||
trans = NULL;
|
|
||||||
goto cleanup_and_out;
|
|
||||||
}
|
|
||||||
trans->block_rsv = &root->fs_info->delalloc_block_rsv;
|
|
||||||
|
|
||||||
/* lets try to make an inline extent */
|
/* lets try to make an inline extent */
|
||||||
if (ret || total_in < (actual_end - start)) {
|
if (ret || total_in < (actual_end - start)) {
|
||||||
/* we didn't compress the entire range, try
|
/* we didn't compress the entire range, try
|
||||||
* to make an uncompressed inline extent.
|
* to make an uncompressed inline extent.
|
||||||
*/
|
*/
|
||||||
ret = cow_file_range_inline(trans, root, inode,
|
ret = cow_file_range_inline(root, inode, start, end,
|
||||||
start, end, 0, 0, NULL);
|
0, 0, NULL);
|
||||||
} else {
|
} else {
|
||||||
/* try making a compressed inline extent */
|
/* try making a compressed inline extent */
|
||||||
ret = cow_file_range_inline(trans, root, inode,
|
ret = cow_file_range_inline(root, inode, start, end,
|
||||||
start, end,
|
|
||||||
total_compressed,
|
total_compressed,
|
||||||
compress_type, pages);
|
compress_type, pages);
|
||||||
}
|
}
|
||||||
@ -498,10 +499,8 @@ cont:
|
|||||||
PAGE_CLEAR_DIRTY |
|
PAGE_CLEAR_DIRTY |
|
||||||
PAGE_SET_WRITEBACK |
|
PAGE_SET_WRITEBACK |
|
||||||
PAGE_END_WRITEBACK);
|
PAGE_END_WRITEBACK);
|
||||||
btrfs_end_transaction(trans, root);
|
|
||||||
goto free_pages_out;
|
goto free_pages_out;
|
||||||
}
|
}
|
||||||
btrfs_end_transaction(trans, root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (will_compress) {
|
if (will_compress) {
|
||||||
@ -592,18 +591,6 @@ free_pages_out:
|
|||||||
kfree(pages);
|
kfree(pages);
|
||||||
|
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
cleanup_and_out:
|
|
||||||
extent_clear_unlock_delalloc(inode, start, end, NULL,
|
|
||||||
EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING |
|
|
||||||
EXTENT_DEFRAG, PAGE_UNLOCK |
|
|
||||||
PAGE_CLEAR_DIRTY | PAGE_SET_WRITEBACK |
|
|
||||||
PAGE_END_WRITEBACK);
|
|
||||||
if (!trans || IS_ERR(trans))
|
|
||||||
btrfs_error(root->fs_info, ret, "Failed to join transaction");
|
|
||||||
else
|
|
||||||
btrfs_abort_transaction(trans, root, ret);
|
|
||||||
goto free_pages_out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -617,7 +604,6 @@ static noinline int submit_compressed_extents(struct inode *inode,
|
|||||||
{
|
{
|
||||||
struct async_extent *async_extent;
|
struct async_extent *async_extent;
|
||||||
u64 alloc_hint = 0;
|
u64 alloc_hint = 0;
|
||||||
struct btrfs_trans_handle *trans;
|
|
||||||
struct btrfs_key ins;
|
struct btrfs_key ins;
|
||||||
struct extent_map *em;
|
struct extent_map *em;
|
||||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||||
@ -678,20 +664,10 @@ retry:
|
|||||||
lock_extent(io_tree, async_extent->start,
|
lock_extent(io_tree, async_extent->start,
|
||||||
async_extent->start + async_extent->ram_size - 1);
|
async_extent->start + async_extent->ram_size - 1);
|
||||||
|
|
||||||
trans = btrfs_join_transaction(root);
|
ret = btrfs_reserve_extent(root,
|
||||||
if (IS_ERR(trans)) {
|
|
||||||
ret = PTR_ERR(trans);
|
|
||||||
} else {
|
|
||||||
trans->block_rsv = &root->fs_info->delalloc_block_rsv;
|
|
||||||
ret = btrfs_reserve_extent(trans, 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);
|
||||||
if (ret && ret != -ENOSPC)
|
|
||||||
btrfs_abort_transaction(trans, root, ret);
|
|
||||||
btrfs_end_transaction(trans, root);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -850,14 +826,13 @@ static u64 get_extent_allocation_hint(struct inode *inode, u64 start,
|
|||||||
* required to start IO on it. It may be clean and already done with
|
* required to start IO on it. It may be clean and already done with
|
||||||
* IO when we return.
|
* IO when we return.
|
||||||
*/
|
*/
|
||||||
static noinline int __cow_file_range(struct btrfs_trans_handle *trans,
|
static noinline int cow_file_range(struct inode *inode,
|
||||||
struct inode *inode,
|
struct page *locked_page,
|
||||||
struct btrfs_root *root,
|
u64 start, u64 end, int *page_started,
|
||||||
struct page *locked_page,
|
unsigned long *nr_written,
|
||||||
u64 start, u64 end, int *page_started,
|
int unlock)
|
||||||
unsigned long *nr_written,
|
|
||||||
int unlock)
|
|
||||||
{
|
{
|
||||||
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||||
u64 alloc_hint = 0;
|
u64 alloc_hint = 0;
|
||||||
u64 num_bytes;
|
u64 num_bytes;
|
||||||
unsigned long ram_size;
|
unsigned long ram_size;
|
||||||
@ -878,12 +853,12 @@ static noinline int __cow_file_range(struct btrfs_trans_handle *trans,
|
|||||||
/* if this is a small write inside eof, kick off defrag */
|
/* if this is a small write inside eof, kick off defrag */
|
||||||
if (num_bytes < 64 * 1024 &&
|
if (num_bytes < 64 * 1024 &&
|
||||||
(start > 0 || end + 1 < BTRFS_I(inode)->disk_i_size))
|
(start > 0 || end + 1 < BTRFS_I(inode)->disk_i_size))
|
||||||
btrfs_add_inode_defrag(trans, inode);
|
btrfs_add_inode_defrag(NULL, inode);
|
||||||
|
|
||||||
if (start == 0) {
|
if (start == 0) {
|
||||||
/* lets try to make an inline extent */
|
/* lets try to make an inline extent */
|
||||||
ret = cow_file_range_inline(trans, root, inode,
|
ret = cow_file_range_inline(root, inode, start, end, 0, 0,
|
||||||
start, end, 0, 0, NULL);
|
NULL);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
extent_clear_unlock_delalloc(inode, start, end, NULL,
|
extent_clear_unlock_delalloc(inode, start, end, NULL,
|
||||||
EXTENT_LOCKED | EXTENT_DELALLOC |
|
EXTENT_LOCKED | EXTENT_DELALLOC |
|
||||||
@ -896,7 +871,6 @@ static noinline int __cow_file_range(struct btrfs_trans_handle *trans,
|
|||||||
*page_started = 1;
|
*page_started = 1;
|
||||||
goto out;
|
goto out;
|
||||||
} else if (ret < 0) {
|
} else if (ret < 0) {
|
||||||
btrfs_abort_transaction(trans, root, ret);
|
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -911,13 +885,11 @@ static noinline int __cow_file_range(struct btrfs_trans_handle *trans,
|
|||||||
unsigned long op;
|
unsigned long op;
|
||||||
|
|
||||||
cur_alloc_size = disk_num_bytes;
|
cur_alloc_size = disk_num_bytes;
|
||||||
ret = btrfs_reserve_extent(trans, 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);
|
||||||
if (ret < 0) {
|
if (ret < 0)
|
||||||
btrfs_abort_transaction(trans, root, ret);
|
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
|
||||||
|
|
||||||
em = alloc_extent_map();
|
em = alloc_extent_map();
|
||||||
if (!em) {
|
if (!em) {
|
||||||
@ -963,10 +935,8 @@ static noinline int __cow_file_range(struct btrfs_trans_handle *trans,
|
|||||||
BTRFS_DATA_RELOC_TREE_OBJECTID) {
|
BTRFS_DATA_RELOC_TREE_OBJECTID) {
|
||||||
ret = btrfs_reloc_clone_csums(inode, start,
|
ret = btrfs_reloc_clone_csums(inode, start,
|
||||||
cur_alloc_size);
|
cur_alloc_size);
|
||||||
if (ret) {
|
if (ret)
|
||||||
btrfs_abort_transaction(trans, root, ret);
|
|
||||||
goto out_reserve;
|
goto out_reserve;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (disk_num_bytes < cur_alloc_size)
|
if (disk_num_bytes < cur_alloc_size)
|
||||||
@ -1005,37 +975,6 @@ out_unlock:
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static noinline int cow_file_range(struct inode *inode,
|
|
||||||
struct page *locked_page,
|
|
||||||
u64 start, u64 end, int *page_started,
|
|
||||||
unsigned long *nr_written,
|
|
||||||
int unlock)
|
|
||||||
{
|
|
||||||
struct btrfs_trans_handle *trans;
|
|
||||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
trans = btrfs_join_transaction(root);
|
|
||||||
if (IS_ERR(trans)) {
|
|
||||||
extent_clear_unlock_delalloc(inode, start, end, locked_page,
|
|
||||||
EXTENT_LOCKED | EXTENT_DELALLOC |
|
|
||||||
EXTENT_DO_ACCOUNTING |
|
|
||||||
EXTENT_DEFRAG, PAGE_UNLOCK |
|
|
||||||
PAGE_CLEAR_DIRTY |
|
|
||||||
PAGE_SET_WRITEBACK |
|
|
||||||
PAGE_END_WRITEBACK);
|
|
||||||
return PTR_ERR(trans);
|
|
||||||
}
|
|
||||||
trans->block_rsv = &root->fs_info->delalloc_block_rsv;
|
|
||||||
|
|
||||||
ret = __cow_file_range(trans, inode, root, locked_page, start, end,
|
|
||||||
page_started, nr_written, unlock);
|
|
||||||
|
|
||||||
btrfs_end_transaction(trans, root);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* work queue call back to started compression on a file and pages
|
* work queue call back to started compression on a file and pages
|
||||||
*/
|
*/
|
||||||
@ -1347,9 +1286,9 @@ out_check:
|
|||||||
|
|
||||||
btrfs_release_path(path);
|
btrfs_release_path(path);
|
||||||
if (cow_start != (u64)-1) {
|
if (cow_start != (u64)-1) {
|
||||||
ret = __cow_file_range(trans, inode, root, locked_page,
|
ret = cow_file_range(inode, locked_page,
|
||||||
cow_start, found_key.offset - 1,
|
cow_start, found_key.offset - 1,
|
||||||
page_started, nr_written, 1);
|
page_started, nr_written, 1);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
btrfs_abort_transaction(trans, root, ret);
|
btrfs_abort_transaction(trans, root, ret);
|
||||||
goto error;
|
goto error;
|
||||||
@ -1423,9 +1362,8 @@ out_check:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cow_start != (u64)-1) {
|
if (cow_start != (u64)-1) {
|
||||||
ret = __cow_file_range(trans, inode, root, locked_page,
|
ret = cow_file_range(inode, locked_page, cow_start, end,
|
||||||
cow_start, end,
|
page_started, nr_written, 1);
|
||||||
page_started, nr_written, 1);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
btrfs_abort_transaction(trans, root, ret);
|
btrfs_abort_transaction(trans, root, ret);
|
||||||
goto error;
|
goto error;
|
||||||
@ -6344,39 +6282,32 @@ static struct extent_map *btrfs_new_extent_direct(struct inode *inode,
|
|||||||
u64 start, u64 len)
|
u64 start, u64 len)
|
||||||
{
|
{
|
||||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||||
struct btrfs_trans_handle *trans;
|
|
||||||
struct extent_map *em;
|
struct extent_map *em;
|
||||||
struct btrfs_key ins;
|
struct btrfs_key ins;
|
||||||
u64 alloc_hint;
|
u64 alloc_hint;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
trans = btrfs_join_transaction(root);
|
|
||||||
if (IS_ERR(trans))
|
|
||||||
return ERR_CAST(trans);
|
|
||||||
|
|
||||||
trans->block_rsv = &root->fs_info->delalloc_block_rsv;
|
|
||||||
|
|
||||||
alloc_hint = get_extent_allocation_hint(inode, start, len);
|
alloc_hint = get_extent_allocation_hint(inode, start, len);
|
||||||
ret = btrfs_reserve_extent(trans, root, len, root->sectorsize, 0,
|
ret = btrfs_reserve_extent(root, len, root->sectorsize, 0,
|
||||||
alloc_hint, &ins, 1);
|
alloc_hint, &ins, 1);
|
||||||
if (ret) {
|
if (ret)
|
||||||
em = ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) {
|
||||||
goto out;
|
btrfs_free_reserved_extent(root, ins.objectid, ins.offset);
|
||||||
|
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);
|
||||||
em = ERR_PTR(ret);
|
free_extent_map(em);
|
||||||
|
return ERR_PTR(ret);
|
||||||
}
|
}
|
||||||
out:
|
|
||||||
btrfs_end_transaction(trans, root);
|
|
||||||
return em;
|
return em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6384,11 +6315,11 @@ out:
|
|||||||
* returns 1 when the nocow is safe, < 1 on error, 0 if the
|
* returns 1 when the nocow is safe, < 1 on error, 0 if the
|
||||||
* block must be cow'd
|
* block must be cow'd
|
||||||
*/
|
*/
|
||||||
noinline int can_nocow_extent(struct btrfs_trans_handle *trans,
|
noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len,
|
||||||
struct inode *inode, u64 offset, u64 *len,
|
|
||||||
u64 *orig_start, u64 *orig_block_len,
|
u64 *orig_start, u64 *orig_block_len,
|
||||||
u64 *ram_bytes)
|
u64 *ram_bytes)
|
||||||
{
|
{
|
||||||
|
struct btrfs_trans_handle *trans;
|
||||||
struct btrfs_path *path;
|
struct btrfs_path *path;
|
||||||
int ret;
|
int ret;
|
||||||
struct extent_buffer *leaf;
|
struct extent_buffer *leaf;
|
||||||
@ -6406,7 +6337,7 @@ noinline int can_nocow_extent(struct btrfs_trans_handle *trans,
|
|||||||
if (!path)
|
if (!path)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = btrfs_lookup_file_extent(trans, root, path, btrfs_ino(inode),
|
ret = btrfs_lookup_file_extent(NULL, root, path, btrfs_ino(inode),
|
||||||
offset, 0);
|
offset, 0);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
@ -6471,9 +6402,19 @@ noinline int can_nocow_extent(struct btrfs_trans_handle *trans,
|
|||||||
* look for other files referencing this extent, if we
|
* look for other files referencing this extent, if we
|
||||||
* find any we must cow
|
* find any we must cow
|
||||||
*/
|
*/
|
||||||
if (btrfs_cross_ref_exist(trans, root, btrfs_ino(inode),
|
trans = btrfs_join_transaction(root);
|
||||||
key.offset - backref_offset, disk_bytenr))
|
if (IS_ERR(trans)) {
|
||||||
|
ret = 0;
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = btrfs_cross_ref_exist(trans, root, btrfs_ino(inode),
|
||||||
|
key.offset - backref_offset, disk_bytenr);
|
||||||
|
btrfs_end_transaction(trans, root);
|
||||||
|
if (ret) {
|
||||||
|
ret = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* adjust disk_bytenr and num_bytes to cover just the bytes
|
* adjust disk_bytenr and num_bytes to cover just the bytes
|
||||||
@ -6615,7 +6556,6 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
|
|||||||
u64 start = iblock << inode->i_blkbits;
|
u64 start = iblock << inode->i_blkbits;
|
||||||
u64 lockstart, lockend;
|
u64 lockstart, lockend;
|
||||||
u64 len = bh_result->b_size;
|
u64 len = bh_result->b_size;
|
||||||
struct btrfs_trans_handle *trans;
|
|
||||||
int unlock_bits = EXTENT_LOCKED;
|
int unlock_bits = EXTENT_LOCKED;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
@ -6697,16 +6637,7 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
|
|||||||
len = min(len, em->len - (start - em->start));
|
len = min(len, em->len - (start - em->start));
|
||||||
block_start = em->block_start + (start - em->start);
|
block_start = em->block_start + (start - em->start);
|
||||||
|
|
||||||
/*
|
if (can_nocow_extent(inode, start, &len, &orig_start,
|
||||||
* we're not going to log anything, but we do need
|
|
||||||
* to make sure the current transaction stays open
|
|
||||||
* while we look for nocow cross refs
|
|
||||||
*/
|
|
||||||
trans = btrfs_join_transaction(root);
|
|
||||||
if (IS_ERR(trans))
|
|
||||||
goto must_cow;
|
|
||||||
|
|
||||||
if (can_nocow_extent(trans, inode, start, &len, &orig_start,
|
|
||||||
&orig_block_len, &ram_bytes) == 1) {
|
&orig_block_len, &ram_bytes) == 1) {
|
||||||
if (type == BTRFS_ORDERED_PREALLOC) {
|
if (type == BTRFS_ORDERED_PREALLOC) {
|
||||||
free_extent_map(em);
|
free_extent_map(em);
|
||||||
@ -6715,24 +6646,20 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
|
|||||||
block_start, len,
|
block_start, len,
|
||||||
orig_block_len,
|
orig_block_len,
|
||||||
ram_bytes, type);
|
ram_bytes, type);
|
||||||
if (IS_ERR(em)) {
|
if (IS_ERR(em))
|
||||||
btrfs_end_transaction(trans, root);
|
|
||||||
goto unlock_err;
|
goto unlock_err;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = btrfs_add_ordered_extent_dio(inode, start,
|
ret = btrfs_add_ordered_extent_dio(inode, start,
|
||||||
block_start, len, len, type);
|
block_start, len, len, type);
|
||||||
btrfs_end_transaction(trans, root);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
free_extent_map(em);
|
free_extent_map(em);
|
||||||
goto unlock_err;
|
goto unlock_err;
|
||||||
}
|
}
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
btrfs_end_transaction(trans, root);
|
|
||||||
}
|
}
|
||||||
must_cow:
|
|
||||||
/*
|
/*
|
||||||
* this will cow the extent, reset the len in case we changed
|
* this will cow the extent, reset the len in case we changed
|
||||||
* it above
|
* it above
|
||||||
@ -8495,8 +8422,8 @@ 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(trans, root, cur_bytes,
|
ret = btrfs_reserve_extent(root, cur_bytes, min_size, 0,
|
||||||
min_size, 0, *alloc_hint, &ins, 1);
|
*alloc_hint, &ins, 1);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (own_trans)
|
if (own_trans)
|
||||||
btrfs_end_transaction(trans, root);
|
btrfs_end_transaction(trans, root);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user