summary refs log tree commit diff
path: root/fs
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2008-01-29 15:11:36 -0500
committerChris Mason <chris.mason@oracle.com>2008-09-25 11:04:00 -0400
commit85e21bac165b4ba1f6f90431ad6fc658ffcbaf3a (patch)
tree6483417c9e5c4f3434fd9f2e7e117a4dc46b94c6 /fs
parent70dec8079d78691e476cc6c7cede40656078ad30 (diff)
downloadlinux-85e21bac165b4ba1f6f90431ad6fc658ffcbaf3a.tar.gz
Btrfs: During deletes and truncate, remove many items at once from the tree
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/ctree.c38
-rw-r--r--fs/btrfs/ctree.h12
-rw-r--r--fs/btrfs/extent_io.c1
-rw-r--r--fs/btrfs/inode.c114
4 files changed, 95 insertions, 70 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 43d23148a4fe..84ad53e06b3a 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -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;
 			}
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 6c65473e0fe3..098cf0883150 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -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
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 1f734c34dc24..8aec72253a17 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -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);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index bac8722e14e1..0a2fe51c4127 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -692,27 +692,6 @@ fail:
 	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,17 +733,19 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
 	key.offset = (u64)-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) {
-		btrfs_init_path(path);
 		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];
 		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 @@ delete:
 						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;