static int qlt_24xx_handle_els()

in qla2xxx/qla_target.c [4979:5277]


static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
	struct imm_ntfy_from_isp *iocb)
{
	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
	struct qla_hw_data *ha = vha->hw;
	struct fc_port *sess = NULL, *conflict_sess = NULL;
	uint64_t wwn;
	port_id_t port_id;
	uint16_t loop_id;
	uint16_t wd3_lo;
	int res = 0;
	unsigned long flags;

	lockdep_assert_held(&ha->hardware_lock);

	wwn = wwn_to_u64(iocb->u.isp24.port_name);

	port_id.b.domain = iocb->u.isp24.port_id[2];
	port_id.b.area   = iocb->u.isp24.port_id[1];
	port_id.b.al_pa  = iocb->u.isp24.port_id[0];
	port_id.b.rsvd_1 = 0;

	loop_id = le16_to_cpu(iocb->u.isp24.nport_handle);

	ql_dbg(ql_dbg_disc, vha, 0xf026,
	    "qla_target(%d): Port ID: %02x:%02x:%02x ELS opcode: 0x%02x lid %d %8phC\n",
	    vha->vp_idx, iocb->u.isp24.port_id[2],
		iocb->u.isp24.port_id[1], iocb->u.isp24.port_id[0],
		   iocb->u.isp24.status_subcode, loop_id,
		iocb->u.isp24.port_name);

	/* res = 1 means ack at the end of thread
	 * res = 0 means ack async/later.
	 */
	switch (iocb->u.isp24.status_subcode) {
	case ELS_PLOGI:
		res = qlt_handle_login(vha, iocb);
		break;

	case ELS_PRLI:
		if (N2N_TOPO(ha)) {
			sess = qla2x00_find_fcport_by_wwpn(vha,
			    iocb->u.isp24.port_name, 1);

			if (vha->hw->flags.edif_enabled && sess &&
			    (!(sess->flags & FCF_FCSP_DEVICE) ||
			     !sess->edif.authok)) {
				ql_dbg(ql_dbg_disc, vha, 0xffff,
				       "%s %d %8phC Term PRLI due to unauthorize PRLI\n",
				       __func__, __LINE__, iocb->u.isp24.port_name);
				qlt_send_term_imm_notif(vha, iocb, 1);
				break;
			}

			if (sess && sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN]) {
				ql_dbg(ql_dbg_disc, vha, 0xffff,
				    "%s %d %8phC Term PRLI due to PLOGI ACK not completed\n",
				    __func__, __LINE__,
				    iocb->u.isp24.port_name);
				qlt_send_term_imm_notif(vha, iocb, 1);
				break;
			}

			res = qlt_handle_login(vha, iocb);
			break;
		}

		if (IS_SW_RESV_ADDR(port_id)) {
			res = 1;
			break;
		}

		wd3_lo = le16_to_cpu(iocb->u.isp24.u.prli.wd3_lo);

		if (wwn) {
			spin_lock_irqsave(&tgt->ha->tgt.sess_lock, flags);
			sess = qlt_find_sess_invalidate_other(vha, wwn, port_id,
				loop_id, &conflict_sess);
			spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock, flags);
		}

		if (conflict_sess) {
			switch (conflict_sess->disc_state) {
			case DSC_DELETED:
			case DSC_DELETE_PEND:
				break;
			default:
				ql_dbg(ql_dbg_tgt_mgt, vha, 0xf09b,
				    "PRLI with conflicting sess %p port %8phC\n",
				    conflict_sess, conflict_sess->port_name);
				conflict_sess->fw_login_state =
				    DSC_LS_PORT_UNAVAIL;
				qlt_send_term_imm_notif(vha, iocb, 1);
				res = 0;
				break;
			}
		}

		if (sess != NULL) {
			bool delete = false;
			int sec;

			if (vha->hw->flags.edif_enabled && sess &&
			    (!(sess->flags & FCF_FCSP_DEVICE) ||
			     !sess->edif.authok)) {
				ql_dbg(ql_dbg_disc, vha, 0xffff,
				       "%s %d %8phC Term PRLI due to unauthorize prli\n",
				       __func__, __LINE__, iocb->u.isp24.port_name);
				qlt_send_term_imm_notif(vha, iocb, 1);
				break;
			}

			spin_lock_irqsave(&tgt->ha->tgt.sess_lock, flags);
			switch (sess->fw_login_state) {
			case DSC_LS_PLOGI_PEND:
			case DSC_LS_PLOGI_COMP:
			case DSC_LS_PRLI_COMP:
				break;
			default:
				delete = true;
				break;
			}

			switch (sess->disc_state) {
			case DSC_UPD_FCPORT:
				spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock,
				    flags);

				sec = jiffies_to_msecs(jiffies -
				    sess->jiffies_at_registration)/1000;
				if (sess->sec_since_registration < sec && sec &&
				    !(sec % 5)) {
					sess->sec_since_registration = sec;
					ql_dbg(ql_dbg_disc, sess->vha, 0xffff,
					    "%s %8phC : Slow Rport registration(%d Sec)\n",
					    __func__, sess->port_name, sec);
				}
				qlt_send_term_imm_notif(vha, iocb, 1);
				return 0;

			case DSC_LOGIN_PEND:
			case DSC_GPDB:
			case DSC_LOGIN_COMPLETE:
			case DSC_ADISC:
				delete = false;
				break;
			default:
				break;
			}

			if (delete) {
				spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock,
				    flags);
				/*
				 * Impatient initiator sent PRLI before last
				 * PLOGI could finish. Will force him to re-try,
				 * while last one finishes.
				 */
				ql_log(ql_log_warn, sess->vha, 0xf095,
				    "sess %p PRLI received, before plogi ack.\n",
				    sess);
				qlt_send_term_imm_notif(vha, iocb, 1);
				res = 0;
				break;
			}

			/*
			 * This shouldn't happen under normal circumstances,
			 * since we have deleted the old session during PLOGI
			 */
			ql_dbg(ql_dbg_tgt_mgt, vha, 0xf096,
			    "PRLI (loop_id %#04x) for existing sess %p (loop_id %#04x)\n",
			    sess->loop_id, sess, iocb->u.isp24.nport_handle);

			sess->local = 0;
			sess->loop_id = loop_id;
			sess->d_id = port_id;
			sess->fw_login_state = DSC_LS_PRLI_PEND;

			if (wd3_lo & BIT_7)
				sess->conf_compl_supported = 1;

			if ((wd3_lo & BIT_4) == 0)
				sess->port_type = FCT_INITIATOR;
			else
				sess->port_type = FCT_TARGET;

			spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock, flags);
		}
		res = 1; /* send notify ack */

		/* Make session global (not used in fabric mode) */
		if (ha->current_topology != ISP_CFG_F) {
			if (sess) {
				ql_dbg(ql_dbg_disc, vha, 0x20fa,
				    "%s %d %8phC post nack\n",
				    __func__, __LINE__, sess->port_name);
				qla24xx_post_nack_work(vha, sess, iocb,
					SRB_NACK_PRLI);
				res = 0;
			} else {
				set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
				set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
				qla2xxx_wake_dpc(vha);
			}
		} else {
			if (sess) {
				ql_dbg(ql_dbg_disc, vha, 0x20fb,
				    "%s %d %8phC post nack\n",
				    __func__, __LINE__, sess->port_name);
				qla24xx_post_nack_work(vha, sess, iocb,
					SRB_NACK_PRLI);
				res = 0;
			}
		}
		break;

	case ELS_TPRLO:
		if (le16_to_cpu(iocb->u.isp24.flags) &
			NOTIFY24XX_FLAGS_GLOBAL_TPRLO) {
			loop_id = 0xFFFF;
			qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS);
			res = 1;
			break;
		}
		fallthrough;
	case ELS_LOGO:
	case ELS_PRLO:
		spin_lock_irqsave(&ha->tgt.sess_lock, flags);
		sess = qla2x00_find_fcport_by_loopid(vha, loop_id);
		spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);

		if (sess) {
			sess->login_gen++;
			sess->fw_login_state = DSC_LS_LOGO_PEND;
			sess->logo_ack_needed = 1;
			memcpy(sess->iocb, iocb, IOCB_SIZE);
		}

		res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS);

		ql_dbg(ql_dbg_disc, vha, 0x20fc,
		    "%s: logo %llx res %d sess %p ",
		    __func__, wwn, res, sess);
		if (res == 0) {
			/*
			 * cmd went upper layer, look for qlt_xmit_tm_rsp()
			 * for LOGO_ACK & sess delete
			 */
			BUG_ON(!sess);
			res = 0;
		} else {
			/* cmd did not go to upper layer. */
			if (sess) {
				qlt_schedule_sess_for_deletion(sess);
				res = 0;
			}
			/* else logo will be ack */
		}
		break;
	case ELS_PDISC:
	case ELS_ADISC:
	{
		struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;

		if (tgt->link_reinit_iocb_pending) {
			qlt_send_notify_ack(ha->base_qpair,
			    &tgt->link_reinit_iocb, 0, 0, 0, 0, 0, 0);
			tgt->link_reinit_iocb_pending = 0;
		}

		sess = qla2x00_find_fcport_by_wwpn(vha,
		    iocb->u.isp24.port_name, 1);
		if (sess) {
			ql_dbg(ql_dbg_disc, vha, 0x20fd,
				"sess %p lid %d|%d DS %d LS %d\n",
				sess, sess->loop_id, loop_id,
				sess->disc_state, sess->fw_login_state);
		}

		res = 1; /* send notify ack */
		break;
	}

	case ELS_FLOGI:	/* should never happen */
	default:
		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf061,
		    "qla_target(%d): Unsupported ELS command %x "
		    "received\n", vha->vp_idx, iocb->u.isp24.status_subcode);
		res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS);
		break;
	}

	ql_dbg(ql_dbg_disc, vha, 0xf026,
	    "qla_target(%d): Exit ELS opcode: 0x%02x res %d\n",
	    vha->vp_idx, iocb->u.isp24.status_subcode, res);

	return res;
}