Skip to contentSkip to conversation

Btrfs-progs: speedup btrfs send/receive

PATCH Superseded Stefan Behrens 2 participants 7 messages May 16, 2013

Subsystems (2)

v3 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

v3 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).

Stefan Behrens (4):
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

 Makefile           |   5 +-
 btrfs-show-super.c |   2 +
 cmds-receive.c     |  23 ++-
 cmds-send.c        |  53 +++++-
 ctree.h            |  39 ++++-
 print-tree.c       |  95 ++++++++++-
 send-utils.c       | 477 +++++++++++++++++++++--------------------------------
 send-utils.h       |   9 +-
 uuid-tree.c        | 174 +++++++++++++++++++
 9 files changed, 562 insertions(+), 315 deletions(-)
 create mode 100644 uuid-tree.c
Signature · 1 line
1.8.2.3
#1 lore raw

PATCH v3 1/4 Btrfs-progs: Support UUID tree and UUID items in btrfs-debug-tree

Support printing these things.

---
 ctree.h      | 29 +++++++++++++++++++
 print-tree.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 121 insertions(+), 3 deletions(-)
ctree.h +29 -0
index 4ea37ac..0f14b41 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
@@ -811,6 +813,16 @@ struct btrfs_csum_item {
 	u8 csum;
 } __attribute__ ((__packed__));
 
+/* for items that use the BTRFS_UUID_KEY */
+#define BTRFS_UUID_ITEM_TYPE_SUBVOL	0 /* for UUIDs assigned to subvols */
+#define BTRFS_UUID_ITEM_TYPE_RECEIVED_SUBVOL	1 /* for UUIDs assigned to
+						   * received subvols */
+struct btrfs_uuid_item {
+	__le16 type;	/* refer to BTRFS_UUID_ITEM_TYPE* defines above */
+	__le32 len;	/* number of following 64bit values */
+	__le64 data[0];	/* data aligned to 64bit */
+} __attribute__ ((__packed__));
+
 /* tag for the radix tree of block groups in ram */
 #define BTRFS_BLOCK_GROUP_DATA		(1ULL << 0)
 #define BTRFS_BLOCK_GROUP_SYSTEM	(1ULL << 1)
@@ -1107,6 +1119,17 @@ 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	251
+
+/*
  * string items are for debugging.  They just store a short string of
  * data in the FS
  */
@@ -2046,6 +2069,12 @@ static inline u32 btrfs_file_extent_inline_item_len(struct extent_buffer *eb,
        return btrfs_item_size(eb, e) - offset;
 }
 
+/* btrfs_uuid_item */
+BTRFS_SETGET_FUNCS(uuid_type, struct btrfs_uuid_item, type, 16);
+BTRFS_SETGET_FUNCS(uuid_len, struct btrfs_uuid_item, len, 32);
+BTRFS_SETGET_STACK_FUNCS(stack_uuid_type, struct btrfs_uuid_item, type, 16);
+BTRFS_SETGET_STACK_FUNCS(stack_uuid_len, struct btrfs_uuid_item, len, 32);
+
 static inline u32 btrfs_level_size(struct btrfs_root *root, int level) {
 	if (level == 0)
 		return root->leafsize;
print-tree.c +92 -3

143 lines not on this page · raw message

Signature · 1 line
1.8.2.3
#2 lore raw

Re: [PATCH v3 1/4] Btrfs-progs: Support UUID tree and UUID items in btrfs-debug-tree

ctree.h struct btrfs_csum_item { · line 824

Quoted 5 lines · Stefan Behrens
ctree.h +5 -0
+struct btrfs_uuid_item {
+	__le16 type;	/* refer to BTRFS_UUID_ITEM_TYPE* defines above */
+	__le32 len;	/* number of following 64bit values */
+	__le64 data[0];	/* data aligned to 64bit */
+} __attribute__ ((__packed__));

With __packed__ (which is preferrably written as __packed) the data is not aligned to u64 as the comment says. Aligning u64's is a good thing, so (for example) pad the space after type (I don't think we need more than u16 here).

david

#3 lore raw

Re: [PATCH v3 1/4] Btrfs-progs: Support UUID tree and UUID items in btrfs-debug-tree

Quoted 11 lines · David Sterba

ctree.h struct btrfs_csum_item { · line 824

Quoted 5 lines · Stefan Behrens
ctree.h +5 -0
+struct btrfs_uuid_item {
+	__le16 type;	/* refer to BTRFS_UUID_ITEM_TYPE* defines above */
+	__le32 len;	/* number of following 64bit values */
+	__le64 data[0];	/* data aligned to 64bit */
+} __attribute__ ((__packed__));

With __packed__ (which is preferrably written as __packed) the data is not aligned to u64 as the comment says. Aligning u64's is a good thing, so (for example) pad the space after type (I don't think we need more than u16 here).

The on-disk format is in general not aligned (it is "packed") and stored on any byte-aligned position on the disk.

In the source code, you can use it for sizeof(), otherwise you use the access functions from ctree.h and struct-funcs.c.

I know that you know this already :) Maybe I am not understanding your review comment?

The "data" part in the btrfs_uuid_item (which represents the "value" of the (type, length, value) tripple) is a multiple of 64 bits, that's what I mean with the "data aligned to 64bit" comment.

#4 lore raw

PATCH v3 2/4 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 | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 182 insertions(+), 2 deletions(-)
Makefile +3 -2
index 7d4faba..66eefb7 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 0f14b41..e36cb17 100644
@@ -2355,4 +2355,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 +174 -0

174 lines not on this page · raw message

Signature · 1 line
1.8.2.3
#5 lore raw

PATCH v3 3/4 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.2.3
#6 lore raw

PATCH v3 4/4 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 e36cb17..cf068b1 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__));
@@ -1941,6 +1942,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.2.3