All of lore.kernel.org
 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: 17+ 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
2026-11-15 15:42 ` [PATCH v9 00/13] Add support for storage element depopulation 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.