Am 13.10.2026 um 10:32 hat Wangyong geschrieben:
Thank you for looking into this.
> Hi,
>
> I'm reporting a bug where VPD Page 0xb0 (Block Limits) is incorrectly
> emulated in QEMU's scsi-generic driver, causing guest VMs to fail
> operations like mkfs.xfs.
>
> Summary:
> When using QEMU stale-11.0 with scsi-block passthrough, the Optimal
> Transfer Length (and related fields) in VPD Page 0xb0 are incorrectly
> capped to a much smaller value than what the host device reports. This
> is caused by commit 3cb29058 which modified blk_get_max_transfer() to
> no longer check bs->bl.max_hw_transfer, and the scsi-generic code at
> line 194-198 uses this function to compute VPD fields(Optimal Transfer
> Length).
> My git repository doesn't contain a commit 3cb29058. Please specify where you got your copy of the QEMU code from.
The issue is related to the following patches:
01ef818 scsi-generic: pass max_segments via max_iov field in BlockLimits
1847346 file-posix: try BLKSECTGET on block devices too, do not round to power of 2
cc071629 block: introduce max_hw_iov for use in scsi-generic
We changed the code to use blk_get_max_hw_transfer() and blk_get_max_hw_iov for both offset 8 and offset 12:
stl_be_p(&buf[8], max_transfer);
stl_be_p(&buf[12], MIN_NON_ZERO(max_transfer, ldl_be_p(&buf[12])));
> Environment:
> Host: Linux with multipath + LUN
> Guest: Linux VM with QEMU 11.0 scsi-block passthrough Host kernel:
> 6.6.0
Supplement information:
[root@ ~]# lspci |grep Emu
02:00.0 Fibre Channel: Emulex Corporation LPe15000/LPe16000 Series 8Gb/16Gb Fibre Channel Adapter (rev 30)
02:00.1 Fibre Channel: Emulex Corporation LPe15000/LPe16000 Series 8Gb/16Gb Fibre Channel Adapter (rev 30)
[root@ ~]# cat /sys/block/dm-1/queue/max_segments
64
[root@ ~]# cat /sys/block/sdd/queue/max_segments
64
>
> Reproduction Steps:
> Set up a multipath environment with a LUN (physical disk with stripe
> geometry) Pass the LUN through to a QEMU VM using scsi-block:
> -device virtio-scsi-pci,id=scsi0,bus=pci.0,addr=0xa \ -drive
> file=/dev/disk/by-id/dm-name-36000d310057df8000000000000000969,format=
> raw,if=none,id=drive-scsi0-0-0-0,cache=none,aio=native \ -device
> scsi-block,bus=scsi0.0,channel=0,scsi-id=0,lun=0,drive=drive-scsi0-0-0
> -0,id=scsi0-0-0-0\
>
> Inside the VM, run mkfs.xfs /dev/sda
>
> Expected Behavior:
> mkfs.xfs should succeed
> VPD Page 0xb0 should report Optimal transfer length matching the
> host's RAID stripe size
>
> Actual Behavior:
> 1. mkfs.xfs fails inside VM
> SB stripe unit sanity check failed
> Metadata corruption detected at 0x...
> xfx_sb block 0x0/0x1000
> libxfs_bwrite: write verifier failed
> SB stripe unit sanity check failed
> AG 0x0 unlinked bucket sanity check failed
> mkfs.xfs: writing AG headers failed, err=117
>
> Note: No error in dmesg because XFS failed during in-memory superblock construction, never issuing actual I/O.
>
> mkfs.xfs -f -d su=0.sw=0 /dev/sda
Disabling striping in mkfs can be verified through testing.
> 2. VPD comparison between host and guest Host (sg_vpd -p 0xb0
> /dev/disk/by-id/dm-name-36000d310057df8000000000000000969)
> Block limits VPD page (SBC)
> Write same non-zero (WSNZ): 0
> Maximum compare and write length: 0x1
> Optimal transfer length granularity: 0x1000
> Maximum transfer length: 0 blocks [not reported]
> Optimal transfer length: 0x1000
> Maximum prefetch length: 0 blocks [not reported]
> Maximum unmap LBA count: 0x200000
> Maximum unmap block descriptor count: 0x1
> Optimal unmap granularity: 0x1000
> Unmap granularity alignment valid: true
> Unmap granularity alignment: 0x0
> Maximum write same length: 0 blocks [not reported]
> Maximum atomic transfer length: 0 blocks [not reported]
> Atomic alignment: 0 blocks [unaligned atomic writes permitted]
> Atomic transfer length granularity: 0 blocks [no granularity requirement]
> Maximum atomic transfer length with atomic boundary: 0 blocks [not reported]
> Maximum atomic boundary size: 0 blocks [can only write atomic 1
> block]
> [root@cvknode151 ~]# ll
> /dev/disk/by-id/dm-name-36000d310057df8000000000000000969
> lrwxrwxrwx 1 root root 10 Oct 13 15:55
> /dev/disk/by-id/dm-name-36000d310057df8000000000000000969 ->
> ../../dm-1
> [root@cvknode151 ~]# cat /sys/block/dm-1/queue/max_segments
> 64
> Guest (sg_vpd -p 0xb0 /dev/sda):
> Block limits VPD page (SBC):
> Write same non-zero (WSNZ): 0
> Maximum compare and write length: 1 blocks
> Optimal transfer length granularity: 4096 blocks
> Maximum transfer length: 512 blocks
> Optimal transfer length: 512 blocks
← INCORRECT
> Maximum prefetch transfer length: 0 blocks [ignored]
> Maximum unmap LBA count: 2097152
> Maximum unmap block descriptor count: 1
> Optimal unmap granularity: 4096 blocks
> Unmap granularity alignment valid: true
> Unmap granularity alignment: 0
> Maximum write same length: 0 blocks [not reported]
> Maximum atomic transfer length: 0 blocks [not reported]
> Atomic alignment: 0 [unaligned atomic writes permitted]
> Atomic transfer length granularity: 0 [no granularity requirement
> Maximum atomic transfer length with atomic boundary: 0 blocks [not reported]
> Maximum atomic boundary size: 0 blocks [can only write atomic 1
> block]
>
> Restore "opt xfer len" to before modification, mkfs.xfs can pass the
> test
>
> @@ -169,15 +169,15 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s)
> if (page == 0xb0) {
> + uint32_t max_transfer_opt =
> + blk_get_max_transfer(s->conf.blk) / s->blocksize;
>
> assert(max_transfer);
> max_transfer = MIN_NON_ZERO(max_transfer, max_iov * qemu_real_host_page_size)
> / s->blocksize;
> stl_be_p(&r->buf[8], max_transfer);
> /* Also take care of the opt xfer len. */
> stl_be_p(&r->buf[12],
> - MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12])));
> + MIN_NON_ZERO(max_transfer_opt,
> + ldl_be_p(&r->buf[12])));
> } else if (s->needs_vpd_bl_emulation && page == 0x00 && r->buflen >= 4) {
> /*
> * Now we're capable of supplying the VPD Block Limits
> Code that roughly looked like this existed in QEMU 7.0 and earlier. You said you're using QEMU 11.0, so it shouldn't exist in your code.
Sorry, update
+++ b/hw/scsi/scsi-generic.c
@@ -192,10 +192,11 @@ static int scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s, int len)
uint8_t buf[16] = {};
uint8_t buf_used = MIN(r->buflen, 16);
uint64_t max_transfer = calculate_max_transfer(s);
+ uint32_t max_transfer_opt = blk_get_max_transfer(s->conf.blk) / s->blocksize;
memcpy(buf, r->buf, buf_used);
stl_be_p(&buf[8], max_transfer);
- stl_be_p(&buf[12], MIN_NON_ZERO(max_transfer, ldl_be_p(&buf[12])));
+ stl_be_p(&buf[12], MIN_NON_ZERO(max_transfer_opt, ldl_be_p(&buf[12])));
memcpy(r->buf + 8, buf + 8, buf_used - 8);
} else if (s->needs_vpd_bl_emulation && page == 0x00 && r->buflen >= 4) {
Kevin