scsi: sym53c8xx_2: Rework reset handling
[linux-2.6-microblaze.git] / drivers / scsi / sym53c8xx_2 / sym_glue.c
index 77e6c3a..a2560cc 100644 (file)
@@ -564,7 +564,10 @@ static void sym53c8xx_timer(struct timer_list *t)
  *  Generic method for our eh processing.
  *  The 'op' argument tells what we have to do.
  */
-static int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd)
+/*
+ * Error handlers called from the eh thread (one thread per HBA).
+ */
+static int sym53c8xx_eh_abort_handler(struct scsi_cmnd *cmd)
 {
        struct sym_ucmd *ucmd = SYM_UCMD_PTR(cmd);
        struct Scsi_Host *shost = cmd->device->host;
@@ -576,7 +579,7 @@ static int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd)
        int sts = -1;
        struct completion eh_done;
 
-       scmd_printk(KERN_WARNING, cmd, "%s operation started\n", opname);
+       scmd_printk(KERN_WARNING, cmd, "ABORT operation started\n");
 
        /*
         * Escalate to host reset if the PCI bus went down
@@ -594,19 +597,7 @@ static int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd)
                }
        }
 
-       /* Try to proceed the operation we have been asked for */
-       sts = -1;
-       switch(op) {
-       case SYM_EH_ABORT:
-               sts = sym_abort_scsiio(np, cmd, 1);
-               break;
-       case SYM_EH_DEVICE_RESET:
-               sts = sym_reset_scsi_target(np, cmd->device->id);
-               break;
-       default:
-               break;
-       }
-
+       sts = sym_abort_scsiio(np, cmd, 1);
        /* On error, restore everything and cross fingers :) */
        if (sts)
                cmd_queued = 0;
@@ -623,23 +614,60 @@ static int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd)
                spin_unlock_irq(shost->host_lock);
        }
 
-       dev_warn(&cmd->device->sdev_gendev, "%s operation %s.\n", opname,
+       dev_warn(&cmd->device->sdev_gendev, "ABORT operation %s.\n",
                        sts==0 ? "complete" :sts==-2 ? "timed-out" : "failed");
        return sts ? SCSI_FAILED : SCSI_SUCCESS;
 }
 
-
-/*
- * Error handlers called from the eh thread (one thread per HBA).
- */
-static int sym53c8xx_eh_abort_handler(struct scsi_cmnd *cmd)
+static int sym53c8xx_eh_target_reset_handler(struct scsi_cmnd *cmd)
 {
-       return sym_eh_handler(SYM_EH_ABORT, "ABORT", cmd);
-}
+       struct scsi_target *starget = scsi_target(cmd->device);
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+       struct sym_data *sym_data = shost_priv(shost);
+       struct pci_dev *pdev = sym_data->pdev;
+       struct sym_hcb *np = sym_data->ncb;
+       SYM_QUEHEAD *qp;
+       int sts;
+       struct completion eh_done;
 
-static int sym53c8xx_eh_device_reset_handler(struct scsi_cmnd *cmd)
-{
-       return sym_eh_handler(SYM_EH_DEVICE_RESET, "DEVICE RESET", cmd);
+       starget_printk(KERN_WARNING, starget,
+                      "TARGET RESET operation started\n");
+
+       /*
+        * Escalate to host reset if the PCI bus went down
+        */
+       if (pci_channel_offline(pdev))
+               return SCSI_FAILED;
+
+       spin_lock_irq(shost->host_lock);
+       sts = sym_reset_scsi_target(np, starget->id);
+       if (!sts) {
+               FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
+                       struct sym_ccb *cp = sym_que_entry(qp, struct sym_ccb,
+                                                          link_ccbq);
+                       struct scsi_cmnd *cmd = cp->cmd;
+                       struct sym_ucmd *ucmd;
+
+                       if (!cmd || cmd->device->channel != starget->channel ||
+                           cmd->device->id != starget->id)
+                               continue;
+
+                       ucmd = SYM_UCMD_PTR(cmd);
+                       init_completion(&eh_done);
+                       ucmd->eh_done = &eh_done;
+                       spin_unlock_irq(shost->host_lock);
+                       if (!wait_for_completion_timeout(&eh_done, 5*HZ)) {
+                               ucmd->eh_done = NULL;
+                               sts = -2;
+                       }
+                       spin_lock_irq(shost->host_lock);
+               }
+       }
+       spin_unlock_irq(shost->host_lock);
+
+       starget_printk(KERN_WARNING, starget, "TARGET RESET operation %s.\n",
+                       sts==0 ? "complete" :sts==-2 ? "timed-out" : "failed");
+       return SCSI_SUCCESS;
 }
 
 static int sym53c8xx_eh_bus_reset_handler(struct scsi_cmnd *cmd)
@@ -1660,7 +1688,7 @@ static const struct scsi_host_template sym2_template = {
        .slave_configure        = sym53c8xx_slave_configure,
        .slave_destroy          = sym53c8xx_slave_destroy,
        .eh_abort_handler       = sym53c8xx_eh_abort_handler,
-       .eh_device_reset_handler = sym53c8xx_eh_device_reset_handler,
+       .eh_target_reset_handler = sym53c8xx_eh_target_reset_handler,
        .eh_bus_reset_handler   = sym53c8xx_eh_bus_reset_handler,
        .eh_host_reset_handler  = sym53c8xx_eh_host_reset_handler,
        .this_id                = 7,