Skip to content

Commit d3a5738

Browse files
kawasakiaxboe
authored andcommitted
null_blk: support read-only and offline zone conditions
In zoned mode, zones with write pointers can have conditions "read-only" or "offline". In read-only condition, zones can not be written. In offline condition, the zones can be neither written nor read. These conditions are intended for zones with media failures, then it is difficult to set those conditions to zones on real devices. To test handling of zones in the conditions, add a feature to null_blk to set up zones in read-only or offline condition. Add new configuration attributes "zone_readonly" and "zone_offline". Write a sector to the attribute files to specify the target zone to set the zone conditions. For example, following command lines do it: echo 0 > nullb1/zone_readonly echo 524288 > nullb1/zone_offline When the specified zones are already in read-only or offline condition, normal empty condition is restored to the zones. These condition changes can be done only after the null_blk device get powered, since status area of each zone is not yet allocated before power-on. Also improve zone condition checks to inhibit all commands for zones in offline conditions. In same manner, inhibit write and zone management commands for zones in read-only condition. Signed-off-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com> Reviewed-by: Damien Le Moal <damien.lemoal@opensource.wdc.com> Link: https://lore.kernel.org/r/20221201061036.2342206-1-shinichiro.kawasaki@wdc.com Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent 677b367 commit d3a5738

File tree

3 files changed

+121
-4
lines changed

3 files changed

+121
-4
lines changed

drivers/block/null_blk/main.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,24 @@ static ssize_t nullb_device_badblocks_store(struct config_item *item,
523523
}
524524
CONFIGFS_ATTR(nullb_device_, badblocks);
525525

526+
static ssize_t nullb_device_zone_readonly_store(struct config_item *item,
527+
const char *page, size_t count)
528+
{
529+
struct nullb_device *dev = to_nullb_device(item);
530+
531+
return zone_cond_store(dev, page, count, BLK_ZONE_COND_READONLY);
532+
}
533+
CONFIGFS_ATTR_WO(nullb_device_, zone_readonly);
534+
535+
static ssize_t nullb_device_zone_offline_store(struct config_item *item,
536+
const char *page, size_t count)
537+
{
538+
struct nullb_device *dev = to_nullb_device(item);
539+
540+
return zone_cond_store(dev, page, count, BLK_ZONE_COND_OFFLINE);
541+
}
542+
CONFIGFS_ATTR_WO(nullb_device_, zone_offline);
543+
526544
static struct configfs_attribute *nullb_device_attrs[] = {
527545
&nullb_device_attr_size,
528546
&nullb_device_attr_completion_nsec,
@@ -549,6 +567,8 @@ static struct configfs_attribute *nullb_device_attrs[] = {
549567
&nullb_device_attr_zone_nr_conv,
550568
&nullb_device_attr_zone_max_open,
551569
&nullb_device_attr_zone_max_active,
570+
&nullb_device_attr_zone_readonly,
571+
&nullb_device_attr_zone_offline,
552572
&nullb_device_attr_virt_boundary,
553573
&nullb_device_attr_no_sched,
554574
&nullb_device_attr_shared_tag_bitmap,
@@ -614,7 +634,7 @@ static ssize_t memb_group_features_show(struct config_item *item, char *page)
614634
"poll_queues,power,queue_mode,shared_tag_bitmap,size,"
615635
"submit_queues,use_per_node_hctx,virt_boundary,zoned,"
616636
"zone_capacity,zone_max_active,zone_max_open,"
617-
"zone_nr_conv,zone_size\n");
637+
"zone_nr_conv,zone_offline,zone_readonly,zone_size\n");
618638
}
619639

620640
CONFIGFS_ATTR_RO(memb_group_, features);

drivers/block/null_blk/null_blk.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_op op,
151151
sector_t sector, sector_t nr_sectors);
152152
size_t null_zone_valid_read_len(struct nullb *nullb,
153153
sector_t sector, unsigned int len);
154+
ssize_t zone_cond_store(struct nullb_device *dev, const char *page,
155+
size_t count, enum blk_zone_cond cond);
154156
#else
155157
static inline int null_init_zoned_dev(struct nullb_device *dev,
156158
struct request_queue *q)
@@ -174,6 +176,12 @@ static inline size_t null_zone_valid_read_len(struct nullb *nullb,
174176
{
175177
return len;
176178
}
179+
static inline ssize_t zone_cond_store(struct nullb_device *dev,
180+
const char *page, size_t count,
181+
enum blk_zone_cond cond)
182+
{
183+
return -EOPNOTSUPP;
184+
}
177185
#define null_report_zones NULL
178186
#endif /* CONFIG_BLK_DEV_ZONED */
179187
#endif /* __NULL_BLK_H */

drivers/block/null_blk/zoned.c

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,10 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
384384

385385
null_lock_zone(dev, zone);
386386

387-
if (zone->cond == BLK_ZONE_COND_FULL) {
388-
/* Cannot write to a full zone */
387+
if (zone->cond == BLK_ZONE_COND_FULL ||
388+
zone->cond == BLK_ZONE_COND_READONLY ||
389+
zone->cond == BLK_ZONE_COND_OFFLINE) {
390+
/* Cannot write to the zone */
389391
ret = BLK_STS_IOERR;
390392
goto unlock;
391393
}
@@ -613,7 +615,9 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_op op,
613615
for (i = dev->zone_nr_conv; i < dev->nr_zones; i++) {
614616
zone = &dev->zones[i];
615617
null_lock_zone(dev, zone);
616-
if (zone->cond != BLK_ZONE_COND_EMPTY) {
618+
if (zone->cond != BLK_ZONE_COND_EMPTY &&
619+
zone->cond != BLK_ZONE_COND_READONLY &&
620+
zone->cond != BLK_ZONE_COND_OFFLINE) {
617621
null_reset_zone(dev, zone);
618622
trace_nullb_zone_op(cmd, i, zone->cond);
619623
}
@@ -627,6 +631,12 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_op op,
627631

628632
null_lock_zone(dev, zone);
629633

634+
if (zone->cond == BLK_ZONE_COND_READONLY ||
635+
zone->cond == BLK_ZONE_COND_OFFLINE) {
636+
ret = BLK_STS_IOERR;
637+
goto unlock;
638+
}
639+
630640
switch (op) {
631641
case REQ_OP_ZONE_RESET:
632642
ret = null_reset_zone(dev, zone);
@@ -648,6 +658,7 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_op op,
648658
if (ret == BLK_STS_OK)
649659
trace_nullb_zone_op(cmd, zone_no, zone->cond);
650660

661+
unlock:
651662
null_unlock_zone(dev, zone);
652663

653664
return ret;
@@ -674,10 +685,88 @@ blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_op op,
674685
default:
675686
dev = cmd->nq->dev;
676687
zone = &dev->zones[null_zone_no(dev, sector)];
688+
if (zone->cond == BLK_ZONE_COND_OFFLINE)
689+
return BLK_STS_IOERR;
677690

678691
null_lock_zone(dev, zone);
679692
sts = null_process_cmd(cmd, op, sector, nr_sectors);
680693
null_unlock_zone(dev, zone);
681694
return sts;
682695
}
683696
}
697+
698+
/*
699+
* Set a zone in the read-only or offline condition.
700+
*/
701+
static void null_set_zone_cond(struct nullb_device *dev,
702+
struct nullb_zone *zone, enum blk_zone_cond cond)
703+
{
704+
if (WARN_ON_ONCE(cond != BLK_ZONE_COND_READONLY &&
705+
cond != BLK_ZONE_COND_OFFLINE))
706+
return;
707+
708+
null_lock_zone(dev, zone);
709+
710+
/*
711+
* If the read-only condition is requested again to zones already in
712+
* read-only condition, restore back normal empty condition. Do the same
713+
* if the offline condition is requested for offline zones. Otherwise,
714+
* set the specified zone condition to the zones. Finish the zones
715+
* beforehand to free up zone resources.
716+
*/
717+
if (zone->cond == cond) {
718+
zone->cond = BLK_ZONE_COND_EMPTY;
719+
zone->wp = zone->start;
720+
if (dev->memory_backed)
721+
null_handle_discard(dev, zone->start, zone->len);
722+
} else {
723+
if (zone->cond != BLK_ZONE_COND_READONLY &&
724+
zone->cond != BLK_ZONE_COND_OFFLINE)
725+
null_finish_zone(dev, zone);
726+
zone->cond = cond;
727+
zone->wp = (sector_t)-1;
728+
}
729+
730+
null_unlock_zone(dev, zone);
731+
}
732+
733+
/*
734+
* Identify a zone from the sector written to configfs file. Then set zone
735+
* condition to the zone.
736+
*/
737+
ssize_t zone_cond_store(struct nullb_device *dev, const char *page,
738+
size_t count, enum blk_zone_cond cond)
739+
{
740+
unsigned long long sector;
741+
unsigned int zone_no;
742+
int ret;
743+
744+
if (!dev->zoned) {
745+
pr_err("null_blk device is not zoned\n");
746+
return -EINVAL;
747+
}
748+
749+
if (!dev->zones) {
750+
pr_err("null_blk device is not yet powered\n");
751+
return -EINVAL;
752+
}
753+
754+
ret = kstrtoull(page, 0, &sector);
755+
if (ret < 0)
756+
return ret;
757+
758+
zone_no = null_zone_no(dev, sector);
759+
if (zone_no >= dev->nr_zones) {
760+
pr_err("Sector out of range\n");
761+
return -EINVAL;
762+
}
763+
764+
if (dev->zones[zone_no].type == BLK_ZONE_TYPE_CONVENTIONAL) {
765+
pr_err("Can not change condition of conventional zones\n");
766+
return -EINVAL;
767+
}
768+
769+
null_set_zone_cond(dev, &dev->zones[zone_no], cond);
770+
771+
return count;
772+
}

0 commit comments

Comments
 (0)