null_set_zone_cond() calls null_finish_zone() to release the zone resources held by a zone before setting it read-only or offline. For an empty or closed zone, null_finish_zone() first runs the zone resource checks, which can close an unrelated implicitly open zone, or fail. As the error is ignored, a failure for a closed zone leaks nr_zones_closed, permanently lowering the effective active zone limit. Setting a zone read-only or offline requires no zone resources, so release the zone resources held by the zone directly instead, using a new helper, null_release_zone_resources(). Fixes: d3a5738849e0 ("null_blk: support read-only and offline zone conditions") Signed-off-by: Niklas Cassel --- drivers/block/null_blk/zoned.c | 35 +++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/block/null_blk/zoned.c b/drivers/block/null_blk/zoned.c index ecdc8d68ca2a..015f5d304c7c 100644 --- a/drivers/block/null_blk/zoned.c +++ b/drivers/block/null_blk/zoned.c @@ -743,6 +743,33 @@ blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_op op, } } +/* + * Release the zone resources held by a zone, before changing the condition of + * the zone to a condition that does not hold any zone resources. + */ +static void null_release_zone_resources(struct nullb_device *dev, + struct nullb_zone *zone) +{ + if (!dev->need_zone_res_mgmt) + return; + + spin_lock(&dev->zone_res_lock); + switch (zone->cond) { + case BLK_ZONE_COND_IMP_OPEN: + dev->nr_zones_imp_open--; + break; + case BLK_ZONE_COND_EXP_OPEN: + dev->nr_zones_exp_open--; + break; + case BLK_ZONE_COND_CLOSED: + dev->nr_zones_closed--; + break; + default: + break; + } + spin_unlock(&dev->zone_res_lock); +} + /* * Set a zone in the read-only or offline condition. */ @@ -759,8 +786,8 @@ static void null_set_zone_cond(struct nullb_device *dev, * If the read-only condition is requested again to zones already in * read-only condition, restore back normal empty condition. Do the same * if the offline condition is requested for offline zones. Otherwise, - * set the specified zone condition to the zones. Finish the zones - * beforehand to free up zone resources. + * set the specified zone condition to the zones. Release the zone + * resources held by the zones beforehand. */ if (zone->cond == cond) { zone->cond = BLK_ZONE_COND_EMPTY; @@ -768,9 +795,7 @@ static void null_set_zone_cond(struct nullb_device *dev, if (dev->memory_backed) null_handle_discard(dev, zone->start, zone->len); } else { - if (zone->cond != BLK_ZONE_COND_READONLY && - zone->cond != BLK_ZONE_COND_OFFLINE) - null_finish_zone(dev, zone); + null_release_zone_resources(dev, zone); zone->cond = cond; zone->wp = NULL_ZONE_INVALID_WP; } -- 2.56.0