summaryrefslogtreecommitdiff
path: root/fs/btrfs
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2008-04-11 00:19:33 +0400
committerChris Mason <chris.mason@oracle.com>2008-09-25 19:04:01 +0400
commitf29844623de29a12358d7fba35d0959465b64adf (patch)
tree0268addb1a152cc1dbfdef1a50e32d6a7e97bdcf /fs/btrfs
parent16432985920f3c45af82da214e2498f3e2f9066b (diff)
downloadlinux-f29844623de29a12358d7fba35d0959465b64adf.tar.xz
Btrfs: Write out all super blocks on commit, and bring back proper barrier support
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/disk-io.c118
-rw-r--r--fs/btrfs/disk-io.h1
-rw-r--r--fs/btrfs/volumes.c8
-rw-r--r--fs/btrfs/volumes.h3
4 files changed, 122 insertions, 8 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 59bdf0474be3..cf1de75f088a 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -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;
}
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index 4fac0ccbf8f8..60b01902db79 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 3b927f698320..07d43553141c 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -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,
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 3d5d0a9cb827..89548837a1cc 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;