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).

Environment:
Host: Linux with multipath + LUN
Guest: Linux VM with QEMU 11.0 scsi-block passthrough
Host kernel: 6.6.0

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
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


Please let me know if you need any additional information.

Best regards,
wangyong



Reply via email to