| From 142c779d05d1fef75134c3cb63f52ccbc96d9e1f Mon Sep 17 00:00:00 2001 |
| From: Alexey Makhalov <amakhalov@vmware.com> |
| Date: Mon, 20 Dec 2021 11:05:14 -0800 |
| Subject: scsi: vmw_pvscsi: Set residual data length conditionally |
| |
| From: Alexey Makhalov <amakhalov@vmware.com> |
| |
| commit 142c779d05d1fef75134c3cb63f52ccbc96d9e1f upstream. |
| |
| The PVSCSI implementation in the VMware hypervisor under specific |
| configuration ("SCSI Bus Sharing" set to "Physical") returns zero dataLen |
| in the completion descriptor for READ CAPACITY(16). As a result, the kernel |
| can not detect proper disk geometry. This can be recognized by the kernel |
| message: |
| |
| [ 0.776588] sd 1:0:0:0: [sdb] Sector size 0 reported, assuming 512. |
| |
| The PVSCSI implementation in QEMU does not set dataLen at all, keeping it |
| zeroed. This leads to a boot hang as was reported by Shmulik Ladkani. |
| |
| It is likely that the controller returns the garbage at the end of the |
| buffer. Residual length should be set by the driver in that case. The SCSI |
| layer will erase corresponding data. See commit bdb2b8cab439 ("[SCSI] erase |
| invalid data returned by device") for details. |
| |
| Commit e662502b3a78 ("scsi: vmw_pvscsi: Set correct residual data length") |
| introduced the issue by setting residual length unconditionally, causing |
| the SCSI layer to erase the useful payload beyond dataLen when this value |
| is returned as 0. |
| |
| As a result, considering existing issues in implementations of PVSCSI |
| controllers, we do not want to call scsi_set_resid() when dataLen == |
| 0. Calling scsi_set_resid() has no effect if dataLen equals buffer length. |
| |
| Link: https://lore.kernel.org/lkml/20210824120028.30d9c071@blondie/ |
| Link: https://lore.kernel.org/r/20211220190514.55935-1-amakhalov@vmware.com |
| Fixes: e662502b3a78 ("scsi: vmw_pvscsi: Set correct residual data length") |
| Cc: Matt Wang <wwentao@vmware.com> |
| Cc: Martin K. Petersen <martin.petersen@oracle.com> |
| Cc: Vishal Bhakta <vbhakta@vmware.com> |
| Cc: VMware PV-Drivers <pv-drivers@vmware.com> |
| Cc: James E.J. Bottomley <jejb@linux.ibm.com> |
| Cc: linux-scsi@vger.kernel.org |
| Cc: stable@vger.kernel.org |
| Reported-and-suggested-by: Shmulik Ladkani <shmulik.ladkani@gmail.com> |
| Signed-off-by: Alexey Makhalov <amakhalov@vmware.com> |
| Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/scsi/vmw_pvscsi.c | 7 +++++-- |
| 1 file changed, 5 insertions(+), 2 deletions(-) |
| |
| --- a/drivers/scsi/vmw_pvscsi.c |
| +++ b/drivers/scsi/vmw_pvscsi.c |
| @@ -578,9 +578,12 @@ static void pvscsi_complete_request(stru |
| * Commands like INQUIRY may transfer less data than |
| * requested by the initiator via bufflen. Set residual |
| * count to make upper layer aware of the actual amount |
| - * of data returned. |
| + * of data returned. There are cases when controller |
| + * returns zero dataLen with non zero data - do not set |
| + * residual count in that case. |
| */ |
| - scsi_set_resid(cmd, scsi_bufflen(cmd) - e->dataLen); |
| + if (e->dataLen && (e->dataLen < scsi_bufflen(cmd))) |
| + scsi_set_resid(cmd, scsi_bufflen(cmd) - e->dataLen); |
| cmd->result = (DID_OK << 16); |
| break; |
| |