⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lpfc_nportdisc.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 4 页
字号:
/******************************************************************* * This file is part of the Emulex Linux Device Driver for         * * Fibre Channel Host Bus Adapters.                                * * Copyright (C) 2004-2005 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"/* Called to verify a rcv'ed ADISC was intended for us. */static intlpfc_check_adisc(struct lpfc_hba * phba, 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)) != 0)		return (0);	if (memcmp(pn, &ndlp->nlp_portname, sizeof (struct lpfc_name)) != 0)		return (0);	/* we match, return success */	return (1);}intlpfc_check_sparm(struct lpfc_hba * phba,		 struct lpfc_nodelist * ndlp, struct serv_parm * sp,		 uint32_t class){	volatile struct serv_parm *hsp = &phba->fc_sparam;	/* First check for supported version */	/* Next check for class validity */	if (sp->cls1.classValid) {		if (sp->cls1.rcvDataSizeMsb > hsp->cls1.rcvDataSizeMsb)			sp->cls1.rcvDataSizeMsb = hsp->cls1.rcvDataSizeMsb;		if (sp->cls1.rcvDataSizeLsb > hsp->cls1.rcvDataSizeLsb)			sp->cls1.rcvDataSizeLsb = hsp->cls1.rcvDataSizeLsb;	} else if (class == CLASS1) {		return (0);	}	if (sp->cls2.classValid) {		if (sp->cls2.rcvDataSizeMsb > hsp->cls2.rcvDataSizeMsb)			sp->cls2.rcvDataSizeMsb = hsp->cls2.rcvDataSizeMsb;		if (sp->cls2.rcvDataSizeLsb > hsp->cls2.rcvDataSizeLsb)			sp->cls2.rcvDataSizeLsb = hsp->cls2.rcvDataSizeLsb;	} else if (class == CLASS2) {		return (0);	}	if (sp->cls3.classValid) {		if (sp->cls3.rcvDataSizeMsb > hsp->cls3.rcvDataSizeMsb)			sp->cls3.rcvDataSizeMsb = hsp->cls3.rcvDataSizeMsb;		if (sp->cls3.rcvDataSizeLsb > hsp->cls3.rcvDataSizeLsb)			sp->cls3.rcvDataSizeLsb = hsp->cls3.rcvDataSizeLsb;	} else if (class == CLASS3) {		return (0);	}	if (sp->cmn.bbRcvSizeMsb > hsp->cmn.bbRcvSizeMsb)		sp->cmn.bbRcvSizeMsb = hsp->cmn.bbRcvSizeMsb;	if (sp->cmn.bbRcvSizeLsb > hsp->cmn.bbRcvSizeLsb)		sp->cmn.bbRcvSizeLsb = hsp->cmn.bbRcvSizeLsb;	/* If check is good, copy wwpn wwnn into ndlp */	memcpy(&ndlp->nlp_nodename, &sp->nodeName, sizeof (struct lpfc_name));	memcpy(&ndlp->nlp_portname, &sp->portName, sizeof (struct lpfc_name));	return (1);}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,	int send_abts){	struct lpfc_sli *psli;	struct lpfc_sli_ring *pring;	struct lpfc_iocbq *iocb, *next_iocb;	IOCB_t *icmd;	int    found = 0;	/* Abort outstanding I/O on NPort <nlp_DID> */	lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,			"%d:0201 Abort outstanding I/O on NPort x%x "			"Data: x%x x%x x%x\n",			phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag,			ndlp->nlp_state, ndlp->nlp_rpi);	psli = &phba->sli;	pring = &psli->ring[LPFC_ELS_RING];	/* First check the txq */	do {		found = 0;		spin_lock_irq(phba->host->host_lock);		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))) {				found = 1;				/* It matches, so deque and call compl with an				   error */				list_del(&iocb->list);				pring->txq_cnt--;				if (iocb->iocb_cmpl) {					icmd = &iocb->iocb;					icmd->ulpStatus = IOSTAT_LOCAL_REJECT;					icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;					spin_unlock_irq(phba->host->host_lock);					(iocb->iocb_cmpl) (phba, iocb, iocb);					spin_lock_irq(phba->host->host_lock);				} else					lpfc_sli_release_iocbq(phba, iocb);				break;			}		}		spin_unlock_irq(phba->host->host_lock);	} while (found);	/* Everything on txcmplq will be returned by firmware	 * with a no rpi / linkdown / abort error.  For ring 0,	 * ELS discovery, we want to get rid of it right here.	 */	/* Next check the txcmplq */	do {		found = 0;		spin_lock_irq(phba->host->host_lock);		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))) {				found = 1;				/* It matches, so deque and call compl with an				   error */				list_del(&iocb->list);				pring->txcmplq_cnt--;				icmd = &iocb->iocb;				/* If the driver is completing an ELS				 * command early, flush it out of the firmware.				 */				if (send_abts &&				   (icmd->ulpCommand == CMD_ELS_REQUEST64_CR) &&				   (icmd->un.elsreq64.bdl.ulpIoTag32)) {					lpfc_sli_issue_abort_iotag32(phba,							     pring, iocb);				}				if (iocb->iocb_cmpl) {					icmd->ulpStatus = IOSTAT_LOCAL_REJECT;					icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;					spin_unlock_irq(phba->host->host_lock);					(iocb->iocb_cmpl) (phba, iocb, iocb);					spin_lock_irq(phba->host->host_lock);				} else					lpfc_sli_release_iocbq(phba, iocb);				break;			}		}		spin_unlock_irq(phba->host->host_lock);	} while(found);	/* If we are delaying issuing an ELS command, cancel it */	if (ndlp->nlp_flag & NLP_DELAY_TMO) {		ndlp->nlp_flag &= ~NLP_DELAY_TMO;		del_timer_sync(&ndlp->nlp_delayfunc);		if (!list_empty(&ndlp->els_retry_evt.evt_listp))			list_del_init(&ndlp->els_retry_evt.evt_listp);	}	return (0);}static intlpfc_rcv_plogi(struct lpfc_hba * phba,		      struct lpfc_nodelist * ndlp,		      struct lpfc_iocbq *cmdiocb){	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 (phba->hba_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 (phba->fc_flag & FC_PT2PT) {			lpfc_els_abort_flogi(phba);		        if (!(phba->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(phba);			}			else {				lpfc_initial_flogi(phba);			}		}		else {			stat.un.b.lsRjtRsnCode = LSRJT_LOGICAL_BSY;			stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;			lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb,					    ndlp);			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(phba, 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(phba, stat.un.lsRjtError, cmdiocb, ndlp);		return (0);	}	icmd = &cmdiocb->iocb;	/* PLOGI chkparm OK */	lpfc_printf_log(phba,			KERN_INFO,			LOG_ELS,			"%d:0114 PLOGI chkparm OK Data: x%x x%x x%x x%x\n",			phba->brd_no,			ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_flag,			ndlp->nlp_rpi);	if ((phba->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(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL, 0);		return (1);	}	if ((phba->fc_flag & FC_PT2PT)	    && !(phba->fc_flag & FC_PT2PT_PLOGI)) {		/* rcv'ed PLOGI decides what our NPortId will be */		phba->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;		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(phba);	}	mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);	if (mbox == NULL)		goto out;	if (lpfc_reg_login(phba, icmd->un.rcvels.remoteID,			   (uint8_t *) sp, mbox, 0)) {		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  = ndlp;	ndlp->nlp_flag |= NLP_ACC_REGLOGIN;	/* If there is an outstanding PLOGI issued, abort it before	 * sending ACC rsp to PLOGI recieved.	 */	if (ndlp->nlp_state == NLP_STE_PLOGI_ISSUE) {		/* software abort outstanding PLOGI */		lpfc_els_abort(phba, ndlp, 1);	}	ndlp->nlp_flag |= NLP_RCV_PLOGI;	lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox, 0);	return (1);out:	stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;	stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE;	lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp);	return (0);}static intlpfc_rcv_padisc(struct lpfc_hba * phba,		struct lpfc_nodelist * ndlp,		struct lpfc_iocbq *cmdiocb){	struct lpfc_dmabuf *pcmd;	struct serv_parm *sp;	struct lpfc_name *pnn, *ppn;	struct ls_rjt stat;	ADISC *ap;	IOCB_t *icmd;	uint32_t *lp;	uint32_t cmd;	pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;	lp = (uint32_t *) pcmd->virt;	cmd = *lp++;	if (cmd == ELS_CMD_ADISC) {		ap = (ADISC *) lp;		pnn = (struct lpfc_name *) & ap->nodeName;		ppn = (struct lpfc_name *) & ap->portName;	} else {		sp = (struct serv_parm *) lp;		pnn = (struct lpfc_name *) & sp->nodeName;		ppn = (struct lpfc_name *) & sp->portName;	}	icmd = &cmdiocb->iocb;	if ((icmd->ulpStatus == 0) &&	    (lpfc_check_adisc(phba, ndlp, pnn, ppn))) {		if (cmd == ELS_CMD_ADISC) {			lpfc_els_rsp_adisc_acc(phba, cmdiocb, ndlp);		}		else {			lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp,				NULL, 0);		}		return (1);	}	/* Reject this request because invalid parameters */	stat.un.b.lsRjtRsvd0 = 0;	stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;	stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS;	stat.un.b.vendorUnique = 0;	lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp);	ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI;	/* 1 sec timeout */	mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);	spin_lock_irq(phba->host->host_lock);	ndlp->nlp_flag |= NLP_DELAY_TMO;	spin_unlock_irq(phba->host->host_lock);	ndlp->nlp_state = NLP_STE_NPR_NODE;	lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);	return (0);}static int

⌨️ 快捷键说明

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