提交 8082510e 编写于 作者: Y Yan, Zheng 提交者: Chris Mason

Btrfs: Make truncate(2) more ENOSPC friendly

truncating and deleting regular files are unbound operations,
so it's not good to do them in a single transaction. This
patch makes btrfs_truncate and btrfs_delete_inode start a
new transaction after all items in a tree leaf are deleted.
Signed-off-by: NYan Zheng <zheng.yan@oracle.com>
Signed-off-by: NChris Mason <chris.mason@oracle.com>
上级 5a303d5d
...@@ -2848,37 +2848,40 @@ static noinline int drop_csum_leaves(struct btrfs_trans_handle *trans, ...@@ -2848,37 +2848,40 @@ static noinline int drop_csum_leaves(struct btrfs_trans_handle *trans,
* min_type is the minimum key type to truncate down to. If set to 0, this * min_type is the minimum key type to truncate down to. If set to 0, this
* will kill all the items on this inode, including the INODE_ITEM_KEY. * will kill all the items on this inode, including the INODE_ITEM_KEY.
*/ */
noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct inode *inode, struct inode *inode,
u64 new_size, u32 min_type) u64 new_size, u32 min_type)
{ {
int ret;
struct btrfs_path *path; struct btrfs_path *path;
struct btrfs_key key;
struct btrfs_key found_key;
u32 found_type = (u8)-1;
struct extent_buffer *leaf; struct extent_buffer *leaf;
struct btrfs_file_extent_item *fi; struct btrfs_file_extent_item *fi;
struct btrfs_key key;
struct btrfs_key found_key;
u64 extent_start = 0; u64 extent_start = 0;
u64 extent_num_bytes = 0; u64 extent_num_bytes = 0;
u64 extent_offset = 0; u64 extent_offset = 0;
u64 item_end = 0; u64 item_end = 0;
u64 mask = root->sectorsize - 1;
u32 found_type = (u8)-1;
int found_extent; int found_extent;
int del_item; int del_item;
int pending_del_nr = 0; int pending_del_nr = 0;
int pending_del_slot = 0; int pending_del_slot = 0;
int extent_type = -1; int extent_type = -1;
int encoding; int encoding;
u64 mask = root->sectorsize - 1; int ret;
int err = 0;
BUG_ON(new_size > 0 && min_type != BTRFS_EXTENT_DATA_KEY);
if (root->ref_cows) if (root->ref_cows)
btrfs_drop_extent_cache(inode, new_size & (~mask), (u64)-1, 0); btrfs_drop_extent_cache(inode, new_size & (~mask), (u64)-1, 0);
path = btrfs_alloc_path(); path = btrfs_alloc_path();
BUG_ON(!path); BUG_ON(!path);
path->reada = -1; path->reada = -1;
/* FIXME, add redo link to tree so we don't leak on crash */
key.objectid = inode->i_ino; key.objectid = inode->i_ino;
key.offset = (u64)-1; key.offset = (u64)-1;
key.type = (u8)-1; key.type = (u8)-1;
...@@ -2886,17 +2889,17 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, ...@@ -2886,17 +2889,17 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
search_again: search_again:
path->leave_spinning = 1; path->leave_spinning = 1;
ret = btrfs_search_slot(trans, root, &key, path, -1, 1); ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
if (ret < 0) if (ret < 0) {
goto error; err = ret;
goto out;
}
if (ret > 0) { if (ret > 0) {
/* there are no items in the tree for us to truncate, we're /* there are no items in the tree for us to truncate, we're
* done * done
*/ */
if (path->slots[0] == 0) { if (path->slots[0] == 0)
ret = 0; goto out;
goto error;
}
path->slots[0]--; path->slots[0]--;
} }
...@@ -2931,28 +2934,17 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, ...@@ -2931,28 +2934,17 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
} }
item_end--; item_end--;
} }
if (item_end < new_size) { if (found_type > min_type) {
if (found_type == BTRFS_DIR_ITEM_KEY) del_item = 1;
found_type = BTRFS_INODE_ITEM_KEY; } else {
else if (found_type == BTRFS_EXTENT_ITEM_KEY) if (item_end < new_size)
found_type = BTRFS_EXTENT_DATA_KEY;
else if (found_type == BTRFS_EXTENT_DATA_KEY)
found_type = BTRFS_XATTR_ITEM_KEY;
else if (found_type == BTRFS_XATTR_ITEM_KEY)
found_type = BTRFS_INODE_REF_KEY;
else if (found_type)
found_type--;
else
break; break;
btrfs_set_key_type(&key, found_type); if (found_key.offset >= new_size)
goto next; del_item = 1;
else
del_item = 0;
} }
if (found_key.offset >= new_size)
del_item = 1;
else
del_item = 0;
found_extent = 0; found_extent = 0;
/* FIXME, shrink the extent if the ref count is only 1 */ /* FIXME, shrink the extent if the ref count is only 1 */
if (found_type != BTRFS_EXTENT_DATA_KEY) if (found_type != BTRFS_EXTENT_DATA_KEY)
goto delete; goto delete;
...@@ -3039,42 +3031,36 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, ...@@ -3039,42 +3031,36 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
inode->i_ino, extent_offset); inode->i_ino, extent_offset);
BUG_ON(ret); BUG_ON(ret);
} }
next:
if (path->slots[0] == 0) {
if (pending_del_nr)
goto del_pending;
btrfs_release_path(root, path);
if (found_type == BTRFS_INODE_ITEM_KEY)
break;
goto search_again;
}
path->slots[0]--; if (found_type == BTRFS_INODE_ITEM_KEY)
if (pending_del_nr && break;
path->slots[0] + 1 != pending_del_slot) {
struct btrfs_key debug; if (path->slots[0] == 0 ||
del_pending: path->slots[0] != pending_del_slot) {
btrfs_item_key_to_cpu(path->nodes[0], &debug, if (root->ref_cows) {
pending_del_slot); err = -EAGAIN;
ret = btrfs_del_items(trans, root, path, goto out;
pending_del_slot, }
pending_del_nr); if (pending_del_nr) {
BUG_ON(ret); ret = btrfs_del_items(trans, root, path,
pending_del_nr = 0; pending_del_slot,
pending_del_nr);
BUG_ON(ret);
pending_del_nr = 0;
}
btrfs_release_path(root, path); btrfs_release_path(root, path);
if (found_type == BTRFS_INODE_ITEM_KEY)
break;
goto search_again; goto search_again;
} else {
path->slots[0]--;
} }
} }
ret = 0; out:
error:
if (pending_del_nr) { if (pending_del_nr) {
ret = btrfs_del_items(trans, root, path, pending_del_slot, ret = btrfs_del_items(trans, root, path, pending_del_slot,
pending_del_nr); pending_del_nr);
} }
btrfs_free_path(path); btrfs_free_path(path);
return ret; return err;
} }
/* /*
...@@ -3194,10 +3180,6 @@ int btrfs_cont_expand(struct inode *inode, loff_t size) ...@@ -3194,10 +3180,6 @@ int btrfs_cont_expand(struct inode *inode, loff_t size)
if (size <= hole_start) if (size <= hole_start)
return 0; return 0;
err = btrfs_truncate_page(inode->i_mapping, inode->i_size);
if (err)
return err;
while (1) { while (1) {
struct btrfs_ordered_extent *ordered; struct btrfs_ordered_extent *ordered;
btrfs_wait_ordered_range(inode, hole_start, btrfs_wait_ordered_range(inode, hole_start,
...@@ -3210,9 +3192,6 @@ int btrfs_cont_expand(struct inode *inode, loff_t size) ...@@ -3210,9 +3192,6 @@ int btrfs_cont_expand(struct inode *inode, loff_t size)
btrfs_put_ordered_extent(ordered); btrfs_put_ordered_extent(ordered);
} }
trans = btrfs_start_transaction(root, 1);
btrfs_set_trans_block_group(trans, inode);
cur_offset = hole_start; cur_offset = hole_start;
while (1) { while (1) {
em = btrfs_get_extent(inode, NULL, 0, cur_offset, em = btrfs_get_extent(inode, NULL, 0, cur_offset,
...@@ -3220,38 +3199,120 @@ int btrfs_cont_expand(struct inode *inode, loff_t size) ...@@ -3220,38 +3199,120 @@ int btrfs_cont_expand(struct inode *inode, loff_t size)
BUG_ON(IS_ERR(em) || !em); BUG_ON(IS_ERR(em) || !em);
last_byte = min(extent_map_end(em), block_end); last_byte = min(extent_map_end(em), block_end);
last_byte = (last_byte + mask) & ~mask; last_byte = (last_byte + mask) & ~mask;
if (test_bit(EXTENT_FLAG_VACANCY, &em->flags)) { if (!test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) {
u64 hint_byte = 0; u64 hint_byte = 0;
hole_size = last_byte - cur_offset; hole_size = last_byte - cur_offset;
err = btrfs_drop_extents(trans, inode, cur_offset,
cur_offset + hole_size,
&hint_byte, 1);
if (err)
break;
err = btrfs_reserve_metadata_space(root, 1); err = btrfs_reserve_metadata_space(root, 2);
if (err) if (err)
break; break;
trans = btrfs_start_transaction(root, 1);
btrfs_set_trans_block_group(trans, inode);
err = btrfs_drop_extents(trans, inode, cur_offset,
cur_offset + hole_size,
&hint_byte, 1);
BUG_ON(err);
err = btrfs_insert_file_extent(trans, root, err = btrfs_insert_file_extent(trans, root,
inode->i_ino, cur_offset, 0, inode->i_ino, cur_offset, 0,
0, hole_size, 0, hole_size, 0, hole_size, 0, hole_size,
0, 0, 0); 0, 0, 0);
BUG_ON(err);
btrfs_drop_extent_cache(inode, hole_start, btrfs_drop_extent_cache(inode, hole_start,
last_byte - 1, 0); last_byte - 1, 0);
btrfs_unreserve_metadata_space(root, 1);
btrfs_end_transaction(trans, root);
btrfs_unreserve_metadata_space(root, 2);
} }
free_extent_map(em); free_extent_map(em);
cur_offset = last_byte; cur_offset = last_byte;
if (err || cur_offset >= block_end) if (cur_offset >= block_end)
break; break;
} }
btrfs_end_transaction(trans, root);
unlock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS); unlock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS);
return err; return err;
} }
static int btrfs_setattr_size(struct inode *inode, struct iattr *attr)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_trans_handle *trans;
unsigned long nr;
int ret;
if (attr->ia_size == inode->i_size)
return 0;
if (attr->ia_size > inode->i_size) {
unsigned long limit;
limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
if (attr->ia_size > inode->i_sb->s_maxbytes)
return -EFBIG;
if (limit != RLIM_INFINITY && attr->ia_size > limit) {
send_sig(SIGXFSZ, current, 0);
return -EFBIG;
}
}
ret = btrfs_reserve_metadata_space(root, 1);
if (ret)
return ret;
trans = btrfs_start_transaction(root, 1);
btrfs_set_trans_block_group(trans, inode);
ret = btrfs_orphan_add(trans, inode);
BUG_ON(ret);
nr = trans->blocks_used;
btrfs_end_transaction(trans, root);
btrfs_unreserve_metadata_space(root, 1);
btrfs_btree_balance_dirty(root, nr);
if (attr->ia_size > inode->i_size) {
ret = btrfs_cont_expand(inode, attr->ia_size);
if (ret) {
btrfs_truncate(inode);
return ret;
}
i_size_write(inode, attr->ia_size);
btrfs_ordered_update_i_size(inode, inode->i_size, NULL);
trans = btrfs_start_transaction(root, 1);
btrfs_set_trans_block_group(trans, inode);
ret = btrfs_update_inode(trans, root, inode);
BUG_ON(ret);
if (inode->i_nlink > 0) {
ret = btrfs_orphan_del(trans, inode);
BUG_ON(ret);
}
nr = trans->blocks_used;
btrfs_end_transaction(trans, root);
btrfs_btree_balance_dirty(root, nr);
return 0;
}
/*
* We're truncating a file that used to have good data down to
* zero. Make sure it gets into the ordered flush list so that
* any new writes get down to disk quickly.
*/
if (attr->ia_size == 0)
BTRFS_I(inode)->ordered_data_close = 1;
/* we don't support swapfiles, so vmtruncate shouldn't fail */
ret = vmtruncate(inode, attr->ia_size);
BUG_ON(ret);
return 0;
}
static int btrfs_setattr(struct dentry *dentry, struct iattr *attr) static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
...@@ -3262,23 +3323,14 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -3262,23 +3323,14 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
return err; return err;
if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) { if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
if (attr->ia_size > inode->i_size) { err = btrfs_setattr_size(inode, attr);
err = btrfs_cont_expand(inode, attr->ia_size); if (err)
if (err) return err;
return err;
} else if (inode->i_size > 0 &&
attr->ia_size == 0) {
/* we're truncating a file that used to have good
* data down to zero. Make sure it gets into
* the ordered flush list so that any new writes
* get down to disk quickly.
*/
BTRFS_I(inode)->ordered_data_close = 1;
}
} }
attr->ia_valid &= ~ATTR_SIZE;
err = inode_setattr(inode, attr); if (attr->ia_valid)
err = inode_setattr(inode, attr);
if (!err && ((attr->ia_valid & ATTR_MODE))) if (!err && ((attr->ia_valid & ATTR_MODE)))
err = btrfs_acl_chmod(inode); err = btrfs_acl_chmod(inode);
...@@ -3310,30 +3362,32 @@ void btrfs_delete_inode(struct inode *inode) ...@@ -3310,30 +3362,32 @@ void btrfs_delete_inode(struct inode *inode)
} }
btrfs_i_size_write(inode, 0); btrfs_i_size_write(inode, 0);
trans = btrfs_join_transaction(root, 1);
btrfs_set_trans_block_group(trans, inode); while (1) {
ret = btrfs_truncate_inode_items(trans, root, inode, inode->i_size, 0); trans = btrfs_start_transaction(root, 1);
if (ret) { btrfs_set_trans_block_group(trans, inode);
btrfs_orphan_del(NULL, inode); ret = btrfs_truncate_inode_items(trans, root, inode, 0, 0);
goto no_delete_lock;
}
btrfs_orphan_del(trans, inode); if (ret != -EAGAIN)
break;
nr = trans->blocks_used; nr = trans->blocks_used;
clear_inode(inode); btrfs_end_transaction(trans, root);
trans = NULL;
btrfs_btree_balance_dirty(root, nr);
}
btrfs_end_transaction(trans, root); if (ret == 0) {
btrfs_btree_balance_dirty(root, nr); ret = btrfs_orphan_del(trans, inode);
return; BUG_ON(ret);
}
no_delete_lock:
nr = trans->blocks_used; nr = trans->blocks_used;
btrfs_end_transaction(trans, root); btrfs_end_transaction(trans, root);
btrfs_btree_balance_dirty(root, nr); btrfs_btree_balance_dirty(root, nr);
no_delete: no_delete:
clear_inode(inode); clear_inode(inode);
return;
} }
/* /*
...@@ -5097,17 +5151,20 @@ static void btrfs_truncate(struct inode *inode) ...@@ -5097,17 +5151,20 @@ static void btrfs_truncate(struct inode *inode)
unsigned long nr; unsigned long nr;
u64 mask = root->sectorsize - 1; u64 mask = root->sectorsize - 1;
if (!S_ISREG(inode->i_mode)) if (!S_ISREG(inode->i_mode)) {
return; WARN_ON(1);
if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
return; return;
}
ret = btrfs_truncate_page(inode->i_mapping, inode->i_size); ret = btrfs_truncate_page(inode->i_mapping, inode->i_size);
if (ret) if (ret)
return; return;
btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1); btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1);
btrfs_ordered_update_i_size(inode, inode->i_size, NULL);
trans = btrfs_start_transaction(root, 1); trans = btrfs_start_transaction(root, 1);
btrfs_set_trans_block_group(trans, inode);
/* /*
* setattr is responsible for setting the ordered_data_close flag, * setattr is responsible for setting the ordered_data_close flag,
...@@ -5129,21 +5186,32 @@ static void btrfs_truncate(struct inode *inode) ...@@ -5129,21 +5186,32 @@ static void btrfs_truncate(struct inode *inode)
if (inode->i_size == 0 && BTRFS_I(inode)->ordered_data_close) if (inode->i_size == 0 && BTRFS_I(inode)->ordered_data_close)
btrfs_add_ordered_operation(trans, root, inode); btrfs_add_ordered_operation(trans, root, inode);
btrfs_set_trans_block_group(trans, inode); while (1) {
btrfs_i_size_write(inode, inode->i_size); ret = btrfs_truncate_inode_items(trans, root, inode,
inode->i_size,
BTRFS_EXTENT_DATA_KEY);
if (ret != -EAGAIN)
break;
ret = btrfs_orphan_add(trans, inode); ret = btrfs_update_inode(trans, root, inode);
if (ret) BUG_ON(ret);
goto out;
/* FIXME, add redo link to tree so we don't leak on crash */ nr = trans->blocks_used;
ret = btrfs_truncate_inode_items(trans, root, inode, inode->i_size, btrfs_end_transaction(trans, root);
BTRFS_EXTENT_DATA_KEY); btrfs_btree_balance_dirty(root, nr);
btrfs_update_inode(trans, root, inode);
trans = btrfs_start_transaction(root, 1);
btrfs_set_trans_block_group(trans, inode);
}
ret = btrfs_orphan_del(trans, inode); if (ret == 0 && inode->i_nlink > 0) {
ret = btrfs_orphan_del(trans, inode);
BUG_ON(ret);
}
ret = btrfs_update_inode(trans, root, inode);
BUG_ON(ret); BUG_ON(ret);
out:
nr = trans->blocks_used; nr = trans->blocks_used;
ret = btrfs_end_transaction_throttle(trans, root); ret = btrfs_end_transaction_throttle(trans, root);
BUG_ON(ret); BUG_ON(ret);
...@@ -5240,9 +5308,9 @@ void btrfs_destroy_inode(struct inode *inode) ...@@ -5240,9 +5308,9 @@ void btrfs_destroy_inode(struct inode *inode)
spin_lock(&root->list_lock); spin_lock(&root->list_lock);
if (!list_empty(&BTRFS_I(inode)->i_orphan)) { if (!list_empty(&BTRFS_I(inode)->i_orphan)) {
printk(KERN_ERR "BTRFS: inode %lu: inode still on the orphan" printk(KERN_INFO "BTRFS: inode %lu still on the orphan list\n",
" list\n", inode->i_ino); inode->i_ino);
dump_stack(); list_del_init(&BTRFS_I(inode)->i_orphan);
} }
spin_unlock(&root->list_lock); spin_unlock(&root->list_lock);
......
...@@ -1561,6 +1561,20 @@ static int invalidate_extent_cache(struct btrfs_root *root, ...@@ -1561,6 +1561,20 @@ static int invalidate_extent_cache(struct btrfs_root *root,
return 0; return 0;
} }
static void put_inodes(struct list_head *list)
{
struct inodevec *ivec;
while (!list_empty(list)) {
ivec = list_entry(list->next, struct inodevec, list);
list_del(&ivec->list);
while (ivec->nr > 0) {
ivec->nr--;
iput(ivec->inode[ivec->nr]);
}
kfree(ivec);
}
}
static int find_next_key(struct btrfs_path *path, int level, static int find_next_key(struct btrfs_path *path, int level,
struct btrfs_key *key) struct btrfs_key *key)
...@@ -1723,6 +1737,11 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc, ...@@ -1723,6 +1737,11 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
btrfs_btree_balance_dirty(root, nr); btrfs_btree_balance_dirty(root, nr);
/*
* put inodes outside transaction, otherwise we may deadlock.
*/
put_inodes(&inode_list);
if (replaced && rc->stage == UPDATE_DATA_PTRS) if (replaced && rc->stage == UPDATE_DATA_PTRS)
invalidate_extent_cache(root, &key, &next_key); invalidate_extent_cache(root, &key, &next_key);
} }
...@@ -1752,19 +1771,7 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc, ...@@ -1752,19 +1771,7 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
btrfs_btree_balance_dirty(root, nr); btrfs_btree_balance_dirty(root, nr);
/* put_inodes(&inode_list);
* put inodes while we aren't holding the tree locks
*/
while (!list_empty(&inode_list)) {
struct inodevec *ivec;
ivec = list_entry(inode_list.next, struct inodevec, list);
list_del(&ivec->list);
while (ivec->nr > 0) {
ivec->nr--;
iput(ivec->inode[ivec->nr]);
}
kfree(ivec);
}
if (replaced && rc->stage == UPDATE_DATA_PTRS) if (replaced && rc->stage == UPDATE_DATA_PTRS)
invalidate_extent_cache(root, &key, &next_key); invalidate_extent_cache(root, &key, &next_key);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册