lpfc_ct.c

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

C
1,663
字号
/******************************************************************* * 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                                                  * *                                                                 * * 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.                                     * *******************************************************************//* * Fibre Channel SCSI LAN Device Driver CT support */#include <linux/blkdev.h>#include <linux/pci.h>#include <linux/interrupt.h>#include <linux/utsname.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_version.h"#include "lpfc_vport.h"#include "lpfc_debugfs.h"#define HBA_PORTSPEED_UNKNOWN               0	/* Unknown - transceiver						 * incapable of reporting */#define HBA_PORTSPEED_1GBIT                 1	/* 1 GBit/sec */#define HBA_PORTSPEED_2GBIT                 2	/* 2 GBit/sec */#define HBA_PORTSPEED_4GBIT                 8   /* 4 GBit/sec */#define HBA_PORTSPEED_8GBIT                16   /* 8 GBit/sec */#define HBA_PORTSPEED_10GBIT                4	/* 10 GBit/sec */#define HBA_PORTSPEED_NOT_NEGOTIATED        5	/* Speed not established */#define FOURBYTES	4static char *lpfc_release_version = LPFC_DRIVER_VERSION;/* * lpfc_ct_unsol_event */static voidlpfc_ct_unsol_buffer(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq,		     struct lpfc_dmabuf *mp, uint32_t size){	if (!mp) {		printk(KERN_ERR "%s (%d): Unsolited CT, no buffer, "		       "piocbq = %p, status = x%x, mp = %p, size = %d\n",		       __FUNCTION__, __LINE__,		       piocbq, piocbq->iocb.ulpStatus, mp, size);	}	printk(KERN_ERR "%s (%d): Ignoring unsolicted CT piocbq = %p, "	       "buffer = %p, size = %d, status = x%x\n",	       __FUNCTION__, __LINE__,	       piocbq, mp, size,	       piocbq->iocb.ulpStatus);}static voidlpfc_ct_ignore_hbq_buffer(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq,			  struct lpfc_dmabuf *mp, uint32_t size){	if (!mp) {		printk(KERN_ERR "%s (%d): Unsolited CT, no "		       "HBQ buffer, piocbq = %p, status = x%x\n",		       __FUNCTION__, __LINE__,		       piocbq, piocbq->iocb.ulpStatus);	} else {		lpfc_ct_unsol_buffer(phba, piocbq, mp, size);		printk(KERN_ERR "%s (%d): Ignoring unsolicted CT "		       "piocbq = %p, buffer = %p, size = %d, "		       "status = x%x\n",		       __FUNCTION__, __LINE__,		       piocbq, mp, size, piocbq->iocb.ulpStatus);	}}voidlpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,		    struct lpfc_iocbq *piocbq){	struct lpfc_dmabuf *mp = NULL;	IOCB_t *icmd = &piocbq->iocb;	int i;	struct lpfc_iocbq *iocbq;	dma_addr_t paddr;	uint32_t size;	struct lpfc_dmabuf *bdeBuf1 = piocbq->context2;	struct lpfc_dmabuf *bdeBuf2 = piocbq->context3;	piocbq->context2 = NULL;	piocbq->context3 = NULL;	if (unlikely(icmd->ulpStatus == IOSTAT_NEED_BUFFER)) {		lpfc_sli_hbqbuf_add_hbqs(phba, LPFC_ELS_HBQ);	} else if ((icmd->ulpStatus == IOSTAT_LOCAL_REJECT) &&		((icmd->un.ulpWord[4] & 0xff) == IOERR_RCV_BUFFER_WAITING)) {		/* Not enough posted buffers; Try posting more buffers */		phba->fc_stat.NoRcvBuf++;		if (!(phba->sli3_options & LPFC_SLI3_HBQ_ENABLED))			lpfc_post_buffer(phba, pring, 0, 1);		return;	}	/* If there are no BDEs associated with this IOCB,	 * there is nothing to do.	 */	if (icmd->ulpBdeCount == 0)		return;	if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {		list_for_each_entry(iocbq, &piocbq->list, list) {			icmd = &iocbq->iocb;			if (icmd->ulpBdeCount == 0) {				printk(KERN_ERR "%s (%d): Unsolited CT, no "				       "BDE, iocbq = %p, status = x%x\n",				       __FUNCTION__, __LINE__,				       iocbq, iocbq->iocb.ulpStatus);				continue;			}			size  = icmd->un.cont64[0].tus.f.bdeSize;			lpfc_ct_ignore_hbq_buffer(phba, piocbq, bdeBuf1, size);			lpfc_in_buf_free(phba, bdeBuf1);			if (icmd->ulpBdeCount == 2) {				lpfc_ct_ignore_hbq_buffer(phba, piocbq, bdeBuf2,							  size);				lpfc_in_buf_free(phba, bdeBuf2);			}		}	} else {		struct lpfc_iocbq  *next;		list_for_each_entry_safe(iocbq, next, &piocbq->list, list) {			icmd = &iocbq->iocb;			if (icmd->ulpBdeCount == 0) {				printk(KERN_ERR "%s (%d): Unsolited CT, no "				       "BDE, iocbq = %p, status = x%x\n",				       __FUNCTION__, __LINE__,				       iocbq, iocbq->iocb.ulpStatus);				continue;			}			for (i = 0; i < icmd->ulpBdeCount; i++) {				paddr = getPaddr(icmd->un.cont64[i].addrHigh,						 icmd->un.cont64[i].addrLow);				mp = lpfc_sli_ringpostbuf_get(phba, pring,							      paddr);				size = icmd->un.cont64[i].tus.f.bdeSize;				lpfc_ct_unsol_buffer(phba, piocbq, mp, size);				lpfc_in_buf_free(phba, mp);			}			list_del(&iocbq->list);			lpfc_sli_release_iocbq(phba, iocbq);		}	}}static voidlpfc_free_ct_rsp(struct lpfc_hba *phba, struct lpfc_dmabuf *mlist){	struct lpfc_dmabuf *mlast, *next_mlast;	list_for_each_entry_safe(mlast, next_mlast, &mlist->list, list) {		lpfc_mbuf_free(phba, mlast->virt, mlast->phys);		list_del(&mlast->list);		kfree(mlast);	}	lpfc_mbuf_free(phba, mlist->virt, mlist->phys);	kfree(mlist);	return;}static struct lpfc_dmabuf *lpfc_alloc_ct_rsp(struct lpfc_hba *phba, int cmdcode, struct ulp_bde64 *bpl,		  uint32_t size, int *entries){	struct lpfc_dmabuf *mlist = NULL;	struct lpfc_dmabuf *mp;	int cnt, i = 0;	/* We get chucks of FCELSSIZE */	cnt = size > FCELSSIZE ? FCELSSIZE: size;	while (size) {		/* Allocate buffer for rsp payload */		mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);		if (!mp) {			if (mlist)				lpfc_free_ct_rsp(phba, mlist);			return NULL;		}		INIT_LIST_HEAD(&mp->list);		if (cmdcode == be16_to_cpu(SLI_CTNS_GID_FT) ||		    cmdcode == be16_to_cpu(SLI_CTNS_GFF_ID))			mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(mp->phys));		else			mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys));		if (!mp->virt) {			kfree(mp);			if (mlist)				lpfc_free_ct_rsp(phba, mlist);			return NULL;		}		/* Queue it to a linked list */		if (!mlist)			mlist = mp;		else			list_add_tail(&mp->list, &mlist->list);		bpl->tus.f.bdeFlags = BUFF_USE_RCV;		/* build buffer ptr list for IOCB */		bpl->addrLow = le32_to_cpu(putPaddrLow(mp->phys) );		bpl->addrHigh = le32_to_cpu(putPaddrHigh(mp->phys) );		bpl->tus.f.bdeSize = (uint16_t) cnt;		bpl->tus.w = le32_to_cpu(bpl->tus.w);		bpl++;		i++;		size -= cnt;	}	*entries = i;	return mlist;}intlpfc_ct_free_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *ctiocb){	struct lpfc_dmabuf *buf_ptr;	if (ctiocb->context_un.ndlp) {		lpfc_nlp_put(ctiocb->context_un.ndlp);		ctiocb->context_un.ndlp = NULL;	}	if (ctiocb->context1) {		buf_ptr = (struct lpfc_dmabuf *) ctiocb->context1;		lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);		kfree(buf_ptr);		ctiocb->context1 = NULL;	}	if (ctiocb->context2) {		lpfc_free_ct_rsp(phba, (struct lpfc_dmabuf *) ctiocb->context2);		ctiocb->context2 = NULL;	}	if (ctiocb->context3) {		buf_ptr = (struct lpfc_dmabuf *) ctiocb->context3;		lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);		kfree(buf_ptr);		ctiocb->context1 = NULL;	}	lpfc_sli_release_iocbq(phba, ctiocb);	return 0;}static intlpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,	     struct lpfc_dmabuf *inp, struct lpfc_dmabuf *outp,	     void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,		     struct lpfc_iocbq *),	     struct lpfc_nodelist *ndlp, uint32_t usr_flg, uint32_t num_entry,	     uint32_t tmo, uint8_t retry){	struct lpfc_hba  *phba = vport->phba;	struct lpfc_sli  *psli = &phba->sli;	struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];	IOCB_t *icmd;	struct lpfc_iocbq *geniocb;	int rc;	/* Allocate buffer for  command iocb */	geniocb = lpfc_sli_get_iocbq(phba);	if (geniocb == NULL)		return 1;	icmd = &geniocb->iocb;	icmd->un.genreq64.bdl.ulpIoTag32 = 0;	icmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys);	icmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys);	icmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BDL;	icmd->un.genreq64.bdl.bdeSize = (num_entry * sizeof (struct ulp_bde64));	if (usr_flg)		geniocb->context3 = NULL;	else		geniocb->context3 = (uint8_t *) bmp;	/* Save for completion so we can release these resources */	geniocb->context1 = (uint8_t *) inp;	geniocb->context2 = (uint8_t *) outp;	geniocb->context_un.ndlp = ndlp;	/* Fill in payload, bp points to frame payload */	icmd->ulpCommand = CMD_GEN_REQUEST64_CR;	/* Fill in rest of iocb */	icmd->un.genreq64.w5.hcsw.Fctl = (SI | LA);	icmd->un.genreq64.w5.hcsw.Dfctl = 0;	icmd->un.genreq64.w5.hcsw.Rctl = FC_UNSOL_CTL;	icmd->un.genreq64.w5.hcsw.Type = FC_COMMON_TRANSPORT_ULP;	if (!tmo) {		 /* FC spec states we need 3 * ratov for CT requests */		tmo = (3 * phba->fc_ratov);	}	icmd->ulpTimeout = tmo;	icmd->ulpBdeCount = 1;	icmd->ulpLe = 1;	icmd->ulpClass = CLASS3;	icmd->ulpContext = ndlp->nlp_rpi;	if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {		/* For GEN_REQUEST64_CR, use the RPI */		icmd->ulpCt_h = 0;		icmd->ulpCt_l = 0;	}	/* Issue GEN REQ IOCB for NPORT <did> */	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,			 "0119 Issue GEN REQ IOCB to NPORT x%x "			 "Data: x%x x%x\n",			 ndlp->nlp_DID, icmd->ulpIoTag,			 vport->port_state);	geniocb->iocb_cmpl = cmpl;	geniocb->drvrTimeout = icmd->ulpTimeout + LPFC_DRVR_TIMEOUT;	geniocb->vport = vport;	geniocb->retry = retry;	rc = lpfc_sli_issue_iocb(phba, pring, geniocb, 0);	if (rc == IOCB_ERROR) {		lpfc_sli_release_iocbq(phba, geniocb);		return 1;	}	return 0;}static intlpfc_ct_cmd(struct lpfc_vport *vport, struct lpfc_dmabuf *inmp,	    struct lpfc_dmabuf *bmp, struct lpfc_nodelist *ndlp,	    void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,			  struct lpfc_iocbq *),	    uint32_t rsp_size, uint8_t retry){	struct lpfc_hba  *phba = vport->phba;	struct ulp_bde64 *bpl = (struct ulp_bde64 *) bmp->virt;	struct lpfc_dmabuf *outmp;	int cnt = 0, status;	int cmdcode = ((struct lpfc_sli_ct_request *) inmp->virt)->		CommandResponse.bits.CmdRsp;	bpl++;			/* Skip past ct request */	/* Put buffer(s) for ct rsp in bpl */	outmp = lpfc_alloc_ct_rsp(phba, cmdcode, bpl, rsp_size, &cnt);	if (!outmp)		return -ENOMEM;	status = lpfc_gen_req(vport, bmp, inmp, outmp, cmpl, ndlp, 0,			      cnt+1, 0, retry);	if (status) {		lpfc_free_ct_rsp(phba, outmp);		return -ENOMEM;	}	return 0;}struct lpfc_vport *lpfc_find_vport_by_did(struct lpfc_hba *phba, uint32_t did) {	struct lpfc_vport *vport_curr;	unsigned long flags;	spin_lock_irqsave(&phba->hbalock, flags);	list_for_each_entry(vport_curr, &phba->port_list, listentry) {		if ((vport_curr->fc_myDID) && (vport_curr->fc_myDID == did)) {			spin_unlock_irqrestore(&phba->hbalock, flags);			return vport_curr;		}	}	spin_unlock_irqrestore(&phba->hbalock, flags);	return NULL;}static intlpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint32_t Size){

⌨️ 快捷键说明

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