Commit 85e21bac authored by Chris Mason's avatar Chris Mason

Btrfs: During deletes and truncate, remove many items at once from the tree

Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 70dec807
...@@ -2514,34 +2514,36 @@ static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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); ...@@ -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, ...@@ -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);
......
...@@ -692,27 +692,6 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -692,27 +692,6 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
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, ...@@ -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, ...@@ -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,9 +733,8 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans, ...@@ -751,9 +733,8 @@ 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;
while(1) {
btrfs_init_path(path); btrfs_init_path(path);
fi = NULL; search_again:
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; goto error;
...@@ -762,6 +743,9 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans, ...@@ -762,6 +743,9 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
BUG_ON(path->slots[0] == 0); BUG_ON(path->slots[0] == 0);
path->slots[0]--; path->slots[0]--;
} }
while(1) {
fi = NULL;
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, ...@@ -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, ...@@ -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, ...@@ -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 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans, ...@@ -875,9 +867,36 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
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) ...@@ -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) ...@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment