Commit cd02dca5 authored by Chris Mason's avatar Chris Mason

Btrfs: account for missing devices in RAID allocation profiles

When we mount in RAID degraded mode without adding a new device to
replace the failed one, we can end up using the wrong RAID flags for
allocations.

This results in strange combinations of block groups (raid1 in a raid10
filesystem) and corruptions when we try to allocate blocks from single
spindle chunks on drives that are actually missing.

The first device has two small 4MB chunks in it that mkfs creates and
these are usually unused in a raid1 or raid10 setup.  But, in -o degraded,
the allocator will fall back to these because the mask of desired raid groups
isn't correct.

The fix here is to count the missing devices as we build up the list
of devices in the system.  This count is used when picking the
raid level to make sure we continue using the same levels that were
in place before we lost a drive.
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 68433b73
...@@ -3044,7 +3044,13 @@ static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags) ...@@ -3044,7 +3044,13 @@ static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags)
u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags) u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags)
{ {
u64 num_devices = root->fs_info->fs_devices->rw_devices; /*
* we add in the count of missing devices because we want
* to make sure that any RAID levels on a degraded FS
* continue to be honored.
*/
u64 num_devices = root->fs_info->fs_devices->rw_devices +
root->fs_info->fs_devices->missing_devices;
if (num_devices == 1) if (num_devices == 1)
flags &= ~(BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID0); flags &= ~(BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID0);
...@@ -7891,7 +7897,14 @@ static u64 update_block_group_flags(struct btrfs_root *root, u64 flags) ...@@ -7891,7 +7897,14 @@ static u64 update_block_group_flags(struct btrfs_root *root, u64 flags)
u64 stripped = BTRFS_BLOCK_GROUP_RAID0 | u64 stripped = BTRFS_BLOCK_GROUP_RAID0 |
BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID10; BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID10;
num_devices = root->fs_info->fs_devices->rw_devices; /*
* we add in the count of missing devices because we want
* to make sure that any RAID levels on a degraded FS
* continue to be honored.
*/
num_devices = root->fs_info->fs_devices->rw_devices +
root->fs_info->fs_devices->missing_devices;
if (num_devices == 1) { if (num_devices == 1) {
stripped |= BTRFS_BLOCK_GROUP_DUP; stripped |= BTRFS_BLOCK_GROUP_DUP;
stripped = flags & ~stripped; stripped = flags & ~stripped;
......
...@@ -413,12 +413,16 @@ static noinline int device_list_add(const char *path, ...@@ -413,12 +413,16 @@ static noinline int device_list_add(const char *path,
device->fs_devices = fs_devices; device->fs_devices = fs_devices;
fs_devices->num_devices++; fs_devices->num_devices++;
} else if (strcmp(device->name, path)) { } else if (!device->name || strcmp(device->name, path)) {
name = kstrdup(path, GFP_NOFS); name = kstrdup(path, GFP_NOFS);
if (!name) if (!name)
return -ENOMEM; return -ENOMEM;
kfree(device->name); kfree(device->name);
device->name = name; device->name = name;
if (device->missing) {
fs_devices->missing_devices--;
device->missing = 0;
}
} }
if (found_transid > fs_devices->latest_trans) { if (found_transid > fs_devices->latest_trans) {
...@@ -1238,6 +1242,9 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) ...@@ -1238,6 +1242,9 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
device->fs_devices->num_devices--; device->fs_devices->num_devices--;
if (device->missing)
root->fs_info->fs_devices->missing_devices--;
next_device = list_entry(root->fs_info->fs_devices->devices.next, next_device = list_entry(root->fs_info->fs_devices->devices.next,
struct btrfs_device, dev_list); struct btrfs_device, dev_list);
if (device->bdev == root->fs_info->sb->s_bdev) if (device->bdev == root->fs_info->sb->s_bdev)
...@@ -3084,7 +3091,9 @@ static struct btrfs_device *add_missing_dev(struct btrfs_root *root, ...@@ -3084,7 +3091,9 @@ static struct btrfs_device *add_missing_dev(struct btrfs_root *root,
device->devid = devid; device->devid = devid;
device->work.func = pending_bios_fn; device->work.func = pending_bios_fn;
device->fs_devices = fs_devices; device->fs_devices = fs_devices;
device->missing = 1;
fs_devices->num_devices++; fs_devices->num_devices++;
fs_devices->missing_devices++;
spin_lock_init(&device->io_lock); spin_lock_init(&device->io_lock);
INIT_LIST_HEAD(&device->dev_alloc_list); INIT_LIST_HEAD(&device->dev_alloc_list);
memcpy(device->uuid, dev_uuid, BTRFS_UUID_SIZE); memcpy(device->uuid, dev_uuid, BTRFS_UUID_SIZE);
...@@ -3282,6 +3291,15 @@ static int read_one_dev(struct btrfs_root *root, ...@@ -3282,6 +3291,15 @@ static int read_one_dev(struct btrfs_root *root,
device = add_missing_dev(root, devid, dev_uuid); device = add_missing_dev(root, devid, dev_uuid);
if (!device) if (!device)
return -ENOMEM; return -ENOMEM;
} else if (!device->missing) {
/*
* this happens when a device that was properly setup
* in the device info lists suddenly goes bad.
* device->bdev is NULL, and so we have to set
* device->missing to one here
*/
root->fs_info->fs_devices->missing_devices++;
device->missing = 1;
} }
} }
......
...@@ -45,6 +45,7 @@ struct btrfs_device { ...@@ -45,6 +45,7 @@ struct btrfs_device {
int barriers; int barriers;
int writeable; int writeable;
int in_fs_metadata; int in_fs_metadata;
int missing;
spinlock_t io_lock; spinlock_t io_lock;
...@@ -94,6 +95,7 @@ struct btrfs_fs_devices { ...@@ -94,6 +95,7 @@ struct btrfs_fs_devices {
u64 num_devices; u64 num_devices;
u64 open_devices; u64 open_devices;
u64 rw_devices; u64 rw_devices;
u64 missing_devices;
u64 total_rw_bytes; u64 total_rw_bytes;
struct block_device *latest_bdev; struct block_device *latest_bdev;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment