Merge branch 'for-4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux
[sfrench/cifs-2.6.git] / fs / btrfs / volumes.c
index 002aa318da67bb560979430ae6eee0f6ad263417..c188256a367c99ebdcff970ce13147e4365a2cbd 100644 (file)
@@ -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) {
@@ -1578,7 +1571,6 @@ out:
 
 static int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
                                  struct btrfs_device *device,
-                                 u64 chunk_tree, u64 chunk_objectid,
                                  u64 chunk_offset, u64 start, u64 num_bytes)
 {
        int ret;
@@ -1606,12 +1598,12 @@ static int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
        leaf = path->nodes[0];
        extent = btrfs_item_ptr(leaf, path->slots[0],
                                struct btrfs_dev_extent);
-       btrfs_set_dev_extent_chunk_tree(leaf, extent, chunk_tree);
-       btrfs_set_dev_extent_chunk_objectid(leaf, extent, chunk_objectid);
+       btrfs_set_dev_extent_chunk_tree(leaf, extent,
+                                       BTRFS_CHUNK_TREE_OBJECTID);
+       btrfs_set_dev_extent_chunk_objectid(leaf, extent,
+                                           BTRFS_FIRST_CHUNK_TREE_OBJECTID);
        btrfs_set_dev_extent_chunk_offset(leaf, extent, chunk_offset);
 
-       write_extent_buffer_chunk_tree_uuid(leaf, fs_info->chunk_tree_uuid);
-
        btrfs_set_dev_extent_length(leaf, extent, num_bytes);
        btrfs_mark_buffer_dirty(leaf);
 out:
@@ -1726,7 +1718,7 @@ static int btrfs_add_device(struct btrfs_trans_handle *trans,
        ptr = btrfs_device_uuid(dev_item);
        write_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE);
        ptr = btrfs_device_fsid(dev_item);
-       write_extent_buffer(leaf, fs_info->fsid, ptr, BTRFS_UUID_SIZE);
+       write_extent_buffer(leaf, fs_info->fsid, ptr, BTRFS_FSID_SIZE);
        btrfs_mark_buffer_dirty(leaf);
 
        ret = 0;
@@ -1872,7 +1864,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 +1899,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 +1977,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 +2189,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);
 
@@ -2261,7 +2248,7 @@ static int btrfs_finish_sprout(struct btrfs_trans_handle *trans,
        struct btrfs_dev_item *dev_item;
        struct btrfs_device *device;
        struct btrfs_key key;
-       u8 fs_uuid[BTRFS_UUID_SIZE];
+       u8 fs_uuid[BTRFS_FSID_SIZE];
        u8 dev_uuid[BTRFS_UUID_SIZE];
        u64 devid;
        int ret;
@@ -2304,7 +2291,7 @@ next_slot:
                read_extent_buffer(leaf, dev_uuid, btrfs_device_uuid(dev_item),
                                   BTRFS_UUID_SIZE);
                read_extent_buffer(leaf, fs_uuid, btrfs_device_fsid(dev_item),
-                                  BTRFS_UUID_SIZE);
+                                  BTRFS_FSID_SIZE);
                device = btrfs_find_device(fs_info, devid, dev_uuid, fs_uuid);
                BUG_ON(!device); /* Logic error */
 
@@ -2407,7 +2394,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 +2474,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 +2597,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++;
@@ -2728,8 +2713,7 @@ int btrfs_grow_device(struct btrfs_trans_handle *trans,
 }
 
 static int btrfs_free_chunk(struct btrfs_trans_handle *trans,
-                           struct btrfs_fs_info *fs_info, u64 chunk_objectid,
-                           u64 chunk_offset)
+                           struct btrfs_fs_info *fs_info, u64 chunk_offset)
 {
        struct btrfs_root *root = fs_info->chunk_root;
        int ret;
@@ -2740,7 +2724,7 @@ static int btrfs_free_chunk(struct btrfs_trans_handle *trans,
        if (!path)
                return -ENOMEM;
 
-       key.objectid = chunk_objectid;
+       key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
        key.offset = chunk_offset;
        key.type = BTRFS_CHUNK_ITEM_KEY;
 
@@ -2763,8 +2747,7 @@ out:
        return ret;
 }
 
-static int btrfs_del_sys_chunk(struct btrfs_fs_info *fs_info,
-                              u64 chunk_objectid, u64 chunk_offset)
+static int btrfs_del_sys_chunk(struct btrfs_fs_info *fs_info, u64 chunk_offset)
 {
        struct btrfs_super_block *super_copy = fs_info->super_copy;
        struct btrfs_disk_key *disk_key;
@@ -2797,7 +2780,7 @@ static int btrfs_del_sys_chunk(struct btrfs_fs_info *fs_info,
                        ret = -EIO;
                        break;
                }
-               if (key.objectid == chunk_objectid &&
+               if (key.objectid == BTRFS_FIRST_CHUNK_TREE_OBJECTID &&
                    key.offset == chunk_offset) {
                        memmove(ptr, ptr + len, array_size - (cur + len));
                        array_size -= len;
@@ -2846,7 +2829,6 @@ int btrfs_remove_chunk(struct btrfs_trans_handle *trans,
        struct extent_map *em;
        struct map_lookup *map;
        u64 dev_extent_len = 0;
-       u64 chunk_objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
        int i, ret = 0;
        struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
 
@@ -2902,7 +2884,7 @@ int btrfs_remove_chunk(struct btrfs_trans_handle *trans,
        }
        mutex_unlock(&fs_devices->device_list_mutex);
 
-       ret = btrfs_free_chunk(trans, fs_info, chunk_objectid, chunk_offset);
+       ret = btrfs_free_chunk(trans, fs_info, chunk_offset);
        if (ret) {
                btrfs_abort_transaction(trans, ret);
                goto out;
@@ -2911,8 +2893,7 @@ int btrfs_remove_chunk(struct btrfs_trans_handle *trans,
        trace_btrfs_chunk_free(fs_info, map, chunk_offset, em->len);
 
        if (map->type & BTRFS_BLOCK_GROUP_SYSTEM) {
-               ret = btrfs_del_sys_chunk(fs_info, chunk_objectid,
-                                         chunk_offset);
+               ret = btrfs_del_sys_chunk(fs_info, chunk_offset);
                if (ret) {
                        btrfs_abort_transaction(trans, ret);
                        goto out;
@@ -3312,7 +3293,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 +3419,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 +3878,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 +3900,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 +4095,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 +4113,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 +4564,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 +4586,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 +4606,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 +4659,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 +4718,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 +4740,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 +4769,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) {
@@ -4843,10 +4786,9 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
                                                   j * stripe_size;
                }
        }
-       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;
 
@@ -4881,9 +4823,7 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
                goto error;
        }
 
-       ret = btrfs_make_block_group(trans, info, 0, type,
-                                    BTRFS_FIRST_CHUNK_TREE_OBJECTID,
-                                    start, num_bytes);
+       ret = btrfs_make_block_group(trans, info, 0, type, start, num_bytes);
        if (ret)
                goto error_del_extent;
 
@@ -4963,11 +4903,8 @@ int btrfs_finish_chunk_alloc(struct btrfs_trans_handle *trans,
                ret = btrfs_update_device(trans, device);
                if (ret)
                        break;
-               ret = btrfs_alloc_dev_extent(trans, device,
-                                            chunk_root->root_key.objectid,
-                                            BTRFS_FIRST_CHUNK_TREE_OBJECTID,
-                                            chunk_offset, dev_offset,
-                                            stripe_size);
+               ret = btrfs_alloc_dev_extent(trans, device, chunk_offset,
+                                            dev_offset, stripe_size);
                if (ret)
                        break;
        }
@@ -5172,7 +5109,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 +5116,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,9 +6232,8 @@ struct btrfs_device *btrfs_find_device(struct btrfs_fs_info *fs_info, u64 devid,
        cur_devices = fs_info->fs_devices;
        while (cur_devices) {
                if (!fsid ||
-                   !memcmp(cur_devices->fsid, fsid, BTRFS_UUID_SIZE)) {
-                       device = __find_device(&cur_devices->devices,
-                                              devid, uuid);
+                   !memcmp(cur_devices->fsid, fsid, BTRFS_FSID_SIZE)) {
+                       device = find_device(cur_devices, devid, uuid);
                        if (device)
                                return device;
                }
@@ -6450,7 +6386,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;
@@ -6459,7 +6394,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);
@@ -6498,7 +6432,6 @@ static int read_one_chunk(struct btrfs_fs_info *fs_info, struct btrfs_key *key,
        map->num_stripes = num_stripes;
        map->io_width = btrfs_chunk_io_width(leaf, chunk);
        map->io_align = btrfs_chunk_io_align(leaf, chunk);
-       map->sector_size = btrfs_chunk_sector_size(leaf, chunk);
        map->stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
        map->type = btrfs_chunk_type(leaf, chunk);
        map->sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk);
@@ -6514,6 +6447,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) {
@@ -6524,8 +6458,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;
        }
@@ -6569,10 +6502,11 @@ 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) {
-               if (!memcmp(fs_devices->fsid, fsid, BTRFS_UUID_SIZE))
+               if (!memcmp(fs_devices->fsid, fsid, BTRFS_FSID_SIZE))
                        return fs_devices;
 
                fs_devices = fs_devices->seed;
@@ -6625,16 +6559,16 @@ static int read_one_dev(struct btrfs_fs_info *fs_info,
        struct btrfs_device *device;
        u64 devid;
        int ret;
-       u8 fs_uuid[BTRFS_UUID_SIZE];
+       u8 fs_uuid[BTRFS_FSID_SIZE];
        u8 dev_uuid[BTRFS_UUID_SIZE];
 
        devid = btrfs_device_id(leaf, dev_item);
        read_extent_buffer(leaf, dev_uuid, btrfs_device_uuid(dev_item),
                           BTRFS_UUID_SIZE);
        read_extent_buffer(leaf, fs_uuid, btrfs_device_fsid(dev_item),
-                          BTRFS_UUID_SIZE);
+                          BTRFS_FSID_SIZE);
 
-       if (memcmp(fs_uuid, fs_info->fsid, BTRFS_UUID_SIZE)) {
+       if (memcmp(fs_uuid, fs_info->fsid, BTRFS_FSID_SIZE)) {
                fs_devices = open_seed_devices(fs_info, fs_uuid);
                if (IS_ERR(fs_devices))
                        return PTR_ERR(fs_devices);
@@ -6642,17 +6576,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) {
                        /*
@@ -6818,6 +6756,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;