mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-13 01:08:50 +00:00
Btrfs: During deletes and truncate, remove many items at once from the tree
Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
parent
70dec8079d
commit
85e21bac16
@ -2514,34 +2514,36 @@ static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
|||||||
* delete the item at the leaf level in path. If that empties
|
* delete the item at the leaf level in path. If that empties
|
||||||
* the leaf, remove it from the tree
|
* the leaf, remove it from the tree
|
||||||
*/
|
*/
|
||||||
int btrfs_del_item(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||||
struct btrfs_path *path)
|
struct btrfs_path *path, int slot, int nr)
|
||||||
{
|
{
|
||||||
int slot;
|
|
||||||
struct extent_buffer *leaf;
|
struct extent_buffer *leaf;
|
||||||
struct btrfs_item *item;
|
struct btrfs_item *item;
|
||||||
int doff;
|
int last_off;
|
||||||
int dsize;
|
int dsize = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int wret;
|
int wret;
|
||||||
|
int i;
|
||||||
u32 nritems;
|
u32 nritems;
|
||||||
|
|
||||||
leaf = path->nodes[0];
|
leaf = path->nodes[0];
|
||||||
slot = path->slots[0];
|
last_off = btrfs_item_offset_nr(leaf, slot + nr - 1);
|
||||||
doff = btrfs_item_offset_nr(leaf, slot);
|
|
||||||
dsize = btrfs_item_size_nr(leaf, slot);
|
for (i = 0; i < nr; i++)
|
||||||
|
dsize += btrfs_item_size_nr(leaf, slot + i);
|
||||||
|
|
||||||
nritems = btrfs_header_nritems(leaf);
|
nritems = btrfs_header_nritems(leaf);
|
||||||
|
|
||||||
if (slot != nritems - 1) {
|
if (slot + nr != nritems) {
|
||||||
int i;
|
int i;
|
||||||
int data_end = leaf_data_end(root, leaf);
|
int data_end = leaf_data_end(root, leaf);
|
||||||
|
|
||||||
memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
|
memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
|
||||||
data_end + dsize,
|
data_end + dsize,
|
||||||
btrfs_leaf_data(leaf) + data_end,
|
btrfs_leaf_data(leaf) + data_end,
|
||||||
doff - data_end);
|
last_off - data_end);
|
||||||
|
|
||||||
for (i = slot + 1; i < nritems; i++) {
|
for (i = slot + nr; i < nritems; i++) {
|
||||||
u32 ioff;
|
u32 ioff;
|
||||||
|
|
||||||
item = btrfs_item_nr(leaf, i);
|
item = btrfs_item_nr(leaf, i);
|
||||||
@ -2562,12 +2564,12 @@ int btrfs_del_item(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
|||||||
}
|
}
|
||||||
|
|
||||||
memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot),
|
memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot),
|
||||||
btrfs_item_nr_offset(slot + 1),
|
btrfs_item_nr_offset(slot + nr),
|
||||||
sizeof(struct btrfs_item) *
|
sizeof(struct btrfs_item) *
|
||||||
(nritems - slot - 1));
|
(nritems - slot - nr));
|
||||||
}
|
}
|
||||||
btrfs_set_header_nritems(leaf, nritems - 1);
|
btrfs_set_header_nritems(leaf, nritems - nr);
|
||||||
nritems--;
|
nritems -= nr;
|
||||||
|
|
||||||
/* delete the leaf if we've emptied it */
|
/* delete the leaf if we've emptied it */
|
||||||
if (nritems == 0) {
|
if (nritems == 0) {
|
||||||
@ -2600,7 +2602,7 @@ int btrfs_del_item(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* delete the leaf if it is mostly empty */
|
/* delete the leaf if it is mostly empty */
|
||||||
if (used < BTRFS_LEAF_DATA_SIZE(root) / 3) {
|
if (used < BTRFS_LEAF_DATA_SIZE(root) / 4) {
|
||||||
/* push_leaf_left fixes the path.
|
/* push_leaf_left fixes the path.
|
||||||
* make sure the path still points to our leaf
|
* make sure the path still points to our leaf
|
||||||
* for possible call to del_ptr below
|
* for possible call to del_ptr below
|
||||||
@ -2608,13 +2610,13 @@ int btrfs_del_item(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
|||||||
slot = path->slots[1];
|
slot = path->slots[1];
|
||||||
extent_buffer_get(leaf);
|
extent_buffer_get(leaf);
|
||||||
|
|
||||||
wret = push_leaf_right(trans, root, path, 1, 1);
|
wret = push_leaf_left(trans, root, path, 1, 1);
|
||||||
if (wret < 0 && wret != -ENOSPC)
|
if (wret < 0 && wret != -ENOSPC)
|
||||||
ret = wret;
|
ret = wret;
|
||||||
|
|
||||||
if (path->nodes[0] == leaf &&
|
if (path->nodes[0] == leaf &&
|
||||||
btrfs_header_nritems(leaf)) {
|
btrfs_header_nritems(leaf)) {
|
||||||
wret = push_leaf_left(trans, root, path, 1, 1);
|
wret = push_leaf_right(trans, root, path, 1, 1);
|
||||||
if (wret < 0 && wret != -ENOSPC)
|
if (wret < 0 && wret != -ENOSPC)
|
||||||
ret = wret;
|
ret = wret;
|
||||||
}
|
}
|
||||||
|
@ -1038,8 +1038,16 @@ void btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p);
|
|||||||
struct btrfs_path *btrfs_alloc_path(void);
|
struct btrfs_path *btrfs_alloc_path(void);
|
||||||
void btrfs_free_path(struct btrfs_path *p);
|
void btrfs_free_path(struct btrfs_path *p);
|
||||||
void btrfs_init_path(struct btrfs_path *p);
|
void btrfs_init_path(struct btrfs_path *p);
|
||||||
int btrfs_del_item(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||||
struct btrfs_path *path);
|
struct btrfs_path *path, int slot, int nr);
|
||||||
|
|
||||||
|
static inline int btrfs_del_item(struct btrfs_trans_handle *trans,
|
||||||
|
struct btrfs_root *root,
|
||||||
|
struct btrfs_path *path)
|
||||||
|
{
|
||||||
|
return btrfs_del_items(trans, root, path, path->slots[0], 1);
|
||||||
|
}
|
||||||
|
|
||||||
int btrfs_insert_item(struct btrfs_trans_handle *trans, struct btrfs_root
|
int btrfs_insert_item(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||||
*root, struct btrfs_key *key, void *data, u32 data_size);
|
*root, struct btrfs_key *key, void *data, u32 data_size);
|
||||||
int btrfs_insert_empty_item(struct btrfs_trans_handle *trans, struct btrfs_root
|
int btrfs_insert_empty_item(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||||
|
@ -2863,7 +2863,6 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
|
|||||||
if (ret || !wait) {
|
if (ret || !wait) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = start_i; i < num_pages; i++) {
|
for (i = start_i; i < num_pages; i++) {
|
||||||
page = extent_buffer_page(eb, i);
|
page = extent_buffer_page(eb, i);
|
||||||
wait_on_page_locked(page);
|
wait_on_page_locked(page);
|
||||||
|
114
fs/btrfs/inode.c
114
fs/btrfs/inode.c
@ -692,27 +692,6 @@ fail:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int btrfs_free_inode(struct btrfs_trans_handle *trans,
|
|
||||||
struct btrfs_root *root,
|
|
||||||
struct inode *inode)
|
|
||||||
{
|
|
||||||
struct btrfs_path *path;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
clear_inode(inode);
|
|
||||||
|
|
||||||
path = btrfs_alloc_path();
|
|
||||||
BUG_ON(!path);
|
|
||||||
ret = btrfs_lookup_inode(trans, root, path,
|
|
||||||
&BTRFS_I(inode)->location, -1);
|
|
||||||
if (ret > 0)
|
|
||||||
ret = -ENOENT;
|
|
||||||
if (!ret)
|
|
||||||
ret = btrfs_del_item(trans, root, path);
|
|
||||||
btrfs_free_path(path);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* this can truncate away extent items, csum items and directory items.
|
* this can truncate away extent items, csum items and directory items.
|
||||||
* It starts at a high offset and removes keys until it can't find
|
* It starts at a high offset and removes keys until it can't find
|
||||||
@ -723,7 +702,8 @@ static int btrfs_free_inode(struct btrfs_trans_handle *trans,
|
|||||||
*/
|
*/
|
||||||
static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
|
static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_root *root,
|
struct btrfs_root *root,
|
||||||
struct inode *inode)
|
struct inode *inode,
|
||||||
|
u32 min_type)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct btrfs_path *path;
|
struct btrfs_path *path;
|
||||||
@ -739,6 +719,8 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
|
|||||||
u64 root_owner = 0;
|
u64 root_owner = 0;
|
||||||
int found_extent;
|
int found_extent;
|
||||||
int del_item;
|
int del_item;
|
||||||
|
int pending_del_nr = 0;
|
||||||
|
int pending_del_slot = 0;
|
||||||
int extent_type = -1;
|
int extent_type = -1;
|
||||||
|
|
||||||
btrfs_drop_extent_cache(inode, inode->i_size, (u64)-1);
|
btrfs_drop_extent_cache(inode, inode->i_size, (u64)-1);
|
||||||
@ -751,17 +733,19 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
|
|||||||
key.offset = (u64)-1;
|
key.offset = (u64)-1;
|
||||||
key.type = (u8)-1;
|
key.type = (u8)-1;
|
||||||
|
|
||||||
|
btrfs_init_path(path);
|
||||||
|
search_again:
|
||||||
|
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (ret > 0) {
|
||||||
|
BUG_ON(path->slots[0] == 0);
|
||||||
|
path->slots[0]--;
|
||||||
|
}
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
btrfs_init_path(path);
|
|
||||||
fi = NULL;
|
fi = NULL;
|
||||||
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (ret > 0) {
|
|
||||||
BUG_ON(path->slots[0] == 0);
|
|
||||||
path->slots[0]--;
|
|
||||||
}
|
|
||||||
leaf = path->nodes[0];
|
leaf = path->nodes[0];
|
||||||
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
|
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
|
||||||
found_type = btrfs_key_type(&found_key);
|
found_type = btrfs_key_type(&found_key);
|
||||||
@ -769,10 +753,7 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
|
|||||||
if (found_key.objectid != inode->i_ino)
|
if (found_key.objectid != inode->i_ino)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (found_type != BTRFS_CSUM_ITEM_KEY &&
|
if (found_type < min_type)
|
||||||
found_type != BTRFS_DIR_ITEM_KEY &&
|
|
||||||
found_type != BTRFS_DIR_INDEX_KEY &&
|
|
||||||
found_type != BTRFS_EXTENT_DATA_KEY)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
item_end = found_key.offset;
|
item_end = found_key.offset;
|
||||||
@ -801,14 +782,17 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
|
|||||||
found_type = BTRFS_INODE_ITEM_KEY;
|
found_type = BTRFS_INODE_ITEM_KEY;
|
||||||
} else if (found_type == BTRFS_EXTENT_ITEM_KEY) {
|
} else if (found_type == BTRFS_EXTENT_ITEM_KEY) {
|
||||||
found_type = BTRFS_CSUM_ITEM_KEY;
|
found_type = BTRFS_CSUM_ITEM_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) {
|
} else if (found_type) {
|
||||||
found_type--;
|
found_type--;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
btrfs_set_key_type(&key, found_type);
|
btrfs_set_key_type(&key, found_type);
|
||||||
btrfs_release_path(root, path);
|
goto next;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if (found_key.offset >= inode->i_size)
|
if (found_key.offset >= inode->i_size)
|
||||||
del_item = 1;
|
del_item = 1;
|
||||||
@ -860,13 +844,21 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
|
|||||||
}
|
}
|
||||||
delete:
|
delete:
|
||||||
if (del_item) {
|
if (del_item) {
|
||||||
ret = btrfs_del_item(trans, root, path);
|
if (!pending_del_nr) {
|
||||||
if (ret)
|
/* no pending yet, add ourselves */
|
||||||
goto error;
|
pending_del_slot = path->slots[0];
|
||||||
|
pending_del_nr = 1;
|
||||||
|
} else if (pending_del_nr &&
|
||||||
|
path->slots[0] + 1 == pending_del_slot) {
|
||||||
|
/* hop on the pending chunk */
|
||||||
|
pending_del_nr++;
|
||||||
|
pending_del_slot = path->slots[0];
|
||||||
|
} else {
|
||||||
|
printk("bad pending slot %d pending_del_nr %d pending_del_slot %d\n", path->slots[0], pending_del_nr, pending_del_slot);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
btrfs_release_path(root, path);
|
|
||||||
if (found_extent) {
|
if (found_extent) {
|
||||||
ret = btrfs_free_extent(trans, root, extent_start,
|
ret = btrfs_free_extent(trans, root, extent_start,
|
||||||
extent_num_bytes,
|
extent_num_bytes,
|
||||||
@ -875,9 +867,36 @@ delete:
|
|||||||
found_key.offset, 0);
|
found_key.offset, 0);
|
||||||
BUG_ON(ret);
|
BUG_ON(ret);
|
||||||
}
|
}
|
||||||
|
next:
|
||||||
|
if (path->slots[0] == 0) {
|
||||||
|
if (pending_del_nr)
|
||||||
|
goto del_pending;
|
||||||
|
btrfs_release_path(root, path);
|
||||||
|
goto search_again;
|
||||||
|
}
|
||||||
|
|
||||||
|
path->slots[0]--;
|
||||||
|
if (pending_del_nr &&
|
||||||
|
path->slots[0] + 1 != pending_del_slot) {
|
||||||
|
struct btrfs_key debug;
|
||||||
|
del_pending:
|
||||||
|
btrfs_item_key_to_cpu(path->nodes[0], &debug,
|
||||||
|
pending_del_slot);
|
||||||
|
ret = btrfs_del_items(trans, root, path,
|
||||||
|
pending_del_slot,
|
||||||
|
pending_del_nr);
|
||||||
|
BUG_ON(ret);
|
||||||
|
pending_del_nr = 0;
|
||||||
|
btrfs_release_path(root, path);
|
||||||
|
goto search_again;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
error:
|
error:
|
||||||
|
if (pending_del_nr) {
|
||||||
|
ret = btrfs_del_items(trans, root, path, pending_del_slot,
|
||||||
|
pending_del_nr);
|
||||||
|
}
|
||||||
btrfs_release_path(root, path);
|
btrfs_release_path(root, path);
|
||||||
btrfs_free_path(path);
|
btrfs_free_path(path);
|
||||||
inode->i_sb->s_dirt = 1;
|
inode->i_sb->s_dirt = 1;
|
||||||
@ -1067,16 +1086,12 @@ void btrfs_delete_inode(struct inode *inode)
|
|||||||
trans = btrfs_start_transaction(root, 1);
|
trans = btrfs_start_transaction(root, 1);
|
||||||
|
|
||||||
btrfs_set_trans_block_group(trans, inode);
|
btrfs_set_trans_block_group(trans, inode);
|
||||||
ret = btrfs_truncate_in_trans(trans, root, inode);
|
ret = btrfs_truncate_in_trans(trans, root, inode, 0);
|
||||||
if (ret)
|
|
||||||
goto no_delete_lock;
|
|
||||||
ret = btrfs_delete_xattrs(trans, root, inode);
|
|
||||||
if (ret)
|
|
||||||
goto no_delete_lock;
|
|
||||||
ret = btrfs_free_inode(trans, root, inode);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto no_delete_lock;
|
goto no_delete_lock;
|
||||||
|
|
||||||
nr = trans->blocks_used;
|
nr = trans->blocks_used;
|
||||||
|
clear_inode(inode);
|
||||||
|
|
||||||
btrfs_end_transaction(trans, root);
|
btrfs_end_transaction(trans, root);
|
||||||
mutex_unlock(&root->fs_info->fs_mutex);
|
mutex_unlock(&root->fs_info->fs_mutex);
|
||||||
@ -2190,7 +2205,8 @@ static void btrfs_truncate(struct inode *inode)
|
|||||||
btrfs_set_trans_block_group(trans, inode);
|
btrfs_set_trans_block_group(trans, inode);
|
||||||
|
|
||||||
/* FIXME, add redo link to tree so we don't leak on crash */
|
/* FIXME, add redo link to tree so we don't leak on crash */
|
||||||
ret = btrfs_truncate_in_trans(trans, root, inode);
|
ret = btrfs_truncate_in_trans(trans, root, inode,
|
||||||
|
BTRFS_EXTENT_DATA_KEY);
|
||||||
btrfs_update_inode(trans, root, inode);
|
btrfs_update_inode(trans, root, inode);
|
||||||
nr = trans->blocks_used;
|
nr = trans->blocks_used;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user