Btrfs: add a device id to device items

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 454eb88..ef3583c 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -91,10 +91,12 @@
 	__le64 total_blocks;
 	__le64 blocks_used;
 	__le64 root_dir_objectid;
+	__le64 last_device_id;
 	/* fields below here vary with the underlying disk */
 	__le64 device_block_start;
 	__le64 device_num_blocks;
 	__le64 device_root;
+	__le64 device_id;
 } __attribute__ ((__packed__));
 
 /*
@@ -230,6 +232,7 @@
 
 struct btrfs_device_item {
 	__le16 pathlen;
+	__le64 device_id;
 } __attribute__ ((__packed__));
 
 struct crypto_hash;
@@ -798,6 +801,28 @@
 	s->root_dir_objectid = cpu_to_le64(val);
 }
 
+static inline u64 btrfs_super_last_device_id(struct btrfs_super_block *s)
+{
+	return le64_to_cpu(s->last_device_id);
+}
+
+static inline void btrfs_set_super_last_device_id(struct btrfs_super_block *s,
+						  u64 val)
+{
+	s->last_device_id = cpu_to_le64(val);
+}
+
+static inline u64 btrfs_super_device_id(struct btrfs_super_block *s)
+{
+	return le64_to_cpu(s->device_id);
+}
+
+static inline void btrfs_set_super_device_id(struct btrfs_super_block *s,
+						  u64 val)
+{
+	s->device_id = cpu_to_le64(val);
+}
+
 static inline u64 btrfs_super_device_block_start(struct btrfs_super_block *s)
 {
 	return le64_to_cpu(s->device_block_start);
@@ -910,6 +935,17 @@
 	d->pathlen = cpu_to_le16(val);
 }
 
+static inline u64 btrfs_device_id(struct btrfs_device_item *d)
+{
+	return le64_to_cpu(d->device_id);
+}
+
+static inline void btrfs_set_device_id(struct btrfs_device_item *d,
+						u64 val)
+{
+	d->device_id = cpu_to_le64(val);
+}
+
 static inline struct btrfs_root *btrfs_sb(struct super_block *sb)
 {
 	return sb->s_fs_info;
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 3ba4df2..06b969c 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -13,11 +13,13 @@
 struct dev_lookup {
 	u64 block_start;
 	u64 num_blocks;
+	u64 device_id;
 	struct block_device *bdev;
 };
 
 int btrfs_insert_dev_radix(struct btrfs_root *root,
 			   struct block_device *bdev,
+			   u64 device_id,
 			   u64 block_start,
 			   u64 num_blocks)
 {
@@ -31,6 +33,7 @@
 	lookup->block_start = block_start;
 	lookup->num_blocks = num_blocks;
 	lookup->bdev = bdev;
+	lookup->device_id = device_id;
 printk("inserting %s into dev radix %Lu %Lu\n", bdevname(bdev, b), block_start, num_blocks);
 
 	ret = radix_tree_insert(&root->fs_info->dev_radix, block_start +
@@ -418,17 +421,14 @@
 	return root;
 }
 
-int btrfs_open_disk(struct btrfs_root *root, u64 block_start, u64 num_blocks,
-		    char *filename, int name_len)
+static int btrfs_open_disk(struct btrfs_root *root, u64 device_id,
+			   u64 block_start, u64 num_blocks,
+			   char *filename, int name_len)
 {
 	char *null_filename;
 	struct block_device *bdev;
 	int ret;
 
-	if (block_start == 0) {
-printk("skipping disk with block_start == 0\n");
-return 0;
-	}
 	null_filename = kmalloc(name_len + 1, GFP_NOFS);
 	if (!null_filename)
 		return -ENOMEM;
@@ -441,7 +441,8 @@
 		goto out;
 	}
 	set_blocksize(bdev, root->fs_info->sb->s_blocksize);
-	ret = btrfs_insert_dev_radix(root, bdev, block_start, num_blocks);
+	ret = btrfs_insert_dev_radix(root, bdev, device_id,
+				     block_start, num_blocks);
 	BUG_ON(ret);
 	ret = 0;
 out:
@@ -490,10 +491,14 @@
 		}
 		dev_item = btrfs_item_ptr(leaf, slot, struct btrfs_device_item);
 printk("found key %Lu %Lu\n", key.objectid, key.offset);
-		ret = btrfs_open_disk(root, key.objectid, key.offset,
-				      (char *)(dev_item + 1),
-				      btrfs_device_pathlen(dev_item));
-		BUG_ON(ret);
+		if (btrfs_device_id(dev_item) !=
+		    btrfs_super_device_id(root->fs_info->disk_super)) {
+			ret = btrfs_open_disk(root, btrfs_device_id(dev_item),
+					      key.objectid, key.offset,
+					      (char *)(dev_item + 1),
+					      btrfs_device_pathlen(dev_item));
+			BUG_ON(ret);
+		}
 		path->slots[0]++;
 	}
 	btrfs_free_path(path);
@@ -556,6 +561,7 @@
 	dev_lookup->block_start = 0;
 	dev_lookup->num_blocks = (u32)-2;
 	dev_lookup->bdev = sb->s_bdev;
+	dev_lookup->device_id = 0;
 	ret = radix_tree_insert(&fs_info->dev_radix, (u32)-2, dev_lookup);
 	BUG_ON(ret);
 	fs_info->sb_buffer = read_tree_block(tree_root,
@@ -575,6 +581,8 @@
 	radix_tree_delete(&fs_info->dev_radix, (u32)-2);
 	dev_lookup->block_start = btrfs_super_device_block_start(disk_super);
 	dev_lookup->num_blocks = btrfs_super_device_num_blocks(disk_super);
+	dev_lookup->device_id = btrfs_super_device_id(disk_super);
+
 	ret = radix_tree_insert(&fs_info->dev_radix,
 				dev_lookup->block_start +
 				dev_lookup->num_blocks - 1, dev_lookup);
@@ -659,6 +667,7 @@
 	}
 	return 0;
 }
+
 static int free_dev_radix(struct btrfs_fs_info *fs_info)
 {
 	struct dev_lookup *lookup[8];
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index aa9f56c..444ebb0 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -44,6 +44,7 @@
 u64 bh_blocknr(struct buffer_head *bh);
 int btrfs_insert_dev_radix(struct btrfs_root *root,
 			   struct block_device *bdev,
+			   u64 device_id,
 			   u64 block_start,
 			   u64 num_blocks);
 int btrfs_map_bh_to_logical(struct btrfs_root *root, struct buffer_head *bh,
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index c46d7ea..8dcf600 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1840,7 +1840,9 @@
 	u16 item_size;
 	u64 num_blocks;
 	u64 new_blocks;
+	u64 device_id;
 	int ret;
+
 printk("adding disk %s\n", name);
 	path = btrfs_alloc_path();
 	if (!path)
@@ -1875,9 +1877,14 @@
 				  path->slots[0], struct btrfs_device_item);
 	btrfs_set_device_pathlen(dev_item, namelen);
 	memcpy(dev_item + 1, name, namelen);
+
+	device_id = btrfs_super_last_device_id(root->fs_info->disk_super) + 1;
+	btrfs_set_super_last_device_id(root->fs_info->disk_super, device_id);
+	btrfs_set_device_id(dev_item, device_id);
 	mark_buffer_dirty(path->nodes[0]);
 
-	ret = btrfs_insert_dev_radix(root, bdev, num_blocks, new_blocks);
+	ret = btrfs_insert_dev_radix(root, bdev, device_id, num_blocks,
+				     new_blocks);
 
 	if (!ret) {
 		btrfs_set_super_total_blocks(root->fs_info->disk_super,