Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f2984462 authored by Chris Mason's avatar Chris Mason
Browse files

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

parent 16432985
Loading
Loading
Loading
Loading
+113 −5
Original line number Diff line number Diff line
@@ -382,7 +382,7 @@ static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
	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 @@ struct btrfs_root *open_ctree(struct super_block *sb,
	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 @@ fail:
	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 @@ int write_ctree_super(struct btrfs_trans_handle *trans, struct btrfs_root
				     super->start, super->len);
	if (!btrfs_test_opt(root, NOBARRIER))
		blkdev_issue_flush(sb->s_bdev, NULL);
#endif
	return ret;
}

+1 −0
Original line number Diff line number Diff line
@@ -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;

+5 −3
Original line number Diff line number Diff line
@@ -125,6 +125,7 @@ static int device_list_add(const char *path,
			return -ENOMEM;
		}
		device->devid = devid;
		device->barriers = 1;
		device->name = kstrdup(path, GFP_NOFS);
		if (!device->name) {
			kfree(device);
@@ -208,6 +209,7 @@ int btrfs_scan_one_device(const char *path, int flags, void *holder,
	struct buffer_head *bh;
	int ret;
	u64 devid;
	u64 transid;

	mutex_lock(&uuid_mutex);

@@ -236,14 +238,14 @@ int btrfs_scan_one_device(const char *path, int flags, void *holder,
		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 @@ static int read_one_dev(struct btrfs_root *root,
	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,
+3 −0
Original line number Diff line number Diff line
@@ -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;