Btrfs: Write out all super blocks on commit, and bring back proper barrier support
Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
parent
1643298592
commit
f29844623d
|
@ -382,7 +382,7 @@ static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
|
||||||
BUG_ON(ret);
|
BUG_ON(ret);
|
||||||
|
|
||||||
if (offset == BTRFS_SUPER_INFO_OFFSET) {
|
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);
|
submit_bio(rw, bio);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -988,7 +988,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
|
||||||
spin_lock_init(&fs_info->new_trans_lock);
|
spin_lock_init(&fs_info->new_trans_lock);
|
||||||
|
|
||||||
init_completion(&fs_info->kobj_unregister);
|
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->tree_root = tree_root;
|
||||||
fs_info->extent_root = extent_root;
|
fs_info->extent_root = extent_root;
|
||||||
fs_info->chunk_root = chunk_root;
|
fs_info->chunk_root = chunk_root;
|
||||||
|
@ -1169,14 +1169,121 @@ fail:
|
||||||
return ERR_PTR(err);
|
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
|
int write_ctree_super(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||||
*root)
|
*root)
|
||||||
{
|
{
|
||||||
int ret;
|
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))
|
if (!btrfs_test_opt(root, NOBARRIER))
|
||||||
blkdev_issue_flush(sb->s_bdev, NULL);
|
blkdev_issue_flush(sb->s_bdev, NULL);
|
||||||
set_extent_buffer_dirty(&BTRFS_I(btree_inode)->io_tree, super);
|
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);
|
super->start, super->len);
|
||||||
if (!btrfs_test_opt(root, NOBARRIER))
|
if (!btrfs_test_opt(root, NOBARRIER))
|
||||||
blkdev_issue_flush(sb->s_bdev, NULL);
|
blkdev_issue_flush(sb->s_bdev, NULL);
|
||||||
|
#endif
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#define __DISKIO__
|
#define __DISKIO__
|
||||||
|
|
||||||
#define BTRFS_SUPER_INFO_OFFSET (16 * 1024)
|
#define BTRFS_SUPER_INFO_OFFSET (16 * 1024)
|
||||||
|
#define BTRFS_SUPER_INFO_SIZE 4096
|
||||||
struct btrfs_device;
|
struct btrfs_device;
|
||||||
struct btrfs_fs_devices;
|
struct btrfs_fs_devices;
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,7 @@ static int device_list_add(const char *path,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
device->devid = devid;
|
device->devid = devid;
|
||||||
|
device->barriers = 1;
|
||||||
device->name = kstrdup(path, GFP_NOFS);
|
device->name = kstrdup(path, GFP_NOFS);
|
||||||
if (!device->name) {
|
if (!device->name) {
|
||||||
kfree(device);
|
kfree(device);
|
||||||
|
@ -208,6 +209,7 @@ int btrfs_scan_one_device(const char *path, int flags, void *holder,
|
||||||
struct buffer_head *bh;
|
struct buffer_head *bh;
|
||||||
int ret;
|
int ret;
|
||||||
u64 devid;
|
u64 devid;
|
||||||
|
u64 transid;
|
||||||
|
|
||||||
mutex_lock(&uuid_mutex);
|
mutex_lock(&uuid_mutex);
|
||||||
|
|
||||||
|
@ -236,14 +238,14 @@ int btrfs_scan_one_device(const char *path, int flags, void *holder,
|
||||||
goto error_brelse;
|
goto error_brelse;
|
||||||
}
|
}
|
||||||
devid = le64_to_cpu(disk_super->dev_item.devid);
|
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);
|
ret = device_list_add(path, disk_super, devid, fs_devices_ret);
|
||||||
|
|
||||||
error_brelse:
|
error_brelse:
|
||||||
brelse(bh);
|
brelse(bh);
|
||||||
error_close:
|
error_close:
|
||||||
close_bdev_excl(bdev);
|
close_bdev_excl(bdev);
|
||||||
printk("scan one closes bdev %s\n", path);
|
|
||||||
error:
|
error:
|
||||||
mutex_unlock(&uuid_mutex);
|
mutex_unlock(&uuid_mutex);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1143,7 +1145,7 @@ static int read_one_dev(struct btrfs_root *root,
|
||||||
device = btrfs_find_device(root, devid);
|
device = btrfs_find_device(root, devid);
|
||||||
if (!device) {
|
if (!device) {
|
||||||
printk("warning devid %Lu not found already\n", devid);
|
printk("warning devid %Lu not found already\n", devid);
|
||||||
device = kmalloc(sizeof(*device), GFP_NOFS);
|
device = kzalloc(sizeof(*device), GFP_NOFS);
|
||||||
if (!device)
|
if (!device)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
list_add(&device->dev_list,
|
list_add(&device->dev_list,
|
||||||
|
|
|
@ -21,9 +21,12 @@
|
||||||
|
|
||||||
#include <linux/bio.h>
|
#include <linux/bio.h>
|
||||||
|
|
||||||
|
struct buffer_head;
|
||||||
struct btrfs_device {
|
struct btrfs_device {
|
||||||
struct list_head dev_list;
|
struct list_head dev_list;
|
||||||
struct btrfs_root *dev_root;
|
struct btrfs_root *dev_root;
|
||||||
|
struct buffer_head *pending_io;
|
||||||
|
int barriers;
|
||||||
spinlock_t io_lock;
|
spinlock_t io_lock;
|
||||||
|
|
||||||
struct block_device *bdev;
|
struct block_device *bdev;
|
||||||
|
|
Loading…
Reference in a new issue