Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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

parent 70dec807
Loading
Loading
Loading
Loading
+20 −18
Original line number Diff line number Diff line
@@ -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
 * the leaf, remove it from the tree
 */
int btrfs_del_item(struct btrfs_trans_handle *trans, struct btrfs_root *root,
		   struct btrfs_path *path)
int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
		    struct btrfs_path *path, int slot, int nr)
{
	int slot;
	struct extent_buffer *leaf;
	struct btrfs_item *item;
	int doff;
	int dsize;
	int last_off;
	int dsize = 0;
	int ret = 0;
	int wret;
	int i;
	u32 nritems;

	leaf = path->nodes[0];
	slot = path->slots[0];
	doff = btrfs_item_offset_nr(leaf, slot);
	dsize = btrfs_item_size_nr(leaf, slot);
	last_off = btrfs_item_offset_nr(leaf, slot + nr - 1);

	for (i = 0; i < nr; i++)
		dsize += btrfs_item_size_nr(leaf, slot + i);

	nritems = btrfs_header_nritems(leaf);

	if (slot != nritems - 1) {
	if (slot + nr != nritems) {
		int i;
		int data_end = leaf_data_end(root, leaf);

		memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
			      data_end + dsize,
			      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;

			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),
			      btrfs_item_nr_offset(slot + 1),
			      btrfs_item_nr_offset(slot + nr),
			      sizeof(struct btrfs_item) *
			      (nritems - slot - 1));
			      (nritems - slot - nr));
	}
	btrfs_set_header_nritems(leaf, nritems - 1);
	nritems--;
	btrfs_set_header_nritems(leaf, nritems - nr);
	nritems -= nr;

	/* delete the leaf if we've emptied it */
	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 */
		if (used < BTRFS_LEAF_DATA_SIZE(root) / 3) {
		if (used < BTRFS_LEAF_DATA_SIZE(root) / 4) {
			/* push_leaf_left fixes the path.
			 * make sure the path still points to our leaf
			 * 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];
			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)
				ret = wret;

			if (path->nodes[0] == 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)
					ret = wret;
			}
+10 −2
Original line number Diff line number Diff line
@@ -1038,8 +1038,16 @@ void btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p);
struct btrfs_path *btrfs_alloc_path(void);
void btrfs_free_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,
		   struct btrfs_path *path);
int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
		   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
		      *root, struct btrfs_key *key, void *data, u32 data_size);
int btrfs_insert_empty_item(struct btrfs_trans_handle *trans, struct btrfs_root
+0 −1
Original line number Diff line number Diff line
@@ -2863,7 +2863,6 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
	if (ret || !wait) {
		return ret;
	}

	for (i = start_i; i < num_pages; i++) {
		page = extent_buffer_page(eb, i);
		wait_on_page_locked(page);
+65 −49
Original line number Diff line number Diff line
@@ -692,27 +692,6 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
	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.
 * 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,
				   struct btrfs_root *root,
				   struct inode *inode)
				   struct inode *inode,
				   u32 min_type)
{
	int ret;
	struct btrfs_path *path;
@@ -739,6 +719,8 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
	u64 root_owner = 0;
	int found_extent;
	int del_item;
	int pending_del_nr = 0;
	int pending_del_slot = 0;
	int extent_type = -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,
	key.offset = (u64)-1;
	key.type = (u8)-1;

	while(1) {
	btrfs_init_path(path);
		fi = NULL;
search_again:
	ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
	if (ret < 0) {
		goto error;
@@ -762,6 +743,9 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
		BUG_ON(path->slots[0] == 0);
		path->slots[0]--;
	}

	while(1) {
		fi = NULL;
		leaf = path->nodes[0];
		btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
		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)
			break;

		if (found_type != BTRFS_CSUM_ITEM_KEY &&
		    found_type != BTRFS_DIR_ITEM_KEY &&
		    found_type != BTRFS_DIR_INDEX_KEY &&
		    found_type != BTRFS_EXTENT_DATA_KEY)
		if (found_type < min_type)
			break;

		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;
			} else if (found_type == BTRFS_EXTENT_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) {
				found_type--;
			} else {
				break;
			}
			btrfs_set_key_type(&key, found_type);
			btrfs_release_path(root, path);
			continue;
			goto next;
		}
		if (found_key.offset >= inode->i_size)
			del_item = 1;
@@ -860,13 +844,21 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
		}
delete:
		if (del_item) {
			ret = btrfs_del_item(trans, root, path);
			if (ret)
				goto error;
			if (!pending_del_nr) {
				/* no pending yet, add ourselves */
				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 {
			break;
		}
		btrfs_release_path(root, path);
		if (found_extent) {
			ret = btrfs_free_extent(trans, root, extent_start,
						extent_num_bytes,
@@ -875,9 +867,36 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
						found_key.offset, 0);
			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;
error:
	if (pending_del_nr) {
		ret = btrfs_del_items(trans, root, path, pending_del_slot,
				      pending_del_nr);
	}
	btrfs_release_path(root, path);
	btrfs_free_path(path);
	inode->i_sb->s_dirt = 1;
@@ -1067,16 +1086,12 @@ void btrfs_delete_inode(struct inode *inode)
	trans = btrfs_start_transaction(root, 1);

	btrfs_set_trans_block_group(trans, inode);
	ret = btrfs_truncate_in_trans(trans, root, inode);
	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);
	ret = btrfs_truncate_in_trans(trans, root, inode, 0);
	if (ret)
		goto no_delete_lock;

	nr = trans->blocks_used;
	clear_inode(inode);

	btrfs_end_transaction(trans, root);
	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);

	/* 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);
	nr = trans->blocks_used;