Btrfs: Write out all super blocks on commit, and bring back proper barrier support

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 59bdf04..cf1de75 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -382,7 +382,7 @@
 	BUG_ON(ret);
 
 	if (offset == BTRFS_SUPER_INFO_OFFSET) {
-		bio->bi_bdev = root->fs_info->sb->s_bdev;
+		bio->bi_bdev = root->fs_info->fs_devices->latest_bdev;
 		submit_bio(rw, bio);
 		return 0;
 	}
@@ -988,7 +988,7 @@
 	spin_lock_init(&fs_info->new_trans_lock);
 
 	init_completion(&fs_info->kobj_unregister);
-	sb_set_blocksize(sb, 4096);
+	sb_set_blocksize(sb, BTRFS_SUPER_INFO_SIZE);
 	fs_info->tree_root = tree_root;
 	fs_info->extent_root = extent_root;
 	fs_info->chunk_root = chunk_root;
@@ -1169,14 +1169,121 @@
 	return ERR_PTR(err);
 }
 
+static void btrfs_end_buffer_write_sync(struct buffer_head *bh, int uptodate)
+{
+	char b[BDEVNAME_SIZE];
+
+	if (uptodate) {
+		set_buffer_uptodate(bh);
+	} else {
+		if (!buffer_eopnotsupp(bh) && printk_ratelimit()) {
+			printk(KERN_WARNING "lost page write due to "
+					"I/O error on %s\n",
+				       bdevname(bh->b_bdev, b));
+		}
+		set_buffer_write_io_error(bh);
+		clear_buffer_uptodate(bh);
+	}
+	unlock_buffer(bh);
+	put_bh(bh);
+}
+
+int write_all_supers(struct btrfs_root *root)
+{
+	struct list_head *cur;
+	struct list_head *head = &root->fs_info->fs_devices->devices;
+	struct btrfs_device *dev;
+	struct extent_buffer *sb;
+	struct btrfs_dev_item *dev_item;
+	struct buffer_head *bh;
+	int ret;
+	int do_barriers;
+
+	do_barriers = !btrfs_test_opt(root, NOBARRIER);
+
+	sb = root->fs_info->sb_buffer;
+	dev_item = (struct btrfs_dev_item *)offsetof(struct btrfs_super_block,
+						      dev_item);
+	list_for_each(cur, head) {
+		dev = list_entry(cur, struct btrfs_device, dev_list);
+		btrfs_set_device_type(sb, dev_item, dev->type);
+		btrfs_set_device_id(sb, dev_item, dev->devid);
+		btrfs_set_device_total_bytes(sb, dev_item, dev->total_bytes);
+		btrfs_set_device_bytes_used(sb, dev_item, dev->bytes_used);
+		btrfs_set_device_io_align(sb, dev_item, dev->io_align);
+		btrfs_set_device_io_width(sb, dev_item, dev->io_width);
+		btrfs_set_device_sector_size(sb, dev_item, dev->sector_size);
+		write_extent_buffer(sb, dev->uuid,
+				    (unsigned long)btrfs_device_uuid(dev_item),
+				    BTRFS_DEV_UUID_SIZE);
+
+		btrfs_set_header_flag(sb, BTRFS_HEADER_FLAG_WRITTEN);
+		csum_tree_block(root, sb, 0);
+
+		bh = __getblk(dev->bdev, BTRFS_SUPER_INFO_OFFSET /
+			      root->fs_info->sb->s_blocksize,
+			      BTRFS_SUPER_INFO_SIZE);
+
+		read_extent_buffer(sb, bh->b_data, 0, BTRFS_SUPER_INFO_SIZE);
+		dev->pending_io = bh;
+
+		get_bh(bh);
+		set_buffer_uptodate(bh);
+		lock_buffer(bh);
+		bh->b_end_io = btrfs_end_buffer_write_sync;
+
+		if (do_barriers && dev->barriers) {
+			ret = submit_bh(WRITE_BARRIER, bh);
+			if (ret == -EOPNOTSUPP) {
+				printk("btrfs: disabling barriers on dev %s\n",
+				       dev->name);
+				set_buffer_uptodate(bh);
+				dev->barriers = 0;
+				get_bh(bh);
+				lock_buffer(bh);
+				ret = submit_bh(WRITE, bh);
+			}
+		} else {
+			ret = submit_bh(WRITE, bh);
+		}
+		BUG_ON(ret);
+	}
+
+	list_for_each(cur, head) {
+		dev = list_entry(cur, struct btrfs_device, dev_list);
+		BUG_ON(!dev->pending_io);
+		bh = dev->pending_io;
+		wait_on_buffer(bh);
+		if (!buffer_uptodate(dev->pending_io)) {
+			if (do_barriers && dev->barriers) {
+				printk("btrfs: disabling barriers on dev %s\n",
+				       dev->name);
+				set_buffer_uptodate(bh);
+				get_bh(bh);
+				lock_buffer(bh);
+				dev->barriers = 0;
+				ret = submit_bh(WRITE, bh);
+				BUG_ON(ret);
+				wait_on_buffer(bh);
+				BUG_ON(!buffer_uptodate(bh));
+			} else {
+				BUG();
+			}
+
+		}
+		dev->pending_io = NULL;
+		brelse(bh);
+	}
+	return 0;
+}
+
 int write_ctree_super(struct btrfs_trans_handle *trans, struct btrfs_root
 		      *root)
 {
 	int ret;
-	struct extent_buffer *super = root->fs_info->sb_buffer;
-	struct inode *btree_inode = root->fs_info->btree_inode;
-	struct super_block *sb = root->fs_info->sb;
 
+	ret = write_all_supers(root);
+#if 0
 	if (!btrfs_test_opt(root, NOBARRIER))
 		blkdev_issue_flush(sb->s_bdev, NULL);
 	set_extent_buffer_dirty(&BTRFS_I(btree_inode)->io_tree, super);
@@ -1184,6 +1291,7 @@
 				     super->start, super->len);
 	if (!btrfs_test_opt(root, NOBARRIER))
 		blkdev_issue_flush(sb->s_bdev, NULL);
+#endif
 	return ret;
 }
 
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index 4fac0cc..60b0190 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -20,6 +20,7 @@
 #define __DISKIO__
 
 #define BTRFS_SUPER_INFO_OFFSET (16 * 1024)
+#define BTRFS_SUPER_INFO_SIZE 4096
 struct btrfs_device;
 struct btrfs_fs_devices;
 
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 3b927f6..07d4355 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -125,6 +125,7 @@
 			return -ENOMEM;
 		}
 		device->devid = devid;
+		device->barriers = 1;
 		device->name = kstrdup(path, GFP_NOFS);
 		if (!device->name) {
 			kfree(device);
@@ -208,6 +209,7 @@
 	struct buffer_head *bh;
 	int ret;
 	u64 devid;
+	u64 transid;
 
 	mutex_lock(&uuid_mutex);
 
@@ -236,14 +238,14 @@
 		goto error_brelse;
 	}
 	devid = le64_to_cpu(disk_super->dev_item.devid);
-	printk("found device %Lu on %s\n", devid, path);
+	transid = btrfs_super_generation(disk_super);
+	printk("found device %Lu transid %Lu on %s\n", devid, transid, path);
 	ret = device_list_add(path, disk_super, devid, fs_devices_ret);
 
 error_brelse:
 	brelse(bh);
 error_close:
 	close_bdev_excl(bdev);
-	printk("scan one closes bdev %s\n", path);
 error:
 	mutex_unlock(&uuid_mutex);
 	return ret;
@@ -1143,7 +1145,7 @@
 	device = btrfs_find_device(root, devid);
 	if (!device) {
 		printk("warning devid %Lu not found already\n", devid);
-		device = kmalloc(sizeof(*device), GFP_NOFS);
+		device = kzalloc(sizeof(*device), GFP_NOFS);
 		if (!device)
 			return -ENOMEM;
 		list_add(&device->dev_list,
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 3d5d0a9..8954883 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -21,9 +21,12 @@
 
 #include <linux/bio.h>
 
+struct buffer_head;
 struct btrfs_device {
 	struct list_head dev_list;
 	struct btrfs_root *dev_root;
+	struct buffer_head *pending_io;
+	int barriers;
 	spinlock_t io_lock;
 
 	struct block_device *bdev;