2005-04-17 05:20:36 +07:00
|
|
|
/*
|
2005-10-28 01:10:08 +07:00
|
|
|
* QLogic Fibre Channel HBA Driver
|
2013-02-08 13:57:54 +07:00
|
|
|
* Copyright (c) 2003-2013 QLogic Corporation
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
2005-10-28 01:10:08 +07:00
|
|
|
* See LICENSE.qla2xxx for copyright and licensing details.
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
|
|
|
#include "qla_def.h"
|
|
|
|
|
|
|
|
#include <linux/moduleparam.h>
|
|
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include <linux/delay.h>
|
2006-02-15 00:46:22 +07:00
|
|
|
#include <linux/kthread.h>
|
2008-05-13 12:21:10 +07:00
|
|
|
#include <linux/mutex.h>
|
2009-10-14 05:16:45 +07:00
|
|
|
#include <linux/kobject.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 15:04:11 +07:00
|
|
|
#include <linux/slab.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <scsi/scsi_tcq.h>
|
|
|
|
#include <scsi/scsicam.h>
|
|
|
|
#include <scsi/scsi_transport.h>
|
|
|
|
#include <scsi/scsi_transport_fc.h>
|
|
|
|
|
2012-05-16 01:34:28 +07:00
|
|
|
#include "qla_target.h"
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* Driver version
|
|
|
|
*/
|
|
|
|
char qla2x00_version_str[40];
|
|
|
|
|
2010-05-05 05:01:24 +07:00
|
|
|
static int apidev_major;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* SRB allocation cache
|
|
|
|
*/
|
2006-12-07 11:33:20 +07:00
|
|
|
static struct kmem_cache *srb_cachep;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-04-13 07:59:55 +07:00
|
|
|
/*
|
|
|
|
* CT6 CTX allocation cache
|
|
|
|
*/
|
|
|
|
static struct kmem_cache *ctx_cachep;
|
2011-07-15 02:00:12 +07:00
|
|
|
/*
|
|
|
|
* error level for logging
|
|
|
|
*/
|
|
|
|
int ql_errlev = ql_log_all;
|
2010-04-13 07:59:55 +07:00
|
|
|
|
2012-11-21 14:40:29 +07:00
|
|
|
static int ql2xenableclass2;
|
2012-05-16 01:34:28 +07:00
|
|
|
module_param(ql2xenableclass2, int, S_IRUGO|S_IRUSR);
|
|
|
|
MODULE_PARM_DESC(ql2xenableclass2,
|
|
|
|
"Specify if Class 2 operations are supported from the very "
|
|
|
|
"beginning. Default is 0 - class 2 not supported.");
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
int ql2xlogintimeout = 20;
|
2010-12-22 07:00:22 +07:00
|
|
|
module_param(ql2xlogintimeout, int, S_IRUGO);
|
2005-04-17 05:20:36 +07:00
|
|
|
MODULE_PARM_DESC(ql2xlogintimeout,
|
|
|
|
"Login timeout value in seconds.");
|
|
|
|
|
2007-05-07 21:42:59 +07:00
|
|
|
int qlport_down_retry;
|
2010-12-22 07:00:22 +07:00
|
|
|
module_param(qlport_down_retry, int, S_IRUGO);
|
2005-04-17 05:20:36 +07:00
|
|
|
MODULE_PARM_DESC(qlport_down_retry,
|
2006-06-30 16:33:07 +07:00
|
|
|
"Maximum number of command retries to a port that returns "
|
2005-04-17 05:20:36 +07:00
|
|
|
"a PORT-DOWN status.");
|
|
|
|
|
|
|
|
int ql2xplogiabsentdevice;
|
|
|
|
module_param(ql2xplogiabsentdevice, int, S_IRUGO|S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(ql2xplogiabsentdevice,
|
|
|
|
"Option to enable PLOGI to devices that are not present after "
|
2006-06-30 16:33:07 +07:00
|
|
|
"a Fabric scan. This is needed for several broken switches. "
|
2005-04-17 05:20:36 +07:00
|
|
|
"Default is 0 - no PLOGI. 1 - perfom PLOGI.");
|
|
|
|
|
|
|
|
int ql2xloginretrycount = 0;
|
2010-12-22 07:00:22 +07:00
|
|
|
module_param(ql2xloginretrycount, int, S_IRUGO);
|
2005-04-17 05:20:36 +07:00
|
|
|
MODULE_PARM_DESC(ql2xloginretrycount,
|
|
|
|
"Specify an alternate value for the NVRAM login retry count.");
|
|
|
|
|
2006-06-24 06:10:29 +07:00
|
|
|
int ql2xallocfwdump = 1;
|
2010-12-22 07:00:22 +07:00
|
|
|
module_param(ql2xallocfwdump, int, S_IRUGO);
|
2006-06-24 06:10:29 +07:00
|
|
|
MODULE_PARM_DESC(ql2xallocfwdump,
|
|
|
|
"Option to enable allocation of memory for a firmware dump "
|
|
|
|
"during HBA initialization. Memory allocation requirements "
|
|
|
|
"vary by ISP type. Default is 1 - allocate memory.");
|
|
|
|
|
2006-10-06 23:54:59 +07:00
|
|
|
int ql2xextended_error_logging;
|
2007-03-13 00:41:30 +07:00
|
|
|
module_param(ql2xextended_error_logging, int, S_IRUGO|S_IWUSR);
|
2006-10-06 23:54:59 +07:00
|
|
|
MODULE_PARM_DESC(ql2xextended_error_logging,
|
2011-07-15 02:00:12 +07:00
|
|
|
"Option to enable extended error logging,\n"
|
|
|
|
"\t\tDefault is 0 - no logging. 0x40000000 - Module Init & Probe.\n"
|
|
|
|
"\t\t0x20000000 - Mailbox Cmnds. 0x10000000 - Device Discovery.\n"
|
|
|
|
"\t\t0x08000000 - IO tracing. 0x04000000 - DPC Thread.\n"
|
|
|
|
"\t\t0x02000000 - Async events. 0x01000000 - Timer routines.\n"
|
|
|
|
"\t\t0x00800000 - User space. 0x00400000 - Task Management.\n"
|
|
|
|
"\t\t0x00200000 - AER/EEH. 0x00100000 - Multi Q.\n"
|
|
|
|
"\t\t0x00080000 - P3P Specific. 0x00040000 - Virtual Port.\n"
|
|
|
|
"\t\t0x00020000 - Buffer Dump. 0x00010000 - Misc.\n"
|
2012-11-21 14:40:41 +07:00
|
|
|
"\t\t0x00008000 - Verbose. 0x00004000 - Target.\n"
|
|
|
|
"\t\t0x00002000 - Target Mgmt. 0x00001000 - Target TMF.\n"
|
2011-07-15 02:00:12 +07:00
|
|
|
"\t\t0x7fffffff - For enabling all logs, can be too many logs.\n"
|
2011-11-19 00:03:07 +07:00
|
|
|
"\t\t0x1e400000 - Preferred value for capturing essential "
|
|
|
|
"debug information (equivalent to old "
|
|
|
|
"ql2xextended_error_logging=1).\n"
|
2011-07-15 02:00:12 +07:00
|
|
|
"\t\tDo LOGICAL OR of the value to enable more than one level");
|
2006-06-24 06:11:10 +07:00
|
|
|
|
2010-04-13 07:59:55 +07:00
|
|
|
int ql2xshiftctondsd = 6;
|
2010-12-22 07:00:22 +07:00
|
|
|
module_param(ql2xshiftctondsd, int, S_IRUGO);
|
2010-04-13 07:59:55 +07:00
|
|
|
MODULE_PARM_DESC(ql2xshiftctondsd,
|
|
|
|
"Set to control shifting of command type processing "
|
|
|
|
"based on total number of SG elements.");
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static void qla2x00_free_device(scsi_qla_host_t *);
|
|
|
|
|
2008-04-25 05:21:26 +07:00
|
|
|
int ql2xfdmienable=1;
|
2010-12-22 07:00:22 +07:00
|
|
|
module_param(ql2xfdmienable, int, S_IRUGO);
|
2005-08-27 09:08:30 +07:00
|
|
|
MODULE_PARM_DESC(ql2xfdmienable,
|
2010-03-24 00:14:59 +07:00
|
|
|
"Enables FDMI registrations. "
|
|
|
|
"0 - no FDMI. Default is 1 - perform FDMI.");
|
2005-08-27 09:08:30 +07:00
|
|
|
|
2013-01-30 15:34:38 +07:00
|
|
|
int ql2xmaxqdepth = MAX_Q_DEPTH;
|
2006-10-13 23:33:39 +07:00
|
|
|
module_param(ql2xmaxqdepth, int, S_IRUGO|S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(ql2xmaxqdepth,
|
2012-08-23 01:21:23 +07:00
|
|
|
"Maximum queue depth to set for each LUN. "
|
|
|
|
"Default is 32.");
|
2006-10-13 23:33:39 +07:00
|
|
|
|
2012-08-23 01:21:31 +07:00
|
|
|
int ql2xenabledif = 2;
|
|
|
|
module_param(ql2xenabledif, int, S_IRUGO);
|
2010-05-05 05:01:30 +07:00
|
|
|
MODULE_PARM_DESC(ql2xenabledif,
|
|
|
|
" Enable T10-CRC-DIF "
|
2011-08-17 01:29:22 +07:00
|
|
|
" Default is 0 - No DIF Support. 1 - Enable it"
|
|
|
|
", 2 - Enable DIF for all types, except Type 0.");
|
2010-05-05 05:01:30 +07:00
|
|
|
|
2011-08-17 01:29:22 +07:00
|
|
|
int ql2xenablehba_err_chk = 2;
|
2010-05-05 05:01:30 +07:00
|
|
|
module_param(ql2xenablehba_err_chk, int, S_IRUGO|S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(ql2xenablehba_err_chk,
|
2011-08-17 01:29:22 +07:00
|
|
|
" Enable T10-CRC-DIF Error isolation by HBA:\n"
|
|
|
|
" Default is 1.\n"
|
|
|
|
" 0 -- Error isolation disabled\n"
|
|
|
|
" 1 -- Error isolation enabled only for DIX Type 0\n"
|
|
|
|
" 2 -- Error isolation enabled for all Types\n");
|
2010-05-05 05:01:30 +07:00
|
|
|
|
2008-07-11 06:55:52 +07:00
|
|
|
int ql2xiidmaenable=1;
|
2010-12-22 07:00:22 +07:00
|
|
|
module_param(ql2xiidmaenable, int, S_IRUGO);
|
2008-07-11 06:55:52 +07:00
|
|
|
MODULE_PARM_DESC(ql2xiidmaenable,
|
|
|
|
"Enables iIDMA settings "
|
|
|
|
"Default is 1 - perform iIDMA. 0 - no iIDMA.");
|
|
|
|
|
2008-12-10 07:45:39 +07:00
|
|
|
int ql2xmaxqueues = 1;
|
2010-12-22 07:00:22 +07:00
|
|
|
module_param(ql2xmaxqueues, int, S_IRUGO);
|
2008-12-10 07:45:39 +07:00
|
|
|
MODULE_PARM_DESC(ql2xmaxqueues,
|
|
|
|
"Enables MQ settings "
|
2010-08-11 08:01:21 +07:00
|
|
|
"Default is 1 for single queue. Set it to number "
|
|
|
|
"of queues in MQ mode.");
|
2009-04-07 12:33:41 +07:00
|
|
|
|
|
|
|
int ql2xmultique_tag;
|
2010-12-22 07:00:22 +07:00
|
|
|
module_param(ql2xmultique_tag, int, S_IRUGO);
|
2009-04-07 12:33:41 +07:00
|
|
|
MODULE_PARM_DESC(ql2xmultique_tag,
|
|
|
|
"Enables CPU affinity settings for the driver "
|
|
|
|
"Default is 0 for no affinity of request and response IO. "
|
|
|
|
"Set it to 1 to turn on the cpu affinity.");
|
2009-04-07 12:33:49 +07:00
|
|
|
|
|
|
|
int ql2xfwloadbin;
|
2011-08-17 01:31:47 +07:00
|
|
|
module_param(ql2xfwloadbin, int, S_IRUGO|S_IWUSR);
|
2009-04-07 12:33:49 +07:00
|
|
|
MODULE_PARM_DESC(ql2xfwloadbin,
|
2011-07-15 02:00:13 +07:00
|
|
|
"Option to specify location from which to load ISP firmware:.\n"
|
|
|
|
" 2 -- load firmware via the request_firmware() (hotplug).\n"
|
2009-04-07 12:33:49 +07:00
|
|
|
" interface.\n"
|
|
|
|
" 1 -- load firmware from flash.\n"
|
|
|
|
" 0 -- use default semantics.\n");
|
|
|
|
|
2010-02-19 01:07:28 +07:00
|
|
|
int ql2xetsenable;
|
2010-12-22 07:00:22 +07:00
|
|
|
module_param(ql2xetsenable, int, S_IRUGO);
|
2010-02-19 01:07:28 +07:00
|
|
|
MODULE_PARM_DESC(ql2xetsenable,
|
|
|
|
"Enables firmware ETS burst."
|
|
|
|
"Default is 0 - skip ETS enablement.");
|
|
|
|
|
2010-05-29 05:08:28 +07:00
|
|
|
int ql2xdbwr = 1;
|
2011-08-17 01:31:47 +07:00
|
|
|
module_param(ql2xdbwr, int, S_IRUGO|S_IWUSR);
|
2010-04-13 07:59:55 +07:00
|
|
|
MODULE_PARM_DESC(ql2xdbwr,
|
2011-08-17 01:31:44 +07:00
|
|
|
"Option to specify scheme for request queue posting.\n"
|
|
|
|
" 0 -- Regular doorbell.\n"
|
|
|
|
" 1 -- CAMRAM doorbell (faster).\n");
|
2010-04-13 07:59:55 +07:00
|
|
|
|
2010-05-05 05:01:33 +07:00
|
|
|
int ql2xtargetreset = 1;
|
2010-12-22 07:00:22 +07:00
|
|
|
module_param(ql2xtargetreset, int, S_IRUGO);
|
2010-05-05 05:01:33 +07:00
|
|
|
MODULE_PARM_DESC(ql2xtargetreset,
|
|
|
|
"Enable target reset."
|
|
|
|
"Default is 1 - use hw defaults.");
|
|
|
|
|
2010-10-16 01:27:40 +07:00
|
|
|
int ql2xgffidenable;
|
2010-12-22 07:00:22 +07:00
|
|
|
module_param(ql2xgffidenable, int, S_IRUGO);
|
2010-10-16 01:27:40 +07:00
|
|
|
MODULE_PARM_DESC(ql2xgffidenable,
|
|
|
|
"Enables GFF_ID checks of port type. "
|
|
|
|
"Default is 0 - Do not use GFF_ID information.");
|
2010-04-13 07:59:55 +07:00
|
|
|
|
2010-05-05 05:01:29 +07:00
|
|
|
int ql2xasynctmfenable;
|
2010-12-22 07:00:22 +07:00
|
|
|
module_param(ql2xasynctmfenable, int, S_IRUGO);
|
2010-05-05 05:01:29 +07:00
|
|
|
MODULE_PARM_DESC(ql2xasynctmfenable,
|
|
|
|
"Enables issue of TM IOCBs asynchronously via IOCB mechanism"
|
|
|
|
"Default is 0 - Issue TM IOCBs via mailbox mechanism.");
|
2011-03-31 01:46:29 +07:00
|
|
|
|
|
|
|
int ql2xdontresethba;
|
2011-08-17 01:31:47 +07:00
|
|
|
module_param(ql2xdontresethba, int, S_IRUGO|S_IWUSR);
|
2011-03-31 01:46:29 +07:00
|
|
|
MODULE_PARM_DESC(ql2xdontresethba,
|
2011-08-17 01:31:44 +07:00
|
|
|
"Option to specify reset behaviour.\n"
|
|
|
|
" 0 (Default) -- Reset on failure.\n"
|
|
|
|
" 1 -- Do not reset on failure.\n");
|
2011-03-31 01:46:29 +07:00
|
|
|
|
2011-05-11 01:30:13 +07:00
|
|
|
uint ql2xmaxlun = MAX_LUNS;
|
|
|
|
module_param(ql2xmaxlun, uint, S_IRUGO);
|
|
|
|
MODULE_PARM_DESC(ql2xmaxlun,
|
|
|
|
"Defines the maximum LU number to register with the SCSI "
|
|
|
|
"midlayer. Default is 65535.");
|
|
|
|
|
2011-08-17 01:31:44 +07:00
|
|
|
int ql2xmdcapmask = 0x1F;
|
|
|
|
module_param(ql2xmdcapmask, int, S_IRUGO);
|
|
|
|
MODULE_PARM_DESC(ql2xmdcapmask,
|
|
|
|
"Set the Minidump driver capture mask level. "
|
2011-11-19 00:03:14 +07:00
|
|
|
"Default is 0x1F - Can be set to 0x3, 0x7, 0xF, 0x1F, 0x7F.");
|
2011-08-17 01:31:44 +07:00
|
|
|
|
2011-11-19 00:02:14 +07:00
|
|
|
int ql2xmdenable = 1;
|
2011-08-17 01:31:44 +07:00
|
|
|
module_param(ql2xmdenable, int, S_IRUGO);
|
|
|
|
MODULE_PARM_DESC(ql2xmdenable,
|
|
|
|
"Enable/disable MiniDump. "
|
2011-11-19 00:02:14 +07:00
|
|
|
"0 - MiniDump disabled. "
|
|
|
|
"1 (Default) - MiniDump enabled.");
|
2011-08-17 01:31:44 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
2005-07-07 00:32:07 +07:00
|
|
|
* SCSI host template entry points
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
|
|
|
static int qla2xxx_slave_configure(struct scsi_device * device);
|
2005-04-18 03:02:26 +07:00
|
|
|
static int qla2xxx_slave_alloc(struct scsi_device *);
|
2006-11-22 23:24:48 +07:00
|
|
|
static int qla2xxx_scan_finished(struct Scsi_Host *, unsigned long time);
|
|
|
|
static void qla2xxx_scan_start(struct Scsi_Host *);
|
2005-04-18 03:02:26 +07:00
|
|
|
static void qla2xxx_slave_destroy(struct scsi_device *);
|
2010-11-16 14:10:29 +07:00
|
|
|
static int qla2xxx_queuecommand(struct Scsi_Host *h, struct scsi_cmnd *cmd);
|
2005-04-17 05:20:36 +07:00
|
|
|
static int qla2xxx_eh_abort(struct scsi_cmnd *);
|
|
|
|
static int qla2xxx_eh_device_reset(struct scsi_cmnd *);
|
2008-04-04 03:13:24 +07:00
|
|
|
static int qla2xxx_eh_target_reset(struct scsi_cmnd *);
|
2005-04-17 05:20:36 +07:00
|
|
|
static int qla2xxx_eh_bus_reset(struct scsi_cmnd *);
|
|
|
|
static int qla2xxx_eh_host_reset(struct scsi_cmnd *);
|
|
|
|
|
2009-10-16 07:46:39 +07:00
|
|
|
static int qla2x00_change_queue_depth(struct scsi_device *, int, int);
|
2005-08-27 09:09:30 +07:00
|
|
|
static int qla2x00_change_queue_type(struct scsi_device *, int);
|
|
|
|
|
2009-03-24 23:07:56 +07:00
|
|
|
struct scsi_host_template qla2xxx_driver_template = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.module = THIS_MODULE,
|
2006-05-18 05:09:45 +07:00
|
|
|
.name = QLA2XXX_DRIVER_NAME,
|
2009-03-24 23:07:56 +07:00
|
|
|
.queuecommand = qla2xxx_queuecommand,
|
2005-07-07 00:31:47 +07:00
|
|
|
|
|
|
|
.eh_abort_handler = qla2xxx_eh_abort,
|
|
|
|
.eh_device_reset_handler = qla2xxx_eh_device_reset,
|
2008-04-04 03:13:24 +07:00
|
|
|
.eh_target_reset_handler = qla2xxx_eh_target_reset,
|
2005-07-07 00:31:47 +07:00
|
|
|
.eh_bus_reset_handler = qla2xxx_eh_bus_reset,
|
|
|
|
.eh_host_reset_handler = qla2xxx_eh_host_reset,
|
|
|
|
|
|
|
|
.slave_configure = qla2xxx_slave_configure,
|
|
|
|
|
|
|
|
.slave_alloc = qla2xxx_slave_alloc,
|
|
|
|
.slave_destroy = qla2xxx_slave_destroy,
|
2007-03-13 00:41:27 +07:00
|
|
|
.scan_finished = qla2xxx_scan_finished,
|
|
|
|
.scan_start = qla2xxx_scan_start,
|
2005-08-27 09:09:30 +07:00
|
|
|
.change_queue_depth = qla2x00_change_queue_depth,
|
|
|
|
.change_queue_type = qla2x00_change_queue_type,
|
2005-07-07 00:31:47 +07:00
|
|
|
.this_id = -1,
|
|
|
|
.cmd_per_lun = 3,
|
|
|
|
.use_clustering = ENABLE_CLUSTERING,
|
|
|
|
.sg_tablesize = SG_ALL,
|
|
|
|
|
|
|
|
.max_sectors = 0xFFFF,
|
2005-08-27 09:09:40 +07:00
|
|
|
.shost_attrs = qla2x00_host_attrs,
|
2012-05-16 01:34:28 +07:00
|
|
|
|
|
|
|
.supported_mode = MODE_INITIATOR,
|
2005-07-07 00:31:47 +07:00
|
|
|
};
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static struct scsi_transport_template *qla2xxx_transport_template = NULL;
|
2007-07-06 03:16:51 +07:00
|
|
|
struct scsi_transport_template *qla2xxx_transport_vport_template = NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* TODO Convert to inlines
|
|
|
|
*
|
|
|
|
* Timer routines
|
|
|
|
*/
|
|
|
|
|
2007-07-06 03:16:51 +07:00
|
|
|
__inline__ void
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_start_timer(scsi_qla_host_t *vha, void *func, unsigned long interval)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
init_timer(&vha->timer);
|
|
|
|
vha->timer.expires = jiffies + interval * HZ;
|
|
|
|
vha->timer.data = (unsigned long)vha;
|
|
|
|
vha->timer.function = (void (*)(unsigned long))func;
|
|
|
|
add_timer(&vha->timer);
|
|
|
|
vha->timer_active = 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_restart_timer(scsi_qla_host_t *vha, unsigned long interval)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2010-04-13 07:59:55 +07:00
|
|
|
/* Currently used for 82XX only. */
|
2011-07-15 02:00:13 +07:00
|
|
|
if (vha->device_flags & DFLG_DEV_FAILED) {
|
|
|
|
ql_dbg(ql_dbg_timer, vha, 0x600d,
|
|
|
|
"Device in a failed state, returning.\n");
|
2010-04-13 07:59:55 +07:00
|
|
|
return;
|
2011-07-15 02:00:13 +07:00
|
|
|
}
|
2010-04-13 07:59:55 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
mod_timer(&vha->timer, jiffies + interval * HZ);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-01-18 00:02:15 +07:00
|
|
|
static __inline__ void
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_stop_timer(scsi_qla_host_t *vha)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
del_timer_sync(&vha->timer);
|
|
|
|
vha->timer_active = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int qla2x00_do_dpc(void *data);
|
|
|
|
|
|
|
|
static void qla2x00_rst_aen(scsi_qla_host_t *);
|
|
|
|
|
2008-12-10 07:45:39 +07:00
|
|
|
static int qla2x00_mem_alloc(struct qla_hw_data *, uint16_t, uint16_t,
|
|
|
|
struct req_que **, struct rsp_que **);
|
2010-10-16 01:27:46 +07:00
|
|
|
static void qla2x00_free_fw_dump(struct qla_hw_data *);
|
2008-11-07 01:40:51 +07:00
|
|
|
static void qla2x00_mem_free(struct qla_hw_data *);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
2012-05-16 01:34:14 +07:00
|
|
|
static int qla2x00_alloc_queues(struct qla_hw_data *ha, struct req_que *req,
|
|
|
|
struct rsp_que *rsp)
|
2008-12-10 07:45:39 +07:00
|
|
|
{
|
2011-07-15 02:00:13 +07:00
|
|
|
scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
|
2009-04-07 12:33:40 +07:00
|
|
|
ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_req_queues,
|
2008-12-10 07:45:39 +07:00
|
|
|
GFP_KERNEL);
|
|
|
|
if (!ha->req_q_map) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_fatal, vha, 0x003b,
|
|
|
|
"Unable to allocate memory for request queue ptrs.\n");
|
2008-12-10 07:45:39 +07:00
|
|
|
goto fail_req_map;
|
|
|
|
}
|
|
|
|
|
2009-04-07 12:33:40 +07:00
|
|
|
ha->rsp_q_map = kzalloc(sizeof(struct rsp_que *) * ha->max_rsp_queues,
|
2008-12-10 07:45:39 +07:00
|
|
|
GFP_KERNEL);
|
|
|
|
if (!ha->rsp_q_map) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_fatal, vha, 0x003c,
|
|
|
|
"Unable to allocate memory for response queue ptrs.\n");
|
2008-12-10 07:45:39 +07:00
|
|
|
goto fail_rsp_map;
|
|
|
|
}
|
2012-05-16 01:34:14 +07:00
|
|
|
/*
|
|
|
|
* Make sure we record at least the request and response queue zero in
|
|
|
|
* case we need to free them if part of the probe fails.
|
|
|
|
*/
|
|
|
|
ha->rsp_q_map[0] = rsp;
|
|
|
|
ha->req_q_map[0] = req;
|
2008-12-10 07:45:39 +07:00
|
|
|
set_bit(0, ha->rsp_qid_map);
|
|
|
|
set_bit(0, ha->req_qid_map);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
fail_rsp_map:
|
|
|
|
kfree(ha->req_q_map);
|
|
|
|
ha->req_q_map = NULL;
|
|
|
|
fail_req_map:
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2009-04-07 12:33:40 +07:00
|
|
|
static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req)
|
2008-12-10 07:45:39 +07:00
|
|
|
{
|
|
|
|
if (req && req->ring)
|
|
|
|
dma_free_coherent(&ha->pdev->dev,
|
|
|
|
(req->length + 1) * sizeof(request_t),
|
|
|
|
req->ring, req->dma);
|
|
|
|
|
2013-01-30 15:34:37 +07:00
|
|
|
if (req)
|
|
|
|
kfree(req->outstanding_cmds);
|
|
|
|
|
2008-12-10 07:45:39 +07:00
|
|
|
kfree(req);
|
|
|
|
req = NULL;
|
|
|
|
}
|
|
|
|
|
2009-04-07 12:33:40 +07:00
|
|
|
static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp)
|
|
|
|
{
|
|
|
|
if (rsp && rsp->ring)
|
|
|
|
dma_free_coherent(&ha->pdev->dev,
|
|
|
|
(rsp->length + 1) * sizeof(response_t),
|
|
|
|
rsp->ring, rsp->dma);
|
|
|
|
|
|
|
|
kfree(rsp);
|
|
|
|
rsp = NULL;
|
|
|
|
}
|
|
|
|
|
2008-12-10 07:45:39 +07:00
|
|
|
static void qla2x00_free_queues(struct qla_hw_data *ha)
|
|
|
|
{
|
|
|
|
struct req_que *req;
|
|
|
|
struct rsp_que *rsp;
|
|
|
|
int cnt;
|
|
|
|
|
2009-04-07 12:33:40 +07:00
|
|
|
for (cnt = 0; cnt < ha->max_req_queues; cnt++) {
|
2008-12-10 07:45:39 +07:00
|
|
|
req = ha->req_q_map[cnt];
|
2009-04-07 12:33:40 +07:00
|
|
|
qla2x00_free_req_que(ha, req);
|
2008-12-10 07:45:39 +07:00
|
|
|
}
|
|
|
|
kfree(ha->req_q_map);
|
|
|
|
ha->req_q_map = NULL;
|
2009-04-07 12:33:40 +07:00
|
|
|
|
|
|
|
for (cnt = 0; cnt < ha->max_rsp_queues; cnt++) {
|
|
|
|
rsp = ha->rsp_q_map[cnt];
|
|
|
|
qla2x00_free_rsp_que(ha, rsp);
|
|
|
|
}
|
|
|
|
kfree(ha->rsp_q_map);
|
|
|
|
ha->rsp_q_map = NULL;
|
2008-12-10 07:45:39 +07:00
|
|
|
}
|
|
|
|
|
2009-04-07 12:33:41 +07:00
|
|
|
static int qla25xx_setup_mode(struct scsi_qla_host *vha)
|
|
|
|
{
|
|
|
|
uint16_t options = 0;
|
|
|
|
int ques, req, ret;
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
|
2009-08-05 23:18:40 +07:00
|
|
|
if (!(ha->fw_attributes & BIT_6)) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_warn, vha, 0x00d8,
|
|
|
|
"Firmware is not multi-queue capable.\n");
|
2009-08-05 23:18:40 +07:00
|
|
|
goto fail;
|
|
|
|
}
|
2009-04-07 12:33:41 +07:00
|
|
|
if (ql2xmultique_tag) {
|
|
|
|
/* create a request queue for IO */
|
|
|
|
options |= BIT_7;
|
|
|
|
req = qla25xx_create_req_que(ha, options, 0, 0, -1,
|
|
|
|
QLA_DEFAULT_QUE_QOS);
|
|
|
|
if (!req) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_warn, vha, 0x00e0,
|
|
|
|
"Failed to create request queue.\n");
|
2009-04-07 12:33:41 +07:00
|
|
|
goto fail;
|
|
|
|
}
|
2011-02-01 17:42:42 +07:00
|
|
|
ha->wq = alloc_workqueue("qla2xxx_wq", WQ_MEM_RECLAIM, 1);
|
2009-04-07 12:33:41 +07:00
|
|
|
vha->req = ha->req_q_map[req];
|
|
|
|
options |= BIT_1;
|
|
|
|
for (ques = 1; ques < ha->max_rsp_queues; ques++) {
|
|
|
|
ret = qla25xx_create_rsp_que(ha, options, 0, 0, req);
|
|
|
|
if (!ret) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_warn, vha, 0x00e8,
|
|
|
|
"Failed to create response queue.\n");
|
2009-04-07 12:33:41 +07:00
|
|
|
goto fail2;
|
|
|
|
}
|
|
|
|
}
|
2009-08-05 23:18:40 +07:00
|
|
|
ha->flags.cpu_affinity_enabled = 1;
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_multiq, vha, 0xc007,
|
|
|
|
"CPU affinity mode enalbed, "
|
|
|
|
"no. of response queues:%d no. of request queues:%d.\n",
|
|
|
|
ha->max_rsp_queues, ha->max_req_queues);
|
|
|
|
ql_dbg(ql_dbg_init, vha, 0x00e9,
|
|
|
|
"CPU affinity mode enalbed, "
|
|
|
|
"no. of response queues:%d no. of request queues:%d.\n",
|
|
|
|
ha->max_rsp_queues, ha->max_req_queues);
|
2009-04-07 12:33:41 +07:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
fail2:
|
|
|
|
qla25xx_delete_queues(vha);
|
2009-08-05 23:18:40 +07:00
|
|
|
destroy_workqueue(ha->wq);
|
|
|
|
ha->wq = NULL;
|
2011-11-19 00:02:12 +07:00
|
|
|
vha->req = ha->req_q_map[0];
|
2009-04-07 12:33:41 +07:00
|
|
|
fail:
|
|
|
|
ha->mqenable = 0;
|
2009-08-05 23:18:40 +07:00
|
|
|
kfree(ha->req_q_map);
|
|
|
|
kfree(ha->rsp_q_map);
|
|
|
|
ha->max_req_queues = ha->max_rsp_queues = 1;
|
2009-04-07 12:33:41 +07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static char *
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_pci_info_str(struct scsi_qla_host *vha, char *str)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-04-17 05:20:36 +07:00
|
|
|
static char *pci_bus_modes[] = {
|
|
|
|
"33", "66", "100", "133",
|
|
|
|
};
|
|
|
|
uint16_t pci_bus;
|
|
|
|
|
|
|
|
strcpy(str, "PCI");
|
|
|
|
pci_bus = (ha->pci_attr & (BIT_9 | BIT_10)) >> 9;
|
|
|
|
if (pci_bus) {
|
|
|
|
strcat(str, "-X (");
|
|
|
|
strcat(str, pci_bus_modes[pci_bus]);
|
|
|
|
} else {
|
|
|
|
pci_bus = (ha->pci_attr & BIT_8) >> 8;
|
|
|
|
strcat(str, " (");
|
|
|
|
strcat(str, pci_bus_modes[pci_bus]);
|
|
|
|
}
|
|
|
|
strcat(str, " MHz)");
|
|
|
|
|
|
|
|
return (str);
|
|
|
|
}
|
|
|
|
|
2005-07-07 00:31:47 +07:00
|
|
|
static char *
|
2008-11-07 01:40:51 +07:00
|
|
|
qla24xx_pci_info_str(struct scsi_qla_host *vha, char *str)
|
2005-07-07 00:31:47 +07:00
|
|
|
{
|
|
|
|
static char *pci_bus_modes[] = { "33", "66", "100", "133", };
|
2008-11-07 01:40:51 +07:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-07-07 00:31:47 +07:00
|
|
|
uint32_t pci_bus;
|
|
|
|
int pcie_reg;
|
|
|
|
|
2012-07-11 04:57:56 +07:00
|
|
|
pcie_reg = pci_pcie_cap(ha->pdev);
|
2005-07-07 00:31:47 +07:00
|
|
|
if (pcie_reg) {
|
|
|
|
char lwstr[6];
|
|
|
|
uint16_t pcie_lstat, lspeed, lwidth;
|
|
|
|
|
2012-07-11 04:57:56 +07:00
|
|
|
pcie_reg += PCI_EXP_LNKCAP;
|
2005-07-07 00:31:47 +07:00
|
|
|
pci_read_config_word(ha->pdev, pcie_reg, &pcie_lstat);
|
|
|
|
lspeed = pcie_lstat & (BIT_0 | BIT_1 | BIT_2 | BIT_3);
|
|
|
|
lwidth = (pcie_lstat &
|
|
|
|
(BIT_4 | BIT_5 | BIT_6 | BIT_7 | BIT_8 | BIT_9)) >> 4;
|
|
|
|
|
|
|
|
strcpy(str, "PCIe (");
|
2012-11-21 14:40:34 +07:00
|
|
|
switch (lspeed) {
|
|
|
|
case 1:
|
2008-04-04 03:13:21 +07:00
|
|
|
strcat(str, "2.5GT/s ");
|
2012-11-21 14:40:34 +07:00
|
|
|
break;
|
|
|
|
case 2:
|
2008-04-04 03:13:21 +07:00
|
|
|
strcat(str, "5.0GT/s ");
|
2012-11-21 14:40:34 +07:00
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
strcat(str, "8.0GT/s ");
|
|
|
|
break;
|
|
|
|
default:
|
2005-07-07 00:31:47 +07:00
|
|
|
strcat(str, "<unknown> ");
|
2012-11-21 14:40:34 +07:00
|
|
|
break;
|
|
|
|
}
|
2005-07-07 00:31:47 +07:00
|
|
|
snprintf(lwstr, sizeof(lwstr), "x%d)", lwidth);
|
|
|
|
strcat(str, lwstr);
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(str, "PCI");
|
|
|
|
pci_bus = (ha->pci_attr & CSRX_PCIX_BUS_MODE_MASK) >> 8;
|
|
|
|
if (pci_bus == 0 || pci_bus == 8) {
|
|
|
|
strcat(str, " (");
|
|
|
|
strcat(str, pci_bus_modes[pci_bus >> 3]);
|
|
|
|
} else {
|
|
|
|
strcat(str, "-X ");
|
|
|
|
if (pci_bus & BIT_2)
|
|
|
|
strcat(str, "Mode 2");
|
|
|
|
else
|
|
|
|
strcat(str, "Mode 1");
|
|
|
|
strcat(str, " (");
|
|
|
|
strcat(str, pci_bus_modes[pci_bus & ~BIT_2]);
|
|
|
|
}
|
|
|
|
strcat(str, " MHz)");
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2006-11-09 10:55:50 +07:00
|
|
|
static char *
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_fw_version_str(struct scsi_qla_host *vha, char *str)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
char un_str[10];
|
2008-11-07 01:40:51 +07:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-07-07 00:32:07 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
sprintf(str, "%d.%02d.%02d ", ha->fw_major_version,
|
|
|
|
ha->fw_minor_version,
|
|
|
|
ha->fw_subminor_version);
|
|
|
|
|
|
|
|
if (ha->fw_attributes & BIT_9) {
|
|
|
|
strcat(str, "FLX");
|
|
|
|
return (str);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (ha->fw_attributes & 0xFF) {
|
|
|
|
case 0x7:
|
|
|
|
strcat(str, "EF");
|
|
|
|
break;
|
|
|
|
case 0x17:
|
|
|
|
strcat(str, "TP");
|
|
|
|
break;
|
|
|
|
case 0x37:
|
|
|
|
strcat(str, "IP");
|
|
|
|
break;
|
|
|
|
case 0x77:
|
|
|
|
strcat(str, "VI");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
sprintf(un_str, "(%x)", ha->fw_attributes);
|
|
|
|
strcat(str, un_str);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ha->fw_attributes & 0x100)
|
|
|
|
strcat(str, "X");
|
|
|
|
|
|
|
|
return (str);
|
|
|
|
}
|
|
|
|
|
2006-11-09 10:55:50 +07:00
|
|
|
static char *
|
2008-11-07 01:40:51 +07:00
|
|
|
qla24xx_fw_version_str(struct scsi_qla_host *vha, char *str)
|
2005-07-07 00:31:47 +07:00
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-07-09 07:58:43 +07:00
|
|
|
|
2009-01-06 02:18:11 +07:00
|
|
|
sprintf(str, "%d.%02d.%02d (%x)", ha->fw_major_version,
|
|
|
|
ha->fw_minor_version, ha->fw_subminor_version, ha->fw_attributes);
|
2005-07-07 00:31:47 +07:00
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2012-02-10 02:15:36 +07:00
|
|
|
void
|
|
|
|
qla2x00_sp_free_dma(void *vha, void *ptr)
|
2005-07-07 00:31:47 +07:00
|
|
|
{
|
2012-02-10 02:15:36 +07:00
|
|
|
srb_t *sp = (srb_t *)ptr;
|
|
|
|
struct scsi_cmnd *cmd = GET_CMD_SP(sp);
|
|
|
|
struct qla_hw_data *ha = sp->fcport->vha->hw;
|
|
|
|
void *ctx = GET_CMD_CTX_SP(sp);
|
2005-07-07 00:31:47 +07:00
|
|
|
|
2012-02-10 02:15:36 +07:00
|
|
|
if (sp->flags & SRB_DMA_VALID) {
|
|
|
|
scsi_dma_unmap(cmd);
|
|
|
|
sp->flags &= ~SRB_DMA_VALID;
|
2011-07-15 02:00:13 +07:00
|
|
|
}
|
2005-07-07 00:31:47 +07:00
|
|
|
|
2012-02-10 02:15:36 +07:00
|
|
|
if (sp->flags & SRB_CRC_PROT_DMA_VALID) {
|
|
|
|
dma_unmap_sg(&ha->pdev->dev, scsi_prot_sglist(cmd),
|
|
|
|
scsi_prot_sg_count(cmd), cmd->sc_data_direction);
|
|
|
|
sp->flags &= ~SRB_CRC_PROT_DMA_VALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sp->flags & SRB_CRC_CTX_DSD_VALID) {
|
|
|
|
/* List assured to be having elements */
|
|
|
|
qla2x00_clean_dsd_pool(ha, sp);
|
|
|
|
sp->flags &= ~SRB_CRC_CTX_DSD_VALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sp->flags & SRB_CRC_CTX_DMA_VALID) {
|
|
|
|
dma_pool_free(ha->dl_dma_pool, ctx,
|
|
|
|
((struct crc_context *)ctx)->crc_ctx_dma);
|
|
|
|
sp->flags &= ~SRB_CRC_CTX_DMA_VALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sp->flags & SRB_FCP_CMND_DMA_VALID) {
|
|
|
|
struct ct6_dsd *ctx1 = (struct ct6_dsd *)ctx;
|
2005-07-07 00:31:47 +07:00
|
|
|
|
2012-02-10 02:15:36 +07:00
|
|
|
dma_pool_free(ha->fcp_cmnd_dma_pool, ctx1->fcp_cmnd,
|
|
|
|
ctx1->fcp_cmnd_dma);
|
|
|
|
list_splice(&ctx1->dsd_list, &ha->gbl_dsd_list);
|
|
|
|
ha->gbl_dsd_inuse -= ctx1->dsd_use_cnt;
|
|
|
|
ha->gbl_dsd_avail += ctx1->dsd_use_cnt;
|
|
|
|
mempool_free(ctx1, ha->ctx_mempool);
|
|
|
|
ctx1 = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
CMD_SP(cmd) = NULL;
|
2013-02-08 13:57:50 +07:00
|
|
|
qla2x00_rel_sp(sp->fcport->vha, sp);
|
2012-02-10 02:15:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
qla2x00_sp_compl(void *data, void *ptr, int res)
|
|
|
|
{
|
|
|
|
struct qla_hw_data *ha = (struct qla_hw_data *)data;
|
|
|
|
srb_t *sp = (srb_t *)ptr;
|
|
|
|
struct scsi_cmnd *cmd = GET_CMD_SP(sp);
|
|
|
|
|
|
|
|
cmd->result = res;
|
|
|
|
|
|
|
|
if (atomic_read(&sp->ref_count) == 0) {
|
|
|
|
ql_dbg(ql_dbg_io, sp->fcport->vha, 0x3015,
|
|
|
|
"SP reference-count to ZERO -- sp=%p cmd=%p.\n",
|
|
|
|
sp, GET_CMD_SP(sp));
|
|
|
|
if (ql2xextended_error_logging & ql_dbg_io)
|
|
|
|
BUG();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!atomic_dec_and_test(&sp->ref_count))
|
|
|
|
return;
|
|
|
|
|
|
|
|
qla2x00_sp_free_dma(ha, sp);
|
|
|
|
cmd->scsi_done(cmd);
|
2005-07-07 00:31:47 +07:00
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int
|
2011-02-24 06:27:06 +07:00
|
|
|
qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
|
2005-07-07 00:31:47 +07:00
|
|
|
{
|
2011-05-11 01:30:08 +07:00
|
|
|
scsi_qla_host_t *vha = shost_priv(host);
|
2005-07-07 00:31:47 +07:00
|
|
|
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
|
[SCSI] update fc_transport for removal of block/unblock functions
We recently went back to implement a board reset. When we perform the
reset, we wanted to tear down the internal data structures and rebuild
them. Unfortunately, when it came to the rport structure, things were
odd. If we deleted them, the scsi targets and sdevs would be
torn down. Not a good thing for a temporary reset. We could block the
rports, but we either maintain the internal structures to keep the
rport reference (perhaps even replicating what's in the transport),
or we have to fatten the fc transport with new search routines to find
the rport (and deal with a case of a dangling rport that the driver
forgets).
It dawned on me that we had actually reached this state incorrectly.
When the fc transport first started, we did the block/unblock first, then
added the rport interface. The purpose of block/unblock is to hide the
temporary disappearance of the rport (e.g. being deleted, then readded).
Why are we making the driver do the block/unblock ? We should be making
the transport have only an rport add/delete, and the let the transport
handle the block/unblock.
So... This patch removes the existing fc_remote_port_block/unblock
functions. It moves the block/unblock functionality into the
fc_remote_port_add/delete functions. Updates for the lpfc driver are
included. Qlogic driver updates are also enclosed, thanks to the
contributions of Andrew Vasquez. [Note: the qla2xxx changes are
relative to the scsi-misc-2.6 tree as of this morning - which does
not include the recent patches sent by Andrew]. The zfcp driver does
not use the block/unblock functions.
One last comment: The resulting behavior feels very clean. The LLDD is
concerned only with add/delete, which corresponds to the physical
disappearance. However, the fact that the scsi target and sdevs are
not immediately torn down after the LLDD calls delete causes an
interesting scenario... the midlayer can call the xxx_slave_alloc and
xxx_queuecommand functions with a sdev that is at the location the
rport used to be. The driver must validate the device exists when it
first enters these functions. In thinking about it, this has always
been the case for the LLDD and these routines. The existing drivers
already check for existence. However, this highlights that simple
validation via data structure dereferencing needs to be watched.
To deal with this, a new transport function, fc_remote_port_chkready()
was created that LLDDs should call when they first enter these two
routines. It validates the rport state, and returns a scsi result
which could be returned. In addition to solving the above, it also
creates consistent behavior from the LLDD's when the block and deletes
are occuring.
Rejections fixed up and
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2005-10-18 23:03:35 +07:00
|
|
|
struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device));
|
2008-11-07 01:40:51 +07:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
|
2005-07-07 00:31:47 +07:00
|
|
|
srb_t *sp;
|
|
|
|
int rval;
|
|
|
|
|
2009-12-16 12:29:46 +07:00
|
|
|
if (ha->flags.eeh_busy) {
|
2011-07-15 02:00:13 +07:00
|
|
|
if (ha->flags.pci_channel_io_perm_failure) {
|
2012-05-16 01:34:15 +07:00
|
|
|
ql_dbg(ql_dbg_aer, vha, 0x9010,
|
2011-07-15 02:00:13 +07:00
|
|
|
"PCI Channel IO permanent failure, exiting "
|
|
|
|
"cmd=%p.\n", cmd);
|
2009-03-24 23:08:18 +07:00
|
|
|
cmd->result = DID_NO_CONNECT << 16;
|
2011-07-15 02:00:13 +07:00
|
|
|
} else {
|
2012-05-16 01:34:15 +07:00
|
|
|
ql_dbg(ql_dbg_aer, vha, 0x9011,
|
2011-07-15 02:00:13 +07:00
|
|
|
"EEH_Busy, Requeuing the cmd=%p.\n", cmd);
|
2009-12-16 12:29:46 +07:00
|
|
|
cmd->result = DID_REQUEUE << 16;
|
2011-07-15 02:00:13 +07:00
|
|
|
}
|
2007-09-21 04:07:36 +07:00
|
|
|
goto qc24_fail_command;
|
|
|
|
}
|
|
|
|
|
[SCSI] update fc_transport for removal of block/unblock functions
We recently went back to implement a board reset. When we perform the
reset, we wanted to tear down the internal data structures and rebuild
them. Unfortunately, when it came to the rport structure, things were
odd. If we deleted them, the scsi targets and sdevs would be
torn down. Not a good thing for a temporary reset. We could block the
rports, but we either maintain the internal structures to keep the
rport reference (perhaps even replicating what's in the transport),
or we have to fatten the fc transport with new search routines to find
the rport (and deal with a case of a dangling rport that the driver
forgets).
It dawned on me that we had actually reached this state incorrectly.
When the fc transport first started, we did the block/unblock first, then
added the rport interface. The purpose of block/unblock is to hide the
temporary disappearance of the rport (e.g. being deleted, then readded).
Why are we making the driver do the block/unblock ? We should be making
the transport have only an rport add/delete, and the let the transport
handle the block/unblock.
So... This patch removes the existing fc_remote_port_block/unblock
functions. It moves the block/unblock functionality into the
fc_remote_port_add/delete functions. Updates for the lpfc driver are
included. Qlogic driver updates are also enclosed, thanks to the
contributions of Andrew Vasquez. [Note: the qla2xxx changes are
relative to the scsi-misc-2.6 tree as of this morning - which does
not include the recent patches sent by Andrew]. The zfcp driver does
not use the block/unblock functions.
One last comment: The resulting behavior feels very clean. The LLDD is
concerned only with add/delete, which corresponds to the physical
disappearance. However, the fact that the scsi target and sdevs are
not immediately torn down after the LLDD calls delete causes an
interesting scenario... the midlayer can call the xxx_slave_alloc and
xxx_queuecommand functions with a sdev that is at the location the
rport used to be. The driver must validate the device exists when it
first enters these functions. In thinking about it, this has always
been the case for the LLDD and these routines. The existing drivers
already check for existence. However, this highlights that simple
validation via data structure dereferencing needs to be watched.
To deal with this, a new transport function, fc_remote_port_chkready()
was created that LLDDs should call when they first enter these two
routines. It validates the rport state, and returns a scsi result
which could be returned. In addition to solving the above, it also
creates consistent behavior from the LLDD's when the block and deletes
are occuring.
Rejections fixed up and
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2005-10-18 23:03:35 +07:00
|
|
|
rval = fc_remote_port_chkready(rport);
|
|
|
|
if (rval) {
|
|
|
|
cmd->result = rval;
|
2012-05-16 01:34:15 +07:00
|
|
|
ql_dbg(ql_dbg_io + ql_dbg_verbose, vha, 0x3003,
|
2011-07-15 02:00:13 +07:00
|
|
|
"fc_remote_port_chkready failed for cmd=%p, rval=0x%x.\n",
|
|
|
|
cmd, rval);
|
2005-07-07 00:31:47 +07:00
|
|
|
goto qc24_fail_command;
|
|
|
|
}
|
|
|
|
|
2010-05-05 05:01:30 +07:00
|
|
|
if (!vha->flags.difdix_supported &&
|
|
|
|
scsi_get_prot_op(cmd) != SCSI_PROT_NORMAL) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_io, vha, 0x3004,
|
|
|
|
"DIF Cap not reg, fail DIF capable cmd's:%p.\n",
|
|
|
|
cmd);
|
2010-05-05 05:01:30 +07:00
|
|
|
cmd->result = DID_NO_CONNECT << 16;
|
|
|
|
goto qc24_fail_command;
|
|
|
|
}
|
2012-02-10 02:14:04 +07:00
|
|
|
|
|
|
|
if (!fcport) {
|
|
|
|
cmd->result = DID_NO_CONNECT << 16;
|
|
|
|
goto qc24_fail_command;
|
|
|
|
}
|
|
|
|
|
2005-07-07 00:31:47 +07:00
|
|
|
if (atomic_read(&fcport->state) != FCS_ONLINE) {
|
|
|
|
if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
|
2010-10-16 01:27:49 +07:00
|
|
|
atomic_read(&base_vha->loop_state) == LOOP_DEAD) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_io, vha, 0x3005,
|
|
|
|
"Returning DNC, fcport_state=%d loop_state=%d.\n",
|
|
|
|
atomic_read(&fcport->state),
|
|
|
|
atomic_read(&base_vha->loop_state));
|
2005-07-07 00:31:47 +07:00
|
|
|
cmd->result = DID_NO_CONNECT << 16;
|
|
|
|
goto qc24_fail_command;
|
|
|
|
}
|
2008-08-18 03:24:40 +07:00
|
|
|
goto qc24_target_busy;
|
2005-07-07 00:31:47 +07:00
|
|
|
}
|
|
|
|
|
2013-02-08 13:57:50 +07:00
|
|
|
sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC);
|
2013-01-30 15:34:38 +07:00
|
|
|
if (!sp) {
|
|
|
|
set_bit(HOST_RAMP_DOWN_QUEUE_DEPTH, &vha->dpc_flags);
|
2011-02-24 06:27:06 +07:00
|
|
|
goto qc24_host_busy;
|
2013-01-30 15:34:38 +07:00
|
|
|
}
|
2005-07-07 00:31:47 +07:00
|
|
|
|
2012-02-10 02:15:36 +07:00
|
|
|
sp->u.scmd.cmd = cmd;
|
|
|
|
sp->type = SRB_SCSI_CMD;
|
|
|
|
atomic_set(&sp->ref_count, 1);
|
|
|
|
CMD_SP(cmd) = (void *)sp;
|
|
|
|
sp->free = qla2x00_sp_free_dma;
|
|
|
|
sp->done = qla2x00_sp_compl;
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
rval = ha->isp_ops->start_scsi(sp);
|
2011-07-15 02:00:13 +07:00
|
|
|
if (rval != QLA_SUCCESS) {
|
2012-11-21 14:40:32 +07:00
|
|
|
ql_dbg(ql_dbg_io + ql_dbg_verbose, vha, 0x3013,
|
2011-07-15 02:00:13 +07:00
|
|
|
"Start scsi failed rval=%d for cmd=%p.\n", rval, cmd);
|
2013-01-30 15:34:38 +07:00
|
|
|
set_bit(HOST_RAMP_DOWN_QUEUE_DEPTH, &vha->dpc_flags);
|
2005-07-07 00:31:47 +07:00
|
|
|
goto qc24_host_busy_free_sp;
|
2011-07-15 02:00:13 +07:00
|
|
|
}
|
2005-07-07 00:31:47 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
qc24_host_busy_free_sp:
|
2012-02-10 02:15:36 +07:00
|
|
|
qla2x00_sp_free_dma(ha, sp);
|
2005-07-07 00:31:47 +07:00
|
|
|
|
2011-02-24 06:27:06 +07:00
|
|
|
qc24_host_busy:
|
2005-07-07 00:31:47 +07:00
|
|
|
return SCSI_MLQUEUE_HOST_BUSY;
|
|
|
|
|
2008-08-18 03:24:40 +07:00
|
|
|
qc24_target_busy:
|
|
|
|
return SCSI_MLQUEUE_TARGET_BUSY;
|
|
|
|
|
2005-07-07 00:31:47 +07:00
|
|
|
qc24_fail_command:
|
2011-02-24 06:27:06 +07:00
|
|
|
cmd->scsi_done(cmd);
|
2005-07-07 00:31:47 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* qla2x00_eh_wait_on_command
|
|
|
|
* Waits for the command to be returned by the Firmware for some
|
|
|
|
* max time.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* cmd = Scsi Command to wait on.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* Not Found : 0
|
|
|
|
* Found : 1
|
|
|
|
*/
|
|
|
|
static int
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_eh_wait_on_command(struct scsi_cmnd *cmd)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-08-27 09:10:10 +07:00
|
|
|
#define ABORT_POLLING_PERIOD 1000
|
|
|
|
#define ABORT_WAIT_ITER ((10 * 1000) / (ABORT_POLLING_PERIOD))
|
2005-04-18 03:02:26 +07:00
|
|
|
unsigned long wait_iter = ABORT_WAIT_ITER;
|
2009-12-16 12:29:46 +07:00
|
|
|
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-04-18 03:02:26 +07:00
|
|
|
int ret = QLA_SUCCESS;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-12-16 12:29:46 +07:00
|
|
|
if (unlikely(pci_channel_offline(ha->pdev)) || ha->flags.eeh_busy) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_taskm, vha, 0x8005,
|
|
|
|
"Return:eh_wait.\n");
|
2009-12-16 12:29:46 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-08-26 01:36:18 +07:00
|
|
|
while (CMD_SP(cmd) && wait_iter--) {
|
2005-08-27 09:10:10 +07:00
|
|
|
msleep(ABORT_POLLING_PERIOD);
|
2005-04-18 03:02:26 +07:00
|
|
|
}
|
|
|
|
if (CMD_SP(cmd))
|
|
|
|
ret = QLA_FUNCTION_FAILED;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-04-18 03:02:26 +07:00
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qla2x00_wait_for_hba_online
|
2005-07-07 00:32:07 +07:00
|
|
|
* Wait till the HBA is online after going through
|
2005-04-17 05:20:36 +07:00
|
|
|
* <= MAX_RETRIES_OF_ISP_ABORT or
|
|
|
|
* finally HBA is disabled ie marked offline
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* ha - pointer to host adapter structure
|
2005-07-07 00:32:07 +07:00
|
|
|
*
|
|
|
|
* Note:
|
2005-04-17 05:20:36 +07:00
|
|
|
* Does context switching-Release SPIN_LOCK
|
|
|
|
* (if any) before calling this routine.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* Success (Adapter is online) : 0
|
|
|
|
* Failed (Adapter is offline/disabled) : 1
|
|
|
|
*/
|
2006-02-01 07:05:17 +07:00
|
|
|
int
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_wait_for_hba_online(scsi_qla_host_t *vha)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-07-07 00:31:47 +07:00
|
|
|
int return_status;
|
|
|
|
unsigned long wait_online;
|
2008-11-07 01:40:51 +07:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-07-07 00:32:07 +07:00
|
|
|
wait_online = jiffies + (MAX_LOOP_TIMEOUT * HZ);
|
2008-11-07 01:40:51 +07:00
|
|
|
while (((test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) ||
|
|
|
|
test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) ||
|
|
|
|
test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) ||
|
|
|
|
ha->dpc_active) && time_before(jiffies, wait_online)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
msleep(1000);
|
|
|
|
}
|
2008-11-07 01:40:51 +07:00
|
|
|
if (base_vha->flags.online)
|
2005-07-07 00:32:07 +07:00
|
|
|
return_status = QLA_SUCCESS;
|
2005-04-17 05:20:36 +07:00
|
|
|
else
|
|
|
|
return_status = QLA_FUNCTION_FAILED;
|
|
|
|
|
|
|
|
return (return_status);
|
|
|
|
}
|
|
|
|
|
2010-05-05 05:01:32 +07:00
|
|
|
/*
|
|
|
|
* qla2x00_wait_for_reset_ready
|
|
|
|
* Wait till the HBA is online after going through
|
|
|
|
* <= MAX_RETRIES_OF_ISP_ABORT or
|
|
|
|
* finally HBA is disabled ie marked offline or flash
|
|
|
|
* operations are in progress.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* ha - pointer to host adapter structure
|
|
|
|
*
|
|
|
|
* Note:
|
|
|
|
* Does context switching-Release SPIN_LOCK
|
|
|
|
* (if any) before calling this routine.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* Success (Adapter is online/no flash ops) : 0
|
|
|
|
* Failed (Adapter is offline/disabled/flash ops in progress) : 1
|
|
|
|
*/
|
2010-07-23 17:28:37 +07:00
|
|
|
static int
|
2010-05-05 05:01:32 +07:00
|
|
|
qla2x00_wait_for_reset_ready(scsi_qla_host_t *vha)
|
|
|
|
{
|
|
|
|
int return_status;
|
|
|
|
unsigned long wait_online;
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
|
|
|
|
|
|
|
|
wait_online = jiffies + (MAX_LOOP_TIMEOUT * HZ);
|
|
|
|
while (((test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) ||
|
|
|
|
test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) ||
|
|
|
|
test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) ||
|
|
|
|
ha->optrom_state != QLA_SWAITING ||
|
|
|
|
ha->dpc_active) && time_before(jiffies, wait_online))
|
|
|
|
msleep(1000);
|
|
|
|
|
|
|
|
if (base_vha->flags.online && ha->optrom_state == QLA_SWAITING)
|
|
|
|
return_status = QLA_SUCCESS;
|
|
|
|
else
|
|
|
|
return_status = QLA_FUNCTION_FAILED;
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_taskm, vha, 0x8019,
|
|
|
|
"%s return status=%d.\n", __func__, return_status);
|
2010-05-05 05:01:32 +07:00
|
|
|
|
|
|
|
return return_status;
|
|
|
|
}
|
|
|
|
|
2009-03-24 23:08:07 +07:00
|
|
|
int
|
|
|
|
qla2x00_wait_for_chip_reset(scsi_qla_host_t *vha)
|
|
|
|
{
|
|
|
|
int return_status;
|
|
|
|
unsigned long wait_reset;
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
|
|
|
|
|
|
|
|
wait_reset = jiffies + (MAX_LOOP_TIMEOUT * HZ);
|
|
|
|
while (((test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) ||
|
|
|
|
test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) ||
|
|
|
|
test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) ||
|
|
|
|
ha->dpc_active) && time_before(jiffies, wait_reset)) {
|
|
|
|
|
|
|
|
msleep(1000);
|
|
|
|
|
|
|
|
if (!test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags) &&
|
|
|
|
ha->flags.chip_reset_done)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ha->flags.chip_reset_done)
|
|
|
|
return_status = QLA_SUCCESS;
|
|
|
|
else
|
|
|
|
return_status = QLA_FUNCTION_FAILED;
|
|
|
|
|
|
|
|
return return_status;
|
|
|
|
}
|
|
|
|
|
2010-05-29 05:08:18 +07:00
|
|
|
static void
|
|
|
|
sp_get(struct srb *sp)
|
|
|
|
{
|
|
|
|
atomic_inc(&sp->ref_count);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/**************************************************************************
|
|
|
|
* qla2xxx_eh_abort
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* The abort function will abort the specified command.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* cmd = Linux SCSI command packet to be aborted.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* Either SUCCESS or FAILED.
|
|
|
|
*
|
|
|
|
* Note:
|
2006-04-28 06:25:30 +07:00
|
|
|
* Only return FAILED if command not returned by firmware.
|
2005-04-17 05:20:36 +07:00
|
|
|
**************************************************************************/
|
2006-11-09 10:55:50 +07:00
|
|
|
static int
|
2005-04-17 05:20:36 +07:00
|
|
|
qla2xxx_eh_abort(struct scsi_cmnd *cmd)
|
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
|
2005-04-18 03:02:26 +07:00
|
|
|
srb_t *sp;
|
2011-02-24 06:27:17 +07:00
|
|
|
int ret;
|
2005-04-18 03:02:26 +07:00
|
|
|
unsigned int id, lun;
|
2005-05-28 05:04:47 +07:00
|
|
|
unsigned long flags;
|
2006-04-28 06:25:30 +07:00
|
|
|
int wait = 0;
|
2008-11-07 01:40:51 +07:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-04-18 03:02:26 +07:00
|
|
|
if (!CMD_SP(cmd))
|
2006-04-28 06:25:30 +07:00
|
|
|
return SUCCESS;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-02-24 06:27:17 +07:00
|
|
|
ret = fc_block_scsi_eh(cmd);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
|
|
|
ret = SUCCESS;
|
|
|
|
|
2005-04-18 03:02:26 +07:00
|
|
|
id = cmd->device->id;
|
|
|
|
lun = cmd->device->lun;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
2010-10-16 01:27:47 +07:00
|
|
|
sp = (srb_t *) CMD_SP(cmd);
|
|
|
|
if (!sp) {
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_taskm, vha, 0x8002,
|
2011-11-19 00:03:07 +07:00
|
|
|
"Aborting from RISC nexus=%ld:%d:%d sp=%p cmd=%p\n",
|
|
|
|
vha->host_no, id, lun, sp, cmd);
|
2008-12-19 01:06:15 +07:00
|
|
|
|
2010-10-16 01:27:47 +07:00
|
|
|
/* Get a reference to the sp and drop the lock.*/
|
|
|
|
sp_get(sp);
|
2010-05-29 05:08:18 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
2010-10-16 01:27:47 +07:00
|
|
|
if (ha->isp_ops->abort_command(sp)) {
|
2012-02-10 02:14:03 +07:00
|
|
|
ret = FAILED;
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_taskm, vha, 0x8003,
|
2011-11-19 00:03:07 +07:00
|
|
|
"Abort command mbx failed cmd=%p.\n", cmd);
|
2010-10-16 01:27:47 +07:00
|
|
|
} else {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_taskm, vha, 0x8004,
|
2011-11-19 00:03:07 +07:00
|
|
|
"Abort command mbx success cmd=%p.\n", cmd);
|
2010-10-16 01:27:47 +07:00
|
|
|
wait = 1;
|
|
|
|
}
|
2011-08-17 01:29:25 +07:00
|
|
|
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
2012-02-10 02:15:36 +07:00
|
|
|
sp->done(ha, sp, 0);
|
2011-08-17 01:29:25 +07:00
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-08-17 01:29:26 +07:00
|
|
|
/* Did the command return during mailbox execution? */
|
|
|
|
if (ret == FAILED && !CMD_SP(cmd))
|
|
|
|
ret = SUCCESS;
|
|
|
|
|
2005-04-18 03:02:26 +07:00
|
|
|
/* Wait for the command to be returned. */
|
2006-04-28 06:25:30 +07:00
|
|
|
if (wait) {
|
2008-11-07 01:40:51 +07:00
|
|
|
if (qla2x00_eh_wait_on_command(cmd) != QLA_SUCCESS) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_warn, vha, 0x8006,
|
2011-11-19 00:03:07 +07:00
|
|
|
"Abort handler timed out cmd=%p.\n", cmd);
|
2006-04-28 06:25:30 +07:00
|
|
|
ret = FAILED;
|
2005-04-18 03:02:26 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_info, vha, 0x801c,
|
2011-11-19 00:03:07 +07:00
|
|
|
"Abort command issued nexus=%ld:%d:%d -- %d %x.\n",
|
|
|
|
vha->host_no, id, lun, wait, ret);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-04-18 03:02:26 +07:00
|
|
|
return ret;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-07-23 17:28:35 +07:00
|
|
|
int
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t,
|
2010-07-23 17:28:35 +07:00
|
|
|
unsigned int l, enum nexus_wait_type type)
|
2005-04-18 03:02:26 +07:00
|
|
|
{
|
2008-12-19 01:06:15 +07:00
|
|
|
int cnt, match, status;
|
2005-05-28 05:04:47 +07:00
|
|
|
unsigned long flags;
|
2008-11-07 01:40:51 +07:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2008-12-10 07:45:39 +07:00
|
|
|
struct req_que *req;
|
2010-07-23 17:28:35 +07:00
|
|
|
srb_t *sp;
|
2012-02-10 02:15:36 +07:00
|
|
|
struct scsi_cmnd *cmd;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-04-04 03:13:24 +07:00
|
|
|
status = QLA_SUCCESS;
|
2008-12-19 01:06:15 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
2009-04-07 12:33:42 +07:00
|
|
|
req = vha->req;
|
2008-12-19 01:06:15 +07:00
|
|
|
for (cnt = 1; status == QLA_SUCCESS &&
|
2013-01-30 15:34:37 +07:00
|
|
|
cnt < req->num_outstanding_cmds; cnt++) {
|
2008-12-19 01:06:15 +07:00
|
|
|
sp = req->outstanding_cmds[cnt];
|
|
|
|
if (!sp)
|
2008-04-04 03:13:24 +07:00
|
|
|
continue;
|
2012-02-10 02:15:36 +07:00
|
|
|
if (sp->type != SRB_SCSI_CMD)
|
2009-08-21 01:06:04 +07:00
|
|
|
continue;
|
2008-12-19 01:06:15 +07:00
|
|
|
if (vha->vp_idx != sp->fcport->vha->vp_idx)
|
|
|
|
continue;
|
|
|
|
match = 0;
|
2012-02-10 02:15:36 +07:00
|
|
|
cmd = GET_CMD_SP(sp);
|
2008-12-19 01:06:15 +07:00
|
|
|
switch (type) {
|
|
|
|
case WAIT_HOST:
|
|
|
|
match = 1;
|
|
|
|
break;
|
|
|
|
case WAIT_TARGET:
|
2012-02-10 02:15:36 +07:00
|
|
|
match = cmd->device->id == t;
|
2008-12-19 01:06:15 +07:00
|
|
|
break;
|
|
|
|
case WAIT_LUN:
|
2012-02-10 02:15:36 +07:00
|
|
|
match = (cmd->device->id == t &&
|
|
|
|
cmd->device->lun == l);
|
2008-12-19 01:06:15 +07:00
|
|
|
break;
|
2008-12-10 07:45:39 +07:00
|
|
|
}
|
2008-12-19 01:06:15 +07:00
|
|
|
if (!match)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
2012-02-10 02:15:36 +07:00
|
|
|
status = qla2x00_eh_wait_on_command(cmd);
|
2008-12-19 01:06:15 +07:00
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2008-11-07 01:40:51 +07:00
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
2008-04-04 03:13:24 +07:00
|
|
|
|
|
|
|
return status;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-04-04 03:13:24 +07:00
|
|
|
static char *reset_errors[] = {
|
|
|
|
"HBA not online",
|
|
|
|
"HBA not ready",
|
|
|
|
"Task management failed",
|
|
|
|
"Waiting for command completions",
|
|
|
|
};
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-11-09 10:55:50 +07:00
|
|
|
static int
|
2008-04-04 03:13:24 +07:00
|
|
|
__qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
|
2009-04-07 12:33:40 +07:00
|
|
|
struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int, int))
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
|
2005-04-18 03:06:53 +07:00
|
|
|
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
|
2008-04-04 03:13:24 +07:00
|
|
|
int err;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
if (!fcport) {
|
2008-04-04 03:13:24 +07:00
|
|
|
return FAILED;
|
2011-07-15 02:00:13 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-02-24 06:27:17 +07:00
|
|
|
err = fc_block_scsi_eh(cmd);
|
|
|
|
if (err != 0)
|
|
|
|
return err;
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_info, vha, 0x8009,
|
2011-11-19 00:03:07 +07:00
|
|
|
"%s RESET ISSUED nexus=%ld:%d:%d cmd=%p.\n", name, vha->host_no,
|
2011-07-15 02:00:13 +07:00
|
|
|
cmd->device->id, cmd->device->lun, cmd);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-04-04 03:13:24 +07:00
|
|
|
err = 0;
|
2011-07-15 02:00:13 +07:00
|
|
|
if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
|
|
|
|
ql_log(ql_log_warn, vha, 0x800a,
|
|
|
|
"Wait for hba online failed for cmd=%p.\n", cmd);
|
2008-04-04 03:13:24 +07:00
|
|
|
goto eh_reset_failed;
|
2011-07-15 02:00:13 +07:00
|
|
|
}
|
2008-04-04 03:13:24 +07:00
|
|
|
err = 2;
|
2009-04-07 12:33:40 +07:00
|
|
|
if (do_reset(fcport, cmd->device->lun, cmd->request->cpu + 1)
|
2011-07-15 02:00:13 +07:00
|
|
|
!= QLA_SUCCESS) {
|
|
|
|
ql_log(ql_log_warn, vha, 0x800c,
|
|
|
|
"do_reset failed for cmd=%p.\n", cmd);
|
2008-04-04 03:13:24 +07:00
|
|
|
goto eh_reset_failed;
|
2011-07-15 02:00:13 +07:00
|
|
|
}
|
2008-04-04 03:13:24 +07:00
|
|
|
err = 3;
|
2008-11-07 01:40:51 +07:00
|
|
|
if (qla2x00_eh_wait_for_pending_commands(vha, cmd->device->id,
|
2011-07-15 02:00:13 +07:00
|
|
|
cmd->device->lun, type) != QLA_SUCCESS) {
|
|
|
|
ql_log(ql_log_warn, vha, 0x800d,
|
2012-08-23 01:20:58 +07:00
|
|
|
"wait for pending cmds failed for cmd=%p.\n", cmd);
|
2008-04-04 03:13:24 +07:00
|
|
|
goto eh_reset_failed;
|
2011-07-15 02:00:13 +07:00
|
|
|
}
|
2008-04-04 03:13:24 +07:00
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_info, vha, 0x800e,
|
2011-11-19 00:03:07 +07:00
|
|
|
"%s RESET SUCCEEDED nexus:%ld:%d:%d cmd=%p.\n", name,
|
|
|
|
vha->host_no, cmd->device->id, cmd->device->lun, cmd);
|
2008-04-04 03:13:24 +07:00
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
|
2010-07-23 17:28:35 +07:00
|
|
|
eh_reset_failed:
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_info, vha, 0x800f,
|
2011-11-19 00:03:07 +07:00
|
|
|
"%s RESET FAILED: %s nexus=%ld:%d:%d cmd=%p.\n", name,
|
|
|
|
reset_errors[err], vha->host_no, cmd->device->id, cmd->device->lun,
|
|
|
|
cmd);
|
2008-04-04 03:13:24 +07:00
|
|
|
return FAILED;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-04-04 03:13:24 +07:00
|
|
|
static int
|
|
|
|
qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
|
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-04-04 03:13:24 +07:00
|
|
|
return __qla2xxx_eh_generic_reset("DEVICE", WAIT_LUN, cmd,
|
|
|
|
ha->isp_ops->lun_reset);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2008-04-04 03:13:24 +07:00
|
|
|
qla2xxx_eh_target_reset(struct scsi_cmnd *cmd)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-04-04 03:13:24 +07:00
|
|
|
return __qla2xxx_eh_generic_reset("TARGET", WAIT_TARGET, cmd,
|
|
|
|
ha->isp_ops->target_reset);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
* qla2xxx_eh_bus_reset
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* The bus reset function will reset the bus and abort any executing
|
|
|
|
* commands.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* cmd = Linux SCSI command packet of the command that cause the
|
|
|
|
* bus reset.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* SUCCESS/FAILURE (defined as macro in scsi.h).
|
|
|
|
*
|
|
|
|
**************************************************************************/
|
2006-11-09 10:55:50 +07:00
|
|
|
static int
|
2005-04-17 05:20:36 +07:00
|
|
|
qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
|
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
|
2005-04-18 03:06:53 +07:00
|
|
|
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
|
2007-07-06 03:16:51 +07:00
|
|
|
int ret = FAILED;
|
2005-04-18 03:02:26 +07:00
|
|
|
unsigned int id, lun;
|
|
|
|
|
|
|
|
id = cmd->device->id;
|
|
|
|
lun = cmd->device->lun;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
if (!fcport) {
|
2005-04-18 03:02:26 +07:00
|
|
|
return ret;
|
2011-07-15 02:00:13 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-02-24 06:27:17 +07:00
|
|
|
ret = fc_block_scsi_eh(cmd);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
|
|
|
ret = FAILED;
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_info, vha, 0x8012,
|
2012-05-16 01:34:17 +07:00
|
|
|
"BUS RESET ISSUED nexus=%ld:%d:%d.\n", vha->host_no, id, lun);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_fatal, vha, 0x8013,
|
|
|
|
"Wait for hba online failed board disabled.\n");
|
2005-04-18 03:02:26 +07:00
|
|
|
goto eh_bus_reset_done;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2011-11-19 00:02:09 +07:00
|
|
|
if (qla2x00_loop_reset(vha) == QLA_SUCCESS)
|
|
|
|
ret = SUCCESS;
|
|
|
|
|
2005-04-18 03:02:26 +07:00
|
|
|
if (ret == FAILED)
|
|
|
|
goto eh_bus_reset_done;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-09-21 03:25:53 +07:00
|
|
|
/* Flush outstanding commands. */
|
2010-07-23 17:28:35 +07:00
|
|
|
if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, WAIT_HOST) !=
|
2011-07-15 02:00:13 +07:00
|
|
|
QLA_SUCCESS) {
|
|
|
|
ql_log(ql_log_warn, vha, 0x8014,
|
|
|
|
"Wait for pending commands failed.\n");
|
2005-09-21 03:25:53 +07:00
|
|
|
ret = FAILED;
|
2011-07-15 02:00:13 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-04-18 03:02:26 +07:00
|
|
|
eh_bus_reset_done:
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_warn, vha, 0x802b,
|
2011-11-19 00:03:07 +07:00
|
|
|
"BUS RESET %s nexus=%ld:%d:%d.\n",
|
2012-08-23 01:20:58 +07:00
|
|
|
(ret == FAILED) ? "FAILED" : "SUCCEEDED", vha->host_no, id, lun);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-04-18 03:02:26 +07:00
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
* qla2xxx_eh_host_reset
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* The reset function will reset the Adapter.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* cmd = Linux SCSI command packet of the command that cause the
|
|
|
|
* adapter reset.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* Either SUCCESS or FAILED.
|
|
|
|
*
|
|
|
|
* Note:
|
|
|
|
**************************************************************************/
|
2006-11-09 10:55:50 +07:00
|
|
|
static int
|
2005-04-17 05:20:36 +07:00
|
|
|
qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
|
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2007-07-06 03:16:51 +07:00
|
|
|
int ret = FAILED;
|
2005-04-18 03:02:26 +07:00
|
|
|
unsigned int id, lun;
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-04-18 03:02:26 +07:00
|
|
|
id = cmd->device->id;
|
|
|
|
lun = cmd->device->lun;
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_info, vha, 0x8018,
|
2011-11-19 00:03:07 +07:00
|
|
|
"ADAPTER RESET ISSUED nexus=%ld:%d:%d.\n", vha->host_no, id, lun);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-05-05 05:01:32 +07:00
|
|
|
if (qla2x00_wait_for_reset_ready(vha) != QLA_SUCCESS)
|
2005-04-18 03:02:26 +07:00
|
|
|
goto eh_host_reset_lock;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
if (vha != base_vha) {
|
|
|
|
if (qla2x00_vp_abort_isp(vha))
|
2005-04-18 03:02:26 +07:00
|
|
|
goto eh_host_reset_lock;
|
2008-11-07 01:40:51 +07:00
|
|
|
} else {
|
2010-04-13 07:59:55 +07:00
|
|
|
if (IS_QLA82XX(vha->hw)) {
|
|
|
|
if (!qla82xx_fcoe_ctx_reset(vha)) {
|
|
|
|
/* Ctx reset success */
|
|
|
|
ret = SUCCESS;
|
|
|
|
goto eh_host_reset_lock;
|
|
|
|
}
|
|
|
|
/* fall thru if ctx reset failed */
|
|
|
|
}
|
2009-04-07 12:33:41 +07:00
|
|
|
if (ha->wq)
|
|
|
|
flush_workqueue(ha->wq);
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
|
2010-04-13 07:59:55 +07:00
|
|
|
if (ha->isp_ops->abort_isp(base_vha)) {
|
2008-11-07 01:40:51 +07:00
|
|
|
clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
|
|
|
|
/* failed. schedule dpc to try */
|
|
|
|
set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
|
|
|
|
ql_log(ql_log_warn, vha, 0x802a,
|
|
|
|
"wait for hba online failed.\n");
|
2008-11-07 01:40:51 +07:00
|
|
|
goto eh_host_reset_lock;
|
2011-07-15 02:00:13 +07:00
|
|
|
}
|
2008-11-07 01:40:51 +07:00
|
|
|
}
|
|
|
|
clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
|
2005-07-07 00:32:07 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
/* Waiting for command to be returned to OS.*/
|
2010-07-23 17:28:35 +07:00
|
|
|
if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, WAIT_HOST) ==
|
2008-11-07 01:40:51 +07:00
|
|
|
QLA_SUCCESS)
|
2005-04-18 03:02:26 +07:00
|
|
|
ret = SUCCESS;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-04-18 03:02:26 +07:00
|
|
|
eh_host_reset_lock:
|
2011-11-19 00:03:07 +07:00
|
|
|
ql_log(ql_log_info, vha, 0x8017,
|
|
|
|
"ADAPTER RESET %s nexus=%ld:%d:%d.\n",
|
|
|
|
(ret == FAILED) ? "FAILED" : "SUCCEEDED", vha->host_no, id, lun);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-04-18 03:02:26 +07:00
|
|
|
return ret;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* qla2x00_loop_reset
|
|
|
|
* Issue loop reset.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* ha = adapter block pointer.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* 0 = success
|
|
|
|
*/
|
2008-01-18 00:02:12 +07:00
|
|
|
int
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_loop_reset(scsi_qla_host_t *vha)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-12-14 10:20:30 +07:00
|
|
|
int ret;
|
2005-04-18 03:06:53 +07:00
|
|
|
struct fc_port *fcport;
|
2008-11-07 01:40:51 +07:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-05-05 05:01:33 +07:00
|
|
|
if (ql2xtargetreset == 1 && ha->flags.enable_target_reset) {
|
2010-02-19 01:07:25 +07:00
|
|
|
list_for_each_entry(fcport, &vha->vp_fcports, list) {
|
|
|
|
if (fcport->port_type != FCT_TARGET)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ret = ha->isp_ops->target_reset(fcport, 0, 0);
|
|
|
|
if (ret != QLA_SUCCESS) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_taskm, vha, 0x802c,
|
|
|
|
"Bus Reset failed: Target Reset=%d "
|
|
|
|
"d_id=%x.\n", ret, fcport->d_id.b24);
|
2010-02-19 01:07:25 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-10 02:15:34 +07:00
|
|
|
if (ha->flags.enable_lip_full_login && !IS_CNA_CAPABLE(ha)) {
|
2013-02-08 13:57:42 +07:00
|
|
|
atomic_set(&vha->loop_state, LOOP_DOWN);
|
|
|
|
atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
|
|
|
|
qla2x00_mark_all_devices_lost(vha, 0);
|
2008-11-07 01:40:51 +07:00
|
|
|
ret = qla2x00_full_login_lip(vha);
|
2006-12-14 10:20:30 +07:00
|
|
|
if (ret != QLA_SUCCESS) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_taskm, vha, 0x802d,
|
|
|
|
"full_login_lip=%d.\n", ret);
|
2008-11-15 04:48:12 +07:00
|
|
|
}
|
2006-12-14 10:20:30 +07:00
|
|
|
}
|
|
|
|
|
2009-08-26 01:36:19 +07:00
|
|
|
if (ha->flags.enable_lip_reset) {
|
2008-11-07 01:40:51 +07:00
|
|
|
ret = qla2x00_lip_reset(vha);
|
2011-11-19 00:02:09 +07:00
|
|
|
if (ret != QLA_SUCCESS)
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_taskm, vha, 0x802e,
|
|
|
|
"lip_reset failed (%d).\n", ret);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Issue marker command only when we are going to start the I/O */
|
2008-11-07 01:40:51 +07:00
|
|
|
vha->marker_needed = 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-12-14 10:20:30 +07:00
|
|
|
return QLA_SUCCESS;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-02-01 03:33:46 +07:00
|
|
|
void
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res)
|
2008-02-01 03:33:46 +07:00
|
|
|
{
|
2008-12-10 07:45:39 +07:00
|
|
|
int que, cnt;
|
2008-02-01 03:33:46 +07:00
|
|
|
unsigned long flags;
|
|
|
|
srb_t *sp;
|
2008-11-07 01:40:51 +07:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2008-12-10 07:45:39 +07:00
|
|
|
struct req_que *req;
|
2008-02-01 03:33:46 +07:00
|
|
|
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
2009-04-07 12:33:40 +07:00
|
|
|
for (que = 0; que < ha->max_req_queues; que++) {
|
2009-01-09 06:41:08 +07:00
|
|
|
req = ha->req_q_map[que];
|
2008-12-10 07:45:39 +07:00
|
|
|
if (!req)
|
|
|
|
continue;
|
2013-01-30 15:34:37 +07:00
|
|
|
if (!req->outstanding_cmds)
|
|
|
|
continue;
|
|
|
|
for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++) {
|
2008-12-10 07:45:39 +07:00
|
|
|
sp = req->outstanding_cmds[cnt];
|
2009-03-24 23:08:04 +07:00
|
|
|
if (sp) {
|
2008-12-10 07:45:39 +07:00
|
|
|
req->outstanding_cmds[cnt] = NULL;
|
2012-02-10 02:15:36 +07:00
|
|
|
sp->done(vha, sp, res);
|
2008-12-10 07:45:39 +07:00
|
|
|
}
|
2008-02-01 03:33:46 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
}
|
|
|
|
|
2005-04-18 03:02:26 +07:00
|
|
|
static int
|
|
|
|
qla2xxx_slave_alloc(struct scsi_device *sdev)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-04-18 03:06:53 +07:00
|
|
|
struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
|
2005-04-17 05:20:36 +07:00
|
|
|
|
[SCSI] update fc_transport for removal of block/unblock functions
We recently went back to implement a board reset. When we perform the
reset, we wanted to tear down the internal data structures and rebuild
them. Unfortunately, when it came to the rport structure, things were
odd. If we deleted them, the scsi targets and sdevs would be
torn down. Not a good thing for a temporary reset. We could block the
rports, but we either maintain the internal structures to keep the
rport reference (perhaps even replicating what's in the transport),
or we have to fatten the fc transport with new search routines to find
the rport (and deal with a case of a dangling rport that the driver
forgets).
It dawned on me that we had actually reached this state incorrectly.
When the fc transport first started, we did the block/unblock first, then
added the rport interface. The purpose of block/unblock is to hide the
temporary disappearance of the rport (e.g. being deleted, then readded).
Why are we making the driver do the block/unblock ? We should be making
the transport have only an rport add/delete, and the let the transport
handle the block/unblock.
So... This patch removes the existing fc_remote_port_block/unblock
functions. It moves the block/unblock functionality into the
fc_remote_port_add/delete functions. Updates for the lpfc driver are
included. Qlogic driver updates are also enclosed, thanks to the
contributions of Andrew Vasquez. [Note: the qla2xxx changes are
relative to the scsi-misc-2.6 tree as of this morning - which does
not include the recent patches sent by Andrew]. The zfcp driver does
not use the block/unblock functions.
One last comment: The resulting behavior feels very clean. The LLDD is
concerned only with add/delete, which corresponds to the physical
disappearance. However, the fact that the scsi target and sdevs are
not immediately torn down after the LLDD calls delete causes an
interesting scenario... the midlayer can call the xxx_slave_alloc and
xxx_queuecommand functions with a sdev that is at the location the
rport used to be. The driver must validate the device exists when it
first enters these functions. In thinking about it, this has always
been the case for the LLDD and these routines. The existing drivers
already check for existence. However, this highlights that simple
validation via data structure dereferencing needs to be watched.
To deal with this, a new transport function, fc_remote_port_chkready()
was created that LLDDs should call when they first enter these two
routines. It validates the rport state, and returns a scsi result
which could be returned. In addition to solving the above, it also
creates consistent behavior from the LLDD's when the block and deletes
are occuring.
Rejections fixed up and
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2005-10-18 23:03:35 +07:00
|
|
|
if (!rport || fc_remote_port_chkready(rport))
|
2005-04-18 03:02:26 +07:00
|
|
|
return -ENXIO;
|
2005-04-18 03:06:53 +07:00
|
|
|
|
[SCSI] update fc_transport for removal of block/unblock functions
We recently went back to implement a board reset. When we perform the
reset, we wanted to tear down the internal data structures and rebuild
them. Unfortunately, when it came to the rport structure, things were
odd. If we deleted them, the scsi targets and sdevs would be
torn down. Not a good thing for a temporary reset. We could block the
rports, but we either maintain the internal structures to keep the
rport reference (perhaps even replicating what's in the transport),
or we have to fatten the fc transport with new search routines to find
the rport (and deal with a case of a dangling rport that the driver
forgets).
It dawned on me that we had actually reached this state incorrectly.
When the fc transport first started, we did the block/unblock first, then
added the rport interface. The purpose of block/unblock is to hide the
temporary disappearance of the rport (e.g. being deleted, then readded).
Why are we making the driver do the block/unblock ? We should be making
the transport have only an rport add/delete, and the let the transport
handle the block/unblock.
So... This patch removes the existing fc_remote_port_block/unblock
functions. It moves the block/unblock functionality into the
fc_remote_port_add/delete functions. Updates for the lpfc driver are
included. Qlogic driver updates are also enclosed, thanks to the
contributions of Andrew Vasquez. [Note: the qla2xxx changes are
relative to the scsi-misc-2.6 tree as of this morning - which does
not include the recent patches sent by Andrew]. The zfcp driver does
not use the block/unblock functions.
One last comment: The resulting behavior feels very clean. The LLDD is
concerned only with add/delete, which corresponds to the physical
disappearance. However, the fact that the scsi target and sdevs are
not immediately torn down after the LLDD calls delete causes an
interesting scenario... the midlayer can call the xxx_slave_alloc and
xxx_queuecommand functions with a sdev that is at the location the
rport used to be. The driver must validate the device exists when it
first enters these functions. In thinking about it, this has always
been the case for the LLDD and these routines. The existing drivers
already check for existence. However, this highlights that simple
validation via data structure dereferencing needs to be watched.
To deal with this, a new transport function, fc_remote_port_chkready()
was created that LLDDs should call when they first enter these two
routines. It validates the rport state, and returns a scsi result
which could be returned. In addition to solving the above, it also
creates consistent behavior from the LLDD's when the block and deletes
are occuring.
Rejections fixed up and
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2005-10-18 23:03:35 +07:00
|
|
|
sdev->hostdata = *(fc_port_t **)rport->dd_data;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-04-18 03:02:26 +07:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-04-18 03:02:26 +07:00
|
|
|
static int
|
|
|
|
qla2xxx_slave_configure(struct scsi_device *sdev)
|
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_qla_host_t *vha = shost_priv(sdev->host);
|
2009-04-07 12:33:40 +07:00
|
|
|
struct req_que *req = vha->req;
|
2005-04-18 03:04:54 +07:00
|
|
|
|
2012-08-23 01:21:31 +07:00
|
|
|
if (IS_T10_PI_CAPABLE(vha->hw))
|
|
|
|
blk_queue_update_dma_alignment(sdev->request_queue, 0x7);
|
|
|
|
|
2005-04-18 03:02:26 +07:00
|
|
|
if (sdev->tagged_supported)
|
2008-12-10 07:45:39 +07:00
|
|
|
scsi_activate_tcq(sdev, req->max_q_depth);
|
2005-04-18 03:02:26 +07:00
|
|
|
else
|
2008-12-10 07:45:39 +07:00
|
|
|
scsi_deactivate_tcq(sdev, req->max_q_depth);
|
2005-04-18 03:02:26 +07:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-04-18 03:02:26 +07:00
|
|
|
static void
|
|
|
|
qla2xxx_slave_destroy(struct scsi_device *sdev)
|
|
|
|
{
|
|
|
|
sdev->hostdata = NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-12-03 01:36:54 +07:00
|
|
|
static void qla2x00_handle_queue_full(struct scsi_device *sdev, int qdepth)
|
|
|
|
{
|
|
|
|
fc_port_t *fcport = (struct fc_port *) sdev->hostdata;
|
|
|
|
|
|
|
|
if (!scsi_track_queue_full(sdev, qdepth))
|
|
|
|
return;
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_io, fcport->vha, 0x3029,
|
2011-11-19 00:03:07 +07:00
|
|
|
"Queue depth adjusted-down to %d for nexus=%ld:%d:%d.\n",
|
|
|
|
sdev->queue_depth, fcport->vha->host_no, sdev->id, sdev->lun);
|
2009-12-03 01:36:54 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, int qdepth)
|
|
|
|
{
|
|
|
|
fc_port_t *fcport = sdev->hostdata;
|
|
|
|
struct scsi_qla_host *vha = fcport->vha;
|
|
|
|
struct req_que *req = NULL;
|
|
|
|
|
|
|
|
req = vha->req;
|
|
|
|
if (!req)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (req->max_q_depth <= sdev->queue_depth || req->max_q_depth < qdepth)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (sdev->ordered_tags)
|
|
|
|
scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, qdepth);
|
|
|
|
else
|
|
|
|
scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, qdepth);
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_io, vha, 0x302a,
|
2011-11-19 00:03:07 +07:00
|
|
|
"Queue depth adjusted-up to %d for nexus=%ld:%d:%d.\n",
|
|
|
|
sdev->queue_depth, fcport->vha->host_no, sdev->id, sdev->lun);
|
2009-12-03 01:36:54 +07:00
|
|
|
}
|
|
|
|
|
2005-08-27 09:09:30 +07:00
|
|
|
static int
|
2009-10-16 07:46:39 +07:00
|
|
|
qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
|
2005-08-27 09:09:30 +07:00
|
|
|
{
|
2009-12-03 01:36:54 +07:00
|
|
|
switch (reason) {
|
|
|
|
case SCSI_QDEPTH_DEFAULT:
|
|
|
|
scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
|
|
|
|
break;
|
|
|
|
case SCSI_QDEPTH_QFULL:
|
|
|
|
qla2x00_handle_queue_full(sdev, qdepth);
|
|
|
|
break;
|
|
|
|
case SCSI_QDEPTH_RAMP_UP:
|
|
|
|
qla2x00_adjust_sdev_qdepth_up(sdev, qdepth);
|
|
|
|
break;
|
|
|
|
default:
|
2010-01-29 17:25:19 +07:00
|
|
|
return -EOPNOTSUPP;
|
2009-12-03 01:36:54 +07:00
|
|
|
}
|
2009-10-16 07:46:39 +07:00
|
|
|
|
2005-08-27 09:09:30 +07:00
|
|
|
return sdev->queue_depth;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
qla2x00_change_queue_type(struct scsi_device *sdev, int tag_type)
|
|
|
|
{
|
|
|
|
if (sdev->tagged_supported) {
|
|
|
|
scsi_set_tag_type(sdev, tag_type);
|
|
|
|
if (tag_type)
|
|
|
|
scsi_activate_tcq(sdev, sdev->queue_depth);
|
|
|
|
else
|
|
|
|
scsi_deactivate_tcq(sdev, sdev->queue_depth);
|
|
|
|
} else
|
|
|
|
tag_type = 0;
|
|
|
|
|
|
|
|
return tag_type;
|
|
|
|
}
|
|
|
|
|
2013-01-30 15:34:38 +07:00
|
|
|
static void
|
|
|
|
qla2x00_host_ramp_down_queuedepth(scsi_qla_host_t *vha)
|
|
|
|
{
|
|
|
|
scsi_qla_host_t *vp;
|
|
|
|
struct Scsi_Host *shost;
|
|
|
|
struct scsi_device *sdev;
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
ha->host_last_rampdown_time = jiffies;
|
|
|
|
|
|
|
|
if (ha->cfg_lun_q_depth <= vha->host->cmd_per_lun)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ((ha->cfg_lun_q_depth / 2) < vha->host->cmd_per_lun)
|
|
|
|
ha->cfg_lun_q_depth = vha->host->cmd_per_lun;
|
|
|
|
else
|
|
|
|
ha->cfg_lun_q_depth = ha->cfg_lun_q_depth / 2;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Geometrically ramp down the queue depth for all devices on this
|
|
|
|
* adapter
|
|
|
|
*/
|
|
|
|
spin_lock_irqsave(&ha->vport_slock, flags);
|
|
|
|
list_for_each_entry(vp, &ha->vp_list, list) {
|
|
|
|
shost = vp->host;
|
|
|
|
shost_for_each_device(sdev, shost) {
|
|
|
|
if (sdev->queue_depth > shost->cmd_per_lun) {
|
|
|
|
if (sdev->queue_depth < ha->cfg_lun_q_depth)
|
|
|
|
continue;
|
|
|
|
ql_log(ql_log_warn, vp, 0x3031,
|
|
|
|
"%ld:%d:%d: Ramping down queue depth to %d",
|
|
|
|
vp->host_no, sdev->id, sdev->lun,
|
|
|
|
ha->cfg_lun_q_depth);
|
|
|
|
qla2x00_change_queue_depth(sdev,
|
|
|
|
ha->cfg_lun_q_depth, SCSI_QDEPTH_DEFAULT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ha->vport_slock, flags);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
qla2x00_host_ramp_up_queuedepth(scsi_qla_host_t *vha)
|
|
|
|
{
|
|
|
|
scsi_qla_host_t *vp;
|
|
|
|
struct Scsi_Host *shost;
|
|
|
|
struct scsi_device *sdev;
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
ha->host_last_rampup_time = jiffies;
|
|
|
|
ha->cfg_lun_q_depth++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Linearly ramp up the queue depth for all devices on this
|
|
|
|
* adapter
|
|
|
|
*/
|
|
|
|
spin_lock_irqsave(&ha->vport_slock, flags);
|
|
|
|
list_for_each_entry(vp, &ha->vp_list, list) {
|
|
|
|
shost = vp->host;
|
|
|
|
shost_for_each_device(sdev, shost) {
|
|
|
|
if (sdev->queue_depth > ha->cfg_lun_q_depth)
|
|
|
|
continue;
|
|
|
|
qla2x00_change_queue_depth(sdev, ha->cfg_lun_q_depth,
|
|
|
|
SCSI_QDEPTH_RAMP_UP);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ha->vport_slock, flags);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/**
|
|
|
|
* qla2x00_config_dma_addressing() - Configure OS DMA addressing method.
|
|
|
|
* @ha: HA context
|
|
|
|
*
|
|
|
|
* At exit, the @ha's flags.enable_64bit_addressing set to indicated
|
|
|
|
* supported addressing method.
|
|
|
|
*/
|
|
|
|
static void
|
2009-01-23 00:45:37 +07:00
|
|
|
qla2x00_config_dma_addressing(struct qla_hw_data *ha)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-08-27 09:08:00 +07:00
|
|
|
/* Assume a 32bit DMA mask. */
|
2005-04-17 05:20:36 +07:00
|
|
|
ha->flags.enable_64bit_addressing = 0;
|
|
|
|
|
2009-04-07 09:01:13 +07:00
|
|
|
if (!dma_set_mask(&ha->pdev->dev, DMA_BIT_MASK(64))) {
|
2005-08-27 09:08:00 +07:00
|
|
|
/* Any upper-dword bits set? */
|
|
|
|
if (MSD(dma_get_required_mask(&ha->pdev->dev)) &&
|
2009-04-07 09:01:13 +07:00
|
|
|
!pci_set_consistent_dma_mask(ha->pdev, DMA_BIT_MASK(64))) {
|
2005-08-27 09:08:00 +07:00
|
|
|
/* Ok, a 64bit DMA mask is applicable. */
|
2005-04-17 05:20:36 +07:00
|
|
|
ha->flags.enable_64bit_addressing = 1;
|
2007-07-20 05:06:00 +07:00
|
|
|
ha->isp_ops->calc_req_entries = qla2x00_calc_iocbs_64;
|
|
|
|
ha->isp_ops->build_iocbs = qla2x00_build_scsi_iocbs_64;
|
2005-08-27 09:08:00 +07:00
|
|
|
return;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
2005-08-27 09:08:00 +07:00
|
|
|
|
2009-04-07 09:01:15 +07:00
|
|
|
dma_set_mask(&ha->pdev->dev, DMA_BIT_MASK(32));
|
|
|
|
pci_set_consistent_dma_mask(ha->pdev, DMA_BIT_MASK(32));
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2007-07-20 05:06:00 +07:00
|
|
|
static void
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_enable_intrs(struct qla_hw_data *ha)
|
2007-07-20 05:06:00 +07:00
|
|
|
{
|
|
|
|
unsigned long flags = 0;
|
|
|
|
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
ha->interrupts_on = 1;
|
|
|
|
/* enable risc and host interrupts */
|
|
|
|
WRT_REG_WORD(®->ictrl, ICR_EN_INT | ICR_EN_RISC);
|
|
|
|
RD_REG_WORD(®->ictrl);
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_disable_intrs(struct qla_hw_data *ha)
|
2007-07-20 05:06:00 +07:00
|
|
|
{
|
|
|
|
unsigned long flags = 0;
|
|
|
|
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
ha->interrupts_on = 0;
|
|
|
|
/* disable risc and host interrupts */
|
|
|
|
WRT_REG_WORD(®->ictrl, 0);
|
|
|
|
RD_REG_WORD(®->ictrl);
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-11-07 01:40:51 +07:00
|
|
|
qla24xx_enable_intrs(struct qla_hw_data *ha)
|
2007-07-20 05:06:00 +07:00
|
|
|
{
|
|
|
|
unsigned long flags = 0;
|
|
|
|
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
ha->interrupts_on = 1;
|
|
|
|
WRT_REG_DWORD(®->ictrl, ICRX_EN_RISC_INT);
|
|
|
|
RD_REG_DWORD(®->ictrl);
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-11-07 01:40:51 +07:00
|
|
|
qla24xx_disable_intrs(struct qla_hw_data *ha)
|
2007-07-20 05:06:00 +07:00
|
|
|
{
|
|
|
|
unsigned long flags = 0;
|
|
|
|
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
|
|
|
|
|
2009-01-06 02:18:06 +07:00
|
|
|
if (IS_NOPOLLING_TYPE(ha))
|
|
|
|
return;
|
2007-07-20 05:06:00 +07:00
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
ha->interrupts_on = 0;
|
|
|
|
WRT_REG_DWORD(®->ictrl, 0);
|
|
|
|
RD_REG_DWORD(®->ictrl);
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
}
|
|
|
|
|
2011-11-19 00:03:16 +07:00
|
|
|
static int
|
|
|
|
qla2x00_iospace_config(struct qla_hw_data *ha)
|
|
|
|
{
|
|
|
|
resource_size_t pio;
|
|
|
|
uint16_t msix;
|
|
|
|
int cpus;
|
|
|
|
|
|
|
|
if (pci_request_selected_regions(ha->pdev, ha->bars,
|
|
|
|
QLA2XXX_DRIVER_NAME)) {
|
|
|
|
ql_log_pci(ql_log_fatal, ha->pdev, 0x0011,
|
|
|
|
"Failed to reserve PIO/MMIO regions (%s), aborting.\n",
|
|
|
|
pci_name(ha->pdev));
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
|
|
|
if (!(ha->bars & 1))
|
|
|
|
goto skip_pio;
|
|
|
|
|
|
|
|
/* We only need PIO for Flash operations on ISP2312 v2 chips. */
|
|
|
|
pio = pci_resource_start(ha->pdev, 0);
|
|
|
|
if (pci_resource_flags(ha->pdev, 0) & IORESOURCE_IO) {
|
|
|
|
if (pci_resource_len(ha->pdev, 0) < MIN_IOBASE_LEN) {
|
|
|
|
ql_log_pci(ql_log_warn, ha->pdev, 0x0012,
|
|
|
|
"Invalid pci I/O region size (%s).\n",
|
|
|
|
pci_name(ha->pdev));
|
|
|
|
pio = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ql_log_pci(ql_log_warn, ha->pdev, 0x0013,
|
|
|
|
"Region #0 no a PIO resource (%s).\n",
|
|
|
|
pci_name(ha->pdev));
|
|
|
|
pio = 0;
|
|
|
|
}
|
|
|
|
ha->pio_address = pio;
|
|
|
|
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0014,
|
|
|
|
"PIO address=%llu.\n",
|
|
|
|
(unsigned long long)ha->pio_address);
|
|
|
|
|
|
|
|
skip_pio:
|
|
|
|
/* Use MMIO operations for all accesses. */
|
|
|
|
if (!(pci_resource_flags(ha->pdev, 1) & IORESOURCE_MEM)) {
|
|
|
|
ql_log_pci(ql_log_fatal, ha->pdev, 0x0015,
|
|
|
|
"Region #1 not an MMIO resource (%s), aborting.\n",
|
|
|
|
pci_name(ha->pdev));
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
|
|
|
if (pci_resource_len(ha->pdev, 1) < MIN_IOBASE_LEN) {
|
|
|
|
ql_log_pci(ql_log_fatal, ha->pdev, 0x0016,
|
|
|
|
"Invalid PCI mem region size (%s), aborting.\n",
|
|
|
|
pci_name(ha->pdev));
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
ha->iobase = ioremap(pci_resource_start(ha->pdev, 1), MIN_IOBASE_LEN);
|
|
|
|
if (!ha->iobase) {
|
|
|
|
ql_log_pci(ql_log_fatal, ha->pdev, 0x0017,
|
|
|
|
"Cannot remap MMIO (%s), aborting.\n",
|
|
|
|
pci_name(ha->pdev));
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Determine queue resources */
|
|
|
|
ha->max_req_queues = ha->max_rsp_queues = 1;
|
|
|
|
if ((ql2xmaxqueues <= 1 && !ql2xmultique_tag) ||
|
|
|
|
(ql2xmaxqueues > 1 && ql2xmultique_tag) ||
|
|
|
|
(!IS_QLA25XX(ha) && !IS_QLA81XX(ha)))
|
|
|
|
goto mqiobase_exit;
|
|
|
|
|
|
|
|
ha->mqiobase = ioremap(pci_resource_start(ha->pdev, 3),
|
|
|
|
pci_resource_len(ha->pdev, 3));
|
|
|
|
if (ha->mqiobase) {
|
|
|
|
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0018,
|
|
|
|
"MQIO Base=%p.\n", ha->mqiobase);
|
|
|
|
/* Read MSIX vector size of the board */
|
|
|
|
pci_read_config_word(ha->pdev, QLA_PCI_MSIX_CONTROL, &msix);
|
|
|
|
ha->msix_count = msix;
|
|
|
|
/* Max queues are bounded by available msix vectors */
|
|
|
|
/* queue 0 uses two msix vectors */
|
|
|
|
if (ql2xmultique_tag) {
|
|
|
|
cpus = num_online_cpus();
|
|
|
|
ha->max_rsp_queues = (ha->msix_count - 1 > cpus) ?
|
|
|
|
(cpus + 1) : (ha->msix_count - 1);
|
|
|
|
ha->max_req_queues = 2;
|
|
|
|
} else if (ql2xmaxqueues > 1) {
|
|
|
|
ha->max_req_queues = ql2xmaxqueues > QLA_MQ_SIZE ?
|
|
|
|
QLA_MQ_SIZE : ql2xmaxqueues;
|
|
|
|
ql_dbg_pci(ql_dbg_multiq, ha->pdev, 0xc008,
|
|
|
|
"QoS mode set, max no of request queues:%d.\n",
|
|
|
|
ha->max_req_queues);
|
|
|
|
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0019,
|
|
|
|
"QoS mode set, max no of request queues:%d.\n",
|
|
|
|
ha->max_req_queues);
|
|
|
|
}
|
|
|
|
ql_log_pci(ql_log_info, ha->pdev, 0x001a,
|
|
|
|
"MSI-X vector count: %d.\n", msix);
|
|
|
|
} else
|
|
|
|
ql_log_pci(ql_log_info, ha->pdev, 0x001b,
|
|
|
|
"BAR 3 not enabled.\n");
|
|
|
|
|
|
|
|
mqiobase_exit:
|
|
|
|
ha->msix_count = ha->max_rsp_queues + 1;
|
|
|
|
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x001c,
|
|
|
|
"MSIX Count:%d.\n", ha->msix_count);
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
iospace_error_exit:
|
|
|
|
return (-ENOMEM);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-10 02:15:34 +07:00
|
|
|
static int
|
|
|
|
qla83xx_iospace_config(struct qla_hw_data *ha)
|
|
|
|
{
|
|
|
|
uint16_t msix;
|
|
|
|
int cpus;
|
|
|
|
|
|
|
|
if (pci_request_selected_regions(ha->pdev, ha->bars,
|
|
|
|
QLA2XXX_DRIVER_NAME)) {
|
|
|
|
ql_log_pci(ql_log_fatal, ha->pdev, 0x0117,
|
|
|
|
"Failed to reserve PIO/MMIO regions (%s), aborting.\n",
|
|
|
|
pci_name(ha->pdev));
|
|
|
|
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Use MMIO operations for all accesses. */
|
|
|
|
if (!(pci_resource_flags(ha->pdev, 0) & IORESOURCE_MEM)) {
|
|
|
|
ql_log_pci(ql_log_warn, ha->pdev, 0x0118,
|
|
|
|
"Invalid pci I/O region size (%s).\n",
|
|
|
|
pci_name(ha->pdev));
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
|
|
|
if (pci_resource_len(ha->pdev, 0) < MIN_IOBASE_LEN) {
|
|
|
|
ql_log_pci(ql_log_warn, ha->pdev, 0x0119,
|
|
|
|
"Invalid PCI mem region size (%s), aborting\n",
|
|
|
|
pci_name(ha->pdev));
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
ha->iobase = ioremap(pci_resource_start(ha->pdev, 0), MIN_IOBASE_LEN);
|
|
|
|
if (!ha->iobase) {
|
|
|
|
ql_log_pci(ql_log_fatal, ha->pdev, 0x011a,
|
|
|
|
"Cannot remap MMIO (%s), aborting.\n",
|
|
|
|
pci_name(ha->pdev));
|
|
|
|
goto iospace_error_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 64bit PCI BAR - BAR2 will correspoond to region 4 */
|
|
|
|
/* 83XX 26XX always use MQ type access for queues
|
|
|
|
* - mbar 2, a.k.a region 4 */
|
|
|
|
ha->max_req_queues = ha->max_rsp_queues = 1;
|
|
|
|
ha->mqiobase = ioremap(pci_resource_start(ha->pdev, 4),
|
|
|
|
pci_resource_len(ha->pdev, 4));
|
|
|
|
|
|
|
|
if (!ha->mqiobase) {
|
|
|
|
ql_log_pci(ql_log_fatal, ha->pdev, 0x011d,
|
|
|
|
"BAR2/region4 not enabled\n");
|
|
|
|
goto mqiobase_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
ha->msixbase = ioremap(pci_resource_start(ha->pdev, 2),
|
|
|
|
pci_resource_len(ha->pdev, 2));
|
|
|
|
if (ha->msixbase) {
|
|
|
|
/* Read MSIX vector size of the board */
|
|
|
|
pci_read_config_word(ha->pdev,
|
|
|
|
QLA_83XX_PCI_MSIX_CONTROL, &msix);
|
|
|
|
ha->msix_count = msix;
|
|
|
|
/* Max queues are bounded by available msix vectors */
|
|
|
|
/* queue 0 uses two msix vectors */
|
|
|
|
if (ql2xmultique_tag) {
|
|
|
|
cpus = num_online_cpus();
|
|
|
|
ha->max_rsp_queues = (ha->msix_count - 1 > cpus) ?
|
|
|
|
(cpus + 1) : (ha->msix_count - 1);
|
|
|
|
ha->max_req_queues = 2;
|
|
|
|
} else if (ql2xmaxqueues > 1) {
|
|
|
|
ha->max_req_queues = ql2xmaxqueues > QLA_MQ_SIZE ?
|
|
|
|
QLA_MQ_SIZE : ql2xmaxqueues;
|
|
|
|
ql_dbg_pci(ql_dbg_multiq, ha->pdev, 0xc00c,
|
|
|
|
"QoS mode set, max no of request queues:%d.\n",
|
|
|
|
ha->max_req_queues);
|
|
|
|
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011b,
|
|
|
|
"QoS mode set, max no of request queues:%d.\n",
|
|
|
|
ha->max_req_queues);
|
|
|
|
}
|
|
|
|
ql_log_pci(ql_log_info, ha->pdev, 0x011c,
|
|
|
|
"MSI-X vector count: %d.\n", msix);
|
|
|
|
} else
|
|
|
|
ql_log_pci(ql_log_info, ha->pdev, 0x011e,
|
|
|
|
"BAR 1 not enabled.\n");
|
|
|
|
|
|
|
|
mqiobase_exit:
|
|
|
|
ha->msix_count = ha->max_rsp_queues + 1;
|
2013-01-30 15:34:39 +07:00
|
|
|
|
|
|
|
qlt_83xx_iospace_config(ha);
|
|
|
|
|
2012-02-10 02:15:34 +07:00
|
|
|
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011f,
|
|
|
|
"MSIX Count:%d.\n", ha->msix_count);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
iospace_error_exit:
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2007-07-20 05:06:00 +07:00
|
|
|
static struct isp_operations qla2100_isp_ops = {
|
|
|
|
.pci_config = qla2100_pci_config,
|
|
|
|
.reset_chip = qla2x00_reset_chip,
|
|
|
|
.chip_diag = qla2x00_chip_diag,
|
|
|
|
.config_rings = qla2x00_config_rings,
|
|
|
|
.reset_adapter = qla2x00_reset_adapter,
|
|
|
|
.nvram_config = qla2x00_nvram_config,
|
|
|
|
.update_fw_options = qla2x00_update_fw_options,
|
|
|
|
.load_risc = qla2x00_load_risc,
|
|
|
|
.pci_info_str = qla2x00_pci_info_str,
|
|
|
|
.fw_version_str = qla2x00_fw_version_str,
|
|
|
|
.intr_handler = qla2100_intr_handler,
|
|
|
|
.enable_intrs = qla2x00_enable_intrs,
|
|
|
|
.disable_intrs = qla2x00_disable_intrs,
|
|
|
|
.abort_command = qla2x00_abort_command,
|
2008-04-04 03:13:24 +07:00
|
|
|
.target_reset = qla2x00_abort_target,
|
|
|
|
.lun_reset = qla2x00_lun_reset,
|
2007-07-20 05:06:00 +07:00
|
|
|
.fabric_login = qla2x00_login_fabric,
|
|
|
|
.fabric_logout = qla2x00_fabric_logout,
|
|
|
|
.calc_req_entries = qla2x00_calc_iocbs_32,
|
|
|
|
.build_iocbs = qla2x00_build_scsi_iocbs_32,
|
|
|
|
.prep_ms_iocb = qla2x00_prep_ms_iocb,
|
|
|
|
.prep_ms_fdmi_iocb = qla2x00_prep_ms_fdmi_iocb,
|
|
|
|
.read_nvram = qla2x00_read_nvram_data,
|
|
|
|
.write_nvram = qla2x00_write_nvram_data,
|
|
|
|
.fw_dump = qla2100_fw_dump,
|
|
|
|
.beacon_on = NULL,
|
|
|
|
.beacon_off = NULL,
|
|
|
|
.beacon_blink = NULL,
|
|
|
|
.read_optrom = qla2x00_read_optrom_data,
|
|
|
|
.write_optrom = qla2x00_write_optrom_data,
|
|
|
|
.get_flash_version = qla2x00_get_flash_version,
|
2008-11-07 01:40:51 +07:00
|
|
|
.start_scsi = qla2x00_start_scsi,
|
2010-04-13 07:59:55 +07:00
|
|
|
.abort_isp = qla2x00_abort_isp,
|
2011-11-19 00:03:16 +07:00
|
|
|
.iospace_config = qla2x00_iospace_config,
|
2007-07-20 05:06:00 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct isp_operations qla2300_isp_ops = {
|
|
|
|
.pci_config = qla2300_pci_config,
|
|
|
|
.reset_chip = qla2x00_reset_chip,
|
|
|
|
.chip_diag = qla2x00_chip_diag,
|
|
|
|
.config_rings = qla2x00_config_rings,
|
|
|
|
.reset_adapter = qla2x00_reset_adapter,
|
|
|
|
.nvram_config = qla2x00_nvram_config,
|
|
|
|
.update_fw_options = qla2x00_update_fw_options,
|
|
|
|
.load_risc = qla2x00_load_risc,
|
|
|
|
.pci_info_str = qla2x00_pci_info_str,
|
|
|
|
.fw_version_str = qla2x00_fw_version_str,
|
|
|
|
.intr_handler = qla2300_intr_handler,
|
|
|
|
.enable_intrs = qla2x00_enable_intrs,
|
|
|
|
.disable_intrs = qla2x00_disable_intrs,
|
|
|
|
.abort_command = qla2x00_abort_command,
|
2008-04-04 03:13:24 +07:00
|
|
|
.target_reset = qla2x00_abort_target,
|
|
|
|
.lun_reset = qla2x00_lun_reset,
|
2007-07-20 05:06:00 +07:00
|
|
|
.fabric_login = qla2x00_login_fabric,
|
|
|
|
.fabric_logout = qla2x00_fabric_logout,
|
|
|
|
.calc_req_entries = qla2x00_calc_iocbs_32,
|
|
|
|
.build_iocbs = qla2x00_build_scsi_iocbs_32,
|
|
|
|
.prep_ms_iocb = qla2x00_prep_ms_iocb,
|
|
|
|
.prep_ms_fdmi_iocb = qla2x00_prep_ms_fdmi_iocb,
|
|
|
|
.read_nvram = qla2x00_read_nvram_data,
|
|
|
|
.write_nvram = qla2x00_write_nvram_data,
|
|
|
|
.fw_dump = qla2300_fw_dump,
|
|
|
|
.beacon_on = qla2x00_beacon_on,
|
|
|
|
.beacon_off = qla2x00_beacon_off,
|
|
|
|
.beacon_blink = qla2x00_beacon_blink,
|
|
|
|
.read_optrom = qla2x00_read_optrom_data,
|
|
|
|
.write_optrom = qla2x00_write_optrom_data,
|
|
|
|
.get_flash_version = qla2x00_get_flash_version,
|
2008-11-07 01:40:51 +07:00
|
|
|
.start_scsi = qla2x00_start_scsi,
|
2010-04-13 07:59:55 +07:00
|
|
|
.abort_isp = qla2x00_abort_isp,
|
2011-11-19 00:03:16 +07:00
|
|
|
.iospace_config = qla2x00_iospace_config,
|
2007-07-20 05:06:00 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct isp_operations qla24xx_isp_ops = {
|
|
|
|
.pci_config = qla24xx_pci_config,
|
|
|
|
.reset_chip = qla24xx_reset_chip,
|
|
|
|
.chip_diag = qla24xx_chip_diag,
|
|
|
|
.config_rings = qla24xx_config_rings,
|
|
|
|
.reset_adapter = qla24xx_reset_adapter,
|
|
|
|
.nvram_config = qla24xx_nvram_config,
|
|
|
|
.update_fw_options = qla24xx_update_fw_options,
|
|
|
|
.load_risc = qla24xx_load_risc,
|
|
|
|
.pci_info_str = qla24xx_pci_info_str,
|
|
|
|
.fw_version_str = qla24xx_fw_version_str,
|
|
|
|
.intr_handler = qla24xx_intr_handler,
|
|
|
|
.enable_intrs = qla24xx_enable_intrs,
|
|
|
|
.disable_intrs = qla24xx_disable_intrs,
|
|
|
|
.abort_command = qla24xx_abort_command,
|
2008-04-04 03:13:24 +07:00
|
|
|
.target_reset = qla24xx_abort_target,
|
|
|
|
.lun_reset = qla24xx_lun_reset,
|
2007-07-20 05:06:00 +07:00
|
|
|
.fabric_login = qla24xx_login_fabric,
|
|
|
|
.fabric_logout = qla24xx_fabric_logout,
|
|
|
|
.calc_req_entries = NULL,
|
|
|
|
.build_iocbs = NULL,
|
|
|
|
.prep_ms_iocb = qla24xx_prep_ms_iocb,
|
|
|
|
.prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb,
|
|
|
|
.read_nvram = qla24xx_read_nvram_data,
|
|
|
|
.write_nvram = qla24xx_write_nvram_data,
|
|
|
|
.fw_dump = qla24xx_fw_dump,
|
|
|
|
.beacon_on = qla24xx_beacon_on,
|
|
|
|
.beacon_off = qla24xx_beacon_off,
|
|
|
|
.beacon_blink = qla24xx_beacon_blink,
|
|
|
|
.read_optrom = qla24xx_read_optrom_data,
|
|
|
|
.write_optrom = qla24xx_write_optrom_data,
|
|
|
|
.get_flash_version = qla24xx_get_flash_version,
|
2008-11-07 01:40:51 +07:00
|
|
|
.start_scsi = qla24xx_start_scsi,
|
2010-04-13 07:59:55 +07:00
|
|
|
.abort_isp = qla2x00_abort_isp,
|
2011-11-19 00:03:16 +07:00
|
|
|
.iospace_config = qla2x00_iospace_config,
|
2007-07-20 05:06:00 +07:00
|
|
|
};
|
|
|
|
|
2007-07-20 10:37:34 +07:00
|
|
|
static struct isp_operations qla25xx_isp_ops = {
|
|
|
|
.pci_config = qla25xx_pci_config,
|
|
|
|
.reset_chip = qla24xx_reset_chip,
|
|
|
|
.chip_diag = qla24xx_chip_diag,
|
|
|
|
.config_rings = qla24xx_config_rings,
|
|
|
|
.reset_adapter = qla24xx_reset_adapter,
|
|
|
|
.nvram_config = qla24xx_nvram_config,
|
|
|
|
.update_fw_options = qla24xx_update_fw_options,
|
|
|
|
.load_risc = qla24xx_load_risc,
|
|
|
|
.pci_info_str = qla24xx_pci_info_str,
|
|
|
|
.fw_version_str = qla24xx_fw_version_str,
|
|
|
|
.intr_handler = qla24xx_intr_handler,
|
|
|
|
.enable_intrs = qla24xx_enable_intrs,
|
|
|
|
.disable_intrs = qla24xx_disable_intrs,
|
|
|
|
.abort_command = qla24xx_abort_command,
|
2008-04-04 03:13:24 +07:00
|
|
|
.target_reset = qla24xx_abort_target,
|
|
|
|
.lun_reset = qla24xx_lun_reset,
|
2007-07-20 10:37:34 +07:00
|
|
|
.fabric_login = qla24xx_login_fabric,
|
|
|
|
.fabric_logout = qla24xx_fabric_logout,
|
|
|
|
.calc_req_entries = NULL,
|
|
|
|
.build_iocbs = NULL,
|
|
|
|
.prep_ms_iocb = qla24xx_prep_ms_iocb,
|
|
|
|
.prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb,
|
|
|
|
.read_nvram = qla25xx_read_nvram_data,
|
|
|
|
.write_nvram = qla25xx_write_nvram_data,
|
|
|
|
.fw_dump = qla25xx_fw_dump,
|
|
|
|
.beacon_on = qla24xx_beacon_on,
|
|
|
|
.beacon_off = qla24xx_beacon_off,
|
|
|
|
.beacon_blink = qla24xx_beacon_blink,
|
2007-09-21 04:07:33 +07:00
|
|
|
.read_optrom = qla25xx_read_optrom_data,
|
2007-07-20 10:37:34 +07:00
|
|
|
.write_optrom = qla24xx_write_optrom_data,
|
|
|
|
.get_flash_version = qla24xx_get_flash_version,
|
2010-05-05 05:01:30 +07:00
|
|
|
.start_scsi = qla24xx_dif_start_scsi,
|
2010-04-13 07:59:55 +07:00
|
|
|
.abort_isp = qla2x00_abort_isp,
|
2011-11-19 00:03:16 +07:00
|
|
|
.iospace_config = qla2x00_iospace_config,
|
2007-07-20 10:37:34 +07:00
|
|
|
};
|
|
|
|
|
2009-01-06 02:18:11 +07:00
|
|
|
static struct isp_operations qla81xx_isp_ops = {
|
|
|
|
.pci_config = qla25xx_pci_config,
|
|
|
|
.reset_chip = qla24xx_reset_chip,
|
|
|
|
.chip_diag = qla24xx_chip_diag,
|
|
|
|
.config_rings = qla24xx_config_rings,
|
|
|
|
.reset_adapter = qla24xx_reset_adapter,
|
|
|
|
.nvram_config = qla81xx_nvram_config,
|
|
|
|
.update_fw_options = qla81xx_update_fw_options,
|
2009-01-23 00:45:32 +07:00
|
|
|
.load_risc = qla81xx_load_risc,
|
2009-01-06 02:18:11 +07:00
|
|
|
.pci_info_str = qla24xx_pci_info_str,
|
|
|
|
.fw_version_str = qla24xx_fw_version_str,
|
|
|
|
.intr_handler = qla24xx_intr_handler,
|
|
|
|
.enable_intrs = qla24xx_enable_intrs,
|
|
|
|
.disable_intrs = qla24xx_disable_intrs,
|
|
|
|
.abort_command = qla24xx_abort_command,
|
|
|
|
.target_reset = qla24xx_abort_target,
|
|
|
|
.lun_reset = qla24xx_lun_reset,
|
|
|
|
.fabric_login = qla24xx_login_fabric,
|
|
|
|
.fabric_logout = qla24xx_fabric_logout,
|
|
|
|
.calc_req_entries = NULL,
|
|
|
|
.build_iocbs = NULL,
|
|
|
|
.prep_ms_iocb = qla24xx_prep_ms_iocb,
|
|
|
|
.prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb,
|
2009-03-24 23:08:14 +07:00
|
|
|
.read_nvram = NULL,
|
|
|
|
.write_nvram = NULL,
|
2009-01-06 02:18:11 +07:00
|
|
|
.fw_dump = qla81xx_fw_dump,
|
|
|
|
.beacon_on = qla24xx_beacon_on,
|
|
|
|
.beacon_off = qla24xx_beacon_off,
|
2012-02-10 02:15:34 +07:00
|
|
|
.beacon_blink = qla83xx_beacon_blink,
|
2009-01-06 02:18:11 +07:00
|
|
|
.read_optrom = qla25xx_read_optrom_data,
|
|
|
|
.write_optrom = qla24xx_write_optrom_data,
|
|
|
|
.get_flash_version = qla24xx_get_flash_version,
|
2010-05-29 05:08:27 +07:00
|
|
|
.start_scsi = qla24xx_dif_start_scsi,
|
2010-04-13 07:59:55 +07:00
|
|
|
.abort_isp = qla2x00_abort_isp,
|
2011-11-19 00:03:16 +07:00
|
|
|
.iospace_config = qla2x00_iospace_config,
|
2010-04-13 07:59:55 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct isp_operations qla82xx_isp_ops = {
|
|
|
|
.pci_config = qla82xx_pci_config,
|
|
|
|
.reset_chip = qla82xx_reset_chip,
|
|
|
|
.chip_diag = qla24xx_chip_diag,
|
|
|
|
.config_rings = qla82xx_config_rings,
|
|
|
|
.reset_adapter = qla24xx_reset_adapter,
|
|
|
|
.nvram_config = qla81xx_nvram_config,
|
|
|
|
.update_fw_options = qla24xx_update_fw_options,
|
|
|
|
.load_risc = qla82xx_load_risc,
|
2012-08-23 01:21:14 +07:00
|
|
|
.pci_info_str = qla24xx_pci_info_str,
|
2010-04-13 07:59:55 +07:00
|
|
|
.fw_version_str = qla24xx_fw_version_str,
|
|
|
|
.intr_handler = qla82xx_intr_handler,
|
|
|
|
.enable_intrs = qla82xx_enable_intrs,
|
|
|
|
.disable_intrs = qla82xx_disable_intrs,
|
|
|
|
.abort_command = qla24xx_abort_command,
|
|
|
|
.target_reset = qla24xx_abort_target,
|
|
|
|
.lun_reset = qla24xx_lun_reset,
|
|
|
|
.fabric_login = qla24xx_login_fabric,
|
|
|
|
.fabric_logout = qla24xx_fabric_logout,
|
|
|
|
.calc_req_entries = NULL,
|
|
|
|
.build_iocbs = NULL,
|
|
|
|
.prep_ms_iocb = qla24xx_prep_ms_iocb,
|
|
|
|
.prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb,
|
|
|
|
.read_nvram = qla24xx_read_nvram_data,
|
|
|
|
.write_nvram = qla24xx_write_nvram_data,
|
|
|
|
.fw_dump = qla24xx_fw_dump,
|
2011-08-17 01:31:45 +07:00
|
|
|
.beacon_on = qla82xx_beacon_on,
|
|
|
|
.beacon_off = qla82xx_beacon_off,
|
|
|
|
.beacon_blink = NULL,
|
2010-04-13 07:59:55 +07:00
|
|
|
.read_optrom = qla82xx_read_optrom_data,
|
|
|
|
.write_optrom = qla82xx_write_optrom_data,
|
|
|
|
.get_flash_version = qla24xx_get_flash_version,
|
|
|
|
.start_scsi = qla82xx_start_scsi,
|
|
|
|
.abort_isp = qla82xx_abort_isp,
|
2011-11-19 00:03:16 +07:00
|
|
|
.iospace_config = qla82xx_iospace_config,
|
2009-01-06 02:18:11 +07:00
|
|
|
};
|
|
|
|
|
2012-02-10 02:15:34 +07:00
|
|
|
static struct isp_operations qla83xx_isp_ops = {
|
|
|
|
.pci_config = qla25xx_pci_config,
|
|
|
|
.reset_chip = qla24xx_reset_chip,
|
|
|
|
.chip_diag = qla24xx_chip_diag,
|
|
|
|
.config_rings = qla24xx_config_rings,
|
|
|
|
.reset_adapter = qla24xx_reset_adapter,
|
|
|
|
.nvram_config = qla81xx_nvram_config,
|
|
|
|
.update_fw_options = qla81xx_update_fw_options,
|
|
|
|
.load_risc = qla81xx_load_risc,
|
|
|
|
.pci_info_str = qla24xx_pci_info_str,
|
|
|
|
.fw_version_str = qla24xx_fw_version_str,
|
|
|
|
.intr_handler = qla24xx_intr_handler,
|
|
|
|
.enable_intrs = qla24xx_enable_intrs,
|
|
|
|
.disable_intrs = qla24xx_disable_intrs,
|
|
|
|
.abort_command = qla24xx_abort_command,
|
|
|
|
.target_reset = qla24xx_abort_target,
|
|
|
|
.lun_reset = qla24xx_lun_reset,
|
|
|
|
.fabric_login = qla24xx_login_fabric,
|
|
|
|
.fabric_logout = qla24xx_fabric_logout,
|
|
|
|
.calc_req_entries = NULL,
|
|
|
|
.build_iocbs = NULL,
|
|
|
|
.prep_ms_iocb = qla24xx_prep_ms_iocb,
|
|
|
|
.prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb,
|
|
|
|
.read_nvram = NULL,
|
|
|
|
.write_nvram = NULL,
|
|
|
|
.fw_dump = qla83xx_fw_dump,
|
|
|
|
.beacon_on = qla24xx_beacon_on,
|
|
|
|
.beacon_off = qla24xx_beacon_off,
|
|
|
|
.beacon_blink = qla83xx_beacon_blink,
|
|
|
|
.read_optrom = qla25xx_read_optrom_data,
|
|
|
|
.write_optrom = qla24xx_write_optrom_data,
|
|
|
|
.get_flash_version = qla24xx_get_flash_version,
|
|
|
|
.start_scsi = qla24xx_dif_start_scsi,
|
|
|
|
.abort_isp = qla2x00_abort_isp,
|
|
|
|
.iospace_config = qla83xx_iospace_config,
|
|
|
|
};
|
|
|
|
|
2006-03-10 05:27:08 +07:00
|
|
|
static inline void
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_set_isp_flags(struct qla_hw_data *ha)
|
2006-03-10 05:27:08 +07:00
|
|
|
{
|
|
|
|
ha->device_type = DT_EXTENDED_IDS;
|
|
|
|
switch (ha->pdev->device) {
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2100:
|
|
|
|
ha->device_type |= DT_ISP2100;
|
|
|
|
ha->device_type &= ~DT_EXTENDED_IDS;
|
2006-05-18 05:09:34 +07:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2100;
|
2006-03-10 05:27:08 +07:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2200:
|
|
|
|
ha->device_type |= DT_ISP2200;
|
|
|
|
ha->device_type &= ~DT_EXTENDED_IDS;
|
2006-05-18 05:09:34 +07:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2100;
|
2006-03-10 05:27:08 +07:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2300:
|
|
|
|
ha->device_type |= DT_ISP2300;
|
2006-03-10 05:27:39 +07:00
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
2006-05-18 05:09:34 +07:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2300;
|
2006-03-10 05:27:08 +07:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2312:
|
|
|
|
ha->device_type |= DT_ISP2312;
|
2006-03-10 05:27:39 +07:00
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
2006-05-18 05:09:34 +07:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2300;
|
2006-03-10 05:27:08 +07:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2322:
|
|
|
|
ha->device_type |= DT_ISP2322;
|
2006-03-10 05:27:39 +07:00
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
2006-03-10 05:27:08 +07:00
|
|
|
if (ha->pdev->subsystem_vendor == 0x1028 &&
|
|
|
|
ha->pdev->subsystem_device == 0x0170)
|
|
|
|
ha->device_type |= DT_OEM_001;
|
2006-05-18 05:09:34 +07:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2300;
|
2006-03-10 05:27:08 +07:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP6312:
|
|
|
|
ha->device_type |= DT_ISP6312;
|
2006-05-18 05:09:34 +07:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2300;
|
2006-03-10 05:27:08 +07:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP6322:
|
|
|
|
ha->device_type |= DT_ISP6322;
|
2006-05-18 05:09:34 +07:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2300;
|
2006-03-10 05:27:08 +07:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2422:
|
|
|
|
ha->device_type |= DT_ISP2422;
|
2006-03-10 05:27:39 +07:00
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
2007-07-20 05:05:56 +07:00
|
|
|
ha->device_type |= DT_FWI2;
|
2007-07-20 05:05:57 +07:00
|
|
|
ha->device_type |= DT_IIDMA;
|
2006-05-18 05:09:34 +07:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2400;
|
2006-03-10 05:27:08 +07:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2432:
|
|
|
|
ha->device_type |= DT_ISP2432;
|
2006-03-10 05:27:39 +07:00
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
2007-07-20 05:05:56 +07:00
|
|
|
ha->device_type |= DT_FWI2;
|
2007-07-20 05:05:57 +07:00
|
|
|
ha->device_type |= DT_IIDMA;
|
2006-05-18 05:09:34 +07:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2400;
|
2006-03-10 05:27:08 +07:00
|
|
|
break;
|
2008-04-04 03:13:26 +07:00
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP8432:
|
|
|
|
ha->device_type |= DT_ISP8432;
|
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
|
|
|
ha->device_type |= DT_FWI2;
|
|
|
|
ha->device_type |= DT_IIDMA;
|
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2400;
|
|
|
|
break;
|
2006-03-10 05:27:13 +07:00
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP5422:
|
|
|
|
ha->device_type |= DT_ISP5422;
|
2007-07-20 05:05:56 +07:00
|
|
|
ha->device_type |= DT_FWI2;
|
2006-05-18 05:09:34 +07:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2400;
|
2006-03-10 05:27:08 +07:00
|
|
|
break;
|
2006-03-10 05:27:13 +07:00
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP5432:
|
|
|
|
ha->device_type |= DT_ISP5432;
|
2007-07-20 05:05:56 +07:00
|
|
|
ha->device_type |= DT_FWI2;
|
2006-05-18 05:09:34 +07:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2400;
|
2006-03-10 05:27:08 +07:00
|
|
|
break;
|
2007-07-20 10:37:34 +07:00
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2532:
|
|
|
|
ha->device_type |= DT_ISP2532;
|
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
|
|
|
ha->device_type |= DT_FWI2;
|
|
|
|
ha->device_type |= DT_IIDMA;
|
2006-05-18 05:09:34 +07:00
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2400;
|
2006-03-10 05:27:08 +07:00
|
|
|
break;
|
2009-01-06 02:18:11 +07:00
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP8001:
|
|
|
|
ha->device_type |= DT_ISP8001;
|
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
|
|
|
ha->device_type |= DT_FWI2;
|
|
|
|
ha->device_type |= DT_IIDMA;
|
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2400;
|
|
|
|
break;
|
2010-04-13 07:59:55 +07:00
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP8021:
|
|
|
|
ha->device_type |= DT_ISP8021;
|
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
|
|
|
ha->device_type |= DT_FWI2;
|
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2400;
|
|
|
|
/* Initialize 82XX ISP flags */
|
|
|
|
qla82xx_init_flags(ha);
|
|
|
|
break;
|
2012-02-10 02:15:34 +07:00
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2031:
|
|
|
|
ha->device_type |= DT_ISP2031;
|
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
|
|
|
ha->device_type |= DT_FWI2;
|
|
|
|
ha->device_type |= DT_IIDMA;
|
|
|
|
ha->device_type |= DT_T10_PI;
|
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2400;
|
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_QLOGIC_ISP8031:
|
|
|
|
ha->device_type |= DT_ISP8031;
|
|
|
|
ha->device_type |= DT_ZIO_SUPPORTED;
|
|
|
|
ha->device_type |= DT_FWI2;
|
|
|
|
ha->device_type |= DT_IIDMA;
|
|
|
|
ha->device_type |= DT_T10_PI;
|
|
|
|
ha->fw_srisc_address = RISC_START_ADDRESS_2400;
|
|
|
|
break;
|
2006-03-10 05:27:08 +07:00
|
|
|
}
|
2009-04-07 12:33:50 +07:00
|
|
|
|
2010-04-13 07:59:55 +07:00
|
|
|
if (IS_QLA82XX(ha))
|
|
|
|
ha->port_no = !(ha->portnum & 1);
|
|
|
|
else
|
|
|
|
/* Get adapter physical port no from interrupt pin register. */
|
|
|
|
pci_read_config_byte(ha->pdev, PCI_INTERRUPT_PIN, &ha->port_no);
|
|
|
|
|
2009-04-07 12:33:50 +07:00
|
|
|
if (ha->port_no & 1)
|
|
|
|
ha->flags.port0 = 1;
|
|
|
|
else
|
|
|
|
ha->flags.port0 = 0;
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x000b,
|
2011-11-19 00:03:06 +07:00
|
|
|
"device_type=0x%x port=%d fw_srisc_address=0x%x.\n",
|
2011-07-15 02:00:13 +07:00
|
|
|
ha->device_type, ha->flags.port0, ha->fw_srisc_address);
|
2006-03-10 05:27:08 +07:00
|
|
|
}
|
|
|
|
|
2006-11-22 23:24:48 +07:00
|
|
|
static void
|
|
|
|
qla2xxx_scan_start(struct Scsi_Host *shost)
|
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_qla_host_t *vha = shost_priv(shost);
|
2006-11-22 23:24:48 +07:00
|
|
|
|
2009-06-03 23:55:17 +07:00
|
|
|
if (vha->hw->flags.running_gold_fw)
|
|
|
|
return;
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
|
|
|
|
set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
|
|
|
|
set_bit(RSCN_UPDATE, &vha->dpc_flags);
|
|
|
|
set_bit(NPIV_CONFIG_NEEDED, &vha->dpc_flags);
|
2006-11-22 23:24:48 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
qla2xxx_scan_finished(struct Scsi_Host *shost, unsigned long time)
|
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_qla_host_t *vha = shost_priv(shost);
|
2006-11-22 23:24:48 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
if (!vha->host)
|
2006-11-22 23:24:48 +07:00
|
|
|
return 1;
|
2008-11-07 01:40:51 +07:00
|
|
|
if (time > vha->hw->loop_reset_delay * HZ)
|
2006-11-22 23:24:48 +07:00
|
|
|
return 1;
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
return atomic_read(&vha->loop_state) == LOOP_READY;
|
2006-11-22 23:24:48 +07:00
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* PCI driver interface
|
|
|
|
*/
|
2012-12-22 04:08:55 +07:00
|
|
|
static int
|
2006-06-24 06:11:22 +07:00
|
|
|
qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-06-10 07:21:28 +07:00
|
|
|
int ret = -ENODEV;
|
2005-04-17 05:20:36 +07:00
|
|
|
struct Scsi_Host *host;
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_qla_host_t *base_vha = NULL;
|
|
|
|
struct qla_hw_data *ha;
|
2007-08-13 08:22:52 +07:00
|
|
|
char pci_info[30];
|
2012-08-23 01:21:03 +07:00
|
|
|
char fw_str[30], wq_name[30];
|
2005-11-10 06:49:04 +07:00
|
|
|
struct scsi_host_template *sht;
|
2012-02-10 02:15:57 +07:00
|
|
|
int bars, mem_only = 0;
|
2008-11-07 01:40:51 +07:00
|
|
|
uint16_t req_length = 0, rsp_length = 0;
|
2008-12-10 07:45:39 +07:00
|
|
|
struct req_que *req = NULL;
|
|
|
|
struct rsp_que *rsp = NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-10-20 05:59:17 +07:00
|
|
|
bars = pci_select_bars(pdev, IORESOURCE_MEM | IORESOURCE_IO);
|
2009-03-24 23:07:56 +07:00
|
|
|
sht = &qla2xxx_driver_template;
|
2005-11-10 06:49:04 +07:00
|
|
|
if (pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2422 ||
|
2006-12-14 10:20:29 +07:00
|
|
|
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2432 ||
|
2008-04-04 03:13:26 +07:00
|
|
|
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8432 ||
|
2006-12-14 10:20:29 +07:00
|
|
|
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5422 ||
|
2007-07-20 10:37:34 +07:00
|
|
|
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5432 ||
|
2009-01-06 02:18:11 +07:00
|
|
|
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2532 ||
|
2010-04-13 07:59:55 +07:00
|
|
|
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8001 ||
|
2012-02-10 02:15:34 +07:00
|
|
|
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8021 ||
|
|
|
|
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2031 ||
|
|
|
|
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8031) {
|
2007-10-20 05:59:17 +07:00
|
|
|
bars = pci_select_bars(pdev, IORESOURCE_MEM);
|
2007-12-20 11:28:09 +07:00
|
|
|
mem_only = 1;
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg_pci(ql_dbg_init, pdev, 0x0007,
|
|
|
|
"Mem only adapter.\n");
|
2007-10-20 05:59:17 +07:00
|
|
|
}
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg_pci(ql_dbg_init, pdev, 0x0008,
|
|
|
|
"Bars=%d.\n", bars);
|
2007-10-20 05:59:17 +07:00
|
|
|
|
2007-12-20 11:28:09 +07:00
|
|
|
if (mem_only) {
|
|
|
|
if (pci_enable_device_mem(pdev))
|
|
|
|
goto probe_out;
|
|
|
|
} else {
|
|
|
|
if (pci_enable_device(pdev))
|
|
|
|
goto probe_out;
|
|
|
|
}
|
2007-10-20 05:59:17 +07:00
|
|
|
|
2008-10-19 07:33:19 +07:00
|
|
|
/* This may fail but that's ok */
|
|
|
|
pci_enable_pcie_error_reporting(pdev);
|
2007-10-20 05:59:17 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
ha = kzalloc(sizeof(struct qla_hw_data), GFP_KERNEL);
|
|
|
|
if (!ha) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log_pci(ql_log_fatal, pdev, 0x0009,
|
|
|
|
"Unable to allocate memory for ha.\n");
|
2008-11-07 01:40:51 +07:00
|
|
|
goto probe_out;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg_pci(ql_dbg_init, pdev, 0x000a,
|
|
|
|
"Memory allocated for ha=%p.\n", ha);
|
2008-11-07 01:40:51 +07:00
|
|
|
ha->pdev = pdev;
|
2012-05-16 01:34:28 +07:00
|
|
|
ha->tgt.enable_class_2 = ql2xenableclass2;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Clear our data area */
|
2007-10-20 05:59:17 +07:00
|
|
|
ha->bars = bars;
|
2007-12-20 11:28:09 +07:00
|
|
|
ha->mem_only = mem_only;
|
2008-02-01 03:33:46 +07:00
|
|
|
spin_lock_init(&ha->hardware_lock);
|
2010-10-16 01:27:45 +07:00
|
|
|
spin_lock_init(&ha->vport_slock);
|
2012-08-23 01:21:01 +07:00
|
|
|
mutex_init(&ha->selflogin_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-03-10 05:27:08 +07:00
|
|
|
/* Set ISP-type information. */
|
|
|
|
qla2x00_set_isp_flags(ha);
|
2009-12-16 12:29:47 +07:00
|
|
|
|
|
|
|
/* Set EEH reset type to fundamental if required by hba */
|
2012-08-23 01:21:20 +07:00
|
|
|
if (IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha) ||
|
|
|
|
IS_QLA83XX(ha))
|
2009-12-16 12:29:47 +07:00
|
|
|
pdev->needs_freset = 1;
|
|
|
|
|
2011-11-19 00:03:21 +07:00
|
|
|
ha->prev_topology = 0;
|
|
|
|
ha->init_cb_size = sizeof(init_cb_t);
|
|
|
|
ha->link_data_rate = PORT_SPEED_UNKNOWN;
|
|
|
|
ha->optrom_size = OPTROM_SIZE_2300;
|
2013-01-30 15:34:38 +07:00
|
|
|
ha->cfg_lun_q_depth = ql2xmaxqdepth;
|
2011-11-19 00:03:21 +07:00
|
|
|
|
2005-07-07 00:30:05 +07:00
|
|
|
/* Assign ISP specific operations. */
|
2005-04-17 05:20:36 +07:00
|
|
|
if (IS_QLA2100(ha)) {
|
2012-02-10 02:15:57 +07:00
|
|
|
ha->max_fibre_devices = MAX_FIBRE_DEVICES_2100;
|
2005-04-17 05:20:36 +07:00
|
|
|
ha->mbx_count = MAILBOX_REGISTER_COUNT_2100;
|
2008-11-07 01:40:51 +07:00
|
|
|
req_length = REQUEST_ENTRY_CNT_2100;
|
|
|
|
rsp_length = RESPONSE_ENTRY_CNT_2100;
|
|
|
|
ha->max_loop_id = SNS_LAST_LOOP_ID_2100;
|
2005-07-07 00:30:05 +07:00
|
|
|
ha->gid_list_info_size = 4;
|
2009-01-06 02:18:11 +07:00
|
|
|
ha->flash_conf_off = ~0;
|
|
|
|
ha->flash_data_off = ~0;
|
|
|
|
ha->nvram_conf_off = ~0;
|
|
|
|
ha->nvram_data_off = ~0;
|
2007-07-20 05:06:00 +07:00
|
|
|
ha->isp_ops = &qla2100_isp_ops;
|
2005-04-17 05:20:36 +07:00
|
|
|
} else if (IS_QLA2200(ha)) {
|
2012-02-10 02:15:57 +07:00
|
|
|
ha->max_fibre_devices = MAX_FIBRE_DEVICES_2100;
|
2012-02-10 02:14:08 +07:00
|
|
|
ha->mbx_count = MAILBOX_REGISTER_COUNT_2200;
|
2008-11-07 01:40:51 +07:00
|
|
|
req_length = REQUEST_ENTRY_CNT_2200;
|
|
|
|
rsp_length = RESPONSE_ENTRY_CNT_2100;
|
|
|
|
ha->max_loop_id = SNS_LAST_LOOP_ID_2100;
|
2005-07-07 00:30:05 +07:00
|
|
|
ha->gid_list_info_size = 4;
|
2009-01-06 02:18:11 +07:00
|
|
|
ha->flash_conf_off = ~0;
|
|
|
|
ha->flash_data_off = ~0;
|
|
|
|
ha->nvram_conf_off = ~0;
|
|
|
|
ha->nvram_data_off = ~0;
|
2007-07-20 05:06:00 +07:00
|
|
|
ha->isp_ops = &qla2100_isp_ops;
|
2005-07-07 00:31:47 +07:00
|
|
|
} else if (IS_QLA23XX(ha)) {
|
2012-02-10 02:15:57 +07:00
|
|
|
ha->max_fibre_devices = MAX_FIBRE_DEVICES_2100;
|
2005-04-17 05:20:36 +07:00
|
|
|
ha->mbx_count = MAILBOX_REGISTER_COUNT;
|
2008-11-07 01:40:51 +07:00
|
|
|
req_length = REQUEST_ENTRY_CNT_2200;
|
|
|
|
rsp_length = RESPONSE_ENTRY_CNT_2300;
|
|
|
|
ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
|
2005-07-07 00:30:05 +07:00
|
|
|
ha->gid_list_info_size = 6;
|
2006-02-01 07:05:17 +07:00
|
|
|
if (IS_QLA2322(ha) || IS_QLA6322(ha))
|
|
|
|
ha->optrom_size = OPTROM_SIZE_2322;
|
2009-01-06 02:18:11 +07:00
|
|
|
ha->flash_conf_off = ~0;
|
|
|
|
ha->flash_data_off = ~0;
|
|
|
|
ha->nvram_conf_off = ~0;
|
|
|
|
ha->nvram_data_off = ~0;
|
2007-07-20 05:06:00 +07:00
|
|
|
ha->isp_ops = &qla2300_isp_ops;
|
2008-04-04 03:13:26 +07:00
|
|
|
} else if (IS_QLA24XX_TYPE(ha)) {
|
2012-02-10 02:15:57 +07:00
|
|
|
ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
|
2005-07-07 00:31:47 +07:00
|
|
|
ha->mbx_count = MAILBOX_REGISTER_COUNT;
|
2008-11-07 01:40:51 +07:00
|
|
|
req_length = REQUEST_ENTRY_CNT_24XX;
|
|
|
|
rsp_length = RESPONSE_ENTRY_CNT_2300;
|
2012-05-16 01:34:28 +07:00
|
|
|
ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX;
|
2008-11-07 01:40:51 +07:00
|
|
|
ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
|
2007-07-06 03:16:51 +07:00
|
|
|
ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
|
2005-07-07 00:31:47 +07:00
|
|
|
ha->gid_list_info_size = 8;
|
2006-02-01 07:05:17 +07:00
|
|
|
ha->optrom_size = OPTROM_SIZE_24XX;
|
2008-12-10 07:45:39 +07:00
|
|
|
ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA24XX;
|
2007-07-20 05:06:00 +07:00
|
|
|
ha->isp_ops = &qla24xx_isp_ops;
|
2009-01-06 02:18:11 +07:00
|
|
|
ha->flash_conf_off = FARX_ACCESS_FLASH_CONF;
|
|
|
|
ha->flash_data_off = FARX_ACCESS_FLASH_DATA;
|
|
|
|
ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF;
|
|
|
|
ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA;
|
2007-07-20 10:37:34 +07:00
|
|
|
} else if (IS_QLA25XX(ha)) {
|
2012-02-10 02:15:57 +07:00
|
|
|
ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
|
2007-07-20 10:37:34 +07:00
|
|
|
ha->mbx_count = MAILBOX_REGISTER_COUNT;
|
2008-11-07 01:40:51 +07:00
|
|
|
req_length = REQUEST_ENTRY_CNT_24XX;
|
|
|
|
rsp_length = RESPONSE_ENTRY_CNT_2300;
|
2012-05-16 01:34:28 +07:00
|
|
|
ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX;
|
2008-11-07 01:40:51 +07:00
|
|
|
ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
|
2007-07-20 10:37:34 +07:00
|
|
|
ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
|
|
|
|
ha->gid_list_info_size = 8;
|
|
|
|
ha->optrom_size = OPTROM_SIZE_25XX;
|
2008-12-10 07:45:39 +07:00
|
|
|
ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
|
2007-07-20 10:37:34 +07:00
|
|
|
ha->isp_ops = &qla25xx_isp_ops;
|
2009-01-06 02:18:11 +07:00
|
|
|
ha->flash_conf_off = FARX_ACCESS_FLASH_CONF;
|
|
|
|
ha->flash_data_off = FARX_ACCESS_FLASH_DATA;
|
|
|
|
ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF;
|
|
|
|
ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA;
|
|
|
|
} else if (IS_QLA81XX(ha)) {
|
2012-02-10 02:15:57 +07:00
|
|
|
ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
|
2009-01-06 02:18:11 +07:00
|
|
|
ha->mbx_count = MAILBOX_REGISTER_COUNT;
|
|
|
|
req_length = REQUEST_ENTRY_CNT_24XX;
|
|
|
|
rsp_length = RESPONSE_ENTRY_CNT_2300;
|
2013-01-30 15:34:39 +07:00
|
|
|
ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX;
|
2009-01-06 02:18:11 +07:00
|
|
|
ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
|
|
|
|
ha->init_cb_size = sizeof(struct mid_init_cb_81xx);
|
|
|
|
ha->gid_list_info_size = 8;
|
|
|
|
ha->optrom_size = OPTROM_SIZE_81XX;
|
2009-06-03 23:55:16 +07:00
|
|
|
ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
|
2009-01-06 02:18:11 +07:00
|
|
|
ha->isp_ops = &qla81xx_isp_ops;
|
|
|
|
ha->flash_conf_off = FARX_ACCESS_FLASH_CONF_81XX;
|
|
|
|
ha->flash_data_off = FARX_ACCESS_FLASH_DATA_81XX;
|
|
|
|
ha->nvram_conf_off = ~0;
|
|
|
|
ha->nvram_data_off = ~0;
|
2010-04-13 07:59:55 +07:00
|
|
|
} else if (IS_QLA82XX(ha)) {
|
2012-02-10 02:15:57 +07:00
|
|
|
ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
|
2010-04-13 07:59:55 +07:00
|
|
|
ha->mbx_count = MAILBOX_REGISTER_COUNT;
|
|
|
|
req_length = REQUEST_ENTRY_CNT_82XX;
|
|
|
|
rsp_length = RESPONSE_ENTRY_CNT_82XX;
|
|
|
|
ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
|
|
|
|
ha->init_cb_size = sizeof(struct mid_init_cb_81xx);
|
|
|
|
ha->gid_list_info_size = 8;
|
|
|
|
ha->optrom_size = OPTROM_SIZE_82XX;
|
2010-11-24 07:52:48 +07:00
|
|
|
ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
|
2010-04-13 07:59:55 +07:00
|
|
|
ha->isp_ops = &qla82xx_isp_ops;
|
|
|
|
ha->flash_conf_off = FARX_ACCESS_FLASH_CONF;
|
|
|
|
ha->flash_data_off = FARX_ACCESS_FLASH_DATA;
|
|
|
|
ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF;
|
|
|
|
ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA;
|
2012-02-10 02:15:34 +07:00
|
|
|
} else if (IS_QLA83XX(ha)) {
|
2012-08-23 01:21:03 +07:00
|
|
|
ha->portnum = PCI_FUNC(ha->pdev->devfn);
|
2012-02-10 02:15:57 +07:00
|
|
|
ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
|
2012-02-10 02:15:34 +07:00
|
|
|
ha->mbx_count = MAILBOX_REGISTER_COUNT;
|
|
|
|
req_length = REQUEST_ENTRY_CNT_24XX;
|
|
|
|
rsp_length = RESPONSE_ENTRY_CNT_2300;
|
2013-01-30 15:34:40 +07:00
|
|
|
ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX;
|
2012-02-10 02:15:34 +07:00
|
|
|
ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
|
|
|
|
ha->init_cb_size = sizeof(struct mid_init_cb_81xx);
|
|
|
|
ha->gid_list_info_size = 8;
|
|
|
|
ha->optrom_size = OPTROM_SIZE_83XX;
|
|
|
|
ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
|
|
|
|
ha->isp_ops = &qla83xx_isp_ops;
|
|
|
|
ha->flash_conf_off = FARX_ACCESS_FLASH_CONF_81XX;
|
|
|
|
ha->flash_data_off = FARX_ACCESS_FLASH_DATA_81XX;
|
|
|
|
ha->nvram_conf_off = ~0;
|
|
|
|
ha->nvram_data_off = ~0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2012-02-10 02:15:34 +07:00
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg_pci(ql_dbg_init, pdev, 0x001e,
|
|
|
|
"mbx_count=%d, req_length=%d, "
|
|
|
|
"rsp_length=%d, max_loop_id=%d, init_cb_size=%d, "
|
2012-02-10 02:15:57 +07:00
|
|
|
"gid_list_info_size=%d, optrom_size=%d, nvram_npiv_size=%d, "
|
|
|
|
"max_fibre_devices=%d.\n",
|
2011-07-15 02:00:13 +07:00
|
|
|
ha->mbx_count, req_length, rsp_length, ha->max_loop_id,
|
|
|
|
ha->init_cb_size, ha->gid_list_info_size, ha->optrom_size,
|
2012-02-10 02:15:57 +07:00
|
|
|
ha->nvram_npiv_size, ha->max_fibre_devices);
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg_pci(ql_dbg_init, pdev, 0x001f,
|
|
|
|
"isp_ops=%p, flash_conf_off=%d, "
|
|
|
|
"flash_data_off=%d, nvram_conf_off=%d, nvram_data_off=%d.\n",
|
|
|
|
ha->isp_ops, ha->flash_conf_off, ha->flash_data_off,
|
|
|
|
ha->nvram_conf_off, ha->nvram_data_off);
|
2011-11-19 00:03:16 +07:00
|
|
|
|
|
|
|
/* Configure PCI I/O space */
|
|
|
|
ret = ha->isp_ops->iospace_config(ha);
|
|
|
|
if (ret)
|
2012-11-21 14:40:43 +07:00
|
|
|
goto iospace_config_failed;
|
2011-11-19 00:03:16 +07:00
|
|
|
|
|
|
|
ql_log_pci(ql_log_info, pdev, 0x001d,
|
|
|
|
"Found an ISP%04X irq %d iobase 0x%p.\n",
|
|
|
|
pdev->device, pdev->irq, ha->iobase);
|
2008-05-13 12:21:11 +07:00
|
|
|
mutex_init(&ha->vport_lock);
|
2008-01-18 00:02:13 +07:00
|
|
|
init_completion(&ha->mbx_cmd_comp);
|
|
|
|
complete(&ha->mbx_cmd_comp);
|
|
|
|
init_completion(&ha->mbx_intr_comp);
|
2010-05-29 05:08:21 +07:00
|
|
|
init_completion(&ha->dcbx_comp);
|
2013-02-08 13:58:04 +07:00
|
|
|
init_completion(&ha->lb_portup_comp);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-07-06 03:16:51 +07:00
|
|
|
set_bit(0, (unsigned long *) ha->vp_idx_map);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-01-23 00:45:37 +07:00
|
|
|
qla2x00_config_dma_addressing(ha);
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg_pci(ql_dbg_init, pdev, 0x0020,
|
|
|
|
"64 Bit addressing is %s.\n",
|
|
|
|
ha->flags.enable_64bit_addressing ? "enable" :
|
|
|
|
"disable");
|
2008-12-10 07:45:39 +07:00
|
|
|
ret = qla2x00_mem_alloc(ha, req_length, rsp_length, &req, &rsp);
|
2008-11-07 01:40:51 +07:00
|
|
|
if (!ret) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log_pci(ql_log_fatal, pdev, 0x0031,
|
|
|
|
"Failed to allocate memory for adapter, aborting.\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
goto probe_hw_failed;
|
|
|
|
}
|
|
|
|
|
2008-12-10 07:45:39 +07:00
|
|
|
req->max_q_depth = MAX_Q_DEPTH;
|
2008-11-07 01:40:51 +07:00
|
|
|
if (ql2xmaxqdepth != 0 && ql2xmaxqdepth <= 0xffffU)
|
2008-12-10 07:45:39 +07:00
|
|
|
req->max_q_depth = ql2xmaxqdepth;
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
|
|
|
|
base_vha = qla2x00_create_host(sht, ha);
|
|
|
|
if (!base_vha) {
|
2005-06-10 07:21:28 +07:00
|
|
|
ret = -ENOMEM;
|
2009-01-23 00:45:28 +07:00
|
|
|
qla2x00_mem_free(ha);
|
2009-04-07 12:33:40 +07:00
|
|
|
qla2x00_free_req_que(ha, req);
|
|
|
|
qla2x00_free_rsp_que(ha, rsp);
|
2008-11-07 01:40:51 +07:00
|
|
|
goto probe_hw_failed;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
pci_set_drvdata(pdev, base_vha);
|
|
|
|
|
|
|
|
host = base_vha->host;
|
2009-04-07 12:33:40 +07:00
|
|
|
base_vha->req = req;
|
2008-12-10 07:45:39 +07:00
|
|
|
host->can_queue = req->length + 128;
|
|
|
|
if (IS_QLA2XXX_MIDTYPE(ha))
|
2008-11-07 01:40:51 +07:00
|
|
|
base_vha->mgmt_svr_loop_id = 10 + base_vha->vp_idx;
|
2008-12-10 07:45:39 +07:00
|
|
|
else
|
2008-11-07 01:40:51 +07:00
|
|
|
base_vha->mgmt_svr_loop_id = MANAGEMENT_SERVER +
|
|
|
|
base_vha->vp_idx;
|
2010-09-04 05:20:56 +07:00
|
|
|
|
|
|
|
/* Set the SG table size based on ISP type */
|
|
|
|
if (!IS_FWI2_CAPABLE(ha)) {
|
|
|
|
if (IS_QLA2100(ha))
|
|
|
|
host->sg_tablesize = 32;
|
|
|
|
} else {
|
|
|
|
if (!IS_QLA82XX(ha))
|
|
|
|
host->sg_tablesize = QLA_SG_ALL;
|
|
|
|
}
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_init, base_vha, 0x0032,
|
|
|
|
"can_queue=%d, req=%p, "
|
|
|
|
"mgmt_svr_loop_id=%d, sg_tablesize=%d.\n",
|
|
|
|
host->can_queue, base_vha->req,
|
|
|
|
base_vha->mgmt_svr_loop_id, host->sg_tablesize);
|
2012-02-10 02:15:57 +07:00
|
|
|
host->max_id = ha->max_fibre_devices;
|
2008-11-07 01:40:51 +07:00
|
|
|
host->cmd_per_lun = 3;
|
|
|
|
host->unique_id = host->host_no;
|
2011-08-17 01:29:23 +07:00
|
|
|
if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif)
|
2010-07-23 17:28:38 +07:00
|
|
|
host->max_cmd_len = 32;
|
|
|
|
else
|
|
|
|
host->max_cmd_len = MAX_CMDSZ;
|
2008-11-07 01:40:51 +07:00
|
|
|
host->max_channel = MAX_BUSES - 1;
|
2011-05-11 01:30:13 +07:00
|
|
|
host->max_lun = ql2xmaxlun;
|
2008-11-07 01:40:51 +07:00
|
|
|
host->transportt = qla2xxx_transport_template;
|
2010-01-13 04:02:47 +07:00
|
|
|
sht->vendor_id = (SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_QLOGIC);
|
2008-11-07 01:40:51 +07:00
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_init, base_vha, 0x0033,
|
|
|
|
"max_id=%d this_id=%d "
|
|
|
|
"cmd_per_len=%d unique_id=%d max_cmd_len=%d max_channel=%d "
|
2011-11-19 00:03:06 +07:00
|
|
|
"max_lun=%d transportt=%p, vendor_id=%llu.\n", host->max_id,
|
2011-07-15 02:00:13 +07:00
|
|
|
host->this_id, host->cmd_per_lun, host->unique_id,
|
|
|
|
host->max_cmd_len, host->max_channel, host->max_lun,
|
|
|
|
host->transportt, sht->vendor_id);
|
|
|
|
|
2012-05-16 01:34:14 +07:00
|
|
|
que_init:
|
|
|
|
/* Alloc arrays of request and response ring ptrs */
|
|
|
|
if (!qla2x00_alloc_queues(ha, req, rsp)) {
|
|
|
|
ql_log(ql_log_fatal, base_vha, 0x003d,
|
|
|
|
"Failed to allocate memory for queue pointers..."
|
|
|
|
"aborting.\n");
|
|
|
|
goto probe_init_failed;
|
|
|
|
}
|
|
|
|
|
2012-05-16 01:34:28 +07:00
|
|
|
qlt_probe_one_stage1(base_vha, ha);
|
2012-05-16 01:34:14 +07:00
|
|
|
|
2008-12-10 07:45:39 +07:00
|
|
|
/* Set up the irqs */
|
|
|
|
ret = qla2x00_request_irqs(ha, rsp);
|
|
|
|
if (ret)
|
2009-01-23 00:45:28 +07:00
|
|
|
goto probe_init_failed;
|
2010-01-13 04:02:46 +07:00
|
|
|
|
|
|
|
pci_save_state(pdev);
|
|
|
|
|
2012-05-16 01:34:14 +07:00
|
|
|
/* Assign back pointers */
|
2009-04-07 12:33:40 +07:00
|
|
|
rsp->req = req;
|
|
|
|
req->rsp = rsp;
|
2012-05-16 01:34:14 +07:00
|
|
|
|
2009-03-24 23:07:55 +07:00
|
|
|
/* FWI2-capable only. */
|
|
|
|
req->req_q_in = &ha->iobase->isp24.req_q_in;
|
|
|
|
req->req_q_out = &ha->iobase->isp24.req_q_out;
|
|
|
|
rsp->rsp_q_in = &ha->iobase->isp24.rsp_q_in;
|
|
|
|
rsp->rsp_q_out = &ha->iobase->isp24.rsp_q_out;
|
2012-02-10 02:15:34 +07:00
|
|
|
if (ha->mqenable || IS_QLA83XX(ha)) {
|
2009-03-24 23:07:55 +07:00
|
|
|
req->req_q_in = &ha->mqiobase->isp25mq.req_q_in;
|
|
|
|
req->req_q_out = &ha->mqiobase->isp25mq.req_q_out;
|
|
|
|
rsp->rsp_q_in = &ha->mqiobase->isp25mq.rsp_q_in;
|
|
|
|
rsp->rsp_q_out = &ha->mqiobase->isp25mq.rsp_q_out;
|
2008-12-19 01:06:15 +07:00
|
|
|
}
|
|
|
|
|
2010-04-13 07:59:55 +07:00
|
|
|
if (IS_QLA82XX(ha)) {
|
|
|
|
req->req_q_out = &ha->iobase->isp82.req_q_out[0];
|
|
|
|
rsp->rsp_q_in = &ha->iobase->isp82.rsp_q_in[0];
|
|
|
|
rsp->rsp_q_out = &ha->iobase->isp82.rsp_q_out[0];
|
|
|
|
}
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_multiq, base_vha, 0xc009,
|
|
|
|
"rsp_q_map=%p req_q_map=%p rsp->req=%p req->rsp=%p.\n",
|
|
|
|
ha->rsp_q_map, ha->req_q_map, rsp->req, req->rsp);
|
|
|
|
ql_dbg(ql_dbg_multiq, base_vha, 0xc00a,
|
|
|
|
"req->req_q_in=%p req->req_q_out=%p "
|
|
|
|
"rsp->rsp_q_in=%p rsp->rsp_q_out=%p.\n",
|
|
|
|
req->req_q_in, req->req_q_out,
|
|
|
|
rsp->rsp_q_in, rsp->rsp_q_out);
|
|
|
|
ql_dbg(ql_dbg_init, base_vha, 0x003e,
|
|
|
|
"rsp_q_map=%p req_q_map=%p rsp->req=%p req->rsp=%p.\n",
|
|
|
|
ha->rsp_q_map, ha->req_q_map, rsp->req, req->rsp);
|
|
|
|
ql_dbg(ql_dbg_init, base_vha, 0x003f,
|
|
|
|
"req->req_q_in=%p req->req_q_out=%p rsp->rsp_q_in=%p rsp->rsp_q_out=%p.\n",
|
|
|
|
req->req_q_in, req->req_q_out, rsp->rsp_q_in, rsp->rsp_q_out);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
if (qla2x00_initialize_adapter(base_vha)) {
|
|
|
|
ql_log(ql_log_fatal, base_vha, 0x00d6,
|
|
|
|
"Failed to initialize adapter - Adapter flags %x.\n",
|
|
|
|
base_vha->device_flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-04-13 07:59:55 +07:00
|
|
|
if (IS_QLA82XX(ha)) {
|
|
|
|
qla82xx_idc_lock(ha);
|
|
|
|
qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
2012-08-23 01:21:03 +07:00
|
|
|
QLA8XXX_DEV_FAILED);
|
2010-04-13 07:59:55 +07:00
|
|
|
qla82xx_idc_unlock(ha);
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_fatal, base_vha, 0x00d7,
|
|
|
|
"HW State: FAILED.\n");
|
2010-04-13 07:59:55 +07:00
|
|
|
}
|
|
|
|
|
2005-06-10 07:21:28 +07:00
|
|
|
ret = -ENODEV;
|
2005-04-17 05:20:36 +07:00
|
|
|
goto probe_failed;
|
|
|
|
}
|
|
|
|
|
2009-08-05 23:18:40 +07:00
|
|
|
if (ha->mqenable) {
|
|
|
|
if (qla25xx_setup_mode(base_vha)) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_warn, base_vha, 0x00ec,
|
|
|
|
"Failed to create queues, falling back to single queue mode.\n");
|
2009-08-05 23:18:40 +07:00
|
|
|
goto que_init;
|
|
|
|
}
|
|
|
|
}
|
2009-04-07 12:33:41 +07:00
|
|
|
|
2009-06-03 23:55:17 +07:00
|
|
|
if (ha->flags.running_gold_fw)
|
|
|
|
goto skip_dpc;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* Startup the kernel thread for this host adapter
|
|
|
|
*/
|
2006-02-15 00:46:22 +07:00
|
|
|
ha->dpc_thread = kthread_create(qla2x00_do_dpc, ha,
|
2011-07-15 02:00:13 +07:00
|
|
|
"%s_dpc", base_vha->host_str);
|
2006-02-15 00:46:22 +07:00
|
|
|
if (IS_ERR(ha->dpc_thread)) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_fatal, base_vha, 0x00ed,
|
|
|
|
"Failed to start DPC thread.\n");
|
2006-02-15 00:46:22 +07:00
|
|
|
ret = PTR_ERR(ha->dpc_thread);
|
2005-04-17 05:20:36 +07:00
|
|
|
goto probe_failed;
|
|
|
|
}
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_init, base_vha, 0x00ee,
|
|
|
|
"DPC thread started successfully.\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-05-16 01:34:28 +07:00
|
|
|
/*
|
|
|
|
* If we're not coming up in initiator mode, we might sit for
|
|
|
|
* a while without waking up the dpc thread, which leads to a
|
|
|
|
* stuck process warning. So just kick the dpc once here and
|
|
|
|
* let the kthread start (and go back to sleep in qla2x00_do_dpc).
|
|
|
|
*/
|
|
|
|
qla2xxx_wake_dpc(base_vha);
|
|
|
|
|
2012-08-23 01:21:04 +07:00
|
|
|
if (IS_QLA8031(ha) || IS_MCTP_CAPABLE(ha)) {
|
|
|
|
sprintf(wq_name, "qla2xxx_%lu_dpc_lp_wq", base_vha->host_no);
|
|
|
|
ha->dpc_lp_wq = create_singlethread_workqueue(wq_name);
|
|
|
|
INIT_WORK(&ha->idc_aen, qla83xx_service_idc_aen);
|
|
|
|
|
|
|
|
sprintf(wq_name, "qla2xxx_%lu_dpc_hp_wq", base_vha->host_no);
|
|
|
|
ha->dpc_hp_wq = create_singlethread_workqueue(wq_name);
|
|
|
|
INIT_WORK(&ha->nic_core_reset, qla83xx_nic_core_reset_work);
|
|
|
|
INIT_WORK(&ha->idc_state_handler,
|
|
|
|
qla83xx_idc_state_handler_work);
|
|
|
|
INIT_WORK(&ha->nic_core_unrecoverable,
|
|
|
|
qla83xx_nic_core_unrecoverable_work);
|
|
|
|
}
|
|
|
|
|
2009-06-03 23:55:17 +07:00
|
|
|
skip_dpc:
|
2008-11-07 01:40:51 +07:00
|
|
|
list_add_tail(&base_vha->list, &ha->vp_list);
|
|
|
|
base_vha->host->irq = ha->pdev->irq;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Initialized the timer */
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_start_timer(base_vha, qla2x00_timer, WATCH_INTERVAL);
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_init, base_vha, 0x00ef,
|
|
|
|
"Started qla2x00_timer with "
|
|
|
|
"interval=%d.\n", WATCH_INTERVAL);
|
|
|
|
ql_dbg(ql_dbg_init, base_vha, 0x00f0,
|
|
|
|
"Detected hba at address=%p.\n",
|
|
|
|
ha);
|
2006-11-22 23:22:19 +07:00
|
|
|
|
2011-08-17 01:29:23 +07:00
|
|
|
if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) {
|
2010-05-05 05:01:30 +07:00
|
|
|
if (ha->fw_attributes & BIT_4) {
|
2012-08-23 01:21:31 +07:00
|
|
|
int prot = 0, guard;
|
2010-05-05 05:01:30 +07:00
|
|
|
base_vha->flags.difdix_supported = 1;
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_init, base_vha, 0x00f1,
|
|
|
|
"Registering for DIF/DIX type 1 and 3 protection.\n");
|
2011-08-17 01:29:22 +07:00
|
|
|
if (ql2xenabledif == 1)
|
|
|
|
prot = SHOST_DIX_TYPE0_PROTECTION;
|
2010-05-05 05:01:30 +07:00
|
|
|
scsi_host_set_prot(host,
|
2011-08-17 01:29:22 +07:00
|
|
|
prot | SHOST_DIF_TYPE1_PROTECTION
|
2010-07-23 17:28:38 +07:00
|
|
|
| SHOST_DIF_TYPE2_PROTECTION
|
2010-05-05 05:01:30 +07:00
|
|
|
| SHOST_DIF_TYPE3_PROTECTION
|
|
|
|
| SHOST_DIX_TYPE1_PROTECTION
|
2010-07-23 17:28:38 +07:00
|
|
|
| SHOST_DIX_TYPE2_PROTECTION
|
2010-05-05 05:01:30 +07:00
|
|
|
| SHOST_DIX_TYPE3_PROTECTION);
|
2012-08-23 01:21:31 +07:00
|
|
|
|
|
|
|
guard = SHOST_DIX_GUARD_CRC;
|
|
|
|
|
|
|
|
if (IS_PI_IPGUARD_CAPABLE(ha) &&
|
|
|
|
(ql2xenabledif > 1 || IS_PI_DIFB_DIX0_CAPABLE(ha)))
|
|
|
|
guard |= SHOST_DIX_GUARD_IP;
|
|
|
|
|
|
|
|
scsi_host_set_guard(host, guard);
|
2010-05-05 05:01:30 +07:00
|
|
|
} else
|
|
|
|
base_vha->flags.difdix_supported = 0;
|
|
|
|
}
|
|
|
|
|
2010-04-13 07:59:55 +07:00
|
|
|
ha->isp_ops->enable_intrs(ha);
|
|
|
|
|
2005-06-10 07:21:28 +07:00
|
|
|
ret = scsi_add_host(host, &pdev->dev);
|
|
|
|
if (ret)
|
|
|
|
goto probe_failed;
|
|
|
|
|
2009-12-02 22:11:16 +07:00
|
|
|
base_vha->flags.init_done = 1;
|
|
|
|
base_vha->flags.online = 1;
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_init, base_vha, 0x00f2,
|
|
|
|
"Init done and hba is online.\n");
|
|
|
|
|
2012-05-16 01:34:28 +07:00
|
|
|
if (qla_ini_mode_enabled(base_vha))
|
|
|
|
scsi_scan_host(host);
|
|
|
|
else
|
|
|
|
ql_dbg(ql_dbg_init, base_vha, 0x0122,
|
|
|
|
"skipping scsi_scan_host() for non-initiator port\n");
|
2006-11-22 23:24:48 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_alloc_sysfs_attr(base_vha);
|
2005-06-10 07:21:28 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_init_host_attr(base_vha);
|
2005-06-10 07:21:28 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_dfs_setup(base_vha);
|
2008-01-18 00:02:17 +07:00
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_info, base_vha, 0x00fb,
|
|
|
|
"QLogic %s - %s.\n",
|
|
|
|
ha->model_number, ha->model_desc ? ha->model_desc : "");
|
|
|
|
ql_log(ql_log_info, base_vha, 0x00fc,
|
|
|
|
"ISP%04X: %s @ %s hdma%c host#=%ld fw=%s.\n",
|
|
|
|
pdev->device, ha->isp_ops->pci_info_str(base_vha, pci_info),
|
|
|
|
pci_name(pdev), ha->flags.enable_64bit_addressing ? '+' : '-',
|
|
|
|
base_vha->host_no,
|
2008-11-07 01:40:51 +07:00
|
|
|
ha->isp_ops->fw_version_str(base_vha, fw_str));
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-05-16 01:34:28 +07:00
|
|
|
qlt_add_target(ha, base_vha);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
|
2009-01-23 00:45:28 +07:00
|
|
|
probe_init_failed:
|
2009-04-07 12:33:40 +07:00
|
|
|
qla2x00_free_req_que(ha, req);
|
2012-05-16 01:34:14 +07:00
|
|
|
ha->req_q_map[0] = NULL;
|
|
|
|
clear_bit(0, ha->req_qid_map);
|
2009-04-07 12:33:40 +07:00
|
|
|
qla2x00_free_rsp_que(ha, rsp);
|
2012-05-16 01:34:14 +07:00
|
|
|
ha->rsp_q_map[0] = NULL;
|
|
|
|
clear_bit(0, ha->rsp_qid_map);
|
2009-04-07 12:33:40 +07:00
|
|
|
ha->max_req_queues = ha->max_rsp_queues = 0;
|
2009-01-23 00:45:28 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
probe_failed:
|
2009-03-24 23:08:05 +07:00
|
|
|
if (base_vha->timer_active)
|
|
|
|
qla2x00_stop_timer(base_vha);
|
|
|
|
base_vha->flags.online = 0;
|
|
|
|
if (ha->dpc_thread) {
|
|
|
|
struct task_struct *t = ha->dpc_thread;
|
|
|
|
|
|
|
|
ha->dpc_thread = NULL;
|
|
|
|
kthread_stop(t);
|
|
|
|
}
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_free_device(base_vha);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_host_put(base_vha->host);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
probe_hw_failed:
|
2010-04-13 07:59:55 +07:00
|
|
|
if (IS_QLA82XX(ha)) {
|
|
|
|
qla82xx_idc_lock(ha);
|
|
|
|
qla82xx_clear_drv_active(ha);
|
|
|
|
qla82xx_idc_unlock(ha);
|
2012-11-21 14:40:43 +07:00
|
|
|
}
|
|
|
|
iospace_config_failed:
|
|
|
|
if (IS_QLA82XX(ha)) {
|
|
|
|
if (!ha->nx_pcibase)
|
|
|
|
iounmap((device_reg_t __iomem *)ha->nx_pcibase);
|
2010-04-13 07:59:55 +07:00
|
|
|
if (!ql2xdbwr)
|
|
|
|
iounmap((device_reg_t __iomem *)ha->nxdb_wr_ptr);
|
|
|
|
} else {
|
|
|
|
if (ha->iobase)
|
|
|
|
iounmap(ha->iobase);
|
|
|
|
}
|
2008-11-07 01:40:51 +07:00
|
|
|
pci_release_selected_regions(ha->pdev, ha->bars);
|
|
|
|
kfree(ha);
|
|
|
|
ha = NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-06-10 07:21:28 +07:00
|
|
|
probe_out:
|
2008-11-07 01:40:51 +07:00
|
|
|
pci_disable_device(pdev);
|
2005-06-10 07:21:28 +07:00
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2012-05-16 01:34:28 +07:00
|
|
|
static void
|
|
|
|
qla2x00_stop_dpc_thread(scsi_qla_host_t *vha)
|
|
|
|
{
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
struct task_struct *t = ha->dpc_thread;
|
|
|
|
|
|
|
|
if (ha->dpc_thread == NULL)
|
|
|
|
return;
|
|
|
|
/*
|
|
|
|
* qla2xxx_wake_dpc checks for ->dpc_thread
|
|
|
|
* so we need to zero it out.
|
|
|
|
*/
|
|
|
|
ha->dpc_thread = NULL;
|
|
|
|
kthread_stop(t);
|
|
|
|
}
|
|
|
|
|
2010-10-16 01:27:46 +07:00
|
|
|
static void
|
|
|
|
qla2x00_shutdown(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
scsi_qla_host_t *vha;
|
|
|
|
struct qla_hw_data *ha;
|
|
|
|
|
2013-02-08 13:57:44 +07:00
|
|
|
if (!atomic_read(&pdev->enable_cnt))
|
|
|
|
return;
|
|
|
|
|
2010-10-16 01:27:46 +07:00
|
|
|
vha = pci_get_drvdata(pdev);
|
|
|
|
ha = vha->hw;
|
|
|
|
|
|
|
|
/* Turn-off FCE trace */
|
|
|
|
if (ha->flags.fce_enabled) {
|
|
|
|
qla2x00_disable_fce_trace(vha, NULL, NULL);
|
|
|
|
ha->flags.fce_enabled = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Turn-off EFT trace */
|
|
|
|
if (ha->eft)
|
|
|
|
qla2x00_disable_eft_trace(vha);
|
|
|
|
|
|
|
|
/* Stop currently executing firmware. */
|
|
|
|
qla2x00_try_to_stop_firmware(vha);
|
|
|
|
|
|
|
|
/* Turn adapter off line */
|
|
|
|
vha->flags.online = 0;
|
|
|
|
|
|
|
|
/* turn-off interrupts on the card */
|
|
|
|
if (ha->interrupts_on) {
|
|
|
|
vha->flags.init_done = 0;
|
|
|
|
ha->isp_ops->disable_intrs(ha);
|
|
|
|
}
|
|
|
|
|
|
|
|
qla2x00_free_irqs(vha);
|
|
|
|
|
|
|
|
qla2x00_free_fw_dump(ha);
|
|
|
|
}
|
|
|
|
|
2008-01-14 15:55:16 +07:00
|
|
|
static void
|
2006-06-24 06:11:22 +07:00
|
|
|
qla2x00_remove_one(struct pci_dev *pdev)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2010-09-04 04:57:00 +07:00
|
|
|
scsi_qla_host_t *base_vha, *vha;
|
2008-11-07 01:40:51 +07:00
|
|
|
struct qla_hw_data *ha;
|
2010-09-04 04:57:00 +07:00
|
|
|
unsigned long flags;
|
2008-11-07 01:40:51 +07:00
|
|
|
|
2012-05-16 01:34:14 +07:00
|
|
|
/*
|
|
|
|
* If the PCI device is disabled that means that probe failed and any
|
|
|
|
* resources should be have cleaned up on probe exit.
|
|
|
|
*/
|
|
|
|
if (!atomic_read(&pdev->enable_cnt))
|
|
|
|
return;
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
base_vha = pci_get_drvdata(pdev);
|
|
|
|
ha = base_vha->hw;
|
|
|
|
|
2012-05-16 01:34:28 +07:00
|
|
|
ha->flags.host_shutting_down = 1;
|
|
|
|
|
2012-11-21 14:39:55 +07:00
|
|
|
set_bit(UNLOADING, &base_vha->dpc_flags);
|
2011-05-11 01:18:16 +07:00
|
|
|
mutex_lock(&ha->vport_lock);
|
|
|
|
while (ha->cur_vport_count) {
|
|
|
|
struct Scsi_Host *scsi_host;
|
2010-09-04 04:57:00 +07:00
|
|
|
|
2011-05-11 01:18:16 +07:00
|
|
|
spin_lock_irqsave(&ha->vport_slock, flags);
|
2010-09-04 04:57:00 +07:00
|
|
|
|
2011-05-11 01:18:16 +07:00
|
|
|
BUG_ON(base_vha->list.next == &ha->vp_list);
|
|
|
|
/* This assumes first entry in ha->vp_list is always base vha */
|
|
|
|
vha = list_first_entry(&base_vha->list, scsi_qla_host_t, list);
|
|
|
|
scsi_host = scsi_host_get(vha->host);
|
2010-09-04 04:57:00 +07:00
|
|
|
|
2011-05-11 01:18:16 +07:00
|
|
|
spin_unlock_irqrestore(&ha->vport_slock, flags);
|
|
|
|
mutex_unlock(&ha->vport_lock);
|
|
|
|
|
|
|
|
fc_vport_terminate(vha->fc_vport);
|
|
|
|
scsi_host_put(vha->host);
|
2010-09-04 04:57:00 +07:00
|
|
|
|
2011-05-11 01:18:16 +07:00
|
|
|
mutex_lock(&ha->vport_lock);
|
2008-11-07 01:40:51 +07:00
|
|
|
}
|
2011-05-11 01:18:16 +07:00
|
|
|
mutex_unlock(&ha->vport_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-08-23 01:21:03 +07:00
|
|
|
if (IS_QLA8031(ha)) {
|
|
|
|
ql_dbg(ql_dbg_p3p, base_vha, 0xb07e,
|
|
|
|
"Clearing fcoe driver presence.\n");
|
|
|
|
if (qla83xx_clear_drv_presence(base_vha) != QLA_SUCCESS)
|
|
|
|
ql_dbg(ql_dbg_p3p, base_vha, 0xb079,
|
|
|
|
"Error while clearing DRV-Presence.\n");
|
|
|
|
}
|
|
|
|
|
2009-03-24 23:08:05 +07:00
|
|
|
qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16);
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_dfs_remove(base_vha);
|
2008-08-14 11:37:01 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
qla84xx_put_chip(base_vha);
|
2008-08-14 11:37:01 +07:00
|
|
|
|
2009-03-24 23:08:05 +07:00
|
|
|
/* Disable timer */
|
|
|
|
if (base_vha->timer_active)
|
|
|
|
qla2x00_stop_timer(base_vha);
|
|
|
|
|
|
|
|
base_vha->flags.online = 0;
|
|
|
|
|
2009-04-07 12:33:41 +07:00
|
|
|
/* Flush the work queue and remove it */
|
|
|
|
if (ha->wq) {
|
|
|
|
flush_workqueue(ha->wq);
|
|
|
|
destroy_workqueue(ha->wq);
|
|
|
|
ha->wq = NULL;
|
|
|
|
}
|
|
|
|
|
2012-08-23 01:21:03 +07:00
|
|
|
/* Cancel all work and destroy DPC workqueues */
|
|
|
|
if (ha->dpc_lp_wq) {
|
|
|
|
cancel_work_sync(&ha->idc_aen);
|
|
|
|
destroy_workqueue(ha->dpc_lp_wq);
|
|
|
|
ha->dpc_lp_wq = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ha->dpc_hp_wq) {
|
|
|
|
cancel_work_sync(&ha->nic_core_reset);
|
|
|
|
cancel_work_sync(&ha->idc_state_handler);
|
|
|
|
cancel_work_sync(&ha->nic_core_unrecoverable);
|
|
|
|
destroy_workqueue(ha->dpc_hp_wq);
|
|
|
|
ha->dpc_hp_wq = NULL;
|
|
|
|
}
|
|
|
|
|
2009-03-24 23:08:05 +07:00
|
|
|
/* Kill the kernel thread for this host */
|
|
|
|
if (ha->dpc_thread) {
|
|
|
|
struct task_struct *t = ha->dpc_thread;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qla2xxx_wake_dpc checks for ->dpc_thread
|
|
|
|
* so we need to zero it out.
|
|
|
|
*/
|
|
|
|
ha->dpc_thread = NULL;
|
|
|
|
kthread_stop(t);
|
|
|
|
}
|
2012-05-16 01:34:28 +07:00
|
|
|
qlt_remove_target(ha, base_vha);
|
2009-03-24 23:08:05 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_free_sysfs_attr(base_vha);
|
2008-01-18 00:02:17 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
fc_remove_host(base_vha->host);
|
2008-04-04 03:13:26 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_remove_host(base_vha->host);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_free_device(base_vha);
|
2005-04-18 03:06:53 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_host_put(base_vha->host);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-04-13 07:59:55 +07:00
|
|
|
if (IS_QLA82XX(ha)) {
|
2010-05-29 05:08:15 +07:00
|
|
|
qla82xx_idc_lock(ha);
|
|
|
|
qla82xx_clear_drv_active(ha);
|
|
|
|
qla82xx_idc_unlock(ha);
|
|
|
|
|
2010-04-13 07:59:55 +07:00
|
|
|
iounmap((device_reg_t __iomem *)ha->nx_pcibase);
|
|
|
|
if (!ql2xdbwr)
|
|
|
|
iounmap((device_reg_t __iomem *)ha->nxdb_wr_ptr);
|
|
|
|
} else {
|
|
|
|
if (ha->iobase)
|
|
|
|
iounmap(ha->iobase);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-04-13 07:59:55 +07:00
|
|
|
if (ha->mqiobase)
|
|
|
|
iounmap(ha->mqiobase);
|
2012-02-10 02:15:34 +07:00
|
|
|
|
|
|
|
if (IS_QLA83XX(ha) && ha->msixbase)
|
|
|
|
iounmap(ha->msixbase);
|
2010-04-13 07:59:55 +07:00
|
|
|
}
|
2008-12-10 07:45:39 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
pci_release_selected_regions(ha->pdev, ha->bars);
|
|
|
|
kfree(ha);
|
|
|
|
ha = NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-01-13 04:02:46 +07:00
|
|
|
pci_disable_pcie_error_reporting(pdev);
|
|
|
|
|
2007-03-28 05:49:49 +07:00
|
|
|
pci_disable_device(pdev);
|
2005-04-17 05:20:36 +07:00
|
|
|
pci_set_drvdata(pdev, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_free_device(scsi_qla_host_t *vha)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-12-16 12:29:46 +07:00
|
|
|
qla2x00_abort_all_cmds(vha, DID_NO_CONNECT << 16);
|
|
|
|
|
|
|
|
/* Disable timer */
|
|
|
|
if (vha->timer_active)
|
|
|
|
qla2x00_stop_timer(vha);
|
|
|
|
|
2012-05-16 01:34:28 +07:00
|
|
|
qla2x00_stop_dpc_thread(vha);
|
2009-12-16 12:29:46 +07:00
|
|
|
|
2009-04-07 12:33:40 +07:00
|
|
|
qla25xx_delete_queues(vha);
|
2008-01-18 00:02:17 +07:00
|
|
|
if (ha->flags.fce_enabled)
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_disable_fce_trace(vha, NULL, NULL);
|
2008-01-18 00:02:17 +07:00
|
|
|
|
2006-06-24 06:10:29 +07:00
|
|
|
if (ha->eft)
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_disable_eft_trace(vha);
|
2006-06-24 06:10:29 +07:00
|
|
|
|
2005-08-27 09:10:20 +07:00
|
|
|
/* Stop currently executing firmware. */
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_try_to_stop_firmware(vha);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-12-16 12:29:46 +07:00
|
|
|
vha->flags.online = 0;
|
|
|
|
|
2005-08-27 09:10:20 +07:00
|
|
|
/* turn-off interrupts on the card */
|
2010-04-13 07:59:55 +07:00
|
|
|
if (ha->interrupts_on) {
|
|
|
|
vha->flags.init_done = 0;
|
2007-07-20 05:06:00 +07:00
|
|
|
ha->isp_ops->disable_intrs(ha);
|
2010-04-13 07:59:55 +07:00
|
|
|
}
|
2005-08-27 09:10:20 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_free_irqs(vha);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-07-23 17:28:30 +07:00
|
|
|
qla2x00_free_fcports(vha);
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_mem_free(ha);
|
2008-12-10 07:45:39 +07:00
|
|
|
|
2011-08-17 01:31:44 +07:00
|
|
|
qla82xx_md_free(vha);
|
|
|
|
|
2008-12-10 07:45:39 +07:00
|
|
|
qla2x00_free_queues(ha);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2010-07-23 17:28:30 +07:00
|
|
|
void qla2x00_free_fcports(struct scsi_qla_host *vha)
|
|
|
|
{
|
|
|
|
fc_port_t *fcport, *tfcport;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(fcport, tfcport, &vha->vp_fcports, list) {
|
|
|
|
list_del(&fcport->list);
|
2012-08-23 01:21:00 +07:00
|
|
|
qla2x00_clear_loop_id(fcport);
|
2010-07-23 17:28:30 +07:00
|
|
|
kfree(fcport);
|
|
|
|
fcport = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-01-21 05:53:13 +07:00
|
|
|
static inline void
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport,
|
2006-01-21 05:53:13 +07:00
|
|
|
int defer)
|
|
|
|
{
|
|
|
|
struct fc_rport *rport;
|
2009-08-26 01:36:20 +07:00
|
|
|
scsi_qla_host_t *base_vha;
|
2011-01-29 06:17:56 +07:00
|
|
|
unsigned long flags;
|
2006-01-21 05:53:13 +07:00
|
|
|
|
|
|
|
if (!fcport->rport)
|
|
|
|
return;
|
|
|
|
|
|
|
|
rport = fcport->rport;
|
|
|
|
if (defer) {
|
2009-08-26 01:36:20 +07:00
|
|
|
base_vha = pci_get_drvdata(vha->hw->pdev);
|
2011-01-29 06:17:56 +07:00
|
|
|
spin_lock_irqsave(vha->host->host_lock, flags);
|
2006-01-21 05:53:13 +07:00
|
|
|
fcport->drport = rport;
|
2011-01-29 06:17:56 +07:00
|
|
|
spin_unlock_irqrestore(vha->host->host_lock, flags);
|
2009-08-26 01:36:20 +07:00
|
|
|
set_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags);
|
|
|
|
qla2xxx_wake_dpc(base_vha);
|
2012-05-16 01:34:28 +07:00
|
|
|
} else {
|
2006-01-21 05:53:13 +07:00
|
|
|
fc_remote_port_delete(rport);
|
2012-05-16 01:34:28 +07:00
|
|
|
qlt_fc_port_deleted(vha, fcport);
|
|
|
|
}
|
2006-01-21 05:53:13 +07:00
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* qla2x00_mark_device_lost Updates fcport state when device goes offline.
|
|
|
|
*
|
|
|
|
* Input: ha = adapter block pointer. fcport = port structure pointer.
|
|
|
|
*
|
|
|
|
* Return: None.
|
|
|
|
*
|
|
|
|
* Context:
|
|
|
|
*/
|
2008-11-07 01:40:51 +07:00
|
|
|
void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport,
|
2006-01-21 05:53:13 +07:00
|
|
|
int do_login, int defer)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2007-07-06 03:16:51 +07:00
|
|
|
if (atomic_read(&fcport->state) == FCS_ONLINE &&
|
2012-05-16 01:34:20 +07:00
|
|
|
vha->vp_idx == fcport->vha->vp_idx) {
|
2011-03-31 01:46:32 +07:00
|
|
|
qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_schedule_rport_del(vha, fcport, defer);
|
|
|
|
}
|
2005-07-07 00:32:07 +07:00
|
|
|
/*
|
2005-04-17 05:20:36 +07:00
|
|
|
* We may need to retry the login, so don't change the state of the
|
|
|
|
* port but do the retries.
|
|
|
|
*/
|
|
|
|
if (atomic_read(&fcport->state) != FCS_DEVICE_DEAD)
|
2011-03-31 01:46:32 +07:00
|
|
|
qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (!do_login)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (fcport->login_retry == 0) {
|
2008-11-07 01:40:51 +07:00
|
|
|
fcport->login_retry = vha->hw->login_retry_count;
|
|
|
|
set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_disc, vha, 0x2067,
|
|
|
|
"Port login retry "
|
2005-04-17 05:20:36 +07:00
|
|
|
"%02x%02x%02x%02x%02x%02x%02x%02x, "
|
2011-07-15 02:00:13 +07:00
|
|
|
"id = 0x%04x retry cnt=%d.\n",
|
|
|
|
fcport->port_name[0], fcport->port_name[1],
|
|
|
|
fcport->port_name[2], fcport->port_name[3],
|
|
|
|
fcport->port_name[4], fcport->port_name[5],
|
|
|
|
fcport->port_name[6], fcport->port_name[7],
|
|
|
|
fcport->loop_id, fcport->login_retry);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qla2x00_mark_all_devices_lost
|
|
|
|
* Updates fcport state when device goes offline.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* ha = adapter block pointer.
|
|
|
|
* fcport = port structure pointer.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Context:
|
|
|
|
*/
|
|
|
|
void
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
fc_port_t *fcport;
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
list_for_each_entry(fcport, &vha->vp_fcports, list) {
|
2012-05-16 01:34:20 +07:00
|
|
|
if (vha->vp_idx != 0 && vha->vp_idx != fcport->vha->vp_idx)
|
2005-04-17 05:20:36 +07:00
|
|
|
continue;
|
2009-08-26 01:36:19 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* No point in marking the device as lost, if the device is
|
|
|
|
* already DEAD.
|
|
|
|
*/
|
|
|
|
if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD)
|
|
|
|
continue;
|
2008-11-07 01:40:51 +07:00
|
|
|
if (atomic_read(&fcport->state) == FCS_ONLINE) {
|
2011-03-31 01:46:32 +07:00
|
|
|
qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
|
2009-08-26 01:36:19 +07:00
|
|
|
if (defer)
|
|
|
|
qla2x00_schedule_rport_del(vha, fcport, defer);
|
2012-05-16 01:34:20 +07:00
|
|
|
else if (vha->vp_idx == fcport->vha->vp_idx)
|
2009-08-26 01:36:19 +07:00
|
|
|
qla2x00_schedule_rport_del(vha, fcport, defer);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qla2x00_mem_alloc
|
|
|
|
* Allocates adapter memory.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* 0 = success.
|
2008-02-01 03:33:48 +07:00
|
|
|
* !0 = failure.
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2008-02-01 03:33:48 +07:00
|
|
|
static int
|
2008-12-10 07:45:39 +07:00
|
|
|
qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
|
|
|
|
struct req_que **req, struct rsp_que **rsp)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
char name[16];
|
|
|
|
|
2008-02-01 03:33:48 +07:00
|
|
|
ha->init_cb = dma_alloc_coherent(&ha->pdev->dev, ha->init_cb_size,
|
2008-11-07 01:40:51 +07:00
|
|
|
&ha->init_cb_dma, GFP_KERNEL);
|
2008-02-01 03:33:48 +07:00
|
|
|
if (!ha->init_cb)
|
2008-11-07 01:40:51 +07:00
|
|
|
goto fail;
|
2008-02-01 03:33:48 +07:00
|
|
|
|
2012-05-16 01:34:28 +07:00
|
|
|
if (qlt_mem_alloc(ha) < 0)
|
|
|
|
goto fail_free_init_cb;
|
|
|
|
|
2012-02-10 02:15:57 +07:00
|
|
|
ha->gid_list = dma_alloc_coherent(&ha->pdev->dev,
|
|
|
|
qla2x00_gid_list_size(ha), &ha->gid_list_dma, GFP_KERNEL);
|
2008-11-07 01:40:51 +07:00
|
|
|
if (!ha->gid_list)
|
2012-05-16 01:34:28 +07:00
|
|
|
goto fail_free_tgt_mem;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-02-01 03:33:48 +07:00
|
|
|
ha->srb_mempool = mempool_create_slab_pool(SRB_MIN_REQ, srb_cachep);
|
|
|
|
if (!ha->srb_mempool)
|
2008-11-07 01:40:51 +07:00
|
|
|
goto fail_free_gid_list;
|
2008-02-01 03:33:48 +07:00
|
|
|
|
2010-04-13 07:59:55 +07:00
|
|
|
if (IS_QLA82XX(ha)) {
|
|
|
|
/* Allocate cache for CT6 Ctx. */
|
|
|
|
if (!ctx_cachep) {
|
|
|
|
ctx_cachep = kmem_cache_create("qla2xxx_ctx",
|
|
|
|
sizeof(struct ct6_dsd), 0,
|
|
|
|
SLAB_HWCACHE_ALIGN, NULL);
|
|
|
|
if (!ctx_cachep)
|
|
|
|
goto fail_free_gid_list;
|
|
|
|
}
|
|
|
|
ha->ctx_mempool = mempool_create_slab_pool(SRB_MIN_REQ,
|
|
|
|
ctx_cachep);
|
|
|
|
if (!ha->ctx_mempool)
|
|
|
|
goto fail_free_srb_mempool;
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0021,
|
|
|
|
"ctx_cachep=%p ctx_mempool=%p.\n",
|
|
|
|
ctx_cachep, ha->ctx_mempool);
|
2010-04-13 07:59:55 +07:00
|
|
|
}
|
|
|
|
|
2008-02-01 03:33:48 +07:00
|
|
|
/* Get memory for cached NVRAM */
|
|
|
|
ha->nvram = kzalloc(MAX_NVRAM_SIZE, GFP_KERNEL);
|
|
|
|
if (!ha->nvram)
|
2010-04-13 07:59:55 +07:00
|
|
|
goto fail_free_ctx_mempool;
|
2008-02-01 03:33:48 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
snprintf(name, sizeof(name), "%s_%d", QLA2XXX_DRIVER_NAME,
|
|
|
|
ha->pdev->device);
|
|
|
|
ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev,
|
|
|
|
DMA_POOL_SIZE, 8, 0);
|
|
|
|
if (!ha->s_dma_pool)
|
|
|
|
goto fail_free_nvram;
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0022,
|
|
|
|
"init_cb=%p gid_list=%p, srb_mempool=%p s_dma_pool=%p.\n",
|
|
|
|
ha->init_cb, ha->gid_list, ha->srb_mempool, ha->s_dma_pool);
|
|
|
|
|
2010-05-05 05:01:30 +07:00
|
|
|
if (IS_QLA82XX(ha) || ql2xenabledif) {
|
2010-04-13 07:59:55 +07:00
|
|
|
ha->dl_dma_pool = dma_pool_create(name, &ha->pdev->dev,
|
|
|
|
DSD_LIST_DMA_POOL_SIZE, 8, 0);
|
|
|
|
if (!ha->dl_dma_pool) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log_pci(ql_log_fatal, ha->pdev, 0x0023,
|
|
|
|
"Failed to allocate memory for dl_dma_pool.\n");
|
2010-04-13 07:59:55 +07:00
|
|
|
goto fail_s_dma_pool;
|
|
|
|
}
|
|
|
|
|
|
|
|
ha->fcp_cmnd_dma_pool = dma_pool_create(name, &ha->pdev->dev,
|
|
|
|
FCP_CMND_DMA_POOL_SIZE, 8, 0);
|
|
|
|
if (!ha->fcp_cmnd_dma_pool) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log_pci(ql_log_fatal, ha->pdev, 0x0024,
|
|
|
|
"Failed to allocate memory for fcp_cmnd_dma_pool.\n");
|
2010-04-13 07:59:55 +07:00
|
|
|
goto fail_dl_dma_pool;
|
|
|
|
}
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0025,
|
|
|
|
"dl_dma_pool=%p fcp_cmnd_dma_pool=%p.\n",
|
|
|
|
ha->dl_dma_pool, ha->fcp_cmnd_dma_pool);
|
2010-04-13 07:59:55 +07:00
|
|
|
}
|
|
|
|
|
2008-02-01 03:33:48 +07:00
|
|
|
/* Allocate memory for SNS commands */
|
|
|
|
if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
|
2008-11-07 01:40:51 +07:00
|
|
|
/* Get consistent memory allocated for SNS commands */
|
2008-02-01 03:33:48 +07:00
|
|
|
ha->sns_cmd = dma_alloc_coherent(&ha->pdev->dev,
|
2008-11-07 01:40:51 +07:00
|
|
|
sizeof(struct sns_cmd_pkt), &ha->sns_cmd_dma, GFP_KERNEL);
|
2008-02-01 03:33:48 +07:00
|
|
|
if (!ha->sns_cmd)
|
2008-11-07 01:40:51 +07:00
|
|
|
goto fail_dma_pool;
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0026,
|
2011-11-19 00:03:06 +07:00
|
|
|
"sns_cmd: %p.\n", ha->sns_cmd);
|
2008-02-01 03:33:48 +07:00
|
|
|
} else {
|
2008-11-07 01:40:51 +07:00
|
|
|
/* Get consistent memory allocated for MS IOCB */
|
2008-02-01 03:33:48 +07:00
|
|
|
ha->ms_iocb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
|
2008-11-07 01:40:51 +07:00
|
|
|
&ha->ms_iocb_dma);
|
2008-02-01 03:33:48 +07:00
|
|
|
if (!ha->ms_iocb)
|
2008-11-07 01:40:51 +07:00
|
|
|
goto fail_dma_pool;
|
|
|
|
/* Get consistent memory allocated for CT SNS commands */
|
2008-02-01 03:33:48 +07:00
|
|
|
ha->ct_sns = dma_alloc_coherent(&ha->pdev->dev,
|
2008-11-07 01:40:51 +07:00
|
|
|
sizeof(struct ct_sns_pkt), &ha->ct_sns_dma, GFP_KERNEL);
|
2008-02-01 03:33:48 +07:00
|
|
|
if (!ha->ct_sns)
|
|
|
|
goto fail_free_ms_iocb;
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0027,
|
|
|
|
"ms_iocb=%p ct_sns=%p.\n",
|
|
|
|
ha->ms_iocb, ha->ct_sns);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
/* Allocate memory for request ring */
|
2008-12-10 07:45:39 +07:00
|
|
|
*req = kzalloc(sizeof(struct req_que), GFP_KERNEL);
|
|
|
|
if (!*req) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log_pci(ql_log_fatal, ha->pdev, 0x0028,
|
|
|
|
"Failed to allocate memory for req.\n");
|
2008-11-07 01:40:51 +07:00
|
|
|
goto fail_req;
|
|
|
|
}
|
2008-12-10 07:45:39 +07:00
|
|
|
(*req)->length = req_len;
|
|
|
|
(*req)->ring = dma_alloc_coherent(&ha->pdev->dev,
|
|
|
|
((*req)->length + 1) * sizeof(request_t),
|
|
|
|
&(*req)->dma, GFP_KERNEL);
|
|
|
|
if (!(*req)->ring) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log_pci(ql_log_fatal, ha->pdev, 0x0029,
|
|
|
|
"Failed to allocate memory for req_ring.\n");
|
2008-11-07 01:40:51 +07:00
|
|
|
goto fail_req_ring;
|
|
|
|
}
|
|
|
|
/* Allocate memory for response ring */
|
2008-12-10 07:45:39 +07:00
|
|
|
*rsp = kzalloc(sizeof(struct rsp_que), GFP_KERNEL);
|
|
|
|
if (!*rsp) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log_pci(ql_log_fatal, ha->pdev, 0x002a,
|
|
|
|
"Failed to allocate memory for rsp.\n");
|
2008-11-07 01:40:51 +07:00
|
|
|
goto fail_rsp;
|
|
|
|
}
|
2008-12-10 07:45:39 +07:00
|
|
|
(*rsp)->hw = ha;
|
|
|
|
(*rsp)->length = rsp_len;
|
|
|
|
(*rsp)->ring = dma_alloc_coherent(&ha->pdev->dev,
|
|
|
|
((*rsp)->length + 1) * sizeof(response_t),
|
|
|
|
&(*rsp)->dma, GFP_KERNEL);
|
|
|
|
if (!(*rsp)->ring) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log_pci(ql_log_fatal, ha->pdev, 0x002b,
|
|
|
|
"Failed to allocate memory for rsp_ring.\n");
|
2008-11-07 01:40:51 +07:00
|
|
|
goto fail_rsp_ring;
|
|
|
|
}
|
2008-12-10 07:45:39 +07:00
|
|
|
(*req)->rsp = *rsp;
|
|
|
|
(*rsp)->req = *req;
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002c,
|
|
|
|
"req=%p req->length=%d req->ring=%p rsp=%p "
|
|
|
|
"rsp->length=%d rsp->ring=%p.\n",
|
|
|
|
*req, (*req)->length, (*req)->ring, *rsp, (*rsp)->length,
|
|
|
|
(*rsp)->ring);
|
2008-12-10 07:45:39 +07:00
|
|
|
/* Allocate memory for NVRAM data for vports */
|
|
|
|
if (ha->nvram_npiv_size) {
|
|
|
|
ha->npiv_info = kzalloc(sizeof(struct qla_npiv_entry) *
|
2011-07-15 02:00:13 +07:00
|
|
|
ha->nvram_npiv_size, GFP_KERNEL);
|
2008-12-10 07:45:39 +07:00
|
|
|
if (!ha->npiv_info) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log_pci(ql_log_fatal, ha->pdev, 0x002d,
|
|
|
|
"Failed to allocate memory for npiv_info.\n");
|
2008-12-10 07:45:39 +07:00
|
|
|
goto fail_npiv_info;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
ha->npiv_info = NULL;
|
2008-02-01 03:33:48 +07:00
|
|
|
|
2009-03-24 23:08:01 +07:00
|
|
|
/* Get consistent memory allocated for EX-INIT-CB. */
|
2012-02-10 02:15:34 +07:00
|
|
|
if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha)) {
|
2009-03-24 23:08:01 +07:00
|
|
|
ha->ex_init_cb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
|
|
|
|
&ha->ex_init_cb_dma);
|
|
|
|
if (!ha->ex_init_cb)
|
|
|
|
goto fail_ex_init_cb;
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002e,
|
|
|
|
"ex_init_cb=%p.\n", ha->ex_init_cb);
|
2009-03-24 23:08:01 +07:00
|
|
|
}
|
|
|
|
|
2010-04-13 07:59:55 +07:00
|
|
|
INIT_LIST_HEAD(&ha->gbl_dsd_list);
|
|
|
|
|
2010-05-05 05:01:26 +07:00
|
|
|
/* Get consistent memory allocated for Async Port-Database. */
|
|
|
|
if (!IS_FWI2_CAPABLE(ha)) {
|
|
|
|
ha->async_pd = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
|
|
|
|
&ha->async_pd_dma);
|
|
|
|
if (!ha->async_pd)
|
|
|
|
goto fail_async_pd;
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002f,
|
|
|
|
"async_pd=%p.\n", ha->async_pd);
|
2010-05-05 05:01:26 +07:00
|
|
|
}
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
INIT_LIST_HEAD(&ha->vp_list);
|
2012-08-23 01:21:00 +07:00
|
|
|
|
|
|
|
/* Allocate memory for our loop_id bitmap */
|
|
|
|
ha->loop_id_map = kzalloc(BITS_TO_LONGS(LOOPID_MAP_SIZE) * sizeof(long),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!ha->loop_id_map)
|
|
|
|
goto fail_async_pd;
|
|
|
|
else {
|
|
|
|
qla2x00_set_reserved_loop_ids(ha);
|
|
|
|
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0123,
|
|
|
|
"loop_id_map=%p. \n", ha->loop_id_map);
|
|
|
|
}
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
return 1;
|
|
|
|
|
2010-05-05 05:01:26 +07:00
|
|
|
fail_async_pd:
|
|
|
|
dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma);
|
2009-03-24 23:08:01 +07:00
|
|
|
fail_ex_init_cb:
|
|
|
|
kfree(ha->npiv_info);
|
2008-12-10 07:45:39 +07:00
|
|
|
fail_npiv_info:
|
|
|
|
dma_free_coherent(&ha->pdev->dev, ((*rsp)->length + 1) *
|
|
|
|
sizeof(response_t), (*rsp)->ring, (*rsp)->dma);
|
|
|
|
(*rsp)->ring = NULL;
|
|
|
|
(*rsp)->dma = 0;
|
2008-11-07 01:40:51 +07:00
|
|
|
fail_rsp_ring:
|
2008-12-10 07:45:39 +07:00
|
|
|
kfree(*rsp);
|
2008-11-07 01:40:51 +07:00
|
|
|
fail_rsp:
|
2008-12-10 07:45:39 +07:00
|
|
|
dma_free_coherent(&ha->pdev->dev, ((*req)->length + 1) *
|
|
|
|
sizeof(request_t), (*req)->ring, (*req)->dma);
|
|
|
|
(*req)->ring = NULL;
|
|
|
|
(*req)->dma = 0;
|
2008-11-07 01:40:51 +07:00
|
|
|
fail_req_ring:
|
2008-12-10 07:45:39 +07:00
|
|
|
kfree(*req);
|
2008-11-07 01:40:51 +07:00
|
|
|
fail_req:
|
|
|
|
dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt),
|
|
|
|
ha->ct_sns, ha->ct_sns_dma);
|
|
|
|
ha->ct_sns = NULL;
|
|
|
|
ha->ct_sns_dma = 0;
|
2008-02-01 03:33:48 +07:00
|
|
|
fail_free_ms_iocb:
|
|
|
|
dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
|
|
|
|
ha->ms_iocb = NULL;
|
|
|
|
ha->ms_iocb_dma = 0;
|
2008-11-07 01:40:51 +07:00
|
|
|
fail_dma_pool:
|
2010-05-05 05:01:30 +07:00
|
|
|
if (IS_QLA82XX(ha) || ql2xenabledif) {
|
2010-04-13 07:59:55 +07:00
|
|
|
dma_pool_destroy(ha->fcp_cmnd_dma_pool);
|
|
|
|
ha->fcp_cmnd_dma_pool = NULL;
|
|
|
|
}
|
|
|
|
fail_dl_dma_pool:
|
2010-05-05 05:01:30 +07:00
|
|
|
if (IS_QLA82XX(ha) || ql2xenabledif) {
|
2010-04-13 07:59:55 +07:00
|
|
|
dma_pool_destroy(ha->dl_dma_pool);
|
|
|
|
ha->dl_dma_pool = NULL;
|
|
|
|
}
|
|
|
|
fail_s_dma_pool:
|
2008-11-07 01:40:51 +07:00
|
|
|
dma_pool_destroy(ha->s_dma_pool);
|
|
|
|
ha->s_dma_pool = NULL;
|
2008-02-01 03:33:48 +07:00
|
|
|
fail_free_nvram:
|
|
|
|
kfree(ha->nvram);
|
|
|
|
ha->nvram = NULL;
|
2010-04-13 07:59:55 +07:00
|
|
|
fail_free_ctx_mempool:
|
|
|
|
mempool_destroy(ha->ctx_mempool);
|
|
|
|
ha->ctx_mempool = NULL;
|
2008-02-01 03:33:48 +07:00
|
|
|
fail_free_srb_mempool:
|
|
|
|
mempool_destroy(ha->srb_mempool);
|
|
|
|
ha->srb_mempool = NULL;
|
|
|
|
fail_free_gid_list:
|
2012-02-10 02:15:57 +07:00
|
|
|
dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
|
|
|
|
ha->gid_list,
|
2008-11-07 01:40:51 +07:00
|
|
|
ha->gid_list_dma);
|
2008-02-01 03:33:48 +07:00
|
|
|
ha->gid_list = NULL;
|
|
|
|
ha->gid_list_dma = 0;
|
2012-05-16 01:34:28 +07:00
|
|
|
fail_free_tgt_mem:
|
|
|
|
qlt_mem_free(ha);
|
2008-11-07 01:40:51 +07:00
|
|
|
fail_free_init_cb:
|
|
|
|
dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, ha->init_cb,
|
|
|
|
ha->init_cb_dma);
|
|
|
|
ha->init_cb = NULL;
|
|
|
|
ha->init_cb_dma = 0;
|
2008-02-01 03:33:48 +07:00
|
|
|
fail:
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_fatal, NULL, 0x0030,
|
|
|
|
"Memory allocation failure.\n");
|
2008-02-01 03:33:48 +07:00
|
|
|
return -ENOMEM;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-10-16 01:27:46 +07:00
|
|
|
* qla2x00_free_fw_dump
|
|
|
|
* Frees fw dump stuff.
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
|
|
|
* Input:
|
2010-10-16 01:27:46 +07:00
|
|
|
* ha = adapter block pointer.
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2008-01-18 00:02:15 +07:00
|
|
|
static void
|
2010-10-16 01:27:46 +07:00
|
|
|
qla2x00_free_fw_dump(struct qla_hw_data *ha)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-01-18 00:02:17 +07:00
|
|
|
if (ha->fce)
|
|
|
|
dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce,
|
2010-10-16 01:27:46 +07:00
|
|
|
ha->fce_dma);
|
2008-01-18 00:02:17 +07:00
|
|
|
|
2006-06-24 06:10:29 +07:00
|
|
|
if (ha->fw_dump) {
|
|
|
|
if (ha->eft)
|
|
|
|
dma_free_coherent(&ha->pdev->dev,
|
2010-10-16 01:27:46 +07:00
|
|
|
ntohl(ha->fw_dump->eft_size), ha->eft, ha->eft_dma);
|
2006-06-24 06:10:29 +07:00
|
|
|
vfree(ha->fw_dump);
|
|
|
|
}
|
2010-10-16 01:27:46 +07:00
|
|
|
ha->fce = NULL;
|
|
|
|
ha->fce_dma = 0;
|
|
|
|
ha->eft = NULL;
|
|
|
|
ha->eft_dma = 0;
|
|
|
|
ha->fw_dump = NULL;
|
|
|
|
ha->fw_dumped = 0;
|
|
|
|
ha->fw_dump_reading = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qla2x00_mem_free
|
|
|
|
* Frees all adapter allocated memory.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* ha = adapter block pointer.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
qla2x00_mem_free(struct qla_hw_data *ha)
|
|
|
|
{
|
|
|
|
qla2x00_free_fw_dump(ha);
|
|
|
|
|
2012-08-23 01:21:04 +07:00
|
|
|
if (ha->mctp_dump)
|
|
|
|
dma_free_coherent(&ha->pdev->dev, MCTP_DUMP_SIZE, ha->mctp_dump,
|
|
|
|
ha->mctp_dump_dma);
|
|
|
|
|
2010-10-16 01:27:46 +07:00
|
|
|
if (ha->srb_mempool)
|
|
|
|
mempool_destroy(ha->srb_mempool);
|
2006-06-24 06:10:29 +07:00
|
|
|
|
2009-06-03 23:55:14 +07:00
|
|
|
if (ha->dcbx_tlv)
|
|
|
|
dma_free_coherent(&ha->pdev->dev, DCBX_TLV_DATA_SIZE,
|
|
|
|
ha->dcbx_tlv, ha->dcbx_tlv_dma);
|
|
|
|
|
2009-06-03 23:55:13 +07:00
|
|
|
if (ha->xgmac_data)
|
|
|
|
dma_free_coherent(&ha->pdev->dev, XGMAC_DATA_SIZE,
|
|
|
|
ha->xgmac_data, ha->xgmac_data_dma);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (ha->sns_cmd)
|
|
|
|
dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt),
|
2008-11-07 01:40:51 +07:00
|
|
|
ha->sns_cmd, ha->sns_cmd_dma);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (ha->ct_sns)
|
|
|
|
dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt),
|
2008-11-07 01:40:51 +07:00
|
|
|
ha->ct_sns, ha->ct_sns_dma);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-24 06:10:50 +07:00
|
|
|
if (ha->sfp_data)
|
|
|
|
dma_pool_free(ha->s_dma_pool, ha->sfp_data, ha->sfp_data_dma);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (ha->ms_iocb)
|
|
|
|
dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
|
|
|
|
|
2009-03-24 23:08:01 +07:00
|
|
|
if (ha->ex_init_cb)
|
2010-04-13 07:59:55 +07:00
|
|
|
dma_pool_free(ha->s_dma_pool,
|
|
|
|
ha->ex_init_cb, ha->ex_init_cb_dma);
|
2009-03-24 23:08:01 +07:00
|
|
|
|
2010-05-05 05:01:26 +07:00
|
|
|
if (ha->async_pd)
|
|
|
|
dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (ha->s_dma_pool)
|
|
|
|
dma_pool_destroy(ha->s_dma_pool);
|
|
|
|
|
|
|
|
if (ha->gid_list)
|
2012-02-10 02:15:57 +07:00
|
|
|
dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
|
|
|
|
ha->gid_list, ha->gid_list_dma);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-04-13 07:59:55 +07:00
|
|
|
if (IS_QLA82XX(ha)) {
|
|
|
|
if (!list_empty(&ha->gbl_dsd_list)) {
|
|
|
|
struct dsd_dma *dsd_ptr, *tdsd_ptr;
|
|
|
|
|
|
|
|
/* clean up allocated prev pool */
|
|
|
|
list_for_each_entry_safe(dsd_ptr,
|
|
|
|
tdsd_ptr, &ha->gbl_dsd_list, list) {
|
|
|
|
dma_pool_free(ha->dl_dma_pool,
|
|
|
|
dsd_ptr->dsd_addr, dsd_ptr->dsd_list_dma);
|
|
|
|
list_del(&dsd_ptr->list);
|
|
|
|
kfree(dsd_ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ha->dl_dma_pool)
|
|
|
|
dma_pool_destroy(ha->dl_dma_pool);
|
|
|
|
|
|
|
|
if (ha->fcp_cmnd_dma_pool)
|
|
|
|
dma_pool_destroy(ha->fcp_cmnd_dma_pool);
|
|
|
|
|
|
|
|
if (ha->ctx_mempool)
|
|
|
|
mempool_destroy(ha->ctx_mempool);
|
|
|
|
|
2012-05-16 01:34:28 +07:00
|
|
|
qlt_mem_free(ha);
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
if (ha->init_cb)
|
|
|
|
dma_free_coherent(&ha->pdev->dev, ha->init_cb_size,
|
2010-04-13 07:59:55 +07:00
|
|
|
ha->init_cb, ha->init_cb_dma);
|
2008-11-07 01:40:51 +07:00
|
|
|
vfree(ha->optrom_buffer);
|
|
|
|
kfree(ha->nvram);
|
2008-12-10 07:45:39 +07:00
|
|
|
kfree(ha->npiv_info);
|
2012-02-10 02:15:56 +07:00
|
|
|
kfree(ha->swl);
|
2012-08-23 01:21:00 +07:00
|
|
|
kfree(ha->loop_id_map);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-02-01 03:33:48 +07:00
|
|
|
ha->srb_mempool = NULL;
|
2010-04-13 07:59:55 +07:00
|
|
|
ha->ctx_mempool = NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
ha->sns_cmd = NULL;
|
|
|
|
ha->sns_cmd_dma = 0;
|
|
|
|
ha->ct_sns = NULL;
|
|
|
|
ha->ct_sns_dma = 0;
|
|
|
|
ha->ms_iocb = NULL;
|
|
|
|
ha->ms_iocb_dma = 0;
|
|
|
|
ha->init_cb = NULL;
|
|
|
|
ha->init_cb_dma = 0;
|
2009-03-24 23:08:01 +07:00
|
|
|
ha->ex_init_cb = NULL;
|
|
|
|
ha->ex_init_cb_dma = 0;
|
2010-05-05 05:01:26 +07:00
|
|
|
ha->async_pd = NULL;
|
|
|
|
ha->async_pd_dma = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
ha->s_dma_pool = NULL;
|
2010-04-13 07:59:55 +07:00
|
|
|
ha->dl_dma_pool = NULL;
|
|
|
|
ha->fcp_cmnd_dma_pool = NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
ha->gid_list = NULL;
|
|
|
|
ha->gid_list_dma = 0;
|
2012-05-16 01:34:28 +07:00
|
|
|
|
|
|
|
ha->tgt.atio_ring = NULL;
|
|
|
|
ha->tgt.atio_dma = 0;
|
|
|
|
ha->tgt.tgt_vp_map = NULL;
|
2008-11-07 01:40:51 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht,
|
|
|
|
struct qla_hw_data *ha)
|
|
|
|
{
|
|
|
|
struct Scsi_Host *host;
|
|
|
|
struct scsi_qla_host *vha = NULL;
|
2006-02-01 07:05:17 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
host = scsi_host_alloc(sht, sizeof(scsi_qla_host_t));
|
|
|
|
if (host == NULL) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log_pci(ql_log_fatal, ha->pdev, 0x0107,
|
|
|
|
"Failed to allocate host from the scsi layer, aborting.\n");
|
2008-11-07 01:40:51 +07:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear our data area */
|
|
|
|
vha = shost_priv(host);
|
|
|
|
memset(vha, 0, sizeof(scsi_qla_host_t));
|
|
|
|
|
|
|
|
vha->host = host;
|
|
|
|
vha->host_no = host->host_no;
|
|
|
|
vha->hw = ha;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&vha->vp_fcports);
|
|
|
|
INIT_LIST_HEAD(&vha->work_list);
|
|
|
|
INIT_LIST_HEAD(&vha->list);
|
|
|
|
|
2009-06-03 23:55:28 +07:00
|
|
|
spin_lock_init(&vha->work_lock);
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no);
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_init, vha, 0x0041,
|
|
|
|
"Allocated the host=%p hw=%p vha=%p dev_name=%s",
|
|
|
|
vha->host, vha->hw, vha,
|
|
|
|
dev_name(&(ha->pdev->dev)));
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
return vha;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
return vha;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-04-25 05:21:27 +07:00
|
|
|
static struct qla_work_evt *
|
2009-06-03 23:55:28 +07:00
|
|
|
qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type)
|
2008-04-04 03:13:18 +07:00
|
|
|
{
|
|
|
|
struct qla_work_evt *e;
|
2010-09-04 04:57:00 +07:00
|
|
|
uint8_t bail;
|
|
|
|
|
|
|
|
QLA_VHA_MARK_BUSY(vha, bail);
|
|
|
|
if (bail)
|
|
|
|
return NULL;
|
2008-04-04 03:13:18 +07:00
|
|
|
|
2009-06-03 23:55:28 +07:00
|
|
|
e = kzalloc(sizeof(struct qla_work_evt), GFP_ATOMIC);
|
2010-09-04 04:57:00 +07:00
|
|
|
if (!e) {
|
|
|
|
QLA_VHA_MARK_NOT_BUSY(vha);
|
2008-04-04 03:13:18 +07:00
|
|
|
return NULL;
|
2010-09-04 04:57:00 +07:00
|
|
|
}
|
2008-04-04 03:13:18 +07:00
|
|
|
|
|
|
|
INIT_LIST_HEAD(&e->list);
|
|
|
|
e->type = type;
|
|
|
|
e->flags = QLA_EVT_FLAG_FREE;
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2008-04-25 05:21:27 +07:00
|
|
|
static int
|
2009-06-03 23:55:28 +07:00
|
|
|
qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e)
|
2008-04-04 03:13:18 +07:00
|
|
|
{
|
2009-06-03 23:55:28 +07:00
|
|
|
unsigned long flags;
|
2008-04-04 03:13:18 +07:00
|
|
|
|
2009-06-03 23:55:28 +07:00
|
|
|
spin_lock_irqsave(&vha->work_lock, flags);
|
2008-11-07 01:40:51 +07:00
|
|
|
list_add_tail(&e->list, &vha->work_list);
|
2009-06-03 23:55:28 +07:00
|
|
|
spin_unlock_irqrestore(&vha->work_lock, flags);
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2xxx_wake_dpc(vha);
|
2009-06-03 23:55:28 +07:00
|
|
|
|
2008-04-04 03:13:18 +07:00
|
|
|
return QLA_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_post_aen_work(struct scsi_qla_host *vha, enum fc_host_event_code code,
|
2008-04-04 03:13:18 +07:00
|
|
|
u32 data)
|
|
|
|
{
|
|
|
|
struct qla_work_evt *e;
|
|
|
|
|
2009-06-03 23:55:28 +07:00
|
|
|
e = qla2x00_alloc_work(vha, QLA_EVT_AEN);
|
2008-04-04 03:13:18 +07:00
|
|
|
if (!e)
|
|
|
|
return QLA_FUNCTION_FAILED;
|
|
|
|
|
|
|
|
e->u.aen.code = code;
|
|
|
|
e->u.aen.data = data;
|
2009-06-03 23:55:28 +07:00
|
|
|
return qla2x00_post_work(vha, e);
|
2008-04-04 03:13:18 +07:00
|
|
|
}
|
|
|
|
|
2009-02-09 11:50:12 +07:00
|
|
|
int
|
|
|
|
qla2x00_post_idc_ack_work(struct scsi_qla_host *vha, uint16_t *mb)
|
|
|
|
{
|
|
|
|
struct qla_work_evt *e;
|
|
|
|
|
2009-06-03 23:55:28 +07:00
|
|
|
e = qla2x00_alloc_work(vha, QLA_EVT_IDC_ACK);
|
2009-02-09 11:50:12 +07:00
|
|
|
if (!e)
|
|
|
|
return QLA_FUNCTION_FAILED;
|
|
|
|
|
|
|
|
memcpy(e->u.idc_ack.mb, mb, QLA_IDC_ACK_REGS * sizeof(uint16_t));
|
2009-06-03 23:55:28 +07:00
|
|
|
return qla2x00_post_work(vha, e);
|
2009-02-09 11:50:12 +07:00
|
|
|
}
|
|
|
|
|
2009-08-21 01:06:05 +07:00
|
|
|
#define qla2x00_post_async_work(name, type) \
|
|
|
|
int qla2x00_post_async_##name##_work( \
|
|
|
|
struct scsi_qla_host *vha, \
|
|
|
|
fc_port_t *fcport, uint16_t *data) \
|
|
|
|
{ \
|
|
|
|
struct qla_work_evt *e; \
|
|
|
|
\
|
|
|
|
e = qla2x00_alloc_work(vha, type); \
|
|
|
|
if (!e) \
|
|
|
|
return QLA_FUNCTION_FAILED; \
|
|
|
|
\
|
|
|
|
e->u.logio.fcport = fcport; \
|
|
|
|
if (data) { \
|
|
|
|
e->u.logio.data[0] = data[0]; \
|
|
|
|
e->u.logio.data[1] = data[1]; \
|
|
|
|
} \
|
|
|
|
return qla2x00_post_work(vha, e); \
|
|
|
|
}
|
|
|
|
|
|
|
|
qla2x00_post_async_work(login, QLA_EVT_ASYNC_LOGIN);
|
|
|
|
qla2x00_post_async_work(login_done, QLA_EVT_ASYNC_LOGIN_DONE);
|
|
|
|
qla2x00_post_async_work(logout, QLA_EVT_ASYNC_LOGOUT);
|
|
|
|
qla2x00_post_async_work(logout_done, QLA_EVT_ASYNC_LOGOUT_DONE);
|
2010-05-05 05:01:26 +07:00
|
|
|
qla2x00_post_async_work(adisc, QLA_EVT_ASYNC_ADISC);
|
|
|
|
qla2x00_post_async_work(adisc_done, QLA_EVT_ASYNC_ADISC_DONE);
|
2009-08-21 01:06:05 +07:00
|
|
|
|
2009-10-14 05:16:45 +07:00
|
|
|
int
|
|
|
|
qla2x00_post_uevent_work(struct scsi_qla_host *vha, u32 code)
|
|
|
|
{
|
|
|
|
struct qla_work_evt *e;
|
|
|
|
|
|
|
|
e = qla2x00_alloc_work(vha, QLA_EVT_UEVENT);
|
|
|
|
if (!e)
|
|
|
|
return QLA_FUNCTION_FAILED;
|
|
|
|
|
|
|
|
e->u.uevent.code = code;
|
|
|
|
return qla2x00_post_work(vha, e);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
qla2x00_uevent_emit(struct scsi_qla_host *vha, u32 code)
|
|
|
|
{
|
|
|
|
char event_string[40];
|
|
|
|
char *envp[] = { event_string, NULL };
|
|
|
|
|
|
|
|
switch (code) {
|
|
|
|
case QLA_UEVENT_CODE_FW_DUMP:
|
|
|
|
snprintf(event_string, sizeof(event_string), "FW_DUMP=%ld",
|
|
|
|
vha->host_no);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* do nothing */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
kobject_uevent_env(&vha->hw->pdev->dev.kobj, KOBJ_CHANGE, envp);
|
|
|
|
}
|
|
|
|
|
2009-08-21 01:06:05 +07:00
|
|
|
void
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_do_work(struct scsi_qla_host *vha)
|
2008-04-04 03:13:18 +07:00
|
|
|
{
|
2009-06-03 23:55:28 +07:00
|
|
|
struct qla_work_evt *e, *tmp;
|
|
|
|
unsigned long flags;
|
|
|
|
LIST_HEAD(work);
|
2008-04-04 03:13:18 +07:00
|
|
|
|
2009-06-03 23:55:28 +07:00
|
|
|
spin_lock_irqsave(&vha->work_lock, flags);
|
|
|
|
list_splice_init(&vha->work_list, &work);
|
|
|
|
spin_unlock_irqrestore(&vha->work_lock, flags);
|
|
|
|
|
|
|
|
list_for_each_entry_safe(e, tmp, &work, list) {
|
2008-04-04 03:13:18 +07:00
|
|
|
list_del_init(&e->list);
|
|
|
|
|
|
|
|
switch (e->type) {
|
|
|
|
case QLA_EVT_AEN:
|
2008-11-07 01:40:51 +07:00
|
|
|
fc_host_post_event(vha->host, fc_get_event_number(),
|
2008-04-04 03:13:18 +07:00
|
|
|
e->u.aen.code, e->u.aen.data);
|
|
|
|
break;
|
2009-02-09 11:50:12 +07:00
|
|
|
case QLA_EVT_IDC_ACK:
|
|
|
|
qla81xx_idc_ack(vha, e->u.idc_ack.mb);
|
|
|
|
break;
|
2009-08-21 01:06:05 +07:00
|
|
|
case QLA_EVT_ASYNC_LOGIN:
|
|
|
|
qla2x00_async_login(vha, e->u.logio.fcport,
|
|
|
|
e->u.logio.data);
|
|
|
|
break;
|
|
|
|
case QLA_EVT_ASYNC_LOGIN_DONE:
|
|
|
|
qla2x00_async_login_done(vha, e->u.logio.fcport,
|
|
|
|
e->u.logio.data);
|
|
|
|
break;
|
|
|
|
case QLA_EVT_ASYNC_LOGOUT:
|
|
|
|
qla2x00_async_logout(vha, e->u.logio.fcport);
|
|
|
|
break;
|
|
|
|
case QLA_EVT_ASYNC_LOGOUT_DONE:
|
|
|
|
qla2x00_async_logout_done(vha, e->u.logio.fcport,
|
|
|
|
e->u.logio.data);
|
|
|
|
break;
|
2010-05-05 05:01:26 +07:00
|
|
|
case QLA_EVT_ASYNC_ADISC:
|
|
|
|
qla2x00_async_adisc(vha, e->u.logio.fcport,
|
|
|
|
e->u.logio.data);
|
|
|
|
break;
|
|
|
|
case QLA_EVT_ASYNC_ADISC_DONE:
|
|
|
|
qla2x00_async_adisc_done(vha, e->u.logio.fcport,
|
|
|
|
e->u.logio.data);
|
|
|
|
break;
|
2009-10-14 05:16:45 +07:00
|
|
|
case QLA_EVT_UEVENT:
|
|
|
|
qla2x00_uevent_emit(vha, e->u.uevent.code);
|
|
|
|
break;
|
2008-04-04 03:13:18 +07:00
|
|
|
}
|
|
|
|
if (e->flags & QLA_EVT_FLAG_FREE)
|
|
|
|
kfree(e);
|
2010-09-04 04:57:00 +07:00
|
|
|
|
|
|
|
/* For each work completed decrement vha ref count */
|
|
|
|
QLA_VHA_MARK_NOT_BUSY(vha);
|
2008-11-07 01:40:51 +07:00
|
|
|
}
|
|
|
|
}
|
2009-06-03 23:55:28 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
/* Relogins all the fcports of a vport
|
|
|
|
* Context: dpc thread
|
|
|
|
*/
|
|
|
|
void qla2x00_relogin(struct scsi_qla_host *vha)
|
|
|
|
{
|
|
|
|
fc_port_t *fcport;
|
2009-03-06 02:07:03 +07:00
|
|
|
int status;
|
2008-11-07 01:40:51 +07:00
|
|
|
uint16_t next_loopid = 0;
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2009-08-21 01:06:05 +07:00
|
|
|
uint16_t data[2];
|
2008-11-07 01:40:51 +07:00
|
|
|
|
|
|
|
list_for_each_entry(fcport, &vha->vp_fcports, list) {
|
|
|
|
/*
|
|
|
|
* If the port is not ONLINE then try to login
|
|
|
|
* to it if we haven't run out of retries.
|
|
|
|
*/
|
2010-05-05 05:01:26 +07:00
|
|
|
if (atomic_read(&fcport->state) != FCS_ONLINE &&
|
|
|
|
fcport->login_retry && !(fcport->flags & FCF_ASYNC_SENT)) {
|
2009-08-21 01:06:05 +07:00
|
|
|
fcport->login_retry--;
|
2008-11-07 01:40:51 +07:00
|
|
|
if (fcport->flags & FCF_FABRIC_DEVICE) {
|
2010-01-13 03:59:48 +07:00
|
|
|
if (fcport->flags & FCF_FCP2_DEVICE)
|
2008-11-07 01:40:51 +07:00
|
|
|
ha->isp_ops->fabric_logout(vha,
|
|
|
|
fcport->loop_id,
|
|
|
|
fcport->d_id.b.domain,
|
|
|
|
fcport->d_id.b.area,
|
|
|
|
fcport->d_id.b.al_pa);
|
|
|
|
|
2011-03-31 01:46:27 +07:00
|
|
|
if (fcport->loop_id == FC_NO_LOOP_ID) {
|
|
|
|
fcport->loop_id = next_loopid =
|
|
|
|
ha->min_external_loopid;
|
|
|
|
status = qla2x00_find_new_loop_id(
|
|
|
|
vha, fcport);
|
|
|
|
if (status != QLA_SUCCESS) {
|
|
|
|
/* Ran out of IDs to use */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-21 01:06:05 +07:00
|
|
|
if (IS_ALOGIO_CAPABLE(ha)) {
|
2010-05-05 05:01:26 +07:00
|
|
|
fcport->flags |= FCF_ASYNC_SENT;
|
2009-08-21 01:06:05 +07:00
|
|
|
data[0] = 0;
|
|
|
|
data[1] = QLA_LOGIO_LOGIN_RETRIED;
|
|
|
|
status = qla2x00_post_async_login_work(
|
|
|
|
vha, fcport, data);
|
|
|
|
if (status == QLA_SUCCESS)
|
|
|
|
continue;
|
|
|
|
/* Attempt a retry. */
|
|
|
|
status = 1;
|
2012-04-25 21:26:16 +07:00
|
|
|
} else {
|
2009-08-21 01:06:05 +07:00
|
|
|
status = qla2x00_fabric_login(vha,
|
|
|
|
fcport, &next_loopid);
|
2012-04-25 21:26:16 +07:00
|
|
|
if (status == QLA_SUCCESS) {
|
|
|
|
int status2;
|
|
|
|
uint8_t opts;
|
|
|
|
|
|
|
|
opts = 0;
|
|
|
|
if (fcport->flags &
|
|
|
|
FCF_FCP2_DEVICE)
|
|
|
|
opts |= BIT_1;
|
2012-11-21 14:40:31 +07:00
|
|
|
status2 =
|
|
|
|
qla2x00_get_port_database(
|
|
|
|
vha, fcport, opts);
|
2012-04-25 21:26:16 +07:00
|
|
|
if (status2 != QLA_SUCCESS)
|
|
|
|
status = 1;
|
|
|
|
}
|
|
|
|
}
|
2008-11-07 01:40:51 +07:00
|
|
|
} else
|
|
|
|
status = qla2x00_local_device_login(vha,
|
|
|
|
fcport);
|
|
|
|
|
|
|
|
if (status == QLA_SUCCESS) {
|
|
|
|
fcport->old_loop_id = fcport->loop_id;
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_disc, vha, 0x2003,
|
|
|
|
"Port login OK: logged in ID 0x%x.\n",
|
|
|
|
fcport->loop_id);
|
2008-11-07 01:40:51 +07:00
|
|
|
|
|
|
|
qla2x00_update_fcport(vha, fcport);
|
|
|
|
|
|
|
|
} else if (status == 1) {
|
|
|
|
set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
|
|
|
|
/* retry the login again */
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_disc, vha, 0x2007,
|
|
|
|
"Retrying %d login again loop_id 0x%x.\n",
|
|
|
|
fcport->login_retry, fcport->loop_id);
|
2008-11-07 01:40:51 +07:00
|
|
|
} else {
|
|
|
|
fcport->login_retry = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fcport->login_retry == 0 && status != QLA_SUCCESS)
|
2012-08-23 01:21:00 +07:00
|
|
|
qla2x00_clear_loop_id(fcport);
|
2008-11-07 01:40:51 +07:00
|
|
|
}
|
|
|
|
if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
|
|
|
|
break;
|
2008-04-04 03:13:18 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-23 01:21:03 +07:00
|
|
|
/* Schedule work on any of the dpc-workqueues */
|
|
|
|
void
|
|
|
|
qla83xx_schedule_work(scsi_qla_host_t *base_vha, int work_code)
|
|
|
|
{
|
|
|
|
struct qla_hw_data *ha = base_vha->hw;
|
|
|
|
|
|
|
|
switch (work_code) {
|
|
|
|
case MBA_IDC_AEN: /* 0x8200 */
|
|
|
|
if (ha->dpc_lp_wq)
|
|
|
|
queue_work(ha->dpc_lp_wq, &ha->idc_aen);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case QLA83XX_NIC_CORE_RESET: /* 0x1 */
|
|
|
|
if (!ha->flags.nic_core_reset_hdlr_active) {
|
|
|
|
if (ha->dpc_hp_wq)
|
|
|
|
queue_work(ha->dpc_hp_wq, &ha->nic_core_reset);
|
|
|
|
} else
|
|
|
|
ql_dbg(ql_dbg_p3p, base_vha, 0xb05e,
|
|
|
|
"NIC Core reset is already active. Skip "
|
|
|
|
"scheduling it again.\n");
|
|
|
|
break;
|
|
|
|
case QLA83XX_IDC_STATE_HANDLER: /* 0x2 */
|
|
|
|
if (ha->dpc_hp_wq)
|
|
|
|
queue_work(ha->dpc_hp_wq, &ha->idc_state_handler);
|
|
|
|
break;
|
|
|
|
case QLA83XX_NIC_CORE_UNRECOVERABLE: /* 0x3 */
|
|
|
|
if (ha->dpc_hp_wq)
|
|
|
|
queue_work(ha->dpc_hp_wq, &ha->nic_core_unrecoverable);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ql_log(ql_log_warn, base_vha, 0xb05f,
|
|
|
|
"Unknow work-code=0x%x.\n", work_code);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Work: Perform NIC Core Unrecoverable state handling */
|
|
|
|
void
|
|
|
|
qla83xx_nic_core_unrecoverable_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct qla_hw_data *ha =
|
2012-08-23 01:21:35 +07:00
|
|
|
container_of(work, struct qla_hw_data, nic_core_unrecoverable);
|
2012-08-23 01:21:03 +07:00
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
|
|
|
|
uint32_t dev_state = 0;
|
|
|
|
|
|
|
|
qla83xx_idc_lock(base_vha, 0);
|
|
|
|
qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state);
|
|
|
|
qla83xx_reset_ownership(base_vha);
|
|
|
|
if (ha->flags.nic_core_reset_owner) {
|
|
|
|
ha->flags.nic_core_reset_owner = 0;
|
|
|
|
qla83xx_wr_reg(base_vha, QLA83XX_IDC_DEV_STATE,
|
|
|
|
QLA8XXX_DEV_FAILED);
|
|
|
|
ql_log(ql_log_info, base_vha, 0xb060, "HW State: FAILED.\n");
|
|
|
|
qla83xx_schedule_work(base_vha, QLA83XX_IDC_STATE_HANDLER);
|
|
|
|
}
|
|
|
|
qla83xx_idc_unlock(base_vha, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Work: Execute IDC state handler */
|
|
|
|
void
|
|
|
|
qla83xx_idc_state_handler_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct qla_hw_data *ha =
|
2012-08-23 01:21:35 +07:00
|
|
|
container_of(work, struct qla_hw_data, idc_state_handler);
|
2012-08-23 01:21:03 +07:00
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
|
|
|
|
uint32_t dev_state = 0;
|
|
|
|
|
|
|
|
qla83xx_idc_lock(base_vha, 0);
|
|
|
|
qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state);
|
|
|
|
if (dev_state == QLA8XXX_DEV_FAILED ||
|
|
|
|
dev_state == QLA8XXX_DEV_NEED_QUIESCENT)
|
|
|
|
qla83xx_idc_state_handler(base_vha);
|
|
|
|
qla83xx_idc_unlock(base_vha, 0);
|
|
|
|
}
|
|
|
|
|
2012-11-21 14:40:29 +07:00
|
|
|
static int
|
2012-08-23 01:21:03 +07:00
|
|
|
qla83xx_check_nic_core_fw_alive(scsi_qla_host_t *base_vha)
|
|
|
|
{
|
|
|
|
int rval = QLA_SUCCESS;
|
|
|
|
unsigned long heart_beat_wait = jiffies + (1 * HZ);
|
|
|
|
uint32_t heart_beat_counter1, heart_beat_counter2;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (time_after(jiffies, heart_beat_wait)) {
|
|
|
|
ql_dbg(ql_dbg_p3p, base_vha, 0xb07c,
|
|
|
|
"Nic Core f/w is not alive.\n");
|
|
|
|
rval = QLA_FUNCTION_FAILED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
qla83xx_idc_lock(base_vha, 0);
|
|
|
|
qla83xx_rd_reg(base_vha, QLA83XX_FW_HEARTBEAT,
|
|
|
|
&heart_beat_counter1);
|
|
|
|
qla83xx_idc_unlock(base_vha, 0);
|
|
|
|
msleep(100);
|
|
|
|
qla83xx_idc_lock(base_vha, 0);
|
|
|
|
qla83xx_rd_reg(base_vha, QLA83XX_FW_HEARTBEAT,
|
|
|
|
&heart_beat_counter2);
|
|
|
|
qla83xx_idc_unlock(base_vha, 0);
|
|
|
|
} while (heart_beat_counter1 == heart_beat_counter2);
|
|
|
|
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Work: Perform NIC Core Reset handling */
|
|
|
|
void
|
|
|
|
qla83xx_nic_core_reset_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct qla_hw_data *ha =
|
|
|
|
container_of(work, struct qla_hw_data, nic_core_reset);
|
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
|
|
|
|
uint32_t dev_state = 0;
|
|
|
|
|
2012-08-23 01:21:04 +07:00
|
|
|
if (IS_QLA2031(ha)) {
|
|
|
|
if (qla2xxx_mctp_dump(base_vha) != QLA_SUCCESS)
|
|
|
|
ql_log(ql_log_warn, base_vha, 0xb081,
|
|
|
|
"Failed to dump mctp\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-08-23 01:21:03 +07:00
|
|
|
if (!ha->flags.nic_core_reset_hdlr_active) {
|
|
|
|
if (qla83xx_check_nic_core_fw_alive(base_vha) == QLA_SUCCESS) {
|
|
|
|
qla83xx_idc_lock(base_vha, 0);
|
|
|
|
qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE,
|
|
|
|
&dev_state);
|
|
|
|
qla83xx_idc_unlock(base_vha, 0);
|
|
|
|
if (dev_state != QLA8XXX_DEV_NEED_RESET) {
|
|
|
|
ql_dbg(ql_dbg_p3p, base_vha, 0xb07a,
|
|
|
|
"Nic Core f/w is alive.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ha->flags.nic_core_reset_hdlr_active = 1;
|
|
|
|
if (qla83xx_nic_core_reset(base_vha)) {
|
|
|
|
/* NIC Core reset failed. */
|
|
|
|
ql_dbg(ql_dbg_p3p, base_vha, 0xb061,
|
|
|
|
"NIC Core reset failed.\n");
|
|
|
|
}
|
|
|
|
ha->flags.nic_core_reset_hdlr_active = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Work: Handle 8200 IDC aens */
|
|
|
|
void
|
|
|
|
qla83xx_service_idc_aen(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct qla_hw_data *ha =
|
|
|
|
container_of(work, struct qla_hw_data, idc_aen);
|
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
|
|
|
|
uint32_t dev_state, idc_control;
|
|
|
|
|
|
|
|
qla83xx_idc_lock(base_vha, 0);
|
|
|
|
qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state);
|
|
|
|
qla83xx_rd_reg(base_vha, QLA83XX_IDC_CONTROL, &idc_control);
|
|
|
|
qla83xx_idc_unlock(base_vha, 0);
|
|
|
|
if (dev_state == QLA8XXX_DEV_NEED_RESET) {
|
|
|
|
if (idc_control & QLA83XX_IDC_GRACEFUL_RESET) {
|
|
|
|
ql_dbg(ql_dbg_p3p, base_vha, 0xb062,
|
|
|
|
"Application requested NIC Core Reset.\n");
|
|
|
|
qla83xx_schedule_work(base_vha, QLA83XX_NIC_CORE_RESET);
|
|
|
|
} else if (qla83xx_check_nic_core_fw_alive(base_vha) ==
|
|
|
|
QLA_SUCCESS) {
|
|
|
|
ql_dbg(ql_dbg_p3p, base_vha, 0xb07b,
|
|
|
|
"Other protocol driver requested NIC Core Reset.\n");
|
|
|
|
qla83xx_schedule_work(base_vha, QLA83XX_NIC_CORE_RESET);
|
|
|
|
}
|
|
|
|
} else if (dev_state == QLA8XXX_DEV_FAILED ||
|
|
|
|
dev_state == QLA8XXX_DEV_NEED_QUIESCENT) {
|
|
|
|
qla83xx_schedule_work(base_vha, QLA83XX_IDC_STATE_HANDLER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
qla83xx_wait_logic(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Yield CPU */
|
|
|
|
if (!in_interrupt()) {
|
|
|
|
/*
|
|
|
|
* Wait about 200ms before retrying again.
|
|
|
|
* This controls the number of retries for single
|
|
|
|
* lock operation.
|
|
|
|
*/
|
|
|
|
msleep(100);
|
|
|
|
schedule();
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < 20; i++)
|
|
|
|
cpu_relax(); /* This a nop instr on i386 */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-21 14:40:29 +07:00
|
|
|
static int
|
2012-08-23 01:21:03 +07:00
|
|
|
qla83xx_force_lock_recovery(scsi_qla_host_t *base_vha)
|
|
|
|
{
|
|
|
|
int rval;
|
|
|
|
uint32_t data;
|
|
|
|
uint32_t idc_lck_rcvry_stage_mask = 0x3;
|
|
|
|
uint32_t idc_lck_rcvry_owner_mask = 0x3c;
|
|
|
|
struct qla_hw_data *ha = base_vha->hw;
|
2013-02-08 13:57:53 +07:00
|
|
|
ql_dbg(ql_dbg_p3p, base_vha, 0xb086,
|
|
|
|
"Trying force recovery of the IDC lock.\n");
|
2012-08-23 01:21:03 +07:00
|
|
|
|
|
|
|
rval = qla83xx_rd_reg(base_vha, QLA83XX_IDC_LOCK_RECOVERY, &data);
|
|
|
|
if (rval)
|
|
|
|
return rval;
|
|
|
|
|
|
|
|
if ((data & idc_lck_rcvry_stage_mask) > 0) {
|
|
|
|
return QLA_SUCCESS;
|
|
|
|
} else {
|
|
|
|
data = (IDC_LOCK_RECOVERY_STAGE1) | (ha->portnum << 2);
|
|
|
|
rval = qla83xx_wr_reg(base_vha, QLA83XX_IDC_LOCK_RECOVERY,
|
|
|
|
data);
|
|
|
|
if (rval)
|
|
|
|
return rval;
|
|
|
|
|
|
|
|
msleep(200);
|
|
|
|
|
|
|
|
rval = qla83xx_rd_reg(base_vha, QLA83XX_IDC_LOCK_RECOVERY,
|
|
|
|
&data);
|
|
|
|
if (rval)
|
|
|
|
return rval;
|
|
|
|
|
|
|
|
if (((data & idc_lck_rcvry_owner_mask) >> 2) == ha->portnum) {
|
|
|
|
data &= (IDC_LOCK_RECOVERY_STAGE2 |
|
|
|
|
~(idc_lck_rcvry_stage_mask));
|
|
|
|
rval = qla83xx_wr_reg(base_vha,
|
|
|
|
QLA83XX_IDC_LOCK_RECOVERY, data);
|
|
|
|
if (rval)
|
|
|
|
return rval;
|
|
|
|
|
|
|
|
/* Forcefully perform IDC UnLock */
|
|
|
|
rval = qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_UNLOCK,
|
|
|
|
&data);
|
|
|
|
if (rval)
|
|
|
|
return rval;
|
|
|
|
/* Clear lock-id by setting 0xff */
|
|
|
|
rval = qla83xx_wr_reg(base_vha, QLA83XX_DRIVER_LOCKID,
|
|
|
|
0xff);
|
|
|
|
if (rval)
|
|
|
|
return rval;
|
|
|
|
/* Clear lock-recovery by setting 0x0 */
|
|
|
|
rval = qla83xx_wr_reg(base_vha,
|
|
|
|
QLA83XX_IDC_LOCK_RECOVERY, 0x0);
|
|
|
|
if (rval)
|
|
|
|
return rval;
|
|
|
|
} else
|
|
|
|
return QLA_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
|
2012-11-21 14:40:29 +07:00
|
|
|
static int
|
2012-08-23 01:21:03 +07:00
|
|
|
qla83xx_idc_lock_recovery(scsi_qla_host_t *base_vha)
|
|
|
|
{
|
|
|
|
int rval = QLA_SUCCESS;
|
|
|
|
uint32_t o_drv_lockid, n_drv_lockid;
|
|
|
|
unsigned long lock_recovery_timeout;
|
|
|
|
|
|
|
|
lock_recovery_timeout = jiffies + QLA83XX_MAX_LOCK_RECOVERY_WAIT;
|
|
|
|
retry_lockid:
|
|
|
|
rval = qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, &o_drv_lockid);
|
|
|
|
if (rval)
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
/* MAX wait time before forcing IDC Lock recovery = 2 secs */
|
|
|
|
if (time_after_eq(jiffies, lock_recovery_timeout)) {
|
|
|
|
if (qla83xx_force_lock_recovery(base_vha) == QLA_SUCCESS)
|
|
|
|
return QLA_SUCCESS;
|
|
|
|
else
|
|
|
|
return QLA_FUNCTION_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
rval = qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, &n_drv_lockid);
|
|
|
|
if (rval)
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
if (o_drv_lockid == n_drv_lockid) {
|
|
|
|
qla83xx_wait_logic();
|
|
|
|
goto retry_lockid;
|
|
|
|
} else
|
|
|
|
return QLA_SUCCESS;
|
|
|
|
|
|
|
|
exit:
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
qla83xx_idc_lock(scsi_qla_host_t *base_vha, uint16_t requester_id)
|
|
|
|
{
|
|
|
|
uint16_t options = (requester_id << 15) | BIT_6;
|
|
|
|
uint32_t data;
|
2013-02-08 13:57:53 +07:00
|
|
|
uint32_t lock_owner;
|
2012-08-23 01:21:03 +07:00
|
|
|
struct qla_hw_data *ha = base_vha->hw;
|
|
|
|
|
|
|
|
/* IDC-lock implementation using driver-lock/lock-id remote registers */
|
|
|
|
retry_lock:
|
|
|
|
if (qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCK, &data)
|
|
|
|
== QLA_SUCCESS) {
|
|
|
|
if (data) {
|
|
|
|
/* Setting lock-id to our function-number */
|
|
|
|
qla83xx_wr_reg(base_vha, QLA83XX_DRIVER_LOCKID,
|
|
|
|
ha->portnum);
|
|
|
|
} else {
|
2013-02-08 13:57:53 +07:00
|
|
|
qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID,
|
|
|
|
&lock_owner);
|
2012-08-23 01:21:03 +07:00
|
|
|
ql_dbg(ql_dbg_p3p, base_vha, 0xb063,
|
2013-02-08 13:57:53 +07:00
|
|
|
"Failed to acquire IDC lock, acquired by %d, "
|
|
|
|
"retrying...\n", lock_owner);
|
2012-08-23 01:21:03 +07:00
|
|
|
|
|
|
|
/* Retry/Perform IDC-Lock recovery */
|
|
|
|
if (qla83xx_idc_lock_recovery(base_vha)
|
|
|
|
== QLA_SUCCESS) {
|
|
|
|
qla83xx_wait_logic();
|
|
|
|
goto retry_lock;
|
|
|
|
} else
|
|
|
|
ql_log(ql_log_warn, base_vha, 0xb075,
|
|
|
|
"IDC Lock recovery FAILED.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* XXX: IDC-lock implementation using access-control mbx */
|
|
|
|
retry_lock2:
|
|
|
|
if (qla83xx_access_control(base_vha, options, 0, 0, NULL)) {
|
|
|
|
ql_dbg(ql_dbg_p3p, base_vha, 0xb072,
|
|
|
|
"Failed to acquire IDC lock. retrying...\n");
|
|
|
|
/* Retry/Perform IDC-Lock recovery */
|
|
|
|
if (qla83xx_idc_lock_recovery(base_vha) == QLA_SUCCESS) {
|
|
|
|
qla83xx_wait_logic();
|
|
|
|
goto retry_lock2;
|
|
|
|
} else
|
|
|
|
ql_log(ql_log_warn, base_vha, 0xb076,
|
|
|
|
"IDC Lock recovery FAILED.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
qla83xx_idc_unlock(scsi_qla_host_t *base_vha, uint16_t requester_id)
|
|
|
|
{
|
|
|
|
uint16_t options = (requester_id << 15) | BIT_7, retry;
|
|
|
|
uint32_t data;
|
|
|
|
struct qla_hw_data *ha = base_vha->hw;
|
|
|
|
|
|
|
|
/* IDC-unlock implementation using driver-unlock/lock-id
|
|
|
|
* remote registers
|
|
|
|
*/
|
|
|
|
retry = 0;
|
|
|
|
retry_unlock:
|
|
|
|
if (qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, &data)
|
|
|
|
== QLA_SUCCESS) {
|
|
|
|
if (data == ha->portnum) {
|
|
|
|
qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_UNLOCK, &data);
|
|
|
|
/* Clearing lock-id by setting 0xff */
|
|
|
|
qla83xx_wr_reg(base_vha, QLA83XX_DRIVER_LOCKID, 0xff);
|
|
|
|
} else if (retry < 10) {
|
|
|
|
/* SV: XXX: IDC unlock retrying needed here? */
|
|
|
|
|
|
|
|
/* Retry for IDC-unlock */
|
|
|
|
qla83xx_wait_logic();
|
|
|
|
retry++;
|
|
|
|
ql_dbg(ql_dbg_p3p, base_vha, 0xb064,
|
|
|
|
"Failed to release IDC lock, retyring=%d\n", retry);
|
|
|
|
goto retry_unlock;
|
|
|
|
}
|
|
|
|
} else if (retry < 10) {
|
|
|
|
/* Retry for IDC-unlock */
|
|
|
|
qla83xx_wait_logic();
|
|
|
|
retry++;
|
|
|
|
ql_dbg(ql_dbg_p3p, base_vha, 0xb065,
|
|
|
|
"Failed to read drv-lockid, retyring=%d\n", retry);
|
|
|
|
goto retry_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* XXX: IDC-unlock implementation using access-control mbx */
|
|
|
|
retry = 0;
|
|
|
|
retry_unlock2:
|
|
|
|
if (qla83xx_access_control(base_vha, options, 0, 0, NULL)) {
|
|
|
|
if (retry < 10) {
|
|
|
|
/* Retry for IDC-unlock */
|
|
|
|
qla83xx_wait_logic();
|
|
|
|
retry++;
|
|
|
|
ql_dbg(ql_dbg_p3p, base_vha, 0xb066,
|
|
|
|
"Failed to release IDC lock, retyring=%d\n", retry);
|
|
|
|
goto retry_unlock2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
__qla83xx_set_drv_presence(scsi_qla_host_t *vha)
|
|
|
|
{
|
|
|
|
int rval = QLA_SUCCESS;
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
uint32_t drv_presence;
|
|
|
|
|
|
|
|
rval = qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence);
|
|
|
|
if (rval == QLA_SUCCESS) {
|
|
|
|
drv_presence |= (1 << ha->portnum);
|
|
|
|
rval = qla83xx_wr_reg(vha, QLA83XX_IDC_DRV_PRESENCE,
|
|
|
|
drv_presence);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
qla83xx_set_drv_presence(scsi_qla_host_t *vha)
|
|
|
|
{
|
|
|
|
int rval = QLA_SUCCESS;
|
|
|
|
|
|
|
|
qla83xx_idc_lock(vha, 0);
|
|
|
|
rval = __qla83xx_set_drv_presence(vha);
|
|
|
|
qla83xx_idc_unlock(vha, 0);
|
|
|
|
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
__qla83xx_clear_drv_presence(scsi_qla_host_t *vha)
|
|
|
|
{
|
|
|
|
int rval = QLA_SUCCESS;
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
uint32_t drv_presence;
|
|
|
|
|
|
|
|
rval = qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence);
|
|
|
|
if (rval == QLA_SUCCESS) {
|
|
|
|
drv_presence &= ~(1 << ha->portnum);
|
|
|
|
rval = qla83xx_wr_reg(vha, QLA83XX_IDC_DRV_PRESENCE,
|
|
|
|
drv_presence);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
qla83xx_clear_drv_presence(scsi_qla_host_t *vha)
|
|
|
|
{
|
|
|
|
int rval = QLA_SUCCESS;
|
|
|
|
|
|
|
|
qla83xx_idc_lock(vha, 0);
|
|
|
|
rval = __qla83xx_clear_drv_presence(vha);
|
|
|
|
qla83xx_idc_unlock(vha, 0);
|
|
|
|
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
|
2012-11-21 14:40:29 +07:00
|
|
|
static void
|
2012-08-23 01:21:03 +07:00
|
|
|
qla83xx_need_reset_handler(scsi_qla_host_t *vha)
|
|
|
|
{
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
uint32_t drv_ack, drv_presence;
|
|
|
|
unsigned long ack_timeout;
|
|
|
|
|
|
|
|
/* Wait for IDC ACK from all functions (DRV-ACK == DRV-PRESENCE) */
|
|
|
|
ack_timeout = jiffies + (ha->fcoe_reset_timeout * HZ);
|
|
|
|
while (1) {
|
|
|
|
qla83xx_rd_reg(vha, QLA83XX_IDC_DRIVER_ACK, &drv_ack);
|
|
|
|
qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence);
|
2012-11-21 14:40:36 +07:00
|
|
|
if ((drv_ack & drv_presence) == drv_presence)
|
2012-08-23 01:21:03 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
if (time_after_eq(jiffies, ack_timeout)) {
|
|
|
|
ql_log(ql_log_warn, vha, 0xb067,
|
|
|
|
"RESET ACK TIMEOUT! drv_presence=0x%x "
|
|
|
|
"drv_ack=0x%x\n", drv_presence, drv_ack);
|
|
|
|
/*
|
|
|
|
* The function(s) which did not ack in time are forced
|
|
|
|
* to withdraw any further participation in the IDC
|
|
|
|
* reset.
|
|
|
|
*/
|
|
|
|
if (drv_ack != drv_presence)
|
|
|
|
qla83xx_wr_reg(vha, QLA83XX_IDC_DRV_PRESENCE,
|
|
|
|
drv_ack);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
qla83xx_idc_unlock(vha, 0);
|
|
|
|
msleep(1000);
|
|
|
|
qla83xx_idc_lock(vha, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_COLD);
|
|
|
|
ql_log(ql_log_info, vha, 0xb068, "HW State: COLD/RE-INIT.\n");
|
|
|
|
}
|
|
|
|
|
2012-11-21 14:40:29 +07:00
|
|
|
static int
|
2012-08-23 01:21:03 +07:00
|
|
|
qla83xx_device_bootstrap(scsi_qla_host_t *vha)
|
|
|
|
{
|
|
|
|
int rval = QLA_SUCCESS;
|
|
|
|
uint32_t idc_control;
|
|
|
|
|
|
|
|
qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_INITIALIZING);
|
|
|
|
ql_log(ql_log_info, vha, 0xb069, "HW State: INITIALIZING.\n");
|
|
|
|
|
|
|
|
/* Clearing IDC-Control Graceful-Reset Bit before resetting f/w */
|
|
|
|
__qla83xx_get_idc_control(vha, &idc_control);
|
|
|
|
idc_control &= ~QLA83XX_IDC_GRACEFUL_RESET;
|
|
|
|
__qla83xx_set_idc_control(vha, 0);
|
|
|
|
|
|
|
|
qla83xx_idc_unlock(vha, 0);
|
|
|
|
rval = qla83xx_restart_nic_firmware(vha);
|
|
|
|
qla83xx_idc_lock(vha, 0);
|
|
|
|
|
|
|
|
if (rval != QLA_SUCCESS) {
|
|
|
|
ql_log(ql_log_fatal, vha, 0xb06a,
|
|
|
|
"Failed to restart NIC f/w.\n");
|
|
|
|
qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_FAILED);
|
|
|
|
ql_log(ql_log_info, vha, 0xb06b, "HW State: FAILED.\n");
|
|
|
|
} else {
|
|
|
|
ql_dbg(ql_dbg_p3p, vha, 0xb06c,
|
|
|
|
"Success in restarting nic f/w.\n");
|
|
|
|
qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_READY);
|
|
|
|
ql_log(ql_log_info, vha, 0xb06d, "HW State: READY.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assumes idc_lock always held on entry */
|
|
|
|
int
|
|
|
|
qla83xx_idc_state_handler(scsi_qla_host_t *base_vha)
|
|
|
|
{
|
|
|
|
struct qla_hw_data *ha = base_vha->hw;
|
|
|
|
int rval = QLA_SUCCESS;
|
|
|
|
unsigned long dev_init_timeout;
|
|
|
|
uint32_t dev_state;
|
|
|
|
|
|
|
|
/* Wait for MAX-INIT-TIMEOUT for the device to go ready */
|
|
|
|
dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout * HZ);
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
|
|
|
if (time_after_eq(jiffies, dev_init_timeout)) {
|
|
|
|
ql_log(ql_log_warn, base_vha, 0xb06e,
|
|
|
|
"Initialization TIMEOUT!\n");
|
|
|
|
/* Init timeout. Disable further NIC Core
|
|
|
|
* communication.
|
|
|
|
*/
|
|
|
|
qla83xx_wr_reg(base_vha, QLA83XX_IDC_DEV_STATE,
|
|
|
|
QLA8XXX_DEV_FAILED);
|
|
|
|
ql_log(ql_log_info, base_vha, 0xb06f,
|
|
|
|
"HW State: FAILED.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state);
|
|
|
|
switch (dev_state) {
|
|
|
|
case QLA8XXX_DEV_READY:
|
|
|
|
if (ha->flags.nic_core_reset_owner)
|
|
|
|
qla83xx_idc_audit(base_vha,
|
|
|
|
IDC_AUDIT_COMPLETION);
|
|
|
|
ha->flags.nic_core_reset_owner = 0;
|
|
|
|
ql_dbg(ql_dbg_p3p, base_vha, 0xb070,
|
|
|
|
"Reset_owner reset by 0x%x.\n",
|
|
|
|
ha->portnum);
|
|
|
|
goto exit;
|
|
|
|
case QLA8XXX_DEV_COLD:
|
|
|
|
if (ha->flags.nic_core_reset_owner)
|
|
|
|
rval = qla83xx_device_bootstrap(base_vha);
|
|
|
|
else {
|
|
|
|
/* Wait for AEN to change device-state */
|
|
|
|
qla83xx_idc_unlock(base_vha, 0);
|
|
|
|
msleep(1000);
|
|
|
|
qla83xx_idc_lock(base_vha, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case QLA8XXX_DEV_INITIALIZING:
|
|
|
|
/* Wait for AEN to change device-state */
|
|
|
|
qla83xx_idc_unlock(base_vha, 0);
|
|
|
|
msleep(1000);
|
|
|
|
qla83xx_idc_lock(base_vha, 0);
|
|
|
|
break;
|
|
|
|
case QLA8XXX_DEV_NEED_RESET:
|
|
|
|
if (!ql2xdontresethba && ha->flags.nic_core_reset_owner)
|
|
|
|
qla83xx_need_reset_handler(base_vha);
|
|
|
|
else {
|
|
|
|
/* Wait for AEN to change device-state */
|
|
|
|
qla83xx_idc_unlock(base_vha, 0);
|
|
|
|
msleep(1000);
|
|
|
|
qla83xx_idc_lock(base_vha, 0);
|
|
|
|
}
|
|
|
|
/* reset timeout value after need reset handler */
|
|
|
|
dev_init_timeout = jiffies +
|
|
|
|
(ha->fcoe_dev_init_timeout * HZ);
|
|
|
|
break;
|
|
|
|
case QLA8XXX_DEV_NEED_QUIESCENT:
|
|
|
|
/* XXX: DEBUG for now */
|
|
|
|
qla83xx_idc_unlock(base_vha, 0);
|
|
|
|
msleep(1000);
|
|
|
|
qla83xx_idc_lock(base_vha, 0);
|
|
|
|
break;
|
|
|
|
case QLA8XXX_DEV_QUIESCENT:
|
|
|
|
/* XXX: DEBUG for now */
|
|
|
|
if (ha->flags.quiesce_owner)
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
qla83xx_idc_unlock(base_vha, 0);
|
|
|
|
msleep(1000);
|
|
|
|
qla83xx_idc_lock(base_vha, 0);
|
|
|
|
dev_init_timeout = jiffies +
|
|
|
|
(ha->fcoe_dev_init_timeout * HZ);
|
|
|
|
break;
|
|
|
|
case QLA8XXX_DEV_FAILED:
|
|
|
|
if (ha->flags.nic_core_reset_owner)
|
|
|
|
qla83xx_idc_audit(base_vha,
|
|
|
|
IDC_AUDIT_COMPLETION);
|
|
|
|
ha->flags.nic_core_reset_owner = 0;
|
|
|
|
__qla83xx_clear_drv_presence(base_vha);
|
|
|
|
qla83xx_idc_unlock(base_vha, 0);
|
|
|
|
qla8xxx_dev_failed_handler(base_vha);
|
|
|
|
rval = QLA_FUNCTION_FAILED;
|
|
|
|
qla83xx_idc_lock(base_vha, 0);
|
|
|
|
goto exit;
|
|
|
|
case QLA8XXX_BAD_VALUE:
|
|
|
|
qla83xx_idc_unlock(base_vha, 0);
|
|
|
|
msleep(1000);
|
|
|
|
qla83xx_idc_lock(base_vha, 0);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ql_log(ql_log_warn, base_vha, 0xb071,
|
|
|
|
"Unknow Device State: %x.\n", dev_state);
|
|
|
|
qla83xx_idc_unlock(base_vha, 0);
|
|
|
|
qla8xxx_dev_failed_handler(base_vha);
|
|
|
|
rval = QLA_FUNCTION_FAILED;
|
|
|
|
qla83xx_idc_lock(base_vha, 0);
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
exit:
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/**************************************************************************
|
|
|
|
* qla2x00_do_dpc
|
|
|
|
* This kernel thread is a task that is schedule by the interrupt handler
|
|
|
|
* to perform the background processing for interrupts.
|
|
|
|
*
|
|
|
|
* Notes:
|
|
|
|
* This task always run in the context of a kernel thread. It
|
|
|
|
* is kick-off by the driver's detect code and starts up
|
|
|
|
* up one per adapter. It immediately goes to sleep and waits for
|
|
|
|
* some fibre event. When either the interrupt handler or
|
|
|
|
* the timer routine detects a event it will one of the task
|
|
|
|
* bits then wake us up.
|
|
|
|
**************************************************************************/
|
|
|
|
static int
|
|
|
|
qla2x00_do_dpc(void *data)
|
|
|
|
{
|
2007-07-06 03:16:51 +07:00
|
|
|
int rval;
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_qla_host_t *base_vha;
|
|
|
|
struct qla_hw_data *ha;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
ha = (struct qla_hw_data *)data;
|
|
|
|
base_vha = pci_get_drvdata(ha->pdev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
set_user_nice(current, -20);
|
|
|
|
|
2011-01-28 04:12:37 +07:00
|
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
2006-02-15 00:46:22 +07:00
|
|
|
while (!kthread_should_stop()) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_dpc, base_vha, 0x4000,
|
|
|
|
"DPC handler sleeping.\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-02-15 00:46:22 +07:00
|
|
|
schedule();
|
|
|
|
__set_current_state(TASK_RUNNING);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-11-19 00:03:10 +07:00
|
|
|
if (!base_vha->flags.init_done || ha->flags.mbox_busy)
|
|
|
|
goto end_loop;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-12-16 12:29:46 +07:00
|
|
|
if (ha->flags.eeh_busy) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_dpc, base_vha, 0x4003,
|
|
|
|
"eeh_busy=%d.\n", ha->flags.eeh_busy);
|
2011-11-19 00:03:10 +07:00
|
|
|
goto end_loop;
|
2009-12-16 12:29:46 +07:00
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
ha->dpc_active = 1;
|
|
|
|
|
2012-05-16 01:34:15 +07:00
|
|
|
ql_dbg(ql_dbg_dpc + ql_dbg_verbose, base_vha, 0x4001,
|
|
|
|
"DPC handler waking up, dpc_flags=0x%lx.\n",
|
|
|
|
base_vha->dpc_flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_do_work(base_vha);
|
2008-04-04 03:13:18 +07:00
|
|
|
|
2010-04-13 07:59:55 +07:00
|
|
|
if (IS_QLA82XX(ha)) {
|
|
|
|
if (test_and_clear_bit(ISP_UNRECOVERABLE,
|
|
|
|
&base_vha->dpc_flags)) {
|
|
|
|
qla82xx_idc_lock(ha);
|
|
|
|
qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
2012-08-23 01:21:03 +07:00
|
|
|
QLA8XXX_DEV_FAILED);
|
2010-04-13 07:59:55 +07:00
|
|
|
qla82xx_idc_unlock(ha);
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_info, base_vha, 0x4004,
|
|
|
|
"HW State: FAILED.\n");
|
2010-04-13 07:59:55 +07:00
|
|
|
qla82xx_device_state_handler(base_vha);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (test_and_clear_bit(FCOE_CTX_RESET_NEEDED,
|
|
|
|
&base_vha->dpc_flags)) {
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_dpc, base_vha, 0x4005,
|
|
|
|
"FCoE context reset scheduled.\n");
|
2010-04-13 07:59:55 +07:00
|
|
|
if (!(test_and_set_bit(ABORT_ISP_ACTIVE,
|
|
|
|
&base_vha->dpc_flags))) {
|
|
|
|
if (qla82xx_fcoe_ctx_reset(base_vha)) {
|
|
|
|
/* FCoE-ctx reset failed.
|
|
|
|
* Escalate to chip-reset
|
|
|
|
*/
|
|
|
|
set_bit(ISP_ABORT_NEEDED,
|
|
|
|
&base_vha->dpc_flags);
|
|
|
|
}
|
|
|
|
clear_bit(ABORT_ISP_ACTIVE,
|
|
|
|
&base_vha->dpc_flags);
|
|
|
|
}
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_dpc, base_vha, 0x4006,
|
|
|
|
"FCoE context reset end.\n");
|
2010-04-13 07:59:55 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
if (test_and_clear_bit(ISP_ABORT_NEEDED,
|
|
|
|
&base_vha->dpc_flags)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_dpc, base_vha, 0x4007,
|
|
|
|
"ISP abort scheduled.\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!(test_and_set_bit(ABORT_ISP_ACTIVE,
|
2008-11-07 01:40:51 +07:00
|
|
|
&base_vha->dpc_flags))) {
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-04-13 07:59:55 +07:00
|
|
|
if (ha->isp_ops->abort_isp(base_vha)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
/* failed. retry later */
|
|
|
|
set_bit(ISP_ABORT_NEEDED,
|
2008-11-07 01:40:51 +07:00
|
|
|
&base_vha->dpc_flags);
|
2008-02-01 03:33:51 +07:00
|
|
|
}
|
2008-11-07 01:40:51 +07:00
|
|
|
clear_bit(ABORT_ISP_ACTIVE,
|
|
|
|
&base_vha->dpc_flags);
|
2008-02-01 03:33:51 +07:00
|
|
|
}
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_dpc, base_vha, 0x4008,
|
|
|
|
"ISP abort end.\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
[SCSI] qla2xxx: Test and clear FCPORT_UPDATE_NEEDED atomically.
When the qla2xxx driver loses access to multiple, remote ports, there is a race
condition which can occur which will keep the request stuck on a scsi request
queue indefinitely.
This bad state occurred do to a race condition with how the FCPORT_UPDATE_NEEDED
bit is set in qla2x00_schedule_rport_del(), and how it is cleared in
qla2x00_do_dpc(). The problem port has its drport pointer set, but it has never
been processed by the driver to inform the fc transport that the port has been
lost. qla2x00_schedule_rport_del() sets drport, and then sets the
FCPORT_UPDATE_NEEDED bit. In qla2x00_do_dpc(), the port lists are walked and
any drport pointer is handled and the fc transport informed of the port loss,
then the FCPORT_UPDATE_NEEDED bit is cleared. This leaves a race where the
dpc thread is processing one port removal, another port removal is marked
with a call to qla2x00_schedule_rport_del(), and the dpc thread clears the
bit for both removals, even though only the first removal was actually
handled. Until another event occurs to set FCPORT_UPDATE_NEEDED, the later
port removal is never finished and qla2xxx stays in a bad state which causes
requests to become stuck on request queues.
This patch updates the driver to test and clear FCPORT_UPDATE_NEEDED
atomically. This ensures the port state changes are processed and not lost.
Signed-off-by: David Jeffery <djeffery@redhat.com>
Signed-off-by: Chad Dupuis <chad.dupuis@qlogic.com>
Cc: stable@vger.kernel.org
Signed-off-by: Saurav Kashyap <saurav.kashyap@qlogic.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
2012-11-21 14:39:54 +07:00
|
|
|
if (test_and_clear_bit(FCPORT_UPDATE_NEEDED,
|
|
|
|
&base_vha->dpc_flags)) {
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_update_fcports(base_vha);
|
2008-07-24 22:31:49 +07:00
|
|
|
}
|
2006-01-21 05:53:13 +07:00
|
|
|
|
2012-05-16 01:34:28 +07:00
|
|
|
if (test_bit(SCR_PENDING, &base_vha->dpc_flags)) {
|
|
|
|
int ret;
|
|
|
|
ret = qla2x00_send_change_request(base_vha, 0x3, 0);
|
|
|
|
if (ret != QLA_SUCCESS)
|
|
|
|
ql_log(ql_log_warn, base_vha, 0x121,
|
|
|
|
"Failed to enable receiving of RSCN "
|
|
|
|
"requests: 0x%x.\n", ret);
|
|
|
|
clear_bit(SCR_PENDING, &base_vha->dpc_flags);
|
|
|
|
}
|
|
|
|
|
2010-12-22 07:00:14 +07:00
|
|
|
if (test_bit(ISP_QUIESCE_NEEDED, &base_vha->dpc_flags)) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_dpc, base_vha, 0x4009,
|
|
|
|
"Quiescence mode scheduled.\n");
|
2012-08-23 01:21:06 +07:00
|
|
|
if (IS_QLA82XX(ha)) {
|
|
|
|
qla82xx_device_state_handler(base_vha);
|
|
|
|
clear_bit(ISP_QUIESCE_NEEDED,
|
|
|
|
&base_vha->dpc_flags);
|
|
|
|
if (!ha->flags.quiesce_owner) {
|
|
|
|
qla2x00_perform_loop_resync(base_vha);
|
|
|
|
|
|
|
|
qla82xx_idc_lock(ha);
|
|
|
|
qla82xx_clear_qsnt_ready(base_vha);
|
|
|
|
qla82xx_idc_unlock(ha);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
clear_bit(ISP_QUIESCE_NEEDED,
|
|
|
|
&base_vha->dpc_flags);
|
|
|
|
qla2x00_quiesce_io(base_vha);
|
2010-12-22 07:00:14 +07:00
|
|
|
}
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_dpc, base_vha, 0x400a,
|
|
|
|
"Quiescence mode end.\n");
|
2010-12-22 07:00:14 +07:00
|
|
|
}
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
if (test_and_clear_bit(RESET_MARKER_NEEDED,
|
|
|
|
&base_vha->dpc_flags) &&
|
|
|
|
(!(test_and_set_bit(RESET_ACTIVE, &base_vha->dpc_flags)))) {
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_dpc, base_vha, 0x400b,
|
|
|
|
"Reset marker scheduled.\n");
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_rst_aen(base_vha);
|
|
|
|
clear_bit(RESET_ACTIVE, &base_vha->dpc_flags);
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_dpc, base_vha, 0x400c,
|
|
|
|
"Reset marker end.\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Retry each device up to login retry count */
|
2008-11-07 01:40:51 +07:00
|
|
|
if ((test_and_clear_bit(RELOGIN_NEEDED,
|
|
|
|
&base_vha->dpc_flags)) &&
|
|
|
|
!test_bit(LOOP_RESYNC_NEEDED, &base_vha->dpc_flags) &&
|
|
|
|
atomic_read(&base_vha->loop_state) != LOOP_DOWN) {
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_dpc, base_vha, 0x400d,
|
|
|
|
"Relogin scheduled.\n");
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_relogin(base_vha);
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_dpc, base_vha, 0x400e,
|
|
|
|
"Relogin end.\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
if (test_and_clear_bit(LOOP_RESYNC_NEEDED,
|
|
|
|
&base_vha->dpc_flags)) {
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_dpc, base_vha, 0x400f,
|
|
|
|
"Loop resync scheduled.\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE,
|
2008-11-07 01:40:51 +07:00
|
|
|
&base_vha->dpc_flags))) {
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
rval = qla2x00_loop_resync(base_vha);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
clear_bit(LOOP_RESYNC_ACTIVE,
|
|
|
|
&base_vha->dpc_flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_dpc, base_vha, 0x4010,
|
|
|
|
"Loop resync end.\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
if (test_bit(NPIV_CONFIG_NEEDED, &base_vha->dpc_flags) &&
|
|
|
|
atomic_read(&base_vha->loop_state) == LOOP_READY) {
|
|
|
|
clear_bit(NPIV_CONFIG_NEEDED, &base_vha->dpc_flags);
|
|
|
|
qla2xxx_flash_npiv_conf(base_vha);
|
2008-09-12 11:22:50 +07:00
|
|
|
}
|
|
|
|
|
2013-01-30 15:34:38 +07:00
|
|
|
if (test_and_clear_bit(HOST_RAMP_DOWN_QUEUE_DEPTH,
|
|
|
|
&base_vha->dpc_flags)) {
|
|
|
|
/* Prevents simultaneous ramp up and down */
|
|
|
|
clear_bit(HOST_RAMP_UP_QUEUE_DEPTH,
|
|
|
|
&base_vha->dpc_flags);
|
|
|
|
qla2x00_host_ramp_down_queuedepth(base_vha);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (test_and_clear_bit(HOST_RAMP_UP_QUEUE_DEPTH,
|
|
|
|
&base_vha->dpc_flags))
|
|
|
|
qla2x00_host_ramp_up_queuedepth(base_vha);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!ha->interrupts_on)
|
2007-07-20 05:06:00 +07:00
|
|
|
ha->isp_ops->enable_intrs(ha);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
if (test_and_clear_bit(BEACON_BLINK_NEEDED,
|
|
|
|
&base_vha->dpc_flags))
|
|
|
|
ha->isp_ops->beacon_blink(base_vha);
|
2006-02-01 07:05:07 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_do_dpc_all_vps(base_vha);
|
2007-07-06 03:16:51 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
ha->dpc_active = 0;
|
2011-11-19 00:03:10 +07:00
|
|
|
end_loop:
|
2011-01-28 04:12:37 +07:00
|
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
2005-04-17 05:20:36 +07:00
|
|
|
} /* End of while(1) */
|
2011-01-28 04:12:37 +07:00
|
|
|
__set_current_state(TASK_RUNNING);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_dpc, base_vha, 0x4011,
|
|
|
|
"DPC handler exiting.\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure that nobody tries to wake us up again.
|
|
|
|
*/
|
|
|
|
ha->dpc_active = 0;
|
|
|
|
|
2009-08-21 01:06:05 +07:00
|
|
|
/* Cleanup any residual CTX SRBs. */
|
|
|
|
qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16);
|
|
|
|
|
2006-02-15 00:46:22 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2xxx_wake_dpc(struct scsi_qla_host *vha)
|
2006-02-15 00:46:22 +07:00
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2008-08-14 11:37:01 +07:00
|
|
|
struct task_struct *t = ha->dpc_thread;
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
if (!test_bit(UNLOADING, &vha->dpc_flags) && t)
|
2008-08-14 11:37:01 +07:00
|
|
|
wake_up_process(t);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qla2x00_rst_aen
|
|
|
|
* Processes asynchronous reset.
|
|
|
|
*
|
|
|
|
* Input:
|
|
|
|
* ha = adapter block pointer.
|
|
|
|
*/
|
|
|
|
static void
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_rst_aen(scsi_qla_host_t *vha)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
if (vha->flags.online && !vha->flags.reset_active &&
|
|
|
|
!atomic_read(&vha->loop_down_timer) &&
|
|
|
|
!(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))) {
|
2005-04-17 05:20:36 +07:00
|
|
|
do {
|
2008-11-07 01:40:51 +07:00
|
|
|
clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Issue marker command only when we are going to start
|
|
|
|
* the I/O.
|
|
|
|
*/
|
2008-11-07 01:40:51 +07:00
|
|
|
vha->marker_needed = 1;
|
|
|
|
} while (!atomic_read(&vha->loop_down_timer) &&
|
|
|
|
(test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags)));
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
* qla2x00_timer
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* One second timer
|
|
|
|
*
|
|
|
|
* Context: Interrupt
|
|
|
|
***************************************************************************/
|
2007-07-06 03:16:51 +07:00
|
|
|
void
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_timer(scsi_qla_host_t *vha)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
unsigned long cpu_flags = 0;
|
|
|
|
int start_dpc = 0;
|
|
|
|
int index;
|
|
|
|
srb_t *sp;
|
2009-12-16 12:29:46 +07:00
|
|
|
uint16_t w;
|
2008-11-07 01:40:51 +07:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2008-12-10 07:45:39 +07:00
|
|
|
struct req_que *req;
|
2009-12-16 12:29:46 +07:00
|
|
|
|
2010-09-04 05:20:50 +07:00
|
|
|
if (ha->flags.eeh_busy) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_timer, vha, 0x6000,
|
|
|
|
"EEH = %d, restarting timer.\n",
|
|
|
|
ha->flags.eeh_busy);
|
2010-09-04 05:20:50 +07:00
|
|
|
qla2x00_restart_timer(vha, WATCH_INTERVAL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-12-16 12:29:46 +07:00
|
|
|
/* Hardware read to raise pending EEH errors during mailbox waits. */
|
|
|
|
if (!pci_channel_offline(ha->pdev))
|
|
|
|
pci_read_config_word(ha->pdev, PCI_VENDOR_ID, &w);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-05-11 01:18:18 +07:00
|
|
|
/* Make sure qla82xx_watchdog is run only for physical port */
|
|
|
|
if (!vha->vp_idx && IS_QLA82XX(ha)) {
|
2010-12-22 07:00:14 +07:00
|
|
|
if (test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags))
|
|
|
|
start_dpc++;
|
|
|
|
qla82xx_watchdog(vha);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Loop down handler. */
|
2008-11-07 01:40:51 +07:00
|
|
|
if (atomic_read(&vha->loop_down_timer) > 0 &&
|
2011-03-31 01:46:26 +07:00
|
|
|
!(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) &&
|
|
|
|
!(test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags))
|
2008-11-07 01:40:51 +07:00
|
|
|
&& vha->flags.online) {
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
if (atomic_read(&vha->loop_down_timer) ==
|
|
|
|
vha->loop_down_abort_time) {
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_info, vha, 0x6008,
|
|
|
|
"Loop down - aborting the queues before time expires.\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
if (!IS_QLA2100(ha) && vha->link_down_timeout)
|
|
|
|
atomic_set(&vha->loop_state, LOOP_DEAD);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-01-13 03:59:48 +07:00
|
|
|
/*
|
|
|
|
* Schedule an ISP abort to return any FCP2-device
|
|
|
|
* commands.
|
|
|
|
*/
|
2007-07-06 03:16:51 +07:00
|
|
|
/* NPIV - scan physical port only */
|
2008-11-07 01:40:51 +07:00
|
|
|
if (!vha->vp_idx) {
|
2007-07-06 03:16:51 +07:00
|
|
|
spin_lock_irqsave(&ha->hardware_lock,
|
|
|
|
cpu_flags);
|
2008-12-10 07:45:39 +07:00
|
|
|
req = ha->req_q_map[0];
|
2007-07-06 03:16:51 +07:00
|
|
|
for (index = 1;
|
2013-01-30 15:34:37 +07:00
|
|
|
index < req->num_outstanding_cmds;
|
2007-07-06 03:16:51 +07:00
|
|
|
index++) {
|
|
|
|
fc_port_t *sfcp;
|
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
sp = req->outstanding_cmds[index];
|
2007-07-06 03:16:51 +07:00
|
|
|
if (!sp)
|
|
|
|
continue;
|
2012-02-10 02:15:36 +07:00
|
|
|
if (sp->type != SRB_SCSI_CMD)
|
2009-08-21 01:06:04 +07:00
|
|
|
continue;
|
2007-07-06 03:16:51 +07:00
|
|
|
sfcp = sp->fcport;
|
2010-01-13 03:59:48 +07:00
|
|
|
if (!(sfcp->flags & FCF_FCP2_DEVICE))
|
2007-07-06 03:16:51 +07:00
|
|
|
continue;
|
2005-04-18 03:06:53 +07:00
|
|
|
|
2011-03-31 01:46:26 +07:00
|
|
|
if (IS_QLA82XX(ha))
|
|
|
|
set_bit(FCOE_CTX_RESET_NEEDED,
|
|
|
|
&vha->dpc_flags);
|
|
|
|
else
|
|
|
|
set_bit(ISP_ABORT_NEEDED,
|
2008-11-07 01:40:51 +07:00
|
|
|
&vha->dpc_flags);
|
2007-07-06 03:16:51 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock,
|
2008-11-07 01:40:51 +07:00
|
|
|
cpu_flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
start_dpc++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if the loop has been down for 4 minutes, reinit adapter */
|
2008-11-07 01:40:51 +07:00
|
|
|
if (atomic_dec_and_test(&vha->loop_down_timer) != 0) {
|
2009-08-26 01:36:19 +07:00
|
|
|
if (!(vha->device_flags & DFLG_NO_CABLE)) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_warn, vha, 0x6009,
|
2005-04-17 05:20:36 +07:00
|
|
|
"Loop down - aborting ISP.\n");
|
|
|
|
|
2011-03-31 01:46:26 +07:00
|
|
|
if (IS_QLA82XX(ha))
|
|
|
|
set_bit(FCOE_CTX_RESET_NEEDED,
|
|
|
|
&vha->dpc_flags);
|
|
|
|
else
|
|
|
|
set_bit(ISP_ABORT_NEEDED,
|
|
|
|
&vha->dpc_flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_timer, vha, 0x600a,
|
|
|
|
"Loop down - seconds remaining %d.\n",
|
|
|
|
atomic_read(&vha->loop_down_timer));
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2011-05-11 01:18:18 +07:00
|
|
|
/* Check if beacon LED needs to be blinked for physical host only */
|
|
|
|
if (!vha->vp_idx && (ha->beacon_blink_led == 1)) {
|
2011-08-17 01:31:45 +07:00
|
|
|
/* There is no beacon_blink function for ISP82xx */
|
|
|
|
if (!IS_QLA82XX(ha)) {
|
|
|
|
set_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags);
|
|
|
|
start_dpc++;
|
|
|
|
}
|
2006-02-01 07:05:07 +07:00
|
|
|
}
|
|
|
|
|
2008-04-25 05:21:23 +07:00
|
|
|
/* Process any deferred work. */
|
2008-11-07 01:40:51 +07:00
|
|
|
if (!list_empty(&vha->work_list))
|
2008-04-25 05:21:23 +07:00
|
|
|
start_dpc++;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Schedule the DPC routine if needed */
|
2008-11-07 01:40:51 +07:00
|
|
|
if ((test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) ||
|
|
|
|
test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags) ||
|
|
|
|
test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags) ||
|
2005-04-17 05:20:36 +07:00
|
|
|
start_dpc ||
|
2008-11-07 01:40:51 +07:00
|
|
|
test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) ||
|
|
|
|
test_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags) ||
|
2010-04-13 07:59:55 +07:00
|
|
|
test_bit(ISP_UNRECOVERABLE, &vha->dpc_flags) ||
|
|
|
|
test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags) ||
|
2008-11-07 01:40:51 +07:00
|
|
|
test_bit(VP_DPC_NEEDED, &vha->dpc_flags) ||
|
2013-01-30 15:34:38 +07:00
|
|
|
test_bit(RELOGIN_NEEDED, &vha->dpc_flags) ||
|
|
|
|
test_bit(HOST_RAMP_DOWN_QUEUE_DEPTH, &vha->dpc_flags) ||
|
|
|
|
test_bit(HOST_RAMP_UP_QUEUE_DEPTH, &vha->dpc_flags))) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_timer, vha, 0x600b,
|
|
|
|
"isp_abort_needed=%d loop_resync_needed=%d "
|
|
|
|
"fcport_update_needed=%d start_dpc=%d "
|
|
|
|
"reset_marker_needed=%d",
|
|
|
|
test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags),
|
|
|
|
test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags),
|
|
|
|
test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags),
|
|
|
|
start_dpc,
|
|
|
|
test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags));
|
|
|
|
ql_dbg(ql_dbg_timer, vha, 0x600c,
|
|
|
|
"beacon_blink_needed=%d isp_unrecoverable=%d "
|
|
|
|
"fcoe_ctx_reset_needed=%d vp_dpc_needed=%d "
|
2013-01-30 15:34:38 +07:00
|
|
|
"relogin_needed=%d, host_ramp_down_needed=%d "
|
|
|
|
"host_ramp_up_needed=%d.\n",
|
2011-07-15 02:00:13 +07:00
|
|
|
test_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags),
|
|
|
|
test_bit(ISP_UNRECOVERABLE, &vha->dpc_flags),
|
|
|
|
test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags),
|
|
|
|
test_bit(VP_DPC_NEEDED, &vha->dpc_flags),
|
2013-01-30 15:34:38 +07:00
|
|
|
test_bit(RELOGIN_NEEDED, &vha->dpc_flags),
|
|
|
|
test_bit(HOST_RAMP_UP_QUEUE_DEPTH, &vha->dpc_flags),
|
|
|
|
test_bit(HOST_RAMP_DOWN_QUEUE_DEPTH, &vha->dpc_flags));
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2xxx_wake_dpc(vha);
|
2011-07-15 02:00:13 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_restart_timer(vha, WATCH_INTERVAL);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2005-11-10 06:49:04 +07:00
|
|
|
/* Firmware interface routines. */
|
|
|
|
|
2012-02-10 02:15:34 +07:00
|
|
|
#define FW_BLOBS 10
|
2005-11-10 06:49:04 +07:00
|
|
|
#define FW_ISP21XX 0
|
|
|
|
#define FW_ISP22XX 1
|
|
|
|
#define FW_ISP2300 2
|
|
|
|
#define FW_ISP2322 3
|
2006-03-10 05:27:18 +07:00
|
|
|
#define FW_ISP24XX 4
|
2007-07-20 10:37:34 +07:00
|
|
|
#define FW_ISP25XX 5
|
2009-01-06 02:18:11 +07:00
|
|
|
#define FW_ISP81XX 6
|
2010-04-13 07:59:55 +07:00
|
|
|
#define FW_ISP82XX 7
|
2012-02-10 02:15:34 +07:00
|
|
|
#define FW_ISP2031 8
|
|
|
|
#define FW_ISP8031 9
|
2005-11-10 06:49:04 +07:00
|
|
|
|
2006-10-03 02:00:48 +07:00
|
|
|
#define FW_FILE_ISP21XX "ql2100_fw.bin"
|
|
|
|
#define FW_FILE_ISP22XX "ql2200_fw.bin"
|
|
|
|
#define FW_FILE_ISP2300 "ql2300_fw.bin"
|
|
|
|
#define FW_FILE_ISP2322 "ql2322_fw.bin"
|
|
|
|
#define FW_FILE_ISP24XX "ql2400_fw.bin"
|
2007-07-20 10:37:34 +07:00
|
|
|
#define FW_FILE_ISP25XX "ql2500_fw.bin"
|
2009-01-06 02:18:11 +07:00
|
|
|
#define FW_FILE_ISP81XX "ql8100_fw.bin"
|
2010-04-13 07:59:55 +07:00
|
|
|
#define FW_FILE_ISP82XX "ql8200_fw.bin"
|
2012-02-10 02:15:34 +07:00
|
|
|
#define FW_FILE_ISP2031 "ql2600_fw.bin"
|
|
|
|
#define FW_FILE_ISP8031 "ql8300_fw.bin"
|
2006-10-03 02:00:48 +07:00
|
|
|
|
2008-05-13 12:21:10 +07:00
|
|
|
static DEFINE_MUTEX(qla_fw_lock);
|
2005-11-10 06:49:04 +07:00
|
|
|
|
|
|
|
static struct fw_blob qla_fw_blobs[FW_BLOBS] = {
|
2006-10-03 02:00:48 +07:00
|
|
|
{ .name = FW_FILE_ISP21XX, .segs = { 0x1000, 0 }, },
|
|
|
|
{ .name = FW_FILE_ISP22XX, .segs = { 0x1000, 0 }, },
|
|
|
|
{ .name = FW_FILE_ISP2300, .segs = { 0x800, 0 }, },
|
|
|
|
{ .name = FW_FILE_ISP2322, .segs = { 0x800, 0x1c000, 0x1e000, 0 }, },
|
|
|
|
{ .name = FW_FILE_ISP24XX, },
|
2007-07-20 10:37:34 +07:00
|
|
|
{ .name = FW_FILE_ISP25XX, },
|
2009-01-06 02:18:11 +07:00
|
|
|
{ .name = FW_FILE_ISP81XX, },
|
2010-04-13 07:59:55 +07:00
|
|
|
{ .name = FW_FILE_ISP82XX, },
|
2012-02-10 02:15:34 +07:00
|
|
|
{ .name = FW_FILE_ISP2031, },
|
|
|
|
{ .name = FW_FILE_ISP8031, },
|
2005-11-10 06:49:04 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
struct fw_blob *
|
2008-11-07 01:40:51 +07:00
|
|
|
qla2x00_request_firmware(scsi_qla_host_t *vha)
|
2005-11-10 06:49:04 +07:00
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
struct qla_hw_data *ha = vha->hw;
|
2005-11-10 06:49:04 +07:00
|
|
|
struct fw_blob *blob;
|
|
|
|
|
|
|
|
if (IS_QLA2100(ha)) {
|
|
|
|
blob = &qla_fw_blobs[FW_ISP21XX];
|
|
|
|
} else if (IS_QLA2200(ha)) {
|
|
|
|
blob = &qla_fw_blobs[FW_ISP22XX];
|
2006-03-10 05:27:18 +07:00
|
|
|
} else if (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA6312(ha)) {
|
2005-11-10 06:49:04 +07:00
|
|
|
blob = &qla_fw_blobs[FW_ISP2300];
|
2006-03-10 05:27:18 +07:00
|
|
|
} else if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
|
2005-11-10 06:49:04 +07:00
|
|
|
blob = &qla_fw_blobs[FW_ISP2322];
|
2008-04-04 03:13:26 +07:00
|
|
|
} else if (IS_QLA24XX_TYPE(ha)) {
|
2005-11-10 06:49:04 +07:00
|
|
|
blob = &qla_fw_blobs[FW_ISP24XX];
|
2007-07-20 10:37:34 +07:00
|
|
|
} else if (IS_QLA25XX(ha)) {
|
|
|
|
blob = &qla_fw_blobs[FW_ISP25XX];
|
2009-01-06 02:18:11 +07:00
|
|
|
} else if (IS_QLA81XX(ha)) {
|
|
|
|
blob = &qla_fw_blobs[FW_ISP81XX];
|
2010-04-13 07:59:55 +07:00
|
|
|
} else if (IS_QLA82XX(ha)) {
|
|
|
|
blob = &qla_fw_blobs[FW_ISP82XX];
|
2012-02-10 02:15:34 +07:00
|
|
|
} else if (IS_QLA2031(ha)) {
|
|
|
|
blob = &qla_fw_blobs[FW_ISP2031];
|
|
|
|
} else if (IS_QLA8031(ha)) {
|
|
|
|
blob = &qla_fw_blobs[FW_ISP8031];
|
2012-02-21 14:29:40 +07:00
|
|
|
} else {
|
|
|
|
return NULL;
|
2005-11-10 06:49:04 +07:00
|
|
|
}
|
|
|
|
|
2008-05-13 12:21:10 +07:00
|
|
|
mutex_lock(&qla_fw_lock);
|
2005-11-10 06:49:04 +07:00
|
|
|
if (blob->fw)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (request_firmware(&blob->fw, blob->name, &ha->pdev->dev)) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_warn, vha, 0x0063,
|
|
|
|
"Failed to load firmware image (%s).\n", blob->name);
|
2005-11-10 06:49:04 +07:00
|
|
|
blob->fw = NULL;
|
|
|
|
blob = NULL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2008-05-13 12:21:10 +07:00
|
|
|
mutex_unlock(&qla_fw_lock);
|
2005-11-10 06:49:04 +07:00
|
|
|
return blob;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
qla2x00_release_firmware(void)
|
|
|
|
{
|
|
|
|
int idx;
|
|
|
|
|
2008-05-13 12:21:10 +07:00
|
|
|
mutex_lock(&qla_fw_lock);
|
2005-11-10 06:49:04 +07:00
|
|
|
for (idx = 0; idx < FW_BLOBS; idx++)
|
2012-04-10 03:51:41 +07:00
|
|
|
release_firmware(qla_fw_blobs[idx].fw);
|
2008-05-13 12:21:10 +07:00
|
|
|
mutex_unlock(&qla_fw_lock);
|
2005-11-10 06:49:04 +07:00
|
|
|
}
|
|
|
|
|
2007-09-21 04:07:36 +07:00
|
|
|
static pci_ers_result_t
|
|
|
|
qla2xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
|
|
|
|
{
|
2009-12-16 12:29:46 +07:00
|
|
|
scsi_qla_host_t *vha = pci_get_drvdata(pdev);
|
|
|
|
struct qla_hw_data *ha = vha->hw;
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_aer, vha, 0x9000,
|
|
|
|
"PCI error detected, state %x.\n", state);
|
2009-03-24 23:08:18 +07:00
|
|
|
|
2007-09-21 04:07:36 +07:00
|
|
|
switch (state) {
|
|
|
|
case pci_channel_io_normal:
|
2009-12-16 12:29:46 +07:00
|
|
|
ha->flags.eeh_busy = 0;
|
2007-09-21 04:07:36 +07:00
|
|
|
return PCI_ERS_RESULT_CAN_RECOVER;
|
|
|
|
case pci_channel_io_frozen:
|
2009-12-16 12:29:46 +07:00
|
|
|
ha->flags.eeh_busy = 1;
|
2010-09-04 05:20:50 +07:00
|
|
|
/* For ISP82XX complete any pending mailbox cmd */
|
|
|
|
if (IS_QLA82XX(ha)) {
|
2011-02-24 06:27:10 +07:00
|
|
|
ha->flags.isp82xx_fw_hung = 1;
|
2011-11-19 00:02:17 +07:00
|
|
|
ql_dbg(ql_dbg_aer, vha, 0x9001, "Pci channel io frozen\n");
|
|
|
|
qla82xx_clear_pending_mbx(vha);
|
2010-09-04 05:20:50 +07:00
|
|
|
}
|
2010-01-13 04:02:46 +07:00
|
|
|
qla2x00_free_irqs(vha);
|
2007-09-21 04:07:36 +07:00
|
|
|
pci_disable_device(pdev);
|
2010-09-04 05:20:53 +07:00
|
|
|
/* Return back all IOs */
|
|
|
|
qla2x00_abort_all_cmds(vha, DID_RESET << 16);
|
2007-09-21 04:07:36 +07:00
|
|
|
return PCI_ERS_RESULT_NEED_RESET;
|
|
|
|
case pci_channel_io_perm_failure:
|
2009-12-16 12:29:46 +07:00
|
|
|
ha->flags.pci_channel_io_perm_failure = 1;
|
|
|
|
qla2x00_abort_all_cmds(vha, DID_NO_CONNECT << 16);
|
2007-09-21 04:07:36 +07:00
|
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
|
|
}
|
|
|
|
return PCI_ERS_RESULT_NEED_RESET;
|
|
|
|
}
|
|
|
|
|
|
|
|
static pci_ers_result_t
|
|
|
|
qla2xxx_pci_mmio_enabled(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
int risc_paused = 0;
|
|
|
|
uint32_t stat;
|
|
|
|
unsigned long flags;
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
|
|
|
|
struct qla_hw_data *ha = base_vha->hw;
|
2007-09-21 04:07:36 +07:00
|
|
|
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
|
|
|
|
struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
|
|
|
|
|
2010-09-04 05:20:57 +07:00
|
|
|
if (IS_QLA82XX(ha))
|
|
|
|
return PCI_ERS_RESULT_RECOVERED;
|
|
|
|
|
2007-09-21 04:07:36 +07:00
|
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
if (IS_QLA2100(ha) || IS_QLA2200(ha)){
|
|
|
|
stat = RD_REG_DWORD(®->hccr);
|
|
|
|
if (stat & HCCR_RISC_PAUSE)
|
|
|
|
risc_paused = 1;
|
|
|
|
} else if (IS_QLA23XX(ha)) {
|
|
|
|
stat = RD_REG_DWORD(®->u.isp2300.host_status);
|
|
|
|
if (stat & HSR_RISC_PAUSED)
|
|
|
|
risc_paused = 1;
|
|
|
|
} else if (IS_FWI2_CAPABLE(ha)) {
|
|
|
|
stat = RD_REG_DWORD(®24->host_status);
|
|
|
|
if (stat & HSRX_RISC_PAUSED)
|
|
|
|
risc_paused = 1;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
|
|
|
|
if (risc_paused) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_info, base_vha, 0x9003,
|
|
|
|
"RISC paused -- mmio_enabled, Dumping firmware.\n");
|
2008-11-07 01:40:51 +07:00
|
|
|
ha->isp_ops->fw_dump(base_vha, 0);
|
2007-09-21 04:07:36 +07:00
|
|
|
|
|
|
|
return PCI_ERS_RESULT_NEED_RESET;
|
|
|
|
} else
|
|
|
|
return PCI_ERS_RESULT_RECOVERED;
|
|
|
|
}
|
|
|
|
|
2012-11-21 14:40:29 +07:00
|
|
|
static uint32_t
|
|
|
|
qla82xx_error_recovery(scsi_qla_host_t *base_vha)
|
2010-09-04 05:20:50 +07:00
|
|
|
{
|
|
|
|
uint32_t rval = QLA_FUNCTION_FAILED;
|
|
|
|
uint32_t drv_active = 0;
|
|
|
|
struct qla_hw_data *ha = base_vha->hw;
|
|
|
|
int fn;
|
|
|
|
struct pci_dev *other_pdev = NULL;
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_aer, base_vha, 0x9006,
|
|
|
|
"Entered %s.\n", __func__);
|
2010-09-04 05:20:50 +07:00
|
|
|
|
|
|
|
set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
|
|
|
|
|
|
|
|
if (base_vha->flags.online) {
|
|
|
|
/* Abort all outstanding commands,
|
|
|
|
* so as to be requeued later */
|
|
|
|
qla2x00_abort_isp_cleanup(base_vha);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn = PCI_FUNC(ha->pdev->devfn);
|
|
|
|
while (fn > 0) {
|
|
|
|
fn--;
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_aer, base_vha, 0x9007,
|
|
|
|
"Finding pci device at function = 0x%x.\n", fn);
|
2010-09-04 05:20:50 +07:00
|
|
|
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)) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_aer, base_vha, 0x9008,
|
|
|
|
"Found PCI func available and enable at 0x%x.\n",
|
|
|
|
fn);
|
2010-09-04 05:20:50 +07:00
|
|
|
pci_dev_put(other_pdev);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pci_dev_put(other_pdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fn) {
|
|
|
|
/* Reset owner */
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_aer, base_vha, 0x9009,
|
|
|
|
"This devfn is reset owner = 0x%x.\n",
|
|
|
|
ha->pdev->devfn);
|
2010-09-04 05:20:50 +07:00
|
|
|
qla82xx_idc_lock(ha);
|
|
|
|
|
|
|
|
qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
2012-08-23 01:21:03 +07:00
|
|
|
QLA8XXX_DEV_INITIALIZING);
|
2010-09-04 05:20:50 +07:00
|
|
|
|
|
|
|
qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION,
|
|
|
|
QLA82XX_IDC_VERSION);
|
|
|
|
|
|
|
|
drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_aer, base_vha, 0x900a,
|
|
|
|
"drv_active = 0x%x.\n", drv_active);
|
2010-09-04 05:20:50 +07:00
|
|
|
|
|
|
|
qla82xx_idc_unlock(ha);
|
|
|
|
/* Reset if device is not already reset
|
|
|
|
* drv_active would be 0 if a reset has already been done
|
|
|
|
*/
|
|
|
|
if (drv_active)
|
|
|
|
rval = qla82xx_start_firmware(base_vha);
|
|
|
|
else
|
|
|
|
rval = QLA_SUCCESS;
|
|
|
|
qla82xx_idc_lock(ha);
|
|
|
|
|
|
|
|
if (rval != QLA_SUCCESS) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_info, base_vha, 0x900b,
|
|
|
|
"HW State: FAILED.\n");
|
2010-09-04 05:20:50 +07:00
|
|
|
qla82xx_clear_drv_active(ha);
|
|
|
|
qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
2012-08-23 01:21:03 +07:00
|
|
|
QLA8XXX_DEV_FAILED);
|
2010-09-04 05:20:50 +07:00
|
|
|
} else {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_info, base_vha, 0x900c,
|
|
|
|
"HW State: READY.\n");
|
2010-09-04 05:20:50 +07:00
|
|
|
qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
2012-08-23 01:21:03 +07:00
|
|
|
QLA8XXX_DEV_READY);
|
2010-09-04 05:20:50 +07:00
|
|
|
qla82xx_idc_unlock(ha);
|
2011-02-24 06:27:10 +07:00
|
|
|
ha->flags.isp82xx_fw_hung = 0;
|
2010-09-04 05:20:50 +07:00
|
|
|
rval = qla82xx_restart_isp(base_vha);
|
|
|
|
qla82xx_idc_lock(ha);
|
|
|
|
/* Clear driver state register */
|
|
|
|
qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, 0);
|
|
|
|
qla82xx_set_drv_active(base_vha);
|
|
|
|
}
|
|
|
|
qla82xx_idc_unlock(ha);
|
|
|
|
} else {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_aer, base_vha, 0x900d,
|
|
|
|
"This devfn is not reset owner = 0x%x.\n",
|
|
|
|
ha->pdev->devfn);
|
2010-09-04 05:20:50 +07:00
|
|
|
if ((qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE) ==
|
2012-08-23 01:21:03 +07:00
|
|
|
QLA8XXX_DEV_READY)) {
|
2011-02-24 06:27:10 +07:00
|
|
|
ha->flags.isp82xx_fw_hung = 0;
|
2010-09-04 05:20:50 +07:00
|
|
|
rval = qla82xx_restart_isp(base_vha);
|
|
|
|
qla82xx_idc_lock(ha);
|
|
|
|
qla82xx_set_drv_active(base_vha);
|
|
|
|
qla82xx_idc_unlock(ha);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
|
|
|
|
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
|
2007-09-21 04:07:36 +07:00
|
|
|
static pci_ers_result_t
|
|
|
|
qla2xxx_pci_slot_reset(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
pci_ers_result_t ret = PCI_ERS_RESULT_DISCONNECT;
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
|
|
|
|
struct qla_hw_data *ha = base_vha->hw;
|
2010-01-13 04:02:46 +07:00
|
|
|
struct rsp_que *rsp;
|
|
|
|
int rc, retries = 10;
|
2007-12-20 11:28:09 +07:00
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_aer, base_vha, 0x9004,
|
|
|
|
"Slot Reset.\n");
|
2009-12-16 12:29:46 +07:00
|
|
|
|
2010-01-13 04:02:46 +07:00
|
|
|
/* Workaround: qla2xxx driver which access hardware earlier
|
|
|
|
* needs error state to be pci_channel_io_online.
|
|
|
|
* Otherwise mailbox command timesout.
|
|
|
|
*/
|
|
|
|
pdev->error_state = pci_channel_io_normal;
|
|
|
|
|
|
|
|
pci_restore_state(pdev);
|
|
|
|
|
2010-02-19 01:07:29 +07:00
|
|
|
/* pci_restore_state() clears the saved_state flag of the device
|
|
|
|
* save restored state which resets saved_state flag
|
|
|
|
*/
|
|
|
|
pci_save_state(pdev);
|
|
|
|
|
2007-12-20 11:28:09 +07:00
|
|
|
if (ha->mem_only)
|
|
|
|
rc = pci_enable_device_mem(pdev);
|
|
|
|
else
|
|
|
|
rc = pci_enable_device(pdev);
|
2007-09-21 04:07:36 +07:00
|
|
|
|
2007-12-20 11:28:09 +07:00
|
|
|
if (rc) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_warn, base_vha, 0x9005,
|
2007-09-21 04:07:36 +07:00
|
|
|
"Can't re-enable PCI device after reset.\n");
|
2010-09-04 05:20:50 +07:00
|
|
|
goto exit_slot_reset;
|
2007-09-21 04:07:36 +07:00
|
|
|
}
|
|
|
|
|
2010-01-13 04:02:46 +07:00
|
|
|
rsp = ha->rsp_q_map[0];
|
|
|
|
if (qla2x00_request_irqs(ha, rsp))
|
2010-09-04 05:20:50 +07:00
|
|
|
goto exit_slot_reset;
|
2010-01-13 04:02:46 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
if (ha->isp_ops->pci_config(base_vha))
|
2010-09-04 05:20:50 +07:00
|
|
|
goto exit_slot_reset;
|
|
|
|
|
|
|
|
if (IS_QLA82XX(ha)) {
|
|
|
|
if (qla82xx_error_recovery(base_vha) == QLA_SUCCESS) {
|
|
|
|
ret = PCI_ERS_RESULT_RECOVERED;
|
|
|
|
goto exit_slot_reset;
|
|
|
|
} else
|
|
|
|
goto exit_slot_reset;
|
|
|
|
}
|
2007-09-21 04:07:36 +07:00
|
|
|
|
2010-01-13 04:02:46 +07:00
|
|
|
while (ha->flags.mbox_busy && retries--)
|
|
|
|
msleep(1000);
|
2009-12-16 12:29:46 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
|
2010-04-13 07:59:55 +07:00
|
|
|
if (ha->isp_ops->abort_isp(base_vha) == QLA_SUCCESS)
|
2007-09-21 04:07:36 +07:00
|
|
|
ret = PCI_ERS_RESULT_RECOVERED;
|
2008-11-07 01:40:51 +07:00
|
|
|
clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
|
2007-09-21 04:07:36 +07:00
|
|
|
|
2010-01-13 04:02:46 +07:00
|
|
|
|
2010-09-04 05:20:50 +07:00
|
|
|
exit_slot_reset:
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_aer, base_vha, 0x900e,
|
|
|
|
"slot_reset return %x.\n", ret);
|
2009-12-16 12:29:46 +07:00
|
|
|
|
2007-09-21 04:07:36 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
qla2xxx_pci_resume(struct pci_dev *pdev)
|
|
|
|
{
|
2008-11-07 01:40:51 +07:00
|
|
|
scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
|
|
|
|
struct qla_hw_data *ha = base_vha->hw;
|
2007-09-21 04:07:36 +07:00
|
|
|
int ret;
|
|
|
|
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_dbg(ql_dbg_aer, base_vha, 0x900f,
|
|
|
|
"pci_resume.\n");
|
2009-12-16 12:29:46 +07:00
|
|
|
|
2008-11-07 01:40:51 +07:00
|
|
|
ret = qla2x00_wait_for_hba_online(base_vha);
|
2007-09-21 04:07:36 +07:00
|
|
|
if (ret != QLA_SUCCESS) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_fatal, base_vha, 0x9002,
|
|
|
|
"The device failed to resume I/O from slot/link_reset.\n");
|
2007-09-21 04:07:36 +07:00
|
|
|
}
|
2009-12-16 12:29:46 +07:00
|
|
|
|
2010-05-05 05:01:23 +07:00
|
|
|
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
|
|
|
2009-12-16 12:29:46 +07:00
|
|
|
ha->flags.eeh_busy = 0;
|
2007-09-21 04:07:36 +07:00
|
|
|
}
|
|
|
|
|
2012-09-07 23:33:16 +07:00
|
|
|
static const struct pci_error_handlers qla2xxx_err_handler = {
|
2007-09-21 04:07:36 +07:00
|
|
|
.error_detected = qla2xxx_pci_error_detected,
|
|
|
|
.mmio_enabled = qla2xxx_pci_mmio_enabled,
|
|
|
|
.slot_reset = qla2xxx_pci_slot_reset,
|
|
|
|
.resume = qla2xxx_pci_resume,
|
|
|
|
};
|
|
|
|
|
2005-11-10 06:49:04 +07:00
|
|
|
static struct pci_device_id qla2xxx_pci_tbl[] = {
|
2006-05-18 05:09:39 +07:00
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2100) },
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2200) },
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2300) },
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2312) },
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2322) },
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP6312) },
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP6322) },
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2422) },
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2432) },
|
2008-04-04 03:13:26 +07:00
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8432) },
|
2006-05-18 05:09:39 +07:00
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5422) },
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5432) },
|
2007-07-20 10:37:34 +07:00
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2532) },
|
2012-02-10 02:15:34 +07:00
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2031) },
|
2009-01-06 02:18:11 +07:00
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8001) },
|
2010-04-13 07:59:55 +07:00
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8021) },
|
2012-08-23 01:20:55 +07:00
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8031) },
|
2005-11-10 06:49:04 +07:00
|
|
|
{ 0 },
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, qla2xxx_pci_tbl);
|
|
|
|
|
2005-07-07 00:31:47 +07:00
|
|
|
static struct pci_driver qla2xxx_pci_driver = {
|
2006-05-18 05:09:45 +07:00
|
|
|
.name = QLA2XXX_DRIVER_NAME,
|
2005-12-02 01:51:50 +07:00
|
|
|
.driver = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
},
|
2005-07-07 00:31:47 +07:00
|
|
|
.id_table = qla2xxx_pci_tbl,
|
2006-06-24 06:11:22 +07:00
|
|
|
.probe = qla2x00_probe_one,
|
2008-01-14 15:55:16 +07:00
|
|
|
.remove = qla2x00_remove_one,
|
2010-10-16 01:27:46 +07:00
|
|
|
.shutdown = qla2x00_shutdown,
|
2007-09-21 04:07:36 +07:00
|
|
|
.err_handler = &qla2xxx_err_handler,
|
2005-07-07 00:31:47 +07:00
|
|
|
};
|
|
|
|
|
2010-05-05 05:01:24 +07:00
|
|
|
static struct file_operations apidev_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
llseek: automatically add .llseek fop
All file_operations should get a .llseek operation so we can make
nonseekable_open the default for future file operations without a
.llseek pointer.
The three cases that we can automatically detect are no_llseek, seq_lseek
and default_llseek. For cases where we can we can automatically prove that
the file offset is always ignored, we use noop_llseek, which maintains
the current behavior of not returning an error from a seek.
New drivers should normally not use noop_llseek but instead use no_llseek
and call nonseekable_open at open time. Existing drivers can be converted
to do the same when the maintainer knows for certain that no user code
relies on calling seek on the device file.
The generated code is often incorrectly indented and right now contains
comments that clarify for each added line why a specific variant was
chosen. In the version that gets submitted upstream, the comments will
be gone and I will manually fix the indentation, because there does not
seem to be a way to do that using coccinelle.
Some amount of new code is currently sitting in linux-next that should get
the same modifications, which I will do at the end of the merge window.
Many thanks to Julia Lawall for helping me learn to write a semantic
patch that does all this.
===== begin semantic patch =====
// This adds an llseek= method to all file operations,
// as a preparation for making no_llseek the default.
//
// The rules are
// - use no_llseek explicitly if we do nonseekable_open
// - use seq_lseek for sequential files
// - use default_llseek if we know we access f_pos
// - use noop_llseek if we know we don't access f_pos,
// but we still want to allow users to call lseek
//
@ open1 exists @
identifier nested_open;
@@
nested_open(...)
{
<+...
nonseekable_open(...)
...+>
}
@ open exists@
identifier open_f;
identifier i, f;
identifier open1.nested_open;
@@
int open_f(struct inode *i, struct file *f)
{
<+...
(
nonseekable_open(...)
|
nested_open(...)
)
...+>
}
@ read disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ read_no_fpos disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
... when != off
}
@ write @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ write_no_fpos @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
... when != off
}
@ fops0 @
identifier fops;
@@
struct file_operations fops = {
...
};
@ has_llseek depends on fops0 @
identifier fops0.fops;
identifier llseek_f;
@@
struct file_operations fops = {
...
.llseek = llseek_f,
...
};
@ has_read depends on fops0 @
identifier fops0.fops;
identifier read_f;
@@
struct file_operations fops = {
...
.read = read_f,
...
};
@ has_write depends on fops0 @
identifier fops0.fops;
identifier write_f;
@@
struct file_operations fops = {
...
.write = write_f,
...
};
@ has_open depends on fops0 @
identifier fops0.fops;
identifier open_f;
@@
struct file_operations fops = {
...
.open = open_f,
...
};
// use no_llseek if we call nonseekable_open
////////////////////////////////////////////
@ nonseekable1 depends on !has_llseek && has_open @
identifier fops0.fops;
identifier nso ~= "nonseekable_open";
@@
struct file_operations fops = {
... .open = nso, ...
+.llseek = no_llseek, /* nonseekable */
};
@ nonseekable2 depends on !has_llseek @
identifier fops0.fops;
identifier open.open_f;
@@
struct file_operations fops = {
... .open = open_f, ...
+.llseek = no_llseek, /* open uses nonseekable */
};
// use seq_lseek for sequential files
/////////////////////////////////////
@ seq depends on !has_llseek @
identifier fops0.fops;
identifier sr ~= "seq_read";
@@
struct file_operations fops = {
... .read = sr, ...
+.llseek = seq_lseek, /* we have seq_read */
};
// use default_llseek if there is a readdir
///////////////////////////////////////////
@ fops1 depends on !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier readdir_e;
@@
// any other fop is used that changes pos
struct file_operations fops = {
... .readdir = readdir_e, ...
+.llseek = default_llseek, /* readdir is present */
};
// use default_llseek if at least one of read/write touches f_pos
/////////////////////////////////////////////////////////////////
@ fops2 depends on !fops1 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read.read_f;
@@
// read fops use offset
struct file_operations fops = {
... .read = read_f, ...
+.llseek = default_llseek, /* read accesses f_pos */
};
@ fops3 depends on !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write.write_f;
@@
// write fops use offset
struct file_operations fops = {
... .write = write_f, ...
+ .llseek = default_llseek, /* write accesses f_pos */
};
// Use noop_llseek if neither read nor write accesses f_pos
///////////////////////////////////////////////////////////
@ fops4 depends on !fops1 && !fops2 && !fops3 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
identifier write_no_fpos.write_f;
@@
// write fops use offset
struct file_operations fops = {
...
.write = write_f,
.read = read_f,
...
+.llseek = noop_llseek, /* read and write both use no f_pos */
};
@ depends on has_write && !has_read && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write_no_fpos.write_f;
@@
struct file_operations fops = {
... .write = write_f, ...
+.llseek = noop_llseek, /* write uses no f_pos */
};
@ depends on has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
@@
struct file_operations fops = {
... .read = read_f, ...
+.llseek = noop_llseek, /* read uses no f_pos */
};
@ depends on !has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
@@
struct file_operations fops = {
...
+.llseek = noop_llseek, /* no read or write fn */
};
===== End semantic patch =====
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Julia Lawall <julia@diku.dk>
Cc: Christoph Hellwig <hch@infradead.org>
2010-08-15 23:52:59 +07:00
|
|
|
.llseek = noop_llseek,
|
2010-05-05 05:01:24 +07:00
|
|
|
};
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/**
|
|
|
|
* qla2x00_module_init - Module initialization.
|
|
|
|
**/
|
|
|
|
static int __init
|
|
|
|
qla2x00_module_init(void)
|
|
|
|
{
|
2005-07-07 00:31:47 +07:00
|
|
|
int ret = 0;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Allocate cache for SRBs. */
|
2005-04-23 13:47:27 +07:00
|
|
|
srb_cachep = kmem_cache_create("qla2xxx_srbs", sizeof(srb_t), 0,
|
2007-07-20 08:11:58 +07:00
|
|
|
SLAB_HWCACHE_ALIGN, NULL);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (srb_cachep == NULL) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_fatal, NULL, 0x0001,
|
|
|
|
"Unable to allocate SRB cache...Failing load!.\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2012-05-16 01:34:28 +07:00
|
|
|
/* Initialize target kmem_cache and mem_pools */
|
|
|
|
ret = qlt_init();
|
|
|
|
if (ret < 0) {
|
|
|
|
kmem_cache_destroy(srb_cachep);
|
|
|
|
return ret;
|
|
|
|
} else if (ret > 0) {
|
|
|
|
/*
|
|
|
|
* If initiator mode is explictly disabled by qlt_init(),
|
|
|
|
* prevent scsi_transport_fc.c:fc_scsi_scan_rport() from
|
|
|
|
* performing scsi_scan_target() during LOOP UP event.
|
|
|
|
*/
|
|
|
|
qla2xxx_transport_functions.disable_target_scan = 1;
|
|
|
|
qla2xxx_transport_vport_functions.disable_target_scan = 1;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Derive version string. */
|
|
|
|
strcpy(qla2x00_version_str, QLA2XXX_VERSION);
|
2006-10-06 23:54:59 +07:00
|
|
|
if (ql2xextended_error_logging)
|
2006-06-24 06:11:10 +07:00
|
|
|
strcat(qla2x00_version_str, "-debug");
|
|
|
|
|
2005-04-22 03:13:36 +07:00
|
|
|
qla2xxx_transport_template =
|
|
|
|
fc_attach_transport(&qla2xxx_transport_functions);
|
2007-07-06 03:16:51 +07:00
|
|
|
if (!qla2xxx_transport_template) {
|
|
|
|
kmem_cache_destroy(srb_cachep);
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_fatal, NULL, 0x0002,
|
|
|
|
"fc_attach_transport failed...Failing load!.\n");
|
2012-05-16 01:34:28 +07:00
|
|
|
qlt_exit();
|
2005-04-17 05:20:36 +07:00
|
|
|
return -ENODEV;
|
2007-07-06 03:16:51 +07:00
|
|
|
}
|
2010-05-05 05:01:24 +07:00
|
|
|
|
|
|
|
apidev_major = register_chrdev(0, QLA2XXX_APIDEV, &apidev_fops);
|
|
|
|
if (apidev_major < 0) {
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_fatal, NULL, 0x0003,
|
|
|
|
"Unable to register char device %s.\n", QLA2XXX_APIDEV);
|
2010-05-05 05:01:24 +07:00
|
|
|
}
|
|
|
|
|
2007-07-06 03:16:51 +07:00
|
|
|
qla2xxx_transport_vport_template =
|
|
|
|
fc_attach_transport(&qla2xxx_transport_vport_functions);
|
|
|
|
if (!qla2xxx_transport_vport_template) {
|
|
|
|
kmem_cache_destroy(srb_cachep);
|
2012-05-16 01:34:28 +07:00
|
|
|
qlt_exit();
|
2007-07-06 03:16:51 +07:00
|
|
|
fc_release_transport(qla2xxx_transport_template);
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_fatal, NULL, 0x0004,
|
|
|
|
"fc_attach_transport vport failed...Failing load!.\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
return -ENODEV;
|
2007-07-06 03:16:51 +07:00
|
|
|
}
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_info, NULL, 0x0005,
|
|
|
|
"QLogic Fibre Channel HBA Driver: %s.\n",
|
2008-05-13 12:21:08 +07:00
|
|
|
qla2x00_version_str);
|
2006-06-24 06:11:22 +07:00
|
|
|
ret = pci_register_driver(&qla2xxx_pci_driver);
|
2005-07-07 00:31:47 +07:00
|
|
|
if (ret) {
|
|
|
|
kmem_cache_destroy(srb_cachep);
|
2012-05-16 01:34:28 +07:00
|
|
|
qlt_exit();
|
2005-07-07 00:31:47 +07:00
|
|
|
fc_release_transport(qla2xxx_transport_template);
|
2007-07-06 03:16:51 +07:00
|
|
|
fc_release_transport(qla2xxx_transport_vport_template);
|
2011-07-15 02:00:13 +07:00
|
|
|
ql_log(ql_log_fatal, NULL, 0x0006,
|
|
|
|
"pci_register_driver failed...ret=%d Failing load!.\n",
|
|
|
|
ret);
|
2005-07-07 00:31:47 +07:00
|
|
|
}
|
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qla2x00_module_exit - Module cleanup.
|
|
|
|
**/
|
|
|
|
static void __exit
|
|
|
|
qla2x00_module_exit(void)
|
|
|
|
{
|
2010-05-05 05:01:24 +07:00
|
|
|
unregister_chrdev(apidev_major, QLA2XXX_APIDEV);
|
2006-06-24 06:11:22 +07:00
|
|
|
pci_unregister_driver(&qla2xxx_pci_driver);
|
2005-11-10 06:49:04 +07:00
|
|
|
qla2x00_release_firmware();
|
2005-04-23 13:47:27 +07:00
|
|
|
kmem_cache_destroy(srb_cachep);
|
2012-05-16 01:34:28 +07:00
|
|
|
qlt_exit();
|
2010-04-13 07:59:55 +07:00
|
|
|
if (ctx_cachep)
|
|
|
|
kmem_cache_destroy(ctx_cachep);
|
2005-04-17 05:20:36 +07:00
|
|
|
fc_release_transport(qla2xxx_transport_template);
|
2007-07-06 03:16:51 +07:00
|
|
|
fc_release_transport(qla2xxx_transport_vport_template);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(qla2x00_module_init);
|
|
|
|
module_exit(qla2x00_module_exit);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("QLogic Corporation");
|
|
|
|
MODULE_DESCRIPTION("QLogic Fibre Channel HBA Driver");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_VERSION(QLA2XXX_VERSION);
|
2006-10-03 02:00:48 +07:00
|
|
|
MODULE_FIRMWARE(FW_FILE_ISP21XX);
|
|
|
|
MODULE_FIRMWARE(FW_FILE_ISP22XX);
|
|
|
|
MODULE_FIRMWARE(FW_FILE_ISP2300);
|
|
|
|
MODULE_FIRMWARE(FW_FILE_ISP2322);
|
|
|
|
MODULE_FIRMWARE(FW_FILE_ISP24XX);
|
2008-02-01 03:33:45 +07:00
|
|
|
MODULE_FIRMWARE(FW_FILE_ISP25XX);
|