2005-04-18 01:05:31 +04:00
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Enterprise Fibre Channel Host Bus Adapters . *
* Refer to the README file included with this package for *
* driver version and adapter support . *
* Copyright ( C ) 2004 Emulex Corporation . *
* www . emulex . com *
* *
* This program is free software ; you can redistribute it and / or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation ; either version 2 *
* of the License , or ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU General Public License for more details , a copy of which *
* can be found in the file COPYING included with this package . *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* $ Id : lpfc_scsi . c 1.37 2005 / 04 / 13 14 : 27 : 09 EDT sf_support Exp $
*/
# include <linux/pci.h>
# include <linux/interrupt.h>
# include <scsi/scsi.h>
# include <scsi/scsi_device.h>
# include <scsi/scsi_host.h>
# include <scsi/scsi_tcq.h>
# include <scsi/scsi_transport_fc.h>
# include "lpfc_version.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"
# define LPFC_RESET_WAIT 2
# define LPFC_ABORT_WAIT 2
static inline void lpfc_put_lun ( struct fcp_cmnd * fcmd , unsigned int lun )
{
fcmd - > fcpLunLsl = 0 ;
fcmd - > fcpLunMsl = swab16 ( ( uint16_t ) lun ) ;
}
/*
* This routine allocates a scsi buffer , which contains all the necessary
* information needed to initiate a SCSI I / O . The non - DMAable buffer region
* contains information to build the IOCB . The DMAable region contains
* memory for the FCP CMND , FCP RSP , and the inital BPL . In addition to
* allocating memeory , the FCP CMND and FCP RSP BDEs are setup in the BPL
* and the BPL BDE is setup in the IOCB .
*/
static struct lpfc_scsi_buf *
lpfc_get_scsi_buf ( struct lpfc_hba * phba )
{
struct lpfc_scsi_buf * psb ;
struct ulp_bde64 * bpl ;
IOCB_t * iocb ;
dma_addr_t pdma_phys ;
psb = kmalloc ( sizeof ( struct lpfc_scsi_buf ) , GFP_KERNEL ) ;
if ( ! psb )
return NULL ;
memset ( psb , 0 , sizeof ( struct lpfc_scsi_buf ) ) ;
psb - > scsi_hba = phba ;
/*
* Get memory from the pci pool to map the virt space to pci bus space
* for an I / O . The DMA buffer includes space for the struct fcp_cmnd ,
* struct fcp_rsp and the number of bde ' s necessary to support the
* sg_tablesize .
*/
psb - > data = pci_pool_alloc ( phba - > lpfc_scsi_dma_buf_pool , GFP_KERNEL ,
& psb - > dma_handle ) ;
if ( ! psb - > data ) {
kfree ( psb ) ;
return NULL ;
}
/* Initialize virtual ptrs to dma_buf region. */
memset ( psb - > data , 0 , phba - > cfg_sg_dma_buf_size ) ;
psb - > fcp_cmnd = psb - > data ;
psb - > fcp_rsp = psb - > data + sizeof ( struct fcp_cmnd ) ;
psb - > fcp_bpl = psb - > data + sizeof ( struct fcp_cmnd ) +
sizeof ( struct fcp_rsp ) ;
/* Initialize local short-hand pointers. */
bpl = psb - > fcp_bpl ;
pdma_phys = psb - > dma_handle ;
/*
* The first two bdes are the FCP_CMD and FCP_RSP . The balance are sg
* list bdes . Initialize the first two and leave the rest for
* queuecommand .
*/
bpl - > addrHigh = le32_to_cpu ( putPaddrHigh ( pdma_phys ) ) ;
bpl - > addrLow = le32_to_cpu ( putPaddrLow ( pdma_phys ) ) ;
bpl - > tus . f . bdeSize = sizeof ( struct fcp_cmnd ) ;
bpl - > tus . f . bdeFlags = BUFF_USE_CMND ;
bpl - > tus . w = le32_to_cpu ( bpl - > tus . w ) ;
bpl + + ;
/* Setup the physical region for the FCP RSP */
pdma_phys + = sizeof ( struct fcp_cmnd ) ;
bpl - > addrHigh = le32_to_cpu ( putPaddrHigh ( pdma_phys ) ) ;
bpl - > addrLow = le32_to_cpu ( putPaddrLow ( pdma_phys ) ) ;
bpl - > tus . f . bdeSize = sizeof ( struct fcp_rsp ) ;
bpl - > tus . f . bdeFlags = ( BUFF_USE_CMND | BUFF_USE_RCV ) ;
bpl - > tus . w = le32_to_cpu ( bpl - > tus . w ) ;
/*
* Since the IOCB for the FCP I / O is built into this lpfc_scsi_buf ,
* initialize it with all known data now .
*/
pdma_phys + = ( sizeof ( struct fcp_rsp ) ) ;
iocb = & psb - > cur_iocbq . iocb ;
iocb - > un . fcpi64 . bdl . ulpIoTag32 = 0 ;
iocb - > un . fcpi64 . bdl . addrHigh = putPaddrHigh ( pdma_phys ) ;
iocb - > un . fcpi64 . bdl . addrLow = putPaddrLow ( pdma_phys ) ;
iocb - > un . fcpi64 . bdl . bdeSize = ( 2 * sizeof ( struct ulp_bde64 ) ) ;
iocb - > un . fcpi64 . bdl . bdeFlags = BUFF_TYPE_BDL ;
iocb - > ulpBdeCount = 1 ;
iocb - > ulpClass = CLASS3 ;
return psb ;
}
static void
lpfc_free_scsi_buf ( struct lpfc_scsi_buf * psb )
{
struct lpfc_hba * phba = psb - > scsi_hba ;
/*
* There are only two special cases to consider . ( 1 ) the scsi command
* requested scatter - gather usage or ( 2 ) the scsi command allocated
* a request buffer , but did not request use_sg . There is a third
* case , but it does not require resource deallocation .
*/
if ( ( psb - > seg_cnt > 0 ) & & ( psb - > pCmd - > use_sg ) ) {
dma_unmap_sg ( & phba - > pcidev - > dev , psb - > pCmd - > request_buffer ,
psb - > seg_cnt , psb - > pCmd - > sc_data_direction ) ;
} else {
if ( ( psb - > nonsg_phys ) & & ( psb - > pCmd - > request_bufflen ) ) {
dma_unmap_single ( & phba - > pcidev - > dev , psb - > nonsg_phys ,
psb - > pCmd - > request_bufflen ,
psb - > pCmd - > sc_data_direction ) ;
}
}
list_add_tail ( & psb - > list , & phba - > lpfc_scsi_buf_list ) ;
}
static int
lpfc_scsi_prep_dma_buf ( struct lpfc_hba * phba , struct lpfc_scsi_buf * lpfc_cmd )
{
struct scsi_cmnd * scsi_cmnd = lpfc_cmd - > pCmd ;
struct scatterlist * sgel = NULL ;
struct fcp_cmnd * fcp_cmnd = lpfc_cmd - > fcp_cmnd ;
struct ulp_bde64 * bpl = lpfc_cmd - > fcp_bpl ;
IOCB_t * iocb_cmd = & lpfc_cmd - > cur_iocbq . iocb ;
dma_addr_t physaddr ;
uint32_t i , num_bde = 0 ;
int datadir = scsi_cmnd - > sc_data_direction ;
int dma_error ;
/*
* There are three possibilities here - use scatter - gather segment , use
* the single mapping , or neither . Start the lpfc command prep by
* bumping the bpl beyond the fcp_cmnd and fcp_rsp regions to the first
* data bde entry .
*/
bpl + = 2 ;
if ( scsi_cmnd - > use_sg ) {
/*
* The driver stores the segment count returned from pci_map_sg
* because this a count of dma - mappings used to map the use_sg
* pages . They are not guaranteed to be the same for those
* architectures that implement an IOMMU .
*/
sgel = ( struct scatterlist * ) scsi_cmnd - > request_buffer ;
lpfc_cmd - > seg_cnt = dma_map_sg ( & phba - > pcidev - > dev , sgel ,
scsi_cmnd - > use_sg , datadir ) ;
if ( lpfc_cmd - > seg_cnt = = 0 )
return 1 ;
if ( lpfc_cmd - > seg_cnt > phba - > cfg_sg_seg_cnt ) {
printk ( KERN_ERR " %s: Too many sg segments from "
" dma_map_sg. Config %d, seg_cnt %d " ,
__FUNCTION__ , phba - > cfg_sg_seg_cnt ,
lpfc_cmd - > seg_cnt ) ;
dma_unmap_sg ( & phba - > pcidev - > dev , sgel ,
lpfc_cmd - > seg_cnt , datadir ) ;
return 1 ;
}
/*
* The driver established a maximum scatter - gather segment count
* during probe that limits the number of sg elements in any
* single scsi command . Just run through the seg_cnt and format
* the bde ' s .
*/
for ( i = 0 ; i < lpfc_cmd - > seg_cnt ; i + + ) {
physaddr = sg_dma_address ( sgel ) ;
bpl - > addrLow = le32_to_cpu ( putPaddrLow ( physaddr ) ) ;
bpl - > addrHigh = le32_to_cpu ( putPaddrHigh ( physaddr ) ) ;
bpl - > tus . f . bdeSize = sg_dma_len ( sgel ) ;
if ( datadir = = DMA_TO_DEVICE )
bpl - > tus . f . bdeFlags = 0 ;
else
bpl - > tus . f . bdeFlags = BUFF_USE_RCV ;
bpl - > tus . w = le32_to_cpu ( bpl - > tus . w ) ;
bpl + + ;
sgel + + ;
num_bde + + ;
}
} else if ( scsi_cmnd - > request_buffer & & scsi_cmnd - > request_bufflen ) {
physaddr = dma_map_single ( & phba - > pcidev - > dev ,
scsi_cmnd - > request_buffer ,
scsi_cmnd - > request_bufflen ,
datadir ) ;
dma_error = dma_mapping_error ( physaddr ) ;
if ( dma_error ) {
lpfc_printf_log ( phba , KERN_ERR , LOG_FCP ,
" %d:0718 Unable to dma_map_single "
" request_buffer: x%x \n " ,
phba - > brd_no , dma_error ) ;
return 1 ;
}
lpfc_cmd - > nonsg_phys = physaddr ;
bpl - > addrLow = le32_to_cpu ( putPaddrLow ( physaddr ) ) ;
bpl - > addrHigh = le32_to_cpu ( putPaddrHigh ( physaddr ) ) ;
bpl - > tus . f . bdeSize = scsi_cmnd - > request_bufflen ;
if ( datadir = = DMA_TO_DEVICE )
bpl - > tus . f . bdeFlags = 0 ;
bpl - > tus . w = le32_to_cpu ( bpl - > tus . w ) ;
num_bde = 1 ;
bpl + + ;
}
/*
* Finish initializing those IOCB fields that are dependent on the
* scsi_cmnd request_buffer
*/
iocb_cmd - > un . fcpi64 . bdl . bdeSize + =
( num_bde * sizeof ( struct ulp_bde64 ) ) ;
iocb_cmd - > ulpBdeCount = 1 ;
iocb_cmd - > ulpLe = 1 ;
fcp_cmnd - > fcpDl = be32_to_cpu ( scsi_cmnd - > request_bufflen ) ;
return 0 ;
}
static void
lpfc_handle_fcp_err ( struct lpfc_scsi_buf * lpfc_cmd )
{
struct scsi_cmnd * cmnd = lpfc_cmd - > pCmd ;
struct fcp_cmnd * fcpcmd = lpfc_cmd - > fcp_cmnd ;
struct fcp_rsp * fcprsp = lpfc_cmd - > fcp_rsp ;
struct lpfc_hba * phba = lpfc_cmd - > scsi_hba ;
uint32_t fcpi_parm = lpfc_cmd - > cur_iocbq . iocb . un . fcpi . fcpi_parm ;
uint32_t resp_info = fcprsp - > rspStatus2 ;
uint32_t scsi_status = fcprsp - > rspStatus3 ;
uint32_t host_status = DID_OK ;
uint32_t rsplen = 0 ;
/*
* If this is a task management command , there is no
* scsi packet associated with this lpfc_cmd . The driver
* consumes it .
*/
if ( fcpcmd - > fcpCntl2 ) {
scsi_status = 0 ;
goto out ;
}
lpfc_printf_log ( phba , KERN_WARNING , LOG_FCP ,
" %d:0730 FCP command failed: RSP "
" Data: x%x x%x x%x x%x x%x x%x \n " ,
phba - > brd_no , resp_info , scsi_status ,
be32_to_cpu ( fcprsp - > rspResId ) ,
be32_to_cpu ( fcprsp - > rspSnsLen ) ,
be32_to_cpu ( fcprsp - > rspRspLen ) ,
fcprsp - > rspInfo3 ) ;
if ( resp_info & RSP_LEN_VALID ) {
rsplen = be32_to_cpu ( fcprsp - > rspRspLen ) ;
if ( ( rsplen ! = 0 & & rsplen ! = 4 & & rsplen ! = 8 ) | |
( fcprsp - > rspInfo3 ! = RSP_NO_FAILURE ) ) {
host_status = DID_ERROR ;
goto out ;
}
}
if ( ( resp_info & SNS_LEN_VALID ) & & fcprsp - > rspSnsLen ) {
uint32_t snslen = be32_to_cpu ( fcprsp - > rspSnsLen ) ;
if ( snslen > SCSI_SENSE_BUFFERSIZE )
snslen = SCSI_SENSE_BUFFERSIZE ;
memcpy ( cmnd - > sense_buffer , & fcprsp - > rspInfo0 + rsplen , snslen ) ;
}
cmnd - > resid = 0 ;
if ( resp_info & RESID_UNDER ) {
cmnd - > resid = be32_to_cpu ( fcprsp - > rspResId ) ;
lpfc_printf_log ( phba , KERN_INFO , LOG_FCP ,
" %d:0716 FCP Read Underrun, expected %d, "
" residual %d Data: x%x x%x x%x \n " , phba - > brd_no ,
be32_to_cpu ( fcpcmd - > fcpDl ) , cmnd - > resid ,
fcpi_parm , cmnd - > cmnd [ 0 ] , cmnd - > underflow ) ;
/*
* The cmnd - > underflow is the minimum number of bytes that must
* be transfered for this command . Provided a sense condition
* is not present , make sure the actual amount transferred is at
* least the underflow value or fail .
*/
if ( ! ( resp_info & SNS_LEN_VALID ) & &
( scsi_status = = SAM_STAT_GOOD ) & &
( cmnd - > request_bufflen - cmnd - > resid ) < cmnd - > underflow ) {
lpfc_printf_log ( phba , KERN_INFO , LOG_FCP ,
" %d:0717 FCP command x%x residual "
" underrun converted to error "
" Data: x%x x%x x%x \n " , phba - > brd_no ,
cmnd - > cmnd [ 0 ] , cmnd - > request_bufflen ,
cmnd - > resid , cmnd - > underflow ) ;
host_status = DID_ERROR ;
}
} else if ( resp_info & RESID_OVER ) {
lpfc_printf_log ( phba , KERN_WARNING , LOG_FCP ,
" %d:0720 FCP command x%x residual "
" overrun error. Data: x%x x%x \n " ,
phba - > brd_no , cmnd - > cmnd [ 0 ] ,
cmnd - > request_bufflen , cmnd - > resid ) ;
host_status = DID_ERROR ;
/*
* Check SLI validation that all the transfer was actually done
* ( fcpi_parm should be zero ) . Apply check only to reads .
*/
} else if ( ( scsi_status = = SAM_STAT_GOOD ) & & fcpi_parm & &
( cmnd - > sc_data_direction = = DMA_FROM_DEVICE ) ) {
lpfc_printf_log ( phba , KERN_WARNING , LOG_FCP ,
" %d:0734 FCP Read Check Error Data: "
" x%x x%x x%x x%x \n " , phba - > brd_no ,
be32_to_cpu ( fcpcmd - > fcpDl ) ,
be32_to_cpu ( fcprsp - > rspResId ) ,
fcpi_parm , cmnd - > cmnd [ 0 ] ) ;
host_status = DID_ERROR ;
cmnd - > resid = cmnd - > request_bufflen ;
}
out :
cmnd - > result = ScsiResult ( host_status , scsi_status ) ;
}
static void
lpfc_scsi_cmd_iocb_cmpl ( struct lpfc_hba * phba , struct lpfc_iocbq * pIocbIn ,
struct lpfc_iocbq * pIocbOut )
{
struct lpfc_scsi_buf * lpfc_cmd =
( struct lpfc_scsi_buf * ) pIocbIn - > context1 ;
struct lpfc_rport_data * rdata = lpfc_cmd - > rdata ;
struct lpfc_nodelist * pnode = rdata - > pnode ;
struct scsi_cmnd * cmd = lpfc_cmd - > pCmd ;
unsigned long iflag ;
lpfc_cmd - > result = pIocbOut - > iocb . un . ulpWord [ 4 ] ;
lpfc_cmd - > status = pIocbOut - > iocb . ulpStatus ;
if ( lpfc_cmd - > status ) {
if ( lpfc_cmd - > status = = IOSTAT_LOCAL_REJECT & &
( lpfc_cmd - > result & IOERR_DRVR_MASK ) )
lpfc_cmd - > status = IOSTAT_DRIVER_REJECT ;
else if ( lpfc_cmd - > status > = IOSTAT_CNT )
lpfc_cmd - > status = IOSTAT_DEFAULT ;
lpfc_printf_log ( phba , KERN_WARNING , LOG_FCP ,
" %d:0729 FCP cmd x%x failed <%d/%d> status: "
" x%x result: x%x Data: x%x x%x \n " ,
phba - > brd_no , cmd - > cmnd [ 0 ] , cmd - > device - > id ,
cmd - > device - > lun , lpfc_cmd - > status ,
lpfc_cmd - > result , pIocbOut - > iocb . ulpContext ,
lpfc_cmd - > cur_iocbq . iocb . ulpIoTag ) ;
switch ( lpfc_cmd - > status ) {
case IOSTAT_FCP_RSP_ERROR :
/* Call FCP RSP handler to determine result */
lpfc_handle_fcp_err ( lpfc_cmd ) ;
break ;
case IOSTAT_NPORT_BSY :
case IOSTAT_FABRIC_BSY :
cmd - > result = ScsiResult ( DID_BUS_BUSY , 0 ) ;
break ;
default :
cmd - > result = ScsiResult ( DID_ERROR , 0 ) ;
break ;
}
if ( pnode ) {
if ( pnode - > nlp_state ! = NLP_STE_MAPPED_NODE )
cmd - > result = ScsiResult ( DID_BUS_BUSY ,
SAM_STAT_BUSY ) ;
}
else {
cmd - > result = ScsiResult ( DID_NO_CONNECT , 0 ) ;
}
} else {
cmd - > result = ScsiResult ( DID_OK , 0 ) ;
}
if ( cmd - > result | | lpfc_cmd - > fcp_rsp - > rspSnsLen ) {
uint32_t * lp = ( uint32_t * ) cmd - > sense_buffer ;
lpfc_printf_log ( phba , KERN_INFO , LOG_FCP ,
" %d:0710 Iodone <%d/%d> cmd %p, error x%x "
" SNS x%x x%x Data: x%x x%x \n " ,
phba - > brd_no , cmd - > device - > id ,
cmd - > device - > lun , cmd , cmd - > result ,
* lp , * ( lp + 3 ) , cmd - > retries , cmd - > resid ) ;
}
spin_lock_irqsave ( phba - > host - > host_lock , iflag ) ;
lpfc_free_scsi_buf ( lpfc_cmd ) ;
cmd - > host_scribble = NULL ;
spin_unlock_irqrestore ( phba - > host - > host_lock , iflag ) ;
cmd - > scsi_done ( cmd ) ;
}
static void
lpfc_scsi_prep_cmnd ( struct lpfc_hba * phba , struct lpfc_scsi_buf * lpfc_cmd ,
struct lpfc_nodelist * pnode )
{
struct scsi_cmnd * scsi_cmnd = lpfc_cmd - > pCmd ;
struct fcp_cmnd * fcp_cmnd = lpfc_cmd - > fcp_cmnd ;
IOCB_t * iocb_cmd = & lpfc_cmd - > cur_iocbq . iocb ;
struct lpfc_iocbq * piocbq = & ( lpfc_cmd - > cur_iocbq ) ;
int datadir = scsi_cmnd - > sc_data_direction ;
lpfc_cmd - > fcp_rsp - > rspSnsLen = 0 ;
lpfc_put_lun ( lpfc_cmd - > fcp_cmnd , lpfc_cmd - > pCmd - > device - > lun ) ;
memcpy ( & fcp_cmnd - > fcpCdb [ 0 ] , scsi_cmnd - > cmnd , 16 ) ;
if ( scsi_cmnd - > device - > tagged_supported ) {
switch ( scsi_cmnd - > tag ) {
case HEAD_OF_QUEUE_TAG :
fcp_cmnd - > fcpCntl1 = HEAD_OF_Q ;
break ;
case ORDERED_QUEUE_TAG :
fcp_cmnd - > fcpCntl1 = ORDERED_Q ;
break ;
default :
fcp_cmnd - > fcpCntl1 = SIMPLE_Q ;
break ;
}
} else
fcp_cmnd - > fcpCntl1 = 0 ;
/*
* There are three possibilities here - use scatter - gather segment , use
* the single mapping , or neither . Start the lpfc command prep by
* bumping the bpl beyond the fcp_cmnd and fcp_rsp regions to the first
* data bde entry .
*/
if ( scsi_cmnd - > use_sg ) {
if ( datadir = = DMA_TO_DEVICE ) {
iocb_cmd - > ulpCommand = CMD_FCP_IWRITE64_CR ;
iocb_cmd - > un . fcpi . fcpi_parm = 0 ;
iocb_cmd - > ulpPU = 0 ;
fcp_cmnd - > fcpCntl3 = WRITE_DATA ;
phba - > fc4OutputRequests + + ;
} else {
iocb_cmd - > ulpCommand = CMD_FCP_IREAD64_CR ;
iocb_cmd - > ulpPU = PARM_READ_CHECK ;
iocb_cmd - > un . fcpi . fcpi_parm =
scsi_cmnd - > request_bufflen ;
fcp_cmnd - > fcpCntl3 = READ_DATA ;
phba - > fc4InputRequests + + ;
}
} else if ( scsi_cmnd - > request_buffer & & scsi_cmnd - > request_bufflen ) {
if ( datadir = = DMA_TO_DEVICE ) {
iocb_cmd - > ulpCommand = CMD_FCP_IWRITE64_CR ;
iocb_cmd - > un . fcpi . fcpi_parm = 0 ;
iocb_cmd - > ulpPU = 0 ;
fcp_cmnd - > fcpCntl3 = WRITE_DATA ;
phba - > fc4OutputRequests + + ;
} else {
iocb_cmd - > ulpCommand = CMD_FCP_IREAD64_CR ;
iocb_cmd - > ulpPU = PARM_READ_CHECK ;
iocb_cmd - > un . fcpi . fcpi_parm =
scsi_cmnd - > request_bufflen ;
fcp_cmnd - > fcpCntl3 = READ_DATA ;
phba - > fc4InputRequests + + ;
}
} else {
iocb_cmd - > ulpCommand = CMD_FCP_ICMND64_CR ;
iocb_cmd - > un . fcpi . fcpi_parm = 0 ;
iocb_cmd - > ulpPU = 0 ;
fcp_cmnd - > fcpCntl3 = 0 ;
phba - > fc4ControlRequests + + ;
}
/*
* Finish initializing those IOCB fields that are independent
* of the scsi_cmnd request_buffer
*/
piocbq - > iocb . ulpContext = pnode - > nlp_rpi ;
if ( pnode - > nlp_fcp_info & NLP_FCP_2_DEVICE )
piocbq - > iocb . ulpFCP2Rcvy = 1 ;
piocbq - > iocb . ulpClass = ( pnode - > nlp_fcp_info & 0x0f ) ;
piocbq - > context1 = lpfc_cmd ;
piocbq - > iocb_cmpl = lpfc_scsi_cmd_iocb_cmpl ;
piocbq - > iocb . ulpTimeout = lpfc_cmd - > timeout ;
}
static int
lpfc_scsi_prep_task_mgmt_cmd ( struct lpfc_hba * phba ,
struct lpfc_scsi_buf * lpfc_cmd ,
uint8_t task_mgmt_cmd )
{
struct lpfc_sli * psli ;
struct lpfc_iocbq * piocbq ;
IOCB_t * piocb ;
struct fcp_cmnd * fcp_cmnd ;
struct scsi_device * scsi_dev = lpfc_cmd - > pCmd - > device ;
struct lpfc_rport_data * rdata = scsi_dev - > hostdata ;
struct lpfc_nodelist * ndlp = rdata - > pnode ;
if ( ( ndlp = = 0 ) | | ( ndlp - > nlp_state ! = NLP_STE_MAPPED_NODE ) ) {
return 0 ;
}
psli = & phba - > sli ;
piocbq = & ( lpfc_cmd - > cur_iocbq ) ;
piocb = & piocbq - > iocb ;
fcp_cmnd = lpfc_cmd - > fcp_cmnd ;
lpfc_put_lun ( lpfc_cmd - > fcp_cmnd , lpfc_cmd - > pCmd - > device - > lun ) ;
fcp_cmnd - > fcpCntl2 = task_mgmt_cmd ;
piocb - > ulpCommand = CMD_FCP_ICMND64_CR ;
piocb - > ulpContext = ndlp - > nlp_rpi ;
if ( ndlp - > nlp_fcp_info & NLP_FCP_2_DEVICE ) {
piocb - > ulpFCP2Rcvy = 1 ;
}
piocb - > ulpClass = ( ndlp - > nlp_fcp_info & 0x0f ) ;
/* ulpTimeout is only one byte */
if ( lpfc_cmd - > timeout > 0xff ) {
/*
* Do not timeout the command at the firmware level .
* The driver will provide the timeout mechanism .
*/
piocb - > ulpTimeout = 0 ;
} else {
piocb - > ulpTimeout = lpfc_cmd - > timeout ;
}
lpfc_cmd - > rdata = rdata ;
switch ( task_mgmt_cmd ) {
case FCP_LUN_RESET :
/* Issue LUN Reset to TGT <num> LUN <num> */
lpfc_printf_log ( phba ,
KERN_INFO ,
LOG_FCP ,
" %d:0703 Issue LUN Reset to TGT %d LUN %d "
" Data: x%x x%x \n " ,
phba - > brd_no ,
scsi_dev - > id , scsi_dev - > lun ,
ndlp - > nlp_rpi , ndlp - > nlp_flag ) ;
break ;
case FCP_ABORT_TASK_SET :
/* Issue Abort Task Set to TGT <num> LUN <num> */
lpfc_printf_log ( phba ,
KERN_INFO ,
LOG_FCP ,
" %d:0701 Issue Abort Task Set to TGT %d LUN %d "
" Data: x%x x%x \n " ,
phba - > brd_no ,
scsi_dev - > id , scsi_dev - > lun ,
ndlp - > nlp_rpi , ndlp - > nlp_flag ) ;
break ;
case FCP_TARGET_RESET :
/* Issue Target Reset to TGT <num> */
lpfc_printf_log ( phba ,
KERN_INFO ,
LOG_FCP ,
" %d:0702 Issue Target Reset to TGT %d "
" Data: x%x x%x \n " ,
phba - > brd_no ,
scsi_dev - > id , ndlp - > nlp_rpi ,
ndlp - > nlp_flag ) ;
break ;
}
return ( 1 ) ;
}
static int
lpfc_scsi_tgt_reset ( struct lpfc_scsi_buf * lpfc_cmd , struct lpfc_hba * phba )
{
struct lpfc_iocbq * iocbq ;
struct lpfc_iocbq * iocbqrsp = NULL ;
struct list_head * lpfc_iocb_list = & phba - > lpfc_iocb_list ;
int ret ;
ret = lpfc_scsi_prep_task_mgmt_cmd ( phba , lpfc_cmd , FCP_TARGET_RESET ) ;
if ( ! ret )
return FAILED ;
lpfc_cmd - > scsi_hba = phba ;
iocbq = & lpfc_cmd - > cur_iocbq ;
list_remove_head ( lpfc_iocb_list , iocbqrsp , struct lpfc_iocbq , list ) ;
if ( ! iocbqrsp )
return FAILED ;
memset ( iocbqrsp , 0 , sizeof ( struct lpfc_iocbq ) ) ;
iocbq - > iocb_flag | = LPFC_IO_POLL ;
ret = lpfc_sli_issue_iocb_wait_high_priority ( phba ,
& phba - > sli . ring [ phba - > sli . fcp_ring ] ,
iocbq , SLI_IOCB_HIGH_PRIORITY ,
iocbqrsp ,
lpfc_cmd - > timeout ) ;
if ( ret ! = IOCB_SUCCESS ) {
lpfc_cmd - > status = IOSTAT_DRIVER_REJECT ;
ret = FAILED ;
} else {
ret = SUCCESS ;
lpfc_cmd - > result = iocbqrsp - > iocb . un . ulpWord [ 4 ] ;
lpfc_cmd - > status = iocbqrsp - > iocb . ulpStatus ;
if ( lpfc_cmd - > status = = IOSTAT_LOCAL_REJECT & &
( lpfc_cmd - > result & IOERR_DRVR_MASK ) )
lpfc_cmd - > status = IOSTAT_DRIVER_REJECT ;
}
/*
* All outstanding txcmplq I / Os should have been aborted by the target .
* Unfortunately , some targets do not abide by this forcing the driver
* to double check .
*/
lpfc_sli_abort_iocb ( phba , & phba - > sli . ring [ phba - > sli . fcp_ring ] ,
lpfc_cmd - > pCmd - > device - > id ,
lpfc_cmd - > pCmd - > device - > lun , 0 , LPFC_CTX_TGT ) ;
/* Return response IOCB to free list. */
list_add_tail ( & iocbqrsp - > list , lpfc_iocb_list ) ;
return ret ;
}
static void
lpfc_scsi_cmd_iocb_cleanup ( struct lpfc_hba * phba , struct lpfc_iocbq * pIocbIn ,
struct lpfc_iocbq * pIocbOut )
{
unsigned long iflag ;
struct lpfc_scsi_buf * lpfc_cmd =
( struct lpfc_scsi_buf * ) pIocbIn - > context1 ;
spin_lock_irqsave ( phba - > host - > host_lock , iflag ) ;
lpfc_free_scsi_buf ( lpfc_cmd ) ;
spin_unlock_irqrestore ( phba - > host - > host_lock , iflag ) ;
}
static void
lpfc_scsi_cmd_iocb_cmpl_aborted ( struct lpfc_hba * phba ,
struct lpfc_iocbq * pIocbIn ,
struct lpfc_iocbq * pIocbOut )
{
struct scsi_cmnd * ml_cmd =
( ( struct lpfc_scsi_buf * ) pIocbIn - > context1 ) - > pCmd ;
lpfc_scsi_cmd_iocb_cleanup ( phba , pIocbIn , pIocbOut ) ;
ml_cmd - > host_scribble = NULL ;
}
const char *
lpfc_info ( struct Scsi_Host * host )
{
struct lpfc_hba * phba = ( struct lpfc_hba * ) host - > hostdata [ 0 ] ;
int len ;
static char lpfcinfobuf [ 384 ] ;
memset ( lpfcinfobuf , 0 , 384 ) ;
if ( phba & & phba - > pcidev ) {
strncpy ( lpfcinfobuf , phba - > ModelDesc , 256 ) ;
len = strlen ( lpfcinfobuf ) ;
snprintf ( lpfcinfobuf + len ,
384 - len ,
" on PCI bus %02x device %02x irq %d " ,
phba - > pcidev - > bus - > number ,
phba - > pcidev - > devfn ,
phba - > pcidev - > irq ) ;
len = strlen ( lpfcinfobuf ) ;
if ( phba - > Port [ 0 ] ) {
snprintf ( lpfcinfobuf + len ,
384 - len ,
" port %s " ,
phba - > Port ) ;
}
}
return lpfcinfobuf ;
}
static int
lpfc_queuecommand ( struct scsi_cmnd * cmnd , void ( * done ) ( struct scsi_cmnd * ) )
{
struct lpfc_hba * phba =
( struct lpfc_hba * ) cmnd - > device - > host - > hostdata [ 0 ] ;
struct lpfc_sli * psli = & phba - > sli ;
struct lpfc_rport_data * rdata = cmnd - > device - > hostdata ;
struct lpfc_nodelist * ndlp = rdata - > pnode ;
struct lpfc_scsi_buf * lpfc_cmd = NULL ;
struct list_head * scsi_buf_list = & phba - > lpfc_scsi_buf_list ;
int err = 0 ;
/*
* The target pointer is guaranteed not to be NULL because the driver
* only clears the device - > hostdata field in lpfc_slave_destroy . This
* approach guarantees no further IO calls on this target .
*/
if ( ! ndlp ) {
cmnd - > result = ScsiResult ( DID_NO_CONNECT , 0 ) ;
goto out_fail_command ;
}
/*
* A Fibre Channel target is present and functioning only when the node
* state is MAPPED . Any other state is a failure .
*/
if ( ndlp - > nlp_state ! = NLP_STE_MAPPED_NODE ) {
if ( ( ndlp - > nlp_state = = NLP_STE_UNMAPPED_NODE ) | |
( ndlp - > nlp_state = = NLP_STE_UNUSED_NODE ) ) {
cmnd - > result = ScsiResult ( DID_NO_CONNECT , 0 ) ;
goto out_fail_command ;
}
/*
* The device is most likely recovered and the driver
* needs a bit more time to finish . Ask the midlayer
* to retry .
*/
goto out_host_busy ;
}
list_remove_head ( scsi_buf_list , lpfc_cmd , struct lpfc_scsi_buf , list ) ;
if ( lpfc_cmd = = NULL ) {
printk ( KERN_WARNING " %s: No buffer available - list empty, "
" total count %d \n " , __FUNCTION__ , phba - > total_scsi_bufs ) ;
goto out_host_busy ;
}
/*
* Store the midlayer ' s command structure for the completion phase
* and complete the command initialization .
*/
lpfc_cmd - > pCmd = cmnd ;
lpfc_cmd - > rdata = rdata ;
lpfc_cmd - > timeout = 0 ;
cmnd - > host_scribble = ( unsigned char * ) lpfc_cmd ;
cmnd - > scsi_done = done ;
err = lpfc_scsi_prep_dma_buf ( phba , lpfc_cmd ) ;
if ( err )
goto out_host_busy_free_buf ;
lpfc_scsi_prep_cmnd ( phba , lpfc_cmd , ndlp ) ;
err = lpfc_sli_issue_iocb ( phba , & phba - > sli . ring [ psli - > fcp_ring ] ,
& lpfc_cmd - > cur_iocbq , SLI_IOCB_RET_IOCB ) ;
if ( err )
goto out_host_busy_free_buf ;
return 0 ;
out_host_busy_free_buf :
lpfc_free_scsi_buf ( lpfc_cmd ) ;
cmnd - > host_scribble = NULL ;
out_host_busy :
return SCSI_MLQUEUE_HOST_BUSY ;
out_fail_command :
done ( cmnd ) ;
return 0 ;
}
static int
2005-05-28 15:54:40 +04:00
__lpfc_abort_handler ( struct scsi_cmnd * cmnd )
2005-04-18 01:05:31 +04:00
{
struct lpfc_hba * phba =
( struct lpfc_hba * ) cmnd - > device - > host - > hostdata [ 0 ] ;
struct lpfc_sli_ring * pring = & phba - > sli . ring [ phba - > sli . fcp_ring ] ;
struct lpfc_iocbq * iocb , * next_iocb ;
struct lpfc_iocbq * abtsiocb = NULL ;
struct lpfc_scsi_buf * lpfc_cmd ;
struct list_head * lpfc_iocb_list = & phba - > lpfc_iocb_list ;
IOCB_t * cmd , * icmd ;
unsigned long snum ;
unsigned int id , lun ;
unsigned int loop_count = 0 ;
int ret = IOCB_SUCCESS ;
/*
* If the host_scribble data area is NULL , then the driver has already
* completed this command , but the midlayer did not see the completion
* before the eh fired . Just return SUCCESS .
*/
lpfc_cmd = ( struct lpfc_scsi_buf * ) cmnd - > host_scribble ;
if ( ! lpfc_cmd )
return SUCCESS ;
/* save these now since lpfc_cmd can be freed */
id = lpfc_cmd - > pCmd - > device - > id ;
lun = lpfc_cmd - > pCmd - > device - > lun ;
snum = lpfc_cmd - > pCmd - > serial_number ;
list_for_each_entry_safe ( iocb , next_iocb , & pring - > txq , list ) {
cmd = & iocb - > iocb ;
if ( iocb - > context1 ! = lpfc_cmd )
continue ;
list_del_init ( & iocb - > list ) ;
pring - > txq_cnt - - ;
if ( ! iocb - > iocb_cmpl ) {
list_add_tail ( & iocb - > list , lpfc_iocb_list ) ;
}
else {
cmd - > ulpStatus = IOSTAT_LOCAL_REJECT ;
cmd - > un . ulpWord [ 4 ] = IOERR_SLI_ABORTED ;
lpfc_scsi_cmd_iocb_cmpl_aborted ( phba , iocb , iocb ) ;
}
goto out ;
}
list_remove_head ( lpfc_iocb_list , abtsiocb , struct lpfc_iocbq , list ) ;
if ( abtsiocb = = NULL )
return FAILED ;
memset ( abtsiocb , 0 , sizeof ( struct lpfc_iocbq ) ) ;
/*
* The scsi command was not in the txq . Check the txcmplq and if it is
* found , send an abort to the FW .
*/
list_for_each_entry_safe ( iocb , next_iocb , & pring - > txcmplq , list ) {
if ( iocb - > context1 ! = lpfc_cmd )
continue ;
iocb - > iocb_cmpl = lpfc_scsi_cmd_iocb_cmpl_aborted ;
cmd = & iocb - > iocb ;
icmd = & abtsiocb - > iocb ;
icmd - > un . acxri . abortType = ABORT_TYPE_ABTS ;
icmd - > un . acxri . abortContextTag = cmd - > ulpContext ;
icmd - > un . acxri . abortIoTag = cmd - > ulpIoTag ;
icmd - > ulpLe = 1 ;
icmd - > ulpClass = cmd - > ulpClass ;
if ( phba - > hba_state > = LPFC_LINK_UP )
icmd - > ulpCommand = CMD_ABORT_XRI_CN ;
else
icmd - > ulpCommand = CMD_CLOSE_XRI_CN ;
if ( lpfc_sli_issue_iocb ( phba , pring , abtsiocb , 0 ) = =
IOCB_ERROR ) {
list_add_tail ( & abtsiocb - > list , lpfc_iocb_list ) ;
ret = IOCB_ERROR ;
break ;
}
/* Wait for abort to complete */
while ( cmnd - > host_scribble )
{
spin_unlock_irq ( phba - > host - > host_lock ) ;
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
schedule_timeout ( LPFC_ABORT_WAIT * HZ ) ;
spin_lock_irq ( phba - > host - > host_lock ) ;
if ( + + loop_count
> ( 2 * phba - > cfg_nodev_tmo ) / LPFC_ABORT_WAIT )
break ;
}
if ( cmnd - > host_scribble ) {
lpfc_printf_log ( phba , KERN_ERR , LOG_FCP ,
" %d:0748 abort handler timed "
" out waiting for abort to "
" complete. Data: "
" x%x x%x x%x x%lx \n " ,
phba - > brd_no , ret , id , lun , snum ) ;
cmnd - > host_scribble = NULL ;
iocb - > iocb_cmpl = lpfc_scsi_cmd_iocb_cleanup ;
ret = IOCB_ERROR ;
}
break ;
}
out :
lpfc_printf_log ( phba , KERN_WARNING , LOG_FCP ,
" %d:0749 SCSI layer issued abort device "
" Data: x%x x%x x%x x%lx \n " ,
phba - > brd_no , ret , id , lun , snum ) ;
return ret = = IOCB_SUCCESS ? SUCCESS : FAILED ;
}
2005-05-28 15:54:40 +04:00
static int
lpfc_abort_handler ( struct scsi_cmnd * cmnd )
{
int rc ;
spin_lock_irq ( cmnd - > device - > host - > host_lock ) ;
rc = __lpfc_abort_handler ( cmnd ) ;
spin_unlock_irq ( cmnd - > device - > host - > host_lock ) ;
return rc ;
}
2005-04-18 01:05:31 +04:00
static int
2005-05-28 15:55:48 +04:00
__lpfc_reset_lun_handler ( struct scsi_cmnd * cmnd )
2005-04-18 01:05:31 +04:00
{
struct Scsi_Host * shost = cmnd - > device - > host ;
struct lpfc_hba * phba = ( struct lpfc_hba * ) shost - > hostdata [ 0 ] ;
struct lpfc_sli * psli = & phba - > sli ;
struct lpfc_scsi_buf * lpfc_cmd = NULL ;
struct list_head * scsi_buf_list = & phba - > lpfc_scsi_buf_list ;
struct list_head * lpfc_iocb_list = & phba - > lpfc_iocb_list ;
struct lpfc_iocbq * iocbq , * iocbqrsp = NULL ;
struct lpfc_rport_data * rdata = cmnd - > device - > hostdata ;
struct lpfc_nodelist * pnode = rdata - > pnode ;
int ret = FAILED ;
int cnt , loopcnt ;
/*
* If target is not in a MAPPED state , delay the reset until
* target is rediscovered or nodev timeout expires .
*/
while ( 1 ) {
if ( ! pnode )
break ;
if ( pnode - > nlp_state ! = NLP_STE_MAPPED_NODE ) {
spin_unlock_irq ( phba - > host - > host_lock ) ;
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
schedule_timeout ( HZ / 2 ) ;
spin_lock_irq ( phba - > host - > host_lock ) ;
}
if ( ( pnode ) & & ( pnode - > nlp_state = = NLP_STE_MAPPED_NODE ) )
break ;
}
list_remove_head ( scsi_buf_list , lpfc_cmd , struct lpfc_scsi_buf , list ) ;
if ( lpfc_cmd = = NULL )
goto out ;
lpfc_cmd - > pCmd = cmnd ;
lpfc_cmd - > timeout = 60 ;
lpfc_cmd - > scsi_hba = phba ;
ret = lpfc_scsi_prep_task_mgmt_cmd ( phba , lpfc_cmd , FCP_LUN_RESET ) ;
if ( ! ret )
goto out_free_scsi_buf ;
iocbq = & lpfc_cmd - > cur_iocbq ;
/* get a buffer for this IOCB command response */
list_remove_head ( lpfc_iocb_list , iocbqrsp , struct lpfc_iocbq , list ) ;
if ( iocbqrsp = = NULL )
goto out_free_scsi_buf ;
memset ( iocbqrsp , 0 , sizeof ( struct lpfc_iocbq ) ) ;
iocbq - > iocb_flag | = LPFC_IO_POLL ;
iocbq - > iocb_cmpl = lpfc_sli_wake_iocb_high_priority ;
ret = lpfc_sli_issue_iocb_wait_high_priority ( phba ,
& phba - > sli . ring [ psli - > fcp_ring ] ,
iocbq , 0 , iocbqrsp , 60 ) ;
if ( ret = = IOCB_SUCCESS )
ret = SUCCESS ;
lpfc_cmd - > result = iocbqrsp - > iocb . un . ulpWord [ 4 ] ;
lpfc_cmd - > status = iocbqrsp - > iocb . ulpStatus ;
if ( lpfc_cmd - > status = = IOSTAT_LOCAL_REJECT )
if ( lpfc_cmd - > result & IOERR_DRVR_MASK )
lpfc_cmd - > status = IOSTAT_DRIVER_REJECT ;
/*
* All outstanding txcmplq I / Os should have been aborted by the target .
* Unfortunately , some targets do not abide by this forcing the driver
* to double check .
*/
lpfc_sli_abort_iocb ( phba , & phba - > sli . ring [ phba - > sli . fcp_ring ] ,
cmnd - > device - > id , cmnd - > device - > lun , 0 ,
LPFC_CTX_LUN ) ;
loopcnt = 0 ;
while ( ( cnt = lpfc_sli_sum_iocb ( phba ,
& phba - > sli . ring [ phba - > sli . fcp_ring ] ,
cmnd - > device - > id , cmnd - > device - > lun ,
LPFC_CTX_LUN ) ) ) {
spin_unlock_irq ( phba - > host - > host_lock ) ;
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
schedule_timeout ( LPFC_RESET_WAIT * HZ ) ;
spin_lock_irq ( phba - > host - > host_lock ) ;
if ( + + loopcnt
> ( 2 * phba - > cfg_nodev_tmo ) / LPFC_RESET_WAIT )
break ;
}
if ( cnt ) {
lpfc_printf_log ( phba , KERN_INFO , LOG_FCP ,
" %d:0719 LUN Reset I/O flush failure: cnt x%x \n " ,
phba - > brd_no , cnt ) ;
}
list_add_tail ( & iocbqrsp - > list , lpfc_iocb_list ) ;
out_free_scsi_buf :
lpfc_printf_log ( phba , KERN_ERR , LOG_FCP ,
" %d:0713 SCSI layer issued LUN reset (%d, %d) "
" Data: x%x x%x x%x \n " ,
phba - > brd_no , lpfc_cmd - > pCmd - > device - > id ,
lpfc_cmd - > pCmd - > device - > lun , ret , lpfc_cmd - > status ,
lpfc_cmd - > result ) ;
lpfc_free_scsi_buf ( lpfc_cmd ) ;
out :
return ret ;
}
2005-05-28 15:55:48 +04:00
static int
lpfc_reset_lun_handler ( struct scsi_cmnd * cmnd )
{
int rc ;
spin_lock_irq ( cmnd - > device - > host - > host_lock ) ;
rc = __lpfc_reset_lun_handler ( cmnd ) ;
spin_unlock_irq ( cmnd - > device - > host - > host_lock ) ;
return rc ;
}
2005-04-18 01:05:31 +04:00
/*
* Note : midlayer calls this function with the host_lock held
*/
static int
lpfc_reset_bus_handler ( struct scsi_cmnd * cmnd )
{
struct Scsi_Host * shost = cmnd - > device - > host ;
struct lpfc_hba * phba = ( struct lpfc_hba * ) shost - > hostdata [ 0 ] ;
struct lpfc_nodelist * ndlp = NULL ;
int match ;
int ret = FAILED , i , err_count = 0 ;
int cnt , loopcnt ;
unsigned int midlayer_id = 0 ;
struct lpfc_scsi_buf * lpfc_cmd = NULL ;
struct list_head * scsi_buf_list = & phba - > lpfc_scsi_buf_list ;
list_remove_head ( scsi_buf_list , lpfc_cmd , struct lpfc_scsi_buf , list ) ;
if ( lpfc_cmd = = NULL )
goto out ;
/* The lpfc_cmd storage is reused. Set all loop invariants. */
lpfc_cmd - > timeout = 60 ;
lpfc_cmd - > pCmd = cmnd ;
lpfc_cmd - > scsi_hba = phba ;
/*
* Since the driver manages a single bus device , reset all
* targets known to the driver . Should any target reset
* fail , this routine returns failure to the midlayer .
*/
midlayer_id = cmnd - > device - > id ;
for ( i = 0 ; i < MAX_FCP_TARGET ; i + + ) {
/* Search the mapped list for this target ID */
match = 0 ;
list_for_each_entry ( ndlp , & phba - > fc_nlpmap_list , nlp_listp ) {
if ( ( i = = ndlp - > nlp_sid ) & & ndlp - > rport ) {
match = 1 ;
break ;
}
}
if ( ! match )
continue ;
lpfc_cmd - > pCmd - > device - > id = i ;
lpfc_cmd - > pCmd - > device - > hostdata = ndlp - > rport - > dd_data ;
ret = lpfc_scsi_tgt_reset ( lpfc_cmd , phba ) ;
if ( ret ! = SUCCESS ) {
lpfc_printf_log ( phba , KERN_INFO , LOG_FCP ,
" %d:0713 Bus Reset on target %d failed \n " ,
phba - > brd_no , i ) ;
err_count + + ;
}
}
cmnd - > device - > id = midlayer_id ;
loopcnt = 0 ;
while ( ( cnt = lpfc_sli_sum_iocb ( phba ,
& phba - > sli . ring [ phba - > sli . fcp_ring ] ,
0 , 0 , LPFC_CTX_HOST ) ) ) {
spin_unlock_irq ( phba - > host - > host_lock ) ;
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
schedule_timeout ( LPFC_RESET_WAIT * HZ ) ;
spin_lock_irq ( phba - > host - > host_lock ) ;
if ( + + loopcnt
> ( 2 * phba - > cfg_nodev_tmo ) / LPFC_RESET_WAIT )
break ;
}
if ( cnt ) {
/* flush all outstanding commands on the host */
i = lpfc_sli_abort_iocb ( phba ,
& phba - > sli . ring [ phba - > sli . fcp_ring ] , 0 , 0 , 0 ,
LPFC_CTX_HOST ) ;
lpfc_printf_log ( phba , KERN_INFO , LOG_FCP ,
" %d:0715 Bus Reset I/O flush failure: cnt x%x left x%x \n " ,
phba - > brd_no , cnt , i ) ;
}
if ( ! err_count )
ret = SUCCESS ;
lpfc_free_scsi_buf ( lpfc_cmd ) ;
lpfc_printf_log ( phba ,
KERN_ERR ,
LOG_FCP ,
" %d:0714 SCSI layer issued Bus Reset Data: x%x \n " ,
phba - > brd_no , ret ) ;
out :
return ret ;
}
static int
lpfc_slave_alloc ( struct scsi_device * sdev )
{
struct lpfc_hba * phba = ( struct lpfc_hba * ) sdev - > host - > hostdata [ 0 ] ;
struct lpfc_nodelist * ndlp = NULL ;
int match = 0 ;
struct lpfc_scsi_buf * scsi_buf = NULL ;
uint32_t total = 0 , i ;
uint32_t num_to_alloc = 0 ;
unsigned long flags ;
struct list_head * listp ;
struct list_head * node_list [ 6 ] ;
/*
* Store the target pointer in the scsi_device hostdata pointer provided
* the driver has already discovered the target id .
*/
/* Search the nlp lists other than unmap_list for this target ID */
node_list [ 0 ] = & phba - > fc_npr_list ;
node_list [ 1 ] = & phba - > fc_nlpmap_list ;
node_list [ 2 ] = & phba - > fc_prli_list ;
node_list [ 3 ] = & phba - > fc_reglogin_list ;
node_list [ 4 ] = & phba - > fc_adisc_list ;
node_list [ 5 ] = & phba - > fc_plogi_list ;
for ( i = 0 ; i < 6 & & ! match ; i + + ) {
listp = node_list [ i ] ;
if ( list_empty ( listp ) )
continue ;
list_for_each_entry ( ndlp , listp , nlp_listp ) {
if ( ( sdev - > id = = ndlp - > nlp_sid ) & & ndlp - > rport ) {
match = 1 ;
break ;
}
}
}
if ( ! match )
return - ENXIO ;
sdev - > hostdata = ndlp - > rport - > dd_data ;
/*
* Populate the cmds_per_lun count scsi_bufs into this host ' s globally
* available list of scsi buffers . Don ' t allocate more than the
* HBA limit conveyed to the midlayer via the host structure . Note
* that this list of scsi bufs exists for the lifetime of the driver .
*/
total = phba - > total_scsi_bufs ;
num_to_alloc = LPFC_CMD_PER_LUN ;
if ( total > = phba - > cfg_hba_queue_depth ) {
printk ( KERN_WARNING " %s, At config limitation of "
" %d allocated scsi_bufs \n " , __FUNCTION__ , total ) ;
return 0 ;
} else if ( total + num_to_alloc > phba - > cfg_hba_queue_depth ) {
num_to_alloc = phba - > cfg_hba_queue_depth - total ;
}
for ( i = 0 ; i < num_to_alloc ; i + + ) {
scsi_buf = lpfc_get_scsi_buf ( phba ) ;
if ( ! scsi_buf ) {
printk ( KERN_ERR " %s, failed to allocate "
" scsi_buf \n " , __FUNCTION__ ) ;
break ;
}
spin_lock_irqsave ( phba - > host - > host_lock , flags ) ;
phba - > total_scsi_bufs + + ;
list_add_tail ( & scsi_buf - > list , & phba - > lpfc_scsi_buf_list ) ;
spin_unlock_irqrestore ( phba - > host - > host_lock , flags ) ;
}
return 0 ;
}
static int
lpfc_slave_configure ( struct scsi_device * sdev )
{
struct lpfc_hba * phba = ( struct lpfc_hba * ) sdev - > host - > hostdata [ 0 ] ;
struct fc_rport * rport = starget_to_rport ( sdev - > sdev_target ) ;
if ( sdev - > tagged_supported )
scsi_activate_tcq ( sdev , phba - > cfg_lun_queue_depth ) ;
else
scsi_deactivate_tcq ( sdev , phba - > cfg_lun_queue_depth ) ;
/*
* Initialize the fc transport attributes for the target
* containing this scsi device . Also note that the driver ' s
* target pointer is stored in the starget_data for the
* driver ' s sysfs entry point functions .
*/
rport - > dev_loss_tmo = phba - > cfg_nodev_tmo + 5 ;
return 0 ;
}
static void
lpfc_slave_destroy ( struct scsi_device * sdev )
{
sdev - > hostdata = NULL ;
return ;
}
struct scsi_host_template lpfc_template = {
. module = THIS_MODULE ,
. name = LPFC_DRIVER_NAME ,
. info = lpfc_info ,
. queuecommand = lpfc_queuecommand ,
. eh_abort_handler = lpfc_abort_handler ,
. eh_device_reset_handler = lpfc_reset_lun_handler ,
. eh_bus_reset_handler = lpfc_reset_bus_handler ,
. slave_alloc = lpfc_slave_alloc ,
. slave_configure = lpfc_slave_configure ,
. slave_destroy = lpfc_slave_destroy ,
. this_id = - 1 ,
. sg_tablesize = LPFC_SG_SEG_CNT ,
. cmd_per_lun = LPFC_CMD_PER_LUN ,
. use_clustering = ENABLE_CLUSTERING ,
. shost_attrs = lpfc_host_attrs ,
} ;