Commit 392bbeb8 authored by Kashyap Desai's avatar Kashyap Desai Committed by Martin K. Petersen

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: default avatarHannes Reinecke <hare@suse.de>
Reviewed-by: default avatarTomas Henzl <thenzl@redhat.com>
Reviewed-by: default avatarHimanshu Madhani <himanshu.madhani@oracle.com>
Signed-off-by: default avatarKashyap Desai <kashyap.desai@broadcom.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 82141ddb
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment