lpfc_nportdisc.c

来自「linux 内核源代码」· C语言 代码 · 共 2,073 行 · 第 1/5 页

C
2,073
字号
 /******************************************************************* * This file is part of the Emulex Linux Device Driver for         * * Fibre Channel Host Bus Adapters.                                * * Copyright (C) 2004-2007 Emulex.  All rights reserved.           * * EMULEX and SLI are trademarks of Emulex.                        * * www.emulex.com                                                  * * Portions Copyright (C) 2004-2005 Christoph Hellwig              * *                                                                 * * This program is free software; you can redistribute it and/or   * * modify it under the terms of version 2 of the GNU General       * * Public License as published by the Free Software Foundation.    * * This program is distributed in the hope that it will be useful. * * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND          * * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,  * * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE      * * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * * TO BE LEGALLY INVALID.  See the GNU General Public License for  * * more details, a copy of which can be found in the file COPYING  * * included with this package.                                     * *******************************************************************/#include <linux/blkdev.h>#include <linux/pci.h>#include <linux/interrupt.h>#include <scsi/scsi.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>#include <scsi/scsi_transport_fc.h>#include "lpfc_hw.h"#include "lpfc_sli.h"#include "lpfc_disc.h"#include "lpfc_scsi.h"#include "lpfc.h"#include "lpfc_logmsg.h"#include "lpfc_crtn.h"#include "lpfc_vport.h"#include "lpfc_debugfs.h"/* Called to verify a rcv'ed ADISC was intended for us. */static intlpfc_check_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,		 struct lpfc_name *nn, struct lpfc_name *pn){	/* Compare the ADISC rsp WWNN / WWPN matches our internal node	 * table entry for that node.	 */	if (memcmp(nn, &ndlp->nlp_nodename, sizeof (struct lpfc_name)))		return 0;	if (memcmp(pn, &ndlp->nlp_portname, sizeof (struct lpfc_name)))		return 0;	/* we match, return success */	return 1;}intlpfc_check_sparm(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,		 struct serv_parm * sp, uint32_t class){	volatile struct serv_parm *hsp = &vport->fc_sparam;	uint16_t hsp_value, ssp_value = 0;	/*	 * The receive data field size and buffer-to-buffer receive data field	 * size entries are 16 bits but are represented as two 8-bit fields in	 * the driver data structure to account for rsvd bits and other control	 * bits.  Reconstruct and compare the fields as a 16-bit values before	 * correcting the byte values.	 */	if (sp->cls1.classValid) {		hsp_value = (hsp->cls1.rcvDataSizeMsb << 8) |				hsp->cls1.rcvDataSizeLsb;		ssp_value = (sp->cls1.rcvDataSizeMsb << 8) |				sp->cls1.rcvDataSizeLsb;		if (!ssp_value)			goto bad_service_param;		if (ssp_value > hsp_value) {			sp->cls1.rcvDataSizeLsb = hsp->cls1.rcvDataSizeLsb;			sp->cls1.rcvDataSizeMsb = hsp->cls1.rcvDataSizeMsb;		}	} else if (class == CLASS1) {		goto bad_service_param;	}	if (sp->cls2.classValid) {		hsp_value = (hsp->cls2.rcvDataSizeMsb << 8) |				hsp->cls2.rcvDataSizeLsb;		ssp_value = (sp->cls2.rcvDataSizeMsb << 8) |				sp->cls2.rcvDataSizeLsb;		if (!ssp_value)			goto bad_service_param;		if (ssp_value > hsp_value) {			sp->cls2.rcvDataSizeLsb = hsp->cls2.rcvDataSizeLsb;			sp->cls2.rcvDataSizeMsb = hsp->cls2.rcvDataSizeMsb;		}	} else if (class == CLASS2) {		goto bad_service_param;	}	if (sp->cls3.classValid) {		hsp_value = (hsp->cls3.rcvDataSizeMsb << 8) |				hsp->cls3.rcvDataSizeLsb;		ssp_value = (sp->cls3.rcvDataSizeMsb << 8) |				sp->cls3.rcvDataSizeLsb;		if (!ssp_value)			goto bad_service_param;		if (ssp_value > hsp_value) {			sp->cls3.rcvDataSizeLsb = hsp->cls3.rcvDataSizeLsb;			sp->cls3.rcvDataSizeMsb = hsp->cls3.rcvDataSizeMsb;		}	} else if (class == CLASS3) {		goto bad_service_param;	}	/*	 * Preserve the upper four bits of the MSB from the PLOGI response.	 * These bits contain the Buffer-to-Buffer State Change Number	 * from the target and need to be passed to the FW.	 */	hsp_value = (hsp->cmn.bbRcvSizeMsb << 8) | hsp->cmn.bbRcvSizeLsb;	ssp_value = (sp->cmn.bbRcvSizeMsb << 8) | sp->cmn.bbRcvSizeLsb;	if (ssp_value > hsp_value) {		sp->cmn.bbRcvSizeLsb = hsp->cmn.bbRcvSizeLsb;		sp->cmn.bbRcvSizeMsb = (sp->cmn.bbRcvSizeMsb & 0xF0) |				       (hsp->cmn.bbRcvSizeMsb & 0x0F);	}	memcpy(&ndlp->nlp_nodename, &sp->nodeName, sizeof (struct lpfc_name));	memcpy(&ndlp->nlp_portname, &sp->portName, sizeof (struct lpfc_name));	return 1;bad_service_param:	lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,			 "0207 Device %x "			 "(%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x) sent "			 "invalid service parameters.  Ignoring device.\n",			 ndlp->nlp_DID,			 sp->nodeName.u.wwn[0], sp->nodeName.u.wwn[1],			 sp->nodeName.u.wwn[2], sp->nodeName.u.wwn[3],			 sp->nodeName.u.wwn[4], sp->nodeName.u.wwn[5],			 sp->nodeName.u.wwn[6], sp->nodeName.u.wwn[7]);	return 0;}static void *lpfc_check_elscmpl_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,			struct lpfc_iocbq *rspiocb){	struct lpfc_dmabuf *pcmd, *prsp;	uint32_t *lp;	void     *ptr = NULL;	IOCB_t   *irsp;	irsp = &rspiocb->iocb;	pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;	/* For lpfc_els_abort, context2 could be zero'ed to delay	 * freeing associated memory till after ABTS completes.	 */	if (pcmd) {		prsp =  list_get_first(&pcmd->list, struct lpfc_dmabuf,				       list);		if (prsp) {			lp = (uint32_t *) prsp->virt;			ptr = (void *)((uint8_t *)lp + sizeof(uint32_t));		}	} else {		/* Force ulpStatus error since we are returning NULL ptr */		if (!(irsp->ulpStatus)) {			irsp->ulpStatus = IOSTAT_LOCAL_REJECT;			irsp->un.ulpWord[4] = IOERR_SLI_ABORTED;		}		ptr = NULL;	}	return ptr;}/* * Free resources / clean up outstanding I/Os * associated with a LPFC_NODELIST entry. This * routine effectively results in a "software abort". */intlpfc_els_abort(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp){	LIST_HEAD(completions);	struct lpfc_sli  *psli = &phba->sli;	struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];	struct lpfc_iocbq *iocb, *next_iocb;	IOCB_t *cmd;	/* Abort outstanding I/O on NPort <nlp_DID> */	lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_DISCOVERY,			 "0205 Abort outstanding I/O on NPort x%x "			 "Data: x%x x%x x%x\n",			 ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,			 ndlp->nlp_rpi);	lpfc_fabric_abort_nport(ndlp);	/* First check the txq */	spin_lock_irq(&phba->hbalock);	list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {		/* Check to see if iocb matches the nport we are looking for */		if (lpfc_check_sli_ndlp(phba, pring, iocb, ndlp)) {			/* It matches, so deque and call compl with anp error */			list_move_tail(&iocb->list, &completions);			pring->txq_cnt--;		}	}	/* Next check the txcmplq */	list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {		/* Check to see if iocb matches the nport we are looking for */		if (lpfc_check_sli_ndlp(phba, pring, iocb, ndlp)) {			lpfc_sli_issue_abort_iotag(phba, pring, iocb);		}	}	spin_unlock_irq(&phba->hbalock);	while (!list_empty(&completions)) {		iocb = list_get_first(&completions, struct lpfc_iocbq, list);		cmd = &iocb->iocb;		list_del_init(&iocb->list);		if (!iocb->iocb_cmpl)			lpfc_sli_release_iocbq(phba, iocb);		else {			cmd->ulpStatus = IOSTAT_LOCAL_REJECT;			cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;			(iocb->iocb_cmpl) (phba, iocb, iocb);		}	}	/* If we are delaying issuing an ELS command, cancel it */	if (ndlp->nlp_flag & NLP_DELAY_TMO)		lpfc_cancel_retry_delay_tmo(phba->pport, ndlp);	return 0;}static intlpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,	       struct lpfc_iocbq *cmdiocb){	struct Scsi_Host   *shost = lpfc_shost_from_vport(vport);	struct lpfc_hba    *phba = vport->phba;	struct lpfc_dmabuf *pcmd;	uint32_t *lp;	IOCB_t *icmd;	struct serv_parm *sp;	LPFC_MBOXQ_t *mbox;	struct ls_rjt stat;	int rc;	memset(&stat, 0, sizeof (struct ls_rjt));	if (vport->port_state <= LPFC_FLOGI) {		/* Before responding to PLOGI, check for pt2pt mode.		 * If we are pt2pt, with an outstanding FLOGI, abort		 * the FLOGI and resend it first.		 */		if (vport->fc_flag & FC_PT2PT) {			 lpfc_els_abort_flogi(phba);		        if (!(vport->fc_flag & FC_PT2PT_PLOGI)) {				/* If the other side is supposed to initiate				 * the PLOGI anyway, just ACC it now and				 * move on with discovery.				 */				phba->fc_edtov = FF_DEF_EDTOV;				phba->fc_ratov = FF_DEF_RATOV;				/* Start discovery - this should just do				   CLEAR_LA */				lpfc_disc_start(vport);			} else				lpfc_initial_flogi(vport);		} else {			stat.un.b.lsRjtRsnCode = LSRJT_LOGICAL_BSY;			stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;			lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb,					    ndlp, NULL);			return 0;		}	}	pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;	lp = (uint32_t *) pcmd->virt;	sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t));	if ((lpfc_check_sparm(vport, ndlp, sp, CLASS3) == 0)) {		/* Reject this request because invalid parameters */		stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;		stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS;		lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp,			NULL);		return 0;	}	icmd = &cmdiocb->iocb;	/* PLOGI chkparm OK */	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,			 "0114 PLOGI chkparm OK Data: x%x x%x x%x x%x\n",			 ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_flag,			 ndlp->nlp_rpi);	if (vport->cfg_fcp_class == 2 && sp->cls2.classValid)		ndlp->nlp_fcp_info |= CLASS2;	else		ndlp->nlp_fcp_info |= CLASS3;	ndlp->nlp_class_sup = 0;	if (sp->cls1.classValid)		ndlp->nlp_class_sup |= FC_COS_CLASS1;	if (sp->cls2.classValid)		ndlp->nlp_class_sup |= FC_COS_CLASS2;	if (sp->cls3.classValid)		ndlp->nlp_class_sup |= FC_COS_CLASS3;	if (sp->cls4.classValid)		ndlp->nlp_class_sup |= FC_COS_CLASS4;	ndlp->nlp_maxframe =		((sp->cmn.bbRcvSizeMsb & 0x0F) << 8) | sp->cmn.bbRcvSizeLsb;	/* no need to reg_login if we are already in one of these states */	switch (ndlp->nlp_state) {	case  NLP_STE_NPR_NODE:		if (!(ndlp->nlp_flag & NLP_NPR_ADISC))			break;	case  NLP_STE_REG_LOGIN_ISSUE:	case  NLP_STE_PRLI_ISSUE:	case  NLP_STE_UNMAPPED_NODE:	case  NLP_STE_MAPPED_NODE:		lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL);		return 1;	}	if ((vport->fc_flag & FC_PT2PT) &&	    !(vport->fc_flag & FC_PT2PT_PLOGI)) {		/* rcv'ed PLOGI decides what our NPortId will be */		vport->fc_myDID = icmd->un.rcvels.parmRo;		mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);		if (mbox == NULL)			goto out;		lpfc_config_link(phba, mbox);		mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;		mbox->vport = vport;		rc = lpfc_sli_issue_mbox			(phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB));		if (rc == MBX_NOT_FINISHED) {			mempool_free(mbox, phba->mbox_mem_pool);			goto out;		}		lpfc_can_disctmo(vport);	}	mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);	if (!mbox)		goto out;	rc = lpfc_reg_login(phba, vport->vpi, icmd->un.rcvels.remoteID,			    (uint8_t *) sp, mbox, 0);	if (rc) {		mempool_free(mbox, phba->mbox_mem_pool);		goto out;	}	/* ACC PLOGI rsp command needs to execute first,	 * queue this mbox command to be processed later.	 */	mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login;	/*	 * mbox->context2 = lpfc_nlp_get(ndlp) deferred until mailbox	 * command issued in lpfc_cmpl_els_acc().	 */	mbox->vport = vport;	spin_lock_irq(shost->host_lock);	ndlp->nlp_flag |= (NLP_ACC_REGLOGIN | NLP_RCV_PLOGI);	spin_unlock_irq(shost->host_lock);	/*	 * If there is an outstanding PLOGI issued, abort it before	 * sending ACC rsp for received PLOGI. If pending plogi	 * is not canceled here, the plogi will be rejected by	 * remote port and will be retried. On a configuration with	 * single discovery thread, this will cause a huge delay in	 * discovery. Also this will cause multiple state machines	 * running in parallel for this node.	 */	if (ndlp->nlp_state == NLP_STE_PLOGI_ISSUE) {		/* software abort outstanding PLOGI */		lpfc_els_abort(phba, ndlp);	}	if ((vport->port_type == LPFC_NPIV_PORT &&	     vport->cfg_restrict_login)) {		/* In order to preserve RPIs, we want to cleanup		 * the default RPI the firmware created to rcv		 * this ELS request. The only way to do this is		 * to register, then unregister the RPI.		 */		spin_lock_irq(shost->host_lock);		ndlp->nlp_flag |= NLP_RM_DFLT_RPI;		spin_unlock_irq(shost->host_lock);		stat.un.b.lsRjtRsnCode = LSRJT_INVALID_CMD;		stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;		lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb,			ndlp, mbox);		return 1;	}	lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox);	return 1;out:	stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;	stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?