2017-02-13 00:52:32 +03:00
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters . *
2017-02-13 00:52:39 +03:00
* Copyright ( C ) 2017 Broadcom . All Rights Reserved . The term *
* “ Broadcom ” refers to Broadcom Limited and / or its subsidiaries . *
2017-02-13 00:52:32 +03:00
* Copyright ( C ) 2004 - 2016 Emulex . All rights reserved . *
* EMULEX and SLI are trademarks of Emulex . *
2017-02-13 00:52:39 +03:00
* www . broadcom . com *
2017-02-13 00:52:32 +03:00
* 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/pci.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <asm/unaligned.h>
# include <linux/crc-t10dif.h>
# include <net/checksum.h>
# include <scsi/scsi.h>
# include <scsi/scsi_device.h>
# include <scsi/scsi_eh.h>
# include <scsi/scsi_host.h>
# include <scsi/scsi_tcq.h>
# include <scsi/scsi_transport_fc.h>
# include <scsi/fc/fc_fs.h>
# include <linux/nvme.h>
# include <linux/nvme-fc-driver.h>
# include <linux/nvme-fc.h>
# include "lpfc_version.h"
# include "lpfc_hw4.h"
# include "lpfc_hw.h"
# include "lpfc_sli.h"
# include "lpfc_sli4.h"
# include "lpfc_nl.h"
# include "lpfc_disc.h"
# include "lpfc.h"
# include "lpfc_nvme.h"
# include "lpfc_scsi.h"
# include "lpfc_logmsg.h"
# include "lpfc_crtn.h"
# include "lpfc_vport.h"
2017-02-13 00:52:33 +03:00
# include "lpfc_debugfs.h"
2017-02-13 00:52:32 +03:00
/* NVME initiator-based functions */
static struct lpfc_nvme_buf *
lpfc_get_nvme_buf ( struct lpfc_hba * phba , struct lpfc_nodelist * ndlp ) ;
static void
lpfc_release_nvme_buf ( struct lpfc_hba * , struct lpfc_nvme_buf * ) ;
/**
* lpfc_nvme_create_queue -
* @ lpfc_pnvme : Pointer to the driver ' s nvme instance data
* @ qidx : An cpu index used to affinitize IO queues and MSIX vectors .
* @ handle : An opaque driver handle used in follow - up calls .
*
* Driver registers this routine to preallocate and initialize any
* internal data structures to bind the @ qidx to its internal IO queues .
* A hardware queue maps ( qidx ) to a specific driver MSI - X vector / EQ / CQ / WQ .
*
* Return value :
* 0 - Success
* - EINVAL - Unsupported input value .
* - ENOMEM - Could not alloc necessary memory
* */
static int
lpfc_nvme_create_queue ( struct nvme_fc_local_port * pnvme_lport ,
unsigned int qidx , u16 qsize ,
void * * handle )
{
struct lpfc_nvme_lport * lport ;
struct lpfc_vport * vport ;
struct lpfc_nvme_qhandle * qhandle ;
char * str ;
lport = ( struct lpfc_nvme_lport * ) pnvme_lport - > private ;
vport = lport - > vport ;
qhandle = kzalloc ( sizeof ( struct lpfc_nvme_qhandle ) , GFP_KERNEL ) ;
if ( qhandle = = NULL )
return - ENOMEM ;
qhandle - > cpu_id = smp_processor_id ( ) ;
qhandle - > qidx = qidx ;
/*
* NVME qidx = = 0 is the admin queue , so both admin queue
* and first IO queue will use MSI - X vector and associated
* EQ / CQ / WQ at index 0. After that they are sequentially assigned .
*/
if ( qidx ) {
str = " IO " ; /* IO queue */
qhandle - > index = ( ( qidx - 1 ) %
vport - > phba - > cfg_nvme_io_channel ) ;
} else {
str = " ADM " ; /* Admin queue */
qhandle - > index = qidx ;
}
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME ,
" 6073 Binding %s HdwQueue %d (cpu %d) to "
" io_channel %d qhandle %p \n " , str ,
qidx , qhandle - > cpu_id , qhandle - > index , qhandle ) ;
* handle = ( void * ) qhandle ;
return 0 ;
}
/**
* lpfc_nvme_delete_queue -
* @ lpfc_pnvme : Pointer to the driver ' s nvme instance data
* @ qidx : An cpu index used to affinitize IO queues and MSIX vectors .
* @ handle : An opaque driver handle from lpfc_nvme_create_queue
*
* Driver registers this routine to free
* any internal data structures to bind the @ qidx to its internal
* IO queues .
*
* Return value :
* 0 - Success
* TODO : What are the failure codes .
* */
static void
lpfc_nvme_delete_queue ( struct nvme_fc_local_port * pnvme_lport ,
unsigned int qidx ,
void * handle )
{
struct lpfc_nvme_lport * lport ;
struct lpfc_vport * vport ;
lport = ( struct lpfc_nvme_lport * ) pnvme_lport - > private ;
vport = lport - > vport ;
lpfc_printf_vlog ( vport , KERN_INFO , LOG_NVME ,
" 6001 ENTER. lpfc_pnvme %p, qidx x%xi qhandle %p \n " ,
lport , qidx , handle ) ;
kfree ( handle ) ;
}
static void
lpfc_nvme_localport_delete ( struct nvme_fc_local_port * localport )
{
struct lpfc_nvme_lport * lport = localport - > private ;
/* release any threads waiting for the unreg to complete */
complete ( & lport - > lport_unreg_done ) ;
}
/* lpfc_nvme_remoteport_delete
*
* @ remoteport : Pointer to an nvme transport remoteport instance .
*
* This is a template downcall . NVME transport calls this function
* when it has completed the unregistration of a previously
* registered remoteport .
*
* Return value :
* None
*/
void
lpfc_nvme_remoteport_delete ( struct nvme_fc_remote_port * remoteport )
{
struct lpfc_nvme_rport * rport = remoteport - > private ;
struct lpfc_vport * vport ;
struct lpfc_nodelist * ndlp ;
ndlp = rport - > ndlp ;
if ( ! ndlp )
goto rport_err ;
vport = ndlp - > vport ;
if ( ! vport )
goto rport_err ;
/* Remove this rport from the lport's list - memory is owned by the
* transport . Remove the ndlp reference for the NVME transport before
* calling state machine to remove the node , this is devloss = 0
* semantics .
*/
lpfc_printf_vlog ( vport , KERN_INFO , LOG_NVME_DISC ,
" 6146 remoteport delete complete %p \n " ,
remoteport ) ;
list_del ( & rport - > list ) ;
lpfc_nlp_put ( ndlp ) ;
rport_err :
/* This call has to execute as long as the rport is valid.
* Release any threads waiting for the unreg to complete .
*/
complete ( & rport - > rport_unreg_done ) ;
}
static void
lpfc_nvme_cmpl_gen_req ( struct lpfc_hba * phba , struct lpfc_iocbq * cmdwqe ,
struct lpfc_wcqe_complete * wcqe )
{
struct lpfc_vport * vport = cmdwqe - > vport ;
uint32_t status ;
struct nvmefc_ls_req * pnvme_lsreq ;
struct lpfc_dmabuf * buf_ptr ;
struct lpfc_nodelist * ndlp ;
vport - > phba - > fc4NvmeLsCmpls + + ;
pnvme_lsreq = ( struct nvmefc_ls_req * ) cmdwqe - > context2 ;
status = bf_get ( lpfc_wcqe_c_status , wcqe ) & LPFC_IOCB_STATUS_MASK ;
ndlp = ( struct lpfc_nodelist * ) cmdwqe - > context1 ;
lpfc_printf_vlog ( vport , KERN_INFO , LOG_NVME_DISC ,
" 6047 nvme cmpl Enter "
" Data %p DID %x Xri: %x status %x cmd:%p lsreg:%p "
" bmp:%p ndlp:%p \n " ,
pnvme_lsreq , ndlp ? ndlp - > nlp_DID : 0 ,
cmdwqe - > sli4_xritag , status ,
cmdwqe , pnvme_lsreq , cmdwqe - > context3 , ndlp ) ;
2017-02-13 00:52:33 +03:00
lpfc_nvmeio_data ( phba , " NVME LS CMPL: xri x%x stat x%x parm x%x \n " ,
cmdwqe - > sli4_xritag , status , wcqe - > parameter ) ;
2017-02-13 00:52:32 +03:00
if ( cmdwqe - > context3 ) {
buf_ptr = ( struct lpfc_dmabuf * ) cmdwqe - > context3 ;
lpfc_mbuf_free ( phba , buf_ptr - > virt , buf_ptr - > phys ) ;
kfree ( buf_ptr ) ;
cmdwqe - > context3 = NULL ;
}
if ( pnvme_lsreq - > done )
pnvme_lsreq - > done ( pnvme_lsreq , status ) ;
else
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_DISC ,
" 6046 nvme cmpl without done call back? "
" Data %p DID %x Xri: %x status %x \n " ,
pnvme_lsreq , ndlp ? ndlp - > nlp_DID : 0 ,
cmdwqe - > sli4_xritag , status ) ;
if ( ndlp ) {
lpfc_nlp_put ( ndlp ) ;
cmdwqe - > context1 = NULL ;
}
lpfc_sli_release_iocbq ( phba , cmdwqe ) ;
}
static int
lpfc_nvme_gen_req ( struct lpfc_vport * vport , struct lpfc_dmabuf * bmp ,
struct lpfc_dmabuf * inp ,
struct nvmefc_ls_req * pnvme_lsreq ,
void ( * cmpl ) ( struct lpfc_hba * , struct lpfc_iocbq * ,
struct lpfc_wcqe_complete * ) ,
struct lpfc_nodelist * ndlp , uint32_t num_entry ,
uint32_t tmo , uint8_t retry )
{
struct lpfc_hba * phba = vport - > phba ;
union lpfc_wqe * wqe ;
struct lpfc_iocbq * genwqe ;
struct ulp_bde64 * bpl ;
struct ulp_bde64 bde ;
int i , rc , xmit_len , first_len ;
/* Allocate buffer for command WQE */
genwqe = lpfc_sli_get_iocbq ( phba ) ;
if ( genwqe = = NULL )
return 1 ;
wqe = & genwqe - > wqe ;
memset ( wqe , 0 , sizeof ( union lpfc_wqe ) ) ;
genwqe - > context3 = ( uint8_t * ) bmp ;
genwqe - > iocb_flag | = LPFC_IO_NVME_LS ;
/* Save for completion so we can release these resources */
genwqe - > context1 = lpfc_nlp_get ( ndlp ) ;
genwqe - > context2 = ( uint8_t * ) pnvme_lsreq ;
/* Fill in payload, bp points to frame payload */
if ( ! tmo )
/* FC spec states we need 3 * ratov for CT requests */
tmo = ( 3 * phba - > fc_ratov ) ;
/* For this command calculate the xmit length of the request bde. */
xmit_len = 0 ;
first_len = 0 ;
bpl = ( struct ulp_bde64 * ) bmp - > virt ;
for ( i = 0 ; i < num_entry ; i + + ) {
bde . tus . w = bpl [ i ] . tus . w ;
if ( bde . tus . f . bdeFlags ! = BUFF_TYPE_BDE_64 )
break ;
xmit_len + = bde . tus . f . bdeSize ;
if ( i = = 0 )
first_len = xmit_len ;
}
genwqe - > rsvd2 = num_entry ;
genwqe - > hba_wqidx = 0 ;
/* Words 0 - 2 */
wqe - > generic . bde . tus . f . bdeFlags = BUFF_TYPE_BDE_64 ;
wqe - > generic . bde . tus . f . bdeSize = first_len ;
wqe - > generic . bde . addrLow = bpl [ 0 ] . addrLow ;
wqe - > generic . bde . addrHigh = bpl [ 0 ] . addrHigh ;
/* Word 3 */
wqe - > gen_req . request_payload_len = first_len ;
/* Word 4 */
/* Word 5 */
bf_set ( wqe_dfctl , & wqe - > gen_req . wge_ctl , 0 ) ;
bf_set ( wqe_si , & wqe - > gen_req . wge_ctl , 1 ) ;
bf_set ( wqe_la , & wqe - > gen_req . wge_ctl , 1 ) ;
2017-03-04 20:30:26 +03:00
bf_set ( wqe_rctl , & wqe - > gen_req . wge_ctl , FC_RCTL_ELS4_REQ ) ;
2017-02-13 00:52:32 +03:00
bf_set ( wqe_type , & wqe - > gen_req . wge_ctl , FC_TYPE_NVME ) ;
/* Word 6 */
bf_set ( wqe_ctxt_tag , & wqe - > gen_req . wqe_com ,
phba - > sli4_hba . rpi_ids [ ndlp - > nlp_rpi ] ) ;
bf_set ( wqe_xri_tag , & wqe - > gen_req . wqe_com , genwqe - > sli4_xritag ) ;
/* Word 7 */
bf_set ( wqe_tmo , & wqe - > gen_req . wqe_com , ( vport - > phba - > fc_ratov - 1 ) ) ;
bf_set ( wqe_class , & wqe - > gen_req . wqe_com , CLASS3 ) ;
bf_set ( wqe_cmnd , & wqe - > gen_req . wqe_com , CMD_GEN_REQUEST64_WQE ) ;
bf_set ( wqe_ct , & wqe - > gen_req . wqe_com , SLI4_CT_RPI ) ;
/* Word 8 */
wqe - > gen_req . wqe_com . abort_tag = genwqe - > iotag ;
/* Word 9 */
bf_set ( wqe_reqtag , & wqe - > gen_req . wqe_com , genwqe - > iotag ) ;
/* Word 10 */
bf_set ( wqe_dbde , & wqe - > gen_req . wqe_com , 1 ) ;
bf_set ( wqe_iod , & wqe - > gen_req . wqe_com , LPFC_WQE_IOD_READ ) ;
bf_set ( wqe_qosd , & wqe - > gen_req . wqe_com , 1 ) ;
bf_set ( wqe_lenloc , & wqe - > gen_req . wqe_com , LPFC_WQE_LENLOC_NONE ) ;
bf_set ( wqe_ebde_cnt , & wqe - > gen_req . wqe_com , 0 ) ;
/* Word 11 */
bf_set ( wqe_cqid , & wqe - > gen_req . wqe_com , LPFC_WQE_CQ_ID_DEFAULT ) ;
bf_set ( wqe_cmd_type , & wqe - > gen_req . wqe_com , OTHER_COMMAND ) ;
/* Issue GEN REQ WQE for NPORT <did> */
lpfc_printf_vlog ( vport , KERN_INFO , LOG_ELS ,
" 6050 Issue GEN REQ WQE to NPORT x%x "
" Data: x%x x%x wq:%p lsreq:%p bmp:%p xmit:%d 1st:%d \n " ,
ndlp - > nlp_DID , genwqe - > iotag ,
vport - > port_state ,
genwqe , pnvme_lsreq , bmp , xmit_len , first_len ) ;
genwqe - > wqe_cmpl = cmpl ;
genwqe - > iocb_cmpl = NULL ;
genwqe - > drvrTimeout = tmo + LPFC_DRVR_TIMEOUT ;
genwqe - > vport = vport ;
genwqe - > retry = retry ;
2017-02-13 00:52:33 +03:00
lpfc_nvmeio_data ( phba , " NVME LS XMIT: xri x%x iotag x%x to x%06x \n " ,
genwqe - > sli4_xritag , genwqe - > iotag , ndlp - > nlp_DID ) ;
2017-02-13 00:52:32 +03:00
rc = lpfc_sli4_issue_wqe ( phba , LPFC_ELS_RING , genwqe ) ;
if ( rc = = WQE_ERROR ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_ELS ,
" 6045 Issue GEN REQ WQE to NPORT x%x "
" Data: x%x x%x \n " ,
ndlp - > nlp_DID , genwqe - > iotag ,
vport - > port_state ) ;
lpfc_sli_release_iocbq ( phba , genwqe ) ;
return 1 ;
}
return 0 ;
}
/**
* lpfc_nvme_ls_req - Issue an Link Service request
* @ lpfc_pnvme : Pointer to the driver ' s nvme instance data
* @ lpfc_nvme_lport : Pointer to the driver ' s local port data
* @ lpfc_nvme_rport : Pointer to the rport getting the @ lpfc_nvme_ereq
*
* Driver registers this routine to handle any link service request
* from the nvme_fc transport to a remote nvme - aware port .
*
* Return value :
* 0 - Success
* TODO : What are the failure codes .
* */
static int
lpfc_nvme_ls_req ( struct nvme_fc_local_port * pnvme_lport ,
struct nvme_fc_remote_port * pnvme_rport ,
struct nvmefc_ls_req * pnvme_lsreq )
{
int ret = 0 ;
struct lpfc_nvme_lport * lport ;
struct lpfc_vport * vport ;
struct lpfc_nodelist * ndlp ;
struct ulp_bde64 * bpl ;
struct lpfc_dmabuf * bmp ;
/* there are two dma buf in the request, actually there is one and
* the second one is just the start address + cmd size .
* Before calling lpfc_nvme_gen_req these buffers need to be wrapped
* in a lpfc_dmabuf struct . When freeing we just free the wrapper
* because the nvem layer owns the data bufs .
* We do not have to break these packets open , we don ' t care what is in
* them . And we do not have to look at the resonse data , we only care
* that we got a response . All of the caring is going to happen in the
* nvme - fc layer .
*/
lport = ( struct lpfc_nvme_lport * ) pnvme_lport - > private ;
vport = lport - > vport ;
ndlp = lpfc_findnode_did ( vport , pnvme_rport - > port_id ) ;
if ( ! ndlp ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_DISC ,
" 6043 Could not find node for DID %x \n " ,
pnvme_rport - > port_id ) ;
return 1 ;
}
bmp = kmalloc ( sizeof ( struct lpfc_dmabuf ) , GFP_KERNEL ) ;
if ( ! bmp ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_DISC ,
" 6044 Could not find node for DID %x \n " ,
pnvme_rport - > port_id ) ;
return 2 ;
}
INIT_LIST_HEAD ( & bmp - > list ) ;
bmp - > virt = lpfc_mbuf_alloc ( vport - > phba , MEM_PRI , & ( bmp - > phys ) ) ;
if ( ! bmp - > virt ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_DISC ,
" 6042 Could not find node for DID %x \n " ,
pnvme_rport - > port_id ) ;
kfree ( bmp ) ;
return 3 ;
}
bpl = ( struct ulp_bde64 * ) bmp - > virt ;
bpl - > addrHigh = le32_to_cpu ( putPaddrHigh ( pnvme_lsreq - > rqstdma ) ) ;
bpl - > addrLow = le32_to_cpu ( putPaddrLow ( pnvme_lsreq - > rqstdma ) ) ;
bpl - > tus . f . bdeFlags = 0 ;
bpl - > tus . f . bdeSize = pnvme_lsreq - > rqstlen ;
bpl - > tus . w = le32_to_cpu ( bpl - > tus . w ) ;
bpl + + ;
bpl - > addrHigh = le32_to_cpu ( putPaddrHigh ( pnvme_lsreq - > rspdma ) ) ;
bpl - > addrLow = le32_to_cpu ( putPaddrLow ( pnvme_lsreq - > rspdma ) ) ;
bpl - > tus . f . bdeFlags = BUFF_TYPE_BDE_64I ;
bpl - > tus . f . bdeSize = pnvme_lsreq - > rsplen ;
bpl - > tus . w = le32_to_cpu ( bpl - > tus . w ) ;
/* Expand print to include key fields. */
lpfc_printf_vlog ( vport , KERN_INFO , LOG_NVME_DISC ,
" 6051 ENTER. lport %p, rport %p lsreq%p rqstlen:%d "
2017-02-27 23:37:12 +03:00
" rsplen:%d %pad %pad \n " ,
2017-02-13 00:52:32 +03:00
pnvme_lport , pnvme_rport ,
pnvme_lsreq , pnvme_lsreq - > rqstlen ,
2017-02-27 23:37:12 +03:00
pnvme_lsreq - > rsplen , & pnvme_lsreq - > rqstdma ,
& pnvme_lsreq - > rspdma ) ;
2017-02-13 00:52:32 +03:00
vport - > phba - > fc4NvmeLsRequests + + ;
/* Hardcode the wait to 30 seconds. Connections are failing otherwise.
* This code allows it all to work .
*/
ret = lpfc_nvme_gen_req ( vport , bmp , pnvme_lsreq - > rqstaddr ,
pnvme_lsreq , lpfc_nvme_cmpl_gen_req ,
ndlp , 2 , 30 , 0 ) ;
if ( ret ! = WQE_SUCCESS ) {
lpfc_printf_vlog ( vport , KERN_INFO , LOG_NVME_DISC ,
" 6052 EXIT. issue ls wqe failed lport %p, "
" rport %p lsreq%p Status %x DID %x \n " ,
pnvme_lport , pnvme_rport , pnvme_lsreq ,
ret , ndlp - > nlp_DID ) ;
lpfc_mbuf_free ( vport - > phba , bmp - > virt , bmp - > phys ) ;
kfree ( bmp ) ;
return ret ;
}
/* Stub in routine and return 0 for now. */
return ret ;
}
/**
* lpfc_nvme_ls_abort - Issue an Link Service request
* @ lpfc_pnvme : Pointer to the driver ' s nvme instance data
* @ lpfc_nvme_lport : Pointer to the driver ' s local port data
* @ lpfc_nvme_rport : Pointer to the rport getting the @ lpfc_nvme_ereq
*
* Driver registers this routine to handle any link service request
* from the nvme_fc transport to a remote nvme - aware port .
*
* Return value :
* 0 - Success
* TODO : What are the failure codes .
* */
static void
lpfc_nvme_ls_abort ( struct nvme_fc_local_port * pnvme_lport ,
struct nvme_fc_remote_port * pnvme_rport ,
struct nvmefc_ls_req * pnvme_lsreq )
{
struct lpfc_nvme_lport * lport ;
struct lpfc_vport * vport ;
struct lpfc_hba * phba ;
struct lpfc_nodelist * ndlp ;
LIST_HEAD ( abort_list ) ;
struct lpfc_sli_ring * pring ;
struct lpfc_iocbq * wqe , * next_wqe ;
lport = ( struct lpfc_nvme_lport * ) pnvme_lport - > private ;
vport = lport - > vport ;
phba = vport - > phba ;
ndlp = lpfc_findnode_did ( vport , pnvme_rport - > port_id ) ;
if ( ! ndlp ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_ABTS ,
" 6049 Could not find node for DID %x \n " ,
pnvme_rport - > port_id ) ;
return ;
}
/* Expand print to include key fields. */
lpfc_printf_vlog ( vport , KERN_INFO , LOG_NVME_ABTS ,
" 6040 ENTER. lport %p, rport %p lsreq %p rqstlen:%d "
2017-02-27 23:37:12 +03:00
" rsplen:%d %pad %pad \n " ,
2017-02-13 00:52:32 +03:00
pnvme_lport , pnvme_rport ,
pnvme_lsreq , pnvme_lsreq - > rqstlen ,
2017-02-27 23:37:12 +03:00
pnvme_lsreq - > rsplen , & pnvme_lsreq - > rqstdma ,
& pnvme_lsreq - > rspdma ) ;
2017-02-13 00:52:32 +03:00
/*
* Lock the ELS ring txcmplq and build a local list of all ELS IOs
* that need an ABTS . The IOs need to stay on the txcmplq so that
* the abort operation completes them successfully .
*/
pring = phba - > sli4_hba . nvmels_wq - > pring ;
spin_lock_irq ( & phba - > hbalock ) ;
spin_lock ( & pring - > ring_lock ) ;
list_for_each_entry_safe ( wqe , next_wqe , & pring - > txcmplq , list ) {
/* Add to abort_list on on NDLP match. */
if ( lpfc_check_sli_ndlp ( phba , pring , wqe , ndlp ) ) {
wqe - > iocb_flag | = LPFC_DRIVER_ABORTED ;
list_add_tail ( & wqe - > dlist , & abort_list ) ;
}
}
spin_unlock ( & pring - > ring_lock ) ;
spin_unlock_irq ( & phba - > hbalock ) ;
/* Abort the targeted IOs and remove them from the abort list. */
list_for_each_entry_safe ( wqe , next_wqe , & abort_list , dlist ) {
spin_lock_irq ( & phba - > hbalock ) ;
list_del_init ( & wqe - > dlist ) ;
lpfc_sli_issue_abort_iotag ( phba , pring , wqe ) ;
spin_unlock_irq ( & phba - > hbalock ) ;
}
}
/* Fix up the existing sgls for NVME IO. */
static void
lpfc_nvme_adj_fcp_sgls ( struct lpfc_vport * vport ,
struct lpfc_nvme_buf * lpfc_ncmd ,
struct nvmefc_fcp_req * nCmd )
{
struct sli4_sge * sgl ;
union lpfc_wqe128 * wqe ;
uint32_t * wptr , * dptr ;
/*
* Adjust the FCP_CMD and FCP_RSP DMA data and sge_len to
* match NVME . NVME sends 96 bytes . Also , use the
* nvme commands command and response dma addresses
* rather than the virtual memory to ease the restore
* operation .
*/
sgl = lpfc_ncmd - > nvme_sgl ;
sgl - > sge_len = cpu_to_le32 ( nCmd - > cmdlen ) ;
sgl + + ;
/* Setup the physical region for the FCP RSP */
sgl - > addr_hi = cpu_to_le32 ( putPaddrHigh ( nCmd - > rspdma ) ) ;
sgl - > addr_lo = cpu_to_le32 ( putPaddrLow ( nCmd - > rspdma ) ) ;
sgl - > word2 = le32_to_cpu ( sgl - > word2 ) ;
if ( nCmd - > sg_cnt )
bf_set ( lpfc_sli4_sge_last , sgl , 0 ) ;
else
bf_set ( lpfc_sli4_sge_last , sgl , 1 ) ;
sgl - > word2 = cpu_to_le32 ( sgl - > word2 ) ;
sgl - > sge_len = cpu_to_le32 ( nCmd - > rsplen ) ;
/*
* Get a local pointer to the built - in wqe and correct
* the cmd size to match NVME ' s 96 bytes and fix
* the dma address .
*/
/* 128 byte wqe support here */
wqe = ( union lpfc_wqe128 * ) & lpfc_ncmd - > cur_iocbq . wqe ;
/* Word 0-2 - NVME CMND IU (embedded payload) */
wqe - > generic . bde . tus . f . bdeFlags = BUFF_TYPE_BDE_IMMED ;
wqe - > generic . bde . tus . f . bdeSize = 60 ;
wqe - > generic . bde . addrHigh = 0 ;
wqe - > generic . bde . addrLow = 64 ; /* Word 16 */
/* Word 3 */
bf_set ( payload_offset_len , & wqe - > fcp_icmd ,
( nCmd - > rsplen + nCmd - > cmdlen ) ) ;
/* Word 10 */
bf_set ( wqe_nvme , & wqe - > fcp_icmd . wqe_com , 1 ) ;
bf_set ( wqe_wqes , & wqe - > fcp_icmd . wqe_com , 1 ) ;
/*
* Embed the payload in the last half of the WQE
* WQE words 16 - 30 get the NVME CMD IU payload
*
* WQE Word 16 is already setup with flags
* WQE words 17 - 19 get payload Words 2 - 4
* WQE words 20 - 21 get payload Words 6 - 7
* WQE words 22 - 29 get payload Words 16 - 23
*/
wptr = & wqe - > words [ 17 ] ; /* WQE ptr */
dptr = ( uint32_t * ) nCmd - > cmdaddr ; /* payload ptr */
dptr + = 2 ; /* Skip Words 0-1 in payload */
* wptr + + = * dptr + + ; /* Word 2 */
* wptr + + = * dptr + + ; /* Word 3 */
* wptr + + = * dptr + + ; /* Word 4 */
dptr + + ; /* Skip Word 5 in payload */
* wptr + + = * dptr + + ; /* Word 6 */
* wptr + + = * dptr + + ; /* Word 7 */
dptr + = 8 ; /* Skip Words 8-15 in payload */
* wptr + + = * dptr + + ; /* Word 16 */
* wptr + + = * dptr + + ; /* Word 17 */
* wptr + + = * dptr + + ; /* Word 18 */
* wptr + + = * dptr + + ; /* Word 19 */
* wptr + + = * dptr + + ; /* Word 20 */
* wptr + + = * dptr + + ; /* Word 21 */
* wptr + + = * dptr + + ; /* Word 22 */
* wptr = * dptr ; /* Word 23 */
}
2017-02-13 00:52:33 +03:00
# ifdef CONFIG_SCSI_LPFC_DEBUG_FS
static void
lpfc_nvme_ktime ( struct lpfc_hba * phba ,
struct lpfc_nvme_buf * lpfc_ncmd )
{
uint64_t seg1 , seg2 , seg3 , seg4 ;
if ( ! phba - > ktime_on )
return ;
if ( ! lpfc_ncmd - > ts_last_cmd | |
! lpfc_ncmd - > ts_cmd_start | |
! lpfc_ncmd - > ts_cmd_wqput | |
! lpfc_ncmd - > ts_isr_cmpl | |
! lpfc_ncmd - > ts_data_nvme )
return ;
if ( lpfc_ncmd - > ts_cmd_start < lpfc_ncmd - > ts_last_cmd )
return ;
if ( lpfc_ncmd - > ts_cmd_wqput < lpfc_ncmd - > ts_cmd_start )
return ;
if ( lpfc_ncmd - > ts_isr_cmpl < lpfc_ncmd - > ts_cmd_wqput )
return ;
if ( lpfc_ncmd - > ts_data_nvme < lpfc_ncmd - > ts_isr_cmpl )
return ;
/*
* Segment 1 - Time from Last FCP command cmpl is handed
* off to NVME Layer to start of next command .
* Segment 2 - Time from Driver receives a IO cmd start
* from NVME Layer to WQ put is done on IO cmd .
* Segment 3 - Time from Driver WQ put is done on IO cmd
* to MSI - X ISR for IO cmpl .
* Segment 4 - Time from MSI - X ISR for IO cmpl to when
* cmpl is handled off to the NVME Layer .
*/
seg1 = lpfc_ncmd - > ts_cmd_start - lpfc_ncmd - > ts_last_cmd ;
if ( seg1 > 5000000 ) /* 5 ms - for sequential IOs */
return ;
/* Calculate times relative to start of IO */
seg2 = ( lpfc_ncmd - > ts_cmd_wqput - lpfc_ncmd - > ts_cmd_start ) ;
seg3 = ( lpfc_ncmd - > ts_isr_cmpl -
lpfc_ncmd - > ts_cmd_start ) - seg2 ;
seg4 = ( lpfc_ncmd - > ts_data_nvme -
lpfc_ncmd - > ts_cmd_start ) - seg2 - seg3 ;
phba - > ktime_data_samples + + ;
phba - > ktime_seg1_total + = seg1 ;
if ( seg1 < phba - > ktime_seg1_min )
phba - > ktime_seg1_min = seg1 ;
else if ( seg1 > phba - > ktime_seg1_max )
phba - > ktime_seg1_max = seg1 ;
phba - > ktime_seg2_total + = seg2 ;
if ( seg2 < phba - > ktime_seg2_min )
phba - > ktime_seg2_min = seg2 ;
else if ( seg2 > phba - > ktime_seg2_max )
phba - > ktime_seg2_max = seg2 ;
phba - > ktime_seg3_total + = seg3 ;
if ( seg3 < phba - > ktime_seg3_min )
phba - > ktime_seg3_min = seg3 ;
else if ( seg3 > phba - > ktime_seg3_max )
phba - > ktime_seg3_max = seg3 ;
phba - > ktime_seg4_total + = seg4 ;
if ( seg4 < phba - > ktime_seg4_min )
phba - > ktime_seg4_min = seg4 ;
else if ( seg4 > phba - > ktime_seg4_max )
phba - > ktime_seg4_max = seg4 ;
lpfc_ncmd - > ts_last_cmd = 0 ;
lpfc_ncmd - > ts_cmd_start = 0 ;
lpfc_ncmd - > ts_cmd_wqput = 0 ;
lpfc_ncmd - > ts_isr_cmpl = 0 ;
lpfc_ncmd - > ts_data_nvme = 0 ;
}
# endif
2017-02-13 00:52:32 +03:00
/**
* lpfc_nvme_io_cmd_wqe_cmpl - Complete an NVME - over - FCP IO
* @ lpfc_pnvme : Pointer to the driver ' s nvme instance data
* @ lpfc_nvme_lport : Pointer to the driver ' s local port data
* @ lpfc_nvme_rport : Pointer to the rport getting the @ lpfc_nvme_ereq
*
* Driver registers this routine as it io request handler . This
* routine issues an fcp WQE with data from the @ lpfc_nvme_fcpreq
* data structure to the rport indicated in @ lpfc_nvme_rport .
*
* Return value :
* 0 - Success
* TODO : What are the failure codes .
* */
static void
lpfc_nvme_io_cmd_wqe_cmpl ( struct lpfc_hba * phba , struct lpfc_iocbq * pwqeIn ,
struct lpfc_wcqe_complete * wcqe )
{
struct lpfc_nvme_buf * lpfc_ncmd =
( struct lpfc_nvme_buf * ) pwqeIn - > context1 ;
struct lpfc_vport * vport = pwqeIn - > vport ;
struct nvmefc_fcp_req * nCmd ;
struct nvme_fc_ersp_iu * ep ;
struct nvme_fc_cmd_iu * cp ;
struct lpfc_nvme_rport * rport ;
struct lpfc_nodelist * ndlp ;
unsigned long flags ;
uint32_t code ;
uint16_t cid , sqhd , data ;
uint32_t * ptr ;
/* Sanity check on return of outstanding command */
if ( ! lpfc_ncmd | | ! lpfc_ncmd - > nvmeCmd | | ! lpfc_ncmd - > nrport ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NODE | LOG_NVME_IOERR ,
" 6071 Completion pointers bad on wqe %p. \n " ,
wcqe ) ;
return ;
}
phba - > fc4NvmeIoCmpls + + ;
nCmd = lpfc_ncmd - > nvmeCmd ;
rport = lpfc_ncmd - > nrport ;
2017-02-13 00:52:33 +03:00
lpfc_nvmeio_data ( phba , " NVME FCP CMPL: xri x%x stat x%x parm x%x \n " ,
lpfc_ncmd - > cur_iocbq . sli4_xritag ,
bf_get ( lpfc_wcqe_c_status , wcqe ) , wcqe - > parameter ) ;
2017-02-13 00:52:32 +03:00
/*
* Catch race where our node has transitioned , but the
* transport is still transitioning .
*/
ndlp = rport - > ndlp ;
if ( ! ndlp | | ! NLP_CHK_NODE_ACT ( ndlp ) ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NODE | LOG_NVME_IOERR ,
" 6061 rport %p, ndlp %p, DID x%06x ndlp "
" not ready. \n " ,
rport , ndlp , rport - > remoteport - > port_id ) ;
ndlp = lpfc_findnode_did ( vport , rport - > remoteport - > port_id ) ;
if ( ! ndlp ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_IOERR ,
" 6062 Ignoring NVME cmpl. No ndlp \n " ) ;
goto out_err ;
}
}
code = bf_get ( lpfc_wcqe_c_code , wcqe ) ;
if ( code = = CQE_CODE_NVME_ERSP ) {
/* For this type of CQE, we need to rebuild the rsp */
ep = ( struct nvme_fc_ersp_iu * ) nCmd - > rspaddr ;
/*
* Get Command Id from cmd to plug into response . This
* code is not needed in the next NVME Transport drop .
*/
cp = ( struct nvme_fc_cmd_iu * ) nCmd - > cmdaddr ;
cid = cp - > sqe . common . command_id ;
/*
* RSN is in CQE word 2
* SQHD is in CQE Word 3 bits 15 : 0
* Cmd Specific info is in CQE Word 1
* and in CQE Word 0 bits 15 : 0
*/
sqhd = bf_get ( lpfc_wcqe_c_sqhead , wcqe ) ;
/* Now lets build the NVME ERSP IU */
ep - > iu_len = cpu_to_be16 ( 8 ) ;
ep - > rsn = wcqe - > parameter ;
ep - > xfrd_len = cpu_to_be32 ( nCmd - > payload_length ) ;
ep - > rsvd12 = 0 ;
ptr = ( uint32_t * ) & ep - > cqe . result . u64 ;
* ptr + + = wcqe - > total_data_placed ;
data = bf_get ( lpfc_wcqe_c_ersp0 , wcqe ) ;
* ptr = ( uint32_t ) data ;
ep - > cqe . sq_head = sqhd ;
ep - > cqe . sq_id = nCmd - > sqid ;
ep - > cqe . command_id = cid ;
ep - > cqe . status = 0 ;
lpfc_ncmd - > status = IOSTAT_SUCCESS ;
lpfc_ncmd - > result = 0 ;
nCmd - > rcv_rsplen = LPFC_NVME_ERSP_LEN ;
nCmd - > transferred_length = nCmd - > payload_length ;
} else {
lpfc_ncmd - > status = ( bf_get ( lpfc_wcqe_c_status , wcqe ) &
LPFC_IOCB_STATUS_MASK ) ;
lpfc_ncmd - > result = wcqe - > parameter ;
/* For NVME, the only failure path that results in an
* IO error is when the adapter rejects it . All other
* conditions are a success case and resolved by the
* transport .
* IOSTAT_FCP_RSP_ERROR means :
* 1. Length of data received doesn ' t match total
* transfer length in WQE
* 2. If the RSP payload does NOT match these cases :
* a . RSP length 12 / 24 bytes and all zeros
* b . NVME ERSP
*/
switch ( lpfc_ncmd - > status ) {
case IOSTAT_SUCCESS :
nCmd - > transferred_length = wcqe - > total_data_placed ;
nCmd - > rcv_rsplen = 0 ;
nCmd - > status = 0 ;
break ;
case IOSTAT_FCP_RSP_ERROR :
nCmd - > transferred_length = wcqe - > total_data_placed ;
nCmd - > rcv_rsplen = wcqe - > parameter ;
nCmd - > status = 0 ;
/* Sanity check */
if ( nCmd - > rcv_rsplen = = LPFC_NVME_ERSP_LEN )
break ;
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_IOERR ,
" 6081 NVME Completion Protocol Error: "
" status x%x result x%x placed x%x \n " ,
lpfc_ncmd - > status , lpfc_ncmd - > result ,
wcqe - > total_data_placed ) ;
break ;
default :
out_err :
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_IOERR ,
" 6072 NVME Completion Error: "
" status x%x result x%x placed x%x \n " ,
lpfc_ncmd - > status , lpfc_ncmd - > result ,
wcqe - > total_data_placed ) ;
nCmd - > transferred_length = 0 ;
nCmd - > rcv_rsplen = 0 ;
nCmd - > status = NVME_SC_FC_TRANSPORT_ERROR ;
}
}
/* pick up SLI4 exhange busy condition */
if ( bf_get ( lpfc_wcqe_c_xb , wcqe ) )
lpfc_ncmd - > flags | = LPFC_SBUF_XBUSY ;
else
lpfc_ncmd - > flags & = ~ LPFC_SBUF_XBUSY ;
if ( ndlp & & NLP_CHK_NODE_ACT ( ndlp ) )
atomic_dec ( & ndlp - > cmd_pending ) ;
/* Update stats and complete the IO. There is
* no need for dma unprep because the nvme_transport
* owns the dma address .
*/
2017-02-13 00:52:33 +03:00
# ifdef CONFIG_SCSI_LPFC_DEBUG_FS
if ( phba - > ktime_on ) {
lpfc_ncmd - > ts_isr_cmpl = pwqeIn - > isr_timestamp ;
lpfc_ncmd - > ts_data_nvme = ktime_get_ns ( ) ;
phba - > ktime_last_cmd = lpfc_ncmd - > ts_data_nvme ;
lpfc_nvme_ktime ( phba , lpfc_ncmd ) ;
}
if ( phba - > cpucheck_on & LPFC_CHECK_NVME_IO ) {
if ( lpfc_ncmd - > cpu ! = smp_processor_id ( ) )
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_IOERR ,
" 6701 CPU Check cmpl: "
" cpu %d expect %d \n " ,
smp_processor_id ( ) , lpfc_ncmd - > cpu ) ;
if ( lpfc_ncmd - > cpu < LPFC_CHECK_CPU_CNT )
phba - > cpucheck_cmpl_io [ lpfc_ncmd - > cpu ] + + ;
}
# endif
2017-02-13 00:52:32 +03:00
nCmd - > done ( nCmd ) ;
spin_lock_irqsave ( & phba - > hbalock , flags ) ;
lpfc_ncmd - > nrport = NULL ;
spin_unlock_irqrestore ( & phba - > hbalock , flags ) ;
lpfc_release_nvme_buf ( phba , lpfc_ncmd ) ;
}
/**
* lpfc_nvme_prep_io_cmd - Issue an NVME - over - FCP IO
* @ lpfc_pnvme : Pointer to the driver ' s nvme instance data
* @ lpfc_nvme_lport : Pointer to the driver ' s local port data
* @ lpfc_nvme_rport : Pointer to the rport getting the @ lpfc_nvme_ereq
* @ lpfc_nvme_fcreq : IO request from nvme fc to driver .
* @ hw_queue_handle : Driver - returned handle in lpfc_nvme_create_queue
*
* Driver registers this routine as it io request handler . This
* routine issues an fcp WQE with data from the @ lpfc_nvme_fcpreq
* data structure to the rport indicated in @ lpfc_nvme_rport .
*
* Return value :
* 0 - Success
* TODO : What are the failure codes .
* */
static int
lpfc_nvme_prep_io_cmd ( struct lpfc_vport * vport ,
struct lpfc_nvme_buf * lpfc_ncmd ,
struct lpfc_nodelist * pnode )
{
struct lpfc_hba * phba = vport - > phba ;
struct nvmefc_fcp_req * nCmd = lpfc_ncmd - > nvmeCmd ;
struct lpfc_iocbq * pwqeq = & ( lpfc_ncmd - > cur_iocbq ) ;
union lpfc_wqe128 * wqe = ( union lpfc_wqe128 * ) & pwqeq - > wqe ;
uint32_t req_len ;
if ( ! pnode | | ! NLP_CHK_NODE_ACT ( pnode ) )
return - EINVAL ;
/*
* There are three possibilities here - use scatter - gather segment , use
* the single mapping , or neither .
*/
wqe - > fcp_iwrite . initial_xfer_len = 0 ;
if ( nCmd - > sg_cnt ) {
if ( nCmd - > io_dir = = NVMEFC_FCP_WRITE ) {
/* Word 5 */
if ( ( phba - > cfg_nvme_enable_fb ) & &
( pnode - > nlp_flag & NLP_FIRSTBURST ) ) {
req_len = lpfc_ncmd - > nvmeCmd - > payload_length ;
if ( req_len < pnode - > nvme_fb_size )
wqe - > fcp_iwrite . initial_xfer_len =
req_len ;
else
wqe - > fcp_iwrite . initial_xfer_len =
pnode - > nvme_fb_size ;
}
/* Word 7 */
bf_set ( wqe_cmnd , & wqe - > generic . wqe_com ,
CMD_FCP_IWRITE64_WQE ) ;
bf_set ( wqe_pu , & wqe - > generic . wqe_com ,
PARM_READ_CHECK ) ;
/* Word 10 */
bf_set ( wqe_qosd , & wqe - > fcp_iwrite . wqe_com , 0 ) ;
bf_set ( wqe_iod , & wqe - > fcp_iwrite . wqe_com ,
LPFC_WQE_IOD_WRITE ) ;
bf_set ( wqe_lenloc , & wqe - > fcp_iwrite . wqe_com ,
LPFC_WQE_LENLOC_WORD4 ) ;
if ( phba - > cfg_nvme_oas )
bf_set ( wqe_oas , & wqe - > fcp_iwrite . wqe_com , 1 ) ;
/* Word 11 */
bf_set ( wqe_cmd_type , & wqe - > generic . wqe_com ,
NVME_WRITE_CMD ) ;
/* Word 16 */
wqe - > words [ 16 ] = LPFC_NVME_EMBED_WRITE ;
phba - > fc4NvmeOutputRequests + + ;
} else {
/* Word 7 */
bf_set ( wqe_cmnd , & wqe - > generic . wqe_com ,
CMD_FCP_IREAD64_WQE ) ;
bf_set ( wqe_pu , & wqe - > generic . wqe_com ,
PARM_READ_CHECK ) ;
/* Word 10 */
bf_set ( wqe_qosd , & wqe - > fcp_iread . wqe_com , 0 ) ;
bf_set ( wqe_iod , & wqe - > fcp_iread . wqe_com ,
LPFC_WQE_IOD_READ ) ;
bf_set ( wqe_lenloc , & wqe - > fcp_iread . wqe_com ,
LPFC_WQE_LENLOC_WORD4 ) ;
if ( phba - > cfg_nvme_oas )
bf_set ( wqe_oas , & wqe - > fcp_iread . wqe_com , 1 ) ;
/* Word 11 */
bf_set ( wqe_cmd_type , & wqe - > generic . wqe_com ,
NVME_READ_CMD ) ;
/* Word 16 */
wqe - > words [ 16 ] = LPFC_NVME_EMBED_READ ;
phba - > fc4NvmeInputRequests + + ;
}
} else {
/* Word 4 */
wqe - > fcp_icmd . rsrvd4 = 0 ;
/* Word 7 */
bf_set ( wqe_cmnd , & wqe - > generic . wqe_com , CMD_FCP_ICMND64_WQE ) ;
bf_set ( wqe_pu , & wqe - > generic . wqe_com , 0 ) ;
/* Word 10 */
bf_set ( wqe_qosd , & wqe - > fcp_icmd . wqe_com , 1 ) ;
bf_set ( wqe_iod , & wqe - > fcp_icmd . wqe_com , LPFC_WQE_IOD_WRITE ) ;
bf_set ( wqe_lenloc , & wqe - > fcp_icmd . wqe_com ,
LPFC_WQE_LENLOC_NONE ) ;
if ( phba - > cfg_nvme_oas )
bf_set ( wqe_oas , & wqe - > fcp_icmd . wqe_com , 1 ) ;
/* Word 11 */
bf_set ( wqe_cmd_type , & wqe - > generic . wqe_com , NVME_READ_CMD ) ;
/* Word 16 */
wqe - > words [ 16 ] = LPFC_NVME_EMBED_CMD ;
phba - > fc4NvmeControlRequests + + ;
}
/*
* Finish initializing those WQE fields that are independent
* of the nvme_cmnd request_buffer
*/
/* Word 6 */
bf_set ( wqe_ctxt_tag , & wqe - > generic . wqe_com ,
phba - > sli4_hba . rpi_ids [ pnode - > nlp_rpi ] ) ;
bf_set ( wqe_xri_tag , & wqe - > generic . wqe_com , pwqeq - > sli4_xritag ) ;
/* Word 7 */
/* Preserve Class data in the ndlp. */
bf_set ( wqe_class , & wqe - > generic . wqe_com ,
( pnode - > nlp_fcp_info & 0x0f ) ) ;
/* Word 8 */
wqe - > generic . wqe_com . abort_tag = pwqeq - > iotag ;
/* Word 9 */
bf_set ( wqe_reqtag , & wqe - > generic . wqe_com , pwqeq - > iotag ) ;
/* Word 11 */
bf_set ( wqe_cqid , & wqe - > generic . wqe_com , LPFC_WQE_CQ_ID_DEFAULT ) ;
pwqeq - > vport = vport ;
return 0 ;
}
/**
* lpfc_nvme_prep_io_dma - Issue an NVME - over - FCP IO
* @ lpfc_pnvme : Pointer to the driver ' s nvme instance data
* @ lpfc_nvme_lport : Pointer to the driver ' s local port data
* @ lpfc_nvme_rport : Pointer to the rport getting the @ lpfc_nvme_ereq
* @ lpfc_nvme_fcreq : IO request from nvme fc to driver .
* @ hw_queue_handle : Driver - returned handle in lpfc_nvme_create_queue
*
* Driver registers this routine as it io request handler . This
* routine issues an fcp WQE with data from the @ lpfc_nvme_fcpreq
* data structure to the rport indicated in @ lpfc_nvme_rport .
*
* Return value :
* 0 - Success
* TODO : What are the failure codes .
* */
static int
lpfc_nvme_prep_io_dma ( struct lpfc_vport * vport ,
struct lpfc_nvme_buf * lpfc_ncmd )
{
struct lpfc_hba * phba = vport - > phba ;
struct nvmefc_fcp_req * nCmd = lpfc_ncmd - > nvmeCmd ;
union lpfc_wqe128 * wqe = ( union lpfc_wqe128 * ) & lpfc_ncmd - > cur_iocbq . wqe ;
struct sli4_sge * sgl = lpfc_ncmd - > nvme_sgl ;
struct scatterlist * data_sg ;
struct sli4_sge * first_data_sgl ;
dma_addr_t physaddr ;
uint32_t num_bde = 0 ;
uint32_t dma_len ;
uint32_t dma_offset = 0 ;
int nseg , i ;
/* Fix up the command and response DMA stuff. */
lpfc_nvme_adj_fcp_sgls ( vport , lpfc_ncmd , nCmd ) ;
/*
* There are three possibilities here - use scatter - gather segment , use
* the single mapping , or neither .
*/
if ( nCmd - > sg_cnt ) {
/*
* Jump over the cmd and rsp SGEs . The fix routine
* has already adjusted for this .
*/
sgl + = 2 ;
first_data_sgl = sgl ;
lpfc_ncmd - > seg_cnt = nCmd - > sg_cnt ;
if ( lpfc_ncmd - > seg_cnt > phba - > cfg_sg_seg_cnt ) {
lpfc_printf_log ( phba , KERN_ERR , LOG_NVME_IOERR ,
" 6058 Too many sg segments from "
" NVME Transport. Max %d, "
" nvmeIO sg_cnt %d \n " ,
phba - > cfg_sg_seg_cnt ,
lpfc_ncmd - > seg_cnt ) ;
lpfc_ncmd - > seg_cnt = 0 ;
return 1 ;
}
/*
* The driver established a maximum scatter - gather segment count
* during probe that limits the number of sg elements in any
* single nvme command . Just run through the seg_cnt and format
* the sge ' s .
*/
nseg = nCmd - > sg_cnt ;
data_sg = nCmd - > first_sgl ;
for ( i = 0 ; i < nseg ; i + + ) {
if ( data_sg = = NULL ) {
lpfc_printf_log ( phba , KERN_ERR , LOG_NVME_IOERR ,
" 6059 dptr err %d, nseg %d \n " ,
i , nseg ) ;
lpfc_ncmd - > seg_cnt = 0 ;
return 1 ;
}
physaddr = data_sg - > dma_address ;
dma_len = data_sg - > length ;
sgl - > addr_lo = cpu_to_le32 ( putPaddrLow ( physaddr ) ) ;
sgl - > addr_hi = cpu_to_le32 ( putPaddrHigh ( physaddr ) ) ;
sgl - > word2 = le32_to_cpu ( sgl - > word2 ) ;
if ( ( num_bde + 1 ) = = nseg )
bf_set ( lpfc_sli4_sge_last , sgl , 1 ) ;
else
bf_set ( lpfc_sli4_sge_last , sgl , 0 ) ;
bf_set ( lpfc_sli4_sge_offset , sgl , dma_offset ) ;
bf_set ( lpfc_sli4_sge_type , sgl , LPFC_SGE_TYPE_DATA ) ;
sgl - > word2 = cpu_to_le32 ( sgl - > word2 ) ;
sgl - > sge_len = cpu_to_le32 ( dma_len ) ;
dma_offset + = dma_len ;
data_sg = sg_next ( data_sg ) ;
sgl + + ;
}
} else {
/* For this clause to be valid, the payload_length
* and sg_cnt must zero .
*/
if ( nCmd - > payload_length ! = 0 ) {
lpfc_printf_log ( phba , KERN_ERR , LOG_NVME_IOERR ,
" 6063 NVME DMA Prep Err: sg_cnt %d "
" payload_length x%x \n " ,
nCmd - > sg_cnt , nCmd - > payload_length ) ;
return 1 ;
}
}
/*
* Due to difference in data length between DIF / non - DIF paths ,
* we need to set word 4 of WQE here
*/
wqe - > fcp_iread . total_xfer_len = nCmd - > payload_length ;
return 0 ;
}
/**
* lpfc_nvme_fcp_io_submit - Issue an NVME - over - FCP IO
* @ lpfc_pnvme : Pointer to the driver ' s nvme instance data
* @ lpfc_nvme_lport : Pointer to the driver ' s local port data
* @ lpfc_nvme_rport : Pointer to the rport getting the @ lpfc_nvme_ereq
* @ lpfc_nvme_fcreq : IO request from nvme fc to driver .
* @ hw_queue_handle : Driver - returned handle in lpfc_nvme_create_queue
*
* Driver registers this routine as it io request handler . This
* routine issues an fcp WQE with data from the @ lpfc_nvme_fcpreq
* data structure to the rport
indicated in @ lpfc_nvme_rport .
*
* Return value :
* 0 - Success
* TODO : What are the failure codes .
* */
static int
lpfc_nvme_fcp_io_submit ( struct nvme_fc_local_port * pnvme_lport ,
struct nvme_fc_remote_port * pnvme_rport ,
void * hw_queue_handle ,
struct nvmefc_fcp_req * pnvme_fcreq )
{
int ret = 0 ;
struct lpfc_nvme_lport * lport ;
struct lpfc_vport * vport ;
struct lpfc_hba * phba ;
struct lpfc_nodelist * ndlp ;
struct lpfc_nvme_buf * lpfc_ncmd ;
struct lpfc_nvme_rport * rport ;
struct lpfc_nvme_qhandle * lpfc_queue_info ;
2017-02-13 00:52:33 +03:00
# ifdef CONFIG_SCSI_LPFC_DEBUG_FS
uint64_t start = 0 ;
# endif
2017-02-13 00:52:32 +03:00
lport = ( struct lpfc_nvme_lport * ) pnvme_lport - > private ;
vport = lport - > vport ;
phba = vport - > phba ;
2017-02-13 00:52:33 +03:00
# ifdef CONFIG_SCSI_LPFC_DEBUG_FS
if ( phba - > ktime_on )
start = ktime_get_ns ( ) ;
# endif
2017-02-13 00:52:32 +03:00
rport = ( struct lpfc_nvme_rport * ) pnvme_rport - > private ;
lpfc_queue_info = ( struct lpfc_nvme_qhandle * ) hw_queue_handle ;
/*
* Catch race where our node has transitioned , but the
* transport is still transitioning .
*/
ndlp = rport - > ndlp ;
if ( ! ndlp | | ! NLP_CHK_NODE_ACT ( ndlp ) ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NODE | LOG_NVME_IOERR ,
" 6053 rport %p, ndlp %p, DID x%06x "
" ndlp not ready. \n " ,
rport , ndlp , pnvme_rport - > port_id ) ;
ndlp = lpfc_findnode_did ( vport , pnvme_rport - > port_id ) ;
if ( ! ndlp ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_IOERR ,
" 6066 Missing node for DID %x \n " ,
pnvme_rport - > port_id ) ;
ret = - ENODEV ;
goto out_fail ;
}
}
/* The remote node has to be a mapped target or it's an error. */
if ( ( ndlp - > nlp_type & NLP_NVME_TARGET ) & &
( ndlp - > nlp_state ! = NLP_STE_MAPPED_NODE ) ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NODE | LOG_NVME_IOERR ,
" 6036 rport %p, DID x%06x not ready for "
" IO. State x%x, Type x%x \n " ,
rport , pnvme_rport - > port_id ,
ndlp - > nlp_state , ndlp - > nlp_type ) ;
ret = - ENODEV ;
goto out_fail ;
}
/* The node is shared with FCP IO, make sure the IO pending count does
* not exceed the programmed depth .
*/
if ( atomic_read ( & ndlp - > cmd_pending ) > = ndlp - > cmd_qdepth ) {
ret = - EAGAIN ;
goto out_fail ;
}
lpfc_ncmd = lpfc_get_nvme_buf ( phba , ndlp ) ;
if ( lpfc_ncmd = = NULL ) {
lpfc_printf_vlog ( vport , KERN_INFO , LOG_NVME_IOERR ,
" 6065 driver's buffer pool is empty, "
" IO failed \n " ) ;
ret = - ENOMEM ;
goto out_fail ;
}
2017-02-13 00:52:33 +03:00
# ifdef CONFIG_SCSI_LPFC_DEBUG_FS
if ( phba - > ktime_on ) {
lpfc_ncmd - > ts_cmd_start = start ;
lpfc_ncmd - > ts_last_cmd = phba - > ktime_last_cmd ;
}
# endif
2017-02-13 00:52:32 +03:00
/*
* Store the data needed by the driver to issue , abort , and complete
* an IO .
* Do not let the IO hang out forever . There is no midlayer issuing
* an abort so inform the FW of the maximum IO pending time .
*/
pnvme_fcreq - > private = ( void * ) lpfc_ncmd ;
lpfc_ncmd - > nvmeCmd = pnvme_fcreq ;
lpfc_ncmd - > nrport = rport ;
lpfc_ncmd - > start_time = jiffies ;
lpfc_nvme_prep_io_cmd ( vport , lpfc_ncmd , ndlp ) ;
ret = lpfc_nvme_prep_io_dma ( vport , lpfc_ncmd ) ;
if ( ret ) {
ret = - ENOMEM ;
goto out_free_nvme_buf ;
}
atomic_inc ( & ndlp - > cmd_pending ) ;
/*
* Issue the IO on the WQ indicated by index in the hw_queue_handle .
* This identfier was create in our hardware queue create callback
* routine . The driver now is dependent on the IO queue steering from
* the transport . We are trusting the upper NVME layers know which
* index to use and that they have affinitized a CPU to this hardware
* queue . A hardware queue maps to a driver MSI - X vector / EQ / CQ / WQ .
*/
lpfc_ncmd - > cur_iocbq . hba_wqidx = lpfc_queue_info - > index ;
2017-02-13 00:52:33 +03:00
lpfc_nvmeio_data ( phba , " NVME FCP XMIT: xri x%x idx %d to %06x \n " ,
lpfc_ncmd - > cur_iocbq . sli4_xritag ,
lpfc_queue_info - > index , ndlp - > nlp_DID ) ;
2017-02-13 00:52:32 +03:00
ret = lpfc_sli4_issue_wqe ( phba , LPFC_FCP_RING , & lpfc_ncmd - > cur_iocbq ) ;
if ( ret ) {
atomic_dec ( & ndlp - > cmd_pending ) ;
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_IOERR ,
" 6113 FCP could not issue WQE err %x "
" sid: x%x did: x%x oxid: x%x \n " ,
ret , vport - > fc_myDID , ndlp - > nlp_DID ,
lpfc_ncmd - > cur_iocbq . sli4_xritag ) ;
ret = - EINVAL ;
goto out_free_nvme_buf ;
}
2017-02-13 00:52:33 +03:00
# ifdef CONFIG_SCSI_LPFC_DEBUG_FS
if ( phba - > ktime_on )
lpfc_ncmd - > ts_cmd_wqput = ktime_get_ns ( ) ;
if ( phba - > cpucheck_on & LPFC_CHECK_NVME_IO ) {
lpfc_ncmd - > cpu = smp_processor_id ( ) ;
if ( lpfc_ncmd - > cpu ! = lpfc_queue_info - > index ) {
/* Check for admin queue */
if ( lpfc_queue_info - > qidx ) {
lpfc_printf_vlog ( vport ,
KERN_ERR , LOG_NVME_IOERR ,
" 6702 CPU Check cmd: "
" cpu %d wq %d \n " ,
lpfc_ncmd - > cpu ,
lpfc_queue_info - > index ) ;
}
lpfc_ncmd - > cpu = lpfc_queue_info - > index ;
}
if ( lpfc_ncmd - > cpu < LPFC_CHECK_CPU_CNT )
phba - > cpucheck_xmt_io [ lpfc_ncmd - > cpu ] + + ;
}
# endif
2017-02-13 00:52:32 +03:00
return 0 ;
out_free_nvme_buf :
lpfc_release_nvme_buf ( phba , lpfc_ncmd ) ;
out_fail :
return ret ;
}
/**
* lpfc_nvme_abort_fcreq_cmpl - Complete an NVME FCP abort request .
* @ phba : Pointer to HBA context object
* @ cmdiocb : Pointer to command iocb object .
* @ rspiocb : Pointer to response iocb object .
*
* This is the callback function for any NVME FCP IO that was aborted .
*
* Return value :
* None
* */
void
lpfc_nvme_abort_fcreq_cmpl ( struct lpfc_hba * phba , struct lpfc_iocbq * cmdiocb ,
struct lpfc_wcqe_complete * abts_cmpl )
{
lpfc_printf_log ( phba , KERN_ERR , LOG_NVME ,
" 6145 ABORT_XRI_CN completing on rpi x%x "
" original iotag x%x, abort cmd iotag x%x "
" req_tag x%x, status x%x, hwstatus x%x \n " ,
cmdiocb - > iocb . un . acxri . abortContextTag ,
cmdiocb - > iocb . un . acxri . abortIoTag ,
cmdiocb - > iotag ,
bf_get ( lpfc_wcqe_c_request_tag , abts_cmpl ) ,
bf_get ( lpfc_wcqe_c_status , abts_cmpl ) ,
bf_get ( lpfc_wcqe_c_hw_status , abts_cmpl ) ) ;
lpfc_sli_release_iocbq ( phba , cmdiocb ) ;
}
/**
* lpfc_nvme_fcp_abort - Issue an NVME - over - FCP ABTS
* @ lpfc_pnvme : Pointer to the driver ' s nvme instance data
* @ lpfc_nvme_lport : Pointer to the driver ' s local port data
* @ lpfc_nvme_rport : Pointer to the rport getting the @ lpfc_nvme_ereq
* @ lpfc_nvme_fcreq : IO request from nvme fc to driver .
* @ hw_queue_handle : Driver - returned handle in lpfc_nvme_create_queue
*
* Driver registers this routine as its nvme request io abort handler . This
* routine issues an fcp Abort WQE with data from the @ lpfc_nvme_fcpreq
* data structure to the rport indicated in @ lpfc_nvme_rport . This routine
* is executed asynchronously - one the target is validated as " MAPPED " and
* ready for IO , the driver issues the abort request and returns .
*
* Return value :
* None
* */
static void
lpfc_nvme_fcp_abort ( struct nvme_fc_local_port * pnvme_lport ,
struct nvme_fc_remote_port * pnvme_rport ,
void * hw_queue_handle ,
struct nvmefc_fcp_req * pnvme_fcreq )
{
struct lpfc_nvme_lport * lport ;
struct lpfc_vport * vport ;
struct lpfc_hba * phba ;
struct lpfc_nodelist * ndlp ;
struct lpfc_nvme_rport * rport ;
struct lpfc_nvme_buf * lpfc_nbuf ;
struct lpfc_iocbq * abts_buf ;
struct lpfc_iocbq * nvmereq_wqe ;
union lpfc_wqe * abts_wqe ;
unsigned long flags ;
int ret_val ;
lport = ( struct lpfc_nvme_lport * ) pnvme_lport - > private ;
rport = ( struct lpfc_nvme_rport * ) pnvme_rport - > private ;
vport = lport - > vport ;
phba = vport - > phba ;
/* Announce entry to new IO submit field. */
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_ABTS ,
" 6002 Abort Request to rport DID x%06x "
" for nvme_fc_req %p \n " ,
pnvme_rport - > port_id ,
pnvme_fcreq ) ;
/*
* Catch race where our node has transitioned , but the
* transport is still transitioning .
*/
ndlp = rport - > ndlp ;
if ( ! ndlp | | ! NLP_CHK_NODE_ACT ( ndlp ) ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NODE | LOG_NVME_ABTS ,
" 6054 rport %p, ndlp %p, DID x%06x ndlp "
" not ready. \n " ,
rport , ndlp , pnvme_rport - > port_id ) ;
ndlp = lpfc_findnode_did ( vport , pnvme_rport - > port_id ) ;
if ( ! ndlp ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_ABTS ,
" 6055 Could not find node for "
" DID %x \n " ,
pnvme_rport - > port_id ) ;
return ;
}
}
/* The remote node has to be ready to send an abort. */
if ( ( ndlp - > nlp_state ! = NLP_STE_MAPPED_NODE ) & &
! ( ndlp - > nlp_type & NLP_NVME_TARGET ) ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NODE | LOG_NVME_ABTS ,
" 6048 rport %p, DID x%06x not ready for "
" IO. State x%x, Type x%x \n " ,
rport , pnvme_rport - > port_id ,
ndlp - > nlp_state , ndlp - > nlp_type ) ;
return ;
}
/* If the hba is getting reset, this flag is set. It is
* cleared when the reset is complete and rings reestablished .
*/
spin_lock_irqsave ( & phba - > hbalock , flags ) ;
/* driver queued commands are in process of being flushed */
if ( phba - > hba_flag & HBA_NVME_IOQ_FLUSH ) {
spin_unlock_irqrestore ( & phba - > hbalock , flags ) ;
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME ,
" 6139 Driver in reset cleanup - flushing "
" NVME Req now. hba_flag x%x \n " ,
phba - > hba_flag ) ;
return ;
}
lpfc_nbuf = ( struct lpfc_nvme_buf * ) pnvme_fcreq - > private ;
if ( ! lpfc_nbuf ) {
spin_unlock_irqrestore ( & phba - > hbalock , flags ) ;
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME ,
" 6140 NVME IO req has no matching lpfc nvme "
" io buffer. Skipping abort req. \n " ) ;
return ;
} else if ( ! lpfc_nbuf - > nvmeCmd ) {
spin_unlock_irqrestore ( & phba - > hbalock , flags ) ;
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME ,
" 6141 lpfc NVME IO req has no nvme_fcreq "
" io buffer. Skipping abort req. \n " ) ;
return ;
}
/*
* The lpfc_nbuf and the mapped nvme_fcreq in the driver ' s
* state must match the nvme_fcreq passed by the nvme
* transport . If they don ' t match , it is likely the driver
* has already completed the NVME IO and the nvme transport
* has not seen it yet .
*/
if ( lpfc_nbuf - > nvmeCmd ! = pnvme_fcreq ) {
spin_unlock_irqrestore ( & phba - > hbalock , flags ) ;
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME ,
" 6143 NVME req mismatch: "
" lpfc_nbuf %p nvmeCmd %p, "
" pnvme_fcreq %p. Skipping Abort \n " ,
lpfc_nbuf , lpfc_nbuf - > nvmeCmd ,
pnvme_fcreq ) ;
return ;
}
/* Don't abort IOs no longer on the pending queue. */
nvmereq_wqe = & lpfc_nbuf - > cur_iocbq ;
if ( ! ( nvmereq_wqe - > iocb_flag & LPFC_IO_ON_TXCMPLQ ) ) {
spin_unlock_irqrestore ( & phba - > hbalock , flags ) ;
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME ,
" 6142 NVME IO req %p not queued - skipping "
" abort req \n " ,
pnvme_fcreq ) ;
return ;
}
2017-02-13 00:52:33 +03:00
lpfc_nvmeio_data ( phba , " NVME FCP ABORT: xri x%x idx %d to %06x \n " ,
nvmereq_wqe - > sli4_xritag ,
nvmereq_wqe - > hba_wqidx , ndlp - > nlp_DID ) ;
2017-02-13 00:52:32 +03:00
/* Outstanding abort is in progress */
if ( nvmereq_wqe - > iocb_flag & LPFC_DRIVER_ABORTED ) {
spin_unlock_irqrestore ( & phba - > hbalock , flags ) ;
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME ,
" 6144 Outstanding NVME I/O Abort Request "
" still pending on nvme_fcreq %p, "
" lpfc_ncmd %p \n " ,
pnvme_fcreq , lpfc_nbuf ) ;
return ;
}
abts_buf = __lpfc_sli_get_iocbq ( phba ) ;
if ( ! abts_buf ) {
spin_unlock_irqrestore ( & phba - > hbalock , flags ) ;
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME ,
" 6136 No available abort wqes. Skipping "
" Abts req for nvme_fcreq %p. \n " ,
pnvme_fcreq ) ;
return ;
}
/* Ready - mark outstanding as aborted by driver. */
nvmereq_wqe - > iocb_flag | = LPFC_DRIVER_ABORTED ;
/* Complete prepping the abort wqe and issue to the FW. */
abts_wqe = & abts_buf - > wqe ;
/* WQEs are reused. Clear stale data and set key fields to
* zero like ia , iaab , iaar , xri_tag , and ctxt_tag .
*/
memset ( abts_wqe , 0 , sizeof ( union lpfc_wqe ) ) ;
bf_set ( abort_cmd_criteria , & abts_wqe - > abort_cmd , T_XRI_TAG ) ;
/* word 7 */
bf_set ( wqe_ct , & abts_wqe - > abort_cmd . wqe_com , 0 ) ;
bf_set ( wqe_cmnd , & abts_wqe - > abort_cmd . wqe_com , CMD_ABORT_XRI_CX ) ;
bf_set ( wqe_class , & abts_wqe - > abort_cmd . wqe_com ,
nvmereq_wqe - > iocb . ulpClass ) ;
/* word 8 - tell the FW to abort the IO associated with this
* outstanding exchange ID .
*/
abts_wqe - > abort_cmd . wqe_com . abort_tag = nvmereq_wqe - > sli4_xritag ;
/* word 9 - this is the iotag for the abts_wqe completion. */
bf_set ( wqe_reqtag , & abts_wqe - > abort_cmd . wqe_com ,
abts_buf - > iotag ) ;
/* word 10 */
bf_set ( wqe_wqid , & abts_wqe - > abort_cmd . wqe_com , nvmereq_wqe - > hba_wqidx ) ;
bf_set ( wqe_qosd , & abts_wqe - > abort_cmd . wqe_com , 1 ) ;
bf_set ( wqe_lenloc , & abts_wqe - > abort_cmd . wqe_com , LPFC_WQE_LENLOC_NONE ) ;
/* word 11 */
bf_set ( wqe_cmd_type , & abts_wqe - > abort_cmd . wqe_com , OTHER_COMMAND ) ;
bf_set ( wqe_wqec , & abts_wqe - > abort_cmd . wqe_com , 1 ) ;
bf_set ( wqe_cqid , & abts_wqe - > abort_cmd . wqe_com , LPFC_WQE_CQ_ID_DEFAULT ) ;
/* ABTS WQE must go to the same WQ as the WQE to be aborted */
abts_buf - > iocb_flag | = LPFC_IO_NVME ;
abts_buf - > hba_wqidx = nvmereq_wqe - > hba_wqidx ;
abts_buf - > vport = vport ;
abts_buf - > wqe_cmpl = lpfc_nvme_abort_fcreq_cmpl ;
ret_val = lpfc_sli4_issue_wqe ( phba , LPFC_FCP_RING , abts_buf ) ;
spin_unlock_irqrestore ( & phba - > hbalock , flags ) ;
if ( ret_val = = IOCB_ERROR ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME ,
" 6137 Failed abts issue_wqe with status x%x "
" for nvme_fcreq %p. \n " ,
ret_val , pnvme_fcreq ) ;
lpfc_sli_release_iocbq ( phba , abts_buf ) ;
return ;
}
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME ,
" 6138 Transport Abort NVME Request Issued for \n "
" ox_id x%x on reqtag x%x \n " ,
nvmereq_wqe - > sli4_xritag ,
abts_buf - > iotag ) ;
}
/* Declare and initialization an instance of the FC NVME template. */
static struct nvme_fc_port_template lpfc_nvme_template = {
/* initiator-based functions */
. localport_delete = lpfc_nvme_localport_delete ,
. remoteport_delete = lpfc_nvme_remoteport_delete ,
. create_queue = lpfc_nvme_create_queue ,
. delete_queue = lpfc_nvme_delete_queue ,
. ls_req = lpfc_nvme_ls_req ,
. fcp_io = lpfc_nvme_fcp_io_submit ,
. ls_abort = lpfc_nvme_ls_abort ,
. fcp_abort = lpfc_nvme_fcp_abort ,
. max_hw_queues = 1 ,
. max_sgl_segments = LPFC_NVME_DEFAULT_SEGS ,
. max_dif_sgl_segments = LPFC_NVME_DEFAULT_SEGS ,
. dma_boundary = 0xFFFFFFFF ,
/* Sizes of additional private data for data structures.
* No use for the last two sizes at this time .
*/
. local_priv_sz = sizeof ( struct lpfc_nvme_lport ) ,
. remote_priv_sz = sizeof ( struct lpfc_nvme_rport ) ,
. lsrqst_priv_sz = 0 ,
. fcprqst_priv_sz = 0 ,
} ;
/**
* lpfc_sli4_post_nvme_sgl_block - post a block of nvme sgl list to firmware
* @ phba : pointer to lpfc hba data structure .
* @ nblist : pointer to nvme buffer list .
* @ count : number of scsi buffers on the list .
*
* This routine is invoked to post a block of @ count scsi sgl pages from a
* SCSI buffer list @ nblist to the HBA using non - embedded mailbox command .
* No Lock is held .
*
* */
static int
lpfc_sli4_post_nvme_sgl_block ( struct lpfc_hba * phba ,
struct list_head * nblist ,
int count )
{
struct lpfc_nvme_buf * lpfc_ncmd ;
struct lpfc_mbx_post_uembed_sgl_page1 * sgl ;
struct sgl_page_pairs * sgl_pg_pairs ;
void * viraddr ;
LPFC_MBOXQ_t * mbox ;
uint32_t reqlen , alloclen , pg_pairs ;
uint32_t mbox_tmo ;
uint16_t xritag_start = 0 ;
int rc = 0 ;
uint32_t shdr_status , shdr_add_status ;
dma_addr_t pdma_phys_bpl1 ;
union lpfc_sli4_cfg_shdr * shdr ;
/* Calculate the requested length of the dma memory */
reqlen = count * sizeof ( struct sgl_page_pairs ) +
sizeof ( union lpfc_sli4_cfg_shdr ) + sizeof ( uint32_t ) ;
if ( reqlen > SLI4_PAGE_SIZE ) {
lpfc_printf_log ( phba , KERN_WARNING , LOG_INIT ,
" 6118 Block sgl registration required DMA "
" size (%d) great than a page \n " , reqlen ) ;
return - ENOMEM ;
}
mbox = mempool_alloc ( phba - > mbox_mem_pool , GFP_KERNEL ) ;
if ( ! mbox ) {
lpfc_printf_log ( phba , KERN_ERR , LOG_INIT ,
" 6119 Failed to allocate mbox cmd memory \n " ) ;
return - ENOMEM ;
}
/* Allocate DMA memory and set up the non-embedded mailbox command */
alloclen = lpfc_sli4_config ( phba , mbox , LPFC_MBOX_SUBSYSTEM_FCOE ,
LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES , reqlen ,
LPFC_SLI4_MBX_NEMBED ) ;
if ( alloclen < reqlen ) {
lpfc_printf_log ( phba , KERN_ERR , LOG_INIT ,
" 6120 Allocated DMA memory size (%d) is "
" less than the requested DMA memory "
" size (%d) \n " , alloclen , reqlen ) ;
lpfc_sli4_mbox_cmd_free ( phba , mbox ) ;
return - ENOMEM ;
}
/* Get the first SGE entry from the non-embedded DMA memory */
viraddr = mbox - > sge_array - > addr [ 0 ] ;
/* Set up the SGL pages in the non-embedded DMA pages */
sgl = ( struct lpfc_mbx_post_uembed_sgl_page1 * ) viraddr ;
sgl_pg_pairs = & sgl - > sgl_pg_pairs ;
pg_pairs = 0 ;
list_for_each_entry ( lpfc_ncmd , nblist , list ) {
/* Set up the sge entry */
sgl_pg_pairs - > sgl_pg0_addr_lo =
cpu_to_le32 ( putPaddrLow ( lpfc_ncmd - > dma_phys_sgl ) ) ;
sgl_pg_pairs - > sgl_pg0_addr_hi =
cpu_to_le32 ( putPaddrHigh ( lpfc_ncmd - > dma_phys_sgl ) ) ;
if ( phba - > cfg_sg_dma_buf_size > SGL_PAGE_SIZE )
pdma_phys_bpl1 = lpfc_ncmd - > dma_phys_sgl +
SGL_PAGE_SIZE ;
else
pdma_phys_bpl1 = 0 ;
sgl_pg_pairs - > sgl_pg1_addr_lo =
cpu_to_le32 ( putPaddrLow ( pdma_phys_bpl1 ) ) ;
sgl_pg_pairs - > sgl_pg1_addr_hi =
cpu_to_le32 ( putPaddrHigh ( pdma_phys_bpl1 ) ) ;
/* Keep the first xritag on the list */
if ( pg_pairs = = 0 )
xritag_start = lpfc_ncmd - > cur_iocbq . sli4_xritag ;
sgl_pg_pairs + + ;
pg_pairs + + ;
}
bf_set ( lpfc_post_sgl_pages_xri , sgl , xritag_start ) ;
bf_set ( lpfc_post_sgl_pages_xricnt , sgl , pg_pairs ) ;
/* Perform endian conversion if necessary */
sgl - > word0 = cpu_to_le32 ( sgl - > word0 ) ;
if ( ! phba - > sli4_hba . intr_enable )
rc = lpfc_sli_issue_mbox ( phba , mbox , MBX_POLL ) ;
else {
mbox_tmo = lpfc_mbox_tmo_val ( phba , mbox ) ;
rc = lpfc_sli_issue_mbox_wait ( phba , mbox , mbox_tmo ) ;
}
shdr = ( union lpfc_sli4_cfg_shdr * ) & sgl - > cfg_shdr ;
shdr_status = bf_get ( lpfc_mbox_hdr_status , & shdr - > response ) ;
shdr_add_status = bf_get ( lpfc_mbox_hdr_add_status , & shdr - > response ) ;
if ( rc ! = MBX_TIMEOUT )
lpfc_sli4_mbox_cmd_free ( phba , mbox ) ;
if ( shdr_status | | shdr_add_status | | rc ) {
lpfc_printf_log ( phba , KERN_ERR , LOG_SLI ,
" 6125 POST_SGL_BLOCK mailbox command failed "
" status x%x add_status x%x mbx status x%x \n " ,
shdr_status , shdr_add_status , rc ) ;
rc = - ENXIO ;
}
return rc ;
}
/**
* lpfc_post_nvme_sgl_list - Post blocks of nvme buffer sgls from a list
* @ phba : pointer to lpfc hba data structure .
* @ post_nblist : pointer to the nvme buffer list .
*
* This routine walks a list of nvme buffers that was passed in . It attempts
* to construct blocks of nvme buffer sgls which contains contiguous xris and
* uses the non - embedded SGL block post mailbox commands to post to the port .
* For single NVME buffer sgl with non - contiguous xri , if any , it shall use
* embedded SGL post mailbox command for posting . The @ post_nblist passed in
* must be local list , thus no lock is needed when manipulate the list .
*
* Returns : 0 = failure , non - zero number of successfully posted buffers .
* */
static int
lpfc_post_nvme_sgl_list ( struct lpfc_hba * phba ,
struct list_head * post_nblist , int sb_count )
{
struct lpfc_nvme_buf * lpfc_ncmd , * lpfc_ncmd_next ;
int status , sgl_size ;
int post_cnt = 0 , block_cnt = 0 , num_posting = 0 , num_posted = 0 ;
dma_addr_t pdma_phys_sgl1 ;
int last_xritag = NO_XRI ;
int cur_xritag ;
LIST_HEAD ( prep_nblist ) ;
LIST_HEAD ( blck_nblist ) ;
LIST_HEAD ( nvme_nblist ) ;
/* sanity check */
if ( sb_count < = 0 )
return - EINVAL ;
sgl_size = phba - > cfg_sg_dma_buf_size ;
list_for_each_entry_safe ( lpfc_ncmd , lpfc_ncmd_next , post_nblist , list ) {
list_del_init ( & lpfc_ncmd - > list ) ;
block_cnt + + ;
if ( ( last_xritag ! = NO_XRI ) & &
( lpfc_ncmd - > cur_iocbq . sli4_xritag ! = last_xritag + 1 ) ) {
/* a hole in xri block, form a sgl posting block */
list_splice_init ( & prep_nblist , & blck_nblist ) ;
post_cnt = block_cnt - 1 ;
/* prepare list for next posting block */
list_add_tail ( & lpfc_ncmd - > list , & prep_nblist ) ;
block_cnt = 1 ;
} else {
/* prepare list for next posting block */
list_add_tail ( & lpfc_ncmd - > list , & prep_nblist ) ;
/* enough sgls for non-embed sgl mbox command */
if ( block_cnt = = LPFC_NEMBED_MBOX_SGL_CNT ) {
list_splice_init ( & prep_nblist , & blck_nblist ) ;
post_cnt = block_cnt ;
block_cnt = 0 ;
}
}
num_posting + + ;
last_xritag = lpfc_ncmd - > cur_iocbq . sli4_xritag ;
/* end of repost sgl list condition for NVME buffers */
if ( num_posting = = sb_count ) {
if ( post_cnt = = 0 ) {
/* last sgl posting block */
list_splice_init ( & prep_nblist , & blck_nblist ) ;
post_cnt = block_cnt ;
} else if ( block_cnt = = 1 ) {
/* last single sgl with non-contiguous xri */
if ( sgl_size > SGL_PAGE_SIZE )
pdma_phys_sgl1 =
lpfc_ncmd - > dma_phys_sgl +
SGL_PAGE_SIZE ;
else
pdma_phys_sgl1 = 0 ;
cur_xritag = lpfc_ncmd - > cur_iocbq . sli4_xritag ;
status = lpfc_sli4_post_sgl ( phba ,
lpfc_ncmd - > dma_phys_sgl ,
pdma_phys_sgl1 , cur_xritag ) ;
if ( status ) {
/* failure, put on abort nvme list */
lpfc_ncmd - > exch_busy = 1 ;
} else {
/* success, put on NVME buffer list */
lpfc_ncmd - > exch_busy = 0 ;
lpfc_ncmd - > status = IOSTAT_SUCCESS ;
num_posted + + ;
}
/* success, put on NVME buffer sgl list */
list_add_tail ( & lpfc_ncmd - > list , & nvme_nblist ) ;
}
}
/* continue until a nembed page worth of sgls */
if ( post_cnt = = 0 )
continue ;
/* post block of NVME buffer list sgls */
status = lpfc_sli4_post_nvme_sgl_block ( phba , & blck_nblist ,
post_cnt ) ;
/* don't reset xirtag due to hole in xri block */
if ( block_cnt = = 0 )
last_xritag = NO_XRI ;
/* reset NVME buffer post count for next round of posting */
post_cnt = 0 ;
/* put posted NVME buffer-sgl posted on NVME buffer sgl list */
while ( ! list_empty ( & blck_nblist ) ) {
list_remove_head ( & blck_nblist , lpfc_ncmd ,
struct lpfc_nvme_buf , list ) ;
if ( status ) {
/* failure, put on abort nvme list */
lpfc_ncmd - > exch_busy = 1 ;
} else {
/* success, put on NVME buffer list */
lpfc_ncmd - > exch_busy = 0 ;
lpfc_ncmd - > status = IOSTAT_SUCCESS ;
num_posted + + ;
}
list_add_tail ( & lpfc_ncmd - > list , & nvme_nblist ) ;
}
}
/* Push NVME buffers with sgl posted to the available list */
while ( ! list_empty ( & nvme_nblist ) ) {
list_remove_head ( & nvme_nblist , lpfc_ncmd ,
struct lpfc_nvme_buf , list ) ;
lpfc_release_nvme_buf ( phba , lpfc_ncmd ) ;
}
return num_posted ;
}
/**
* lpfc_repost_nvme_sgl_list - Repost all the allocated nvme buffer sgls
* @ phba : pointer to lpfc hba data structure .
*
* This routine walks the list of nvme buffers that have been allocated and
* repost them to the port by using SGL block post . This is needed after a
* pci_function_reset / warm_start or start . The lpfc_hba_down_post_s4 routine
* is responsible for moving all nvme buffers on the lpfc_abts_nvme_sgl_list
* to the lpfc_nvme_buf_list . If the repost fails , reject all nvme buffers .
*
* Returns : 0 = success , non - zero failure .
* */
int
lpfc_repost_nvme_sgl_list ( struct lpfc_hba * phba )
{
LIST_HEAD ( post_nblist ) ;
int num_posted , rc = 0 ;
/* get all NVME buffers need to repost to a local list */
spin_lock_irq ( & phba - > nvme_buf_list_get_lock ) ;
spin_lock ( & phba - > nvme_buf_list_put_lock ) ;
list_splice_init ( & phba - > lpfc_nvme_buf_list_get , & post_nblist ) ;
list_splice ( & phba - > lpfc_nvme_buf_list_put , & post_nblist ) ;
spin_unlock ( & phba - > nvme_buf_list_put_lock ) ;
spin_unlock_irq ( & phba - > nvme_buf_list_get_lock ) ;
/* post the list of nvme buffer sgls to port if available */
if ( ! list_empty ( & post_nblist ) ) {
num_posted = lpfc_post_nvme_sgl_list ( phba , & post_nblist ,
phba - > sli4_hba . nvme_xri_cnt ) ;
/* failed to post any nvme buffer, return error */
if ( num_posted = = 0 )
rc = - EIO ;
}
return rc ;
}
/**
* lpfc_new_nvme_buf - Scsi buffer allocator for HBA with SLI4 IF spec
* @ vport : The virtual port for which this call being executed .
* @ num_to_allocate : The requested number of buffers to allocate .
*
* This routine allocates nvme buffers for device with SLI - 4 interface spec ,
* the nvme buffer contains all the necessary information needed to initiate
* a NVME I / O . After allocating up to @ num_to_allocate NVME buffers and put
* them on a list , it post them to the port by using SGL block post .
*
* Return codes :
* int - number of nvme buffers that were allocated and posted .
* 0 = failure , less than num_to_alloc is a partial failure .
* */
static int
lpfc_new_nvme_buf ( struct lpfc_vport * vport , int num_to_alloc )
{
struct lpfc_hba * phba = vport - > phba ;
struct lpfc_nvme_buf * lpfc_ncmd ;
struct lpfc_iocbq * pwqeq ;
union lpfc_wqe128 * wqe ;
struct sli4_sge * sgl ;
dma_addr_t pdma_phys_sgl ;
uint16_t iotag , lxri = 0 ;
int bcnt , num_posted , sgl_size ;
LIST_HEAD ( prep_nblist ) ;
LIST_HEAD ( post_nblist ) ;
LIST_HEAD ( nvme_nblist ) ;
sgl_size = phba - > cfg_sg_dma_buf_size ;
for ( bcnt = 0 ; bcnt < num_to_alloc ; bcnt + + ) {
lpfc_ncmd = kzalloc ( sizeof ( struct lpfc_nvme_buf ) , GFP_KERNEL ) ;
if ( ! lpfc_ncmd )
break ;
/*
* Get memory from the pci pool to map the virt space to
* pci bus space for an I / O . The DMA buffer includes the
* number of SGE ' s necessary to support the sg_tablesize .
*/
lpfc_ncmd - > data = pci_pool_alloc ( phba - > lpfc_sg_dma_buf_pool ,
GFP_KERNEL ,
& lpfc_ncmd - > dma_handle ) ;
if ( ! lpfc_ncmd - > data ) {
kfree ( lpfc_ncmd ) ;
break ;
}
memset ( lpfc_ncmd - > data , 0 , phba - > cfg_sg_dma_buf_size ) ;
lxri = lpfc_sli4_next_xritag ( phba ) ;
if ( lxri = = NO_XRI ) {
pci_pool_free ( phba - > lpfc_sg_dma_buf_pool ,
lpfc_ncmd - > data , lpfc_ncmd - > dma_handle ) ;
kfree ( lpfc_ncmd ) ;
break ;
}
pwqeq = & ( lpfc_ncmd - > cur_iocbq ) ;
wqe = ( union lpfc_wqe128 * ) & pwqeq - > wqe ;
/* Allocate iotag for lpfc_ncmd->cur_iocbq. */
iotag = lpfc_sli_next_iotag ( phba , pwqeq ) ;
if ( iotag = = 0 ) {
pci_pool_free ( phba - > lpfc_sg_dma_buf_pool ,
lpfc_ncmd - > data , lpfc_ncmd - > dma_handle ) ;
kfree ( lpfc_ncmd ) ;
lpfc_printf_log ( phba , KERN_ERR , LOG_NVME_IOERR ,
" 6121 Failed to allocated IOTAG for "
" XRI:0x%x \n " , lxri ) ;
lpfc_sli4_free_xri ( phba , lxri ) ;
break ;
}
pwqeq - > sli4_lxritag = lxri ;
pwqeq - > sli4_xritag = phba - > sli4_hba . xri_ids [ lxri ] ;
pwqeq - > iocb_flag | = LPFC_IO_NVME ;
pwqeq - > context1 = lpfc_ncmd ;
pwqeq - > wqe_cmpl = lpfc_nvme_io_cmd_wqe_cmpl ;
/* Initialize local short-hand pointers. */
lpfc_ncmd - > nvme_sgl = lpfc_ncmd - > data ;
sgl = lpfc_ncmd - > nvme_sgl ;
pdma_phys_sgl = lpfc_ncmd - > dma_handle ;
lpfc_ncmd - > dma_phys_sgl = pdma_phys_sgl ;
/* Rsp SGE will be filled in when we rcv an IO
* from the NVME Layer to be sent .
* The cmd is going to be embedded so we need a SKIP SGE .
*/
bf_set ( lpfc_sli4_sge_type , sgl , LPFC_SGE_TYPE_SKIP ) ;
bf_set ( lpfc_sli4_sge_last , sgl , 0 ) ;
sgl - > word2 = cpu_to_le32 ( sgl - > word2 ) ;
/* Fill in word 3 / sgl_len during cmd submission */
lpfc_ncmd - > cur_iocbq . context1 = lpfc_ncmd ;
/* Word 7 */
bf_set ( wqe_erp , & wqe - > generic . wqe_com , 0 ) ;
/* NVME upper layers will time things out, if needed */
bf_set ( wqe_tmo , & wqe - > generic . wqe_com , 0 ) ;
/* Word 10 */
bf_set ( wqe_ebde_cnt , & wqe - > generic . wqe_com , 0 ) ;
bf_set ( wqe_dbde , & wqe - > generic . wqe_com , 1 ) ;
/* add the nvme buffer to a post list */
list_add_tail ( & lpfc_ncmd - > list , & post_nblist ) ;
spin_lock_irq ( & phba - > nvme_buf_list_get_lock ) ;
phba - > sli4_hba . nvme_xri_cnt + + ;
spin_unlock_irq ( & phba - > nvme_buf_list_get_lock ) ;
}
lpfc_printf_log ( phba , KERN_INFO , LOG_NVME ,
" 6114 Allocate %d out of %d requested new NVME "
" buffers \n " , bcnt , num_to_alloc ) ;
/* post the list of nvme buffer sgls to port if available */
if ( ! list_empty ( & post_nblist ) )
num_posted = lpfc_post_nvme_sgl_list ( phba ,
& post_nblist , bcnt ) ;
else
num_posted = 0 ;
return num_posted ;
}
/**
* lpfc_get_nvme_buf - Get a nvme buffer from lpfc_nvme_buf_list of the HBA
* @ phba : The HBA for which this call is being executed .
*
* This routine removes a nvme buffer from head of @ phba lpfc_nvme_buf_list list
* and returns to caller .
*
* Return codes :
* NULL - Error
* Pointer to lpfc_nvme_buf - Success
* */
static struct lpfc_nvme_buf *
lpfc_get_nvme_buf ( struct lpfc_hba * phba , struct lpfc_nodelist * ndlp )
{
struct lpfc_nvme_buf * lpfc_ncmd , * lpfc_ncmd_next ;
unsigned long iflag = 0 ;
int found = 0 ;
spin_lock_irqsave ( & phba - > nvme_buf_list_get_lock , iflag ) ;
list_for_each_entry_safe ( lpfc_ncmd , lpfc_ncmd_next ,
& phba - > lpfc_nvme_buf_list_get , list ) {
if ( lpfc_test_rrq_active ( phba , ndlp ,
lpfc_ncmd - > cur_iocbq . sli4_lxritag ) )
continue ;
list_del ( & lpfc_ncmd - > list ) ;
found = 1 ;
break ;
}
if ( ! found ) {
spin_lock ( & phba - > nvme_buf_list_put_lock ) ;
list_splice ( & phba - > lpfc_nvme_buf_list_put ,
& phba - > lpfc_nvme_buf_list_get ) ;
INIT_LIST_HEAD ( & phba - > lpfc_nvme_buf_list_put ) ;
spin_unlock ( & phba - > nvme_buf_list_put_lock ) ;
list_for_each_entry_safe ( lpfc_ncmd , lpfc_ncmd_next ,
& phba - > lpfc_nvme_buf_list_get , list ) {
if ( lpfc_test_rrq_active (
phba , ndlp , lpfc_ncmd - > cur_iocbq . sli4_lxritag ) )
continue ;
list_del ( & lpfc_ncmd - > list ) ;
found = 1 ;
break ;
}
}
spin_unlock_irqrestore ( & phba - > nvme_buf_list_get_lock , iflag ) ;
if ( ! found )
return NULL ;
return lpfc_ncmd ;
}
/**
* lpfc_release_nvme_buf : Return a nvme buffer back to hba nvme buf list .
* @ phba : The Hba for which this call is being executed .
* @ lpfc_ncmd : The nvme buffer which is being released .
*
* This routine releases @ lpfc_ncmd nvme buffer by adding it to tail of @ phba
* lpfc_nvme_buf_list list . For SLI4 XRI ' s are tied to the nvme buffer
* and cannot be reused for at least RA_TOV amount of time if it was
* aborted .
* */
static void
lpfc_release_nvme_buf ( struct lpfc_hba * phba , struct lpfc_nvme_buf * lpfc_ncmd )
{
unsigned long iflag = 0 ;
lpfc_ncmd - > nonsg_phys = 0 ;
if ( lpfc_ncmd - > exch_busy ) {
spin_lock_irqsave ( & phba - > sli4_hba . abts_nvme_buf_list_lock ,
iflag ) ;
lpfc_ncmd - > nvmeCmd = NULL ;
list_add_tail ( & lpfc_ncmd - > list ,
& phba - > sli4_hba . lpfc_abts_nvme_buf_list ) ;
spin_unlock_irqrestore ( & phba - > sli4_hba . abts_nvme_buf_list_lock ,
iflag ) ;
} else {
lpfc_ncmd - > nvmeCmd = NULL ;
lpfc_ncmd - > cur_iocbq . iocb_flag = LPFC_IO_NVME ;
spin_lock_irqsave ( & phba - > nvme_buf_list_put_lock , iflag ) ;
list_add_tail ( & lpfc_ncmd - > list , & phba - > lpfc_nvme_buf_list_put ) ;
spin_unlock_irqrestore ( & phba - > nvme_buf_list_put_lock , iflag ) ;
}
}
/**
* lpfc_nvme_create_localport - Create / Bind an nvme localport instance .
* @ pvport - the lpfc_vport instance requesting a localport .
*
* This routine is invoked to create an nvme localport instance to bind
* to the nvme_fc_transport . It is called once during driver load
* like lpfc_create_shost after all other services are initialized .
* It requires a vport , vpi , and wwns at call time . Other localport
* parameters are modified as the driver ' s FCID and the Fabric WWN
* are established .
*
* Return codes
* 0 - successful
* - ENOMEM - no heap memory available
* other values - from nvme registration upcall
* */
int
lpfc_nvme_create_localport ( struct lpfc_vport * vport )
{
struct lpfc_hba * phba = vport - > phba ;
struct nvme_fc_port_info nfcp_info ;
struct nvme_fc_local_port * localport ;
struct lpfc_nvme_lport * lport ;
int len , ret = 0 ;
/* Initialize this localport instance. The vport wwn usage ensures
* that NPIV is accounted for .
*/
memset ( & nfcp_info , 0 , sizeof ( struct nvme_fc_port_info ) ) ;
nfcp_info . port_role = FC_PORT_ROLE_NVME_INITIATOR ;
nfcp_info . node_name = wwn_to_u64 ( vport - > fc_nodename . u . wwn ) ;
nfcp_info . port_name = wwn_to_u64 ( vport - > fc_portname . u . wwn ) ;
/* For now need + 1 to get around NVME transport logic */
lpfc_nvme_template . max_sgl_segments = phba - > cfg_sg_seg_cnt + 1 ;
lpfc_nvme_template . max_hw_queues = phba - > cfg_nvme_io_channel ;
/* localport is allocated from the stack, but the registration
* call allocates heap memory as well as the private area .
*/
ret = nvme_fc_register_localport ( & nfcp_info , & lpfc_nvme_template ,
& vport - > phba - > pcidev - > dev , & localport ) ;
if ( ! ret ) {
lpfc_printf_vlog ( vport , KERN_INFO , LOG_NVME | LOG_NVME_DISC ,
" 6005 Successfully registered local "
" NVME port num %d, localP %p, private %p, "
" sg_seg %d \n " ,
localport - > port_num , localport ,
localport - > private ,
lpfc_nvme_template . max_sgl_segments ) ;
/* Private is our lport size declared in the template. */
lport = ( struct lpfc_nvme_lport * ) localport - > private ;
vport - > localport = localport ;
lport - > vport = vport ;
INIT_LIST_HEAD ( & lport - > rport_list ) ;
vport - > nvmei_support = 1 ;
}
len = lpfc_new_nvme_buf ( vport , phba - > sli4_hba . nvme_xri_max ) ;
vport - > phba - > total_nvme_bufs + = len ;
return ret ;
}
/**
* lpfc_nvme_destroy_localport - Destroy lpfc_nvme bound to nvme transport .
* @ pnvme : pointer to lpfc nvme data structure .
*
* This routine is invoked to destroy all lports bound to the phba .
* The lport memory was allocated by the nvme fc transport and is
* released there . This routine ensures all rports bound to the
* lport have been disconnected .
*
* */
void
lpfc_nvme_destroy_localport ( struct lpfc_vport * vport )
{
struct nvme_fc_local_port * localport ;
struct lpfc_nvme_lport * lport ;
struct lpfc_nvme_rport * rport = NULL , * rport_next = NULL ;
int ret ;
if ( vport - > nvmei_support = = 0 )
return ;
localport = vport - > localport ;
vport - > localport = NULL ;
lport = ( struct lpfc_nvme_lport * ) localport - > private ;
lpfc_printf_vlog ( vport , KERN_INFO , LOG_NVME ,
" 6011 Destroying NVME localport %p \n " ,
localport ) ;
list_for_each_entry_safe ( rport , rport_next , & lport - > rport_list , list ) {
/* The last node ref has to get released now before the rport
* private memory area is released by the transport .
*/
list_del ( & rport - > list ) ;
init_completion ( & rport - > rport_unreg_done ) ;
ret = nvme_fc_unregister_remoteport ( rport - > remoteport ) ;
if ( ret )
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_DISC ,
" 6008 rport fail destroy %x \n " , ret ) ;
wait_for_completion_timeout ( & rport - > rport_unreg_done , 5 ) ;
}
/* lport's rport list is clear. Unregister
* lport and release resources .
*/
init_completion ( & lport - > lport_unreg_done ) ;
ret = nvme_fc_unregister_localport ( localport ) ;
wait_for_completion_timeout ( & lport - > lport_unreg_done , 5 ) ;
/* Regardless of the unregister upcall response, clear
* nvmei_support . All rports are unregistered and the
* driver will clean up .
*/
vport - > nvmei_support = 0 ;
if ( ret = = 0 ) {
lpfc_printf_vlog ( vport ,
KERN_INFO , LOG_NVME_DISC ,
" 6009 Unregistered lport Success \n " ) ;
} else {
lpfc_printf_vlog ( vport ,
KERN_INFO , LOG_NVME_DISC ,
" 6010 Unregistered lport "
" Failed, status x%x \n " ,
ret ) ;
}
}
void
lpfc_nvme_update_localport ( struct lpfc_vport * vport )
{
struct nvme_fc_local_port * localport ;
struct lpfc_nvme_lport * lport ;
localport = vport - > localport ;
lport = ( struct lpfc_nvme_lport * ) localport - > private ;
lpfc_printf_vlog ( vport , KERN_INFO , LOG_NVME ,
" 6012 Update NVME lport %p did x%x \n " ,
localport , vport - > fc_myDID ) ;
localport - > port_id = vport - > fc_myDID ;
if ( localport - > port_id = = 0 )
localport - > port_role = FC_PORT_ROLE_NVME_DISCOVERY ;
else
localport - > port_role = FC_PORT_ROLE_NVME_INITIATOR ;
lpfc_printf_vlog ( vport , KERN_INFO , LOG_NVME_DISC ,
" 6030 bound lport %p to DID x%06x \n " ,
lport , localport - > port_id ) ;
}
int
lpfc_nvme_register_port ( struct lpfc_vport * vport , struct lpfc_nodelist * ndlp )
{
int ret = 0 ;
struct nvme_fc_local_port * localport ;
struct lpfc_nvme_lport * lport ;
struct lpfc_nvme_rport * rport ;
struct nvme_fc_remote_port * remote_port ;
struct nvme_fc_port_info rpinfo ;
lpfc_printf_vlog ( ndlp - > vport , KERN_INFO , LOG_NVME_DISC ,
" 6006 Register NVME PORT. DID x%06x nlptype x%x \n " ,
ndlp - > nlp_DID , ndlp - > nlp_type ) ;
localport = vport - > localport ;
lport = ( struct lpfc_nvme_lport * ) localport - > private ;
if ( ndlp - > nlp_type & ( NLP_NVME_TARGET | NLP_NVME_INITIATOR ) ) {
/* The driver isn't expecting the rport wwn to change
* but it might get a different DID on a different
* fabric .
*/
list_for_each_entry ( rport , & lport - > rport_list , list ) {
if ( rport - > remoteport - > port_name ! =
wwn_to_u64 ( ndlp - > nlp_portname . u . wwn ) )
continue ;
lpfc_printf_vlog ( ndlp - > vport , KERN_INFO , LOG_NVME_DISC ,
" 6035 lport %p, found matching rport "
" at wwpn 0x%llx, Data: x%x x%x x%x "
" x%06x \n " ,
lport ,
rport - > remoteport - > port_name ,
rport - > remoteport - > port_id ,
rport - > remoteport - > port_role ,
ndlp - > nlp_type ,
ndlp - > nlp_DID ) ;
remote_port = rport - > remoteport ;
if ( ( remote_port - > port_id = = 0 ) & &
( remote_port - > port_role = =
FC_PORT_ROLE_NVME_DISCOVERY ) ) {
remote_port - > port_id = ndlp - > nlp_DID ;
remote_port - > port_role & =
~ FC_PORT_ROLE_NVME_DISCOVERY ;
if ( ndlp - > nlp_type & NLP_NVME_TARGET )
remote_port - > port_role | =
FC_PORT_ROLE_NVME_TARGET ;
if ( ndlp - > nlp_type & NLP_NVME_INITIATOR )
remote_port - > port_role | =
FC_PORT_ROLE_NVME_INITIATOR ;
lpfc_printf_vlog ( ndlp - > vport , KERN_INFO ,
LOG_NVME_DISC ,
" 6014 Rebinding lport to "
" rport wwpn 0x%llx, "
" Data: x%x x%x x%x x%06x \n " ,
remote_port - > port_name ,
remote_port - > port_id ,
remote_port - > port_role ,
ndlp - > nlp_type ,
ndlp - > nlp_DID ) ;
}
return 0 ;
}
/* NVME rports are not preserved across devloss.
* Just register this instance .
*/
rpinfo . port_id = ndlp - > nlp_DID ;
rpinfo . port_role = 0 ;
if ( ndlp - > nlp_type & NLP_NVME_TARGET )
rpinfo . port_role | = FC_PORT_ROLE_NVME_TARGET ;
if ( ndlp - > nlp_type & NLP_NVME_INITIATOR )
rpinfo . port_role | = FC_PORT_ROLE_NVME_INITIATOR ;
rpinfo . port_name = wwn_to_u64 ( ndlp - > nlp_portname . u . wwn ) ;
rpinfo . node_name = wwn_to_u64 ( ndlp - > nlp_nodename . u . wwn ) ;
ret = nvme_fc_register_remoteport ( localport , & rpinfo ,
& remote_port ) ;
if ( ! ret ) {
rport = remote_port - > private ;
rport - > remoteport = remote_port ;
rport - > lport = lport ;
rport - > ndlp = lpfc_nlp_get ( ndlp ) ;
if ( ! rport - > ndlp )
return - 1 ;
ndlp - > nrport = rport ;
INIT_LIST_HEAD ( & rport - > list ) ;
list_add_tail ( & rport - > list , & lport - > rport_list ) ;
lpfc_printf_vlog ( vport , KERN_INFO ,
LOG_NVME_DISC | LOG_NODE ,
" 6022 Binding new rport to lport %p "
" Rport WWNN 0x%llx, Rport WWPN 0x%llx "
" DID x%06x Role x%x \n " ,
lport ,
rpinfo . node_name , rpinfo . port_name ,
rpinfo . port_id , rpinfo . port_role ) ;
} else {
lpfc_printf_vlog ( vport , KERN_ERR ,
LOG_NVME_DISC | LOG_NODE ,
" 6031 RemotePort Registration failed "
" err: %d, DID x%06x \n " ,
ret , ndlp - > nlp_DID ) ;
}
} else {
ret = - EINVAL ;
lpfc_printf_vlog ( vport , KERN_INFO , LOG_NVME_DISC ,
" 6027 Unknown nlp_type x%x on DID x%06x "
" ndlp %p. Not Registering nvme rport \n " ,
ndlp - > nlp_type , ndlp - > nlp_DID , ndlp ) ;
}
return ret ;
}
/* lpfc_nvme_unregister_port - unbind the DID and port_role from this rport.
*
* There is no notion of Devloss or rport recovery from the current
* nvme_transport perspective . Loss of an rport just means IO cannot
* be sent and recovery is completely up to the initator .
* For now , the driver just unbinds the DID and port_role so that
* no further IO can be issued . Changes are planned for later .
*
* Notes - the ndlp reference count is not decremented here since
* since there is no nvme_transport api for devloss . Node ref count
* is only adjusted in driver unload .
*/
void
lpfc_nvme_unregister_port ( struct lpfc_vport * vport , struct lpfc_nodelist * ndlp )
{
int ret ;
struct nvme_fc_local_port * localport ;
struct lpfc_nvme_lport * lport ;
struct lpfc_nvme_rport * rport ;
struct nvme_fc_remote_port * remoteport ;
localport = vport - > localport ;
/* This is fundamental error. The localport is always
* available until driver unload . Just exit .
*/
if ( ! localport )
return ;
lport = ( struct lpfc_nvme_lport * ) localport - > private ;
if ( ! lport )
goto input_err ;
rport = ndlp - > nrport ;
if ( ! rport )
goto input_err ;
remoteport = rport - > remoteport ;
lpfc_printf_vlog ( vport , KERN_INFO , LOG_NVME_DISC ,
" 6033 Unreg nvme remoteport %p, portname x%llx, "
" port_id x%06x, portstate x%x port type x%x \n " ,
remoteport , remoteport - > port_name ,
remoteport - > port_id , remoteport - > port_state ,
ndlp - > nlp_type ) ;
/* Sanity check ndlp type. Only call for NVME ports. Don't
* clear any rport state until the transport calls back .
*/
if ( ndlp - > nlp_type & ( NLP_NVME_TARGET | NLP_NVME_INITIATOR ) ) {
init_completion ( & rport - > rport_unreg_done ) ;
ret = nvme_fc_unregister_remoteport ( remoteport ) ;
if ( ret ! = 0 ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_DISC ,
" 6167 NVME unregister failed %d "
" port_state x%x \n " ,
ret , remoteport - > port_state ) ;
}
/* Wait for the driver's delete completion routine to finish
* before proceeding . This guarantees the transport and driver
* have completed the unreg process .
*/
ret = wait_for_completion_timeout ( & rport - > rport_unreg_done , 5 ) ;
if ( ret = = 0 ) {
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_DISC ,
" 6169 Unreg nvme wait failed %d \n " ,
ret ) ;
}
}
return ;
input_err :
lpfc_printf_vlog ( vport , KERN_ERR , LOG_NVME_DISC ,
" 6168: State error: lport %p, rport%p FCID x%06x \n " ,
vport - > localport , ndlp - > rport , ndlp - > nlp_DID ) ;
}