From 1a5295302d4c9bddf90c545228d5a6645b7e4fe1 Mon Sep 17 00:00:00 2001
From: Martin Krizek <martin.krizek@gmail.com>
Date: Tue, 21 May 2013 17:57:16 -0400
Subject: [PATCH] btrfs-progs: Add online deduplication

This patch adds the dedup tree support for btrfs-progs.
---
 btrfs-convert.c   | 11 ++++++++---
 btrfs-find-root.c |  3 +++
 ctree.h           |  2 ++
 disk-io.c         | 19 +++++++++++++++++++
 ioctl.h           |  1 +
 mkfs.c            |  9 +++++++--
 utils.c           | 33 ++++++++++++++++++++++++++++-----
 7 files changed, 68 insertions(+), 10 deletions(-)

diff --git a/btrfs-convert.c b/btrfs-convert.c
index 399856f..1fdd59b 100644
--- a/btrfs-convert.c
+++ b/btrfs-convert.c
@@ -1717,6 +1717,11 @@ static int init_btrfs(struct btrfs_root *root)
 	BUG_ON(ret);
 	free_extent_buffer(tmp);
 
+	ret = __btrfs_cow_block(trans, fs_info->dedup_root,
+				fs_info->dedup_root->node, NULL, 0, &tmp, 0, 0);
+	BUG_ON(ret);
+	free_extent_buffer(tmp);
+
 	ret = btrfs_commit_transaction(trans, root);
 	BUG_ON(ret);
 err:
@@ -2282,7 +2287,7 @@ int do_convert(const char *devname, int datacsum, int packing, int noxattr)
 	int i, ret;
 	int fd = -1;
 	u32 blocksize;
-	u64 blocks[7];
+	u64 blocks[8];
 	u64 total_bytes;
 	u64 super_bytenr;
 	ext2_filsys ext2_fs;
@@ -2305,7 +2310,7 @@ int do_convert(const char *devname, int datacsum, int packing, int noxattr)
 		fprintf(stderr, "filetype feature is missing\n");
 		goto fail;
 	}
-	for (i = 0; i < 7; i++) {
+	for (i = 0; i < 8; i++) {
 		ret = ext2_alloc_block(ext2_fs, 0, blocks + i);
 		if (ret) {
 			fprintf(stderr, "not enough free space\n");
@@ -2344,7 +2349,7 @@ int do_convert(const char *devname, int datacsum, int packing, int noxattr)
 	}
 	root->fs_info->extent_ops = &extent_ops;
 	/* recover block allocation bitmap */
-	for (i = 0; i < 7; i++) {
+	for (i = 0; i < 8; i++) {
 		blocks[i] /= blocksize;
 		ext2_free_block(ext2_fs, blocks[i]);
 	}
diff --git a/btrfs-find-root.c b/btrfs-find-root.c
index 810d835..c3a4c78 100644
--- a/btrfs-find-root.c
+++ b/btrfs-find-root.c
@@ -94,6 +94,7 @@ static struct btrfs_root *open_ctree_broken(int fd, const char *device)
 	struct btrfs_root *chunk_root = malloc(sizeof(struct btrfs_root));
 	struct btrfs_root *dev_root = malloc(sizeof(struct btrfs_root));
 	struct btrfs_root *csum_root = malloc(sizeof(struct btrfs_root));
+	struct btrfs_root *dedup_root = malloc(sizeof(struct btrfs_root));
 	struct btrfs_fs_info *fs_info = malloc(sizeof(*fs_info));
 	int ret;
 	struct btrfs_super_block *disk_super;
@@ -122,6 +123,7 @@ static struct btrfs_root *open_ctree_broken(int fd, const char *device)
 	fs_info->chunk_root = chunk_root;
 	fs_info->dev_root = dev_root;
 	fs_info->csum_root = csum_root;
+	fs_info->dedup_root = dedup_root;
 
 	fs_info->readonly = 1;
 
@@ -228,6 +230,7 @@ out:
 	free(chunk_root);
 	free(dev_root);
 	free(csum_root);
+	free(dedup_root);
 	free(fs_info);
 	return NULL;
 }
diff --git a/ctree.h b/ctree.h
index 4ea37ac..4151d62 100644
--- a/ctree.h
+++ b/ctree.h
@@ -70,6 +70,7 @@ struct btrfs_free_space_ctl;
 /* holds checksums of all the data extents */
 #define BTRFS_CSUM_TREE_OBJECTID 7ULL
 #define BTRFS_QUOTA_TREE_OBJECTID 8ULL
+#define BTRFS_DEDUP_TREE_OBJECTID 9ULL
 
 
 /* for storing balance parameters in the root tree */
@@ -908,6 +909,7 @@ struct btrfs_fs_info {
 	struct btrfs_root *chunk_root;
 	struct btrfs_root *dev_root;
 	struct btrfs_root *csum_root;
+	struct btrfs_root *dedup_root;
 
 	struct cache_tree fs_root_cache;
 
diff --git a/disk-io.c b/disk-io.c
index 21b410d..88174b0 100644
--- a/disk-io.c
+++ b/disk-io.c
@@ -774,6 +774,8 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
 		return fs_info->dev_root;
 	if (location->objectid == BTRFS_CSUM_TREE_OBJECTID)
 		return fs_info->csum_root;
+	if (location->objectid == BTRFS_DEDUP_TREE_OBJECTID)
+		return fs_info->dedup_root;
 
 	BUG_ON(location->objectid == BTRFS_TREE_RELOC_OBJECTID ||
 	       location->offset != (u64)-1);
@@ -812,6 +814,7 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
 	struct btrfs_root *chunk_root = malloc(sizeof(struct btrfs_root));
 	struct btrfs_root *dev_root = malloc(sizeof(struct btrfs_root));
 	struct btrfs_root *csum_root = malloc(sizeof(struct btrfs_root));
+	struct btrfs_root *dedup_root = malloc(sizeof(struct btrfs_root));
 	struct btrfs_fs_info *fs_info = malloc(sizeof(*fs_info));
 	int ret;
 	struct btrfs_super_block *disk_super;
@@ -847,6 +850,7 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
 	fs_info->chunk_root = chunk_root;
 	fs_info->dev_root = dev_root;
 	fs_info->csum_root = csum_root;
+	fs_info->dedup_root = dedup_root;
 
 	if (!writes)
 		fs_info->readonly = 1;
@@ -989,6 +993,15 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
 	}
 	csum_root->track_dirty = 1;
 
+	ret = find_and_setup_root(tree_root, fs_info,
+				  BTRFS_DEDUP_TREE_OBJECTID, dedup_root);
+	if (ret) {
+		printk("Couldn't setup dedup tree\n");
+		if (!partial)
+			goto out_failed;
+	}
+	dedup_root->track_dirty = 1;
+
 	find_and_setup_log_root(tree_root, fs_info, disk_super);
 
 	fs_info->generation = generation;
@@ -1015,6 +1028,8 @@ out_failed:
 
 	if (fs_info->csum_root)
 		free_extent_buffer(fs_info->csum_root->node);
+	if (fs_info->dedup_root)
+		free_extent_buffer(fs_info->dedup_root->node);
 	if (fs_info->dev_root)
 		free_extent_buffer(fs_info->dev_root->node);
 	if (fs_info->extent_root)
@@ -1039,6 +1054,7 @@ out:
 	free(chunk_root);
 	free(dev_root);
 	free(csum_root);
+	free(dedup_root);
 	free(fs_info);
 	return NULL;
 }
@@ -1326,6 +1342,8 @@ int close_ctree(struct btrfs_root *root)
 		free_extent_buffer(fs_info->dev_root->node);
 	if (fs_info->csum_root->node)
 		free_extent_buffer(fs_info->csum_root->node);
+	if (fs_info->dedup_root->node)
+		free_extent_buffer(fs_info->dedup_root->node);
 
 	if (fs_info->log_root_tree) {
 		if (fs_info->log_root_tree->node)
@@ -1347,6 +1365,7 @@ int close_ctree(struct btrfs_root *root)
 	free(fs_info->chunk_root);
 	free(fs_info->dev_root);
 	free(fs_info->csum_root);
+	free(fs_info->dedup_root);
 	free(fs_info);
 
 	return 0;
diff --git a/ioctl.h b/ioctl.h
index abe6dd4..9548814 100644
--- a/ioctl.h
+++ b/ioctl.h
@@ -537,6 +537,7 @@ struct btrfs_ioctl_clone_range_args {
 				      struct btrfs_ioctl_get_dev_stats)
 #define BTRFS_IOC_DEV_REPLACE _IOWR(BTRFS_IOCTL_MAGIC, 53, \
 				    struct btrfs_ioctl_dev_replace_args)
+#define BTRFS_IOC_DEDUP_SYNC _IO(BTRFS_IOCTL_MAGIC, 54)
 
 #ifdef __cplusplus
 }
diff --git a/mkfs.c b/mkfs.c
index 7ff60e5..43b9ff4 100644
--- a/mkfs.c
+++ b/mkfs.c
@@ -183,6 +183,11 @@ static int recow_roots(struct btrfs_trans_handle *trans,
 	BUG_ON(ret);
 	free_extent_buffer(tmp);
 
+	ret = __btrfs_cow_block(trans, info->dedup_root, info->dedup_root->node,
+				NULL, 0, &tmp, 0, 0);
+	BUG_ON(ret);
+	free_extent_buffer(tmp);
+
 	return 0;
 }
 
@@ -1262,7 +1267,7 @@ int main(int ac, char **av)
 	char *first_file;
 	u64 block_count = 0;
 	u64 dev_block_count = 0;
-	u64 blocks[7];
+	u64 blocks[8];
 	u64 alloc_start = 0;
 	u64 metadata_profile = 0;
 	u64 data_profile = 0;
@@ -1437,7 +1442,7 @@ int main(int ac, char **av)
 	}
 
 	blocks[0] = BTRFS_SUPER_INFO_OFFSET;
-	for (i = 1; i < 7; i++) {
+	for (i = 1; i < 8; i++) {
 		blocks[i] = BTRFS_SUPER_INFO_OFFSET + 1024 * 1024 +
 			leafsize * i;
 	}
diff --git a/utils.c b/utils.c
index 7b4cd74..a372208 100644
--- a/utils.c
+++ b/utils.c
@@ -77,10 +77,11 @@ static u64 reference_root_table[] = {
 	[4] =	BTRFS_DEV_TREE_OBJECTID,
 	[5] =	BTRFS_FS_TREE_OBJECTID,
 	[6] =	BTRFS_CSUM_TREE_OBJECTID,
+	[7] =	BTRFS_DEDUP_TREE_OBJECTID,
 };
 
 int make_btrfs(int fd, const char *device, const char *label,
-	       u64 blocks[7], u64 num_bytes, u32 nodesize,
+	       u64 blocks[8], u64 num_bytes, u32 nodesize,
 	       u32 leafsize, u32 sectorsize, u32 stripesize)
 {
 	struct btrfs_super_block super;
@@ -120,7 +121,7 @@ int make_btrfs(int fd, const char *device, const char *label,
 	btrfs_set_super_root(&super, blocks[1]);
 	btrfs_set_super_chunk_root(&super, blocks[3]);
 	btrfs_set_super_total_bytes(&super, num_bytes);
-	btrfs_set_super_bytes_used(&super, 6 * leafsize);
+	btrfs_set_super_bytes_used(&super, 7 * leafsize);
 	btrfs_set_super_sectorsize(&super, sectorsize);
 	btrfs_set_super_leafsize(&super, leafsize);
 	btrfs_set_super_nodesize(&super, nodesize);
@@ -137,7 +138,7 @@ int make_btrfs(int fd, const char *device, const char *label,
 	memset(buf->data, 0, leafsize);
 	buf->len = leafsize;
 	btrfs_set_header_bytenr(buf, blocks[1]);
-	btrfs_set_header_nritems(buf, 4);
+	btrfs_set_header_nritems(buf, 5);
 	btrfs_set_header_generation(buf, 1);
 	btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV);
 	btrfs_set_header_owner(buf, BTRFS_ROOT_TREE_OBJECTID);
@@ -212,6 +213,18 @@ int make_btrfs(int fd, const char *device, const char *label,
 			    sizeof(root_item));
 	nritems++;
 
+	itemoff = itemoff - sizeof(root_item);
+	btrfs_set_root_bytenr(&root_item, blocks[7]);
+	btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEDUP_TREE_OBJECTID);
+	btrfs_set_item_key(buf, &disk_key, nritems);
+	btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff);
+	btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems),
+			    sizeof(root_item));
+	write_extent_buffer(buf, &root_item,
+			    btrfs_item_ptr_offset(buf, nritems),
+			    sizeof(root_item));
+	nritems++;
+
 
 	csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
 	ret = pwrite(fd, buf->data, leafsize, blocks[1]);
@@ -222,7 +235,7 @@ int make_btrfs(int fd, const char *device, const char *label,
 		leafsize-sizeof(struct btrfs_header));
 	nritems = 0;
 	itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize);
-	for (i = 1; i < 7; i++) {
+	for (i = 1; i < 8; i++) {
 		BUG_ON(blocks[i] < first_free);
 		BUG_ON(blocks[i] < blocks[i - 1]);
 
@@ -392,7 +405,7 @@ int make_btrfs(int fd, const char *device, const char *label,
 	ret = pwrite(fd, buf->data, leafsize, blocks[5]);
 	BUG_ON(ret != leafsize);
 
-	/* finally create the csum root */
+	/* create the csum root */
 	memset(buf->data+sizeof(struct btrfs_header), 0,
 		leafsize-sizeof(struct btrfs_header));
 	btrfs_set_header_bytenr(buf, blocks[6]);
@@ -402,6 +415,16 @@ int make_btrfs(int fd, const char *device, const char *label,
 	ret = pwrite(fd, buf->data, leafsize, blocks[6]);
 	BUG_ON(ret != leafsize);
 
+	/* finally create the dedup root */
+	memset(buf->data+sizeof(struct btrfs_header), 0,
+		leafsize-sizeof(struct btrfs_header));
+	btrfs_set_header_bytenr(buf, blocks[7]);
+	btrfs_set_header_owner(buf, BTRFS_DEDUP_TREE_OBJECTID);
+	btrfs_set_header_nritems(buf, 0);
+	csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+	ret = pwrite(fd, buf->data, leafsize, blocks[7]);
+	BUG_ON(ret != leafsize);
+
 	/* and write out the super block */
 	BUG_ON(sizeof(super) > sectorsize);
 	memset(buf->data, 0, sectorsize);
-- 
1.8.1.4

