On Wed, Jun 17, 2026 at 11:17:27AM -0400, Michael Bommarito wrote:
The zoned block-device fields in the virtio-blk header are typed
__virtio{32,64}, so their endianness follows VIRTIO_F_VERSION_1. The
zoned feature is only defined for VIRTIO 1.x devices, and the virtio
specification defines all of its fields as little-endian. Commit
b16a1756c716 ("virtio_blk: mark all zone fields LE") tagged them
__le* for exactly this reason, but commit f1ba4e674feb ("virtio-blk:
fix to match virtio spec") re-applied the reviewed version of the
original zoned series -- which predated b16a1756 -- and silently
restored the __virtio* typing together with the matching
virtio*_to_cpu() / virtio_cread() accessors in the driver.
Restore the little-endian typing for the zoned configuration-space
characteristics, the zone descriptor, the zone report header and the
ZONE_APPEND in-header sector, and read them with le*_to_cpu() and
virtio_cread_le() to match.
There is no functional change on any spec-compliant device: zoned
requires VIRTIO_F_VERSION_1, and for a VERSION_1 device
virtio*_to_cpu() is identical to le*_to_cpu(). The change makes the
uapi types describe the actual wire format and removes a latent
endianness mismatch for a (non-conformant) legacy device on a
big-endian guest.
Not for this patch, but at this point should we do the same also for the
fields gated by the following features that IIUC are all added in 1.*:
- VIRTIO_BLK_F_MQ
- VIRTIO_BLK_F_DISCARD
- VIRTIO_BLK_F_WRITE_ZEROES
- VIRTIO_BLK_F_SECURE_ERASE
Fixes: f1ba4e674feb ("virtio-blk: fix to match virtio spec")
Suggested-by: Michael S. Tsirkin <[email protected]>
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <[email protected]>
---
Testing:
- Builds with no new warnings; sparse endian-clean (C=2,
__CHECK_ENDIAN__, CONFIG_BLK_DEV_ZONED=y) both before and after.
- Booted under QEMU with a host-managed zoned device exposed through
virtio-blk. Zone revalidation, blkzone report and a sequential
write / write-pointer check return correct values; blktests zbd
device tests 001-006 (sysfs+ioctl, report zone, reset, write split,
write ordering, revalidate) pass, with results identical before and
after this change -- expected, since on a VIRTIO_F_VERSION_1 device
virtio*_to_cpu() == le*_to_cpu().
drivers/block/virtio_blk.c | 38 +++++++++++++++------------------
include/uapi/linux/virtio_blk.h | 18 ++++++++--------
2 files changed, 26 insertions(+), 30 deletions(-)
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index b1c9a27fe00f3..5532cfbde7bfe 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -99,7 +99,7 @@ struct virtblk_req {
* be the last byte.
*/
struct {
- __virtio64 sector;
+ __le64 sector;
u8 status;
} zone_append;
} in_hdr;
@@ -335,14 +335,12 @@ static inline void virtblk_request_done(struct request
*req)
{
struct virtblk_req *vbr = blk_mq_rq_to_pdu(req);
blk_status_t status = virtblk_result(virtblk_vbr_status(vbr));
- struct virtio_blk *vblk = req->mq_hctx->queue->queuedata;
virtblk_unmap_data(req, vbr);
virtblk_cleanup_cmd(req);
if (req_op(req) == REQ_OP_ZONE_APPEND)
- req->__sector = virtio64_to_cpu(vblk->vdev,
- vbr->in_hdr.zone_append.sector);
+ req->__sector = le64_to_cpu(vbr->in_hdr.zone_append.sector);
blk_mq_end_request(req, status);
}
@@ -589,13 +587,13 @@ static int virtblk_parse_zone(struct virtio_blk *vblk,
{
struct blk_zone zone = { };
- zone.start = virtio64_to_cpu(vblk->vdev, entry->z_start);
+ zone.start = le64_to_cpu(entry->z_start);
if (zone.start + vblk->zone_sectors <= get_capacity(vblk->disk))
zone.len = vblk->zone_sectors;
else
zone.len = get_capacity(vblk->disk) - zone.start;
- zone.capacity = virtio64_to_cpu(vblk->vdev, entry->z_cap);
- zone.wp = virtio64_to_cpu(vblk->vdev, entry->z_wp);
+ zone.capacity = le64_to_cpu(entry->z_cap);
+ zone.wp = le64_to_cpu(entry->z_wp);
switch (entry->z_type) {
case VIRTIO_BLK_ZT_SWR:
@@ -687,8 +685,7 @@ static int virtblk_report_zones(struct gendisk *disk,
sector_t sector,
if (ret)
goto fail_report;
- nz = min_t(u64, virtio64_to_cpu(vblk->vdev, report->nr_zones),
- nr_zones);
+ nz = min_t(u64, le64_to_cpu(report->nr_zones), nr_zones);
if (!nz)
break;
@@ -698,8 +695,7 @@ static int virtblk_report_zones(struct gendisk *disk,
sector_t sector,
if (ret)
goto fail_report;
- sector = virtio64_to_cpu(vblk->vdev,
- report->zones[i].z_start) +
+ sector = le64_to_cpu(report->zones[i].z_start) +
vblk->zone_sectors;
zone_idx++;
}
@@ -725,18 +721,18 @@ static int virtblk_read_zoned_limits(struct virtio_blk
*vblk,
lim->features |= BLK_FEAT_ZONED;
- virtio_cread(vdev, struct virtio_blk_config,
- zoned.max_open_zones, &v);
+ virtio_cread_le(vdev, struct virtio_blk_config,
+ zoned.max_open_zones, &v);
lim->max_open_zones = v;
dev_dbg(&vdev->dev, "max open zones = %u\n", v);
- virtio_cread(vdev, struct virtio_blk_config,
- zoned.max_active_zones, &v);
+ virtio_cread_le(vdev, struct virtio_blk_config,
+ zoned.max_active_zones, &v);
lim->max_active_zones = v;
dev_dbg(&vdev->dev, "max active zones = %u\n", v);
- virtio_cread(vdev, struct virtio_blk_config,
- zoned.write_granularity, &wg);
+ virtio_cread_le(vdev, struct virtio_blk_config,
+ zoned.write_granularity, &wg);
if (!wg) {
dev_warn(&vdev->dev, "zero write granularity reported\n");
return -ENODEV;
@@ -750,8 +746,8 @@ static int virtblk_read_zoned_limits(struct virtio_blk
*vblk,
* virtio ZBD specification doesn't require zones to be a power of
* two sectors in size, but the code in this driver expects that.
*/
- virtio_cread(vdev, struct virtio_blk_config, zoned.zone_sectors,
- &vblk->zone_sectors);
+ virtio_cread_le(vdev, struct virtio_blk_config, zoned.zone_sectors,
+ &vblk->zone_sectors);
if (vblk->zone_sectors == 0 || !is_power_of_2(vblk->zone_sectors)) {
dev_err(&vdev->dev,
"zoned device with non power of two zone size %u\n",
@@ -767,8 +763,8 @@ static int virtblk_read_zoned_limits(struct virtio_blk
*vblk,
lim->max_hw_discard_sectors = 0;
}
- virtio_cread(vdev, struct virtio_blk_config,
- zoned.max_append_sectors, &v);
+ virtio_cread_le(vdev, struct virtio_blk_config,
+ zoned.max_append_sectors, &v);
if (!v) {
dev_warn(&vdev->dev, "zero max_append_sectors reported\n");
return -ENODEV;
diff --git a/include/uapi/linux/virtio_blk.h b/include/uapi/linux/virtio_blk.h
index 3744e4da1b2a7..5af2a0300bb9d 100644
--- a/include/uapi/linux/virtio_blk.h
+++ b/include/uapi/linux/virtio_blk.h
@@ -140,11 +140,11 @@ struct virtio_blk_config {
To avoid making this mistake again, how about adding a note here to
clarify that all the fields listed below are defined only for VIRTIO 1.x
devices and are therefore always little-endian?
Anyway, the patch LGTM:
Reviewed-by: Stefano Garzarella <[email protected]>
/* Zoned block device characteristics (if VIRTIO_BLK_F_ZONED) */
struct virtio_blk_zoned_characteristics {
- __virtio32 zone_sectors;
- __virtio32 max_open_zones;
- __virtio32 max_active_zones;
- __virtio32 max_append_sectors;
- __virtio32 write_granularity;
+ __le32 zone_sectors;
+ __le32 max_open_zones;
+ __le32 max_active_zones;
+ __le32 max_append_sectors;
+ __le32 write_granularity;
__u8 model;
__u8 unused2[3];
} zoned;
@@ -241,11 +241,11 @@ struct virtio_blk_outhdr {
*/
struct virtio_blk_zone_descriptor {
/* Zone capacity */
- __virtio64 z_cap;
+ __le64 z_cap;
/* The starting sector of the zone */
- __virtio64 z_start;
+ __le64 z_start;
/* Zone write pointer position in sectors */
- __virtio64 z_wp;
+ __le64 z_wp;
/* Zone type */
__u8 z_type;
/* Zone state */
@@ -254,7 +254,7 @@ struct virtio_blk_zone_descriptor {
};
struct virtio_blk_zone_report {
- __virtio64 nr_zones;
+ __le64 nr_zones;
__u8 reserved[56];
struct virtio_blk_zone_descriptor zones[];
};
--
2.53.0