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, §or); 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