2005-04-16 22:20:36 +00:00
|
|
|
/*
|
2005-10-27 18:10:08 +00:00
|
|
|
* QLogic Fibre Channel HBA Driver
|
2008-04-03 20:13:13 +00:00
|
|
|
* Copyright (c) 2003-2008 QLogic Corporation
|
2005-04-16 22:20:36 +00:00
|
|
|
*
|
2005-10-27 18:10:08 +00:00
|
|
|
* See LICENSE.qla2xxx for copyright and licensing details.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
#include "qla_def.h"
|
|
|
|
|
|
|
|
#include <linux/moduleparam.h>
|
|
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include <linux/delay.h>
|
2006-02-14 17:46:22 +00:00
|
|
|
#include <linux/kthread.h>
|
2008-05-13 05:21:10 +00:00
|
|
|
#include <linux/mutex.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
#include <scsi/scsi_tcq.h>
|
|
|
|
#include <scsi/scsicam.h>
|
|
|
|
#include <scsi/scsi_transport.h>
|
|
|
|
#include <scsi/scsi_transport_fc.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Driver version
|
|
|
|
*/
|
|
|
|
char qla2x00_version_str[40];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SRB allocation cache
|
|
|
|
*/
|
2006-12-07 04:33:20 +00:00
|
|
|
static struct kmem_cache *srb_cachep;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
int ql2xlogintimeout = 20;
|
|
|
|
module_param(ql2xlogintimeout, int, S_IRUGO|S_IRUSR);
|
|
|
|
MODULE_PARM_DESC(ql2xlogintimeout,
|
|
|
|
"Login timeout value in seconds.");
|
|
|
|
|
2007-05-07 14:42:59 +00:00
|
|
|
int qlport_down_retry;
|
2005-04-16 22:20:36 +00:00
|
|
|
module_param(qlport_down_retry, int, S_IRUGO|S_IRUSR);
|
|
|
|
MODULE_PARM_DESC(qlport_down_retry,
|
2006-06-30 09:33:07 +00:00
|
|
|
"Maximum number of command retries to a port that returns "
|
2005-04-16 22:20:36 +00:00
|
|
|
"a PORT-DOWN status.");
|
|
|
|
|
|
|
|
int ql2xplogiabsentdevice;
|
|
|
|
module_param(ql2xplogiabsentdevice, int, S_IRUGO|S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(ql2xplogiabsentdevice,
|
|
|
|
"Option to enable PLOGI to devices that are not present after "
|
2006-06-30 09:33:07 +00:00
|
|
|
"a Fabric scan. This is needed for several broken switches. "
|
2005-04-16 22:20:36 +00:00
|
|
|
"Default is 0 - no PLOGI. 1 - perfom PLOGI.");
|
|
|
|
|
|
|
|
int ql2xloginretrycount = 0;
|
|
|
|
module_param(ql2xloginretrycount, int, S_IRUGO|S_IRUSR);
|
|
|
|
MODULE_PARM_DESC(ql2xloginretrycount,
|
|
|
|
"Specify an alternate value for the NVRAM login retry count.");
|
|
|
|
|
2006-06-23 23:10:29 +00:00
|
|
|
int ql2xallocfwdump = 1;
|
|
|
|
module_param(ql2xallocfwdump, int, S_IRUGO|S_IRUSR);
|
|
|
|
MODULE_PARM_DESC(ql2xallocfwdump,
|
|
|
|
"Option to enable allocation of memory for a firmware dump "
|
|
|
|
"during HBA initialization. Memory allocation requirements "
|
|
|
|
"vary by ISP type. Default is 1 - allocate memory.");
|
|
|
|
|
2006-10-06 16:54:59 +00:00
|
|
|
int ql2xextended_error_logging;
|
2007-03-12 17:41:30 +00:00
|
|
|
module_param(ql2xextended_error_logging, int, S_IRUGO|S_IWUSR);
|
2006-10-06 16:54:59 +00:00
|
|
|
MODULE_PARM_DESC(ql2xextended_error_logging,
|
2006-06-23 23:11:10 +00:00
|
|
|
"Option to enable extended error logging, "
|
|
|
|
"Default is 0 - no logging. 1 - log errors.");
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static void qla2x00_free_device(scsi_qla_host_t *);
|
|
|
|
|
|
|
|
static void qla2x00_config_dma_addressing(scsi_qla_host_t *ha);
|
|
|
|
|
2008-04-24 22:21:26 +00:00
|
|
|
int ql2xfdmienable=1;
|
2005-08-27 02:08:30 +00:00
|
|
|
module_param(ql2xfdmienable, int, S_IRUGO|S_IRUSR);
|
|
|
|
MODULE_PARM_DESC(ql2xfdmienable,
|
|
|
|
"Enables FDMI registratons "
|
|
|
|
"Default is 0 - no FDMI. 1 - perfom FDMI.");
|
|
|
|
|
2006-10-13 16:33:39 +00:00
|
|
|
#define MAX_Q_DEPTH 32
|
|
|
|
static int ql2xmaxqdepth = MAX_Q_DEPTH;
|
|
|
|
module_param(ql2xmaxqdepth, int, S_IRUGO|S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(ql2xmaxqdepth,
|
|
|
|
"Maximum queue depth to report for target devices.");
|
|
|
|
|
|
|
|
int ql2xqfullrampup = 120;
|
|
|
|
module_param(ql2xqfullrampup, int, S_IRUGO|S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(ql2xqfullrampup,
|
|
|
|
"Number of seconds to wait to begin to ramp-up the queue "
|
|
|
|
"depth for a device after a queue-full condition has been "
|
|
|
|
"detected. Default is 120 seconds.");
|
|
|
|
|
2008-07-10 23:55:52 +00:00
|
|
|
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.");
|
|
|
|
|
2008-12-10 00:45:39 +00:00
|
|
|
int ql2xmaxqueues = 1;
|
|
|
|
module_param(ql2xmaxqueues, int, S_IRUGO|S_IRUSR);
|
|
|
|
MODULE_PARM_DESC(ql2xmaxqueues,
|
|
|
|
"Enables MQ settings "
|
|
|
|
"Default is 1 for single queue. Set it to number \
|
|
|
|
of queues in MQ mode.");
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
2005-07-06 17:32:07 +00:00
|
|
|
* SCSI host template entry points
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
static int qla2xxx_slave_configure(struct scsi_device * device);
|
2005-04-17 20:02:26 +00:00
|
|
|
static int qla2xxx_slave_alloc(struct scsi_device *);
|
2006-11-22 16:24:48 +00:00
|
|
|
static int qla2xxx_scan_finished(struct Scsi_Host *, unsigned long time);
|
|
|
|
static void qla2xxx_scan_start(struct Scsi_Host *);
|
2005-04-17 20:02:26 +00:00
|
|
|
static void qla2xxx_slave_destroy(struct scsi_device *);
|
2005-04-16 22:20:36 +00:00
|
|
|
static int qla2x00_queuecommand(struct scsi_cmnd *cmd,
|
|
|
|
void (*fn)(struct scsi_cmnd *));
|
2005-07-06 17:31:47 +00:00
|
|
|
static int qla24xx_queuecommand(struct scsi_cmnd *cmd,
|
|
|
|
void (*fn)(struct scsi_cmnd *));
|
2005-04-16 22:20:36 +00:00
|
|
|
static int qla2xxx_eh_abort(struct scsi_cmnd *);
|
|
|
|
static int qla2xxx_eh_device_reset(struct scsi_cmnd *);
|
2008-04-03 20:13:24 +00:00
|
|
|
static int qla2xxx_eh_target_reset(struct scsi_cmnd *);
|
2005-04-16 22:20:36 +00:00
|
|
|
static int qla2xxx_eh_bus_reset(struct scsi_cmnd *);
|
|
|
|
static int qla2xxx_eh_host_reset(struct scsi_cmnd *);
|
|
|
|
|
2005-08-27 02:09:30 +00:00
|
|
|
static int qla2x00_change_queue_depth(struct scsi_device *, int);
|
|
|
|
static int qla2x00_change_queue_type(struct scsi_device *, int);
|
|
|
|
|
2008-01-17 17:02:15 +00:00
|
|
|
static struct scsi_host_template qla2x00_driver_template = {
|
2005-04-16 22:20:36 +00:00
|
|
|
.module = THIS_MODULE,
|
2006-05-17 22:09:45 +00:00
|
|
|
.name = QLA2XXX_DRIVER_NAME,
|
2005-04-16 22:20:36 +00:00
|
|
|
.queuecommand = qla2x00_queuecommand,
|
|
|
|
|
|
|
|
.eh_abort_handler = qla2xxx_eh_abort,
|
|
|
|
.eh_device_reset_handler = qla2xxx_eh_device_reset,
|
2008-04-03 20:13:24 +00:00
|
|
|
.eh_target_reset_handler = qla2xxx_eh_target_reset,
|
2005-04-16 22:20:36 +00:00
|
|
|
.eh_bus_reset_handler = qla2xxx_eh_bus_reset,
|
|
|
|
.eh_host_reset_handler = qla2xxx_eh_host_reset,
|
|
|
|
|
|
|
|
.slave_configure = qla2xxx_slave_configure,
|
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
.slave_alloc = qla2xxx_slave_alloc,
|
|
|
|
.slave_destroy = qla2xxx_slave_destroy,
|
2006-11-22 16:24:48 +00:00
|
|
|
.scan_finished = qla2xxx_scan_finished,
|
|
|
|
.scan_start = qla2xxx_scan_start,
|
2005-08-27 02:09:30 +00:00
|
|
|
.change_queue_depth = qla2x00_change_queue_depth,
|
|
|
|
.change_queue_type = qla2x00_change_queue_type,
|
2005-04-16 22:20:36 +00:00
|
|
|
.this_id = -1,
|
|
|
|
.cmd_per_lun = 3,
|
|
|
|
.use_clustering = ENABLE_CLUSTERING,
|
|
|
|
.sg_tablesize = SG_ALL,
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The RISC allows for each command to transfer (2^32-1) bytes of data,
|
|
|
|
* which equates to 0x800000 sectors.
|
|
|
|
*/
|
|
|
|
.max_sectors = 0xFFFF,
|
2005-08-27 02:09:40 +00:00
|
|
|
.shost_attrs = qla2x00_host_attrs,
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
|
|
|
|
2007-07-05 20:16:51 +00:00
|
|
|
struct scsi_host_template qla24xx_driver_template = {
|
2005-07-06 17:31:47 +00:00
|
|
|
.module = THIS_MODULE,
|
2006-05-17 22:09:45 +00:00
|
|
|
.name = QLA2XXX_DRIVER_NAME,
|
2005-07-06 17:31:47 +00:00
|
|
|
.queuecommand = qla24xx_queuecommand,
|
|
|
|
|
|
|
|
.eh_abort_handler = qla2xxx_eh_abort,
|
|
|
|
.eh_device_reset_handler = qla2xxx_eh_device_reset,
|
2008-04-03 20:13:24 +00:00
|
|
|
.eh_target_reset_handler = qla2xxx_eh_target_reset,
|
2005-07-06 17:31:47 +00:00
|
|
|
.eh_bus_reset_handler = qla2xxx_eh_bus_reset,
|
|
|
|
.eh_host_reset_handler = qla2xxx_eh_host_reset,
|
|
|
|
|
|
|
|
.slave_configure = qla2xxx_slave_configure,
|
|
|
|
|
|
|
|
.slave_alloc = qla2xxx_slave_alloc,
|
|
|
|
.slave_destroy = qla2xxx_slave_destroy,
|
2007-03-12 17:41:27 +00:00
|
|
|
.scan_finished = qla2xxx_scan_finished,
|
|
|
|
.scan_start = qla2xxx_scan_start,
|
2005-08-27 02:09:30 +00:00
|
|
|
.change_queue_depth = qla2x00_change_queue_depth,
|
|
|
|
.change_queue_type = qla2x00_change_queue_type,
|
2005-07-06 17:31:47 +00:00
|
|
|
.this_id = -1,
|
|
|
|
.cmd_per_lun = 3,
|
|
|
|
.use_clustering = ENABLE_CLUSTERING,
|
|
|
|
.sg_tablesize = SG_ALL,
|
|
|
|
|
|
|
|
.max_sectors = 0xFFFF,
|
2005-08-27 02:09:40 +00:00
|
|
|
.shost_attrs = qla2x00_host_attrs,
|
2005-07-06 17:31:47 +00:00
|
|
|
};
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static struct scsi_transport_template *qla2xxx_transport_template = NULL;
|
2007-07-05 20:16:51 +00:00
|
|
|
struct scsi_transport_template *qla2xxx_transport_vport_template = NULL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* TODO Convert to inlines
|
|
|
|
*
|
|
|
|
* Timer routines
|
|
|
|
*/
|
|
|
|
|
2007-07-05 20:16:51 +00:00
|
|
|
__inline__ void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_start_timer(scsi_qla_host_t *vha, void *func, unsigned long interval)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
init_timer(&vha->timer);
|
|
|
|
vha->timer.expires = jiffies + interval * HZ;
|
|
|
|
vha->timer.data = (unsigned long)vha;
|
|
|
|
vha->timer.function = (void (*)(unsigned long))func;
|
|
|
|
add_timer(&vha->timer);
|
|
|
|
vha->timer_active = 1;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_restart_timer(scsi_qla_host_t *vha, unsigned long interval)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
mod_timer(&vha->timer, jiffies + interval * HZ);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-01-17 17:02:15 +00:00
|
|
|
static __inline__ void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_stop_timer(scsi_qla_host_t *vha)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
del_timer_sync(&vha->timer);
|
|
|
|
vha->timer_active = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int qla2x00_do_dpc(void *data);
|
|
|
|
|
|
|
|
static void qla2x00_rst_aen(scsi_qla_host_t *);
|
|
|
|
|
2008-12-10 00:45:39 +00:00
|
|
|
static int qla2x00_mem_alloc(struct qla_hw_data *, uint16_t, uint16_t,
|
|
|
|
struct req_que **, struct rsp_que **);
|
2008-11-06 18:40:51 +00:00
|
|
|
static void qla2x00_mem_free(struct qla_hw_data *);
|
|
|
|
static void qla2x00_sp_free_dma(srb_t *);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
2008-12-10 00:45:39 +00:00
|
|
|
static int qla2x00_alloc_queues(struct qla_hw_data *ha)
|
|
|
|
{
|
|
|
|
ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_queues,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!ha->req_q_map) {
|
|
|
|
qla_printk(KERN_WARNING, ha,
|
|
|
|
"Unable to allocate memory for request queue ptrs\n");
|
|
|
|
goto fail_req_map;
|
|
|
|
}
|
|
|
|
|
|
|
|
ha->rsp_q_map = kzalloc(sizeof(struct rsp_que *) * ha->max_queues,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!ha->rsp_q_map) {
|
|
|
|
qla_printk(KERN_WARNING, ha,
|
|
|
|
"Unable to allocate memory for response queue ptrs\n");
|
|
|
|
goto fail_rsp_map;
|
|
|
|
}
|
|
|
|
set_bit(0, ha->rsp_qid_map);
|
|
|
|
set_bit(0, ha->req_qid_map);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
fail_rsp_map:
|
|
|
|
kfree(ha->req_q_map);
|
|
|
|
ha->req_q_map = NULL;
|
|
|
|
fail_req_map:
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qla2x00_free_que(struct qla_hw_data *ha, struct req_que *req,
|
|
|
|
struct rsp_que *rsp)
|
|
|
|
{
|
|
|
|
if (rsp && rsp->ring)
|
|
|
|
dma_free_coherent(&ha->pdev->dev,
|
|
|
|
(rsp->length + 1) * sizeof(response_t),
|
|
|
|
rsp->ring, rsp->dma);
|
|
|
|
|
|
|
|
kfree(rsp);
|
|
|
|
rsp = NULL;
|
|
|
|
if (req && req->ring)
|
|
|
|
dma_free_coherent(&ha->pdev->dev,
|
|
|
|
(req->length + 1) * sizeof(request_t),
|
|
|
|
req->ring, req->dma);
|
|
|
|
|
|
|
|
kfree(req);
|
|
|
|
req = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qla2x00_free_queues(struct qla_hw_data *ha)
|
|
|
|
{
|
|
|
|
struct req_que *req;
|
|
|
|
struct rsp_que *rsp;
|
|
|
|
int cnt;
|
|
|
|
|
|
|
|
for (cnt = 0; cnt < ha->max_queues; cnt++) {
|
|
|
|
rsp = ha->rsp_q_map[cnt];
|
|
|
|
req = ha->req_q_map[cnt];
|
|
|
|
qla2x00_free_que(ha, req, rsp);
|
|
|
|
}
|
|
|
|
kfree(ha->rsp_q_map);
|
|
|
|
ha->rsp_q_map = NULL;
|
|
|
|
|
|
|
|
kfree(ha->req_q_map);
|
|
|
|
ha->req_q_map = NULL;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static char *
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_pci_info_str(struct scsi_qla_host *vha, char *str)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-04-16 22:20:36 +00:00
|
|
|
static char *pci_bus_modes[] = {
|
|
|
|
"33", "66", "100", "133",
|
|
|
|
};
|
|
|
|
uint16_t pci_bus;
|
|
|
|
|
|
|
|
strcpy(str, "PCI");
|
|
|
|
pci_bus = (ha->pci_attr & (BIT_9 | BIT_10)) >> 9;
|
|
|
|
if (pci_bus) {
|
|
|
|
strcat(str, "-X (");
|
|
|
|
strcat(str, pci_bus_modes[pci_bus]);
|
|
|
|
} else {
|
|
|
|
pci_bus = (ha->pci_attr & BIT_8) >> 8;
|
|
|
|
strcat(str, " (");
|
|
|
|
strcat(str, pci_bus_modes[pci_bus]);
|
|
|
|
}
|
|
|
|
strcat(str, " MHz)");
|
|
|
|
|
|
|
|
return (str);
|
|
|
|
}
|
|
|
|
|
2005-07-06 17:31:47 +00:00
|
|
|
static char *
|
2008-11-06 18:40:51 +00:00
|
|
|
qla24xx_pci_info_str(struct scsi_qla_host *vha, char *str)
|
2005-07-06 17:31:47 +00:00
|
|
|
{
|
|
|
|
static char *pci_bus_modes[] = { "33", "66", "100", "133", };
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-07-06 17:31:47 +00:00
|
|
|
uint32_t pci_bus;
|
|
|
|
int pcie_reg;
|
|
|
|
|
|
|
|
pcie_reg = pci_find_capability(ha->pdev, PCI_CAP_ID_EXP);
|
|
|
|
if (pcie_reg) {
|
|
|
|
char lwstr[6];
|
|
|
|
uint16_t pcie_lstat, lspeed, lwidth;
|
|
|
|
|
|
|
|
pcie_reg += 0x12;
|
|
|
|
pci_read_config_word(ha->pdev, pcie_reg, &pcie_lstat);
|
|
|
|
lspeed = pcie_lstat & (BIT_0 | BIT_1 | BIT_2 | BIT_3);
|
|
|
|
lwidth = (pcie_lstat &
|
|
|
|
(BIT_4 | BIT_5 | BIT_6 | BIT_7 | BIT_8 | BIT_9)) >> 4;
|
|
|
|
|
|
|
|
strcpy(str, "PCIe (");
|
|
|
|
if (lspeed == 1)
|
2008-04-03 20:13:21 +00:00
|
|
|
strcat(str, "2.5GT/s ");
|
2007-07-20 03:37:34 +00:00
|
|
|
else if (lspeed == 2)
|
2008-04-03 20:13:21 +00:00
|
|
|
strcat(str, "5.0GT/s ");
|
2005-07-06 17:31:47 +00:00
|
|
|
else
|
|
|
|
strcat(str, "<unknown> ");
|
|
|
|
snprintf(lwstr, sizeof(lwstr), "x%d)", lwidth);
|
|
|
|
strcat(str, lwstr);
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(str, "PCI");
|
|
|
|
pci_bus = (ha->pci_attr & CSRX_PCIX_BUS_MODE_MASK) >> 8;
|
|
|
|
if (pci_bus == 0 || pci_bus == 8) {
|
|
|
|
strcat(str, " (");
|
|
|
|
strcat(str, pci_bus_modes[pci_bus >> 3]);
|
|
|
|
} else {
|
|
|
|
strcat(str, "-X ");
|
|
|
|
if (pci_bus & BIT_2)
|
|
|
|
strcat(str, "Mode 2");
|
|
|
|
else
|
|
|
|
strcat(str, "Mode 1");
|
|
|
|
strcat(str, " (");
|
|
|
|
strcat(str, pci_bus_modes[pci_bus & ~BIT_2]);
|
|
|
|
}
|
|
|
|
strcat(str, " MHz)");
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2006-11-09 03:55:50 +00:00
|
|
|
static char *
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_fw_version_str(struct scsi_qla_host *vha, char *str)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
char un_str[10];
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-07-06 17:32:07 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
sprintf(str, "%d.%02d.%02d ", ha->fw_major_version,
|
|
|
|
ha->fw_minor_version,
|
|
|
|
ha->fw_subminor_version);
|
|
|
|
|
|
|
|
if (ha->fw_attributes & BIT_9) {
|
|
|
|
strcat(str, "FLX");
|
|
|
|
return (str);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (ha->fw_attributes & 0xFF) {
|
|
|
|
case 0x7:
|
|
|
|
strcat(str, "EF");
|
|
|
|
break;
|
|
|
|
case 0x17:
|
|
|
|
strcat(str, "TP");
|
|
|
|
break;
|
|
|
|
case 0x37:
|
|
|
|
strcat(str, "IP");
|
|
|
|
break;
|
|
|
|
case 0x77:
|
|
|
|
strcat(str, "VI");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
sprintf(un_str, "(%x)", ha->fw_attributes);
|
|
|
|
strcat(str, un_str);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ha->fw_attributes & 0x100)
|
|
|
|
strcat(str, "X");
|
|
|
|
|
|
|
|
return (str);
|
|
|
|
}
|
|
|
|
|
2006-11-09 03:55:50 +00:00
|
|
|
static char *
|
2008-11-06 18:40:51 +00:00
|
|
|
qla24xx_fw_version_str(struct scsi_qla_host *vha, char *str)
|
2005-07-06 17:31:47 +00:00
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-07-09 00:58:43 +00:00
|
|
|
sprintf(str, "%d.%02d.%02d ", ha->fw_major_version,
|
|
|
|
ha->fw_minor_version,
|
|
|
|
ha->fw_subminor_version);
|
|
|
|
|
2005-07-06 17:31:47 +00:00
|
|
|
if (ha->fw_attributes & BIT_0)
|
|
|
|
strcat(str, "[Class 2] ");
|
|
|
|
if (ha->fw_attributes & BIT_1)
|
|
|
|
strcat(str, "[IP] ");
|
|
|
|
if (ha->fw_attributes & BIT_2)
|
|
|
|
strcat(str, "[Multi-ID] ");
|
2007-07-20 03:37:34 +00:00
|
|
|
if (ha->fw_attributes & BIT_3)
|
|
|
|
strcat(str, "[SB-2] ");
|
|
|
|
if (ha->fw_attributes & BIT_4)
|
|
|
|
strcat(str, "[T10 CRC] ");
|
|
|
|
if (ha->fw_attributes & BIT_5)
|
|
|
|
strcat(str, "[VI] ");
|
2008-04-03 20:13:26 +00:00
|
|
|
if (ha->fw_attributes & BIT_10)
|
|
|
|
strcat(str, "[84XX] ");
|
2005-07-06 17:31:47 +00:00
|
|
|
if (ha->fw_attributes & BIT_13)
|
|
|
|
strcat(str, "[Experimental]");
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline srb_t *
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_get_new_sp(scsi_qla_host_t *vha, fc_port_t *fcport,
|
2005-07-06 17:31:47 +00:00
|
|
|
struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
|
|
|
|
{
|
|
|
|
srb_t *sp;
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-07-06 17:31:47 +00:00
|
|
|
|
|
|
|
sp = mempool_alloc(ha->srb_mempool, GFP_ATOMIC);
|
|
|
|
if (!sp)
|
|
|
|
return sp;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
sp->vha = vha;
|
2005-07-06 17:31:47 +00:00
|
|
|
sp->fcport = fcport;
|
|
|
|
sp->cmd = cmd;
|
2008-12-18 18:06:15 +00:00
|
|
|
sp->que = ha->req_q_map[0];
|
2005-07-06 17:31:47 +00:00
|
|
|
sp->flags = 0;
|
|
|
|
CMD_SP(cmd) = (void *)sp;
|
|
|
|
cmd->scsi_done = done;
|
|
|
|
|
|
|
|
return sp;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static int
|
2005-04-17 20:02:26 +00:00
|
|
|
qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
|
2005-04-17 20:06:53 +00:00
|
|
|
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
|
[SCSI] update fc_transport for removal of block/unblock functions
We recently went back to implement a board reset. When we perform the
reset, we wanted to tear down the internal data structures and rebuild
them. Unfortunately, when it came to the rport structure, things were
odd. If we deleted them, the scsi targets and sdevs would be
torn down. Not a good thing for a temporary reset. We could block the
rports, but we either maintain the internal structures to keep the
rport reference (perhaps even replicating what's in the transport),
or we have to fatten the fc transport with new search routines to find
the rport (and deal with a case of a dangling rport that the driver
forgets).
It dawned on me that we had actually reached this state incorrectly.
When the fc transport first started, we did the block/unblock first, then
added the rport interface. The purpose of block/unblock is to hide the
temporary disappearance of the rport (e.g. being deleted, then readded).
Why are we making the driver do the block/unblock ? We should be making
the transport have only an rport add/delete, and the let the transport
handle the block/unblock.
So... This patch removes the existing fc_remote_port_block/unblock
functions. It moves the block/unblock functionality into the
fc_remote_port_add/delete functions. Updates for the lpfc driver are
included. Qlogic driver updates are also enclosed, thanks to the
contributions of Andrew Vasquez. [Note: the qla2xxx changes are
relative to the scsi-misc-2.6 tree as of this morning - which does
not include the recent patches sent by Andrew]. The zfcp driver does
not use the block/unblock functions.
One last comment: The resulting behavior feels very clean. The LLDD is
concerned only with add/delete, which corresponds to the physical
disappearance. However, the fact that the scsi target and sdevs are
not immediately torn down after the LLDD calls delete causes an
interesting scenario... the midlayer can call the xxx_slave_alloc and
xxx_queuecommand functions with a sdev that is at the location the
rport used to be. The driver must validate the device exists when it
first enters these functions. In thinking about it, this has always
been the case for the LLDD and these routines. The existing drivers
already check for existence. However, this highlights that simple
validation via data structure dereferencing needs to be watched.
To deal with this, a new transport function, fc_remote_port_chkready()
was created that LLDDs should call when they first enter these two
routines. It validates the rport state, and returns a scsi result
which could be returned. In addition to solving the above, it also
creates consistent behavior from the LLDD's when the block and deletes
are occuring.
Rejections fixed up and
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2005-10-18 16:03:35 +00:00
|
|
|
struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device));
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-04-17 20:02:26 +00:00
|
|
|
srb_t *sp;
|
|
|
|
int rval;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-09-20 21:07:36 +00:00
|
|
|
if (unlikely(pci_channel_offline(ha->pdev))) {
|
|
|
|
cmd->result = DID_REQUEUE << 16;
|
|
|
|
goto qc_fail_command;
|
|
|
|
}
|
|
|
|
|
[SCSI] update fc_transport for removal of block/unblock functions
We recently went back to implement a board reset. When we perform the
reset, we wanted to tear down the internal data structures and rebuild
them. Unfortunately, when it came to the rport structure, things were
odd. If we deleted them, the scsi targets and sdevs would be
torn down. Not a good thing for a temporary reset. We could block the
rports, but we either maintain the internal structures to keep the
rport reference (perhaps even replicating what's in the transport),
or we have to fatten the fc transport with new search routines to find
the rport (and deal with a case of a dangling rport that the driver
forgets).
It dawned on me that we had actually reached this state incorrectly.
When the fc transport first started, we did the block/unblock first, then
added the rport interface. The purpose of block/unblock is to hide the
temporary disappearance of the rport (e.g. being deleted, then readded).
Why are we making the driver do the block/unblock ? We should be making
the transport have only an rport add/delete, and the let the transport
handle the block/unblock.
So... This patch removes the existing fc_remote_port_block/unblock
functions. It moves the block/unblock functionality into the
fc_remote_port_add/delete functions. Updates for the lpfc driver are
included. Qlogic driver updates are also enclosed, thanks to the
contributions of Andrew Vasquez. [Note: the qla2xxx changes are
relative to the scsi-misc-2.6 tree as of this morning - which does
not include the recent patches sent by Andrew]. The zfcp driver does
not use the block/unblock functions.
One last comment: The resulting behavior feels very clean. The LLDD is
concerned only with add/delete, which corresponds to the physical
disappearance. However, the fact that the scsi target and sdevs are
not immediately torn down after the LLDD calls delete causes an
interesting scenario... the midlayer can call the xxx_slave_alloc and
xxx_queuecommand functions with a sdev that is at the location the
rport used to be. The driver must validate the device exists when it
first enters these functions. In thinking about it, this has always
been the case for the LLDD and these routines. The existing drivers
already check for existence. However, this highlights that simple
validation via data structure dereferencing needs to be watched.
To deal with this, a new transport function, fc_remote_port_chkready()
was created that LLDDs should call when they first enter these two
routines. It validates the rport state, and returns a scsi result
which could be returned. In addition to solving the above, it also
creates consistent behavior from the LLDD's when the block and deletes
are occuring.
Rejections fixed up and
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2005-10-18 16:03:35 +00:00
|
|
|
rval = fc_remote_port_chkready(rport);
|
|
|
|
if (rval) {
|
|
|
|
cmd->result = rval;
|
2005-04-17 20:02:26 +00:00
|
|
|
goto qc_fail_command;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-02-07 16:45:45 +00:00
|
|
|
/* Close window on fcport/rport state-transitioning. */
|
2008-08-17 20:24:40 +00:00
|
|
|
if (fcport->drport)
|
|
|
|
goto qc_target_busy;
|
2006-02-07 16:45:45 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
if (atomic_read(&fcport->state) != FCS_ONLINE) {
|
|
|
|
if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
|
2008-11-06 18:40:51 +00:00
|
|
|
atomic_read(&vha->loop_state) == LOOP_DEAD) {
|
2005-04-17 20:02:26 +00:00
|
|
|
cmd->result = DID_NO_CONNECT << 16;
|
|
|
|
goto qc_fail_command;
|
|
|
|
}
|
2008-08-17 20:24:40 +00:00
|
|
|
goto qc_target_busy;
|
2005-04-17 20:02:26 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_unlock_irq(vha->host->host_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
sp = qla2x00_get_new_sp(vha, fcport, cmd, done);
|
2005-07-06 17:31:47 +00:00
|
|
|
if (!sp)
|
2005-04-17 20:02:26 +00:00
|
|
|
goto qc_host_busy_lock;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
rval = ha->isp_ops->start_scsi(sp);
|
2005-04-17 20:02:26 +00:00
|
|
|
if (rval != QLA_SUCCESS)
|
|
|
|
goto qc_host_busy_free_sp;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_lock_irq(vha->host->host_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
return 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
qc_host_busy_free_sp:
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_sp_free_dma(sp);
|
2005-04-17 20:02:26 +00:00
|
|
|
mempool_free(sp, ha->srb_mempool);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
qc_host_busy_lock:
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_lock_irq(vha->host->host_lock);
|
2005-04-17 20:02:26 +00:00
|
|
|
return SCSI_MLQUEUE_HOST_BUSY;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-08-17 20:24:40 +00:00
|
|
|
qc_target_busy:
|
|
|
|
return SCSI_MLQUEUE_TARGET_BUSY;
|
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
qc_fail_command:
|
|
|
|
done(cmd);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
return 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2005-07-06 17:31:47 +00:00
|
|
|
|
|
|
|
static int
|
|
|
|
qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
|
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
|
2005-07-06 17:31:47 +00:00
|
|
|
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
|
[SCSI] update fc_transport for removal of block/unblock functions
We recently went back to implement a board reset. When we perform the
reset, we wanted to tear down the internal data structures and rebuild
them. Unfortunately, when it came to the rport structure, things were
odd. If we deleted them, the scsi targets and sdevs would be
torn down. Not a good thing for a temporary reset. We could block the
rports, but we either maintain the internal structures to keep the
rport reference (perhaps even replicating what's in the transport),
or we have to fatten the fc transport with new search routines to find
the rport (and deal with a case of a dangling rport that the driver
forgets).
It dawned on me that we had actually reached this state incorrectly.
When the fc transport first started, we did the block/unblock first, then
added the rport interface. The purpose of block/unblock is to hide the
temporary disappearance of the rport (e.g. being deleted, then readded).
Why are we making the driver do the block/unblock ? We should be making
the transport have only an rport add/delete, and the let the transport
handle the block/unblock.
So... This patch removes the existing fc_remote_port_block/unblock
functions. It moves the block/unblock functionality into the
fc_remote_port_add/delete functions. Updates for the lpfc driver are
included. Qlogic driver updates are also enclosed, thanks to the
contributions of Andrew Vasquez. [Note: the qla2xxx changes are
relative to the scsi-misc-2.6 tree as of this morning - which does
not include the recent patches sent by Andrew]. The zfcp driver does
not use the block/unblock functions.
One last comment: The resulting behavior feels very clean. The LLDD is
concerned only with add/delete, which corresponds to the physical
disappearance. However, the fact that the scsi target and sdevs are
not immediately torn down after the LLDD calls delete causes an
interesting scenario... the midlayer can call the xxx_slave_alloc and
xxx_queuecommand functions with a sdev that is at the location the
rport used to be. The driver must validate the device exists when it
first enters these functions. In thinking about it, this has always
been the case for the LLDD and these routines. The existing drivers
already check for existence. However, this highlights that simple
validation via data structure dereferencing needs to be watched.
To deal with this, a new transport function, fc_remote_port_chkready()
was created that LLDDs should call when they first enter these two
routines. It validates the rport state, and returns a scsi result
which could be returned. In addition to solving the above, it also
creates consistent behavior from the LLDD's when the block and deletes
are occuring.
Rejections fixed up and
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2005-10-18 16:03:35 +00:00
|
|
|
struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device));
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
|
2005-07-06 17:31:47 +00:00
|
|
|
srb_t *sp;
|
|
|
|
int rval;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (unlikely(pci_channel_offline(ha->pdev))) {
|
2007-09-20 21:07:36 +00:00
|
|
|
cmd->result = DID_REQUEUE << 16;
|
|
|
|
goto qc24_fail_command;
|
|
|
|
}
|
|
|
|
|
[SCSI] update fc_transport for removal of block/unblock functions
We recently went back to implement a board reset. When we perform the
reset, we wanted to tear down the internal data structures and rebuild
them. Unfortunately, when it came to the rport structure, things were
odd. If we deleted them, the scsi targets and sdevs would be
torn down. Not a good thing for a temporary reset. We could block the
rports, but we either maintain the internal structures to keep the
rport reference (perhaps even replicating what's in the transport),
or we have to fatten the fc transport with new search routines to find
the rport (and deal with a case of a dangling rport that the driver
forgets).
It dawned on me that we had actually reached this state incorrectly.
When the fc transport first started, we did the block/unblock first, then
added the rport interface. The purpose of block/unblock is to hide the
temporary disappearance of the rport (e.g. being deleted, then readded).
Why are we making the driver do the block/unblock ? We should be making
the transport have only an rport add/delete, and the let the transport
handle the block/unblock.
So... This patch removes the existing fc_remote_port_block/unblock
functions. It moves the block/unblock functionality into the
fc_remote_port_add/delete functions. Updates for the lpfc driver are
included. Qlogic driver updates are also enclosed, thanks to the
contributions of Andrew Vasquez. [Note: the qla2xxx changes are
relative to the scsi-misc-2.6 tree as of this morning - which does
not include the recent patches sent by Andrew]. The zfcp driver does
not use the block/unblock functions.
One last comment: The resulting behavior feels very clean. The LLDD is
concerned only with add/delete, which corresponds to the physical
disappearance. However, the fact that the scsi target and sdevs are
not immediately torn down after the LLDD calls delete causes an
interesting scenario... the midlayer can call the xxx_slave_alloc and
xxx_queuecommand functions with a sdev that is at the location the
rport used to be. The driver must validate the device exists when it
first enters these functions. In thinking about it, this has always
been the case for the LLDD and these routines. The existing drivers
already check for existence. However, this highlights that simple
validation via data structure dereferencing needs to be watched.
To deal with this, a new transport function, fc_remote_port_chkready()
was created that LLDDs should call when they first enter these two
routines. It validates the rport state, and returns a scsi result
which could be returned. In addition to solving the above, it also
creates consistent behavior from the LLDD's when the block and deletes
are occuring.
Rejections fixed up and
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2005-10-18 16:03:35 +00:00
|
|
|
rval = fc_remote_port_chkready(rport);
|
|
|
|
if (rval) {
|
|
|
|
cmd->result = rval;
|
2005-07-06 17:31:47 +00:00
|
|
|
goto qc24_fail_command;
|
|
|
|
}
|
|
|
|
|
2006-02-07 16:45:45 +00:00
|
|
|
/* Close window on fcport/rport state-transitioning. */
|
2008-08-17 20:24:40 +00:00
|
|
|
if (fcport->drport)
|
|
|
|
goto qc24_target_busy;
|
2006-02-07 16:45:45 +00:00
|
|
|
|
2005-07-06 17:31:47 +00:00
|
|
|
if (atomic_read(&fcport->state) != FCS_ONLINE) {
|
|
|
|
if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
|
2008-11-06 18:40:51 +00:00
|
|
|
atomic_read(&base_vha->loop_state) == LOOP_DEAD) {
|
2005-07-06 17:31:47 +00:00
|
|
|
cmd->result = DID_NO_CONNECT << 16;
|
|
|
|
goto qc24_fail_command;
|
|
|
|
}
|
2008-08-17 20:24:40 +00:00
|
|
|
goto qc24_target_busy;
|
2005-07-06 17:31:47 +00:00
|
|
|
}
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_unlock_irq(vha->host->host_lock);
|
2005-07-06 17:31:47 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
sp = qla2x00_get_new_sp(base_vha, fcport, cmd, done);
|
2005-07-06 17:31:47 +00:00
|
|
|
if (!sp)
|
|
|
|
goto qc24_host_busy_lock;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
rval = ha->isp_ops->start_scsi(sp);
|
2005-07-06 17:31:47 +00:00
|
|
|
if (rval != QLA_SUCCESS)
|
|
|
|
goto qc24_host_busy_free_sp;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_lock_irq(vha->host->host_lock);
|
2005-07-06 17:31:47 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
qc24_host_busy_free_sp:
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_sp_free_dma(sp);
|
|
|
|
mempool_free(sp, ha->srb_mempool);
|
2005-07-06 17:31:47 +00:00
|
|
|
|
|
|
|
qc24_host_busy_lock:
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_lock_irq(vha->host->host_lock);
|
2005-07-06 17:31:47 +00:00
|
|
|
return SCSI_MLQUEUE_HOST_BUSY;
|
|
|
|
|
2008-08-17 20:24:40 +00:00
|
|
|
qc24_target_busy:
|
|
|
|
return SCSI_MLQUEUE_TARGET_BUSY;
|
|
|
|
|
2005-07-06 17:31:47 +00:00
|
|
|
qc24_fail_command:
|
|
|
|
done(cmd);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* qla2x00_eh_wait_on_command
|
|
|
|
* Waits for the command to be returned by the Firmware for some
|
|
|
|
* max time.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* cmd = Scsi Command to wait on.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* Not Found : 0
|
|
|
|
* Found : 1
|
|
|
|
*/
|
|
|
|
static int
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_eh_wait_on_command(struct scsi_cmnd *cmd)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-08-27 02:10:10 +00:00
|
|
|
#define ABORT_POLLING_PERIOD 1000
|
|
|
|
#define ABORT_WAIT_ITER ((10 * 1000) / (ABORT_POLLING_PERIOD))
|
2005-04-17 20:02:26 +00:00
|
|
|
unsigned long wait_iter = ABORT_WAIT_ITER;
|
|
|
|
int ret = QLA_SUCCESS;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
while (CMD_SP(cmd)) {
|
2005-08-27 02:10:10 +00:00
|
|
|
msleep(ABORT_POLLING_PERIOD);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
if (--wait_iter)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (CMD_SP(cmd))
|
|
|
|
ret = QLA_FUNCTION_FAILED;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qla2x00_wait_for_hba_online
|
2005-07-06 17:32:07 +00:00
|
|
|
* Wait till the HBA is online after going through
|
2005-04-16 22:20:36 +00:00
|
|
|
* <= MAX_RETRIES_OF_ISP_ABORT or
|
|
|
|
* finally HBA is disabled ie marked offline
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* ha - pointer to host adapter structure
|
2005-07-06 17:32:07 +00:00
|
|
|
*
|
|
|
|
* Note:
|
2005-04-16 22:20:36 +00:00
|
|
|
* Does context switching-Release SPIN_LOCK
|
|
|
|
* (if any) before calling this routine.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* Success (Adapter is online) : 0
|
|
|
|
* Failed (Adapter is offline/disabled) : 1
|
|
|
|
*/
|
2006-02-01 00:05:17 +00:00
|
|
|
int
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_wait_for_hba_online(scsi_qla_host_t *vha)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-07-06 17:31:47 +00:00
|
|
|
int return_status;
|
|
|
|
unsigned long wait_online;
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-07-06 17:32:07 +00:00
|
|
|
wait_online = jiffies + (MAX_LOOP_TIMEOUT * HZ);
|
2008-11-06 18:40:51 +00:00
|
|
|
while (((test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) ||
|
|
|
|
test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) ||
|
|
|
|
test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) ||
|
|
|
|
ha->dpc_active) && time_before(jiffies, wait_online)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
msleep(1000);
|
|
|
|
}
|
2008-11-06 18:40:51 +00:00
|
|
|
if (base_vha->flags.online)
|
2005-07-06 17:32:07 +00:00
|
|
|
return_status = QLA_SUCCESS;
|
2005-04-16 22:20:36 +00:00
|
|
|
else
|
|
|
|
return_status = QLA_FUNCTION_FAILED;
|
|
|
|
|
|
|
|
return (return_status);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qla2x00_wait_for_loop_ready
|
|
|
|
* Wait for MAX_LOOP_TIMEOUT(5 min) value for loop
|
2005-07-06 17:32:07 +00:00
|
|
|
* to be in LOOP_READY state.
|
2005-04-16 22:20:36 +00:00
|
|
|
* Input:
|
|
|
|
* ha - pointer to host adapter structure
|
2005-07-06 17:32:07 +00:00
|
|
|
*
|
|
|
|
* Note:
|
2005-04-16 22:20:36 +00:00
|
|
|
* Does context switching-Release SPIN_LOCK
|
|
|
|
* (if any) before calling this routine.
|
2005-07-06 17:32:07 +00:00
|
|
|
*
|
2005-04-16 22:20:36 +00:00
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* Success (LOOP_READY) : 0
|
|
|
|
* Failed (LOOP_NOT_READY) : 1
|
|
|
|
*/
|
2005-07-06 17:32:07 +00:00
|
|
|
static inline int
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_wait_for_loop_ready(scsi_qla_host_t *vha)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int return_status = QLA_SUCCESS;
|
|
|
|
unsigned long loop_timeout ;
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* wait for 5 min at the max for loop to be ready */
|
2005-07-06 17:32:07 +00:00
|
|
|
loop_timeout = jiffies + (MAX_LOOP_TIMEOUT * HZ);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
while ((!atomic_read(&base_vha->loop_down_timer) &&
|
|
|
|
atomic_read(&base_vha->loop_state) == LOOP_DOWN) ||
|
|
|
|
atomic_read(&base_vha->loop_state) != LOOP_READY) {
|
|
|
|
if (atomic_read(&base_vha->loop_state) == LOOP_DEAD) {
|
2006-05-17 22:08:44 +00:00
|
|
|
return_status = QLA_FUNCTION_FAILED;
|
|
|
|
break;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
msleep(1000);
|
|
|
|
if (time_after_eq(jiffies, loop_timeout)) {
|
|
|
|
return_status = QLA_FUNCTION_FAILED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2005-07-06 17:32:07 +00:00
|
|
|
return (return_status);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-07-10 23:55:47 +00:00
|
|
|
void
|
|
|
|
qla2x00_abort_fcport_cmds(fc_port_t *fcport)
|
|
|
|
{
|
2008-12-10 00:45:39 +00:00
|
|
|
int cnt, que, id;
|
2008-07-10 23:55:47 +00:00
|
|
|
unsigned long flags;
|
|
|
|
srb_t *sp;
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *vha = fcport->vha;
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2008-12-10 00:45:39 +00:00
|
|
|
struct req_que *req;
|
2008-07-10 23:55:47 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
2008-12-10 00:45:39 +00:00
|
|
|
for (que = 0; que < QLA_MAX_HOST_QUES; que++) {
|
|
|
|
id = vha->req_ques[que];
|
|
|
|
req = ha->req_q_map[id];
|
|
|
|
if (!req)
|
2008-07-10 23:55:47 +00:00
|
|
|
continue;
|
2008-12-10 00:45:39 +00:00
|
|
|
for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
|
|
|
|
sp = req->outstanding_cmds[cnt];
|
|
|
|
if (!sp)
|
|
|
|
continue;
|
|
|
|
if (sp->fcport != fcport)
|
|
|
|
continue;
|
2008-07-10 23:55:47 +00:00
|
|
|
|
2008-12-10 00:45:39 +00:00
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
if (ha->isp_ops->abort_command(vha, sp, req)) {
|
2008-07-10 23:55:47 +00:00
|
|
|
DEBUG2(qla_printk(KERN_WARNING, ha,
|
2008-12-10 00:45:39 +00:00
|
|
|
"Abort failed -- %lx\n",
|
|
|
|
sp->cmd->serial_number));
|
|
|
|
} else {
|
|
|
|
if (qla2x00_eh_wait_on_command(sp->cmd) !=
|
|
|
|
QLA_SUCCESS)
|
|
|
|
DEBUG2(qla_printk(KERN_WARNING, ha,
|
|
|
|
"Abort failed while waiting -- %lx\n",
|
|
|
|
sp->cmd->serial_number));
|
|
|
|
}
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
2008-07-10 23:55:47 +00:00
|
|
|
}
|
|
|
|
}
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
2008-07-10 23:55:47 +00:00
|
|
|
}
|
|
|
|
|
2006-10-02 19:00:49 +00:00
|
|
|
static void
|
|
|
|
qla2x00_block_error_handler(struct scsi_cmnd *cmnd)
|
|
|
|
{
|
|
|
|
struct Scsi_Host *shost = cmnd->device->host;
|
|
|
|
struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(shost->host_lock, flags);
|
|
|
|
while (rport->port_state == FC_PORTSTATE_BLOCKED) {
|
|
|
|
spin_unlock_irqrestore(shost->host_lock, flags);
|
|
|
|
msleep(1000);
|
|
|
|
spin_lock_irqsave(shost->host_lock, flags);
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(shost->host_lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/**************************************************************************
|
|
|
|
* qla2xxx_eh_abort
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* The abort function will abort the specified command.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* cmd = Linux SCSI command packet to be aborted.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* Either SUCCESS or FAILED.
|
|
|
|
*
|
|
|
|
* Note:
|
2006-04-27 23:25:30 +00:00
|
|
|
* Only return FAILED if command not returned by firmware.
|
2005-04-16 22:20:36 +00:00
|
|
|
**************************************************************************/
|
2006-11-09 03:55:50 +00:00
|
|
|
static int
|
2005-04-16 22:20:36 +00:00
|
|
|
qla2xxx_eh_abort(struct scsi_cmnd *cmd)
|
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
|
2005-04-17 20:02:26 +00:00
|
|
|
srb_t *sp;
|
2008-12-18 18:06:15 +00:00
|
|
|
int ret, i;
|
2005-04-17 20:02:26 +00:00
|
|
|
unsigned int id, lun;
|
|
|
|
unsigned long serial;
|
2005-05-27 22:04:47 +00:00
|
|
|
unsigned long flags;
|
2006-04-27 23:25:30 +00:00
|
|
|
int wait = 0;
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2008-12-10 00:45:39 +00:00
|
|
|
struct req_que *req;
|
2008-12-18 18:06:15 +00:00
|
|
|
srb_t *spt;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-10-02 19:00:49 +00:00
|
|
|
qla2x00_block_error_handler(cmd);
|
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
if (!CMD_SP(cmd))
|
2006-04-27 23:25:30 +00:00
|
|
|
return SUCCESS;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-27 23:25:30 +00:00
|
|
|
ret = SUCCESS;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
id = cmd->device->id;
|
|
|
|
lun = cmd->device->lun;
|
|
|
|
serial = cmd->serial_number;
|
2008-12-18 18:06:15 +00:00
|
|
|
spt = (srb_t *) CMD_SP(cmd);
|
|
|
|
if (!spt)
|
|
|
|
return SUCCESS;
|
|
|
|
req = spt->que;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
/* Check active list for command command. */
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
2008-12-18 18:06:15 +00:00
|
|
|
for (i = 1; i < MAX_OUTSTANDING_COMMANDS; i++) {
|
|
|
|
sp = req->outstanding_cmds[i];
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-12-18 18:06:15 +00:00
|
|
|
if (sp == NULL)
|
|
|
|
continue;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-12-18 18:06:15 +00:00
|
|
|
if (sp->cmd != cmd)
|
|
|
|
continue;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-12-18 18:06:15 +00:00
|
|
|
DEBUG2(printk("%s(%ld): aborting sp %p from RISC."
|
|
|
|
" pid=%ld.\n", __func__, vha->host_no, sp, serial));
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
if (ha->isp_ops->abort_command(vha, sp, req)) {
|
|
|
|
DEBUG2(printk("%s(%ld): abort_command "
|
|
|
|
"mbx failed.\n", __func__, vha->host_no));
|
|
|
|
} else {
|
|
|
|
DEBUG3(printk("%s(%ld): abort_command "
|
|
|
|
"mbx success.\n", __func__, vha->host_no));
|
|
|
|
wait = 1;
|
2008-12-10 00:45:39 +00:00
|
|
|
}
|
2008-12-18 18:06:15 +00:00
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
break;
|
2005-04-17 20:02:26 +00:00
|
|
|
}
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
/* Wait for the command to be returned. */
|
2006-04-27 23:25:30 +00:00
|
|
|
if (wait) {
|
2008-11-06 18:40:51 +00:00
|
|
|
if (qla2x00_eh_wait_on_command(cmd) != QLA_SUCCESS) {
|
2005-07-06 17:32:07 +00:00
|
|
|
qla_printk(KERN_ERR, ha,
|
2005-04-17 20:02:26 +00:00
|
|
|
"scsi(%ld:%d:%d): Abort handler timed out -- %lx "
|
2008-11-06 18:40:51 +00:00
|
|
|
"%x.\n", vha->host_no, id, lun, serial, ret);
|
2006-04-27 23:25:30 +00:00
|
|
|
ret = FAILED;
|
2005-04-17 20:02:26 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2005-07-06 17:32:07 +00:00
|
|
|
qla_printk(KERN_INFO, ha,
|
2006-04-27 23:25:30 +00:00
|
|
|
"scsi(%ld:%d:%d): Abort command issued -- %d %lx %x.\n",
|
2008-11-06 18:40:51 +00:00
|
|
|
vha->host_no, id, lun, wait, serial, ret);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-04-03 20:13:24 +00:00
|
|
|
enum nexus_wait_type {
|
|
|
|
WAIT_HOST = 0,
|
|
|
|
WAIT_TARGET,
|
|
|
|
WAIT_LUN,
|
|
|
|
};
|
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
static int
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t,
|
2008-12-18 18:06:15 +00:00
|
|
|
unsigned int l, srb_t *sp, enum nexus_wait_type type)
|
2005-04-17 20:02:26 +00:00
|
|
|
{
|
2008-12-18 18:06:15 +00:00
|
|
|
int cnt, match, status;
|
2005-05-27 22:04:47 +00:00
|
|
|
unsigned long flags;
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2008-12-10 00:45:39 +00:00
|
|
|
struct req_que *req;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-04-03 20:13:24 +00:00
|
|
|
status = QLA_SUCCESS;
|
2008-12-18 18:06:15 +00:00
|
|
|
if (!sp)
|
|
|
|
return status;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
2008-12-18 18:06:15 +00:00
|
|
|
req = sp->que;
|
|
|
|
for (cnt = 1; status == QLA_SUCCESS &&
|
|
|
|
cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
|
|
|
|
sp = req->outstanding_cmds[cnt];
|
|
|
|
if (!sp)
|
2008-04-03 20:13:24 +00:00
|
|
|
continue;
|
2008-08-14 04:37:00 +00:00
|
|
|
|
2008-12-18 18:06:15 +00:00
|
|
|
if (vha->vp_idx != sp->fcport->vha->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;
|
2008-12-10 00:45:39 +00:00
|
|
|
}
|
2008-12-18 18:06:15 +00:00
|
|
|
if (!match)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
status = qla2x00_eh_wait_on_command(sp->cmd);
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
2008-04-03 20:13:24 +00:00
|
|
|
|
|
|
|
return status;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-04-03 20:13:24 +00:00
|
|
|
static char *reset_errors[] = {
|
|
|
|
"HBA not online",
|
|
|
|
"HBA not ready",
|
|
|
|
"Task management failed",
|
|
|
|
"Waiting for command completions",
|
|
|
|
};
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-11-09 03:55:50 +00:00
|
|
|
static int
|
2008-04-03 20:13:24 +00:00
|
|
|
__qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
|
|
|
|
struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int))
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
|
2005-04-17 20:06:53 +00:00
|
|
|
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
|
2008-04-03 20:13:24 +00:00
|
|
|
int err;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-10-02 19:00:49 +00:00
|
|
|
qla2x00_block_error_handler(cmd);
|
|
|
|
|
2006-08-01 20:48:15 +00:00
|
|
|
if (!fcport)
|
2008-04-03 20:13:24 +00:00
|
|
|
return FAILED;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET ISSUED.\n",
|
|
|
|
vha->host_no, cmd->device->id, cmd->device->lun, name);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-04-03 20:13:24 +00:00
|
|
|
err = 0;
|
2008-11-06 18:40:51 +00:00
|
|
|
if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS)
|
2008-04-03 20:13:24 +00:00
|
|
|
goto eh_reset_failed;
|
|
|
|
err = 1;
|
2008-11-06 18:40:51 +00:00
|
|
|
if (qla2x00_wait_for_loop_ready(vha) != QLA_SUCCESS)
|
2008-04-03 20:13:24 +00:00
|
|
|
goto eh_reset_failed;
|
|
|
|
err = 2;
|
|
|
|
if (do_reset(fcport, cmd->device->lun) != QLA_SUCCESS)
|
|
|
|
goto eh_reset_failed;
|
|
|
|
err = 3;
|
2008-11-06 18:40:51 +00:00
|
|
|
if (qla2x00_eh_wait_for_pending_commands(vha, cmd->device->id,
|
2008-12-18 18:06:15 +00:00
|
|
|
cmd->device->lun, (srb_t *) CMD_SP(cmd), type) != QLA_SUCCESS)
|
2008-04-03 20:13:24 +00:00
|
|
|
goto eh_reset_failed;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET SUCCEEDED.\n",
|
|
|
|
vha->host_no, cmd->device->id, cmd->device->lun, name);
|
2008-04-03 20:13:24 +00:00
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
|
|
|
|
eh_reset_failed:
|
2008-11-06 18:40:51 +00:00
|
|
|
qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET FAILED: %s.\n"
|
|
|
|
, vha->host_no, cmd->device->id, cmd->device->lun, name,
|
2008-04-03 20:13:24 +00:00
|
|
|
reset_errors[err]);
|
|
|
|
return FAILED;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-04-03 20:13:24 +00:00
|
|
|
static int
|
|
|
|
qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
|
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-04-03 20:13:24 +00:00
|
|
|
return __qla2xxx_eh_generic_reset("DEVICE", WAIT_LUN, cmd,
|
|
|
|
ha->isp_ops->lun_reset);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2008-04-03 20:13:24 +00:00
|
|
|
qla2xxx_eh_target_reset(struct scsi_cmnd *cmd)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-04-03 20:13:24 +00:00
|
|
|
return __qla2xxx_eh_generic_reset("TARGET", WAIT_TARGET, cmd,
|
|
|
|
ha->isp_ops->target_reset);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
* qla2xxx_eh_bus_reset
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* The bus reset function will reset the bus and abort any executing
|
|
|
|
* commands.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* cmd = Linux SCSI command packet of the command that cause the
|
|
|
|
* bus reset.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* SUCCESS/FAILURE (defined as macro in scsi.h).
|
|
|
|
*
|
|
|
|
**************************************************************************/
|
2006-11-09 03:55:50 +00:00
|
|
|
static int
|
2005-04-16 22:20:36 +00:00
|
|
|
qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
|
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
|
2005-04-17 20:06:53 +00:00
|
|
|
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
|
2007-07-05 20:16:51 +00:00
|
|
|
int ret = FAILED;
|
2005-04-17 20:02:26 +00:00
|
|
|
unsigned int id, lun;
|
|
|
|
unsigned long serial;
|
2008-12-18 18:06:15 +00:00
|
|
|
srb_t *sp = (srb_t *) CMD_SP(cmd);
|
2005-04-17 20:02:26 +00:00
|
|
|
|
2006-10-02 19:00:49 +00:00
|
|
|
qla2x00_block_error_handler(cmd);
|
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
id = cmd->device->id;
|
|
|
|
lun = cmd->device->lun;
|
|
|
|
serial = cmd->serial_number;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-08-01 20:48:15 +00:00
|
|
|
if (!fcport)
|
2005-04-17 20:02:26 +00:00
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla_printk(KERN_INFO, vha->hw,
|
2008-11-14 21:48:12 +00:00
|
|
|
"scsi(%ld:%d:%d): BUS RESET ISSUED.\n", vha->host_no, id, lun);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
|
2005-04-16 22:20:36 +00:00
|
|
|
DEBUG2(printk("%s failed:board disabled\n",__func__));
|
2005-04-17 20:02:26 +00:00
|
|
|
goto eh_bus_reset_done;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (qla2x00_wait_for_loop_ready(vha) == QLA_SUCCESS) {
|
|
|
|
if (qla2x00_loop_reset(vha) == QLA_SUCCESS)
|
2005-04-17 20:02:26 +00:00
|
|
|
ret = SUCCESS;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2005-04-17 20:02:26 +00:00
|
|
|
if (ret == FAILED)
|
|
|
|
goto eh_bus_reset_done;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-09-20 20:25:53 +00:00
|
|
|
/* Flush outstanding commands. */
|
2008-12-18 18:06:15 +00:00
|
|
|
if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, sp, WAIT_HOST) !=
|
2008-04-03 20:13:24 +00:00
|
|
|
QLA_SUCCESS)
|
2005-09-20 20:25:53 +00:00
|
|
|
ret = FAILED;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
eh_bus_reset_done:
|
2008-11-06 18:40:51 +00:00
|
|
|
qla_printk(KERN_INFO, vha->hw, "%s: reset %s\n", __func__,
|
2005-04-17 20:02:26 +00:00
|
|
|
(ret == FAILED) ? "failed" : "succeded");
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
* qla2xxx_eh_host_reset
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* The reset function will reset the Adapter.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* cmd = Linux SCSI command packet of the command that cause the
|
|
|
|
* adapter reset.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* Either SUCCESS or FAILED.
|
|
|
|
*
|
|
|
|
* Note:
|
|
|
|
**************************************************************************/
|
2006-11-09 03:55:50 +00:00
|
|
|
static int
|
2005-04-16 22:20:36 +00:00
|
|
|
qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
|
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
|
2005-04-17 20:06:53 +00:00
|
|
|
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2007-07-05 20:16:51 +00:00
|
|
|
int ret = FAILED;
|
2005-04-17 20:02:26 +00:00
|
|
|
unsigned int id, lun;
|
|
|
|
unsigned long serial;
|
2008-12-18 18:06:15 +00:00
|
|
|
srb_t *sp = (srb_t *) CMD_SP(cmd);
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-10-02 19:00:49 +00:00
|
|
|
qla2x00_block_error_handler(cmd);
|
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
id = cmd->device->id;
|
|
|
|
lun = cmd->device->lun;
|
|
|
|
serial = cmd->serial_number;
|
|
|
|
|
2006-08-01 20:48:15 +00:00
|
|
|
if (!fcport)
|
2005-04-17 20:02:26 +00:00
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
qla_printk(KERN_INFO, ha,
|
2008-11-06 18:40:51 +00:00
|
|
|
"scsi(%ld:%d:%d): ADAPTER RESET ISSUED.\n", vha->host_no, id, lun);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS)
|
2005-04-17 20:02:26 +00:00
|
|
|
goto eh_host_reset_lock;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Fixme-may be dpc thread is active and processing
|
2005-07-06 17:32:07 +00:00
|
|
|
* loop_resync,so wait a while for it to
|
2005-04-16 22:20:36 +00:00
|
|
|
* be completed and then issue big hammer.Otherwise
|
|
|
|
* it may cause I/O failure as big hammer marks the
|
|
|
|
* devices as lost kicking of the port_down_timer
|
|
|
|
* while dpc is stuck for the mailbox to complete.
|
|
|
|
*/
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_wait_for_loop_ready(vha);
|
|
|
|
if (vha != base_vha) {
|
|
|
|
if (qla2x00_vp_abort_isp(vha))
|
2005-04-17 20:02:26 +00:00
|
|
|
goto eh_host_reset_lock;
|
2008-11-06 18:40:51 +00:00
|
|
|
} else {
|
|
|
|
set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
|
|
|
|
if (qla2x00_abort_isp(base_vha)) {
|
|
|
|
clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
|
|
|
|
/* failed. schedule dpc to try */
|
|
|
|
set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
|
|
|
|
|
|
|
|
if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS)
|
|
|
|
goto eh_host_reset_lock;
|
|
|
|
}
|
|
|
|
clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
|
2005-07-06 17:32:07 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
/* Waiting for command to be returned to OS.*/
|
2008-12-18 18:06:15 +00:00
|
|
|
if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, sp, WAIT_HOST) ==
|
2008-11-06 18:40:51 +00:00
|
|
|
QLA_SUCCESS)
|
2005-04-17 20:02:26 +00:00
|
|
|
ret = SUCCESS;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
eh_host_reset_lock:
|
|
|
|
qla_printk(KERN_INFO, ha, "%s: reset %s\n", __func__,
|
|
|
|
(ret == FAILED) ? "failed" : "succeded");
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* qla2x00_loop_reset
|
|
|
|
* Issue loop reset.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* ha = adapter block pointer.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* 0 = success
|
|
|
|
*/
|
2008-01-17 17:02:12 +00:00
|
|
|
int
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_loop_reset(scsi_qla_host_t *vha)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2006-12-14 03:20:30 +00:00
|
|
|
int ret;
|
2005-04-17 20:06:53 +00:00
|
|
|
struct fc_port *fcport;
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-14 21:48:12 +00:00
|
|
|
if (ha->flags.enable_lip_full_login && !vha->vp_idx) {
|
2008-11-06 18:40:51 +00:00
|
|
|
ret = qla2x00_full_login_lip(vha);
|
2006-12-14 03:20:30 +00:00
|
|
|
if (ret != QLA_SUCCESS) {
|
2008-11-14 21:48:12 +00:00
|
|
|
DEBUG2_3(printk("%s(%ld): failed: "
|
2008-11-06 18:40:51 +00:00
|
|
|
"full_login_lip=%d.\n", __func__, vha->host_no,
|
2006-12-14 03:20:30 +00:00
|
|
|
ret));
|
2008-11-14 21:48:12 +00:00
|
|
|
}
|
|
|
|
atomic_set(&vha->loop_state, LOOP_DOWN);
|
|
|
|
atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
|
|
|
|
qla2x00_mark_all_devices_lost(vha, 0);
|
|
|
|
qla2x00_wait_for_loop_ready(vha);
|
2006-12-14 03:20:30 +00:00
|
|
|
}
|
|
|
|
|
2008-11-14 21:48:12 +00:00
|
|
|
if (ha->flags.enable_lip_reset && !vha->vp_idx) {
|
2008-11-06 18:40:51 +00:00
|
|
|
ret = qla2x00_lip_reset(vha);
|
2006-12-14 03:20:30 +00:00
|
|
|
if (ret != QLA_SUCCESS) {
|
2008-11-14 21:48:12 +00:00
|
|
|
DEBUG2_3(printk("%s(%ld): failed: "
|
2008-11-06 18:40:51 +00:00
|
|
|
"lip_reset=%d.\n", __func__, vha->host_no, ret));
|
|
|
|
} else
|
|
|
|
qla2x00_wait_for_loop_ready(vha);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2006-12-14 03:20:30 +00:00
|
|
|
if (ha->flags.enable_target_reset) {
|
2008-11-06 18:40:51 +00:00
|
|
|
list_for_each_entry(fcport, &vha->vp_fcports, list) {
|
2005-04-17 20:06:53 +00:00
|
|
|
if (fcport->port_type != FCT_TARGET)
|
2005-04-16 22:20:36 +00:00
|
|
|
continue;
|
|
|
|
|
2008-04-03 20:13:24 +00:00
|
|
|
ret = ha->isp_ops->target_reset(fcport, 0);
|
2006-12-14 03:20:30 +00:00
|
|
|
if (ret != QLA_SUCCESS) {
|
|
|
|
DEBUG2_3(printk("%s(%ld): bus_reset failed: "
|
|
|
|
"target_reset=%d d_id=%x.\n", __func__,
|
2008-11-06 18:40:51 +00:00
|
|
|
vha->host_no, ret, fcport->d_id.b24));
|
2006-12-14 03:20:30 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Issue marker command only when we are going to start the I/O */
|
2008-11-06 18:40:51 +00:00
|
|
|
vha->marker_needed = 1;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-12-14 03:20:30 +00:00
|
|
|
return QLA_SUCCESS;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-01-31 20:33:46 +00:00
|
|
|
void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res)
|
2008-01-31 20:33:46 +00:00
|
|
|
{
|
2008-12-10 00:45:39 +00:00
|
|
|
int que, cnt;
|
2008-01-31 20:33:46 +00:00
|
|
|
unsigned long flags;
|
|
|
|
srb_t *sp;
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2008-12-10 00:45:39 +00:00
|
|
|
struct req_que *req;
|
2008-01-31 20:33:46 +00:00
|
|
|
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
2008-12-10 00:45:39 +00:00
|
|
|
for (que = 0; que < QLA_MAX_HOST_QUES; que++) {
|
|
|
|
req = ha->req_q_map[vha->req_ques[que]];
|
|
|
|
if (!req)
|
|
|
|
continue;
|
|
|
|
for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
|
|
|
|
sp = req->outstanding_cmds[cnt];
|
|
|
|
if (sp && sp->vha == vha) {
|
|
|
|
req->outstanding_cmds[cnt] = NULL;
|
|
|
|
sp->cmd->result = res;
|
|
|
|
qla2x00_sp_compl(ha, sp);
|
|
|
|
}
|
2008-01-31 20:33:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
}
|
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
static int
|
|
|
|
qla2xxx_slave_alloc(struct scsi_device *sdev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-04-17 20:06:53 +00:00
|
|
|
struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
[SCSI] update fc_transport for removal of block/unblock functions
We recently went back to implement a board reset. When we perform the
reset, we wanted to tear down the internal data structures and rebuild
them. Unfortunately, when it came to the rport structure, things were
odd. If we deleted them, the scsi targets and sdevs would be
torn down. Not a good thing for a temporary reset. We could block the
rports, but we either maintain the internal structures to keep the
rport reference (perhaps even replicating what's in the transport),
or we have to fatten the fc transport with new search routines to find
the rport (and deal with a case of a dangling rport that the driver
forgets).
It dawned on me that we had actually reached this state incorrectly.
When the fc transport first started, we did the block/unblock first, then
added the rport interface. The purpose of block/unblock is to hide the
temporary disappearance of the rport (e.g. being deleted, then readded).
Why are we making the driver do the block/unblock ? We should be making
the transport have only an rport add/delete, and the let the transport
handle the block/unblock.
So... This patch removes the existing fc_remote_port_block/unblock
functions. It moves the block/unblock functionality into the
fc_remote_port_add/delete functions. Updates for the lpfc driver are
included. Qlogic driver updates are also enclosed, thanks to the
contributions of Andrew Vasquez. [Note: the qla2xxx changes are
relative to the scsi-misc-2.6 tree as of this morning - which does
not include the recent patches sent by Andrew]. The zfcp driver does
not use the block/unblock functions.
One last comment: The resulting behavior feels very clean. The LLDD is
concerned only with add/delete, which corresponds to the physical
disappearance. However, the fact that the scsi target and sdevs are
not immediately torn down after the LLDD calls delete causes an
interesting scenario... the midlayer can call the xxx_slave_alloc and
xxx_queuecommand functions with a sdev that is at the location the
rport used to be. The driver must validate the device exists when it
first enters these functions. In thinking about it, this has always
been the case for the LLDD and these routines. The existing drivers
already check for existence. However, this highlights that simple
validation via data structure dereferencing needs to be watched.
To deal with this, a new transport function, fc_remote_port_chkready()
was created that LLDDs should call when they first enter these two
routines. It validates the rport state, and returns a scsi result
which could be returned. In addition to solving the above, it also
creates consistent behavior from the LLDD's when the block and deletes
are occuring.
Rejections fixed up and
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2005-10-18 16:03:35 +00:00
|
|
|
if (!rport || fc_remote_port_chkready(rport))
|
2005-04-17 20:02:26 +00:00
|
|
|
return -ENXIO;
|
2005-04-17 20:06:53 +00:00
|
|
|
|
[SCSI] update fc_transport for removal of block/unblock functions
We recently went back to implement a board reset. When we perform the
reset, we wanted to tear down the internal data structures and rebuild
them. Unfortunately, when it came to the rport structure, things were
odd. If we deleted them, the scsi targets and sdevs would be
torn down. Not a good thing for a temporary reset. We could block the
rports, but we either maintain the internal structures to keep the
rport reference (perhaps even replicating what's in the transport),
or we have to fatten the fc transport with new search routines to find
the rport (and deal with a case of a dangling rport that the driver
forgets).
It dawned on me that we had actually reached this state incorrectly.
When the fc transport first started, we did the block/unblock first, then
added the rport interface. The purpose of block/unblock is to hide the
temporary disappearance of the rport (e.g. being deleted, then readded).
Why are we making the driver do the block/unblock ? We should be making
the transport have only an rport add/delete, and the let the transport
handle the block/unblock.
So... This patch removes the existing fc_remote_port_block/unblock
functions. It moves the block/unblock functionality into the
fc_remote_port_add/delete functions. Updates for the lpfc driver are
included. Qlogic driver updates are also enclosed, thanks to the
contributions of Andrew Vasquez. [Note: the qla2xxx changes are
relative to the scsi-misc-2.6 tree as of this morning - which does
not include the recent patches sent by Andrew]. The zfcp driver does
not use the block/unblock functions.
One last comment: The resulting behavior feels very clean. The LLDD is
concerned only with add/delete, which corresponds to the physical
disappearance. However, the fact that the scsi target and sdevs are
not immediately torn down after the LLDD calls delete causes an
interesting scenario... the midlayer can call the xxx_slave_alloc and
xxx_queuecommand functions with a sdev that is at the location the
rport used to be. The driver must validate the device exists when it
first enters these functions. In thinking about it, this has always
been the case for the LLDD and these routines. The existing drivers
already check for existence. However, this highlights that simple
validation via data structure dereferencing needs to be watched.
To deal with this, a new transport function, fc_remote_port_chkready()
was created that LLDDs should call when they first enter these two
routines. It validates the rport state, and returns a scsi result
which could be returned. In addition to solving the above, it also
creates consistent behavior from the LLDD's when the block and deletes
are occuring.
Rejections fixed up and
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2005-10-18 16:03:35 +00:00
|
|
|
sdev->hostdata = *(fc_port_t **)rport->dd_data;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
static int
|
|
|
|
qla2xxx_slave_configure(struct scsi_device *sdev)
|
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *vha = shost_priv(sdev->host);
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-04-17 20:04:54 +00:00
|
|
|
struct fc_rport *rport = starget_to_rport(sdev->sdev_target);
|
2008-12-10 00:45:39 +00:00
|
|
|
struct req_que *req = ha->req_q_map[0];
|
2005-04-17 20:04:54 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
if (sdev->tagged_supported)
|
2008-12-10 00:45:39 +00:00
|
|
|
scsi_activate_tcq(sdev, req->max_q_depth);
|
2005-04-17 20:02:26 +00:00
|
|
|
else
|
2008-12-10 00:45:39 +00:00
|
|
|
scsi_deactivate_tcq(sdev, req->max_q_depth);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-07-10 23:55:48 +00:00
|
|
|
rport->dev_loss_tmo = ha->port_down_retry_count;
|
2005-04-17 20:04:54 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
static void
|
|
|
|
qla2xxx_slave_destroy(struct scsi_device *sdev)
|
|
|
|
{
|
|
|
|
sdev->hostdata = NULL;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2005-08-27 02:09:30 +00:00
|
|
|
static int
|
|
|
|
qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth)
|
|
|
|
{
|
|
|
|
scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
|
|
|
|
return sdev->queue_depth;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
qla2x00_change_queue_type(struct scsi_device *sdev, int tag_type)
|
|
|
|
{
|
|
|
|
if (sdev->tagged_supported) {
|
|
|
|
scsi_set_tag_type(sdev, tag_type);
|
|
|
|
if (tag_type)
|
|
|
|
scsi_activate_tcq(sdev, sdev->queue_depth);
|
|
|
|
else
|
|
|
|
scsi_deactivate_tcq(sdev, sdev->queue_depth);
|
|
|
|
} else
|
|
|
|
tag_type = 0;
|
|
|
|
|
|
|
|
return tag_type;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/**
|
|
|
|
* qla2x00_config_dma_addressing() - Configure OS DMA addressing method.
|
|
|
|
* @ha: HA context
|
|
|
|
*
|
|
|
|
* At exit, the @ha's flags.enable_64bit_addressing set to indicated
|
|
|
|
* supported addressing method.
|
|
|
|
*/
|
|
|
|
static void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_config_dma_addressing(scsi_qla_host_t *vha)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-08-27 02:08:00 +00:00
|
|
|
/* Assume a 32bit DMA mask. */
|
2005-04-16 22:20:36 +00:00
|
|
|
ha->flags.enable_64bit_addressing = 0;
|
|
|
|
|
2005-08-27 02:08:00 +00:00
|
|
|
if (!dma_set_mask(&ha->pdev->dev, DMA_64BIT_MASK)) {
|
|
|
|
/* Any upper-dword bits set? */
|
|
|
|
if (MSD(dma_get_required_mask(&ha->pdev->dev)) &&
|
|
|
|
!pci_set_consistent_dma_mask(ha->pdev, DMA_64BIT_MASK)) {
|
|
|
|
/* Ok, a 64bit DMA mask is applicable. */
|
2005-04-16 22:20:36 +00:00
|
|
|
ha->flags.enable_64bit_addressing = 1;
|
2007-07-19 22:06:00 +00:00
|
|
|
ha->isp_ops->calc_req_entries = qla2x00_calc_iocbs_64;
|
|
|
|
ha->isp_ops->build_iocbs = qla2x00_build_scsi_iocbs_64;
|
2005-08-27 02:08:00 +00:00
|
|
|
return;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
2005-08-27 02:08:00 +00:00
|
|
|
|
|
|
|
dma_set_mask(&ha->pdev->dev, DMA_32BIT_MASK);
|
|
|
|
pci_set_consistent_dma_mask(ha->pdev, DMA_32BIT_MASK);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2007-07-19 22:06:00 +00:00
|
|
|
static void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_enable_intrs(struct qla_hw_data *ha)
|
2007-07-19 22:06:00 +00:00
|
|
|
{
|
|
|
|
unsigned long flags = 0;
|
|
|
|
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
ha->interrupts_on = 1;
|
|
|
|
/* enable risc and host interrupts */
|
|
|
|
WRT_REG_WORD(®->ictrl, ICR_EN_INT | ICR_EN_RISC);
|
|
|
|
RD_REG_WORD(®->ictrl);
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_disable_intrs(struct qla_hw_data *ha)
|
2007-07-19 22:06:00 +00:00
|
|
|
{
|
|
|
|
unsigned long flags = 0;
|
|
|
|
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
ha->interrupts_on = 0;
|
|
|
|
/* disable risc and host interrupts */
|
|
|
|
WRT_REG_WORD(®->ictrl, 0);
|
|
|
|
RD_REG_WORD(®->ictrl);
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla24xx_enable_intrs(struct qla_hw_data *ha)
|
2007-07-19 22:06:00 +00:00
|
|
|
{
|
|
|
|
unsigned long flags = 0;
|
|
|
|
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
ha->interrupts_on = 1;
|
|
|
|
WRT_REG_DWORD(®->ictrl, ICRX_EN_RISC_INT);
|
|
|
|
RD_REG_DWORD(®->ictrl);
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla24xx_disable_intrs(struct qla_hw_data *ha)
|
2007-07-19 22:06:00 +00:00
|
|
|
{
|
|
|
|
unsigned long flags = 0;
|
|
|
|
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
|
|
|
|
|
2009-01-05 19:18:06 +00:00
|
|
|
if (IS_NOPOLLING_TYPE(ha))
|
|
|
|
return;
|
2007-07-19 22:06:00 +00:00
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
ha->interrupts_on = 0;
|
|
|
|
WRT_REG_DWORD(®->ictrl, 0);
|
|
|
|
RD_REG_DWORD(®->ictrl);
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct isp_operations qla2100_isp_ops = {
|
|
|
|
.pci_config = qla2100_pci_config,
|
|
|
|
.reset_chip = qla2x00_reset_chip,
|
|
|
|
.chip_diag = qla2x00_chip_diag,
|
|
|
|
.config_rings = qla2x00_config_rings,
|
|
|
|
.reset_adapter = qla2x00_reset_adapter,
|
|
|
|
.nvram_config = qla2x00_nvram_config,
|
|
|
|
.update_fw_options = qla2x00_update_fw_options,
|
|
|
|
.load_risc = qla2x00_load_risc,
|
|
|
|
.pci_info_str = qla2x00_pci_info_str,
|
|
|
|
.fw_version_str = qla2x00_fw_version_str,
|
|
|
|
.intr_handler = qla2100_intr_handler,
|
|
|
|
.enable_intrs = qla2x00_enable_intrs,
|
|
|
|
.disable_intrs = qla2x00_disable_intrs,
|
|
|
|
.abort_command = qla2x00_abort_command,
|
2008-04-03 20:13:24 +00:00
|
|
|
.target_reset = qla2x00_abort_target,
|
|
|
|
.lun_reset = qla2x00_lun_reset,
|
2007-07-19 22:06:00 +00:00
|
|
|
.fabric_login = qla2x00_login_fabric,
|
|
|
|
.fabric_logout = qla2x00_fabric_logout,
|
|
|
|
.calc_req_entries = qla2x00_calc_iocbs_32,
|
|
|
|
.build_iocbs = qla2x00_build_scsi_iocbs_32,
|
|
|
|
.prep_ms_iocb = qla2x00_prep_ms_iocb,
|
|
|
|
.prep_ms_fdmi_iocb = qla2x00_prep_ms_fdmi_iocb,
|
|
|
|
.read_nvram = qla2x00_read_nvram_data,
|
|
|
|
.write_nvram = qla2x00_write_nvram_data,
|
|
|
|
.fw_dump = qla2100_fw_dump,
|
|
|
|
.beacon_on = NULL,
|
|
|
|
.beacon_off = NULL,
|
|
|
|
.beacon_blink = NULL,
|
|
|
|
.read_optrom = qla2x00_read_optrom_data,
|
|
|
|
.write_optrom = qla2x00_write_optrom_data,
|
|
|
|
.get_flash_version = qla2x00_get_flash_version,
|
2008-11-06 18:40:51 +00:00
|
|
|
.start_scsi = qla2x00_start_scsi,
|
2008-12-18 18:06:15 +00:00
|
|
|
.wrt_req_reg = NULL,
|
|
|
|
.wrt_rsp_reg = NULL,
|
|
|
|
.rd_req_reg = NULL,
|
2007-07-19 22:06:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct isp_operations qla2300_isp_ops = {
|
|
|
|
.pci_config = qla2300_pci_config,
|
|
|
|
.reset_chip = qla2x00_reset_chip,
|
|
|
|
.chip_diag = qla2x00_chip_diag,
|
|
|
|
.config_rings = qla2x00_config_rings,
|
|
|
|
.reset_adapter = qla2x00_reset_adapter,
|
|
|
|
.nvram_config = qla2x00_nvram_config,
|
|
|
|
.update_fw_options = qla2x00_update_fw_options,
|
|
|
|
.load_risc = qla2x00_load_risc,
|
|
|
|
.pci_info_str = qla2x00_pci_info_str,
|
|
|
|
.fw_version_str = qla2x00_fw_version_str,
|
|
|
|
.intr_handler = qla2300_intr_handler,
|
|
|
|
.enable_intrs = qla2x00_enable_intrs,
|
|
|
|
.disable_intrs = qla2x00_disable_intrs,
|
|
|
|
.abort_command = qla2x00_abort_command,
|
2008-04-03 20:13:24 +00:00
|
|
|
.target_reset = qla2x00_abort_target,
|
|
|
|
.lun_reset = qla2x00_lun_reset,
|
2007-07-19 22:06:00 +00:00
|
|
|
.fabric_login = qla2x00_login_fabric,
|
|
|
|
.fabric_logout = qla2x00_fabric_logout,
|
|
|
|
.calc_req_entries = qla2x00_calc_iocbs_32,
|
|
|
|
.build_iocbs = qla2x00_build_scsi_iocbs_32,
|
|
|
|
.prep_ms_iocb = qla2x00_prep_ms_iocb,
|
|
|
|
.prep_ms_fdmi_iocb = qla2x00_prep_ms_fdmi_iocb,
|
|
|
|
.read_nvram = qla2x00_read_nvram_data,
|
|
|
|
.write_nvram = qla2x00_write_nvram_data,
|
|
|
|
.fw_dump = qla2300_fw_dump,
|
|
|
|
.beacon_on = qla2x00_beacon_on,
|
|
|
|
.beacon_off = qla2x00_beacon_off,
|
|
|
|
.beacon_blink = qla2x00_beacon_blink,
|
|
|
|
.read_optrom = qla2x00_read_optrom_data,
|
|
|
|
.write_optrom = qla2x00_write_optrom_data,
|
|
|
|
.get_flash_version = qla2x00_get_flash_version,
|
2008-11-06 18:40:51 +00:00
|
|
|
.start_scsi = qla2x00_start_scsi,
|
2008-12-18 18:06:15 +00:00
|
|
|
.wrt_req_reg = NULL,
|
|
|
|
.wrt_rsp_reg = NULL,
|
|
|
|
.rd_req_reg = NULL,
|
2007-07-19 22:06:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct isp_operations qla24xx_isp_ops = {
|
|
|
|
.pci_config = qla24xx_pci_config,
|
|
|
|
.reset_chip = qla24xx_reset_chip,
|
|
|
|
.chip_diag = qla24xx_chip_diag,
|
|
|
|
.config_rings = qla24xx_config_rings,
|
|
|
|
.reset_adapter = qla24xx_reset_adapter,
|
|
|
|
.nvram_config = qla24xx_nvram_config,
|
|
|
|
.update_fw_options = qla24xx_update_fw_options,
|
|
|
|
.load_risc = qla24xx_load_risc,
|
|
|
|
.pci_info_str = qla24xx_pci_info_str,
|
|
|
|
.fw_version_str = qla24xx_fw_version_str,
|
|
|
|
.intr_handler = qla24xx_intr_handler,
|
|
|
|
.enable_intrs = qla24xx_enable_intrs,
|
|
|
|
.disable_intrs = qla24xx_disable_intrs,
|
|
|
|
.abort_command = qla24xx_abort_command,
|
2008-04-03 20:13:24 +00:00
|
|
|
.target_reset = qla24xx_abort_target,
|
|
|
|
.lun_reset = qla24xx_lun_reset,
|
2007-07-19 22:06:00 +00:00
|
|
|
.fabric_login = qla24xx_login_fabric,
|
|
|
|
.fabric_logout = qla24xx_fabric_logout,
|
|
|
|
.calc_req_entries = NULL,
|
|
|
|
.build_iocbs = NULL,
|
|
|
|
.prep_ms_iocb = qla24xx_prep_ms_iocb,
|
|
|
|
.prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb,
|
|
|
|
.read_nvram = qla24xx_read_nvram_data,
|
|
|
|
.write_nvram = qla24xx_write_nvram_data,
|
|
|
|
.fw_dump = qla24xx_fw_dump,
|
|
|
|
.beacon_on = qla24xx_beacon_on,
|
|
|
|
.beacon_off = qla24xx_beacon_off,
|
|
|
|
.beacon_blink = qla24xx_beacon_blink,
|
|
|
|
.read_optrom = qla24xx_read_optrom_data,
|
|
|
|
.write_optrom = qla24xx_write_optrom_data,
|
|
|
|
.get_flash_version = qla24xx_get_flash_version,
|
2008-11-06 18:40:51 +00:00
|
|
|
.start_scsi = qla24xx_start_scsi,
|
2008-12-18 18:06:15 +00:00
|
|
|
.wrt_req_reg = qla24xx_wrt_req_reg,
|
|
|
|
.wrt_rsp_reg = qla24xx_wrt_rsp_reg,
|
|
|
|
.rd_req_reg = qla24xx_rd_req_reg,
|
2007-07-19 22:06:00 +00:00
|
|
|
};
|
|
|
|
|
2007-07-20 03:37:34 +00:00
|
|
|
static struct isp_operations qla25xx_isp_ops = {
|
|
|
|
.pci_config = qla25xx_pci_config,
|
|
|
|
.reset_chip = qla24xx_reset_chip,
|
|
|
|
.chip_diag = qla24xx_chip_diag,
|
|
|
|
.config_rings = qla24xx_config_rings,
|
|
|
|
.reset_adapter = qla24xx_reset_adapter,
|
|
|
|
.nvram_config = qla24xx_nvram_config,
|
|
|
|
.update_fw_options = qla24xx_update_fw_options,
|
|
|
|
.load_risc = qla24xx_load_risc,
|
|
|
|
.pci_info_str = qla24xx_pci_info_str,
|
|
|
|
.fw_version_str = qla24xx_fw_version_str,
|
|
|
|
.intr_handler = qla24xx_intr_handler,
|
|
|
|
.enable_intrs = qla24xx_enable_intrs,
|
|
|
|
.disable_intrs = qla24xx_disable_intrs,
|
|
|
|
.abort_command = qla24xx_abort_command,
|
2008-04-03 20:13:24 +00:00
|
|
|
.target_reset = qla24xx_abort_target,
|
|
|
|
.lun_reset = qla24xx_lun_reset,
|
2007-07-20 03:37:34 +00:00
|
|
|
.fabric_login = qla24xx_login_fabric,
|
|
|
|
.fabric_logout = qla24xx_fabric_logout,
|
|
|
|
.calc_req_entries = NULL,
|
|
|
|
.build_iocbs = NULL,
|
|
|
|
.prep_ms_iocb = qla24xx_prep_ms_iocb,
|
|
|
|
.prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb,
|
|
|
|
.read_nvram = qla25xx_read_nvram_data,
|
|
|
|
.write_nvram = qla25xx_write_nvram_data,
|
|
|
|
.fw_dump = qla25xx_fw_dump,
|
|
|
|
.beacon_on = qla24xx_beacon_on,
|
|
|
|
.beacon_off = qla24xx_beacon_off,
|
|
|
|
.beacon_blink = qla24xx_beacon_blink,
|
2007-09-20 21:07:33 +00:00
|
|
|
.read_optrom = qla25xx_read_optrom_data,
|
2007-07-20 03:37:34 +00:00
|
|
|
.write_optrom = qla24xx_write_optrom_data,
|
|
|
|
.get_flash_version = qla24xx_get_flash_version,
|
2008-11-06 18:40:51 +00:00
|
|
|
.start_scsi = qla24xx_start_scsi,
|
2008-12-18 18:06:15 +00:00
|
|
|
.wrt_req_reg = qla24xx_wrt_req_reg,
|
|
|
|
.wrt_rsp_reg = qla24xx_wrt_rsp_reg,
|
|
|
|
.rd_req_reg = qla24xx_rd_req_reg,
|
2007-07-20 03:37:34 +00:00
|
|
|
};
|
|
|
|
|
2006-03-09 22:27:08 +00:00
|
|
|
static inline void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_set_isp_flags(struct qla_hw_data *ha)
|
2006-03-09 22:27:08 +00:00
|
|
|
{
|
|
|
|
ha->device_type = DT_EXTENDED_IDS;
|
|
|
|
switch (ha->pdev->device) {
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2100:
|
|
|
|
ha->device_type |= DT_ISP2100;
|
|
|
|
ha->device_type &= ~DT_EXTENDED_IDS;
|
2006-05-17 22:09:34 +00:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2100;
|
2006-03-09 22:27:08 +00:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2200:
|
|
|
|
ha->device_type |= DT_ISP2200;
|
|
|
|
ha->device_type &= ~DT_EXTENDED_IDS;
|
2006-05-17 22:09:34 +00:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2100;
|
2006-03-09 22:27:08 +00:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2300:
|
|
|
|
ha->device_type |= DT_ISP2300;
|
2006-03-09 22:27:39 +00:00
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
2006-05-17 22:09:34 +00:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2300;
|
2006-03-09 22:27:08 +00:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2312:
|
|
|
|
ha->device_type |= DT_ISP2312;
|
2006-03-09 22:27:39 +00:00
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
2006-05-17 22:09:34 +00:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2300;
|
2006-03-09 22:27:08 +00:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2322:
|
|
|
|
ha->device_type |= DT_ISP2322;
|
2006-03-09 22:27:39 +00:00
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
2006-03-09 22:27:08 +00:00
|
|
|
if (ha->pdev->subsystem_vendor == 0x1028 &&
|
|
|
|
ha->pdev->subsystem_device == 0x0170)
|
|
|
|
ha->device_type |= DT_OEM_001;
|
2006-05-17 22:09:34 +00:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2300;
|
2006-03-09 22:27:08 +00:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP6312:
|
|
|
|
ha->device_type |= DT_ISP6312;
|
2006-05-17 22:09:34 +00:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2300;
|
2006-03-09 22:27:08 +00:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP6322:
|
|
|
|
ha->device_type |= DT_ISP6322;
|
2006-05-17 22:09:34 +00:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2300;
|
2006-03-09 22:27:08 +00:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2422:
|
|
|
|
ha->device_type |= DT_ISP2422;
|
2006-03-09 22:27:39 +00:00
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
2007-07-19 22:05:56 +00:00
|
|
|
ha->device_type |= DT_FWI2;
|
2007-07-19 22:05:57 +00:00
|
|
|
ha->device_type |= DT_IIDMA;
|
2006-05-17 22:09:34 +00:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2400;
|
2006-03-09 22:27:08 +00:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2432:
|
|
|
|
ha->device_type |= DT_ISP2432;
|
2006-03-09 22:27:39 +00:00
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
2007-07-19 22:05:56 +00:00
|
|
|
ha->device_type |= DT_FWI2;
|
2007-07-19 22:05:57 +00:00
|
|
|
ha->device_type |= DT_IIDMA;
|
2006-05-17 22:09:34 +00:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2400;
|
2006-03-09 22:27:08 +00:00
|
|
|
break;
|
2008-04-03 20:13:26 +00:00
|
|
|
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;
|
2006-03-09 22:27:13 +00:00
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP5422:
|
|
|
|
ha->device_type |= DT_ISP5422;
|
2007-07-19 22:05:56 +00:00
|
|
|
ha->device_type |= DT_FWI2;
|
2006-05-17 22:09:34 +00:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2400;
|
2006-03-09 22:27:08 +00:00
|
|
|
break;
|
2006-03-09 22:27:13 +00:00
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP5432:
|
|
|
|
ha->device_type |= DT_ISP5432;
|
2007-07-19 22:05:56 +00:00
|
|
|
ha->device_type |= DT_FWI2;
|
2006-05-17 22:09:34 +00:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2400;
|
2006-03-09 22:27:08 +00:00
|
|
|
break;
|
2007-07-20 03:37:34 +00:00
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2532:
|
|
|
|
ha->device_type |= DT_ISP2532;
|
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
|
|
|
ha->device_type |= DT_FWI2;
|
|
|
|
ha->device_type |= DT_IIDMA;
|
2006-05-17 22:09:34 +00:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2400;
|
2006-03-09 22:27:08 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static int
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_iospace_config(struct qla_hw_data *ha)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-01-17 17:02:09 +00:00
|
|
|
resource_size_t pio;
|
2008-12-10 00:45:39 +00:00
|
|
|
uint16_t msix;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-10-19 22:59:17 +00:00
|
|
|
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;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* We only need PIO for Flash operations on ISP2312 v2 chips. */
|
|
|
|
pio = pci_resource_start(ha->pdev, 0);
|
2008-01-17 17:02:09 +00:00
|
|
|
if (pci_resource_flags(ha->pdev, 0) & IORESOURCE_IO) {
|
|
|
|
if (pci_resource_len(ha->pdev, 0) < MIN_IOBASE_LEN) {
|
2005-04-16 22:20:36 +00:00
|
|
|
qla_printk(KERN_WARNING, ha,
|
|
|
|
"Invalid PCI I/O region size (%s)...\n",
|
|
|
|
pci_name(ha->pdev));
|
|
|
|
pio = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
qla_printk(KERN_WARNING, ha,
|
|
|
|
"region #0 not a PIO resource (%s)...\n",
|
|
|
|
pci_name(ha->pdev));
|
|
|
|
pio = 0;
|
|
|
|
}
|
2007-10-19 22:59:17 +00:00
|
|
|
ha->pio_address = pio;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-10-19 22:59:17 +00:00
|
|
|
skip_pio:
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Use MMIO operations for all accesses. */
|
2008-01-17 17:02:09 +00:00
|
|
|
if (!(pci_resource_flags(ha->pdev, 1) & IORESOURCE_MEM)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
qla_printk(KERN_ERR, ha,
|
2008-01-17 17:02:09 +00:00
|
|
|
"region #1 not an MMIO resource (%s), aborting\n",
|
2005-04-16 22:20:36 +00:00
|
|
|
pci_name(ha->pdev));
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
2008-01-17 17:02:09 +00:00
|
|
|
if (pci_resource_len(ha->pdev, 1) < MIN_IOBASE_LEN) {
|
2005-04-16 22:20:36 +00:00
|
|
|
qla_printk(KERN_ERR, ha,
|
|
|
|
"Invalid PCI mem region size (%s), aborting\n",
|
|
|
|
pci_name(ha->pdev));
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
|
|
|
|
2008-01-17 17:02:09 +00:00
|
|
|
ha->iobase = ioremap(pci_resource_start(ha->pdev, 1), MIN_IOBASE_LEN);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!ha->iobase) {
|
|
|
|
qla_printk(KERN_ERR, ha,
|
|
|
|
"cannot remap MMIO (%s), aborting\n", pci_name(ha->pdev));
|
|
|
|
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
|
|
|
|
2008-12-10 00:45:39 +00:00
|
|
|
/* Determine queue resources */
|
|
|
|
ha->max_queues = 1;
|
2008-12-18 18:06:15 +00:00
|
|
|
if (ql2xmaxqueues <= 1 || !IS_QLA25XX(ha))
|
|
|
|
goto mqiobase_exit;
|
|
|
|
ha->mqiobase = ioremap(pci_resource_start(ha->pdev, 3),
|
|
|
|
pci_resource_len(ha->pdev, 3));
|
|
|
|
if (ha->mqiobase) {
|
|
|
|
/* Read MSIX vector size of the board */
|
|
|
|
pci_read_config_word(ha->pdev, QLA_PCI_MSIX_CONTROL, &msix);
|
|
|
|
ha->msix_count = msix;
|
|
|
|
/* Max queues are bounded by available msix vectors */
|
|
|
|
/* queue 0 uses two msix vectors */
|
|
|
|
if (ha->msix_count - 1 < ql2xmaxqueues)
|
|
|
|
ha->max_queues = ha->msix_count - 1;
|
|
|
|
else if (ql2xmaxqueues > QLA_MQ_SIZE)
|
|
|
|
ha->max_queues = QLA_MQ_SIZE;
|
|
|
|
else
|
|
|
|
ha->max_queues = ql2xmaxqueues;
|
|
|
|
qla_printk(KERN_INFO, ha,
|
|
|
|
"MSI-X vector count: %d\n", msix);
|
2008-12-10 00:45:39 +00:00
|
|
|
}
|
2008-12-18 18:06:15 +00:00
|
|
|
|
|
|
|
mqiobase_exit:
|
2008-12-10 00:45:39 +00:00
|
|
|
ha->msix_count = ha->max_queues + 1;
|
2005-04-16 22:20:36 +00:00
|
|
|
return (0);
|
|
|
|
|
|
|
|
iospace_error_exit:
|
|
|
|
return (-ENOMEM);
|
|
|
|
}
|
|
|
|
|
2006-11-22 16:24:48 +00:00
|
|
|
static void
|
|
|
|
qla2xxx_scan_start(struct Scsi_Host *shost)
|
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *vha = shost_priv(shost);
|
2006-11-22 16:24:48 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
|
|
|
|
set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
|
|
|
|
set_bit(RSCN_UPDATE, &vha->dpc_flags);
|
|
|
|
set_bit(NPIV_CONFIG_NEEDED, &vha->dpc_flags);
|
2006-11-22 16:24:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
qla2xxx_scan_finished(struct Scsi_Host *shost, unsigned long time)
|
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *vha = shost_priv(shost);
|
2006-11-22 16:24:48 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (!vha->host)
|
2006-11-22 16:24:48 +00:00
|
|
|
return 1;
|
2008-11-06 18:40:51 +00:00
|
|
|
if (time > vha->hw->loop_reset_delay * HZ)
|
2006-11-22 16:24:48 +00:00
|
|
|
return 1;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
return atomic_read(&vha->loop_state) == LOOP_READY;
|
2006-11-22 16:24:48 +00:00
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* PCI driver interface
|
|
|
|
*/
|
2006-06-23 23:11:22 +00:00
|
|
|
static int __devinit
|
|
|
|
qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-06-10 00:21:28 +00:00
|
|
|
int ret = -ENODEV;
|
2005-04-16 22:20:36 +00:00
|
|
|
struct Scsi_Host *host;
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *base_vha = NULL;
|
|
|
|
struct qla_hw_data *ha;
|
2007-08-13 01:22:52 +00:00
|
|
|
char pci_info[30];
|
2005-04-16 22:20:36 +00:00
|
|
|
char fw_str[30];
|
2005-11-09 23:49:04 +00:00
|
|
|
struct scsi_host_template *sht;
|
2008-11-10 23:53:20 +00:00
|
|
|
int bars, max_id, mem_only = 0;
|
2008-11-06 18:40:51 +00:00
|
|
|
uint16_t req_length = 0, rsp_length = 0;
|
2008-12-10 00:45:39 +00:00
|
|
|
struct req_que *req = NULL;
|
|
|
|
struct rsp_que *rsp = NULL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-10-19 22:59:17 +00:00
|
|
|
bars = pci_select_bars(pdev, IORESOURCE_MEM | IORESOURCE_IO);
|
2005-11-09 23:49:04 +00:00
|
|
|
sht = &qla2x00_driver_template;
|
|
|
|
if (pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2422 ||
|
2006-12-14 03:20:29 +00:00
|
|
|
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2432 ||
|
2008-04-03 20:13:26 +00:00
|
|
|
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8432 ||
|
2006-12-14 03:20:29 +00:00
|
|
|
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5422 ||
|
2007-07-20 03:37:34 +00:00
|
|
|
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5432 ||
|
2007-10-19 22:59:17 +00:00
|
|
|
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2532) {
|
|
|
|
bars = pci_select_bars(pdev, IORESOURCE_MEM);
|
2005-11-09 23:49:04 +00:00
|
|
|
sht = &qla24xx_driver_template;
|
2007-12-20 04:28:09 +00:00
|
|
|
mem_only = 1;
|
2007-10-19 22:59:17 +00:00
|
|
|
}
|
|
|
|
|
2007-12-20 04:28:09 +00:00
|
|
|
if (mem_only) {
|
|
|
|
if (pci_enable_device_mem(pdev))
|
|
|
|
goto probe_out;
|
|
|
|
} else {
|
|
|
|
if (pci_enable_device(pdev))
|
|
|
|
goto probe_out;
|
|
|
|
}
|
2007-10-19 22:59:17 +00:00
|
|
|
|
2008-10-19 00:33:19 +00:00
|
|
|
/* This may fail but that's ok */
|
|
|
|
pci_enable_pcie_error_reporting(pdev);
|
2007-10-19 22:59:17 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
ha = kzalloc(sizeof(struct qla_hw_data), GFP_KERNEL);
|
|
|
|
if (!ha) {
|
|
|
|
DEBUG(printk("Unable to allocate memory for ha\n"));
|
|
|
|
goto probe_out;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2008-11-06 18:40:51 +00:00
|
|
|
ha->pdev = pdev;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* Clear our data area */
|
2007-10-19 22:59:17 +00:00
|
|
|
ha->bars = bars;
|
2007-12-20 04:28:09 +00:00
|
|
|
ha->mem_only = mem_only;
|
2008-01-31 20:33:46 +00:00
|
|
|
spin_lock_init(&ha->hardware_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-03-09 22:27:08 +00:00
|
|
|
/* Set ISP-type information. */
|
|
|
|
qla2x00_set_isp_flags(ha);
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Configure PCI I/O space */
|
|
|
|
ret = qla2x00_iospace_config(ha);
|
2005-06-10 00:21:28 +00:00
|
|
|
if (ret)
|
2008-11-06 18:40:51 +00:00
|
|
|
goto probe_hw_failed;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
qla_printk(KERN_INFO, ha,
|
2005-11-09 23:49:04 +00:00
|
|
|
"Found an ISP%04X, irq %d, iobase 0x%p\n", pdev->device, pdev->irq,
|
|
|
|
ha->iobase);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
ha->prev_topology = 0;
|
2005-07-06 17:31:47 +00:00
|
|
|
ha->init_cb_size = sizeof(init_cb_t);
|
2006-10-02 19:00:43 +00:00
|
|
|
ha->link_data_rate = PORT_SPEED_UNKNOWN;
|
2006-02-01 00:05:17 +00:00
|
|
|
ha->optrom_size = OPTROM_SIZE_2300;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-07-06 17:30:05 +00:00
|
|
|
/* Assign ISP specific operations. */
|
2008-11-06 18:40:51 +00:00
|
|
|
max_id = MAX_TARGETS_2200;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (IS_QLA2100(ha)) {
|
2008-11-06 18:40:51 +00:00
|
|
|
max_id = MAX_TARGETS_2100;
|
2005-04-16 22:20:36 +00:00
|
|
|
ha->mbx_count = MAILBOX_REGISTER_COUNT_2100;
|
2008-11-06 18:40:51 +00:00
|
|
|
req_length = REQUEST_ENTRY_CNT_2100;
|
|
|
|
rsp_length = RESPONSE_ENTRY_CNT_2100;
|
|
|
|
ha->max_loop_id = SNS_LAST_LOOP_ID_2100;
|
2005-07-06 17:30:05 +00:00
|
|
|
ha->gid_list_info_size = 4;
|
2007-07-19 22:06:00 +00:00
|
|
|
ha->isp_ops = &qla2100_isp_ops;
|
2005-04-16 22:20:36 +00:00
|
|
|
} else if (IS_QLA2200(ha)) {
|
|
|
|
ha->mbx_count = MAILBOX_REGISTER_COUNT;
|
2008-11-06 18:40:51 +00:00
|
|
|
req_length = REQUEST_ENTRY_CNT_2200;
|
|
|
|
rsp_length = RESPONSE_ENTRY_CNT_2100;
|
|
|
|
ha->max_loop_id = SNS_LAST_LOOP_ID_2100;
|
2005-07-06 17:30:05 +00:00
|
|
|
ha->gid_list_info_size = 4;
|
2007-07-19 22:06:00 +00:00
|
|
|
ha->isp_ops = &qla2100_isp_ops;
|
2005-07-06 17:31:47 +00:00
|
|
|
} else if (IS_QLA23XX(ha)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
ha->mbx_count = MAILBOX_REGISTER_COUNT;
|
2008-11-06 18:40:51 +00:00
|
|
|
req_length = REQUEST_ENTRY_CNT_2200;
|
|
|
|
rsp_length = RESPONSE_ENTRY_CNT_2300;
|
|
|
|
ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
|
2005-07-06 17:30:05 +00:00
|
|
|
ha->gid_list_info_size = 6;
|
2006-02-01 00:05:17 +00:00
|
|
|
if (IS_QLA2322(ha) || IS_QLA6322(ha))
|
|
|
|
ha->optrom_size = OPTROM_SIZE_2322;
|
2007-07-19 22:06:00 +00:00
|
|
|
ha->isp_ops = &qla2300_isp_ops;
|
2008-04-03 20:13:26 +00:00
|
|
|
} else if (IS_QLA24XX_TYPE(ha)) {
|
2005-07-06 17:31:47 +00:00
|
|
|
ha->mbx_count = MAILBOX_REGISTER_COUNT;
|
2008-11-06 18:40:51 +00:00
|
|
|
req_length = REQUEST_ENTRY_CNT_24XX;
|
|
|
|
rsp_length = RESPONSE_ENTRY_CNT_2300;
|
|
|
|
ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
|
2007-07-05 20:16:51 +00:00
|
|
|
ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
|
2005-07-06 17:31:47 +00:00
|
|
|
ha->gid_list_info_size = 8;
|
2006-02-01 00:05:17 +00:00
|
|
|
ha->optrom_size = OPTROM_SIZE_24XX;
|
2008-12-10 00:45:39 +00:00
|
|
|
ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA24XX;
|
2007-07-19 22:06:00 +00:00
|
|
|
ha->isp_ops = &qla24xx_isp_ops;
|
2007-07-20 03:37:34 +00:00
|
|
|
} else if (IS_QLA25XX(ha)) {
|
|
|
|
ha->mbx_count = MAILBOX_REGISTER_COUNT;
|
2008-11-06 18:40:51 +00:00
|
|
|
req_length = REQUEST_ENTRY_CNT_24XX;
|
|
|
|
rsp_length = RESPONSE_ENTRY_CNT_2300;
|
|
|
|
ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
|
2007-07-20 03:37:34 +00:00
|
|
|
ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
|
|
|
|
ha->gid_list_info_size = 8;
|
|
|
|
ha->optrom_size = OPTROM_SIZE_25XX;
|
2008-12-10 00:45:39 +00:00
|
|
|
ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
|
2007-07-20 03:37:34 +00:00
|
|
|
ha->isp_ops = &qla25xx_isp_ops;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-05-13 05:21:11 +00:00
|
|
|
mutex_init(&ha->vport_lock);
|
2008-01-17 17:02:13 +00:00
|
|
|
init_completion(&ha->mbx_cmd_comp);
|
|
|
|
complete(&ha->mbx_cmd_comp);
|
|
|
|
init_completion(&ha->mbx_intr_comp);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-07-05 20:16:51 +00:00
|
|
|
set_bit(0, (unsigned long *) ha->vp_idx_map);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-12-10 00:45:39 +00:00
|
|
|
ret = qla2x00_mem_alloc(ha, req_length, rsp_length, &req, &rsp);
|
2008-11-06 18:40:51 +00:00
|
|
|
if (!ret) {
|
2005-04-16 22:20:36 +00:00
|
|
|
qla_printk(KERN_WARNING, ha,
|
|
|
|
"[ERROR] Failed to allocate memory for adapter\n");
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
goto probe_hw_failed;
|
|
|
|
}
|
|
|
|
|
2008-12-10 00:45:39 +00:00
|
|
|
req->max_q_depth = MAX_Q_DEPTH;
|
2008-11-06 18:40:51 +00:00
|
|
|
if (ql2xmaxqdepth != 0 && ql2xmaxqdepth <= 0xffffU)
|
2008-12-10 00:45:39 +00:00
|
|
|
req->max_q_depth = ql2xmaxqdepth;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
|
|
|
|
base_vha = qla2x00_create_host(sht, ha);
|
|
|
|
if (!base_vha) {
|
|
|
|
qla_printk(KERN_WARNING, ha,
|
|
|
|
"[ERROR] Failed to allocate memory for scsi_host\n");
|
|
|
|
|
2005-06-10 00:21:28 +00:00
|
|
|
ret = -ENOMEM;
|
2008-11-06 18:40:51 +00:00
|
|
|
goto probe_hw_failed;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
pci_set_drvdata(pdev, base_vha);
|
|
|
|
|
|
|
|
qla2x00_config_dma_addressing(base_vha);
|
|
|
|
|
|
|
|
host = base_vha->host;
|
2008-12-10 00:45:39 +00:00
|
|
|
base_vha->req_ques[0] = req->id;
|
|
|
|
host->can_queue = req->length + 128;
|
|
|
|
if (IS_QLA2XXX_MIDTYPE(ha))
|
2008-11-06 18:40:51 +00:00
|
|
|
base_vha->mgmt_svr_loop_id = 10 + base_vha->vp_idx;
|
2008-12-10 00:45:39 +00:00
|
|
|
else
|
2008-11-06 18:40:51 +00:00
|
|
|
base_vha->mgmt_svr_loop_id = MANAGEMENT_SERVER +
|
|
|
|
base_vha->vp_idx;
|
|
|
|
if (IS_QLA2100(ha))
|
|
|
|
host->sg_tablesize = 32;
|
|
|
|
host->max_id = max_id;
|
|
|
|
host->this_id = 255;
|
|
|
|
host->cmd_per_lun = 3;
|
|
|
|
host->unique_id = host->host_no;
|
|
|
|
host->max_cmd_len = MAX_CMDSZ;
|
|
|
|
host->max_channel = MAX_BUSES - 1;
|
|
|
|
host->max_lun = MAX_LUNS;
|
|
|
|
host->transportt = qla2xxx_transport_template;
|
|
|
|
|
2008-12-10 00:45:39 +00:00
|
|
|
/* Set up the irqs */
|
|
|
|
ret = qla2x00_request_irqs(ha, rsp);
|
|
|
|
if (ret)
|
|
|
|
goto probe_failed;
|
|
|
|
|
|
|
|
/* Alloc arrays of request and response ring ptrs */
|
|
|
|
if (!qla2x00_alloc_queues(ha)) {
|
|
|
|
qla_printk(KERN_WARNING, ha,
|
|
|
|
"[ERROR] Failed to allocate memory for queue"
|
|
|
|
" pointers\n");
|
|
|
|
goto probe_failed;
|
|
|
|
}
|
|
|
|
ha->rsp_q_map[0] = rsp;
|
|
|
|
ha->req_q_map[0] = req;
|
|
|
|
|
2008-12-18 18:06:15 +00:00
|
|
|
if (ha->mqenable) {
|
|
|
|
ha->isp_ops->wrt_req_reg = qla25xx_wrt_req_reg;
|
|
|
|
ha->isp_ops->wrt_rsp_reg = qla25xx_wrt_rsp_reg;
|
|
|
|
ha->isp_ops->rd_req_reg = qla25xx_rd_req_reg;
|
|
|
|
}
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (qla2x00_initialize_adapter(base_vha)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
qla_printk(KERN_WARNING, ha,
|
|
|
|
"Failed to initialize adapter\n");
|
|
|
|
|
|
|
|
DEBUG2(printk("scsi(%ld): Failed to initialize adapter - "
|
|
|
|
"Adapter flags %x.\n",
|
2008-11-06 18:40:51 +00:00
|
|
|
base_vha->host_no, base_vha->device_flags));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-06-10 00:21:28 +00:00
|
|
|
ret = -ENODEV;
|
2005-04-16 22:20:36 +00:00
|
|
|
goto probe_failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Startup the kernel thread for this host adapter
|
|
|
|
*/
|
2006-02-14 17:46:22 +00:00
|
|
|
ha->dpc_thread = kthread_create(qla2x00_do_dpc, ha,
|
2008-11-06 18:40:51 +00:00
|
|
|
"%s_dpc", base_vha->host_str);
|
2006-02-14 17:46:22 +00:00
|
|
|
if (IS_ERR(ha->dpc_thread)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
qla_printk(KERN_WARNING, ha,
|
|
|
|
"Unable to start DPC thread!\n");
|
2006-02-14 17:46:22 +00:00
|
|
|
ret = PTR_ERR(ha->dpc_thread);
|
2005-04-16 22:20:36 +00:00
|
|
|
goto probe_failed;
|
|
|
|
}
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
list_add_tail(&base_vha->list, &ha->vp_list);
|
|
|
|
base_vha->host->irq = ha->pdev->irq;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* Initialized the timer */
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_start_timer(base_vha, qla2x00_timer, WATCH_INTERVAL);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
DEBUG2(printk("DEBUG: detect hba %ld at address = %p\n",
|
2008-11-06 18:40:51 +00:00
|
|
|
base_vha->host_no, ha));
|
2006-11-22 16:22:19 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
base_vha->flags.init_done = 1;
|
|
|
|
base_vha->flags.online = 1;
|
2006-11-22 16:22:19 +00:00
|
|
|
|
2005-06-10 00:21:28 +00:00
|
|
|
ret = scsi_add_host(host, &pdev->dev);
|
|
|
|
if (ret)
|
|
|
|
goto probe_failed;
|
|
|
|
|
[SCSI] qla2xxx: Defer enablement of RISC interrupts until ISP initialization completes.
Josip Rodin noted
(http://article.gmane.org/gmane.linux.ports.sparc/10152) the
driver oopsing during registration of an rport to the
FC-transport layer with a backtrace indicating a dereferencing of
an shost->shost_data equal to NULL. David Miller identified a
small window in driver logic where this could happen:
> Look at how the driver registers the IRQ handler before the host has
> been registered with the SCSI layer.
>
> That leads to a window of time where the shost hasn't been setup
> fully, yet ISRs can come in and trigger DPC thread events, such as
> loop resyncs, which expect the transport area to be setup.
>
> But it won't be setup, because scsi_add_host() hasn't finished yet.
>
> Note that in Josip's crash log, we don't even see the
>
> qla_printk(KERN_INFO, ha, "\n"
> " QLogic Fibre Channel HBA Driver: %s\n"
> " QLogic %s - %s\n"
> " ISP%04X: %s @ %s hdma%c, host#=%ld, fw=%s\n",
> ...
>
> message yet.
>
> Which means that the crash occurs between qla2x00_request_irqs()
> and printing that message.
Close this window by enabling RISC interrupts after the host has
been registered with the SCSI midlayer.
Reported-by: Josip Rodin <joy@entuzijast.net>
Cc: Stable Tree <stable@kernel.org>
Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
2008-09-12 05:19:45 +00:00
|
|
|
ha->isp_ops->enable_intrs(ha);
|
|
|
|
|
2006-11-22 16:24:48 +00:00
|
|
|
scsi_scan_host(host);
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_alloc_sysfs_attr(base_vha);
|
2005-06-10 00:21:28 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_init_host_attr(base_vha);
|
2005-06-10 00:21:28 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_dfs_setup(base_vha);
|
2008-01-17 17:02:17 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
qla_printk(KERN_INFO, ha, "\n"
|
|
|
|
" QLogic Fibre Channel HBA Driver: %s\n"
|
|
|
|
" QLogic %s - %s\n"
|
2005-11-09 23:49:04 +00:00
|
|
|
" ISP%04X: %s @ %s hdma%c, host#=%ld, fw=%s\n",
|
|
|
|
qla2x00_version_str, ha->model_number,
|
2008-11-06 18:40:51 +00:00
|
|
|
ha->model_desc ? ha->model_desc : "", pdev->device,
|
|
|
|
ha->isp_ops->pci_info_str(base_vha, pci_info), pci_name(pdev),
|
|
|
|
ha->flags.enable_64bit_addressing ? '+' : '-', base_vha->host_no,
|
|
|
|
ha->isp_ops->fw_version_str(base_vha, fw_str));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
probe_failed:
|
2008-12-10 00:45:39 +00:00
|
|
|
qla2x00_free_que(ha, req, rsp);
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_free_device(base_vha);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_host_put(base_vha->host);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
probe_hw_failed:
|
|
|
|
if (ha->iobase)
|
|
|
|
iounmap(ha->iobase);
|
|
|
|
|
|
|
|
pci_release_selected_regions(ha->pdev, ha->bars);
|
|
|
|
kfree(ha);
|
|
|
|
ha = NULL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-06-10 00:21:28 +00:00
|
|
|
probe_out:
|
2008-11-06 18:40:51 +00:00
|
|
|
pci_disable_device(pdev);
|
2005-06-10 00:21:28 +00:00
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-01-14 08:55:16 +00:00
|
|
|
static void
|
2006-06-23 23:11:22 +00:00
|
|
|
qla2x00_remove_one(struct pci_dev *pdev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *base_vha, *vha, *temp;
|
|
|
|
struct qla_hw_data *ha;
|
|
|
|
|
|
|
|
base_vha = pci_get_drvdata(pdev);
|
|
|
|
ha = base_vha->hw;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(vha, temp, &ha->vp_list, list) {
|
|
|
|
if (vha && vha->fc_vport)
|
|
|
|
fc_vport_terminate(vha->fc_vport);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
set_bit(UNLOADING, &base_vha->dpc_flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_dfs_remove(base_vha);
|
2008-08-14 04:37:01 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla84xx_put_chip(base_vha);
|
2008-08-14 04:37:01 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_free_sysfs_attr(base_vha);
|
2008-01-17 17:02:17 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
fc_remove_host(base_vha->host);
|
2008-04-03 20:13:26 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_remove_host(base_vha->host);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_free_device(base_vha);
|
2005-04-17 20:06:53 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_host_put(base_vha->host);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (ha->iobase)
|
|
|
|
iounmap(ha->iobase);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-12-10 00:45:39 +00:00
|
|
|
if (ha->mqiobase)
|
|
|
|
iounmap(ha->mqiobase);
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
pci_release_selected_regions(ha->pdev, ha->bars);
|
|
|
|
kfree(ha);
|
|
|
|
ha = NULL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-03-27 22:49:49 +00:00
|
|
|
pci_disable_device(pdev);
|
2005-04-16 22:20:36 +00:00
|
|
|
pci_set_drvdata(pdev, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_free_device(scsi_qla_host_t *vha)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
qla2x00_abort_all_cmds(vha, DID_NO_CONNECT << 16);
|
2008-01-31 20:33:46 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Disable timer */
|
2008-11-06 18:40:51 +00:00
|
|
|
if (vha->timer_active)
|
|
|
|
qla2x00_stop_timer(vha);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
vha->flags.online = 0;
|
2008-01-31 20:33:46 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Kill the kernel thread for this host */
|
2006-02-14 17:46:22 +00:00
|
|
|
if (ha->dpc_thread) {
|
|
|
|
struct task_struct *t = ha->dpc_thread;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-02-14 17:46:22 +00:00
|
|
|
/*
|
|
|
|
* qla2xxx_wake_dpc checks for ->dpc_thread
|
|
|
|
* so we need to zero it out.
|
|
|
|
*/
|
|
|
|
ha->dpc_thread = NULL;
|
|
|
|
kthread_stop(t);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-01-17 17:02:17 +00:00
|
|
|
if (ha->flags.fce_enabled)
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_disable_fce_trace(vha, NULL, NULL);
|
2008-01-17 17:02:17 +00:00
|
|
|
|
2006-06-23 23:10:29 +00:00
|
|
|
if (ha->eft)
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_disable_eft_trace(vha);
|
2006-06-23 23:10:29 +00:00
|
|
|
|
2005-08-27 02:10:20 +00:00
|
|
|
/* Stop currently executing firmware. */
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_try_to_stop_firmware(vha);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-08-27 02:10:20 +00:00
|
|
|
/* turn-off interrupts on the card */
|
|
|
|
if (ha->interrupts_on)
|
2007-07-19 22:06:00 +00:00
|
|
|
ha->isp_ops->disable_intrs(ha);
|
2005-08-27 02:10:20 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_free_irqs(vha);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_mem_free(ha);
|
2008-12-10 00:45:39 +00:00
|
|
|
|
|
|
|
qla2x00_free_queues(ha);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2006-01-20 22:53:13 +00:00
|
|
|
static inline void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport,
|
2006-01-20 22:53:13 +00:00
|
|
|
int defer)
|
|
|
|
{
|
|
|
|
struct fc_rport *rport;
|
|
|
|
|
|
|
|
if (!fcport->rport)
|
|
|
|
return;
|
|
|
|
|
|
|
|
rport = fcport->rport;
|
|
|
|
if (defer) {
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_lock_irq(vha->host->host_lock);
|
2006-01-20 22:53:13 +00:00
|
|
|
fcport->drport = rport;
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_unlock_irq(vha->host->host_lock);
|
|
|
|
set_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags);
|
|
|
|
qla2xxx_wake_dpc(vha);
|
2008-07-10 23:55:47 +00:00
|
|
|
} else
|
2006-01-20 22:53:13 +00:00
|
|
|
fc_remote_port_delete(rport);
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* qla2x00_mark_device_lost Updates fcport state when device goes offline.
|
|
|
|
*
|
|
|
|
* Input: ha = adapter block pointer. fcport = port structure pointer.
|
|
|
|
*
|
|
|
|
* Return: None.
|
|
|
|
*
|
|
|
|
* Context:
|
|
|
|
*/
|
2008-11-06 18:40:51 +00:00
|
|
|
void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport,
|
2006-01-20 22:53:13 +00:00
|
|
|
int do_login, int defer)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2007-07-05 20:16:51 +00:00
|
|
|
if (atomic_read(&fcport->state) == FCS_ONLINE &&
|
2008-11-06 18:40:51 +00:00
|
|
|
vha->vp_idx == fcport->vp_idx) {
|
|
|
|
atomic_set(&fcport->state, FCS_DEVICE_LOST);
|
|
|
|
qla2x00_schedule_rport_del(vha, fcport, defer);
|
|
|
|
}
|
2005-07-06 17:32:07 +00:00
|
|
|
/*
|
2005-04-16 22:20:36 +00:00
|
|
|
* We may need to retry the login, so don't change the state of the
|
|
|
|
* port but do the retries.
|
|
|
|
*/
|
|
|
|
if (atomic_read(&fcport->state) != FCS_DEVICE_DEAD)
|
|
|
|
atomic_set(&fcport->state, FCS_DEVICE_LOST);
|
|
|
|
|
|
|
|
if (!do_login)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (fcport->login_retry == 0) {
|
2008-11-06 18:40:51 +00:00
|
|
|
fcport->login_retry = vha->hw->login_retry_count;
|
|
|
|
set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
DEBUG(printk("scsi(%ld): Port login retry: "
|
|
|
|
"%02x%02x%02x%02x%02x%02x%02x%02x, "
|
|
|
|
"id = 0x%04x retry cnt=%d\n",
|
2008-11-06 18:40:51 +00:00
|
|
|
vha->host_no,
|
2005-04-16 22:20:36 +00:00
|
|
|
fcport->port_name[0],
|
|
|
|
fcport->port_name[1],
|
|
|
|
fcport->port_name[2],
|
|
|
|
fcport->port_name[3],
|
|
|
|
fcport->port_name[4],
|
|
|
|
fcport->port_name[5],
|
|
|
|
fcport->port_name[6],
|
|
|
|
fcport->port_name[7],
|
|
|
|
fcport->loop_id,
|
|
|
|
fcport->login_retry));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qla2x00_mark_all_devices_lost
|
|
|
|
* Updates fcport state when device goes offline.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* ha = adapter block pointer.
|
|
|
|
* fcport = port structure pointer.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Context:
|
|
|
|
*/
|
|
|
|
void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
fc_port_t *fcport;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
list_for_each_entry(fcport, &vha->vp_fcports, list) {
|
|
|
|
if (vha->vp_idx != fcport->vp_idx)
|
2005-04-16 22:20:36 +00:00
|
|
|
continue;
|
|
|
|
/*
|
|
|
|
* No point in marking the device as lost, if the device is
|
|
|
|
* already DEAD.
|
|
|
|
*/
|
|
|
|
if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD)
|
|
|
|
continue;
|
2008-11-06 18:40:51 +00:00
|
|
|
if (atomic_read(&fcport->state) == FCS_ONLINE) {
|
|
|
|
atomic_set(&fcport->state, FCS_DEVICE_LOST);
|
|
|
|
qla2x00_schedule_rport_del(vha, fcport, defer);
|
|
|
|
} else
|
|
|
|
atomic_set(&fcport->state, FCS_DEVICE_LOST);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qla2x00_mem_alloc
|
|
|
|
* Allocates adapter memory.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* 0 = success.
|
2008-01-31 20:33:48 +00:00
|
|
|
* !0 = failure.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2008-01-31 20:33:48 +00:00
|
|
|
static int
|
2008-12-10 00:45:39 +00:00
|
|
|
qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
|
|
|
|
struct req_que **req, struct rsp_que **rsp)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
char name[16];
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
ha->init_cb_size = sizeof(init_cb_t);
|
|
|
|
if (IS_QLA2XXX_MIDTYPE(ha))
|
|
|
|
ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
|
2008-01-31 20:33:48 +00:00
|
|
|
|
|
|
|
ha->init_cb = dma_alloc_coherent(&ha->pdev->dev, ha->init_cb_size,
|
2008-11-06 18:40:51 +00:00
|
|
|
&ha->init_cb_dma, GFP_KERNEL);
|
2008-01-31 20:33:48 +00:00
|
|
|
if (!ha->init_cb)
|
2008-11-06 18:40:51 +00:00
|
|
|
goto fail;
|
2008-01-31 20:33:48 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
ha->gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE,
|
|
|
|
&ha->gid_list_dma, GFP_KERNEL);
|
|
|
|
if (!ha->gid_list)
|
2008-01-31 20:33:48 +00:00
|
|
|
goto fail_free_init_cb;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-01-31 20:33:48 +00:00
|
|
|
ha->srb_mempool = mempool_create_slab_pool(SRB_MIN_REQ, srb_cachep);
|
|
|
|
if (!ha->srb_mempool)
|
2008-11-06 18:40:51 +00:00
|
|
|
goto fail_free_gid_list;
|
2008-01-31 20:33:48 +00:00
|
|
|
|
|
|
|
/* Get memory for cached NVRAM */
|
|
|
|
ha->nvram = kzalloc(MAX_NVRAM_SIZE, GFP_KERNEL);
|
|
|
|
if (!ha->nvram)
|
|
|
|
goto fail_free_srb_mempool;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
snprintf(name, sizeof(name), "%s_%d", QLA2XXX_DRIVER_NAME,
|
|
|
|
ha->pdev->device);
|
|
|
|
ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev,
|
|
|
|
DMA_POOL_SIZE, 8, 0);
|
|
|
|
if (!ha->s_dma_pool)
|
|
|
|
goto fail_free_nvram;
|
|
|
|
|
2008-01-31 20:33:48 +00:00
|
|
|
/* Allocate memory for SNS commands */
|
|
|
|
if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
|
2008-11-06 18:40:51 +00:00
|
|
|
/* Get consistent memory allocated for SNS commands */
|
2008-01-31 20:33:48 +00:00
|
|
|
ha->sns_cmd = dma_alloc_coherent(&ha->pdev->dev,
|
2008-11-06 18:40:51 +00:00
|
|
|
sizeof(struct sns_cmd_pkt), &ha->sns_cmd_dma, GFP_KERNEL);
|
2008-01-31 20:33:48 +00:00
|
|
|
if (!ha->sns_cmd)
|
2008-11-06 18:40:51 +00:00
|
|
|
goto fail_dma_pool;
|
2008-01-31 20:33:48 +00:00
|
|
|
} else {
|
2008-11-06 18:40:51 +00:00
|
|
|
/* Get consistent memory allocated for MS IOCB */
|
2008-01-31 20:33:48 +00:00
|
|
|
ha->ms_iocb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
|
2008-11-06 18:40:51 +00:00
|
|
|
&ha->ms_iocb_dma);
|
2008-01-31 20:33:48 +00:00
|
|
|
if (!ha->ms_iocb)
|
2008-11-06 18:40:51 +00:00
|
|
|
goto fail_dma_pool;
|
|
|
|
/* Get consistent memory allocated for CT SNS commands */
|
2008-01-31 20:33:48 +00:00
|
|
|
ha->ct_sns = dma_alloc_coherent(&ha->pdev->dev,
|
2008-11-06 18:40:51 +00:00
|
|
|
sizeof(struct ct_sns_pkt), &ha->ct_sns_dma, GFP_KERNEL);
|
2008-01-31 20:33:48 +00:00
|
|
|
if (!ha->ct_sns)
|
|
|
|
goto fail_free_ms_iocb;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
/* Allocate memory for request ring */
|
2008-12-10 00:45:39 +00:00
|
|
|
*req = kzalloc(sizeof(struct req_que), GFP_KERNEL);
|
|
|
|
if (!*req) {
|
2008-11-06 18:40:51 +00:00
|
|
|
DEBUG(printk("Unable to allocate memory for req\n"));
|
|
|
|
goto fail_req;
|
|
|
|
}
|
2008-12-10 00:45:39 +00:00
|
|
|
(*req)->length = req_len;
|
|
|
|
(*req)->ring = dma_alloc_coherent(&ha->pdev->dev,
|
|
|
|
((*req)->length + 1) * sizeof(request_t),
|
|
|
|
&(*req)->dma, GFP_KERNEL);
|
|
|
|
if (!(*req)->ring) {
|
2008-11-06 18:40:51 +00:00
|
|
|
DEBUG(printk("Unable to allocate memory for req_ring\n"));
|
|
|
|
goto fail_req_ring;
|
|
|
|
}
|
|
|
|
/* Allocate memory for response ring */
|
2008-12-10 00:45:39 +00:00
|
|
|
*rsp = kzalloc(sizeof(struct rsp_que), GFP_KERNEL);
|
|
|
|
if (!*rsp) {
|
|
|
|
qla_printk(KERN_WARNING, ha,
|
|
|
|
"Unable to allocate memory for rsp\n");
|
2008-11-06 18:40:51 +00:00
|
|
|
goto fail_rsp;
|
|
|
|
}
|
2008-12-10 00:45:39 +00:00
|
|
|
(*rsp)->hw = ha;
|
|
|
|
(*rsp)->length = rsp_len;
|
|
|
|
(*rsp)->ring = dma_alloc_coherent(&ha->pdev->dev,
|
|
|
|
((*rsp)->length + 1) * sizeof(response_t),
|
|
|
|
&(*rsp)->dma, GFP_KERNEL);
|
|
|
|
if (!(*rsp)->ring) {
|
|
|
|
qla_printk(KERN_WARNING, ha,
|
|
|
|
"Unable to allocate memory for rsp_ring\n");
|
2008-11-06 18:40:51 +00:00
|
|
|
goto fail_rsp_ring;
|
|
|
|
}
|
2008-12-10 00:45:39 +00:00
|
|
|
(*req)->rsp = *rsp;
|
|
|
|
(*rsp)->req = *req;
|
|
|
|
/* Allocate memory for NVRAM data for vports */
|
|
|
|
if (ha->nvram_npiv_size) {
|
|
|
|
ha->npiv_info = kzalloc(sizeof(struct qla_npiv_entry) *
|
|
|
|
ha->nvram_npiv_size, GFP_KERNEL);
|
|
|
|
if (!ha->npiv_info) {
|
|
|
|
qla_printk(KERN_WARNING, ha,
|
|
|
|
"Unable to allocate memory for npiv info\n");
|
|
|
|
goto fail_npiv_info;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
ha->npiv_info = NULL;
|
2008-01-31 20:33:48 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
INIT_LIST_HEAD(&ha->vp_list);
|
|
|
|
return 1;
|
|
|
|
|
2008-12-10 00:45:39 +00:00
|
|
|
fail_npiv_info:
|
|
|
|
dma_free_coherent(&ha->pdev->dev, ((*rsp)->length + 1) *
|
|
|
|
sizeof(response_t), (*rsp)->ring, (*rsp)->dma);
|
|
|
|
(*rsp)->ring = NULL;
|
|
|
|
(*rsp)->dma = 0;
|
2008-11-06 18:40:51 +00:00
|
|
|
fail_rsp_ring:
|
2008-12-10 00:45:39 +00:00
|
|
|
kfree(*rsp);
|
2008-11-06 18:40:51 +00:00
|
|
|
fail_rsp:
|
2008-12-10 00:45:39 +00:00
|
|
|
dma_free_coherent(&ha->pdev->dev, ((*req)->length + 1) *
|
|
|
|
sizeof(request_t), (*req)->ring, (*req)->dma);
|
|
|
|
(*req)->ring = NULL;
|
|
|
|
(*req)->dma = 0;
|
2008-11-06 18:40:51 +00:00
|
|
|
fail_req_ring:
|
2008-12-10 00:45:39 +00:00
|
|
|
kfree(*req);
|
2008-11-06 18:40:51 +00:00
|
|
|
fail_req:
|
|
|
|
dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt),
|
|
|
|
ha->ct_sns, ha->ct_sns_dma);
|
|
|
|
ha->ct_sns = NULL;
|
|
|
|
ha->ct_sns_dma = 0;
|
2008-01-31 20:33:48 +00:00
|
|
|
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;
|
2008-11-06 18:40:51 +00:00
|
|
|
fail_dma_pool:
|
|
|
|
dma_pool_destroy(ha->s_dma_pool);
|
|
|
|
ha->s_dma_pool = NULL;
|
2008-01-31 20:33:48 +00:00
|
|
|
fail_free_nvram:
|
|
|
|
kfree(ha->nvram);
|
|
|
|
ha->nvram = NULL;
|
|
|
|
fail_free_srb_mempool:
|
|
|
|
mempool_destroy(ha->srb_mempool);
|
|
|
|
ha->srb_mempool = NULL;
|
|
|
|
fail_free_gid_list:
|
|
|
|
dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list,
|
2008-11-06 18:40:51 +00:00
|
|
|
ha->gid_list_dma);
|
2008-01-31 20:33:48 +00:00
|
|
|
ha->gid_list = NULL;
|
|
|
|
ha->gid_list_dma = 0;
|
2008-11-06 18:40:51 +00:00
|
|
|
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;
|
2008-01-31 20:33:48 +00:00
|
|
|
fail:
|
2008-11-06 18:40:51 +00:00
|
|
|
DEBUG(printk("%s: Memory allocation failure\n", __func__));
|
2008-01-31 20:33:48 +00:00
|
|
|
return -ENOMEM;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qla2x00_mem_free
|
|
|
|
* Frees all adapter allocated memory.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* ha = adapter block pointer.
|
|
|
|
*/
|
2008-01-17 17:02:15 +00:00
|
|
|
static void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_mem_free(struct qla_hw_data *ha)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-01-31 20:33:48 +00:00
|
|
|
if (ha->srb_mempool)
|
|
|
|
mempool_destroy(ha->srb_mempool);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-01-17 17:02:17 +00:00
|
|
|
if (ha->fce)
|
|
|
|
dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce,
|
2008-11-06 18:40:51 +00:00
|
|
|
ha->fce_dma);
|
2008-01-17 17:02:17 +00:00
|
|
|
|
2006-06-23 23:10:29 +00:00
|
|
|
if (ha->fw_dump) {
|
|
|
|
if (ha->eft)
|
|
|
|
dma_free_coherent(&ha->pdev->dev,
|
2008-11-06 18:40:51 +00:00
|
|
|
ntohl(ha->fw_dump->eft_size), ha->eft, ha->eft_dma);
|
2006-06-23 23:10:29 +00:00
|
|
|
vfree(ha->fw_dump);
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (ha->sns_cmd)
|
|
|
|
dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt),
|
2008-11-06 18:40:51 +00:00
|
|
|
ha->sns_cmd, ha->sns_cmd_dma);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (ha->ct_sns)
|
|
|
|
dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt),
|
2008-11-06 18:40:51 +00:00
|
|
|
ha->ct_sns, ha->ct_sns_dma);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-06-23 23:10:50 +00:00
|
|
|
if (ha->sfp_data)
|
|
|
|
dma_pool_free(ha->s_dma_pool, ha->sfp_data, ha->sfp_data_dma);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (ha->ms_iocb)
|
|
|
|
dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
|
|
|
|
|
|
|
|
if (ha->s_dma_pool)
|
|
|
|
dma_pool_destroy(ha->s_dma_pool);
|
|
|
|
|
2007-07-05 20:16:51 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (ha->gid_list)
|
|
|
|
dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list,
|
2008-11-06 18:40:51 +00:00
|
|
|
ha->gid_list_dma);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (ha->init_cb)
|
|
|
|
dma_free_coherent(&ha->pdev->dev, ha->init_cb_size,
|
|
|
|
ha->init_cb, ha->init_cb_dma);
|
|
|
|
vfree(ha->optrom_buffer);
|
|
|
|
kfree(ha->nvram);
|
2008-12-10 00:45:39 +00:00
|
|
|
kfree(ha->npiv_info);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-01-31 20:33:48 +00:00
|
|
|
ha->srb_mempool = NULL;
|
2006-06-23 23:10:29 +00:00
|
|
|
ha->eft = NULL;
|
|
|
|
ha->eft_dma = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
ha->sns_cmd = NULL;
|
|
|
|
ha->sns_cmd_dma = 0;
|
|
|
|
ha->ct_sns = NULL;
|
|
|
|
ha->ct_sns_dma = 0;
|
|
|
|
ha->ms_iocb = NULL;
|
|
|
|
ha->ms_iocb_dma = 0;
|
|
|
|
ha->init_cb = NULL;
|
|
|
|
ha->init_cb_dma = 0;
|
|
|
|
|
|
|
|
ha->s_dma_pool = NULL;
|
|
|
|
|
|
|
|
ha->gid_list = NULL;
|
|
|
|
ha->gid_list_dma = 0;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
ha->fw_dump = NULL;
|
|
|
|
ha->fw_dumped = 0;
|
|
|
|
ha->fw_dump_reading = 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht,
|
|
|
|
struct qla_hw_data *ha)
|
|
|
|
{
|
|
|
|
struct Scsi_Host *host;
|
|
|
|
struct scsi_qla_host *vha = NULL;
|
2006-02-01 00:05:17 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
host = scsi_host_alloc(sht, sizeof(scsi_qla_host_t));
|
|
|
|
if (host == NULL) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"qla2xxx: Couldn't allocate host from scsi layer!\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear our data area */
|
|
|
|
vha = shost_priv(host);
|
|
|
|
memset(vha, 0, sizeof(scsi_qla_host_t));
|
|
|
|
|
|
|
|
vha->host = host;
|
|
|
|
vha->host_no = host->host_no;
|
|
|
|
vha->hw = ha;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&vha->vp_fcports);
|
|
|
|
INIT_LIST_HEAD(&vha->work_list);
|
|
|
|
INIT_LIST_HEAD(&vha->list);
|
|
|
|
|
|
|
|
sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no);
|
|
|
|
return vha;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
return vha;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-04-24 22:21:27 +00:00
|
|
|
static struct qla_work_evt *
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type,
|
2008-04-03 20:13:18 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-04-24 22:21:27 +00:00
|
|
|
static int
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e, int locked)
|
2008-04-03 20:13:18 +00:00
|
|
|
{
|
2008-07-24 15:31:48 +00:00
|
|
|
unsigned long uninitialized_var(flags);
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2008-04-03 20:13:18 +00:00
|
|
|
|
|
|
|
if (!locked)
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
list_add_tail(&e->list, &vha->work_list);
|
|
|
|
qla2xxx_wake_dpc(vha);
|
2008-04-03 20:13:18 +00:00
|
|
|
if (!locked)
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
2008-04-03 20:13:18 +00:00
|
|
|
return QLA_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_post_aen_work(struct scsi_qla_host *vha, enum fc_host_event_code code,
|
2008-04-03 20:13:18 +00:00
|
|
|
u32 data)
|
|
|
|
{
|
|
|
|
struct qla_work_evt *e;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
e = qla2x00_alloc_work(vha, QLA_EVT_AEN, 1);
|
2008-04-03 20:13:18 +00:00
|
|
|
if (!e)
|
|
|
|
return QLA_FUNCTION_FAILED;
|
|
|
|
|
|
|
|
e->u.aen.code = code;
|
|
|
|
e->u.aen.data = data;
|
2008-11-06 18:40:51 +00:00
|
|
|
return qla2x00_post_work(vha, e, 1);
|
2008-04-03 20:13:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_do_work(struct scsi_qla_host *vha)
|
2008-04-03 20:13:18 +00:00
|
|
|
{
|
|
|
|
struct qla_work_evt *e;
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2008-04-03 20:13:18 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_lock_irq(&ha->hardware_lock);
|
|
|
|
while (!list_empty(&vha->work_list)) {
|
|
|
|
e = list_entry(vha->work_list.next, struct qla_work_evt, list);
|
2008-04-03 20:13:18 +00:00
|
|
|
list_del_init(&e->list);
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_unlock_irq(&ha->hardware_lock);
|
2008-04-03 20:13:18 +00:00
|
|
|
|
|
|
|
switch (e->type) {
|
|
|
|
case QLA_EVT_AEN:
|
2008-11-06 18:40:51 +00:00
|
|
|
fc_host_post_event(vha->host, fc_get_event_number(),
|
2008-04-03 20:13:18 +00:00
|
|
|
e->u.aen.code, e->u.aen.data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (e->flags & QLA_EVT_FLAG_FREE)
|
|
|
|
kfree(e);
|
2008-11-06 18:40:51 +00:00
|
|
|
spin_lock_irq(&ha->hardware_lock);
|
|
|
|
}
|
|
|
|
spin_unlock_irq(&ha->hardware_lock);
|
|
|
|
}
|
|
|
|
/* Relogins all the fcports of a vport
|
|
|
|
* Context: dpc thread
|
|
|
|
*/
|
|
|
|
void qla2x00_relogin(struct scsi_qla_host *vha)
|
|
|
|
{
|
|
|
|
fc_port_t *fcport;
|
|
|
|
uint8_t status;
|
|
|
|
uint16_t next_loopid = 0;
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
|
|
|
|
list_for_each_entry(fcport, &vha->vp_fcports, list) {
|
|
|
|
/*
|
|
|
|
* If the port is not ONLINE then try to login
|
|
|
|
* to it if we haven't run out of retries.
|
|
|
|
*/
|
|
|
|
if (atomic_read(&fcport->state) !=
|
|
|
|
FCS_ONLINE && fcport->login_retry) {
|
|
|
|
|
|
|
|
if (fcport->flags & FCF_FABRIC_DEVICE) {
|
|
|
|
if (fcport->flags & FCF_TAPE_PRESENT)
|
|
|
|
ha->isp_ops->fabric_logout(vha,
|
|
|
|
fcport->loop_id,
|
|
|
|
fcport->d_id.b.domain,
|
|
|
|
fcport->d_id.b.area,
|
|
|
|
fcport->d_id.b.al_pa);
|
|
|
|
|
|
|
|
status = qla2x00_fabric_login(vha, fcport,
|
|
|
|
&next_loopid);
|
|
|
|
} else
|
|
|
|
status = qla2x00_local_device_login(vha,
|
|
|
|
fcport);
|
|
|
|
|
|
|
|
fcport->login_retry--;
|
|
|
|
if (status == QLA_SUCCESS) {
|
|
|
|
fcport->old_loop_id = fcport->loop_id;
|
|
|
|
|
|
|
|
DEBUG(printk("scsi(%ld): port login OK: logged "
|
|
|
|
"in ID 0x%x\n", vha->host_no, fcport->loop_id));
|
|
|
|
|
|
|
|
qla2x00_update_fcport(vha, fcport);
|
|
|
|
|
|
|
|
} else if (status == 1) {
|
|
|
|
set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
|
|
|
|
/* retry the login again */
|
|
|
|
DEBUG(printk("scsi(%ld): Retrying"
|
|
|
|
" %d login again loop_id 0x%x\n",
|
|
|
|
vha->host_no, fcport->login_retry,
|
|
|
|
fcport->loop_id));
|
|
|
|
} 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, &vha->dpc_flags))
|
|
|
|
break;
|
2008-04-03 20:13:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/**************************************************************************
|
|
|
|
* qla2x00_do_dpc
|
|
|
|
* This kernel thread is a task that is schedule by the interrupt handler
|
|
|
|
* to perform the background processing for interrupts.
|
|
|
|
*
|
|
|
|
* Notes:
|
|
|
|
* This task always run in the context of a kernel thread. It
|
|
|
|
* is kick-off by the driver's detect code and starts up
|
|
|
|
* up one per adapter. It immediately goes to sleep and waits for
|
|
|
|
* some fibre event. When either the interrupt handler or
|
|
|
|
* the timer routine detects a event it will one of the task
|
|
|
|
* bits then wake us up.
|
|
|
|
**************************************************************************/
|
|
|
|
static int
|
|
|
|
qla2x00_do_dpc(void *data)
|
|
|
|
{
|
2007-07-05 20:16:51 +00:00
|
|
|
int rval;
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *base_vha;
|
|
|
|
struct qla_hw_data *ha;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
ha = (struct qla_hw_data *)data;
|
|
|
|
base_vha = pci_get_drvdata(ha->pdev);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
set_user_nice(current, -20);
|
|
|
|
|
2006-02-14 17:46:22 +00:00
|
|
|
while (!kthread_should_stop()) {
|
2005-04-16 22:20:36 +00:00
|
|
|
DEBUG3(printk("qla2x00: DPC handler sleeping\n"));
|
|
|
|
|
2006-02-14 17:46:22 +00:00
|
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
|
|
schedule();
|
|
|
|
__set_current_state(TASK_RUNNING);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
DEBUG3(printk("qla2x00: DPC handler waking up\n"));
|
|
|
|
|
|
|
|
/* Initialization not yet finished. Don't do anything yet. */
|
2008-11-06 18:40:51 +00:00
|
|
|
if (!base_vha->flags.init_done)
|
2005-04-16 22:20:36 +00:00
|
|
|
continue;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
DEBUG3(printk("scsi(%ld): DPC handler\n", base_vha->host_no));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
ha->dpc_active = 1;
|
|
|
|
|
|
|
|
if (ha->flags.mbox_busy) {
|
|
|
|
ha->dpc_active = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_do_work(base_vha);
|
2008-04-03 20:13:18 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (test_and_clear_bit(ISP_ABORT_NEEDED,
|
|
|
|
&base_vha->dpc_flags)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
DEBUG(printk("scsi(%ld): dpc: sched "
|
|
|
|
"qla2x00_abort_isp ha = %p\n",
|
2008-11-06 18:40:51 +00:00
|
|
|
base_vha->host_no, ha));
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!(test_and_set_bit(ABORT_ISP_ACTIVE,
|
2008-11-06 18:40:51 +00:00
|
|
|
&base_vha->dpc_flags))) {
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (qla2x00_abort_isp(base_vha)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
/* failed. retry later */
|
|
|
|
set_bit(ISP_ABORT_NEEDED,
|
2008-11-06 18:40:51 +00:00
|
|
|
&base_vha->dpc_flags);
|
2008-01-31 20:33:51 +00:00
|
|
|
}
|
2008-11-06 18:40:51 +00:00
|
|
|
clear_bit(ABORT_ISP_ACTIVE,
|
|
|
|
&base_vha->dpc_flags);
|
2008-01-31 20:33:51 +00:00
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
DEBUG(printk("scsi(%ld): dpc: qla2x00_abort_isp end\n",
|
2008-11-06 18:40:51 +00:00
|
|
|
base_vha->host_no));
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (test_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags)) {
|
|
|
|
qla2x00_update_fcports(base_vha);
|
|
|
|
clear_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags);
|
2008-07-24 15:31:49 +00:00
|
|
|
}
|
2006-01-20 22:53:13 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (test_and_clear_bit(RESET_MARKER_NEEDED,
|
|
|
|
&base_vha->dpc_flags) &&
|
|
|
|
(!(test_and_set_bit(RESET_ACTIVE, &base_vha->dpc_flags)))) {
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
DEBUG(printk("scsi(%ld): qla2x00_reset_marker()\n",
|
2008-11-06 18:40:51 +00:00
|
|
|
base_vha->host_no));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_rst_aen(base_vha);
|
|
|
|
clear_bit(RESET_ACTIVE, &base_vha->dpc_flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Retry each device up to login retry count */
|
2008-11-06 18:40:51 +00:00
|
|
|
if ((test_and_clear_bit(RELOGIN_NEEDED,
|
|
|
|
&base_vha->dpc_flags)) &&
|
|
|
|
!test_bit(LOOP_RESYNC_NEEDED, &base_vha->dpc_flags) &&
|
|
|
|
atomic_read(&base_vha->loop_state) != LOOP_DOWN) {
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
DEBUG(printk("scsi(%ld): qla2x00_port_login()\n",
|
2008-11-06 18:40:51 +00:00
|
|
|
base_vha->host_no));
|
|
|
|
qla2x00_relogin(base_vha);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
DEBUG(printk("scsi(%ld): qla2x00_port_login - end\n",
|
2008-11-06 18:40:51 +00:00
|
|
|
base_vha->host_no));
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (test_and_clear_bit(LOOP_RESYNC_NEEDED,
|
|
|
|
&base_vha->dpc_flags)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
DEBUG(printk("scsi(%ld): qla2x00_loop_resync()\n",
|
2008-11-06 18:40:51 +00:00
|
|
|
base_vha->host_no));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE,
|
2008-11-06 18:40:51 +00:00
|
|
|
&base_vha->dpc_flags))) {
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
rval = qla2x00_loop_resync(base_vha);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
clear_bit(LOOP_RESYNC_ACTIVE,
|
|
|
|
&base_vha->dpc_flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG(printk("scsi(%ld): qla2x00_loop_resync - end\n",
|
2008-11-06 18:40:51 +00:00
|
|
|
base_vha->host_no));
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (test_bit(NPIV_CONFIG_NEEDED, &base_vha->dpc_flags) &&
|
|
|
|
atomic_read(&base_vha->loop_state) == LOOP_READY) {
|
|
|
|
clear_bit(NPIV_CONFIG_NEEDED, &base_vha->dpc_flags);
|
|
|
|
qla2xxx_flash_npiv_conf(base_vha);
|
2008-09-12 04:22:50 +00:00
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!ha->interrupts_on)
|
2007-07-19 22:06:00 +00:00
|
|
|
ha->isp_ops->enable_intrs(ha);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (test_and_clear_bit(BEACON_BLINK_NEEDED,
|
|
|
|
&base_vha->dpc_flags))
|
|
|
|
ha->isp_ops->beacon_blink(base_vha);
|
2006-02-01 00:05:07 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_do_dpc_all_vps(base_vha);
|
2007-07-05 20:16:51 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
ha->dpc_active = 0;
|
|
|
|
} /* End of while(1) */
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
DEBUG(printk("scsi(%ld): DPC handler exiting\n", base_vha->host_no));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure that nobody tries to wake us up again.
|
|
|
|
*/
|
|
|
|
ha->dpc_active = 0;
|
|
|
|
|
2006-02-14 17:46:22 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2xxx_wake_dpc(struct scsi_qla_host *vha)
|
2006-02-14 17:46:22 +00:00
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2008-08-14 04:37:01 +00:00
|
|
|
struct task_struct *t = ha->dpc_thread;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (!test_bit(UNLOADING, &vha->dpc_flags) && t)
|
2008-08-14 04:37:01 +00:00
|
|
|
wake_up_process(t);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qla2x00_rst_aen
|
|
|
|
* Processes asynchronous reset.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* ha = adapter block pointer.
|
|
|
|
*/
|
|
|
|
static void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_rst_aen(scsi_qla_host_t *vha)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
if (vha->flags.online && !vha->flags.reset_active &&
|
|
|
|
!atomic_read(&vha->loop_down_timer) &&
|
|
|
|
!(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))) {
|
2005-04-16 22:20:36 +00:00
|
|
|
do {
|
2008-11-06 18:40:51 +00:00
|
|
|
clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Issue marker command only when we are going to start
|
|
|
|
* the I/O.
|
|
|
|
*/
|
2008-11-06 18:40:51 +00:00
|
|
|
vha->marker_needed = 1;
|
|
|
|
} while (!atomic_read(&vha->loop_down_timer) &&
|
|
|
|
(test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags)));
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 20:02:26 +00:00
|
|
|
static void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_sp_free_dma(srb_t *sp)
|
2005-04-17 20:02:26 +00:00
|
|
|
{
|
|
|
|
struct scsi_cmnd *cmd = sp->cmd;
|
|
|
|
|
|
|
|
if (sp->flags & SRB_DMA_VALID) {
|
2007-05-25 16:55:38 +00:00
|
|
|
scsi_dma_unmap(cmd);
|
2005-04-17 20:02:26 +00:00
|
|
|
sp->flags &= ~SRB_DMA_VALID;
|
|
|
|
}
|
2005-07-06 17:31:47 +00:00
|
|
|
CMD_SP(cmd) = NULL;
|
2005-04-17 20:02:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-12-10 00:45:39 +00:00
|
|
|
qla2x00_sp_compl(struct qla_hw_data *ha, srb_t *sp)
|
2005-04-17 20:02:26 +00:00
|
|
|
{
|
|
|
|
struct scsi_cmnd *cmd = sp->cmd;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_sp_free_dma(sp);
|
2005-04-17 20:02:26 +00:00
|
|
|
|
|
|
|
mempool_free(sp, ha->srb_mempool);
|
|
|
|
|
|
|
|
cmd->scsi_done(cmd);
|
|
|
|
}
|
2005-04-17 20:06:53 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/**************************************************************************
|
|
|
|
* qla2x00_timer
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* One second timer
|
|
|
|
*
|
|
|
|
* Context: Interrupt
|
|
|
|
***************************************************************************/
|
2007-07-05 20:16:51 +00:00
|
|
|
void
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_timer(scsi_qla_host_t *vha)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
unsigned long cpu_flags = 0;
|
|
|
|
fc_port_t *fcport;
|
|
|
|
int start_dpc = 0;
|
|
|
|
int index;
|
|
|
|
srb_t *sp;
|
2005-04-17 20:02:26 +00:00
|
|
|
int t;
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2008-12-10 00:45:39 +00:00
|
|
|
struct req_que *req;
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Ports - Port down timer.
|
|
|
|
*
|
|
|
|
* Whenever, a port is in the LOST state we start decrementing its port
|
|
|
|
* down timer every second until it reaches zero. Once it reaches zero
|
2005-07-06 17:32:07 +00:00
|
|
|
* the port it marked DEAD.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
t = 0;
|
2008-11-06 18:40:51 +00:00
|
|
|
list_for_each_entry(fcport, &vha->vp_fcports, list) {
|
2005-04-16 22:20:36 +00:00
|
|
|
if (fcport->port_type != FCT_TARGET)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (atomic_read(&fcport->state) == FCS_DEVICE_LOST) {
|
|
|
|
|
|
|
|
if (atomic_read(&fcport->port_down_timer) == 0)
|
|
|
|
continue;
|
|
|
|
|
2005-07-06 17:32:07 +00:00
|
|
|
if (atomic_dec_and_test(&fcport->port_down_timer) != 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
atomic_set(&fcport->state, FCS_DEVICE_DEAD);
|
2005-07-06 17:32:07 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
DEBUG(printk("scsi(%ld): fcport-%d - port retry count: "
|
2005-07-06 17:31:47 +00:00
|
|
|
"%d remaining\n",
|
2008-11-06 18:40:51 +00:00
|
|
|
vha->host_no,
|
2005-04-16 22:20:36 +00:00
|
|
|
t, atomic_read(&fcport->port_down_timer)));
|
|
|
|
}
|
|
|
|
t++;
|
|
|
|
} /* End of for fcport */
|
|
|
|
|
|
|
|
|
|
|
|
/* Loop down handler. */
|
2008-11-06 18:40:51 +00:00
|
|
|
if (atomic_read(&vha->loop_down_timer) > 0 &&
|
|
|
|
!(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))
|
|
|
|
&& vha->flags.online) {
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (atomic_read(&vha->loop_down_timer) ==
|
|
|
|
vha->loop_down_abort_time) {
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
DEBUG(printk("scsi(%ld): Loop Down - aborting the "
|
|
|
|
"queues before time expire\n",
|
2008-11-06 18:40:51 +00:00
|
|
|
vha->host_no));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (!IS_QLA2100(ha) && vha->link_down_timeout)
|
|
|
|
atomic_set(&vha->loop_state, LOOP_DEAD);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* Schedule an ISP abort to return any tape commands. */
|
2007-07-05 20:16:51 +00:00
|
|
|
/* NPIV - scan physical port only */
|
2008-11-06 18:40:51 +00:00
|
|
|
if (!vha->vp_idx) {
|
2007-07-05 20:16:51 +00:00
|
|
|
spin_lock_irqsave(&ha->hardware_lock,
|
|
|
|
cpu_flags);
|
2008-12-10 00:45:39 +00:00
|
|
|
req = ha->req_q_map[0];
|
2007-07-05 20:16:51 +00:00
|
|
|
for (index = 1;
|
|
|
|
index < MAX_OUTSTANDING_COMMANDS;
|
|
|
|
index++) {
|
|
|
|
fc_port_t *sfcp;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
sp = req->outstanding_cmds[index];
|
2007-07-05 20:16:51 +00:00
|
|
|
if (!sp)
|
|
|
|
continue;
|
|
|
|
sfcp = sp->fcport;
|
|
|
|
if (!(sfcp->flags & FCF_TAPE_PRESENT))
|
|
|
|
continue;
|
2005-04-17 20:06:53 +00:00
|
|
|
|
2007-07-05 20:16:51 +00:00
|
|
|
set_bit(ISP_ABORT_NEEDED,
|
2008-11-06 18:40:51 +00:00
|
|
|
&vha->dpc_flags);
|
2007-07-05 20:16:51 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock,
|
2008-11-06 18:40:51 +00:00
|
|
|
cpu_flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2008-11-06 18:40:51 +00:00
|
|
|
set_bit(ABORT_QUEUES_NEEDED, &vha->dpc_flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
start_dpc++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if the loop has been down for 4 minutes, reinit adapter */
|
2008-11-06 18:40:51 +00:00
|
|
|
if (atomic_dec_and_test(&vha->loop_down_timer) != 0) {
|
2005-04-16 22:20:36 +00:00
|
|
|
DEBUG(printk("scsi(%ld): Loop down exceed 4 mins - "
|
|
|
|
"restarting queues.\n",
|
2008-11-06 18:40:51 +00:00
|
|
|
vha->host_no));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
set_bit(RESTART_QUEUES_NEEDED, &vha->dpc_flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
start_dpc++;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (!(vha->device_flags & DFLG_NO_CABLE) &&
|
|
|
|
!vha->vp_idx) {
|
2005-04-16 22:20:36 +00:00
|
|
|
DEBUG(printk("scsi(%ld): Loop down - "
|
|
|
|
"aborting ISP.\n",
|
2008-11-06 18:40:51 +00:00
|
|
|
vha->host_no));
|
2005-04-16 22:20:36 +00:00
|
|
|
qla_printk(KERN_WARNING, ha,
|
|
|
|
"Loop down - aborting ISP.\n");
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
2005-07-06 17:31:47 +00:00
|
|
|
DEBUG3(printk("scsi(%ld): Loop Down - seconds remaining %d\n",
|
2008-11-06 18:40:51 +00:00
|
|
|
vha->host_no,
|
|
|
|
atomic_read(&vha->loop_down_timer)));
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2006-02-01 00:05:07 +00:00
|
|
|
/* Check if beacon LED needs to be blinked */
|
|
|
|
if (ha->beacon_blink_led == 1) {
|
2008-11-06 18:40:51 +00:00
|
|
|
set_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags);
|
2006-02-01 00:05:07 +00:00
|
|
|
start_dpc++;
|
|
|
|
}
|
|
|
|
|
2008-04-24 22:21:23 +00:00
|
|
|
/* Process any deferred work. */
|
2008-11-06 18:40:51 +00:00
|
|
|
if (!list_empty(&vha->work_list))
|
2008-04-24 22:21:23 +00:00
|
|
|
start_dpc++;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Schedule the DPC routine if needed */
|
2008-11-06 18:40:51 +00:00
|
|
|
if ((test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) ||
|
|
|
|
test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags) ||
|
|
|
|
test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags) ||
|
2005-04-16 22:20:36 +00:00
|
|
|
start_dpc ||
|
2008-11-06 18:40:51 +00:00
|
|
|
test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) ||
|
|
|
|
test_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags) ||
|
|
|
|
test_bit(VP_DPC_NEEDED, &vha->dpc_flags) ||
|
|
|
|
test_bit(RELOGIN_NEEDED, &vha->dpc_flags)))
|
|
|
|
qla2xxx_wake_dpc(vha);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_restart_timer(vha, WATCH_INTERVAL);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2005-11-09 23:49:04 +00:00
|
|
|
/* Firmware interface routines. */
|
|
|
|
|
2007-07-20 03:37:34 +00:00
|
|
|
#define FW_BLOBS 6
|
2005-11-09 23:49:04 +00:00
|
|
|
#define FW_ISP21XX 0
|
|
|
|
#define FW_ISP22XX 1
|
|
|
|
#define FW_ISP2300 2
|
|
|
|
#define FW_ISP2322 3
|
2006-03-09 22:27:18 +00:00
|
|
|
#define FW_ISP24XX 4
|
2007-07-20 03:37:34 +00:00
|
|
|
#define FW_ISP25XX 5
|
2005-11-09 23:49:04 +00:00
|
|
|
|
2006-10-02 19:00:48 +00:00
|
|
|
#define FW_FILE_ISP21XX "ql2100_fw.bin"
|
|
|
|
#define FW_FILE_ISP22XX "ql2200_fw.bin"
|
|
|
|
#define FW_FILE_ISP2300 "ql2300_fw.bin"
|
|
|
|
#define FW_FILE_ISP2322 "ql2322_fw.bin"
|
|
|
|
#define FW_FILE_ISP24XX "ql2400_fw.bin"
|
2007-07-20 03:37:34 +00:00
|
|
|
#define FW_FILE_ISP25XX "ql2500_fw.bin"
|
2006-10-02 19:00:48 +00:00
|
|
|
|
2008-05-13 05:21:10 +00:00
|
|
|
static DEFINE_MUTEX(qla_fw_lock);
|
2005-11-09 23:49:04 +00:00
|
|
|
|
|
|
|
static struct fw_blob qla_fw_blobs[FW_BLOBS] = {
|
2006-10-02 19:00:48 +00:00
|
|
|
{ .name = FW_FILE_ISP21XX, .segs = { 0x1000, 0 }, },
|
|
|
|
{ .name = FW_FILE_ISP22XX, .segs = { 0x1000, 0 }, },
|
|
|
|
{ .name = FW_FILE_ISP2300, .segs = { 0x800, 0 }, },
|
|
|
|
{ .name = FW_FILE_ISP2322, .segs = { 0x800, 0x1c000, 0x1e000, 0 }, },
|
|
|
|
{ .name = FW_FILE_ISP24XX, },
|
2007-07-20 03:37:34 +00:00
|
|
|
{ .name = FW_FILE_ISP25XX, },
|
2005-11-09 23:49:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct fw_blob *
|
2008-11-06 18:40:51 +00:00
|
|
|
qla2x00_request_firmware(scsi_qla_host_t *vha)
|
2005-11-09 23:49:04 +00:00
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-11-09 23:49:04 +00:00
|
|
|
struct fw_blob *blob;
|
|
|
|
|
|
|
|
blob = NULL;
|
|
|
|
if (IS_QLA2100(ha)) {
|
|
|
|
blob = &qla_fw_blobs[FW_ISP21XX];
|
|
|
|
} else if (IS_QLA2200(ha)) {
|
|
|
|
blob = &qla_fw_blobs[FW_ISP22XX];
|
2006-03-09 22:27:18 +00:00
|
|
|
} else if (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA6312(ha)) {
|
2005-11-09 23:49:04 +00:00
|
|
|
blob = &qla_fw_blobs[FW_ISP2300];
|
2006-03-09 22:27:18 +00:00
|
|
|
} else if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
|
2005-11-09 23:49:04 +00:00
|
|
|
blob = &qla_fw_blobs[FW_ISP2322];
|
2008-04-03 20:13:26 +00:00
|
|
|
} else if (IS_QLA24XX_TYPE(ha)) {
|
2005-11-09 23:49:04 +00:00
|
|
|
blob = &qla_fw_blobs[FW_ISP24XX];
|
2007-07-20 03:37:34 +00:00
|
|
|
} else if (IS_QLA25XX(ha)) {
|
|
|
|
blob = &qla_fw_blobs[FW_ISP25XX];
|
2005-11-09 23:49:04 +00:00
|
|
|
}
|
|
|
|
|
2008-05-13 05:21:10 +00:00
|
|
|
mutex_lock(&qla_fw_lock);
|
2005-11-09 23:49:04 +00:00
|
|
|
if (blob->fw)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (request_firmware(&blob->fw, blob->name, &ha->pdev->dev)) {
|
|
|
|
DEBUG2(printk("scsi(%ld): Failed to load firmware image "
|
2008-11-06 18:40:51 +00:00
|
|
|
"(%s).\n", vha->host_no, blob->name));
|
2005-11-09 23:49:04 +00:00
|
|
|
blob->fw = NULL;
|
|
|
|
blob = NULL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2008-05-13 05:21:10 +00:00
|
|
|
mutex_unlock(&qla_fw_lock);
|
2005-11-09 23:49:04 +00:00
|
|
|
return blob;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
qla2x00_release_firmware(void)
|
|
|
|
{
|
|
|
|
int idx;
|
|
|
|
|
2008-05-13 05:21:10 +00:00
|
|
|
mutex_lock(&qla_fw_lock);
|
2005-11-09 23:49:04 +00:00
|
|
|
for (idx = 0; idx < FW_BLOBS; idx++)
|
|
|
|
if (qla_fw_blobs[idx].fw)
|
|
|
|
release_firmware(qla_fw_blobs[idx].fw);
|
2008-05-13 05:21:10 +00:00
|
|
|
mutex_unlock(&qla_fw_lock);
|
2005-11-09 23:49:04 +00:00
|
|
|
}
|
|
|
|
|
2007-09-20 21:07:36 +00:00
|
|
|
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;
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
|
|
|
|
struct qla_hw_data *ha = base_vha->hw;
|
2007-09-20 21:07:36 +00:00
|
|
|
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(®->hccr);
|
|
|
|
if (stat & HCCR_RISC_PAUSE)
|
|
|
|
risc_paused = 1;
|
|
|
|
} else if (IS_QLA23XX(ha)) {
|
|
|
|
stat = RD_REG_DWORD(®->u.isp2300.host_status);
|
|
|
|
if (stat & HSR_RISC_PAUSED)
|
|
|
|
risc_paused = 1;
|
|
|
|
} else if (IS_FWI2_CAPABLE(ha)) {
|
|
|
|
stat = RD_REG_DWORD(®24->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");
|
2008-11-06 18:40:51 +00:00
|
|
|
ha->isp_ops->fw_dump(base_vha, 0);
|
2007-09-20 21:07:36 +00:00
|
|
|
|
|
|
|
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;
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
|
|
|
|
struct qla_hw_data *ha = base_vha->hw;
|
2007-12-20 04:28:09 +00:00
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (ha->mem_only)
|
|
|
|
rc = pci_enable_device_mem(pdev);
|
|
|
|
else
|
|
|
|
rc = pci_enable_device(pdev);
|
2007-09-20 21:07:36 +00:00
|
|
|
|
2007-12-20 04:28:09 +00:00
|
|
|
if (rc) {
|
2007-09-20 21:07:36 +00:00
|
|
|
qla_printk(KERN_WARNING, ha,
|
|
|
|
"Can't re-enable PCI device after reset.\n");
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
pci_set_master(pdev);
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
if (ha->isp_ops->pci_config(base_vha))
|
2007-09-20 21:07:36 +00:00
|
|
|
return ret;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
|
|
|
|
if (qla2x00_abort_isp(base_vha) == QLA_SUCCESS)
|
2007-09-20 21:07:36 +00:00
|
|
|
ret = PCI_ERS_RESULT_RECOVERED;
|
2008-11-06 18:40:51 +00:00
|
|
|
clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
|
2007-09-20 21:07:36 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
qla2xxx_pci_resume(struct pci_dev *pdev)
|
|
|
|
{
|
2008-11-06 18:40:51 +00:00
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
|
|
|
|
struct qla_hw_data *ha = base_vha->hw;
|
2007-09-20 21:07:36 +00:00
|
|
|
int ret;
|
|
|
|
|
2008-11-06 18:40:51 +00:00
|
|
|
ret = qla2x00_wait_for_hba_online(base_vha);
|
2007-09-20 21:07:36 +00:00
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
2005-11-09 23:49:04 +00:00
|
|
|
static struct pci_device_id qla2xxx_pci_tbl[] = {
|
2006-05-17 22:09:39 +00:00
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2100) },
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2200) },
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2300) },
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2312) },
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2322) },
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP6312) },
|
|
|
|
{ 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) },
|
2008-04-03 20:13:26 +00:00
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8432) },
|
2006-05-17 22:09:39 +00:00
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5422) },
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5432) },
|
2007-07-20 03:37:34 +00:00
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2532) },
|
2005-11-09 23:49:04 +00:00
|
|
|
{ 0 },
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, qla2xxx_pci_tbl);
|
|
|
|
|
2005-07-06 17:31:47 +00:00
|
|
|
static struct pci_driver qla2xxx_pci_driver = {
|
2006-05-17 22:09:45 +00:00
|
|
|
.name = QLA2XXX_DRIVER_NAME,
|
2005-12-01 18:51:50 +00:00
|
|
|
.driver = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
},
|
2005-07-06 17:31:47 +00:00
|
|
|
.id_table = qla2xxx_pci_tbl,
|
2006-06-23 23:11:22 +00:00
|
|
|
.probe = qla2x00_probe_one,
|
2008-01-14 08:55:16 +00:00
|
|
|
.remove = qla2x00_remove_one,
|
2007-09-20 21:07:36 +00:00
|
|
|
.err_handler = &qla2xxx_err_handler,
|
2005-07-06 17:31:47 +00:00
|
|
|
};
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/**
|
|
|
|
* qla2x00_module_init - Module initialization.
|
|
|
|
**/
|
|
|
|
static int __init
|
|
|
|
qla2x00_module_init(void)
|
|
|
|
{
|
2005-07-06 17:31:47 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Allocate cache for SRBs. */
|
2005-04-23 06:47:27 +00:00
|
|
|
srb_cachep = kmem_cache_create("qla2xxx_srbs", sizeof(srb_t), 0,
|
2007-07-20 01:11:58 +00:00
|
|
|
SLAB_HWCACHE_ALIGN, NULL);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (srb_cachep == NULL) {
|
|
|
|
printk(KERN_ERR
|
|
|
|
"qla2xxx: Unable to allocate SRB cache...Failing load!\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Derive version string. */
|
|
|
|
strcpy(qla2x00_version_str, QLA2XXX_VERSION);
|
2006-10-06 16:54:59 +00:00
|
|
|
if (ql2xextended_error_logging)
|
2006-06-23 23:11:10 +00:00
|
|
|
strcat(qla2x00_version_str, "-debug");
|
|
|
|
|
2005-04-21 20:13:36 +00:00
|
|
|
qla2xxx_transport_template =
|
|
|
|
fc_attach_transport(&qla2xxx_transport_functions);
|
2007-07-05 20:16:51 +00:00
|
|
|
if (!qla2xxx_transport_template) {
|
|
|
|
kmem_cache_destroy(srb_cachep);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
2007-07-05 20:16:51 +00:00
|
|
|
}
|
|
|
|
qla2xxx_transport_vport_template =
|
|
|
|
fc_attach_transport(&qla2xxx_transport_vport_functions);
|
|
|
|
if (!qla2xxx_transport_vport_template) {
|
|
|
|
kmem_cache_destroy(srb_cachep);
|
|
|
|
fc_release_transport(qla2xxx_transport_template);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENODEV;
|
2007-07-05 20:16:51 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-05-13 05:21:08 +00:00
|
|
|
printk(KERN_INFO "QLogic Fibre Channel HBA Driver: %s\n",
|
|
|
|
qla2x00_version_str);
|
2006-06-23 23:11:22 +00:00
|
|
|
ret = pci_register_driver(&qla2xxx_pci_driver);
|
2005-07-06 17:31:47 +00:00
|
|
|
if (ret) {
|
|
|
|
kmem_cache_destroy(srb_cachep);
|
|
|
|
fc_release_transport(qla2xxx_transport_template);
|
2007-07-05 20:16:51 +00:00
|
|
|
fc_release_transport(qla2xxx_transport_vport_template);
|
2005-07-06 17:31:47 +00:00
|
|
|
}
|
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qla2x00_module_exit - Module cleanup.
|
|
|
|
**/
|
|
|
|
static void __exit
|
|
|
|
qla2x00_module_exit(void)
|
|
|
|
{
|
2006-06-23 23:11:22 +00:00
|
|
|
pci_unregister_driver(&qla2xxx_pci_driver);
|
2005-11-09 23:49:04 +00:00
|
|
|
qla2x00_release_firmware();
|
2005-04-23 06:47:27 +00:00
|
|
|
kmem_cache_destroy(srb_cachep);
|
2005-04-16 22:20:36 +00:00
|
|
|
fc_release_transport(qla2xxx_transport_template);
|
2007-07-05 20:16:51 +00:00
|
|
|
fc_release_transport(qla2xxx_transport_vport_template);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(qla2x00_module_init);
|
|
|
|
module_exit(qla2x00_module_exit);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("QLogic Corporation");
|
|
|
|
MODULE_DESCRIPTION("QLogic Fibre Channel HBA Driver");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_VERSION(QLA2XXX_VERSION);
|
2006-10-02 19:00:48 +00:00
|
|
|
MODULE_FIRMWARE(FW_FILE_ISP21XX);
|
|
|
|
MODULE_FIRMWARE(FW_FILE_ISP22XX);
|
|
|
|
MODULE_FIRMWARE(FW_FILE_ISP2300);
|
|
|
|
MODULE_FIRMWARE(FW_FILE_ISP2322);
|
|
|
|
MODULE_FIRMWARE(FW_FILE_ISP24XX);
|
2008-01-31 20:33:45 +00:00
|
|
|
MODULE_FIRMWARE(FW_FILE_ISP25XX);
|