- 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.ben...@microsemi.com>
Reviewed-by: Scott Teel <scott.t...@microsemi.com>
Reviewed-by: Kevin Barnett <kevin.barn...@microsemi.com>
Signed-off-by: Don Brace <don.br...@microsemi.com>
---
 drivers/scsi/hpsa.c |   62 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 61 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 50f7c09..68d020a 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -4588,7 +4588,55 @@ static int hpsa_scatter_gather(struct ctlr_info *h,
        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;
@@ -4655,6 +4703,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;
@@ -4819,6 +4873,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;

Reply via email to