A logical unit explains a CHECK CONDITION through its sense data, and without it a caller cannot tell a transient condition from a permanent one. struct scsi_cmd has carried a sense buffer all along that no driver ever filled, so fill it, and clear the length in scsi_exec() so that one command cannot be read as another's sense.
Signed-off-by: Alexey Charkov <[email protected]> --- drivers/scsi/scsi-uclass.c | 3 +++ drivers/ufs/ufs-uclass.c | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/scsi-uclass.c b/drivers/scsi/scsi-uclass.c index 39b4c7476d45..54afac6eaf51 100644 --- a/drivers/scsi/scsi-uclass.c +++ b/drivers/scsi/scsi-uclass.c @@ -22,6 +22,9 @@ int scsi_exec(struct udevice *dev, struct scsi_cmd *pccb) if (!ops->exec) return -ENOSYS; + /* Whatever a previous command left behind is not this command's sense */ + pccb->sensedatalen = 0; + return ops->exec(dev, pccb); } diff --git a/drivers/ufs/ufs-uclass.c b/drivers/ufs/ufs-uclass.c index 6ef01f8e4aa7..acaecfa1e341 100644 --- a/drivers/ufs/ufs-uclass.c +++ b/drivers/ufs/ufs-uclass.c @@ -1657,6 +1657,24 @@ static void prepare_prdt_table(struct ufs_hba *hba, struct scsi_cmd *pccb) ufshcd_cache_flush(req_desc, sizeof(*req_desc)); } +/** + * ufshcd_copy_sense_data - hand the response sense data to the SCSI layer + * + * A logical unit explains a CHECK CONDITION through its sense data, which the + * caller needs to tell a transient condition from a permanent one. + */ +static void ufshcd_copy_sense_data(struct ufs_hba *hba, struct scsi_cmd *pccb) +{ + struct utp_upiu_rsp *rsp = hba->ucd_rsp_ptr; + u16 len = be16_to_cpu(rsp->sr.sense_data_len); + + len = min_t(u16, len, RESPONSE_UPIU_SENSE_DATA_LENGTH); + len = min_t(u16, len, sizeof(pccb->sense_buf)); + + memcpy(pccb->sense_buf, rsp->sr.sense_data, len); + pccb->sensedatalen = len; +} + static int ufs_scsi_exec(struct udevice *scsi_dev, struct scsi_cmd *pccb) { struct ufs_hba *hba = dev_get_uclass_priv(scsi_dev->parent); @@ -1683,8 +1701,11 @@ static int ufs_scsi_exec(struct udevice *scsi_dev, struct scsi_cmd *pccb) result = ufshcd_get_rsp_upiu_result(hba->ucd_rsp_ptr); scsi_status = result & MASK_SCSI_STATUS; - if (scsi_status) + if (scsi_status) { + ufshcd_copy_sense_data(hba, pccb); + pccb->status = scsi_status; return -EINVAL; + } break; case UPIU_TRANSACTION_REJECT_UPIU: -- 2.54.0
