blob: a314cfc5b263f223e4548c4647e1d645208db69f [file] [log] [blame]
// 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(&reg->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(&reg->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<