static void qla24xx_handle_gnl_done_event()

in qla2xxx/qla_init.c [673:970]


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;
		default:
			break;
		}
	}
} /* gnl_event */