]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
btrfs: Introduce a function to check if all chunks a OK for degraded rw mount
authorQu Wenruo <quwenruo@cn.fujitsu.com>
Thu, 9 Mar 2017 01:34:36 +0000 (09:34 +0800)
committerDavid Sterba <dsterba@suse.com>
Wed, 16 Aug 2017 14:12:02 +0000 (16:12 +0200)
Introduce a new function, btrfs_check_rw_degradable(), to check if all
chunks in btrfs is OK for degraded rw mount.

It provides the new basis for accurate btrfs mount/remount and even
runtime degraded mount check other than old one-size-fit-all method.

Btrfs currently uses num_tolerated_disk_barrier_failures to do global
check for tolerated missing device.

Although the one-size-fit-all solution is quite safe, it's too strict
if data and metadata has different duplication level.

For example, if one use Single data and RAID1 metadata for 2 disks, it
means any missing device will make the fs unable to be degraded
mounted.

But in fact, some times all single chunks may be in the existing
device and in that case, we should allow it to be rw degraded mounted.

Such case can be easily reproduced using the following script:
 # mkfs.btrfs -f -m raid1 -d sing /dev/sdb /dev/sdc
 # wipefs -f /dev/sdc
 # mount /dev/sdb -o degraded,rw

If using btrfs-debug-tree to check /dev/sdb, one should find that the
data chunk is only in sdb, so in fact it should allow degraded mount.

This patchset will introduce a new per-chunk degradable check for
btrfs, allow above case to succeed, and it's quite small anyway.

Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: Anand Jain <anand.jain@oracle.com>
Reviewed-by: David Sterba <dsterba@suse.com>
[ copied text from cover letter with more details about the problem being
  solved ]
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/volumes.c
fs/btrfs/volumes.h

index 59f82939e634e9f81c30c75c98a71577385af44b..1bc822b5a4c090f83054c6d41c605e39f5fe16d6 100644 (file)
@@ -6813,6 +6813,64 @@ out_short_read:
        return -EIO;
 }
 
+/*
+ * 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;
index 6f45fd60d15a92d7aea7a5711bb365a201cb09f9..5824cdc0b3fca3237426200fed754b79ef12b51f 100644 (file)
@@ -543,4 +543,6 @@ struct list_head *btrfs_get_fs_uuids(void);
 void btrfs_set_fs_info_ptr(struct btrfs_fs_info *fs_info);
 void btrfs_reset_fs_info_ptr(struct btrfs_fs_info *fs_info);
 
+bool btrfs_check_rw_degradable(struct btrfs_fs_info *fs_info);
+
 #endif