diff options
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_sli.c')
| -rw-r--r-- | drivers/scsi/lpfc/lpfc_sli.c | 413 | 
1 files changed, 349 insertions, 64 deletions
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 35e3b96d4e0..fe6660ca645 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -494,7 +494,7 @@ __lpfc_clear_active_sglq(struct lpfc_hba *phba, uint16_t xritag)   *   * Returns sglq ponter = success, NULL = Failure.   **/ -static struct lpfc_sglq * +struct lpfc_sglq *  __lpfc_get_active_sglq(struct lpfc_hba *phba, uint16_t xritag)  {  	uint16_t adj_xri; @@ -526,6 +526,7 @@ __lpfc_sli_get_sglq(struct lpfc_hba *phba)  		return NULL;  	adj_xri = sglq->sli4_xritag - phba->sli4_hba.max_cfg_param.xri_base;  	phba->sli4_hba.lpfc_sglq_active_list[adj_xri] = sglq; +	sglq->state = SGL_ALLOCATED;  	return sglq;  } @@ -580,15 +581,18 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)  	else  		sglq = __lpfc_clear_active_sglq(phba, iocbq->sli4_xritag);  	if (sglq)  { -		if (iocbq->iocb_flag & LPFC_EXCHANGE_BUSY) { +		if ((iocbq->iocb_flag & LPFC_EXCHANGE_BUSY) && +			(sglq->state != SGL_XRI_ABORTED)) {  			spin_lock_irqsave(&phba->sli4_hba.abts_sgl_list_lock,  					iflag);  			list_add(&sglq->list,  				&phba->sli4_hba.lpfc_abts_els_sgl_list);  			spin_unlock_irqrestore(  				&phba->sli4_hba.abts_sgl_list_lock, iflag); -		} else +		} else { +			sglq->state = SGL_FREED;  			list_add(&sglq->list, &phba->sli4_hba.lpfc_sgl_list); +		}  	} @@ -2258,41 +2262,56 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,  					spin_unlock_irqrestore(&phba->hbalock,  							       iflag);  				} -				if ((phba->sli_rev == LPFC_SLI_REV4) && -				    (saveq->iocb_flag & LPFC_EXCHANGE_BUSY)) { -					/* Set cmdiocb flag for the exchange -					 * busy so sgl (xri) will not be -					 * released until the abort xri is -					 * received from hba, clear the -					 * LPFC_DRIVER_ABORTED bit in case -					 * it was driver initiated abort. -					 */ -					spin_lock_irqsave(&phba->hbalock, -							  iflag); -					cmdiocbp->iocb_flag &= -						~LPFC_DRIVER_ABORTED; -					cmdiocbp->iocb_flag |= -						LPFC_EXCHANGE_BUSY; -					spin_unlock_irqrestore(&phba->hbalock, -							       iflag); -					cmdiocbp->iocb.ulpStatus = -						IOSTAT_LOCAL_REJECT; -					cmdiocbp->iocb.un.ulpWord[4] = -						IOERR_ABORT_REQUESTED; -					/* -					 * For SLI4, irsiocb contains NO_XRI -					 * in sli_xritag, it shall not affect -					 * releasing sgl (xri) process. -					 */ -					saveq->iocb.ulpStatus = -						IOSTAT_LOCAL_REJECT; -					saveq->iocb.un.ulpWord[4] = -						IOERR_SLI_ABORTED; -					spin_lock_irqsave(&phba->hbalock, -							  iflag); -					saveq->iocb_flag |= LPFC_DELAY_MEM_FREE; -					spin_unlock_irqrestore(&phba->hbalock, -							       iflag); +				if (phba->sli_rev == LPFC_SLI_REV4) { +					if (saveq->iocb_flag & +					    LPFC_EXCHANGE_BUSY) { +						/* Set cmdiocb flag for the +						 * exchange busy so sgl (xri) +						 * will not be released until +						 * the abort xri is received +						 * from hba. +						 */ +						spin_lock_irqsave( +							&phba->hbalock, iflag); +						cmdiocbp->iocb_flag |= +							LPFC_EXCHANGE_BUSY; +						spin_unlock_irqrestore( +							&phba->hbalock, iflag); +					} +					if (cmdiocbp->iocb_flag & +					    LPFC_DRIVER_ABORTED) { +						/* +						 * Clear LPFC_DRIVER_ABORTED +						 * bit in case it was driver +						 * initiated abort. +						 */ +						spin_lock_irqsave( +							&phba->hbalock, iflag); +						cmdiocbp->iocb_flag &= +							~LPFC_DRIVER_ABORTED; +						spin_unlock_irqrestore( +							&phba->hbalock, iflag); +						cmdiocbp->iocb.ulpStatus = +							IOSTAT_LOCAL_REJECT; +						cmdiocbp->iocb.un.ulpWord[4] = +							IOERR_ABORT_REQUESTED; +						/* +						 * For SLI4, irsiocb contains +						 * NO_XRI in sli_xritag, it +						 * shall not affect releasing +						 * sgl (xri) process. +						 */ +						saveq->iocb.ulpStatus = +							IOSTAT_LOCAL_REJECT; +						saveq->iocb.un.ulpWord[4] = +							IOERR_SLI_ABORTED; +						spin_lock_irqsave( +							&phba->hbalock, iflag); +						saveq->iocb_flag |= +							LPFC_DELAY_MEM_FREE; +						spin_unlock_irqrestore( +							&phba->hbalock, iflag); +					}  				}  			}  			(cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq); @@ -2515,14 +2534,16 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,  			cmdiocbq = lpfc_sli_iocbq_lookup(phba, pring,  							 &rspiocbq); -			if ((cmdiocbq) && (cmdiocbq->iocb_cmpl)) { -					spin_unlock_irqrestore(&phba->hbalock, -							       iflag); -					(cmdiocbq->iocb_cmpl)(phba, cmdiocbq, -							      &rspiocbq); -					spin_lock_irqsave(&phba->hbalock, -							  iflag); -				} +			if (unlikely(!cmdiocbq)) +				break; +			if (cmdiocbq->iocb_flag & LPFC_DRIVER_ABORTED) +				cmdiocbq->iocb_flag &= ~LPFC_DRIVER_ABORTED; +			if (cmdiocbq->iocb_cmpl) { +				spin_unlock_irqrestore(&phba->hbalock, iflag); +				(cmdiocbq->iocb_cmpl)(phba, cmdiocbq, +						      &rspiocbq); +				spin_lock_irqsave(&phba->hbalock, iflag); +			}  			break;  		case LPFC_UNSOL_IOCB:  			spin_unlock_irqrestore(&phba->hbalock, iflag); @@ -3091,6 +3112,12 @@ lpfc_sli_brdready_s3(struct lpfc_hba *phba, uint32_t mask)  	/* Check to see if any errors occurred during init */  	if ((status & HS_FFERM) || (i >= 20)) { +		lpfc_printf_log(phba, KERN_ERR, LOG_INIT, +				"2751 Adapter failed to restart, " +				"status reg x%x, FW Data: A8 x%x AC x%x\n", +				status, +				readl(phba->MBslimaddr + 0xa8), +				readl(phba->MBslimaddr + 0xac));  		phba->link_state = LPFC_HBA_ERROR;  		retval = 1;  	} @@ -3278,6 +3305,9 @@ lpfc_sli_brdkill(struct lpfc_hba *phba)  	if (retval != MBX_SUCCESS) {  		if (retval != MBX_BUSY)  			mempool_free(pmb, phba->mbox_mem_pool); +		lpfc_printf_log(phba, KERN_ERR, LOG_SLI, +				"2752 KILL_BOARD command failed retval %d\n", +				retval);  		spin_lock_irq(&phba->hbalock);  		phba->link_flag &= ~LS_IGNORE_ERATT;  		spin_unlock_irq(&phba->hbalock); @@ -4035,7 +4065,7 @@ lpfc_sli_hba_setup(struct lpfc_hba *phba)  lpfc_sli_hba_setup_error:  	phba->link_state = LPFC_HBA_ERROR; -	lpfc_printf_log(phba, KERN_INFO, LOG_INIT, +	lpfc_printf_log(phba, KERN_ERR, LOG_INIT,  			"0445 Firmware initialization failed\n");  	return rc;  } @@ -4388,7 +4418,13 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)  	spin_unlock_irq(&phba->hbalock);  	/* Read the port's service parameters. */ -	lpfc_read_sparam(phba, mboxq, vport->vpi); +	rc = lpfc_read_sparam(phba, mboxq, vport->vpi); +	if (rc) { +		phba->link_state = LPFC_HBA_ERROR; +		rc = -ENOMEM; +		goto out_free_vpd; +	} +  	mboxq->vport = vport;  	rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);  	mp = (struct lpfc_dmabuf *) mboxq->context1; @@ -4483,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)); @@ -7436,6 +7476,7 @@ lpfc_sli_wake_iocb_wait(struct lpfc_hba *phba,  {  	wait_queue_head_t *pdone_q;  	unsigned long iflags; +	struct lpfc_scsi_buf *lpfc_cmd;  	spin_lock_irqsave(&phba->hbalock, iflags);  	cmdiocbq->iocb_flag |= LPFC_IO_WAKE; @@ -7443,6 +7484,14 @@ lpfc_sli_wake_iocb_wait(struct lpfc_hba *phba,  		memcpy(&((struct lpfc_iocbq *)cmdiocbq->context2)->iocb,  		       &rspiocbq->iocb, sizeof(IOCB_t)); +	/* Set the exchange busy flag for task management commands */ +	if ((cmdiocbq->iocb_flag & LPFC_IO_FCP) && +		!(cmdiocbq->iocb_flag & LPFC_IO_LIBDFC)) { +		lpfc_cmd = container_of(cmdiocbq, struct lpfc_scsi_buf, +			cur_iocbq); +		lpfc_cmd->exch_busy = rspiocbq->iocb_flag & LPFC_EXCHANGE_BUSY; +	} +  	pdone_q = cmdiocbq->context_un.wait_queue;  	if (pdone_q)  		wake_up(pdone_q); @@ -9061,6 +9110,12 @@ lpfc_sli4_fp_handle_fcp_wcqe(struct lpfc_hba *phba,  	/* Fake the irspiocb and copy necessary response information */  	lpfc_sli4_iocb_param_transfer(phba, &irspiocbq, cmdiocbq, wcqe); +	if (cmdiocbq->iocb_flag & LPFC_DRIVER_ABORTED) { +		spin_lock_irqsave(&phba->hbalock, iflags); +		cmdiocbq->iocb_flag &= ~LPFC_DRIVER_ABORTED; +		spin_unlock_irqrestore(&phba->hbalock, iflags); +	} +  	/* Pass the cmd_iocb and the rsp state to the upper layer */  	(cmdiocbq->iocb_cmpl)(phba, cmdiocbq, &irspiocbq);  } @@ -11941,15 +11996,19 @@ lpfc_sli4_build_dflt_fcf_record(struct lpfc_hba *phba,  }  /** - * lpfc_sli4_read_fcf_record - Read the driver's default FCF Record. + * lpfc_sli4_fcf_scan_read_fcf_rec - Read hba fcf record for fcf scan.   * @phba: pointer to lpfc hba data structure.   * @fcf_index: FCF table entry offset.   * - * This routine is invoked to read up to @fcf_num of FCF record from the - * device starting with the given @fcf_index. + * This routine is invoked to scan the entire FCF table by reading FCF + * record and processing it one at a time starting from the @fcf_index + * for initial FCF discovery or fast FCF failover rediscovery. + * + * Return 0 if the mailbox command is submitted sucessfully, none 0 + * otherwise.   **/  int -lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index) +lpfc_sli4_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, uint16_t fcf_index)  {  	int rc = 0, error;  	LPFC_MBOXQ_t *mboxq; @@ -11961,17 +12020,17 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)  				"2000 Failed to allocate mbox for "  				"READ_FCF cmd\n");  		error = -ENOMEM; -		goto fail_fcfscan; +		goto fail_fcf_scan;  	}  	/* Construct the read FCF record mailbox command */ -	rc = lpfc_sli4_mbx_read_fcf_record(phba, mboxq, fcf_index); +	rc = lpfc_sli4_mbx_read_fcf_rec(phba, mboxq, fcf_index);  	if (rc) {  		error = -EINVAL; -		goto fail_fcfscan; +		goto fail_fcf_scan;  	}  	/* Issue the mailbox command asynchronously */  	mboxq->vport = phba->pport; -	mboxq->mbox_cmpl = lpfc_mbx_cmpl_read_fcf_record; +	mboxq->mbox_cmpl = lpfc_mbx_cmpl_fcf_scan_read_fcf_rec;  	rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);  	if (rc == MBX_NOT_FINISHED)  		error = -EIO; @@ -11979,9 +12038,13 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)  		spin_lock_irq(&phba->hbalock);  		phba->hba_flag |= FCF_DISC_INPROGRESS;  		spin_unlock_irq(&phba->hbalock); +		/* Reset FCF round robin index bmask for new scan */ +		if (fcf_index == LPFC_FCOE_FCF_GET_FIRST) +			memset(phba->fcf.fcf_rr_bmask, 0, +			       sizeof(*phba->fcf.fcf_rr_bmask));  		error = 0;  	} -fail_fcfscan: +fail_fcf_scan:  	if (error) {  		if (mboxq)  			lpfc_sli4_mbox_cmd_free(phba, mboxq); @@ -11994,6 +12057,181 @@ fail_fcfscan:  }  /** + * lpfc_sli4_fcf_rr_read_fcf_rec - Read hba fcf record for round robin fcf. + * @phba: pointer to lpfc hba data structure. + * @fcf_index: FCF table entry offset. + * + * This routine is invoked to read an FCF record indicated by @fcf_index + * and to use it for FLOGI round robin FCF failover. + * + * Return 0 if the mailbox command is submitted sucessfully, none 0 + * otherwise. + **/ +int +lpfc_sli4_fcf_rr_read_fcf_rec(struct lpfc_hba *phba, uint16_t fcf_index) +{ +	int rc = 0, error; +	LPFC_MBOXQ_t *mboxq; + +	mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); +	if (!mboxq) { +		lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_INIT, +				"2763 Failed to allocate mbox for " +				"READ_FCF cmd\n"); +		error = -ENOMEM; +		goto fail_fcf_read; +	} +	/* Construct the read FCF record mailbox command */ +	rc = lpfc_sli4_mbx_read_fcf_rec(phba, mboxq, fcf_index); +	if (rc) { +		error = -EINVAL; +		goto fail_fcf_read; +	} +	/* Issue the mailbox command asynchronously */ +	mboxq->vport = phba->pport; +	mboxq->mbox_cmpl = lpfc_mbx_cmpl_fcf_rr_read_fcf_rec; +	rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT); +	if (rc == MBX_NOT_FINISHED) +		error = -EIO; +	else +		error = 0; + +fail_fcf_read: +	if (error && mboxq) +		lpfc_sli4_mbox_cmd_free(phba, mboxq); +	return error; +} + +/** + * lpfc_sli4_read_fcf_rec - Read hba fcf record for update eligible fcf bmask. + * @phba: pointer to lpfc hba data structure. + * @fcf_index: FCF table entry offset. + * + * This routine is invoked to read an FCF record indicated by @fcf_index to + * determine whether it's eligible for FLOGI round robin failover list. + * + * Return 0 if the mailbox command is submitted sucessfully, none 0 + * otherwise. + **/ +int +lpfc_sli4_read_fcf_rec(struct lpfc_hba *phba, uint16_t fcf_index) +{ +	int rc = 0, error; +	LPFC_MBOXQ_t *mboxq; + +	mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); +	if (!mboxq) { +		lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_INIT, +				"2758 Failed to allocate mbox for " +				"READ_FCF cmd\n"); +				error = -ENOMEM; +				goto fail_fcf_read; +	} +	/* Construct the read FCF record mailbox command */ +	rc = lpfc_sli4_mbx_read_fcf_rec(phba, mboxq, fcf_index); +	if (rc) { +		error = -EINVAL; +		goto fail_fcf_read; +	} +	/* Issue the mailbox command asynchronously */ +	mboxq->vport = phba->pport; +	mboxq->mbox_cmpl = lpfc_mbx_cmpl_read_fcf_rec; +	rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT); +	if (rc == MBX_NOT_FINISHED) +		error = -EIO; +	else +		error = 0; + +fail_fcf_read: +	if (error && mboxq) +		lpfc_sli4_mbox_cmd_free(phba, mboxq); +	return error; +} + +/** + * lpfc_sli4_fcf_rr_next_index_get - Get next eligible fcf record index + * @phba: pointer to lpfc hba data structure. + * + * This routine is to get the next eligible FCF record index in a round + * robin fashion. If the next eligible FCF record index equals to the + * initial round robin FCF record index, LPFC_FCOE_FCF_NEXT_NONE (0xFFFF) + * shall be returned, otherwise, the next eligible FCF record's index + * shall be returned. + **/ +uint16_t +lpfc_sli4_fcf_rr_next_index_get(struct lpfc_hba *phba) +{ +	uint16_t next_fcf_index; + +	/* Search from the currently registered FCF index */ +	next_fcf_index = find_next_bit(phba->fcf.fcf_rr_bmask, +				       LPFC_SLI4_FCF_TBL_INDX_MAX, +				       phba->fcf.current_rec.fcf_indx); +	/* Wrap around condition on phba->fcf.fcf_rr_bmask */ +	if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) +		next_fcf_index = find_next_bit(phba->fcf.fcf_rr_bmask, +					       LPFC_SLI4_FCF_TBL_INDX_MAX, 0); +	/* Round robin failover stop condition */ +	if (next_fcf_index == phba->fcf.fcf_rr_init_indx) +		return LPFC_FCOE_FCF_NEXT_NONE; + +	return next_fcf_index; +} + +/** + * lpfc_sli4_fcf_rr_index_set - Set bmask with eligible fcf record index + * @phba: pointer to lpfc hba data structure. + * + * This routine sets the FCF record index in to the eligible bmask for + * round robin failover search. It checks to make sure that the index + * does not go beyond the range of the driver allocated bmask dimension + * before setting the bit. + * + * Returns 0 if the index bit successfully set, otherwise, it returns + * -EINVAL. + **/ +int +lpfc_sli4_fcf_rr_index_set(struct lpfc_hba *phba, uint16_t fcf_index) +{ +	if (fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) { +		lpfc_printf_log(phba, KERN_ERR, LOG_FIP, +				"2610 HBA FCF index reached driver's " +				"book keeping dimension: fcf_index:%d, " +				"driver_bmask_max:%d\n", +				fcf_index, LPFC_SLI4_FCF_TBL_INDX_MAX); +		return -EINVAL; +	} +	/* Set the eligible FCF record index bmask */ +	set_bit(fcf_index, phba->fcf.fcf_rr_bmask); + +	return 0; +} + +/** + * lpfc_sli4_fcf_rr_index_set - Clear bmask from eligible fcf record index + * @phba: pointer to lpfc hba data structure. + * + * This routine clears the FCF record index from the eligible bmask for + * round robin failover search. It checks to make sure that the index + * does not go beyond the range of the driver allocated bmask dimension + * before clearing the bit. + **/ +void +lpfc_sli4_fcf_rr_index_clear(struct lpfc_hba *phba, uint16_t fcf_index) +{ +	if (fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) { +		lpfc_printf_log(phba, KERN_ERR, LOG_FIP, +				"2762 HBA FCF index goes beyond driver's " +				"book keeping dimension: fcf_index:%d, " +				"driver_bmask_max:%d\n", +				fcf_index, LPFC_SLI4_FCF_TBL_INDX_MAX); +		return; +	} +	/* Clear the eligible FCF record index bmask */ +	clear_bit(fcf_index, phba->fcf.fcf_rr_bmask); +} + +/**   * lpfc_mbx_cmpl_redisc_fcf_table - completion routine for rediscover FCF table   * @phba: pointer to lpfc hba data structure.   * @@ -12014,21 +12252,40 @@ lpfc_mbx_cmpl_redisc_fcf_table(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)  	shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,  			     &redisc_fcf->header.cfg_shdr.response);  	if (shdr_status || shdr_add_status) { -		lpfc_printf_log(phba, KERN_ERR, LOG_SLI, +		lpfc_printf_log(phba, KERN_ERR, LOG_FIP,  				"2746 Requesting for FCF rediscovery failed "  				"status x%x add_status x%x\n",  				shdr_status, shdr_add_status); -		/* -		 * Request failed, last resort to re-try current -		 * registered FCF entry -		 */ -		lpfc_retry_pport_discovery(phba); -	} else +		if (phba->fcf.fcf_flag & FCF_ACVL_DISC) { +			spin_lock_irq(&phba->hbalock); +			phba->fcf.fcf_flag &= ~FCF_ACVL_DISC; +			spin_unlock_irq(&phba->hbalock); +			/* +			 * 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_DISC; +			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 { +		lpfc_printf_log(phba, KERN_INFO, LOG_FIP, +				"2775 Start FCF rediscovery quiescent period " +				"wait timer before scaning FCF table\n");  		/*  		 * Start FCF rediscovery wait timer for pending FCF  		 * before rescan FCF record table.  		 */  		lpfc_fcf_redisc_wait_start_timer(phba); +	}  	mempool_free(mbox, phba->mbox_mem_pool);  } @@ -12047,6 +12304,9 @@ lpfc_sli4_redisc_fcf_table(struct lpfc_hba *phba)  	struct lpfc_mbx_redisc_fcf_tbl *redisc_fcf;  	int rc, length; +	/* Cancel retry delay timers to all vports before FCF rediscover */ +	lpfc_cancel_all_vport_retry_delay_timer(phba); +  	mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);  	if (!mbox) {  		lpfc_printf_log(phba, KERN_ERR, LOG_SLI, @@ -12078,6 +12338,31 @@ lpfc_sli4_redisc_fcf_table(struct lpfc_hba *phba)  }  /** + * 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.   *  |