diff options
| -rw-r--r-- | fs/btrfs/block-group.c | 9 | ||||
| -rw-r--r-- | fs/btrfs/disk-io.c | 42 | ||||
| -rw-r--r-- | fs/btrfs/scrub.c | 3 | ||||
| -rw-r--r-- | fs/btrfs/volumes.c | 20 | ||||
| -rw-r--r-- | fs/btrfs/zoned.c | 327 | ||||
| -rw-r--r-- | fs/btrfs/zoned.h | 40 | 
6 files changed, 429 insertions, 12 deletions
diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index ccc3271c20ca..9c3523b6e016 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -1679,6 +1679,7 @@ out:  static int exclude_super_stripes(struct btrfs_block_group *cache)  {  	struct btrfs_fs_info *fs_info = cache->fs_info; +	const bool zoned = btrfs_is_zoned(fs_info);  	u64 bytenr;  	u64 *logical;  	int stripe_len; @@ -1700,6 +1701,14 @@ static int exclude_super_stripes(struct btrfs_block_group *cache)  		if (ret)  			return ret; +		/* Shouldn't have super stripes in sequential zones */ +		if (zoned && nr) { +			btrfs_err(fs_info, +			"zoned: block group %llu must not contain super block", +				  cache->start); +			return -EUCLEAN; +		} +  		while (nr--) {  			u64 len = min_t(u64, stripe_len,  				cache->start + cache->length - logical[nr]); diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 32e29e2bc99a..aa92c0de0cd6 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -3488,10 +3488,17 @@ struct btrfs_super_block *btrfs_read_dev_one_super(struct block_device *bdev,  {  	struct btrfs_super_block *super;  	struct page *page; -	u64 bytenr; +	u64 bytenr, bytenr_orig;  	struct address_space *mapping = bdev->bd_inode->i_mapping; +	int ret; + +	bytenr_orig = btrfs_sb_offset(copy_num); +	ret = btrfs_sb_log_location_bdev(bdev, copy_num, READ, &bytenr); +	if (ret == -ENOENT) +		return ERR_PTR(-EINVAL); +	else if (ret) +		return ERR_PTR(ret); -	bytenr = btrfs_sb_offset(copy_num);  	if (bytenr + BTRFS_SUPER_INFO_SIZE >= i_size_read(bdev->bd_inode))  		return ERR_PTR(-EINVAL); @@ -3505,7 +3512,7 @@ struct btrfs_super_block *btrfs_read_dev_one_super(struct block_device *bdev,  		return ERR_PTR(-ENODATA);  	} -	if (btrfs_super_bytenr(super) != bytenr) { +	if (btrfs_super_bytenr(super) != bytenr_orig) {  		btrfs_release_disk_super(super);  		return ERR_PTR(-EINVAL);  	} @@ -3560,7 +3567,8 @@ static int write_dev_supers(struct btrfs_device *device,  	SHASH_DESC_ON_STACK(shash, fs_info->csum_shash);  	int i;  	int errors = 0; -	u64 bytenr; +	int ret; +	u64 bytenr, bytenr_orig;  	if (max_mirrors == 0)  		max_mirrors = BTRFS_SUPER_MIRROR_MAX; @@ -3572,12 +3580,22 @@ static int write_dev_supers(struct btrfs_device *device,  		struct bio *bio;  		struct btrfs_super_block *disk_super; -		bytenr = btrfs_sb_offset(i); +		bytenr_orig = btrfs_sb_offset(i); +		ret = btrfs_sb_log_location(device, i, WRITE, &bytenr); +		if (ret == -ENOENT) { +			continue; +		} else if (ret < 0) { +			btrfs_err(device->fs_info, +				"couldn't get super block location for mirror %d", +				i); +			errors++; +			continue; +		}  		if (bytenr + BTRFS_SUPER_INFO_SIZE >=  		    device->commit_total_bytes)  			break; -		btrfs_set_super_bytenr(sb, bytenr); +		btrfs_set_super_bytenr(sb, bytenr_orig);  		crypto_shash_digest(shash, (const char *)sb + BTRFS_CSUM_SIZE,  				    BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE, @@ -3622,6 +3640,7 @@ static int write_dev_supers(struct btrfs_device *device,  			bio->bi_opf |= REQ_FUA;  		btrfsic_submit_bio(bio); +		btrfs_advance_sb_log(device, i);  	}  	return errors < i ? 0 : -1;  } @@ -3638,6 +3657,7 @@ static int wait_dev_supers(struct btrfs_device *device, int max_mirrors)  	int i;  	int errors = 0;  	bool primary_failed = false; +	int ret;  	u64 bytenr;  	if (max_mirrors == 0) @@ -3646,7 +3666,15 @@ static int wait_dev_supers(struct btrfs_device *device, int max_mirrors)  	for (i = 0; i < max_mirrors; i++) {  		struct page *page; -		bytenr = btrfs_sb_offset(i); +		ret = btrfs_sb_log_location(device, i, READ, &bytenr); +		if (ret == -ENOENT) { +			break; +		} else if (ret < 0) { +			errors++; +			if (i == 0) +				primary_failed = true; +			continue; +		}  		if (bytenr + BTRFS_SUPER_INFO_SIZE >=  		    device->commit_total_bytes)  			break; diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index cb50e4cb97d5..78759bc9c980 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -20,6 +20,7 @@  #include "rcu-string.h"  #include "raid56.h"  #include "block-group.h" +#include "zoned.h"  /*   * This is only the first step towards a full-features scrub. It reads all @@ -3732,6 +3733,8 @@ static noinline_for_stack int scrub_supers(struct scrub_ctx *sctx,  		if (bytenr + BTRFS_SUPER_INFO_SIZE >  		    scrub_dev->commit_total_bytes)  			break; +		if (!btrfs_check_super_location(scrub_dev, bytenr)) +			continue;  		ret = scrub_pages(sctx, bytenr, BTRFS_SUPER_INFO_SIZE, bytenr,  				  scrub_dev, BTRFS_EXTENT_FLAG_SUPER, gen, i, diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index cf3f4975107f..fd9263402b8a 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1276,7 +1276,7 @@ void btrfs_release_disk_super(struct btrfs_super_block *super)  }  static struct btrfs_super_block *btrfs_read_disk_super(struct block_device *bdev, -						       u64 bytenr) +						       u64 bytenr, u64 bytenr_orig)  {  	struct btrfs_super_block *disk_super;  	struct page *page; @@ -1307,7 +1307,7 @@ static struct btrfs_super_block *btrfs_read_disk_super(struct block_device *bdev  	/* align our pointer to the offset of the super block */  	disk_super = p + offset_in_page(bytenr); -	if (btrfs_super_bytenr(disk_super) != bytenr || +	if (btrfs_super_bytenr(disk_super) != bytenr_orig ||  	    btrfs_super_magic(disk_super) != BTRFS_MAGIC) {  		btrfs_release_disk_super(p);  		return ERR_PTR(-EINVAL); @@ -1342,7 +1342,8 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, fmode_t flags,  	bool new_device_added = false;  	struct btrfs_device *device = NULL;  	struct block_device *bdev; -	u64 bytenr; +	u64 bytenr, bytenr_orig; +	int ret;  	lockdep_assert_held(&uuid_mutex); @@ -1352,14 +1353,18 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, fmode_t flags,  	 * So, we need to add a special mount option to scan for  	 * later supers, using BTRFS_SUPER_MIRROR_MAX instead  	 */ -	bytenr = btrfs_sb_offset(0);  	flags |= FMODE_EXCL;  	bdev = blkdev_get_by_path(path, flags, holder);  	if (IS_ERR(bdev))  		return ERR_CAST(bdev); -	disk_super = btrfs_read_disk_super(bdev, bytenr); +	bytenr_orig = btrfs_sb_offset(0); +	ret = btrfs_sb_log_location_bdev(bdev, 0, READ, &bytenr); +	if (ret) +		return ERR_PTR(ret); + +	disk_super = btrfs_read_disk_super(bdev, bytenr, bytenr_orig);  	if (IS_ERR(disk_super)) {  		device = ERR_CAST(disk_super);  		goto error_bdev_put; @@ -2023,6 +2028,11 @@ void btrfs_scratch_superblocks(struct btrfs_fs_info *fs_info,  		if (IS_ERR(disk_super))  			continue; +		if (bdev_is_zoned(bdev)) { +			btrfs_reset_sb_log_zones(bdev, copy_num); +			continue; +		} +  		memset(&disk_super->magic, 0, sizeof(disk_super->magic));  		page = virt_to_page(disk_super); diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 7814f6b3ff68..155545180046 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -10,6 +10,9 @@  /* Maximum number of zones to report per blkdev_report_zones() call */  #define BTRFS_REPORT_NR_ZONES   4096 +/* Number of superblock log zones */ +#define BTRFS_NR_SB_LOG_ZONES 2 +  static int copy_zone_info_cb(struct blk_zone *zone, unsigned int idx, void *data)  {  	struct blk_zone *zones = data; @@ -19,6 +22,103 @@ static int copy_zone_info_cb(struct blk_zone *zone, unsigned int idx, void *data  	return 0;  } +static int sb_write_pointer(struct block_device *bdev, struct blk_zone *zones, +			    u64 *wp_ret) +{ +	bool empty[BTRFS_NR_SB_LOG_ZONES]; +	bool full[BTRFS_NR_SB_LOG_ZONES]; +	sector_t sector; + +	ASSERT(zones[0].type != BLK_ZONE_TYPE_CONVENTIONAL && +	       zones[1].type != BLK_ZONE_TYPE_CONVENTIONAL); + +	empty[0] = (zones[0].cond == BLK_ZONE_COND_EMPTY); +	empty[1] = (zones[1].cond == BLK_ZONE_COND_EMPTY); +	full[0] = (zones[0].cond == BLK_ZONE_COND_FULL); +	full[1] = (zones[1].cond == BLK_ZONE_COND_FULL); + +	/* +	 * Possible states of log buffer zones +	 * +	 *           Empty[0]  In use[0]  Full[0] +	 * Empty[1]         *          x        0 +	 * In use[1]        0          x        0 +	 * Full[1]          1          1        C +	 * +	 * Log position: +	 *   *: Special case, no superblock is written +	 *   0: Use write pointer of zones[0] +	 *   1: Use write pointer of zones[1] +	 *   C: Compare super blcoks from zones[0] and zones[1], use the latest +	 *      one determined by generation +	 *   x: Invalid state +	 */ + +	if (empty[0] && empty[1]) { +		/* Special case to distinguish no superblock to read */ +		*wp_ret = zones[0].start << SECTOR_SHIFT; +		return -ENOENT; +	} else if (full[0] && full[1]) { +		/* Compare two super blocks */ +		struct address_space *mapping = bdev->bd_inode->i_mapping; +		struct page *page[BTRFS_NR_SB_LOG_ZONES]; +		struct btrfs_super_block *super[BTRFS_NR_SB_LOG_ZONES]; +		int i; + +		for (i = 0; i < BTRFS_NR_SB_LOG_ZONES; i++) { +			u64 bytenr; + +			bytenr = ((zones[i].start + zones[i].len) +				   << SECTOR_SHIFT) - BTRFS_SUPER_INFO_SIZE; + +			page[i] = read_cache_page_gfp(mapping, +					bytenr >> PAGE_SHIFT, GFP_NOFS); +			if (IS_ERR(page[i])) { +				if (i == 1) +					btrfs_release_disk_super(super[0]); +				return PTR_ERR(page[i]); +			} +			super[i] = page_address(page[i]); +		} + +		if (super[0]->generation > super[1]->generation) +			sector = zones[1].start; +		else +			sector = zones[0].start; + +		for (i = 0; i < BTRFS_NR_SB_LOG_ZONES; i++) +			btrfs_release_disk_super(super[i]); +	} else if (!full[0] && (empty[1] || full[1])) { +		sector = zones[0].wp; +	} else if (full[0]) { +		sector = zones[1].wp; +	} else { +		return -EUCLEAN; +	} +	*wp_ret = sector << SECTOR_SHIFT; +	return 0; +} + +/* + * The following zones are reserved as the circular buffer on ZONED btrfs. + *  - The primary superblock: zones 0 and 1 + *  - The first copy: zones 16 and 17 + *  - The second copy: zones 1024 or zone at 256GB which is minimum, and + *                     the following one + */ +static inline u32 sb_zone_number(int shift, int mirror) +{ +	ASSERT(mirror < BTRFS_SUPER_MIRROR_MAX); + +	switch (mirror) { +	case 0: return 0; +	case 1: return 16; +	case 2: return min_t(u64, btrfs_sb_offset(mirror) >> shift, 1024); +	} + +	return 0; +} +  static int btrfs_get_dev_zones(struct btrfs_device *device, u64 pos,  			       struct blk_zone *zones, unsigned int *nr_zones)  { @@ -122,6 +222,52 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device)  		goto out;  	} +	/* Validate superblock log */ +	nr_zones = BTRFS_NR_SB_LOG_ZONES; +	for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { +		u32 sb_zone; +		u64 sb_wp; +		int sb_pos = BTRFS_NR_SB_LOG_ZONES * i; + +		sb_zone = sb_zone_number(zone_info->zone_size_shift, i); +		if (sb_zone + 1 >= zone_info->nr_zones) +			continue; + +		sector = sb_zone << (zone_info->zone_size_shift - SECTOR_SHIFT); +		ret = btrfs_get_dev_zones(device, sector << SECTOR_SHIFT, +					  &zone_info->sb_zones[sb_pos], +					  &nr_zones); +		if (ret) +			goto out; + +		if (nr_zones != BTRFS_NR_SB_LOG_ZONES) { +			btrfs_err_in_rcu(device->fs_info, +	"zoned: failed to read super block log zone info at devid %llu zone %u", +					 device->devid, sb_zone); +			ret = -EUCLEAN; +			goto out; +		} + +		/* +		 * If zones[0] is conventional, always use the beggining of the +		 * zone to record superblock. No need to validate in that case. +		 */ +		if (zone_info->sb_zones[BTRFS_NR_SB_LOG_ZONES * i].type == +		    BLK_ZONE_TYPE_CONVENTIONAL) +			continue; + +		ret = sb_write_pointer(device->bdev, +				       &zone_info->sb_zones[sb_pos], &sb_wp); +		if (ret != -ENOENT && ret) { +			btrfs_err_in_rcu(device->fs_info, +			"zoned: super block log zone corrupted devid %llu zone %u", +					 device->devid, sb_zone); +			ret = -EUCLEAN; +			goto out; +		} +	} + +  	kfree(zones);  	device->zone_info = zone_info; @@ -287,3 +433,184 @@ int btrfs_check_mountopts_zoned(struct btrfs_fs_info *info)  	return 0;  } + +static int sb_log_location(struct block_device *bdev, struct blk_zone *zones, +			   int rw, u64 *bytenr_ret) +{ +	u64 wp; +	int ret; + +	if (zones[0].type == BLK_ZONE_TYPE_CONVENTIONAL) { +		*bytenr_ret = zones[0].start << SECTOR_SHIFT; +		return 0; +	} + +	ret = sb_write_pointer(bdev, zones, &wp); +	if (ret != -ENOENT && ret < 0) +		return ret; + +	if (rw == WRITE) { +		struct blk_zone *reset = NULL; + +		if (wp == zones[0].start << SECTOR_SHIFT) +			reset = &zones[0]; +		else if (wp == zones[1].start << SECTOR_SHIFT) +			reset = &zones[1]; + +		if (reset && reset->cond != BLK_ZONE_COND_EMPTY) { +			ASSERT(reset->cond == BLK_ZONE_COND_FULL); + +			ret = blkdev_zone_mgmt(bdev, REQ_OP_ZONE_RESET, +					       reset->start, reset->len, +					       GFP_NOFS); +			if (ret) +				return ret; + +			reset->cond = BLK_ZONE_COND_EMPTY; +			reset->wp = reset->start; +		} +	} else if (ret != -ENOENT) { +		/* For READ, we want the precious one */ +		if (wp == zones[0].start << SECTOR_SHIFT) +			wp = (zones[1].start + zones[1].len) << SECTOR_SHIFT; +		wp -= BTRFS_SUPER_INFO_SIZE; +	} + +	*bytenr_ret = wp; +	return 0; + +} + +int btrfs_sb_log_location_bdev(struct block_device *bdev, int mirror, int rw, +			       u64 *bytenr_ret) +{ +	struct blk_zone zones[BTRFS_NR_SB_LOG_ZONES]; +	unsigned int zone_sectors; +	u32 sb_zone; +	int ret; +	u64 zone_size; +	u8 zone_sectors_shift; +	sector_t nr_sectors; +	u32 nr_zones; + +	if (!bdev_is_zoned(bdev)) { +		*bytenr_ret = btrfs_sb_offset(mirror); +		return 0; +	} + +	ASSERT(rw == READ || rw == WRITE); + +	zone_sectors = bdev_zone_sectors(bdev); +	if (!is_power_of_2(zone_sectors)) +		return -EINVAL; +	zone_size = zone_sectors << SECTOR_SHIFT; +	zone_sectors_shift = ilog2(zone_sectors); +	nr_sectors = bdev->bd_part->nr_sects; +	nr_zones = nr_sectors >> zone_sectors_shift; + +	sb_zone = sb_zone_number(zone_sectors_shift + SECTOR_SHIFT, mirror); +	if (sb_zone + 1 >= nr_zones) +		return -ENOENT; + +	ret = blkdev_report_zones(bdev, sb_zone << zone_sectors_shift, +				  BTRFS_NR_SB_LOG_ZONES, copy_zone_info_cb, +				  zones); +	if (ret < 0) +		return ret; +	if (ret != BTRFS_NR_SB_LOG_ZONES) +		return -EIO; + +	return sb_log_location(bdev, zones, rw, bytenr_ret); +} + +int btrfs_sb_log_location(struct btrfs_device *device, int mirror, int rw, +			  u64 *bytenr_ret) +{ +	struct btrfs_zoned_device_info *zinfo = device->zone_info; +	u32 zone_num; + +	if (!zinfo) { +		*bytenr_ret = btrfs_sb_offset(mirror); +		return 0; +	} + +	zone_num = sb_zone_number(zinfo->zone_size_shift, mirror); +	if (zone_num + 1 >= zinfo->nr_zones) +		return -ENOENT; + +	return sb_log_location(device->bdev, +			       &zinfo->sb_zones[BTRFS_NR_SB_LOG_ZONES * mirror], +			       rw, bytenr_ret); +} + +static inline bool is_sb_log_zone(struct btrfs_zoned_device_info *zinfo, +				  int mirror) +{ +	u32 zone_num; + +	if (!zinfo) +		return false; + +	zone_num = sb_zone_number(zinfo->zone_size_shift, mirror); +	if (zone_num + 1 >= zinfo->nr_zones) +		return false; + +	if (!test_bit(zone_num, zinfo->seq_zones)) +		return false; + +	return true; +} + +void btrfs_advance_sb_log(struct btrfs_device *device, int mirror) +{ +	struct btrfs_zoned_device_info *zinfo = device->zone_info; +	struct blk_zone *zone; + +	if (!is_sb_log_zone(zinfo, mirror)) +		return; + +	zone = &zinfo->sb_zones[BTRFS_NR_SB_LOG_ZONES * mirror]; +	if (zone->cond != BLK_ZONE_COND_FULL) { +		if (zone->cond == BLK_ZONE_COND_EMPTY) +			zone->cond = BLK_ZONE_COND_IMP_OPEN; + +		zone->wp += (BTRFS_SUPER_INFO_SIZE >> SECTOR_SHIFT); + +		if (zone->wp == zone->start + zone->len) +			zone->cond = BLK_ZONE_COND_FULL; + +		return; +	} + +	zone++; +	ASSERT(zone->cond != BLK_ZONE_COND_FULL); +	if (zone->cond == BLK_ZONE_COND_EMPTY) +		zone->cond = BLK_ZONE_COND_IMP_OPEN; + +	zone->wp += (BTRFS_SUPER_INFO_SIZE >> SECTOR_SHIFT); + +	if (zone->wp == zone->start + zone->len) +		zone->cond = BLK_ZONE_COND_FULL; +} + +int btrfs_reset_sb_log_zones(struct block_device *bdev, int mirror) +{ +	sector_t zone_sectors; +	sector_t nr_sectors; +	u8 zone_sectors_shift; +	u32 sb_zone; +	u32 nr_zones; + +	zone_sectors = bdev_zone_sectors(bdev); +	zone_sectors_shift = ilog2(zone_sectors); +	nr_sectors = bdev->bd_part->nr_sects; +	nr_zones = nr_sectors >> zone_sectors_shift; + +	sb_zone = sb_zone_number(zone_sectors_shift + SECTOR_SHIFT, mirror); +	if (sb_zone + 1 >= nr_zones) +		return -ENOENT; + +	return blkdev_zone_mgmt(bdev, REQ_OP_ZONE_RESET, +				sb_zone << zone_sectors_shift, +				zone_sectors * BTRFS_NR_SB_LOG_ZONES, GFP_NOFS); +} diff --git a/fs/btrfs/zoned.h b/fs/btrfs/zoned.h index af01070e185b..8abe2f83272b 100644 --- a/fs/btrfs/zoned.h +++ b/fs/btrfs/zoned.h @@ -5,6 +5,8 @@  #include <linux/types.h>  #include <linux/blkdev.h> +#include "volumes.h" +#include "disk-io.h"  struct btrfs_zoned_device_info {  	/* @@ -17,6 +19,7 @@ struct btrfs_zoned_device_info {  	u32 nr_zones;  	unsigned long *seq_zones;  	unsigned long *empty_zones; +	struct blk_zone sb_zones[2 * BTRFS_SUPER_MIRROR_MAX];  };  #ifdef CONFIG_BLK_DEV_ZONED @@ -26,6 +29,12 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device);  void btrfs_destroy_dev_zone_info(struct btrfs_device *device);  int btrfs_check_zoned_mode(struct btrfs_fs_info *fs_info);  int btrfs_check_mountopts_zoned(struct btrfs_fs_info *info); +int btrfs_sb_log_location_bdev(struct block_device *bdev, int mirror, int rw, +			       u64 *bytenr_ret); +int btrfs_sb_log_location(struct btrfs_device *device, int mirror, int rw, +			  u64 *bytenr_ret); +void btrfs_advance_sb_log(struct btrfs_device *device, int mirror); +int btrfs_reset_sb_log_zones(struct block_device *bdev, int mirror);  #else /* CONFIG_BLK_DEV_ZONED */  static inline int btrfs_get_dev_zone(struct btrfs_device *device, u64 pos,  				     struct blk_zone *zone) @@ -54,6 +63,28 @@ static inline int btrfs_check_mountopts_zoned(struct btrfs_fs_info *info)  	return 0;  } +static inline int btrfs_sb_log_location_bdev(struct block_device *bdev, +					     int mirror, int rw, u64 *bytenr_ret) +{ +	*bytenr_ret = btrfs_sb_offset(mirror); +	return 0; +} + +static inline int btrfs_sb_log_location(struct btrfs_device *device, int mirror, +					int rw, u64 *bytenr_ret) +{ +	*bytenr_ret = btrfs_sb_offset(mirror); +	return 0; +} + +static inline void btrfs_advance_sb_log(struct btrfs_device *device, int mirror) +{ } + +static inline int btrfs_reset_sb_log_zones(struct block_device *bdev, int mirror) +{ +	return 0; +} +  #endif  static inline bool btrfs_dev_is_sequential(struct btrfs_device *device, u64 pos) @@ -117,4 +148,13 @@ static inline bool btrfs_check_device_zone_type(const struct btrfs_fs_info *fs_i  	return bdev_zoned_model(bdev) != BLK_ZONED_HM;  } +static inline bool btrfs_check_super_location(struct btrfs_device *device, u64 pos) +{ +	/* +	 * On a non-zoned device, any address is OK. On a zoned device, +	 * non-SEQUENTIAL WRITE REQUIRED zones are capable. +	 */ +	return device->zone_info == NULL || !btrfs_dev_is_sequential(device, pos); +} +  #endif  | 
