lpfc_els.c

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

C
2,382
字号
/******************************************************************* * 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"static int lpfc_els_retry(struct lpfc_hba *, struct lpfc_iocbq *,			  struct lpfc_iocbq *);static void lpfc_cmpl_fabric_iocb(struct lpfc_hba *, struct lpfc_iocbq *,			struct lpfc_iocbq *);static int lpfc_max_els_tries = 3;intlpfc_els_chk_latt(struct lpfc_vport *vport){	struct Scsi_Host *shost = lpfc_shost_from_vport(vport);	struct lpfc_hba  *phba = vport->phba;	uint32_t ha_copy;	if (vport->port_state >= LPFC_VPORT_READY ||	    phba->link_state == LPFC_LINK_DOWN)		return 0;	/* Read the HBA Host Attention Register */	ha_copy = readl(phba->HAregaddr);	if (!(ha_copy & HA_LATT))		return 0;	/* Pending Link Event during Discovery */	lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,			 "0237 Pending Link Event during "			 "Discovery: State x%x\n",			 phba->pport->port_state);	/* CLEAR_LA should re-enable link attention events and	 * we should then imediately take a LATT event. The	 * LATT processing should call lpfc_linkdown() which	 * will cleanup any left over in-progress discovery	 * events.	 */	spin_lock_irq(shost->host_lock);	vport->fc_flag |= FC_ABORT_DISCOVERY;	spin_unlock_irq(shost->host_lock);	if (phba->link_state != LPFC_CLEAR_LA)		lpfc_issue_clear_la(phba, vport);	return 1;}static struct lpfc_iocbq *lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,		   uint16_t cmdSize, uint8_t retry,		   struct lpfc_nodelist *ndlp, uint32_t did,		   uint32_t elscmd){	struct lpfc_hba  *phba = vport->phba;	struct lpfc_iocbq *elsiocb;	struct lpfc_dmabuf *pcmd, *prsp, *pbuflist;	struct ulp_bde64 *bpl;	IOCB_t *icmd;	if (!lpfc_is_link_up(phba))		return NULL;	/* Allocate buffer for  command iocb */	elsiocb = lpfc_sli_get_iocbq(phba);	if (elsiocb == NULL)		return NULL;	icmd = &elsiocb->iocb;	/* fill in BDEs for command */	/* Allocate buffer for command payload */	if (((pcmd = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL)) == 0) ||	    ((pcmd->virt = lpfc_mbuf_alloc(phba,					   MEM_PRI, &(pcmd->phys))) == 0)) {		kfree(pcmd);		lpfc_sli_release_iocbq(phba, elsiocb);		return NULL;	}	INIT_LIST_HEAD(&pcmd->list);	/* Allocate buffer for response payload */	if (expectRsp) {		prsp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);		if (prsp)			prsp->virt = lpfc_mbuf_alloc(phba, MEM_PRI,						     &prsp->phys);		if (prsp == 0 || prsp->virt == 0) {			kfree(prsp);			lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys);			kfree(pcmd);			lpfc_sli_release_iocbq(phba, elsiocb);			return NULL;		}		INIT_LIST_HEAD(&prsp->list);	} else {		prsp = NULL;	}	/* Allocate buffer for Buffer ptr list */	pbuflist = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);	if (pbuflist)		pbuflist->virt = lpfc_mbuf_alloc(phba, MEM_PRI,						 &pbuflist->phys);	if (pbuflist == 0 || pbuflist->virt == 0) {		lpfc_sli_release_iocbq(phba, elsiocb);		lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys);		lpfc_mbuf_free(phba, prsp->virt, prsp->phys);		kfree(pcmd);		kfree(prsp);		kfree(pbuflist);		return NULL;	}	INIT_LIST_HEAD(&pbuflist->list);	icmd->un.elsreq64.bdl.addrHigh = putPaddrHigh(pbuflist->phys);	icmd->un.elsreq64.bdl.addrLow = putPaddrLow(pbuflist->phys);	icmd->un.elsreq64.bdl.bdeFlags = BUFF_TYPE_BDL;	icmd->un.elsreq64.remoteID = did;	/* DID */	if (expectRsp) {		icmd->un.elsreq64.bdl.bdeSize = (2 * sizeof(struct ulp_bde64));		icmd->ulpCommand = CMD_ELS_REQUEST64_CR;		icmd->ulpTimeout = phba->fc_ratov * 2;	} else {		icmd->un.elsreq64.bdl.bdeSize = sizeof(struct ulp_bde64);		icmd->ulpCommand = CMD_XMIT_ELS_RSP64_CX;	}	icmd->ulpBdeCount = 1;	icmd->ulpLe = 1;	icmd->ulpClass = CLASS3;	if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {		icmd->un.elsreq64.myID = vport->fc_myDID;		/* For ELS_REQUEST64_CR, use the VPI by default */		icmd->ulpContext = vport->vpi;		icmd->ulpCt_h = 0;		icmd->ulpCt_l = 1;	}	bpl = (struct ulp_bde64 *) pbuflist->virt;	bpl->addrLow = le32_to_cpu(putPaddrLow(pcmd->phys));	bpl->addrHigh = le32_to_cpu(putPaddrHigh(pcmd->phys));	bpl->tus.f.bdeSize = cmdSize;	bpl->tus.f.bdeFlags = 0;	bpl->tus.w = le32_to_cpu(bpl->tus.w);	if (expectRsp) {		bpl++;		bpl->addrLow = le32_to_cpu(putPaddrLow(prsp->phys));		bpl->addrHigh = le32_to_cpu(putPaddrHigh(prsp->phys));		bpl->tus.f.bdeSize = FCELSSIZE;		bpl->tus.f.bdeFlags = BUFF_USE_RCV;		bpl->tus.w = le32_to_cpu(bpl->tus.w);	}	elsiocb->context1 = lpfc_nlp_get(ndlp);	elsiocb->context2 = pcmd;	elsiocb->context3 = pbuflist;	elsiocb->retry = retry;	elsiocb->vport = vport;	elsiocb->drvrTimeout = (phba->fc_ratov << 1) + LPFC_DRVR_TIMEOUT;	if (prsp) {		list_add(&prsp->list, &pcmd->list);	}	if (expectRsp) {		/* Xmit ELS command <elsCmd> to remote NPORT <did> */		lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,				 "0116 Xmit ELS command x%x to remote "				 "NPORT x%x I/O tag: x%x, port state: x%x\n",				 elscmd, did, elsiocb->iotag,				 vport->port_state);	} else {		/* Xmit ELS response <elsCmd> to remote NPORT <did> */		lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,				 "0117 Xmit ELS response x%x to remote "				 "NPORT x%x I/O tag: x%x, size: x%x\n",				 elscmd, ndlp->nlp_DID, elsiocb->iotag,				 cmdSize);	}	return elsiocb;}static intlpfc_issue_fabric_reglogin(struct lpfc_vport *vport){	struct lpfc_hba  *phba = vport->phba;	LPFC_MBOXQ_t *mbox;	struct lpfc_dmabuf *mp;	struct lpfc_nodelist *ndlp;	struct serv_parm *sp;	int rc;	sp = &phba->fc_fabparam;	ndlp = lpfc_findnode_did(vport, Fabric_DID);	if (!ndlp)		goto fail;	mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);	if (!mbox)		goto fail;	vport->port_state = LPFC_FABRIC_CFG_LINK;	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)		goto fail_free_mbox;	mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);	if (!mbox)		goto fail;	rc = lpfc_reg_login(phba, vport->vpi, Fabric_DID, (uint8_t *)sp, mbox,			    0);	if (rc)		goto fail_free_mbox;	mbox->mbox_cmpl = lpfc_mbx_cmpl_fabric_reg_login;	mbox->vport = vport;	mbox->context2 = lpfc_nlp_get(ndlp);	rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT | MBX_STOP_IOCB);	if (rc == MBX_NOT_FINISHED)		goto fail_issue_reg_login;	return 0;fail_issue_reg_login:	lpfc_nlp_put(ndlp);	mp = (struct lpfc_dmabuf *) mbox->context1;	lpfc_mbuf_free(phba, mp->virt, mp->phys);	kfree(mp);fail_free_mbox:	mempool_free(mbox, phba->mbox_mem_pool);fail:	lpfc_vport_set_state(vport, FC_VPORT_FAILED);	lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,		"0249 Cannot issue Register Fabric login\n");	return -ENXIO;}static intlpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,			   struct serv_parm *sp, IOCB_t *irsp){	struct Scsi_Host *shost = lpfc_shost_from_vport(vport);	struct lpfc_hba  *phba = vport->phba;	struct lpfc_nodelist *np;	struct lpfc_nodelist *next_np;	spin_lock_irq(shost->host_lock);	vport->fc_flag |= FC_FABRIC;	spin_unlock_irq(shost->host_lock);	phba->fc_edtov = be32_to_cpu(sp->cmn.e_d_tov);	if (sp->cmn.edtovResolution)	/* E_D_TOV ticks are in nanoseconds */		phba->fc_edtov = (phba->fc_edtov + 999999) / 1000000;	phba->fc_ratov = (be32_to_cpu(sp->cmn.w2.r_a_tov) + 999) / 1000;	if (phba->fc_topology == TOPOLOGY_LOOP) {		spin_lock_irq(shost->host_lock);		vport->fc_flag |= FC_PUBLIC_LOOP;		spin_unlock_irq(shost->host_lock);	} else {		/*		 * If we are a N-port connected to a Fabric, fixup sparam's so		 * logins to devices on remote loops work.		 */		vport->fc_sparam.cmn.altBbCredit = 1;	}	vport->fc_myDID = irsp->un.ulpWord[4] & Mask_DID;	memcpy(&ndlp->nlp_portname, &sp->portName, sizeof(struct lpfc_name));	memcpy(&ndlp->nlp_nodename, &sp->nodeName, sizeof(struct lpfc_name));	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;	memcpy(&phba->fc_fabparam, sp, sizeof(struct serv_parm));	if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {		if (sp->cmn.response_multiple_NPort) {			lpfc_printf_vlog(vport, KERN_WARNING,					 LOG_ELS | LOG_VPORT,					 "1816 FLOGI NPIV supported, "					 "response data 0x%x\n",					 sp->cmn.response_multiple_NPort);			phba->link_flag |= LS_NPIV_FAB_SUPPORTED;		} else {			/* Because we asked f/w for NPIV it still expects us			to call reg_vnpid atleast for the physcial host */			lpfc_printf_vlog(vport, KERN_WARNING,					 LOG_ELS | LOG_VPORT,					 "1817 Fabric does not support NPIV "					 "- configuring single port mode.\n");			phba->link_flag &= ~LS_NPIV_FAB_SUPPORTED;		}	}	if ((vport->fc_prevDID != vport->fc_myDID) &&		!(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) {		/* If our NportID changed, we need to ensure all		 * remaining NPORTs get unreg_login'ed.		 */		list_for_each_entry_safe(np, next_np,					&vport->fc_nodes, nlp_listp) {			if ((np->nlp_state != NLP_STE_NPR_NODE) ||				   !(np->nlp_flag & NLP_NPR_ADISC))				continue;			spin_lock_irq(shost->host_lock);			np->nlp_flag &= ~NLP_NPR_ADISC;			spin_unlock_irq(shost->host_lock);			lpfc_unreg_rpi(vport, np);		}		if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {			lpfc_mbx_unreg_vpi(vport);			vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;		}	}	ndlp->nlp_sid = irsp->un.ulpWord[4] & Mask_DID;	lpfc_nlp_set_state(vport, ndlp, NLP_STE_REG_LOGIN_ISSUE);	if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED &&	    vport->fc_flag & FC_VPORT_NEEDS_REG_VPI) {		lpfc_register_new_vport(phba, vport, ndlp);		return 0;	}	lpfc_issue_fabric_reglogin(vport);	return 0;}/* * We FLOGIed into an NPort, initiate pt2pt protocol */static intlpfc_cmpl_els_flogi_nport(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,			  struct serv_parm *sp){	struct Scsi_Host *shost = lpfc_shost_from_vport(vport);	struct lpfc_hba  *phba = vport->phba;	LPFC_MBOXQ_t *mbox;	int rc;	spin_lock_irq(shost->host_lock);	vport->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);	spin_unlock_irq(shost->host_lock);	phba->fc_edtov = FF_DEF_EDTOV;	phba->fc_ratov = FF_DEF_RATOV;	rc = memcmp(&vport->fc_portname, &sp->portName,		    sizeof(vport->fc_portname));	if (rc >= 0) {		/* This side will initiate the PLOGI */		spin_lock_irq(shost->host_lock);		vport->fc_flag |= FC_PT2PT_PLOGI;		spin_unlock_irq(shost->host_lock);		/*		 * N_Port ID cannot be 0, set our to LocalID the other		 * side will be RemoteID.		 */		/* not equal */		if (rc)			vport->fc_myDID = PT2PT_LocalID;		mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);		if (!mbox)			goto fail;		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 fail;		}		lpfc_nlp_put(ndlp);		ndlp = lpfc_findnode_did(vport, PT2PT_RemoteID);		if (!ndlp) {			/*			 * Cannot find existing Fabric ndlp, so allocate a			 * new one			 */			ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL);			if (!ndlp)				goto fail;			lpfc_nlp_init(vport, ndlp, PT2PT_RemoteID);		}		memcpy(&ndlp->nlp_portname, &sp->portName,		       sizeof(struct lpfc_name));		memcpy(&ndlp->nlp_nodename, &sp->nodeName,		       sizeof(struct lpfc_name));		lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);		spin_lock_irq(shost->host_lock);		ndlp->nlp_flag |= NLP_NPR_2B_DISC;		spin_unlock_irq(shost->host_lock);	} else {		/* This side will wait for the PLOGI */		lpfc_nlp_put(ndlp);	}	spin_lock_irq(shost->host_lock);	vport->fc_flag |= FC_PT2PT;	spin_unlock_irq(shost->host_lock);	/* Start discovery - this should just do CLEAR_LA */	lpfc_disc_start(vport);	return 0;fail:	return -ENXIO;}static void

⌨️ 快捷键说明

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