for-4.15-rc2-tag
-----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE8rQSAMVO+zA4DBdWxWXV+ddtWDsFAlofBpkACgkQxWXV+ddt WDvtTQ//emI1QsD4N0e4BxMcZ1bcigiEk3jc4gj+biRapnMHHAHOqJbVtpK1v8gS PCTw+4uD5UOGvhBtS4kXJn8e2qxWCESWJDXwVlW0RHmWLfwd9z7ly0sBMi3oiIqH qief8CIkk3oTexiuuJ3mZGxqnDQjRGtWx2LM+bRJBWMk+jN32v2ObSlv9V505a5M 1daDBsjWojFWa8d4r3YZNJq1df2om/dwVQZ0Wk59bacIo9Xbvok0X459cOlWuv0p mjx8m8uA/z+HVdkTYlzyKpq08O8Z4shj3GrBbSnZ511gKzV+c9jJPxij5pKm3Z2z KW4Mp17+/7GSNcSsJiqnOYi+wtOrak2lD0COlZTijnY2jrv18h8ianoIM6CpzUdy +b09yuFXbPLoUfyl6vFaO/JHuvAkQdaR2tJbds6lvW+liC1ReoL4W1WcUjY6nv9f 6wTaIv0vwgrHaxeIzxKNpnsTlpHAgorFFk0/w8nLb40WX8AoJ/95lo2zws8oaFDN 0Fylu3NYhoDrJZK+D8dbsWx2eTsFVCqep4w0+iEVZl3lfuy3FZl1pu8CL7ru9vJl DNieh+lUvK1Fk+SYIoilGoriW96RbU8+jPo2W4A1ENzeMJfrNCSWtUSZZp4XT4tO 8m1PGud07XBLSxd62bAEDV3KZO2DnY1WxgXbKuIHSi9D5CI1LMo= =7UW+ -----END PGP SIGNATURE----- Merge tag 'for-4.15-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux Pull btrfs fixes from David Sterba: "We've collected some fixes in since the pre-merge window freeze. There's technically only one regression fix for 4.15, but the rest seems important and candidates for stable. - fix missing flush bio puts in error cases (is serious, but rarely happens) - fix reporting stat::st_blocks for buffered append writes - fix space cache invalidation - fix out of bound memory access when setting zlib level - fix potential memory corruption when fsync fails in the middle - fix crash in integrity checker - incremetnal send fix, path mixup for certain unlink/rename combination - pass flags to writeback so compressed writes can be throttled properly - error handling fixes" * tag 'for-4.15-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux: Btrfs: incremental send, fix wrong unlink path after renaming file btrfs: tree-checker: Fix false panic for sanity test Btrfs: fix list_add corruption and soft lockups in fsync btrfs: Fix wild memory access in compression level parser btrfs: fix deadlock when writing out space cache btrfs: clear space cache inode generation always Btrfs: fix reported number of inode blocks after buffered append writes Btrfs: move definition of the function btrfs_find_new_delalloc_bytes Btrfs: bail out gracefully rather than BUG_ON btrfs: dev_alloc_list is not protected by RCU, use normal list_del btrfs: add missing device::flush_bio puts btrfs: Fix transaction abort during failure in btrfs_rm_dev_item Btrfs: add write_flags for compression bio
This commit is contained in:
Коммит
26cd94744e
|
@ -295,7 +295,8 @@ blk_status_t btrfs_submit_compressed_write(struct inode *inode, u64 start,
|
|||
unsigned long len, u64 disk_start,
|
||||
unsigned long compressed_len,
|
||||
struct page **compressed_pages,
|
||||
unsigned long nr_pages)
|
||||
unsigned long nr_pages,
|
||||
unsigned int write_flags)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
|
||||
struct bio *bio = NULL;
|
||||
|
@ -327,7 +328,7 @@ blk_status_t btrfs_submit_compressed_write(struct inode *inode, u64 start,
|
|||
bdev = fs_info->fs_devices->latest_bdev;
|
||||
|
||||
bio = btrfs_bio_alloc(bdev, first_byte);
|
||||
bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
|
||||
bio->bi_opf = REQ_OP_WRITE | write_flags;
|
||||
bio->bi_private = cb;
|
||||
bio->bi_end_io = end_compressed_bio_write;
|
||||
refcount_set(&cb->pending_bios, 1);
|
||||
|
@ -374,7 +375,7 @@ blk_status_t btrfs_submit_compressed_write(struct inode *inode, u64 start,
|
|||
bio_put(bio);
|
||||
|
||||
bio = btrfs_bio_alloc(bdev, first_byte);
|
||||
bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
|
||||
bio->bi_opf = REQ_OP_WRITE | write_flags;
|
||||
bio->bi_private = cb;
|
||||
bio->bi_end_io = end_compressed_bio_write;
|
||||
bio_add_page(bio, page, PAGE_SIZE, 0);
|
||||
|
@ -1528,5 +1529,5 @@ unsigned int btrfs_compress_str2level(const char *str)
|
|||
if (str[4] == ':' && '1' <= str[5] && str[5] <= '9' && str[6] == 0)
|
||||
return str[5] - '0';
|
||||
|
||||
return 0;
|
||||
return BTRFS_ZLIB_DEFAULT_LEVEL;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
/* Maximum size of data before compression */
|
||||
#define BTRFS_MAX_UNCOMPRESSED (SZ_128K)
|
||||
|
||||
#define BTRFS_ZLIB_DEFAULT_LEVEL 3
|
||||
|
||||
struct compressed_bio {
|
||||
/* number of bios pending for this compressed extent */
|
||||
refcount_t pending_bios;
|
||||
|
@ -91,7 +93,8 @@ blk_status_t btrfs_submit_compressed_write(struct inode *inode, u64 start,
|
|||
unsigned long len, u64 disk_start,
|
||||
unsigned long compressed_len,
|
||||
struct page **compressed_pages,
|
||||
unsigned long nr_pages);
|
||||
unsigned long nr_pages,
|
||||
unsigned int write_flags);
|
||||
blk_status_t btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
|
||||
int mirror_num, unsigned long bio_flags);
|
||||
|
||||
|
|
|
@ -3180,6 +3180,7 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput);
|
|||
int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput,
|
||||
int nr);
|
||||
int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end,
|
||||
unsigned int extra_bits,
|
||||
struct extent_state **cached_state, int dedupe);
|
||||
int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *new_root,
|
||||
|
|
|
@ -610,7 +610,7 @@ static int btree_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
|
|||
* that we don't try and read the other copies of this block, just
|
||||
* return -EIO.
|
||||
*/
|
||||
if (found_level == 0 && btrfs_check_leaf(root, eb)) {
|
||||
if (found_level == 0 && btrfs_check_leaf_full(root, eb)) {
|
||||
set_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags);
|
||||
ret = -EIO;
|
||||
}
|
||||
|
@ -3848,7 +3848,13 @@ void btrfs_mark_buffer_dirty(struct extent_buffer *buf)
|
|||
buf->len,
|
||||
fs_info->dirty_metadata_batch);
|
||||
#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
|
||||
if (btrfs_header_level(buf) == 0 && btrfs_check_leaf(root, buf)) {
|
||||
/*
|
||||
* Since btrfs_mark_buffer_dirty() can be called with item pointer set
|
||||
* but item data not updated.
|
||||
* So here we should only check item pointers, not item data.
|
||||
*/
|
||||
if (btrfs_header_level(buf) == 0 &&
|
||||
btrfs_check_leaf_relaxed(root, buf)) {
|
||||
btrfs_print_leaf(buf);
|
||||
ASSERT(0);
|
||||
}
|
||||
|
|
|
@ -3502,13 +3502,6 @@ again:
|
|||
goto again;
|
||||
}
|
||||
|
||||
/* We've already setup this transaction, go ahead and exit */
|
||||
if (block_group->cache_generation == trans->transid &&
|
||||
i_size_read(inode)) {
|
||||
dcs = BTRFS_DC_SETUP;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
/*
|
||||
* We want to set the generation to 0, that way if anything goes wrong
|
||||
* from here on out we know not to trust this cache when we load up next
|
||||
|
@ -3532,6 +3525,13 @@ again:
|
|||
}
|
||||
WARN_ON(ret);
|
||||
|
||||
/* We've already setup this transaction, go ahead and exit */
|
||||
if (block_group->cache_generation == trans->transid &&
|
||||
i_size_read(inode)) {
|
||||
dcs = BTRFS_DC_SETUP;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
if (i_size_read(inode) > 0) {
|
||||
ret = btrfs_check_trunc_cache_free_space(fs_info,
|
||||
&fs_info->global_block_rsv);
|
||||
|
|
|
@ -3253,7 +3253,7 @@ static noinline_for_stack int writepage_delalloc(struct inode *inode,
|
|||
delalloc_start,
|
||||
delalloc_end,
|
||||
&page_started,
|
||||
nr_written);
|
||||
nr_written, wbc);
|
||||
/* File system has been set read-only */
|
||||
if (ret) {
|
||||
SetPageError(page);
|
||||
|
|
|
@ -116,7 +116,8 @@ struct extent_io_ops {
|
|||
*/
|
||||
int (*fill_delalloc)(void *private_data, struct page *locked_page,
|
||||
u64 start, u64 end, int *page_started,
|
||||
unsigned long *nr_written);
|
||||
unsigned long *nr_written,
|
||||
struct writeback_control *wbc);
|
||||
|
||||
int (*writepage_start_hook)(struct page *page, u64 start, u64 end);
|
||||
void (*writepage_end_io_hook)(struct page *page, u64 start, u64 end,
|
||||
|
@ -365,10 +366,11 @@ int convert_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
|
|||
struct extent_state **cached_state);
|
||||
|
||||
static inline int set_extent_delalloc(struct extent_io_tree *tree, u64 start,
|
||||
u64 end, struct extent_state **cached_state)
|
||||
u64 end, unsigned int extra_bits,
|
||||
struct extent_state **cached_state)
|
||||
{
|
||||
return set_extent_bit(tree, start, end,
|
||||
EXTENT_DELALLOC | EXTENT_UPTODATE,
|
||||
EXTENT_DELALLOC | EXTENT_UPTODATE | extra_bits,
|
||||
NULL, cached_state, GFP_NOFS);
|
||||
}
|
||||
|
||||
|
|
130
fs/btrfs/file.c
130
fs/btrfs/file.c
|
@ -477,6 +477,47 @@ static void btrfs_drop_pages(struct page **pages, size_t num_pages)
|
|||
}
|
||||
}
|
||||
|
||||
static int btrfs_find_new_delalloc_bytes(struct btrfs_inode *inode,
|
||||
const u64 start,
|
||||
const u64 len,
|
||||
struct extent_state **cached_state)
|
||||
{
|
||||
u64 search_start = start;
|
||||
const u64 end = start + len - 1;
|
||||
|
||||
while (search_start < end) {
|
||||
const u64 search_len = end - search_start + 1;
|
||||
struct extent_map *em;
|
||||
u64 em_len;
|
||||
int ret = 0;
|
||||
|
||||
em = btrfs_get_extent(inode, NULL, 0, search_start,
|
||||
search_len, 0);
|
||||
if (IS_ERR(em))
|
||||
return PTR_ERR(em);
|
||||
|
||||
if (em->block_start != EXTENT_MAP_HOLE)
|
||||
goto next;
|
||||
|
||||
em_len = em->len;
|
||||
if (em->start < search_start)
|
||||
em_len -= search_start - em->start;
|
||||
if (em_len > search_len)
|
||||
em_len = search_len;
|
||||
|
||||
ret = set_extent_bit(&inode->io_tree, search_start,
|
||||
search_start + em_len - 1,
|
||||
EXTENT_DELALLOC_NEW,
|
||||
NULL, cached_state, GFP_NOFS);
|
||||
next:
|
||||
search_start = extent_map_end(em);
|
||||
free_extent_map(em);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* after copy_from_user, pages need to be dirtied and we need to make
|
||||
* sure holes are created between the current EOF and the start of
|
||||
|
@ -497,14 +538,34 @@ int btrfs_dirty_pages(struct inode *inode, struct page **pages,
|
|||
u64 end_of_last_block;
|
||||
u64 end_pos = pos + write_bytes;
|
||||
loff_t isize = i_size_read(inode);
|
||||
unsigned int extra_bits = 0;
|
||||
|
||||
start_pos = pos & ~((u64) fs_info->sectorsize - 1);
|
||||
num_bytes = round_up(write_bytes + pos - start_pos,
|
||||
fs_info->sectorsize);
|
||||
|
||||
end_of_last_block = start_pos + num_bytes - 1;
|
||||
|
||||
if (!btrfs_is_free_space_inode(BTRFS_I(inode))) {
|
||||
if (start_pos >= isize &&
|
||||
!(BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC)) {
|
||||
/*
|
||||
* There can't be any extents following eof in this case
|
||||
* so just set the delalloc new bit for the range
|
||||
* directly.
|
||||
*/
|
||||
extra_bits |= EXTENT_DELALLOC_NEW;
|
||||
} else {
|
||||
err = btrfs_find_new_delalloc_bytes(BTRFS_I(inode),
|
||||
start_pos,
|
||||
num_bytes, cached);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
err = btrfs_set_extent_delalloc(inode, start_pos, end_of_last_block,
|
||||
cached, 0);
|
||||
extra_bits, cached, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -1404,47 +1465,6 @@ fail:
|
|||
|
||||
}
|
||||
|
||||
static int btrfs_find_new_delalloc_bytes(struct btrfs_inode *inode,
|
||||
const u64 start,
|
||||
const u64 len,
|
||||
struct extent_state **cached_state)
|
||||
{
|
||||
u64 search_start = start;
|
||||
const u64 end = start + len - 1;
|
||||
|
||||
while (search_start < end) {
|
||||
const u64 search_len = end - search_start + 1;
|
||||
struct extent_map *em;
|
||||
u64 em_len;
|
||||
int ret = 0;
|
||||
|
||||
em = btrfs_get_extent(inode, NULL, 0, search_start,
|
||||
search_len, 0);
|
||||
if (IS_ERR(em))
|
||||
return PTR_ERR(em);
|
||||
|
||||
if (em->block_start != EXTENT_MAP_HOLE)
|
||||
goto next;
|
||||
|
||||
em_len = em->len;
|
||||
if (em->start < search_start)
|
||||
em_len -= search_start - em->start;
|
||||
if (em_len > search_len)
|
||||
em_len = search_len;
|
||||
|
||||
ret = set_extent_bit(&inode->io_tree, search_start,
|
||||
search_start + em_len - 1,
|
||||
EXTENT_DELALLOC_NEW,
|
||||
NULL, cached_state, GFP_NOFS);
|
||||
next:
|
||||
search_start = extent_map_end(em);
|
||||
free_extent_map(em);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function locks the extent and properly waits for data=ordered extents
|
||||
* to finish before allowing the pages to be modified if need.
|
||||
|
@ -1473,10 +1493,8 @@ lock_and_cleanup_extent_if_need(struct btrfs_inode *inode, struct page **pages,
|
|||
+ round_up(pos + write_bytes - start_pos,
|
||||
fs_info->sectorsize) - 1;
|
||||
|
||||
if (start_pos < inode->vfs_inode.i_size ||
|
||||
(inode->flags & BTRFS_INODE_PREALLOC)) {
|
||||
if (start_pos < inode->vfs_inode.i_size) {
|
||||
struct btrfs_ordered_extent *ordered;
|
||||
unsigned int clear_bits;
|
||||
|
||||
lock_extent_bits(&inode->io_tree, start_pos, last_pos,
|
||||
cached_state);
|
||||
|
@ -1498,19 +1516,10 @@ lock_and_cleanup_extent_if_need(struct btrfs_inode *inode, struct page **pages,
|
|||
}
|
||||
if (ordered)
|
||||
btrfs_put_ordered_extent(ordered);
|
||||
ret = btrfs_find_new_delalloc_bytes(inode, start_pos,
|
||||
last_pos - start_pos + 1,
|
||||
cached_state);
|
||||
clear_bits = EXTENT_DIRTY | EXTENT_DELALLOC |
|
||||
EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG;
|
||||
if (ret)
|
||||
clear_bits |= EXTENT_DELALLOC_NEW | EXTENT_LOCKED;
|
||||
clear_extent_bit(&inode->io_tree, start_pos,
|
||||
last_pos, clear_bits,
|
||||
(clear_bits & EXTENT_LOCKED) ? 1 : 0,
|
||||
0, cached_state, GFP_NOFS);
|
||||
if (ret)
|
||||
return ret;
|
||||
clear_extent_bit(&inode->io_tree, start_pos, last_pos,
|
||||
EXTENT_DIRTY | EXTENT_DELALLOC |
|
||||
EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG,
|
||||
0, 0, cached_state, GFP_NOFS);
|
||||
*lockstart = start_pos;
|
||||
*lockend = last_pos;
|
||||
ret = 1;
|
||||
|
@ -2048,6 +2057,8 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
|
|||
len = (u64)end - (u64)start + 1;
|
||||
trace_btrfs_sync_file(file, datasync);
|
||||
|
||||
btrfs_init_log_ctx(&ctx, inode);
|
||||
|
||||
/*
|
||||
* We write the dirty pages in the range and wait until they complete
|
||||
* out of the ->i_mutex. If so, we can flush the dirty pages by
|
||||
|
@ -2194,8 +2205,6 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
|
|||
}
|
||||
trans->sync = true;
|
||||
|
||||
btrfs_init_log_ctx(&ctx, inode);
|
||||
|
||||
ret = btrfs_log_dentry_safe(trans, root, dentry, start, end, &ctx);
|
||||
if (ret < 0) {
|
||||
/* Fallthrough and commit/free transaction. */
|
||||
|
@ -2253,6 +2262,7 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
|
|||
ret = btrfs_end_transaction(trans);
|
||||
}
|
||||
out:
|
||||
ASSERT(list_empty(&ctx.list));
|
||||
err = file_check_and_advance_wb_err(file);
|
||||
if (!ret)
|
||||
ret = err;
|
||||
|
|
|
@ -1264,7 +1264,7 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
|
|||
/* Lock all pages first so we can lock the extent safely. */
|
||||
ret = io_ctl_prepare_pages(io_ctl, inode, 0);
|
||||
if (ret)
|
||||
goto out;
|
||||
goto out_unlock;
|
||||
|
||||
lock_extent_bits(&BTRFS_I(inode)->io_tree, 0, i_size_read(inode) - 1,
|
||||
&cached_state);
|
||||
|
@ -1358,6 +1358,7 @@ out_nospc_locked:
|
|||
out_nospc:
|
||||
cleanup_write_cache_enospc(inode, io_ctl, &cached_state);
|
||||
|
||||
out_unlock:
|
||||
if (block_group && (block_group->flags & BTRFS_BLOCK_GROUP_DATA))
|
||||
up_write(&block_group->data_rwsem);
|
||||
|
||||
|
|
|
@ -378,6 +378,7 @@ struct async_cow {
|
|||
struct page *locked_page;
|
||||
u64 start;
|
||||
u64 end;
|
||||
unsigned int write_flags;
|
||||
struct list_head extents;
|
||||
struct btrfs_work work;
|
||||
};
|
||||
|
@ -857,7 +858,8 @@ retry:
|
|||
async_extent->ram_size,
|
||||
ins.objectid,
|
||||
ins.offset, async_extent->pages,
|
||||
async_extent->nr_pages)) {
|
||||
async_extent->nr_pages,
|
||||
async_cow->write_flags)) {
|
||||
struct extent_io_tree *tree = &BTRFS_I(inode)->io_tree;
|
||||
struct page *p = async_extent->pages[0];
|
||||
const u64 start = async_extent->start;
|
||||
|
@ -1191,7 +1193,8 @@ static noinline void async_cow_free(struct btrfs_work *work)
|
|||
|
||||
static int cow_file_range_async(struct inode *inode, struct page *locked_page,
|
||||
u64 start, u64 end, int *page_started,
|
||||
unsigned long *nr_written)
|
||||
unsigned long *nr_written,
|
||||
unsigned int write_flags)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
|
||||
struct async_cow *async_cow;
|
||||
|
@ -1208,6 +1211,7 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page,
|
|||
async_cow->root = root;
|
||||
async_cow->locked_page = locked_page;
|
||||
async_cow->start = start;
|
||||
async_cow->write_flags = write_flags;
|
||||
|
||||
if (BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS &&
|
||||
!btrfs_test_opt(fs_info, FORCE_COMPRESS))
|
||||
|
@ -1577,11 +1581,13 @@ static inline int need_force_cow(struct inode *inode, u64 start, u64 end)
|
|||
*/
|
||||
static int run_delalloc_range(void *private_data, struct page *locked_page,
|
||||
u64 start, u64 end, int *page_started,
|
||||
unsigned long *nr_written)
|
||||
unsigned long *nr_written,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
struct inode *inode = private_data;
|
||||
int ret;
|
||||
int force_cow = need_force_cow(inode, start, end);
|
||||
unsigned int write_flags = wbc_to_write_flags(wbc);
|
||||
|
||||
if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW && !force_cow) {
|
||||
ret = run_delalloc_nocow(inode, locked_page, start, end,
|
||||
|
@ -1596,7 +1602,8 @@ static int run_delalloc_range(void *private_data, struct page *locked_page,
|
|||
set_bit(BTRFS_INODE_HAS_ASYNC_EXTENT,
|
||||
&BTRFS_I(inode)->runtime_flags);
|
||||
ret = cow_file_range_async(inode, locked_page, start, end,
|
||||
page_started, nr_written);
|
||||
page_started, nr_written,
|
||||
write_flags);
|
||||
}
|
||||
if (ret)
|
||||
btrfs_cleanup_ordered_extents(inode, start, end - start + 1);
|
||||
|
@ -2025,11 +2032,12 @@ static noinline int add_pending_csums(struct btrfs_trans_handle *trans,
|
|||
}
|
||||
|
||||
int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end,
|
||||
unsigned int extra_bits,
|
||||
struct extent_state **cached_state, int dedupe)
|
||||
{
|
||||
WARN_ON((end & (PAGE_SIZE - 1)) == 0);
|
||||
return set_extent_delalloc(&BTRFS_I(inode)->io_tree, start, end,
|
||||
cached_state);
|
||||
extra_bits, cached_state);
|
||||
}
|
||||
|
||||
/* see btrfs_writepage_start_hook for details on why this is required */
|
||||
|
@ -2090,7 +2098,7 @@ again:
|
|||
goto out;
|
||||
}
|
||||
|
||||
btrfs_set_extent_delalloc(inode, page_start, page_end, &cached_state,
|
||||
btrfs_set_extent_delalloc(inode, page_start, page_end, 0, &cached_state,
|
||||
0);
|
||||
ClearPageChecked(page);
|
||||
set_page_dirty(page);
|
||||
|
@ -4790,7 +4798,7 @@ again:
|
|||
EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG,
|
||||
0, 0, &cached_state, GFP_NOFS);
|
||||
|
||||
ret = btrfs_set_extent_delalloc(inode, block_start, block_end,
|
||||
ret = btrfs_set_extent_delalloc(inode, block_start, block_end, 0,
|
||||
&cached_state, 0);
|
||||
if (ret) {
|
||||
unlock_extent_cached(io_tree, block_start, block_end,
|
||||
|
@ -5438,6 +5446,14 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
|
|||
goto out_err;
|
||||
|
||||
btrfs_dir_item_key_to_cpu(path->nodes[0], di, location);
|
||||
if (location->type != BTRFS_INODE_ITEM_KEY &&
|
||||
location->type != BTRFS_ROOT_ITEM_KEY) {
|
||||
btrfs_warn(root->fs_info,
|
||||
"%s gets something invalid in DIR_ITEM (name %s, directory ino %llu, location(%llu %u %llu))",
|
||||
__func__, name, btrfs_ino(BTRFS_I(dir)),
|
||||
location->objectid, location->type, location->offset);
|
||||
goto out_err;
|
||||
}
|
||||
out:
|
||||
btrfs_free_path(path);
|
||||
return ret;
|
||||
|
@ -5754,8 +5770,6 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
|
|||
return inode;
|
||||
}
|
||||
|
||||
BUG_ON(location.type != BTRFS_ROOT_ITEM_KEY);
|
||||
|
||||
index = srcu_read_lock(&fs_info->subvol_srcu);
|
||||
ret = fixup_tree_root_location(fs_info, dir, dentry,
|
||||
&location, &sub_root);
|
||||
|
@ -9150,7 +9164,7 @@ again:
|
|||
EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG,
|
||||
0, 0, &cached_state, GFP_NOFS);
|
||||
|
||||
ret = btrfs_set_extent_delalloc(inode, page_start, end,
|
||||
ret = btrfs_set_extent_delalloc(inode, page_start, end, 0,
|
||||
&cached_state, 0);
|
||||
if (ret) {
|
||||
unlock_extent_cached(io_tree, page_start, page_end,
|
||||
|
|
|
@ -3268,7 +3268,8 @@ static int relocate_file_extent_cluster(struct inode *inode,
|
|||
nr++;
|
||||
}
|
||||
|
||||
btrfs_set_extent_delalloc(inode, page_start, page_end, NULL, 0);
|
||||
btrfs_set_extent_delalloc(inode, page_start, page_end, 0, NULL,
|
||||
0);
|
||||
set_page_dirty(page);
|
||||
|
||||
unlock_extent(&BTRFS_I(inode)->io_tree,
|
||||
|
|
130
fs/btrfs/send.c
130
fs/btrfs/send.c
|
@ -3521,7 +3521,40 @@ out:
|
|||
}
|
||||
|
||||
/*
|
||||
* Check if ino ino1 is an ancestor of inode ino2 in the given root.
|
||||
* Check if inode ino2, or any of its ancestors, is inode ino1.
|
||||
* Return 1 if true, 0 if false and < 0 on error.
|
||||
*/
|
||||
static int check_ino_in_path(struct btrfs_root *root,
|
||||
const u64 ino1,
|
||||
const u64 ino1_gen,
|
||||
const u64 ino2,
|
||||
const u64 ino2_gen,
|
||||
struct fs_path *fs_path)
|
||||
{
|
||||
u64 ino = ino2;
|
||||
|
||||
if (ino1 == ino2)
|
||||
return ino1_gen == ino2_gen;
|
||||
|
||||
while (ino > BTRFS_FIRST_FREE_OBJECTID) {
|
||||
u64 parent;
|
||||
u64 parent_gen;
|
||||
int ret;
|
||||
|
||||
fs_path_reset(fs_path);
|
||||
ret = get_first_ref(root, ino, &parent, &parent_gen, fs_path);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (parent == ino1)
|
||||
return parent_gen == ino1_gen;
|
||||
ino = parent;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if ino ino1 is an ancestor of inode ino2 in the given root for any
|
||||
* possible path (in case ino2 is not a directory and has multiple hard links).
|
||||
* Return 1 if true, 0 if false and < 0 on error.
|
||||
*/
|
||||
static int is_ancestor(struct btrfs_root *root,
|
||||
|
@ -3530,36 +3563,91 @@ static int is_ancestor(struct btrfs_root *root,
|
|||
const u64 ino2,
|
||||
struct fs_path *fs_path)
|
||||
{
|
||||
u64 ino = ino2;
|
||||
bool free_path = false;
|
||||
bool free_fs_path = false;
|
||||
int ret = 0;
|
||||
struct btrfs_path *path = NULL;
|
||||
struct btrfs_key key;
|
||||
|
||||
if (!fs_path) {
|
||||
fs_path = fs_path_alloc();
|
||||
if (!fs_path)
|
||||
return -ENOMEM;
|
||||
free_path = true;
|
||||
free_fs_path = true;
|
||||
}
|
||||
|
||||
while (ino > BTRFS_FIRST_FREE_OBJECTID) {
|
||||
u64 parent;
|
||||
u64 parent_gen;
|
||||
|
||||
fs_path_reset(fs_path);
|
||||
ret = get_first_ref(root, ino, &parent, &parent_gen, fs_path);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOENT && ino == ino2)
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
if (parent == ino1) {
|
||||
ret = parent_gen == ino1_gen ? 1 : 0;
|
||||
goto out;
|
||||
}
|
||||
ino = parent;
|
||||
path = alloc_path_for_send();
|
||||
if (!path) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
key.objectid = ino2;
|
||||
key.type = BTRFS_INODE_REF_KEY;
|
||||
key.offset = 0;
|
||||
|
||||
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
while (true) {
|
||||
struct extent_buffer *leaf = path->nodes[0];
|
||||
int slot = path->slots[0];
|
||||
u32 cur_offset = 0;
|
||||
u32 item_size;
|
||||
|
||||
if (slot >= btrfs_header_nritems(leaf)) {
|
||||
ret = btrfs_next_leaf(root, path);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (ret > 0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
btrfs_item_key_to_cpu(leaf, &key, slot);
|
||||
if (key.objectid != ino2)
|
||||
break;
|
||||
if (key.type != BTRFS_INODE_REF_KEY &&
|
||||
key.type != BTRFS_INODE_EXTREF_KEY)
|
||||
break;
|
||||
|
||||
item_size = btrfs_item_size_nr(leaf, slot);
|
||||
while (cur_offset < item_size) {
|
||||
u64 parent;
|
||||
u64 parent_gen;
|
||||
|
||||
if (key.type == BTRFS_INODE_EXTREF_KEY) {
|
||||
unsigned long ptr;
|
||||
struct btrfs_inode_extref *extref;
|
||||
|
||||
ptr = btrfs_item_ptr_offset(leaf, slot);
|
||||
extref = (struct btrfs_inode_extref *)
|
||||
(ptr + cur_offset);
|
||||
parent = btrfs_inode_extref_parent(leaf,
|
||||
extref);
|
||||
cur_offset += sizeof(*extref);
|
||||
cur_offset += btrfs_inode_extref_name_len(leaf,
|
||||
extref);
|
||||
} else {
|
||||
parent = key.offset;
|
||||
cur_offset = item_size;
|
||||
}
|
||||
|
||||
ret = get_inode_info(root, parent, NULL, &parent_gen,
|
||||
NULL, NULL, NULL, NULL);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
ret = check_ino_in_path(root, ino1, ino1_gen,
|
||||
parent, parent_gen, fs_path);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
path->slots[0]++;
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
if (free_path)
|
||||
btrfs_free_path(path);
|
||||
if (free_fs_path)
|
||||
fs_path_free(fs_path);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -507,9 +507,18 @@ int btrfs_parse_options(struct btrfs_fs_info *info, char *options,
|
|||
token == Opt_compress_force ||
|
||||
strncmp(args[0].from, "zlib", 4) == 0) {
|
||||
compress_type = "zlib";
|
||||
|
||||
info->compress_type = BTRFS_COMPRESS_ZLIB;
|
||||
info->compress_level =
|
||||
btrfs_compress_str2level(args[0].from);
|
||||
info->compress_level = BTRFS_ZLIB_DEFAULT_LEVEL;
|
||||
/*
|
||||
* args[0] contains uninitialized data since
|
||||
* for these tokens we don't expect any
|
||||
* parameter.
|
||||
*/
|
||||
if (token != Opt_compress &&
|
||||
token != Opt_compress_force)
|
||||
info->compress_level =
|
||||
btrfs_compress_str2level(args[0].from);
|
||||
btrfs_set_opt(info->mount_opt, COMPRESS);
|
||||
btrfs_clear_opt(info->mount_opt, NODATACOW);
|
||||
btrfs_clear_opt(info->mount_opt, NODATASUM);
|
||||
|
|
|
@ -114,7 +114,7 @@ static int test_find_delalloc(u32 sectorsize)
|
|||
* |--- delalloc ---|
|
||||
* |--- search ---|
|
||||
*/
|
||||
set_extent_delalloc(&tmp, 0, sectorsize - 1, NULL);
|
||||
set_extent_delalloc(&tmp, 0, sectorsize - 1, 0, NULL);
|
||||
start = 0;
|
||||
end = 0;
|
||||
found = find_lock_delalloc_range(inode, &tmp, locked_page, &start,
|
||||
|
@ -145,7 +145,7 @@ static int test_find_delalloc(u32 sectorsize)
|
|||
test_msg("Couldn't find the locked page\n");
|
||||
goto out_bits;
|
||||
}
|
||||
set_extent_delalloc(&tmp, sectorsize, max_bytes - 1, NULL);
|
||||
set_extent_delalloc(&tmp, sectorsize, max_bytes - 1, 0, NULL);
|
||||
start = test_start;
|
||||
end = 0;
|
||||
found = find_lock_delalloc_range(inode, &tmp, locked_page, &start,
|
||||
|
@ -200,7 +200,7 @@ static int test_find_delalloc(u32 sectorsize)
|
|||
*
|
||||
* We are re-using our test_start from above since it works out well.
|
||||
*/
|
||||
set_extent_delalloc(&tmp, max_bytes, total_dirty - 1, NULL);
|
||||
set_extent_delalloc(&tmp, max_bytes, total_dirty - 1, 0, NULL);
|
||||
start = test_start;
|
||||
end = 0;
|
||||
found = find_lock_delalloc_range(inode, &tmp, locked_page, &start,
|
||||
|
|
|
@ -968,7 +968,7 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize)
|
|||
btrfs_test_inode_set_ops(inode);
|
||||
|
||||
/* [BTRFS_MAX_EXTENT_SIZE] */
|
||||
ret = btrfs_set_extent_delalloc(inode, 0, BTRFS_MAX_EXTENT_SIZE - 1,
|
||||
ret = btrfs_set_extent_delalloc(inode, 0, BTRFS_MAX_EXTENT_SIZE - 1, 0,
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
|
||||
|
@ -984,7 +984,7 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize)
|
|||
/* [BTRFS_MAX_EXTENT_SIZE][sectorsize] */
|
||||
ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE,
|
||||
BTRFS_MAX_EXTENT_SIZE + sectorsize - 1,
|
||||
NULL, 0);
|
||||
0, NULL, 0);
|
||||
if (ret) {
|
||||
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
|
||||
goto out;
|
||||
|
@ -1018,7 +1018,7 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize)
|
|||
ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE >> 1,
|
||||
(BTRFS_MAX_EXTENT_SIZE >> 1)
|
||||
+ sectorsize - 1,
|
||||
NULL, 0);
|
||||
0, NULL, 0);
|
||||
if (ret) {
|
||||
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
|
||||
goto out;
|
||||
|
@ -1036,7 +1036,7 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize)
|
|||
ret = btrfs_set_extent_delalloc(inode,
|
||||
BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize,
|
||||
(BTRFS_MAX_EXTENT_SIZE << 1) + 3 * sectorsize - 1,
|
||||
NULL, 0);
|
||||
0, NULL, 0);
|
||||
if (ret) {
|
||||
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
|
||||
goto out;
|
||||
|
@ -1053,7 +1053,7 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize)
|
|||
*/
|
||||
ret = btrfs_set_extent_delalloc(inode,
|
||||
BTRFS_MAX_EXTENT_SIZE + sectorsize,
|
||||
BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, NULL, 0);
|
||||
BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, 0, NULL, 0);
|
||||
if (ret) {
|
||||
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
|
||||
goto out;
|
||||
|
@ -1089,7 +1089,7 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize)
|
|||
*/
|
||||
ret = btrfs_set_extent_delalloc(inode,
|
||||
BTRFS_MAX_EXTENT_SIZE + sectorsize,
|
||||
BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, NULL, 0);
|
||||
BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, 0, NULL, 0);
|
||||
if (ret) {
|
||||
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
|
||||
goto out;
|
||||
|
|
|
@ -242,7 +242,8 @@ static int check_leaf_item(struct btrfs_root *root,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int btrfs_check_leaf(struct btrfs_root *root, struct extent_buffer *leaf)
|
||||
static int check_leaf(struct btrfs_root *root, struct extent_buffer *leaf,
|
||||
bool check_item_data)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||
/* No valid key type is 0, so all key should be larger than this key */
|
||||
|
@ -361,10 +362,15 @@ int btrfs_check_leaf(struct btrfs_root *root, struct extent_buffer *leaf)
|
|||
return -EUCLEAN;
|
||||
}
|
||||
|
||||
/* Check if the item size and content meet other criteria */
|
||||
ret = check_leaf_item(root, leaf, &key, slot);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (check_item_data) {
|
||||
/*
|
||||
* Check if the item size and content meet other
|
||||
* criteria
|
||||
*/
|
||||
ret = check_leaf_item(root, leaf, &key, slot);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
prev_key.objectid = key.objectid;
|
||||
prev_key.type = key.type;
|
||||
|
@ -374,6 +380,17 @@ int btrfs_check_leaf(struct btrfs_root *root, struct extent_buffer *leaf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_check_leaf_full(struct btrfs_root *root, struct extent_buffer *leaf)
|
||||
{
|
||||
return check_leaf(root, leaf, true);
|
||||
}
|
||||
|
||||
int btrfs_check_leaf_relaxed(struct btrfs_root *root,
|
||||
struct extent_buffer *leaf)
|
||||
{
|
||||
return check_leaf(root, leaf, false);
|
||||
}
|
||||
|
||||
int btrfs_check_node(struct btrfs_root *root, struct extent_buffer *node)
|
||||
{
|
||||
unsigned long nr = btrfs_header_nritems(node);
|
||||
|
|
|
@ -20,7 +20,19 @@
|
|||
#include "ctree.h"
|
||||
#include "extent_io.h"
|
||||
|
||||
int btrfs_check_leaf(struct btrfs_root *root, struct extent_buffer *leaf);
|
||||
/*
|
||||
* Comprehensive leaf checker.
|
||||
* Will check not only the item pointers, but also every possible member
|
||||
* in item data.
|
||||
*/
|
||||
int btrfs_check_leaf_full(struct btrfs_root *root, struct extent_buffer *leaf);
|
||||
|
||||
/*
|
||||
* Less strict leaf checker.
|
||||
* Will only check item pointers, not reading item data.
|
||||
*/
|
||||
int btrfs_check_leaf_relaxed(struct btrfs_root *root,
|
||||
struct extent_buffer *leaf);
|
||||
int btrfs_check_node(struct btrfs_root *root, struct extent_buffer *node);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4102,7 +4102,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
|
|||
|
||||
if (ordered_io_err) {
|
||||
ctx->io_err = -EIO;
|
||||
return 0;
|
||||
return ctx->io_err;
|
||||
}
|
||||
|
||||
btrfs_init_map_token(&token);
|
||||
|
|
|
@ -189,6 +189,7 @@ static void free_fs_devices(struct btrfs_fs_devices *fs_devices)
|
|||
struct btrfs_device, dev_list);
|
||||
list_del(&device->dev_list);
|
||||
rcu_string_free(device->name);
|
||||
bio_put(device->flush_bio);
|
||||
kfree(device);
|
||||
}
|
||||
kfree(fs_devices);
|
||||
|
@ -578,6 +579,7 @@ static void btrfs_free_stale_device(struct btrfs_device *cur_dev)
|
|||
fs_devs->num_devices--;
|
||||
list_del(&dev->dev_list);
|
||||
rcu_string_free(dev->name);
|
||||
bio_put(dev->flush_bio);
|
||||
kfree(dev);
|
||||
}
|
||||
break;
|
||||
|
@ -630,6 +632,7 @@ static noinline int device_list_add(const char *path,
|
|||
|
||||
name = rcu_string_strdup(path, GFP_NOFS);
|
||||
if (!name) {
|
||||
bio_put(device->flush_bio);
|
||||
kfree(device);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
@ -742,6 +745,7 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig)
|
|||
name = rcu_string_strdup(orig_dev->name->str,
|
||||
GFP_KERNEL);
|
||||
if (!name) {
|
||||
bio_put(device->flush_bio);
|
||||
kfree(device);
|
||||
goto error;
|
||||
}
|
||||
|
@ -807,6 +811,7 @@ again:
|
|||
list_del_init(&device->dev_list);
|
||||
fs_devices->num_devices--;
|
||||
rcu_string_free(device->name);
|
||||
bio_put(device->flush_bio);
|
||||
kfree(device);
|
||||
}
|
||||
|
||||
|
@ -1750,20 +1755,24 @@ static int btrfs_rm_dev_item(struct btrfs_fs_info *fs_info,
|
|||
key.offset = device->devid;
|
||||
|
||||
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (ret > 0) {
|
||||
ret = -ENOENT;
|
||||
if (ret) {
|
||||
if (ret > 0)
|
||||
ret = -ENOENT;
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
btrfs_end_transaction(trans);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = btrfs_del_item(trans, root, path);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
btrfs_end_transaction(trans);
|
||||
}
|
||||
|
||||
out:
|
||||
btrfs_free_path(path);
|
||||
btrfs_commit_transaction(trans);
|
||||
if (!ret)
|
||||
ret = btrfs_commit_transaction(trans);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1993,7 +2002,7 @@ void btrfs_rm_dev_replace_remove_srcdev(struct btrfs_fs_info *fs_info,
|
|||
fs_devices = srcdev->fs_devices;
|
||||
|
||||
list_del_rcu(&srcdev->dev_list);
|
||||
list_del_rcu(&srcdev->dev_alloc_list);
|
||||
list_del(&srcdev->dev_alloc_list);
|
||||
fs_devices->num_devices--;
|
||||
if (srcdev->missing)
|
||||
fs_devices->missing_devices--;
|
||||
|
@ -2349,6 +2358,7 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
|
|||
|
||||
name = rcu_string_strdup(device_path, GFP_KERNEL);
|
||||
if (!name) {
|
||||
bio_put(device->flush_bio);
|
||||
kfree(device);
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
|
@ -2358,6 +2368,7 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
|
|||
trans = btrfs_start_transaction(root, 0);
|
||||
if (IS_ERR(trans)) {
|
||||
rcu_string_free(device->name);
|
||||
bio_put(device->flush_bio);
|
||||
kfree(device);
|
||||
ret = PTR_ERR(trans);
|
||||
goto error;
|
||||
|
@ -2501,6 +2512,7 @@ error_trans:
|
|||
if (trans)
|
||||
btrfs_end_transaction(trans);
|
||||
rcu_string_free(device->name);
|
||||
bio_put(device->flush_bio);
|
||||
kfree(device);
|
||||
error:
|
||||
blkdev_put(bdev, FMODE_EXCL);
|
||||
|
@ -2567,6 +2579,7 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
|
|||
|
||||
name = rcu_string_strdup(device_path, GFP_KERNEL);
|
||||
if (!name) {
|
||||
bio_put(device->flush_bio);
|
||||
kfree(device);
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
|
@ -6284,6 +6297,7 @@ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info,
|
|||
|
||||
ret = find_next_devid(fs_info, &tmp);
|
||||
if (ret) {
|
||||
bio_put(dev->flush_bio);
|
||||
kfree(dev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче