162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/vmalloc.h>
362306a36Sopenharmony_ci#include <linux/bitmap.h>
462306a36Sopenharmony_ci#include "null_blk.h"
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
762306a36Sopenharmony_ci#include "trace.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#undef pr_fmt
1062306a36Sopenharmony_ci#define pr_fmt(fmt)	"null_blk: " fmt
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic inline sector_t mb_to_sects(unsigned long mb)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	return ((sector_t)mb * SZ_1M) >> SECTOR_SHIFT;
1562306a36Sopenharmony_ci}
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic inline unsigned int null_zone_no(struct nullb_device *dev, sector_t sect)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	return sect >> ilog2(dev->zone_size_sects);
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic inline void null_lock_zone_res(struct nullb_device *dev)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	if (dev->need_zone_res_mgmt)
2562306a36Sopenharmony_ci		spin_lock_irq(&dev->zone_res_lock);
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic inline void null_unlock_zone_res(struct nullb_device *dev)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	if (dev->need_zone_res_mgmt)
3162306a36Sopenharmony_ci		spin_unlock_irq(&dev->zone_res_lock);
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic inline void null_init_zone_lock(struct nullb_device *dev,
3562306a36Sopenharmony_ci				       struct nullb_zone *zone)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	if (!dev->memory_backed)
3862306a36Sopenharmony_ci		spin_lock_init(&zone->spinlock);
3962306a36Sopenharmony_ci	else
4062306a36Sopenharmony_ci		mutex_init(&zone->mutex);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic inline void null_lock_zone(struct nullb_device *dev,
4462306a36Sopenharmony_ci				  struct nullb_zone *zone)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	if (!dev->memory_backed)
4762306a36Sopenharmony_ci		spin_lock_irq(&zone->spinlock);
4862306a36Sopenharmony_ci	else
4962306a36Sopenharmony_ci		mutex_lock(&zone->mutex);
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic inline void null_unlock_zone(struct nullb_device *dev,
5362306a36Sopenharmony_ci				    struct nullb_zone *zone)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	if (!dev->memory_backed)
5662306a36Sopenharmony_ci		spin_unlock_irq(&zone->spinlock);
5762306a36Sopenharmony_ci	else
5862306a36Sopenharmony_ci		mutex_unlock(&zone->mutex);
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ciint null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	sector_t dev_capacity_sects, zone_capacity_sects;
6462306a36Sopenharmony_ci	struct nullb_zone *zone;
6562306a36Sopenharmony_ci	sector_t sector = 0;
6662306a36Sopenharmony_ci	unsigned int i;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (!is_power_of_2(dev->zone_size)) {
6962306a36Sopenharmony_ci		pr_err("zone_size must be power-of-two\n");
7062306a36Sopenharmony_ci		return -EINVAL;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci	if (dev->zone_size > dev->size) {
7362306a36Sopenharmony_ci		pr_err("Zone size larger than device capacity\n");
7462306a36Sopenharmony_ci		return -EINVAL;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (!dev->zone_capacity)
7862306a36Sopenharmony_ci		dev->zone_capacity = dev->zone_size;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (dev->zone_capacity > dev->zone_size) {
8162306a36Sopenharmony_ci		pr_err("zone capacity (%lu MB) larger than zone size (%lu MB)\n",
8262306a36Sopenharmony_ci		       dev->zone_capacity, dev->zone_size);
8362306a36Sopenharmony_ci		return -EINVAL;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	zone_capacity_sects = mb_to_sects(dev->zone_capacity);
8762306a36Sopenharmony_ci	dev_capacity_sects = mb_to_sects(dev->size);
8862306a36Sopenharmony_ci	dev->zone_size_sects = mb_to_sects(dev->zone_size);
8962306a36Sopenharmony_ci	dev->nr_zones = round_up(dev_capacity_sects, dev->zone_size_sects)
9062306a36Sopenharmony_ci		>> ilog2(dev->zone_size_sects);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	dev->zones = kvmalloc_array(dev->nr_zones, sizeof(struct nullb_zone),
9362306a36Sopenharmony_ci				    GFP_KERNEL | __GFP_ZERO);
9462306a36Sopenharmony_ci	if (!dev->zones)
9562306a36Sopenharmony_ci		return -ENOMEM;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	spin_lock_init(&dev->zone_res_lock);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (dev->zone_nr_conv >= dev->nr_zones) {
10062306a36Sopenharmony_ci		dev->zone_nr_conv = dev->nr_zones - 1;
10162306a36Sopenharmony_ci		pr_info("changed the number of conventional zones to %u",
10262306a36Sopenharmony_ci			dev->zone_nr_conv);
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/* Max active zones has to be < nbr of seq zones in order to be enforceable */
10662306a36Sopenharmony_ci	if (dev->zone_max_active >= dev->nr_zones - dev->zone_nr_conv) {
10762306a36Sopenharmony_ci		dev->zone_max_active = 0;
10862306a36Sopenharmony_ci		pr_info("zone_max_active limit disabled, limit >= zone count\n");
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* Max open zones has to be <= max active zones */
11262306a36Sopenharmony_ci	if (dev->zone_max_active && dev->zone_max_open > dev->zone_max_active) {
11362306a36Sopenharmony_ci		dev->zone_max_open = dev->zone_max_active;
11462306a36Sopenharmony_ci		pr_info("changed the maximum number of open zones to %u\n",
11562306a36Sopenharmony_ci			dev->nr_zones);
11662306a36Sopenharmony_ci	} else if (dev->zone_max_open >= dev->nr_zones - dev->zone_nr_conv) {
11762306a36Sopenharmony_ci		dev->zone_max_open = 0;
11862306a36Sopenharmony_ci		pr_info("zone_max_open limit disabled, limit >= zone count\n");
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci	dev->need_zone_res_mgmt = dev->zone_max_active || dev->zone_max_open;
12162306a36Sopenharmony_ci	dev->imp_close_zone_no = dev->zone_nr_conv;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	for (i = 0; i <  dev->zone_nr_conv; i++) {
12462306a36Sopenharmony_ci		zone = &dev->zones[i];
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci		null_init_zone_lock(dev, zone);
12762306a36Sopenharmony_ci		zone->start = sector;
12862306a36Sopenharmony_ci		zone->len = dev->zone_size_sects;
12962306a36Sopenharmony_ci		zone->capacity = zone->len;
13062306a36Sopenharmony_ci		zone->wp = zone->start + zone->len;
13162306a36Sopenharmony_ci		zone->type = BLK_ZONE_TYPE_CONVENTIONAL;
13262306a36Sopenharmony_ci		zone->cond = BLK_ZONE_COND_NOT_WP;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		sector += dev->zone_size_sects;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	for (i = dev->zone_nr_conv; i < dev->nr_zones; i++) {
13862306a36Sopenharmony_ci		zone = &dev->zones[i];
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci		null_init_zone_lock(dev, zone);
14162306a36Sopenharmony_ci		zone->start = zone->wp = sector;
14262306a36Sopenharmony_ci		if (zone->start + dev->zone_size_sects > dev_capacity_sects)
14362306a36Sopenharmony_ci			zone->len = dev_capacity_sects - zone->start;
14462306a36Sopenharmony_ci		else
14562306a36Sopenharmony_ci			zone->len = dev->zone_size_sects;
14662306a36Sopenharmony_ci		zone->capacity =
14762306a36Sopenharmony_ci			min_t(sector_t, zone->len, zone_capacity_sects);
14862306a36Sopenharmony_ci		zone->type = BLK_ZONE_TYPE_SEQWRITE_REQ;
14962306a36Sopenharmony_ci		zone->cond = BLK_ZONE_COND_EMPTY;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		sector += dev->zone_size_sects;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return 0;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ciint null_register_zoned_dev(struct nullb *nullb)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct nullb_device *dev = nullb->dev;
16062306a36Sopenharmony_ci	struct request_queue *q = nullb->q;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	disk_set_zoned(nullb->disk, BLK_ZONED_HM);
16362306a36Sopenharmony_ci	blk_queue_flag_set(QUEUE_FLAG_ZONE_RESETALL, q);
16462306a36Sopenharmony_ci	blk_queue_required_elevator_features(q, ELEVATOR_F_ZBD_SEQ_WRITE);
16562306a36Sopenharmony_ci	blk_queue_chunk_sectors(q, dev->zone_size_sects);
16662306a36Sopenharmony_ci	nullb->disk->nr_zones = bdev_nr_zones(nullb->disk->part0);
16762306a36Sopenharmony_ci	blk_queue_max_zone_append_sectors(q, dev->zone_size_sects);
16862306a36Sopenharmony_ci	disk_set_max_open_zones(nullb->disk, dev->zone_max_open);
16962306a36Sopenharmony_ci	disk_set_max_active_zones(nullb->disk, dev->zone_max_active);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (queue_is_mq(q))
17262306a36Sopenharmony_ci		return blk_revalidate_disk_zones(nullb->disk, NULL);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return 0;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_civoid null_free_zoned_dev(struct nullb_device *dev)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	kvfree(dev->zones);
18062306a36Sopenharmony_ci	dev->zones = NULL;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ciint null_report_zones(struct gendisk *disk, sector_t sector,
18462306a36Sopenharmony_ci		unsigned int nr_zones, report_zones_cb cb, void *data)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct nullb *nullb = disk->private_data;
18762306a36Sopenharmony_ci	struct nullb_device *dev = nullb->dev;
18862306a36Sopenharmony_ci	unsigned int first_zone, i;
18962306a36Sopenharmony_ci	struct nullb_zone *zone;
19062306a36Sopenharmony_ci	struct blk_zone blkz;
19162306a36Sopenharmony_ci	int error;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	first_zone = null_zone_no(dev, sector);
19462306a36Sopenharmony_ci	if (first_zone >= dev->nr_zones)
19562306a36Sopenharmony_ci		return 0;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	nr_zones = min(nr_zones, dev->nr_zones - first_zone);
19862306a36Sopenharmony_ci	trace_nullb_report_zones(nullb, nr_zones);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	memset(&blkz, 0, sizeof(struct blk_zone));
20162306a36Sopenharmony_ci	zone = &dev->zones[first_zone];
20262306a36Sopenharmony_ci	for (i = 0; i < nr_zones; i++, zone++) {
20362306a36Sopenharmony_ci		/*
20462306a36Sopenharmony_ci		 * Stacked DM target drivers will remap the zone information by
20562306a36Sopenharmony_ci		 * modifying the zone information passed to the report callback.
20662306a36Sopenharmony_ci		 * So use a local copy to avoid corruption of the device zone
20762306a36Sopenharmony_ci		 * array.
20862306a36Sopenharmony_ci		 */
20962306a36Sopenharmony_ci		null_lock_zone(dev, zone);
21062306a36Sopenharmony_ci		blkz.start = zone->start;
21162306a36Sopenharmony_ci		blkz.len = zone->len;
21262306a36Sopenharmony_ci		blkz.wp = zone->wp;
21362306a36Sopenharmony_ci		blkz.type = zone->type;
21462306a36Sopenharmony_ci		blkz.cond = zone->cond;
21562306a36Sopenharmony_ci		blkz.capacity = zone->capacity;
21662306a36Sopenharmony_ci		null_unlock_zone(dev, zone);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci		error = cb(&blkz, i, data);
21962306a36Sopenharmony_ci		if (error)
22062306a36Sopenharmony_ci			return error;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	return nr_zones;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/*
22762306a36Sopenharmony_ci * This is called in the case of memory backing from null_process_cmd()
22862306a36Sopenharmony_ci * with the target zone already locked.
22962306a36Sopenharmony_ci */
23062306a36Sopenharmony_cisize_t null_zone_valid_read_len(struct nullb *nullb,
23162306a36Sopenharmony_ci				sector_t sector, unsigned int len)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct nullb_device *dev = nullb->dev;
23462306a36Sopenharmony_ci	struct nullb_zone *zone = &dev->zones[null_zone_no(dev, sector)];
23562306a36Sopenharmony_ci	unsigned int nr_sectors = len >> SECTOR_SHIFT;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	/* Read must be below the write pointer position */
23862306a36Sopenharmony_ci	if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL ||
23962306a36Sopenharmony_ci	    sector + nr_sectors <= zone->wp)
24062306a36Sopenharmony_ci		return len;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (sector > zone->wp)
24362306a36Sopenharmony_ci		return 0;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	return (zone->wp - sector) << SECTOR_SHIFT;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic blk_status_t __null_close_zone(struct nullb_device *dev,
24962306a36Sopenharmony_ci				      struct nullb_zone *zone)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	switch (zone->cond) {
25262306a36Sopenharmony_ci	case BLK_ZONE_COND_CLOSED:
25362306a36Sopenharmony_ci		/* close operation on closed is not an error */
25462306a36Sopenharmony_ci		return BLK_STS_OK;
25562306a36Sopenharmony_ci	case BLK_ZONE_COND_IMP_OPEN:
25662306a36Sopenharmony_ci		dev->nr_zones_imp_open--;
25762306a36Sopenharmony_ci		break;
25862306a36Sopenharmony_ci	case BLK_ZONE_COND_EXP_OPEN:
25962306a36Sopenharmony_ci		dev->nr_zones_exp_open--;
26062306a36Sopenharmony_ci		break;
26162306a36Sopenharmony_ci	case BLK_ZONE_COND_EMPTY:
26262306a36Sopenharmony_ci	case BLK_ZONE_COND_FULL:
26362306a36Sopenharmony_ci	default:
26462306a36Sopenharmony_ci		return BLK_STS_IOERR;
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (zone->wp == zone->start) {
26862306a36Sopenharmony_ci		zone->cond = BLK_ZONE_COND_EMPTY;
26962306a36Sopenharmony_ci	} else {
27062306a36Sopenharmony_ci		zone->cond = BLK_ZONE_COND_CLOSED;
27162306a36Sopenharmony_ci		dev->nr_zones_closed++;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	return BLK_STS_OK;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic void null_close_imp_open_zone(struct nullb_device *dev)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct nullb_zone *zone;
28062306a36Sopenharmony_ci	unsigned int zno, i;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	zno = dev->imp_close_zone_no;
28362306a36Sopenharmony_ci	if (zno >= dev->nr_zones)
28462306a36Sopenharmony_ci		zno = dev->zone_nr_conv;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	for (i = dev->zone_nr_conv; i < dev->nr_zones; i++) {
28762306a36Sopenharmony_ci		zone = &dev->zones[zno];
28862306a36Sopenharmony_ci		zno++;
28962306a36Sopenharmony_ci		if (zno >= dev->nr_zones)
29062306a36Sopenharmony_ci			zno = dev->zone_nr_conv;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		if (zone->cond == BLK_ZONE_COND_IMP_OPEN) {
29362306a36Sopenharmony_ci			__null_close_zone(dev, zone);
29462306a36Sopenharmony_ci			dev->imp_close_zone_no = zno;
29562306a36Sopenharmony_ci			return;
29662306a36Sopenharmony_ci		}
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic blk_status_t null_check_active(struct nullb_device *dev)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	if (!dev->zone_max_active)
30362306a36Sopenharmony_ci		return BLK_STS_OK;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (dev->nr_zones_exp_open + dev->nr_zones_imp_open +
30662306a36Sopenharmony_ci			dev->nr_zones_closed < dev->zone_max_active)
30762306a36Sopenharmony_ci		return BLK_STS_OK;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	return BLK_STS_ZONE_ACTIVE_RESOURCE;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic blk_status_t null_check_open(struct nullb_device *dev)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	if (!dev->zone_max_open)
31562306a36Sopenharmony_ci		return BLK_STS_OK;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (dev->nr_zones_exp_open + dev->nr_zones_imp_open < dev->zone_max_open)
31862306a36Sopenharmony_ci		return BLK_STS_OK;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (dev->nr_zones_imp_open) {
32162306a36Sopenharmony_ci		if (null_check_active(dev) == BLK_STS_OK) {
32262306a36Sopenharmony_ci			null_close_imp_open_zone(dev);
32362306a36Sopenharmony_ci			return BLK_STS_OK;
32462306a36Sopenharmony_ci		}
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	return BLK_STS_ZONE_OPEN_RESOURCE;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci/*
33162306a36Sopenharmony_ci * This function matches the manage open zone resources function in the ZBC standard,
33262306a36Sopenharmony_ci * with the addition of max active zones support (added in the ZNS standard).
33362306a36Sopenharmony_ci *
33462306a36Sopenharmony_ci * The function determines if a zone can transition to implicit open or explicit open,
33562306a36Sopenharmony_ci * while maintaining the max open zone (and max active zone) limit(s). It may close an
33662306a36Sopenharmony_ci * implicit open zone in order to make additional zone resources available.
33762306a36Sopenharmony_ci *
33862306a36Sopenharmony_ci * ZBC states that an implicit open zone shall be closed only if there is not
33962306a36Sopenharmony_ci * room within the open limit. However, with the addition of an active limit,
34062306a36Sopenharmony_ci * it is not certain that closing an implicit open zone will allow a new zone
34162306a36Sopenharmony_ci * to be opened, since we might already be at the active limit capacity.
34262306a36Sopenharmony_ci */
34362306a36Sopenharmony_cistatic blk_status_t null_check_zone_resources(struct nullb_device *dev,
34462306a36Sopenharmony_ci					      struct nullb_zone *zone)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	blk_status_t ret;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	switch (zone->cond) {
34962306a36Sopenharmony_ci	case BLK_ZONE_COND_EMPTY:
35062306a36Sopenharmony_ci		ret = null_check_active(dev);
35162306a36Sopenharmony_ci		if (ret != BLK_STS_OK)
35262306a36Sopenharmony_ci			return ret;
35362306a36Sopenharmony_ci		fallthrough;
35462306a36Sopenharmony_ci	case BLK_ZONE_COND_CLOSED:
35562306a36Sopenharmony_ci		return null_check_open(dev);
35662306a36Sopenharmony_ci	default:
35762306a36Sopenharmony_ci		/* Should never be called for other states */
35862306a36Sopenharmony_ci		WARN_ON(1);
35962306a36Sopenharmony_ci		return BLK_STS_IOERR;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
36462306a36Sopenharmony_ci				    unsigned int nr_sectors, bool append)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct nullb_device *dev = cmd->nq->dev;
36762306a36Sopenharmony_ci	unsigned int zno = null_zone_no(dev, sector);
36862306a36Sopenharmony_ci	struct nullb_zone *zone = &dev->zones[zno];
36962306a36Sopenharmony_ci	blk_status_t ret;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	trace_nullb_zone_op(cmd, zno, zone->cond);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL) {
37462306a36Sopenharmony_ci		if (append)
37562306a36Sopenharmony_ci			return BLK_STS_IOERR;
37662306a36Sopenharmony_ci		return null_process_cmd(cmd, REQ_OP_WRITE, sector, nr_sectors);
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	null_lock_zone(dev, zone);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (zone->cond == BLK_ZONE_COND_FULL ||
38262306a36Sopenharmony_ci	    zone->cond == BLK_ZONE_COND_READONLY ||
38362306a36Sopenharmony_ci	    zone->cond == BLK_ZONE_COND_OFFLINE) {
38462306a36Sopenharmony_ci		/* Cannot write to the zone */
38562306a36Sopenharmony_ci		ret = BLK_STS_IOERR;
38662306a36Sopenharmony_ci		goto unlock;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/*
39062306a36Sopenharmony_ci	 * Regular writes must be at the write pointer position.
39162306a36Sopenharmony_ci	 * Zone append writes are automatically issued at the write
39262306a36Sopenharmony_ci	 * pointer and the position returned using the request or BIO
39362306a36Sopenharmony_ci	 * sector.
39462306a36Sopenharmony_ci	 */
39562306a36Sopenharmony_ci	if (append) {
39662306a36Sopenharmony_ci		sector = zone->wp;
39762306a36Sopenharmony_ci		if (dev->queue_mode == NULL_Q_MQ)
39862306a36Sopenharmony_ci			cmd->rq->__sector = sector;
39962306a36Sopenharmony_ci		else
40062306a36Sopenharmony_ci			cmd->bio->bi_iter.bi_sector = sector;
40162306a36Sopenharmony_ci	} else if (sector != zone->wp) {
40262306a36Sopenharmony_ci		ret = BLK_STS_IOERR;
40362306a36Sopenharmony_ci		goto unlock;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (zone->wp + nr_sectors > zone->start + zone->capacity) {
40762306a36Sopenharmony_ci		ret = BLK_STS_IOERR;
40862306a36Sopenharmony_ci		goto unlock;
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (zone->cond == BLK_ZONE_COND_CLOSED ||
41262306a36Sopenharmony_ci	    zone->cond == BLK_ZONE_COND_EMPTY) {
41362306a36Sopenharmony_ci		null_lock_zone_res(dev);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		ret = null_check_zone_resources(dev, zone);
41662306a36Sopenharmony_ci		if (ret != BLK_STS_OK) {
41762306a36Sopenharmony_ci			null_unlock_zone_res(dev);
41862306a36Sopenharmony_ci			goto unlock;
41962306a36Sopenharmony_ci		}
42062306a36Sopenharmony_ci		if (zone->cond == BLK_ZONE_COND_CLOSED) {
42162306a36Sopenharmony_ci			dev->nr_zones_closed--;
42262306a36Sopenharmony_ci			dev->nr_zones_imp_open++;
42362306a36Sopenharmony_ci		} else if (zone->cond == BLK_ZONE_COND_EMPTY) {
42462306a36Sopenharmony_ci			dev->nr_zones_imp_open++;
42562306a36Sopenharmony_ci		}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci		if (zone->cond != BLK_ZONE_COND_EXP_OPEN)
42862306a36Sopenharmony_ci			zone->cond = BLK_ZONE_COND_IMP_OPEN;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		null_unlock_zone_res(dev);
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	ret = null_process_cmd(cmd, REQ_OP_WRITE, sector, nr_sectors);
43462306a36Sopenharmony_ci	if (ret != BLK_STS_OK)
43562306a36Sopenharmony_ci		goto unlock;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	zone->wp += nr_sectors;
43862306a36Sopenharmony_ci	if (zone->wp == zone->start + zone->capacity) {
43962306a36Sopenharmony_ci		null_lock_zone_res(dev);
44062306a36Sopenharmony_ci		if (zone->cond == BLK_ZONE_COND_EXP_OPEN)
44162306a36Sopenharmony_ci			dev->nr_zones_exp_open--;
44262306a36Sopenharmony_ci		else if (zone->cond == BLK_ZONE_COND_IMP_OPEN)
44362306a36Sopenharmony_ci			dev->nr_zones_imp_open--;
44462306a36Sopenharmony_ci		zone->cond = BLK_ZONE_COND_FULL;
44562306a36Sopenharmony_ci		null_unlock_zone_res(dev);
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	ret = BLK_STS_OK;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ciunlock:
45162306a36Sopenharmony_ci	null_unlock_zone(dev, zone);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	return ret;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic blk_status_t null_open_zone(struct nullb_device *dev,
45762306a36Sopenharmony_ci				   struct nullb_zone *zone)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	blk_status_t ret = BLK_STS_OK;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
46262306a36Sopenharmony_ci		return BLK_STS_IOERR;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	null_lock_zone_res(dev);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	switch (zone->cond) {
46762306a36Sopenharmony_ci	case BLK_ZONE_COND_EXP_OPEN:
46862306a36Sopenharmony_ci		/* open operation on exp open is not an error */
46962306a36Sopenharmony_ci		goto unlock;
47062306a36Sopenharmony_ci	case BLK_ZONE_COND_EMPTY:
47162306a36Sopenharmony_ci		ret = null_check_zone_resources(dev, zone);
47262306a36Sopenharmony_ci		if (ret != BLK_STS_OK)
47362306a36Sopenharmony_ci			goto unlock;
47462306a36Sopenharmony_ci		break;
47562306a36Sopenharmony_ci	case BLK_ZONE_COND_IMP_OPEN:
47662306a36Sopenharmony_ci		dev->nr_zones_imp_open--;
47762306a36Sopenharmony_ci		break;
47862306a36Sopenharmony_ci	case BLK_ZONE_COND_CLOSED:
47962306a36Sopenharmony_ci		ret = null_check_zone_resources(dev, zone);
48062306a36Sopenharmony_ci		if (ret != BLK_STS_OK)
48162306a36Sopenharmony_ci			goto unlock;
48262306a36Sopenharmony_ci		dev->nr_zones_closed--;
48362306a36Sopenharmony_ci		break;
48462306a36Sopenharmony_ci	case BLK_ZONE_COND_FULL:
48562306a36Sopenharmony_ci	default:
48662306a36Sopenharmony_ci		ret = BLK_STS_IOERR;
48762306a36Sopenharmony_ci		goto unlock;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	zone->cond = BLK_ZONE_COND_EXP_OPEN;
49162306a36Sopenharmony_ci	dev->nr_zones_exp_open++;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ciunlock:
49462306a36Sopenharmony_ci	null_unlock_zone_res(dev);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	return ret;
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic blk_status_t null_close_zone(struct nullb_device *dev,
50062306a36Sopenharmony_ci				    struct nullb_zone *zone)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	blk_status_t ret;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
50562306a36Sopenharmony_ci		return BLK_STS_IOERR;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	null_lock_zone_res(dev);
50862306a36Sopenharmony_ci	ret = __null_close_zone(dev, zone);
50962306a36Sopenharmony_ci	null_unlock_zone_res(dev);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	return ret;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic blk_status_t null_finish_zone(struct nullb_device *dev,
51562306a36Sopenharmony_ci				     struct nullb_zone *zone)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	blk_status_t ret = BLK_STS_OK;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
52062306a36Sopenharmony_ci		return BLK_STS_IOERR;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	null_lock_zone_res(dev);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	switch (zone->cond) {
52562306a36Sopenharmony_ci	case BLK_ZONE_COND_FULL:
52662306a36Sopenharmony_ci		/* finish operation on full is not an error */
52762306a36Sopenharmony_ci		goto unlock;
52862306a36Sopenharmony_ci	case BLK_ZONE_COND_EMPTY:
52962306a36Sopenharmony_ci		ret = null_check_zone_resources(dev, zone);
53062306a36Sopenharmony_ci		if (ret != BLK_STS_OK)
53162306a36Sopenharmony_ci			goto unlock;
53262306a36Sopenharmony_ci		break;
53362306a36Sopenharmony_ci	case BLK_ZONE_COND_IMP_OPEN:
53462306a36Sopenharmony_ci		dev->nr_zones_imp_open--;
53562306a36Sopenharmony_ci		break;
53662306a36Sopenharmony_ci	case BLK_ZONE_COND_EXP_OPEN:
53762306a36Sopenharmony_ci		dev->nr_zones_exp_open--;
53862306a36Sopenharmony_ci		break;
53962306a36Sopenharmony_ci	case BLK_ZONE_COND_CLOSED:
54062306a36Sopenharmony_ci		ret = null_check_zone_resources(dev, zone);
54162306a36Sopenharmony_ci		if (ret != BLK_STS_OK)
54262306a36Sopenharmony_ci			goto unlock;
54362306a36Sopenharmony_ci		dev->nr_zones_closed--;
54462306a36Sopenharmony_ci		break;
54562306a36Sopenharmony_ci	default:
54662306a36Sopenharmony_ci		ret = BLK_STS_IOERR;
54762306a36Sopenharmony_ci		goto unlock;
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	zone->cond = BLK_ZONE_COND_FULL;
55162306a36Sopenharmony_ci	zone->wp = zone->start + zone->len;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ciunlock:
55462306a36Sopenharmony_ci	null_unlock_zone_res(dev);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	return ret;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic blk_status_t null_reset_zone(struct nullb_device *dev,
56062306a36Sopenharmony_ci				    struct nullb_zone *zone)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
56362306a36Sopenharmony_ci		return BLK_STS_IOERR;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	null_lock_zone_res(dev);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	switch (zone->cond) {
56862306a36Sopenharmony_ci	case BLK_ZONE_COND_EMPTY:
56962306a36Sopenharmony_ci		/* reset operation on empty is not an error */
57062306a36Sopenharmony_ci		null_unlock_zone_res(dev);
57162306a36Sopenharmony_ci		return BLK_STS_OK;
57262306a36Sopenharmony_ci	case BLK_ZONE_COND_IMP_OPEN:
57362306a36Sopenharmony_ci		dev->nr_zones_imp_open--;
57462306a36Sopenharmony_ci		break;
57562306a36Sopenharmony_ci	case BLK_ZONE_COND_EXP_OPEN:
57662306a36Sopenharmony_ci		dev->nr_zones_exp_open--;
57762306a36Sopenharmony_ci		break;
57862306a36Sopenharmony_ci	case BLK_ZONE_COND_CLOSED:
57962306a36Sopenharmony_ci		dev->nr_zones_closed--;
58062306a36Sopenharmony_ci		break;
58162306a36Sopenharmony_ci	case BLK_ZONE_COND_FULL:
58262306a36Sopenharmony_ci		break;
58362306a36Sopenharmony_ci	default:
58462306a36Sopenharmony_ci		null_unlock_zone_res(dev);
58562306a36Sopenharmony_ci		return BLK_STS_IOERR;
58662306a36Sopenharmony_ci	}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	zone->cond = BLK_ZONE_COND_EMPTY;
58962306a36Sopenharmony_ci	zone->wp = zone->start;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	null_unlock_zone_res(dev);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (dev->memory_backed)
59462306a36Sopenharmony_ci		return null_handle_discard(dev, zone->start, zone->len);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	return BLK_STS_OK;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_op op,
60062306a36Sopenharmony_ci				   sector_t sector)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	struct nullb_device *dev = cmd->nq->dev;
60362306a36Sopenharmony_ci	unsigned int zone_no;
60462306a36Sopenharmony_ci	struct nullb_zone *zone;
60562306a36Sopenharmony_ci	blk_status_t ret;
60662306a36Sopenharmony_ci	size_t i;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	if (op == REQ_OP_ZONE_RESET_ALL) {
60962306a36Sopenharmony_ci		for (i = dev->zone_nr_conv; i < dev->nr_zones; i++) {
61062306a36Sopenharmony_ci			zone = &dev->zones[i];
61162306a36Sopenharmony_ci			null_lock_zone(dev, zone);
61262306a36Sopenharmony_ci			if (zone->cond != BLK_ZONE_COND_EMPTY &&
61362306a36Sopenharmony_ci			    zone->cond != BLK_ZONE_COND_READONLY &&
61462306a36Sopenharmony_ci			    zone->cond != BLK_ZONE_COND_OFFLINE) {
61562306a36Sopenharmony_ci				null_reset_zone(dev, zone);
61662306a36Sopenharmony_ci				trace_nullb_zone_op(cmd, i, zone->cond);
61762306a36Sopenharmony_ci			}
61862306a36Sopenharmony_ci			null_unlock_zone(dev, zone);
61962306a36Sopenharmony_ci		}
62062306a36Sopenharmony_ci		return BLK_STS_OK;
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	zone_no = null_zone_no(dev, sector);
62462306a36Sopenharmony_ci	zone = &dev->zones[zone_no];
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	null_lock_zone(dev, zone);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if (zone->cond == BLK_ZONE_COND_READONLY ||
62962306a36Sopenharmony_ci	    zone->cond == BLK_ZONE_COND_OFFLINE) {
63062306a36Sopenharmony_ci		ret = BLK_STS_IOERR;
63162306a36Sopenharmony_ci		goto unlock;
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	switch (op) {
63562306a36Sopenharmony_ci	case REQ_OP_ZONE_RESET:
63662306a36Sopenharmony_ci		ret = null_reset_zone(dev, zone);
63762306a36Sopenharmony_ci		break;
63862306a36Sopenharmony_ci	case REQ_OP_ZONE_OPEN:
63962306a36Sopenharmony_ci		ret = null_open_zone(dev, zone);
64062306a36Sopenharmony_ci		break;
64162306a36Sopenharmony_ci	case REQ_OP_ZONE_CLOSE:
64262306a36Sopenharmony_ci		ret = null_close_zone(dev, zone);
64362306a36Sopenharmony_ci		break;
64462306a36Sopenharmony_ci	case REQ_OP_ZONE_FINISH:
64562306a36Sopenharmony_ci		ret = null_finish_zone(dev, zone);
64662306a36Sopenharmony_ci		break;
64762306a36Sopenharmony_ci	default:
64862306a36Sopenharmony_ci		ret = BLK_STS_NOTSUPP;
64962306a36Sopenharmony_ci		break;
65062306a36Sopenharmony_ci	}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	if (ret == BLK_STS_OK)
65362306a36Sopenharmony_ci		trace_nullb_zone_op(cmd, zone_no, zone->cond);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ciunlock:
65662306a36Sopenharmony_ci	null_unlock_zone(dev, zone);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	return ret;
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ciblk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_op op,
66262306a36Sopenharmony_ci				    sector_t sector, sector_t nr_sectors)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	struct nullb_device *dev;
66562306a36Sopenharmony_ci	struct nullb_zone *zone;
66662306a36Sopenharmony_ci	blk_status_t sts;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	switch (op) {
66962306a36Sopenharmony_ci	case REQ_OP_WRITE:
67062306a36Sopenharmony_ci		return null_zone_write(cmd, sector, nr_sectors, false);
67162306a36Sopenharmony_ci	case REQ_OP_ZONE_APPEND:
67262306a36Sopenharmony_ci		return null_zone_write(cmd, sector, nr_sectors, true);
67362306a36Sopenharmony_ci	case REQ_OP_ZONE_RESET:
67462306a36Sopenharmony_ci	case REQ_OP_ZONE_RESET_ALL:
67562306a36Sopenharmony_ci	case REQ_OP_ZONE_OPEN:
67662306a36Sopenharmony_ci	case REQ_OP_ZONE_CLOSE:
67762306a36Sopenharmony_ci	case REQ_OP_ZONE_FINISH:
67862306a36Sopenharmony_ci		return null_zone_mgmt(cmd, op, sector);
67962306a36Sopenharmony_ci	default:
68062306a36Sopenharmony_ci		dev = cmd->nq->dev;
68162306a36Sopenharmony_ci		zone = &dev->zones[null_zone_no(dev, sector)];
68262306a36Sopenharmony_ci		if (zone->cond == BLK_ZONE_COND_OFFLINE)
68362306a36Sopenharmony_ci			return BLK_STS_IOERR;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci		null_lock_zone(dev, zone);
68662306a36Sopenharmony_ci		sts = null_process_cmd(cmd, op, sector, nr_sectors);
68762306a36Sopenharmony_ci		null_unlock_zone(dev, zone);
68862306a36Sopenharmony_ci		return sts;
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci/*
69362306a36Sopenharmony_ci * Set a zone in the read-only or offline condition.
69462306a36Sopenharmony_ci */
69562306a36Sopenharmony_cistatic void null_set_zone_cond(struct nullb_device *dev,
69662306a36Sopenharmony_ci			       struct nullb_zone *zone, enum blk_zone_cond cond)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	if (WARN_ON_ONCE(cond != BLK_ZONE_COND_READONLY &&
69962306a36Sopenharmony_ci			 cond != BLK_ZONE_COND_OFFLINE))
70062306a36Sopenharmony_ci		return;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	null_lock_zone(dev, zone);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	/*
70562306a36Sopenharmony_ci	 * If the read-only condition is requested again to zones already in
70662306a36Sopenharmony_ci	 * read-only condition, restore back normal empty condition. Do the same
70762306a36Sopenharmony_ci	 * if the offline condition is requested for offline zones. Otherwise,
70862306a36Sopenharmony_ci	 * set the specified zone condition to the zones. Finish the zones
70962306a36Sopenharmony_ci	 * beforehand to free up zone resources.
71062306a36Sopenharmony_ci	 */
71162306a36Sopenharmony_ci	if (zone->cond == cond) {
71262306a36Sopenharmony_ci		zone->cond = BLK_ZONE_COND_EMPTY;
71362306a36Sopenharmony_ci		zone->wp = zone->start;
71462306a36Sopenharmony_ci		if (dev->memory_backed)
71562306a36Sopenharmony_ci			null_handle_discard(dev, zone->start, zone->len);
71662306a36Sopenharmony_ci	} else {
71762306a36Sopenharmony_ci		if (zone->cond != BLK_ZONE_COND_READONLY &&
71862306a36Sopenharmony_ci		    zone->cond != BLK_ZONE_COND_OFFLINE)
71962306a36Sopenharmony_ci			null_finish_zone(dev, zone);
72062306a36Sopenharmony_ci		zone->cond = cond;
72162306a36Sopenharmony_ci		zone->wp = (sector_t)-1;
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	null_unlock_zone(dev, zone);
72562306a36Sopenharmony_ci}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci/*
72862306a36Sopenharmony_ci * Identify a zone from the sector written to configfs file. Then set zone
72962306a36Sopenharmony_ci * condition to the zone.
73062306a36Sopenharmony_ci */
73162306a36Sopenharmony_cissize_t zone_cond_store(struct nullb_device *dev, const char *page,
73262306a36Sopenharmony_ci			size_t count, enum blk_zone_cond cond)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	unsigned long long sector;
73562306a36Sopenharmony_ci	unsigned int zone_no;
73662306a36Sopenharmony_ci	int ret;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	if (!dev->zoned) {
73962306a36Sopenharmony_ci		pr_err("null_blk device is not zoned\n");
74062306a36Sopenharmony_ci		return -EINVAL;
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	if (!dev->zones) {
74462306a36Sopenharmony_ci		pr_err("null_blk device is not yet powered\n");
74562306a36Sopenharmony_ci		return -EINVAL;
74662306a36Sopenharmony_ci	}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	ret = kstrtoull(page, 0, &sector);
74962306a36Sopenharmony_ci	if (ret < 0)
75062306a36Sopenharmony_ci		return ret;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	zone_no = null_zone_no(dev, sector);
75362306a36Sopenharmony_ci	if (zone_no >= dev->nr_zones) {
75462306a36Sopenharmony_ci		pr_err("Sector out of range\n");
75562306a36Sopenharmony_ci		return -EINVAL;
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (dev->zones[zone_no].type == BLK_ZONE_TYPE_CONVENTIONAL) {
75962306a36Sopenharmony_ci		pr_err("Can not change condition of conventional zones\n");
76062306a36Sopenharmony_ci		return -EINVAL;
76162306a36Sopenharmony_ci	}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	null_set_zone_cond(dev, &dev->zones[zone_no], cond);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	return count;
76662306a36Sopenharmony_ci}
767