Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
[safe/jmp/linux-2.6] / drivers / scsi / qla4xxx / ql4_os.c
index 10a233e..db7ea3b 100644 (file)
@@ -46,6 +46,8 @@ MODULE_PARM_DESC(ql4xextended_error_logging,
 
 int ql4_mod_unload = 0;
 
+#define QL4_DEF_QDEPTH 32
+
 /*
  * SCSI host template entry points
  */
@@ -71,10 +73,12 @@ static void qla4xxx_recovery_timedout(struct iscsi_cls_session *session);
 static int qla4xxx_queuecommand(struct scsi_cmnd *cmd,
                                void (*done) (struct scsi_cmnd *));
 static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd);
+static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd);
 static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd);
 static int qla4xxx_slave_alloc(struct scsi_device *device);
 static int qla4xxx_slave_configure(struct scsi_device *device);
 static void qla4xxx_slave_destroy(struct scsi_device *sdev);
+static void qla4xxx_scan_start(struct Scsi_Host *shost);
 
 static struct scsi_host_template qla4xxx_driver_template = {
        .module                 = THIS_MODULE,
@@ -83,6 +87,7 @@ static struct scsi_host_template qla4xxx_driver_template = {
        .queuecommand           = qla4xxx_queuecommand,
 
        .eh_device_reset_handler = qla4xxx_eh_device_reset,
+       .eh_target_reset_handler = qla4xxx_eh_target_reset,
        .eh_host_reset_handler  = qla4xxx_eh_host_reset,
 
        .slave_configure        = qla4xxx_slave_configure,
@@ -90,6 +95,7 @@ static struct scsi_host_template qla4xxx_driver_template = {
        .slave_destroy          = qla4xxx_slave_destroy,
 
        .scan_finished          = iscsi_scan_finished,
+       .scan_start             = qla4xxx_scan_start,
 
        .this_id                = -1,
        .cmd_per_lun            = 3,
@@ -109,9 +115,6 @@ static struct iscsi_transport qla4xxx_iscsi_transport = {
        .host_param_mask        = ISCSI_HOST_HWADDRESS |
                                  ISCSI_HOST_IPADDRESS |
                                  ISCSI_HOST_INITIATOR_NAME,
-       .sessiondata_size       = sizeof(struct ddb_entry),
-       .host_template          = &qla4xxx_driver_template,
-
        .tgt_dscvr              = qla4xxx_tgt_dscvr,
        .get_conn_param         = qla4xxx_conn_get_param,
        .get_session_param      = qla4xxx_sess_get_param,
@@ -271,7 +274,7 @@ int qla4xxx_add_sess(struct ddb_entry *ddb_entry)
                return err;
        }
 
-       ddb_entry->conn = iscsi_create_conn(ddb_entry->sess, 0);
+       ddb_entry->conn = iscsi_create_conn(ddb_entry->sess, 0, 0);
        if (!ddb_entry->conn) {
                iscsi_remove_session(ddb_entry->sess);
                DEBUG2(printk(KERN_ERR "Could not add connection.\n"));
@@ -288,7 +291,8 @@ struct ddb_entry *qla4xxx_alloc_sess(struct scsi_qla_host *ha)
        struct ddb_entry *ddb_entry;
        struct iscsi_cls_session *sess;
 
-       sess = iscsi_alloc_session(ha->host, &qla4xxx_iscsi_transport);
+       sess = iscsi_alloc_session(ha->host, &qla4xxx_iscsi_transport,
+                                  sizeof(struct ddb_entry));
        if (!sess)
                return NULL;
 
@@ -299,6 +303,18 @@ struct ddb_entry *qla4xxx_alloc_sess(struct scsi_qla_host *ha)
        return ddb_entry;
 }
 
+static void qla4xxx_scan_start(struct Scsi_Host *shost)
+{
+       struct scsi_qla_host *ha = shost_priv(shost);
+       struct ddb_entry *ddb_entry, *ddbtemp;
+
+       /* finish setup of sessions that were already setup in firmware */
+       list_for_each_entry_safe(ddb_entry, ddbtemp, &ha->ddb_list, list) {
+               if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE)
+                       qla4xxx_add_sess(ddb_entry);
+       }
+}
+
 /*
  * Timer routines
  */
@@ -337,7 +353,7 @@ void qla4xxx_mark_device_missing(struct scsi_qla_host *ha,
                      ha->host_no, ddb_entry->bus, ddb_entry->target,
                      ddb_entry->fw_ddb_index));
        iscsi_block_session(ddb_entry->sess);
-       iscsi_conn_error(ddb_entry->conn, ISCSI_ERR_CONN_FAILED);
+       iscsi_conn_error_event(ddb_entry->conn, ISCSI_ERR_CONN_FAILED);
 }
 
 static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha,
@@ -423,7 +439,7 @@ static int qla4xxx_queuecommand(struct scsi_cmnd *cmd,
                        cmd->result = DID_NO_CONNECT << 16;
                        goto qc_fail_command;
                }
-               goto qc_host_busy;
+               return SCSI_MLQUEUE_TARGET_BUSY;
        }
 
        if (test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags))
@@ -1178,7 +1194,6 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
        int ret = -ENODEV, status;
        struct Scsi_Host *host;
        struct scsi_qla_host *ha;
-       struct ddb_entry *ddb_entry, *ddbtemp;
        uint8_t init_retry_count = 0;
        char buf[34];
 
@@ -1297,13 +1312,6 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
        if (ret)
                goto probe_failed;
 
-       /* Update transport device information for all devices. */
-       list_for_each_entry_safe(ddb_entry, ddbtemp, &ha->ddb_list, list) {
-               if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE)
-                       if (qla4xxx_add_sess(ddb_entry))
-                               goto remove_host;
-       }
-
        printk(KERN_INFO
               " QLogic iSCSI HBA Driver version: %s\n"
               "  QLogic ISP%04x @ %s, host#=%ld, fw=%02d.%02d.%02d.%02d\n",
@@ -1313,10 +1321,6 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
        scsi_scan_host(host);
        return 0;
 
-remove_host:
-       qla4xxx_free_ddb_list(ha);
-       scsi_remove_host(host);
-
 probe_failed:
        qla4xxx_free_adapter(ha);
        scsi_host_put(ha->host);
@@ -1385,7 +1389,7 @@ static int qla4xxx_slave_alloc(struct scsi_device *sdev)
 
        sdev->hostdata = ddb;
        sdev->tagged_supported = 1;
-       scsi_activate_tcq(sdev, sdev->host->can_queue);
+       scsi_activate_tcq(sdev, QL4_DEF_QDEPTH);
        return 0;
 }
 
@@ -1480,7 +1484,7 @@ static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha)
 }
 
 /**
- * qla4xxx_eh_wait_for_active_target_commands - wait for active cmds to finish.
+ * qla4xxx_eh_wait_for_commands - wait for active cmds to finish.
  * @ha: pointer to to HBA
  * @t: target id
  * @l: lun id
@@ -1488,20 +1492,22 @@ static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha)
  * This function waits for all outstanding commands to a lun to complete. It
  * returns 0 if all pending commands are returned and 1 otherwise.
  **/
-static int qla4xxx_eh_wait_for_active_target_commands(struct scsi_qla_host *ha,
-                                                int t, int l)
+static int qla4xxx_eh_wait_for_commands(struct scsi_qla_host *ha,
+                                       struct scsi_target *stgt,
+                                       struct scsi_device *sdev)
 {
        int cnt;
        int status = 0;
        struct scsi_cmnd *cmd;
 
        /*
-        * Waiting for all commands for the designated target in the active
-        * array
+        * Waiting for all commands for the designated target or dev
+        * in the active array
         */
        for (cnt = 0; cnt < ha->host->can_queue; cnt++) {
                cmd = scsi_host_find_tag(ha->host, cnt);
-               if (cmd && cmd->device->id == t && cmd->device->lun == l) {
+               if (cmd && stgt == scsi_target(cmd->device) &&
+                   (!sdev || sdev == cmd->device)) {
                        if (!qla4xxx_eh_wait_on_command(ha, cmd)) {
                                status++;
                                break;
@@ -1536,7 +1542,7 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd)
        DEBUG2(printk(KERN_INFO
                      "scsi%ld: DEVICE_RESET cmd=%p jiffies = 0x%lx, to=%x,"
                      "dpc_flags=%lx, status=%x allowed=%d\n", ha->host_no,
-                     cmd, jiffies, cmd->timeout_per_command / HZ,
+                     cmd, jiffies, cmd->request->timeout / HZ,
                      ha->dpc_flags, cmd->result, cmd->allowed));
 
        /* FIXME: wait for hba to go online */
@@ -1546,24 +1552,19 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd)
                goto eh_dev_reset_done;
        }
 
-       /* Send marker. */
-       ha->marker_needed = 1;
-
-       /*
-        * If we are coming down the EH path, wait for all commands to complete
-        * for the device.
-        */
-       if (cmd->device->host->shost_state == SHOST_RECOVERY) {
-               if (qla4xxx_eh_wait_for_active_target_commands(ha,
-                                                         cmd->device->id,
-                                                         cmd->device->lun)){
-                       dev_info(&ha->pdev->dev,
-                                  "DEVICE RESET FAILED - waiting for "
-                                  "commands.\n");
-                       goto eh_dev_reset_done;
-               }
+       if (qla4xxx_eh_wait_for_commands(ha, scsi_target(cmd->device),
+                                        cmd->device)) {
+               dev_info(&ha->pdev->dev,
+                          "DEVICE RESET FAILED - waiting for "
+                          "commands.\n");
+               goto eh_dev_reset_done;
        }
 
+       /* Send marker. */
+       if (qla4xxx_send_marker_iocb(ha, ddb_entry, cmd->device->lun,
+               MM_LUN_RESET) != QLA_SUCCESS)
+               goto eh_dev_reset_done;
+
        dev_info(&ha->pdev->dev,
                   "scsi(%ld:%d:%d:%d): DEVICE RESET SUCCEEDED.\n",
                   ha->host_no, cmd->device->channel, cmd->device->id,
@@ -1577,6 +1578,59 @@ eh_dev_reset_done:
 }
 
 /**
+ * qla4xxx_eh_target_reset - callback for target reset.
+ * @cmd: Pointer to Linux's SCSI command structure
+ *
+ * This routine is called by the Linux OS to reset the target.
+ **/
+static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd)
+{
+       struct scsi_qla_host *ha = to_qla_host(cmd->device->host);
+       struct ddb_entry *ddb_entry = cmd->device->hostdata;
+       int stat;
+
+       if (!ddb_entry)
+               return FAILED;
+
+       starget_printk(KERN_INFO, scsi_target(cmd->device),
+                      "WARM TARGET RESET ISSUED.\n");
+
+       DEBUG2(printk(KERN_INFO
+                     "scsi%ld: TARGET_DEVICE_RESET cmd=%p jiffies = 0x%lx, "
+                     "to=%x,dpc_flags=%lx, status=%x allowed=%d\n",
+                     ha->host_no, cmd, jiffies, cmd->request->timeout / HZ,
+                     ha->dpc_flags, cmd->result, cmd->allowed));
+
+       stat = qla4xxx_reset_target(ha, ddb_entry);
+       if (stat != QLA_SUCCESS) {
+               starget_printk(KERN_INFO, scsi_target(cmd->device),
+                              "WARM TARGET RESET FAILED.\n");
+               return FAILED;
+       }
+
+       if (qla4xxx_eh_wait_for_commands(ha, scsi_target(cmd->device),
+                                        NULL)) {
+               starget_printk(KERN_INFO, scsi_target(cmd->device),
+                              "WARM TARGET DEVICE RESET FAILED - "
+                              "waiting for commands.\n");
+               return FAILED;
+       }
+
+       /* Send marker. */
+       if (qla4xxx_send_marker_iocb(ha, ddb_entry, cmd->device->lun,
+               MM_TGT_WARM_RESET) != QLA_SUCCESS) {
+               starget_printk(KERN_INFO, scsi_target(cmd->device),
+                              "WARM TARGET DEVICE RESET FAILED - "
+                              "marker iocb failed.\n");
+               return FAILED;
+       }
+
+       starget_printk(KERN_INFO, scsi_target(cmd->device),
+                      "WARM TARGET RESET SUCCEEDED.\n");
+       return SUCCESS;
+}
+
+/**
  * qla4xxx_eh_host_reset - kernel callback
  * @cmd: Pointer to Linux's SCSI command structure
  *