aboutsummaryrefslogtreecommitdiff
path: root/fs/btrfs/volumes.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs/volumes.c')
-rw-r--r--fs/btrfs/volumes.c242
1 files changed, 128 insertions, 114 deletions
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index c95f018d4a1e..c663aad880b9 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -152,7 +152,15 @@ struct list_head *btrfs_get_fs_uuids(void)
return &fs_uuids;
}
-static struct btrfs_fs_devices *__alloc_fs_devices(void)
+/*
+ * alloc_fs_devices - allocate struct btrfs_fs_devices
+ * @fsid: if not NULL, copy the uuid to fs_devices::fsid
+ *
+ * Return a pointer to a new struct btrfs_fs_devices on success, or ERR_PTR().
+ * The returned struct is not linked onto any lists and can be destroyed with
+ * kfree() right away.
+ */
+static struct btrfs_fs_devices *alloc_fs_devices(const u8 *fsid)
{
struct btrfs_fs_devices *fs_devs;
@@ -166,31 +174,8 @@ static struct btrfs_fs_devices *__alloc_fs_devices(void)
INIT_LIST_HEAD(&fs_devs->resized_devices);
INIT_LIST_HEAD(&fs_devs->alloc_list);
INIT_LIST_HEAD(&fs_devs->list);
-
- return fs_devs;
-}
-
-/**
- * alloc_fs_devices - allocate struct btrfs_fs_devices
- * @fsid: a pointer to UUID for this FS. If NULL a new UUID is
- * generated.
- *
- * Return: a pointer to a new &struct btrfs_fs_devices on success;
- * ERR_PTR() on error. Returned struct is not linked onto any lists and
- * can be destroyed with kfree() right away.
- */
-static struct btrfs_fs_devices *alloc_fs_devices(const u8 *fsid)
-{
- struct btrfs_fs_devices *fs_devs;
-
- fs_devs = __alloc_fs_devices();
- if (IS_ERR(fs_devs))
- return fs_devs;
-
if (fsid)
memcpy(fs_devs->fsid, fsid, BTRFS_FSID_SIZE);
- else
- generate_random_uuid(fs_devs->fsid);
return fs_devs;
}
@@ -269,9 +254,17 @@ static struct btrfs_device *__alloc_device(void)
return dev;
}
-static noinline struct btrfs_device *__find_device(struct list_head *head,
- u64 devid, u8 *uuid)
+/*
+ * Find a device specified by @devid or @uuid in the list of @fs_devices, or
+ * return NULL.
+ *
+ * If devid and uuid are both specified, the match must be exact, otherwise
+ * only devid is used.
+ */
+static struct btrfs_device *find_device(struct btrfs_fs_devices *fs_devices,
+ u64 devid, const u8 *uuid)
{
+ struct list_head *head = &fs_devices->devices;
struct btrfs_device *dev;
list_for_each_entry(dev, head, dev_list) {
@@ -310,7 +303,7 @@ btrfs_get_bdev_and_sb(const char *device_path, fmode_t flags, void *holder,
if (flush)
filemap_write_and_wait((*bdev)->bd_inode->i_mapping);
- ret = set_blocksize(*bdev, 4096);
+ ret = set_blocksize(*bdev, BTRFS_BDEV_BLOCKSIZE);
if (ret) {
blkdev_put(*bdev, flags);
goto error;
@@ -636,8 +629,8 @@ static noinline int device_list_add(const char *path,
device = NULL;
} else {
- device = __find_device(&fs_devices->devices, devid,
- disk_super->dev_item.uuid);
+ device = find_device(fs_devices, devid,
+ disk_super->dev_item.uuid);
}
if (!device) {
@@ -1872,7 +1865,6 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path,
struct btrfs_fs_devices *cur_devices;
u64 num_devices;
int ret = 0;
- bool clear_super = false;
mutex_lock(&uuid_mutex);
@@ -1908,7 +1900,6 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path,
list_del_init(&device->dev_alloc_list);
device->fs_devices->rw_devices--;
mutex_unlock(&fs_info->chunk_mutex);
- clear_super = true;
}
mutex_unlock(&uuid_mutex);
@@ -1987,9 +1978,6 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path,
free_fs_devices(cur_devices);
}
- fs_info->num_tolerated_disk_barrier_failures =
- btrfs_calc_num_tolerated_disk_barrier_failures(fs_info);
-
out:
mutex_unlock(&uuid_mutex);
return ret;
@@ -2202,7 +2190,7 @@ static int btrfs_prepare_sprout(struct btrfs_fs_info *fs_info)
if (!fs_devices->seeding)
return -EINVAL;
- seed_devices = __alloc_fs_devices();
+ seed_devices = alloc_fs_devices(NULL);
if (IS_ERR(seed_devices))
return PTR_ERR(seed_devices);
@@ -2407,7 +2395,7 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
device->is_tgtdev_for_dev_replace = 0;
device->mode = FMODE_EXCL;
device->dev_stats_valid = 1;
- set_blocksize(device->bdev, 4096);
+ set_blocksize(device->bdev, BTRFS_BDEV_BLOCKSIZE);
if (seeding_dev) {
sb->s_flags &= ~MS_RDONLY;
@@ -2487,8 +2475,6 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
"sysfs: failed to create fsid for sprout");
}
- fs_info->num_tolerated_disk_barrier_failures =
- btrfs_calc_num_tolerated_disk_barrier_failures(fs_info);
ret = btrfs_commit_transaction(trans);
if (seeding_dev) {
@@ -2612,7 +2598,7 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
device->is_tgtdev_for_dev_replace = 1;
device->mode = FMODE_EXCL;
device->dev_stats_valid = 1;
- set_blocksize(device->bdev, 4096);
+ set_blocksize(device->bdev, BTRFS_BDEV_BLOCKSIZE);
device->fs_devices = fs_info->fs_devices;
list_add(&device->dev_list, &fs_info->fs_devices->devices);
fs_info->fs_devices->num_devices++;
@@ -3312,7 +3298,6 @@ static int chunk_devid_filter(struct extent_buffer *leaf,
/* [pstart, pend) */
static int chunk_drange_filter(struct extent_buffer *leaf,
struct btrfs_chunk *chunk,
- u64 chunk_offset,
struct btrfs_balance_args *bargs)
{
struct btrfs_stripe *stripe;
@@ -3439,7 +3424,7 @@ static int should_balance_chunk(struct btrfs_fs_info *fs_info,
/* drange filter, makes sense only with devid filter */
if ((bargs->flags & BTRFS_BALANCE_ARGS_DRANGE) &&
- chunk_drange_filter(leaf, chunk, chunk_offset, bargs)) {
+ chunk_drange_filter(leaf, chunk, bargs)) {
return 0;
}
@@ -3898,13 +3883,6 @@ int btrfs_balance(struct btrfs_balance_control *bctl,
meta_target, data_target);
}
- if (bctl->sys.flags & BTRFS_BALANCE_ARGS_CONVERT) {
- fs_info->num_tolerated_disk_barrier_failures = min(
- btrfs_calc_num_tolerated_disk_barrier_failures(fs_info),
- btrfs_get_num_tolerated_disk_barrier_failures(
- bctl->sys.target));
- }
-
ret = insert_balance_item(fs_info, bctl);
if (ret && ret != -EEXIST)
goto out;
@@ -3927,11 +3905,6 @@ int btrfs_balance(struct btrfs_balance_control *bctl,
mutex_lock(&fs_info->balance_mutex);
atomic_dec(&fs_info->balance_running);
- if (bctl->sys.flags & BTRFS_BALANCE_ARGS_CONVERT) {
- fs_info->num_tolerated_disk_barrier_failures =
- btrfs_calc_num_tolerated_disk_barrier_failures(fs_info);
- }
-
if (bargs) {
memset(bargs, 0, sizeof(*bargs));
update_ioctl_balance_args(fs_info, 0, bargs);
@@ -4127,7 +4100,6 @@ static int btrfs_uuid_scan_kthread(void *data)
struct btrfs_fs_info *fs_info = data;
struct btrfs_root *root = fs_info->tree_root;
struct btrfs_key key;
- struct btrfs_key max_key;
struct btrfs_path *path = NULL;
int ret = 0;
struct extent_buffer *eb;
@@ -4146,10 +4118,6 @@ static int btrfs_uuid_scan_kthread(void *data)
key.type = BTRFS_ROOT_ITEM_KEY;
key.offset = 0;
- max_key.objectid = (u64)-1;
- max_key.type = BTRFS_ROOT_ITEM_KEY;
- max_key.offset = (u64)-1;
-
while (1) {
ret = btrfs_search_forward(root, &key, path, 0);
if (ret) {
@@ -4601,12 +4569,6 @@ static int btrfs_cmp_device_info(const void *a, const void *b)
return 0;
}
-static u32 find_raid56_stripe_len(u32 data_devices, u32 dev_stripe_target)
-{
- /* TODO allow them to set a preferred stripe size */
- return SZ_64K;
-}
-
static void check_raid56_incompat_flag(struct btrfs_fs_info *info, u64 type)
{
if (!(type & BTRFS_BLOCK_GROUP_RAID56_MASK))
@@ -4629,7 +4591,7 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
{
struct btrfs_fs_info *info = trans->fs_info;
struct btrfs_fs_devices *fs_devices = info->fs_devices;
- struct list_head *cur;
+ struct btrfs_device *device;
struct map_lookup *map = NULL;
struct extent_map_tree *em_tree;
struct extent_map *em;
@@ -4649,7 +4611,6 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
u64 max_chunk_size;
u64 stripe_size;
u64 num_bytes;
- u64 raid_stripe_len = BTRFS_STRIPE_LEN;
int ndevs;
int i;
int j;
@@ -4703,22 +4664,15 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
if (!devices_info)
return -ENOMEM;
- cur = fs_devices->alloc_list.next;
-
/*
* in the first pass through the devices list, we gather information
* about the available holes on each device.
*/
ndevs = 0;
- while (cur != &fs_devices->alloc_list) {
- struct btrfs_device *device;
+ list_for_each_entry(device, &fs_devices->alloc_list, dev_alloc_list) {
u64 max_avail;
u64 dev_offset;
- device = list_entry(cur, struct btrfs_device, dev_alloc_list);
-
- cur = cur->next;
-
if (!device->writeable) {
WARN(1, KERN_ERR
"BTRFS: read-only device in alloc_list\n");
@@ -4769,15 +4723,15 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
btrfs_cmp_device_info, NULL);
/* round down to number of usable stripes */
- ndevs -= ndevs % devs_increment;
+ ndevs = round_down(ndevs, devs_increment);
if (ndevs < devs_increment * sub_stripes || ndevs < devs_min) {
ret = -ENOSPC;
goto error;
}
- if (devs_max && ndevs > devs_max)
- ndevs = devs_max;
+ ndevs = min(ndevs, devs_max);
+
/*
* the primary goal is to maximize the number of stripes, so use as many
* devices as possible, even if the stripes are not maximum sized.
@@ -4791,16 +4745,11 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
*/
data_stripes = num_stripes / ncopies;
- if (type & BTRFS_BLOCK_GROUP_RAID5) {
- raid_stripe_len = find_raid56_stripe_len(ndevs - 1,
- info->stripesize);
+ if (type & BTRFS_BLOCK_GROUP_RAID5)
data_stripes = num_stripes - 1;
- }
- if (type & BTRFS_BLOCK_GROUP_RAID6) {
- raid_stripe_len = find_raid56_stripe_len(ndevs - 2,
- info->stripesize);
+
+ if (type & BTRFS_BLOCK_GROUP_RAID6)
data_stripes = num_stripes - 2;
- }
/*
* Use the number of data stripes to figure out how big this chunk
@@ -4825,8 +4774,7 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
stripe_size = div_u64(stripe_size, dev_stripes);
/* align to BTRFS_STRIPE_LEN */
- stripe_size = div64_u64(stripe_size, raid_stripe_len);
- stripe_size *= raid_stripe_len;
+ stripe_size = round_down(stripe_size, BTRFS_STRIPE_LEN);
map = kmalloc(map_lookup_size(num_stripes), GFP_NOFS);
if (!map) {
@@ -4844,9 +4792,9 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
}
}
map->sector_size = info->sectorsize;
- map->stripe_len = raid_stripe_len;
- map->io_align = raid_stripe_len;
- map->io_width = raid_stripe_len;
+ map->stripe_len = BTRFS_STRIPE_LEN;
+ map->io_align = BTRFS_STRIPE_LEN;
+ map->io_width = BTRFS_STRIPE_LEN;
map->type = type;
map->sub_stripes = sub_stripes;
@@ -5172,7 +5120,6 @@ int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len)
}
unsigned long btrfs_full_stripe_len(struct btrfs_fs_info *fs_info,
- struct btrfs_mapping_tree *map_tree,
u64 logical)
{
struct extent_map *em;
@@ -5180,29 +5127,30 @@ unsigned long btrfs_full_stripe_len(struct btrfs_fs_info *fs_info,
unsigned long len = fs_info->sectorsize;
em = get_chunk_map(fs_info, logical, len);
- WARN_ON(IS_ERR(em));
- map = em->map_lookup;
- if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK)
- len = map->stripe_len * nr_data_stripes(map);
- free_extent_map(em);
+ if (!WARN_ON(IS_ERR(em))) {
+ map = em->map_lookup;
+ if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK)
+ len = map->stripe_len * nr_data_stripes(map);
+ free_extent_map(em);
+ }
return len;
}
-int btrfs_is_parity_mirror(struct btrfs_fs_info *fs_info,
- u64 logical, u64 len, int mirror_num)
+int btrfs_is_parity_mirror(struct btrfs_fs_info *fs_info, u64 logical, u64 len)
{
struct extent_map *em;
struct map_lookup *map;
int ret = 0;
em = get_chunk_map(fs_info, logical, len);
- WARN_ON(IS_ERR(em));
- map = em->map_lookup;
- if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK)
- ret = 1;
- free_extent_map(em);
+ if(!WARN_ON(IS_ERR(em))) {
+ map = em->map_lookup;
+ if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK)
+ ret = 1;
+ free_extent_map(em);
+ }
return ret;
}
@@ -6295,8 +6243,7 @@ struct btrfs_device *btrfs_find_device(struct btrfs_fs_info *fs_info, u64 devid,
while (cur_devices) {
if (!fsid ||
!memcmp(cur_devices->fsid, fsid, BTRFS_UUID_SIZE)) {
- device = __find_device(&cur_devices->devices,
- devid, uuid);
+ device = find_device(cur_devices, devid, uuid);
if (device)
return device;
}
@@ -6449,7 +6396,6 @@ static int read_one_chunk(struct btrfs_fs_info *fs_info, struct btrfs_key *key,
struct extent_map *em;
u64 logical;
u64 length;
- u64 stripe_len;
u64 devid;
u8 uuid[BTRFS_UUID_SIZE];
int num_stripes;
@@ -6458,7 +6404,6 @@ static int read_one_chunk(struct btrfs_fs_info *fs_info, struct btrfs_key *key,
logical = key->offset;
length = btrfs_chunk_length(leaf, chunk);
- stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
ret = btrfs_check_chunk_valid(fs_info, leaf, chunk, logical);
@@ -6513,6 +6458,7 @@ static int read_one_chunk(struct btrfs_fs_info *fs_info, struct btrfs_key *key,
if (!map->stripes[i].dev &&
!btrfs_test_opt(fs_info, DEGRADED)) {
free_extent_map(em);
+ btrfs_report_missing_device(fs_info, devid, uuid);
return -EIO;
}
if (!map->stripes[i].dev) {
@@ -6523,8 +6469,7 @@ static int read_one_chunk(struct btrfs_fs_info *fs_info, struct btrfs_key *key,
free_extent_map(em);
return -EIO;
}
- btrfs_warn(fs_info, "devid %llu uuid %pU is missing",
- devid, uuid);
+ btrfs_report_missing_device(fs_info, devid, uuid);
}
map->stripes[i].dev->in_fs_metadata = 1;
}
@@ -6568,6 +6513,7 @@ static struct btrfs_fs_devices *open_seed_devices(struct btrfs_fs_info *fs_info,
int ret;
BUG_ON(!mutex_is_locked(&uuid_mutex));
+ ASSERT(fsid);
fs_devices = fs_info->fs_devices->seed;
while (fs_devices) {
@@ -6641,17 +6587,21 @@ static int read_one_dev(struct btrfs_fs_info *fs_info,
device = btrfs_find_device(fs_info, devid, dev_uuid, fs_uuid);
if (!device) {
- if (!btrfs_test_opt(fs_info, DEGRADED))
+ if (!btrfs_test_opt(fs_info, DEGRADED)) {
+ btrfs_report_missing_device(fs_info, devid, dev_uuid);
return -EIO;
+ }
device = add_missing_dev(fs_devices, devid, dev_uuid);
if (!device)
return -ENOMEM;
- btrfs_warn(fs_info, "devid %llu uuid %pU missing",
- devid, dev_uuid);
+ btrfs_report_missing_device(fs_info, devid, dev_uuid);
} else {
- if (!device->bdev && !btrfs_test_opt(fs_info, DEGRADED))
- return -EIO;
+ if (!device->bdev) {
+ btrfs_report_missing_device(fs_info, devid, dev_uuid);
+ if (!btrfs_test_opt(fs_info, DEGRADED))
+ return -EIO;
+ }
if(!device->bdev && !device->missing) {
/*
@@ -6817,6 +6767,70 @@ out_short_read:
return -EIO;
}
+void btrfs_report_missing_device(struct btrfs_fs_info *fs_info, u64 devid,
+ u8 *uuid)
+{
+ btrfs_warn_rl(fs_info, "devid %llu uuid %pU is missing", devid, uuid);
+}
+
+/*
+ * Check if all chunks in the fs are OK for read-write degraded mount
+ *
+ * Return true if all chunks meet the minimal RW mount requirements.
+ * Return false if any chunk doesn't meet the minimal RW mount requirements.
+ */
+bool btrfs_check_rw_degradable(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
+ struct extent_map *em;
+ u64 next_start = 0;
+ bool ret = true;
+
+ read_lock(&map_tree->map_tree.lock);
+ em = lookup_extent_mapping(&map_tree->map_tree, 0, (u64)-1);
+ read_unlock(&map_tree->map_tree.lock);
+ /* No chunk at all? Return false anyway */
+ if (!em) {
+ ret = false;
+ goto out;
+ }
+ while (em) {
+ struct map_lookup *map;
+ int missing = 0;
+ int max_tolerated;
+ int i;
+
+ map = em->map_lookup;
+ max_tolerated =
+ btrfs_get_num_tolerated_disk_barrier_failures(
+ map->type);
+ for (i = 0; i < map->num_stripes; i++) {
+ struct btrfs_device *dev = map->stripes[i].dev;
+
+ if (!dev || !dev->bdev || dev->missing ||
+ dev->last_flush_error)
+ missing++;
+ }
+ if (missing > max_tolerated) {
+ btrfs_warn(fs_info,
+ "chunk %llu missing %d devices, max tolerance is %d for writeable mount",
+ em->start, missing, max_tolerated);
+ free_extent_map(em);
+ ret = false;
+ goto out;
+ }
+ next_start = extent_map_end(em);
+ free_extent_map(em);
+
+ read_lock(&map_tree->map_tree.lock);
+ em = lookup_extent_mapping(&map_tree->map_tree, next_start,
+ (u64)(-1) - next_start);
+ read_unlock(&map_tree->map_tree.lock);
+ }
+out:
+ return ret;
+}
+
int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info)
{
struct btrfs_root *root = fs_info->chunk_root;