diff options
Diffstat (limited to '')
| -rw-r--r-- | fs/btrfs/ctree.h | 14 | ||||
| -rw-r--r-- | fs/btrfs/dev-replace.c | 7 | ||||
| -rw-r--r-- | fs/btrfs/disk-io.c | 10 | ||||
| -rw-r--r-- | fs/btrfs/super.c | 1 | ||||
| -rw-r--r-- | fs/btrfs/volumes.c | 5 | ||||
| -rw-r--r-- | fs/btrfs/zoned.c | 80 | ||||
| -rw-r--r-- | fs/btrfs/zoned.h | 26 | 
7 files changed, 143 insertions, 0 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index c0c6e79c43f9..8f20219e2caa 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -956,6 +956,15 @@ struct btrfs_fs_info {  	/* Type of exclusive operation running */  	unsigned long exclusive_operation; +	/* +	 * Zone size > 0 when in ZONED mode, otherwise it's used for a check +	 * if the mode is enabled +	 */ +	union { +		u64 zone_size; +		u64 zoned; +	}; +  #ifdef CONFIG_BTRFS_FS_REF_VERIFY  	spinlock_t ref_verify_lock;  	struct rb_root block_tree; @@ -3677,4 +3686,9 @@ static inline int btrfs_is_testing(struct btrfs_fs_info *fs_info)  }  #endif +static inline bool btrfs_is_zoned(const struct btrfs_fs_info *fs_info) +{ +	return fs_info->zoned != 0; +} +  #endif diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 71a1bddcbc76..a98e33f232d5 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -260,6 +260,13 @@ static int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,  		return PTR_ERR(bdev);  	} +	if (!btrfs_check_device_zone_type(fs_info, bdev)) { +		btrfs_err(fs_info, +		"dev-replace: zoned type of target device mismatch with filesystem"); +		ret = -EINVAL; +		goto error; +	} +  	sync_blockdev(bdev);  	list_for_each_entry(device, &fs_info->fs_devices->devices, dev_list) { diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 37b2e0aad162..32e29e2bc99a 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -42,6 +42,7 @@  #include "block-group.h"  #include "discard.h"  #include "space-info.h" +#include "zoned.h"  #define BTRFS_SUPER_FLAG_SUPP	(BTRFS_HEADER_FLAG_WRITTEN |\  				 BTRFS_HEADER_FLAG_RELOC |\ @@ -3046,6 +3047,8 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device  	if (features & BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)  		btrfs_info(fs_info, "has skinny extents"); +	fs_info->zoned = (features & BTRFS_FEATURE_INCOMPAT_ZONED); +  	/*  	 * flag our filesystem as having big metadata blocks if  	 * they are bigger than the page size @@ -3202,6 +3205,13 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device  		goto fail_block_groups;  	} +	ret = btrfs_check_zoned_mode(fs_info); +	if (ret) { +		btrfs_err(fs_info, "failed to initialize zoned mode: %d", +			  ret); +		goto fail_block_groups; +	} +  	ret = btrfs_sysfs_add_fsid(fs_devices);  	if (ret) {  		btrfs_err(fs_info, "failed to init sysfs fsid interface: %d", diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 3c39e363aec5..b754205f21d0 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -44,6 +44,7 @@  #include "backref.h"  #include "space-info.h"  #include "sysfs.h" +#include "zoned.h"  #include "tests/btrfs-tests.h"  #include "block-group.h"  #include "discard.h" diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index f250bf4dc91f..cf3f4975107f 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2515,6 +2515,11 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path  	if (IS_ERR(bdev))  		return PTR_ERR(bdev); +	if (!btrfs_check_device_zone_type(fs_info, bdev)) { +		ret = -EINVAL; +		goto error; +	} +  	if (fs_devices->seeding) {  		seeding_dev = 1;  		down_write(&sb->s_umount); diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 38d7bf45b83f..56bb701e3b53 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -166,3 +166,83 @@ int btrfs_get_dev_zone(struct btrfs_device *device, u64 pos,  	return 0;  } + +int btrfs_check_zoned_mode(struct btrfs_fs_info *fs_info) +{ +	struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; +	struct btrfs_device *device; +	u64 zoned_devices = 0; +	u64 nr_devices = 0; +	u64 zone_size = 0; +	const bool incompat_zoned = btrfs_is_zoned(fs_info); +	int ret = 0; + +	/* Count zoned devices */ +	list_for_each_entry(device, &fs_devices->devices, dev_list) { +		enum blk_zoned_model model; + +		if (!device->bdev) +			continue; + +		model = bdev_zoned_model(device->bdev); +		if (model == BLK_ZONED_HM || +		    (model == BLK_ZONED_HA && incompat_zoned)) { +			zoned_devices++; +			if (!zone_size) { +				zone_size = device->zone_info->zone_size; +			} else if (device->zone_info->zone_size != zone_size) { +				btrfs_err(fs_info, +		"zoned: unequal block device zone sizes: have %llu found %llu", +					  device->zone_info->zone_size, +					  zone_size); +				ret = -EINVAL; +				goto out; +			} +		} +		nr_devices++; +	} + +	if (!zoned_devices && !incompat_zoned) +		goto out; + +	if (!zoned_devices && incompat_zoned) { +		/* No zoned block device found on ZONED filesystem */ +		btrfs_err(fs_info, +			  "zoned: no zoned devices found on a zoned filesystem"); +		ret = -EINVAL; +		goto out; +	} + +	if (zoned_devices && !incompat_zoned) { +		btrfs_err(fs_info, +			  "zoned: mode not enabled but zoned device found"); +		ret = -EINVAL; +		goto out; +	} + +	if (zoned_devices != nr_devices) { +		btrfs_err(fs_info, +			  "zoned: cannot mix zoned and regular devices"); +		ret = -EINVAL; +		goto out; +	} + +	/* +	 * stripe_size is always aligned to BTRFS_STRIPE_LEN in +	 * __btrfs_alloc_chunk(). Since we want stripe_len == zone_size, +	 * check the alignment here. +	 */ +	if (!IS_ALIGNED(zone_size, BTRFS_STRIPE_LEN)) { +		btrfs_err(fs_info, +			  "zoned: zone size %llu not aligned to stripe %u", +			  zone_size, BTRFS_STRIPE_LEN); +		ret = -EINVAL; +		goto out; +	} + +	fs_info->zone_size = zone_size; + +	btrfs_info(fs_info, "zoned mode enabled with zone size %llu", zone_size); +out: +	return ret; +} diff --git a/fs/btrfs/zoned.h b/fs/btrfs/zoned.h index 00c871f09d03..e4ee1a50be2d 100644 --- a/fs/btrfs/zoned.h +++ b/fs/btrfs/zoned.h @@ -4,6 +4,7 @@  #define BTRFS_ZONED_H  #include <linux/types.h> +#include <linux/blkdev.h>  struct btrfs_zoned_device_info {  	/* @@ -22,6 +23,7 @@ int btrfs_get_dev_zone(struct btrfs_device *device, u64 pos,  		       struct blk_zone *zone);  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);  #else /* CONFIG_BLK_DEV_ZONED */  static inline int btrfs_get_dev_zone(struct btrfs_device *device, u64 pos,  				     struct blk_zone *zone) @@ -36,6 +38,15 @@ static inline int btrfs_get_dev_zone_info(struct btrfs_device *device)  static inline void btrfs_destroy_dev_zone_info(struct btrfs_device *device) { } +static inline int btrfs_check_zoned_mode(const struct btrfs_fs_info *fs_info) +{ +	if (!btrfs_is_zoned(fs_info)) +		return 0; + +	btrfs_err(fs_info, "zoned block devices support is not enabled"); +	return -EOPNOTSUPP; +} +  #endif  static inline bool btrfs_dev_is_sequential(struct btrfs_device *device, u64 pos) @@ -84,4 +95,19 @@ static inline void btrfs_dev_clear_zone_empty(struct btrfs_device *device, u64 p  	btrfs_dev_set_empty_zone_bit(device, pos, false);  } +static inline bool btrfs_check_device_zone_type(const struct btrfs_fs_info *fs_info, +						struct block_device *bdev) +{ +	u64 zone_size; + +	if (btrfs_is_zoned(fs_info)) { +		zone_size = bdev_zone_sectors(bdev) << SECTOR_SHIFT; +		/* Do not allow non-zoned device */ +		return bdev_is_zoned(bdev) && fs_info->zone_size == zone_size; +	} + +	/* Do not allow Host Manged zoned device */ +	return bdev_zoned_model(bdev) != BLK_ZONED_HM; +} +  #endif  | 
