scsi: hpsa: send ioaccel requests with 0 length down raid path
authorDon Brace <don.brace@microsemi.com>
Thu, 4 May 2017 22:51:42 +0000 (17:51 -0500)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 13 Jun 2017 00:48:01 +0000 (20:48 -0400)
- Block I/O requests with 0 length transfers which go down the ioaccel
  path. This causes lockup issues down in the basecode.
- These issues have been fixed, but there are customers who are
  experiencing the issues when running older firmware.

Reviewed-by: Scott Benesh <scott.benesh@microsemi.com>
Reviewed-by: Scott Teel <scott.teel@microsemi.com>
Reviewed-by: Kevin Barnett <kevin.barnett@microsemi.com>
Signed-off-by: Don Brace <don.brace@microsemi.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/hpsa.c

index 2ec90799340016b7ed905774543a6b94174a74e8..ea778cc453dc594c4eefa2846b9ff47b7728115f 100644 (file)
@@ -4591,7 +4591,55 @@ sglist_finished:
        return 0;
 }
 
-#define IO_ACCEL_INELIGIBLE (1)
+#define BUFLEN 128
+static inline void warn_zero_length_transfer(struct ctlr_info *h,
+                                               u8 *cdb, int cdb_len,
+                                               const char *func)
+{
+       char buf[BUFLEN];
+       int outlen;
+       int i;
+
+       outlen = scnprintf(buf, BUFLEN,
+                               "%s: Blocking zero-length request: CDB:", func);
+       for (i = 0; i < cdb_len; i++)
+               outlen += scnprintf(buf+outlen, BUFLEN - outlen,
+                                       "%02hhx", cdb[i]);
+       dev_warn(&h->pdev->dev, "%s\n", buf);
+}
+
+#define IO_ACCEL_INELIGIBLE 1
+/* zero-length transfers trigger hardware errors. */
+static bool is_zero_length_transfer(u8 *cdb)
+{
+       u32 block_cnt;
+
+       /* Block zero-length transfer sizes on certain commands. */
+       switch (cdb[0]) {
+       case READ_10:
+       case WRITE_10:
+       case VERIFY:            /* 0x2F */
+       case WRITE_VERIFY:      /* 0x2E */
+               block_cnt = get_unaligned_be16(&cdb[7]);
+               break;
+       case READ_12:
+       case WRITE_12:
+       case VERIFY_12: /* 0xAF */
+       case WRITE_VERIFY_12:   /* 0xAE */
+               block_cnt = get_unaligned_be32(&cdb[6]);
+               break;
+       case READ_16:
+       case WRITE_16:
+       case VERIFY_16:         /* 0x8F */
+               block_cnt = get_unaligned_be32(&cdb[10]);
+               break;
+       default:
+               return false;
+       }
+
+       return block_cnt == 0;
+}
+
 static int fixup_ioaccel_cdb(u8 *cdb, int *cdb_len)
 {
        int is_write = 0;
@@ -4658,6 +4706,12 @@ static int hpsa_scsi_ioaccel1_queue_command(struct ctlr_info *h,
 
        BUG_ON(cmd->cmd_len > IOACCEL1_IOFLAGS_CDBLEN_MAX);
 
+       if (is_zero_length_transfer(cdb)) {
+               warn_zero_length_transfer(h, cdb, cdb_len, __func__);
+               atomic_dec(&phys_disk->ioaccel_cmds_out);
+               return IO_ACCEL_INELIGIBLE;
+       }
+
        if (fixup_ioaccel_cdb(cdb, &cdb_len)) {
                atomic_dec(&phys_disk->ioaccel_cmds_out);
                return IO_ACCEL_INELIGIBLE;
@@ -4822,6 +4876,12 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h,
 
        BUG_ON(scsi_sg_count(cmd) > h->maxsgentries);
 
+       if (is_zero_length_transfer(cdb)) {
+               warn_zero_length_transfer(h, cdb, cdb_len, __func__);
+               atomic_dec(&phys_disk->ioaccel_cmds_out);
+               return IO_ACCEL_INELIGIBLE;
+       }
+
        if (fixup_ioaccel_cdb(cdb, &cdb_len)) {
                atomic_dec(&phys_disk->ioaccel_cmds_out);
                return IO_ACCEL_INELIGIBLE;