2006-09-20 01:28:00 +08:00
|
|
|
/*
|
|
|
|
* QLogic iSCSI HBA Driver
|
|
|
|
* Copyright (c) 2003-2006 QLogic Corporation
|
|
|
|
*
|
|
|
|
* See LICENSE.qla4xxx for copyright and licensing details.
|
|
|
|
*/
|
|
|
|
#include <linux/moduleparam.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
#include <scsi/scsi_tcq.h>
|
|
|
|
#include <scsi/scsicam.h>
|
|
|
|
|
|
|
|
#include "ql4_def.h"
|
2007-05-24 09:03:32 +08:00
|
|
|
#include "ql4_version.h"
|
|
|
|
#include "ql4_glbl.h"
|
|
|
|
#include "ql4_dbg.h"
|
|
|
|
#include "ql4_inline.h"
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Driver version
|
|
|
|
*/
|
2007-04-26 15:35:16 +08:00
|
|
|
static char qla4xxx_version_str[40];
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* SRB allocation cache
|
|
|
|
*/
|
2006-12-07 12:33:20 +08:00
|
|
|
static struct kmem_cache *srb_cachep;
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Module parameter information and variables
|
|
|
|
*/
|
|
|
|
int ql4xdiscoverywait = 60;
|
2010-07-28 18:23:44 +08:00
|
|
|
module_param(ql4xdiscoverywait, int, S_IRUGO | S_IWUSR);
|
2006-09-20 01:28:00 +08:00
|
|
|
MODULE_PARM_DESC(ql4xdiscoverywait, "Discovery wait time");
|
2010-07-28 18:23:44 +08:00
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
int ql4xdontresethba = 0;
|
2010-07-28 18:23:44 +08:00
|
|
|
module_param(ql4xdontresethba, int, S_IRUGO | S_IWUSR);
|
2006-09-20 01:28:00 +08:00
|
|
|
MODULE_PARM_DESC(ql4xdontresethba,
|
2010-07-28 18:23:44 +08:00
|
|
|
"Don't reset the HBA for driver recovery \n"
|
|
|
|
" 0 - It will reset HBA (Default)\n"
|
|
|
|
" 1 - It will NOT reset HBA");
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2006-10-07 00:54:59 +08:00
|
|
|
int ql4xextended_error_logging = 0; /* 0 = off, 1 = log errors */
|
2010-07-28 18:23:44 +08:00
|
|
|
module_param(ql4xextended_error_logging, int, S_IRUGO | S_IWUSR);
|
2006-10-07 00:54:59 +08:00
|
|
|
MODULE_PARM_DESC(ql4xextended_error_logging,
|
2006-09-20 01:28:00 +08:00
|
|
|
"Option to enable extended error logging, "
|
|
|
|
"Default is 0 - no logging, 1 - debug logging");
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
int ql4xenablemsix = 1;
|
|
|
|
module_param(ql4xenablemsix, int, S_IRUGO|S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(ql4xenablemsix,
|
|
|
|
"Set to enable MSI or MSI-X interrupt mechanism.\n"
|
|
|
|
" 0 = enable INTx interrupt mechanism.\n"
|
|
|
|
" 1 = enable MSI-X interrupt mechanism (Default).\n"
|
|
|
|
" 2 = enable MSI interrupt mechanism.");
|
2007-01-23 04:26:11 +08:00
|
|
|
|
2008-07-12 08:50:33 +08:00
|
|
|
#define QL4_DEF_QDEPTH 32
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
/*
|
|
|
|
* SCSI host template entry points
|
|
|
|
*/
|
2007-04-26 15:35:16 +08:00
|
|
|
static void qla4xxx_config_dma_addressing(struct scsi_qla_host *ha);
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* iSCSI template entry points
|
|
|
|
*/
|
2007-05-31 01:57:10 +08:00
|
|
|
static int qla4xxx_tgt_dscvr(struct Scsi_Host *shost,
|
|
|
|
enum iscsi_tgt_dscvr type, uint32_t enable,
|
|
|
|
struct sockaddr *dst_addr);
|
2006-09-20 01:28:00 +08:00
|
|
|
static int qla4xxx_conn_get_param(struct iscsi_cls_conn *conn,
|
|
|
|
enum iscsi_param param, char *buf);
|
|
|
|
static int qla4xxx_sess_get_param(struct iscsi_cls_session *sess,
|
|
|
|
enum iscsi_param param, char *buf);
|
2007-05-31 01:57:09 +08:00
|
|
|
static int qla4xxx_host_get_param(struct Scsi_Host *shost,
|
|
|
|
enum iscsi_host_param param, char *buf);
|
2006-09-20 01:28:00 +08:00
|
|
|
static void qla4xxx_recovery_timedout(struct iscsi_cls_session *session);
|
2009-07-16 04:02:59 +08:00
|
|
|
static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc);
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* SCSI host template entry points
|
|
|
|
*/
|
|
|
|
static int qla4xxx_queuecommand(struct scsi_cmnd *cmd,
|
|
|
|
void (*done) (struct scsi_cmnd *));
|
2010-04-28 14:12:24 +08:00
|
|
|
static int qla4xxx_eh_abort(struct scsi_cmnd *cmd);
|
2006-09-20 01:28:00 +08:00
|
|
|
static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd);
|
2008-03-01 08:25:20 +08:00
|
|
|
static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd);
|
2006-09-20 01:28:00 +08:00
|
|
|
static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd);
|
|
|
|
static int qla4xxx_slave_alloc(struct scsi_device *device);
|
|
|
|
static int qla4xxx_slave_configure(struct scsi_device *device);
|
|
|
|
static void qla4xxx_slave_destroy(struct scsi_device *sdev);
|
2008-03-05 03:26:54 +08:00
|
|
|
static void qla4xxx_scan_start(struct Scsi_Host *shost);
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
static struct qla4_8xxx_legacy_intr_set legacy_intr[] =
|
|
|
|
QLA82XX_LEGACY_INTR_CONFIG;
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
static struct scsi_host_template qla4xxx_driver_template = {
|
|
|
|
.module = THIS_MODULE,
|
|
|
|
.name = DRIVER_NAME,
|
|
|
|
.proc_name = DRIVER_NAME,
|
|
|
|
.queuecommand = qla4xxx_queuecommand,
|
|
|
|
|
2010-04-28 14:12:24 +08:00
|
|
|
.eh_abort_handler = qla4xxx_eh_abort,
|
2006-09-20 01:28:00 +08:00
|
|
|
.eh_device_reset_handler = qla4xxx_eh_device_reset,
|
2008-03-01 08:25:20 +08:00
|
|
|
.eh_target_reset_handler = qla4xxx_eh_target_reset,
|
2006-09-20 01:28:00 +08:00
|
|
|
.eh_host_reset_handler = qla4xxx_eh_host_reset,
|
2009-07-16 04:02:59 +08:00
|
|
|
.eh_timed_out = qla4xxx_eh_cmd_timed_out,
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
.slave_configure = qla4xxx_slave_configure,
|
|
|
|
.slave_alloc = qla4xxx_slave_alloc,
|
|
|
|
.slave_destroy = qla4xxx_slave_destroy,
|
|
|
|
|
2008-02-01 03:36:49 +08:00
|
|
|
.scan_finished = iscsi_scan_finished,
|
2008-03-05 03:26:54 +08:00
|
|
|
.scan_start = qla4xxx_scan_start,
|
2008-02-01 03:36:49 +08:00
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
.this_id = -1,
|
|
|
|
.cmd_per_lun = 3,
|
|
|
|
.use_clustering = ENABLE_CLUSTERING,
|
|
|
|
.sg_tablesize = SG_ALL,
|
|
|
|
|
|
|
|
.max_sectors = 0xFFFF,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct iscsi_transport qla4xxx_iscsi_transport = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.name = DRIVER_NAME,
|
[SCSI] iscsi class, iscsi_tcp, iser, qla4xxx: add netdevname sysfs attr
iSCSI must support software iscsi (iscsi_tcp, iser), hardware iscsi (qla4xxx),
and partial offload (broadcom). To be able to allow each stack or driver
or port (virtual or physical) to be able to log into the same target portal
we use the initiator tuple [[HWADDRESS | NETDEVNAME], INITIATOR_NAME] and
the target tuple [TARGETNAME, CONN_ADDRESS, CONN_PORT] to id a session.
This patch adds the netdev name, which is used by software iscsi when
it binds a session to a netdevice using the SO_BINDTODEVICE sock opt.
It cannot use HWADDRESS because if someone did vlans then the same netdevice
will have the same mac and the initiator,target id will not be unique.
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Cc: Roland Dreier <rdreier@cisco.com>
Cc: David C Somayajulu <david.somayajulu@qlogic.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2007-05-31 01:57:25 +08:00
|
|
|
.caps = CAP_FW_DB | CAP_SENDTARGETS_OFFLOAD |
|
|
|
|
CAP_DATA_PATH_OFFLOAD,
|
2007-05-31 01:57:09 +08:00
|
|
|
.param_mask = ISCSI_CONN_PORT | ISCSI_CONN_ADDRESS |
|
2010-07-10 17:21:30 +08:00
|
|
|
ISCSI_TARGET_NAME | ISCSI_TPGT |
|
|
|
|
ISCSI_TARGET_ALIAS,
|
2007-05-31 01:57:13 +08:00
|
|
|
.host_param_mask = ISCSI_HOST_HWADDRESS |
|
2007-05-31 01:57:24 +08:00
|
|
|
ISCSI_HOST_IPADDRESS |
|
2007-05-31 01:57:13 +08:00
|
|
|
ISCSI_HOST_INITIATOR_NAME,
|
2006-09-20 01:28:00 +08:00
|
|
|
.tgt_dscvr = qla4xxx_tgt_dscvr,
|
|
|
|
.get_conn_param = qla4xxx_conn_get_param,
|
|
|
|
.get_session_param = qla4xxx_sess_get_param,
|
2007-05-31 01:57:09 +08:00
|
|
|
.get_host_param = qla4xxx_host_get_param,
|
2006-09-20 01:28:00 +08:00
|
|
|
.session_recovery_timedout = qla4xxx_recovery_timedout,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct scsi_transport_template *qla4xxx_scsi_transport;
|
|
|
|
|
2009-07-16 04:02:59 +08:00
|
|
|
static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc)
|
|
|
|
{
|
|
|
|
struct iscsi_cls_session *session;
|
|
|
|
struct ddb_entry *ddb_entry;
|
|
|
|
|
|
|
|
session = starget_to_session(scsi_target(sc->device));
|
|
|
|
ddb_entry = session->dd_data;
|
|
|
|
|
|
|
|
/* if we are not logged in then the LLD is going to clean up the cmd */
|
|
|
|
if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE)
|
|
|
|
return BLK_EH_RESET_TIMER;
|
|
|
|
else
|
|
|
|
return BLK_EH_NOT_HANDLED;
|
|
|
|
}
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
static void qla4xxx_recovery_timedout(struct iscsi_cls_session *session)
|
|
|
|
{
|
|
|
|
struct ddb_entry *ddb_entry = session->dd_data;
|
|
|
|
struct scsi_qla_host *ha = ddb_entry->ha;
|
|
|
|
|
2008-02-01 03:36:47 +08:00
|
|
|
if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) {
|
|
|
|
atomic_set(&ddb_entry->state, DDB_STATE_DEAD);
|
|
|
|
|
2010-07-30 16:55:46 +08:00
|
|
|
DEBUG2(printk("scsi%ld: %s: ddb [%d] session recovery timeout "
|
2008-02-01 03:36:47 +08:00
|
|
|
"of (%d) secs exhausted, marking device DEAD.\n",
|
|
|
|
ha->host_no, __func__, ddb_entry->fw_ddb_index,
|
2010-07-30 16:55:46 +08:00
|
|
|
QL4_SESS_RECOVERY_TMO));
|
2008-02-01 03:36:47 +08:00
|
|
|
}
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
|
2007-05-31 01:57:09 +08:00
|
|
|
static int qla4xxx_host_get_param(struct Scsi_Host *shost,
|
|
|
|
enum iscsi_host_param param, char *buf)
|
|
|
|
{
|
|
|
|
struct scsi_qla_host *ha = to_qla_host(shost);
|
|
|
|
int len;
|
|
|
|
|
|
|
|
switch (param) {
|
|
|
|
case ISCSI_HOST_PARAM_HWADDRESS:
|
2007-12-25 13:28:09 +08:00
|
|
|
len = sysfs_format_mac(buf, ha->my_mac, MAC_ADDR_LEN);
|
2007-05-31 01:57:13 +08:00
|
|
|
break;
|
2007-05-31 01:57:24 +08:00
|
|
|
case ISCSI_HOST_PARAM_IPADDRESS:
|
|
|
|
len = sprintf(buf, "%d.%d.%d.%d\n", ha->ip_address[0],
|
|
|
|
ha->ip_address[1], ha->ip_address[2],
|
|
|
|
ha->ip_address[3]);
|
|
|
|
break;
|
2007-05-31 01:57:13 +08:00
|
|
|
case ISCSI_HOST_PARAM_INITIATOR_NAME:
|
2007-05-31 01:57:24 +08:00
|
|
|
len = sprintf(buf, "%s\n", ha->name_string);
|
2007-05-31 01:57:09 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
static int qla4xxx_sess_get_param(struct iscsi_cls_session *sess,
|
|
|
|
enum iscsi_param param, char *buf)
|
|
|
|
{
|
|
|
|
struct ddb_entry *ddb_entry = sess->dd_data;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
switch (param) {
|
|
|
|
case ISCSI_PARAM_TARGET_NAME:
|
|
|
|
len = snprintf(buf, PAGE_SIZE - 1, "%s\n",
|
|
|
|
ddb_entry->iscsi_name);
|
|
|
|
break;
|
|
|
|
case ISCSI_PARAM_TPGT:
|
|
|
|
len = sprintf(buf, "%u\n", ddb_entry->tpgt);
|
|
|
|
break;
|
2010-07-10 17:21:30 +08:00
|
|
|
case ISCSI_PARAM_TARGET_ALIAS:
|
|
|
|
len = snprintf(buf, PAGE_SIZE - 1, "%s\n",
|
|
|
|
ddb_entry->iscsi_alias);
|
|
|
|
break;
|
2006-09-20 01:28:00 +08:00
|
|
|
default:
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qla4xxx_conn_get_param(struct iscsi_cls_conn *conn,
|
|
|
|
enum iscsi_param param, char *buf)
|
|
|
|
{
|
|
|
|
struct iscsi_cls_session *session;
|
|
|
|
struct ddb_entry *ddb_entry;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
session = iscsi_dev_to_session(conn->dev.parent);
|
|
|
|
ddb_entry = session->dd_data;
|
|
|
|
|
|
|
|
switch (param) {
|
|
|
|
case ISCSI_PARAM_CONN_PORT:
|
|
|
|
len = sprintf(buf, "%hu\n", ddb_entry->port);
|
|
|
|
break;
|
|
|
|
case ISCSI_PARAM_CONN_ADDRESS:
|
|
|
|
/* TODO: what are the ipv6 bits */
|
2008-10-31 15:56:00 +08:00
|
|
|
len = sprintf(buf, "%pI4\n", &ddb_entry->ip_addr);
|
2006-09-20 01:28:00 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2007-05-31 01:57:10 +08:00
|
|
|
static int qla4xxx_tgt_dscvr(struct Scsi_Host *shost,
|
|
|
|
enum iscsi_tgt_dscvr type, uint32_t enable,
|
|
|
|
struct sockaddr *dst_addr)
|
2006-09-20 01:28:00 +08:00
|
|
|
{
|
|
|
|
struct scsi_qla_host *ha;
|
|
|
|
struct sockaddr_in *addr;
|
|
|
|
struct sockaddr_in6 *addr6;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
ha = (struct scsi_qla_host *) shost->hostdata;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case ISCSI_TGT_DSCVR_SEND_TARGETS:
|
|
|
|
if (dst_addr->sa_family == AF_INET) {
|
|
|
|
addr = (struct sockaddr_in *)dst_addr;
|
|
|
|
if (qla4xxx_send_tgts(ha, (char *)&addr->sin_addr,
|
|
|
|
addr->sin_port) != QLA_SUCCESS)
|
|
|
|
ret = -EIO;
|
|
|
|
} else if (dst_addr->sa_family == AF_INET6) {
|
|
|
|
/*
|
|
|
|
* TODO: fix qla4xxx_send_tgts
|
|
|
|
*/
|
|
|
|
addr6 = (struct sockaddr_in6 *)dst_addr;
|
|
|
|
if (qla4xxx_send_tgts(ha, (char *)&addr6->sin6_addr,
|
|
|
|
addr6->sin6_port) != QLA_SUCCESS)
|
|
|
|
ret = -EIO;
|
|
|
|
} else
|
|
|
|
ret = -ENOSYS;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = -ENOSYS;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qla4xxx_destroy_sess(struct ddb_entry *ddb_entry)
|
|
|
|
{
|
|
|
|
if (!ddb_entry->sess)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ddb_entry->conn) {
|
2007-12-14 02:43:29 +08:00
|
|
|
atomic_set(&ddb_entry->state, DDB_STATE_DEAD);
|
2006-09-20 01:28:00 +08:00
|
|
|
iscsi_remove_session(ddb_entry->sess);
|
|
|
|
}
|
|
|
|
iscsi_free_session(ddb_entry->sess);
|
|
|
|
}
|
|
|
|
|
|
|
|
int qla4xxx_add_sess(struct ddb_entry *ddb_entry)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2010-07-30 16:55:46 +08:00
|
|
|
ddb_entry->sess->recovery_tmo = QL4_SESS_RECOVERY_TMO;
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
err = iscsi_add_session(ddb_entry->sess, ddb_entry->fw_ddb_index);
|
|
|
|
if (err) {
|
|
|
|
DEBUG2(printk(KERN_ERR "Could not add session.\n"));
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2008-05-22 04:54:01 +08:00
|
|
|
ddb_entry->conn = iscsi_create_conn(ddb_entry->sess, 0, 0);
|
2006-09-20 01:28:00 +08:00
|
|
|
if (!ddb_entry->conn) {
|
|
|
|
iscsi_remove_session(ddb_entry->sess);
|
|
|
|
DEBUG2(printk(KERN_ERR "Could not add connection.\n"));
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2008-02-01 03:36:44 +08:00
|
|
|
|
|
|
|
/* finally ready to go */
|
|
|
|
iscsi_unblock_session(ddb_entry->sess);
|
2006-09-20 01:28:00 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ddb_entry *qla4xxx_alloc_sess(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
struct ddb_entry *ddb_entry;
|
|
|
|
struct iscsi_cls_session *sess;
|
|
|
|
|
2008-05-22 04:54:01 +08:00
|
|
|
sess = iscsi_alloc_session(ha->host, &qla4xxx_iscsi_transport,
|
|
|
|
sizeof(struct ddb_entry));
|
2006-09-20 01:28:00 +08:00
|
|
|
if (!sess)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ddb_entry = sess->dd_data;
|
|
|
|
memset(ddb_entry, 0, sizeof(*ddb_entry));
|
|
|
|
ddb_entry->ha = ha;
|
|
|
|
ddb_entry->sess = sess;
|
|
|
|
return ddb_entry;
|
|
|
|
}
|
|
|
|
|
2008-03-05 03:26:54 +08:00
|
|
|
static void qla4xxx_scan_start(struct Scsi_Host *shost)
|
|
|
|
{
|
|
|
|
struct scsi_qla_host *ha = shost_priv(shost);
|
|
|
|
struct ddb_entry *ddb_entry, *ddbtemp;
|
|
|
|
|
|
|
|
/* finish setup of sessions that were already setup in firmware */
|
|
|
|
list_for_each_entry_safe(ddb_entry, ddbtemp, &ha->ddb_list, list) {
|
|
|
|
if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE)
|
|
|
|
qla4xxx_add_sess(ddb_entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
/*
|
|
|
|
* Timer routines
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void qla4xxx_start_timer(struct scsi_qla_host *ha, void *func,
|
|
|
|
unsigned long interval)
|
|
|
|
{
|
|
|
|
DEBUG(printk("scsi: %s: Starting timer thread for adapter %d\n",
|
|
|
|
__func__, ha->host->host_no));
|
|
|
|
init_timer(&ha->timer);
|
|
|
|
ha->timer.expires = jiffies + interval * HZ;
|
|
|
|
ha->timer.data = (unsigned long)ha;
|
|
|
|
ha->timer.function = (void (*)(unsigned long))func;
|
|
|
|
add_timer(&ha->timer);
|
|
|
|
ha->timer_active = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qla4xxx_stop_timer(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
del_timer_sync(&ha->timer);
|
|
|
|
ha->timer_active = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
* qla4xxx_mark_device_missing - mark a device as missing.
|
|
|
|
* @ha: Pointer to host adapter structure.
|
|
|
|
* @ddb_entry: Pointer to device database entry
|
|
|
|
*
|
2010-07-28 18:23:44 +08:00
|
|
|
* This routine marks a device missing and close connection.
|
2006-09-20 01:28:00 +08:00
|
|
|
**/
|
|
|
|
void qla4xxx_mark_device_missing(struct scsi_qla_host *ha,
|
|
|
|
struct ddb_entry *ddb_entry)
|
|
|
|
{
|
2010-07-28 18:23:44 +08:00
|
|
|
if ((atomic_read(&ddb_entry->state) != DDB_STATE_DEAD)) {
|
|
|
|
atomic_set(&ddb_entry->state, DDB_STATE_MISSING);
|
|
|
|
DEBUG2(printk("scsi%ld: ddb [%d] marked MISSING\n",
|
|
|
|
ha->host_no, ddb_entry->fw_ddb_index));
|
|
|
|
} else
|
|
|
|
DEBUG2(printk("scsi%ld: ddb [%d] DEAD\n", ha->host_no,
|
|
|
|
ddb_entry->fw_ddb_index))
|
|
|
|
|
2008-02-01 03:36:44 +08:00
|
|
|
iscsi_block_session(ddb_entry->sess);
|
2008-09-25 00:46:10 +08:00
|
|
|
iscsi_conn_error_event(ddb_entry->conn, ISCSI_ERR_CONN_FAILED);
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
/**
|
|
|
|
* qla4xxx_mark_all_devices_missing - mark all devices as missing.
|
|
|
|
* @ha: Pointer to host adapter structure.
|
|
|
|
*
|
|
|
|
* This routine marks a device missing and resets the relogin retry count.
|
|
|
|
**/
|
|
|
|
void qla4xxx_mark_all_devices_missing(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
struct ddb_entry *ddb_entry, *ddbtemp;
|
|
|
|
list_for_each_entry_safe(ddb_entry, ddbtemp, &ha->ddb_list, list) {
|
|
|
|
qla4xxx_mark_device_missing(ha, ddb_entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha,
|
|
|
|
struct ddb_entry *ddb_entry,
|
|
|
|
struct scsi_cmnd *cmd,
|
|
|
|
void (*done)(struct scsi_cmnd *))
|
|
|
|
{
|
|
|
|
struct srb *srb;
|
|
|
|
|
|
|
|
srb = mempool_alloc(ha->srb_mempool, GFP_ATOMIC);
|
|
|
|
if (!srb)
|
|
|
|
return srb;
|
|
|
|
|
2010-04-28 14:12:24 +08:00
|
|
|
kref_init(&srb->srb_ref);
|
2006-09-20 01:28:00 +08:00
|
|
|
srb->ha = ha;
|
|
|
|
srb->ddb = ddb_entry;
|
|
|
|
srb->cmd = cmd;
|
|
|
|
srb->flags = 0;
|
2010-04-28 14:11:59 +08:00
|
|
|
CMD_SP(cmd) = (void *)srb;
|
2006-09-20 01:28:00 +08:00
|
|
|
cmd->scsi_done = done;
|
|
|
|
|
|
|
|
return srb;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qla4xxx_srb_free_dma(struct scsi_qla_host *ha, struct srb *srb)
|
|
|
|
{
|
|
|
|
struct scsi_cmnd *cmd = srb->cmd;
|
|
|
|
|
|
|
|
if (srb->flags & SRB_DMA_VALID) {
|
2007-05-26 13:08:20 +08:00
|
|
|
scsi_dma_unmap(cmd);
|
2006-09-20 01:28:00 +08:00
|
|
|
srb->flags &= ~SRB_DMA_VALID;
|
|
|
|
}
|
2010-04-28 14:11:59 +08:00
|
|
|
CMD_SP(cmd) = NULL;
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
|
2010-04-28 14:12:24 +08:00
|
|
|
void qla4xxx_srb_compl(struct kref *ref)
|
2006-09-20 01:28:00 +08:00
|
|
|
{
|
2010-04-28 14:12:24 +08:00
|
|
|
struct srb *srb = container_of(ref, struct srb, srb_ref);
|
2006-09-20 01:28:00 +08:00
|
|
|
struct scsi_cmnd *cmd = srb->cmd;
|
2010-04-28 14:12:24 +08:00
|
|
|
struct scsi_qla_host *ha = srb->ha;
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
qla4xxx_srb_free_dma(ha, srb);
|
|
|
|
|
|
|
|
mempool_free(srb, ha->srb_mempool);
|
|
|
|
|
|
|
|
cmd->scsi_done(cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qla4xxx_queuecommand - scsi layer issues scsi command to driver.
|
|
|
|
* @cmd: Pointer to Linux's SCSI command structure
|
|
|
|
* @done_fn: Function that the driver calls to notify the SCSI mid-layer
|
|
|
|
* that the command has been processed.
|
|
|
|
*
|
|
|
|
* Remarks:
|
|
|
|
* This routine is invoked by Linux to send a SCSI command to the driver.
|
|
|
|
* The mid-level driver tries to ensure that queuecommand never gets
|
|
|
|
* invoked concurrently with itself or the interrupt handler (although
|
|
|
|
* the interrupt handler may call this routine as part of request-
|
|
|
|
* completion handling). Unfortunely, it sometimes calls the scheduler
|
|
|
|
* in interrupt context which is a big NO! NO!.
|
|
|
|
**/
|
|
|
|
static int qla4xxx_queuecommand(struct scsi_cmnd *cmd,
|
|
|
|
void (*done)(struct scsi_cmnd *))
|
|
|
|
{
|
|
|
|
struct scsi_qla_host *ha = to_qla_host(cmd->device->host);
|
|
|
|
struct ddb_entry *ddb_entry = cmd->device->hostdata;
|
2008-02-01 03:36:45 +08:00
|
|
|
struct iscsi_cls_session *sess = ddb_entry->sess;
|
2006-09-20 01:28:00 +08:00
|
|
|
struct srb *srb;
|
|
|
|
int rval;
|
|
|
|
|
2010-07-30 17:08:47 +08:00
|
|
|
if (test_bit(AF_EEH_BUSY, &ha->flags)) {
|
|
|
|
if (test_bit(AF_PCI_CHANNEL_IO_PERM_FAILURE, &ha->flags))
|
|
|
|
cmd->result = DID_NO_CONNECT << 16;
|
|
|
|
else
|
|
|
|
cmd->result = DID_REQUEUE << 16;
|
|
|
|
goto qc_fail_command;
|
|
|
|
}
|
|
|
|
|
2008-02-01 03:36:45 +08:00
|
|
|
if (!sess) {
|
|
|
|
cmd->result = DID_IMM_RETRY << 16;
|
|
|
|
goto qc_fail_command;
|
|
|
|
}
|
|
|
|
|
|
|
|
rval = iscsi_session_chkready(sess);
|
|
|
|
if (rval) {
|
|
|
|
cmd->result = rval;
|
|
|
|
goto qc_fail_command;
|
|
|
|
}
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) {
|
|
|
|
if (atomic_read(&ddb_entry->state) == DDB_STATE_DEAD) {
|
|
|
|
cmd->result = DID_NO_CONNECT << 16;
|
|
|
|
goto qc_fail_command;
|
|
|
|
}
|
2008-08-18 04:24:39 +08:00
|
|
|
return SCSI_MLQUEUE_TARGET_BUSY;
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
if (test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) ||
|
|
|
|
test_bit(DPC_RESET_ACTIVE, &ha->dpc_flags) ||
|
|
|
|
test_bit(DPC_RESET_HA, &ha->dpc_flags) ||
|
|
|
|
test_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags) ||
|
|
|
|
test_bit(DPC_HA_NEED_QUIESCENT, &ha->dpc_flags) ||
|
|
|
|
!test_bit(AF_ONLINE, &ha->flags) ||
|
|
|
|
test_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags))
|
2007-01-23 04:26:11 +08:00
|
|
|
goto qc_host_busy;
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
spin_unlock_irq(ha->host->host_lock);
|
|
|
|
|
|
|
|
srb = qla4xxx_get_new_srb(ha, ddb_entry, cmd, done);
|
|
|
|
if (!srb)
|
|
|
|
goto qc_host_busy_lock;
|
|
|
|
|
|
|
|
rval = qla4xxx_send_command_to_isp(ha, srb);
|
|
|
|
if (rval != QLA_SUCCESS)
|
|
|
|
goto qc_host_busy_free_sp;
|
|
|
|
|
|
|
|
spin_lock_irq(ha->host->host_lock);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
qc_host_busy_free_sp:
|
|
|
|
qla4xxx_srb_free_dma(ha, srb);
|
|
|
|
mempool_free(srb, ha->srb_mempool);
|
|
|
|
|
|
|
|
qc_host_busy_lock:
|
|
|
|
spin_lock_irq(ha->host->host_lock);
|
|
|
|
|
|
|
|
qc_host_busy:
|
|
|
|
return SCSI_MLQUEUE_HOST_BUSY;
|
|
|
|
|
|
|
|
qc_fail_command:
|
|
|
|
done(cmd);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qla4xxx_mem_free - frees memory allocated to adapter
|
|
|
|
* @ha: Pointer to host adapter structure.
|
|
|
|
*
|
|
|
|
* Frees memory previously allocated by qla4xxx_mem_alloc
|
|
|
|
**/
|
|
|
|
static void qla4xxx_mem_free(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
if (ha->queues)
|
|
|
|
dma_free_coherent(&ha->pdev->dev, ha->queues_len, ha->queues,
|
|
|
|
ha->queues_dma);
|
|
|
|
|
|
|
|
ha->queues_len = 0;
|
|
|
|
ha->queues = NULL;
|
|
|
|
ha->queues_dma = 0;
|
|
|
|
ha->request_ring = NULL;
|
|
|
|
ha->request_dma = 0;
|
|
|
|
ha->response_ring = NULL;
|
|
|
|
ha->response_dma = 0;
|
|
|
|
ha->shadow_regs = NULL;
|
|
|
|
ha->shadow_regs_dma = 0;
|
|
|
|
|
|
|
|
/* Free srb pool. */
|
|
|
|
if (ha->srb_mempool)
|
|
|
|
mempool_destroy(ha->srb_mempool);
|
|
|
|
|
|
|
|
ha->srb_mempool = NULL;
|
|
|
|
|
|
|
|
/* release io space registers */
|
2010-07-28 18:23:44 +08:00
|
|
|
if (is_qla8022(ha)) {
|
|
|
|
if (ha->nx_pcibase)
|
|
|
|
iounmap(
|
|
|
|
(struct device_reg_82xx __iomem *)ha->nx_pcibase);
|
|
|
|
|
|
|
|
if (ha->nx_db_wr_ptr)
|
|
|
|
iounmap(
|
|
|
|
(struct device_reg_82xx __iomem *)ha->nx_db_wr_ptr);
|
|
|
|
} else if (ha->reg)
|
2006-09-20 01:28:00 +08:00
|
|
|
iounmap(ha->reg);
|
|
|
|
pci_release_regions(ha->pdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qla4xxx_mem_alloc - allocates memory for use by adapter.
|
|
|
|
* @ha: Pointer to host adapter structure
|
|
|
|
*
|
|
|
|
* Allocates DMA memory for request and response queues. Also allocates memory
|
|
|
|
* for srbs.
|
|
|
|
**/
|
|
|
|
static int qla4xxx_mem_alloc(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
unsigned long align;
|
|
|
|
|
|
|
|
/* Allocate contiguous block of DMA memory for queues. */
|
|
|
|
ha->queues_len = ((REQUEST_QUEUE_DEPTH * QUEUE_SIZE) +
|
|
|
|
(RESPONSE_QUEUE_DEPTH * QUEUE_SIZE) +
|
|
|
|
sizeof(struct shadow_regs) +
|
|
|
|
MEM_ALIGN_VALUE +
|
|
|
|
(PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
|
|
|
|
ha->queues = dma_alloc_coherent(&ha->pdev->dev, ha->queues_len,
|
|
|
|
&ha->queues_dma, GFP_KERNEL);
|
|
|
|
if (ha->queues == NULL) {
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_WARNING, ha,
|
|
|
|
"Memory Allocation failed - queues.\n");
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
goto mem_alloc_error_exit;
|
|
|
|
}
|
|
|
|
memset(ha->queues, 0, ha->queues_len);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* As per RISC alignment requirements -- the bus-address must be a
|
|
|
|
* multiple of the request-ring size (in bytes).
|
|
|
|
*/
|
|
|
|
align = 0;
|
|
|
|
if ((unsigned long)ha->queues_dma & (MEM_ALIGN_VALUE - 1))
|
|
|
|
align = MEM_ALIGN_VALUE - ((unsigned long)ha->queues_dma &
|
|
|
|
(MEM_ALIGN_VALUE - 1));
|
|
|
|
|
|
|
|
/* Update request and response queue pointers. */
|
|
|
|
ha->request_dma = ha->queues_dma + align;
|
|
|
|
ha->request_ring = (struct queue_entry *) (ha->queues + align);
|
|
|
|
ha->response_dma = ha->queues_dma + align +
|
|
|
|
(REQUEST_QUEUE_DEPTH * QUEUE_SIZE);
|
|
|
|
ha->response_ring = (struct queue_entry *) (ha->queues + align +
|
|
|
|
(REQUEST_QUEUE_DEPTH *
|
|
|
|
QUEUE_SIZE));
|
|
|
|
ha->shadow_regs_dma = ha->queues_dma + align +
|
|
|
|
(REQUEST_QUEUE_DEPTH * QUEUE_SIZE) +
|
|
|
|
(RESPONSE_QUEUE_DEPTH * QUEUE_SIZE);
|
|
|
|
ha->shadow_regs = (struct shadow_regs *) (ha->queues + align +
|
|
|
|
(REQUEST_QUEUE_DEPTH *
|
|
|
|
QUEUE_SIZE) +
|
|
|
|
(RESPONSE_QUEUE_DEPTH *
|
|
|
|
QUEUE_SIZE));
|
|
|
|
|
|
|
|
/* Allocate memory for srb pool. */
|
|
|
|
ha->srb_mempool = mempool_create(SRB_MIN_REQ, mempool_alloc_slab,
|
|
|
|
mempool_free_slab, srb_cachep);
|
|
|
|
if (ha->srb_mempool == NULL) {
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_WARNING, ha,
|
|
|
|
"Memory Allocation failed - SRB Pool.\n");
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
goto mem_alloc_error_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QLA_SUCCESS;
|
|
|
|
|
|
|
|
mem_alloc_error_exit:
|
|
|
|
qla4xxx_mem_free(ha);
|
|
|
|
return QLA_ERROR;
|
|
|
|
}
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
/**
|
|
|
|
* qla4_8xxx_check_fw_alive - Check firmware health
|
|
|
|
* @ha: Pointer to host adapter structure.
|
|
|
|
*
|
|
|
|
* Context: Interrupt
|
|
|
|
**/
|
|
|
|
static void qla4_8xxx_check_fw_alive(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
uint32_t fw_heartbeat_counter, halt_status;
|
|
|
|
|
|
|
|
fw_heartbeat_counter = qla4_8xxx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER);
|
2010-07-30 17:08:47 +08:00
|
|
|
/* If PEG_ALIVE_COUNTER is 0xffffffff, AER/EEH is in progress, ignore */
|
|
|
|
if (fw_heartbeat_counter == 0xffffffff) {
|
|
|
|
DEBUG2(printk(KERN_WARNING "scsi%ld: %s: Device in frozen "
|
|
|
|
"state, QLA82XX_PEG_ALIVE_COUNTER is 0xffffffff\n",
|
|
|
|
ha->host_no, __func__));
|
|
|
|
return;
|
|
|
|
}
|
2010-07-28 18:23:44 +08:00
|
|
|
|
|
|
|
if (ha->fw_heartbeat_counter == fw_heartbeat_counter) {
|
|
|
|
ha->seconds_since_last_heartbeat++;
|
|
|
|
/* FW not alive after 2 seconds */
|
|
|
|
if (ha->seconds_since_last_heartbeat == 2) {
|
|
|
|
ha->seconds_since_last_heartbeat = 0;
|
|
|
|
halt_status = qla4_8xxx_rd_32(ha,
|
|
|
|
QLA82XX_PEG_HALT_STATUS1);
|
2010-07-30 16:58:07 +08:00
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
/* Since we cannot change dev_state in interrupt
|
|
|
|
* context, set appropriate DPC flag then wakeup
|
|
|
|
* DPC */
|
|
|
|
if (halt_status & HALT_STATUS_UNRECOVERABLE)
|
|
|
|
set_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags);
|
|
|
|
else {
|
|
|
|
printk("scsi%ld: %s: detect abort needed!\n",
|
|
|
|
ha->host_no, __func__);
|
|
|
|
set_bit(DPC_RESET_HA, &ha->dpc_flags);
|
|
|
|
}
|
|
|
|
qla4xxx_wake_dpc(ha);
|
2010-07-30 16:58:07 +08:00
|
|
|
qla4xxx_mailbox_premature_completion(ha);
|
2010-07-28 18:23:44 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ha->fw_heartbeat_counter = fw_heartbeat_counter;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qla4_8xxx_watchdog - Poll dev state
|
|
|
|
* @ha: Pointer to host adapter structure.
|
|
|
|
*
|
|
|
|
* Context: Interrupt
|
|
|
|
**/
|
|
|
|
void qla4_8xxx_watchdog(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
uint32_t dev_state;
|
|
|
|
|
|
|
|
dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
|
|
|
|
|
|
|
|
/* don't poll if reset is going on */
|
|
|
|
if (!test_bit(DPC_RESET_ACTIVE, &ha->dpc_flags)) {
|
|
|
|
if (dev_state == QLA82XX_DEV_NEED_RESET &&
|
|
|
|
!test_bit(DPC_RESET_HA, &ha->dpc_flags)) {
|
|
|
|
printk("scsi%ld: %s: HW State: NEED RESET!\n",
|
|
|
|
ha->host_no, __func__);
|
|
|
|
set_bit(DPC_RESET_HA, &ha->dpc_flags);
|
|
|
|
qla4xxx_wake_dpc(ha);
|
2010-07-30 16:58:07 +08:00
|
|
|
qla4xxx_mailbox_premature_completion(ha);
|
2010-07-28 18:23:44 +08:00
|
|
|
} else if (dev_state == QLA82XX_DEV_NEED_QUIESCENT &&
|
|
|
|
!test_bit(DPC_HA_NEED_QUIESCENT, &ha->dpc_flags)) {
|
|
|
|
printk("scsi%ld: %s: HW State: NEED QUIES!\n",
|
|
|
|
ha->host_no, __func__);
|
|
|
|
set_bit(DPC_HA_NEED_QUIESCENT, &ha->dpc_flags);
|
|
|
|
qla4xxx_wake_dpc(ha);
|
|
|
|
} else {
|
|
|
|
/* Check firmware health */
|
|
|
|
qla4_8xxx_check_fw_alive(ha);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
/**
|
|
|
|
* qla4xxx_timer - checks every second for work to do.
|
|
|
|
* @ha: Pointer to host adapter structure.
|
|
|
|
**/
|
|
|
|
static void qla4xxx_timer(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
struct ddb_entry *ddb_entry, *dtemp;
|
|
|
|
int start_dpc = 0;
|
2010-07-30 17:08:47 +08:00
|
|
|
uint16_t w;
|
|
|
|
|
|
|
|
/* If we are in the middle of AER/EEH processing
|
|
|
|
* skip any processing and reschedule the timer
|
|
|
|
*/
|
|
|
|
if (test_bit(AF_EEH_BUSY, &ha->flags)) {
|
|
|
|
mod_timer(&ha->timer, jiffies + HZ);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Hardware read to trigger an EEH error during mailbox waits. */
|
|
|
|
if (!pci_channel_offline(ha->pdev))
|
|
|
|
pci_read_config_word(ha->pdev, PCI_VENDOR_ID, &w);
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
if (test_bit(AF_HBA_GOING_AWAY, &ha->flags)) {
|
|
|
|
DEBUG2(ql4_printk(KERN_INFO, ha, "%s exited. HBA GOING AWAY\n",
|
|
|
|
__func__));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_qla8022(ha)) {
|
|
|
|
qla4_8xxx_watchdog(ha);
|
|
|
|
}
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
/* Search for relogin's to time-out and port down retry. */
|
|
|
|
list_for_each_entry_safe(ddb_entry, dtemp, &ha->ddb_list, list) {
|
|
|
|
/* Count down time between sending relogins */
|
|
|
|
if (adapter_up(ha) &&
|
|
|
|
!test_bit(DF_RELOGIN, &ddb_entry->flags) &&
|
|
|
|
atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) {
|
|
|
|
if (atomic_read(&ddb_entry->retry_relogin_timer) !=
|
|
|
|
INVALID_ENTRY) {
|
|
|
|
if (atomic_read(&ddb_entry->retry_relogin_timer)
|
|
|
|
== 0) {
|
|
|
|
atomic_set(&ddb_entry->
|
|
|
|
retry_relogin_timer,
|
|
|
|
INVALID_ENTRY);
|
|
|
|
set_bit(DPC_RELOGIN_DEVICE,
|
|
|
|
&ha->dpc_flags);
|
|
|
|
set_bit(DF_RELOGIN, &ddb_entry->flags);
|
2010-07-28 18:23:44 +08:00
|
|
|
DEBUG2(printk("scsi%ld: %s: ddb [%d]"
|
2006-09-20 01:28:00 +08:00
|
|
|
" login device\n",
|
|
|
|
ha->host_no, __func__,
|
|
|
|
ddb_entry->fw_ddb_index));
|
|
|
|
} else
|
|
|
|
atomic_dec(&ddb_entry->
|
|
|
|
retry_relogin_timer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait for relogin to timeout */
|
|
|
|
if (atomic_read(&ddb_entry->relogin_timer) &&
|
|
|
|
(atomic_dec_and_test(&ddb_entry->relogin_timer) != 0)) {
|
|
|
|
/*
|
|
|
|
* If the relogin times out and the device is
|
|
|
|
* still NOT ONLINE then try and relogin again.
|
|
|
|
*/
|
|
|
|
if (atomic_read(&ddb_entry->state) !=
|
|
|
|
DDB_STATE_ONLINE &&
|
|
|
|
ddb_entry->fw_ddb_device_state ==
|
|
|
|
DDB_DS_SESSION_FAILED) {
|
|
|
|
/* Reset retry relogin timer */
|
|
|
|
atomic_inc(&ddb_entry->relogin_retry_count);
|
2010-07-28 18:23:44 +08:00
|
|
|
DEBUG2(printk("scsi%ld: ddb [%d] relogin"
|
2006-09-20 01:28:00 +08:00
|
|
|
" timed out-retrying"
|
|
|
|
" relogin (%d)\n",
|
|
|
|
ha->host_no,
|
|
|
|
ddb_entry->fw_ddb_index,
|
|
|
|
atomic_read(&ddb_entry->
|
|
|
|
relogin_retry_count))
|
|
|
|
);
|
|
|
|
start_dpc++;
|
2010-07-28 18:23:44 +08:00
|
|
|
DEBUG(printk("scsi%ld:%d:%d: ddb [%d] "
|
2006-09-20 01:28:00 +08:00
|
|
|
"initate relogin after"
|
|
|
|
" %d seconds\n",
|
|
|
|
ha->host_no, ddb_entry->bus,
|
|
|
|
ddb_entry->target,
|
|
|
|
ddb_entry->fw_ddb_index,
|
|
|
|
ddb_entry->default_time2wait + 4)
|
|
|
|
);
|
|
|
|
|
|
|
|
atomic_set(&ddb_entry->retry_relogin_timer,
|
|
|
|
ddb_entry->default_time2wait + 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
if (!is_qla8022(ha)) {
|
|
|
|
/* Check for heartbeat interval. */
|
|
|
|
if (ha->firmware_options & FWOPT_HEARTBEAT_ENABLE &&
|
|
|
|
ha->heartbeat_interval != 0) {
|
|
|
|
ha->seconds_since_last_heartbeat++;
|
|
|
|
if (ha->seconds_since_last_heartbeat >
|
|
|
|
ha->heartbeat_interval + 2)
|
|
|
|
set_bit(DPC_RESET_HA, &ha->dpc_flags);
|
|
|
|
}
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Wakeup the dpc routine for this adapter, if needed. */
|
|
|
|
if ((start_dpc ||
|
|
|
|
test_bit(DPC_RESET_HA, &ha->dpc_flags) ||
|
|
|
|
test_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags) ||
|
|
|
|
test_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags) ||
|
2010-07-28 18:23:44 +08:00
|
|
|
test_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags) ||
|
2006-09-20 01:28:00 +08:00
|
|
|
test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) ||
|
|
|
|
test_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags) ||
|
2010-04-28 14:08:11 +08:00
|
|
|
test_bit(DPC_LINK_CHANGED, &ha->dpc_flags) ||
|
2010-07-28 18:23:44 +08:00
|
|
|
test_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags) ||
|
|
|
|
test_bit(DPC_HA_NEED_QUIESCENT, &ha->dpc_flags) ||
|
2006-09-20 01:28:00 +08:00
|
|
|
test_bit(DPC_AEN, &ha->dpc_flags)) &&
|
2010-07-28 18:23:44 +08:00
|
|
|
!test_bit(AF_DPC_SCHEDULED, &ha->flags) &&
|
2006-09-20 01:28:00 +08:00
|
|
|
ha->dpc_thread) {
|
|
|
|
DEBUG2(printk("scsi%ld: %s: scheduling dpc routine"
|
|
|
|
" - dpc flags = 0x%lx\n",
|
|
|
|
ha->host_no, __func__, ha->dpc_flags));
|
2010-07-28 18:23:44 +08:00
|
|
|
qla4xxx_wake_dpc(ha);
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Reschedule timer thread to call us back in one second */
|
|
|
|
mod_timer(&ha->timer, jiffies + HZ);
|
|
|
|
|
|
|
|
DEBUG2(ha->seconds_since_last_intr++);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qla4xxx_cmd_wait - waits for all outstanding commands to complete
|
|
|
|
* @ha: Pointer to host adapter structure.
|
|
|
|
*
|
|
|
|
* This routine stalls the driver until all outstanding commands are returned.
|
|
|
|
* Caller must release the Hardware Lock prior to calling this routine.
|
|
|
|
**/
|
|
|
|
static int qla4xxx_cmd_wait(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
uint32_t index = 0;
|
|
|
|
unsigned long flags;
|
|
|
|
struct scsi_cmnd *cmd;
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
unsigned long wtime = jiffies + (WAIT_CMD_TOV * HZ);
|
|
|
|
|
|
|
|
DEBUG2(ql4_printk(KERN_INFO, ha, "Wait up to %d seconds for cmds to "
|
|
|
|
"complete\n", WAIT_CMD_TOV));
|
|
|
|
|
|
|
|
while (!time_after_eq(jiffies, wtime)) {
|
2006-09-20 01:28:00 +08:00
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
/* Find a command that hasn't completed. */
|
|
|
|
for (index = 0; index < ha->host->can_queue; index++) {
|
|
|
|
cmd = scsi_host_find_tag(ha->host, index);
|
|
|
|
if (cmd != NULL)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
|
|
|
|
/* If No Commands are pending, wait is complete */
|
2010-07-28 18:23:44 +08:00
|
|
|
if (index == ha->host->can_queue)
|
|
|
|
return QLA_SUCCESS;
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
msleep(1000);
|
|
|
|
}
|
|
|
|
/* If we timed out on waiting for commands to come back
|
|
|
|
* return ERROR. */
|
|
|
|
return QLA_ERROR;
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
int qla4xxx_hw_reset(struct scsi_qla_host *ha)
|
2006-09-20 01:28:00 +08:00
|
|
|
{
|
|
|
|
uint32_t ctrl_status;
|
2007-01-23 04:26:11 +08:00
|
|
|
unsigned long flags = 0;
|
|
|
|
|
|
|
|
DEBUG2(printk(KERN_ERR "scsi%ld: %s\n", ha->host_no, __func__));
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
if (ql4xxx_lock_drvr_wait(ha) != QLA_SUCCESS)
|
|
|
|
return QLA_ERROR;
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the SCSI Reset Interrupt bit is set, clear it.
|
|
|
|
* Otherwise, the Soft Reset won't work.
|
|
|
|
*/
|
|
|
|
ctrl_status = readw(&ha->reg->ctrl_status);
|
|
|
|
if ((ctrl_status & CSR_SCSI_RESET_INTR) != 0)
|
|
|
|
writel(set_rmask(CSR_SCSI_RESET_INTR), &ha->reg->ctrl_status);
|
|
|
|
|
|
|
|
/* Issue Soft Reset */
|
|
|
|
writel(set_rmask(CSR_SOFT_RESET), &ha->reg->ctrl_status);
|
|
|
|
readl(&ha->reg->ctrl_status);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
2010-07-28 18:23:44 +08:00
|
|
|
return QLA_SUCCESS;
|
2007-01-23 04:26:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qla4xxx_soft_reset - performs soft reset.
|
|
|
|
* @ha: Pointer to host adapter structure.
|
|
|
|
**/
|
|
|
|
int qla4xxx_soft_reset(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
uint32_t max_wait_time;
|
|
|
|
unsigned long flags = 0;
|
2010-10-07 13:48:07 +08:00
|
|
|
int status;
|
2007-01-23 04:26:11 +08:00
|
|
|
uint32_t ctrl_status;
|
|
|
|
|
2010-10-07 13:48:07 +08:00
|
|
|
status = qla4xxx_hw_reset(ha);
|
|
|
|
if (status != QLA_SUCCESS)
|
|
|
|
return status;
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2010-10-07 13:48:07 +08:00
|
|
|
status = QLA_ERROR;
|
2006-09-20 01:28:00 +08:00
|
|
|
/* Wait until the Network Reset Intr bit is cleared */
|
|
|
|
max_wait_time = RESET_INTR_TOV;
|
|
|
|
do {
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
ctrl_status = readw(&ha->reg->ctrl_status);
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
|
|
|
|
if ((ctrl_status & CSR_NET_RESET_INTR) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
msleep(1000);
|
|
|
|
} while ((--max_wait_time));
|
|
|
|
|
|
|
|
if ((ctrl_status & CSR_NET_RESET_INTR) != 0) {
|
|
|
|
DEBUG2(printk(KERN_WARNING
|
|
|
|
"scsi%ld: Network Reset Intr not cleared by "
|
|
|
|
"Network function, clearing it now!\n",
|
|
|
|
ha->host_no));
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
writel(set_rmask(CSR_NET_RESET_INTR), &ha->reg->ctrl_status);
|
|
|
|
readl(&ha->reg->ctrl_status);
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait until the firmware tells us the Soft Reset is done */
|
|
|
|
max_wait_time = SOFT_RESET_TOV;
|
|
|
|
do {
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
ctrl_status = readw(&ha->reg->ctrl_status);
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
|
|
|
|
if ((ctrl_status & CSR_SOFT_RESET) == 0) {
|
|
|
|
status = QLA_SUCCESS;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
msleep(1000);
|
|
|
|
} while ((--max_wait_time));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Also, make sure that the SCSI Reset Interrupt bit has been cleared
|
|
|
|
* after the soft reset has taken place.
|
|
|
|
*/
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
ctrl_status = readw(&ha->reg->ctrl_status);
|
|
|
|
if ((ctrl_status & CSR_SCSI_RESET_INTR) != 0) {
|
|
|
|
writel(set_rmask(CSR_SCSI_RESET_INTR), &ha->reg->ctrl_status);
|
|
|
|
readl(&ha->reg->ctrl_status);
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
|
|
|
|
/* If soft reset fails then most probably the bios on other
|
|
|
|
* function is also enabled.
|
|
|
|
* Since the initialization is sequential the other fn
|
|
|
|
* wont be able to acknowledge the soft reset.
|
|
|
|
* Issue a force soft reset to workaround this scenario.
|
|
|
|
*/
|
|
|
|
if (max_wait_time == 0) {
|
|
|
|
/* Issue Force Soft Reset */
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
writel(set_rmask(CSR_FORCE_SOFT_RESET), &ha->reg->ctrl_status);
|
|
|
|
readl(&ha->reg->ctrl_status);
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
/* Wait until the firmware tells us the Soft Reset is done */
|
|
|
|
max_wait_time = SOFT_RESET_TOV;
|
|
|
|
do {
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
ctrl_status = readw(&ha->reg->ctrl_status);
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
|
|
|
|
if ((ctrl_status & CSR_FORCE_SOFT_RESET) == 0) {
|
|
|
|
status = QLA_SUCCESS;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
msleep(1000);
|
|
|
|
} while ((--max_wait_time));
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-07-28 18:23:44 +08:00
|
|
|
* qla4xxx_abort_active_cmds - returns all outstanding i/o requests to O.S.
|
2006-09-20 01:28:00 +08:00
|
|
|
* @ha: Pointer to host adapter structure.
|
2010-07-28 18:23:44 +08:00
|
|
|
* @res: returned scsi status
|
2006-09-20 01:28:00 +08:00
|
|
|
*
|
|
|
|
* This routine is called just prior to a HARD RESET to return all
|
|
|
|
* outstanding commands back to the Operating System.
|
|
|
|
* Caller should make sure that the following locks are released
|
|
|
|
* before this calling routine: Hardware lock, and io_request_lock.
|
|
|
|
**/
|
2010-07-28 18:23:44 +08:00
|
|
|
static void qla4xxx_abort_active_cmds(struct scsi_qla_host *ha, int res)
|
2006-09-20 01:28:00 +08:00
|
|
|
{
|
|
|
|
struct srb *srb;
|
|
|
|
int i;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
for (i = 0; i < ha->host->can_queue; i++) {
|
|
|
|
srb = qla4xxx_del_from_active_array(ha, i);
|
|
|
|
if (srb != NULL) {
|
2010-07-28 18:23:44 +08:00
|
|
|
srb->cmd->result = res;
|
2010-04-28 14:12:24 +08:00
|
|
|
kref_put(&srb->srb_ref, qla4xxx_srb_compl);
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
}
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
void qla4xxx_dead_adapter_cleanup(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
clear_bit(AF_ONLINE, &ha->flags);
|
|
|
|
|
|
|
|
/* Disable the board */
|
|
|
|
ql4_printk(KERN_INFO, ha, "Disabling the board\n");
|
|
|
|
set_bit(AF_HBA_GOING_AWAY, &ha->flags);
|
|
|
|
|
|
|
|
qla4xxx_abort_active_cmds(ha, DID_NO_CONNECT << 16);
|
|
|
|
qla4xxx_mark_all_devices_missing(ha);
|
|
|
|
clear_bit(AF_INIT_DONE, &ha->flags);
|
|
|
|
}
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
/**
|
|
|
|
* qla4xxx_recover_adapter - recovers adapter after a fatal error
|
|
|
|
* @ha: Pointer to host adapter structure.
|
|
|
|
**/
|
2010-07-28 18:23:44 +08:00
|
|
|
static int qla4xxx_recover_adapter(struct scsi_qla_host *ha)
|
2006-09-20 01:28:00 +08:00
|
|
|
{
|
2010-07-28 18:23:44 +08:00
|
|
|
int status = QLA_ERROR;
|
|
|
|
uint8_t reset_chip = 0;
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
/* Stall incoming I/O until we are done */
|
2010-07-28 18:23:44 +08:00
|
|
|
scsi_block_requests(ha->host);
|
2006-09-20 01:28:00 +08:00
|
|
|
clear_bit(AF_ONLINE, &ha->flags);
|
2008-03-05 03:26:53 +08:00
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
DEBUG2(ql4_printk(KERN_INFO, ha, "%s: adapter OFFLINE\n", __func__));
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
set_bit(DPC_RESET_ACTIVE, &ha->dpc_flags);
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
if (test_bit(DPC_RESET_HA, &ha->dpc_flags))
|
|
|
|
reset_chip = 1;
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
/* For the DPC_RESET_HA_INTR case (ISP-4xxx specific)
|
|
|
|
* do not reset adapter, jump to initialize_adapter */
|
|
|
|
if (test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags)) {
|
|
|
|
status = QLA_SUCCESS;
|
|
|
|
goto recover_ha_init_adapter;
|
|
|
|
}
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
/* For the ISP-82xx adapter, issue a stop_firmware if invoked
|
|
|
|
* from eh_host_reset or ioctl module */
|
|
|
|
if (is_qla8022(ha) && !reset_chip &&
|
|
|
|
test_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags)) {
|
|
|
|
|
|
|
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
|
|
|
"scsi%ld: %s - Performing stop_firmware...\n",
|
|
|
|
ha->host_no, __func__));
|
|
|
|
status = ha->isp_ops->reset_firmware(ha);
|
|
|
|
if (status == QLA_SUCCESS) {
|
2010-10-07 13:49:20 +08:00
|
|
|
if (!test_bit(AF_FW_RECOVERY, &ha->flags))
|
|
|
|
qla4xxx_cmd_wait(ha);
|
2010-07-28 18:23:44 +08:00
|
|
|
ha->isp_ops->disable_intrs(ha);
|
|
|
|
qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
|
|
|
|
qla4xxx_abort_active_cmds(ha, DID_RESET << 16);
|
|
|
|
} else {
|
|
|
|
/* If the stop_firmware fails then
|
|
|
|
* reset the entire chip */
|
|
|
|
reset_chip = 1;
|
|
|
|
clear_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags);
|
|
|
|
set_bit(DPC_RESET_HA, &ha->dpc_flags);
|
|
|
|
}
|
|
|
|
}
|
2009-07-16 04:03:00 +08:00
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
/* Issue full chip reset if recovering from a catastrophic error,
|
|
|
|
* or if stop_firmware fails for ISP-82xx.
|
|
|
|
* This is the default case for ISP-4xxx */
|
|
|
|
if (!is_qla8022(ha) || reset_chip) {
|
2010-10-07 13:49:20 +08:00
|
|
|
if (!test_bit(AF_FW_RECOVERY, &ha->flags))
|
|
|
|
qla4xxx_cmd_wait(ha);
|
2010-07-28 18:23:44 +08:00
|
|
|
qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
|
|
|
|
qla4xxx_abort_active_cmds(ha, DID_RESET << 16);
|
|
|
|
DEBUG2(ql4_printk(KERN_INFO, ha,
|
|
|
|
"scsi%ld: %s - Performing chip reset..\n",
|
|
|
|
ha->host_no, __func__));
|
|
|
|
status = ha->isp_ops->reset_chip(ha);
|
|
|
|
}
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
/* Flush any pending ddb changed AENs */
|
|
|
|
qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
recover_ha_init_adapter:
|
|
|
|
/* Upon successful firmware/chip reset, re-initialize the adapter */
|
2006-09-20 01:28:00 +08:00
|
|
|
if (status == QLA_SUCCESS) {
|
2010-07-28 18:23:44 +08:00
|
|
|
/* For ISP-4xxx, force function 1 to always initialize
|
|
|
|
* before function 3 to prevent both funcions from
|
|
|
|
* stepping on top of the other */
|
|
|
|
if (!is_qla8022(ha) && (ha->mac_index == 3))
|
|
|
|
ssleep(6);
|
|
|
|
|
|
|
|
/* NOTE: AF_ONLINE flag set upon successful completion of
|
|
|
|
* qla4xxx_initialize_adapter */
|
|
|
|
status = qla4xxx_initialize_adapter(ha, PRESERVE_DDB_LIST);
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
/* Retry failed adapter initialization, if necessary
|
|
|
|
* Do not retry initialize_adapter for RESET_HA_INTR (ISP-4xxx specific)
|
|
|
|
* case to prevent ping-pong resets between functions */
|
|
|
|
if (!test_bit(AF_ONLINE, &ha->flags) &&
|
|
|
|
!test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags)) {
|
2006-09-20 01:28:00 +08:00
|
|
|
/* Adapter initialization failed, see if we can retry
|
2010-07-28 18:23:44 +08:00
|
|
|
* resetting the ha.
|
|
|
|
* Since we don't want to block the DPC for too long
|
|
|
|
* with multiple resets in the same thread,
|
|
|
|
* utilize DPC to retry */
|
2006-09-20 01:28:00 +08:00
|
|
|
if (!test_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags)) {
|
|
|
|
ha->retry_reset_ha_cnt = MAX_RESET_HA_RETRIES;
|
|
|
|
DEBUG2(printk("scsi%ld: recover adapter - retrying "
|
|
|
|
"(%d) more times\n", ha->host_no,
|
|
|
|
ha->retry_reset_ha_cnt));
|
|
|
|
set_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags);
|
|
|
|
status = QLA_ERROR;
|
|
|
|
} else {
|
|
|
|
if (ha->retry_reset_ha_cnt > 0) {
|
|
|
|
/* Schedule another Reset HA--DPC will retry */
|
|
|
|
ha->retry_reset_ha_cnt--;
|
|
|
|
DEBUG2(printk("scsi%ld: recover adapter - "
|
|
|
|
"retry remaining %d\n",
|
|
|
|
ha->host_no,
|
|
|
|
ha->retry_reset_ha_cnt));
|
|
|
|
status = QLA_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ha->retry_reset_ha_cnt == 0) {
|
|
|
|
/* Recover adapter retries have been exhausted.
|
|
|
|
* Adapter DEAD */
|
|
|
|
DEBUG2(printk("scsi%ld: recover adapter "
|
|
|
|
"failed - board disabled\n",
|
|
|
|
ha->host_no));
|
2010-07-28 18:23:44 +08:00
|
|
|
qla4xxx_dead_adapter_cleanup(ha);
|
2006-09-20 01:28:00 +08:00
|
|
|
clear_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags);
|
|
|
|
clear_bit(DPC_RESET_HA, &ha->dpc_flags);
|
2010-07-28 18:23:44 +08:00
|
|
|
clear_bit(DPC_RESET_HA_FW_CONTEXT,
|
2006-09-20 01:28:00 +08:00
|
|
|
&ha->dpc_flags);
|
|
|
|
status = QLA_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
clear_bit(DPC_RESET_HA, &ha->dpc_flags);
|
2010-07-28 18:23:44 +08:00
|
|
|
clear_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags);
|
2006-09-20 01:28:00 +08:00
|
|
|
clear_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
ha->adapter_error_count++;
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
if (test_bit(AF_ONLINE, &ha->flags))
|
|
|
|
ha->isp_ops->enable_intrs(ha);
|
|
|
|
|
|
|
|
scsi_unblock_requests(ha->host);
|
|
|
|
|
|
|
|
clear_bit(DPC_RESET_ACTIVE, &ha->dpc_flags);
|
|
|
|
DEBUG2(printk("scsi%ld: recover adapter: %s\n", ha->host_no,
|
|
|
|
status == QLA_ERROR ? "FAILED" : "SUCCEDED"));
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
void qla4xxx_wake_dpc(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
if (ha->dpc_thread &&
|
|
|
|
!test_bit(AF_DPC_SCHEDULED, &ha->flags)) {
|
|
|
|
set_bit(AF_DPC_SCHEDULED, &ha->flags);
|
|
|
|
queue_work(ha->dpc_thread, &ha->dpc_work);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
/**
|
|
|
|
* qla4xxx_do_dpc - dpc routine
|
|
|
|
* @data: in our case pointer to adapter structure
|
|
|
|
*
|
|
|
|
* This routine is a task that is schedule by the interrupt handler
|
|
|
|
* to perform the background processing for interrupts. We put it
|
|
|
|
* on a task queue that is consumed whenever the scheduler runs; that's
|
|
|
|
* so you can do anything (i.e. put the process to sleep etc). In fact,
|
|
|
|
* the mid-level tries to sleep when it reaches the driver threshold
|
|
|
|
* "host->can_queue". This can cause a panic if we were in our interrupt code.
|
|
|
|
**/
|
2006-11-22 22:57:56 +08:00
|
|
|
static void qla4xxx_do_dpc(struct work_struct *work)
|
2006-09-20 01:28:00 +08:00
|
|
|
{
|
2006-11-22 22:57:56 +08:00
|
|
|
struct scsi_qla_host *ha =
|
|
|
|
container_of(work, struct scsi_qla_host, dpc_work);
|
2006-09-20 01:28:00 +08:00
|
|
|
struct ddb_entry *ddb_entry, *dtemp;
|
2007-01-23 04:26:11 +08:00
|
|
|
int status = QLA_ERROR;
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2006-11-16 08:41:09 +08:00
|
|
|
DEBUG2(printk("scsi%ld: %s: DPC handler waking up."
|
2010-07-28 18:23:44 +08:00
|
|
|
"flags = 0x%08lx, dpc_flags = 0x%08lx\n",
|
|
|
|
ha->host_no, __func__, ha->flags, ha->dpc_flags))
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
/* Initialization not yet finished. Don't do anything yet. */
|
|
|
|
if (!test_bit(AF_INIT_DONE, &ha->flags))
|
2010-07-30 16:57:19 +08:00
|
|
|
goto do_dpc_exit;
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2010-07-30 17:08:47 +08:00
|
|
|
if (test_bit(AF_EEH_BUSY, &ha->flags)) {
|
|
|
|
DEBUG2(printk(KERN_INFO "scsi%ld: %s: flags = %lx\n",
|
|
|
|
ha->host_no, __func__, ha->flags));
|
|
|
|
goto do_dpc_exit;
|
|
|
|
}
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
/* HBA is in the process of being permanently disabled.
|
|
|
|
* Don't process anything */
|
|
|
|
if (test_bit(AF_HBA_GOING_AWAY, &ha->flags))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (is_qla8022(ha)) {
|
|
|
|
if (test_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags)) {
|
|
|
|
qla4_8xxx_idc_lock(ha);
|
|
|
|
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
|
|
|
QLA82XX_DEV_FAILED);
|
|
|
|
qla4_8xxx_idc_unlock(ha);
|
|
|
|
ql4_printk(KERN_INFO, ha, "HW State: FAILED\n");
|
|
|
|
qla4_8xxx_device_state_handler(ha);
|
|
|
|
}
|
|
|
|
if (test_and_clear_bit(DPC_HA_NEED_QUIESCENT, &ha->dpc_flags)) {
|
|
|
|
qla4_8xxx_need_qsnt_handler(ha);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!test_bit(DPC_RESET_ACTIVE, &ha->dpc_flags) &&
|
|
|
|
(test_bit(DPC_RESET_HA, &ha->dpc_flags) ||
|
2006-09-20 01:28:00 +08:00
|
|
|
test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) ||
|
2010-07-28 18:23:44 +08:00
|
|
|
test_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags))) {
|
|
|
|
if (ql4xdontresethba) {
|
|
|
|
DEBUG2(printk("scsi%ld: %s: Don't Reset HBA\n",
|
|
|
|
ha->host_no, __func__));
|
|
|
|
clear_bit(DPC_RESET_HA, &ha->dpc_flags);
|
|
|
|
clear_bit(DPC_RESET_HA_INTR, &ha->dpc_flags);
|
|
|
|
clear_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags);
|
|
|
|
goto dpc_post_reset_ha;
|
|
|
|
}
|
|
|
|
if (test_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags) ||
|
|
|
|
test_bit(DPC_RESET_HA, &ha->dpc_flags))
|
|
|
|
qla4xxx_recover_adapter(ha);
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2007-01-23 04:26:11 +08:00
|
|
|
if (test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags)) {
|
2006-09-20 01:28:00 +08:00
|
|
|
uint8_t wait_time = RESET_INTR_TOV;
|
|
|
|
|
|
|
|
while ((readw(&ha->reg->ctrl_status) &
|
|
|
|
(CSR_SOFT_RESET | CSR_FORCE_SOFT_RESET)) != 0) {
|
|
|
|
if (--wait_time == 0)
|
|
|
|
break;
|
|
|
|
msleep(1000);
|
|
|
|
}
|
|
|
|
if (wait_time == 0)
|
|
|
|
DEBUG2(printk("scsi%ld: %s: SR|FSR "
|
|
|
|
"bit not cleared-- resetting\n",
|
|
|
|
ha->host_no, __func__));
|
2010-07-28 18:23:44 +08:00
|
|
|
qla4xxx_abort_active_cmds(ha, DID_RESET << 16);
|
2007-01-23 04:26:11 +08:00
|
|
|
if (ql4xxx_lock_drvr_wait(ha) == QLA_SUCCESS) {
|
|
|
|
qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
|
2010-07-28 18:23:44 +08:00
|
|
|
status = qla4xxx_recover_adapter(ha);
|
2007-01-23 04:26:11 +08:00
|
|
|
}
|
|
|
|
clear_bit(DPC_RESET_HA_INTR, &ha->dpc_flags);
|
|
|
|
if (status == QLA_SUCCESS)
|
2010-07-28 18:23:44 +08:00
|
|
|
ha->isp_ops->enable_intrs(ha);
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
dpc_post_reset_ha:
|
2006-09-20 01:28:00 +08:00
|
|
|
/* ---- process AEN? --- */
|
|
|
|
if (test_and_clear_bit(DPC_AEN, &ha->dpc_flags))
|
|
|
|
qla4xxx_process_aen(ha, PROCESS_ALL_AENS);
|
|
|
|
|
|
|
|
/* ---- Get DHCP IP Address? --- */
|
|
|
|
if (test_and_clear_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags))
|
|
|
|
qla4xxx_get_dhcp_ip_address(ha);
|
|
|
|
|
2010-04-28 14:08:11 +08:00
|
|
|
/* ---- link change? --- */
|
|
|
|
if (test_and_clear_bit(DPC_LINK_CHANGED, &ha->dpc_flags)) {
|
|
|
|
if (!test_bit(AF_LINK_UP, &ha->flags)) {
|
|
|
|
/* ---- link down? --- */
|
|
|
|
list_for_each_entry_safe(ddb_entry, dtemp,
|
|
|
|
&ha->ddb_list, list) {
|
|
|
|
if (atomic_read(&ddb_entry->state) ==
|
|
|
|
DDB_STATE_ONLINE)
|
|
|
|
qla4xxx_mark_device_missing(ha,
|
|
|
|
ddb_entry);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* ---- link up? --- *
|
|
|
|
* F/W will auto login to all devices ONLY ONCE after
|
|
|
|
* link up during driver initialization and runtime
|
|
|
|
* fatal error recovery. Therefore, the driver must
|
|
|
|
* manually relogin to devices when recovering from
|
|
|
|
* connection failures, logouts, expired KATO, etc. */
|
|
|
|
|
|
|
|
list_for_each_entry_safe(ddb_entry, dtemp,
|
|
|
|
&ha->ddb_list, list) {
|
|
|
|
if ((atomic_read(&ddb_entry->state) ==
|
|
|
|
DDB_STATE_MISSING) ||
|
|
|
|
(atomic_read(&ddb_entry->state) ==
|
|
|
|
DDB_STATE_DEAD)) {
|
|
|
|
if (ddb_entry->fw_ddb_device_state ==
|
|
|
|
DDB_DS_SESSION_ACTIVE) {
|
|
|
|
atomic_set(&ddb_entry->state,
|
|
|
|
DDB_STATE_ONLINE);
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_INFO, ha,
|
2010-04-28 14:08:11 +08:00
|
|
|
"scsi%ld: %s: ddb[%d]"
|
2010-07-28 18:23:44 +08:00
|
|
|
" marked ONLINE\n",
|
2010-04-28 14:08:11 +08:00
|
|
|
ha->host_no, __func__,
|
2010-07-28 18:23:44 +08:00
|
|
|
ddb_entry->fw_ddb_index);
|
2010-04-28 14:08:11 +08:00
|
|
|
|
|
|
|
iscsi_unblock_session(
|
|
|
|
ddb_entry->sess);
|
|
|
|
} else
|
|
|
|
qla4xxx_relogin_device(
|
|
|
|
ha, ddb_entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
/* ---- relogin device? --- */
|
|
|
|
if (adapter_up(ha) &&
|
|
|
|
test_and_clear_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags)) {
|
|
|
|
list_for_each_entry_safe(ddb_entry, dtemp,
|
|
|
|
&ha->ddb_list, list) {
|
|
|
|
if (test_and_clear_bit(DF_RELOGIN, &ddb_entry->flags) &&
|
|
|
|
atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE)
|
|
|
|
qla4xxx_relogin_device(ha, ddb_entry);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If mbx cmd times out there is no point
|
|
|
|
* in continuing further.
|
|
|
|
* With large no of targets this can hang
|
|
|
|
* the system.
|
|
|
|
*/
|
|
|
|
if (test_bit(DPC_RESET_HA, &ha->dpc_flags)) {
|
|
|
|
printk(KERN_WARNING "scsi%ld: %s: "
|
|
|
|
"need to reset hba\n",
|
|
|
|
ha->host_no, __func__);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-07-30 16:57:19 +08:00
|
|
|
|
|
|
|
do_dpc_exit:
|
2010-07-28 18:23:44 +08:00
|
|
|
clear_bit(AF_DPC_SCHEDULED, &ha->flags);
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qla4xxx_free_adapter - release the adapter
|
|
|
|
* @ha: pointer to adapter structure
|
|
|
|
**/
|
|
|
|
static void qla4xxx_free_adapter(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (test_bit(AF_INTERRUPTS_ON, &ha->flags)) {
|
|
|
|
/* Turn-off interrupts on the card. */
|
2010-07-28 18:23:44 +08:00
|
|
|
ha->isp_ops->disable_intrs(ha);
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
/* Remove timer thread, if present */
|
|
|
|
if (ha->timer_active)
|
|
|
|
qla4xxx_stop_timer(ha);
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
/* Kill the kernel thread for this host */
|
|
|
|
if (ha->dpc_thread)
|
|
|
|
destroy_workqueue(ha->dpc_thread);
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
/* Put firmware in known state */
|
|
|
|
ha->isp_ops->reset_firmware(ha);
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
if (is_qla8022(ha)) {
|
|
|
|
qla4_8xxx_idc_lock(ha);
|
|
|
|
qla4_8xxx_clear_drv_active(ha);
|
|
|
|
qla4_8xxx_idc_unlock(ha);
|
|
|
|
}
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
/* Detach interrupts */
|
|
|
|
if (test_and_clear_bit(AF_IRQ_ATTACHED, &ha->flags))
|
2010-07-28 18:23:44 +08:00
|
|
|
qla4xxx_free_irqs(ha);
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2007-05-24 09:03:32 +08:00
|
|
|
/* free extra memory */
|
|
|
|
qla4xxx_mem_free(ha);
|
2010-07-28 18:23:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int qla4_8xxx_iospace_config(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
int status = 0;
|
|
|
|
uint8_t revision_id;
|
|
|
|
unsigned long mem_base, mem_len, db_base, db_len;
|
|
|
|
struct pci_dev *pdev = ha->pdev;
|
|
|
|
|
|
|
|
status = pci_request_regions(pdev, DRIVER_NAME);
|
|
|
|
if (status) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"scsi(%ld) Failed to reserve PIO regions (%s) "
|
|
|
|
"status=%d\n", ha->host_no, pci_name(pdev), status);
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
pci_read_config_byte(pdev, PCI_REVISION_ID, &revision_id);
|
|
|
|
DEBUG2(printk(KERN_INFO "%s: revision-id=%d\n",
|
|
|
|
__func__, revision_id));
|
|
|
|
ha->revision_id = revision_id;
|
2007-05-24 09:03:32 +08:00
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
/* remap phys address */
|
|
|
|
mem_base = pci_resource_start(pdev, 0); /* 0 is for BAR 0 */
|
|
|
|
mem_len = pci_resource_len(pdev, 0);
|
|
|
|
DEBUG2(printk(KERN_INFO "%s: ioremap from %lx a size of %lx\n",
|
|
|
|
__func__, mem_base, mem_len));
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
/* mapping of pcibase pointer */
|
|
|
|
ha->nx_pcibase = (unsigned long)ioremap(mem_base, mem_len);
|
|
|
|
if (!ha->nx_pcibase) {
|
|
|
|
printk(KERN_ERR
|
|
|
|
"cannot remap MMIO (%s), aborting\n", pci_name(pdev));
|
|
|
|
pci_release_regions(ha->pdev);
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Mapping of IO base pointer, door bell read and write pointer */
|
|
|
|
|
|
|
|
/* mapping of IO base pointer */
|
|
|
|
ha->qla4_8xxx_reg =
|
|
|
|
(struct device_reg_82xx __iomem *)((uint8_t *)ha->nx_pcibase +
|
|
|
|
0xbc000 + (ha->pdev->devfn << 11));
|
|
|
|
|
|
|
|
db_base = pci_resource_start(pdev, 4); /* doorbell is on bar 4 */
|
|
|
|
db_len = pci_resource_len(pdev, 4);
|
|
|
|
|
|
|
|
/* mapping of doorbell write pointer */
|
|
|
|
ha->nx_db_wr_ptr = (unsigned long)ioremap(db_base +
|
|
|
|
(ha->pdev->devfn << 12), 4);
|
|
|
|
if (!ha->nx_db_wr_ptr) {
|
|
|
|
printk(KERN_ERR
|
|
|
|
"cannot remap MMIO doorbell-write (%s), aborting\n",
|
|
|
|
pci_name(pdev));
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
|
|
|
/* mapping of doorbell read pointer */
|
|
|
|
ha->nx_db_rd_ptr = (uint8_t *) ha->nx_pcibase + (512 * 1024) +
|
|
|
|
(ha->pdev->devfn * 8);
|
|
|
|
if (!ha->nx_db_rd_ptr)
|
|
|
|
printk(KERN_ERR
|
|
|
|
"cannot remap MMIO doorbell-read (%s), aborting\n",
|
|
|
|
pci_name(pdev));
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
iospace_error_exit:
|
|
|
|
return -ENOMEM;
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
* qla4xxx_iospace_config - maps registers
|
|
|
|
* @ha: pointer to adapter structure
|
|
|
|
*
|
|
|
|
* This routines maps HBA's registers from the pci address space
|
|
|
|
* into the kernel virtual address space for memory mapped i/o.
|
|
|
|
**/
|
2010-07-28 18:23:44 +08:00
|
|
|
int qla4xxx_iospace_config(struct scsi_qla_host *ha)
|
2006-09-20 01:28:00 +08:00
|
|
|
{
|
|
|
|
unsigned long pio, pio_len, pio_flags;
|
|
|
|
unsigned long mmio, mmio_len, mmio_flags;
|
|
|
|
|
|
|
|
pio = pci_resource_start(ha->pdev, 0);
|
|
|
|
pio_len = pci_resource_len(ha->pdev, 0);
|
|
|
|
pio_flags = pci_resource_flags(ha->pdev, 0);
|
|
|
|
if (pio_flags & IORESOURCE_IO) {
|
|
|
|
if (pio_len < MIN_IOBASE_LEN) {
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_WARNING, ha,
|
2006-09-20 01:28:00 +08:00
|
|
|
"Invalid PCI I/O region size\n");
|
|
|
|
pio = 0;
|
|
|
|
}
|
|
|
|
} else {
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_WARNING, ha, "region #0 not a PIO resource\n");
|
2006-09-20 01:28:00 +08:00
|
|
|
pio = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Use MMIO operations for all accesses. */
|
|
|
|
mmio = pci_resource_start(ha->pdev, 1);
|
|
|
|
mmio_len = pci_resource_len(ha->pdev, 1);
|
|
|
|
mmio_flags = pci_resource_flags(ha->pdev, 1);
|
|
|
|
|
|
|
|
if (!(mmio_flags & IORESOURCE_MEM)) {
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_ERR, ha,
|
|
|
|
"region #0 not an MMIO resource, aborting\n");
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
2010-07-10 17:21:02 +08:00
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
if (mmio_len < MIN_IOBASE_LEN) {
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_ERR, ha,
|
|
|
|
"Invalid PCI mem region size, aborting\n");
|
2006-09-20 01:28:00 +08:00
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pci_request_regions(ha->pdev, DRIVER_NAME)) {
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_WARNING, ha,
|
|
|
|
"Failed to reserve PIO/MMIO regions\n");
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
ha->pio_address = pio;
|
|
|
|
ha->pio_length = pio_len;
|
|
|
|
ha->reg = ioremap(mmio, MIN_IOBASE_LEN);
|
|
|
|
if (!ha->reg) {
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_ERR, ha,
|
|
|
|
"cannot remap MMIO, aborting\n");
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
iospace_error_exit:
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
static struct isp_operations qla4xxx_isp_ops = {
|
|
|
|
.iospace_config = qla4xxx_iospace_config,
|
|
|
|
.pci_config = qla4xxx_pci_config,
|
|
|
|
.disable_intrs = qla4xxx_disable_intrs,
|
|
|
|
.enable_intrs = qla4xxx_enable_intrs,
|
|
|
|
.start_firmware = qla4xxx_start_firmware,
|
|
|
|
.intr_handler = qla4xxx_intr_handler,
|
|
|
|
.interrupt_service_routine = qla4xxx_interrupt_service_routine,
|
|
|
|
.reset_chip = qla4xxx_soft_reset,
|
|
|
|
.reset_firmware = qla4xxx_hw_reset,
|
|
|
|
.queue_iocb = qla4xxx_queue_iocb,
|
|
|
|
.complete_iocb = qla4xxx_complete_iocb,
|
|
|
|
.rd_shdw_req_q_out = qla4xxx_rd_shdw_req_q_out,
|
|
|
|
.rd_shdw_rsp_q_in = qla4xxx_rd_shdw_rsp_q_in,
|
|
|
|
.get_sys_info = qla4xxx_get_sys_info,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct isp_operations qla4_8xxx_isp_ops = {
|
|
|
|
.iospace_config = qla4_8xxx_iospace_config,
|
|
|
|
.pci_config = qla4_8xxx_pci_config,
|
|
|
|
.disable_intrs = qla4_8xxx_disable_intrs,
|
|
|
|
.enable_intrs = qla4_8xxx_enable_intrs,
|
|
|
|
.start_firmware = qla4_8xxx_load_risc,
|
|
|
|
.intr_handler = qla4_8xxx_intr_handler,
|
|
|
|
.interrupt_service_routine = qla4_8xxx_interrupt_service_routine,
|
|
|
|
.reset_chip = qla4_8xxx_isp_reset,
|
|
|
|
.reset_firmware = qla4_8xxx_stop_firmware,
|
|
|
|
.queue_iocb = qla4_8xxx_queue_iocb,
|
|
|
|
.complete_iocb = qla4_8xxx_complete_iocb,
|
|
|
|
.rd_shdw_req_q_out = qla4_8xxx_rd_shdw_req_q_out,
|
|
|
|
.rd_shdw_rsp_q_in = qla4_8xxx_rd_shdw_rsp_q_in,
|
|
|
|
.get_sys_info = qla4_8xxx_get_sys_info,
|
|
|
|
};
|
|
|
|
|
|
|
|
uint16_t qla4xxx_rd_shdw_req_q_out(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
return (uint16_t)le32_to_cpu(ha->shadow_regs->req_q_out);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t qla4_8xxx_rd_shdw_req_q_out(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
return (uint16_t)le32_to_cpu(readl(&ha->qla4_8xxx_reg->req_q_out));
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t qla4xxx_rd_shdw_rsp_q_in(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
return (uint16_t)le32_to_cpu(ha->shadow_regs->rsp_q_in);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t qla4_8xxx_rd_shdw_rsp_q_in(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
return (uint16_t)le32_to_cpu(readl(&ha->qla4_8xxx_reg->rsp_q_in));
|
|
|
|
}
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
/**
|
|
|
|
* qla4xxx_probe_adapter - callback function to probe HBA
|
|
|
|
* @pdev: pointer to pci_dev structure
|
|
|
|
* @pci_device_id: pointer to pci_device entry
|
|
|
|
*
|
|
|
|
* This routine will probe for Qlogic 4xxx iSCSI host adapters.
|
|
|
|
* It returns zero if successful. It also initializes all data necessary for
|
|
|
|
* the driver.
|
|
|
|
**/
|
|
|
|
static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
|
|
|
|
const struct pci_device_id *ent)
|
|
|
|
{
|
|
|
|
int ret = -ENODEV, status;
|
|
|
|
struct Scsi_Host *host;
|
|
|
|
struct scsi_qla_host *ha;
|
|
|
|
uint8_t init_retry_count = 0;
|
|
|
|
char buf[34];
|
2010-07-28 18:23:44 +08:00
|
|
|
struct qla4_8xxx_legacy_intr_set *nx_legacy_intr;
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
if (pci_enable_device(pdev))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
host = scsi_host_alloc(&qla4xxx_driver_template, sizeof(*ha));
|
|
|
|
if (host == NULL) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"qla4xxx: Couldn't allocate host from scsi layer!\n");
|
|
|
|
goto probe_disable_device;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear our data area */
|
|
|
|
ha = (struct scsi_qla_host *) host->hostdata;
|
|
|
|
memset(ha, 0, sizeof(*ha));
|
|
|
|
|
|
|
|
/* Save the information from PCI BIOS. */
|
|
|
|
ha->pdev = pdev;
|
|
|
|
ha->host = host;
|
|
|
|
ha->host_no = host->host_no;
|
|
|
|
|
2010-07-30 17:08:47 +08:00
|
|
|
pci_enable_pcie_error_reporting(pdev);
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
/* Setup Runtime configurable options */
|
|
|
|
if (is_qla8022(ha)) {
|
|
|
|
ha->isp_ops = &qla4_8xxx_isp_ops;
|
|
|
|
rwlock_init(&ha->hw_lock);
|
|
|
|
ha->qdr_sn_window = -1;
|
|
|
|
ha->ddr_mn_window = -1;
|
|
|
|
ha->curr_window = 255;
|
|
|
|
ha->func_num = PCI_FUNC(ha->pdev->devfn);
|
|
|
|
nx_legacy_intr = &legacy_intr[ha->func_num];
|
|
|
|
ha->nx_legacy_intr.int_vec_bit = nx_legacy_intr->int_vec_bit;
|
|
|
|
ha->nx_legacy_intr.tgt_status_reg =
|
|
|
|
nx_legacy_intr->tgt_status_reg;
|
|
|
|
ha->nx_legacy_intr.tgt_mask_reg = nx_legacy_intr->tgt_mask_reg;
|
|
|
|
ha->nx_legacy_intr.pci_int_reg = nx_legacy_intr->pci_int_reg;
|
|
|
|
} else {
|
|
|
|
ha->isp_ops = &qla4xxx_isp_ops;
|
|
|
|
}
|
|
|
|
|
2010-07-30 17:08:47 +08:00
|
|
|
/* Set EEH reset type to fundamental if required by hba */
|
|
|
|
if (is_qla8022(ha))
|
|
|
|
pdev->needs_freset = 1;
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
/* Configure PCI I/O space. */
|
2010-07-28 18:23:44 +08:00
|
|
|
ret = ha->isp_ops->iospace_config(ha);
|
2006-09-20 01:28:00 +08:00
|
|
|
if (ret)
|
2010-07-28 18:23:44 +08:00
|
|
|
goto probe_failed_ioconfig;
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_INFO, ha, "Found an ISP%04x, irq %d, iobase 0x%p\n",
|
2006-09-20 01:28:00 +08:00
|
|
|
pdev->device, pdev->irq, ha->reg);
|
|
|
|
|
|
|
|
qla4xxx_config_dma_addressing(ha);
|
|
|
|
|
|
|
|
/* Initialize lists and spinlocks. */
|
|
|
|
INIT_LIST_HEAD(&ha->ddb_list);
|
|
|
|
INIT_LIST_HEAD(&ha->free_srb_q);
|
|
|
|
|
|
|
|
mutex_init(&ha->mbox_sem);
|
2010-07-28 18:23:44 +08:00
|
|
|
init_completion(&ha->mbx_intr_comp);
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
spin_lock_init(&ha->hardware_lock);
|
|
|
|
|
|
|
|
/* Allocate dma buffers */
|
|
|
|
if (qla4xxx_mem_alloc(ha)) {
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_WARNING, ha,
|
|
|
|
"[ERROR] Failed to allocate memory for adapter\n");
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto probe_failed;
|
|
|
|
}
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
if (is_qla8022(ha))
|
|
|
|
(void) qla4_8xxx_get_flash_info(ha);
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
/*
|
|
|
|
* Initialize the Host adapter request/response queues and
|
|
|
|
* firmware
|
|
|
|
* NOTE: interrupts enabled upon successful completion
|
|
|
|
*/
|
|
|
|
status = qla4xxx_initialize_adapter(ha, REBUILD_DDB_LIST);
|
2010-07-28 18:23:44 +08:00
|
|
|
while ((!test_bit(AF_ONLINE, &ha->flags)) &&
|
|
|
|
init_retry_count++ < MAX_INIT_RETRIES) {
|
2006-09-20 01:28:00 +08:00
|
|
|
DEBUG2(printk("scsi: %s: retrying adapter initialization "
|
|
|
|
"(%d)\n", __func__, init_retry_count));
|
2010-07-28 18:23:44 +08:00
|
|
|
|
|
|
|
if (ha->isp_ops->reset_chip(ha) == QLA_ERROR)
|
|
|
|
continue;
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
status = qla4xxx_initialize_adapter(ha, REBUILD_DDB_LIST);
|
|
|
|
}
|
2010-07-28 18:23:44 +08:00
|
|
|
|
|
|
|
if (!test_bit(AF_ONLINE, &ha->flags)) {
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_WARNING, ha, "Failed to initialize adapter\n");
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto probe_failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
host->cmd_per_lun = 3;
|
|
|
|
host->max_channel = 0;
|
|
|
|
host->max_lun = MAX_LUNS - 1;
|
|
|
|
host->max_id = MAX_TARGETS;
|
|
|
|
host->max_cmd_len = IOCB_MAX_CDB_LEN;
|
|
|
|
host->can_queue = MAX_SRBS ;
|
|
|
|
host->transportt = qla4xxx_scsi_transport;
|
|
|
|
|
|
|
|
ret = scsi_init_shared_tag_map(host, MAX_SRBS);
|
|
|
|
if (ret) {
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_WARNING, ha,
|
|
|
|
"scsi_init_shared_tag_map failed\n");
|
|
|
|
goto probe_failed;
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Startup the kernel thread for this host adapter. */
|
|
|
|
DEBUG2(printk("scsi: %s: Starting kernel thread for "
|
|
|
|
"qla4xxx_dpc\n", __func__));
|
|
|
|
sprintf(buf, "qla4xxx_%lu_dpc", ha->host_no);
|
|
|
|
ha->dpc_thread = create_singlethread_workqueue(buf);
|
|
|
|
if (!ha->dpc_thread) {
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_WARNING, ha, "Unable to start DPC thread!\n");
|
2006-09-20 01:28:00 +08:00
|
|
|
ret = -ENODEV;
|
|
|
|
goto probe_failed;
|
|
|
|
}
|
2006-11-22 22:57:56 +08:00
|
|
|
INIT_WORK(&ha->dpc_work, qla4xxx_do_dpc);
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
/* For ISP-82XX, request_irqs is called in qla4_8xxx_load_risc
|
|
|
|
* (which is called indirectly by qla4xxx_initialize_adapter),
|
|
|
|
* so that irqs will be registered after crbinit but before
|
|
|
|
* mbx_intr_enable.
|
|
|
|
*/
|
|
|
|
if (!is_qla8022(ha)) {
|
|
|
|
ret = qla4xxx_request_irqs(ha);
|
|
|
|
if (ret) {
|
|
|
|
ql4_printk(KERN_WARNING, ha, "Failed to reserve "
|
|
|
|
"interrupt %d already in use.\n", pdev->irq);
|
|
|
|
goto probe_failed;
|
|
|
|
}
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
|
2010-07-30 17:08:47 +08:00
|
|
|
pci_save_state(ha->pdev);
|
2010-07-28 18:23:44 +08:00
|
|
|
ha->isp_ops->enable_intrs(ha);
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
/* Start timer thread. */
|
|
|
|
qla4xxx_start_timer(ha, qla4xxx_timer, 1);
|
|
|
|
|
|
|
|
set_bit(AF_INIT_DONE, &ha->flags);
|
|
|
|
|
|
|
|
pci_set_drvdata(pdev, ha);
|
|
|
|
|
|
|
|
ret = scsi_add_host(host, &pdev->dev);
|
|
|
|
if (ret)
|
|
|
|
goto probe_failed;
|
|
|
|
|
|
|
|
printk(KERN_INFO
|
|
|
|
" QLogic iSCSI HBA Driver version: %s\n"
|
|
|
|
" QLogic ISP%04x @ %s, host#=%ld, fw=%02d.%02d.%02d.%02d\n",
|
|
|
|
qla4xxx_version_str, ha->pdev->device, pci_name(ha->pdev),
|
|
|
|
ha->host_no, ha->firmware_version[0], ha->firmware_version[1],
|
|
|
|
ha->patch_number, ha->build_number);
|
2008-02-01 03:36:49 +08:00
|
|
|
scsi_scan_host(host);
|
2006-09-20 01:28:00 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
probe_failed:
|
|
|
|
qla4xxx_free_adapter(ha);
|
2010-07-28 18:23:44 +08:00
|
|
|
|
|
|
|
probe_failed_ioconfig:
|
2010-07-30 17:08:47 +08:00
|
|
|
pci_disable_pcie_error_reporting(pdev);
|
2006-09-20 01:28:00 +08:00
|
|
|
scsi_host_put(ha->host);
|
|
|
|
|
|
|
|
probe_disable_device:
|
|
|
|
pci_disable_device(pdev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qla4xxx_remove_adapter - calback function to remove adapter.
|
|
|
|
* @pci_dev: PCI device pointer
|
|
|
|
**/
|
|
|
|
static void __devexit qla4xxx_remove_adapter(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
struct scsi_qla_host *ha;
|
|
|
|
|
|
|
|
ha = pci_get_drvdata(pdev);
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
set_bit(AF_HBA_GOING_AWAY, &ha->flags);
|
2007-05-24 09:03:32 +08:00
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
/* remove devs from iscsi_sessions to scsi_devices */
|
|
|
|
qla4xxx_free_ddb_list(ha);
|
|
|
|
|
|
|
|
scsi_remove_host(ha->host);
|
|
|
|
|
|
|
|
qla4xxx_free_adapter(ha);
|
|
|
|
|
|
|
|
scsi_host_put(ha->host);
|
|
|
|
|
2010-07-30 17:08:47 +08:00
|
|
|
pci_disable_pcie_error_reporting(pdev);
|
2010-07-28 18:23:44 +08:00
|
|
|
pci_disable_device(pdev);
|
2006-09-20 01:28:00 +08:00
|
|
|
pci_set_drvdata(pdev, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qla4xxx_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.
|
|
|
|
*/
|
2007-04-26 15:35:16 +08:00
|
|
|
static void qla4xxx_config_dma_addressing(struct scsi_qla_host *ha)
|
2006-09-20 01:28:00 +08:00
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
/* Update our PCI device dma_mask for full 64 bit mask */
|
2009-04-07 10:01:13 +08:00
|
|
|
if (pci_set_dma_mask(ha->pdev, DMA_BIT_MASK(64)) == 0) {
|
|
|
|
if (pci_set_consistent_dma_mask(ha->pdev, DMA_BIT_MASK(64))) {
|
2006-09-20 01:28:00 +08:00
|
|
|
dev_dbg(&ha->pdev->dev,
|
|
|
|
"Failed to set 64 bit PCI consistent mask; "
|
|
|
|
"using 32 bit.\n");
|
|
|
|
retval = pci_set_consistent_dma_mask(ha->pdev,
|
2009-04-07 10:01:15 +08:00
|
|
|
DMA_BIT_MASK(32));
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
} else
|
2009-04-07 10:01:15 +08:00
|
|
|
retval = pci_set_dma_mask(ha->pdev, DMA_BIT_MASK(32));
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int qla4xxx_slave_alloc(struct scsi_device *sdev)
|
|
|
|
{
|
|
|
|
struct iscsi_cls_session *sess = starget_to_session(sdev->sdev_target);
|
|
|
|
struct ddb_entry *ddb = sess->dd_data;
|
|
|
|
|
|
|
|
sdev->hostdata = ddb;
|
|
|
|
sdev->tagged_supported = 1;
|
2008-07-12 08:50:33 +08:00
|
|
|
scsi_activate_tcq(sdev, QL4_DEF_QDEPTH);
|
2006-09-20 01:28:00 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qla4xxx_slave_configure(struct scsi_device *sdev)
|
|
|
|
{
|
|
|
|
sdev->tagged_supported = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qla4xxx_slave_destroy(struct scsi_device *sdev)
|
|
|
|
{
|
|
|
|
scsi_deactivate_tcq(sdev, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qla4xxx_del_from_active_array - returns an active srb
|
|
|
|
* @ha: Pointer to host adapter structure.
|
2009-07-16 23:13:03 +08:00
|
|
|
* @index: index into the active_array
|
2006-09-20 01:28:00 +08:00
|
|
|
*
|
|
|
|
* This routine removes and returns the srb at the specified index
|
|
|
|
**/
|
2010-07-28 18:23:44 +08:00
|
|
|
struct srb *qla4xxx_del_from_active_array(struct scsi_qla_host *ha,
|
|
|
|
uint32_t index)
|
2006-09-20 01:28:00 +08:00
|
|
|
{
|
|
|
|
struct srb *srb = NULL;
|
2010-04-28 14:11:59 +08:00
|
|
|
struct scsi_cmnd *cmd = NULL;
|
2006-09-20 01:28:00 +08:00
|
|
|
|
2010-04-28 14:11:59 +08:00
|
|
|
cmd = scsi_host_find_tag(ha->host, index);
|
|
|
|
if (!cmd)
|
2006-09-20 01:28:00 +08:00
|
|
|
return srb;
|
|
|
|
|
2010-04-28 14:11:59 +08:00
|
|
|
srb = (struct srb *)CMD_SP(cmd);
|
|
|
|
if (!srb)
|
2006-09-20 01:28:00 +08:00
|
|
|
return srb;
|
|
|
|
|
|
|
|
/* update counters */
|
|
|
|
if (srb->flags & SRB_DMA_VALID) {
|
|
|
|
ha->req_q_count += srb->iocb_cnt;
|
|
|
|
ha->iocb_cnt -= srb->iocb_cnt;
|
|
|
|
if (srb->cmd)
|
2010-04-28 14:11:59 +08:00
|
|
|
srb->cmd->host_scribble =
|
|
|
|
(unsigned char *)(unsigned long) MAX_SRBS;
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
return srb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qla4xxx_eh_wait_on_command - waits for command to be returned by firmware
|
2010-04-28 14:12:24 +08:00
|
|
|
* @ha: Pointer to host adapter structure.
|
2006-09-20 01:28:00 +08:00
|
|
|
* @cmd: Scsi Command to wait on.
|
|
|
|
*
|
|
|
|
* This routine waits for the command to be returned by the Firmware
|
|
|
|
* for some max time.
|
|
|
|
**/
|
|
|
|
static int qla4xxx_eh_wait_on_command(struct scsi_qla_host *ha,
|
|
|
|
struct scsi_cmnd *cmd)
|
|
|
|
{
|
|
|
|
int done = 0;
|
|
|
|
struct srb *rp;
|
|
|
|
uint32_t max_wait_time = EH_WAIT_CMD_TOV;
|
2010-07-30 17:08:47 +08:00
|
|
|
int ret = SUCCESS;
|
|
|
|
|
|
|
|
/* Dont wait on command if PCI error is being handled
|
|
|
|
* by PCI AER driver
|
|
|
|
*/
|
|
|
|
if (unlikely(pci_channel_offline(ha->pdev)) ||
|
|
|
|
(test_bit(AF_EEH_BUSY, &ha->flags))) {
|
|
|
|
ql4_printk(KERN_WARNING, ha, "scsi%ld: Return from %s\n",
|
|
|
|
ha->host_no, __func__);
|
|
|
|
return ret;
|
|
|
|
}
|
2006-09-20 01:28:00 +08:00
|
|
|
|
|
|
|
do {
|
|
|
|
/* Checking to see if its returned to OS */
|
2010-04-28 14:11:59 +08:00
|
|
|
rp = (struct srb *) CMD_SP(cmd);
|
2006-09-20 01:28:00 +08:00
|
|
|
if (rp == NULL) {
|
|
|
|
done++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
msleep(2000);
|
|
|
|
} while (max_wait_time--);
|
|
|
|
|
|
|
|
return done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qla4xxx_wait_for_hba_online - waits for HBA to come online
|
|
|
|
* @ha: Pointer to host adapter structure
|
|
|
|
**/
|
|
|
|
static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
unsigned long wait_online;
|
|
|
|
|
2010-10-07 13:47:48 +08:00
|
|
|
wait_online = jiffies + (HBA_ONLINE_TOV * HZ);
|
2006-09-20 01:28:00 +08:00
|
|
|
while (time_before(jiffies, wait_online)) {
|
|
|
|
|
|
|
|
if (adapter_up(ha))
|
|
|
|
return QLA_SUCCESS;
|
|
|
|
|
|
|
|
msleep(2000);
|
|
|
|
}
|
|
|
|
|
|
|
|
return QLA_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-03-01 08:25:20 +08:00
|
|
|
* qla4xxx_eh_wait_for_commands - wait for active cmds to finish.
|
2009-07-16 23:13:03 +08:00
|
|
|
* @ha: pointer to HBA
|
2006-09-20 01:28:00 +08:00
|
|
|
* @t: target id
|
|
|
|
* @l: lun id
|
|
|
|
*
|
|
|
|
* This function waits for all outstanding commands to a lun to complete. It
|
|
|
|
* returns 0 if all pending commands are returned and 1 otherwise.
|
|
|
|
**/
|
2008-03-01 08:25:20 +08:00
|
|
|
static int qla4xxx_eh_wait_for_commands(struct scsi_qla_host *ha,
|
|
|
|
struct scsi_target *stgt,
|
|
|
|
struct scsi_device *sdev)
|
2006-09-20 01:28:00 +08:00
|
|
|
{
|
|
|
|
int cnt;
|
|
|
|
int status = 0;
|
|
|
|
struct scsi_cmnd *cmd;
|
|
|
|
|
|
|
|
/*
|
2008-03-01 08:25:20 +08:00
|
|
|
* Waiting for all commands for the designated target or dev
|
|
|
|
* in the active array
|
2006-09-20 01:28:00 +08:00
|
|
|
*/
|
|
|
|
for (cnt = 0; cnt < ha->host->can_queue; cnt++) {
|
|
|
|
cmd = scsi_host_find_tag(ha->host, cnt);
|
2008-03-01 08:25:20 +08:00
|
|
|
if (cmd && stgt == scsi_target(cmd->device) &&
|
|
|
|
(!sdev || sdev == cmd->device)) {
|
2006-09-20 01:28:00 +08:00
|
|
|
if (!qla4xxx_eh_wait_on_command(ha, cmd)) {
|
|
|
|
status++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2010-04-28 14:12:24 +08:00
|
|
|
/**
|
|
|
|
* qla4xxx_eh_abort - callback for abort task.
|
|
|
|
* @cmd: Pointer to Linux's SCSI command structure
|
|
|
|
*
|
|
|
|
* This routine is called by the Linux OS to abort the specified
|
|
|
|
* command.
|
|
|
|
**/
|
|
|
|
static int qla4xxx_eh_abort(struct scsi_cmnd *cmd)
|
|
|
|
{
|
|
|
|
struct scsi_qla_host *ha = to_qla_host(cmd->device->host);
|
|
|
|
unsigned int id = cmd->device->id;
|
|
|
|
unsigned int lun = cmd->device->lun;
|
|
|
|
unsigned long serial = cmd->serial_number;
|
|
|
|
struct srb *srb = NULL;
|
|
|
|
int ret = SUCCESS;
|
|
|
|
int wait = 0;
|
|
|
|
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_INFO, ha,
|
2010-04-28 14:12:24 +08:00
|
|
|
"scsi%ld:%d:%d: Abort command issued cmd=%p, pid=%ld\n",
|
|
|
|
ha->host_no, id, lun, cmd, serial);
|
|
|
|
|
|
|
|
srb = (struct srb *) CMD_SP(cmd);
|
|
|
|
|
|
|
|
if (!srb)
|
|
|
|
return SUCCESS;
|
|
|
|
|
|
|
|
kref_get(&srb->srb_ref);
|
|
|
|
|
|
|
|
if (qla4xxx_abort_task(ha, srb) != QLA_SUCCESS) {
|
|
|
|
DEBUG3(printk("scsi%ld:%d:%d: Abort_task mbx failed.\n",
|
|
|
|
ha->host_no, id, lun));
|
|
|
|
ret = FAILED;
|
|
|
|
} else {
|
|
|
|
DEBUG3(printk("scsi%ld:%d:%d: Abort_task mbx success.\n",
|
|
|
|
ha->host_no, id, lun));
|
|
|
|
wait = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
kref_put(&srb->srb_ref, qla4xxx_srb_compl);
|
|
|
|
|
|
|
|
/* Wait for command to complete */
|
|
|
|
if (wait) {
|
|
|
|
if (!qla4xxx_eh_wait_on_command(ha, cmd)) {
|
|
|
|
DEBUG2(printk("scsi%ld:%d:%d: Abort handler timed out\n",
|
|
|
|
ha->host_no, id, lun));
|
|
|
|
ret = FAILED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_INFO, ha,
|
2010-04-28 14:12:24 +08:00
|
|
|
"scsi%ld:%d:%d: Abort command - %s\n",
|
|
|
|
ha->host_no, id, lun, (ret == SUCCESS) ? "succeded" : "failed");
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
/**
|
|
|
|
* qla4xxx_eh_device_reset - callback for target reset.
|
|
|
|
* @cmd: Pointer to Linux's SCSI command structure
|
|
|
|
*
|
|
|
|
* This routine is called by the Linux OS to reset all luns on the
|
|
|
|
* specified target.
|
|
|
|
**/
|
|
|
|
static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd)
|
|
|
|
{
|
|
|
|
struct scsi_qla_host *ha = to_qla_host(cmd->device->host);
|
|
|
|
struct ddb_entry *ddb_entry = cmd->device->hostdata;
|
|
|
|
int ret = FAILED, stat;
|
|
|
|
|
2009-07-16 04:03:01 +08:00
|
|
|
if (!ddb_entry)
|
2006-09-20 01:28:00 +08:00
|
|
|
return ret;
|
|
|
|
|
2010-07-22 19:29:49 +08:00
|
|
|
ret = iscsi_block_scsi_eh(cmd);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = FAILED;
|
|
|
|
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_INFO, ha,
|
2006-09-20 01:28:00 +08:00
|
|
|
"scsi%ld:%d:%d:%d: DEVICE RESET ISSUED.\n", ha->host_no,
|
|
|
|
cmd->device->channel, cmd->device->id, cmd->device->lun);
|
|
|
|
|
|
|
|
DEBUG2(printk(KERN_INFO
|
|
|
|
"scsi%ld: DEVICE_RESET cmd=%p jiffies = 0x%lx, to=%x,"
|
|
|
|
"dpc_flags=%lx, status=%x allowed=%d\n", ha->host_no,
|
2008-09-14 20:55:09 +08:00
|
|
|
cmd, jiffies, cmd->request->timeout / HZ,
|
2006-09-20 01:28:00 +08:00
|
|
|
ha->dpc_flags, cmd->result, cmd->allowed));
|
|
|
|
|
|
|
|
/* FIXME: wait for hba to go online */
|
|
|
|
stat = qla4xxx_reset_lun(ha, ddb_entry, cmd->device->lun);
|
|
|
|
if (stat != QLA_SUCCESS) {
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_INFO, ha, "DEVICE RESET FAILED. %d\n", stat);
|
2006-09-20 01:28:00 +08:00
|
|
|
goto eh_dev_reset_done;
|
|
|
|
}
|
|
|
|
|
2008-03-01 08:25:20 +08:00
|
|
|
if (qla4xxx_eh_wait_for_commands(ha, scsi_target(cmd->device),
|
|
|
|
cmd->device)) {
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_INFO, ha,
|
2008-03-01 08:25:20 +08:00
|
|
|
"DEVICE RESET FAILED - waiting for "
|
|
|
|
"commands.\n");
|
|
|
|
goto eh_dev_reset_done;
|
2006-09-20 01:28:00 +08:00
|
|
|
}
|
|
|
|
|
2008-03-20 02:23:03 +08:00
|
|
|
/* Send marker. */
|
|
|
|
if (qla4xxx_send_marker_iocb(ha, ddb_entry, cmd->device->lun,
|
|
|
|
MM_LUN_RESET) != QLA_SUCCESS)
|
|
|
|
goto eh_dev_reset_done;
|
|
|
|
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_INFO, ha,
|
2006-09-20 01:28:00 +08:00
|
|
|
"scsi(%ld:%d:%d:%d): DEVICE RESET SUCCEEDED.\n",
|
|
|
|
ha->host_no, cmd->device->channel, cmd->device->id,
|
|
|
|
cmd->device->lun);
|
|
|
|
|
|
|
|
ret = SUCCESS;
|
|
|
|
|
|
|
|
eh_dev_reset_done:
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-03-01 08:25:20 +08:00
|
|
|
/**
|
|
|
|
* qla4xxx_eh_target_reset - callback for target reset.
|
|
|
|
* @cmd: Pointer to Linux's SCSI command structure
|
|
|
|
*
|
|
|
|
* This routine is called by the Linux OS to reset the target.
|
|
|
|
**/
|
|
|
|
static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd)
|
|
|
|
{
|
|
|
|
struct scsi_qla_host *ha = to_qla_host(cmd->device->host);
|
|
|
|
struct ddb_entry *ddb_entry = cmd->device->hostdata;
|
2010-07-22 19:29:49 +08:00
|
|
|
int stat, ret;
|
2008-03-01 08:25:20 +08:00
|
|
|
|
|
|
|
if (!ddb_entry)
|
|
|
|
return FAILED;
|
|
|
|
|
2010-07-22 19:29:49 +08:00
|
|
|
ret = iscsi_block_scsi_eh(cmd);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2008-03-01 08:25:20 +08:00
|
|
|
starget_printk(KERN_INFO, scsi_target(cmd->device),
|
|
|
|
"WARM TARGET RESET ISSUED.\n");
|
|
|
|
|
|
|
|
DEBUG2(printk(KERN_INFO
|
|
|
|
"scsi%ld: TARGET_DEVICE_RESET cmd=%p jiffies = 0x%lx, "
|
|
|
|
"to=%x,dpc_flags=%lx, status=%x allowed=%d\n",
|
2008-09-14 20:55:09 +08:00
|
|
|
ha->host_no, cmd, jiffies, cmd->request->timeout / HZ,
|
2008-03-01 08:25:20 +08:00
|
|
|
ha->dpc_flags, cmd->result, cmd->allowed));
|
|
|
|
|
|
|
|
stat = qla4xxx_reset_target(ha, ddb_entry);
|
|
|
|
if (stat != QLA_SUCCESS) {
|
|
|
|
starget_printk(KERN_INFO, scsi_target(cmd->device),
|
|
|
|
"WARM TARGET RESET FAILED.\n");
|
|
|
|
return FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qla4xxx_eh_wait_for_commands(ha, scsi_target(cmd->device),
|
|
|
|
NULL)) {
|
|
|
|
starget_printk(KERN_INFO, scsi_target(cmd->device),
|
|
|
|
"WARM TARGET DEVICE RESET FAILED - "
|
|
|
|
"waiting for commands.\n");
|
|
|
|
return FAILED;
|
|
|
|
}
|
|
|
|
|
2008-03-20 02:23:03 +08:00
|
|
|
/* Send marker. */
|
|
|
|
if (qla4xxx_send_marker_iocb(ha, ddb_entry, cmd->device->lun,
|
|
|
|
MM_TGT_WARM_RESET) != QLA_SUCCESS) {
|
|
|
|
starget_printk(KERN_INFO, scsi_target(cmd->device),
|
|
|
|
"WARM TARGET DEVICE RESET FAILED - "
|
|
|
|
"marker iocb failed.\n");
|
|
|
|
return FAILED;
|
|
|
|
}
|
|
|
|
|
2008-03-01 08:25:20 +08:00
|
|
|
starget_printk(KERN_INFO, scsi_target(cmd->device),
|
|
|
|
"WARM TARGET RESET SUCCEEDED.\n");
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
/**
|
|
|
|
* qla4xxx_eh_host_reset - kernel callback
|
|
|
|
* @cmd: Pointer to Linux's SCSI command structure
|
|
|
|
*
|
|
|
|
* This routine is invoked by the Linux kernel to perform fatal error
|
|
|
|
* recovery on the specified adapter.
|
|
|
|
**/
|
|
|
|
static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd)
|
|
|
|
{
|
|
|
|
int return_status = FAILED;
|
|
|
|
struct scsi_qla_host *ha;
|
|
|
|
|
|
|
|
ha = (struct scsi_qla_host *) cmd->device->host->hostdata;
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
if (ql4xdontresethba) {
|
|
|
|
DEBUG2(printk("scsi%ld: %s: Don't Reset HBA\n",
|
|
|
|
ha->host_no, __func__));
|
|
|
|
return FAILED;
|
|
|
|
}
|
|
|
|
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_INFO, ha,
|
2009-07-16 04:03:00 +08:00
|
|
|
"scsi(%ld:%d:%d:%d): HOST RESET ISSUED.\n", ha->host_no,
|
2006-09-20 01:28:00 +08:00
|
|
|
cmd->device->channel, cmd->device->id, cmd->device->lun);
|
|
|
|
|
|
|
|
if (qla4xxx_wait_for_hba_online(ha) != QLA_SUCCESS) {
|
|
|
|
DEBUG2(printk("scsi%ld:%d: %s: Unable to reset host. Adapter "
|
|
|
|
"DEAD.\n", ha->host_no, cmd->device->channel,
|
|
|
|
__func__));
|
|
|
|
|
|
|
|
return FAILED;
|
|
|
|
}
|
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
if (!test_bit(DPC_RESET_HA, &ha->dpc_flags)) {
|
|
|
|
if (is_qla8022(ha))
|
|
|
|
set_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags);
|
|
|
|
else
|
|
|
|
set_bit(DPC_RESET_HA, &ha->dpc_flags);
|
|
|
|
}
|
2008-03-05 03:26:53 +08:00
|
|
|
|
2010-07-28 18:23:44 +08:00
|
|
|
if (qla4xxx_recover_adapter(ha) == QLA_SUCCESS)
|
2006-09-20 01:28:00 +08:00
|
|
|
return_status = SUCCESS;
|
|
|
|
|
2010-07-10 17:21:02 +08:00
|
|
|
ql4_printk(KERN_INFO, ha, "HOST RESET %s.\n",
|
2006-09-20 01:28:00 +08:00
|
|
|
return_status == FAILED ? "FAILED" : "SUCCEDED");
|
|
|
|
|
|
|
|
return return_status;
|
|
|
|
}
|
|
|
|
|
2010-07-30 17:08:47 +08:00
|
|
|
/* PCI AER driver recovers from all correctable errors w/o
|
|
|
|
* driver intervention. For uncorrectable errors PCI AER
|
|
|
|
* driver calls the following device driver's callbacks
|
|
|
|
*
|
|
|
|
* - Fatal Errors - link_reset
|
|
|
|
* - Non-Fatal Errors - driver's pci_error_detected() which
|
|
|
|
* returns CAN_RECOVER, NEED_RESET or DISCONNECT.
|
|
|
|
*
|
|
|
|
* PCI AER driver calls
|
|
|
|
* CAN_RECOVER - driver's pci_mmio_enabled(), mmio_enabled
|
|
|
|
* returns RECOVERED or NEED_RESET if fw_hung
|
|
|
|
* NEED_RESET - driver's slot_reset()
|
|
|
|
* DISCONNECT - device is dead & cannot recover
|
|
|
|
* RECOVERED - driver's pci_resume()
|
|
|
|
*/
|
|
|
|
static pci_ers_result_t
|
|
|
|
qla4xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
|
|
|
|
{
|
|
|
|
struct scsi_qla_host *ha = pci_get_drvdata(pdev);
|
|
|
|
|
|
|
|
ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: error detected:state %x\n",
|
|
|
|
ha->host_no, __func__, state);
|
|
|
|
|
|
|
|
if (!is_aer_supported(ha))
|
|
|
|
return PCI_ERS_RESULT_NONE;
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case pci_channel_io_normal:
|
|
|
|
clear_bit(AF_EEH_BUSY, &ha->flags);
|
|
|
|
return PCI_ERS_RESULT_CAN_RECOVER;
|
|
|
|
case pci_channel_io_frozen:
|
|
|
|
set_bit(AF_EEH_BUSY, &ha->flags);
|
|
|
|
qla4xxx_mailbox_premature_completion(ha);
|
|
|
|
qla4xxx_free_irqs(ha);
|
|
|
|
pci_disable_device(pdev);
|
|
|
|
return PCI_ERS_RESULT_NEED_RESET;
|
|
|
|
case pci_channel_io_perm_failure:
|
|
|
|
set_bit(AF_EEH_BUSY, &ha->flags);
|
|
|
|
set_bit(AF_PCI_CHANNEL_IO_PERM_FAILURE, &ha->flags);
|
|
|
|
qla4xxx_abort_active_cmds(ha, DID_NO_CONNECT << 16);
|
|
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
|
|
}
|
|
|
|
return PCI_ERS_RESULT_NEED_RESET;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qla4xxx_pci_mmio_enabled() gets called if
|
|
|
|
* qla4xxx_pci_error_detected() returns PCI_ERS_RESULT_CAN_RECOVER
|
|
|
|
* and read/write to the device still works.
|
|
|
|
**/
|
|
|
|
static pci_ers_result_t
|
|
|
|
qla4xxx_pci_mmio_enabled(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
struct scsi_qla_host *ha = pci_get_drvdata(pdev);
|
|
|
|
|
|
|
|
if (!is_aer_supported(ha))
|
|
|
|
return PCI_ERS_RESULT_NONE;
|
|
|
|
|
|
|
|
if (test_bit(AF_FW_RECOVERY, &ha->flags)) {
|
|
|
|
ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: firmware hang -- "
|
|
|
|
"mmio_enabled\n", ha->host_no, __func__);
|
|
|
|
return PCI_ERS_RESULT_NEED_RESET;
|
|
|
|
} else
|
|
|
|
return PCI_ERS_RESULT_RECOVERED;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t qla4_8xxx_error_recovery(struct scsi_qla_host *ha)
|
|
|
|
{
|
|
|
|
uint32_t rval = QLA_ERROR;
|
|
|
|
int fn;
|
|
|
|
struct pci_dev *other_pdev = NULL;
|
|
|
|
|
|
|
|
ql4_printk(KERN_WARNING, ha, "scsi%ld: In %s\n", ha->host_no, __func__);
|
|
|
|
|
|
|
|
set_bit(DPC_RESET_ACTIVE, &ha->dpc_flags);
|
|
|
|
|
|
|
|
if (test_bit(AF_ONLINE, &ha->flags)) {
|
|
|
|
clear_bit(AF_ONLINE, &ha->flags);
|
|
|
|
qla4xxx_mark_all_devices_missing(ha);
|
|
|
|
qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
|
|
|
|
qla4xxx_abort_active_cmds(ha, DID_RESET << 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn = PCI_FUNC(ha->pdev->devfn);
|
|
|
|
while (fn > 0) {
|
|
|
|
fn--;
|
|
|
|
ql4_printk(KERN_INFO, ha, "scsi%ld: %s: Finding PCI device at "
|
|
|
|
"func %x\n", ha->host_no, __func__, fn);
|
|
|
|
/* Get the pci device given the domain, bus,
|
|
|
|
* slot/function number */
|
|
|
|
other_pdev =
|
|
|
|
pci_get_domain_bus_and_slot(pci_domain_nr(ha->pdev->bus),
|
|
|
|
ha->pdev->bus->number, PCI_DEVFN(PCI_SLOT(ha->pdev->devfn),
|
|
|
|
fn));
|
|
|
|
|
|
|
|
if (!other_pdev)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (atomic_read(&other_pdev->enable_cnt)) {
|
|
|
|
ql4_printk(KERN_INFO, ha, "scsi%ld: %s: Found PCI "
|
|
|
|
"func in enabled state%x\n", ha->host_no,
|
|
|
|
__func__, fn);
|
|
|
|
pci_dev_put(other_pdev);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pci_dev_put(other_pdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The first function on the card, the reset owner will
|
|
|
|
* start & initialize the firmware. The other functions
|
|
|
|
* on the card will reset the firmware context
|
|
|
|
*/
|
|
|
|
if (!fn) {
|
|
|
|
ql4_printk(KERN_INFO, ha, "scsi%ld: %s: devfn being reset "
|
|
|
|
"0x%x is the owner\n", ha->host_no, __func__,
|
|
|
|
ha->pdev->devfn);
|
|
|
|
|
|
|
|
qla4_8xxx_idc_lock(ha);
|
|
|
|
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
|
|
|
QLA82XX_DEV_COLD);
|
|
|
|
|
|
|
|
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION,
|
|
|
|
QLA82XX_IDC_VERSION);
|
|
|
|
|
|
|
|
qla4_8xxx_idc_unlock(ha);
|
|
|
|
clear_bit(AF_FW_RECOVERY, &ha->flags);
|
|
|
|
rval = qla4xxx_initialize_adapter(ha, PRESERVE_DDB_LIST);
|
|
|
|
qla4_8xxx_idc_lock(ha);
|
|
|
|
|
|
|
|
if (rval != QLA_SUCCESS) {
|
|
|
|
ql4_printk(KERN_INFO, ha, "scsi%ld: %s: HW State: "
|
|
|
|
"FAILED\n", ha->host_no, __func__);
|
|
|
|
qla4_8xxx_clear_drv_active(ha);
|
|
|
|
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
|
|
|
QLA82XX_DEV_FAILED);
|
|
|
|
} else {
|
|
|
|
ql4_printk(KERN_INFO, ha, "scsi%ld: %s: HW State: "
|
|
|
|
"READY\n", ha->host_no, __func__);
|
|
|
|
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
|
|
|
QLA82XX_DEV_READY);
|
|
|
|
/* Clear driver state register */
|
|
|
|
qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_STATE, 0);
|
|
|
|
qla4_8xxx_set_drv_active(ha);
|
|
|
|
ha->isp_ops->enable_intrs(ha);
|
|
|
|
}
|
|
|
|
qla4_8xxx_idc_unlock(ha);
|
|
|
|
} else {
|
|
|
|
ql4_printk(KERN_INFO, ha, "scsi%ld: %s: devfn 0x%x is not "
|
|
|
|
"the reset owner\n", ha->host_no, __func__,
|
|
|
|
ha->pdev->devfn);
|
|
|
|
if ((qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE) ==
|
|
|
|
QLA82XX_DEV_READY)) {
|
|
|
|
clear_bit(AF_FW_RECOVERY, &ha->flags);
|
|
|
|
rval = qla4xxx_initialize_adapter(ha,
|
|
|
|
PRESERVE_DDB_LIST);
|
|
|
|
if (rval == QLA_SUCCESS)
|
|
|
|
ha->isp_ops->enable_intrs(ha);
|
|
|
|
qla4_8xxx_idc_lock(ha);
|
|
|
|
qla4_8xxx_set_drv_active(ha);
|
|
|
|
qla4_8xxx_idc_unlock(ha);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
clear_bit(DPC_RESET_ACTIVE, &ha->dpc_flags);
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static pci_ers_result_t
|
|
|
|
qla4xxx_pci_slot_reset(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
pci_ers_result_t ret = PCI_ERS_RESULT_DISCONNECT;
|
|
|
|
struct scsi_qla_host *ha = pci_get_drvdata(pdev);
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: slot_reset\n",
|
|
|
|
ha->host_no, __func__);
|
|
|
|
|
|
|
|
if (!is_aer_supported(ha))
|
|
|
|
return PCI_ERS_RESULT_NONE;
|
|
|
|
|
|
|
|
/* Restore the saved state of PCIe device -
|
|
|
|
* BAR registers, PCI Config space, PCIX, MSI,
|
|
|
|
* IOV states
|
|
|
|
*/
|
|
|
|
pci_restore_state(pdev);
|
|
|
|
|
|
|
|
/* pci_restore_state() clears the saved_state flag of the device
|
|
|
|
* save restored state which resets saved_state flag
|
|
|
|
*/
|
|
|
|
pci_save_state(pdev);
|
|
|
|
|
|
|
|
/* Initialize device or resume if in suspended state */
|
|
|
|
rc = pci_enable_device(pdev);
|
|
|
|
if (rc) {
|
|
|
|
ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: Cant re-enable "
|
|
|
|
"device after reset\n", ha->host_no, __func__);
|
|
|
|
goto exit_slot_reset;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qla4xxx_request_irqs(ha);
|
|
|
|
if (ret) {
|
|
|
|
ql4_printk(KERN_WARNING, ha, "Failed to reserve interrupt %d"
|
|
|
|
" already in use.\n", pdev->irq);
|
|
|
|
goto exit_slot_reset;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_qla8022(ha)) {
|
|
|
|
if (qla4_8xxx_error_recovery(ha) == QLA_SUCCESS) {
|
|
|
|
ret = PCI_ERS_RESULT_RECOVERED;
|
|
|
|
goto exit_slot_reset;
|
|
|
|
} else
|
|
|
|
goto exit_slot_reset;
|
|
|
|
}
|
|
|
|
|
|
|
|
exit_slot_reset:
|
|
|
|
ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: Return=%x\n"
|
|
|
|
"device after reset\n", ha->host_no, __func__, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
qla4xxx_pci_resume(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
struct scsi_qla_host *ha = pci_get_drvdata(pdev);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: pci_resume\n",
|
|
|
|
ha->host_no, __func__);
|
|
|
|
|
|
|
|
ret = qla4xxx_wait_for_hba_online(ha);
|
|
|
|
if (ret != QLA_SUCCESS) {
|
|
|
|
ql4_printk(KERN_ERR, ha, "scsi%ld: %s: the device failed to "
|
|
|
|
"resume I/O from slot/link_reset\n", ha->host_no,
|
|
|
|
__func__);
|
|
|
|
}
|
|
|
|
|
|
|
|
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
|
|
clear_bit(AF_EEH_BUSY, &ha->flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct pci_error_handlers qla4xxx_err_handler = {
|
|
|
|
.error_detected = qla4xxx_pci_error_detected,
|
|
|
|
.mmio_enabled = qla4xxx_pci_mmio_enabled,
|
|
|
|
.slot_reset = qla4xxx_pci_slot_reset,
|
|
|
|
.resume = qla4xxx_pci_resume,
|
|
|
|
};
|
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
static struct pci_device_id qla4xxx_pci_tbl[] = {
|
|
|
|
{
|
|
|
|
.vendor = PCI_VENDOR_ID_QLOGIC,
|
|
|
|
.device = PCI_DEVICE_ID_QLOGIC_ISP4010,
|
|
|
|
.subvendor = PCI_ANY_ID,
|
|
|
|
.subdevice = PCI_ANY_ID,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.vendor = PCI_VENDOR_ID_QLOGIC,
|
|
|
|
.device = PCI_DEVICE_ID_QLOGIC_ISP4022,
|
|
|
|
.subvendor = PCI_ANY_ID,
|
|
|
|
.subdevice = PCI_ANY_ID,
|
|
|
|
},
|
2006-11-16 09:38:40 +08:00
|
|
|
{
|
|
|
|
.vendor = PCI_VENDOR_ID_QLOGIC,
|
|
|
|
.device = PCI_DEVICE_ID_QLOGIC_ISP4032,
|
|
|
|
.subvendor = PCI_ANY_ID,
|
|
|
|
.subdevice = PCI_ANY_ID,
|
|
|
|
},
|
2010-07-28 18:23:44 +08:00
|
|
|
{
|
|
|
|
.vendor = PCI_VENDOR_ID_QLOGIC,
|
|
|
|
.device = PCI_DEVICE_ID_QLOGIC_ISP8022,
|
|
|
|
.subvendor = PCI_ANY_ID,
|
|
|
|
.subdevice = PCI_ANY_ID,
|
|
|
|
},
|
2006-09-20 01:28:00 +08:00
|
|
|
{0, 0},
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, qla4xxx_pci_tbl);
|
|
|
|
|
2007-04-26 15:35:16 +08:00
|
|
|
static struct pci_driver qla4xxx_pci_driver = {
|
2006-09-20 01:28:00 +08:00
|
|
|
.name = DRIVER_NAME,
|
|
|
|
.id_table = qla4xxx_pci_tbl,
|
|
|
|
.probe = qla4xxx_probe_adapter,
|
|
|
|
.remove = qla4xxx_remove_adapter,
|
2010-07-30 17:08:47 +08:00
|
|
|
.err_handler = &qla4xxx_err_handler,
|
2006-09-20 01:28:00 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int __init qla4xxx_module_init(void)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Allocate cache for SRBs. */
|
|
|
|
srb_cachep = kmem_cache_create("qla4xxx_srbs", sizeof(struct srb), 0,
|
2007-07-20 09:11:58 +08:00
|
|
|
SLAB_HWCACHE_ALIGN, NULL);
|
2006-09-20 01:28:00 +08:00
|
|
|
if (srb_cachep == NULL) {
|
|
|
|
printk(KERN_ERR
|
|
|
|
"%s: Unable to allocate SRB cache..."
|
|
|
|
"Failing load!\n", DRIVER_NAME);
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto no_srp_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Derive version string. */
|
|
|
|
strcpy(qla4xxx_version_str, QLA4XXX_DRIVER_VERSION);
|
2006-10-07 00:54:59 +08:00
|
|
|
if (ql4xextended_error_logging)
|
2006-09-20 01:28:00 +08:00
|
|
|
strcat(qla4xxx_version_str, "-debug");
|
|
|
|
|
|
|
|
qla4xxx_scsi_transport =
|
|
|
|
iscsi_register_transport(&qla4xxx_iscsi_transport);
|
|
|
|
if (!qla4xxx_scsi_transport){
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto release_srb_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = pci_register_driver(&qla4xxx_pci_driver);
|
|
|
|
if (ret)
|
|
|
|
goto unregister_transport;
|
|
|
|
|
|
|
|
printk(KERN_INFO "QLogic iSCSI HBA Driver\n");
|
|
|
|
return 0;
|
2006-10-06 12:50:07 +08:00
|
|
|
|
2006-09-20 01:28:00 +08:00
|
|
|
unregister_transport:
|
|
|
|
iscsi_unregister_transport(&qla4xxx_iscsi_transport);
|
|
|
|
release_srb_cache:
|
|
|
|
kmem_cache_destroy(srb_cachep);
|
|
|
|
no_srp_cache:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit qla4xxx_module_exit(void)
|
|
|
|
{
|
|
|
|
pci_unregister_driver(&qla4xxx_pci_driver);
|
|
|
|
iscsi_unregister_transport(&qla4xxx_iscsi_transport);
|
|
|
|
kmem_cache_destroy(srb_cachep);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(qla4xxx_module_init);
|
|
|
|
module_exit(qla4xxx_module_exit);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("QLogic Corporation");
|
|
|
|
MODULE_DESCRIPTION("QLogic iSCSI HBA Driver");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_VERSION(QLA4XXX_DRIVER_VERSION);
|