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 + -
显示快捷键?