Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/scsi/qla2xxx/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 265 kB image not shown  

Quelle  qla_init.c   Sprache: C

 
// 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 = timer_container_of(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;

 timer_delete(&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:
 /*
 * use qla24xx_async_gnl_sp_done to purge all pending gnl request.
 * kref_put is call behind the scene.
 */

 sp->u.iocb_cmd.u.mbx.in_mb[0] = MBS_COMMAND_ERROR;
 qla24xx_async_gnl_sp_done(sp, QLA_COMMAND_ERROR);
 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);
}

static void qla_rscn_gen_tick(scsi_qla_host_t *vha, u32 *ret_rscn_gen)
{
 *ret_rscn_gen = atomic_inc_return(&vha->rscn_gen);
 /* memory barrier */
 wmb();
}

void qla2x00_handle_rscn(scsi_qla_host_t *vha, struct event_arg *ea)
{
 fc_port_t *fcport;
 unsigned long flags;
 u32 rscn_gen;

 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;
     qla_rscn_gen_tick(vha, &fcport->rscn_gen);
    }
   } else {
    fcport->scan_needed = 1;
    qla_rscn_gen_tick(vha, &fcport->rscn_gen);
   }
  }
  break;
 case RSCN_AREA_ADDR:
  qla_rscn_gen_tick(vha, &rscn_gen);
  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 = rscn_gen;
   }
  }
  break;
 case RSCN_DOM_ADDR:
  qla_rscn_gen_tick(vha, &rscn_gen);
  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 = rscn_gen;
   }
  }
  break;
 case RSCN_FAB_ADDR:
 default:
  qla_rscn_gen_tick(vha, &rscn_gen);
  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 = 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;
  vha->scan.rscn_gen_start = atomic_read(&vha->rscn_gen);
  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;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=92 G=93

¤ Dauer der Verarbeitung: 0.24 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.