scsi: mpi3mr: Hardware workaround for UNMAP commands to NVMe drives

The controller hardware can not handle certain UNMAP commands for NVMe
drives. Add support in the driver for checking those commands and handle
them appropriately.

Link: https://lore.kernel.org/r/20210520152545.2710479-17-kashyap.desai@broadcom.com
Cc: sathya.prakash@broadcom.com
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Tomas Henzl <thenzl@redhat.com>
Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com>
Signed-off-by: Kashyap Desai <kashyap.desai@broadcom.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Kashyap Desai 2021-05-20 20:55:37 +05:30 committed by Martin K. Petersen
parent 82141ddba9
commit 392bbeb85b

View file

@ -2767,6 +2767,101 @@ static int mpi3mr_target_alloc(struct scsi_target *starget)
return retval;
}
/**
* mpi3mr_check_return_unmap - Whether an unmap is allowed
* @mrioc: Adapter instance reference
* @scmd: SCSI Command reference
*
* The controller hardware cannot handle certain unmap commands
* for NVMe drives, this routine checks those and return true
* and completes the SCSI command with proper status and sense
* data.
*
* Return: TRUE for not allowed unmap, FALSE otherwise.
*/
static bool mpi3mr_check_return_unmap(struct mpi3mr_ioc *mrioc,
struct scsi_cmnd *scmd)
{
unsigned char *buf;
u16 param_len, desc_len;
param_len = get_unaligned_be16(scmd->cmnd + 7);
if (!param_len) {
ioc_warn(mrioc,
"%s: cdb received with zero parameter length\n",
__func__);
scsi_print_command(scmd);
scmd->result = DID_OK << 16;
scmd->scsi_done(scmd);
return true;
}
if (param_len < 24) {
ioc_warn(mrioc,
"%s: cdb received with invalid param_len: %d\n",
__func__, param_len);
scsi_print_command(scmd);
scmd->result = (DRIVER_SENSE << 24) |
SAM_STAT_CHECK_CONDITION;
scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST,
0x1A, 0);
scmd->scsi_done(scmd);
return true;
}
if (param_len != scsi_bufflen(scmd)) {
ioc_warn(mrioc,
"%s: cdb received with param_len: %d bufflen: %d\n",
__func__, param_len, scsi_bufflen(scmd));
scsi_print_command(scmd);
scmd->result = (DRIVER_SENSE << 24) |
SAM_STAT_CHECK_CONDITION;
scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST,
0x1A, 0);
scmd->scsi_done(scmd);
return true;
}
buf = kzalloc(scsi_bufflen(scmd), GFP_ATOMIC);
if (!buf) {
scsi_print_command(scmd);
scmd->result = (DRIVER_SENSE << 24) |
SAM_STAT_CHECK_CONDITION;
scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST,
0x55, 0x03);
scmd->scsi_done(scmd);
return true;
}
scsi_sg_copy_to_buffer(scmd, buf, scsi_bufflen(scmd));
desc_len = get_unaligned_be16(&buf[2]);
if (desc_len < 16) {
ioc_warn(mrioc,
"%s: Invalid descriptor length in param list: %d\n",
__func__, desc_len);
scsi_print_command(scmd);
scmd->result = (DRIVER_SENSE << 24) |
SAM_STAT_CHECK_CONDITION;
scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST,
0x26, 0);
scmd->scsi_done(scmd);
kfree(buf);
return true;
}
if (param_len > (desc_len + 8)) {
scsi_print_command(scmd);
ioc_warn(mrioc,
"%s: Truncating param_len(%d) to desc_len+8(%d)\n",
__func__, param_len, (desc_len + 8));
param_len = desc_len + 8;
put_unaligned_be16(param_len, scmd->cmnd + 7);
scsi_print_command(scmd);
}
kfree(buf);
return false;
}
/**
* mpi3mr_allow_scmd_to_fw - Command is allowed during shutdown
* @scmd: SCSI Command reference
@ -2858,6 +2953,11 @@ static int mpi3mr_qcmd(struct Scsi_Host *shost,
goto out;
}
if ((scmd->cmnd[0] == UNMAP) &&
(stgt_priv_data->dev_type == MPI3_DEVICE_DEVFORM_PCIE) &&
mpi3mr_check_return_unmap(mrioc, scmd))
goto out;
host_tag = mpi3mr_host_tag_for_scmd(mrioc, scmd);
if (host_tag == MPI3MR_HOSTTAG_INVALID) {
scmd->result = DID_ERROR << 16;