Skip to contentSkip to conversation

Btrfs-progs: speedup btrfs send/receive

PATCH New Stefan Behrens 1 participant 6 messages Jun 26, 2013

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

v4 cover Btrfs-progs: speedup btrfs send/receive

The addressed issue is that Btrfs send / receive does not work as it is today when a high number of subvolumes exist.

This commit changes the btrfs send/receive commands to use the UUID tree to map UUIDs to subvolumes, and to use the root tree to map subvolume IDs to paths. Now these tools start fast and are independent on the number of subvolumes/snapshots that exist.

Before this commit, mapping UUIDs to subvolume IDs was an operation with a high effort. The algorithm even had quadratic effort (based on the number of existing subvolumes). E.g. with 15,000 subvolumes it took much more than 5 minutes on a state of the art XEON CPU to start btrfs send or receive before these tools were able to send or receive the first byte).
Even linear effort instead of the current quadratic effort would be too much since it would be a waste. And these data structures to allow mapping UUIDs to subvolume IDs had been created every time a btrfs send/receive instance was started.

It is much more efficient to maintain a searchable persistent data structure in the filesystem, one that is updated whenever a subvolume/snapshot is created and deleted, and when the received subvolume UUID is set by the btrfs-receive tool. The user mode tools can then just use the tree-search ioctl to quickly retrieve all information.

With a recent commit, kernel code was added that is able to maintain data structures in the filesystem that allow to quickly search for a given UUID and to retrieve data that is assigned to this UUID, like which subvolume ID is related to this UUID.

This commit series adds support for the UUID tree to Btrfs-progs and changes the send/receive tools to use it. Additionally, the btrfs-show-super tool is updated to print a new field.

v1 -> v2:
- Addressed the review comments from David Sterba.
- The v2 of the kernel patch adds a uuid_tree_generation field to the superblock, the v2 of the user mode patch adds this field to the btrfs-show-super tool.
- uuid-tree.o is added to the libbtrfs_objects since it is used by send-utils.o which is part of the exported libbtrfs.

v2 -> v3:
- Shrinked the uuid_item (this was a review comment from Liu Bo).

v3 -> v4:
- Use the type field in the key to distinguish the UUID tree item types.

Stefan Behrens (5):
Btrfs-progs: Support UUID tree and UUID items in btrfs-debug-tree
Btrfs-progs: add UUID tree lookup methods
Btrfs-progs: use UUID tree for send/receive
Btrfs-progs: add uuid_tree_gen field to btrfs-show-super
Btrfs-progs: add option to btrfs-debug-tree to print uuid tree only

 Makefile           |   5 +-
 btrfs-debug-tree.c |  31 +++-
 btrfs-show-super.c |   2 +
 cmds-receive.c     |  23 ++-
 cmds-send.c        |  53 +++++-
 ctree.h            |  25 ++-
 print-tree.c       |  46 +++++-
 send-utils.c       | 477 +++++++++++++++++++++--------------------------------
 send-utils.h       |   9 +-
 uuid-tree.c        | 104 ++++++++++++
 10 files changed, 451 insertions(+), 324 deletions(-)
 create mode 100644 uuid-tree.c
Signature · 1 line
1.8.3
#1 lore raw

PATCH v4 1/5 Btrfs-progs: Support UUID tree and UUID items in btrfs-debug-tree

Support printing these things.

---
 ctree.h      | 15 +++++++++++++++
 print-tree.c | 46 +++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 58 insertions(+), 3 deletions(-)
ctree.h +15 -0
index 3fe14b0..f959573 100644
@@ -71,6 +71,8 @@ struct btrfs_free_space_ctl;
 #define BTRFS_CSUM_TREE_OBJECTID 7ULL
 #define BTRFS_QUOTA_TREE_OBJECTID 8ULL
 
+/* for storing items that use the BTRFS_UUID_KEY* */
+#define BTRFS_UUID_TREE_OBJECTID 9ULL
 
 /* for storing balance parameters in the root tree */
 #define BTRFS_BALANCE_OBJECTID -4ULL
@@ -1107,6 +1109,19 @@ struct btrfs_root {
 #define BTRFS_DEV_REPLACE_KEY	250
 
 /*
+ * Stores items that allow to quickly map UUIDs to something else.
+ * These items are part of the filesystem UUID tree.
+ * The key is built like this:
+ * (UUID_upper_64_bits, BTRFS_UUID_KEY*, UUID_lower_64_bits).
+ */
+#if BTRFS_UUID_SIZE != 16
+#error "UUID items require BTRFS_UUID_SIZE == 16!"
+#endif
+#define BTRFS_UUID_KEY_SUBVOL	251	/* for UUIDs assigned to subvols */
+#define BTRFS_UUID_KEY_RECEIVED_SUBVOL	252	/* for UUIDs assigned to
+						 * received subvols */
+
+/*
  * string items are for debugging.  They just store a short string of
  * data in the FS
  */
print-tree.c +43 -3

88 lines not on this page · raw message

Signature · 1 line
1.8.3
#2 lore raw

PATCH v4 2/5 Btrfs-progs: add UUID tree lookup methods

In reply to Stefan Behrens

This commit adds UUID tree lookup methods that make use of the search ioctl. The code is based on the kernel code.

---
 Makefile    |   5 +--
 ctree.h     |   5 +++
 uuid-tree.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 112 insertions(+), 2 deletions(-)
Makefile +3 -2
index 398ef52..511ba6d 100644
@@ -6,12 +6,13 @@ CFLAGS = -g -O1
 objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
 	  root-tree.o dir-item.o file-item.o inode-item.o inode-map.o \
 	  extent-cache.o extent_io.o volumes.o utils.o repair.o \
-	  qgroup.o raid6.o free-space-cache.o
+	  qgroup.o raid6.o free-space-cache.o uuid-tree.o
 cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \
 	       cmds-inspect.o cmds-balance.o cmds-send.o cmds-receive.o \
 	       cmds-quota.o cmds-qgroup.o cmds-replace.o cmds-check.o \
 	       cmds-restore.o
-libbtrfs_objects = send-stream.o send-utils.o rbtree.o btrfs-list.o crc32c.o
+libbtrfs_objects = send-stream.o send-utils.o rbtree.o btrfs-list.o crc32c.o \
+		   uuid-tree.o
 libbtrfs_headers = send-stream.h send-utils.h send.h rbtree.h btrfs-list.h \
 	       crc32c.h list.h kerncompat.h radix-tree.h extent-cache.h \
 	       extent_io.h ioctl.h ctree.h
ctree.h +5 -0
index f959573..e4bca8f 100644
@@ -2346,4 +2346,9 @@ struct btrfs_csum_item *btrfs_lookup_csum(struct btrfs_trans_handle *trans,
 int btrfs_csum_truncate(struct btrfs_trans_handle *trans,
 			struct btrfs_root *root, struct btrfs_path *path,
 			u64 isize);
+
+/* uuid-tree.c */
+int btrfs_lookup_uuid_subvol_item(int fd, const u8 *uuid, u64 *subvol_id);
+int btrfs_lookup_uuid_received_subvol_item(int fd, const u8 *uuid,
+					   u64 *subvol_id);
 #endif
uuid-tree.c new file +104 -0

104 lines not on this page · raw message

Signature · 1 line
1.8.3
#3 lore raw

PATCH v4 3/5 Btrfs-progs: use UUID tree for send/receive

In reply to Stefan Behrens

This commit changes the btrfs send/receive commands to use the UUID tree to map UUIDs to subvolumes, and to use the root tree to map subvolume IDs to paths. Now these tools start fast and are independent on the number of subvolules/snapshot that exist.

Before this commit, mapping UUIDs to subvolume IDs was an operation with a high effort. The algorithm even had quadratic effort (based on the number of existing subvolumes). E.g. with 15,000 subvolumes it took much more than 5 minutes on a state of the art XEON CPU to start btrfs send or receive before these tools were able to send or receive the first byte).
Even linear effort instead of the current quadratic effort would be too much since it would be a waste. And these data structures to allow mapping UUIDs to subvolume IDs had been created every time a btrfs send/receive instance was started.

It is much more efficient to maintain a searchable persistent data structure in the filesystem, one that is updated whenever a subvolume/snapshot is created and deleted, and when the received subvolume UUID is set by the btrfs-receive tool.

Therefore kernel code was added that is able to maintain data structures in the filesystem that allow to quickly search for a given UUID and to retrieve data that is assigned to this UUID, like which subvolume ID is related to this UUID.

---
 cmds-receive.c |  23 ++-
 cmds-send.c    |  53 +++++--
 send-utils.c   | 477 +++++++++++++++++++++++----------------------------------
 send-utils.h   |   9 +-
 4 files changed, 253 insertions(+), 309 deletions(-)
cmds-receive.c +16 -7
index c2fa2e1..4e480f9 100644
@@ -31,6 +31,7 @@
 #include <math.h>
 #include <ftw.h>
 #include <wait.h>
+#include <assert.h>
 
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -129,14 +130,14 @@ static int finish_subvol(struct btrfs_receive *r)
 		goto out;
 	}
 
-	ret = btrfs_list_get_path_rootid(subvol_fd, &r->cur_subvol->root_id);
-	if (ret < 0)
-		goto out;
-	subvol_uuid_search_add(&r->sus, r->cur_subvol);
-	r->cur_subvol = NULL;
 	ret = 0;
 
 out:
+	if (r->cur_subvol) {
+		free(r->cur_subvol->path);
+		free(r->cur_subvol);
+		r->cur_subvol = NULL;
+	}
 	if (subvol_fd != -1)
 		close(subvol_fd);
 	return ret;
@@ -197,7 +198,7 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
 	struct btrfs_receive *r = user;
 	char uuid_str[128];
 	struct btrfs_ioctl_vol_args_v2 args_v2;
-	struct subvol_info *parent_subvol;
+	struct subvol_info *parent_subvol = NULL;
 
 	ret = finish_subvol(r);
 	if (ret < 0)
@@ -268,6 +269,10 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
 	}
 
 out:
+	if (parent_subvol) {
+		free(parent_subvol->path);
+		free(parent_subvol);
+	}
 	return ret;
 }
 
@@ -557,7 +562,7 @@ static int process_clone(const char *path, u64 offset, u64 len,
 			 const char *clone_path, u64 clone_offset,
 			 void *user)
 {
-	int ret = 0;
+	int ret;
 	struct btrfs_receive *r = user;
 	struct btrfs_ioctl_clone_range_args clone_args;
 	struct subvol_info *si = NULL;
@@ -624,6 +629,10 @@ static int process_clone(const char *path, u64 offset, u64 len,
 	}
 
 out:
+	if (si) {
+		free(si->path);
+		free(si);
+	}
 	free(full_path);
 	free(full_clone_path);
 	free(subvol_path);
cmds-send.c +44 -9

123 lines not on this page · raw message

send-utils.c +192 -285

524 lines not on this page · raw message

send-utils.h +1 -8
index 06af75f..ed1a40e 100644
@@ -38,10 +38,6 @@ enum subvol_search_type {
 };
 
 struct subvol_info {
-	struct rb_node rb_root_id_node;
-	struct rb_node rb_local_node;
-	struct rb_node rb_received_node;
-	struct rb_node rb_path_node;
 	u64 root_id;
 	u8 uuid[BTRFS_UUID_SIZE];
 	u8 parent_uuid[BTRFS_UUID_SIZE];
@@ -55,10 +51,7 @@ struct subvol_info {
 };
 
 struct subvol_uuid_search {
-	struct rb_root root_id_subvols;
-	struct rb_root local_subvols;
-	struct rb_root received_subvols;
-	struct rb_root path_subvols;
+	int mnt_fd;
 };
 
 int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s);
Signature · 1 line
1.8.3
#4 lore raw

PATCH v4 4/5 Btrfs-progs: add uuid_tree_gen field to btrfs-show-super

In reply to Stefan Behrens
---
 btrfs-show-super.c | 2 ++
 ctree.h            | 5 ++++-
 2 files changed, 6 insertions(+), 1 deletion(-)
btrfs-show-super.c +2 -0
index f587f10..c815469 100644
@@ -247,6 +247,8 @@ static void dump_superblock(struct btrfs_super_block *sb)
 	       (unsigned long long)btrfs_super_csum_size(sb));
 	printf("cache_generation\t%llu\n",
 	       (unsigned long long)btrfs_super_cache_generation(sb));
+	printf("uuid_tree_generation\t%llu\n",
+	       (unsigned long long)btrfs_super_uuid_tree_generation(sb));
 
 	uuid_unparse(sb->dev_item.uuid, buf);
 	printf("dev_item.uuid\t\t%s\n", buf);
ctree.h +4 -1
index e4bca8f..1ae8715 100644
@@ -437,9 +437,10 @@ struct btrfs_super_block {
 	char label[BTRFS_LABEL_SIZE];
 
 	__le64 cache_generation;
+	__le64 uuid_tree_generation;
 
 	/* future expansion */
-	__le64 reserved[31];
+	__le64 reserved[30];
 	u8 sys_chunk_array[BTRFS_SYSTEM_CHUNK_ARRAY_SIZE];
 	struct btrfs_root_backup super_roots[BTRFS_NUM_BACKUP_ROOTS];
 } __attribute__ ((__packed__));
@@ -1933,6 +1934,8 @@ BTRFS_SETGET_STACK_FUNCS(super_csum_type, struct btrfs_super_block,
 			 csum_type, 16);
 BTRFS_SETGET_STACK_FUNCS(super_cache_generation, struct btrfs_super_block,
 			 cache_generation, 64);
+BTRFS_SETGET_STACK_FUNCS(super_uuid_tree_generation, struct btrfs_super_block,
+			 uuid_tree_generation, 64);
 
 static inline int btrfs_super_csum_size(struct btrfs_super_block *s)
 {
Signature · 1 line
1.8.3
#5 lore raw

PATCH v4 5/5 Btrfs-progs: add option to btrfs-debug-tree to print uuid tree only

In reply to Stefan Behrens
---
 btrfs-debug-tree.c | 31 ++++++++++++++++++++++---------
 1 file changed, 22 insertions(+), 9 deletions(-)
btrfs-debug-tree.c +22 -9
index bae7f94..c0eae9b 100644
@@ -30,13 +30,14 @@
 
 static int print_usage(void)
 {
-	fprintf(stderr, "usage: btrfs-debug-tree [ -e ] [ -d ] [ -r ] [ -R ]\n");
+	fprintf(stderr, "usage: btrfs-debug-tree [-e] [-d] [-r] [-R] [-u]\n");
 	fprintf(stderr, "                        [-b block_num ] device\n");
 	fprintf(stderr, "\t-e : print detailed extents info\n");
 	fprintf(stderr, "\t-d : print info of btrfs device and root tree dirs"
                     " only\n");
 	fprintf(stderr, "\t-r : print info of roots only\n");
 	fprintf(stderr, "\t-R : print info of roots and root backups\n");
+	fprintf(stderr, "\t-u : print info of uuid tree only\n");
 	fprintf(stderr, "\t-b block_num : print info of the specified block"
                     " only\n");
 	fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION);
@@ -129,6 +130,7 @@ int main(int ac, char **av)
 	int slot;
 	int extent_only = 0;
 	int device_only = 0;
+	int uuid_tree_only = 0;
 	int roots_only = 0;
 	int root_backups = 0;
 	u64 block_only = 0;
@@ -138,7 +140,7 @@ int main(int ac, char **av)
 
 	while(1) {
 		int c;
-		c = getopt(ac, av, "deb:rR");
+		c = getopt(ac, av, "deb:rRu");
 		if (c < 0)
 			break;
 		switch(c) {
@@ -151,6 +153,9 @@ int main(int ac, char **av)
 			case 'r':
 				roots_only = 1;
 				break;
+			case 'u':
+				uuid_tree_only = 1;
+				break;
 			case 'R':
 				roots_only = 1;
 				root_backups = 1;
@@ -201,7 +206,7 @@ int main(int ac, char **av)
 		return 0;
 	}
 
-	if (!extent_only) {
+	if (!(extent_only || uuid_tree_only)) {
 		if (roots_only) {
 			printf("root tree: %llu level %d\n",
 			     (unsigned long long)info->tree_root->node->start,
@@ -250,7 +255,7 @@ again:
 		if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) {
 			unsigned long offset;
 			struct extent_buffer *buf;
-			int skip = extent_only | device_only;
+			int skip = extent_only | device_only | uuid_tree_only;
 
 			offset = btrfs_item_ptr_offset(leaf, slot);
 			read_extent_buffer(leaf, &ri, offset, sizeof(ri));
@@ -268,9 +273,9 @@ again:
 					printf("root");
 				break;
 			case BTRFS_EXTENT_TREE_OBJECTID:
-				if (!device_only)
+				if (!device_only && !uuid_tree_only)
 					skip = 0;
-				if (!extent_only && !device_only)
+				if (!skip)
 					printf("extent");
 				break;
 			case BTRFS_CHUNK_TREE_OBJECTID:
@@ -279,8 +284,10 @@ again:
 				}
 				break;
 			case BTRFS_DEV_TREE_OBJECTID:
-				skip = 0;
-				printf("device");
+				if (!uuid_tree_only)
+					skip = 0;
+				if (!skip)
+					printf("device");
 				break;
 			case BTRFS_FS_TREE_OBJECTID:
 				if (!skip) {
@@ -331,6 +338,12 @@ again:
 					printf("quota");
 				}
 				break;
+			case BTRFS_UUID_TREE_OBJECTID:
+				if (!extent_only && !device_only)
+					skip = 0;
+				if (!skip)
+					printf("uuid");
+				break;
 			case BTRFS_MULTIPLE_OBJECTIDS:
 				if (!skip) {
 					printf("multiple");
@@ -368,7 +381,7 @@ no_node:
 		goto again;
 	}
 
-	if (extent_only || device_only)
+	if (extent_only || device_only || uuid_tree_only)
 		return 0;
 
 	if (root_backups)
Signature · 1 line
1.8.3