mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 20:51:44 +00:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs
Pull btrfs fixes from Chris Mason: "Most of these are fixing extent reservation accounting, or corners with tree writeback during commit. Josef's set does add a test, which isn't strictly a fix, but it'll keep us from making this same mistake again" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs: Btrfs: fix outstanding_extents accounting in DIO Btrfs: add sanity test for outstanding_extents accounting Btrfs: just free dummy extent buffers Btrfs: account merges/splits properly Btrfs: prepare block group cache before writing Btrfs: fix ASSERT(list_empty(&cur_trans->dirty_bgs_list) Btrfs: account for the correct number of extents for delalloc reservations Btrfs: fix merge delalloc logic Btrfs: fix comp_oper to get right order Btrfs: catch transaction abortion after waiting for it btrfs: fix sizeof format specifier in btrfs_check_super_valid()
This commit is contained in:
commit
521d474631
@ -3387,6 +3387,8 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
|
|||||||
|
|
||||||
int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
|
int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_root *root);
|
struct btrfs_root *root);
|
||||||
|
int btrfs_setup_space_cache(struct btrfs_trans_handle *trans,
|
||||||
|
struct btrfs_root *root);
|
||||||
int btrfs_extent_readonly(struct btrfs_root *root, u64 bytenr);
|
int btrfs_extent_readonly(struct btrfs_root *root, u64 bytenr);
|
||||||
int btrfs_free_block_groups(struct btrfs_fs_info *info);
|
int btrfs_free_block_groups(struct btrfs_fs_info *info);
|
||||||
int btrfs_read_block_groups(struct btrfs_root *root);
|
int btrfs_read_block_groups(struct btrfs_root *root);
|
||||||
@ -3909,6 +3911,9 @@ int btrfs_prealloc_file_range_trans(struct inode *inode,
|
|||||||
loff_t actual_len, u64 *alloc_hint);
|
loff_t actual_len, u64 *alloc_hint);
|
||||||
int btrfs_inode_check_errors(struct inode *inode);
|
int btrfs_inode_check_errors(struct inode *inode);
|
||||||
extern const struct dentry_operations btrfs_dentry_operations;
|
extern const struct dentry_operations btrfs_dentry_operations;
|
||||||
|
#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
|
||||||
|
void btrfs_test_inode_set_ops(struct inode *inode);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ioctl.c */
|
/* ioctl.c */
|
||||||
long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||||
|
@ -3921,7 +3921,7 @@ static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info,
|
|||||||
}
|
}
|
||||||
if (btrfs_super_sys_array_size(sb) < sizeof(struct btrfs_disk_key)
|
if (btrfs_super_sys_array_size(sb) < sizeof(struct btrfs_disk_key)
|
||||||
+ sizeof(struct btrfs_chunk)) {
|
+ sizeof(struct btrfs_chunk)) {
|
||||||
printk(KERN_ERR "BTRFS: system chunk array too small %u < %lu\n",
|
printk(KERN_ERR "BTRFS: system chunk array too small %u < %zu\n",
|
||||||
btrfs_super_sys_array_size(sb),
|
btrfs_super_sys_array_size(sb),
|
||||||
sizeof(struct btrfs_disk_key)
|
sizeof(struct btrfs_disk_key)
|
||||||
+ sizeof(struct btrfs_chunk));
|
+ sizeof(struct btrfs_chunk));
|
||||||
|
@ -3325,6 +3325,32 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int btrfs_setup_space_cache(struct btrfs_trans_handle *trans,
|
||||||
|
struct btrfs_root *root)
|
||||||
|
{
|
||||||
|
struct btrfs_block_group_cache *cache, *tmp;
|
||||||
|
struct btrfs_transaction *cur_trans = trans->transaction;
|
||||||
|
struct btrfs_path *path;
|
||||||
|
|
||||||
|
if (list_empty(&cur_trans->dirty_bgs) ||
|
||||||
|
!btrfs_test_opt(root, SPACE_CACHE))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
path = btrfs_alloc_path();
|
||||||
|
if (!path)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* Could add new block groups, use _safe just in case */
|
||||||
|
list_for_each_entry_safe(cache, tmp, &cur_trans->dirty_bgs,
|
||||||
|
dirty_list) {
|
||||||
|
if (cache->disk_cache_state == BTRFS_DC_CLEAR)
|
||||||
|
cache_save_setup(cache, trans, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
btrfs_free_path(path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
|
int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_root *root)
|
struct btrfs_root *root)
|
||||||
{
|
{
|
||||||
@ -5110,7 +5136,11 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes)
|
|||||||
num_bytes = ALIGN(num_bytes, root->sectorsize);
|
num_bytes = ALIGN(num_bytes, root->sectorsize);
|
||||||
|
|
||||||
spin_lock(&BTRFS_I(inode)->lock);
|
spin_lock(&BTRFS_I(inode)->lock);
|
||||||
BTRFS_I(inode)->outstanding_extents++;
|
nr_extents = (unsigned)div64_u64(num_bytes +
|
||||||
|
BTRFS_MAX_EXTENT_SIZE - 1,
|
||||||
|
BTRFS_MAX_EXTENT_SIZE);
|
||||||
|
BTRFS_I(inode)->outstanding_extents += nr_extents;
|
||||||
|
nr_extents = 0;
|
||||||
|
|
||||||
if (BTRFS_I(inode)->outstanding_extents >
|
if (BTRFS_I(inode)->outstanding_extents >
|
||||||
BTRFS_I(inode)->reserved_extents)
|
BTRFS_I(inode)->reserved_extents)
|
||||||
@ -5255,6 +5285,9 @@ void btrfs_delalloc_release_metadata(struct inode *inode, u64 num_bytes)
|
|||||||
if (dropped > 0)
|
if (dropped > 0)
|
||||||
to_free += btrfs_calc_trans_metadata_size(root, dropped);
|
to_free += btrfs_calc_trans_metadata_size(root, dropped);
|
||||||
|
|
||||||
|
if (btrfs_test_is_dummy_root(root))
|
||||||
|
return;
|
||||||
|
|
||||||
trace_btrfs_space_reservation(root->fs_info, "delalloc",
|
trace_btrfs_space_reservation(root->fs_info, "delalloc",
|
||||||
btrfs_ino(inode), to_free, 0);
|
btrfs_ino(inode), to_free, 0);
|
||||||
if (root->fs_info->quota_enabled) {
|
if (root->fs_info->quota_enabled) {
|
||||||
|
@ -4968,6 +4968,12 @@ static int release_extent_buffer(struct extent_buffer *eb)
|
|||||||
|
|
||||||
/* Should be safe to release our pages at this point */
|
/* Should be safe to release our pages at this point */
|
||||||
btrfs_release_extent_buffer_page(eb);
|
btrfs_release_extent_buffer_page(eb);
|
||||||
|
#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
|
||||||
|
if (unlikely(test_bit(EXTENT_BUFFER_DUMMY, &eb->bflags))) {
|
||||||
|
__free_extent_buffer(eb);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
call_rcu(&eb->rcu_head, btrfs_release_extent_buffer_rcu);
|
call_rcu(&eb->rcu_head, btrfs_release_extent_buffer_rcu);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
112
fs/btrfs/inode.c
112
fs/btrfs/inode.c
@ -108,6 +108,13 @@ static struct extent_map *create_pinned_em(struct inode *inode, u64 start,
|
|||||||
|
|
||||||
static int btrfs_dirty_inode(struct inode *inode);
|
static int btrfs_dirty_inode(struct inode *inode);
|
||||||
|
|
||||||
|
#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
|
||||||
|
void btrfs_test_inode_set_ops(struct inode *inode)
|
||||||
|
{
|
||||||
|
BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int btrfs_init_inode_security(struct btrfs_trans_handle *trans,
|
static int btrfs_init_inode_security(struct btrfs_trans_handle *trans,
|
||||||
struct inode *inode, struct inode *dir,
|
struct inode *inode, struct inode *dir,
|
||||||
const struct qstr *qstr)
|
const struct qstr *qstr)
|
||||||
@ -1542,30 +1549,17 @@ static void btrfs_split_extent_hook(struct inode *inode,
|
|||||||
u64 new_size;
|
u64 new_size;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need the largest size of the remaining extent to see if we
|
* See the explanation in btrfs_merge_extent_hook, the same
|
||||||
* need to add a new outstanding extent. Think of the following
|
* applies here, just in reverse.
|
||||||
* case
|
|
||||||
*
|
|
||||||
* [MEAX_EXTENT_SIZEx2 - 4k][4k]
|
|
||||||
*
|
|
||||||
* The new_size would just be 4k and we'd think we had enough
|
|
||||||
* outstanding extents for this if we only took one side of the
|
|
||||||
* split, same goes for the other direction. We need to see if
|
|
||||||
* the larger size still is the same amount of extents as the
|
|
||||||
* original size, because if it is we need to add a new
|
|
||||||
* outstanding extent. But if we split up and the larger size
|
|
||||||
* is less than the original then we are good to go since we've
|
|
||||||
* already accounted for the extra extent in our original
|
|
||||||
* accounting.
|
|
||||||
*/
|
*/
|
||||||
new_size = orig->end - split + 1;
|
new_size = orig->end - split + 1;
|
||||||
if ((split - orig->start) > new_size)
|
num_extents = div64_u64(new_size + BTRFS_MAX_EXTENT_SIZE - 1,
|
||||||
new_size = split - orig->start;
|
|
||||||
|
|
||||||
num_extents = div64_u64(size + BTRFS_MAX_EXTENT_SIZE - 1,
|
|
||||||
BTRFS_MAX_EXTENT_SIZE);
|
BTRFS_MAX_EXTENT_SIZE);
|
||||||
if (div64_u64(new_size + BTRFS_MAX_EXTENT_SIZE - 1,
|
new_size = split - orig->start;
|
||||||
BTRFS_MAX_EXTENT_SIZE) < num_extents)
|
num_extents += div64_u64(new_size + BTRFS_MAX_EXTENT_SIZE - 1,
|
||||||
|
BTRFS_MAX_EXTENT_SIZE);
|
||||||
|
if (div64_u64(size + BTRFS_MAX_EXTENT_SIZE - 1,
|
||||||
|
BTRFS_MAX_EXTENT_SIZE) >= num_extents)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1591,8 +1585,10 @@ static void btrfs_merge_extent_hook(struct inode *inode,
|
|||||||
if (!(other->state & EXTENT_DELALLOC))
|
if (!(other->state & EXTENT_DELALLOC))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
old_size = other->end - other->start + 1;
|
if (new->start > other->start)
|
||||||
new_size = old_size + (new->end - new->start + 1);
|
new_size = new->end - other->start + 1;
|
||||||
|
else
|
||||||
|
new_size = other->end - new->start + 1;
|
||||||
|
|
||||||
/* we're not bigger than the max, unreserve the space and go */
|
/* we're not bigger than the max, unreserve the space and go */
|
||||||
if (new_size <= BTRFS_MAX_EXTENT_SIZE) {
|
if (new_size <= BTRFS_MAX_EXTENT_SIZE) {
|
||||||
@ -1603,13 +1599,32 @@ static void btrfs_merge_extent_hook(struct inode *inode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we grew by another max_extent, just return, we want to keep that
|
* We have to add up either side to figure out how many extents were
|
||||||
* reserved amount.
|
* accounted for before we merged into one big extent. If the number of
|
||||||
|
* extents we accounted for is <= the amount we need for the new range
|
||||||
|
* then we can return, otherwise drop. Think of it like this
|
||||||
|
*
|
||||||
|
* [ 4k][MAX_SIZE]
|
||||||
|
*
|
||||||
|
* So we've grown the extent by a MAX_SIZE extent, this would mean we
|
||||||
|
* need 2 outstanding extents, on one side we have 1 and the other side
|
||||||
|
* we have 1 so they are == and we can return. But in this case
|
||||||
|
*
|
||||||
|
* [MAX_SIZE+4k][MAX_SIZE+4k]
|
||||||
|
*
|
||||||
|
* Each range on their own accounts for 2 extents, but merged together
|
||||||
|
* they are only 3 extents worth of accounting, so we need to drop in
|
||||||
|
* this case.
|
||||||
*/
|
*/
|
||||||
|
old_size = other->end - other->start + 1;
|
||||||
num_extents = div64_u64(old_size + BTRFS_MAX_EXTENT_SIZE - 1,
|
num_extents = div64_u64(old_size + BTRFS_MAX_EXTENT_SIZE - 1,
|
||||||
BTRFS_MAX_EXTENT_SIZE);
|
BTRFS_MAX_EXTENT_SIZE);
|
||||||
|
old_size = new->end - new->start + 1;
|
||||||
|
num_extents += div64_u64(old_size + BTRFS_MAX_EXTENT_SIZE - 1,
|
||||||
|
BTRFS_MAX_EXTENT_SIZE);
|
||||||
|
|
||||||
if (div64_u64(new_size + BTRFS_MAX_EXTENT_SIZE - 1,
|
if (div64_u64(new_size + BTRFS_MAX_EXTENT_SIZE - 1,
|
||||||
BTRFS_MAX_EXTENT_SIZE) > num_extents)
|
BTRFS_MAX_EXTENT_SIZE) >= num_extents)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
spin_lock(&BTRFS_I(inode)->lock);
|
spin_lock(&BTRFS_I(inode)->lock);
|
||||||
@ -1686,6 +1701,10 @@ static void btrfs_set_bit_hook(struct inode *inode,
|
|||||||
spin_unlock(&BTRFS_I(inode)->lock);
|
spin_unlock(&BTRFS_I(inode)->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* For sanity tests */
|
||||||
|
if (btrfs_test_is_dummy_root(root))
|
||||||
|
return;
|
||||||
|
|
||||||
__percpu_counter_add(&root->fs_info->delalloc_bytes, len,
|
__percpu_counter_add(&root->fs_info->delalloc_bytes, len,
|
||||||
root->fs_info->delalloc_batch);
|
root->fs_info->delalloc_batch);
|
||||||
spin_lock(&BTRFS_I(inode)->lock);
|
spin_lock(&BTRFS_I(inode)->lock);
|
||||||
@ -1741,6 +1760,10 @@ static void btrfs_clear_bit_hook(struct inode *inode,
|
|||||||
root != root->fs_info->tree_root)
|
root != root->fs_info->tree_root)
|
||||||
btrfs_delalloc_release_metadata(inode, len);
|
btrfs_delalloc_release_metadata(inode, len);
|
||||||
|
|
||||||
|
/* For sanity tests. */
|
||||||
|
if (btrfs_test_is_dummy_root(root))
|
||||||
|
return;
|
||||||
|
|
||||||
if (root->root_key.objectid != BTRFS_DATA_RELOC_TREE_OBJECTID
|
if (root->root_key.objectid != BTRFS_DATA_RELOC_TREE_OBJECTID
|
||||||
&& do_list && !(state->state & EXTENT_NORESERVE))
|
&& do_list && !(state->state & EXTENT_NORESERVE))
|
||||||
btrfs_free_reserved_data_space(inode, len);
|
btrfs_free_reserved_data_space(inode, len);
|
||||||
@ -7213,7 +7236,7 @@ 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;
|
||||||
u64 orig_len = len;
|
u64 *outstanding_extents = NULL;
|
||||||
int unlock_bits = EXTENT_LOCKED;
|
int unlock_bits = EXTENT_LOCKED;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
@ -7225,6 +7248,16 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
|
|||||||
lockstart = start;
|
lockstart = start;
|
||||||
lockend = start + len - 1;
|
lockend = start + len - 1;
|
||||||
|
|
||||||
|
if (current->journal_info) {
|
||||||
|
/*
|
||||||
|
* Need to pull our outstanding extents and set journal_info to NULL so
|
||||||
|
* that anything that needs to check if there's a transction doesn't get
|
||||||
|
* confused.
|
||||||
|
*/
|
||||||
|
outstanding_extents = current->journal_info;
|
||||||
|
current->journal_info = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this errors out it's because we couldn't invalidate pagecache for
|
* If this errors out it's because we couldn't invalidate pagecache for
|
||||||
* this range and we need to fallback to buffered.
|
* this range and we need to fallback to buffered.
|
||||||
@ -7348,11 +7381,20 @@ unlock:
|
|||||||
if (start + len > i_size_read(inode))
|
if (start + len > i_size_read(inode))
|
||||||
i_size_write(inode, start + len);
|
i_size_write(inode, start + len);
|
||||||
|
|
||||||
if (len < orig_len) {
|
/*
|
||||||
|
* If we have an outstanding_extents count still set then we're
|
||||||
|
* within our reservation, otherwise we need to adjust our inode
|
||||||
|
* counter appropriately.
|
||||||
|
*/
|
||||||
|
if (*outstanding_extents) {
|
||||||
|
(*outstanding_extents)--;
|
||||||
|
} else {
|
||||||
spin_lock(&BTRFS_I(inode)->lock);
|
spin_lock(&BTRFS_I(inode)->lock);
|
||||||
BTRFS_I(inode)->outstanding_extents++;
|
BTRFS_I(inode)->outstanding_extents++;
|
||||||
spin_unlock(&BTRFS_I(inode)->lock);
|
spin_unlock(&BTRFS_I(inode)->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
current->journal_info = outstanding_extents;
|
||||||
btrfs_free_reserved_data_space(inode, len);
|
btrfs_free_reserved_data_space(inode, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7376,6 +7418,8 @@ unlock:
|
|||||||
unlock_err:
|
unlock_err:
|
||||||
clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, lockend,
|
clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, lockend,
|
||||||
unlock_bits, 1, 0, &cached_state, GFP_NOFS);
|
unlock_bits, 1, 0, &cached_state, GFP_NOFS);
|
||||||
|
if (outstanding_extents)
|
||||||
|
current->journal_info = outstanding_extents;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8075,6 +8119,7 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
|
|||||||
{
|
{
|
||||||
struct file *file = iocb->ki_filp;
|
struct file *file = iocb->ki_filp;
|
||||||
struct inode *inode = file->f_mapping->host;
|
struct inode *inode = file->f_mapping->host;
|
||||||
|
u64 outstanding_extents = 0;
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
bool wakeup = true;
|
bool wakeup = true;
|
||||||
@ -8112,6 +8157,16 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
|
|||||||
ret = btrfs_delalloc_reserve_space(inode, count);
|
ret = btrfs_delalloc_reserve_space(inode, count);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
outstanding_extents = div64_u64(count +
|
||||||
|
BTRFS_MAX_EXTENT_SIZE - 1,
|
||||||
|
BTRFS_MAX_EXTENT_SIZE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to know how many extents we reserved so that we can
|
||||||
|
* do the accounting properly if we go over the number we
|
||||||
|
* originally calculated. Abuse current->journal_info for this.
|
||||||
|
*/
|
||||||
|
current->journal_info = &outstanding_extents;
|
||||||
} else if (test_bit(BTRFS_INODE_READDIO_NEED_LOCK,
|
} else if (test_bit(BTRFS_INODE_READDIO_NEED_LOCK,
|
||||||
&BTRFS_I(inode)->runtime_flags)) {
|
&BTRFS_I(inode)->runtime_flags)) {
|
||||||
inode_dio_done(inode);
|
inode_dio_done(inode);
|
||||||
@ -8124,6 +8179,7 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
|
|||||||
iter, offset, btrfs_get_blocks_direct, NULL,
|
iter, offset, btrfs_get_blocks_direct, NULL,
|
||||||
btrfs_submit_direct, flags);
|
btrfs_submit_direct, flags);
|
||||||
if (rw & WRITE) {
|
if (rw & WRITE) {
|
||||||
|
current->journal_info = NULL;
|
||||||
if (ret < 0 && ret != -EIOCBQUEUED)
|
if (ret < 0 && ret != -EIOCBQUEUED)
|
||||||
btrfs_delalloc_release_space(inode, count);
|
btrfs_delalloc_release_space(inode, count);
|
||||||
else if (ret >= 0 && (size_t)ret < count)
|
else if (ret >= 0 && (size_t)ret < count)
|
||||||
|
@ -1259,7 +1259,7 @@ static int comp_oper(struct btrfs_qgroup_operation *oper1,
|
|||||||
if (oper1->seq < oper2->seq)
|
if (oper1->seq < oper2->seq)
|
||||||
return -1;
|
return -1;
|
||||||
if (oper1->seq > oper2->seq)
|
if (oper1->seq > oper2->seq)
|
||||||
return -1;
|
return 1;
|
||||||
if (oper1->ref_root < oper2->ref_root)
|
if (oper1->ref_root < oper2->ref_root)
|
||||||
return -1;
|
return -1;
|
||||||
if (oper1->ref_root > oper2->ref_root)
|
if (oper1->ref_root > oper2->ref_root)
|
||||||
|
@ -911,6 +911,197 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int test_extent_accounting(void)
|
||||||
|
{
|
||||||
|
struct inode *inode = NULL;
|
||||||
|
struct btrfs_root *root = NULL;
|
||||||
|
int ret = -ENOMEM;
|
||||||
|
|
||||||
|
inode = btrfs_new_test_inode();
|
||||||
|
if (!inode) {
|
||||||
|
test_msg("Couldn't allocate inode\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
root = btrfs_alloc_dummy_root();
|
||||||
|
if (IS_ERR(root)) {
|
||||||
|
test_msg("Couldn't allocate root\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
root->fs_info = btrfs_alloc_dummy_fs_info();
|
||||||
|
if (!root->fs_info) {
|
||||||
|
test_msg("Couldn't allocate dummy fs info\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
BTRFS_I(inode)->root = root;
|
||||||
|
btrfs_test_inode_set_ops(inode);
|
||||||
|
|
||||||
|
/* [BTRFS_MAX_EXTENT_SIZE] */
|
||||||
|
BTRFS_I(inode)->outstanding_extents++;
|
||||||
|
ret = btrfs_set_extent_delalloc(inode, 0, BTRFS_MAX_EXTENT_SIZE - 1,
|
||||||
|
NULL);
|
||||||
|
if (ret) {
|
||||||
|
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (BTRFS_I(inode)->outstanding_extents != 1) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
test_msg("Miscount, wanted 1, got %u\n",
|
||||||
|
BTRFS_I(inode)->outstanding_extents);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [BTRFS_MAX_EXTENT_SIZE][4k] */
|
||||||
|
BTRFS_I(inode)->outstanding_extents++;
|
||||||
|
ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE,
|
||||||
|
BTRFS_MAX_EXTENT_SIZE + 4095, NULL);
|
||||||
|
if (ret) {
|
||||||
|
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (BTRFS_I(inode)->outstanding_extents != 2) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
test_msg("Miscount, wanted 2, got %u\n",
|
||||||
|
BTRFS_I(inode)->outstanding_extents);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [BTRFS_MAX_EXTENT_SIZE/2][4K HOLE][the rest] */
|
||||||
|
ret = clear_extent_bit(&BTRFS_I(inode)->io_tree,
|
||||||
|
BTRFS_MAX_EXTENT_SIZE >> 1,
|
||||||
|
(BTRFS_MAX_EXTENT_SIZE >> 1) + 4095,
|
||||||
|
EXTENT_DELALLOC | EXTENT_DIRTY |
|
||||||
|
EXTENT_UPTODATE | EXTENT_DO_ACCOUNTING, 0, 0,
|
||||||
|
NULL, GFP_NOFS);
|
||||||
|
if (ret) {
|
||||||
|
test_msg("clear_extent_bit returned %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (BTRFS_I(inode)->outstanding_extents != 2) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
test_msg("Miscount, wanted 2, got %u\n",
|
||||||
|
BTRFS_I(inode)->outstanding_extents);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [BTRFS_MAX_EXTENT_SIZE][4K] */
|
||||||
|
BTRFS_I(inode)->outstanding_extents++;
|
||||||
|
ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE >> 1,
|
||||||
|
(BTRFS_MAX_EXTENT_SIZE >> 1) + 4095,
|
||||||
|
NULL);
|
||||||
|
if (ret) {
|
||||||
|
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (BTRFS_I(inode)->outstanding_extents != 2) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
test_msg("Miscount, wanted 2, got %u\n",
|
||||||
|
BTRFS_I(inode)->outstanding_extents);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* [BTRFS_MAX_EXTENT_SIZE+4K][4K HOLE][BTRFS_MAX_EXTENT_SIZE+4K]
|
||||||
|
*
|
||||||
|
* I'm artificially adding 2 to outstanding_extents because in the
|
||||||
|
* buffered IO case we'd add things up as we go, but I don't feel like
|
||||||
|
* doing that here, this isn't the interesting case we want to test.
|
||||||
|
*/
|
||||||
|
BTRFS_I(inode)->outstanding_extents += 2;
|
||||||
|
ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE + 8192,
|
||||||
|
(BTRFS_MAX_EXTENT_SIZE << 1) + 12287,
|
||||||
|
NULL);
|
||||||
|
if (ret) {
|
||||||
|
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (BTRFS_I(inode)->outstanding_extents != 4) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
test_msg("Miscount, wanted 4, got %u\n",
|
||||||
|
BTRFS_I(inode)->outstanding_extents);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [BTRFS_MAX_EXTENT_SIZE+4k][4k][BTRFS_MAX_EXTENT_SIZE+4k] */
|
||||||
|
BTRFS_I(inode)->outstanding_extents++;
|
||||||
|
ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE+4096,
|
||||||
|
BTRFS_MAX_EXTENT_SIZE+8191, NULL);
|
||||||
|
if (ret) {
|
||||||
|
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (BTRFS_I(inode)->outstanding_extents != 3) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
test_msg("Miscount, wanted 3, got %u\n",
|
||||||
|
BTRFS_I(inode)->outstanding_extents);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [BTRFS_MAX_EXTENT_SIZE+4k][4K HOLE][BTRFS_MAX_EXTENT_SIZE+4k] */
|
||||||
|
ret = clear_extent_bit(&BTRFS_I(inode)->io_tree,
|
||||||
|
BTRFS_MAX_EXTENT_SIZE+4096,
|
||||||
|
BTRFS_MAX_EXTENT_SIZE+8191,
|
||||||
|
EXTENT_DIRTY | EXTENT_DELALLOC |
|
||||||
|
EXTENT_DO_ACCOUNTING | EXTENT_UPTODATE, 0, 0,
|
||||||
|
NULL, GFP_NOFS);
|
||||||
|
if (ret) {
|
||||||
|
test_msg("clear_extent_bit returned %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (BTRFS_I(inode)->outstanding_extents != 4) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
test_msg("Miscount, wanted 4, got %u\n",
|
||||||
|
BTRFS_I(inode)->outstanding_extents);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Refill the hole again just for good measure, because I thought it
|
||||||
|
* might fail and I'd rather satisfy my paranoia at this point.
|
||||||
|
*/
|
||||||
|
BTRFS_I(inode)->outstanding_extents++;
|
||||||
|
ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE+4096,
|
||||||
|
BTRFS_MAX_EXTENT_SIZE+8191, NULL);
|
||||||
|
if (ret) {
|
||||||
|
test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (BTRFS_I(inode)->outstanding_extents != 3) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
test_msg("Miscount, wanted 3, got %u\n",
|
||||||
|
BTRFS_I(inode)->outstanding_extents);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Empty */
|
||||||
|
ret = clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, (u64)-1,
|
||||||
|
EXTENT_DIRTY | EXTENT_DELALLOC |
|
||||||
|
EXTENT_DO_ACCOUNTING | EXTENT_UPTODATE, 0, 0,
|
||||||
|
NULL, GFP_NOFS);
|
||||||
|
if (ret) {
|
||||||
|
test_msg("clear_extent_bit returned %d\n", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (BTRFS_I(inode)->outstanding_extents) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
test_msg("Miscount, wanted 0, got %u\n",
|
||||||
|
BTRFS_I(inode)->outstanding_extents);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
out:
|
||||||
|
if (ret)
|
||||||
|
clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, (u64)-1,
|
||||||
|
EXTENT_DIRTY | EXTENT_DELALLOC |
|
||||||
|
EXTENT_DO_ACCOUNTING | EXTENT_UPTODATE, 0, 0,
|
||||||
|
NULL, GFP_NOFS);
|
||||||
|
iput(inode);
|
||||||
|
btrfs_free_dummy_root(root);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int btrfs_test_inodes(void)
|
int btrfs_test_inodes(void)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@ -924,5 +1115,9 @@ int btrfs_test_inodes(void)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
test_msg("Running hole first btrfs_get_extent test\n");
|
test_msg("Running hole first btrfs_get_extent test\n");
|
||||||
return test_hole_first();
|
ret = test_hole_first();
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
test_msg("Running outstanding_extents tests\n");
|
||||||
|
return test_extent_accounting();
|
||||||
}
|
}
|
||||||
|
@ -1023,17 +1023,13 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
|
|||||||
u64 old_root_bytenr;
|
u64 old_root_bytenr;
|
||||||
u64 old_root_used;
|
u64 old_root_used;
|
||||||
struct btrfs_root *tree_root = root->fs_info->tree_root;
|
struct btrfs_root *tree_root = root->fs_info->tree_root;
|
||||||
bool extent_root = (root->objectid == BTRFS_EXTENT_TREE_OBJECTID);
|
|
||||||
|
|
||||||
old_root_used = btrfs_root_used(&root->root_item);
|
old_root_used = btrfs_root_used(&root->root_item);
|
||||||
btrfs_write_dirty_block_groups(trans, root);
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
old_root_bytenr = btrfs_root_bytenr(&root->root_item);
|
old_root_bytenr = btrfs_root_bytenr(&root->root_item);
|
||||||
if (old_root_bytenr == root->node->start &&
|
if (old_root_bytenr == root->node->start &&
|
||||||
old_root_used == btrfs_root_used(&root->root_item) &&
|
old_root_used == btrfs_root_used(&root->root_item))
|
||||||
(!extent_root ||
|
|
||||||
list_empty(&trans->transaction->dirty_bgs)))
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
btrfs_set_root_node(&root->root_item, root->node);
|
btrfs_set_root_node(&root->root_item, root->node);
|
||||||
@ -1044,14 +1040,6 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
old_root_used = btrfs_root_used(&root->root_item);
|
old_root_used = btrfs_root_used(&root->root_item);
|
||||||
if (extent_root) {
|
|
||||||
ret = btrfs_write_dirty_block_groups(trans, root);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -1068,6 +1056,7 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
|
|||||||
struct btrfs_root *root)
|
struct btrfs_root *root)
|
||||||
{
|
{
|
||||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||||
|
struct list_head *dirty_bgs = &trans->transaction->dirty_bgs;
|
||||||
struct list_head *next;
|
struct list_head *next;
|
||||||
struct extent_buffer *eb;
|
struct extent_buffer *eb;
|
||||||
int ret;
|
int ret;
|
||||||
@ -1095,11 +1084,15 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
ret = btrfs_setup_space_cache(trans, root);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/* run_qgroups might have added some more refs */
|
/* run_qgroups might have added some more refs */
|
||||||
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
|
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
again:
|
||||||
while (!list_empty(&fs_info->dirty_cowonly_roots)) {
|
while (!list_empty(&fs_info->dirty_cowonly_roots)) {
|
||||||
next = fs_info->dirty_cowonly_roots.next;
|
next = fs_info->dirty_cowonly_roots.next;
|
||||||
list_del_init(next);
|
list_del_init(next);
|
||||||
@ -1112,8 +1105,23 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
|
|||||||
ret = update_cowonly_root(trans, root);
|
ret = update_cowonly_root(trans, root);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (!list_empty(dirty_bgs)) {
|
||||||
|
ret = btrfs_write_dirty_block_groups(trans, root);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!list_empty(&fs_info->dirty_cowonly_roots))
|
||||||
|
goto again;
|
||||||
|
|
||||||
list_add_tail(&fs_info->extent_root->dirty_list,
|
list_add_tail(&fs_info->extent_root->dirty_list,
|
||||||
&trans->transaction->switch_commits);
|
&trans->transaction->switch_commits);
|
||||||
btrfs_after_dev_replace_commit(fs_info);
|
btrfs_after_dev_replace_commit(fs_info);
|
||||||
@ -1811,6 +1819,9 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|||||||
|
|
||||||
wait_for_commit(root, cur_trans);
|
wait_for_commit(root, cur_trans);
|
||||||
|
|
||||||
|
if (unlikely(cur_trans->aborted))
|
||||||
|
ret = cur_trans->aborted;
|
||||||
|
|
||||||
btrfs_put_transaction(cur_trans);
|
btrfs_put_transaction(cur_trans);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
Loading…
Reference in New Issue
Block a user