Btrfs: Add mount -o degraded to allow mounts to continue with missing devices

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 574b124..38b0d9e 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -614,21 +614,6 @@
 	return ret;
 }
 
-static int close_all_devices(struct btrfs_fs_info *fs_info)
-{
-	struct list_head *list;
-	struct list_head *next;
-	struct btrfs_device *device;
-
-	list = &fs_info->fs_devices->devices;
-	list_for_each(next, list) {
-		device = list_entry(next, struct btrfs_device, dev_list);
-		close_bdev_excl(device->bdev);
-		device->bdev = NULL;
-	}
-	return 0;
-}
-
 struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
 					    u64 bytenr, u32 blocksize)
 {
@@ -927,6 +912,8 @@
 
 	list_for_each(cur, &info->fs_devices->devices) {
 		device = list_entry(cur, struct btrfs_device, dev_list);
+		if (!device->bdev)
+			continue;
 		bdi = blk_get_backing_dev_info(device->bdev);
 		if (bdi && bdi_congested(bdi, bdi_bits)) {
 			ret = 1;
@@ -1140,7 +1127,8 @@
 }
 
 struct btrfs_root *open_ctree(struct super_block *sb,
-			      struct btrfs_fs_devices *fs_devices)
+			      struct btrfs_fs_devices *fs_devices,
+			      char *options)
 {
 	u32 sectorsize;
 	u32 nodesize;
@@ -1276,12 +1264,19 @@
 	if (!btrfs_super_root(disk_super))
 		goto fail_sb_buffer;
 
-	if (btrfs_super_num_devices(disk_super) != fs_devices->num_devices) {
+	btrfs_parse_options(options, tree_root, NULL);
+
+	if (btrfs_super_num_devices(disk_super) > fs_devices->num_devices) {
 		printk("Btrfs: wanted %llu devices, but found %llu\n",
 		       (unsigned long long)btrfs_super_num_devices(disk_super),
 		       (unsigned long long)fs_devices->num_devices);
-		goto fail_sb_buffer;
+		if (btrfs_test_opt(tree_root, DEGRADED))
+			printk("continuing in degraded mode\n");
+		else {
+			goto fail_sb_buffer;
+		}
 	}
+
 	fs_info->bdi.ra_pages *= btrfs_super_num_devices(disk_super);
 
 	nodesize = btrfs_super_nodesize(disk_super);
@@ -1329,6 +1324,8 @@
 	ret = btrfs_read_chunk_tree(chunk_root);
 	BUG_ON(ret);
 
+	btrfs_close_extra_devices(fs_devices);
+
 	blocksize = btrfs_level_size(tree_root,
 				     btrfs_super_root_level(disk_super));
 
@@ -1374,7 +1371,7 @@
 fail_iput:
 	iput(fs_info->btree_inode);
 fail:
-	close_all_devices(fs_info);
+	btrfs_close_devices(fs_info->fs_devices);
 	btrfs_mapping_tree_free(&fs_info->mapping_tree);
 
 	kfree(extent_root);
@@ -1429,6 +1426,13 @@
 	dev_item = &sb->dev_item;
 	list_for_each(cur, head) {
 		dev = list_entry(cur, struct btrfs_device, dev_list);
+		if (!dev->bdev) {
+			total_errors++;
+			continue;
+		}
+		if (!dev->in_fs_metadata)
+			continue;
+
 		btrfs_set_stack_device_type(dev_item, dev->type);
 		btrfs_set_stack_device_id(dev_item, dev->devid);
 		btrfs_set_stack_device_total_bytes(dev_item, dev->total_bytes);
@@ -1482,6 +1486,11 @@
 
 	list_for_each(cur, head) {
 		dev = list_entry(cur, struct btrfs_device, dev_list);
+		if (!dev->bdev)
+			continue;
+		if (!dev->in_fs_metadata)
+			continue;
+
 		BUG_ON(!dev->pending_io);
 		bh = dev->pending_io;
 		wait_on_buffer(bh);
@@ -1631,7 +1640,7 @@
 		kfree(hasher);
 	}
 #endif
-	close_all_devices(fs_info);
+	btrfs_close_devices(fs_info->fs_devices);
 	btrfs_mapping_tree_free(&fs_info->mapping_tree);
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)