Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
[safe/jmp/linux-2.6] / drivers / scsi / qla2xxx / qla_os.c
index 8bdc5a2..2aed472 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * QLogic Fibre Channel HBA Driver
- * Copyright (c)  2003-2005 QLogic Corporation
+ * Copyright (c)  2003-2008 QLogic Corporation
  *
  * See LICENSE.qla2xxx for copyright and licensing details.
  */
@@ -10,6 +10,7 @@
 #include <linux/vmalloc.h>
 #include <linux/delay.h>
 #include <linux/kthread.h>
+#include <linux/mutex.h>
 
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsicam.h>
@@ -26,10 +27,6 @@ char qla2x00_version_str[40];
  */
 static struct kmem_cache *srb_cachep;
 
-/*
- * Ioctl related information.
- */
-int num_hosts;
 int ql2xlogintimeout = 20;
 module_param(ql2xlogintimeout, int, S_IRUGO|S_IRUSR);
 MODULE_PARM_DESC(ql2xlogintimeout,
@@ -70,7 +67,7 @@ static void qla2x00_free_device(scsi_qla_host_t *);
 
 static void qla2x00_config_dma_addressing(scsi_qla_host_t *ha);
 
-int ql2xfdmienable;
+int ql2xfdmienable=1;
 module_param(ql2xfdmienable, int, S_IRUGO|S_IRUSR);
 MODULE_PARM_DESC(ql2xfdmienable,
                "Enables FDMI registratons "
@@ -89,6 +86,13 @@ MODULE_PARM_DESC(ql2xqfullrampup,
                "depth for a device after a queue-full condition has been "
                "detected.  Default is 120 seconds.");
 
+int ql2xiidmaenable=1;
+module_param(ql2xiidmaenable, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xiidmaenable,
+               "Enables iIDMA settings "
+               "Default is 1 - perform iIDMA. 0 - no iIDMA.");
+
+
 /*
  * SCSI host template entry points
  */
@@ -103,21 +107,21 @@ static int qla24xx_queuecommand(struct scsi_cmnd *cmd,
                void (*fn)(struct scsi_cmnd *));
 static int qla2xxx_eh_abort(struct scsi_cmnd *);
 static int qla2xxx_eh_device_reset(struct scsi_cmnd *);
+static int qla2xxx_eh_target_reset(struct scsi_cmnd *);
 static int qla2xxx_eh_bus_reset(struct scsi_cmnd *);
 static int qla2xxx_eh_host_reset(struct scsi_cmnd *);
-static int qla2x00_loop_reset(scsi_qla_host_t *ha);
-static int qla2x00_device_reset(scsi_qla_host_t *, fc_port_t *);
 
 static int qla2x00_change_queue_depth(struct scsi_device *, int);
 static int qla2x00_change_queue_type(struct scsi_device *, int);
 
-struct scsi_host_template qla2x00_driver_template = {
+static struct scsi_host_template qla2x00_driver_template = {
        .module                 = THIS_MODULE,
        .name                   = QLA2XXX_DRIVER_NAME,
        .queuecommand           = qla2x00_queuecommand,
 
        .eh_abort_handler       = qla2xxx_eh_abort,
        .eh_device_reset_handler = qla2xxx_eh_device_reset,
+       .eh_target_reset_handler = qla2xxx_eh_target_reset,
        .eh_bus_reset_handler   = qla2xxx_eh_bus_reset,
        .eh_host_reset_handler  = qla2xxx_eh_host_reset,
 
@@ -149,6 +153,7 @@ struct scsi_host_template qla24xx_driver_template = {
 
        .eh_abort_handler       = qla2xxx_eh_abort,
        .eh_device_reset_handler = qla2xxx_eh_device_reset,
+       .eh_target_reset_handler = qla2xxx_eh_target_reset,
        .eh_bus_reset_handler   = qla2xxx_eh_bus_reset,
        .eh_host_reset_handler  = qla2xxx_eh_host_reset,
 
@@ -177,13 +182,6 @@ struct scsi_transport_template *qla2xxx_transport_vport_template = NULL;
  * Timer routines
  */
 
-void qla2x00_timer(scsi_qla_host_t *);
-
-__inline__ void qla2x00_start_timer(scsi_qla_host_t *,
-    void *, unsigned long);
-static __inline__ void qla2x00_restart_timer(scsi_qla_host_t *, unsigned long);
-__inline__ void qla2x00_stop_timer(scsi_qla_host_t *);
-
 __inline__ void
 qla2x00_start_timer(scsi_qla_host_t *ha, void *func, unsigned long interval)
 {
@@ -201,7 +199,7 @@ qla2x00_restart_timer(scsi_qla_host_t *ha, unsigned long interval)
        mod_timer(&ha->timer, jiffies + interval * HZ);
 }
 
-__inline__ void
+static __inline__ void
 qla2x00_stop_timer(scsi_qla_host_t *ha)
 {
        del_timer_sync(&ha->timer);
@@ -212,12 +210,9 @@ static int qla2x00_do_dpc(void *data);
 
 static void qla2x00_rst_aen(scsi_qla_host_t *);
 
-uint8_t qla2x00_mem_alloc(scsi_qla_host_t *);
-void qla2x00_mem_free(scsi_qla_host_t *ha);
-static int qla2x00_allocate_sp_pool( scsi_qla_host_t *ha);
-static void qla2x00_free_sp_pool(scsi_qla_host_t *ha);
+static int qla2x00_mem_alloc(scsi_qla_host_t *);
+static void qla2x00_mem_free(scsi_qla_host_t *ha);
 static void qla2x00_sp_free_dma(scsi_qla_host_t *, srb_t *);
-void qla2x00_sp_compl(scsi_qla_host_t *ha, srb_t *);
 
 /* -------------------------------------------------------------------------- */
 
@@ -264,9 +259,9 @@ qla24xx_pci_info_str(struct scsi_qla_host *ha, char *str)
 
                strcpy(str, "PCIe (");
                if (lspeed == 1)
-                       strcat(str, "2.5Gb/s ");
+                       strcat(str, "2.5GT/s ");
                else if (lspeed == 2)
-                       strcat(str, "5.0Gb/s ");
+                       strcat(str, "5.0GT/s ");
                else
                        strcat(str, "<unknown> ");
                snprintf(lwstr, sizeof(lwstr), "x%d)", lwidth);
@@ -351,6 +346,8 @@ qla24xx_fw_version_str(struct scsi_qla_host *ha, char *str)
                strcat(str, "[T10 CRC] ");
        if (ha->fw_attributes & BIT_5)
                strcat(str, "[VI] ");
+       if (ha->fw_attributes & BIT_10)
+               strcat(str, "[84XX] ");
        if (ha->fw_attributes & BIT_13)
                strcat(str, "[Experimental]");
        return str;
@@ -379,12 +376,17 @@ qla2x00_get_new_sp(scsi_qla_host_t *ha, fc_port_t *fcport,
 static int
 qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
 {
-       scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
+       scsi_qla_host_t *ha = shost_priv(cmd->device->host);
        fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
        struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device));
        srb_t *sp;
        int rval;
 
+       if (unlikely(pci_channel_offline(ha->pdev))) {
+               cmd->result = DID_REQUEUE << 16;
+               goto qc_fail_command;
+       }
+
        rval = fc_remote_port_chkready(rport);
        if (rval) {
                cmd->result = rval;
@@ -392,10 +394,8 @@ qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
        }
 
        /* Close window on fcport/rport state-transitioning. */
-       if (!*(fc_port_t **)rport->dd_data) {
-               cmd->result = DID_IMM_RETRY << 16;
-               goto qc_fail_command;
-       }
+       if (fcport->drport)
+               goto qc_target_busy;
 
        if (atomic_read(&fcport->state) != FCS_ONLINE) {
                if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
@@ -403,7 +403,7 @@ qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
                        cmd->result = DID_NO_CONNECT << 16;
                        goto qc_fail_command;
                }
-               goto qc_host_busy;
+               goto qc_target_busy;
        }
 
        spin_unlock_irq(ha->host->host_lock);
@@ -426,10 +426,11 @@ qc_host_busy_free_sp:
 
 qc_host_busy_lock:
        spin_lock_irq(ha->host->host_lock);
-
-qc_host_busy:
        return SCSI_MLQUEUE_HOST_BUSY;
 
+qc_target_busy:
+       return SCSI_MLQUEUE_TARGET_BUSY;
+
 qc_fail_command:
        done(cmd);
 
@@ -440,13 +441,18 @@ qc_fail_command:
 static int
 qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
 {
-       scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
+       scsi_qla_host_t *ha = shost_priv(cmd->device->host);
        fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
        struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device));
        srb_t *sp;
        int rval;
        scsi_qla_host_t *pha = to_qla_parent(ha);
 
+       if (unlikely(pci_channel_offline(pha->pdev))) {
+               cmd->result = DID_REQUEUE << 16;
+               goto qc24_fail_command;
+       }
+
        rval = fc_remote_port_chkready(rport);
        if (rval) {
                cmd->result = rval;
@@ -454,10 +460,8 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
        }
 
        /* Close window on fcport/rport state-transitioning. */
-       if (!*(fc_port_t **)rport->dd_data) {
-               cmd->result = DID_IMM_RETRY << 16;
-               goto qc24_fail_command;
-       }
+       if (fcport->drport)
+               goto qc24_target_busy;
 
        if (atomic_read(&fcport->state) != FCS_ONLINE) {
                if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
@@ -465,7 +469,7 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
                        cmd->result = DID_NO_CONNECT << 16;
                        goto qc24_fail_command;
                }
-               goto qc24_host_busy;
+               goto qc24_target_busy;
        }
 
        spin_unlock_irq(ha->host->host_lock);
@@ -488,10 +492,11 @@ qc24_host_busy_free_sp:
 
 qc24_host_busy_lock:
        spin_lock_irq(ha->host->host_lock);
-
-qc24_host_busy:
        return SCSI_MLQUEUE_HOST_BUSY;
 
+qc24_target_busy:
+       return SCSI_MLQUEUE_TARGET_BUSY;
+
 qc24_fail_command:
        done(cmd);
 
@@ -571,8 +576,6 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *ha)
        else
                return_status = QLA_FUNCTION_FAILED;
 
-       DEBUG2(printk("%s return_status=%d\n",__func__,return_status));
-
        return (return_status);
 }
 
@@ -618,6 +621,40 @@ qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha)
        return (return_status);
 }
 
+void
+qla2x00_abort_fcport_cmds(fc_port_t *fcport)
+{
+       int cnt;
+       unsigned long flags;
+       srb_t *sp;
+       scsi_qla_host_t *ha = fcport->ha;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
+
+       spin_lock_irqsave(&pha->hardware_lock, flags);
+       for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+               sp = pha->outstanding_cmds[cnt];
+               if (!sp)
+                       continue;
+               if (sp->fcport != fcport)
+                       continue;
+
+               spin_unlock_irqrestore(&pha->hardware_lock, flags);
+               if (ha->isp_ops->abort_command(ha, sp)) {
+                       DEBUG2(qla_printk(KERN_WARNING, ha,
+                           "Abort failed --  %lx\n", sp->cmd->serial_number));
+               } else {
+                       if (qla2x00_eh_wait_on_command(ha, sp->cmd) !=
+                           QLA_SUCCESS)
+                               DEBUG2(qla_printk(KERN_WARNING, ha,
+                                   "Abort failed while waiting --  %lx\n",
+                                   sp->cmd->serial_number));
+
+               }
+               spin_lock_irqsave(&pha->hardware_lock, flags);
+       }
+       spin_unlock_irqrestore(&pha->hardware_lock, flags);
+}
+
 static void
 qla2x00_block_error_handler(struct scsi_cmnd *cmnd)
 {
@@ -653,7 +690,7 @@ qla2x00_block_error_handler(struct scsi_cmnd *cmnd)
 static int
 qla2xxx_eh_abort(struct scsi_cmnd *cmd)
 {
-       scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
+       scsi_qla_host_t *ha = shost_priv(cmd->device->host);
        srb_t *sp;
        int ret, i;
        unsigned int id, lun;
@@ -686,7 +723,6 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
 
                DEBUG2(printk("%s(%ld): aborting sp %p from RISC. pid=%ld.\n",
                    __func__, ha->host_no, sp, serial));
-               DEBUG3(qla2x00_print_scsi_cmd(cmd));
 
                spin_unlock_irqrestore(&pha->hardware_lock, flags);
                if (ha->isp_ops->abort_command(ha, sp)) {
@@ -720,190 +756,123 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
        return ret;
 }
 
-/**************************************************************************
-* qla2x00_eh_wait_for_pending_target_commands
-*
-* Description:
-*    Waits for all the commands to come back from the specified target.
-*
-* Input:
-*    ha - pointer to scsi_qla_host structure.
-*    t  - target
-* Returns:
-*    Either SUCCESS or FAILED.
-*
-* Note:
-**************************************************************************/
+enum nexus_wait_type {
+       WAIT_HOST = 0,
+       WAIT_TARGET,
+       WAIT_LUN,
+};
+
 static int
-qla2x00_eh_wait_for_pending_target_commands(scsi_qla_host_t *ha, unsigned int t)
+qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha, unsigned int t,
+    unsigned int l, enum nexus_wait_type type)
 {
-       int     cnt;
-       int     status;
-       srb_t           *sp;
-       struct scsi_cmnd *cmd;
+       int cnt, match, status;
+       srb_t *sp;
        unsigned long flags;
        scsi_qla_host_t *pha = to_qla_parent(ha);
 
-       status = 0;
-
-       /*
-        * Waiting for all commands for the designated target in the active
-        * array
-        */
-       for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
-               spin_lock_irqsave(&pha->hardware_lock, flags);
+       status = QLA_SUCCESS;
+       spin_lock_irqsave(&pha->hardware_lock, flags);
+       for (cnt = 1; status == QLA_SUCCESS && cnt < MAX_OUTSTANDING_COMMANDS;
+           cnt++) {
                sp = pha->outstanding_cmds[cnt];
-               if (sp) {
-                       cmd = sp->cmd;
-                       spin_unlock_irqrestore(&pha->hardware_lock, flags);
-                       if (cmd->device->id == t &&
-                           ha->vp_idx == sp->ha->vp_idx) {
-                               if (!qla2x00_eh_wait_on_command(ha, cmd)) {
-                                       status = 1;
-                                       break;
-                               }
-                       }
-               } else {
-                       spin_unlock_irqrestore(&pha->hardware_lock, flags);
+               if (!sp)
+                       continue;
+
+               if (ha->vp_idx != sp->fcport->ha->vp_idx)
+                       continue;
+               match = 0;
+               switch (type) {
+               case WAIT_HOST:
+                       match = 1;
+                       break;
+               case WAIT_TARGET:
+                       match = sp->cmd->device->id == t;
+                       break;
+               case WAIT_LUN:
+                       match = (sp->cmd->device->id == t &&
+                           sp->cmd->device->lun == l);
+                       break;
                }
+               if (!match)
+                       continue;
+
+               spin_unlock_irqrestore(&pha->hardware_lock, flags);
+               status = qla2x00_eh_wait_on_command(ha, sp->cmd);
+               spin_lock_irqsave(&pha->hardware_lock, flags);
        }
-       return (status);
+       spin_unlock_irqrestore(&pha->hardware_lock, flags);
+
+       return status;
 }
 
+static char *reset_errors[] = {
+       "HBA not online",
+       "HBA not ready",
+       "Task management failed",
+       "Waiting for command completions",
+};
 
-/**************************************************************************
-* qla2xxx_eh_device_reset
-*
-* Description:
-*    The device reset function will reset the target and abort any
-*    executing commands.
-*
-*    NOTE: The use of SP is undefined within this context.  Do *NOT*
-*          attempt to use this value, even if you determine it is
-*          non-null.
-*
-* Input:
-*    cmd = Linux SCSI command packet of the command that cause the
-*          bus device reset.
-*
-* Returns:
-*    SUCCESS/FAILURE (defined as macro in scsi.h).
-*
-**************************************************************************/
 static int
-qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
+__qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
+    struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int))
 {
-       scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
+       scsi_qla_host_t *ha = shost_priv(cmd->device->host);
        fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
-       int ret = FAILED;
-       unsigned int id, lun;
-       unsigned long serial;
+       int err;
 
        qla2x00_block_error_handler(cmd);
 
-       id = cmd->device->id;
-       lun = cmd->device->lun;
-       serial = cmd->serial_number;
-
        if (!fcport)
-               return ret;
+               return FAILED;
 
-       qla_printk(KERN_INFO, ha,
-           "scsi(%ld:%d:%d): DEVICE RESET ISSUED.\n", ha->host_no, id, lun);
+       qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET ISSUED.\n",
+           ha->host_no, cmd->device->id, cmd->device->lun, name);
 
+       err = 0;
        if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS)
-               goto eh_dev_reset_done;
-
-       if (qla2x00_wait_for_loop_ready(ha) == QLA_SUCCESS) {
-               if (qla2x00_device_reset(ha, fcport) == 0)
-                       ret = SUCCESS;
-
-#if defined(LOGOUT_AFTER_DEVICE_RESET)
-               if (ret == SUCCESS) {
-                       if (fcport->flags & FC_FABRIC_DEVICE) {
-                               ha->isp_ops->fabric_logout(ha, fcport->loop_id);
-                               qla2x00_mark_device_lost(ha, fcport, 0, 0);
-                       }
-               }
-#endif
-       } else {
-               DEBUG2(printk(KERN_INFO
-                   "%s failed: loop not ready\n",__func__));
-       }
-
-       if (ret == FAILED) {
-               DEBUG3(printk("%s(%ld): device reset failed\n",
-                   __func__, ha->host_no));
-               qla_printk(KERN_INFO, ha, "%s: device reset failed\n",
-                   __func__);
+               goto eh_reset_failed;
+       err = 1;
+       if (qla2x00_wait_for_loop_ready(ha) != QLA_SUCCESS)
+               goto eh_reset_failed;
+       err = 2;
+       if (do_reset(fcport, cmd->device->lun) != QLA_SUCCESS)
+               goto eh_reset_failed;
+       err = 3;
+       if (qla2x00_eh_wait_for_pending_commands(ha, cmd->device->id,
+           cmd->device->lun, type) != QLA_SUCCESS)
+               goto eh_reset_failed;
+
+       qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET SUCCEEDED.\n",
+           ha->host_no, cmd->device->id, cmd->device->lun, name);
+
+       return SUCCESS;
+
+ eh_reset_failed:
+       qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET FAILED: %s.\n",
+           ha->host_no, cmd->device->id, cmd->device->lun, name,
+           reset_errors[err]);
+       return FAILED;
+}
 
-               goto eh_dev_reset_done;
-       }
+static int
+qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
+{
+       scsi_qla_host_t *ha = shost_priv(cmd->device->host);
 
-       /* Flush outstanding commands. */
-       if (qla2x00_eh_wait_for_pending_target_commands(ha, id))
-               ret = FAILED;
-       if (ret == FAILED) {
-               DEBUG3(printk("%s(%ld): failed while waiting for commands\n",
-                   __func__, ha->host_no));
-               qla_printk(KERN_INFO, ha,
-                   "%s: failed while waiting for commands\n", __func__);
-       } else
-               qla_printk(KERN_INFO, ha,
-                   "scsi(%ld:%d:%d): DEVICE RESET SUCCEEDED.\n", ha->host_no,
-                   id, lun);
- eh_dev_reset_done:
-       return ret;
+       return __qla2xxx_eh_generic_reset("DEVICE", WAIT_LUN, cmd,
+           ha->isp_ops->lun_reset);
 }
 
-/**************************************************************************
-* qla2x00_eh_wait_for_pending_commands
-*
-* Description:
-*    Waits for all the commands to come back from the specified host.
-*
-* Input:
-*    ha - pointer to scsi_qla_host structure.
-*
-* Returns:
-*    1 : SUCCESS
-*    0 : FAILED
-*
-* Note:
-**************************************************************************/
 static int
-qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha)
+qla2xxx_eh_target_reset(struct scsi_cmnd *cmd)
 {
-       int     cnt;
-       int     status;
-       srb_t           *sp;
-       struct scsi_cmnd *cmd;
-       unsigned long flags;
-
-       status = 1;
+       scsi_qla_host_t *ha = shost_priv(cmd->device->host);
 
-       /*
-        * Waiting for all commands for the designated target in the active
-        * array
-        */
-       for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
-               spin_lock_irqsave(&ha->hardware_lock, flags);
-               sp = ha->outstanding_cmds[cnt];
-               if (sp) {
-                       cmd = sp->cmd;
-                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
-                       status = qla2x00_eh_wait_on_command(ha, cmd);
-                       if (status == 0)
-                               break;
-               }
-               else {
-                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
-               }
-       }
-       return (status);
+       return __qla2xxx_eh_generic_reset("TARGET", WAIT_TARGET, cmd,
+           ha->isp_ops->target_reset);
 }
 
-
 /**************************************************************************
 * qla2xxx_eh_bus_reset
 *
@@ -922,7 +891,7 @@ qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha)
 static int
 qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
 {
-       scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
+       scsi_qla_host_t *ha = shost_priv(cmd->device->host);
        scsi_qla_host_t *pha = to_qla_parent(ha);
        fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
        int ret = FAILED;
@@ -954,7 +923,8 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
                goto eh_bus_reset_done;
 
        /* Flush outstanding commands. */
-       if (!qla2x00_eh_wait_for_pending_commands(pha))
+       if (qla2x00_eh_wait_for_pending_commands(pha, 0, 0, WAIT_HOST) !=
+           QLA_SUCCESS)
                ret = FAILED;
 
 eh_bus_reset_done:
@@ -982,7 +952,7 @@ eh_bus_reset_done:
 static int
 qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
 {
-       scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
+       scsi_qla_host_t *ha = shost_priv(cmd->device->host);
        fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
        int ret = FAILED;
        unsigned int id, lun;
@@ -1025,7 +995,8 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
        clear_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags);
 
        /* Waiting for our command in done_queue to be returned to OS.*/
-       if (qla2x00_eh_wait_for_pending_commands(pha))
+       if (qla2x00_eh_wait_for_pending_commands(pha, 0, 0, WAIT_HOST) ==
+           QLA_SUCCESS)
                ret = SUCCESS;
 
        if (ha->parent)
@@ -1048,7 +1019,7 @@ eh_host_reset_lock:
 * Returns:
 *      0 = success
 */
-static int
+int
 qla2x00_loop_reset(scsi_qla_host_t *ha)
 {
        int ret;
@@ -1081,7 +1052,7 @@ qla2x00_loop_reset(scsi_qla_host_t *ha)
                        if (fcport->port_type != FCT_TARGET)
                                continue;
 
-                       ret = qla2x00_device_reset(ha, fcport);
+                       ret = ha->isp_ops->target_reset(fcport, 0);
                        if (ret != QLA_SUCCESS) {
                                DEBUG2_3(printk("%s(%ld): bus_reset failed: "
                                    "target_reset=%d d_id=%x.\n", __func__,
@@ -1096,24 +1067,23 @@ qla2x00_loop_reset(scsi_qla_host_t *ha)
        return QLA_SUCCESS;
 }
 
-/*
- * qla2x00_device_reset
- *     Issue bus device reset message to the target.
- *
- * Input:
- *     ha = adapter block pointer.
- *     t = SCSI ID.
- *     TARGET_QUEUE_LOCK must be released.
- *     ADAPTER_STATE_LOCK must be released.
- *
- * Context:
- *     Kernel context.
- */
-static int
-qla2x00_device_reset(scsi_qla_host_t *ha, fc_port_t *reset_fcport)
+void
+qla2x00_abort_all_cmds(scsi_qla_host_t *ha, int res)
 {
-       /* Abort Target command will clear Reservation */
-       return ha->isp_ops->abort_target(reset_fcport);
+       int cnt;
+       unsigned long flags;
+       srb_t *sp;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+               sp = ha->outstanding_cmds[cnt];
+               if (sp) {
+                       ha->outstanding_cmds[cnt] = NULL;
+                       sp->cmd->result = res;
+                       qla2x00_sp_compl(ha, sp);
+               }
+       }
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
 }
 
 static int
@@ -1132,7 +1102,7 @@ qla2xxx_slave_alloc(struct scsi_device *sdev)
 static int
 qla2xxx_slave_configure(struct scsi_device *sdev)
 {
-       scsi_qla_host_t *ha = to_qla_host(sdev->host);
+       scsi_qla_host_t *ha = shost_priv(sdev->host);
        struct fc_rport *rport = starget_to_rport(sdev->sdev_target);
 
        if (sdev->tagged_supported)
@@ -1140,7 +1110,7 @@ qla2xxx_slave_configure(struct scsi_device *sdev)
        else
                scsi_deactivate_tcq(sdev, ha->max_q_depth);
 
-       rport->dev_loss_tmo = ha->port_down_retry_count + 5;
+       rport->dev_loss_tmo = ha->port_down_retry_count;
 
        return 0;
 }
@@ -1272,7 +1242,8 @@ static struct isp_operations qla2100_isp_ops = {
        .enable_intrs           = qla2x00_enable_intrs,
        .disable_intrs          = qla2x00_disable_intrs,
        .abort_command          = qla2x00_abort_command,
-       .abort_target           = qla2x00_abort_target,
+       .target_reset           = qla2x00_abort_target,
+       .lun_reset              = qla2x00_lun_reset,
        .fabric_login           = qla2x00_login_fabric,
        .fabric_logout          = qla2x00_fabric_logout,
        .calc_req_entries       = qla2x00_calc_iocbs_32,
@@ -1305,7 +1276,8 @@ static struct isp_operations qla2300_isp_ops = {
        .enable_intrs           = qla2x00_enable_intrs,
        .disable_intrs          = qla2x00_disable_intrs,
        .abort_command          = qla2x00_abort_command,
-       .abort_target           = qla2x00_abort_target,
+       .target_reset           = qla2x00_abort_target,
+       .lun_reset              = qla2x00_lun_reset,
        .fabric_login           = qla2x00_login_fabric,
        .fabric_logout          = qla2x00_fabric_logout,
        .calc_req_entries       = qla2x00_calc_iocbs_32,
@@ -1338,7 +1310,8 @@ static struct isp_operations qla24xx_isp_ops = {
        .enable_intrs           = qla24xx_enable_intrs,
        .disable_intrs          = qla24xx_disable_intrs,
        .abort_command          = qla24xx_abort_command,
-       .abort_target           = qla24xx_abort_target,
+       .target_reset           = qla24xx_abort_target,
+       .lun_reset              = qla24xx_lun_reset,
        .fabric_login           = qla24xx_login_fabric,
        .fabric_logout          = qla24xx_fabric_logout,
        .calc_req_entries       = NULL,
@@ -1371,7 +1344,8 @@ static struct isp_operations qla25xx_isp_ops = {
        .enable_intrs           = qla24xx_enable_intrs,
        .disable_intrs          = qla24xx_disable_intrs,
        .abort_command          = qla24xx_abort_command,
-       .abort_target           = qla24xx_abort_target,
+       .target_reset           = qla24xx_abort_target,
+       .lun_reset              = qla24xx_lun_reset,
        .fabric_login           = qla24xx_login_fabric,
        .fabric_logout          = qla24xx_fabric_logout,
        .calc_req_entries       = NULL,
@@ -1384,7 +1358,7 @@ static struct isp_operations qla25xx_isp_ops = {
        .beacon_on              = qla24xx_beacon_on,
        .beacon_off             = qla24xx_beacon_off,
        .beacon_blink           = qla24xx_beacon_blink,
-       .read_optrom            = qla24xx_read_optrom_data,
+       .read_optrom            = qla25xx_read_optrom_data,
        .write_optrom           = qla24xx_write_optrom_data,
        .get_flash_version      = qla24xx_get_flash_version,
 };
@@ -1444,6 +1418,13 @@ qla2x00_set_isp_flags(scsi_qla_host_t *ha)
                ha->device_type |= DT_IIDMA;
                ha->fw_srisc_address = RISC_START_ADDRESS_2400;
                break;
+       case PCI_DEVICE_ID_QLOGIC_ISP8432:
+               ha->device_type |= DT_ISP8432;
+               ha->device_type |= DT_ZIO_SUPPORTED;
+               ha->device_type |= DT_FWI2;
+               ha->device_type |= DT_IIDMA;
+               ha->fw_srisc_address = RISC_START_ADDRESS_2400;
+               break;
        case PCI_DEVICE_ID_QLOGIC_ISP5422:
                ha->device_type |= DT_ISP5422;
                ha->device_type |= DT_FWI2;
@@ -1467,15 +1448,23 @@ qla2x00_set_isp_flags(scsi_qla_host_t *ha)
 static int
 qla2x00_iospace_config(scsi_qla_host_t *ha)
 {
-       unsigned long   pio, pio_len, pio_flags;
-       unsigned long   mmio, mmio_len, mmio_flags;
+       resource_size_t pio;
+
+       if (pci_request_selected_regions(ha->pdev, ha->bars,
+           QLA2XXX_DRIVER_NAME)) {
+               qla_printk(KERN_WARNING, ha,
+                   "Failed to reserve PIO/MMIO regions (%s)\n",
+                   pci_name(ha->pdev));
+
+               goto iospace_error_exit;
+       }
+       if (!(ha->bars & 1))
+               goto skip_pio;
 
        /* We only need PIO for Flash operations on ISP2312 v2 chips. */
        pio = pci_resource_start(ha->pdev, 0);
-       pio_len = pci_resource_len(ha->pdev, 0);
-       pio_flags = pci_resource_flags(ha->pdev, 0);
-       if (pio_flags & IORESOURCE_IO) {
-               if (pio_len < MIN_IOBASE_LEN) {
+       if (pci_resource_flags(ha->pdev, 0) & IORESOURCE_IO) {
+               if (pci_resource_len(ha->pdev, 0) < MIN_IOBASE_LEN) {
                        qla_printk(KERN_WARNING, ha,
                            "Invalid PCI I/O region size (%s)...\n",
                                pci_name(ha->pdev));
@@ -1487,36 +1476,24 @@ qla2x00_iospace_config(scsi_qla_host_t *ha)
                    pci_name(ha->pdev));
                pio = 0;
        }
+       ha->pio_address = pio;
 
+skip_pio:
        /* Use MMIO operations for all accesses. */
-       mmio = pci_resource_start(ha->pdev, 1);
-       mmio_len = pci_resource_len(ha->pdev, 1);
-       mmio_flags = pci_resource_flags(ha->pdev, 1);
-
-       if (!(mmio_flags & IORESOURCE_MEM)) {
+       if (!(pci_resource_flags(ha->pdev, 1) & IORESOURCE_MEM)) {
                qla_printk(KERN_ERR, ha,
-                   "region #0 not an MMIO resource (%s), aborting\n",
+                   "region #1 not an MMIO resource (%s), aborting\n",
                    pci_name(ha->pdev));
                goto iospace_error_exit;
        }
-       if (mmio_len < MIN_IOBASE_LEN) {
+       if (pci_resource_len(ha->pdev, 1) < MIN_IOBASE_LEN) {
                qla_printk(KERN_ERR, ha,
                    "Invalid PCI mem region size (%s), aborting\n",
                        pci_name(ha->pdev));
                goto iospace_error_exit;
        }
 
-       if (pci_request_regions(ha->pdev, QLA2XXX_DRIVER_NAME)) {
-               qla_printk(KERN_WARNING, ha,
-                   "Failed to reserve PIO/MMIO regions (%s)\n",
-                   pci_name(ha->pdev));
-
-               goto iospace_error_exit;
-       }
-
-       ha->pio_address = pio;
-       ha->pio_length = pio_len;
-       ha->iobase = ioremap(mmio, MIN_IOBASE_LEN);
+       ha->iobase = ioremap(pci_resource_start(ha->pdev, 1), MIN_IOBASE_LEN);
        if (!ha->iobase) {
                qla_printk(KERN_ERR, ha,
                    "cannot remap MMIO (%s), aborting\n", pci_name(ha->pdev));
@@ -1533,17 +1510,18 @@ iospace_error_exit:
 static void
 qla2xxx_scan_start(struct Scsi_Host *shost)
 {
-       scsi_qla_host_t *ha = (scsi_qla_host_t *)shost->hostdata;
+       scsi_qla_host_t *ha = shost_priv(shost);
 
        set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
        set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
        set_bit(RSCN_UPDATE, &ha->dpc_flags);
+       set_bit(NPIV_CONFIG_NEEDED, &ha->dpc_flags);
 }
 
 static int
 qla2xxx_scan_finished(struct Scsi_Host *shost, unsigned long time)
 {
-       scsi_qla_host_t *ha = (scsi_qla_host_t *)shost->hostdata;
+       scsi_qla_host_t *ha = shost_priv(shost);
 
        if (!ha->host)
                return 1;
@@ -1560,24 +1538,38 @@ static int __devinit
 qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        int     ret = -ENODEV;
-       device_reg_t __iomem *reg;
        struct Scsi_Host *host;
        scsi_qla_host_t *ha;
-       unsigned long   flags = 0;
-       char pci_info[20];
+       char pci_info[30];
        char fw_str[30];
        struct scsi_host_template *sht;
+       int bars, mem_only = 0;
 
-       if (pci_enable_device(pdev))
-               goto probe_out;
-
+       bars = pci_select_bars(pdev, IORESOURCE_MEM | IORESOURCE_IO);
        sht = &qla2x00_driver_template;
        if (pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2422 ||
            pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2432 ||
+           pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8432 ||
            pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5422 ||
            pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5432 ||
-           pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2532)
+           pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2532) {
+               bars = pci_select_bars(pdev, IORESOURCE_MEM);
                sht = &qla24xx_driver_template;
+               mem_only = 1;
+       }
+
+       if (mem_only) {
+               if (pci_enable_device_mem(pdev))
+                       goto probe_out;
+       } else {
+               if (pci_enable_device(pdev))
+                       goto probe_out;
+       }
+
+       if (pci_find_aer_capability(pdev))
+               if (pci_enable_pcie_error_reporting(pdev))
+                       goto probe_out;
+
        host = scsi_host_alloc(sht, sizeof(scsi_qla_host_t));
        if (host == NULL) {
                printk(KERN_WARNING
@@ -1586,7 +1578,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        }
 
        /* Clear our data area */
-       ha = (scsi_qla_host_t *)host->hostdata;
+       ha = shost_priv(host);
        memset(ha, 0, sizeof(scsi_qla_host_t));
 
        ha->pdev = pdev;
@@ -1594,6 +1586,9 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        ha->host_no = host->host_no;
        sprintf(ha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, ha->host_no);
        ha->parent = NULL;
+       ha->bars = bars;
+       ha->mem_only = mem_only;
+       spin_lock_init(&ha->hardware_lock);
 
        /* Set ISP-type information. */
        qla2x00_set_isp_flags(ha);
@@ -1607,8 +1602,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
            "Found an ISP%04X, irq %d, iobase 0x%p\n", pdev->device, pdev->irq,
            ha->iobase);
 
-       spin_lock_init(&ha->hardware_lock);
-
        ha->prev_topology = 0;
        ha->init_cb_size = sizeof(init_cb_t);
        ha->mgmt_svr_loop_id = MANAGEMENT_SERVER + ha->vp_idx;
@@ -1647,7 +1640,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
                if (IS_QLA2322(ha) || IS_QLA6322(ha))
                        ha->optrom_size = OPTROM_SIZE_2322;
                ha->isp_ops = &qla2300_isp_ops;
-       } else if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) {
+       } else if (IS_QLA24XX_TYPE(ha)) {
                host->max_id = MAX_TARGETS_2200;
                ha->mbx_count = MAILBOX_REGISTER_COUNT;
                ha->request_q_length = REQUEST_ENTRY_CNT_24XX;
@@ -1672,16 +1665,15 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        }
        host->can_queue = ha->request_q_length + 128;
 
-       /* load the F/W, read paramaters, and init the H/W */
-       ha->instance = num_hosts;
-
-       init_MUTEX(&ha->mbx_cmd_sem);
-       init_MUTEX(&ha->vport_sem);
-       init_MUTEX_LOCKED(&ha->mbx_intr_sem);
+       mutex_init(&ha->vport_lock);
+       init_completion(&ha->mbx_cmd_comp);
+       complete(&ha->mbx_cmd_comp);
+       init_completion(&ha->mbx_intr_comp);
 
        INIT_LIST_HEAD(&ha->list);
        INIT_LIST_HEAD(&ha->fcports);
        INIT_LIST_HEAD(&ha->vp_list);
+       INIT_LIST_HEAD(&ha->work_list);
 
        set_bit(0, (unsigned long *) ha->vp_idx_map);
 
@@ -1720,7 +1712,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 
        host->this_id = 255;
        host->cmd_per_lun = 3;
-       host->unique_id = ha->instance;
+       host->unique_id = host->host_no;
        host->max_cmd_len = MAX_CMDSZ;
        host->max_channel = MAX_BUSES - 1;
        host->max_lun = MAX_LUNS;
@@ -1736,51 +1728,25 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        DEBUG2(printk("DEBUG: detect hba %ld at address = %p\n",
            ha->host_no, ha));
 
-       ha->isp_ops->disable_intrs(ha);
-
-       spin_lock_irqsave(&ha->hardware_lock, flags);
-       reg = ha->iobase;
-       if (IS_FWI2_CAPABLE(ha)) {
-               WRT_REG_DWORD(&reg->isp24.hccr, HCCRX_CLR_HOST_INT);
-               WRT_REG_DWORD(&reg->isp24.hccr, HCCRX_CLR_RISC_INT);
-       } else {
-               WRT_REG_WORD(&reg->isp.semaphore, 0);
-               WRT_REG_WORD(&reg->isp.hccr, HCCR_CLR_RISC_INT);
-               WRT_REG_WORD(&reg->isp.hccr, HCCR_CLR_HOST_INT);
-
-               /* Enable proper parity */
-               if (!IS_QLA2100(ha) && !IS_QLA2200(ha)) {
-                       if (IS_QLA2300(ha))
-                               /* SRAM parity */
-                               WRT_REG_WORD(&reg->isp.hccr,
-                                   (HCCR_ENABLE_PARITY + 0x1));
-                       else
-                               /* SRAM, Instruction RAM and GP RAM parity */
-                               WRT_REG_WORD(&reg->isp.hccr,
-                                   (HCCR_ENABLE_PARITY + 0x7));
-               }
-       }
-       spin_unlock_irqrestore(&ha->hardware_lock, flags);
-
-       ha->isp_ops->enable_intrs(ha);
-
        pci_set_drvdata(pdev, ha);
 
        ha->flags.init_done = 1;
        ha->flags.online = 1;
 
-       num_hosts++;
-
        ret = scsi_add_host(host, &pdev->dev);
        if (ret)
                goto probe_failed;
 
+       ha->isp_ops->enable_intrs(ha);
+
        scsi_scan_host(host);
 
        qla2x00_alloc_sysfs_attr(ha);
 
        qla2x00_init_host_attr(ha);
 
+       qla2x00_dfs_setup(ha);
+
        qla_printk(KERN_INFO, ha, "\n"
            " QLogic Fibre Channel HBA Driver: %s\n"
            "  QLogic %s - %s\n"
@@ -1805,13 +1771,22 @@ probe_out:
        return ret;
 }
 
-static void __devexit
+static void
 qla2x00_remove_one(struct pci_dev *pdev)
 {
-       scsi_qla_host_t *ha;
+       scsi_qla_host_t *ha, *vha, *temp;
 
        ha = pci_get_drvdata(pdev);
 
+       list_for_each_entry_safe(vha, temp, &ha->vp_list, vp_list)
+               fc_vport_terminate(vha->fc_vport);
+
+       set_bit(UNLOADING, &ha->dpc_flags);
+
+       qla2x00_dfs_remove(ha);
+
+       qla84xx_put_chip(ha);
+
        qla2x00_free_sysfs_attr(ha);
 
        fc_remove_host(ha->host);
@@ -1829,10 +1804,14 @@ qla2x00_remove_one(struct pci_dev *pdev)
 static void
 qla2x00_free_device(scsi_qla_host_t *ha)
 {
+       qla2x00_abort_all_cmds(ha, DID_NO_CONNECT << 16);
+
        /* Disable timer */
        if (ha->timer_active)
                qla2x00_stop_timer(ha);
 
+       ha->flags.online = 0;
+
        /* Kill the kernel thread for this host */
        if (ha->dpc_thread) {
                struct task_struct *t = ha->dpc_thread;
@@ -1845,10 +1824,11 @@ qla2x00_free_device(scsi_qla_host_t *ha)
                kthread_stop(t);
        }
 
-       if (ha->eft)
-               qla2x00_trace_control(ha, TC_DISABLE, 0, 0);
+       if (ha->flags.fce_enabled)
+               qla2x00_disable_fce_trace(ha, NULL, NULL);
 
-       ha->flags.online = 0;
+       if (ha->eft)
+               qla2x00_disable_eft_trace(ha);
 
        /* Stop currently executing firmware. */
        qla2x00_try_to_stop_firmware(ha);
@@ -1864,34 +1844,28 @@ qla2x00_free_device(scsi_qla_host_t *ha)
        /* release io space registers  */
        if (ha->iobase)
                iounmap(ha->iobase);
-       pci_release_regions(ha->pdev);
+       pci_release_selected_regions(ha->pdev, ha->bars);
 }
 
 static inline void
 qla2x00_schedule_rport_del(struct scsi_qla_host *ha, fc_port_t *fcport,
     int defer)
 {
-       unsigned long flags;
        struct fc_rport *rport;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
        if (!fcport->rport)
                return;
 
        rport = fcport->rport;
        if (defer) {
-               spin_lock_irqsave(&fcport->rport_lock, flags);
+               spin_lock_irq(ha->host->host_lock);
                fcport->drport = rport;
-               fcport->rport = NULL;
-               *(fc_port_t **)rport->dd_data = NULL;
-               spin_unlock_irqrestore(&fcport->rport_lock, flags);
-               set_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags);
-       } else {
-               spin_lock_irqsave(&fcport->rport_lock, flags);
-               fcport->rport = NULL;
-               *(fc_port_t **)rport->dd_data = NULL;
-               spin_unlock_irqrestore(&fcport->rport_lock, flags);
+               spin_unlock_irq(ha->host->host_lock);
+               set_bit(FCPORT_UPDATE_NEEDED, &pha->dpc_flags);
+               qla2xxx_wake_dpc(pha);
+       } else
                fc_remote_port_delete(rport);
-       }
 }
 
 /*
@@ -1961,7 +1935,7 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer)
        scsi_qla_host_t *pha = to_qla_parent(ha);
 
        list_for_each_entry(fcport, &pha->fcports, list) {
-               if (ha->vp_idx != 0 && ha->vp_idx != fcport->vp_idx)
+               if (ha->vp_idx != fcport->vp_idx)
                        continue;
                /*
                 * No point in marking the device as lost, if the device is
@@ -1969,17 +1943,10 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer)
                 */
                if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD)
                        continue;
-               if (atomic_read(&fcport->state) == FCS_ONLINE) {
-                       if (defer)
-                               qla2x00_schedule_rport_del(ha, fcport, defer);
-                       else if (ha->vp_idx == fcport->vp_idx)
-                               qla2x00_schedule_rport_del(ha, fcport, defer);
-               }
+               if (atomic_read(&fcport->state) == FCS_ONLINE)
+                       qla2x00_schedule_rport_del(ha, fcport, defer);
                atomic_set(&fcport->state, FCS_DEVICE_LOST);
        }
-
-       if (defer)
-               qla2xxx_wake_dpc(ha);
 }
 
 /*
@@ -1988,183 +1955,109 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer)
 *
 * Returns:
 *      0  = success.
-*      1  = failure.
+*      !0  = failure.
 */
-uint8_t
+static int
 qla2x00_mem_alloc(scsi_qla_host_t *ha)
 {
        char    name[16];
-       uint8_t   status = 1;
-       int     retry= 10;
-
-       do {
-               /*
-                * This will loop only once if everything goes well, else some
-                * number of retries will be performed to get around a kernel
-                * bug where available mem is not allocated until after a
-                * little delay and a retry.
-                */
-               ha->request_ring = dma_alloc_coherent(&ha->pdev->dev,
-                   (ha->request_q_length + 1) * sizeof(request_t),
-                   &ha->request_dma, GFP_KERNEL);
-               if (ha->request_ring == NULL) {
-                       qla_printk(KERN_WARNING, ha,
-                           "Memory Allocation failed - request_ring\n");
-
-                       qla2x00_mem_free(ha);
-                       msleep(100);
-
-                       continue;
-               }
-
-               ha->response_ring = dma_alloc_coherent(&ha->pdev->dev,
-                   (ha->response_q_length + 1) * sizeof(response_t),
-                   &ha->response_dma, GFP_KERNEL);
-               if (ha->response_ring == NULL) {
-                       qla_printk(KERN_WARNING, ha,
-                           "Memory Allocation failed - response_ring\n");
-
-                       qla2x00_mem_free(ha);
-                       msleep(100);
-
-                       continue;
-               }
-
-               ha->gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE,
-                   &ha->gid_list_dma, GFP_KERNEL);
-               if (ha->gid_list == NULL) {
-                       qla_printk(KERN_WARNING, ha,
-                           "Memory Allocation failed - gid_list\n");
-
-                       qla2x00_mem_free(ha);
-                       msleep(100);
-
-                       continue;
-               }
-
-               /* get consistent memory allocated for init control block */
-               ha->init_cb = dma_alloc_coherent(&ha->pdev->dev,
-                   ha->init_cb_size, &ha->init_cb_dma, GFP_KERNEL);
-               if (ha->init_cb == NULL) {
-                       qla_printk(KERN_WARNING, ha,
-                           "Memory Allocation failed - init_cb\n");
-
-                       qla2x00_mem_free(ha);
-                       msleep(100);
-
-                       continue;
-               }
-               memset(ha->init_cb, 0, ha->init_cb_size);
-
-               snprintf(name, sizeof(name), "%s_%ld", QLA2XXX_DRIVER_NAME,
-                   ha->host_no);
-               ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev,
-                   DMA_POOL_SIZE, 8, 0);
-               if (ha->s_dma_pool == NULL) {
-                       qla_printk(KERN_WARNING, ha,
-                           "Memory Allocation failed - s_dma_pool\n");
-
-                       qla2x00_mem_free(ha);
-                       msleep(100);
-
-                       continue;
-               }
-
-               if (qla2x00_allocate_sp_pool(ha)) {
-                       qla_printk(KERN_WARNING, ha,
-                           "Memory Allocation failed - "
-                           "qla2x00_allocate_sp_pool()\n");
-
-                       qla2x00_mem_free(ha);
-                       msleep(100);
-
-                       continue;
-               }
-
-               /* Allocate memory for SNS commands */
-               if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
-                       /* Get consistent memory allocated for SNS commands */
-                       ha->sns_cmd = dma_alloc_coherent(&ha->pdev->dev,
-                           sizeof(struct sns_cmd_pkt), &ha->sns_cmd_dma,
-                           GFP_KERNEL);
-                       if (ha->sns_cmd == NULL) {
-                               /* error */
-                               qla_printk(KERN_WARNING, ha,
-                                   "Memory Allocation failed - sns_cmd\n");
-
-                               qla2x00_mem_free(ha);
-                               msleep(100);
-
-                               continue;
-                       }
-                       memset(ha->sns_cmd, 0, sizeof(struct sns_cmd_pkt));
-               } else {
-                       /* Get consistent memory allocated for MS IOCB */
-                       ha->ms_iocb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
-                           &ha->ms_iocb_dma);
-                       if (ha->ms_iocb == NULL) {
-                               /* error */
-                               qla_printk(KERN_WARNING, ha,
-                                   "Memory Allocation failed - ms_iocb\n");
-
-                               qla2x00_mem_free(ha);
-                               msleep(100);
-
-                               continue;
-                       }
-                       memset(ha->ms_iocb, 0, sizeof(ms_iocb_entry_t));
-
-                       /*
-                        * Get consistent memory allocated for CT SNS
-                        * commands
-                        */
-                       ha->ct_sns = dma_alloc_coherent(&ha->pdev->dev,
-                           sizeof(struct ct_sns_pkt), &ha->ct_sns_dma,
-                           GFP_KERNEL);
-                       if (ha->ct_sns == NULL) {
-                               /* error */
-                               qla_printk(KERN_WARNING, ha,
-                                   "Memory Allocation failed - ct_sns\n");
-
-                               qla2x00_mem_free(ha);
-                               msleep(100);
-
-                               continue;
-                       }
-                       memset(ha->ct_sns, 0, sizeof(struct ct_sns_pkt));
-
-                       if (IS_FWI2_CAPABLE(ha)) {
-                               /*
-                                * Get consistent memory allocated for SFP
-                                * block.
-                                */
-                               ha->sfp_data = dma_pool_alloc(ha->s_dma_pool,
-                                   GFP_KERNEL, &ha->sfp_data_dma);
-                               if (ha->sfp_data == NULL) {
-                                       qla_printk(KERN_WARNING, ha,
-                                           "Memory Allocation failed - "
-                                           "sfp_data\n");
-
-                                       qla2x00_mem_free(ha);
-                                       msleep(100);
-
-                                       continue;
-                               }
-                               memset(ha->sfp_data, 0, SFP_BLOCK_SIZE);
-                       }
-               }
-
-               /* Done all allocations without any error. */
-               status = 0;
 
-       } while (retry-- && status != 0);
+       ha->request_ring = dma_alloc_coherent(&ha->pdev->dev,
+           (ha->request_q_length + 1) * sizeof(request_t), &ha->request_dma,
+           GFP_KERNEL);
+       if (!ha->request_ring)
+               goto fail;
+
+       ha->response_ring = dma_alloc_coherent(&ha->pdev->dev,
+           (ha->response_q_length + 1) * sizeof(response_t),
+           &ha->response_dma, GFP_KERNEL);
+       if (!ha->response_ring)
+               goto fail_free_request_ring;
+
+       ha->gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE,
+           &ha->gid_list_dma, GFP_KERNEL);
+       if (!ha->gid_list)
+               goto fail_free_response_ring;
+
+       ha->init_cb = dma_alloc_coherent(&ha->pdev->dev, ha->init_cb_size,
+           &ha->init_cb_dma, GFP_KERNEL);
+       if (!ha->init_cb)
+               goto fail_free_gid_list;
+
+       snprintf(name, sizeof(name), "%s_%ld", QLA2XXX_DRIVER_NAME,
+           ha->host_no);
+       ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev,
+           DMA_POOL_SIZE, 8, 0);
+       if (!ha->s_dma_pool)
+               goto fail_free_init_cb;
 
-       if (status) {
-               printk(KERN_WARNING
-                       "%s(): **** FAILED ****\n", __func__);
+       ha->srb_mempool = mempool_create_slab_pool(SRB_MIN_REQ, srb_cachep);
+       if (!ha->srb_mempool)
+               goto fail_free_s_dma_pool;
+
+       /* Get memory for cached NVRAM */
+       ha->nvram = kzalloc(MAX_NVRAM_SIZE, GFP_KERNEL);
+       if (!ha->nvram)
+               goto fail_free_srb_mempool;
+
+       /* Allocate memory for SNS commands */
+       if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+               /* Get consistent memory allocated for SNS commands */
+               ha->sns_cmd = dma_alloc_coherent(&ha->pdev->dev,
+                   sizeof(struct sns_cmd_pkt), &ha->sns_cmd_dma, GFP_KERNEL);
+               if (!ha->sns_cmd)
+                       goto fail_free_nvram;
+       } else {
+               /* Get consistent memory allocated for MS IOCB */
+               ha->ms_iocb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
+                   &ha->ms_iocb_dma);
+               if (!ha->ms_iocb)
+                       goto fail_free_nvram;
+
+               /* Get consistent memory allocated for CT SNS commands */
+               ha->ct_sns = dma_alloc_coherent(&ha->pdev->dev,
+                   sizeof(struct ct_sns_pkt), &ha->ct_sns_dma, GFP_KERNEL);
+               if (!ha->ct_sns)
+                       goto fail_free_ms_iocb;
        }
 
-       return(status);
+       return 0;
+
+fail_free_ms_iocb:
+       dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
+       ha->ms_iocb = NULL;
+       ha->ms_iocb_dma = 0;
+fail_free_nvram:
+       kfree(ha->nvram);
+       ha->nvram = NULL;
+fail_free_srb_mempool:
+       mempool_destroy(ha->srb_mempool);
+       ha->srb_mempool = NULL;
+fail_free_s_dma_pool:
+       dma_pool_destroy(ha->s_dma_pool);
+       ha->s_dma_pool = NULL;
+fail_free_init_cb:
+       dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, ha->init_cb,
+           ha->init_cb_dma);
+       ha->init_cb = NULL;
+       ha->init_cb_dma = 0;
+fail_free_gid_list:
+       dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list,
+           ha->gid_list_dma);
+       ha->gid_list = NULL;
+       ha->gid_list_dma = 0;
+fail_free_response_ring:
+       dma_free_coherent(&ha->pdev->dev, (ha->response_q_length + 1) *
+           sizeof(response_t), ha->response_ring, ha->response_dma);
+       ha->response_ring = NULL;
+       ha->response_dma = 0;
+fail_free_request_ring:
+       dma_free_coherent(&ha->pdev->dev, (ha->request_q_length + 1) *
+           sizeof(request_t), ha->request_ring, ha->request_dma);
+       ha->request_ring = NULL;
+       ha->request_dma = 0;
+fail:
+       return -ENOMEM;
 }
 
 /*
@@ -2174,20 +2067,18 @@ qla2x00_mem_alloc(scsi_qla_host_t *ha)
 * Input:
 *      ha = adapter block pointer.
 */
-void
+static void
 qla2x00_mem_free(scsi_qla_host_t *ha)
 {
        struct list_head        *fcpl, *fcptemp;
        fc_port_t       *fcport;
 
-       if (ha == NULL) {
-               /* error */
-               DEBUG2(printk("%s(): ERROR invalid ha pointer.\n", __func__));
-               return;
-       }
+       if (ha->srb_mempool)
+               mempool_destroy(ha->srb_mempool);
 
-       /* free sp pool */
-       qla2x00_free_sp_pool(ha);
+       if (ha->fce)
+               dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce,
+                   ha->fce_dma);
 
        if (ha->fw_dump) {
                if (ha->eft)
@@ -2231,6 +2122,7 @@ qla2x00_mem_free(scsi_qla_host_t *ha)
                    (ha->request_q_length + 1) * sizeof(request_t),
                    ha->request_ring, ha->request_dma);
 
+       ha->srb_mempool = NULL;
        ha->eft = NULL;
        ha->eft_dma = 0;
        ha->sns_cmd = NULL;
@@ -2266,44 +2158,100 @@ qla2x00_mem_free(scsi_qla_host_t *ha)
        ha->fw_dump_reading = 0;
 
        vfree(ha->optrom_buffer);
+       kfree(ha->nvram);
+}
+
+static struct qla_work_evt *
+qla2x00_alloc_work(struct scsi_qla_host *ha, enum qla_work_type type,
+    int locked)
+{
+       struct qla_work_evt *e;
+
+       e = kzalloc(sizeof(struct qla_work_evt), locked ? GFP_ATOMIC:
+           GFP_KERNEL);
+       if (!e)
+               return NULL;
+
+       INIT_LIST_HEAD(&e->list);
+       e->type = type;
+       e->flags = QLA_EVT_FLAG_FREE;
+       return e;
 }
 
-/*
- * qla2x00_allocate_sp_pool
- *      This routine is called during initialization to allocate
- *      memory for local srb_t.
- *
- * Input:
- *      ha   = adapter block pointer.
- *
- * Context:
- *      Kernel context.
- */
 static int
-qla2x00_allocate_sp_pool(scsi_qla_host_t *ha)
+qla2x00_post_work(struct scsi_qla_host *ha, struct qla_work_evt *e, int locked)
 {
-       int      rval;
+       unsigned long uninitialized_var(flags);
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
-       rval = QLA_SUCCESS;
-       ha->srb_mempool = mempool_create_slab_pool(SRB_MIN_REQ, srb_cachep);
-       if (ha->srb_mempool == NULL) {
-               qla_printk(KERN_INFO, ha, "Unable to allocate SRB mempool.\n");
-               rval = QLA_FUNCTION_FAILED;
-       }
-       return (rval);
+       if (!locked)
+               spin_lock_irqsave(&pha->hardware_lock, flags);
+       list_add_tail(&e->list, &ha->work_list);
+       qla2xxx_wake_dpc(ha);
+       if (!locked)
+               spin_unlock_irqrestore(&pha->hardware_lock, flags);
+       return QLA_SUCCESS;
+}
+
+int
+qla2x00_post_aen_work(struct scsi_qla_host *ha, enum fc_host_event_code code,
+    u32 data)
+{
+       struct qla_work_evt *e;
+
+       e = qla2x00_alloc_work(ha, QLA_EVT_AEN, 1);
+       if (!e)
+               return QLA_FUNCTION_FAILED;
+
+       e->u.aen.code = code;
+       e->u.aen.data = data;
+       return qla2x00_post_work(ha, e, 1);
+}
+
+int
+qla2x00_post_hwe_work(struct scsi_qla_host *ha, uint16_t code, uint16_t d1,
+    uint16_t d2, uint16_t d3)
+{
+       struct qla_work_evt *e;
+
+       e = qla2x00_alloc_work(ha, QLA_EVT_HWE_LOG, 1);
+       if (!e)
+               return QLA_FUNCTION_FAILED;
+
+       e->u.hwe.code = code;
+       e->u.hwe.d1 = d1;
+       e->u.hwe.d2 = d2;
+       e->u.hwe.d3 = d3;
+       return qla2x00_post_work(ha, e, 1);
 }
 
-/*
- *  This routine frees all adapter allocated memory.
- *
- */
 static void
-qla2x00_free_sp_pool( scsi_qla_host_t *ha)
+qla2x00_do_work(struct scsi_qla_host *ha)
 {
-       if (ha->srb_mempool) {
-               mempool_destroy(ha->srb_mempool);
-               ha->srb_mempool = NULL;
+       struct qla_work_evt *e;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
+
+       spin_lock_irq(&pha->hardware_lock);
+       while (!list_empty(&ha->work_list)) {
+               e = list_entry(ha->work_list.next, struct qla_work_evt, list);
+               list_del_init(&e->list);
+               spin_unlock_irq(&pha->hardware_lock);
+
+               switch (e->type) {
+               case QLA_EVT_AEN:
+                       fc_host_post_event(ha->host, fc_get_event_number(),
+                           e->u.aen.code, e->u.aen.data);
+                       break;
+               case QLA_EVT_HWE_LOG:
+                       qla2xxx_hw_event_log(ha, e->u.hwe.code, e->u.hwe.d1,
+                           e->u.hwe.d2, e->u.hwe.d3);
+                       break;
+               }
+               if (e->flags & QLA_EVT_FLAG_FREE)
+                       kfree(e);
+               spin_lock_irq(&pha->hardware_lock);
        }
+       spin_unlock_irq(&pha->hardware_lock);
 }
 
 /**************************************************************************
@@ -2327,6 +2275,9 @@ qla2x00_do_dpc(void *data)
        fc_port_t       *fcport;
        uint8_t         status;
        uint16_t        next_loopid;
+       struct scsi_qla_host *vha;
+       int             i;
+
 
        ha = (scsi_qla_host_t *)data;
 
@@ -2354,6 +2305,8 @@ qla2x00_do_dpc(void *data)
                        continue;
                }
 
+               qla2x00_do_work(ha);
+
                if (test_and_clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) {
 
                        DEBUG(printk("scsi(%ld): dpc: sched "
@@ -2369,17 +2322,25 @@ qla2x00_do_dpc(void *data)
                                }
                                clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
                        }
+
+                       for_each_mapped_vp_idx(ha, i) {
+                               list_for_each_entry(vha, &ha->vp_list,
+                                   vp_list) {
+                                       if (i == vha->vp_idx) {
+                                               set_bit(ISP_ABORT_NEEDED,
+                                                   &vha->dpc_flags);
+                                               break;
+                                       }
+                               }
+                       }
+
                        DEBUG(printk("scsi(%ld): dpc: qla2x00_abort_isp end\n",
                            ha->host_no));
                }
 
-               if (test_and_clear_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags))
+               if (test_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags)) {
                        qla2x00_update_fcports(ha);
-
-               if (test_and_clear_bit(LOOP_RESET_NEEDED, &ha->dpc_flags)) {
-                       DEBUG(printk("scsi(%ld): dpc: sched loop_reset()\n",
-                           ha->host_no));
-                       qla2x00_loop_reset(ha);
+                       clear_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags);
                }
 
                if (test_and_clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) &&
@@ -2409,7 +2370,6 @@ qla2x00_do_dpc(void *data)
                                if (atomic_read(&fcport->state) != FCS_ONLINE &&
                                    fcport->login_retry) {
 
-                                       fcport->login_retry--;
                                        if (fcport->flags & FCF_FABRIC_DEVICE) {
                                                if (fcport->flags &
                                                    FCF_TAPE_PRESENT)
@@ -2425,6 +2385,7 @@ qla2x00_do_dpc(void *data)
                                                    qla2x00_local_device_login(
                                                        ha, fcport);
 
+                                       fcport->login_retry--;
                                        if (status == QLA_SUCCESS) {
                                                fcport->old_loop_id = fcport->loop_id;
 
@@ -2442,6 +2403,8 @@ qla2x00_do_dpc(void *data)
                                        } else {
                                                fcport->login_retry = 0;
                                        }
+                                       if (fcport->login_retry == 0 && status != QLA_SUCCESS)
+                                               fcport->loop_id = FC_NO_LOOP_ID;
                                }
                                if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
                                        break;
@@ -2450,19 +2413,6 @@ qla2x00_do_dpc(void *data)
                            ha->host_no));
                }
 
-               if ((test_bit(LOGIN_RETRY_NEEDED, &ha->dpc_flags)) &&
-                   atomic_read(&ha->loop_state) != LOOP_DOWN) {
-
-                       clear_bit(LOGIN_RETRY_NEEDED, &ha->dpc_flags);
-                       DEBUG(printk("scsi(%ld): qla2x00_login_retry()\n",
-                           ha->host_no));
-
-                       set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
-
-                       DEBUG(printk("scsi(%ld): qla2x00_login_retry - end\n",
-                           ha->host_no));
-               }
-
                if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
 
                        DEBUG(printk("scsi(%ld): qla2x00_loop_resync()\n",
@@ -2480,16 +2430,10 @@ qla2x00_do_dpc(void *data)
                            ha->host_no));
                }
 
-               if (test_and_clear_bit(FCPORT_RESCAN_NEEDED, &ha->dpc_flags)) {
-
-                       DEBUG(printk("scsi(%ld): Rescan flagged fcports...\n",
-                           ha->host_no));
-
-                       qla2x00_rescan_fcports(ha);
-
-                       DEBUG(printk("scsi(%ld): Rescan flagged fcports..."
-                           "end.\n",
-                           ha->host_no));
+               if (test_bit(NPIV_CONFIG_NEEDED, &ha->dpc_flags) &&
+                   atomic_read(&ha->loop_state) == LOOP_READY) {
+                       clear_bit(NPIV_CONFIG_NEEDED, &ha->dpc_flags);
+                       qla2xxx_flash_npiv_conf(ha);
                }
 
                if (!ha->interrupts_on)
@@ -2516,8 +2460,10 @@ qla2x00_do_dpc(void *data)
 void
 qla2xxx_wake_dpc(scsi_qla_host_t *ha)
 {
-       if (ha->dpc_thread)
-               wake_up_process(ha->dpc_thread);
+       struct task_struct *t = ha->dpc_thread;
+
+       if (!test_bit(UNLOADING, &ha->dpc_flags) && t)
+               wake_up_process(t);
 }
 
 /*
@@ -2669,7 +2615,8 @@ qla2x00_timer(scsi_qla_host_t *ha)
                        set_bit(RESTART_QUEUES_NEEDED, &ha->dpc_flags);
                        start_dpc++;
 
-                       if (!(ha->device_flags & DFLG_NO_CABLE)) {
+                       if (!(ha->device_flags & DFLG_NO_CABLE) &&
+                           !ha->parent) {
                                DEBUG(printk("scsi(%ld): Loop down - "
                                    "aborting ISP.\n",
                                    ha->host_no));
@@ -2690,13 +2637,15 @@ qla2x00_timer(scsi_qla_host_t *ha)
                start_dpc++;
        }
 
+       /* Process any deferred work. */
+       if (!list_empty(&ha->work_list))
+               start_dpc++;
+
        /* Schedule the DPC routine if needed */
        if ((test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) ||
            test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) ||
-           test_bit(LOOP_RESET_NEEDED, &ha->dpc_flags) ||
            test_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags) ||
            start_dpc ||
-           test_bit(LOGIN_RETRY_NEEDED, &ha->dpc_flags) ||
            test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) ||
            test_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags) ||
            test_bit(VP_DPC_NEEDED, &ha->dpc_flags) ||
@@ -2706,23 +2655,6 @@ qla2x00_timer(scsi_qla_host_t *ha)
        qla2x00_restart_timer(ha, WATCH_INTERVAL);
 }
 
-/* XXX(hch): crude hack to emulate a down_timeout() */
-int
-qla2x00_down_timeout(struct semaphore *sema, unsigned long timeout)
-{
-       const unsigned int step = 100; /* msecs */
-       unsigned int iterations = jiffies_to_msecs(timeout)/100;
-
-       do {
-               if (!down_trylock(sema))
-                       return 0;
-               if (msleep_interruptible(step))
-                       break;
-       } while (--iterations > 0);
-
-       return -ETIMEDOUT;
-}
-
 /* Firmware interface routines. */
 
 #define FW_BLOBS       6
@@ -2740,7 +2672,7 @@ qla2x00_down_timeout(struct semaphore *sema, unsigned long timeout)
 #define FW_FILE_ISP24XX        "ql2400_fw.bin"
 #define FW_FILE_ISP25XX        "ql2500_fw.bin"
 
-static DECLARE_MUTEX(qla_fw_lock);
+static DEFINE_MUTEX(qla_fw_lock);
 
 static struct fw_blob qla_fw_blobs[FW_BLOBS] = {
        { .name = FW_FILE_ISP21XX, .segs = { 0x1000, 0 }, },
@@ -2765,13 +2697,13 @@ qla2x00_request_firmware(scsi_qla_host_t *ha)
                blob = &qla_fw_blobs[FW_ISP2300];
        } else if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
                blob = &qla_fw_blobs[FW_ISP2322];
-       } else if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) {
+       } else if (IS_QLA24XX_TYPE(ha)) {
                blob = &qla_fw_blobs[FW_ISP24XX];
        } else if (IS_QLA25XX(ha)) {
                blob = &qla_fw_blobs[FW_ISP25XX];
        }
 
-       down(&qla_fw_lock);
+       mutex_lock(&qla_fw_lock);
        if (blob->fw)
                goto out;
 
@@ -2784,7 +2716,7 @@ qla2x00_request_firmware(scsi_qla_host_t *ha)
        }
 
 out:
-       up(&qla_fw_lock);
+       mutex_unlock(&qla_fw_lock);
        return blob;
 }
 
@@ -2793,13 +2725,118 @@ qla2x00_release_firmware(void)
 {
        int idx;
 
-       down(&qla_fw_lock);
+       mutex_lock(&qla_fw_lock);
        for (idx = 0; idx < FW_BLOBS; idx++)
                if (qla_fw_blobs[idx].fw)
                        release_firmware(qla_fw_blobs[idx].fw);
-       up(&qla_fw_lock);
+       mutex_unlock(&qla_fw_lock);
+}
+
+static pci_ers_result_t
+qla2xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
+{
+       switch (state) {
+       case pci_channel_io_normal:
+               return PCI_ERS_RESULT_CAN_RECOVER;
+       case pci_channel_io_frozen:
+               pci_disable_device(pdev);
+               return PCI_ERS_RESULT_NEED_RESET;
+       case pci_channel_io_perm_failure:
+               qla2x00_remove_one(pdev);
+               return PCI_ERS_RESULT_DISCONNECT;
+       }
+       return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t
+qla2xxx_pci_mmio_enabled(struct pci_dev *pdev)
+{
+       int risc_paused = 0;
+       uint32_t stat;
+       unsigned long flags;
+       scsi_qla_host_t *ha = pci_get_drvdata(pdev);
+       struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+       struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       if (IS_QLA2100(ha) || IS_QLA2200(ha)){
+               stat = RD_REG_DWORD(&reg->hccr);
+               if (stat & HCCR_RISC_PAUSE)
+                       risc_paused = 1;
+       } else if (IS_QLA23XX(ha)) {
+               stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
+               if (stat & HSR_RISC_PAUSED)
+                       risc_paused = 1;
+       } else if (IS_FWI2_CAPABLE(ha)) {
+               stat = RD_REG_DWORD(&reg24->host_status);
+               if (stat & HSRX_RISC_PAUSED)
+                       risc_paused = 1;
+       }
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       if (risc_paused) {
+               qla_printk(KERN_INFO, ha, "RISC paused -- mmio_enabled, "
+                   "Dumping firmware!\n");
+               ha->isp_ops->fw_dump(ha, 0);
+
+               return PCI_ERS_RESULT_NEED_RESET;
+       } else
+               return PCI_ERS_RESULT_RECOVERED;
 }
 
+static pci_ers_result_t
+qla2xxx_pci_slot_reset(struct pci_dev *pdev)
+{
+       pci_ers_result_t ret = PCI_ERS_RESULT_DISCONNECT;
+       scsi_qla_host_t *ha = pci_get_drvdata(pdev);
+       int rc;
+
+       if (ha->mem_only)
+               rc = pci_enable_device_mem(pdev);
+       else
+               rc = pci_enable_device(pdev);
+
+       if (rc) {
+               qla_printk(KERN_WARNING, ha,
+                   "Can't re-enable PCI device after reset.\n");
+
+               return ret;
+       }
+       pci_set_master(pdev);
+
+       if (ha->isp_ops->pci_config(ha))
+               return ret;
+
+       set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+       if (qla2x00_abort_isp(ha)== QLA_SUCCESS)
+               ret =  PCI_ERS_RESULT_RECOVERED;
+       clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+
+       return ret;
+}
+
+static void
+qla2xxx_pci_resume(struct pci_dev *pdev)
+{
+       scsi_qla_host_t *ha = pci_get_drvdata(pdev);
+       int ret;
+
+       ret = qla2x00_wait_for_hba_online(ha);
+       if (ret != QLA_SUCCESS) {
+               qla_printk(KERN_ERR, ha,
+                   "the device failed to resume I/O "
+                   "from slot/link_reset");
+       }
+       pci_cleanup_aer_uncorrect_error_status(pdev);
+}
+
+static struct pci_error_handlers qla2xxx_err_handler = {
+       .error_detected = qla2xxx_pci_error_detected,
+       .mmio_enabled = qla2xxx_pci_mmio_enabled,
+       .slot_reset = qla2xxx_pci_slot_reset,
+       .resume = qla2xxx_pci_resume,
+};
+
 static struct pci_device_id qla2xxx_pci_tbl[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2100) },
        { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2200) },
@@ -2810,6 +2847,7 @@ static struct pci_device_id qla2xxx_pci_tbl[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP6322) },
        { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2422) },
        { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2432) },
+       { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8432) },
        { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5422) },
        { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5432) },
        { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2532) },
@@ -2824,7 +2862,8 @@ static struct pci_driver qla2xxx_pci_driver = {
        },
        .id_table       = qla2xxx_pci_tbl,
        .probe          = qla2x00_probe_one,
-       .remove         = __devexit_p(qla2x00_remove_one),
+       .remove         = qla2x00_remove_one,
+       .err_handler    = &qla2xxx_err_handler,
 };
 
 /**
@@ -2837,7 +2876,7 @@ qla2x00_module_init(void)
 
        /* Allocate cache for SRBs. */
        srb_cachep = kmem_cache_create("qla2xxx_srbs", sizeof(srb_t), 0,
-           SLAB_HWCACHE_ALIGN, NULL, NULL);
+           SLAB_HWCACHE_ALIGN, NULL);
        if (srb_cachep == NULL) {
                printk(KERN_ERR
                    "qla2xxx: Unable to allocate SRB cache...Failing load!\n");
@@ -2863,7 +2902,8 @@ qla2x00_module_init(void)
                return -ENODEV;
        }
 
-       printk(KERN_INFO "QLogic Fibre Channel HBA Driver\n");
+       printk(KERN_INFO "QLogic Fibre Channel HBA Driver: %s\n",
+           qla2x00_version_str);
        ret = pci_register_driver(&qla2xxx_pci_driver);
        if (ret) {
                kmem_cache_destroy(srb_cachep);
@@ -2898,3 +2938,4 @@ MODULE_FIRMWARE(FW_FILE_ISP22XX);
 MODULE_FIRMWARE(FW_FILE_ISP2300);
 MODULE_FIRMWARE(FW_FILE_ISP2322);
 MODULE_FIRMWARE(FW_FILE_ISP24XX);
+MODULE_FIRMWARE(FW_FILE_ISP25XX);