Linux SCSI subsystem development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Damien Le Moal" <dlemoal@kernel.org>, linux-scsi@vger.kernel.org
Subject: Re: [PATCH v9 08/13] zloop: add storage element emulation
Date: Sun, 15 Nov 2026 15:11:38 +0000	[thread overview]
Message-ID: <sashiko-outbox-215613@kernel.org> (raw)
In-Reply-To: <20261115145015.938426-9-dlemoal@kernel.org>

Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [High] zloop: inverted zone check and missing unlock in
  zloop_restore_zone_file()
- [Medium] zloop: bool return type on zloop_prep_rq() corrupts
  blk_status_t error codes
- [Medium] zloop: broken write element removal check and zone cond
  transition
- [Low] zloop: wrong enum blk_se_status return type on
  zloop_zone_element_is_ok()
--

commit add2167f11ea8b6280aa470bbfffca31f313cf06
Author: Damien Le Moal <dlemoal@kernel.org>

zloop: add storage element emulation

This commit adds storage element depopulation and restoration emulation to
the zloop driver, controlled via the new stor_elements option and exposed
through zloop_se_ops.

> diff --git a/drivers/block/zloop.c b/drivers/block/zloop.c
> index 99b9efef98741..8274dae7664bf 100644
> --- a/drivers/block/zloop.c
> +++ b/drivers/block/zloop.c

[ ... ]

> @@ -289,22 +314,80 @@ static bool zloop_do_open_zone(struct zloop_device *zlo,

[ ... ]

> +static enum blk_se_status zloop_zone_element_is_ok(struct zloop_device *zlo,
> +						   struct zloop_zone *zone,
> +						   bool for_write)
> +{
> +	enum blk_se_status sts =
> +		zloop_zone_element_status(zlo, zone, for_write);
> +
> +	return sts < BLK_SE_STS_DEGRADED;
> +}

[Severity: Low]
Should zloop_zone_element_is_ok() have a return type of bool instead of
enum blk_se_status, since it returns a boolean comparison result?

[ ... ]

> @@ -885,34 +1007,65 @@ static void zloop_complete_rq(struct request *rq)

[ ... ]

> +static bool zloop_prep_rq(struct zloop_device *zlo, struct request *rq)
> +{
> +	struct zloop_zone *zone = &zlo->zones[rq_zone_no(rq)];
> +	bool is_write = op_is_write(req_op(rq));
> +	blk_status_t ret = BLK_STS_OK;
> +
> +	if (!zlo->nr_elements &&
> +	    (!zlo->ordered_zone_append || req_op(rq) != REQ_OP_ZONE_APPEND))
> +		return BLK_STS_OK;
> +
> +	spin_lock(&zone->wp_lock);
> +
> +	/*
> +	 * If storage element emulation is enabled, check the health state of
> +	 * the storage element serving the zone and the zone condition: do not
> +	 * allow any operation to zones served by an unusable storage element,
> +	 * operations to offline zones, and allow only read operations to
> +	 * read-only zones.
> +	 */
> +	if (zlo->nr_elements) {
> +		if (!zloop_zone_element_is_ok(zlo, zone, is_write) ||
> +		    zone->cond == BLK_ZONE_COND_OFFLINE ||
> +		    (zone->cond == BLK_ZONE_COND_READONLY && is_write)) {
> +			ret = BLK_STS_IOERR;
> +			goto unlock;
> +		}
> +	}
> +
> +	/*
> +	 * If we need to strongly order zone append operations, set the request
> +	 * sector to the zone write pointer location now instead of when the
> +	 * command work runs.
> +	 */
> +	if (zlo->ordered_zone_append && req_op(rq) == REQ_OP_ZONE_APPEND)
> +		ret = zloop_set_zone_append_sector(zlo, zone, rq);
> +
> +unlock:
> +	spin_unlock(&zone->wp_lock);
> +
> +	return ret;
>  }

[Severity: Medium]
Does the bool return type of zloop_prep_rq() turn non-zero blk_status_t
values into 1 (BLK_STS_NOTSUPP)?  When zloop_prep_rq() fails with
BLK_STS_IOERR or BLK_STS_ZONE_OPEN_RESOURCE, zloop_queue_rq() returns
BLK_STS_NOTSUPP (-EOPNOTSUPP) to blk-mq instead.

[ ... ]

> @@ -1021,6 +1188,354 @@ static int zloop_report_zones(struct gendisk *disk, sector_t sector,

[ ... ]

> +static int zloop_execute_remove_element(struct zloop_device *zlo)
> +{
> +	struct gendisk *disk = zlo->disk;
> +	struct blk_se *se, *paired_se = NULL;
> +	enum blk_zone_cond cond;
> +	struct zloop_zone *zone;
> +	unsigned int memflags, i;
> +	int ret = 0;
> +
> +	/* Wait for all IOs to complete. */
> +	memflags = blk_mq_freeze_queue(disk->queue);
> +	blk_mq_unfreeze_queue(disk->queue, memflags);
> +
> +	/*
> +	 * If the element to remove is a read element, zones must go offline and
> +	 * the associated write element is also removed.
> +	 */
> +	se = &zlo->elements[zlo->remove_element_id - 1];
> +	switch (se->type) {
> +	case BLK_SE_TYPE_RDWR:
> +		cond = BLK_ZONE_COND_OFFLINE;
> +		break;
> +	case BLK_SE_TYPE_READ:
> +		cond = BLK_ZONE_COND_OFFLINE;
> +		paired_se = &zlo->elements[se->paired_id - 1];
> +		break;
> +	case BLK_SE_TYPE_WRITE:
> +		cond = BLK_ZONE_COND_READONLY;
> +		break;
> +	default:
> +		WARN_ON_ONCE(1);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Change the condition of the zones owned by the (pair of) elements
> +	 * being removed and mark the elements removed.
> +	 */
> +	for (i = 0, zone = zlo->zones; i < zlo->nr_zones; i++, zone++) {
> +		if (zone->wr_se_id != se->id && zone->rd_se_id != se->id)
> +			continue;
> +		mutex_lock(&zone->lock);
> +		spin_lock(&zone->wp_lock);
> +		zloop_set_zone_cond(zlo, zone, cond);
> +		clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
> +		spin_unlock(&zone->wp_lock);
> +		mutex_unlock(&zone->lock);
> +	}
> +
> +	WRITE_ONCE(se->status, BLK_SE_STS_REMOVED);
> +	if (paired_se && READ_ONCE(paired_se->status) != BLK_SE_STS_REMOVED)
> +		WRITE_ONCE(paired_se->status, BLK_SE_STS_REMOVED);
> +
> +	return ret;
> +}
> +
> +static int zloop_remove_element(struct gendisk *disk, unsigned int element_id)
> +{
> +	struct zloop_device *zlo = disk->private_data;
> +	struct blk_se *se, *rse, *rpse = NULL;
> +	unsigned int i, nr_se_remaining = 0;
> +	int ret = 0;
> +
> +	if (test_bit(GD_DEAD, &disk->state) ||
> +	    data_race(READ_ONCE(zlo->state)) == Zlo_deleting)
> +		return -ENODEV;
> +
> +	if (zlo->stor_elements == ZLOOP_STOR_ELEMENTS_NONE)
> +		return -EOPNOTSUPP;
> +
> +	if (!element_id || element_id > zlo->nr_elements)
> +		return -EINVAL;
> +
> +	mutex_lock(&zlo->stor_elements_lock);
> +
> +	/* If a remove or restore operation is already in progress, bail out. */
> +	if (zlo->remove_element_id || READ_ONCE(zlo->restore_in_progress)) {
> +		ret = -EBUSY;
> +		goto unlock;
> +	}
> +
> +	/*
> +	 * Get the element to remove. If it is a read element, the associated
> +	 * write element must also be removed.
> +	 */
> +	rse = &zlo->elements[element_id - 1];
> +	if (rse->type == BLK_SE_TYPE_READ)
> +		rpse = &zlo->elements[rse->paired_id - 1];
> +
> +	/* If the element is already removed, we have nothing to do. */
> +	if (READ_ONCE(rse->status) == BLK_SE_STS_REMOVED)
> +		goto unlock;
> +
> +	/*
> +	 * Make sure we do not allow removing all storage elements as that does
> +	 * not make any sense. This is consistent with the device advertized
> +	 * limit of SCSI and ATA devices supporting the storage element
> +	 * depopulation feature.
> +	 */
> +	for (i = 0, se = zlo->elements; i < zlo->nr_elements; i++, se++) {
> +		if (se == rse || se == rpse)
> +			continue;
> +		if (READ_ONCE(se->status) <= BLK_SE_STS_DEGRADED)
> +			nr_se_remaining++;
> +	}
> +	if (!nr_se_remaining) {
> +		ret = -EINVAL;
> +		goto unlock;
> +	}

[Severity: Medium]
Can zloop_remove_element() allow removing every write element in
ZLOOP_STOR_ELEMENTS_PAIRS mode?  When removing a BLK_SE_TYPE_WRITE element,
rpse is NULL and the paired BLK_SE_TYPE_READ elements remain in
BLK_SE_STS_OK and are counted in nr_se_remaining, so nr_se_remaining is
always non-zero and all write elements on the device can be removed.
Counting BLK_SE_STS_DEGRADED elements in nr_se_remaining also allows
removing the last healthy element when zloop_zone_element_is_ok() rejects
I/O to degraded elements (sts < BLK_SE_STS_DEGRADED).

In addition, if a removed read+write element pair fails restoration
(leaving both elements in BLK_SE_STS_RESTORE_ERROR and their zones in
BLK_ZONE_COND_OFFLINE), removing the BLK_SE_TYPE_WRITE element in
zloop_execute_remove_element() unconditionally sets those zones to
BLK_ZONE_COND_READONLY even though the paired read element cannot serve
reads.

[ ... ]

> +static int zloop_restore_zone_file(struct zloop_device *zlo,
> +				   struct zloop_zone *zone)
> +{
> +	int ret = 0;
> +
> +	if (!test_bit(ZLOOP_ZONE_CONV, &zone->flags))
> +		return 0;
> +
> +	mutex_lock(&zone->lock);
> +
> +	ret = vfs_truncate(&zone->file->f_path, 0);
> +	if (ret)
> +		set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
> +
> +	return ret;
> +}
> +
> +static void zloop_restore_zone_cond(struct zloop_device *zlo,
> +				    struct zloop_zone *zone)
> +{
> +	mutex_lock(&zone->lock);
> +	spin_lock(&zone->wp_lock);

[Severity: High]
Is the ZLOOP_ZONE_CONV check in zloop_restore_zone_file() inverted, and is
mutex_unlock(&zone->lock) missing before return?

As written, zloop_restore_zone_file() skips sequential zones, truncates
conventional zone backing files to 0 bytes, and returns with zone->lock
held, which immediately deadlocks when zloop_execute_restore_elements()
calls zloop_restore_zone_cond() on the same zone.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261115145015.938426-1-dlemoal@kernel.org?part=8

  reply	other threads:[~2026-11-15 15:11 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-15 14:50 [PATCH v9 00/13] Add support for storage element depopulation Damien Le Moal
2026-11-15 14:50 ` [PATCH v9 01/13] block: introduce disk_zone_is_offline() Damien Le Moal
2026-11-15 14:50 ` [PATCH v9 02/13] block: refactor blk_revalidate_disk_zones() Damien Le Moal
2026-11-15 14:50 ` [PATCH v9 03/13] block: allow dropping all zone write plugs on revalidation Damien Le Moal
2026-11-15 14:50 ` [PATCH v9 04/13] block: introduce storage element management Damien Le Moal
2026-11-15 14:50 ` [PATCH v9 05/13] block: add storage element management ioctls Damien Le Moal
2026-11-15 14:50 ` [PATCH v9 06/13] zloop: keep zone write pointer and condition consistent Damien Le Moal
2026-11-15 15:07   ` sashiko-bot
2026-11-15 14:50 ` [PATCH v9 07/13] zloop: fix zone reporting consistency Damien Le Moal
2026-11-15 14:50 ` [PATCH v9 08/13] zloop: add storage element emulation Damien Le Moal
2026-11-15 15:11   ` sashiko-bot [this message]
2026-11-15 14:50 ` [PATCH v9 09/13] zloop: add degrade_element control command Damien Le Moal
2026-11-15 14:50 ` [PATCH v9 10/13] scsi: scsi_proto: refine the definition of storage element health Damien Le Moal
2026-11-15 14:50 ` [PATCH v9 11/13] scsi: sd_zbc: always revalidate zones for disks supporting head depopulation Damien Le Moal
2026-11-15 14:50 ` [PATCH v9 12/13] scsi: sd_zbc: define storage element management operations Damien Le Moal
2026-11-15 14:50 ` [PATCH v9 13/13] scsi: sd: fail read commands to offline zones early Damien Le Moal

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-215613@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=dlemoal@kernel.org \
    --cc=linux-scsi@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox