diff options
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_os.c')
| -rw-r--r-- | drivers/scsi/qla2xxx/qla_os.c | 4353 |
1 files changed, 3474 insertions, 879 deletions
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 3ddfa889e94..d96bfb55e57 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2008 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -11,29 +11,50 @@ #include <linux/delay.h> #include <linux/kthread.h> #include <linux/mutex.h> - +#include <linux/kobject.h> +#include <linux/slab.h> #include <scsi/scsi_tcq.h> #include <scsi/scsicam.h> #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_fc.h> +#include "qla_target.h" + /* * Driver version */ char qla2x00_version_str[40]; +static int apidev_major; + /* * SRB allocation cache */ static struct kmem_cache *srb_cachep; +/* + * CT6 CTX allocation cache + */ +static struct kmem_cache *ctx_cachep; +/* + * error level for logging + */ +int ql_errlev = ql_log_all; + +static int ql2xenableclass2; +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."); + + int ql2xlogintimeout = 20; -module_param(ql2xlogintimeout, int, S_IRUGO|S_IRUSR); +module_param(ql2xlogintimeout, int, S_IRUGO); MODULE_PARM_DESC(ql2xlogintimeout, "Login timeout value in seconds."); int qlport_down_retry; -module_param(qlport_down_retry, int, S_IRUGO|S_IRUSR); +module_param(qlport_down_retry, int, S_IRUGO); MODULE_PARM_DESC(qlport_down_retry, "Maximum number of command retries to a port that returns " "a PORT-DOWN status."); @@ -46,12 +67,12 @@ MODULE_PARM_DESC(ql2xplogiabsentdevice, "Default is 0 - no PLOGI. 1 - perfom PLOGI."); int ql2xloginretrycount = 0; -module_param(ql2xloginretrycount, int, S_IRUGO|S_IRUSR); +module_param(ql2xloginretrycount, int, S_IRUGO); MODULE_PARM_DESC(ql2xloginretrycount, "Specify an alternate value for the NVRAM login retry count."); int ql2xallocfwdump = 1; -module_param(ql2xallocfwdump, int, S_IRUGO|S_IRUSR); +module_param(ql2xallocfwdump, int, S_IRUGO); MODULE_PARM_DESC(ql2xallocfwdump, "Option to enable allocation of memory for a firmware dump " "during HBA initialization. Memory allocation requirements " @@ -60,42 +81,146 @@ MODULE_PARM_DESC(ql2xallocfwdump, int ql2xextended_error_logging; module_param(ql2xextended_error_logging, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(ql2xextended_error_logging, - "Option to enable extended error logging, " - "Default is 0 - no logging. 1 - log errors."); - -static void qla2x00_free_device(scsi_qla_host_t *); + "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" + "\t\t0x00008000 - Verbose. 0x00004000 - Target.\n" + "\t\t0x00002000 - Target Mgmt. 0x00001000 - Target TMF.\n" + "\t\t0x7fffffff - For enabling all logs, can be too many logs.\n" + "\t\t0x1e400000 - Preferred value for capturing essential " + "debug information (equivalent to old " + "ql2xextended_error_logging=1).\n" + "\t\tDo LOGICAL OR of the value to enable more than one level"); + +int ql2xshiftctondsd = 6; +module_param(ql2xshiftctondsd, int, S_IRUGO); +MODULE_PARM_DESC(ql2xshiftctondsd, + "Set to control shifting of command type processing " + "based on total number of SG elements."); int ql2xfdmienable=1; -module_param(ql2xfdmienable, int, S_IRUGO|S_IRUSR); +module_param(ql2xfdmienable, int, S_IRUGO); MODULE_PARM_DESC(ql2xfdmienable, - "Enables FDMI registratons " - "Default is 0 - no FDMI. 1 - perfom FDMI."); + "Enables FDMI registrations. " + "0 - no FDMI. Default is 1 - perform FDMI."); -#define MAX_Q_DEPTH 32 +#define MAX_Q_DEPTH 32 static int ql2xmaxqdepth = MAX_Q_DEPTH; module_param(ql2xmaxqdepth, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(ql2xmaxqdepth, - "Maximum queue depth to report for target devices."); - -int ql2xqfullrampup = 120; -module_param(ql2xqfullrampup, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(ql2xqfullrampup, - "Number of seconds to wait to begin to ramp-up the queue " - "depth for a device after a queue-full condition has been " - "detected. Default is 120 seconds."); + "Maximum queue depth to set for each LUN. " + "Default is 32."); + +int ql2xenabledif = 2; +module_param(ql2xenabledif, int, S_IRUGO); +MODULE_PARM_DESC(ql2xenabledif, + " Enable T10-CRC-DIF:\n" + " Default is 2.\n" + " 0 -- No DIF Support\n" + " 1 -- Enable DIF for all types\n" + " 2 -- Enable DIF for all types, except Type 0.\n"); + +int ql2xenablehba_err_chk = 2; +module_param(ql2xenablehba_err_chk, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(ql2xenablehba_err_chk, + " Enable T10-CRC-DIF Error isolation by HBA:\n" + " Default is 2.\n" + " 0 -- Error isolation disabled\n" + " 1 -- Error isolation enabled only for DIX Type 0\n" + " 2 -- Error isolation enabled for all Types\n"); int ql2xiidmaenable=1; -module_param(ql2xiidmaenable, int, S_IRUGO|S_IRUSR); +module_param(ql2xiidmaenable, int, S_IRUGO); MODULE_PARM_DESC(ql2xiidmaenable, "Enables iIDMA settings " "Default is 1 - perform iIDMA. 0 - no iIDMA."); int ql2xmaxqueues = 1; -module_param(ql2xmaxqueues, int, S_IRUGO|S_IRUSR); +module_param(ql2xmaxqueues, int, S_IRUGO); MODULE_PARM_DESC(ql2xmaxqueues, "Enables MQ settings " - "Default is 1 for single queue. Set it to number \ - of queues in MQ mode."); + "Default is 1 for single queue. Set it to number " + "of queues in MQ mode."); + +int ql2xmultique_tag; +module_param(ql2xmultique_tag, int, S_IRUGO); +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."); + +int ql2xfwloadbin; +module_param(ql2xfwloadbin, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(ql2xfwloadbin, + "Option to specify location from which to load ISP firmware:.\n" + " 2 -- load firmware via the request_firmware() (hotplug).\n" + " interface.\n" + " 1 -- load firmware from flash.\n" + " 0 -- use default semantics.\n"); + +int ql2xetsenable; +module_param(ql2xetsenable, int, S_IRUGO); +MODULE_PARM_DESC(ql2xetsenable, + "Enables firmware ETS burst." + "Default is 0 - skip ETS enablement."); + +int ql2xdbwr = 1; +module_param(ql2xdbwr, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(ql2xdbwr, + "Option to specify scheme for request queue posting.\n" + " 0 -- Regular doorbell.\n" + " 1 -- CAMRAM doorbell (faster).\n"); + +int ql2xtargetreset = 1; +module_param(ql2xtargetreset, int, S_IRUGO); +MODULE_PARM_DESC(ql2xtargetreset, + "Enable target reset." + "Default is 1 - use hw defaults."); + +int ql2xgffidenable; +module_param(ql2xgffidenable, int, S_IRUGO); +MODULE_PARM_DESC(ql2xgffidenable, + "Enables GFF_ID checks of port type. " + "Default is 0 - Do not use GFF_ID information."); + +int ql2xasynctmfenable; +module_param(ql2xasynctmfenable, int, S_IRUGO); +MODULE_PARM_DESC(ql2xasynctmfenable, + "Enables issue of TM IOCBs asynchronously via IOCB mechanism" + "Default is 0 - Issue TM IOCBs via mailbox mechanism."); + +int ql2xdontresethba; +module_param(ql2xdontresethba, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(ql2xdontresethba, + "Option to specify reset behaviour.\n" + " 0 (Default) -- Reset on failure.\n" + " 1 -- Do not reset on failure.\n"); + +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."); + +int ql2xmdcapmask = 0x1F; +module_param(ql2xmdcapmask, int, S_IRUGO); +MODULE_PARM_DESC(ql2xmdcapmask, + "Set the Minidump driver capture mask level. " + "Default is 0x1F - Can be set to 0x3, 0x7, 0xF, 0x1F, 0x7F."); + +int ql2xmdenable = 1; +module_param(ql2xmdenable, int, S_IRUGO); +MODULE_PARM_DESC(ql2xmdenable, + "Enable/disable MiniDump. " + "0 - MiniDump disabled. " + "1 (Default) - MiniDump enabled."); + /* * SCSI host template entry points */ @@ -104,23 +229,21 @@ static int qla2xxx_slave_alloc(struct scsi_device *); static int qla2xxx_scan_finished(struct Scsi_Host *, unsigned long time); static void qla2xxx_scan_start(struct Scsi_Host *); static void qla2xxx_slave_destroy(struct scsi_device *); -static int qla2x00_queuecommand(struct scsi_cmnd *cmd, - void (*fn)(struct scsi_cmnd *)); -static int qla24xx_queuecommand(struct scsi_cmnd *cmd, - void (*fn)(struct scsi_cmnd *)); +static int qla2xxx_queuecommand(struct Scsi_Host *h, struct scsi_cmnd *cmd); static int qla2xxx_eh_abort(struct scsi_cmnd *); static int qla2xxx_eh_device_reset(struct scsi_cmnd *); static int qla2xxx_eh_target_reset(struct scsi_cmnd *); static int qla2xxx_eh_bus_reset(struct scsi_cmnd *); static int qla2xxx_eh_host_reset(struct scsi_cmnd *); -static int qla2x00_change_queue_depth(struct scsi_device *, int); +static int qla2x00_change_queue_depth(struct scsi_device *, int, int); static int qla2x00_change_queue_type(struct scsi_device *, int); +static void qla2x00_free_device(scsi_qla_host_t *); -static struct scsi_host_template qla2x00_driver_template = { +struct scsi_host_template qla2xxx_driver_template = { .module = THIS_MODULE, .name = QLA2XXX_DRIVER_NAME, - .queuecommand = qla2x00_queuecommand, + .queuecommand = qla2xxx_queuecommand, .eh_abort_handler = qla2xxx_eh_abort, .eh_device_reset_handler = qla2xxx_eh_device_reset, @@ -141,40 +264,10 @@ static struct scsi_host_template qla2x00_driver_template = { .use_clustering = ENABLE_CLUSTERING, .sg_tablesize = SG_ALL, - /* - * The RISC allows for each command to transfer (2^32-1) bytes of data, - * which equates to 0x800000 sectors. - */ .max_sectors = 0xFFFF, .shost_attrs = qla2x00_host_attrs, -}; -struct scsi_host_template qla24xx_driver_template = { - .module = THIS_MODULE, - .name = QLA2XXX_DRIVER_NAME, - .queuecommand = qla24xx_queuecommand, - - .eh_abort_handler = qla2xxx_eh_abort, - .eh_device_reset_handler = qla2xxx_eh_device_reset, - .eh_target_reset_handler = qla2xxx_eh_target_reset, - .eh_bus_reset_handler = qla2xxx_eh_bus_reset, - .eh_host_reset_handler = qla2xxx_eh_host_reset, - - .slave_configure = qla2xxx_slave_configure, - - .slave_alloc = qla2xxx_slave_alloc, - .slave_destroy = qla2xxx_slave_destroy, - .scan_finished = qla2xxx_scan_finished, - .scan_start = qla2xxx_scan_start, - .change_queue_depth = qla2x00_change_queue_depth, - .change_queue_type = qla2x00_change_queue_type, - .this_id = -1, - .cmd_per_lun = 3, - .use_clustering = ENABLE_CLUSTERING, - .sg_tablesize = SG_ALL, - - .max_sectors = 0xFFFF, - .shost_attrs = qla2x00_host_attrs, + .supported_mode = MODE_INITIATOR, }; static struct scsi_transport_template *qla2xxx_transport_template = NULL; @@ -199,6 +292,13 @@ qla2x00_start_timer(scsi_qla_host_t *vha, void *func, unsigned long interval) static inline void qla2x00_restart_timer(scsi_qla_host_t *vha, unsigned long interval) { + /* Currently used for 82XX only. */ + if (vha->device_flags & DFLG_DEV_FAILED) { + ql_dbg(ql_dbg_timer, vha, 0x600d, + "Device in a failed state, returning.\n"); + return; + } + mod_timer(&vha->timer, jiffies + interval * HZ); } @@ -215,27 +315,35 @@ static void qla2x00_rst_aen(scsi_qla_host_t *); static int qla2x00_mem_alloc(struct qla_hw_data *, uint16_t, uint16_t, struct req_que **, struct rsp_que **); +static void qla2x00_free_fw_dump(struct qla_hw_data *); static void qla2x00_mem_free(struct qla_hw_data *); -static void qla2x00_sp_free_dma(srb_t *); /* -------------------------------------------------------------------------- */ -static int qla2x00_alloc_queues(struct qla_hw_data *ha) +static int qla2x00_alloc_queues(struct qla_hw_data *ha, struct req_que *req, + struct rsp_que *rsp) { - ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_queues, + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_req_queues, GFP_KERNEL); if (!ha->req_q_map) { - qla_printk(KERN_WARNING, ha, - "Unable to allocate memory for request queue ptrs\n"); + ql_log(ql_log_fatal, vha, 0x003b, + "Unable to allocate memory for request queue ptrs.\n"); goto fail_req_map; } - ha->rsp_q_map = kzalloc(sizeof(struct rsp_que *) * ha->max_queues, + ha->rsp_q_map = kzalloc(sizeof(struct rsp_que *) * ha->max_rsp_queues, GFP_KERNEL); if (!ha->rsp_q_map) { - qla_printk(KERN_WARNING, ha, - "Unable to allocate memory for response queue ptrs\n"); + ql_log(ql_log_fatal, vha, 0x003c, + "Unable to allocate memory for response queue ptrs.\n"); goto fail_rsp_map; } + /* + * 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; set_bit(0, ha->rsp_qid_map); set_bit(0, ha->req_qid_map); return 1; @@ -247,41 +355,116 @@ fail_req_map: return -ENOMEM; } -static void qla2x00_free_que(struct qla_hw_data *ha, struct req_que *req, - struct rsp_que *rsp) +static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req) { - if (rsp && rsp->ring) - dma_free_coherent(&ha->pdev->dev, - (rsp->length + 1) * sizeof(response_t), - rsp->ring, rsp->dma); - - kfree(rsp); - rsp = NULL; - if (req && req->ring) + if (IS_QLAFX00(ha)) { + if (req && req->ring_fx00) + dma_free_coherent(&ha->pdev->dev, + (req->length_fx00 + 1) * sizeof(request_t), + req->ring_fx00, req->dma_fx00); + } else if (req && req->ring) dma_free_coherent(&ha->pdev->dev, (req->length + 1) * sizeof(request_t), req->ring, req->dma); + if (req) + kfree(req->outstanding_cmds); + kfree(req); req = NULL; } +static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp) +{ + if (IS_QLAFX00(ha)) { + if (rsp && rsp->ring) + dma_free_coherent(&ha->pdev->dev, + (rsp->length_fx00 + 1) * sizeof(request_t), + rsp->ring_fx00, rsp->dma_fx00); + } else if (rsp && rsp->ring) { + dma_free_coherent(&ha->pdev->dev, + (rsp->length + 1) * sizeof(response_t), + rsp->ring, rsp->dma); + } + kfree(rsp); + rsp = NULL; +} + static void qla2x00_free_queues(struct qla_hw_data *ha) { struct req_que *req; struct rsp_que *rsp; int cnt; - for (cnt = 0; cnt < ha->max_queues; cnt++) { - rsp = ha->rsp_q_map[cnt]; + for (cnt = 0; cnt < ha->max_req_queues; cnt++) { req = ha->req_q_map[cnt]; - qla2x00_free_que(ha, req, rsp); + qla2x00_free_req_que(ha, req); + } + kfree(ha->req_q_map); + ha->req_q_map = NULL; + + 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; +} +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; + + if (!(ha->fw_attributes & BIT_6)) { + ql_log(ql_log_warn, vha, 0x00d8, + "Firmware is not multi-queue capable.\n"); + goto fail; + } + 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) { + ql_log(ql_log_warn, vha, 0x00e0, + "Failed to create request queue.\n"); + goto fail; + } + ha->wq = alloc_workqueue("qla2xxx_wq", WQ_MEM_RECLAIM, 1); + 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) { + ql_log(ql_log_warn, vha, 0x00e8, + "Failed to create response queue.\n"); + goto fail2; + } + } + ha->flags.cpu_affinity_enabled = 1; + 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); + } + return 0; +fail2: + qla25xx_delete_queues(vha); + destroy_workqueue(ha->wq); + ha->wq = NULL; + vha->req = ha->req_q_map[0]; +fail: + ha->mqenable = 0; kfree(ha->req_q_map); - ha->req_q_map = NULL; + kfree(ha->rsp_q_map); + ha->max_req_queues = ha->max_rsp_queues = 1; + return 1; } static char * @@ -314,26 +497,30 @@ qla24xx_pci_info_str(struct scsi_qla_host *vha, char *str) static char *pci_bus_modes[] = { "33", "66", "100", "133", }; struct qla_hw_data *ha = vha->hw; uint32_t pci_bus; - int pcie_reg; - pcie_reg = pci_find_capability(ha->pdev, PCI_CAP_ID_EXP); - if (pcie_reg) { + if (pci_is_pcie(ha->pdev)) { char lwstr[6]; - uint16_t pcie_lstat, lspeed, lwidth; + uint32_t lstat, lspeed, lwidth; - pcie_reg += 0x12; - pci_read_config_word(ha->pdev, pcie_reg, &pcie_lstat); - lspeed = pcie_lstat & (BIT_0 | BIT_1 | BIT_2 | BIT_3); - lwidth = (pcie_lstat & - (BIT_4 | BIT_5 | BIT_6 | BIT_7 | BIT_8 | BIT_9)) >> 4; + pcie_capability_read_dword(ha->pdev, PCI_EXP_LNKCAP, &lstat); + lspeed = lstat & PCI_EXP_LNKCAP_SLS; + lwidth = (lstat & PCI_EXP_LNKCAP_MLW) >> 4; strcpy(str, "PCIe ("); - if (lspeed == 1) + switch (lspeed) { + case 1: strcat(str, "2.5GT/s "); - else if (lspeed == 2) + break; + case 2: strcat(str, "5.0GT/s "); - else + break; + case 3: + strcat(str, "8.0GT/s "); + break; + default: strcat(str, "<unknown> "); + break; + } snprintf(lwstr, sizeof(lwstr), "x%d)", lwidth); strcat(str, lwstr); @@ -408,97 +595,84 @@ qla24xx_fw_version_str(struct scsi_qla_host *vha, char *str) return str; } -static inline srb_t * -qla2x00_get_new_sp(scsi_qla_host_t *vha, fc_port_t *fcport, - struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) -{ - srb_t *sp; - struct qla_hw_data *ha = vha->hw; - - sp = mempool_alloc(ha->srb_mempool, GFP_ATOMIC); - if (!sp) - return sp; - - sp->fcport = fcport; - sp->cmd = cmd; - sp->que = ha->req_q_map[0]; - sp->flags = 0; - CMD_SP(cmd) = (void *)sp; - cmd->scsi_done = done; - - return sp; -} - -static int -qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) +void +qla2x00_sp_free_dma(void *vha, void *ptr) { - scsi_qla_host_t *vha = shost_priv(cmd->device->host); - fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; - struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device)); - struct qla_hw_data *ha = vha->hw; - srb_t *sp; - int rval; + 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); - if (unlikely(pci_channel_offline(ha->pdev))) { - cmd->result = DID_REQUEUE << 16; - goto qc_fail_command; + if (sp->flags & SRB_DMA_VALID) { + scsi_dma_unmap(cmd); + sp->flags &= ~SRB_DMA_VALID; } - rval = fc_remote_port_chkready(rport); - if (rval) { - cmd->result = rval; - goto qc_fail_command; + 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; } - /* Close window on fcport/rport state-transitioning. */ - if (fcport->drport) - goto qc_target_busy; - - if (atomic_read(&fcport->state) != FCS_ONLINE) { - if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD || - atomic_read(&vha->loop_state) == LOOP_DEAD) { - cmd->result = DID_NO_CONNECT << 16; - goto qc_fail_command; - } - goto qc_target_busy; + if (sp->flags & SRB_CRC_CTX_DSD_VALID) { + /* List assured to be having elements */ + qla2x00_clean_dsd_pool(ha, sp, NULL); + sp->flags &= ~SRB_CRC_CTX_DSD_VALID; } - spin_unlock_irq(vha->host->host_lock); - - sp = qla2x00_get_new_sp(vha, fcport, cmd, done); - if (!sp) - goto qc_host_busy_lock; - - rval = ha->isp_ops->start_scsi(sp); - if (rval != QLA_SUCCESS) - goto qc_host_busy_free_sp; - - spin_lock_irq(vha->host->host_lock); - - return 0; + 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; + } -qc_host_busy_free_sp: - qla2x00_sp_free_dma(sp); - mempool_free(sp, ha->srb_mempool); + if (sp->flags & SRB_FCP_CMND_DMA_VALID) { + struct ct6_dsd *ctx1 = (struct ct6_dsd *)ctx; -qc_host_busy_lock: - spin_lock_irq(vha->host->host_lock); - return SCSI_MLQUEUE_HOST_BUSY; + 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; + } -qc_target_busy: - return SCSI_MLQUEUE_TARGET_BUSY; + CMD_SP(cmd) = NULL; + qla2x00_rel_sp(sp->fcport->vha, sp); +} -qc_fail_command: - done(cmd); +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; - return 0; + qla2x00_sp_free_dma(ha, sp); + cmd->scsi_done(cmd); } - +/* If we are SP1 here, we need to still take and release the host_lock as SP1 + * does not have the changes necessary to avoid taking host->host_lock. + */ static int -qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) +qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) { - scsi_qla_host_t *vha = shost_priv(cmd->device->host); + scsi_qla_host_t *vha = shost_priv(host); fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device)); struct qla_hw_data *ha = vha->hw; @@ -506,62 +680,91 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) srb_t *sp; int rval; - if (unlikely(pci_channel_offline(ha->pdev))) { - cmd->result = DID_REQUEUE << 16; + if (ha->flags.eeh_busy) { + if (ha->flags.pci_channel_io_perm_failure) { + ql_dbg(ql_dbg_aer, vha, 0x9010, + "PCI Channel IO permanent failure, exiting " + "cmd=%p.\n", cmd); + cmd->result = DID_NO_CONNECT << 16; + } else { + ql_dbg(ql_dbg_aer, vha, 0x9011, + "EEH_Busy, Requeuing the cmd=%p.\n", cmd); + cmd->result = DID_REQUEUE << 16; + } goto qc24_fail_command; } rval = fc_remote_port_chkready(rport); if (rval) { cmd->result = rval; + ql_dbg(ql_dbg_io + ql_dbg_verbose, vha, 0x3003, + "fc_remote_port_chkready failed for cmd=%p, rval=0x%x.\n", + cmd, rval); goto qc24_fail_command; } - /* Close window on fcport/rport state-transitioning. */ - if (fcport->drport) - goto qc24_target_busy; + if (!vha->flags.difdix_supported && + scsi_get_prot_op(cmd) != SCSI_PROT_NORMAL) { + ql_dbg(ql_dbg_io, vha, 0x3004, + "DIF Cap not reg, fail DIF capable cmd's:%p.\n", + cmd); + cmd->result = DID_NO_CONNECT << 16; + goto qc24_fail_command; + } + + if (!fcport) { + cmd->result = DID_NO_CONNECT << 16; + goto qc24_fail_command; + } if (atomic_read(&fcport->state) != FCS_ONLINE) { if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD || - atomic_read(&base_vha->loop_state) == LOOP_DEAD) { + atomic_read(&base_vha->loop_state) == LOOP_DEAD) { + 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)); cmd->result = DID_NO_CONNECT << 16; goto qc24_fail_command; } goto qc24_target_busy; } - spin_unlock_irq(vha->host->host_lock); - - sp = qla2x00_get_new_sp(base_vha, fcport, cmd, done); + sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC); if (!sp) - goto qc24_host_busy_lock; + goto qc24_host_busy; + + 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; rval = ha->isp_ops->start_scsi(sp); - if (rval != QLA_SUCCESS) + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_io + ql_dbg_verbose, vha, 0x3013, + "Start scsi failed rval=%d for cmd=%p.\n", rval, cmd); goto qc24_host_busy_free_sp; - - spin_lock_irq(vha->host->host_lock); + } return 0; qc24_host_busy_free_sp: - qla2x00_sp_free_dma(sp); - mempool_free(sp, ha->srb_mempool); + qla2x00_sp_free_dma(ha, sp); -qc24_host_busy_lock: - spin_lock_irq(vha->host->host_lock); +qc24_host_busy: return SCSI_MLQUEUE_HOST_BUSY; qc24_target_busy: return SCSI_MLQUEUE_TARGET_BUSY; qc24_fail_command: - done(cmd); + cmd->scsi_done(cmd); return 0; } - /* * qla2x00_eh_wait_on_command * Waits for the command to be returned by the Firmware for some @@ -578,15 +781,20 @@ static int qla2x00_eh_wait_on_command(struct scsi_cmnd *cmd) { #define ABORT_POLLING_PERIOD 1000 -#define ABORT_WAIT_ITER ((10 * 1000) / (ABORT_POLLING_PERIOD)) +#define ABORT_WAIT_ITER ((2 * 1000) / (ABORT_POLLING_PERIOD)) unsigned long wait_iter = ABORT_WAIT_ITER; + scsi_qla_host_t *vha = shost_priv(cmd->device->host); + struct qla_hw_data *ha = vha->hw; int ret = QLA_SUCCESS; - while (CMD_SP(cmd)) { - msleep(ABORT_POLLING_PERIOD); + if (unlikely(pci_channel_offline(ha->pdev)) || ha->flags.eeh_busy) { + ql_dbg(ql_dbg_taskm, vha, 0x8005, + "Return:eh_wait.\n"); + return ret; + } - if (--wait_iter) - break; + while (CMD_SP(cmd) && wait_iter--) { + msleep(ABORT_POLLING_PERIOD); } if (CMD_SP(cmd)) ret = QLA_FUNCTION_FAILED; @@ -636,9 +844,9 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *vha) } /* - * qla2x00_wait_for_loop_ready - * Wait for MAX_LOOP_TIMEOUT(5 min) value for loop - * to be in LOOP_READY state. + * qla2x00_wait_for_hba_ready + * Wait till the HBA is ready before doing driver unload + * * Input: * ha - pointer to host adapter structure * @@ -646,94 +854,49 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *vha) * Does context switching-Release SPIN_LOCK * (if any) before calling this routine. * - * - * Return: - * Success (LOOP_READY) : 0 - * Failed (LOOP_NOT_READY) : 1 */ -static inline int -qla2x00_wait_for_loop_ready(scsi_qla_host_t *vha) +static void +qla2x00_wait_for_hba_ready(scsi_qla_host_t *vha) { - int return_status = QLA_SUCCESS; - unsigned long loop_timeout ; struct qla_hw_data *ha = vha->hw; - scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); - /* wait for 5 min at the max for loop to be ready */ - loop_timeout = jiffies + (MAX_LOOP_TIMEOUT * HZ); - - while ((!atomic_read(&base_vha->loop_down_timer) && - atomic_read(&base_vha->loop_state) == LOOP_DOWN) || - atomic_read(&base_vha->loop_state) != LOOP_READY) { - if (atomic_read(&base_vha->loop_state) == LOOP_DEAD) { - return_status = QLA_FUNCTION_FAILED; - break; - } + while ((!(vha->flags.online) || ha->dpc_active || + ha->flags.mbox_busy)) msleep(1000); - if (time_after_eq(jiffies, loop_timeout)) { - return_status = QLA_FUNCTION_FAILED; - break; - } - } - return (return_status); } -void -qla2x00_abort_fcport_cmds(fc_port_t *fcport) +int +qla2x00_wait_for_chip_reset(scsi_qla_host_t *vha) { - int cnt, que, id; - unsigned long flags; - srb_t *sp; - scsi_qla_host_t *vha = fcport->vha; + int return_status; + unsigned long wait_reset; struct qla_hw_data *ha = vha->hw; - struct req_que *req; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); - spin_lock_irqsave(&ha->hardware_lock, flags); - for (que = 0; que < QLA_MAX_HOST_QUES; que++) { - id = vha->req_ques[que]; - req = ha->req_q_map[id]; - if (!req) - continue; - for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { - sp = req->outstanding_cmds[cnt]; - if (!sp) - continue; - if (sp->fcport != fcport) - continue; + 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)) { - spin_unlock_irqrestore(&ha->hardware_lock, flags); - if (ha->isp_ops->abort_command(vha, sp, req)) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "Abort failed -- %lx\n", - sp->cmd->serial_number)); - } else { - if (qla2x00_eh_wait_on_command(sp->cmd) != - QLA_SUCCESS) - DEBUG2(qla_printk(KERN_WARNING, ha, - "Abort failed while waiting -- %lx\n", - sp->cmd->serial_number)); - } - spin_lock_irqsave(&ha->hardware_lock, flags); - } + msleep(1000); + + if (!test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags) && + ha->flags.chip_reset_done) + break; } - spin_unlock_irqrestore(&ha->hardware_lock, flags); + if (ha->flags.chip_reset_done) + return_status = QLA_SUCCESS; + else + return_status = QLA_FUNCTION_FAILED; + + return return_status; } static void -qla2x00_block_error_handler(struct scsi_cmnd *cmnd) +sp_get(struct srb *sp) { - struct Scsi_Host *shost = cmnd->device->host; - struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); - unsigned long flags; - - spin_lock_irqsave(shost->host_lock, flags); - while (rport->port_state == FC_PORTSTATE_BLOCKED) { - spin_unlock_irqrestore(shost->host_lock, flags); - msleep(1000); - spin_lock_irqsave(shost->host_lock, flags); - } - spin_unlock_irqrestore(shost->host_lock, flags); - return; + atomic_inc(&sp->ref_count); } /************************************************************************** @@ -756,123 +919,131 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) { scsi_qla_host_t *vha = shost_priv(cmd->device->host); srb_t *sp; - int ret, i; + int ret; unsigned int id, lun; - unsigned long serial; unsigned long flags; - int wait = 0; + int rval, wait = 0; struct qla_hw_data *ha = vha->hw; - struct req_que *req; - srb_t *spt; - - qla2x00_block_error_handler(cmd); if (!CMD_SP(cmd)) return SUCCESS; + ret = fc_block_scsi_eh(cmd); + if (ret != 0) + return ret; ret = SUCCESS; id = cmd->device->id; lun = cmd->device->lun; - serial = cmd->serial_number; - spt = (srb_t *) CMD_SP(cmd); - if (!spt) - return SUCCESS; - req = spt->que; - /* Check active list for command command. */ spin_lock_irqsave(&ha->hardware_lock, flags); - for (i = 1; i < MAX_OUTSTANDING_COMMANDS; i++) { - sp = req->outstanding_cmds[i]; - - if (sp == NULL) - continue; + sp = (srb_t *) CMD_SP(cmd); + if (!sp) { + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return SUCCESS; + } - if (sp->cmd != cmd) - continue; + ql_dbg(ql_dbg_taskm, vha, 0x8002, + "Aborting from RISC nexus=%ld:%d:%d sp=%p cmd=%p\n", + vha->host_no, id, lun, sp, cmd); - DEBUG2(printk("%s(%ld): aborting sp %p from RISC." - " pid=%ld.\n", __func__, vha->host_no, sp, serial)); + /* Get a reference to the sp and drop the lock.*/ + sp_get(sp); - spin_unlock_irqrestore(&ha->hardware_lock, flags); - if (ha->isp_ops->abort_command(vha, sp, req)) { - DEBUG2(printk("%s(%ld): abort_command " - "mbx failed.\n", __func__, vha->host_no)); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + rval = ha->isp_ops->abort_command(sp); + if (rval) { + if (rval == QLA_FUNCTION_PARAMETER_ERROR) { + /* + * Decrement the ref_count since we can't find the + * command + */ + atomic_dec(&sp->ref_count); + ret = SUCCESS; + } else ret = FAILED; - } else { - DEBUG3(printk("%s(%ld): abort_command " - "mbx success.\n", __func__, vha->host_no)); - wait = 1; - } - spin_lock_irqsave(&ha->hardware_lock, flags); - break; + + ql_dbg(ql_dbg_taskm, vha, 0x8003, + "Abort command mbx failed cmd=%p, rval=%x.\n", cmd, rval); + } else { + ql_dbg(ql_dbg_taskm, vha, 0x8004, + "Abort command mbx success cmd=%p.\n", cmd); + wait = 1; } + + spin_lock_irqsave(&ha->hardware_lock, flags); + /* + * Clear the slot in the oustanding_cmds array if we can't find the + * command to reclaim the resources. + */ + if (rval == QLA_FUNCTION_PARAMETER_ERROR) + vha->req->outstanding_cmds[sp->handle] = NULL; + sp->done(ha, sp, 0); spin_unlock_irqrestore(&ha->hardware_lock, flags); + /* Did the command return during mailbox execution? */ + if (ret == FAILED && !CMD_SP(cmd)) + ret = SUCCESS; + /* Wait for the command to be returned. */ if (wait) { if (qla2x00_eh_wait_on_command(cmd) != QLA_SUCCESS) { - qla_printk(KERN_ERR, ha, - "scsi(%ld:%d:%d): Abort handler timed out -- %lx " - "%x.\n", vha->host_no, id, lun, serial, ret); + ql_log(ql_log_warn, vha, 0x8006, + "Abort handler timed out cmd=%p.\n", cmd); ret = FAILED; } } - qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d): Abort command issued -- %d %lx %x.\n", - vha->host_no, id, lun, wait, serial, ret); + ql_log(ql_log_info, vha, 0x801c, + "Abort command issued nexus=%ld:%d:%d -- %d %x.\n", + vha->host_no, id, lun, wait, ret); return ret; } -enum nexus_wait_type { - WAIT_HOST = 0, - WAIT_TARGET, - WAIT_LUN, -}; - -static int +int qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t, - unsigned int l, srb_t *sp, enum nexus_wait_type type) + unsigned int l, enum nexus_wait_type type) { int cnt, match, status; unsigned long flags; struct qla_hw_data *ha = vha->hw; struct req_que *req; + srb_t *sp; + struct scsi_cmnd *cmd; status = QLA_SUCCESS; - if (!sp) - return status; spin_lock_irqsave(&ha->hardware_lock, flags); - req = sp->que; + req = vha->req; for (cnt = 1; status == QLA_SUCCESS && - cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { + cnt < req->num_outstanding_cmds; cnt++) { sp = req->outstanding_cmds[cnt]; if (!sp) continue; - + if (sp->type != SRB_SCSI_CMD) + continue; if (vha->vp_idx != sp->fcport->vha->vp_idx) continue; match = 0; + cmd = GET_CMD_SP(sp); switch (type) { case WAIT_HOST: match = 1; break; case WAIT_TARGET: - match = sp->cmd->device->id == t; + match = cmd->device->id == t; break; case WAIT_LUN: - match = (sp->cmd->device->id == t && - sp->cmd->device->lun == l); + match = (cmd->device->id == t && + cmd->device->lun == l); break; } if (!match) continue; spin_unlock_irqrestore(&ha->hardware_lock, flags); - status = qla2x00_eh_wait_on_command(sp->cmd); + status = qla2x00_eh_wait_on_command(cmd); spin_lock_irqsave(&ha->hardware_lock, flags); } spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -889,43 +1060,56 @@ static char *reset_errors[] = { static int __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type, - struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int)) + struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int, int)) { scsi_qla_host_t *vha = shost_priv(cmd->device->host); fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; int err; - qla2x00_block_error_handler(cmd); - - if (!fcport) + if (!fcport) { return FAILED; + } + + err = fc_block_scsi_eh(cmd); + if (err != 0) + return err; - qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET ISSUED.\n", - vha->host_no, cmd->device->id, cmd->device->lun, name); + ql_log(ql_log_info, vha, 0x8009, + "%s RESET ISSUED nexus=%ld:%d:%d cmd=%p.\n", name, vha->host_no, + cmd->device->id, cmd->device->lun, cmd); err = 0; - if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) - goto eh_reset_failed; - err = 1; - if (qla2x00_wait_for_loop_ready(vha) != QLA_SUCCESS) + 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); goto eh_reset_failed; + } err = 2; - if (do_reset(fcport, cmd->device->lun) != QLA_SUCCESS) + if (do_reset(fcport, cmd->device->lun, cmd->request->cpu + 1) + != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x800c, + "do_reset failed for cmd=%p.\n", cmd); goto eh_reset_failed; + } err = 3; if (qla2x00_eh_wait_for_pending_commands(vha, cmd->device->id, - cmd->device->lun, (srb_t *) CMD_SP(cmd), type) != QLA_SUCCESS) + cmd->device->lun, type) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x800d, + "wait for pending cmds failed for cmd=%p.\n", cmd); goto eh_reset_failed; + } - qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET SUCCEEDED.\n", - vha->host_no, cmd->device->id, cmd->device->lun, name); + ql_log(ql_log_info, vha, 0x800e, + "%s RESET SUCCEEDED nexus:%ld:%d:%d cmd=%p.\n", name, + vha->host_no, cmd->device->id, cmd->device->lun, cmd); return SUCCESS; - eh_reset_failed: - qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET FAILED: %s.\n" - , vha->host_no, cmd->device->id, cmd->device->lun, name, - reset_errors[err]); +eh_reset_failed: + ql_log(ql_log_info, vha, 0x800f, + "%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); return FAILED; } @@ -971,41 +1155,46 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd) fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; int ret = FAILED; unsigned int id, lun; - unsigned long serial; - srb_t *sp = (srb_t *) CMD_SP(cmd); - - qla2x00_block_error_handler(cmd); id = cmd->device->id; lun = cmd->device->lun; - serial = cmd->serial_number; - if (!fcport) + if (!fcport) { return ret; + } + + ret = fc_block_scsi_eh(cmd); + if (ret != 0) + return ret; + ret = FAILED; - qla_printk(KERN_INFO, vha->hw, - "scsi(%ld:%d:%d): BUS RESET ISSUED.\n", vha->host_no, id, lun); + ql_log(ql_log_info, vha, 0x8012, + "BUS RESET ISSUED nexus=%ld:%d:%d.\n", vha->host_no, id, lun); if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) { - DEBUG2(printk("%s failed:board disabled\n",__func__)); + ql_log(ql_log_fatal, vha, 0x8013, + "Wait for hba online failed board disabled.\n"); goto eh_bus_reset_done; } - if (qla2x00_wait_for_loop_ready(vha) == QLA_SUCCESS) { - if (qla2x00_loop_reset(vha) == QLA_SUCCESS) - ret = SUCCESS; - } + if (qla2x00_loop_reset(vha) == QLA_SUCCESS) + ret = SUCCESS; + if (ret == FAILED) goto eh_bus_reset_done; /* Flush outstanding commands. */ - if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, sp, WAIT_HOST) != - QLA_SUCCESS) + if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, WAIT_HOST) != + QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x8014, + "Wait for pending commands failed.\n"); ret = FAILED; + } eh_bus_reset_done: - qla_printk(KERN_INFO, vha->hw, "%s: reset %s\n", __func__, - (ret == FAILED) ? "failed" : "succeded"); + ql_log(ql_log_warn, vha, 0x802b, + "BUS RESET %s nexus=%ld:%d:%d.\n", + (ret == FAILED) ? "FAILED" : "SUCCEEDED", vha->host_no, id, lun); return ret; } @@ -1029,62 +1218,63 @@ static int qla2xxx_eh_host_reset(struct scsi_cmnd *cmd) { scsi_qla_host_t *vha = shost_priv(cmd->device->host); - fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; struct qla_hw_data *ha = vha->hw; int ret = FAILED; unsigned int id, lun; - unsigned long serial; - srb_t *sp = (srb_t *) CMD_SP(cmd); scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); - qla2x00_block_error_handler(cmd); - id = cmd->device->id; lun = cmd->device->lun; - serial = cmd->serial_number; - if (!fcport) - return ret; - - qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d): ADAPTER RESET ISSUED.\n", vha->host_no, id, lun); - - if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) - goto eh_host_reset_lock; + ql_log(ql_log_info, vha, 0x8018, + "ADAPTER RESET ISSUED nexus=%ld:%d:%d.\n", vha->host_no, id, lun); /* - * Fixme-may be dpc thread is active and processing - * loop_resync,so wait a while for it to - * be completed and then issue big hammer.Otherwise - * it may cause I/O failure as big hammer marks the - * devices as lost kicking of the port_down_timer - * while dpc is stuck for the mailbox to complete. + * No point in issuing another reset if one is active. Also do not + * attempt a reset if we are updating flash. */ - qla2x00_wait_for_loop_ready(vha); + if (qla2x00_reset_active(vha) || ha->optrom_state != QLA_SWAITING) + goto eh_host_reset_lock; + if (vha != base_vha) { if (qla2x00_vp_abort_isp(vha)) goto eh_host_reset_lock; } else { + if (IS_P3P_TYPE(vha->hw)) { + if (!qla82xx_fcoe_ctx_reset(vha)) { + /* Ctx reset success */ + ret = SUCCESS; + goto eh_host_reset_lock; + } + /* fall thru if ctx reset failed */ + } + if (ha->wq) + flush_workqueue(ha->wq); + set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); - if (qla2x00_abort_isp(base_vha)) { + if (ha->isp_ops->abort_isp(base_vha)) { clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); /* failed. schedule dpc to try */ set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags); - if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) + if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x802a, + "wait for hba online failed.\n"); goto eh_host_reset_lock; + } } clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); } /* Waiting for command to be returned to OS.*/ - if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, sp, WAIT_HOST) == + if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, WAIT_HOST) == QLA_SUCCESS) ret = SUCCESS; eh_host_reset_lock: - qla_printk(KERN_INFO, ha, "%s: reset %s\n", __func__, - (ret == FAILED) ? "failed" : "succeded"); + ql_log(ql_log_info, vha, 0x8017, + "ADAPTER RESET %s nexus=%ld:%d:%d.\n", + (ret == FAILED) ? "FAILED" : "SUCCEEDED", vha->host_no, id, lun); return ret; } @@ -1106,41 +1296,43 @@ qla2x00_loop_reset(scsi_qla_host_t *vha) struct fc_port *fcport; struct qla_hw_data *ha = vha->hw; - if (ha->flags.enable_lip_full_login && !vha->vp_idx) { - ret = qla2x00_full_login_lip(vha); - if (ret != QLA_SUCCESS) { - DEBUG2_3(printk("%s(%ld): failed: " - "full_login_lip=%d.\n", __func__, vha->host_no, - ret)); - } - atomic_set(&vha->loop_state, LOOP_DOWN); - atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); - qla2x00_mark_all_devices_lost(vha, 0); - qla2x00_wait_for_loop_ready(vha); - } - - if (ha->flags.enable_lip_reset && !vha->vp_idx) { - ret = qla2x00_lip_reset(vha); - if (ret != QLA_SUCCESS) { - DEBUG2_3(printk("%s(%ld): failed: " - "lip_reset=%d.\n", __func__, vha->host_no, ret)); - } else - qla2x00_wait_for_loop_ready(vha); + if (IS_QLAFX00(ha)) { + return qlafx00_loop_reset(vha); } - if (ha->flags.enable_target_reset) { + if (ql2xtargetreset == 1 && ha->flags.enable_target_reset) { list_for_each_entry(fcport, &vha->vp_fcports, list) { if (fcport->port_type != FCT_TARGET) continue; - ret = ha->isp_ops->target_reset(fcport, 0); + ret = ha->isp_ops->target_reset(fcport, 0, 0); if (ret != QLA_SUCCESS) { - DEBUG2_3(printk("%s(%ld): bus_reset failed: " - "target_reset=%d d_id=%x.\n", __func__, - vha->host_no, ret, fcport->d_id.b24)); + ql_dbg(ql_dbg_taskm, vha, 0x802c, + "Bus Reset failed: Reset=%d " + "d_id=%x.\n", ret, fcport->d_id.b24); } } } + + + if (ha->flags.enable_lip_full_login && !IS_CNA_CAPABLE(ha)) { + atomic_set(&vha->loop_state, LOOP_DOWN); + atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); + qla2x00_mark_all_devices_lost(vha, 0); + ret = qla2x00_full_login_lip(vha); + if (ret != QLA_SUCCESS) { + ql_dbg(ql_dbg_taskm, vha, 0x802d, + "full_login_lip=%d.\n", ret); + } + } + + if (ha->flags.enable_lip_reset) { + ret = qla2x00_lip_reset(vha); + if (ret != QLA_SUCCESS) + ql_dbg(ql_dbg_taskm, vha, 0x802e, + "lip_reset failed (%d).\n", ret); + } + /* Issue marker command only when we are going to start the I/O */ vha->marker_needed = 1; @@ -1157,16 +1349,17 @@ qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res) struct req_que *req; spin_lock_irqsave(&ha->hardware_lock, flags); - for (que = 0; que < ha->max_queues; que++) { + for (que = 0; que < ha->max_req_queues; que++) { req = ha->req_q_map[que]; if (!req) continue; - for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { + if (!req->outstanding_cmds) + continue; + for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++) { sp = req->outstanding_cmds[cnt]; - if (sp && sp->fcport->vha == vha) { + if (sp) { req->outstanding_cmds[cnt] = NULL; - sp->cmd->result = res; - qla2x00_sp_compl(ha, sp); + sp->done(vha, sp, res); } } } @@ -1190,17 +1383,15 @@ static int qla2xxx_slave_configure(struct scsi_device *sdev) { scsi_qla_host_t *vha = shost_priv(sdev->host); - struct qla_hw_data *ha = vha->hw; - struct fc_rport *rport = starget_to_rport(sdev->sdev_target); - struct req_que *req = ha->req_q_map[vha->req_ques[0]]; + struct req_que *req = vha->req; + + if (IS_T10_PI_CAPABLE(vha->hw)) + blk_queue_update_dma_alignment(sdev->request_queue, 0x7); if (sdev->tagged_supported) scsi_activate_tcq(sdev, req->max_q_depth); else scsi_deactivate_tcq(sdev, req->max_q_depth); - - rport->dev_loss_tmo = ha->port_down_retry_count; - return 0; } @@ -1210,10 +1401,58 @@ qla2xxx_slave_destroy(struct scsi_device *sdev) sdev->hostdata = NULL; } +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; + + ql_dbg(ql_dbg_io, fcport->vha, 0x3029, + "Queue depth adjusted-down to %d for nexus=%ld:%d:%d.\n", + sdev->queue_depth, fcport->vha->host_no, sdev->id, sdev->lun); +} + +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); + + ql_dbg(ql_dbg_io, vha, 0x302a, + "Queue depth adjusted-up to %d for nexus=%ld:%d:%d.\n", + sdev->queue_depth, fcport->vha->host_no, sdev->id, sdev->lun); +} + static int -qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth) +qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) { - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); + 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: + return -EOPNOTSUPP; + } + return sdev->queue_depth; } @@ -1245,10 +1484,10 @@ qla2x00_config_dma_addressing(struct qla_hw_data *ha) /* Assume a 32bit DMA mask. */ ha->flags.enable_64bit_addressing = 0; - if (!dma_set_mask(&ha->pdev->dev, DMA_64BIT_MASK)) { + if (!dma_set_mask(&ha->pdev->dev, DMA_BIT_MASK(64))) { /* Any upper-dword bits set? */ if (MSD(dma_get_required_mask(&ha->pdev->dev)) && - !pci_set_consistent_dma_mask(ha->pdev, DMA_64BIT_MASK)) { + !pci_set_consistent_dma_mask(ha->pdev, DMA_BIT_MASK(64))) { /* Ok, a 64bit DMA mask is applicable. */ ha->flags.enable_64bit_addressing = 1; ha->isp_ops->calc_req_entries = qla2x00_calc_iocbs_64; @@ -1257,8 +1496,8 @@ qla2x00_config_dma_addressing(struct qla_hw_data *ha) } } - dma_set_mask(&ha->pdev->dev, DMA_32BIT_MASK); - pci_set_consistent_dma_mask(ha->pdev, DMA_32BIT_MASK); + dma_set_mask(&ha->pdev->dev, DMA_BIT_MASK(32)); + pci_set_consistent_dma_mask(ha->pdev, DMA_BIT_MASK(32)); } static void @@ -1318,6 +1557,208 @@ qla24xx_disable_intrs(struct qla_hw_data *ha) spin_unlock_irqrestore(&ha->hardware_lock, flags); } +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); +} + + +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; + + qlt_83xx_iospace_config(ha); + + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011f, + "MSIX Count:%d.\n", ha->msix_count); + return 0; + +iospace_error_exit: + return -ENOMEM; +} + static struct isp_operations qla2100_isp_ops = { .pci_config = qla2100_pci_config, .reset_chip = qla2x00_reset_chip, @@ -1351,9 +1792,9 @@ static struct isp_operations qla2100_isp_ops = { .write_optrom = qla2x00_write_optrom_data, .get_flash_version = qla2x00_get_flash_version, .start_scsi = qla2x00_start_scsi, - .wrt_req_reg = NULL, - .wrt_rsp_reg = NULL, - .rd_req_reg = NULL, + .abort_isp = qla2x00_abort_isp, + .iospace_config = qla2x00_iospace_config, + .initialize_adapter = qla2x00_initialize_adapter, }; static struct isp_operations qla2300_isp_ops = { @@ -1389,9 +1830,9 @@ static struct isp_operations qla2300_isp_ops = { .write_optrom = qla2x00_write_optrom_data, .get_flash_version = qla2x00_get_flash_version, .start_scsi = qla2x00_start_scsi, - .wrt_req_reg = NULL, - .wrt_rsp_reg = NULL, - .rd_req_reg = NULL, + .abort_isp = qla2x00_abort_isp, + .iospace_config = qla2x00_iospace_config, + .initialize_adapter = qla2x00_initialize_adapter, }; static struct isp_operations qla24xx_isp_ops = { @@ -1427,9 +1868,9 @@ static struct isp_operations qla24xx_isp_ops = { .write_optrom = qla24xx_write_optrom_data, .get_flash_version = qla24xx_get_flash_version, .start_scsi = qla24xx_start_scsi, - .wrt_req_reg = qla24xx_wrt_req_reg, - .wrt_rsp_reg = qla24xx_wrt_rsp_reg, - .rd_req_reg = qla24xx_rd_req_reg, + .abort_isp = qla2x00_abort_isp, + .iospace_config = qla2x00_iospace_config, + .initialize_adapter = qla2x00_initialize_adapter, }; static struct isp_operations qla25xx_isp_ops = { @@ -1464,10 +1905,10 @@ static struct isp_operations qla25xx_isp_ops = { .read_optrom = qla25xx_read_optrom_data, .write_optrom = qla24xx_write_optrom_data, .get_flash_version = qla24xx_get_flash_version, - .start_scsi = qla24xx_start_scsi, - .wrt_req_reg = qla24xx_wrt_req_reg, - .wrt_rsp_reg = qla24xx_wrt_rsp_reg, - .rd_req_reg = qla24xx_rd_req_reg, + .start_scsi = qla24xx_dif_start_scsi, + .abort_isp = qla2x00_abort_isp, + .iospace_config = qla2x00_iospace_config, + .initialize_adapter = qla2x00_initialize_adapter, }; static struct isp_operations qla81xx_isp_ops = { @@ -1493,19 +1934,209 @@ static struct isp_operations qla81xx_isp_ops = { .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, + .read_nvram = NULL, + .write_nvram = NULL, .fw_dump = qla81xx_fw_dump, .beacon_on = qla24xx_beacon_on, .beacon_off = qla24xx_beacon_off, - .beacon_blink = qla24xx_beacon_blink, + .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_start_scsi, - .wrt_req_reg = qla24xx_wrt_req_reg, - .wrt_rsp_reg = qla24xx_wrt_rsp_reg, - .rd_req_reg = qla24xx_rd_req_reg, + .start_scsi = qla24xx_dif_start_scsi, + .abort_isp = qla2x00_abort_isp, + .iospace_config = qla2x00_iospace_config, + .initialize_adapter = qla2x00_initialize_adapter, +}; + +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, + .pci_info_str = qla24xx_pci_info_str, + .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 = qla82xx_fw_dump, + .beacon_on = qla82xx_beacon_on, + .beacon_off = qla82xx_beacon_off, + .beacon_blink = NULL, + .read_optrom = qla82xx_read_optrom_data, + .write_optrom = qla82xx_write_optrom_data, + .get_flash_version = qla82xx_get_flash_version, + .start_scsi = qla82xx_start_scsi, + .abort_isp = qla82xx_abort_isp, + .iospace_config = qla82xx_iospace_config, + .initialize_adapter = qla2x00_initialize_adapter, +}; + +static struct isp_operations qla8044_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, + .pci_info_str = qla24xx_pci_info_str, + .fw_version_str = qla24xx_fw_version_str, + .intr_handler = qla8044_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 = NULL, + .write_nvram = NULL, + .fw_dump = qla8044_fw_dump, + .beacon_on = qla82xx_beacon_on, + .beacon_off = qla82xx_beacon_off, + .beacon_blink = NULL, + .read_optrom = qla8044_read_optrom_data, + .write_optrom = qla8044_write_optrom_data, + .get_flash_version = qla82xx_get_flash_version, + .start_scsi = qla82xx_start_scsi, + .abort_isp = qla8044_abort_isp, + .iospace_config = qla82xx_iospace_config, + .initialize_adapter = qla2x00_initialize_adapter, +}; + +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, + .initialize_adapter = qla2x00_initialize_adapter, +}; + +static struct isp_operations qlafx00_isp_ops = { + .pci_config = qlafx00_pci_config, + .reset_chip = qlafx00_soft_reset, + .chip_diag = qlafx00_chip_diag, + .config_rings = qlafx00_config_rings, + .reset_adapter = qlafx00_soft_reset, + .nvram_config = NULL, + .update_fw_options = NULL, + .load_risc = NULL, + .pci_info_str = qlafx00_pci_info_str, + .fw_version_str = qlafx00_fw_version_str, + .intr_handler = qlafx00_intr_handler, + .enable_intrs = qlafx00_enable_intrs, + .disable_intrs = qlafx00_disable_intrs, + .abort_command = qla24xx_async_abort_command, + .target_reset = qlafx00_abort_target, + .lun_reset = qlafx00_lun_reset, + .fabric_login = NULL, + .fabric_logout = NULL, + .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 = NULL, + .beacon_on = qla24xx_beacon_on, + .beacon_off = qla24xx_beacon_off, + .beacon_blink = NULL, + .read_optrom = qla24xx_read_optrom_data, + .write_optrom = qla24xx_write_optrom_data, + .get_flash_version = qla24xx_get_flash_version, + .start_scsi = qlafx00_start_scsi, + .abort_isp = qlafx00_abort_isp, + .iospace_config = qlafx00_iospace_config, + .initialize_adapter = qlafx00_initialize_adapter, +}; + +static struct isp_operations qla27xx_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 = qla27xx_fwdump, + .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, + .initialize_adapter = qla2x00_initialize_adapter, }; static inline void @@ -1594,94 +2225,71 @@ qla2x00_set_isp_flags(struct qla_hw_data *ha) ha->device_type |= DT_IIDMA; ha->fw_srisc_address = RISC_START_ADDRESS_2400; break; - } -} - -static int -qla2x00_iospace_config(struct qla_hw_data *ha) -{ - resource_size_t pio; - uint16_t msix; - - if (pci_request_selected_regions(ha->pdev, ha->bars, - QLA2XXX_DRIVER_NAME)) { - qla_printk(KERN_WARNING, ha, - "Failed to reserve PIO/MMIO regions (%s)\n", - pci_name(ha->pdev)); - - goto iospace_error_exit; - } - if (!(ha->bars & 1)) - goto skip_pio; - - /* We only need PIO for Flash operations on ISP2312 v2 chips. */ - pio = pci_resource_start(ha->pdev, 0); - if (pci_resource_flags(ha->pdev, 0) & IORESOURCE_IO) { - if (pci_resource_len(ha->pdev, 0) < MIN_IOBASE_LEN) { - qla_printk(KERN_WARNING, ha, - "Invalid PCI I/O region size (%s)...\n", - pci_name(ha->pdev)); - pio = 0; - } - } else { - qla_printk(KERN_WARNING, ha, - "region #0 not a PIO resource (%s)...\n", - pci_name(ha->pdev)); - pio = 0; - } - ha->pio_address = pio; - -skip_pio: - /* Use MMIO operations for all accesses. */ - if (!(pci_resource_flags(ha->pdev, 1) & IORESOURCE_MEM)) { - qla_printk(KERN_ERR, ha, - "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) { - qla_printk(KERN_ERR, ha, - "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) { - qla_printk(KERN_ERR, ha, - "cannot remap MMIO (%s), aborting\n", pci_name(ha->pdev)); - - goto iospace_error_exit; + 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; + case PCI_DEVICE_ID_QLOGIC_ISP8044: + ha->device_type |= DT_ISP8044; + 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; + 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; + case PCI_DEVICE_ID_QLOGIC_ISPF001: + ha->device_type |= DT_ISPFX00; + break; + case PCI_DEVICE_ID_QLOGIC_ISP2071: + ha->device_type |= DT_ISP2071; + ha->device_type |= DT_ZIO_SUPPORTED; + ha->device_type |= DT_FWI2; + ha->device_type |= DT_IIDMA; + ha->fw_srisc_address = RISC_START_ADDRESS_2400; + break; + case PCI_DEVICE_ID_QLOGIC_ISP2271: + ha->device_type |= DT_ISP2271; + 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; } - /* Determine queue resources */ - ha->max_queues = 1; - if (ql2xmaxqueues <= 1 || (!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) { - /* Read MSIX vector size of the board */ - pci_read_config_word(ha->pdev, QLA_PCI_MSIX_CONTROL, &msix); - ha->msix_count = msix; - /* Max queues are bounded by available msix vectors */ - /* queue 0 uses two msix vectors */ - if (ha->msix_count - 1 < ql2xmaxqueues) - ha->max_queues = ha->msix_count - 1; - else if (ql2xmaxqueues > QLA_MQ_SIZE) - ha->max_queues = QLA_MQ_SIZE; + 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); + if (IS_QLA27XX(ha)) + ha->port_no--; else - ha->max_queues = ql2xmaxqueues; - qla_printk(KERN_INFO, ha, - "MSI-X vector count: %d\n", msix); + ha->port_no = !(ha->port_no & 1); } -mqiobase_exit: - ha->msix_count = ha->max_queues + 1; - return (0); - -iospace_error_exit: - return (-ENOMEM); + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x000b, + "device_type=0x%x port=%d fw_srisc_address=0x%x.\n", + ha->device_type, ha->port_no, ha->fw_srisc_address); } static void @@ -1689,6 +2297,9 @@ qla2xxx_scan_start(struct Scsi_Host *shost) { scsi_qla_host_t *vha = shost_priv(shost); + if (vha->hw->flags.running_gold_fw) + return; + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); set_bit(RSCN_UPDATE, &vha->dpc_flags); @@ -1711,7 +2322,7 @@ qla2xxx_scan_finished(struct Scsi_Host *shost, unsigned long time) /* * PCI driver interface */ -static int __devinit +static int qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) { int ret = -ENODEV; @@ -1719,26 +2330,35 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) scsi_qla_host_t *base_vha = NULL; struct qla_hw_data *ha; char pci_info[30]; - char fw_str[30]; + char fw_str[30], wq_name[30]; struct scsi_host_template *sht; - int bars, max_id, mem_only = 0; + int bars, mem_only = 0; uint16_t req_length = 0, rsp_length = 0; struct req_que *req = NULL; struct rsp_que *rsp = NULL; - bars = pci_select_bars(pdev, IORESOURCE_MEM | IORESOURCE_IO); - sht = &qla2x00_driver_template; + sht = &qla2xxx_driver_template; if (pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2422 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2432 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8432 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5422 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5432 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2532 || - pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8001) { + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8001 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8021 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2031 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8031 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISPF001 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8044 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2071 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2271) { bars = pci_select_bars(pdev, IORESOURCE_MEM); - sht = &qla24xx_driver_template; mem_only = 1; + ql_dbg_pci(ql_dbg_init, pdev, 0x0007, + "Mem only adapter.\n"); } + ql_dbg_pci(ql_dbg_init, pdev, 0x0008, + "Bars=%d.\n", bars); if (mem_only) { if (pci_enable_device_mem(pdev)) @@ -1753,26 +2373,30 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha = kzalloc(sizeof(struct qla_hw_data), GFP_KERNEL); if (!ha) { - DEBUG(printk("Unable to allocate memory for ha\n")); + ql_log_pci(ql_log_fatal, pdev, 0x0009, + "Unable to allocate memory for ha.\n"); goto probe_out; } + ql_dbg_pci(ql_dbg_init, pdev, 0x000a, + "Memory allocated for ha=%p.\n", ha); ha->pdev = pdev; + ha->tgt.enable_class_2 = ql2xenableclass2; /* Clear our data area */ ha->bars = bars; ha->mem_only = mem_only; spin_lock_init(&ha->hardware_lock); + spin_lock_init(&ha->vport_slock); + mutex_init(&ha->selflogin_lock); + mutex_init(&ha->optrom_mutex); /* Set ISP-type information. */ qla2x00_set_isp_flags(ha); - /* Configure PCI I/O space */ - ret = qla2x00_iospace_config(ha); - if (ret) - goto probe_hw_failed; - qla_printk(KERN_INFO, ha, - "Found an ISP%04X, irq %d, iobase 0x%p\n", pdev->device, pdev->irq, - ha->iobase); + /* Set EEH reset type to fundamental if required by hba */ + if (IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha) || + IS_QLA83XX(ha) || IS_QLA27XX(ha)) + pdev->needs_freset = 1; ha->prev_topology = 0; ha->init_cb_size = sizeof(init_cb_t); @@ -1780,9 +2404,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->optrom_size = OPTROM_SIZE_2300; /* Assign ISP specific operations. */ - max_id = MAX_TARGETS_2200; if (IS_QLA2100(ha)) { - max_id = MAX_TARGETS_2100; + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2100; ha->mbx_count = MAILBOX_REGISTER_COUNT_2100; req_length = REQUEST_ENTRY_CNT_2100; rsp_length = RESPONSE_ENTRY_CNT_2100; @@ -1794,7 +2417,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->nvram_data_off = ~0; ha->isp_ops = &qla2100_isp_ops; } else if (IS_QLA2200(ha)) { - ha->mbx_count = MAILBOX_REGISTER_COUNT; + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2100; + ha->mbx_count = MAILBOX_REGISTER_COUNT_2200; req_length = REQUEST_ENTRY_CNT_2200; rsp_length = RESPONSE_ENTRY_CNT_2100; ha->max_loop_id = SNS_LAST_LOOP_ID_2100; @@ -1805,6 +2429,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->nvram_data_off = ~0; ha->isp_ops = &qla2100_isp_ops; } else if (IS_QLA23XX(ha)) { + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2100; ha->mbx_count = MAILBOX_REGISTER_COUNT; req_length = REQUEST_ENTRY_CNT_2200; rsp_length = RESPONSE_ENTRY_CNT_2300; @@ -1818,9 +2443,11 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->nvram_data_off = ~0; ha->isp_ops = &qla2300_isp_ops; } else if (IS_QLA24XX_TYPE(ha)) { + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; ha->mbx_count = MAILBOX_REGISTER_COUNT; req_length = REQUEST_ENTRY_CNT_24XX; rsp_length = RESPONSE_ENTRY_CNT_2300; + ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX; ha->max_loop_id = SNS_LAST_LOOP_ID_2300; ha->init_cb_size = sizeof(struct mid_init_cb_24xx); ha->gid_list_info_size = 8; @@ -1832,9 +2459,11 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF; ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA; } else if (IS_QLA25XX(ha)) { + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; ha->mbx_count = MAILBOX_REGISTER_COUNT; req_length = REQUEST_ENTRY_CNT_24XX; rsp_length = RESPONSE_ENTRY_CNT_2300; + ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX; ha->max_loop_id = SNS_LAST_LOOP_ID_2300; ha->init_cb_size = sizeof(struct mid_init_cb_24xx); ha->gid_list_info_size = 8; @@ -1846,32 +2475,140 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF; ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA; } else if (IS_QLA81XX(ha)) { + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; ha->mbx_count = MAILBOX_REGISTER_COUNT; req_length = REQUEST_ENTRY_CNT_24XX; rsp_length = RESPONSE_ENTRY_CNT_2300; + ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX; 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; + ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX; 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; + } else if (IS_QLA82XX(ha)) { + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; + 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; + ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX; + 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; + } else if (IS_QLA8044(ha)) { + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; + 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_83XX; + ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX; + ha->isp_ops = &qla8044_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; + } else if (IS_QLA83XX(ha)) { + ha->portnum = PCI_FUNC(ha->pdev->devfn); + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; + ha->mbx_count = MAILBOX_REGISTER_COUNT; + req_length = REQUEST_ENTRY_CNT_24XX; + rsp_length = RESPONSE_ENTRY_CNT_2300; + ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX; + 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; + } else if (IS_QLAFX00(ha)) { + ha->max_fibre_devices = MAX_FIBRE_DEVICES_FX00; + ha->mbx_count = MAILBOX_REGISTER_COUNT_FX00; + ha->aen_mbx_count = AEN_MAILBOX_REGISTER_COUNT_FX00; + req_length = REQUEST_ENTRY_CNT_FX00; + rsp_length = RESPONSE_ENTRY_CNT_FX00; + ha->isp_ops = &qlafx00_isp_ops; + ha->port_down_retry_count = 30; /* default value */ + ha->mr.fw_hbt_cnt = QLAFX00_HEARTBEAT_INTERVAL; + ha->mr.fw_reset_timer_tick = QLAFX00_RESET_INTERVAL; + ha->mr.fw_critemp_timer_tick = QLAFX00_CRITEMP_INTERVAL; + ha->mr.fw_hbt_en = 1; + ha->mr.host_info_resend = false; + ha->mr.hinfo_resend_timer_tick = QLAFX00_HINFO_RESEND_INTERVAL; + } else if (IS_QLA27XX(ha)) { + ha->portnum = PCI_FUNC(ha->pdev->devfn); + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; + ha->mbx_count = MAILBOX_REGISTER_COUNT; + req_length = REQUEST_ENTRY_CNT_24XX; + rsp_length = RESPONSE_ENTRY_CNT_2300; + 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 = &qla27xx_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; } + 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, " + "gid_list_info_size=%d, optrom_size=%d, nvram_npiv_size=%d, " + "max_fibre_devices=%d.\n", + ha->mbx_count, req_length, rsp_length, ha->max_loop_id, + ha->init_cb_size, ha->gid_list_info_size, ha->optrom_size, + ha->nvram_npiv_size, ha->max_fibre_devices); + 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); + + /* Configure PCI I/O space */ + ret = ha->isp_ops->iospace_config(ha); + if (ret) + goto iospace_config_failed; + + ql_log_pci(ql_log_info, pdev, 0x001d, + "Found an ISP%04X irq %d iobase 0x%p.\n", + pdev->device, pdev->irq, ha->iobase); mutex_init(&ha->vport_lock); init_completion(&ha->mbx_cmd_comp); complete(&ha->mbx_cmd_comp); init_completion(&ha->mbx_intr_comp); + init_completion(&ha->dcbx_comp); + init_completion(&ha->lb_portup_comp); set_bit(0, (unsigned long *) ha->vp_idx_map); qla2x00_config_dma_addressing(ha); + ql_dbg_pci(ql_dbg_init, pdev, 0x0020, + "64 Bit addressing is %s.\n", + ha->flags.enable_64bit_addressing ? "enable" : + "disable"); ret = qla2x00_mem_alloc(ha, req_length, rsp_length, &req, &rsp); - if (!ret) { - qla_printk(KERN_WARNING, ha, - "[ERROR] Failed to allocate memory for adapter\n"); + if (ret) { + ql_log_pci(ql_log_fatal, pdev, 0x0031, + "Failed to allocate memory for adapter, aborting.\n"); goto probe_hw_failed; } @@ -1883,131 +2620,354 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) base_vha = qla2x00_create_host(sht, ha); if (!base_vha) { - qla_printk(KERN_WARNING, ha, - "[ERROR] Failed to allocate memory for scsi_host\n"); - ret = -ENOMEM; qla2x00_mem_free(ha); - qla2x00_free_que(ha, req, rsp); + qla2x00_free_req_que(ha, req); + qla2x00_free_rsp_que(ha, rsp); goto probe_hw_failed; } pci_set_drvdata(pdev, base_vha); host = base_vha->host; - base_vha->req_ques[0] = req->id; - host->can_queue = req->length + 128; + base_vha->req = req; if (IS_QLA2XXX_MIDTYPE(ha)) base_vha->mgmt_svr_loop_id = 10 + base_vha->vp_idx; else base_vha->mgmt_svr_loop_id = MANAGEMENT_SERVER + base_vha->vp_idx; - if (IS_QLA2100(ha)) - host->sg_tablesize = 32; - host->max_id = max_id; - host->this_id = 255; + + /* Setup fcport template structure. */ + ha->mr.fcport.vha = base_vha; + ha->mr.fcport.port_type = FCT_UNKNOWN; + ha->mr.fcport.loop_id = FC_NO_LOOP_ID; + qla2x00_set_fcport_state(&ha->mr.fcport, FCS_UNCONFIGURED); + ha->mr.fcport.supported_classes = FC_COS_UNSPECIFIED; + ha->mr.fcport.scan_state = 1; + + /* 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; + } + host->max_id = ha->max_fibre_devices; host->cmd_per_lun = 3; host->unique_id = host->host_no; - host->max_cmd_len = MAX_CMDSZ; + if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) + host->max_cmd_len = 32; + else + host->max_cmd_len = MAX_CMDSZ; host->max_channel = MAX_BUSES - 1; - host->max_lun = MAX_LUNS; + host->max_lun = ql2xmaxlun; host->transportt = qla2xxx_transport_template; + sht->vendor_id = (SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_QLOGIC); + + 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 " + "max_lun=%d transportt=%p, vendor_id=%llu.\n", host->max_id, + 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); + +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; + } + + qlt_probe_one_stage1(base_vha, ha); /* Set up the irqs */ ret = qla2x00_request_irqs(ha, rsp); if (ret) goto probe_init_failed; - /* Alloc arrays of request and response ring ptrs */ - if (!qla2x00_alloc_queues(ha)) { - qla_printk(KERN_WARNING, ha, - "[ERROR] Failed to allocate memory for queue" - " pointers\n"); - goto probe_init_failed; + + pci_save_state(pdev); + + /* Assign back pointers */ + rsp->req = req; + req->rsp = rsp; + + if (IS_QLAFX00(ha)) { + ha->rsp_q_map[0] = rsp; + ha->req_q_map[0] = req; + set_bit(0, ha->req_qid_map); + set_bit(0, ha->rsp_qid_map); } - ha->rsp_q_map[0] = rsp; - ha->req_q_map[0] = req; - if (ha->mqenable) { - ha->isp_ops->wrt_req_reg = qla25xx_wrt_req_reg; - ha->isp_ops->wrt_rsp_reg = qla25xx_wrt_rsp_reg; - ha->isp_ops->rd_req_reg = qla25xx_rd_req_reg; + /* 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; + if (ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha)) { + 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; } - if (qla2x00_initialize_adapter(base_vha)) { - qla_printk(KERN_WARNING, ha, - "Failed to initialize adapter\n"); + if (IS_QLAFX00(ha)) { + req->req_q_in = &ha->iobase->ispfx00.req_q_in; + req->req_q_out = &ha->iobase->ispfx00.req_q_out; + rsp->rsp_q_in = &ha->iobase->ispfx00.rsp_q_in; + rsp->rsp_q_out = &ha->iobase->ispfx00.rsp_q_out; + } - DEBUG2(printk("scsi(%ld): Failed to initialize adapter - " - "Adapter flags %x.\n", - base_vha->host_no, base_vha->device_flags)); + if (IS_P3P_TYPE(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]; + } + + 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); + + if (ha->isp_ops->initialize_adapter(base_vha)) { + ql_log(ql_log_fatal, base_vha, 0x00d6, + "Failed to initialize adapter - Adapter flags %x.\n", + base_vha->device_flags); + + if (IS_QLA82XX(ha)) { + qla82xx_idc_lock(ha); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA8XXX_DEV_FAILED); + qla82xx_idc_unlock(ha); + ql_log(ql_log_fatal, base_vha, 0x00d7, + "HW State: FAILED.\n"); + } else if (IS_QLA8044(ha)) { + qla8044_idc_lock(ha); + qla8044_wr_direct(base_vha, + QLA8044_CRB_DEV_STATE_INDEX, + QLA8XXX_DEV_FAILED); + qla8044_idc_unlock(ha); + ql_log(ql_log_fatal, base_vha, 0x0150, + "HW State: FAILED.\n"); + } ret = -ENODEV; goto probe_failed; } + if (IS_QLAFX00(ha)) + host->can_queue = QLAFX00_MAX_CANQUEUE; + else + host->can_queue = req->num_outstanding_cmds - 10; + + 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); + + if (ha->mqenable) { + if (qla25xx_setup_mode(base_vha)) { + ql_log(ql_log_warn, base_vha, 0x00ec, + "Failed to create queues, falling back to single queue mode.\n"); + goto que_init; + } + } + + if (ha->flags.running_gold_fw) + goto skip_dpc; + /* * Startup the kernel thread for this host adapter */ ha->dpc_thread = kthread_create(qla2x00_do_dpc, ha, - "%s_dpc", base_vha->host_str); + "%s_dpc", base_vha->host_str); if (IS_ERR(ha->dpc_thread)) { - qla_printk(KERN_WARNING, ha, - "Unable to start DPC thread!\n"); + ql_log(ql_log_fatal, base_vha, 0x00ed, + "Failed to start DPC thread.\n"); ret = PTR_ERR(ha->dpc_thread); goto probe_failed; } + ql_dbg(ql_dbg_init, base_vha, 0x00ee, + "DPC thread started successfully.\n"); + /* + * 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); + + INIT_WORK(&ha->board_disable, qla2x00_disable_board_on_pci_error); + + 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); + } + +skip_dpc: list_add_tail(&base_vha->list, &ha->vp_list); base_vha->host->irq = ha->pdev->irq; /* Initialized the timer */ qla2x00_start_timer(base_vha, qla2x00_timer, WATCH_INTERVAL); + 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); + + if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) { + if (ha->fw_attributes & BIT_4) { + int prot = 0, guard; + base_vha->flags.difdix_supported = 1; + ql_dbg(ql_dbg_init, base_vha, 0x00f1, + "Registering for DIF/DIX type 1 and 3 protection.\n"); + if (ql2xenabledif == 1) + prot = SHOST_DIX_TYPE0_PROTECTION; + scsi_host_set_prot(host, + prot | SHOST_DIF_TYPE1_PROTECTION + | SHOST_DIF_TYPE2_PROTECTION + | SHOST_DIF_TYPE3_PROTECTION + | SHOST_DIX_TYPE1_PROTECTION + | SHOST_DIX_TYPE2_PROTECTION + | SHOST_DIX_TYPE3_PROTECTION); + + 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); + } else + base_vha->flags.difdix_supported = 0; + } - DEBUG2(printk("DEBUG: detect hba %ld at address = %p\n", - base_vha->host_no, ha)); + ha->isp_ops->enable_intrs(ha); - base_vha->flags.init_done = 1; - base_vha->flags.online = 1; + if (IS_QLAFX00(ha)) { + ret = qlafx00_fx_disc(base_vha, + &base_vha->hw->mr.fcport, FXDISC_GET_CONFIG_INFO); + host->sg_tablesize = (ha->mr.extended_io_enabled) ? + QLA_SG_ALL : 128; + } ret = scsi_add_host(host, &pdev->dev); if (ret) goto probe_failed; - ha->isp_ops->enable_intrs(ha); + base_vha->flags.init_done = 1; + base_vha->flags.online = 1; + ha->prev_minidump_failed = 0; - scsi_scan_host(host); + ql_dbg(ql_dbg_init, base_vha, 0x00f2, + "Init done and hba is online.\n"); + + 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"); qla2x00_alloc_sysfs_attr(base_vha); + if (IS_QLAFX00(ha)) { + ret = qlafx00_fx_disc(base_vha, + &base_vha->hw->mr.fcport, FXDISC_GET_PORT_INFO); + + /* Register system information */ + ret = qlafx00_fx_disc(base_vha, + &base_vha->hw->mr.fcport, FXDISC_REG_HOST_INFO); + } + qla2x00_init_host_attr(base_vha); qla2x00_dfs_setup(base_vha); - qla_printk(KERN_INFO, ha, "\n" - " QLogic Fibre Channel HBA Driver: %s\n" - " QLogic %s - %s\n" - " ISP%04X: %s @ %s hdma%c, host#=%ld, fw=%s\n", - qla2x00_version_str, ha->model_number, - ha->model_desc ? ha->model_desc : "", pdev->device, - ha->isp_ops->pci_info_str(base_vha, pci_info), pci_name(pdev), - ha->flags.enable_64bit_addressing ? '+' : '-', base_vha->host_no, + ql_log(ql_log_info, base_vha, 0x00fb, + "QLogic %s - %s.\n", ha->model_number, 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, ha->isp_ops->fw_version_str(base_vha, fw_str)); + qlt_add_target(ha, base_vha); + return 0; probe_init_failed: - qla2x00_free_que(ha, req, rsp); - ha->max_queues = 0; + qla2x00_free_req_que(ha, req); + ha->req_q_map[0] = NULL; + clear_bit(0, ha->req_qid_map); + qla2x00_free_rsp_que(ha, rsp); + ha->rsp_q_map[0] = NULL; + clear_bit(0, ha->rsp_qid_map); + ha->max_req_queues = ha->max_rsp_queues = 0; probe_failed: + 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); + } + qla2x00_free_device(base_vha); scsi_host_put(base_vha->host); probe_hw_failed: - if (ha->iobase) - iounmap(ha->iobase); - + if (IS_QLA82XX(ha)) { + qla82xx_idc_lock(ha); + qla82xx_clear_drv_active(ha); + qla82xx_idc_unlock(ha); + } + if (IS_QLA8044(ha)) { + qla8044_idc_lock(ha); + qla8044_clear_drv_active(ha); + qla8044_idc_unlock(ha); + } +iospace_config_failed: + if (IS_P3P_TYPE(ha)) { + if (!ha->nx_pcibase) + iounmap((device_reg_t *)ha->nx_pcibase); + if (!ql2xdbwr) + iounmap((device_reg_t *)ha->nxdb_wr_ptr); + } else { + if (ha->iobase) + iounmap(ha->iobase); + if (ha->cregbase) + iounmap(ha->cregbase); + } pci_release_selected_regions(ha->pdev, ha->bars); kfree(ha); ha = NULL; @@ -2018,26 +2978,204 @@ probe_out: } static void +qla2x00_shutdown(struct pci_dev *pdev) +{ + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + + if (!atomic_read(&pdev->enable_cnt)) + return; + + vha = pci_get_drvdata(pdev); + ha = vha->hw; + + /* Notify ISPFX00 firmware */ + if (IS_QLAFX00(ha)) + qlafx00_driver_shutdown(vha, 20); + + /* 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); +} + +/* Deletes all the virtual ports for a given ha */ +static void +qla2x00_delete_all_vps(struct qla_hw_data *ha, scsi_qla_host_t *base_vha) +{ + struct Scsi_Host *scsi_host; + scsi_qla_host_t *vha; + unsigned long flags; + + mutex_lock(&ha->vport_lock); + while (ha->cur_vport_count) { + spin_lock_irqsave(&ha->vport_slock, flags); + + 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); + + spin_unlock_irqrestore(&ha->vport_slock, flags); + mutex_unlock(&ha->vport_lock); + + fc_vport_terminate(vha->fc_vport); + scsi_host_put(vha->host); + + mutex_lock(&ha->vport_lock); + } + mutex_unlock(&ha->vport_lock); +} + +/* Stops all deferred work threads */ +static void +qla2x00_destroy_deferred_work(struct qla_hw_data *ha) +{ + /* Flush the work queue and remove it */ + if (ha->wq) { + flush_workqueue(ha->wq); + destroy_workqueue(ha->wq); + ha->wq = NULL; + } + + /* 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; + } + + /* 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); + } +} + +static void +qla2x00_unmap_iobases(struct qla_hw_data *ha) +{ + if (IS_QLA82XX(ha)) { + + iounmap((device_reg_t *)ha->nx_pcibase); + if (!ql2xdbwr) + iounmap((device_reg_t *)ha->nxdb_wr_ptr); + } else { + if (ha->iobase) + iounmap(ha->iobase); + + if (ha->cregbase) + iounmap(ha->cregbase); + + if (ha->mqiobase) + iounmap(ha->mqiobase); + + if ((IS_QLA83XX(ha) || IS_QLA27XX(ha)) && ha->msixbase) + iounmap(ha->msixbase); + } +} + +static void +qla2x00_clear_drv_active(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + + if (IS_QLA8044(ha)) { + qla8044_idc_lock(ha); + qla8044_clear_drv_active(ha); + qla8044_idc_unlock(ha); + } else if (IS_QLA82XX(ha)) { + qla82xx_idc_lock(ha); + qla82xx_clear_drv_active(ha); + qla82xx_idc_unlock(ha); + } +} + +static void qla2x00_remove_one(struct pci_dev *pdev) { - scsi_qla_host_t *base_vha, *vha, *temp; + scsi_qla_host_t *base_vha; struct qla_hw_data *ha; + /* + * 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; + base_vha = pci_get_drvdata(pdev); ha = base_vha->hw; - list_for_each_entry_safe(vha, temp, &ha->vp_list, list) { - if (vha && vha->fc_vport) - fc_vport_terminate(vha->fc_vport); - } + qla2x00_wait_for_hba_ready(base_vha); set_bit(UNLOADING, &base_vha->dpc_flags); + if (IS_QLAFX00(ha)) + qlafx00_driver_shutdown(base_vha, 20); + + qla2x00_delete_all_vps(ha, base_vha); + + 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"); + } + + qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16); + qla2x00_dfs_remove(base_vha); qla84xx_put_chip(base_vha); - qla2x00_free_sysfs_attr(base_vha); + /* Disable timer */ + if (base_vha->timer_active) + qla2x00_stop_timer(base_vha); + + base_vha->flags.online = 0; + + qla2x00_destroy_deferred_work(ha); + + qlt_remove_target(ha, base_vha); + + qla2x00_free_sysfs_attr(base_vha, true); fc_remove_host(base_vha->host); @@ -2047,43 +3185,31 @@ qla2x00_remove_one(struct pci_dev *pdev) scsi_host_put(base_vha->host); - if (ha->iobase) - iounmap(ha->iobase); + qla2x00_clear_drv_active(base_vha); - if (ha->mqiobase) - iounmap(ha->mqiobase); + qla2x00_unmap_iobases(ha); pci_release_selected_regions(ha->pdev, ha->bars); kfree(ha); ha = NULL; + pci_disable_pcie_error_reporting(pdev); + pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } static void qla2x00_free_device(scsi_qla_host_t *vha) { struct qla_hw_data *ha = vha->hw; + qla2x00_abort_all_cmds(vha, DID_NO_CONNECT << 16); /* Disable timer */ if (vha->timer_active) qla2x00_stop_timer(vha); - vha->flags.online = 0; - - /* 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); - } + qla25xx_delete_queues(vha); if (ha->flags.fce_enabled) qla2x00_disable_fce_trace(vha, NULL, NULL); @@ -2094,35 +3220,60 @@ qla2x00_free_device(scsi_qla_host_t *vha) /* Stop currently executing firmware. */ qla2x00_try_to_stop_firmware(vha); + vha->flags.online = 0; + /* turn-off interrupts on the card */ - if (ha->interrupts_on) + if (ha->interrupts_on) { + vha->flags.init_done = 0; ha->isp_ops->disable_intrs(ha); + } qla2x00_free_irqs(vha); + qla2x00_free_fcports(vha); + qla2x00_mem_free(ha); + qla82xx_md_free(vha); + qla2x00_free_queues(ha); } +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); + qla2x00_clear_loop_id(fcport); + kfree(fcport); + fcport = NULL; + } +} + static inline void qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport, int defer) { struct fc_rport *rport; + scsi_qla_host_t *base_vha; + unsigned long flags; if (!fcport->rport) return; rport = fcport->rport; if (defer) { - spin_lock_irq(vha->host->host_lock); + base_vha = pci_get_drvdata(vha->hw->pdev); + spin_lock_irqsave(vha->host->host_lock, flags); fcport->drport = rport; - spin_unlock_irq(vha->host->host_lock); - set_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags); - qla2xxx_wake_dpc(vha); - } else + spin_unlock_irqrestore(vha->host->host_lock, flags); + set_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags); + qla2xxx_wake_dpc(base_vha); + } else { fc_remote_port_delete(rport); + qlt_fc_port_deleted(vha, fcport); + } } /* @@ -2137,9 +3288,15 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport, void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport, int do_login, int defer) { + if (IS_QLAFX00(vha->hw)) { + qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST); + qla2x00_schedule_rport_del(vha, fcport, defer); + return; + } + if (atomic_read(&fcport->state) == FCS_ONLINE && - vha->vp_idx == fcport->vp_idx) { - atomic_set(&fcport->state, FCS_DEVICE_LOST); + vha->vp_idx == fcport->vha->vp_idx) { + qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST); qla2x00_schedule_rport_del(vha, fcport, defer); } /* @@ -2147,7 +3304,7 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport, * port but do the retries. */ if (atomic_read(&fcport->state) != FCS_DEVICE_DEAD) - atomic_set(&fcport->state, FCS_DEVICE_LOST); + qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST); if (!do_login) return; @@ -2156,20 +3313,9 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport, fcport->login_retry = vha->hw->login_retry_count; set_bit(RELOGIN_NEEDED, &vha->dpc_flags); - DEBUG(printk("scsi(%ld): Port login retry: " - "%02x%02x%02x%02x%02x%02x%02x%02x, " - "id = 0x%04x retry cnt=%d\n", - vha->host_no, - 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)); + ql_dbg(ql_dbg_disc, vha, 0x2067, + "Port login retry %8phN, id = 0x%04x retry cnt=%d.\n", + fcport->port_name, fcport->loop_id, fcport->login_retry); } } @@ -2192,8 +3338,9 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer) fc_port_t *fcport; list_for_each_entry(fcport, &vha->vp_fcports, list) { - if (vha->vp_idx != fcport->vp_idx) + if (vha->vp_idx != 0 && vha->vp_idx != fcport->vha->vp_idx) continue; + /* * No point in marking the device as lost, if the device is * already DEAD. @@ -2201,10 +3348,12 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer) if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD) continue; if (atomic_read(&fcport->state) == FCS_ONLINE) { - atomic_set(&fcport->state, FCS_DEVICE_LOST); - qla2x00_schedule_rport_del(vha, fcport, defer); - } else - atomic_set(&fcport->state, FCS_DEVICE_LOST); + qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST); + if (defer) + qla2x00_schedule_rport_del(vha, fcport, defer); + else if (vha->vp_idx == fcport->vha->vp_idx) + qla2x00_schedule_rport_del(vha, fcport, defer); + } } } @@ -2227,19 +3376,40 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, if (!ha->init_cb) goto fail; - ha->gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE, - &ha->gid_list_dma, GFP_KERNEL); - if (!ha->gid_list) + if (qlt_mem_alloc(ha) < 0) goto fail_free_init_cb; + ha->gid_list = dma_alloc_coherent(&ha->pdev->dev, + qla2x00_gid_list_size(ha), &ha->gid_list_dma, GFP_KERNEL); + if (!ha->gid_list) + goto fail_free_tgt_mem; + ha->srb_mempool = mempool_create_slab_pool(SRB_MIN_REQ, srb_cachep); if (!ha->srb_mempool) goto fail_free_gid_list; + if (IS_P3P_TYPE(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; + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0021, + "ctx_cachep=%p ctx_mempool=%p.\n", + ctx_cachep, ha->ctx_mempool); + } + /* Get memory for cached NVRAM */ ha->nvram = kzalloc(MAX_NVRAM_SIZE, GFP_KERNEL); if (!ha->nvram) - goto fail_free_srb_mempool; + goto fail_free_ctx_mempool; snprintf(name, sizeof(name), "%s_%d", QLA2XXX_DRIVER_NAME, ha->pdev->device); @@ -2248,6 +3418,31 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, if (!ha->s_dma_pool) goto fail_free_nvram; + 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); + + if (IS_P3P_TYPE(ha) || ql2xenabledif) { + ha->dl_dma_pool = dma_pool_create(name, &ha->pdev->dev, + DSD_LIST_DMA_POOL_SIZE, 8, 0); + if (!ha->dl_dma_pool) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x0023, + "Failed to allocate memory for dl_dma_pool.\n"); + 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) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x0024, + "Failed to allocate memory for fcp_cmnd_dma_pool.\n"); + goto fail_dl_dma_pool; + } + 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); + } + /* Allocate memory for SNS commands */ if (IS_QLA2100(ha) || IS_QLA2200(ha)) { /* Get consistent memory allocated for SNS commands */ @@ -2255,6 +3450,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, sizeof(struct sns_cmd_pkt), &ha->sns_cmd_dma, GFP_KERNEL); if (!ha->sns_cmd) goto fail_dma_pool; + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0026, + "sns_cmd: %p.\n", ha->sns_cmd); } else { /* Get consistent memory allocated for MS IOCB */ ha->ms_iocb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, @@ -2266,12 +3463,16 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, sizeof(struct ct_sns_pkt), &ha->ct_sns_dma, GFP_KERNEL); if (!ha->ct_sns) goto fail_free_ms_iocb; + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0027, + "ms_iocb=%p ct_sns=%p.\n", + ha->ms_iocb, ha->ct_sns); } /* Allocate memory for request ring */ *req = kzalloc(sizeof(struct req_que), GFP_KERNEL); if (!*req) { - DEBUG(printk("Unable to allocate memory for req\n")); + ql_log_pci(ql_log_fatal, ha->pdev, 0x0028, + "Failed to allocate memory for req.\n"); goto fail_req; } (*req)->length = req_len; @@ -2279,14 +3480,15 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, ((*req)->length + 1) * sizeof(request_t), &(*req)->dma, GFP_KERNEL); if (!(*req)->ring) { - DEBUG(printk("Unable to allocate memory for req_ring\n")); + ql_log_pci(ql_log_fatal, ha->pdev, 0x0029, + "Failed to allocate memory for req_ring.\n"); goto fail_req_ring; } /* Allocate memory for response ring */ *rsp = kzalloc(sizeof(struct rsp_que), GFP_KERNEL); if (!*rsp) { - qla_printk(KERN_WARNING, ha, - "Unable to allocate memory for rsp\n"); + ql_log_pci(ql_log_fatal, ha->pdev, 0x002a, + "Failed to allocate memory for rsp.\n"); goto fail_rsp; } (*rsp)->hw = ha; @@ -2295,27 +3497,70 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, ((*rsp)->length + 1) * sizeof(response_t), &(*rsp)->dma, GFP_KERNEL); if (!(*rsp)->ring) { - qla_printk(KERN_WARNING, ha, - "Unable to allocate memory for rsp_ring\n"); + ql_log_pci(ql_log_fatal, ha->pdev, 0x002b, + "Failed to allocate memory for rsp_ring.\n"); goto fail_rsp_ring; } (*req)->rsp = *rsp; (*rsp)->req = *req; + 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); /* Allocate memory for NVRAM data for vports */ if (ha->nvram_npiv_size) { ha->npiv_info = kzalloc(sizeof(struct qla_npiv_entry) * - ha->nvram_npiv_size, GFP_KERNEL); + ha->nvram_npiv_size, GFP_KERNEL); if (!ha->npiv_info) { - qla_printk(KERN_WARNING, ha, - "Unable to allocate memory for npiv info\n"); + ql_log_pci(ql_log_fatal, ha->pdev, 0x002d, + "Failed to allocate memory for npiv_info.\n"); goto fail_npiv_info; } } else ha->npiv_info = NULL; + /* Get consistent memory allocated for EX-INIT-CB. */ + if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha)) { + 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; + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002e, + "ex_init_cb=%p.\n", ha->ex_init_cb); + } + + INIT_LIST_HEAD(&ha->gbl_dsd_list); + + /* 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; + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002f, + "async_pd=%p.\n", ha->async_pd); + } + INIT_LIST_HEAD(&ha->vp_list); - return 1; + /* 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); + } + + return 0; + +fail_async_pd: + dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma); +fail_ex_init_cb: + kfree(ha->npiv_info); fail_npiv_info: dma_free_coherent(&ha->pdev->dev, ((*rsp)->length + 1) * sizeof(response_t), (*rsp)->ring, (*rsp)->dma); @@ -2340,30 +3585,83 @@ fail_free_ms_iocb: ha->ms_iocb = NULL; ha->ms_iocb_dma = 0; fail_dma_pool: + if (IS_QLA82XX(ha) || ql2xenabledif) { + dma_pool_destroy(ha->fcp_cmnd_dma_pool); + ha->fcp_cmnd_dma_pool = NULL; + } +fail_dl_dma_pool: + if (IS_QLA82XX(ha) || ql2xenabledif) { + dma_pool_destroy(ha->dl_dma_pool); + ha->dl_dma_pool = NULL; + } +fail_s_dma_pool: dma_pool_destroy(ha->s_dma_pool); ha->s_dma_pool = NULL; fail_free_nvram: kfree(ha->nvram); ha->nvram = NULL; +fail_free_ctx_mempool: + mempool_destroy(ha->ctx_mempool); + ha->ctx_mempool = NULL; fail_free_srb_mempool: mempool_destroy(ha->srb_mempool); ha->srb_mempool = NULL; fail_free_gid_list: - dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list, + dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha), + ha->gid_list, ha->gid_list_dma); ha->gid_list = NULL; ha->gid_list_dma = 0; +fail_free_tgt_mem: + qlt_mem_free(ha); fail_free_init_cb: dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, ha->init_cb, ha->init_cb_dma); ha->init_cb = NULL; ha->init_cb_dma = 0; fail: - DEBUG(printk("%s: Memory allocation failure\n", __func__)); + ql_log(ql_log_fatal, NULL, 0x0030, + "Memory allocation failure.\n"); return -ENOMEM; } /* +* qla2x00_free_fw_dump +* Frees fw dump stuff. +* +* Input: +* ha = adapter block pointer +*/ +static void +qla2x00_free_fw_dump(struct qla_hw_data *ha) +{ + if (ha->fce) + dma_free_coherent(&ha->pdev->dev, + FCE_SIZE, ha->fce, ha->fce_dma); + + if (ha->eft) + dma_free_coherent(&ha->pdev->dev, + EFT_SIZE, ha->eft, ha->eft_dma); + + if (ha->fw_dump) + vfree(ha->fw_dump); + if (ha->fw_dump_template) + vfree(ha->fw_dump_template); + + ha->fce = NULL; + ha->fce_dma = 0; + ha->eft = NULL; + ha->eft_dma = 0; + ha->fw_dumped = 0; + ha->fw_dump_cap_flags = 0; + ha->fw_dump_reading = 0; + ha->fw_dump = NULL; + ha->fw_dump_len = 0; + ha->fw_dump_template = NULL; + ha->fw_dump_template_len = 0; +} + +/* * qla2x00_mem_free * Frees all adapter allocated memory. * @@ -2373,19 +3671,22 @@ fail: static void qla2x00_mem_free(struct qla_hw_data *ha) { + qla2x00_free_fw_dump(ha); + + if (ha->mctp_dump) + dma_free_coherent(&ha->pdev->dev, MCTP_DUMP_SIZE, ha->mctp_dump, + ha->mctp_dump_dma); + if (ha->srb_mempool) mempool_destroy(ha->srb_mempool); - if (ha->fce) - dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce, - ha->fce_dma); + if (ha->dcbx_tlv) + dma_free_coherent(&ha->pdev->dev, DCBX_TLV_DATA_SIZE, + ha->dcbx_tlv, ha->dcbx_tlv_dma); - if (ha->fw_dump) { - if (ha->eft) - dma_free_coherent(&ha->pdev->dev, - ntohl(ha->fw_dump->eft_size), ha->eft, ha->eft_dma); - vfree(ha->fw_dump); - } + if (ha->xgmac_data) + dma_free_coherent(&ha->pdev->dev, XGMAC_DATA_SIZE, + ha->xgmac_data, ha->xgmac_data_dma); if (ha->sns_cmd) dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt), @@ -2401,25 +3702,57 @@ qla2x00_mem_free(struct qla_hw_data *ha) if (ha->ms_iocb) dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma); + if (ha->ex_init_cb) + dma_pool_free(ha->s_dma_pool, + ha->ex_init_cb, ha->ex_init_cb_dma); + + if (ha->async_pd) + dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma); + if (ha->s_dma_pool) dma_pool_destroy(ha->s_dma_pool); - if (ha->gid_list) - dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list, - ha->gid_list_dma); + dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha), + ha->gid_list, ha->gid_list_dma); + + 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); + qlt_mem_free(ha); if (ha->init_cb) dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, - ha->init_cb, ha->init_cb_dma); + ha->init_cb, ha->init_cb_dma); vfree(ha->optrom_buffer); kfree(ha->nvram); kfree(ha->npiv_info); + kfree(ha->swl); + kfree(ha->loop_id_map); ha->srb_mempool = NULL; - ha->eft = NULL; - ha->eft_dma = 0; + ha->ctx_mempool = NULL; ha->sns_cmd = NULL; ha->sns_cmd_dma = 0; ha->ct_sns = NULL; @@ -2428,15 +3761,21 @@ qla2x00_mem_free(struct qla_hw_data *ha) ha->ms_iocb_dma = 0; ha->init_cb = NULL; ha->init_cb_dma = 0; + ha->ex_init_cb = NULL; + ha->ex_init_cb_dma = 0; + ha->async_pd = NULL; + ha->async_pd_dma = 0; ha->s_dma_pool = NULL; + ha->dl_dma_pool = NULL; + ha->fcp_cmnd_dma_pool = NULL; ha->gid_list = NULL; ha->gid_list_dma = 0; - ha->fw_dump = NULL; - ha->fw_dumped = 0; - ha->fw_dump_reading = 0; + ha->tgt.atio_ring = NULL; + ha->tgt.atio_dma = 0; + ha->tgt.tgt_vp_map = NULL; } struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, @@ -2447,8 +3786,8 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, host = scsi_host_alloc(sht, sizeof(scsi_qla_host_t)); if (host == NULL) { - printk(KERN_WARNING - "qla2xxx: Couldn't allocate host from scsi layer!\n"); + ql_log_pci(ql_log_fatal, ha->pdev, 0x0107, + "Failed to allocate host from the scsi layer, aborting.\n"); goto fail; } @@ -2464,7 +3803,14 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, INIT_LIST_HEAD(&vha->work_list); INIT_LIST_HEAD(&vha->list); + spin_lock_init(&vha->work_lock); + sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no); + 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))); + return vha; fail: @@ -2472,16 +3818,21 @@ fail: } static struct qla_work_evt * -qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type, - int locked) +qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type) { struct qla_work_evt *e; + uint8_t bail; - e = kzalloc(sizeof(struct qla_work_evt), locked ? GFP_ATOMIC: - GFP_KERNEL); - if (!e) + QLA_VHA_MARK_BUSY(vha, bail); + if (bail) return NULL; + e = kzalloc(sizeof(struct qla_work_evt), GFP_ATOMIC); + if (!e) { + QLA_VHA_MARK_NOT_BUSY(vha); + return NULL; + } + INIT_LIST_HEAD(&e->list); e->type = type; e->flags = QLA_EVT_FLAG_FREE; @@ -2489,17 +3840,15 @@ qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type, } static int -qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e, int locked) +qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e) { - unsigned long uninitialized_var(flags); - struct qla_hw_data *ha = vha->hw; + unsigned long flags; - if (!locked) - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&vha->work_lock, flags); list_add_tail(&e->list, &vha->work_list); + spin_unlock_irqrestore(&vha->work_lock, flags); qla2xxx_wake_dpc(vha); - if (!locked) - spin_unlock_irqrestore(&ha->hardware_lock, flags); + return QLA_SUCCESS; } @@ -2509,13 +3858,13 @@ qla2x00_post_aen_work(struct scsi_qla_host *vha, enum fc_host_event_code code, { struct qla_work_evt *e; - e = qla2x00_alloc_work(vha, QLA_EVT_AEN, 1); + e = qla2x00_alloc_work(vha, QLA_EVT_AEN); if (!e) return QLA_FUNCTION_FAILED; e->u.aen.code = code; e->u.aen.data = data; - return qla2x00_post_work(vha, e, 1); + return qla2x00_post_work(vha, e); } int @@ -2523,25 +3872,100 @@ qla2x00_post_idc_ack_work(struct scsi_qla_host *vha, uint16_t *mb) { struct qla_work_evt *e; - e = qla2x00_alloc_work(vha, QLA_EVT_IDC_ACK, 1); + e = qla2x00_alloc_work(vha, QLA_EVT_IDC_ACK); if (!e) return QLA_FUNCTION_FAILED; memcpy(e->u.idc_ack.mb, mb, QLA_IDC_ACK_REGS * sizeof(uint16_t)); - return qla2x00_post_work(vha, e, 1); + return qla2x00_post_work(vha, e); +} + +#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); +qla2x00_post_async_work(adisc, QLA_EVT_ASYNC_ADISC); +qla2x00_post_async_work(adisc_done, QLA_EVT_ASYNC_ADISC_DONE); + +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_do_work(struct scsi_qla_host *vha) +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); +} + +int +qlafx00_post_aenfx_work(struct scsi_qla_host *vha, uint32_t evtcode, + uint32_t *data, int cnt) { struct qla_work_evt *e; - struct qla_hw_data *ha = vha->hw; - spin_lock_irq(&ha->hardware_lock); - while (!list_empty(&vha->work_list)) { - e = list_entry(vha->work_list.next, struct qla_work_evt, list); + e = qla2x00_alloc_work(vha, QLA_EVT_AENFX); + if (!e) + return QLA_FUNCTION_FAILED; + + e->u.aenfx.evtcode = evtcode; + e->u.aenfx.count = cnt; + memcpy(e->u.aenfx.mbx, data, sizeof(*data) * cnt); + return qla2x00_post_work(vha, e); +} + +void +qla2x00_do_work(struct scsi_qla_host *vha) +{ + struct qla_work_evt *e, *tmp; + unsigned long flags; + LIST_HEAD(work); + + 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) { list_del_init(&e->list); - spin_unlock_irq(&ha->hardware_lock); switch (e->type) { case QLA_EVT_AEN: @@ -2551,13 +3975,44 @@ qla2x00_do_work(struct scsi_qla_host *vha) case QLA_EVT_IDC_ACK: qla81xx_idc_ack(vha, e->u.idc_ack.mb); break; + 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; + 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; + case QLA_EVT_UEVENT: + qla2x00_uevent_emit(vha, e->u.uevent.code); + break; + case QLA_EVT_AENFX: + qlafx00_process_aen(vha, e); + break; } if (e->flags & QLA_EVT_FLAG_FREE) kfree(e); - spin_lock_irq(&ha->hardware_lock); + + /* For each work completed decrement vha ref count */ + QLA_VHA_MARK_NOT_BUSY(vha); } - spin_unlock_irq(&ha->hardware_lock); } + /* Relogins all the fcports of a vport * Context: dpc thread */ @@ -2567,57 +4022,798 @@ void qla2x00_relogin(struct scsi_qla_host *vha) int status; uint16_t next_loopid = 0; struct qla_hw_data *ha = vha->hw; + uint16_t data[2]; list_for_each_entry(fcport, &vha->vp_fcports, list) { /* * If the port is not ONLINE then try to login * to it if we haven't run out of retries. */ - if (atomic_read(&fcport->state) != - FCS_ONLINE && fcport->login_retry) { - + if (atomic_read(&fcport->state) != FCS_ONLINE && + fcport->login_retry && !(fcport->flags & FCF_ASYNC_SENT)) { + fcport->login_retry--; if (fcport->flags & FCF_FABRIC_DEVICE) { - if (fcport->flags & FCF_TAPE_PRESENT) + if (fcport->flags & FCF_FCP2_DEVICE) ha->isp_ops->fabric_logout(vha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); - status = qla2x00_fabric_login(vha, fcport, - &next_loopid); + 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; + } + } + + if (IS_ALOGIO_CAPABLE(ha)) { + fcport->flags |= FCF_ASYNC_SENT; + 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; + } else { + status = qla2x00_fabric_login(vha, + fcport, &next_loopid); + if (status == QLA_SUCCESS) { + int status2; + uint8_t opts; + + opts = 0; + if (fcport->flags & + FCF_FCP2_DEVICE) + opts |= BIT_1; + status2 = + qla2x00_get_port_database( + vha, fcport, opts); + if (status2 != QLA_SUCCESS) + status = 1; + } + } } else status = qla2x00_local_device_login(vha, fcport); - fcport->login_retry--; if (status == QLA_SUCCESS) { fcport->old_loop_id = fcport->loop_id; - DEBUG(printk("scsi(%ld): port login OK: logged " - "in ID 0x%x\n", vha->host_no, fcport->loop_id)); + ql_dbg(ql_dbg_disc, vha, 0x2003, + "Port login OK: logged in ID 0x%x.\n", + fcport->loop_id); qla2x00_update_fcport(vha, fcport); } else if (status == 1) { set_bit(RELOGIN_NEEDED, &vha->dpc_flags); /* retry the login again */ - DEBUG(printk("scsi(%ld): Retrying" - " %d login again loop_id 0x%x\n", - vha->host_no, fcport->login_retry, - fcport->loop_id)); + ql_dbg(ql_dbg_disc, vha, 0x2007, + "Retrying %d login again loop_id 0x%x.\n", + fcport->login_retry, fcport->loop_id); } else { fcport->login_retry = 0; } if (fcport->login_retry == 0 && status != QLA_SUCCESS) - fcport->loop_id = FC_NO_LOOP_ID; + qla2x00_clear_loop_id(fcport); } if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) break; } } +/* 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 = + container_of(work, struct qla_hw_data, nic_core_unrecoverable); + 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 = + container_of(work, struct qla_hw_data, idc_state_handler); + 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); +} + +static int +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; + + 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; + } + + 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 */ + } +} + +static int +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; + ql_dbg(ql_dbg_p3p, base_vha, 0xb086, + "Trying force recovery of the IDC lock.\n"); + + 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; +} + +static int +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; + uint32_t lock_owner; + 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 { + qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, + &lock_owner); + ql_dbg(ql_dbg_p3p, base_vha, 0xb063, + "Failed to acquire IDC lock, acquired by %d, " + "retrying...\n", lock_owner); + + /* 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; +} + +static void +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); + if ((drv_ack & drv_presence) == drv_presence) + 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"); +} + +static int +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; +} + +void +qla2x00_disable_board_on_pci_error(struct work_struct *work) +{ + struct qla_hw_data *ha = container_of(work, struct qla_hw_data, + board_disable); + struct pci_dev *pdev = ha->pdev; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + + ql_log(ql_log_warn, base_vha, 0x015b, + "Disabling adapter.\n"); + + set_bit(UNLOADING, &base_vha->dpc_flags); + + qla2x00_delete_all_vps(ha, base_vha); + + qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16); + + qla2x00_dfs_remove(base_vha); + + qla84xx_put_chip(base_vha); + + if (base_vha->timer_active) + qla2x00_stop_timer(base_vha); + + base_vha->flags.online = 0; + + qla2x00_destroy_deferred_work(ha); + + /* + * Do not try to stop beacon blink as it will issue a mailbox + * command. + */ + qla2x00_free_sysfs_attr(base_vha, false); + + fc_remove_host(base_vha->host); + + scsi_remove_host(base_vha->host); + + base_vha->flags.init_done = 0; + qla25xx_delete_queues(base_vha); + qla2x00_free_irqs(base_vha); + qla2x00_free_fcports(base_vha); + qla2x00_mem_free(ha); + qla82xx_md_free(base_vha); + qla2x00_free_queues(ha); + + scsi_host_put(base_vha->host); + + qla2x00_unmap_iobases(ha); + + pci_release_selected_regions(ha->pdev, ha->bars); + kfree(ha); + ha = NULL; + + pci_disable_pcie_error_reporting(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + +} + /************************************************************************** * qla2x00_do_dpc * This kernel thread is a task that is schedule by the interrupt handler @@ -2641,42 +4837,135 @@ qla2x00_do_dpc(void *data) ha = (struct qla_hw_data *)data; base_vha = pci_get_drvdata(ha->pdev); - set_user_nice(current, -20); + set_user_nice(current, MIN_NICE); + set_current_state(TASK_INTERRUPTIBLE); while (!kthread_should_stop()) { - DEBUG3(printk("qla2x00: DPC handler sleeping\n")); + ql_dbg(ql_dbg_dpc, base_vha, 0x4000, + "DPC handler sleeping.\n"); - set_current_state(TASK_INTERRUPTIBLE); schedule(); __set_current_state(TASK_RUNNING); - DEBUG3(printk("qla2x00: DPC handler waking up\n")); + if (!base_vha->flags.init_done || ha->flags.mbox_busy) + goto end_loop; - /* Initialization not yet finished. Don't do anything yet. */ - if (!base_vha->flags.init_done) - continue; - - DEBUG3(printk("scsi(%ld): DPC handler\n", base_vha->host_no)); + if (ha->flags.eeh_busy) { + ql_dbg(ql_dbg_dpc, base_vha, 0x4003, + "eeh_busy=%d.\n", ha->flags.eeh_busy); + goto end_loop; + } ha->dpc_active = 1; - if (ha->flags.mbox_busy) { - ha->dpc_active = 0; - continue; - } + ql_dbg(ql_dbg_dpc + ql_dbg_verbose, base_vha, 0x4001, + "DPC handler waking up, dpc_flags=0x%lx.\n", + base_vha->dpc_flags); qla2x00_do_work(base_vha); + if (IS_P3P_TYPE(ha)) { + if (IS_QLA8044(ha)) { + if (test_and_clear_bit(ISP_UNRECOVERABLE, + &base_vha->dpc_flags)) { + qla8044_idc_lock(ha); + qla8044_wr_direct(base_vha, + QLA8044_CRB_DEV_STATE_INDEX, + QLA8XXX_DEV_FAILED); + qla8044_idc_unlock(ha); + ql_log(ql_log_info, base_vha, 0x4004, + "HW State: FAILED.\n"); + qla8044_device_state_handler(base_vha); + continue; + } + + } else { + if (test_and_clear_bit(ISP_UNRECOVERABLE, + &base_vha->dpc_flags)) { + qla82xx_idc_lock(ha); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA8XXX_DEV_FAILED); + qla82xx_idc_unlock(ha); + ql_log(ql_log_info, base_vha, 0x0151, + "HW State: FAILED.\n"); + qla82xx_device_state_handler(base_vha); + continue; + } + } + + if (test_and_clear_bit(FCOE_CTX_RESET_NEEDED, + &base_vha->dpc_flags)) { + + ql_dbg(ql_dbg_dpc, base_vha, 0x4005, + "FCoE context reset scheduled.\n"); + 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); + } + + ql_dbg(ql_dbg_dpc, base_vha, 0x4006, + "FCoE context reset end.\n"); + } + } else if (IS_QLAFX00(ha)) { + if (test_and_clear_bit(ISP_UNRECOVERABLE, + &base_vha->dpc_flags)) { + ql_dbg(ql_dbg_dpc, base_vha, 0x4020, + "Firmware Reset Recovery\n"); + if (qlafx00_reset_initialize(base_vha)) { + /* Failed. Abort isp later. */ + if (!test_bit(UNLOADING, + &base_vha->dpc_flags)) { + set_bit(ISP_UNRECOVERABLE, + &base_vha->dpc_flags); + ql_dbg(ql_dbg_dpc, base_vha, + 0x4021, + "Reset Recovery Failed\n"); + } + } + } + + if (test_and_clear_bit(FX00_TARGET_SCAN, + &base_vha->dpc_flags)) { + ql_dbg(ql_dbg_dpc, base_vha, 0x4022, + "ISPFx00 Target Scan scheduled\n"); + if (qlafx00_rescan_isp(base_vha)) { + if (!test_bit(UNLOADING, + &base_vha->dpc_flags)) + set_bit(ISP_UNRECOVERABLE, + &base_vha->dpc_flags); + ql_dbg(ql_dbg_dpc, base_vha, 0x401e, + "ISPFx00 Target Scan Failed\n"); + } + ql_dbg(ql_dbg_dpc, base_vha, 0x401f, + "ISPFx00 Target Scan End\n"); + } + if (test_and_clear_bit(FX00_HOST_INFO_RESEND, + &base_vha->dpc_flags)) { + ql_dbg(ql_dbg_dpc, base_vha, 0x4023, + "ISPFx00 Host Info resend scheduled\n"); + qlafx00_fx_disc(base_vha, + &base_vha->hw->mr.fcport, + FXDISC_REG_HOST_INFO); + } + } + if (test_and_clear_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) { - DEBUG(printk("scsi(%ld): dpc: sched " - "qla2x00_abort_isp ha = %p\n", - base_vha->host_no, ha)); + ql_dbg(ql_dbg_dpc, base_vha, 0x4007, + "ISP abort scheduled.\n"); if (!(test_and_set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags))) { - if (qla2x00_abort_isp(base_vha)) { + if (ha->isp_ops->abort_isp(base_vha)) { /* failed. retry later */ set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags); @@ -2685,24 +4974,71 @@ qla2x00_do_dpc(void *data) &base_vha->dpc_flags); } - DEBUG(printk("scsi(%ld): dpc: qla2x00_abort_isp end\n", - base_vha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x4008, + "ISP abort end.\n"); } - if (test_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags)) { + if (test_and_clear_bit(FCPORT_UPDATE_NEEDED, + &base_vha->dpc_flags)) { qla2x00_update_fcports(base_vha); - clear_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags); + } + + 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); + } + + if (IS_QLAFX00(ha)) + goto loop_resync_check; + + if (test_bit(ISP_QUIESCE_NEEDED, &base_vha->dpc_flags)) { + ql_dbg(ql_dbg_dpc, base_vha, 0x4009, + "Quiescence mode scheduled.\n"); + if (IS_P3P_TYPE(ha)) { + if (IS_QLA82XX(ha)) + qla82xx_device_state_handler(base_vha); + if (IS_QLA8044(ha)) + qla8044_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); + if (IS_QLA82XX(ha)) { + qla82xx_idc_lock(ha); + qla82xx_clear_qsnt_ready( + base_vha); + qla82xx_idc_unlock(ha); + } else if (IS_QLA8044(ha)) { + qla8044_idc_lock(ha); + qla8044_clear_qsnt_ready( + base_vha); + qla8044_idc_unlock(ha); + } + } + } else { + clear_bit(ISP_QUIESCE_NEEDED, + &base_vha->dpc_flags); + qla2x00_quiesce_io(base_vha); + } + ql_dbg(ql_dbg_dpc, base_vha, 0x400a, + "Quiescence mode end.\n"); } if (test_and_clear_bit(RESET_MARKER_NEEDED, - &base_vha->dpc_flags) && + &base_vha->dpc_flags) && (!(test_and_set_bit(RESET_ACTIVE, &base_vha->dpc_flags)))) { - DEBUG(printk("scsi(%ld): qla2x00_reset_marker()\n", - base_vha->host_no)); - + ql_dbg(ql_dbg_dpc, base_vha, 0x400b, + "Reset marker scheduled.\n"); qla2x00_rst_aen(base_vha); clear_bit(RESET_ACTIVE, &base_vha->dpc_flags); + ql_dbg(ql_dbg_dpc, base_vha, 0x400c, + "Reset marker end.\n"); } /* Retry each device up to login retry count */ @@ -2711,19 +5047,18 @@ qla2x00_do_dpc(void *data) !test_bit(LOOP_RESYNC_NEEDED, &base_vha->dpc_flags) && atomic_read(&base_vha->loop_state) != LOOP_DOWN) { - DEBUG(printk("scsi(%ld): qla2x00_port_login()\n", - base_vha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x400d, + "Relogin scheduled.\n"); qla2x00_relogin(base_vha); - - DEBUG(printk("scsi(%ld): qla2x00_port_login - end\n", - base_vha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x400e, + "Relogin end.\n"); } - +loop_resync_check: if (test_and_clear_bit(LOOP_RESYNC_NEEDED, - &base_vha->dpc_flags)) { + &base_vha->dpc_flags)) { - DEBUG(printk("scsi(%ld): qla2x00_loop_resync()\n", - base_vha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x400f, + "Loop resync scheduled.\n"); if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, &base_vha->dpc_flags))) { @@ -2734,35 +5069,49 @@ qla2x00_do_dpc(void *data) &base_vha->dpc_flags); } - DEBUG(printk("scsi(%ld): qla2x00_loop_resync - end\n", - base_vha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x4010, + "Loop resync end.\n"); } + if (IS_QLAFX00(ha)) + goto intr_on_check; + 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); } +intr_on_check: if (!ha->interrupts_on) ha->isp_ops->enable_intrs(ha); if (test_and_clear_bit(BEACON_BLINK_NEEDED, - &base_vha->dpc_flags)) - ha->isp_ops->beacon_blink(base_vha); + &base_vha->dpc_flags)) { + if (ha->beacon_blink_led == 1) + ha->isp_ops->beacon_blink(base_vha); + } - qla2x00_do_dpc_all_vps(base_vha); + if (!IS_QLAFX00(ha)) + qla2x00_do_dpc_all_vps(base_vha); ha->dpc_active = 0; +end_loop: + set_current_state(TASK_INTERRUPTIBLE); } /* End of while(1) */ + __set_current_state(TASK_RUNNING); - DEBUG(printk("scsi(%ld): DPC handler exiting\n", base_vha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x4011, + "DPC handler exiting.\n"); /* * Make sure that nobody tries to wake us up again. */ ha->dpc_active = 0; + /* Cleanup any residual CTX SRBs. */ + qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16); + return 0; } @@ -2802,30 +5151,6 @@ qla2x00_rst_aen(scsi_qla_host_t *vha) } } -static void -qla2x00_sp_free_dma(srb_t *sp) -{ - struct scsi_cmnd *cmd = sp->cmd; - - if (sp->flags & SRB_DMA_VALID) { - scsi_dma_unmap(cmd); - sp->flags &= ~SRB_DMA_VALID; - } - CMD_SP(cmd) = NULL; -} - -void -qla2x00_sp_compl(struct qla_hw_data *ha, srb_t *sp) -{ - struct scsi_cmnd *cmd = sp->cmd; - - qla2x00_sp_free_dma(sp); - - mempool_free(sp, ha->srb_mempool); - - cmd->scsi_done(cmd); -} - /************************************************************************** * qla2x00_timer * @@ -2838,115 +5163,126 @@ void qla2x00_timer(scsi_qla_host_t *vha) { unsigned long cpu_flags = 0; - fc_port_t *fcport; int start_dpc = 0; int index; srb_t *sp; - int t; + uint16_t w; struct qla_hw_data *ha = vha->hw; struct req_que *req; - /* - * Ports - Port down timer. - * - * Whenever, a port is in the LOST state we start decrementing its port - * down timer every second until it reaches zero. Once it reaches zero - * the port it marked DEAD. - */ - t = 0; - list_for_each_entry(fcport, &vha->vp_fcports, list) { - if (fcport->port_type != FCT_TARGET) - continue; - - if (atomic_read(&fcport->state) == FCS_DEVICE_LOST) { - if (atomic_read(&fcport->port_down_timer) == 0) - continue; + if (ha->flags.eeh_busy) { + ql_dbg(ql_dbg_timer, vha, 0x6000, + "EEH = %d, restarting timer.\n", + ha->flags.eeh_busy); + qla2x00_restart_timer(vha, WATCH_INTERVAL); + return; + } - if (atomic_dec_and_test(&fcport->port_down_timer) != 0) - atomic_set(&fcport->state, FCS_DEVICE_DEAD); + /* + * Hardware read to raise pending EEH errors during mailbox waits. If + * the read returns -1 then disable the board. + */ + if (!pci_channel_offline(ha->pdev)) { + pci_read_config_word(ha->pdev, PCI_VENDOR_ID, &w); + if (w == 0xffff) + /* + * Schedule this on the default system workqueue so that + * all the adapter workqueues and the DPC thread can be + * shutdown cleanly. + */ + schedule_work(&ha->board_disable); + } - DEBUG(printk("scsi(%ld): fcport-%d - port retry count: " - "%d remaining\n", - vha->host_no, - t, atomic_read(&fcport->port_down_timer))); - } - t++; - } /* End of for fcport */ + /* Make sure qla82xx_watchdog is run only for physical port */ + if (!vha->vp_idx && IS_P3P_TYPE(ha)) { + if (test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) + start_dpc++; + if (IS_QLA82XX(ha)) + qla82xx_watchdog(vha); + else if (IS_QLA8044(ha)) + qla8044_watchdog(vha); + } + if (!vha->vp_idx && IS_QLAFX00(ha)) + qlafx00_timer_routine(vha); /* Loop down handler. */ if (atomic_read(&vha->loop_down_timer) > 0 && - !(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) + !(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) && + !(test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags)) && vha->flags.online) { if (atomic_read(&vha->loop_down_timer) == vha->loop_down_abort_time) { - DEBUG(printk("scsi(%ld): Loop Down - aborting the " - "queues before time expire\n", - vha->host_no)); + ql_log(ql_log_info, vha, 0x6008, + "Loop down - aborting the queues before time expires.\n"); if (!IS_QLA2100(ha) && vha->link_down_timeout) atomic_set(&vha->loop_state, LOOP_DEAD); - /* Schedule an ISP abort to return any tape commands. */ + /* + * Schedule an ISP abort to return any FCP2-device + * commands. + */ /* NPIV - scan physical port only */ if (!vha->vp_idx) { spin_lock_irqsave(&ha->hardware_lock, cpu_flags); req = ha->req_q_map[0]; for (index = 1; - index < MAX_OUTSTANDING_COMMANDS; + index < req->num_outstanding_cmds; index++) { fc_port_t *sfcp; sp = req->outstanding_cmds[index]; if (!sp) continue; + if (sp->type != SRB_SCSI_CMD) + continue; sfcp = sp->fcport; - if (!(sfcp->flags & FCF_TAPE_PRESENT)) + if (!(sfcp->flags & FCF_FCP2_DEVICE)) continue; - set_bit(ISP_ABORT_NEEDED, + if (IS_QLA82XX(ha)) + set_bit(FCOE_CTX_RESET_NEEDED, + &vha->dpc_flags); + else + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); break; } spin_unlock_irqrestore(&ha->hardware_lock, cpu_flags); } - set_bit(ABORT_QUEUES_NEEDED, &vha->dpc_flags); start_dpc++; } /* if the loop has been down for 4 minutes, reinit adapter */ if (atomic_dec_and_test(&vha->loop_down_timer) != 0) { - DEBUG(printk("scsi(%ld): Loop down exceed 4 mins - " - "restarting queues.\n", - vha->host_no)); - - set_bit(RESTART_QUEUES_NEEDED, &vha->dpc_flags); - start_dpc++; - - if (!(vha->device_flags & DFLG_NO_CABLE) && - !vha->vp_idx) { - DEBUG(printk("scsi(%ld): Loop down - " - "aborting ISP.\n", - vha->host_no)); - qla_printk(KERN_WARNING, ha, + if (!(vha->device_flags & DFLG_NO_CABLE)) { + ql_log(ql_log_warn, vha, 0x6009, "Loop down - aborting ISP.\n"); - set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + if (IS_QLA82XX(ha)) + set_bit(FCOE_CTX_RESET_NEEDED, + &vha->dpc_flags); + else + set_bit(ISP_ABORT_NEEDED, + &vha->dpc_flags); } } - DEBUG3(printk("scsi(%ld): Loop Down - seconds remaining %d\n", - vha->host_no, - atomic_read(&vha->loop_down_timer))); + ql_dbg(ql_dbg_timer, vha, 0x600a, + "Loop down - seconds remaining %d.\n", + atomic_read(&vha->loop_down_timer)); } - - /* Check if beacon LED needs to be blinked */ - if (ha->beacon_blink_led == 1) { - set_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags); - start_dpc++; + /* Check if beacon LED needs to be blinked for physical host only */ + if (!vha->vp_idx && (ha->beacon_blink_led == 1)) { + /* There is no beacon_blink function for ISP82xx */ + if (!IS_P3P_TYPE(ha)) { + set_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags); + start_dpc++; + } } /* Process any deferred work. */ @@ -2960,16 +5296,37 @@ qla2x00_timer(scsi_qla_host_t *vha) start_dpc || test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) || 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) || - test_bit(RELOGIN_NEEDED, &vha->dpc_flags))) + test_bit(RELOGIN_NEEDED, &vha->dpc_flags))) { + 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 " + "relogin_needed=%d.\n", + 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), + test_bit(RELOGIN_NEEDED, &vha->dpc_flags)); qla2xxx_wake_dpc(vha); + } qla2x00_restart_timer(vha, WATCH_INTERVAL); } /* Firmware interface routines. */ -#define FW_BLOBS 7 +#define FW_BLOBS 11 #define FW_ISP21XX 0 #define FW_ISP22XX 1 #define FW_ISP2300 2 @@ -2977,6 +5334,10 @@ qla2x00_timer(scsi_qla_host_t *vha) #define FW_ISP24XX 4 #define FW_ISP25XX 5 #define FW_ISP81XX 6 +#define FW_ISP82XX 7 +#define FW_ISP2031 8 +#define FW_ISP8031 9 +#define FW_ISP27XX 10 #define FW_FILE_ISP21XX "ql2100_fw.bin" #define FW_FILE_ISP22XX "ql2200_fw.bin" @@ -2985,6 +5346,11 @@ qla2x00_timer(scsi_qla_host_t *vha) #define FW_FILE_ISP24XX "ql2400_fw.bin" #define FW_FILE_ISP25XX "ql2500_fw.bin" #define FW_FILE_ISP81XX "ql8100_fw.bin" +#define FW_FILE_ISP82XX "ql8200_fw.bin" +#define FW_FILE_ISP2031 "ql2600_fw.bin" +#define FW_FILE_ISP8031 "ql8300_fw.bin" +#define FW_FILE_ISP27XX "ql2700_fw.bin" + static DEFINE_MUTEX(qla_fw_lock); @@ -2996,6 +5362,10 @@ static struct fw_blob qla_fw_blobs[FW_BLOBS] = { { .name = FW_FILE_ISP24XX, }, { .name = FW_FILE_ISP25XX, }, { .name = FW_FILE_ISP81XX, }, + { .name = FW_FILE_ISP82XX, }, + { .name = FW_FILE_ISP2031, }, + { .name = FW_FILE_ISP8031, }, + { .name = FW_FILE_ISP27XX, }, }; struct fw_blob * @@ -3004,7 +5374,6 @@ qla2x00_request_firmware(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; struct fw_blob *blob; - blob = NULL; if (IS_QLA2100(ha)) { blob = &qla_fw_blobs[FW_ISP21XX]; } else if (IS_QLA2200(ha)) { @@ -3019,6 +5388,16 @@ qla2x00_request_firmware(scsi_qla_host_t *vha) blob = &qla_fw_blobs[FW_ISP25XX]; } else if (IS_QLA81XX(ha)) { blob = &qla_fw_blobs[FW_ISP81XX]; + } else if (IS_QLA82XX(ha)) { + blob = &qla_fw_blobs[FW_ISP82XX]; + } else if (IS_QLA2031(ha)) { + blob = &qla_fw_blobs[FW_ISP2031]; + } else if (IS_QLA8031(ha)) { + blob = &qla_fw_blobs[FW_ISP8031]; + } else if (IS_QLA27XX(ha)) { + blob = &qla_fw_blobs[FW_ISP27XX]; + } else { + return NULL; } mutex_lock(&qla_fw_lock); @@ -3026,8 +5405,8 @@ qla2x00_request_firmware(scsi_qla_host_t *vha) goto out; if (request_firmware(&blob->fw, blob->name, &ha->pdev->dev)) { - DEBUG2(printk("scsi(%ld): Failed to load firmware image " - "(%s).\n", vha->host_no, blob->name)); + ql_log(ql_log_warn, vha, 0x0063, + "Failed to load firmware image (%s).\n", blob->name); blob->fw = NULL; blob = NULL; goto out; @@ -3045,22 +5424,39 @@ qla2x00_release_firmware(void) mutex_lock(&qla_fw_lock); for (idx = 0; idx < FW_BLOBS; idx++) - if (qla_fw_blobs[idx].fw) - release_firmware(qla_fw_blobs[idx].fw); + release_firmware(qla_fw_blobs[idx].fw); mutex_unlock(&qla_fw_lock); } static pci_ers_result_t qla2xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { + scsi_qla_host_t *vha = pci_get_drvdata(pdev); + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_aer, vha, 0x9000, + "PCI error detected, state %x.\n", state); + switch (state) { case pci_channel_io_normal: + ha->flags.eeh_busy = 0; return PCI_ERS_RESULT_CAN_RECOVER; case pci_channel_io_frozen: + ha->flags.eeh_busy = 1; + /* For ISP82XX complete any pending mailbox cmd */ + if (IS_QLA82XX(ha)) { + ha->flags.isp82xx_fw_hung = 1; + ql_dbg(ql_dbg_aer, vha, 0x9001, "Pci channel io frozen\n"); + qla82xx_clear_pending_mbx(vha); + } + qla2x00_free_irqs(vha); pci_disable_device(pdev); + /* Return back all IOs */ + qla2x00_abort_all_cmds(vha, DID_RESET << 16); return PCI_ERS_RESULT_NEED_RESET; case pci_channel_io_perm_failure: - qla2x00_remove_one(pdev); + ha->flags.pci_channel_io_perm_failure = 1; + qla2x00_abort_all_cmds(vha, DID_NO_CONNECT << 16); return PCI_ERS_RESULT_DISCONNECT; } return PCI_ERS_RESULT_NEED_RESET; @@ -3077,6 +5473,9 @@ qla2xxx_pci_mmio_enabled(struct pci_dev *pdev) struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24; + if (IS_QLA82XX(ha)) + return PCI_ERS_RESULT_RECOVERED; + spin_lock_irqsave(&ha->hardware_lock, flags); if (IS_QLA2100(ha) || IS_QLA2200(ha)){ stat = RD_REG_DWORD(®->hccr); @@ -3094,8 +5493,8 @@ qla2xxx_pci_mmio_enabled(struct pci_dev *pdev) spin_unlock_irqrestore(&ha->hardware_lock, flags); if (risc_paused) { - qla_printk(KERN_INFO, ha, "RISC paused -- mmio_enabled, " - "Dumping firmware!\n"); + ql_log(ql_log_info, base_vha, 0x9003, + "RISC paused -- mmio_enabled, Dumping firmware.\n"); ha->isp_ops->fw_dump(base_vha, 0); return PCI_ERS_RESULT_NEED_RESET; @@ -3103,13 +5502,138 @@ qla2xxx_pci_mmio_enabled(struct pci_dev *pdev) return PCI_ERS_RESULT_RECOVERED; } +static uint32_t +qla82xx_error_recovery(scsi_qla_host_t *base_vha) +{ + 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; + + ql_dbg(ql_dbg_aer, base_vha, 0x9006, + "Entered %s.\n", __func__); + + 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--; + ql_dbg(ql_dbg_aer, base_vha, 0x9007, + "Finding pci device at function = 0x%x.\n", fn); + 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)) { + ql_dbg(ql_dbg_aer, base_vha, 0x9008, + "Found PCI func available and enable at 0x%x.\n", + fn); + pci_dev_put(other_pdev); + break; + } + pci_dev_put(other_pdev); + } + + if (!fn) { + /* Reset owner */ + ql_dbg(ql_dbg_aer, base_vha, 0x9009, + "This devfn is reset owner = 0x%x.\n", + ha->pdev->devfn); + qla82xx_idc_lock(ha); + + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA8XXX_DEV_INITIALIZING); + + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, + QLA82XX_IDC_VERSION); + + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + ql_dbg(ql_dbg_aer, base_vha, 0x900a, + "drv_active = 0x%x.\n", drv_active); + + 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) { + ql_log(ql_log_info, base_vha, 0x900b, + "HW State: FAILED.\n"); + qla82xx_clear_drv_active(ha); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA8XXX_DEV_FAILED); + } else { + ql_log(ql_log_info, base_vha, 0x900c, + "HW State: READY.\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA8XXX_DEV_READY); + qla82xx_idc_unlock(ha); + ha->flags.isp82xx_fw_hung = 0; + 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 { + ql_dbg(ql_dbg_aer, base_vha, 0x900d, + "This devfn is not reset owner = 0x%x.\n", + ha->pdev->devfn); + if ((qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE) == + QLA8XXX_DEV_READY)) { + ha->flags.isp82xx_fw_hung = 0; + 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; +} + static pci_ers_result_t qla2xxx_pci_slot_reset(struct pci_dev *pdev) { pci_ers_result_t ret = PCI_ERS_RESULT_DISCONNECT; scsi_qla_host_t *base_vha = pci_get_drvdata(pdev); struct qla_hw_data *ha = base_vha->hw; - int rc; + struct rsp_que *rsp; + int rc, retries = 10; + + ql_dbg(ql_dbg_aer, base_vha, 0x9004, + "Slot Reset.\n"); + + /* 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); + + /* pci_restore_state() clears the saved_state flag of the device + * save restored state which resets saved_state flag + */ + pci_save_state(pdev); if (ha->mem_only) rc = pci_enable_device_mem(pdev); @@ -3117,21 +5641,39 @@ qla2xxx_pci_slot_reset(struct pci_dev *pdev) rc = pci_enable_device(pdev); if (rc) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, base_vha, 0x9005, "Can't re-enable PCI device after reset.\n"); - - return ret; + goto exit_slot_reset; } - pci_set_master(pdev); + + rsp = ha->rsp_q_map[0]; + if (qla2x00_request_irqs(ha, rsp)) + goto exit_slot_reset; if (ha->isp_ops->pci_config(base_vha)) - return ret; + 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; + } + + while (ha->flags.mbox_busy && retries--) + msleep(1000); set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); - if (qla2x00_abort_isp(base_vha) == QLA_SUCCESS) + if (ha->isp_ops->abort_isp(base_vha) == QLA_SUCCESS) ret = PCI_ERS_RESULT_RECOVERED; clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); + +exit_slot_reset: + ql_dbg(ql_dbg_aer, base_vha, 0x900e, + "slot_reset return %x.\n", ret); + return ret; } @@ -3142,16 +5684,21 @@ qla2xxx_pci_resume(struct pci_dev *pdev) struct qla_hw_data *ha = base_vha->hw; int ret; + ql_dbg(ql_dbg_aer, base_vha, 0x900f, + "pci_resume.\n"); + ret = qla2x00_wait_for_hba_online(base_vha); if (ret != QLA_SUCCESS) { - qla_printk(KERN_ERR, ha, - "the device failed to resume I/O " - "from slot/link_reset"); + ql_log(ql_log_fatal, base_vha, 0x9002, + "The device failed to resume I/O from slot/link_reset.\n"); } + pci_cleanup_aer_uncorrect_error_status(pdev); + + ha->flags.eeh_busy = 0; } -static struct pci_error_handlers qla2xxx_err_handler = { +static const struct pci_error_handlers qla2xxx_err_handler = { .error_detected = qla2xxx_pci_error_detected, .mmio_enabled = qla2xxx_pci_mmio_enabled, .slot_reset = qla2xxx_pci_slot_reset, @@ -3172,7 +5719,14 @@ static struct pci_device_id qla2xxx_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5422) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5432) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2532) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2031) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8001) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8021) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8031) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISPF001) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8044) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2071) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2271) }, { 0 }, }; MODULE_DEVICE_TABLE(pci, qla2xxx_pci_tbl); @@ -3185,9 +5739,15 @@ static struct pci_driver qla2xxx_pci_driver = { .id_table = qla2xxx_pci_tbl, .probe = qla2x00_probe_one, .remove = qla2x00_remove_one, + .shutdown = qla2x00_shutdown, .err_handler = &qla2xxx_err_handler, }; +static const struct file_operations apidev_fops = { + .owner = THIS_MODULE, + .llseek = noop_llseek, +}; + /** * qla2x00_module_init - Module initialization. **/ @@ -3200,11 +5760,26 @@ qla2x00_module_init(void) srb_cachep = kmem_cache_create("qla2xxx_srbs", sizeof(srb_t), 0, SLAB_HWCACHE_ALIGN, NULL); if (srb_cachep == NULL) { - printk(KERN_ERR - "qla2xxx: Unable to allocate SRB cache...Failing load!\n"); + ql_log(ql_log_fatal, NULL, 0x0001, + "Unable to allocate SRB cache...Failing load!.\n"); return -ENOMEM; } + /* 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; + } + /* Derive version string. */ strcpy(qla2x00_version_str, QLA2XXX_VERSION); if (ql2xextended_error_logging) @@ -3214,23 +5789,40 @@ qla2x00_module_init(void) fc_attach_transport(&qla2xxx_transport_functions); if (!qla2xxx_transport_template) { kmem_cache_destroy(srb_cachep); + ql_log(ql_log_fatal, NULL, 0x0002, + "fc_attach_transport failed...Failing load!.\n"); + qlt_exit(); return -ENODEV; } + + apidev_major = register_chrdev(0, QLA2XXX_APIDEV, &apidev_fops); + if (apidev_major < 0) { + ql_log(ql_log_fatal, NULL, 0x0003, + "Unable to register char device %s.\n", QLA2XXX_APIDEV); + } + qla2xxx_transport_vport_template = fc_attach_transport(&qla2xxx_transport_vport_functions); if (!qla2xxx_transport_vport_template) { kmem_cache_destroy(srb_cachep); + qlt_exit(); fc_release_transport(qla2xxx_transport_template); + ql_log(ql_log_fatal, NULL, 0x0004, + "fc_attach_transport vport failed...Failing load!.\n"); return -ENODEV; } - - printk(KERN_INFO "QLogic Fibre Channel HBA Driver: %s\n", + ql_log(ql_log_info, NULL, 0x0005, + "QLogic Fibre Channel HBA Driver: %s.\n", qla2x00_version_str); ret = pci_register_driver(&qla2xxx_pci_driver); if (ret) { kmem_cache_destroy(srb_cachep); + qlt_exit(); fc_release_transport(qla2xxx_transport_template); fc_release_transport(qla2xxx_transport_vport_template); + ql_log(ql_log_fatal, NULL, 0x0006, + "pci_register_driver failed...ret=%d Failing load!.\n", + ret); } return ret; } @@ -3241,9 +5833,13 @@ qla2x00_module_init(void) static void __exit qla2x00_module_exit(void) { + unregister_chrdev(apidev_major, QLA2XXX_APIDEV); pci_unregister_driver(&qla2xxx_pci_driver); qla2x00_release_firmware(); kmem_cache_destroy(srb_cachep); + qlt_exit(); + if (ctx_cachep) + kmem_cache_destroy(ctx_cachep); fc_release_transport(qla2xxx_transport_template); fc_release_transport(qla2xxx_transport_vport_template); } @@ -3261,4 +5857,3 @@ MODULE_FIRMWARE(FW_FILE_ISP2300); MODULE_FIRMWARE(FW_FILE_ISP2322); MODULE_FIRMWARE(FW_FILE_ISP24XX); MODULE_FIRMWARE(FW_FILE_ISP25XX); -MODULE_FIRMWARE(FW_FILE_ISP81XX); |
