Commit fc2b989b authored by James Smart's avatar James Smart Committed by James Bottomley
Browse files

[SCSI] lpfc 8.3.10: Fix Discovery issues



- Prevent Vport discovery after reg_new_vport completes when physical
  logged in using FDISC.
- Remove fast FCF failover fabric name matching. Allow failover to FCFs
  connected to different fabrics.
- Added fast FCF failover in response to FCF DEAD event on current
  FCF record.

Signed-off-by: default avatarJames Smart <james.smart@emulex.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@suse.de>
parent e2aed29f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -221,6 +221,7 @@ void lpfc_unregister_fcf_rescan(struct lpfc_hba *);
void lpfc_unregister_unused_fcf(struct lpfc_hba *);
int lpfc_sli4_redisc_fcf_table(struct lpfc_hba *);
void lpfc_fcf_redisc_wait_start_timer(struct lpfc_hba *);
void lpfc_sli4_fcf_dead_failthrough(struct lpfc_hba *);

int lpfc_mem_alloc(struct lpfc_hba *, int align);
void lpfc_mem_free(struct lpfc_hba *);
+6 −1
Original line number Diff line number Diff line
@@ -6004,6 +6004,11 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
			if (phba->sli_rev < LPFC_SLI_REV4)
				lpfc_issue_fabric_reglogin(vport);
			else {
				/*
				 * If the physical port is instantiated using
				 * FDISC, do not start vport discovery.
				 */
				if (vport->port_state != LPFC_FDISC)
					lpfc_start_fdiscs(phba);
				lpfc_do_scr_ns_plogi(phba, vport);
			}
+14 −13
Original line number Diff line number Diff line
@@ -1504,7 +1504,9 @@ lpfc_check_pending_fcoe_event(struct lpfc_hba *phba, uint8_t unreg_fcf)
		 */
		spin_lock_irq(&phba->hbalock);
		phba->hba_flag &= ~FCF_DISC_INPROGRESS;
		phba->fcf.fcf_flag &= ~FCF_REDISC_FOV;
		phba->fcf.fcf_flag &= ~(FCF_REDISC_FOV |
					FCF_DEAD_FOVER |
					FCF_CVL_FOVER);
		spin_unlock_irq(&phba->hbalock);
	}

@@ -1649,7 +1651,9 @@ lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
				__lpfc_sli4_stop_fcf_redisc_wait_timer(phba);
			else if (phba->fcf.fcf_flag & FCF_REDISC_FOV)
				/* If in fast failover, mark it's completed */
				phba->fcf.fcf_flag &= ~FCF_REDISC_FOV;
				phba->fcf.fcf_flag &= ~(FCF_REDISC_FOV |
							FCF_DEAD_FOVER |
							FCF_CVL_FOVER);
			spin_unlock_irqrestore(&phba->hbalock, iflags);
			goto out;
		}
@@ -1669,14 +1673,9 @@ lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
	 * Update on failover FCF record only if it's in FCF fast-failover
	 * period; otherwise, update on current FCF record.
	 */
	if (phba->fcf.fcf_flag & FCF_REDISC_FOV) {
		/* Fast FCF failover only to the same fabric name */
		if (lpfc_fab_name_match(phba->fcf.current_rec.fabric_name,
					new_fcf_record))
	if (phba->fcf.fcf_flag & FCF_REDISC_FOV)
		fcf_rec = &phba->fcf.failover_rec;
	else
			goto read_next_fcf;
	} else
		fcf_rec = &phba->fcf.current_rec;

	if (phba->fcf.fcf_flag & FCF_AVAILABLE) {
@@ -1705,8 +1704,7 @@ lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
		 * If the new hba FCF record has lower priority value
		 * than the driver FCF record, use the new record.
		 */
		if (lpfc_fab_name_match(fcf_rec->fabric_name, new_fcf_record) &&
		    (new_fcf_record->fip_priority < fcf_rec->priority)) {
		if (new_fcf_record->fip_priority < fcf_rec->priority) {
			/* Choose this FCF record */
			__lpfc_update_fcf_record(phba, fcf_rec, new_fcf_record,
					addr_mode, vlan_id, 0);
@@ -1762,7 +1760,9 @@ lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
			       sizeof(struct lpfc_fcf_rec));
			/* mark the FCF fast failover completed */
			spin_lock_irqsave(&phba->hbalock, iflags);
			phba->fcf.fcf_flag &= ~FCF_REDISC_FOV;
			phba->fcf.fcf_flag &= ~(FCF_REDISC_FOV |
						FCF_DEAD_FOVER |
						FCF_CVL_FOVER);
			spin_unlock_irqrestore(&phba->hbalock, iflags);
			/* Register to the new FCF record */
			lpfc_register_fcf(phba);
@@ -4760,6 +4760,7 @@ lpfc_unregister_fcf_rescan(struct lpfc_hba *phba)
		return;
	/* Reset HBA FCF states after successful unregister FCF */
	phba->fcf.fcf_flag = 0;
	phba->fcf.current_rec.flag = 0;

	/*
	 * If driver is not unloading, check if there is any other
+111 −29
Original line number Diff line number Diff line
@@ -2199,8 +2199,10 @@ lpfc_stop_vport_timers(struct lpfc_vport *vport)
void
__lpfc_sli4_stop_fcf_redisc_wait_timer(struct lpfc_hba *phba)
{
	/* Clear pending FCF rediscovery wait timer */
	phba->fcf.fcf_flag &= ~FCF_REDISC_PEND;
	/* Clear pending FCF rediscovery wait and failover in progress flags */
	phba->fcf.fcf_flag &= ~(FCF_REDISC_PEND |
				FCF_DEAD_FOVER  |
				FCF_CVL_FOVER);
	/* Now, try to stop the timer */
	del_timer(&phba->fcf.redisc_wait);
}
@@ -3211,6 +3213,68 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba,
	mempool_free(pmb, phba->mbox_mem_pool);
}

/**
 * lpfc_sli4_perform_vport_cvl - Perform clear virtual link on a vport
 * @vport: pointer to vport data structure.
 *
 * This routine is to perform Clear Virtual Link (CVL) on a vport in
 * response to a CVL event.
 *
 * Return the pointer to the ndlp with the vport if successful, otherwise
 * return NULL.
 **/
static struct lpfc_nodelist *
lpfc_sli4_perform_vport_cvl(struct lpfc_vport *vport)
{
	struct lpfc_nodelist *ndlp;
	struct Scsi_Host *shost;
	struct lpfc_hba *phba;

	if (!vport)
		return NULL;
	ndlp = lpfc_findnode_did(vport, Fabric_DID);
	if (!ndlp)
		return NULL;
	phba = vport->phba;
	if (!phba)
		return NULL;
	if (phba->pport->port_state <= LPFC_FLOGI)
		return NULL;
	/* If virtual link is not yet instantiated ignore CVL */
	if (vport->port_state <= LPFC_FDISC)
		return NULL;
	shost = lpfc_shost_from_vport(vport);
	if (!shost)
		return NULL;
	lpfc_linkdown_port(vport);
	lpfc_cleanup_pending_mbox(vport);
	spin_lock_irq(shost->host_lock);
	vport->fc_flag |= FC_VPORT_CVL_RCVD;
	spin_unlock_irq(shost->host_lock);

	return ndlp;
}

/**
 * lpfc_sli4_perform_all_vport_cvl - Perform clear virtual link on all vports
 * @vport: pointer to lpfc hba data structure.
 *
 * This routine is to perform Clear Virtual Link (CVL) on all vports in
 * response to a FCF dead event.
 **/
static void
lpfc_sli4_perform_all_vport_cvl(struct lpfc_hba *phba)
{
	struct lpfc_vport **vports;
	int i;

	vports = lpfc_create_vport_work_array(phba);
	if (vports)
		for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++)
			lpfc_sli4_perform_vport_cvl(vports[i]);
	lpfc_destroy_vport_work_array(phba, vports);
}

/**
 * lpfc_sli4_async_fcoe_evt - Process the asynchronous fcoe event
 * @phba: pointer to lpfc hba data structure.
@@ -3227,7 +3291,6 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
	struct lpfc_vport *vport;
	struct lpfc_nodelist *ndlp;
	struct Scsi_Host  *shost;
	uint32_t link_state;
	int active_vlink_present;
	struct lpfc_vport **vports;
	int i;
@@ -3284,16 +3347,35 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
		/* If the event is not for currently used fcf do nothing */
		if (phba->fcf.current_rec.fcf_indx != acqe_fcoe->index)
			break;
		/* We request port to rediscover the entire FCF table for
		 * a fast recovery from case that the current FCF record
		 * is no longer valid if the last CVL event hasn't already
		 * triggered process.
		 */
		spin_lock_irq(&phba->hbalock);
		if (phba->fcf.fcf_flag & FCF_CVL_FOVER) {
			spin_unlock_irq(&phba->hbalock);
			break;
		}
		/* Mark the fast failover process in progress */
		phba->fcf.fcf_flag |= FCF_DEAD_FOVER;
		spin_unlock_irq(&phba->hbalock);
		rc = lpfc_sli4_redisc_fcf_table(phba);
		if (rc) {
			spin_lock_irq(&phba->hbalock);
			phba->fcf.fcf_flag &= ~FCF_DEAD_FOVER;
			spin_unlock_irq(&phba->hbalock);
			/*
		 * Currently, driver support only one FCF - so treat this as
		 * a link down, but save the link state because we don't want
		 * it to be changed to Link Down unless it is already down.
			 * Last resort will fail over by treating this
			 * as a link down to FCF registration.
			 */
		link_state = phba->link_state;
		lpfc_linkdown(phba);
		phba->link_state = link_state;
		/* Unregister FCF if no devices connected to it */
		lpfc_unregister_unused_fcf(phba);
			lpfc_sli4_fcf_dead_failthrough(phba);
		} else
			/* Handling fast FCF failover to a DEAD FCF event
			 * is considered equalivant to receiving CVL to all
			 * vports.
			 */
			lpfc_sli4_perform_all_vport_cvl(phba);
		break;
	case LPFC_FCOE_EVENT_TYPE_CVL:
		lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
@@ -3301,23 +3383,9 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
			" tag 0x%x\n", acqe_fcoe->index, acqe_fcoe->event_tag);
		vport = lpfc_find_vport_by_vpid(phba,
				acqe_fcoe->index - phba->vpi_base);
		if (!vport)
			break;
		ndlp = lpfc_findnode_did(vport, Fabric_DID);
		ndlp = lpfc_sli4_perform_vport_cvl(vport);
		if (!ndlp)
			break;
		shost = lpfc_shost_from_vport(vport);
		if (phba->pport->port_state <= LPFC_FLOGI)
			break;
		/* If virtual link is not yet instantiated ignore CVL */
		if (vport->port_state <= LPFC_FDISC)
			break;

		lpfc_linkdown_port(vport);
		lpfc_cleanup_pending_mbox(vport);
		spin_lock_irq(shost->host_lock);
		vport->fc_flag |= FC_VPORT_CVL_RCVD;
		spin_unlock_irq(shost->host_lock);
		active_vlink_present = 0;

		vports = lpfc_create_vport_work_array(phba);
@@ -3340,6 +3408,7 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
			 * re-instantiate the Vlink using FDISC.
			 */
			mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);
			shost = lpfc_shost_from_vport(vport);
			spin_lock_irq(shost->host_lock);
			ndlp->nlp_flag |= NLP_DELAY_TMO;
			spin_unlock_irq(shost->host_lock);
@@ -3350,16 +3419,29 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
			 * Otherwise, we request port to rediscover
			 * the entire FCF table for a fast recovery
			 * from possible case that the current FCF
			 * is no longer valid.
			 * is no longer valid if the FCF_DEAD event
			 * hasn't already triggered process.
			 */
			spin_lock_irq(&phba->hbalock);
			if (phba->fcf.fcf_flag & FCF_DEAD_FOVER) {
				spin_unlock_irq(&phba->hbalock);
				break;
			}
			/* Mark the fast failover process in progress */
			phba->fcf.fcf_flag |= FCF_CVL_FOVER;
			spin_unlock_irq(&phba->hbalock);
			rc = lpfc_sli4_redisc_fcf_table(phba);
			if (rc)
			if (rc) {
				spin_lock_irq(&phba->hbalock);
				phba->fcf.fcf_flag &= ~FCF_CVL_FOVER;
				spin_unlock_irq(&phba->hbalock);
				/*
				 * Last resort will be re-try on the
				 * the current registered FCF entry.
				 */
				lpfc_retry_pport_discovery(phba);
			}
		}
		break;
	default:
		lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+49 −5
Original line number Diff line number Diff line
@@ -4519,6 +4519,10 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
	/* Post receive buffers to the device */
	lpfc_sli4_rb_setup(phba);

	/* Reset HBA FCF states after HBA reset */
	phba->fcf.fcf_flag = 0;
	phba->fcf.current_rec.flag = 0;

	/* Start the ELS watchdog timer */
	mod_timer(&vport->els_tmofunc,
		  jiffies + HZ * (phba->fc_ratov * 2));
@@ -12069,11 +12073,26 @@ lpfc_mbx_cmpl_redisc_fcf_table(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
				"2746 Requesting for FCF rediscovery failed "
				"status x%x add_status x%x\n",
				shdr_status, shdr_add_status);
		if (phba->fcf.fcf_flag & FCF_CVL_FOVER) {
			spin_lock_irq(&phba->hbalock);
			phba->fcf.fcf_flag &= ~FCF_CVL_FOVER;
			spin_unlock_irq(&phba->hbalock);
			/*
		 * Request failed, last resort to re-try current
		 * registered FCF entry
			 * CVL event triggered FCF rediscover request failed,
			 * last resort to re-try current registered FCF entry.
			 */
			lpfc_retry_pport_discovery(phba);
		} else {
			spin_lock_irq(&phba->hbalock);
			phba->fcf.fcf_flag &= ~FCF_DEAD_FOVER;
			spin_unlock_irq(&phba->hbalock);
			/*
			 * DEAD FCF event triggered FCF rediscover request
			 * failed, last resort to fail over as a link down
			 * to FCF registration.
			 */
			lpfc_sli4_fcf_dead_failthrough(phba);
		}
	} else
		/*
		 * Start FCF rediscovery wait timer for pending FCF
@@ -12128,6 +12147,31 @@ lpfc_sli4_redisc_fcf_table(struct lpfc_hba *phba)
	return 0;
}

/**
 * lpfc_sli4_fcf_dead_failthrough - Failthrough routine to fcf dead event
 * @phba: pointer to lpfc hba data structure.
 *
 * This function is the failover routine as a last resort to the FCF DEAD
 * event when driver failed to perform fast FCF failover.
 **/
void
lpfc_sli4_fcf_dead_failthrough(struct lpfc_hba *phba)
{
	uint32_t link_state;

	/*
	 * Last resort as FCF DEAD event failover will treat this as
	 * a link down, but save the link state because we don't want
	 * it to be changed to Link Down unless it is already down.
	 */
	link_state = phba->link_state;
	lpfc_linkdown(phba);
	phba->link_state = link_state;

	/* Unregister FCF if no devices connected to it */
	lpfc_unregister_unused_fcf(phba);
}

/**
 * lpfc_sli_read_link_ste - Read region 23 to decide if link is disabled.
 * @phba: pointer to lpfc hba data structure.
Loading