snic: Handling control path queue issues
authorNarsimhulu Musini <nmusini@cisco.com>
Thu, 17 Mar 2016 07:51:12 +0000 (00:51 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Mon, 11 Apr 2016 20:57:09 +0000 (16:57 -0400)
Fix handles control path queue issues such as queue full and sudden
removal of hardware.

Signed-off-by: Narsimhulu Musini <nmusini@cisco.com>
Signed-off-by: Sesidhar Baddela <sebaddel@cisco.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/snic/vnic_dev.c

index e0b5549bc9fb11b875dafd183f61d2ce97dbb7a0..dad5fc66effb5e37453bbc1ecb88fcc9c97b9c66 100644 (file)
@@ -263,12 +263,20 @@ static int _svnic_dev_cmd2(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
        int wait)
 {
        struct devcmd2_controller *dc2c = vdev->devcmd2;
-       struct devcmd2_result *result = dc2c->result + dc2c->next_result;
+       struct devcmd2_result *result = NULL;
        unsigned int i;
        int delay;
        int err;
        u32 posted;
+       u32 fetch_idx;
        u32 new_posted;
+       u8 color;
+
+       fetch_idx = ioread32(&dc2c->wq_ctrl->fetch_index);
+       if (fetch_idx == 0xFFFFFFFF) { /* check for hardware gone  */
+               /* Hardware surprise removal: return error */
+               return -ENODEV;
+       }
 
        posted = ioread32(&dc2c->wq_ctrl->posted_index);
 
@@ -278,6 +286,13 @@ static int _svnic_dev_cmd2(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
        }
 
        new_posted = (posted + 1) % DEVCMD2_RING_SIZE;
+       if (new_posted == fetch_idx) {
+               pr_err("%s: wq is full while issuing devcmd2 command %d, fetch index: %u, posted index: %u\n",
+                       pci_name(vdev->pdev), _CMD_N(cmd), fetch_idx, posted);
+
+               return -EBUSY;
+       }
+
        dc2c->cmd_ring[posted].cmd = cmd;
        dc2c->cmd_ring[posted].flags = 0;
 
@@ -299,14 +314,22 @@ static int _svnic_dev_cmd2(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
        if (dc2c->cmd_ring[posted].flags & DEVCMD2_FNORESULT)
                return 0;
 
+       result = dc2c->result + dc2c->next_result;
+       color = dc2c->color;
+
+       /*
+        * Increment next_result, after posting the devcmd, irrespective of
+        * devcmd result, and it should be done only once.
+        */
+       dc2c->next_result++;
+       if (dc2c->next_result == dc2c->result_size) {
+               dc2c->next_result = 0;
+               dc2c->color = dc2c->color ? 0 : 1;
+       }
+
        for (delay = 0; delay < wait; delay++) {
                udelay(100);
-               if (result->color == dc2c->color) {
-                       dc2c->next_result++;
-                       if (dc2c->next_result == dc2c->result_size) {
-                               dc2c->next_result = 0;
-                               dc2c->color = dc2c->color ? 0 : 1;
-                       }
+               if (result->color == color) {
                        if (result->error) {
                                err = (int) result->error;
                                if (err != ERR_ECMDUNKNOWN ||
@@ -317,13 +340,6 @@ static int _svnic_dev_cmd2(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
                                return err;
                        }
                        if (_CMD_DIR(cmd) & _CMD_DIR_READ) {
-                               /*
-                                * Adding the rmb() prevents the compiler
-                                * and/or CPU from reordering the reads which
-                                * would potentially result in reading stale
-                                * values.
-                                */
-                               rmb();
                                for (i = 0; i < VNIC_DEVCMD_NARGS; i++)
                                        vdev->args[i] = result->results[i];
                        }