| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * QLogic Fibre Channel HBA Driver |
| * Copyright (c) 2003-2014 QLogic Corporation |
| */ |
| #include "qla_def.h" |
| #include "qla_gbl.h" |
| |
| #include <linux/delay.h> |
| #include <linux/slab.h> |
| #include <linux/vmalloc.h> |
| |
| #include "qla_devtbl.h" |
| |
| #ifdef CONFIG_SPARC |
| #include <asm/prom.h> |
| #endif |
| |
| #include "qla_target.h" |
| |
| /* |
| * QLogic ISP2x00 Hardware Support Function Prototypes. |
| */ |
| static int qla2x00_isp_firmware(scsi_qla_host_t *); |
| static int qla2x00_setup_chip(scsi_qla_host_t *); |
| static int qla2x00_fw_ready(scsi_qla_host_t *); |
| static int qla2x00_configure_hba(scsi_qla_host_t *); |
| static int qla2x00_configure_loop(scsi_qla_host_t *); |
| static int qla2x00_configure_local_loop(scsi_qla_host_t *); |
| static int qla2x00_configure_fabric(scsi_qla_host_t *); |
| static int qla2x00_find_all_fabric_devs(scsi_qla_host_t *); |
| static int qla2x00_restart_isp(scsi_qla_host_t *); |
| |
| static struct qla_chip_state_84xx *qla84xx_get_chip(struct scsi_qla_host *); |
| static int qla84xx_init_chip(scsi_qla_host_t *); |
| static int qla25xx_init_queues(struct qla_hw_data *); |
| static void qla24xx_handle_gpdb_event(scsi_qla_host_t *vha, |
| struct event_arg *ea); |
| static void qla24xx_handle_prli_done_event(struct scsi_qla_host *, |
| struct event_arg *); |
| static void __qla24xx_handle_gpdb_event(scsi_qla_host_t *, struct event_arg *); |
| |
| /* SRB Extensions ---------------------------------------------------------- */ |
| |
| void |
| qla2x00_sp_timeout(struct timer_list *t) |
| { |
| srb_t *sp = from_timer(sp, t, u.iocb_cmd.timer); |
| struct srb_iocb *iocb; |
| scsi_qla_host_t *vha = sp->vha; |
| |
| WARN_ON(irqs_disabled()); |
| iocb = &sp->u.iocb_cmd; |
| iocb->timeout(sp); |
| |
| /* ref: TMR */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| |
| if (vha && qla2x00_isp_reg_stat(vha->hw)) { |
| ql_log(ql_log_info, vha, 0x9008, |
| "PCI/Register disconnect.\n"); |
| qla_pci_set_eeh_busy(vha); |
| } |
| } |
| |
| void qla2x00_sp_free(srb_t *sp) |
| { |
| struct srb_iocb *iocb = &sp->u.iocb_cmd; |
| |
| del_timer(&iocb->timer); |
| qla2x00_rel_sp(sp); |
| } |
| |
| void qla2xxx_rel_done_warning(srb_t *sp, int res) |
| { |
| WARN_ONCE(1, "Calling done() of an already freed srb %p object\n", sp); |
| } |
| |
| void qla2xxx_rel_free_warning(srb_t *sp) |
| { |
| WARN_ONCE(1, "Calling free() of an already freed srb %p object\n", sp); |
| } |
| |
| /* Asynchronous Login/Logout Routines -------------------------------------- */ |
| |
| unsigned long |
| qla2x00_get_async_timeout(struct scsi_qla_host *vha) |
| { |
| unsigned long tmo; |
| struct qla_hw_data *ha = vha->hw; |
| |
| /* Firmware should use switch negotiated r_a_tov for timeout. */ |
| tmo = ha->r_a_tov / 10 * 2; |
| if (IS_QLAFX00(ha)) { |
| tmo = FX00_DEF_RATOV * 2; |
| } else if (!IS_FWI2_CAPABLE(ha)) { |
| /* |
| * Except for earlier ISPs where the timeout is seeded from the |
| * initialization control block. |
| */ |
| tmo = ha->login_timeout; |
| } |
| return tmo; |
| } |
| |
| static void qla24xx_abort_iocb_timeout(void *data) |
| { |
| srb_t *sp = data; |
| struct srb_iocb *abt = &sp->u.iocb_cmd; |
| struct qla_qpair *qpair = sp->qpair; |
| u32 handle; |
| unsigned long flags; |
| int sp_found = 0, cmdsp_found = 0; |
| |
| if (sp->cmd_sp) |
| ql_dbg(ql_dbg_async, sp->vha, 0x507c, |
| "Abort timeout - cmd hdl=%x, cmd type=%x hdl=%x, type=%x\n", |
| sp->cmd_sp->handle, sp->cmd_sp->type, |
| sp->handle, sp->type); |
| else |
| ql_dbg(ql_dbg_async, sp->vha, 0x507c, |
| "Abort timeout 2 - hdl=%x, type=%x\n", |
| sp->handle, sp->type); |
| |
| spin_lock_irqsave(qpair->qp_lock_ptr, flags); |
| for (handle = 1; handle < qpair->req->num_outstanding_cmds; handle++) { |
| if (sp->cmd_sp && (qpair->req->outstanding_cmds[handle] == |
| sp->cmd_sp)) { |
| qpair->req->outstanding_cmds[handle] = NULL; |
| cmdsp_found = 1; |
| qla_put_fw_resources(qpair, &sp->cmd_sp->iores); |
| } |
| |
| /* removing the abort */ |
| if (qpair->req->outstanding_cmds[handle] == sp) { |
| qpair->req->outstanding_cmds[handle] = NULL; |
| sp_found = 1; |
| qla_put_fw_resources(qpair, &sp->iores); |
| break; |
| } |
| } |
| spin_unlock_irqrestore(qpair->qp_lock_ptr, flags); |
| |
| if (cmdsp_found && sp->cmd_sp) { |
| /* |
| * This done function should take care of |
| * original command ref: INIT |
| */ |
| sp->cmd_sp->done(sp->cmd_sp, QLA_OS_TIMER_EXPIRED); |
| } |
| |
| if (sp_found) { |
| abt->u.abt.comp_status = cpu_to_le16(CS_TIMEOUT); |
| sp->done(sp, QLA_OS_TIMER_EXPIRED); |
| } |
| } |
| |
| static void qla24xx_abort_sp_done(srb_t *sp, int res) |
| { |
| struct srb_iocb *abt = &sp->u.iocb_cmd; |
| srb_t *orig_sp = sp->cmd_sp; |
| |
| if (orig_sp) |
| qla_wait_nvme_release_cmd_kref(orig_sp); |
| |
| if (sp->flags & SRB_WAKEUP_ON_COMP) |
| complete(&abt->u.abt.comp); |
| else |
| /* ref: INIT */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| } |
| |
| int qla24xx_async_abort_cmd(srb_t *cmd_sp, bool wait) |
| { |
| scsi_qla_host_t *vha = cmd_sp->vha; |
| struct srb_iocb *abt_iocb; |
| srb_t *sp; |
| int rval = QLA_FUNCTION_FAILED; |
| |
| /* ref: INIT for ABTS command */ |
| sp = qla2xxx_get_qpair_sp(cmd_sp->vha, cmd_sp->qpair, cmd_sp->fcport, |
| GFP_ATOMIC); |
| if (!sp) |
| return QLA_MEMORY_ALLOC_FAILED; |
| |
| qla_vha_mark_busy(vha); |
| abt_iocb = &sp->u.iocb_cmd; |
| sp->type = SRB_ABT_CMD; |
| sp->name = "abort"; |
| sp->qpair = cmd_sp->qpair; |
| sp->cmd_sp = cmd_sp; |
| if (wait) |
| sp->flags = SRB_WAKEUP_ON_COMP; |
| |
| init_completion(&abt_iocb->u.abt.comp); |
| /* FW can send 2 x ABTS's timeout/20s */ |
| qla2x00_init_async_sp(sp, 42, qla24xx_abort_sp_done); |
| sp->u.iocb_cmd.timeout = qla24xx_abort_iocb_timeout; |
| |
| abt_iocb->u.abt.cmd_hndl = cmd_sp->handle; |
| abt_iocb->u.abt.req_que_no = cpu_to_le16(cmd_sp->qpair->req->id); |
| |
| ql_dbg(ql_dbg_async, vha, 0x507c, |
| "Abort command issued - hdl=%x, type=%x\n", cmd_sp->handle, |
| cmd_sp->type); |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) { |
| /* ref: INIT */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| return rval; |
| } |
| |
| if (wait) { |
| wait_for_completion(&abt_iocb->u.abt.comp); |
| rval = abt_iocb->u.abt.comp_status == CS_COMPLETE ? |
| QLA_SUCCESS : QLA_ERR_FROM_FW; |
| /* ref: INIT */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| } |
| |
| return rval; |
| } |
| |
| void |
| qla2x00_async_iocb_timeout(void *data) |
| { |
| srb_t *sp = data; |
| fc_port_t *fcport = sp->fcport; |
| struct srb_iocb *lio = &sp->u.iocb_cmd; |
| int rc, h; |
| unsigned long flags; |
| |
| if (fcport) { |
| ql_dbg(ql_dbg_disc, fcport->vha, 0x2071, |
| "Async-%s timeout - hdl=%x portid=%06x %8phC.\n", |
| sp->name, sp->handle, fcport->d_id.b24, fcport->port_name); |
| |
| fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| } else { |
| pr_info("Async-%s timeout - hdl=%x.\n", |
| sp->name, sp->handle); |
| } |
| |
| switch (sp->type) { |
| case SRB_LOGIN_CMD: |
| rc = qla24xx_async_abort_cmd(sp, false); |
| if (rc) { |
| /* Retry as needed. */ |
| lio->u.logio.data[0] = MBS_COMMAND_ERROR; |
| lio->u.logio.data[1] = |
| lio->u.logio.flags & SRB_LOGIN_RETRIED ? |
| QLA_LOGIO_LOGIN_RETRIED : 0; |
| spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags); |
| for (h = 1; h < sp->qpair->req->num_outstanding_cmds; |
| h++) { |
| if (sp->qpair->req->outstanding_cmds[h] == |
| sp) { |
| sp->qpair->req->outstanding_cmds[h] = |
| NULL; |
| break; |
| } |
| } |
| spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags); |
| sp->done(sp, QLA_FUNCTION_TIMEOUT); |
| } |
| break; |
| case SRB_LOGOUT_CMD: |
| case SRB_CT_PTHRU_CMD: |
| case SRB_MB_IOCB: |
| case SRB_NACK_PLOGI: |
| case SRB_NACK_PRLI: |
| case SRB_NACK_LOGO: |
| case SRB_CTRL_VP: |
| default: |
| rc = qla24xx_async_abort_cmd(sp, false); |
| if (rc) { |
| spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags); |
| for (h = 1; h < sp->qpair->req->num_outstanding_cmds; |
| h++) { |
| if (sp->qpair->req->outstanding_cmds[h] == |
| sp) { |
| sp->qpair->req->outstanding_cmds[h] = |
| NULL; |
| break; |
| } |
| } |
| spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags); |
| sp->done(sp, QLA_FUNCTION_TIMEOUT); |
| } |
| break; |
| } |
| } |
| |
| static void qla2x00_async_login_sp_done(srb_t *sp, int res) |
| { |
| struct scsi_qla_host *vha = sp->vha; |
| struct srb_iocb *lio = &sp->u.iocb_cmd; |
| struct event_arg ea; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20dd, |
| "%s %8phC res %d \n", __func__, sp->fcport->port_name, res); |
| |
| sp->fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| |
| if (!test_bit(UNLOADING, &vha->dpc_flags)) { |
| memset(&ea, 0, sizeof(ea)); |
| ea.fcport = sp->fcport; |
| ea.data[0] = lio->u.logio.data[0]; |
| ea.data[1] = lio->u.logio.data[1]; |
| ea.iop[0] = lio->u.logio.iop[0]; |
| ea.iop[1] = lio->u.logio.iop[1]; |
| ea.sp = sp; |
| if (res) |
| ea.data[0] = MBS_COMMAND_ERROR; |
| qla24xx_handle_plogi_done_event(vha, &ea); |
| } |
| |
| /* ref: INIT */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| } |
| |
| int |
| qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, |
| uint16_t *data) |
| { |
| srb_t *sp; |
| struct srb_iocb *lio; |
| int rval = QLA_FUNCTION_FAILED; |
| |
| if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT) || |
| fcport->loop_id == FC_NO_LOOP_ID) { |
| ql_log(ql_log_warn, vha, 0xffff, |
| "%s: %8phC - not sending command.\n", |
| __func__, fcport->port_name); |
| return rval; |
| } |
| |
| /* ref: INIT */ |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| qla2x00_set_fcport_disc_state(fcport, DSC_LOGIN_PEND); |
| fcport->flags |= FCF_ASYNC_SENT; |
| fcport->logout_completed = 0; |
| |
| sp->type = SRB_LOGIN_CMD; |
| sp->name = "login"; |
| sp->gen1 = fcport->rscn_gen; |
| sp->gen2 = fcport->login_gen; |
| qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2, |
| qla2x00_async_login_sp_done); |
| |
| lio = &sp->u.iocb_cmd; |
| if (N2N_TOPO(fcport->vha->hw) && fcport_is_bigger(fcport)) { |
| lio->u.logio.flags |= SRB_LOGIN_PRLI_ONLY; |
| } else { |
| if (vha->hw->flags.edif_enabled && |
| DBELL_ACTIVE(vha)) { |
| lio->u.logio.flags |= |
| (SRB_LOGIN_FCSP | SRB_LOGIN_SKIP_PRLI); |
| } else { |
| lio->u.logio.flags |= SRB_LOGIN_COND_PLOGI; |
| } |
| } |
| |
| if (NVME_TARGET(vha->hw, fcport)) |
| lio->u.logio.flags |= SRB_LOGIN_SKIP_PRLI; |
| |
| rval = qla2x00_start_sp(sp); |
| |
| ql_dbg(ql_dbg_disc, vha, 0x2072, |
| "Async-login - %8phC hdl=%x, loopid=%x portid=%06x retries=%d %s.\n", |
| fcport->port_name, sp->handle, fcport->loop_id, |
| fcport->d_id.b24, fcport->login_retry, |
| lio->u.logio.flags & SRB_LOGIN_FCSP ? "FCSP" : ""); |
| |
| if (rval != QLA_SUCCESS) { |
| fcport->flags |= FCF_LOGIN_NEEDED; |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| goto done_free_sp; |
| } |
| |
| return rval; |
| |
| done_free_sp: |
| /* ref: INIT */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| done: |
| fcport->flags &= ~FCF_ASYNC_ACTIVE; |
| |
| /* |
| * async login failed. Could be due to iocb/exchange resource |
| * being low. Set state DELETED for re-login process to start again. |
| */ |
| qla2x00_set_fcport_disc_state(fcport, DSC_DELETED); |
| return rval; |
| } |
| |
| static void qla2x00_async_logout_sp_done(srb_t *sp, int res) |
| { |
| sp->fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| sp->fcport->login_gen++; |
| qlt_logo_completion_handler(sp->fcport, sp->u.iocb_cmd.u.logio.data[0]); |
| /* ref: INIT */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| } |
| |
| int |
| qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| srb_t *sp; |
| int rval = QLA_FUNCTION_FAILED; |
| |
| fcport->flags |= FCF_ASYNC_SENT; |
| /* ref: INIT */ |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| sp->type = SRB_LOGOUT_CMD; |
| sp->name = "logout"; |
| qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2, |
| qla2x00_async_logout_sp_done), |
| |
| ql_dbg(ql_dbg_disc, vha, 0x2070, |
| "Async-logout - hdl=%x loop-id=%x portid=%02x%02x%02x %8phC explicit %d.\n", |
| sp->handle, fcport->loop_id, fcport->d_id.b.domain, |
| fcport->d_id.b.area, fcport->d_id.b.al_pa, |
| fcport->port_name, fcport->explicit_logout); |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| return rval; |
| |
| done_free_sp: |
| /* ref: INIT */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| done: |
| fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| return rval; |
| } |
| |
| void |
| qla2x00_async_prlo_done(struct scsi_qla_host *vha, fc_port_t *fcport, |
| uint16_t *data) |
| { |
| fcport->flags &= ~FCF_ASYNC_ACTIVE; |
| /* Don't re-login in target mode */ |
| if (!fcport->tgt_session) |
| qla2x00_mark_device_lost(vha, fcport, 1); |
| qlt_logo_completion_handler(fcport, data[0]); |
| } |
| |
| static void qla2x00_async_prlo_sp_done(srb_t *sp, int res) |
| { |
| struct srb_iocb *lio = &sp->u.iocb_cmd; |
| struct scsi_qla_host *vha = sp->vha; |
| |
| sp->fcport->flags &= ~FCF_ASYNC_ACTIVE; |
| if (!test_bit(UNLOADING, &vha->dpc_flags)) |
| qla2x00_post_async_prlo_done_work(sp->fcport->vha, sp->fcport, |
| lio->u.logio.data); |
| /* ref: INIT */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| } |
| |
| int |
| qla2x00_async_prlo(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| srb_t *sp; |
| int rval; |
| |
| rval = QLA_FUNCTION_FAILED; |
| /* ref: INIT */ |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| sp->type = SRB_PRLO_CMD; |
| sp->name = "prlo"; |
| qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2, |
| qla2x00_async_prlo_sp_done); |
| |
| ql_dbg(ql_dbg_disc, vha, 0x2070, |
| "Async-prlo - hdl=%x loop-id=%x portid=%02x%02x%02x.\n", |
| sp->handle, fcport->loop_id, fcport->d_id.b.domain, |
| fcport->d_id.b.area, fcport->d_id.b.al_pa); |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| |
| return rval; |
| |
| done_free_sp: |
| /* ref: INIT */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| done: |
| fcport->flags &= ~FCF_ASYNC_ACTIVE; |
| return rval; |
| } |
| |
| static |
| void qla24xx_handle_adisc_event(scsi_qla_host_t *vha, struct event_arg *ea) |
| { |
| struct fc_port *fcport = ea->fcport; |
| unsigned long flags; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20d2, |
| "%s %8phC DS %d LS %d rc %d login %d|%d rscn %d|%d lid %d\n", |
| __func__, fcport->port_name, fcport->disc_state, |
| fcport->fw_login_state, ea->rc, fcport->login_gen, ea->sp->gen2, |
| fcport->rscn_gen, ea->sp->gen1, fcport->loop_id); |
| |
| WARN_ONCE(!qla2xxx_is_valid_mbs(ea->data[0]), "mbs: %#x\n", |
| ea->data[0]); |
| |
| if (ea->data[0] != MBS_COMMAND_COMPLETE) { |
| ql_dbg(ql_dbg_disc, vha, 0x2066, |
| "%s %8phC: adisc fail: post delete\n", |
| __func__, ea->fcport->port_name); |
| |
| spin_lock_irqsave(&vha->work_lock, flags); |
| /* deleted = 0 & logout_on_delete = force fw cleanup */ |
| if (fcport->deleted == QLA_SESS_DELETED) |
| fcport->deleted = 0; |
| |
| fcport->logout_on_delete = 1; |
| spin_unlock_irqrestore(&vha->work_lock, flags); |
| |
| qlt_schedule_sess_for_deletion(ea->fcport); |
| return; |
| } |
| |
| if (ea->fcport->disc_state == DSC_DELETE_PEND) |
| return; |
| |
| if (ea->sp->gen2 != ea->fcport->login_gen) { |
| /* target side must have changed it. */ |
| ql_dbg(ql_dbg_disc, vha, 0x20d3, |
| "%s %8phC generation changed\n", |
| __func__, ea->fcport->port_name); |
| return; |
| } else if (ea->sp->gen1 != ea->fcport->rscn_gen) { |
| qla_rscn_replay(fcport); |
| qlt_schedule_sess_for_deletion(fcport); |
| return; |
| } |
| |
| __qla24xx_handle_gpdb_event(vha, ea); |
| } |
| |
| static int qla_post_els_plogi_work(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| struct qla_work_evt *e; |
| |
| e = qla2x00_alloc_work(vha, QLA_EVT_ELS_PLOGI); |
| if (!e) |
| return QLA_FUNCTION_FAILED; |
| |
| e->u.fcport.fcport = fcport; |
| fcport->flags |= FCF_ASYNC_ACTIVE; |
| qla2x00_set_fcport_disc_state(fcport, DSC_LOGIN_PEND); |
| return qla2x00_post_work(vha, e); |
| } |
| |
| static void qla2x00_async_adisc_sp_done(srb_t *sp, int res) |
| { |
| struct scsi_qla_host *vha = sp->vha; |
| struct event_arg ea; |
| struct srb_iocb *lio = &sp->u.iocb_cmd; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x2066, |
| "Async done-%s res %x %8phC\n", |
| sp->name, res, sp->fcport->port_name); |
| |
| sp->fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| |
| memset(&ea, 0, sizeof(ea)); |
| ea.rc = res; |
| ea.data[0] = lio->u.logio.data[0]; |
| ea.data[1] = lio->u.logio.data[1]; |
| ea.iop[0] = lio->u.logio.iop[0]; |
| ea.iop[1] = lio->u.logio.iop[1]; |
| ea.fcport = sp->fcport; |
| ea.sp = sp; |
| if (res) |
| ea.data[0] = MBS_COMMAND_ERROR; |
| |
| qla24xx_handle_adisc_event(vha, &ea); |
| /* ref: INIT */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| } |
| |
| int |
| qla2x00_async_adisc(struct scsi_qla_host *vha, fc_port_t *fcport, |
| uint16_t *data) |
| { |
| srb_t *sp; |
| struct srb_iocb *lio; |
| int rval = QLA_FUNCTION_FAILED; |
| |
| if (IS_SESSION_DELETED(fcport)) { |
| ql_log(ql_log_warn, vha, 0xffff, |
| "%s: %8phC is being delete - not sending command.\n", |
| __func__, fcport->port_name); |
| fcport->flags &= ~FCF_ASYNC_ACTIVE; |
| return rval; |
| } |
| |
| if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT)) |
| return rval; |
| |
| fcport->flags |= FCF_ASYNC_SENT; |
| /* ref: INIT */ |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| sp->type = SRB_ADISC_CMD; |
| sp->name = "adisc"; |
| sp->gen1 = fcport->rscn_gen; |
| sp->gen2 = fcport->login_gen; |
| qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2, |
| qla2x00_async_adisc_sp_done); |
| |
| if (data[1] & QLA_LOGIO_LOGIN_RETRIED) { |
| lio = &sp->u.iocb_cmd; |
| lio->u.logio.flags |= SRB_LOGIN_RETRIED; |
| } |
| |
| ql_dbg(ql_dbg_disc, vha, 0x206f, |
| "Async-adisc - hdl=%x loopid=%x portid=%06x %8phC.\n", |
| sp->handle, fcport->loop_id, fcport->d_id.b24, fcport->port_name); |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| |
| return rval; |
| |
| done_free_sp: |
| /* ref: INIT */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| done: |
| fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| qla2x00_post_async_adisc_work(vha, fcport, data); |
| return rval; |
| } |
| |
| static bool qla2x00_is_reserved_id(scsi_qla_host_t *vha, uint16_t loop_id) |
| { |
| struct qla_hw_data *ha = vha->hw; |
| |
| if (IS_FWI2_CAPABLE(ha)) |
| return loop_id > NPH_LAST_HANDLE; |
| |
| return (loop_id > ha->max_loop_id && loop_id < SNS_FIRST_LOOP_ID) || |
| loop_id == MANAGEMENT_SERVER || loop_id == BROADCAST; |
| } |
| |
| /** |
| * qla2x00_find_new_loop_id - scan through our port list and find a new usable loop ID |
| * @vha: adapter state pointer. |
| * @dev: port structure pointer. |
| * |
| * Returns: |
| * qla2x00 local function return status code. |
| * |
| * Context: |
| * Kernel context. |
| */ |
| static int qla2x00_find_new_loop_id(scsi_qla_host_t *vha, fc_port_t *dev) |
| { |
| int rval; |
| struct qla_hw_data *ha = vha->hw; |
| unsigned long flags = 0; |
| |
| rval = QLA_SUCCESS; |
| |
| spin_lock_irqsave(&ha->vport_slock, flags); |
| |
| dev->loop_id = find_first_zero_bit(ha->loop_id_map, LOOPID_MAP_SIZE); |
| if (dev->loop_id >= LOOPID_MAP_SIZE || |
| qla2x00_is_reserved_id(vha, dev->loop_id)) { |
| dev->loop_id = FC_NO_LOOP_ID; |
| rval = QLA_FUNCTION_FAILED; |
| } else { |
| set_bit(dev->loop_id, ha->loop_id_map); |
| } |
| spin_unlock_irqrestore(&ha->vport_slock, flags); |
| |
| if (rval == QLA_SUCCESS) |
| ql_dbg(ql_dbg_disc, dev->vha, 0x2086, |
| "Assigning new loopid=%x, portid=%x.\n", |
| dev->loop_id, dev->d_id.b24); |
| else |
| ql_log(ql_log_warn, dev->vha, 0x2087, |
| "No loop_id's available, portid=%x.\n", |
| dev->d_id.b24); |
| |
| return rval; |
| } |
| |
| void qla2x00_clear_loop_id(fc_port_t *fcport) |
| { |
| struct qla_hw_data *ha = fcport->vha->hw; |
| |
| if (fcport->loop_id == FC_NO_LOOP_ID || |
| qla2x00_is_reserved_id(fcport->vha, fcport->loop_id)) |
| return; |
| |
| clear_bit(fcport->loop_id, ha->loop_id_map); |
| fcport->loop_id = FC_NO_LOOP_ID; |
| } |
| |
| static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha, |
| struct event_arg *ea) |
| { |
| fc_port_t *fcport, *conflict_fcport; |
| struct get_name_list_extended *e; |
| u16 i, n, found = 0, loop_id; |
| port_id_t id; |
| u64 wwn; |
| u16 data[2]; |
| u8 current_login_state, nvme_cls; |
| |
| fcport = ea->fcport; |
| ql_dbg(ql_dbg_disc, vha, 0xffff, |
| "%s %8phC DS %d LS rc %d %d login %d|%d rscn %d|%d lid %d edif %d\n", |
| __func__, fcport->port_name, fcport->disc_state, |
| fcport->fw_login_state, ea->rc, |
| fcport->login_gen, fcport->last_login_gen, |
| fcport->rscn_gen, fcport->last_rscn_gen, vha->loop_id, fcport->edif.enable); |
| |
| if (fcport->disc_state == DSC_DELETE_PEND) |
| return; |
| |
| if (ea->rc) { /* rval */ |
| if (fcport->login_retry == 0) { |
| ql_dbg(ql_dbg_disc, vha, 0x20de, |
| "GNL failed Port login retry %8phN, retry cnt=%d.\n", |
| fcport->port_name, fcport->login_retry); |
| } |
| return; |
| } |
| |
| if (fcport->last_rscn_gen != fcport->rscn_gen) { |
| qla_rscn_replay(fcport); |
| qlt_schedule_sess_for_deletion(fcport); |
| return; |
| } else if (fcport->last_login_gen != fcport->login_gen) { |
| ql_dbg(ql_dbg_disc, vha, 0x20e0, |
| "%s %8phC login gen changed\n", |
| __func__, fcport->port_name); |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| return; |
| } |
| |
| n = ea->data[0] / sizeof(struct get_name_list_extended); |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20e1, |
| "%s %d %8phC n %d %02x%02x%02x lid %d \n", |
| __func__, __LINE__, fcport->port_name, n, |
| fcport->d_id.b.domain, fcport->d_id.b.area, |
| fcport->d_id.b.al_pa, fcport->loop_id); |
| |
| for (i = 0; i < n; i++) { |
| e = &vha->gnl.l[i]; |
| wwn = wwn_to_u64(e->port_name); |
| id.b.domain = e->port_id[2]; |
| id.b.area = e->port_id[1]; |
| id.b.al_pa = e->port_id[0]; |
| id.b.rsvd_1 = 0; |
| |
| if (memcmp((u8 *)&wwn, fcport->port_name, WWN_SIZE)) |
| continue; |
| |
| if (IS_SW_RESV_ADDR(id)) |
| continue; |
| |
| found = 1; |
| |
| loop_id = le16_to_cpu(e->nport_handle); |
| loop_id = (loop_id & 0x7fff); |
| nvme_cls = e->current_login_state >> 4; |
| current_login_state = e->current_login_state & 0xf; |
| |
| if (PRLI_PHASE(nvme_cls)) { |
| current_login_state = nvme_cls; |
| fcport->fc4_type &= ~FS_FC4TYPE_FCP; |
| fcport->fc4_type |= FS_FC4TYPE_NVME; |
| } else if (PRLI_PHASE(current_login_state)) { |
| fcport->fc4_type |= FS_FC4TYPE_FCP; |
| fcport->fc4_type &= ~FS_FC4TYPE_NVME; |
| } |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20e2, |
| "%s found %8phC CLS [%x|%x] fc4_type %d ID[%06x|%06x] lid[%d|%d]\n", |
| __func__, fcport->port_name, |
| e->current_login_state, fcport->fw_login_state, |
| fcport->fc4_type, id.b24, fcport->d_id.b24, |
| loop_id, fcport->loop_id); |
| |
| switch (fcport->disc_state) { |
| case DSC_DELETE_PEND: |
| case DSC_DELETED: |
| break; |
| default: |
| if ((id.b24 != fcport->d_id.b24 && |
| fcport->d_id.b24 && |
| fcport->loop_id != FC_NO_LOOP_ID) || |
| (fcport->loop_id != FC_NO_LOOP_ID && |
| fcport->loop_id != loop_id)) { |
| ql_dbg(ql_dbg_disc, vha, 0x20e3, |
| "%s %d %8phC post del sess\n", |
| __func__, __LINE__, fcport->port_name); |
| if (fcport->n2n_flag) |
| fcport->d_id.b24 = 0; |
| qlt_schedule_sess_for_deletion(fcport); |
| return; |
| } |
| break; |
| } |
| |
| fcport->loop_id = loop_id; |
| if (fcport->n2n_flag) |
| fcport->d_id.b24 = id.b24; |
| |
| wwn = wwn_to_u64(fcport->port_name); |
| qlt_find_sess_invalidate_other(vha, wwn, |
| id, loop_id, &conflict_fcport); |
| |
| if (conflict_fcport) { |
| /* |
| * Another share fcport share the same loop_id & |
| * nport id. Conflict fcport needs to finish |
| * cleanup before this fcport can proceed to login. |
| */ |
| conflict_fcport->conflict = fcport; |
| fcport->login_pause = 1; |
| } |
| |
| switch (vha->hw->current_topology) { |
| default: |
| switch (current_login_state) { |
| case DSC_LS_PRLI_COMP: |
| ql_dbg(ql_dbg_disc, |
| vha, 0x20e4, "%s %d %8phC post gpdb\n", |
| __func__, __LINE__, fcport->port_name); |
| |
| if ((e->prli_svc_param_word_3[0] & BIT_4) == 0) |
| fcport->port_type = FCT_INITIATOR; |
| else |
| fcport->port_type = FCT_TARGET; |
| data[0] = data[1] = 0; |
| qla2x00_post_async_adisc_work(vha, fcport, |
| data); |
| break; |
| case DSC_LS_PLOGI_COMP: |
| if (vha->hw->flags.edif_enabled) { |
| /* check to see if App support Secure */ |
| qla24xx_post_gpdb_work(vha, fcport, 0); |
| break; |
| } |
| fallthrough; |
| case DSC_LS_PORT_UNAVAIL: |
| default: |
| if (fcport->loop_id == FC_NO_LOOP_ID) { |
| qla2x00_find_new_loop_id(vha, fcport); |
| fcport->fw_login_state = |
| DSC_LS_PORT_UNAVAIL; |
| } |
| ql_dbg(ql_dbg_disc, vha, 0x20e5, |
| "%s %d %8phC\n", __func__, __LINE__, |
| fcport->port_name); |
| qla24xx_fcport_handle_login(vha, fcport); |
| break; |
| } |
| break; |
| case ISP_CFG_N: |
| fcport->fw_login_state = current_login_state; |
| fcport->d_id = id; |
| switch (current_login_state) { |
| case DSC_LS_PRLI_PEND: |
| /* |
| * In the middle of PRLI. Let it finish. |
| * Allow relogin code to recheck state again |
| * with GNL. Push disc_state back to DELETED |
| * so GNL can go out again |
| */ |
| qla2x00_set_fcport_disc_state(fcport, |
| DSC_DELETED); |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| break; |
| case DSC_LS_PRLI_COMP: |
| if ((e->prli_svc_param_word_3[0] & BIT_4) == 0) |
| fcport->port_type = FCT_INITIATOR; |
| else |
| fcport->port_type = FCT_TARGET; |
| |
| data[0] = data[1] = 0; |
| qla2x00_post_async_adisc_work(vha, fcport, |
| data); |
| break; |
| case DSC_LS_PLOGI_COMP: |
| if (vha->hw->flags.edif_enabled && |
| DBELL_ACTIVE(vha)) { |
| /* check to see if App support secure or not */ |
| qla24xx_post_gpdb_work(vha, fcport, 0); |
| break; |
| } |
| if (fcport_is_bigger(fcport)) { |
| /* local adapter is smaller */ |
| if (fcport->loop_id != FC_NO_LOOP_ID) |
| qla2x00_clear_loop_id(fcport); |
| |
| fcport->loop_id = loop_id; |
| qla24xx_fcport_handle_login(vha, |
| fcport); |
| break; |
| } |
| fallthrough; |
| default: |
| if (fcport_is_smaller(fcport)) { |
| /* local adapter is bigger */ |
| if (fcport->loop_id != FC_NO_LOOP_ID) |
| qla2x00_clear_loop_id(fcport); |
| |
| fcport->loop_id = loop_id; |
| qla24xx_fcport_handle_login(vha, |
| fcport); |
| } |
| break; |
| } |
| break; |
| } /* switch (ha->current_topology) */ |
| } |
| |
| if (!found) { |
| switch (vha->hw->current_topology) { |
| case ISP_CFG_F: |
| case ISP_CFG_FL: |
| for (i = 0; i < n; i++) { |
| e = &vha->gnl.l[i]; |
| id.b.domain = e->port_id[0]; |
| id.b.area = e->port_id[1]; |
| id.b.al_pa = e->port_id[2]; |
| id.b.rsvd_1 = 0; |
| loop_id = le16_to_cpu(e->nport_handle); |
| |
| if (fcport->d_id.b24 == id.b24) { |
| conflict_fcport = |
| qla2x00_find_fcport_by_wwpn(vha, |
| e->port_name, 0); |
| if (conflict_fcport) { |
| ql_dbg(ql_dbg_disc + ql_dbg_verbose, |
| vha, 0x20e5, |
| "%s %d %8phC post del sess\n", |
| __func__, __LINE__, |
| conflict_fcport->port_name); |
| qlt_schedule_sess_for_deletion |
| (conflict_fcport); |
| } |
| } |
| /* |
| * FW already picked this loop id for |
| * another fcport |
| */ |
| if (fcport->loop_id == loop_id) |
| fcport->loop_id = FC_NO_LOOP_ID; |
| } |
| qla24xx_fcport_handle_login(vha, fcport); |
| break; |
| case ISP_CFG_N: |
| qla2x00_set_fcport_disc_state(fcport, DSC_DELETED); |
| if (time_after_eq(jiffies, fcport->dm_login_expire)) { |
| if (fcport->n2n_link_reset_cnt < 2) { |
| fcport->n2n_link_reset_cnt++; |
| /* |
| * remote port is not sending PLOGI. |
| * Reset link to kick start his state |
| * machine |
| */ |
| set_bit(N2N_LINK_RESET, |
| &vha->dpc_flags); |
| } else { |
| if (fcport->n2n_chip_reset < 1) { |
| ql_log(ql_log_info, vha, 0x705d, |
| "Chip reset to bring laser down"); |
| set_bit(ISP_ABORT_NEEDED, |
| &vha->dpc_flags); |
| fcport->n2n_chip_reset++; |
| } else { |
| ql_log(ql_log_info, vha, 0x705d, |
| "Remote port %8ph is not coming back\n", |
| fcport->port_name); |
| fcport->scan_state = 0; |
| } |
| } |
| qla2xxx_wake_dpc(vha); |
| } else { |
| /* |
| * report port suppose to do PLOGI. Give him |
| * more time. FW will catch it. |
| */ |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| } |
| break; |
| case ISP_CFG_NL: |
| qla24xx_fcport_handle_login(vha, fcport); |
| break; |
| default: |
| break; |
| } |
| } |
| } /* gnl_event */ |
| |
| static void qla24xx_async_gnl_sp_done(srb_t *sp, int res) |
| { |
| struct scsi_qla_host *vha = sp->vha; |
| unsigned long flags; |
| struct fc_port *fcport = NULL, *tf; |
| u16 i, n = 0, loop_id; |
| struct event_arg ea; |
| struct get_name_list_extended *e; |
| u64 wwn; |
| struct list_head h; |
| bool found = false; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20e7, |
| "Async done-%s res %x mb[1]=%x mb[2]=%x \n", |
| sp->name, res, sp->u.iocb_cmd.u.mbx.in_mb[1], |
| sp->u.iocb_cmd.u.mbx.in_mb[2]); |
| |
| |
| sp->fcport->flags &= ~(FCF_ASYNC_SENT|FCF_ASYNC_ACTIVE); |
| memset(&ea, 0, sizeof(ea)); |
| ea.sp = sp; |
| ea.rc = res; |
| |
| if (sp->u.iocb_cmd.u.mbx.in_mb[1] >= |
| sizeof(struct get_name_list_extended)) { |
| n = sp->u.iocb_cmd.u.mbx.in_mb[1] / |
| sizeof(struct get_name_list_extended); |
| ea.data[0] = sp->u.iocb_cmd.u.mbx.in_mb[1]; /* amnt xfered */ |
| } |
| |
| for (i = 0; i < n; i++) { |
| e = &vha->gnl.l[i]; |
| loop_id = le16_to_cpu(e->nport_handle); |
| /* mask out reserve bit */ |
| loop_id = (loop_id & 0x7fff); |
| set_bit(loop_id, vha->hw->loop_id_map); |
| wwn = wwn_to_u64(e->port_name); |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20e8, |
| "%s %8phC %02x:%02x:%02x CLS %x/%x lid %x \n", |
| __func__, &wwn, e->port_id[2], e->port_id[1], |
| e->port_id[0], e->current_login_state, e->last_login_state, |
| (loop_id & 0x7fff)); |
| } |
| |
| spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); |
| |
| INIT_LIST_HEAD(&h); |
| fcport = tf = NULL; |
| if (!list_empty(&vha->gnl.fcports)) |
| list_splice_init(&vha->gnl.fcports, &h); |
| spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); |
| |
| list_for_each_entry_safe(fcport, tf, &h, gnl_entry) { |
| spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); |
| list_del_init(&fcport->gnl_entry); |
| fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); |
| ea.fcport = fcport; |
| |
| qla24xx_handle_gnl_done_event(vha, &ea); |
| } |
| |
| /* create new fcport if fw has knowledge of new sessions */ |
| for (i = 0; i < n; i++) { |
| port_id_t id; |
| u64 wwnn; |
| |
| e = &vha->gnl.l[i]; |
| wwn = wwn_to_u64(e->port_name); |
| |
| found = false; |
| list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) { |
| if (!memcmp((u8 *)&wwn, fcport->port_name, |
| WWN_SIZE)) { |
| found = true; |
| break; |
| } |
| } |
| |
| id.b.domain = e->port_id[2]; |
| id.b.area = e->port_id[1]; |
| id.b.al_pa = e->port_id[0]; |
| id.b.rsvd_1 = 0; |
| |
| if (!found && wwn && !IS_SW_RESV_ADDR(id)) { |
| ql_dbg(ql_dbg_disc, vha, 0x2065, |
| "%s %d %8phC %06x post new sess\n", |
| __func__, __LINE__, (u8 *)&wwn, id.b24); |
| wwnn = wwn_to_u64(e->node_name); |
| qla24xx_post_newsess_work(vha, &id, (u8 *)&wwn, |
| (u8 *)&wwnn, NULL, 0); |
| } |
| } |
| |
| spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); |
| vha->gnl.sent = 0; |
| if (!list_empty(&vha->gnl.fcports)) { |
| /* retrigger gnl */ |
| list_for_each_entry_safe(fcport, tf, &vha->gnl.fcports, |
| gnl_entry) { |
| list_del_init(&fcport->gnl_entry); |
| fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| if (qla24xx_post_gnl_work(vha, fcport) == QLA_SUCCESS) |
| break; |
| } |
| } |
| spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); |
| |
| /* ref: INIT */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| } |
| |
| int qla24xx_async_gnl(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| srb_t *sp; |
| int rval = QLA_FUNCTION_FAILED; |
| unsigned long flags; |
| u16 *mb; |
| |
| if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT)) |
| goto done; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20d9, |
| "Async-gnlist WWPN %8phC \n", fcport->port_name); |
| |
| spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); |
| fcport->flags |= FCF_ASYNC_SENT; |
| qla2x00_set_fcport_disc_state(fcport, DSC_GNL); |
| fcport->last_rscn_gen = fcport->rscn_gen; |
| fcport->last_login_gen = fcport->login_gen; |
| |
| list_add_tail(&fcport->gnl_entry, &vha->gnl.fcports); |
| if (vha->gnl.sent) { |
| spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); |
| return QLA_SUCCESS; |
| } |
| vha->gnl.sent = 1; |
| spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); |
| |
| /* ref: INIT */ |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| sp->type = SRB_MB_IOCB; |
| sp->name = "gnlist"; |
| sp->gen1 = fcport->rscn_gen; |
| sp->gen2 = fcport->login_gen; |
| qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2, |
| qla24xx_async_gnl_sp_done); |
| |
| mb = sp->u.iocb_cmd.u.mbx.out_mb; |
| mb[0] = MBC_PORT_NODE_NAME_LIST; |
| mb[1] = BIT_2 | BIT_3; |
| mb[2] = MSW(vha->gnl.ldma); |
| mb[3] = LSW(vha->gnl.ldma); |
| mb[6] = MSW(MSD(vha->gnl.ldma)); |
| mb[7] = LSW(MSD(vha->gnl.ldma)); |
| mb[8] = vha->gnl.size; |
| mb[9] = vha->vp_idx; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20da, |
| "Async-%s - OUT WWPN %8phC hndl %x\n", |
| sp->name, fcport->port_name, sp->handle); |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| |
| return rval; |
| |
| done_free_sp: |
| /* ref: INIT */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| fcport->flags &= ~(FCF_ASYNC_SENT); |
| done: |
| fcport->flags &= ~(FCF_ASYNC_ACTIVE); |
| return rval; |
| } |
| |
| int qla24xx_post_gnl_work(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| struct qla_work_evt *e; |
| |
| e = qla2x00_alloc_work(vha, QLA_EVT_GNL); |
| if (!e) |
| return QLA_FUNCTION_FAILED; |
| |
| e->u.fcport.fcport = fcport; |
| fcport->flags |= FCF_ASYNC_ACTIVE; |
| return qla2x00_post_work(vha, e); |
| } |
| |
| static void qla24xx_async_gpdb_sp_done(srb_t *sp, int res) |
| { |
| struct scsi_qla_host *vha = sp->vha; |
| struct qla_hw_data *ha = vha->hw; |
| fc_port_t *fcport = sp->fcport; |
| u16 *mb = sp->u.iocb_cmd.u.mbx.in_mb; |
| struct event_arg ea; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20db, |
| "Async done-%s res %x, WWPN %8phC mb[1]=%x mb[2]=%x \n", |
| sp->name, res, fcport->port_name, mb[1], mb[2]); |
| |
| fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| |
| if (res == QLA_FUNCTION_TIMEOUT) |
| goto done; |
| |
| memset(&ea, 0, sizeof(ea)); |
| ea.fcport = fcport; |
| ea.sp = sp; |
| |
| qla24xx_handle_gpdb_event(vha, &ea); |
| |
| done: |
| dma_pool_free(ha->s_dma_pool, sp->u.iocb_cmd.u.mbx.in, |
| sp->u.iocb_cmd.u.mbx.in_dma); |
| |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| } |
| |
| int qla24xx_post_prli_work(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| struct qla_work_evt *e; |
| |
| if (vha->host->active_mode == MODE_TARGET) |
| return QLA_FUNCTION_FAILED; |
| |
| e = qla2x00_alloc_work(vha, QLA_EVT_PRLI); |
| if (!e) |
| return QLA_FUNCTION_FAILED; |
| |
| e->u.fcport.fcport = fcport; |
| |
| return qla2x00_post_work(vha, e); |
| } |
| |
| static void qla2x00_async_prli_sp_done(srb_t *sp, int res) |
| { |
| struct scsi_qla_host *vha = sp->vha; |
| struct srb_iocb *lio = &sp->u.iocb_cmd; |
| struct event_arg ea; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x2129, |
| "%s %8phC res %x\n", __func__, |
| sp->fcport->port_name, res); |
| |
| sp->fcport->flags &= ~FCF_ASYNC_SENT; |
| |
| if (!test_bit(UNLOADING, &vha->dpc_flags)) { |
| memset(&ea, 0, sizeof(ea)); |
| ea.fcport = sp->fcport; |
| ea.data[0] = lio->u.logio.data[0]; |
| ea.data[1] = lio->u.logio.data[1]; |
| ea.iop[0] = lio->u.logio.iop[0]; |
| ea.iop[1] = lio->u.logio.iop[1]; |
| ea.sp = sp; |
| if (res == QLA_OS_TIMER_EXPIRED) |
| ea.data[0] = QLA_OS_TIMER_EXPIRED; |
| else if (res) |
| ea.data[0] = MBS_COMMAND_ERROR; |
| |
| qla24xx_handle_prli_done_event(vha, &ea); |
| } |
| |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| } |
| |
| int |
| qla24xx_async_prli(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| srb_t *sp; |
| struct srb_iocb *lio; |
| int rval = QLA_FUNCTION_FAILED; |
| |
| if (!vha->flags.online) { |
| ql_dbg(ql_dbg_disc, vha, 0xffff, "%s %d %8phC exit\n", |
| __func__, __LINE__, fcport->port_name); |
| return rval; |
| } |
| |
| if ((fcport->fw_login_state == DSC_LS_PLOGI_PEND || |
| fcport->fw_login_state == DSC_LS_PRLI_PEND) && |
| qla_dual_mode_enabled(vha)) { |
| ql_dbg(ql_dbg_disc, vha, 0xffff, "%s %d %8phC exit\n", |
| __func__, __LINE__, fcport->port_name); |
| return rval; |
| } |
| |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| return rval; |
| |
| fcport->flags |= FCF_ASYNC_SENT; |
| fcport->logout_completed = 0; |
| |
| sp->type = SRB_PRLI_CMD; |
| sp->name = "prli"; |
| qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2, |
| qla2x00_async_prli_sp_done); |
| |
| lio = &sp->u.iocb_cmd; |
| lio->u.logio.flags = 0; |
| |
| if (NVME_TARGET(vha->hw, fcport)) |
| lio->u.logio.flags |= SRB_LOGIN_NVME_PRLI; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x211b, |
| "Async-prli - %8phC hdl=%x, loopid=%x portid=%06x retries=%d fc4type %x priority %x %s.\n", |
| fcport->port_name, sp->handle, fcport->loop_id, fcport->d_id.b24, |
| fcport->login_retry, fcport->fc4_type, vha->hw->fc4_type_priority, |
| NVME_TARGET(vha->hw, fcport) ? "nvme" : "fcp"); |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) { |
| fcport->flags |= FCF_LOGIN_NEEDED; |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| goto done_free_sp; |
| } |
| |
| return rval; |
| |
| done_free_sp: |
| /* ref: INIT */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| return rval; |
| } |
| |
| int qla24xx_post_gpdb_work(struct scsi_qla_host *vha, fc_port_t *fcport, u8 opt) |
| { |
| struct qla_work_evt *e; |
| |
| e = qla2x00_alloc_work(vha, QLA_EVT_GPDB); |
| if (!e) |
| return QLA_FUNCTION_FAILED; |
| |
| e->u.fcport.fcport = fcport; |
| e->u.fcport.opt = opt; |
| fcport->flags |= FCF_ASYNC_ACTIVE; |
| return qla2x00_post_work(vha, e); |
| } |
| |
| int qla24xx_async_gpdb(struct scsi_qla_host *vha, fc_port_t *fcport, u8 opt) |
| { |
| srb_t *sp; |
| struct srb_iocb *mbx; |
| int rval = QLA_FUNCTION_FAILED; |
| u16 *mb; |
| dma_addr_t pd_dma; |
| struct port_database_24xx *pd; |
| struct qla_hw_data *ha = vha->hw; |
| |
| if (IS_SESSION_DELETED(fcport)) { |
| ql_log(ql_log_warn, vha, 0xffff, |
| "%s: %8phC is being delete - not sending command.\n", |
| __func__, fcport->port_name); |
| fcport->flags &= ~FCF_ASYNC_ACTIVE; |
| return rval; |
| } |
| |
| if (!vha->flags.online || fcport->flags & FCF_ASYNC_SENT) { |
| ql_log(ql_log_warn, vha, 0xffff, |
| "%s: %8phC online %d flags %x - not sending command.\n", |
| __func__, fcport->port_name, vha->flags.online, fcport->flags); |
| goto done; |
| } |
| |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| qla2x00_set_fcport_disc_state(fcport, DSC_GPDB); |
| |
| fcport->flags |= FCF_ASYNC_SENT; |
| sp->type = SRB_MB_IOCB; |
| sp->name = "gpdb"; |
| sp->gen1 = fcport->rscn_gen; |
| sp->gen2 = fcport->login_gen; |
| qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2, |
| qla24xx_async_gpdb_sp_done); |
| |
| pd = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &pd_dma); |
| if (pd == NULL) { |
| ql_log(ql_log_warn, vha, 0xd043, |
| "Failed to allocate port database structure.\n"); |
| goto done_free_sp; |
| } |
| |
| mb = sp->u.iocb_cmd.u.mbx.out_mb; |
| mb[0] = MBC_GET_PORT_DATABASE; |
| mb[1] = fcport->loop_id; |
| mb[2] = MSW(pd_dma); |
| mb[3] = LSW(pd_dma); |
| mb[6] = MSW(MSD(pd_dma)); |
| mb[7] = LSW(MSD(pd_dma)); |
| mb[9] = vha->vp_idx; |
| mb[10] = opt; |
| |
| mbx = &sp->u.iocb_cmd; |
| mbx->u.mbx.in = (void *)pd; |
| mbx->u.mbx.in_dma = pd_dma; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20dc, |
| "Async-%s %8phC hndl %x opt %x\n", |
| sp->name, fcport->port_name, sp->handle, opt); |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| return rval; |
| |
| done_free_sp: |
| if (pd) |
| dma_pool_free(ha->s_dma_pool, pd, pd_dma); |
| |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| done: |
| fcport->flags &= ~FCF_ASYNC_ACTIVE; |
| qla24xx_post_gpdb_work(vha, fcport, opt); |
| return rval; |
| } |
| |
| static |
| void __qla24xx_handle_gpdb_event(scsi_qla_host_t *vha, struct event_arg *ea) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); |
| ea->fcport->login_gen++; |
| ea->fcport->logout_on_delete = 1; |
| |
| if (!ea->fcport->login_succ && !IS_SW_RESV_ADDR(ea->fcport->d_id)) { |
| vha->fcport_count++; |
| ea->fcport->login_succ = 1; |
| |
| spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); |
| qla24xx_sched_upd_fcport(ea->fcport); |
| spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); |
| } else if (ea->fcport->login_succ) { |
| /* |
| * We have an existing session. A late RSCN delivery |
| * must have triggered the session to be re-validate. |
| * Session is still valid. |
| */ |
| ql_dbg(ql_dbg_disc, vha, 0x20d6, |
| "%s %d %8phC session revalidate success\n", |
| __func__, __LINE__, ea->fcport->port_name); |
| qla2x00_set_fcport_disc_state(ea->fcport, DSC_LOGIN_COMPLETE); |
| } |
| spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); |
| } |
| |
| static int qla_chk_secure_login(scsi_qla_host_t *vha, fc_port_t *fcport, |
| struct port_database_24xx *pd) |
| { |
| int rc = 0; |
| |
| if (pd->secure_login) { |
| ql_dbg(ql_dbg_disc, vha, 0x104d, |
| "Secure Login established on %8phC\n", |
| fcport->port_name); |
| fcport->flags |= FCF_FCSP_DEVICE; |
| } else { |
| ql_dbg(ql_dbg_disc, vha, 0x104d, |
| "non-Secure Login %8phC", |
| fcport->port_name); |
| fcport->flags &= ~FCF_FCSP_DEVICE; |
| } |
| if (vha->hw->flags.edif_enabled) { |
| if (fcport->flags & FCF_FCSP_DEVICE) { |
| qla2x00_set_fcport_disc_state(fcport, DSC_LOGIN_AUTH_PEND); |
| /* Start edif prli timer & ring doorbell for app */ |
| fcport->edif.rx_sa_set = 0; |
| fcport->edif.tx_sa_set = 0; |
| fcport->edif.rx_sa_pending = 0; |
| fcport->edif.tx_sa_pending = 0; |
| |
| qla2x00_post_aen_work(vha, FCH_EVT_PORT_ONLINE, |
| fcport->d_id.b24); |
| |
| if (DBELL_ACTIVE(vha)) { |
| ql_dbg(ql_dbg_disc, vha, 0x20ef, |
| "%s %d %8phC EDIF: post DB_AUTH: AUTH needed\n", |
| __func__, __LINE__, fcport->port_name); |
| fcport->edif.app_sess_online = 1; |
| |
| qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_NEEDED, |
| fcport->d_id.b24, 0, fcport); |
| } |
| |
| rc = 1; |
| } else if (qla_ini_mode_enabled(vha) || qla_dual_mode_enabled(vha)) { |
| ql_dbg(ql_dbg_disc, vha, 0x2117, |
| "%s %d %8phC post prli\n", |
| __func__, __LINE__, fcport->port_name); |
| qla24xx_post_prli_work(vha, fcport); |
| rc = 1; |
| } |
| } |
| return rc; |
| } |
| |
| static |
| void qla24xx_handle_gpdb_event(scsi_qla_host_t *vha, struct event_arg *ea) |
| { |
| fc_port_t *fcport = ea->fcport; |
| struct port_database_24xx *pd; |
| struct srb *sp = ea->sp; |
| uint8_t ls; |
| |
| pd = (struct port_database_24xx *)sp->u.iocb_cmd.u.mbx.in; |
| |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20d2, |
| "%s %8phC DS %d LS %x fc4_type %x rc %x\n", __func__, |
| fcport->port_name, fcport->disc_state, pd->current_login_state, |
| fcport->fc4_type, ea->rc); |
| |
| if (fcport->disc_state == DSC_DELETE_PEND) { |
| ql_dbg(ql_dbg_disc, vha, 0x20d5, "%s %d %8phC\n", |
| __func__, __LINE__, fcport->port_name); |
| return; |
| } |
| |
| if (NVME_TARGET(vha->hw, fcport)) |
| ls = pd->current_login_state >> 4; |
| else |
| ls = pd->current_login_state & 0xf; |
| |
| if (ea->sp->gen2 != fcport->login_gen) { |
| /* target side must have changed it. */ |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20d3, |
| "%s %8phC generation changed\n", |
| __func__, fcport->port_name); |
| return; |
| } else if (ea->sp->gen1 != fcport->rscn_gen) { |
| qla_rscn_replay(fcport); |
| qlt_schedule_sess_for_deletion(fcport); |
| ql_dbg(ql_dbg_disc, vha, 0x20d5, "%s %d %8phC, ls %x\n", |
| __func__, __LINE__, fcport->port_name, ls); |
| return; |
| } |
| |
| switch (ls) { |
| case PDS_PRLI_COMPLETE: |
| __qla24xx_parse_gpdb(vha, fcport, pd); |
| break; |
| case PDS_PLOGI_COMPLETE: |
| if (qla_chk_secure_login(vha, fcport, pd)) { |
| ql_dbg(ql_dbg_disc, vha, 0x20d5, "%s %d %8phC, ls %x\n", |
| __func__, __LINE__, fcport->port_name, ls); |
| return; |
| } |
| fallthrough; |
| case PDS_PLOGI_PENDING: |
| case PDS_PRLI_PENDING: |
| case PDS_PRLI2_PENDING: |
| /* Set discovery state back to GNL to Relogin attempt */ |
| if (qla_dual_mode_enabled(vha) || |
| qla_ini_mode_enabled(vha)) { |
| qla2x00_set_fcport_disc_state(fcport, DSC_GNL); |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| } |
| ql_dbg(ql_dbg_disc, vha, 0x20d5, "%s %d %8phC, ls %x\n", |
| __func__, __LINE__, fcport->port_name, ls); |
| return; |
| case PDS_LOGO_PENDING: |
| case PDS_PORT_UNAVAILABLE: |
| default: |
| ql_dbg(ql_dbg_disc, vha, 0x20d5, "%s %d %8phC post del sess\n", |
| __func__, __LINE__, fcport->port_name); |
| qlt_schedule_sess_for_deletion(fcport); |
| return; |
| } |
| __qla24xx_handle_gpdb_event(vha, ea); |
| } /* gpdb event */ |
| |
| static void qla_chk_n2n_b4_login(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| u8 login = 0; |
| int rc; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x307b, |
| "%s %8phC DS %d LS %d lid %d retries=%d\n", |
| __func__, fcport->port_name, fcport->disc_state, |
| fcport->fw_login_state, fcport->loop_id, fcport->login_retry); |
| |
| if (qla_tgt_mode_enabled(vha)) |
| return; |
| |
| if (qla_dual_mode_enabled(vha)) { |
| if (N2N_TOPO(vha->hw)) { |
| u64 mywwn, wwn; |
| |
| mywwn = wwn_to_u64(vha->port_name); |
| wwn = wwn_to_u64(fcport->port_name); |
| if (mywwn > wwn) |
| login = 1; |
| else if ((fcport->fw_login_state == DSC_LS_PLOGI_COMP) |
| && time_after_eq(jiffies, |
| fcport->plogi_nack_done_deadline)) |
| login = 1; |
| } else { |
| login = 1; |
| } |
| } else { |
| /* initiator mode */ |
| login = 1; |
| } |
| |
| if (login && fcport->login_retry) { |
| fcport->login_retry--; |
| if (fcport->loop_id == FC_NO_LOOP_ID) { |
| fcport->fw_login_state = DSC_LS_PORT_UNAVAIL; |
| rc = qla2x00_find_new_loop_id(vha, fcport); |
| if (rc) { |
| ql_dbg(ql_dbg_disc, vha, 0x20e6, |
| "%s %d %8phC post del sess - out of loopid\n", |
| __func__, __LINE__, fcport->port_name); |
| fcport->scan_state = 0; |
| qlt_schedule_sess_for_deletion(fcport); |
| return; |
| } |
| } |
| ql_dbg(ql_dbg_disc, vha, 0x20bf, |
| "%s %d %8phC post login\n", |
| __func__, __LINE__, fcport->port_name); |
| qla2x00_post_async_login_work(vha, fcport, NULL); |
| } |
| } |
| |
| int qla24xx_fcport_handle_login(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| u16 data[2]; |
| u16 sec; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20d8, |
| "%s %8phC DS %d LS %d P %d fl %x confl %p rscn %d|%d login %d lid %d scan %d fc4type %x\n", |
| __func__, fcport->port_name, fcport->disc_state, |
| fcport->fw_login_state, fcport->login_pause, fcport->flags, |
| fcport->conflict, fcport->last_rscn_gen, fcport->rscn_gen, |
| fcport->login_gen, fcport->loop_id, fcport->scan_state, |
| fcport->fc4_type); |
| |
| if (fcport->scan_state != QLA_FCPORT_FOUND || |
| fcport->disc_state == DSC_DELETE_PEND) |
| return 0; |
| |
| if ((fcport->loop_id != FC_NO_LOOP_ID) && |
| qla_dual_mode_enabled(vha) && |
| ((fcport->fw_login_state == DSC_LS_PLOGI_PEND) || |
| (fcport->fw_login_state == DSC_LS_PRLI_PEND))) |
| return 0; |
| |
| if (fcport->fw_login_state == DSC_LS_PLOGI_COMP && |
| !N2N_TOPO(vha->hw)) { |
| if (time_before_eq(jiffies, fcport->plogi_nack_done_deadline)) { |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| return 0; |
| } |
| } |
| |
| /* Target won't initiate port login if fabric is present */ |
| if (vha->host->active_mode == MODE_TARGET && !N2N_TOPO(vha->hw)) |
| return 0; |
| |
| if (fcport->flags & (FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE)) { |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| return 0; |
| } |
| |
| switch (fcport->disc_state) { |
| case DSC_DELETED: |
| switch (vha->hw->current_topology) { |
| case ISP_CFG_N: |
| if (fcport_is_smaller(fcport)) { |
| /* this adapter is bigger */ |
| if (fcport->login_retry) { |
| if (fcport->loop_id == FC_NO_LOOP_ID) { |
| qla2x00_find_new_loop_id(vha, |
| fcport); |
| fcport->fw_login_state = |
| DSC_LS_PORT_UNAVAIL; |
| } |
| fcport->login_retry--; |
| qla_post_els_plogi_work(vha, fcport); |
| } else { |
| ql_log(ql_log_info, vha, 0x705d, |
| "Unable to reach remote port %8phC", |
| fcport->port_name); |
| } |
| } else { |
| qla24xx_post_gnl_work(vha, fcport); |
| } |
| break; |
| default: |
| if (fcport->loop_id == FC_NO_LOOP_ID) { |
| ql_dbg(ql_dbg_disc, vha, 0x20bd, |
| "%s %d %8phC post gnl\n", |
| __func__, __LINE__, fcport->port_name); |
| qla24xx_post_gnl_work(vha, fcport); |
| } else { |
| qla_chk_n2n_b4_login(vha, fcport); |
| } |
| break; |
| } |
| break; |
| |
| case DSC_GNL: |
| switch (vha->hw->current_topology) { |
| case ISP_CFG_N: |
| if ((fcport->current_login_state & 0xf) == 0x6) { |
| ql_dbg(ql_dbg_disc, vha, 0x2118, |
| "%s %d %8phC post GPDB work\n", |
| __func__, __LINE__, fcport->port_name); |
| fcport->chip_reset = |
| vha->hw->base_qpair->chip_reset; |
| qla24xx_post_gpdb_work(vha, fcport, 0); |
| } else { |
| ql_dbg(ql_dbg_disc, vha, 0x2118, |
| "%s %d %8phC post %s PRLI\n", |
| __func__, __LINE__, fcport->port_name, |
| NVME_TARGET(vha->hw, fcport) ? "NVME" : |
| "FC"); |
| qla24xx_post_prli_work(vha, fcport); |
| } |
| break; |
| default: |
| if (fcport->login_pause) { |
| ql_dbg(ql_dbg_disc, vha, 0x20d8, |
| "%s %d %8phC exit\n", |
| __func__, __LINE__, |
| fcport->port_name); |
| fcport->last_rscn_gen = fcport->rscn_gen; |
| fcport->last_login_gen = fcport->login_gen; |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| break; |
| } |
| qla_chk_n2n_b4_login(vha, fcport); |
| break; |
| } |
| break; |
| |
| case DSC_LOGIN_FAILED: |
| if (N2N_TOPO(vha->hw)) |
| qla_chk_n2n_b4_login(vha, fcport); |
| else |
| qlt_schedule_sess_for_deletion(fcport); |
| break; |
| |
| case DSC_LOGIN_COMPLETE: |
| /* recheck login state */ |
| data[0] = data[1] = 0; |
| qla2x00_post_async_adisc_work(vha, fcport, data); |
| break; |
| |
| case DSC_LOGIN_PEND: |
| if (vha->hw->flags.edif_enabled) |
| break; |
| |
| if (fcport->fw_login_state == DSC_LS_PLOGI_COMP) { |
| ql_dbg(ql_dbg_disc, vha, 0x2118, |
| "%s %d %8phC post %s PRLI\n", |
| __func__, __LINE__, fcport->port_name, |
| NVME_TARGET(vha->hw, fcport) ? "NVME" : "FC"); |
| qla24xx_post_prli_work(vha, fcport); |
| } |
| break; |
| |
| case DSC_UPD_FCPORT: |
| sec = jiffies_to_msecs(jiffies - |
| fcport->jiffies_at_registration)/1000; |
| if (fcport->sec_since_registration < sec && sec && |
| !(sec % 60)) { |
| fcport->sec_since_registration = sec; |
| ql_dbg(ql_dbg_disc, fcport->vha, 0xffff, |
| "%s %8phC - Slow Rport registration(%d Sec)\n", |
| __func__, fcport->port_name, sec); |
| } |
| |
| if (fcport->next_disc_state != DSC_DELETE_PEND) |
| fcport->next_disc_state = DSC_ADISC; |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| break; |
| |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| int qla24xx_post_newsess_work(struct scsi_qla_host *vha, port_id_t *id, |
| u8 *port_name, u8 *node_name, void *pla, u8 fc4_type) |
| { |
| struct qla_work_evt *e; |
| |
| e = qla2x00_alloc_work(vha, QLA_EVT_NEW_SESS); |
| if (!e) |
| return QLA_FUNCTION_FAILED; |
| |
| e->u.new_sess.id = *id; |
| e->u.new_sess.pla = pla; |
| e->u.new_sess.fc4_type = fc4_type; |
| memcpy(e->u.new_sess.port_name, port_name, WWN_SIZE); |
| if (node_name) |
| memcpy(e->u.new_sess.node_name, node_name, WWN_SIZE); |
| |
| return qla2x00_post_work(vha, e); |
| } |
| |
| void qla2x00_handle_rscn(scsi_qla_host_t *vha, struct event_arg *ea) |
| { |
| fc_port_t *fcport; |
| unsigned long flags; |
| |
| switch (ea->id.b.rsvd_1) { |
| case RSCN_PORT_ADDR: |
| fcport = qla2x00_find_fcport_by_nportid(vha, &ea->id, 1); |
| if (fcport) { |
| if (ql2xfc2target && |
| fcport->flags & FCF_FCP2_DEVICE && |
| atomic_read(&fcport->state) == FCS_ONLINE) { |
| ql_dbg(ql_dbg_disc, vha, 0x2115, |
| "Delaying session delete for FCP2 portid=%06x %8phC ", |
| fcport->d_id.b24, fcport->port_name); |
| return; |
| } |
| |
| if (vha->hw->flags.edif_enabled && DBELL_ACTIVE(vha)) { |
| /* |
| * On ipsec start by remote port, Target port |
| * may use RSCN to trigger initiator to |
| * relogin. If driver is already in the |
| * process of a relogin, then ignore the RSCN |
| * and allow the current relogin to continue. |
| * This reduces thrashing of the connection. |
| */ |
| if (atomic_read(&fcport->state) == FCS_ONLINE) { |
| /* |
| * If state = online, then set scan_needed=1 to do relogin. |
| * Otherwise we're already in the middle of a relogin |
| */ |
| fcport->scan_needed = 1; |
| fcport->rscn_gen++; |
| } |
| } else { |
| fcport->scan_needed = 1; |
| fcport->rscn_gen++; |
| } |
| } |
| break; |
| case RSCN_AREA_ADDR: |
| list_for_each_entry(fcport, &vha->vp_fcports, list) { |
| if (fcport->flags & FCF_FCP2_DEVICE && |
| atomic_read(&fcport->state) == FCS_ONLINE) |
| continue; |
| |
| if ((ea->id.b24 & 0xffff00) == (fcport->d_id.b24 & 0xffff00)) { |
| fcport->scan_needed = 1; |
| fcport->rscn_gen++; |
| } |
| } |
| break; |
| case RSCN_DOM_ADDR: |
| list_for_each_entry(fcport, &vha->vp_fcports, list) { |
| if (fcport->flags & FCF_FCP2_DEVICE && |
| atomic_read(&fcport->state) == FCS_ONLINE) |
| continue; |
| |
| if ((ea->id.b24 & 0xff0000) == (fcport->d_id.b24 & 0xff0000)) { |
| fcport->scan_needed = 1; |
| fcport->rscn_gen++; |
| } |
| } |
| break; |
| case RSCN_FAB_ADDR: |
| default: |
| list_for_each_entry(fcport, &vha->vp_fcports, list) { |
| if (fcport->flags & FCF_FCP2_DEVICE && |
| atomic_read(&fcport->state) == FCS_ONLINE) |
| continue; |
| |
| fcport->scan_needed = 1; |
| fcport->rscn_gen++; |
| } |
| break; |
| } |
| |
| spin_lock_irqsave(&vha->work_lock, flags); |
| if (vha->scan.scan_flags == 0) { |
| ql_dbg(ql_dbg_disc, vha, 0xffff, "%s: schedule\n", __func__); |
| vha->scan.scan_flags |= SF_QUEUED; |
| schedule_delayed_work(&vha->scan.scan_work, 5); |
| } |
| spin_unlock_irqrestore(&vha->work_lock, flags); |
| } |
| |
| void qla24xx_handle_relogin_event(scsi_qla_host_t *vha, |
| struct event_arg *ea) |
| { |
| fc_port_t *fcport = ea->fcport; |
| |
| if (test_bit(UNLOADING, &vha->dpc_flags)) |
| return; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x2102, |
| "%s %8phC DS %d LS %d P %d del %d cnfl %p rscn %d|%d login %d|%d fl %x\n", |
| __func__, fcport->port_name, fcport->disc_state, |
| fcport->fw_login_state, fcport->login_pause, |
| fcport->deleted, fcport->conflict, |
| fcport->last_rscn_gen, fcport->rscn_gen, |
| fcport->last_login_gen, fcport->login_gen, |
| fcport->flags); |
| |
| if (fcport->last_rscn_gen != fcport->rscn_gen) { |
| ql_dbg(ql_dbg_disc, vha, 0x20e9, "%s %d %8phC post gnl\n", |
| __func__, __LINE__, fcport->port_name); |
| qla24xx_post_gnl_work(vha, fcport); |
| return; |
| } |
| |
| qla24xx_fcport_handle_login(vha, fcport); |
| } |
| |
| void qla_handle_els_plogi_done(scsi_qla_host_t *vha, |
| struct event_arg *ea) |
| { |
| if (N2N_TOPO(vha->hw) && fcport_is_smaller(ea->fcport) && |
| vha->hw->flags.edif_enabled) { |
| /* check to see if App support Secure */ |
| qla24xx_post_gpdb_work(vha, ea->fcport, 0); |
| return; |
| } |
| |
| /* for pure Target Mode, PRLI will not be initiated */ |
| if (vha->host->active_mode == MODE_TARGET) |
| return; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x2118, |
| "%s %d %8phC post PRLI\n", |
| __func__, __LINE__, ea->fcport->port_name); |
| qla24xx_post_prli_work(vha, ea->fcport); |
| } |
| |
| /* |
| * RSCN(s) came in for this fcport, but the RSCN(s) was not able |
| * to be consumed by the fcport |
| */ |
| void qla_rscn_replay(fc_port_t *fcport) |
| { |
| struct event_arg ea; |
| |
| switch (fcport->disc_state) { |
| case DSC_DELETE_PEND: |
| return; |
| default: |
| break; |
| } |
| |
| if (fcport->scan_needed) { |
| memset(&ea, 0, sizeof(ea)); |
| ea.id = fcport->d_id; |
| ea.id.b.rsvd_1 = RSCN_PORT_ADDR; |
| qla2x00_handle_rscn(fcport->vha, &ea); |
| } |
| } |
| |
| static void |
| qla2x00_tmf_iocb_timeout(void *data) |
| { |
| srb_t *sp = data; |
| struct srb_iocb *tmf = &sp->u.iocb_cmd; |
| int rc, h; |
| unsigned long flags; |
| |
| if (sp->type == SRB_MARKER) |
| rc = QLA_FUNCTION_FAILED; |
| else |
| rc = qla24xx_async_abort_cmd(sp, false); |
| |
| if (rc) { |
| spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags); |
| for (h = 1; h < sp->qpair->req->num_outstanding_cmds; h++) { |
| if (sp->qpair->req->outstanding_cmds[h] == sp) { |
| sp->qpair->req->outstanding_cmds[h] = NULL; |
| qla_put_fw_resources(sp->qpair, &sp->iores); |
| break; |
| } |
| } |
| spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags); |
| tmf->u.tmf.comp_status = cpu_to_le16(CS_TIMEOUT); |
| tmf->u.tmf.data = QLA_FUNCTION_FAILED; |
| complete(&tmf->u.tmf.comp); |
| } |
| } |
| |
| static void qla_marker_sp_done(srb_t *sp, int res) |
| { |
| struct srb_iocb *tmf = &sp->u.iocb_cmd; |
| |
| if (res != QLA_SUCCESS) |
| ql_dbg(ql_dbg_taskm, sp->vha, 0x8004, |
| "Async-marker fail hdl=%x portid=%06x ctrl=%x lun=%lld qp=%d.\n", |
| sp->handle, sp->fcport->d_id.b24, sp->u.iocb_cmd.u.tmf.flags, |
| sp->u.iocb_cmd.u.tmf.lun, sp->qpair->id); |
| |
| sp->u.iocb_cmd.u.tmf.data = res; |
| complete(&tmf->u.tmf.comp); |
| } |
| |
| #define START_SP_W_RETRIES(_sp, _rval, _chip_gen, _login_gen) \ |
| {\ |
| int cnt = 5; \ |
| do { \ |
| if (_chip_gen != sp->vha->hw->chip_reset || _login_gen != sp->fcport->login_gen) {\ |
| _rval = EINVAL; \ |
| break; \ |
| } \ |
| _rval = qla2x00_start_sp(_sp); \ |
| if (_rval == EAGAIN) \ |
| msleep(1); \ |
| else \ |
| break; \ |
| cnt--; \ |
| } while (cnt); \ |
| } |
| |
| /** |
| * qla26xx_marker: send marker IOCB and wait for the completion of it. |
| * @arg: pointer to argument list. |
| * It is assume caller will provide an fcport pointer and modifier |
| */ |
| static int |
| qla26xx_marker(struct tmf_arg *arg) |
| { |
| struct scsi_qla_host *vha = arg->vha; |
| struct srb_iocb *tm_iocb; |
| srb_t *sp; |
| int rval = QLA_FUNCTION_FAILED; |
| fc_port_t *fcport = arg->fcport; |
| u32 chip_gen, login_gen; |
| |
| if (TMF_NOT_READY(arg->fcport)) { |
| ql_dbg(ql_dbg_taskm, vha, 0x8039, |
| "FC port not ready for marker loop-id=%x portid=%06x modifier=%x lun=%lld qp=%d.\n", |
| fcport->loop_id, fcport->d_id.b24, |
| arg->modifier, arg->lun, arg->qpair->id); |
| return QLA_SUSPENDED; |
| } |
| |
| chip_gen = vha->hw->chip_reset; |
| login_gen = fcport->login_gen; |
| |
| /* ref: INIT */ |
| sp = qla2xxx_get_qpair_sp(vha, arg->qpair, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| sp->type = SRB_MARKER; |
| sp->name = "marker"; |
| qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha), qla_marker_sp_done); |
| sp->u.iocb_cmd.timeout = qla2x00_tmf_iocb_timeout; |
| |
| tm_iocb = &sp->u.iocb_cmd; |
| init_completion(&tm_iocb->u.tmf.comp); |
| tm_iocb->u.tmf.modifier = arg->modifier; |
| tm_iocb->u.tmf.lun = arg->lun; |
| tm_iocb->u.tmf.loop_id = fcport->loop_id; |
| tm_iocb->u.tmf.vp_index = vha->vp_idx; |
| |
| START_SP_W_RETRIES(sp, rval, chip_gen, login_gen); |
| |
| ql_dbg(ql_dbg_taskm, vha, 0x8006, |
| "Async-marker hdl=%x loop-id=%x portid=%06x modifier=%x lun=%lld qp=%d rval %d.\n", |
| sp->handle, fcport->loop_id, fcport->d_id.b24, |
| arg->modifier, arg->lun, sp->qpair->id, rval); |
| |
| if (rval != QLA_SUCCESS) { |
| ql_log(ql_log_warn, vha, 0x8031, |
| "Marker IOCB send failure (%x).\n", rval); |
| goto done_free_sp; |
| } |
| |
| wait_for_completion(&tm_iocb->u.tmf.comp); |
| rval = tm_iocb->u.tmf.data; |
| |
| if (rval != QLA_SUCCESS) { |
| ql_log(ql_log_warn, vha, 0x8019, |
| "Marker failed hdl=%x loop-id=%x portid=%06x modifier=%x lun=%lld qp=%d rval %d.\n", |
| sp->handle, fcport->loop_id, fcport->d_id.b24, |
| arg->modifier, arg->lun, sp->qpair->id, rval); |
| } |
| |
| done_free_sp: |
| /* ref: INIT */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| done: |
| return rval; |
| } |
| |
| static void qla2x00_tmf_sp_done(srb_t *sp, int res) |
| { |
| struct srb_iocb *tmf = &sp->u.iocb_cmd; |
| |
| if (res) |
| tmf->u.tmf.data = res; |
| complete(&tmf->u.tmf.comp); |
| } |
| |
| static int qla_tmf_wait(struct tmf_arg *arg) |
| { |
| /* there are only 2 types of error handling that reaches here, lun or target reset */ |
| if (arg->flags & (TCF_LUN_RESET | TCF_ABORT_TASK_SET | TCF_CLEAR_TASK_SET)) |
| return qla2x00_eh_wait_for_pending_commands(arg->vha, |
| arg->fcport->d_id.b24, arg->lun, WAIT_LUN); |
| else |
| return qla2x00_eh_wait_for_pending_commands(arg->vha, |
| arg->fcport->d_id.b24, arg->lun, WAIT_TARGET); |
| } |
| |
| static int |
| __qla2x00_async_tm_cmd(struct tmf_arg *arg) |
| { |
| struct scsi_qla_host *vha = arg->vha; |
| struct srb_iocb *tm_iocb; |
| srb_t *sp; |
| int rval = QLA_FUNCTION_FAILED; |
| fc_port_t *fcport = arg->fcport; |
| u32 chip_gen, login_gen; |
| u64 jif; |
| |
| if (TMF_NOT_READY(arg->fcport)) { |
| ql_dbg(ql_dbg_taskm, vha, 0x8032, |
| "FC port not ready for TM command loop-id=%x portid=%06x modifier=%x lun=%lld qp=%d.\n", |
| fcport->loop_id, fcport->d_id.b24, |
| arg->modifier, arg->lun, arg->qpair->id); |
| return QLA_SUSPENDED; |
| } |
| |
| chip_gen = vha->hw->chip_reset; |
| login_gen = fcport->login_gen; |
| |
| /* ref: INIT */ |
| sp = qla2xxx_get_qpair_sp(vha, arg->qpair, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| qla_vha_mark_busy(vha); |
| sp->type = SRB_TM_CMD; |
| sp->name = "tmf"; |
| qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha), |
| qla2x00_tmf_sp_done); |
| sp->u.iocb_cmd.timeout = qla2x00_tmf_iocb_timeout; |
| |
| tm_iocb = &sp->u.iocb_cmd; |
| init_completion(&tm_iocb->u.tmf.comp); |
| tm_iocb->u.tmf.flags = arg->flags; |
| tm_iocb->u.tmf.lun = arg->lun; |
| |
| START_SP_W_RETRIES(sp, rval, chip_gen, login_gen); |
| |
| ql_dbg(ql_dbg_taskm, vha, 0x802f, |
| "Async-tmf hdl=%x loop-id=%x portid=%06x ctrl=%x lun=%lld qp=%d rval=%x.\n", |
| sp->handle, fcport->loop_id, fcport->d_id.b24, |
| arg->flags, arg->lun, sp->qpair->id, rval); |
| |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| wait_for_completion(&tm_iocb->u.tmf.comp); |
| |
| rval = tm_iocb->u.tmf.data; |
| |
| if (rval != QLA_SUCCESS) { |
| ql_log(ql_log_warn, vha, 0x8030, |
| "TM IOCB failed (%x).\n", rval); |
| } |
| |
| if (!test_bit(UNLOADING, &vha->dpc_flags) && !IS_QLAFX00(vha->hw)) { |
| jif = jiffies; |
| if (qla_tmf_wait(arg)) { |
| ql_log(ql_log_info, vha, 0x803e, |
| "Waited %u ms Nexus=%ld:%06x:%llu.\n", |
| jiffies_to_msecs(jiffies - jif), vha->host_no, |
| fcport->d_id.b24, arg->lun); |
| } |
| |
| if (chip_gen == vha->hw->chip_reset && login_gen == fcport->login_gen) { |
| rval = qla26xx_marker(arg); |
| } else { |
| ql_log(ql_log_info, vha, 0x803e, |
| "Skip Marker due to disruption. Nexus=%ld:%06x:%llu.\n", |
| vha->host_no, fcport->d_id.b24, arg->lun); |
| rval = QLA_FUNCTION_FAILED; |
| } |
| } |
| if (tm_iocb->u.tmf.data) |
| rval = tm_iocb->u.tmf.data; |
| |
| done_free_sp: |
| /* ref: INIT */ |
| kref_put(&sp->cmd_kref, qla2x00_sp_release); |
| done: |
| return rval; |
| } |
| |
| static void qla_put_tmf(struct tmf_arg *arg) |
| { |
| struct scsi_qla_host *vha = arg->vha; |
| struct qla_hw_data *ha = vha->hw; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&ha->tgt.sess_lock, flags); |
| ha->active_tmf--; |
| list_del(&arg->tmf_elem); |
| spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); |
| } |
| |
| static |
| int qla_get_tmf(struct tmf_arg *arg) |
| { |
| struct scsi_qla_host *vha = arg->vha; |
| struct qla_hw_data *ha = vha->hw; |
| unsigned long flags; |
| fc_port_t *fcport = arg->fcport; |
| int rc = 0; |
| struct tmf_arg *t; |
| |
| spin_lock_irqsave(&ha->tgt.sess_lock, flags); |
| list_for_each_entry(t, &ha->tmf_active, tmf_elem) { |
| if (t->fcport == arg->fcport && t->lun == arg->lun) { |
| /* reject duplicate TMF */ |
| ql_log(ql_log_warn, vha, 0x802c, |
| "found duplicate TMF. Nexus=%ld:%06x:%llu.\n", |
| vha->host_no, fcport->d_id.b24, arg->lun); |
| spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); |
| return -EINVAL; |
| } |
| } |
| |
| list_add_tail(&arg->tmf_elem, &ha->tmf_pending); |
| while (ha->active_tmf >= MAX_ACTIVE_TMF) { |
| spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); |
| |
| msleep(1); |
| |
| spin_lock_irqsave(&ha->tgt.sess_lock, flags); |
| if (TMF_NOT_READY(fcport)) { |
| ql_log(ql_log_warn, vha, 0x802c, |
| "Unable to acquire TM resource due to disruption.\n"); |
| rc = EIO; |
| break; |
| } |
| if (ha->active_tmf < MAX_ACTIVE_TMF && |
| list_is_first(&arg->tmf_elem, &ha->tmf_pending)) |
| break; |
| } |
| |
| list_del(&arg->tmf_elem); |
| |
| if (!rc) { |
| ha->active_tmf++; |
| list_add_tail(&arg->tmf_elem, &ha->tmf_active); |
| } |
| |
| spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); |
| |
| return rc; |
| } |
| |
| int |
| qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t flags, uint64_t lun, |
| uint32_t tag) |
| { |
| struct scsi_qla_host *vha = fcport->vha; |
| struct tmf_arg a; |
| int rval = QLA_SUCCESS; |
| |
| if (TMF_NOT_READY(fcport)) |
| return QLA_SUSPENDED; |
| |
| a.vha = fcport->vha; |
| a.fcport = fcport; |
| a.lun = lun; |
| a.flags = flags; |
| INIT_LIST_HEAD(&a.tmf_elem); |
| |
| if (flags & (TCF_LUN_RESET|TCF_ABORT_TASK_SET|TCF_CLEAR_TASK_SET|TCF_CLEAR_ACA)) { |
| a.modifier = MK_SYNC_ID_LUN; |
| } else { |
| a.modifier = MK_SYNC_ID; |
| } |
| |
| if (qla_get_tmf(&a)) |
| return QLA_FUNCTION_FAILED; |
| |
| a.qpair = vha->hw->base_qpair; |
| rval = __qla2x00_async_tm_cmd(&a); |
| |
| qla_put_tmf(&a); |
| return rval; |
| } |
| |
| int |
| qla24xx_async_abort_command(srb_t *sp) |
| { |
| unsigned long flags = 0; |
| |
| uint32_t handle; |
| fc_port_t *fcport = sp->fcport; |
| struct qla_qpair *qpair = sp->qpair; |
| struct scsi_qla_host *vha = fcport->vha; |
| struct req_que *req = qpair->req; |
| |
| spin_lock_irqsave(qpair->qp_lock_ptr, flags); |
| for (handle = 1; handle < req->num_outstanding_cmds; handle++) { |
| if (req->outstanding_cmds[handle] == sp) |
| break; |
| } |
| spin_unlock_irqrestore(qpair->qp_lock_ptr, flags); |
| |
| if (handle == req->num_outstanding_cmds) { |
| /* Command not found. */ |
| return QLA_ERR_NOT_FOUND; |
| } |
| if (sp->type == SRB_FXIOCB_DCMD) |
| return qlafx00_fx_disc(vha, &vha->hw->mr.fcport, |
| FXDISC_ABORT_IOCTL); |
| |
| return qla24xx_async_abort_cmd(sp, true); |
| } |
| |
| static void |
| qla24xx_handle_prli_done_event(struct scsi_qla_host *vha, struct event_arg *ea) |
| { |
| struct srb *sp; |
| WARN_ONCE(!qla2xxx_is_valid_mbs(ea->data[0]), "mbs: %#x\n", |
| ea->data[0]); |
| |
| switch (ea->data[0]) { |
| case MBS_COMMAND_COMPLETE: |
| ql_dbg(ql_dbg_disc, vha, 0x2118, |
| "%s %d %8phC post gpdb\n", |
| __func__, __LINE__, ea->fcport->port_name); |
| |
| ea->fcport->chip_reset = vha->hw->base_qpair->chip_reset; |
| ea->fcport->logout_on_delete = 1; |
| ea->fcport->nvme_prli_service_param = ea->iop[0]; |
| if (ea->iop[0] & NVME_PRLI_SP_FIRST_BURST) |
| ea->fcport->nvme_first_burst_size = |
| (ea->iop[1] & 0xffff) * 512; |
| else |
| ea->fcport->nvme_first_burst_size = 0; |
| qla24xx_post_gpdb_work(vha, ea->fcport, 0); |
| break; |
| default: |
| sp = ea->sp; |
| ql_dbg(ql_dbg_disc, vha, 0x2118, |
| "%s %d %8phC priority %s, fc4type %x prev try %s\n", |
| __func__, __LINE__, ea->fcport->port_name, |
| vha->hw->fc4_type_priority == FC4_PRIORITY_FCP ? |
| "FCP" : "NVMe", ea->fcport->fc4_type, |
| (sp->u.iocb_cmd.u.logio.flags & SRB_LOGIN_NVME_PRLI) ? |
| "NVME" : "FCP"); |
| |
| if (NVME_FCP_TARGET(ea->fcport)) { |
| if (sp->u.iocb_cmd.u.logio.flags & SRB_LOGIN_NVME_PRLI) |
| ea->fcport->do_prli_nvme = 0; |
| else |
| ea->fcport->do_prli_nvme = 1; |
| } else { |
| ea->fcport->do_prli_nvme = 0; |
| } |
| |
| if (N2N_TOPO(vha->hw)) { |
| if (ea->fcport->n2n_link_reset_cnt == |
| vha->hw->login_retry_count && |
| ea->fcport->flags & FCF_FCSP_DEVICE) { |
| /* remote authentication app just started */ |
| ea->fcport->n2n_link_reset_cnt = 0; |
| } |
| |
| if (ea->fcport->n2n_link_reset_cnt < |
| vha->hw->login_retry_count) { |
| ea->fcport->n2n_link_reset_cnt++; |
| vha->relogin_jif = jiffies + 2 * HZ; |
| /* |
| * PRLI failed. Reset link to kick start |
| * state machine |
| */ |
| set_bit(N2N_LINK_RESET, &vha->dpc_flags); |
| qla2xxx_wake_dpc(vha); |
| } else { |
| ql_log(ql_log_warn, vha, 0x2119, |
| "%s %d %8phC Unable to reconnect\n", |
| __func__, __LINE__, |
| ea->fcport->port_name); |
| } |
| } else { |
| /* |
| * switch connect. login failed. Take connection down |
| * and allow relogin to retrigger |
| */ |
| ea->fcport->flags &= ~FCF_ASYNC_SENT; |
| ea->fcport->keep_nport_handle = 0; |
| ea->fcport->logout_on_delete = 1; |
| qlt_schedule_sess_for_deletion(ea->fcport); |
| } |
| break; |
| } |
| } |
| |
| void |
| qla24xx_handle_plogi_done_event(struct scsi_qla_host *vha, struct event_arg *ea) |
| { |
| port_id_t cid; /* conflict Nport id */ |
| u16 lid; |
| struct fc_port *conflict_fcport; |
| unsigned long flags; |
| struct fc_port *fcport = ea->fcport; |
| |
| ql_dbg(ql_dbg_disc, vha, 0xffff, |
| "%s %8phC DS %d LS %d rc %d login %d|%d rscn %d|%d data %x|%x iop %x|%x\n", |
| __func__, fcport->port_name, fcport->disc_state, |
| fcport->fw_login_state, ea->rc, ea->sp->gen2, fcport->login_gen, |
| ea->sp->gen1, fcport->rscn_gen, |
| ea->data[0], ea->data[1], ea->iop[0], ea->iop[1]); |
| |
| if ((fcport->fw_login_state == DSC_LS_PLOGI_PEND) || |
| (fcport->fw_login_state == DSC_LS_PRLI_PEND)) { |
| ql_dbg(ql_dbg_disc, vha, 0x20ea, |
| "%s %d %8phC Remote is trying to login\n", |
| __func__, __LINE__, fcport->port_name); |
| return; |
| } |
| |
| if ((fcport->disc_state == DSC_DELETE_PEND) || |
| (fcport->disc_state == DSC_DELETED)) { |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| return; |
| } |
| |
| if (ea->sp->gen2 != fcport->login_gen) { |
| /* target side must have changed it. */ |
| ql_dbg(ql_dbg_disc, vha, 0x20d3, |
| "%s %8phC generation changed\n", |
| __func__, fcport->port_name); |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| return; |
| } else if (ea->sp->gen1 != fcport->rscn_gen) { |
| ql_dbg(ql_dbg_disc, vha, 0x20d3, |
| "%s %8phC RSCN generation changed\n", |
| __func__, fcport->port_name); |
| qla_rscn_replay(fcport); |
| qlt_schedule_sess_for_deletion(fcport); |
| return; |
| } |
| |
| WARN_ONCE(!qla2xxx_is_valid_mbs(ea->data[0]), "mbs: %#x\n", |
| ea->data[0]); |
| |
| switch (ea->data[0]) { |
| case MBS_COMMAND_COMPLETE: |
| /* |
| * Driver must validate login state - If PRLI not complete, |
| * force a relogin attempt via implicit LOGO, PLOGI, and PRLI |
| * requests. |
| */ |
| if (vha->hw->flags.edif_enabled) { |
| set_bit(ea->fcport->loop_id, vha->hw->loop_id_map); |
| spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); |
| ea->fcport->chip_reset = vha->hw->base_qpair->chip_reset; |
| ea->fcport->logout_on_delete = 1; |
| ea->fcport->send_els_logo = 0; |
| ea->fcport->fw_login_state = DSC_LS_PLOGI_COMP; |
| spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); |
| |
| qla24xx_post_gpdb_work(vha, ea->fcport, 0); |
| } else { |
| if (NVME_TARGET(vha->hw, fcport)) { |
| ql_dbg(ql_dbg_disc, vha, 0x2117, |
| "%s %d %8phC post prli\n", |
| __func__, __LINE__, fcport->port_name); |
| qla24xx_post_prli_work(vha, fcport); |
| } else { |
| ql_dbg(ql_dbg_disc, vha, 0x20ea, |
| "%s %d %8phC LoopID 0x%x in use with %06x. post gpdb\n", |
| __func__, __LINE__, fcport->port_name, |
| fcport->loop_id, fcport->d_id.b24); |
| |
| set_bit(fcport->loop_id, vha->hw->loop_id_map); |
| spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); |
| fcport->chip_reset = vha->hw->base_qpair->chip_reset; |
| fcport->logout_on_delete = 1; |
| fcport->send_els_logo = 0; |
| fcport->fw_login_state = DSC_LS_PRLI_COMP; |
| spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); |
| |
| qla24xx_post_gpdb_work(vha, fcport, 0); |
| } |
| } |
| break; |
| case MBS_COMMAND_ERROR: |
| ql_dbg(ql_dbg_disc, vha, 0x20eb, "%s %d %8phC cmd error %x\n", |
| __func__, __LINE__, ea->fcport->port_name, ea->data[1]); |
| |
| qlt_schedule_sess_for_deletion(ea->fcport); |
| break; |
| case MBS_LOOP_ID_USED: |
| /* data[1] = IO PARAM 1 = nport ID */ |
| cid.b.domain = (ea->iop[1] >> 16) & 0xff; |
| cid.b.area = (ea->iop[1] >> 8) & 0xff; |
| cid.b.al_pa = ea->iop[1] & 0xff; |
| cid.b.rsvd_1 = 0; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20ec, |
| "%s %d %8phC lid %#x in use with pid %06x post gnl\n", |
| __func__, __LINE__, ea->fcport->port_name, |
| ea->fcport->loop_id, cid.b24); |
| |
| set_bit(ea->fcport->loop_id, vha->hw->loop_id_map); |
| ea->fcport->loop_id = FC_NO_LOOP_ID; |
| qla24xx_post_gnl_work(vha, ea->fcport); |
| break; |
| case MBS_PORT_ID_USED: |
| lid = ea->iop[1] & 0xffff; |
| qlt_find_sess_invalidate_other(vha, |
| wwn_to_u64(ea->fcport->port_name), |
| ea->fcport->d_id, lid, &conflict_fcport); |
| |
| if (conflict_fcport) { |
| /* |
| * Another fcport share the same loop_id/nport id. |
| * Conflict fcport needs to finish cleanup before this |
| * fcport can proceed to login. |
| */ |
| conflict_fcport->conflict = ea->fcport; |
| ea->fcport->login_pause = 1; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20ed, |
| "%s %d %8phC NPortId %06x inuse with loopid 0x%x.\n", |
| __func__, __LINE__, ea->fcport->port_name, |
| ea->fcport->d_id.b24, lid); |
| } else { |
| ql_dbg(ql_dbg_disc, vha, 0x20ed, |
| "%s %d %8phC NPortId %06x inuse with loopid 0x%x. sched delete\n", |
| __func__, __LINE__, ea->fcport->port_name, |
| ea->fcport->d_id.b24, lid); |
| |
| qla2x00_clear_loop_id(ea->fcport); |
| set_bit(lid, vha->hw->loop_id_map); |
| ea->fcport->loop_id = lid; |
| ea->fcport->keep_nport_handle = 0; |
| ea->fcport->logout_on_delete = 1; |
| qlt_schedule_sess_for_deletion(ea->fcport); |
| } |
| break; |
| } |
| return; |
| } |
| |
| /****************************************************************************/ |
| /* QLogic ISP2x00 Hardware Support Functions. */ |
| /****************************************************************************/ |
| |
| static int |
| qla83xx_nic_core_fw_load(scsi_qla_host_t *vha) |
| { |
| int rval = QLA_SUCCESS; |
| struct qla_hw_data *ha = vha->hw; |
| uint32_t idc_major_ver, idc_minor_ver; |
| uint16_t config[4]; |
| |
| qla83xx_idc_lock(vha, 0); |
| |
| /* SV: TODO: Assign initialization timeout from |
| * flash-info / other param |
| */ |
| ha->fcoe_dev_init_timeout = QLA83XX_IDC_INITIALIZATION_TIMEOUT; |
| ha->fcoe_reset_timeout = QLA83XX_IDC_RESET_ACK_TIMEOUT; |
| |
| /* Set our fcoe function presence */ |
| if (__qla83xx_set_drv_presence(vha) != QLA_SUCCESS) { |
| ql_dbg(ql_dbg_p3p, vha, 0xb077, |
| "Error while setting DRV-Presence.\n"); |
| rval = QLA_FUNCTION_FAILED; |
| goto exit; |
| } |
| |
| /* Decide the reset ownership */ |
| qla83xx_reset_ownership(vha); |
| |
| /* |
| * On first protocol driver load: |
| * Init-Owner: Set IDC-Major-Version and Clear IDC-Lock-Recovery |
| * register. |
| * Others: Check compatibility with current IDC Major version. |
| */ |
| qla83xx_rd_reg(vha, QLA83XX_IDC_MAJOR_VERSION, &idc_major_ver); |
| if (ha->flags.nic_core_reset_owner) { |
| /* Set IDC Major version */ |
| idc_major_ver = QLA83XX_SUPP_IDC_MAJOR_VERSION; |
| qla83xx_wr_reg(vha, QLA83XX_IDC_MAJOR_VERSION, idc_major_ver); |
| |
| /* Clearing IDC-Lock-Recovery register */ |
| qla83xx_wr_reg(vha, QLA83XX_IDC_LOCK_RECOVERY, 0); |
| } else if (idc_major_ver != QLA83XX_SUPP_IDC_MAJOR_VERSION) { |
| /* |
| * Clear further IDC participation if we are not compatible with |
| * the current IDC Major Version. |
| */ |
| ql_log(ql_log_warn, vha, 0xb07d, |
| "Failing load, idc_major_ver=%d, expected_major_ver=%d.\n", |
| idc_major_ver, QLA83XX_SUPP_IDC_MAJOR_VERSION); |
| __qla83xx_clear_drv_presence(vha); |
| rval = QLA_FUNCTION_FAILED; |
| goto exit; |
| } |
| /* Each function sets its supported Minor version. */ |
| qla83xx_rd_reg(vha, QLA83XX_IDC_MINOR_VERSION, &idc_minor_ver); |
| idc_minor_ver |= (QLA83XX_SUPP_IDC_MINOR_VERSION << (ha->portnum * 2)); |
| qla83xx_wr_reg(vha, QLA83XX_IDC_MINOR_VERSION, idc_minor_ver); |
| |
| if (ha->flags.nic_core_reset_owner) { |
| memset(config, 0, sizeof(config)); |
| if (!qla81xx_get_port_config(vha, config)) |
| qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, |
| QLA8XXX_DEV_READY); |
| } |
| |
| rval = qla83xx_idc_state_handler(vha); |
| |
| exit: |
| qla83xx_idc_unlock(vha, 0); |
| |
| return rval; |
| } |
| |
| /* |
| * qla2x00_initialize_adapter |
| * Initialize board. |
| * |
| * Input: |
| * ha = adapter block pointer. |
| * |
| * Returns: |
| * 0 = success |
| */ |
| int |
| qla2x00_initialize_adapter(scsi_qla_host_t *vha) |
| { |
| int rval; |
| struct qla_hw_data *ha = vha->hw; |
| struct req_que *req = ha->req_q_map[0]; |
| struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; |
| |
| memset(&vha->qla_stats, 0, sizeof(vha->qla_stats)); |
| memset(&vha->fc_host_stat, 0, sizeof(vha->fc_host_stat)); |
| |
| /* Clear adapter flags. */ |
| vha->flags.online = 0; |
| ha->flags.chip_reset_done = 0; |
| vha->flags.reset_active = 0; |
| ha->flags.pci_channel_io_perm_failure = 0; |
| ha->flags.eeh_busy = 0; |
| vha->qla_stats.jiffies_at_last_reset = get_jiffies_64(); |
| atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); |
| atomic_set(&vha->loop_state, LOOP_DOWN); |
| vha->device_flags = DFLG_NO_CABLE; |
| vha->dpc_flags = 0; |
| vha->flags.management_server_logged_in = 0; |
| vha->marker_needed = 0; |
| ha->isp_abort_cnt = 0; |
| ha->beacon_blink_led = 0; |
| |
| set_bit(0, ha->req_qid_map); |
| set_bit(0, ha->rsp_qid_map); |
| |
| ql_dbg(ql_dbg_init, vha, 0x0040, |
| "Configuring PCI space...\n"); |
| rval = ha->isp_ops->pci_config(vha); |
| if (rval) { |
| ql_log(ql_log_warn, vha, 0x0044, |
| "Unable to configure PCI space.\n"); |
| return (rval); |
| } |
| |
| ha->isp_ops->reset_chip(vha); |
| |
| /* Check for secure flash support */ |
| if (IS_QLA28XX(ha)) { |
| if (rd_reg_word(®->mailbox12) & BIT_0) |
| ha->flags.secure_adapter = 1; |
| ql_log(ql_log_info, vha, 0xffff, "Secure Adapter: %s\n", |
| (ha->flags.secure_adapter) ? "Yes" : "No"); |
| } |
| |
| |
| rval = qla2xxx_get_flash_info(vha); |
| if (rval) { |
| ql_log(ql_log_fatal, vha, 0x004f, |
| "Unable to validate FLASH data.\n"); |
| return rval; |
| } |
| |
| if (IS_QLA8044(ha)) { |
| qla8044_read_reset_template(vha); |
| |
| /* NOTE: If ql2xdontresethba==1, set IDC_CTRL DONTRESET_BIT0. |
| * If DONRESET_BIT0 is set, drivers should not set dev_state |
| * to NEED_RESET. But if NEED_RESET is set, drivers should |
| * should honor the reset. */ |
| if (ql2xdontresethba == 1) |
| qla8044_set_idc_dontreset(vha); |
| } |
| |
| ha->isp_ops->get_flash_version(vha, req->ring); |
| ql_dbg(ql_dbg_init, vha, 0x0061, |
| "Configure NVRAM parameters...\n"); |
| |
| /* Let priority default to FCP, can be overridden by nvram_config */ |
| ha->fc4_type_priority = FC4_PRIORITY_FCP; |
| |
| ha->isp_ops->nvram_config(vha); |
| |
| if (ha->fc4_type_priority != FC4_PRIORITY_FCP && |
| ha->fc4_type_priority != FC4_PRIORITY_NVME) |
| ha->fc4_type_priority = FC4_PRIORITY_FCP; |
| |
| ql_log(ql_log_info, vha, 0xffff, "FC4 priority set to %s\n", |
| ha->fc4_type_priority == FC4_PRIORITY_FCP ? "FCP" : "NVMe"); |
| |
| if (ha->flags.disable_serdes) { |
| /* Mask HBA via NVRAM settings? */ |
| ql_log(ql_log_info, vha, 0x0077, |
| "Masking HBA WWPN %8phN (via NVRAM).\n", vha->port_name); |
| return QLA_FUNCTION_FAILED; |
| } |
| |
| ql_dbg(ql_dbg_init, vha, 0x0078, |
| "Verifying loaded RISC code...\n"); |
| |
| /* If smartsan enabled then require fdmi and rdp enabled */ |
| if (ql2xsmartsan) { |
| ql2xfdmienable = 1; |
| ql2xrdpenable = 1; |
| } |
| |
| if (qla2x00_isp_firmware(vha) != QLA_SUCCESS) { |
| rval = ha->isp_ops->chip_diag(vha); |
| if (rval) |
| return (rval); |
| rval = qla2x00_setup_chip(vha); |
| if (rval) |
| return (rval); |
| } |
| |
| if (IS_QLA84XX(ha)) { |
| ha->cs84xx = qla84xx_get_chip(vha); |
| if (!ha->cs84xx) { |
| ql_log(ql_log_warn, vha, 0x00d0, |
| "Unable to configure ISP84XX.\n"); |
| return QLA_FUNCTION_FAILED; |
| } |
| } |
| |
| if (qla_ini_mode_enabled(vha) || qla_dual_mode_enabled(vha)) |
| rval = qla2x00_init_rings(vha); |
| |
| /* No point in continuing if firmware initialization failed. */ |
| if (rval != QLA_SUCCESS) |
| return rval; |
| |
| ha->flags.chip_reset_done = 1; |
| |
| if (rval == QLA_SUCCESS && IS_QLA84XX(ha)) { |
| /* Issue verify 84xx FW IOCB to complete 84xx initialization */ |
| rval = qla84xx_init_chip(vha); |
| if (rval != QLA_SUCCESS) { |
| ql_log(ql_log_warn, vha, 0x00d4, |
| "Unable to initialize ISP84XX.\n"); |
| qla84xx_put_chip(vha); |
| } |
| } |
| |
| /* Load the NIC Core f/w if we are the first protocol driver. */ |
| if (IS_QLA8031(ha)) { |
| rval = qla83xx_nic_core_fw_load(vha); |
| if (rval) |
| ql_log(ql_log_warn, vha, 0x0124, |
| "Error in initializing NIC Core f/w.\n"); |
| } |
| |
| if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha)) |
| qla24xx_read_fcp_prio_cfg(vha); |
| |
| if (IS_P3P_TYPE(ha)) |
| qla82xx_set_driver_version(vha, QLA2XXX_VERSION); |
| else |
| qla25xx_set_driver_version(vha, QLA2XXX_VERSION); |
| |
| return (rval); |
| } |
| |
| /** |
| * qla2100_pci_config() - Setup ISP21xx PCI configuration registers. |
| * @vha: HA context |
| * |
| * Returns 0 on success. |
| */ |
| int |
| qla2100_pci_config(scsi_qla_host_t *vha) |
| { |
| uint16_t w; |
| unsigned long flags; |
| struct qla_hw_data *ha = vha->hw; |
| struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; |
| |
| pci_set_master(ha->pdev); |
| pci_try_set_mwi(ha->pdev); |
| |
| pci_read_config_word(ha->pdev, PCI_COMMAND, &w); |
| w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR); |
| pci_write_config_word(ha->pdev, PCI_COMMAND, w); |
| |
| pci_disable_rom(ha->pdev); |
| |
| /* Get PCI bus information. */ |
| spin_lock_irqsave(&ha->hardware_lock, flags); |
| ha->pci_attr = rd_reg_word(®->ctrl_status); |
| spin_unlock_irqrestore(&ha->hardware_lock, flags); |
| |
| return QLA_SUCCESS; |
| } |
| |
| /** |
| * qla2300_pci_config() - Setup ISP23xx PCI configuration registers. |
| * @vha: HA context |
| * |
| * Returns 0 on success. |
| */ |
| int |
| qla2300_pci_config(scsi_qla_host_t *vha) |
| { |
| uint16_t w; |
| unsigned long flags = 0; |
| uint32_t cnt; |
| struct qla_hw_data *ha = vha->hw; |
| struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; |
| |
| pci_set_master(ha->pdev); |
| pci_try_set_mwi(ha->pdev); |
| |
| pci_read_config_word(ha->pdev, PCI_COMMAND, &w); |
| w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR); |
| |
| if (IS_QLA2322(ha) || IS_QLA6322(ha)) |
| w &= ~PCI_COMMAND_INTX_DISABLE; |
| pci_write_config_word(ha->pdev, PCI_COMMAND, w); |
| |
| /* |
| * If this is a 2300 card and not 2312, reset the |
| * COMMAND_INVALIDATE due to a bug in the 2300. Unfortunately, |
| * the 2310 also reports itself as a 2300 so we need to get the |
| * fb revision level -- a 6 indicates it really is a 2300 and |
| * not a 2310. |
| */ |
| if (IS_QLA2300(ha)) { |
| spin_lock_irqsave(&ha->hardware_lock<
|