Commit b5c18c9d authored by Justin Tee's avatar Justin Tee Committed by Martin K. Petersen
Browse files

scsi: lpfc: Fix unsolicited FLOGI kref imbalance when in direct attached topology



In direct attached topology, certain target vendors that are quick to issue
FLOGI followed by a cable pull for more than dev_loss_tmo may result in a
kref imbalance for the remote port ndlp object.

Add an nlp_get when the defer_flogi_acc flag is set.  This is expected to
balance the nlp_put in the defer_flogi_acc clause in the
lpfc_issue_els_flogi() routine.  Because we need to retain the ndlp ptr,
reorganize all of the defer_flogi_acc information into one
lpfc_defer_flogi_acc struct.

Signed-off-by: default avatarJustin Tee <justin.tee@broadcom.com>
Link: https://lore.kernel.org/r/20240726231512.92867-6-justintee8345@gmail.com


Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 3976beb1
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -306,6 +306,14 @@ struct lpfc_stats {

struct lpfc_hba;

/* Data structure to keep withheld FLOGI_ACC information */
struct lpfc_defer_flogi_acc {
	bool flag;
	u16 rx_id;
	u16 ox_id;
	struct lpfc_nodelist *ndlp;

};

#define LPFC_VMID_TIMER   300	/* timer interval in seconds */

@@ -1430,9 +1438,7 @@ struct lpfc_hba {
	uint16_t vlan_id;
	struct list_head fcf_conn_rec_list;

	bool defer_flogi_acc_flag;
	uint16_t defer_flogi_acc_rx_id;
	uint16_t defer_flogi_acc_ox_id;
	struct lpfc_defer_flogi_acc defer_flogi_acc;

	spinlock_t ct_ev_lock; /* synchronize access to ct_ev_waiters */
	struct list_head ct_ev_waiters;
+28 −18
Original line number Diff line number Diff line
@@ -1392,7 +1392,7 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
	phba->link_flag &= ~LS_EXTERNAL_LOOPBACK;

	/* Check for a deferred FLOGI ACC condition */
	if (phba->defer_flogi_acc_flag) {
	if (phba->defer_flogi_acc.flag) {
		/* lookup ndlp for received FLOGI */
		ndlp = lpfc_findnode_did(vport, 0);
		if (!ndlp)
@@ -1406,34 +1406,38 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
		if (phba->sli_rev == LPFC_SLI_REV4) {
			bf_set(wqe_ctxt_tag,
			       &defer_flogi_acc.wqe.xmit_els_rsp.wqe_com,
			       phba->defer_flogi_acc_rx_id);
			       phba->defer_flogi_acc.rx_id);
			bf_set(wqe_rcvoxid,
			       &defer_flogi_acc.wqe.xmit_els_rsp.wqe_com,
			       phba->defer_flogi_acc_ox_id);
			       phba->defer_flogi_acc.ox_id);
		} else {
			icmd = &defer_flogi_acc.iocb;
			icmd->ulpContext = phba->defer_flogi_acc_rx_id;
			icmd->ulpContext = phba->defer_flogi_acc.rx_id;
			icmd->unsli3.rcvsli3.ox_id =
				phba->defer_flogi_acc_ox_id;
				phba->defer_flogi_acc.ox_id;
		}

		lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
				 "3354 Xmit deferred FLOGI ACC: rx_id: x%x,"
				 " ox_id: x%x, hba_flag x%lx\n",
				 phba->defer_flogi_acc_rx_id,
				 phba->defer_flogi_acc_ox_id, phba->hba_flag);
				 phba->defer_flogi_acc.rx_id,
				 phba->defer_flogi_acc.ox_id, phba->hba_flag);

		/* Send deferred FLOGI ACC */
		lpfc_els_rsp_acc(vport, ELS_CMD_FLOGI, &defer_flogi_acc,
				 ndlp, NULL);

		phba->defer_flogi_acc_flag = false;
		vport->fc_myDID = did;
		phba->defer_flogi_acc.flag = false;

		/* Decrement ndlp reference count to indicate the node can be
		 * released when other references are removed.
		/* Decrement the held ndlp that was incremented when the
		 * deferred flogi acc flag was set.
		 */
		lpfc_nlp_put(ndlp);
		if (phba->defer_flogi_acc.ndlp) {
			lpfc_nlp_put(phba->defer_flogi_acc.ndlp);
			phba->defer_flogi_acc.ndlp = NULL;
		}

		vport->fc_myDID = did;
	}

	return 0;
@@ -8456,9 +8460,9 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,

	/* Defer ACC response until AFTER we issue a FLOGI */
	if (!test_bit(HBA_FLOGI_ISSUED, &phba->hba_flag)) {
		phba->defer_flogi_acc_rx_id = bf_get(wqe_ctxt_tag,
		phba->defer_flogi_acc.rx_id = bf_get(wqe_ctxt_tag,
						     &wqe->xmit_els_rsp.wqe_com);
		phba->defer_flogi_acc_ox_id = bf_get(wqe_rcvoxid,
		phba->defer_flogi_acc.ox_id = bf_get(wqe_rcvoxid,
						     &wqe->xmit_els_rsp.wqe_com);

		vport->fc_myDID = did;
@@ -8466,11 +8470,17 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
		lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
				 "3344 Deferring FLOGI ACC: rx_id: x%x,"
				 " ox_id: x%x, hba_flag x%lx\n",
				 phba->defer_flogi_acc_rx_id,
				 phba->defer_flogi_acc_ox_id, phba->hba_flag);
				 phba->defer_flogi_acc.rx_id,
				 phba->defer_flogi_acc.ox_id, phba->hba_flag);

		phba->defer_flogi_acc_flag = true;
		phba->defer_flogi_acc.flag = true;

		/* This nlp_get is paired with nlp_puts that reset the
		 * defer_flogi_acc.flag back to false.  We need to retain
		 * a kref on the ndlp until the deferred FLOGI ACC is
		 * processed or cancelled.
		 */
		phba->defer_flogi_acc.ndlp = lpfc_nlp_get(ndlp);
		return 0;
	}

@@ -10506,7 +10516,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,

		lpfc_els_rcv_flogi(vport, elsiocb, ndlp);
		/* retain node if our response is deferred */
		if (phba->defer_flogi_acc_flag)
		if (phba->defer_flogi_acc.flag)
			break;
		if (newnode)
			lpfc_disc_state_machine(vport, ndlp, NULL,
+9 −2
Original line number Diff line number Diff line
@@ -1255,7 +1255,14 @@ lpfc_linkdown(struct lpfc_hba *phba)
	lpfc_scsi_dev_block(phba);
	offline = pci_channel_offline(phba->pcidev);

	phba->defer_flogi_acc_flag = false;
	/* Decrement the held ndlp if there is a deferred flogi acc */
	if (phba->defer_flogi_acc.flag) {
		if (phba->defer_flogi_acc.ndlp) {
			lpfc_nlp_put(phba->defer_flogi_acc.ndlp);
			phba->defer_flogi_acc.ndlp = NULL;
		}
	}
	phba->defer_flogi_acc.flag = false;

	/* Clear external loopback plug detected flag */
	phba->link_flag &= ~LS_EXTERNAL_LOOPBACK;
@@ -1377,7 +1384,7 @@ lpfc_linkup_port(struct lpfc_vport *vport)
		(vport != phba->pport))
		return;

	if (phba->defer_flogi_acc_flag) {
	if (phba->defer_flogi_acc.flag) {
		clear_bit(FC_ABORT_DISCOVERY, &vport->fc_flag);
		clear_bit(FC_RSCN_MODE, &vport->fc_flag);
		clear_bit(FC_NLP_MORE, &vport->fc_flag);