2008-07-01 22:14:30 +04:00
/*
* ibmvfc . c - - driver for IBM Power Virtual Fibre Channel Adapter
*
* Written By : Brian King < brking @ linux . vnet . ibm . com > , IBM Corporation
*
* Copyright ( C ) IBM Corporation , 2008
*
* 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/dma-mapping.h>
# include <linux/dmapool.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/kthread.h>
# include <linux/of.h>
# include <linux/stringify.h>
# include <asm/firmware.h>
# include <asm/irq.h>
# include <asm/vio.h>
# include <scsi/scsi.h>
# include <scsi/scsi_cmnd.h>
# include <scsi/scsi_host.h>
# include <scsi/scsi_device.h>
# include <scsi/scsi_tcq.h>
# include <scsi/scsi_transport_fc.h>
# include "ibmvfc.h"
static unsigned int init_timeout = IBMVFC_INIT_TIMEOUT ;
static unsigned int default_timeout = IBMVFC_DEFAULT_TIMEOUT ;
static unsigned int max_lun = IBMVFC_MAX_LUN ;
static unsigned int max_targets = IBMVFC_MAX_TARGETS ;
static unsigned int max_requests = IBMVFC_MAX_REQUESTS_DEFAULT ;
static unsigned int disc_threads = IBMVFC_MAX_DISC_THREADS ;
static unsigned int dev_loss_tmo = IBMVFC_DEV_LOSS_TMO ;
static unsigned int ibmvfc_debug = IBMVFC_DEBUG ;
static unsigned int log_level = IBMVFC_DEFAULT_LOG_LEVEL ;
static LIST_HEAD ( ibmvfc_head ) ;
static DEFINE_SPINLOCK ( ibmvfc_driver_lock ) ;
static struct scsi_transport_template * ibmvfc_transport_template ;
MODULE_DESCRIPTION ( " IBM Virtual Fibre Channel Driver " ) ;
MODULE_AUTHOR ( " Brian King <brking@linux.vnet.ibm.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( IBMVFC_DRIVER_VERSION ) ;
module_param_named ( init_timeout , init_timeout , uint , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( init_timeout , " Initialization timeout in seconds. "
" [Default= " __stringify ( IBMVFC_INIT_TIMEOUT ) " ] " ) ;
module_param_named ( default_timeout , default_timeout , uint , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( default_timeout ,
" Default timeout in seconds for initialization and EH commands. "
" [Default= " __stringify ( IBMVFC_DEFAULT_TIMEOUT ) " ] " ) ;
module_param_named ( max_requests , max_requests , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( max_requests , " Maximum requests for this adapter. "
" [Default= " __stringify ( IBMVFC_MAX_REQUESTS_DEFAULT ) " ] " ) ;
module_param_named ( max_lun , max_lun , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( max_lun , " Maximum allowed LUN. "
" [Default= " __stringify ( IBMVFC_MAX_LUN ) " ] " ) ;
module_param_named ( max_targets , max_targets , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( max_targets , " Maximum allowed targets. "
" [Default= " __stringify ( IBMVFC_MAX_TARGETS ) " ] " ) ;
2009-03-20 23:44:37 +03:00
module_param_named ( disc_threads , disc_threads , uint , S_IRUGO ) ;
2008-07-01 22:14:30 +04:00
MODULE_PARM_DESC ( disc_threads , " Number of device discovery threads to use. "
" [Default= " __stringify ( IBMVFC_MAX_DISC_THREADS ) " ] " ) ;
module_param_named ( debug , ibmvfc_debug , uint , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( debug , " Enable driver debug information. "
" [Default= " __stringify ( IBMVFC_DEBUG ) " ] " ) ;
module_param_named ( dev_loss_tmo , dev_loss_tmo , uint , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( dev_loss_tmo , " Maximum number of seconds that the FC "
" transport should insulate the loss of a remote port. Once this "
" value is exceeded, the scsi target is removed. "
" [Default= " __stringify ( IBMVFC_DEV_LOSS_TMO ) " ] " ) ;
module_param_named ( log_level , log_level , uint , 0 ) ;
MODULE_PARM_DESC ( log_level , " Set to 0 - 4 for increasing verbosity of device driver. "
" [Default= " __stringify ( IBMVFC_DEFAULT_LOG_LEVEL ) " ] " ) ;
static const struct {
u16 status ;
u16 error ;
u8 result ;
u8 retry ;
int log ;
char * name ;
} cmd_status [ ] = {
{ IBMVFC_FABRIC_MAPPED , IBMVFC_UNABLE_TO_ESTABLISH , DID_ERROR , 1 , 1 , " unable to establish " } ,
{ IBMVFC_FABRIC_MAPPED , IBMVFC_XPORT_FAULT , DID_OK , 1 , 0 , " transport fault " } ,
{ IBMVFC_FABRIC_MAPPED , IBMVFC_CMD_TIMEOUT , DID_TIME_OUT , 1 , 1 , " command timeout " } ,
2008-12-16 02:09:05 +03:00
{ IBMVFC_FABRIC_MAPPED , IBMVFC_ENETDOWN , DID_TRANSPORT_DISRUPTED , 1 , 1 , " network down " } ,
2008-07-01 22:14:30 +04:00
{ IBMVFC_FABRIC_MAPPED , IBMVFC_HW_FAILURE , DID_ERROR , 1 , 1 , " hardware failure " } ,
{ IBMVFC_FABRIC_MAPPED , IBMVFC_LINK_DOWN_ERR , DID_REQUEUE , 0 , 0 , " link down " } ,
{ IBMVFC_FABRIC_MAPPED , IBMVFC_LINK_DEAD_ERR , DID_ERROR , 0 , 0 , " link dead " } ,
{ IBMVFC_FABRIC_MAPPED , IBMVFC_UNABLE_TO_REGISTER , DID_ERROR , 1 , 1 , " unable to register " } ,
{ IBMVFC_FABRIC_MAPPED , IBMVFC_XPORT_BUSY , DID_BUS_BUSY , 1 , 0 , " transport busy " } ,
{ IBMVFC_FABRIC_MAPPED , IBMVFC_XPORT_DEAD , DID_ERROR , 0 , 1 , " transport dead " } ,
{ IBMVFC_FABRIC_MAPPED , IBMVFC_CONFIG_ERROR , DID_ERROR , 1 , 1 , " configuration error " } ,
{ IBMVFC_FABRIC_MAPPED , IBMVFC_NAME_SERVER_FAIL , DID_ERROR , 1 , 1 , " name server failure " } ,
2009-05-29 01:17:30 +04:00
{ IBMVFC_FABRIC_MAPPED , IBMVFC_LINK_HALTED , DID_REQUEUE , 1 , 0 , " link halted " } ,
2008-07-01 22:14:30 +04:00
{ IBMVFC_FABRIC_MAPPED , IBMVFC_XPORT_GENERAL , DID_OK , 1 , 0 , " general transport error " } ,
{ IBMVFC_VIOS_FAILURE , IBMVFC_CRQ_FAILURE , DID_REQUEUE , 1 , 1 , " CRQ failure " } ,
{ IBMVFC_VIOS_FAILURE , IBMVFC_SW_FAILURE , DID_ERROR , 0 , 1 , " software failure " } ,
2008-12-16 02:09:05 +03:00
{ IBMVFC_VIOS_FAILURE , IBMVFC_INVALID_PARAMETER , DID_ERROR , 0 , 1 , " invalid parameter " } ,
{ IBMVFC_VIOS_FAILURE , IBMVFC_MISSING_PARAMETER , DID_ERROR , 0 , 1 , " missing parameter " } ,
2008-07-01 22:14:30 +04:00
{ IBMVFC_VIOS_FAILURE , IBMVFC_HOST_IO_BUS , DID_ERROR , 1 , 1 , " host I/O bus failure " } ,
2008-12-16 02:09:05 +03:00
{ IBMVFC_VIOS_FAILURE , IBMVFC_TRANS_CANCELLED , DID_ERROR , 0 , 1 , " transaction cancelled " } ,
{ IBMVFC_VIOS_FAILURE , IBMVFC_TRANS_CANCELLED_IMPLICIT , DID_ERROR , 0 , 1 , " transaction cancelled implicit " } ,
2008-07-01 22:14:30 +04:00
{ IBMVFC_VIOS_FAILURE , IBMVFC_INSUFFICIENT_RESOURCE , DID_REQUEUE , 1 , 1 , " insufficient resources " } ,
2008-11-14 22:33:53 +03:00
{ IBMVFC_VIOS_FAILURE , IBMVFC_PLOGI_REQUIRED , DID_ERROR , 0 , 1 , " port login required " } ,
2008-07-01 22:14:30 +04:00
{ IBMVFC_VIOS_FAILURE , IBMVFC_COMMAND_FAILED , DID_ERROR , 1 , 1 , " command failed " } ,
{ IBMVFC_FC_FAILURE , IBMVFC_INVALID_ELS_CMD_CODE , DID_ERROR , 0 , 1 , " invalid ELS command code " } ,
{ IBMVFC_FC_FAILURE , IBMVFC_INVALID_VERSION , DID_ERROR , 0 , 1 , " invalid version level " } ,
{ IBMVFC_FC_FAILURE , IBMVFC_LOGICAL_ERROR , DID_ERROR , 1 , 1 , " logical error " } ,
{ IBMVFC_FC_FAILURE , IBMVFC_INVALID_CT_IU_SIZE , DID_ERROR , 0 , 1 , " invalid CT_IU size " } ,
{ IBMVFC_FC_FAILURE , IBMVFC_LOGICAL_BUSY , DID_REQUEUE , 1 , 0 , " logical busy " } ,
{ IBMVFC_FC_FAILURE , IBMVFC_PROTOCOL_ERROR , DID_ERROR , 1 , 1 , " protocol error " } ,
{ IBMVFC_FC_FAILURE , IBMVFC_UNABLE_TO_PERFORM_REQ , DID_ERROR , 1 , 1 , " unable to perform request " } ,
{ IBMVFC_FC_FAILURE , IBMVFC_CMD_NOT_SUPPORTED , DID_ERROR , 0 , 0 , " command not supported " } ,
{ IBMVFC_FC_FAILURE , IBMVFC_SERVER_NOT_AVAIL , DID_ERROR , 0 , 1 , " server not available " } ,
{ IBMVFC_FC_FAILURE , IBMVFC_CMD_IN_PROGRESS , DID_ERROR , 0 , 1 , " command already in progress " } ,
{ IBMVFC_FC_FAILURE , IBMVFC_VENDOR_SPECIFIC , DID_ERROR , 1 , 1 , " vendor specific " } ,
{ IBMVFC_FC_SCSI_ERROR , 0 , DID_OK , 1 , 0 , " SCSI error " } ,
} ;
static void ibmvfc_npiv_login ( struct ibmvfc_host * ) ;
static void ibmvfc_tgt_send_prli ( struct ibmvfc_target * ) ;
static void ibmvfc_tgt_send_plogi ( struct ibmvfc_target * ) ;
static void ibmvfc_tgt_query_target ( struct ibmvfc_target * ) ;
2009-05-29 01:17:29 +04:00
static void ibmvfc_npiv_logout ( struct ibmvfc_host * ) ;
2008-07-01 22:14:30 +04:00
static const char * unknown_error = " unknown error " ;
# ifdef CONFIG_SCSI_IBMVFC_TRACE
/**
* ibmvfc_trc_start - Log a start trace entry
* @ evt : ibmvfc event struct
*
* */
static void ibmvfc_trc_start ( struct ibmvfc_event * evt )
{
struct ibmvfc_host * vhost = evt - > vhost ;
struct ibmvfc_cmd * vfc_cmd = & evt - > iu . cmd ;
struct ibmvfc_mad_common * mad = & evt - > iu . mad_common ;
struct ibmvfc_trace_entry * entry ;
entry = & vhost - > trace [ vhost - > trace_index + + ] ;
entry - > evt = evt ;
entry - > time = jiffies ;
entry - > fmt = evt - > crq . format ;
entry - > type = IBMVFC_TRC_START ;
switch ( entry - > fmt ) {
case IBMVFC_CMD_FORMAT :
entry - > op_code = vfc_cmd - > iu . cdb [ 0 ] ;
entry - > scsi_id = vfc_cmd - > tgt_scsi_id ;
entry - > lun = scsilun_to_int ( & vfc_cmd - > iu . lun ) ;
entry - > tmf_flags = vfc_cmd - > iu . tmf_flags ;
entry - > u . start . xfer_len = vfc_cmd - > iu . xfer_len ;
break ;
case IBMVFC_MAD_FORMAT :
entry - > op_code = mad - > opcode ;
break ;
default :
break ;
} ;
}
/**
* ibmvfc_trc_end - Log an end trace entry
* @ evt : ibmvfc event struct
*
* */
static void ibmvfc_trc_end ( struct ibmvfc_event * evt )
{
struct ibmvfc_host * vhost = evt - > vhost ;
struct ibmvfc_cmd * vfc_cmd = & evt - > xfer_iu - > cmd ;
struct ibmvfc_mad_common * mad = & evt - > xfer_iu - > mad_common ;
struct ibmvfc_trace_entry * entry = & vhost - > trace [ vhost - > trace_index + + ] ;
entry - > evt = evt ;
entry - > time = jiffies ;
entry - > fmt = evt - > crq . format ;
entry - > type = IBMVFC_TRC_END ;
switch ( entry - > fmt ) {
case IBMVFC_CMD_FORMAT :
entry - > op_code = vfc_cmd - > iu . cdb [ 0 ] ;
entry - > scsi_id = vfc_cmd - > tgt_scsi_id ;
entry - > lun = scsilun_to_int ( & vfc_cmd - > iu . lun ) ;
entry - > tmf_flags = vfc_cmd - > iu . tmf_flags ;
entry - > u . end . status = vfc_cmd - > status ;
entry - > u . end . error = vfc_cmd - > error ;
entry - > u . end . fcp_rsp_flags = vfc_cmd - > rsp . flags ;
entry - > u . end . rsp_code = vfc_cmd - > rsp . data . info . rsp_code ;
entry - > u . end . scsi_status = vfc_cmd - > rsp . scsi_status ;
break ;
case IBMVFC_MAD_FORMAT :
entry - > op_code = mad - > opcode ;
entry - > u . end . status = mad - > status ;
break ;
default :
break ;
} ;
}
# else
# define ibmvfc_trc_start(evt) do { } while (0)
# define ibmvfc_trc_end(evt) do { } while (0)
# endif
/**
* ibmvfc_get_err_index - Find the index into cmd_status for the fcp response
* @ status : status / error class
* @ error : error
*
* Return value :
* index into cmd_status / - EINVAL on failure
* */
static int ibmvfc_get_err_index ( u16 status , u16 error )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( cmd_status ) ; i + + )
if ( ( cmd_status [ i ] . status & status ) = = cmd_status [ i ] . status & &
cmd_status [ i ] . error = = error )
return i ;
return - EINVAL ;
}
/**
* ibmvfc_get_cmd_error - Find the error description for the fcp response
* @ status : status / error class
* @ error : error
*
* Return value :
* error description string
* */
static const char * ibmvfc_get_cmd_error ( u16 status , u16 error )
{
int rc = ibmvfc_get_err_index ( status , error ) ;
if ( rc > = 0 )
return cmd_status [ rc ] . name ;
return unknown_error ;
}
/**
* ibmvfc_get_err_result - Find the scsi status to return for the fcp response
* @ vfc_cmd : ibmvfc command struct
*
* Return value :
* SCSI result value to return for completed command
* */
static int ibmvfc_get_err_result ( struct ibmvfc_cmd * vfc_cmd )
{
int err ;
struct ibmvfc_fcp_rsp * rsp = & vfc_cmd - > rsp ;
int fc_rsp_len = rsp - > fcp_rsp_len ;
if ( ( rsp - > flags & FCP_RSP_LEN_VALID ) & &
2009-05-29 01:17:22 +04:00
( ( fc_rsp_len & & fc_rsp_len ! = 4 & & fc_rsp_len ! = 8 ) | |
2008-07-01 22:14:30 +04:00
rsp - > data . info . rsp_code ) )
return DID_ERROR < < 16 ;
err = ibmvfc_get_err_index ( vfc_cmd - > status , vfc_cmd - > error ) ;
if ( err > = 0 )
return rsp - > scsi_status | ( cmd_status [ err ] . result < < 16 ) ;
return rsp - > scsi_status | ( DID_ERROR < < 16 ) ;
}
/**
* ibmvfc_retry_cmd - Determine if error status is retryable
* @ status : status / error class
* @ error : error
*
* Return value :
* 1 if error should be retried / 0 if it should not
* */
static int ibmvfc_retry_cmd ( u16 status , u16 error )
{
int rc = ibmvfc_get_err_index ( status , error ) ;
if ( rc > = 0 )
return cmd_status [ rc ] . retry ;
return 1 ;
}
static const char * unknown_fc_explain = " unknown fc explain " ;
static const struct {
u16 fc_explain ;
char * name ;
} ls_explain [ ] = {
{ 0x00 , " no additional explanation " } ,
{ 0x01 , " service parameter error - options " } ,
{ 0x03 , " service parameter error - initiator control " } ,
{ 0x05 , " service parameter error - recipient control " } ,
{ 0x07 , " service parameter error - received data field size " } ,
{ 0x09 , " service parameter error - concurrent seq " } ,
{ 0x0B , " service parameter error - credit " } ,
{ 0x0D , " invalid N_Port/F_Port_Name " } ,
{ 0x0E , " invalid node/Fabric Name " } ,
{ 0x0F , " invalid common service parameters " } ,
{ 0x11 , " invalid association header " } ,
{ 0x13 , " association header required " } ,
{ 0x15 , " invalid originator S_ID " } ,
{ 0x17 , " invalid OX_ID-RX-ID combination " } ,
{ 0x19 , " command (request) already in progress " } ,
{ 0x1E , " N_Port Login requested " } ,
{ 0x1F , " Invalid N_Port_ID " } ,
} ;
static const struct {
u16 fc_explain ;
char * name ;
} gs_explain [ ] = {
{ 0x00 , " no additional explanation " } ,
{ 0x01 , " port identifier not registered " } ,
{ 0x02 , " port name not registered " } ,
{ 0x03 , " node name not registered " } ,
{ 0x04 , " class of service not registered " } ,
{ 0x06 , " initial process associator not registered " } ,
{ 0x07 , " FC-4 TYPEs not registered " } ,
{ 0x08 , " symbolic port name not registered " } ,
{ 0x09 , " symbolic node name not registered " } ,
{ 0x0A , " port type not registered " } ,
{ 0xF0 , " authorization exception " } ,
{ 0xF1 , " authentication exception " } ,
{ 0xF2 , " data base full " } ,
{ 0xF3 , " data base empty " } ,
{ 0xF4 , " processing request " } ,
{ 0xF5 , " unable to verify connection " } ,
{ 0xF6 , " devices not in a common zone " } ,
} ;
/**
* ibmvfc_get_ls_explain - Return the FC Explain description text
* @ status : FC Explain status
*
* Returns :
* error string
* */
static const char * ibmvfc_get_ls_explain ( u16 status )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( ls_explain ) ; i + + )
if ( ls_explain [ i ] . fc_explain = = status )
return ls_explain [ i ] . name ;
return unknown_fc_explain ;
}
/**
* ibmvfc_get_gs_explain - Return the FC Explain description text
* @ status : FC Explain status
*
* Returns :
* error string
* */
static const char * ibmvfc_get_gs_explain ( u16 status )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( gs_explain ) ; i + + )
if ( gs_explain [ i ] . fc_explain = = status )
return gs_explain [ i ] . name ;
return unknown_fc_explain ;
}
static const struct {
enum ibmvfc_fc_type fc_type ;
char * name ;
} fc_type [ ] = {
{ IBMVFC_FABRIC_REJECT , " fabric reject " } ,
{ IBMVFC_PORT_REJECT , " port reject " } ,
{ IBMVFC_LS_REJECT , " ELS reject " } ,
{ IBMVFC_FABRIC_BUSY , " fabric busy " } ,
{ IBMVFC_PORT_BUSY , " port busy " } ,
{ IBMVFC_BASIC_REJECT , " basic reject " } ,
} ;
static const char * unknown_fc_type = " unknown fc type " ;
/**
* ibmvfc_get_fc_type - Return the FC Type description text
* @ status : FC Type error status
*
* Returns :
* error string
* */
static const char * ibmvfc_get_fc_type ( u16 status )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( fc_type ) ; i + + )
if ( fc_type [ i ] . fc_type = = status )
return fc_type [ i ] . name ;
return unknown_fc_type ;
}
/**
* ibmvfc_set_tgt_action - Set the next init action for the target
* @ tgt : ibmvfc target struct
* @ action : action to perform
*
* */
static void ibmvfc_set_tgt_action ( struct ibmvfc_target * tgt ,
enum ibmvfc_target_action action )
{
switch ( tgt - > action ) {
case IBMVFC_TGT_ACTION_DEL_RPORT :
break ;
default :
2009-05-29 01:17:28 +04:00
if ( action = = IBMVFC_TGT_ACTION_DEL_RPORT )
tgt - > add_rport = 0 ;
2008-07-01 22:14:30 +04:00
tgt - > action = action ;
break ;
}
}
/**
* ibmvfc_set_host_state - Set the state for the host
* @ vhost : ibmvfc host struct
* @ state : state to set host to
*
* Returns :
* 0 if state changed / non - zero if not changed
* */
static int ibmvfc_set_host_state ( struct ibmvfc_host * vhost ,
enum ibmvfc_host_state state )
{
int rc = 0 ;
switch ( vhost - > state ) {
case IBMVFC_HOST_OFFLINE :
rc = - EINVAL ;
break ;
default :
vhost - > state = state ;
break ;
} ;
return rc ;
}
/**
* ibmvfc_set_host_action - Set the next init action for the host
* @ vhost : ibmvfc host struct
* @ action : action to perform
*
* */
static void ibmvfc_set_host_action ( struct ibmvfc_host * vhost ,
enum ibmvfc_host_action action )
{
switch ( action ) {
case IBMVFC_HOST_ACTION_ALLOC_TGTS :
if ( vhost - > action = = IBMVFC_HOST_ACTION_INIT_WAIT )
vhost - > action = action ;
break ;
2009-05-29 01:17:29 +04:00
case IBMVFC_HOST_ACTION_LOGO_WAIT :
if ( vhost - > action = = IBMVFC_HOST_ACTION_LOGO )
vhost - > action = action ;
break ;
2008-07-01 22:14:30 +04:00
case IBMVFC_HOST_ACTION_INIT_WAIT :
if ( vhost - > action = = IBMVFC_HOST_ACTION_INIT )
vhost - > action = action ;
break ;
case IBMVFC_HOST_ACTION_QUERY :
switch ( vhost - > action ) {
case IBMVFC_HOST_ACTION_INIT_WAIT :
case IBMVFC_HOST_ACTION_NONE :
2009-05-29 01:17:28 +04:00
case IBMVFC_HOST_ACTION_TGT_DEL_FAILED :
2008-07-01 22:14:30 +04:00
vhost - > action = action ;
break ;
default :
break ;
} ;
break ;
case IBMVFC_HOST_ACTION_TGT_INIT :
if ( vhost - > action = = IBMVFC_HOST_ACTION_ALLOC_TGTS )
vhost - > action = action ;
break ;
2009-05-29 01:17:29 +04:00
case IBMVFC_HOST_ACTION_LOGO :
2008-07-01 22:14:30 +04:00
case IBMVFC_HOST_ACTION_INIT :
case IBMVFC_HOST_ACTION_TGT_DEL :
case IBMVFC_HOST_ACTION_QUERY_TGTS :
2008-10-29 16:46:45 +03:00
case IBMVFC_HOST_ACTION_TGT_DEL_FAILED :
2008-07-01 22:14:30 +04:00
case IBMVFC_HOST_ACTION_NONE :
default :
vhost - > action = action ;
break ;
} ;
}
/**
* ibmvfc_reinit_host - Re - start host initialization ( no NPIV Login )
* @ vhost : ibmvfc host struct
*
* Return value :
* nothing
* */
static void ibmvfc_reinit_host ( struct ibmvfc_host * vhost )
{
if ( vhost - > action = = IBMVFC_HOST_ACTION_NONE ) {
2008-07-22 17:31:42 +04:00
if ( ! ibmvfc_set_host_state ( vhost , IBMVFC_INITIALIZING ) ) {
scsi_block_requests ( vhost - > host ) ;
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_QUERY ) ;
}
2008-07-01 22:14:30 +04:00
} else
vhost - > reinit = 1 ;
wake_up ( & vhost - > work_wait_q ) ;
}
/**
* ibmvfc_link_down - Handle a link down event from the adapter
* @ vhost : ibmvfc host struct
* @ state : ibmvfc host state to enter
*
* */
static void ibmvfc_link_down ( struct ibmvfc_host * vhost ,
enum ibmvfc_host_state state )
{
struct ibmvfc_target * tgt ;
ENTER ;
scsi_block_requests ( vhost - > host ) ;
list_for_each_entry ( tgt , & vhost - > targets , queue )
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_DEL_RPORT ) ;
ibmvfc_set_host_state ( vhost , state ) ;
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_TGT_DEL ) ;
vhost - > events_to_log | = IBMVFC_AE_LINKDOWN ;
wake_up ( & vhost - > work_wait_q ) ;
LEAVE ;
}
/**
* ibmvfc_init_host - Start host initialization
* @ vhost : ibmvfc host struct
*
* Return value :
* nothing
* */
2009-10-20 00:07:49 +04:00
static void ibmvfc_init_host ( struct ibmvfc_host * vhost )
2008-07-01 22:14:30 +04:00
{
struct ibmvfc_target * tgt ;
if ( vhost - > action = = IBMVFC_HOST_ACTION_INIT_WAIT ) {
2008-12-03 20:02:54 +03:00
if ( + + vhost - > init_retries > IBMVFC_MAX_HOST_INIT_RETRIES ) {
2008-07-01 22:14:30 +04:00
dev_err ( vhost - > dev ,
" Host initialization retries exceeded. Taking adapter offline \n " ) ;
ibmvfc_link_down ( vhost , IBMVFC_HOST_OFFLINE ) ;
return ;
}
}
if ( ! ibmvfc_set_host_state ( vhost , IBMVFC_INITIALIZING ) ) {
2009-10-20 00:07:49 +04:00
memset ( vhost - > async_crq . msgs , 0 , PAGE_SIZE ) ;
vhost - > async_crq . cur = 0 ;
2008-08-15 19:59:23 +04:00
2008-07-01 22:14:30 +04:00
list_for_each_entry ( tgt , & vhost - > targets , queue )
2009-05-29 01:17:32 +04:00
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_DEL_RPORT ) ;
2008-07-01 22:14:30 +04:00
scsi_block_requests ( vhost - > host ) ;
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_INIT ) ;
vhost - > job_step = ibmvfc_npiv_login ;
wake_up ( & vhost - > work_wait_q ) ;
}
}
/**
* ibmvfc_send_crq - Send a CRQ
* @ vhost : ibmvfc host struct
* @ word1 : the first 64 bits of the data
* @ word2 : the second 64 bits of the data
*
* Return value :
* 0 on success / other on failure
* */
static int ibmvfc_send_crq ( struct ibmvfc_host * vhost , u64 word1 , u64 word2 )
{
struct vio_dev * vdev = to_vio_dev ( vhost - > dev ) ;
return plpar_hcall_norets ( H_SEND_CRQ , vdev - > unit_address , word1 , word2 ) ;
}
/**
* ibmvfc_send_crq_init - Send a CRQ init message
* @ vhost : ibmvfc host struct
*
* Return value :
* 0 on success / other on failure
* */
static int ibmvfc_send_crq_init ( struct ibmvfc_host * vhost )
{
ibmvfc_dbg ( vhost , " Sending CRQ init \n " ) ;
return ibmvfc_send_crq ( vhost , 0xC001000000000000LL , 0 ) ;
}
/**
* ibmvfc_send_crq_init_complete - Send a CRQ init complete message
* @ vhost : ibmvfc host struct
*
* Return value :
* 0 on success / other on failure
* */
static int ibmvfc_send_crq_init_complete ( struct ibmvfc_host * vhost )
{
ibmvfc_dbg ( vhost , " Sending CRQ init complete \n " ) ;
return ibmvfc_send_crq ( vhost , 0xC002000000000000LL , 0 ) ;
}
/**
* ibmvfc_release_crq_queue - Deallocates data and unregisters CRQ
* @ vhost : ibmvfc host struct
*
* Frees irq , deallocates a page for messages , unmaps dma , and unregisters
* the crq with the hypervisor .
* */
static void ibmvfc_release_crq_queue ( struct ibmvfc_host * vhost )
{
long rc ;
struct vio_dev * vdev = to_vio_dev ( vhost - > dev ) ;
struct ibmvfc_crq_queue * crq = & vhost - > crq ;
ibmvfc_dbg ( vhost , " Releasing CRQ \n " ) ;
free_irq ( vdev - > irq , vhost ) ;
2009-03-20 23:44:35 +03:00
tasklet_kill ( & vhost - > tasklet ) ;
2008-07-01 22:14:30 +04:00
do {
rc = plpar_hcall_norets ( H_FREE_CRQ , vdev - > unit_address ) ;
} while ( rc = = H_BUSY | | H_IS_LONG_BUSY ( rc ) ) ;
vhost - > state = IBMVFC_NO_CRQ ;
2009-05-29 01:17:29 +04:00
vhost - > logged_in = 0 ;
2008-07-01 22:14:30 +04:00
dma_unmap_single ( vhost - > dev , crq - > msg_token , PAGE_SIZE , DMA_BIDIRECTIONAL ) ;
free_page ( ( unsigned long ) crq - > msgs ) ;
}
/**
* ibmvfc_reenable_crq_queue - reenables the CRQ
* @ vhost : ibmvfc host struct
*
* Return value :
* 0 on success / other on failure
* */
static int ibmvfc_reenable_crq_queue ( struct ibmvfc_host * vhost )
{
int rc ;
struct vio_dev * vdev = to_vio_dev ( vhost - > dev ) ;
/* Re-enable the CRQ */
do {
rc = plpar_hcall_norets ( H_ENABLE_CRQ , vdev - > unit_address ) ;
} while ( rc = = H_IN_PROGRESS | | rc = = H_BUSY | | H_IS_LONG_BUSY ( rc ) ) ;
if ( rc )
dev_err ( vhost - > dev , " Error enabling adapter (rc=%d) \n " , rc ) ;
return rc ;
}
/**
* ibmvfc_reset_crq - resets a crq after a failure
* @ vhost : ibmvfc host struct
*
* Return value :
* 0 on success / other on failure
* */
static int ibmvfc_reset_crq ( struct ibmvfc_host * vhost )
{
int rc ;
struct vio_dev * vdev = to_vio_dev ( vhost - > dev ) ;
struct ibmvfc_crq_queue * crq = & vhost - > crq ;
/* Close the CRQ */
do {
rc = plpar_hcall_norets ( H_FREE_CRQ , vdev - > unit_address ) ;
} while ( rc = = H_BUSY | | H_IS_LONG_BUSY ( rc ) ) ;
vhost - > state = IBMVFC_NO_CRQ ;
2009-05-29 01:17:29 +04:00
vhost - > logged_in = 0 ;
2008-07-01 22:14:30 +04:00
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_NONE ) ;
/* Clean out the queue */
memset ( crq - > msgs , 0 , PAGE_SIZE ) ;
crq - > cur = 0 ;
/* And re-open it again */
rc = plpar_hcall_norets ( H_REG_CRQ , vdev - > unit_address ,
crq - > msg_token , PAGE_SIZE ) ;
if ( rc = = H_CLOSED )
/* Adapter is good, but other end is not ready */
dev_warn ( vhost - > dev , " Partner adapter not ready \n " ) ;
else if ( rc ! = 0 )
dev_warn ( vhost - > dev , " Couldn't register crq (rc=%d) \n " , rc ) ;
return rc ;
}
/**
* ibmvfc_valid_event - Determines if event is valid .
* @ pool : event_pool that contains the event
* @ evt : ibmvfc event to be checked for validity
*
* Return value :
* 1 if event is valid / 0 if event is not valid
* */
static int ibmvfc_valid_event ( struct ibmvfc_event_pool * pool ,
struct ibmvfc_event * evt )
{
int index = evt - pool - > events ;
if ( index < 0 | | index > = pool - > size ) /* outside of bounds */
return 0 ;
if ( evt ! = pool - > events + index ) /* unaligned */
return 0 ;
return 1 ;
}
/**
* ibmvfc_free_event - Free the specified event
* @ evt : ibmvfc_event to be freed
*
* */
static void ibmvfc_free_event ( struct ibmvfc_event * evt )
{
struct ibmvfc_host * vhost = evt - > vhost ;
struct ibmvfc_event_pool * pool = & vhost - > pool ;
BUG_ON ( ! ibmvfc_valid_event ( pool , evt ) ) ;
BUG_ON ( atomic_inc_return ( & evt - > free ) ! = 1 ) ;
list_add_tail ( & evt - > queue , & vhost - > free ) ;
}
/**
* ibmvfc_scsi_eh_done - EH done function for queuecommand commands
* @ evt : ibmvfc event struct
*
* This function does not setup any error status , that must be done
* before this function gets called .
* */
static void ibmvfc_scsi_eh_done ( struct ibmvfc_event * evt )
{
struct scsi_cmnd * cmnd = evt - > cmnd ;
if ( cmnd ) {
scsi_dma_unmap ( cmnd ) ;
cmnd - > scsi_done ( cmnd ) ;
}
2008-10-29 16:46:41 +03:00
if ( evt - > eh_comp )
complete ( evt - > eh_comp ) ;
2008-07-01 22:14:30 +04:00
ibmvfc_free_event ( evt ) ;
}
/**
* ibmvfc_fail_request - Fail request with specified error code
* @ evt : ibmvfc event struct
* @ error_code : error code to fail request with
*
* Return value :
* none
* */
static void ibmvfc_fail_request ( struct ibmvfc_event * evt , int error_code )
{
if ( evt - > cmnd ) {
evt - > cmnd - > result = ( error_code < < 16 ) ;
evt - > done = ibmvfc_scsi_eh_done ;
} else
evt - > xfer_iu - > mad_common . status = IBMVFC_MAD_DRIVER_FAILED ;
list_del ( & evt - > queue ) ;
del_timer ( & evt - > timer ) ;
ibmvfc_trc_end ( evt ) ;
evt - > done ( evt ) ;
}
/**
* ibmvfc_purge_requests - Our virtual adapter just shut down . Purge any sent requests
* @ vhost : ibmvfc host struct
* @ error_code : error code to fail requests with
*
* Return value :
* none
* */
static void ibmvfc_purge_requests ( struct ibmvfc_host * vhost , int error_code )
{
struct ibmvfc_event * evt , * pos ;
ibmvfc_dbg ( vhost , " Purging all requests \n " ) ;
list_for_each_entry_safe ( evt , pos , & vhost - > sent , queue )
ibmvfc_fail_request ( evt , error_code ) ;
}
/**
2009-05-29 01:17:29 +04:00
* ibmvfc_hard_reset_host - Reset the connection to the server by breaking the CRQ
2008-07-01 22:14:30 +04:00
* @ vhost : struct ibmvfc host to reset
* */
2009-05-29 01:17:29 +04:00
static void ibmvfc_hard_reset_host ( struct ibmvfc_host * vhost )
2008-07-01 22:14:30 +04:00
{
int rc ;
scsi_block_requests ( vhost - > host ) ;
ibmvfc_purge_requests ( vhost , DID_ERROR ) ;
if ( ( rc = ibmvfc_reset_crq ( vhost ) ) | |
( rc = ibmvfc_send_crq_init ( vhost ) ) | |
( rc = vio_enable_interrupts ( to_vio_dev ( vhost - > dev ) ) ) ) {
dev_err ( vhost - > dev , " Error after reset rc=%d \n " , rc ) ;
ibmvfc_link_down ( vhost , IBMVFC_LINK_DEAD ) ;
} else
ibmvfc_link_down ( vhost , IBMVFC_LINK_DOWN ) ;
}
/**
2009-05-29 01:17:29 +04:00
* __ibmvfc_reset_host - Reset the connection to the server ( no locking )
2008-07-01 22:14:30 +04:00
* @ vhost : struct ibmvfc host to reset
* */
2009-05-29 01:17:29 +04:00
static void __ibmvfc_reset_host ( struct ibmvfc_host * vhost )
{
if ( vhost - > logged_in & & vhost - > action ! = IBMVFC_HOST_ACTION_LOGO_WAIT & &
! ibmvfc_set_host_state ( vhost , IBMVFC_INITIALIZING ) ) {
scsi_block_requests ( vhost - > host ) ;
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_LOGO ) ;
vhost - > job_step = ibmvfc_npiv_logout ;
wake_up ( & vhost - > work_wait_q ) ;
} else
ibmvfc_hard_reset_host ( vhost ) ;
}
/**
* ibmvfc_reset_host - Reset the connection to the server
* @ vhost : ibmvfc host struct
* */
2008-07-01 22:14:30 +04:00
static void ibmvfc_reset_host ( struct ibmvfc_host * vhost )
{
unsigned long flags ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
__ibmvfc_reset_host ( vhost ) ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
}
/**
* ibmvfc_retry_host_init - Retry host initialization if allowed
* @ vhost : ibmvfc host struct
*
2009-05-29 01:17:26 +04:00
* Returns : 1 if init will be retried / 0 if not
*
2008-07-01 22:14:30 +04:00
* */
2009-05-29 01:17:26 +04:00
static int ibmvfc_retry_host_init ( struct ibmvfc_host * vhost )
2008-07-01 22:14:30 +04:00
{
2009-05-29 01:17:26 +04:00
int retry = 0 ;
2008-07-01 22:14:30 +04:00
if ( vhost - > action = = IBMVFC_HOST_ACTION_INIT_WAIT ) {
2008-12-03 20:02:54 +03:00
vhost - > delay_init = 1 ;
if ( + + vhost - > init_retries > IBMVFC_MAX_HOST_INIT_RETRIES ) {
2008-07-01 22:14:30 +04:00
dev_err ( vhost - > dev ,
" Host initialization retries exceeded. Taking adapter offline \n " ) ;
ibmvfc_link_down ( vhost , IBMVFC_HOST_OFFLINE ) ;
2008-12-03 20:02:54 +03:00
} else if ( vhost - > init_retries = = IBMVFC_MAX_HOST_INIT_RETRIES )
2008-07-01 22:14:30 +04:00
__ibmvfc_reset_host ( vhost ) ;
2009-05-29 01:17:26 +04:00
else {
2008-07-01 22:14:30 +04:00
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_INIT ) ;
2009-05-29 01:17:26 +04:00
retry = 1 ;
}
2008-07-01 22:14:30 +04:00
}
wake_up ( & vhost - > work_wait_q ) ;
2009-05-29 01:17:26 +04:00
return retry ;
2008-07-01 22:14:30 +04:00
}
/**
2008-07-22 17:31:41 +04:00
* __ibmvfc_get_target - Find the specified scsi_target ( no locking )
2008-07-01 22:14:30 +04:00
* @ starget : scsi target struct
*
* Return value :
* ibmvfc_target struct / NULL if not found
* */
2008-07-22 17:31:41 +04:00
static struct ibmvfc_target * __ibmvfc_get_target ( struct scsi_target * starget )
2008-07-01 22:14:30 +04:00
{
struct Scsi_Host * shost = dev_to_shost ( starget - > dev . parent ) ;
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
struct ibmvfc_target * tgt ;
list_for_each_entry ( tgt , & vhost - > targets , queue )
2008-07-22 17:31:41 +04:00
if ( tgt - > target_id = = starget - > id ) {
kref_get ( & tgt - > kref ) ;
2008-07-01 22:14:30 +04:00
return tgt ;
2008-07-22 17:31:41 +04:00
}
2008-07-01 22:14:30 +04:00
return NULL ;
}
/**
2008-07-22 17:31:41 +04:00
* ibmvfc_get_target - Find the specified scsi_target
2008-07-01 22:14:30 +04:00
* @ starget : scsi target struct
*
* Return value :
* ibmvfc_target struct / NULL if not found
* */
2008-07-22 17:31:41 +04:00
static struct ibmvfc_target * ibmvfc_get_target ( struct scsi_target * starget )
2008-07-01 22:14:30 +04:00
{
struct Scsi_Host * shost = dev_to_shost ( starget - > dev . parent ) ;
struct ibmvfc_target * tgt ;
unsigned long flags ;
spin_lock_irqsave ( shost - > host_lock , flags ) ;
2008-07-22 17:31:41 +04:00
tgt = __ibmvfc_get_target ( starget ) ;
2008-07-01 22:14:30 +04:00
spin_unlock_irqrestore ( shost - > host_lock , flags ) ;
return tgt ;
}
/**
* ibmvfc_get_host_speed - Get host port speed
* @ shost : scsi host struct
*
* Return value :
* none
* */
static void ibmvfc_get_host_speed ( struct Scsi_Host * shost )
{
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
unsigned long flags ;
spin_lock_irqsave ( shost - > host_lock , flags ) ;
if ( vhost - > state = = IBMVFC_ACTIVE ) {
switch ( vhost - > login_buf - > resp . link_speed / 100 ) {
case 1 :
fc_host_speed ( shost ) = FC_PORTSPEED_1GBIT ;
break ;
case 2 :
fc_host_speed ( shost ) = FC_PORTSPEED_2GBIT ;
break ;
case 4 :
fc_host_speed ( shost ) = FC_PORTSPEED_4GBIT ;
break ;
case 8 :
fc_host_speed ( shost ) = FC_PORTSPEED_8GBIT ;
break ;
case 10 :
fc_host_speed ( shost ) = FC_PORTSPEED_10GBIT ;
break ;
case 16 :
fc_host_speed ( shost ) = FC_PORTSPEED_16GBIT ;
break ;
default :
2009-01-06 17:59:00 +03:00
ibmvfc_log ( vhost , 3 , " Unknown port speed: %lld Gbit \n " ,
2008-07-01 22:14:30 +04:00
vhost - > login_buf - > resp . link_speed / 100 ) ;
fc_host_speed ( shost ) = FC_PORTSPEED_UNKNOWN ;
break ;
}
} else
fc_host_speed ( shost ) = FC_PORTSPEED_UNKNOWN ;
spin_unlock_irqrestore ( shost - > host_lock , flags ) ;
}
/**
* ibmvfc_get_host_port_state - Get host port state
* @ shost : scsi host struct
*
* Return value :
* none
* */
static void ibmvfc_get_host_port_state ( struct Scsi_Host * shost )
{
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
unsigned long flags ;
spin_lock_irqsave ( shost - > host_lock , flags ) ;
switch ( vhost - > state ) {
case IBMVFC_INITIALIZING :
case IBMVFC_ACTIVE :
fc_host_port_state ( shost ) = FC_PORTSTATE_ONLINE ;
break ;
case IBMVFC_LINK_DOWN :
fc_host_port_state ( shost ) = FC_PORTSTATE_LINKDOWN ;
break ;
case IBMVFC_LINK_DEAD :
case IBMVFC_HOST_OFFLINE :
fc_host_port_state ( shost ) = FC_PORTSTATE_OFFLINE ;
break ;
case IBMVFC_HALTED :
fc_host_port_state ( shost ) = FC_PORTSTATE_BLOCKED ;
break ;
2008-07-22 17:31:39 +04:00
case IBMVFC_NO_CRQ :
fc_host_port_state ( shost ) = FC_PORTSTATE_UNKNOWN ;
break ;
2008-07-01 22:14:30 +04:00
default :
ibmvfc_log ( vhost , 3 , " Unknown port state: %d \n " , vhost - > state ) ;
fc_host_port_state ( shost ) = FC_PORTSTATE_UNKNOWN ;
break ;
}
spin_unlock_irqrestore ( shost - > host_lock , flags ) ;
}
/**
* ibmvfc_set_rport_dev_loss_tmo - Set rport ' s device loss timeout
* @ rport : rport struct
* @ timeout : timeout value
*
* Return value :
* none
* */
static void ibmvfc_set_rport_dev_loss_tmo ( struct fc_rport * rport , u32 timeout )
{
if ( timeout )
rport - > dev_loss_tmo = timeout ;
else
rport - > dev_loss_tmo = 1 ;
}
2008-07-22 17:31:41 +04:00
/**
* ibmvfc_release_tgt - Free memory allocated for a target
* @ kref : kref struct
*
* */
static void ibmvfc_release_tgt ( struct kref * kref )
{
struct ibmvfc_target * tgt = container_of ( kref , struct ibmvfc_target , kref ) ;
kfree ( tgt ) ;
}
2008-07-01 22:14:30 +04:00
/**
* ibmvfc_get_starget_node_name - Get SCSI target ' s node name
* @ starget : scsi target struct
*
* Return value :
* none
* */
static void ibmvfc_get_starget_node_name ( struct scsi_target * starget )
{
2008-07-22 17:31:41 +04:00
struct ibmvfc_target * tgt = ibmvfc_get_target ( starget ) ;
2008-07-01 22:14:30 +04:00
fc_starget_port_name ( starget ) = tgt ? tgt - > ids . node_name : 0 ;
2008-07-22 17:31:41 +04:00
if ( tgt )
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
2008-07-01 22:14:30 +04:00
}
/**
* ibmvfc_get_starget_port_name - Get SCSI target ' s port name
* @ starget : scsi target struct
*
* Return value :
* none
* */
static void ibmvfc_get_starget_port_name ( struct scsi_target * starget )
{
2008-07-22 17:31:41 +04:00
struct ibmvfc_target * tgt = ibmvfc_get_target ( starget ) ;
2008-07-01 22:14:30 +04:00
fc_starget_port_name ( starget ) = tgt ? tgt - > ids . port_name : 0 ;
2008-07-22 17:31:41 +04:00
if ( tgt )
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
2008-07-01 22:14:30 +04:00
}
/**
* ibmvfc_get_starget_port_id - Get SCSI target ' s port ID
* @ starget : scsi target struct
*
* Return value :
* none
* */
static void ibmvfc_get_starget_port_id ( struct scsi_target * starget )
{
2008-07-22 17:31:41 +04:00
struct ibmvfc_target * tgt = ibmvfc_get_target ( starget ) ;
2008-07-01 22:14:30 +04:00
fc_starget_port_id ( starget ) = tgt ? tgt - > scsi_id : - 1 ;
2008-07-22 17:31:41 +04:00
if ( tgt )
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
2008-07-01 22:14:30 +04:00
}
/**
* ibmvfc_wait_while_resetting - Wait while the host resets
* @ vhost : ibmvfc host struct
*
* Return value :
* 0 on success / other on failure
* */
static int ibmvfc_wait_while_resetting ( struct ibmvfc_host * vhost )
{
long timeout = wait_event_timeout ( vhost - > init_wait_q ,
2008-08-15 19:59:21 +04:00
( ( vhost - > state = = IBMVFC_ACTIVE | |
vhost - > state = = IBMVFC_HOST_OFFLINE | |
vhost - > state = = IBMVFC_LINK_DEAD ) & &
vhost - > action = = IBMVFC_HOST_ACTION_NONE ) ,
2008-07-01 22:14:30 +04:00
( init_timeout * HZ ) ) ;
return timeout ? 0 : - EIO ;
}
/**
* ibmvfc_issue_fc_host_lip - Re - initiate link initialization
* @ shost : scsi host struct
*
* Return value :
* 0 on success / other on failure
* */
static int ibmvfc_issue_fc_host_lip ( struct Scsi_Host * shost )
{
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
dev_err ( vhost - > dev , " Initiating host LIP. Resetting connection \n " ) ;
ibmvfc_reset_host ( vhost ) ;
return ibmvfc_wait_while_resetting ( vhost ) ;
}
/**
* ibmvfc_gather_partition_info - Gather info about the LPAR
*
* Return value :
* none
* */
static void ibmvfc_gather_partition_info ( struct ibmvfc_host * vhost )
{
struct device_node * rootdn ;
const char * name ;
const unsigned int * num ;
rootdn = of_find_node_by_path ( " / " ) ;
if ( ! rootdn )
return ;
name = of_get_property ( rootdn , " ibm,partition-name " , NULL ) ;
if ( name )
strncpy ( vhost - > partition_name , name , sizeof ( vhost - > partition_name ) ) ;
num = of_get_property ( rootdn , " ibm,partition-no " , NULL ) ;
if ( num )
vhost - > partition_number = * num ;
of_node_put ( rootdn ) ;
}
/**
* ibmvfc_set_login_info - Setup info for NPIV login
* @ vhost : ibmvfc host struct
*
* Return value :
* none
* */
static void ibmvfc_set_login_info ( struct ibmvfc_host * vhost )
{
struct ibmvfc_npiv_login * login_info = & vhost - > login_info ;
struct device_node * of_node = vhost - > dev - > archdata . of_node ;
const char * location ;
memset ( login_info , 0 , sizeof ( * login_info ) ) ;
login_info - > ostype = IBMVFC_OS_LINUX ;
login_info - > max_dma_len = IBMVFC_MAX_SECTORS < < 9 ;
login_info - > max_payload = sizeof ( struct ibmvfc_fcp_cmd_iu ) ;
login_info - > max_response = sizeof ( struct ibmvfc_fcp_rsp ) ;
login_info - > partition_num = vhost - > partition_number ;
login_info - > vfc_frame_version = 1 ;
login_info - > fcp_version = 3 ;
2009-05-29 01:17:30 +04:00
login_info - > flags = IBMVFC_FLUSH_ON_HALT ;
2008-07-01 22:14:30 +04:00
if ( vhost - > client_migrated )
2009-05-29 01:17:30 +04:00
login_info - > flags | = IBMVFC_CLIENT_MIGRATED ;
2008-07-01 22:14:30 +04:00
login_info - > max_cmds = max_requests + IBMVFC_NUM_INTERNAL_REQ ;
login_info - > capabilities = IBMVFC_CAN_MIGRATE ;
login_info - > async . va = vhost - > async_crq . msg_token ;
2008-07-22 17:31:46 +04:00
login_info - > async . len = vhost - > async_crq . size * sizeof ( * vhost - > async_crq . msgs ) ;
2008-07-01 22:14:30 +04:00
strncpy ( login_info - > partition_name , vhost - > partition_name , IBMVFC_MAX_NAME ) ;
strncpy ( login_info - > device_name ,
2008-12-04 00:41:36 +03:00
dev_name ( & vhost - > host - > shost_gendev ) , IBMVFC_MAX_NAME ) ;
2008-07-01 22:14:30 +04:00
location = of_get_property ( of_node , " ibm,loc-code " , NULL ) ;
2008-12-04 00:41:36 +03:00
location = location ? location : dev_name ( vhost - > dev ) ;
2008-07-01 22:14:30 +04:00
strncpy ( login_info - > drc_name , location , IBMVFC_MAX_NAME ) ;
}
/**
* ibmvfc_init_event_pool - Allocates and initializes the event pool for a host
* @ vhost : ibmvfc host who owns the event pool
*
* Returns zero on success .
* */
static int ibmvfc_init_event_pool ( struct ibmvfc_host * vhost )
{
int i ;
struct ibmvfc_event_pool * pool = & vhost - > pool ;
ENTER ;
pool - > size = max_requests + IBMVFC_NUM_INTERNAL_REQ ;
pool - > events = kcalloc ( pool - > size , sizeof ( * pool - > events ) , GFP_KERNEL ) ;
if ( ! pool - > events )
return - ENOMEM ;
pool - > iu_storage = dma_alloc_coherent ( vhost - > dev ,
pool - > size * sizeof ( * pool - > iu_storage ) ,
& pool - > iu_token , 0 ) ;
if ( ! pool - > iu_storage ) {
kfree ( pool - > events ) ;
return - ENOMEM ;
}
for ( i = 0 ; i < pool - > size ; + + i ) {
struct ibmvfc_event * evt = & pool - > events [ i ] ;
atomic_set ( & evt - > free , 1 ) ;
evt - > crq . valid = 0x80 ;
evt - > crq . ioba = pool - > iu_token + ( sizeof ( * evt - > xfer_iu ) * i ) ;
evt - > xfer_iu = pool - > iu_storage + i ;
evt - > vhost = vhost ;
evt - > ext_list = NULL ;
list_add_tail ( & evt - > queue , & vhost - > free ) ;
}
LEAVE ;
return 0 ;
}
/**
* ibmvfc_free_event_pool - Frees memory of the event pool of a host
* @ vhost : ibmvfc host who owns the event pool
*
* */
static void ibmvfc_free_event_pool ( struct ibmvfc_host * vhost )
{
int i ;
struct ibmvfc_event_pool * pool = & vhost - > pool ;
ENTER ;
for ( i = 0 ; i < pool - > size ; + + i ) {
list_del ( & pool - > events [ i ] . queue ) ;
BUG_ON ( atomic_read ( & pool - > events [ i ] . free ) ! = 1 ) ;
if ( pool - > events [ i ] . ext_list )
dma_pool_free ( vhost - > sg_pool ,
pool - > events [ i ] . ext_list ,
pool - > events [ i ] . ext_list_token ) ;
}
kfree ( pool - > events ) ;
dma_free_coherent ( vhost - > dev ,
pool - > size * sizeof ( * pool - > iu_storage ) ,
pool - > iu_storage , pool - > iu_token ) ;
LEAVE ;
}
/**
* ibmvfc_get_event - Gets the next free event in pool
* @ vhost : ibmvfc host struct
*
* Returns a free event from the pool .
* */
static struct ibmvfc_event * ibmvfc_get_event ( struct ibmvfc_host * vhost )
{
struct ibmvfc_event * evt ;
BUG_ON ( list_empty ( & vhost - > free ) ) ;
evt = list_entry ( vhost - > free . next , struct ibmvfc_event , queue ) ;
atomic_set ( & evt - > free , 0 ) ;
list_del ( & evt - > queue ) ;
return evt ;
}
/**
* ibmvfc_init_event - Initialize fields in an event struct that are always
* required .
* @ evt : The event
* @ done : Routine to call when the event is responded to
* @ format : SRP or MAD format
* */
static void ibmvfc_init_event ( struct ibmvfc_event * evt ,
void ( * done ) ( struct ibmvfc_event * ) , u8 format )
{
evt - > cmnd = NULL ;
evt - > sync_iu = NULL ;
evt - > crq . format = format ;
evt - > done = done ;
2008-10-29 16:46:41 +03:00
evt - > eh_comp = NULL ;
2008-07-01 22:14:30 +04:00
}
/**
* ibmvfc_map_sg_list - Initialize scatterlist
* @ scmd : scsi command struct
* @ nseg : number of scatterlist segments
* @ md : memory descriptor list to initialize
* */
static void ibmvfc_map_sg_list ( struct scsi_cmnd * scmd , int nseg ,
struct srp_direct_buf * md )
{
int i ;
struct scatterlist * sg ;
scsi_for_each_sg ( scmd , sg , nseg , i ) {
md [ i ] . va = sg_dma_address ( sg ) ;
md [ i ] . len = sg_dma_len ( sg ) ;
md [ i ] . key = 0 ;
}
}
/**
* ibmvfc_map_sg_data - Maps dma for a scatterlist and initializes decriptor fields
* @ scmd : Scsi_Cmnd with the scatterlist
* @ evt : ibmvfc event struct
* @ vfc_cmd : vfc_cmd that contains the memory descriptor
* @ dev : device for which to map dma memory
*
* Returns :
* 0 on success / non - zero on failure
* */
static int ibmvfc_map_sg_data ( struct scsi_cmnd * scmd ,
struct ibmvfc_event * evt ,
struct ibmvfc_cmd * vfc_cmd , struct device * dev )
{
int sg_mapped ;
struct srp_direct_buf * data = & vfc_cmd - > ioba ;
struct ibmvfc_host * vhost = dev_get_drvdata ( dev ) ;
sg_mapped = scsi_dma_map ( scmd ) ;
if ( ! sg_mapped ) {
vfc_cmd - > flags | = IBMVFC_NO_MEM_DESC ;
return 0 ;
} else if ( unlikely ( sg_mapped < 0 ) ) {
if ( vhost - > log_level > IBMVFC_DEFAULT_LOG_LEVEL )
scmd_printk ( KERN_ERR , scmd , " Failed to map DMA buffer for command \n " ) ;
return sg_mapped ;
}
if ( scmd - > sc_data_direction = = DMA_TO_DEVICE ) {
vfc_cmd - > flags | = IBMVFC_WRITE ;
vfc_cmd - > iu . add_cdb_len | = IBMVFC_WRDATA ;
} else {
vfc_cmd - > flags | = IBMVFC_READ ;
vfc_cmd - > iu . add_cdb_len | = IBMVFC_RDDATA ;
}
if ( sg_mapped = = 1 ) {
ibmvfc_map_sg_list ( scmd , sg_mapped , data ) ;
return 0 ;
}
vfc_cmd - > flags | = IBMVFC_SCATTERLIST ;
if ( ! evt - > ext_list ) {
evt - > ext_list = dma_pool_alloc ( vhost - > sg_pool , GFP_ATOMIC ,
& evt - > ext_list_token ) ;
if ( ! evt - > ext_list ) {
2009-01-23 00:45:38 +03:00
scsi_dma_unmap ( scmd ) ;
if ( vhost - > log_level > IBMVFC_DEFAULT_LOG_LEVEL )
scmd_printk ( KERN_ERR , scmd , " Can't allocate memory for scatterlist \n " ) ;
2008-07-01 22:14:30 +04:00
return - ENOMEM ;
}
}
ibmvfc_map_sg_list ( scmd , sg_mapped , evt - > ext_list ) ;
data - > va = evt - > ext_list_token ;
data - > len = sg_mapped * sizeof ( struct srp_direct_buf ) ;
data - > key = 0 ;
return 0 ;
}
/**
* ibmvfc_timeout - Internal command timeout handler
* @ evt : struct ibmvfc_event that timed out
*
* Called when an internally generated command times out
* */
static void ibmvfc_timeout ( struct ibmvfc_event * evt )
{
struct ibmvfc_host * vhost = evt - > vhost ;
dev_err ( vhost - > dev , " Command timed out (%p). Resetting connection \n " , evt ) ;
ibmvfc_reset_host ( vhost ) ;
}
/**
* ibmvfc_send_event - Transforms event to u64 array and calls send_crq ( )
* @ evt : event to be sent
* @ vhost : ibmvfc host struct
* @ timeout : timeout in seconds - 0 means do not time command
*
* Returns the value returned from ibmvfc_send_crq ( ) . ( Zero for success )
* */
static int ibmvfc_send_event ( struct ibmvfc_event * evt ,
struct ibmvfc_host * vhost , unsigned long timeout )
{
u64 * crq_as_u64 = ( u64 * ) & evt - > crq ;
int rc ;
/* Copy the IU into the transfer area */
* evt - > xfer_iu = evt - > iu ;
if ( evt - > crq . format = = IBMVFC_CMD_FORMAT )
evt - > xfer_iu - > cmd . tag = ( u64 ) evt ;
else if ( evt - > crq . format = = IBMVFC_MAD_FORMAT )
evt - > xfer_iu - > mad_common . tag = ( u64 ) evt ;
else
BUG ( ) ;
list_add_tail ( & evt - > queue , & vhost - > sent ) ;
init_timer ( & evt - > timer ) ;
if ( timeout ) {
evt - > timer . data = ( unsigned long ) evt ;
evt - > timer . expires = jiffies + ( timeout * HZ ) ;
evt - > timer . function = ( void ( * ) ( unsigned long ) ) ibmvfc_timeout ;
add_timer ( & evt - > timer ) ;
}
2008-12-03 20:02:56 +03:00
mb ( ) ;
2008-07-01 22:14:30 +04:00
if ( ( rc = ibmvfc_send_crq ( vhost , crq_as_u64 [ 0 ] , crq_as_u64 [ 1 ] ) ) ) {
list_del ( & evt - > queue ) ;
del_timer ( & evt - > timer ) ;
/* If send_crq returns H_CLOSED, return SCSI_MLQUEUE_HOST_BUSY.
* Firmware will send a CRQ with a transport event ( 0xFF ) to
* tell this client what has happened to the transport . This
* will be handled in ibmvfc_handle_crq ( )
*/
if ( rc = = H_CLOSED ) {
if ( printk_ratelimit ( ) )
dev_warn ( vhost - > dev , " Send warning. Receive queue closed, will retry. \n " ) ;
if ( evt - > cmnd )
scsi_dma_unmap ( evt - > cmnd ) ;
ibmvfc_free_event ( evt ) ;
return SCSI_MLQUEUE_HOST_BUSY ;
}
dev_err ( vhost - > dev , " Send error (rc=%d) \n " , rc ) ;
if ( evt - > cmnd ) {
evt - > cmnd - > result = DID_ERROR < < 16 ;
evt - > done = ibmvfc_scsi_eh_done ;
} else
evt - > xfer_iu - > mad_common . status = IBMVFC_MAD_CRQ_ERROR ;
evt - > done ( evt ) ;
} else
ibmvfc_trc_start ( evt ) ;
return 0 ;
}
/**
* ibmvfc_log_error - Log an error for the failed command if appropriate
* @ evt : ibmvfc event to log
*
* */
static void ibmvfc_log_error ( struct ibmvfc_event * evt )
{
struct ibmvfc_cmd * vfc_cmd = & evt - > xfer_iu - > cmd ;
struct ibmvfc_host * vhost = evt - > vhost ;
struct ibmvfc_fcp_rsp * rsp = & vfc_cmd - > rsp ;
struct scsi_cmnd * cmnd = evt - > cmnd ;
const char * err = unknown_error ;
int index = ibmvfc_get_err_index ( vfc_cmd - > status , vfc_cmd - > error ) ;
int logerr = 0 ;
int rsp_code = 0 ;
if ( index > = 0 ) {
logerr = cmd_status [ index ] . log ;
err = cmd_status [ index ] . name ;
}
2008-07-22 17:31:39 +04:00
if ( ! logerr & & ( vhost - > log_level < = ( IBMVFC_DEFAULT_LOG_LEVEL + 1 ) ) )
2008-07-01 22:14:30 +04:00
return ;
if ( rsp - > flags & FCP_RSP_LEN_VALID )
rsp_code = rsp - > data . info . rsp_code ;
scmd_printk ( KERN_ERR , cmnd , " Command (%02X) failed: %s (%x:%x) "
" flags: %x fcp_rsp: %x, resid=%d, scsi_status: %x \n " ,
cmnd - > cmnd [ 0 ] , err , vfc_cmd - > status , vfc_cmd - > error ,
rsp - > flags , rsp_code , scsi_get_resid ( cmnd ) , rsp - > scsi_status ) ;
}
2009-05-29 01:17:33 +04:00
/**
* ibmvfc_relogin - Log back into the specified device
* @ sdev : scsi device struct
*
* */
static void ibmvfc_relogin ( struct scsi_device * sdev )
{
struct ibmvfc_host * vhost = shost_priv ( sdev - > host ) ;
struct fc_rport * rport = starget_to_rport ( scsi_target ( sdev ) ) ;
struct ibmvfc_target * tgt ;
list_for_each_entry ( tgt , & vhost - > targets , queue ) {
if ( rport = = tgt - > rport ) {
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_DEL_RPORT ) ;
break ;
}
}
ibmvfc_reinit_host ( vhost ) ;
}
2008-07-01 22:14:30 +04:00
/**
* ibmvfc_scsi_done - Handle responses from commands
* @ evt : ibmvfc event to be handled
*
* Used as a callback when sending scsi cmds .
* */
static void ibmvfc_scsi_done ( struct ibmvfc_event * evt )
{
struct ibmvfc_cmd * vfc_cmd = & evt - > xfer_iu - > cmd ;
struct ibmvfc_fcp_rsp * rsp = & vfc_cmd - > rsp ;
struct scsi_cmnd * cmnd = evt - > cmnd ;
2008-08-15 19:59:26 +04:00
u32 rsp_len = 0 ;
u32 sense_len = rsp - > fcp_sense_len ;
2008-07-01 22:14:30 +04:00
if ( cmnd ) {
if ( vfc_cmd - > response_flags & IBMVFC_ADAPTER_RESID_VALID )
scsi_set_resid ( cmnd , vfc_cmd - > adapter_resid ) ;
else if ( rsp - > flags & FCP_RESID_UNDER )
scsi_set_resid ( cmnd , rsp - > fcp_resid ) ;
else
scsi_set_resid ( cmnd , 0 ) ;
if ( vfc_cmd - > status ) {
cmnd - > result = ibmvfc_get_err_result ( vfc_cmd ) ;
if ( rsp - > flags & FCP_RSP_LEN_VALID )
rsp_len = rsp - > fcp_rsp_len ;
if ( ( sense_len + rsp_len ) > SCSI_SENSE_BUFFERSIZE )
sense_len = SCSI_SENSE_BUFFERSIZE - rsp_len ;
2008-08-15 19:59:26 +04:00
if ( ( rsp - > flags & FCP_SNS_LEN_VALID ) & & rsp - > fcp_sense_len & & rsp_len < = 8 )
2008-07-01 22:14:30 +04:00
memcpy ( cmnd - > sense_buffer , rsp - > data . sense + rsp_len , sense_len ) ;
2008-11-14 22:33:53 +03:00
if ( ( vfc_cmd - > status & IBMVFC_VIOS_FAILURE ) & & ( vfc_cmd - > error = = IBMVFC_PLOGI_REQUIRED ) )
2009-05-29 01:17:33 +04:00
ibmvfc_relogin ( cmnd - > device ) ;
2008-07-01 22:14:30 +04:00
2008-10-29 16:46:36 +03:00
if ( ! cmnd - > result & & ( ! scsi_get_resid ( cmnd ) | | ( rsp - > flags & FCP_RESID_OVER ) ) )
cmnd - > result = ( DID_ERROR < < 16 ) ;
2008-07-01 22:14:30 +04:00
ibmvfc_log_error ( evt ) ;
}
if ( ! cmnd - > result & &
( scsi_bufflen ( cmnd ) - scsi_get_resid ( cmnd ) < cmnd - > underflow ) )
cmnd - > result = ( DID_ERROR < < 16 ) ;
scsi_dma_unmap ( cmnd ) ;
cmnd - > scsi_done ( cmnd ) ;
}
2008-10-29 16:46:41 +03:00
if ( evt - > eh_comp )
complete ( evt - > eh_comp ) ;
2008-07-01 22:14:30 +04:00
ibmvfc_free_event ( evt ) ;
}
/**
* ibmvfc_host_chkready - Check if the host can accept commands
* @ vhost : struct ibmvfc host
*
* Returns :
* 1 if host can accept command / 0 if not
* */
static inline int ibmvfc_host_chkready ( struct ibmvfc_host * vhost )
{
int result = 0 ;
switch ( vhost - > state ) {
case IBMVFC_LINK_DEAD :
case IBMVFC_HOST_OFFLINE :
result = DID_NO_CONNECT < < 16 ;
break ;
case IBMVFC_NO_CRQ :
case IBMVFC_INITIALIZING :
case IBMVFC_HALTED :
case IBMVFC_LINK_DOWN :
result = DID_REQUEUE < < 16 ;
break ;
case IBMVFC_ACTIVE :
result = 0 ;
break ;
} ;
return result ;
}
/**
* ibmvfc_queuecommand - The queuecommand function of the scsi template
* @ cmnd : struct scsi_cmnd to be executed
* @ done : Callback function to be called when cmnd is completed
*
* Returns :
* 0 on success / other on failure
* */
static int ibmvfc_queuecommand ( struct scsi_cmnd * cmnd ,
void ( * done ) ( struct scsi_cmnd * ) )
{
struct ibmvfc_host * vhost = shost_priv ( cmnd - > device - > host ) ;
struct fc_rport * rport = starget_to_rport ( scsi_target ( cmnd - > device ) ) ;
struct ibmvfc_cmd * vfc_cmd ;
struct ibmvfc_event * evt ;
u8 tag [ 2 ] ;
int rc ;
if ( unlikely ( ( rc = fc_remote_port_chkready ( rport ) ) ) | |
unlikely ( ( rc = ibmvfc_host_chkready ( vhost ) ) ) ) {
cmnd - > result = rc ;
done ( cmnd ) ;
return 0 ;
}
cmnd - > result = ( DID_OK < < 16 ) ;
evt = ibmvfc_get_event ( vhost ) ;
ibmvfc_init_event ( evt , ibmvfc_scsi_done , IBMVFC_CMD_FORMAT ) ;
evt - > cmnd = cmnd ;
cmnd - > scsi_done = done ;
vfc_cmd = & evt - > iu . cmd ;
memset ( vfc_cmd , 0 , sizeof ( * vfc_cmd ) ) ;
vfc_cmd - > resp . va = ( u64 ) evt - > crq . ioba + offsetof ( struct ibmvfc_cmd , rsp ) ;
vfc_cmd - > resp . len = sizeof ( vfc_cmd - > rsp ) ;
vfc_cmd - > frame_type = IBMVFC_SCSI_FCP_TYPE ;
vfc_cmd - > payload_len = sizeof ( vfc_cmd - > iu ) ;
vfc_cmd - > resp_len = sizeof ( vfc_cmd - > rsp ) ;
vfc_cmd - > cancel_key = ( unsigned long ) cmnd - > device - > hostdata ;
vfc_cmd - > tgt_scsi_id = rport - > port_id ;
vfc_cmd - > iu . xfer_len = scsi_bufflen ( cmnd ) ;
int_to_scsilun ( cmnd - > device - > lun , & vfc_cmd - > iu . lun ) ;
memcpy ( vfc_cmd - > iu . cdb , cmnd - > cmnd , cmnd - > cmd_len ) ;
if ( scsi_populate_tag_msg ( cmnd , tag ) ) {
vfc_cmd - > task_tag = tag [ 1 ] ;
switch ( tag [ 0 ] ) {
case MSG_SIMPLE_TAG :
vfc_cmd - > iu . pri_task_attr = IBMVFC_SIMPLE_TASK ;
break ;
case MSG_HEAD_TAG :
vfc_cmd - > iu . pri_task_attr = IBMVFC_HEAD_OF_QUEUE ;
break ;
case MSG_ORDERED_TAG :
vfc_cmd - > iu . pri_task_attr = IBMVFC_ORDERED_TASK ;
break ;
} ;
}
if ( likely ( ! ( rc = ibmvfc_map_sg_data ( cmnd , evt , vfc_cmd , vhost - > dev ) ) ) )
return ibmvfc_send_event ( evt , vhost , 0 ) ;
ibmvfc_free_event ( evt ) ;
if ( rc = = - ENOMEM )
return SCSI_MLQUEUE_HOST_BUSY ;
if ( vhost - > log_level > IBMVFC_DEFAULT_LOG_LEVEL )
scmd_printk ( KERN_ERR , cmnd ,
" Failed to map DMA buffer for command. rc=%d \n " , rc ) ;
cmnd - > result = DID_ERROR < < 16 ;
done ( cmnd ) ;
return 0 ;
}
/**
* ibmvfc_sync_completion - Signal that a synchronous command has completed
* @ evt : ibmvfc event struct
*
* */
static void ibmvfc_sync_completion ( struct ibmvfc_event * evt )
{
/* copy the response back */
if ( evt - > sync_iu )
* evt - > sync_iu = * evt - > xfer_iu ;
complete ( & evt - > comp ) ;
}
/**
* ibmvfc_reset_device - Reset the device with the specified reset type
* @ sdev : scsi device to reset
* @ type : reset type
* @ desc : reset type description for log messages
*
* Returns :
* 0 on success / other on failure
* */
static int ibmvfc_reset_device ( struct scsi_device * sdev , int type , char * desc )
{
struct ibmvfc_host * vhost = shost_priv ( sdev - > host ) ;
struct fc_rport * rport = starget_to_rport ( scsi_target ( sdev ) ) ;
struct ibmvfc_cmd * tmf ;
2008-10-29 16:46:47 +03:00
struct ibmvfc_event * evt = NULL ;
2008-07-01 22:14:30 +04:00
union ibmvfc_iu rsp_iu ;
struct ibmvfc_fcp_rsp * fc_rsp = & rsp_iu . cmd . rsp ;
int rsp_rc = - EBUSY ;
unsigned long flags ;
int rsp_code = 0 ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
if ( vhost - > state = = IBMVFC_ACTIVE ) {
evt = ibmvfc_get_event ( vhost ) ;
ibmvfc_init_event ( evt , ibmvfc_sync_completion , IBMVFC_CMD_FORMAT ) ;
tmf = & evt - > iu . cmd ;
memset ( tmf , 0 , sizeof ( * tmf ) ) ;
tmf - > resp . va = ( u64 ) evt - > crq . ioba + offsetof ( struct ibmvfc_cmd , rsp ) ;
tmf - > resp . len = sizeof ( tmf - > rsp ) ;
tmf - > frame_type = IBMVFC_SCSI_FCP_TYPE ;
tmf - > payload_len = sizeof ( tmf - > iu ) ;
tmf - > resp_len = sizeof ( tmf - > rsp ) ;
tmf - > cancel_key = ( unsigned long ) sdev - > hostdata ;
tmf - > tgt_scsi_id = rport - > port_id ;
int_to_scsilun ( sdev - > lun , & tmf - > iu . lun ) ;
tmf - > flags = ( IBMVFC_NO_MEM_DESC | IBMVFC_TMF ) ;
tmf - > iu . tmf_flags = type ;
evt - > sync_iu = & rsp_iu ;
init_completion ( & evt - > comp ) ;
rsp_rc = ibmvfc_send_event ( evt , vhost , default_timeout ) ;
}
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
if ( rsp_rc ! = 0 ) {
sdev_printk ( KERN_ERR , sdev , " Failed to send %s reset event. rc=%d \n " ,
desc , rsp_rc ) ;
return - EIO ;
}
sdev_printk ( KERN_INFO , sdev , " Resetting %s \n " , desc ) ;
wait_for_completion ( & evt - > comp ) ;
2009-10-20 00:07:47 +04:00
if ( rsp_iu . cmd . status )
rsp_code = ibmvfc_get_err_result ( & rsp_iu . cmd ) ;
if ( rsp_code ) {
2008-07-01 22:14:30 +04:00
if ( fc_rsp - > flags & FCP_RSP_LEN_VALID )
rsp_code = fc_rsp - > data . info . rsp_code ;
sdev_printk ( KERN_ERR , sdev , " %s reset failed: %s (%x:%x) "
" flags: %x fcp_rsp: %x, scsi_status: %x \n " ,
desc , ibmvfc_get_cmd_error ( rsp_iu . cmd . status , rsp_iu . cmd . error ) ,
rsp_iu . cmd . status , rsp_iu . cmd . error , fc_rsp - > flags , rsp_code ,
fc_rsp - > scsi_status ) ;
rsp_rc = - EIO ;
} else
sdev_printk ( KERN_INFO , sdev , " %s reset successful \n " , desc ) ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
ibmvfc_free_event ( evt ) ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
return rsp_rc ;
}
/**
* ibmvfc_abort_task_set - Abort outstanding commands to the device
* @ sdev : scsi device to abort commands
*
* This sends an Abort Task Set to the VIOS for the specified device . This does
* NOT send any cancel to the VIOS . That must be done separately .
*
* Returns :
* 0 on success / other on failure
* */
static int ibmvfc_abort_task_set ( struct scsi_device * sdev )
{
struct ibmvfc_host * vhost = shost_priv ( sdev - > host ) ;
struct fc_rport * rport = starget_to_rport ( scsi_target ( sdev ) ) ;
struct ibmvfc_cmd * tmf ;
struct ibmvfc_event * evt , * found_evt ;
union ibmvfc_iu rsp_iu ;
struct ibmvfc_fcp_rsp * fc_rsp = & rsp_iu . cmd . rsp ;
int rsp_rc = - EBUSY ;
unsigned long flags ;
int rsp_code = 0 ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
found_evt = NULL ;
list_for_each_entry ( evt , & vhost - > sent , queue ) {
if ( evt - > cmnd & & evt - > cmnd - > device = = sdev ) {
found_evt = evt ;
break ;
}
}
if ( ! found_evt ) {
if ( vhost - > log_level > IBMVFC_DEFAULT_LOG_LEVEL )
sdev_printk ( KERN_INFO , sdev , " No events found to abort \n " ) ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
return 0 ;
}
if ( vhost - > state = = IBMVFC_ACTIVE ) {
evt = ibmvfc_get_event ( vhost ) ;
ibmvfc_init_event ( evt , ibmvfc_sync_completion , IBMVFC_CMD_FORMAT ) ;
tmf = & evt - > iu . cmd ;
memset ( tmf , 0 , sizeof ( * tmf ) ) ;
tmf - > resp . va = ( u64 ) evt - > crq . ioba + offsetof ( struct ibmvfc_cmd , rsp ) ;
tmf - > resp . len = sizeof ( tmf - > rsp ) ;
tmf - > frame_type = IBMVFC_SCSI_FCP_TYPE ;
tmf - > payload_len = sizeof ( tmf - > iu ) ;
tmf - > resp_len = sizeof ( tmf - > rsp ) ;
tmf - > cancel_key = ( unsigned long ) sdev - > hostdata ;
tmf - > tgt_scsi_id = rport - > port_id ;
int_to_scsilun ( sdev - > lun , & tmf - > iu . lun ) ;
tmf - > flags = ( IBMVFC_NO_MEM_DESC | IBMVFC_TMF ) ;
tmf - > iu . tmf_flags = IBMVFC_ABORT_TASK_SET ;
evt - > sync_iu = & rsp_iu ;
init_completion ( & evt - > comp ) ;
rsp_rc = ibmvfc_send_event ( evt , vhost , default_timeout ) ;
}
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
if ( rsp_rc ! = 0 ) {
sdev_printk ( KERN_ERR , sdev , " Failed to send abort. rc=%d \n " , rsp_rc ) ;
return - EIO ;
}
sdev_printk ( KERN_INFO , sdev , " Aborting outstanding commands \n " ) ;
wait_for_completion ( & evt - > comp ) ;
2009-10-20 00:07:47 +04:00
if ( rsp_iu . cmd . status )
rsp_code = ibmvfc_get_err_result ( & rsp_iu . cmd ) ;
if ( rsp_code ) {
2008-07-01 22:14:30 +04:00
if ( fc_rsp - > flags & FCP_RSP_LEN_VALID )
rsp_code = fc_rsp - > data . info . rsp_code ;
sdev_printk ( KERN_ERR , sdev , " Abort failed: %s (%x:%x) "
" flags: %x fcp_rsp: %x, scsi_status: %x \n " ,
ibmvfc_get_cmd_error ( rsp_iu . cmd . status , rsp_iu . cmd . error ) ,
rsp_iu . cmd . status , rsp_iu . cmd . error , fc_rsp - > flags , rsp_code ,
fc_rsp - > scsi_status ) ;
rsp_rc = - EIO ;
} else
sdev_printk ( KERN_INFO , sdev , " Abort successful \n " ) ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
ibmvfc_free_event ( evt ) ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
return rsp_rc ;
}
/**
* ibmvfc_cancel_all - Cancel all outstanding commands to the device
* @ sdev : scsi device to cancel commands
* @ type : type of error recovery being performed
*
* This sends a cancel to the VIOS for the specified device . This does
* NOT send any abort to the actual device . That must be done separately .
*
* Returns :
* 0 on success / other on failure
* */
static int ibmvfc_cancel_all ( struct scsi_device * sdev , int type )
{
struct ibmvfc_host * vhost = shost_priv ( sdev - > host ) ;
2008-10-29 16:46:41 +03:00
struct scsi_target * starget = scsi_target ( sdev ) ;
struct fc_rport * rport = starget_to_rport ( starget ) ;
2008-07-01 22:14:30 +04:00
struct ibmvfc_tmf * tmf ;
struct ibmvfc_event * evt , * found_evt ;
union ibmvfc_iu rsp ;
int rsp_rc = - EBUSY ;
unsigned long flags ;
u16 status ;
ENTER ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
found_evt = NULL ;
list_for_each_entry ( evt , & vhost - > sent , queue ) {
if ( evt - > cmnd & & evt - > cmnd - > device = = sdev ) {
found_evt = evt ;
break ;
}
}
if ( ! found_evt ) {
if ( vhost - > log_level > IBMVFC_DEFAULT_LOG_LEVEL )
sdev_printk ( KERN_INFO , sdev , " No events found to cancel \n " ) ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
return 0 ;
}
if ( vhost - > state = = IBMVFC_ACTIVE ) {
evt = ibmvfc_get_event ( vhost ) ;
ibmvfc_init_event ( evt , ibmvfc_sync_completion , IBMVFC_MAD_FORMAT ) ;
tmf = & evt - > iu . tmf ;
memset ( tmf , 0 , sizeof ( * tmf ) ) ;
tmf - > common . version = 1 ;
tmf - > common . opcode = IBMVFC_TMF_MAD ;
tmf - > common . length = sizeof ( * tmf ) ;
tmf - > scsi_id = rport - > port_id ;
int_to_scsilun ( sdev - > lun , & tmf - > lun ) ;
tmf - > flags = ( type | IBMVFC_TMF_LUA_VALID ) ;
tmf - > cancel_key = ( unsigned long ) sdev - > hostdata ;
2008-10-29 16:46:41 +03:00
tmf - > my_cancel_key = ( unsigned long ) starget - > hostdata ;
2008-07-01 22:14:30 +04:00
evt - > sync_iu = & rsp ;
init_completion ( & evt - > comp ) ;
rsp_rc = ibmvfc_send_event ( evt , vhost , default_timeout ) ;
}
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
if ( rsp_rc ! = 0 ) {
sdev_printk ( KERN_ERR , sdev , " Failed to send cancel event. rc=%d \n " , rsp_rc ) ;
return - EIO ;
}
sdev_printk ( KERN_INFO , sdev , " Cancelling outstanding commands. \n " ) ;
wait_for_completion ( & evt - > comp ) ;
status = rsp . mad_common . status ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
ibmvfc_free_event ( evt ) ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
if ( status ! = IBMVFC_MAD_SUCCESS ) {
sdev_printk ( KERN_WARNING , sdev , " Cancel failed with rc=%x \n " , status ) ;
return - EIO ;
}
sdev_printk ( KERN_INFO , sdev , " Successfully cancelled outstanding commands \n " ) ;
return 0 ;
}
2008-10-29 16:46:41 +03:00
/**
* ibmvfc_match_target - Match function for specified target
* @ evt : ibmvfc event struct
* @ device : device to match ( starget )
*
* Returns :
* 1 if event matches starget / 0 if event does not match starget
* */
static int ibmvfc_match_target ( struct ibmvfc_event * evt , void * device )
{
if ( evt - > cmnd & & scsi_target ( evt - > cmnd - > device ) = = device )
return 1 ;
return 0 ;
}
/**
* ibmvfc_match_lun - Match function for specified LUN
* @ evt : ibmvfc event struct
* @ device : device to match ( sdev )
*
* Returns :
* 1 if event matches sdev / 0 if event does not match sdev
* */
static int ibmvfc_match_lun ( struct ibmvfc_event * evt , void * device )
{
if ( evt - > cmnd & & evt - > cmnd - > device = = device )
return 1 ;
return 0 ;
}
/**
* ibmvfc_wait_for_ops - Wait for ops to complete
* @ vhost : ibmvfc host struct
* @ device : device to match ( starget or sdev )
* @ match : match function
*
* Returns :
* SUCCESS / FAILED
* */
static int ibmvfc_wait_for_ops ( struct ibmvfc_host * vhost , void * device ,
int ( * match ) ( struct ibmvfc_event * , void * ) )
{
struct ibmvfc_event * evt ;
DECLARE_COMPLETION_ONSTACK ( comp ) ;
int wait ;
unsigned long flags ;
signed long timeout = init_timeout * HZ ;
ENTER ;
do {
wait = 0 ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
list_for_each_entry ( evt , & vhost - > sent , queue ) {
if ( match ( evt , device ) ) {
evt - > eh_comp = & comp ;
wait + + ;
}
}
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
if ( wait ) {
timeout = wait_for_completion_timeout ( & comp , timeout ) ;
if ( ! timeout ) {
wait = 0 ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
list_for_each_entry ( evt , & vhost - > sent , queue ) {
if ( match ( evt , device ) ) {
evt - > eh_comp = NULL ;
wait + + ;
}
}
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
if ( wait )
dev_err ( vhost - > dev , " Timed out waiting for aborted commands \n " ) ;
LEAVE ;
return wait ? FAILED : SUCCESS ;
}
}
} while ( wait ) ;
LEAVE ;
return SUCCESS ;
}
2008-07-01 22:14:30 +04:00
/**
* ibmvfc_eh_abort_handler - Abort a command
* @ cmd : scsi command to abort
*
* Returns :
* SUCCESS / FAILED
* */
static int ibmvfc_eh_abort_handler ( struct scsi_cmnd * cmd )
{
2008-10-29 16:46:41 +03:00
struct scsi_device * sdev = cmd - > device ;
struct ibmvfc_host * vhost = shost_priv ( sdev - > host ) ;
2008-07-01 22:14:30 +04:00
int cancel_rc , abort_rc ;
2008-10-29 16:46:41 +03:00
int rc = FAILED ;
2008-07-01 22:14:30 +04:00
ENTER ;
ibmvfc_wait_while_resetting ( vhost ) ;
2008-10-29 16:46:41 +03:00
cancel_rc = ibmvfc_cancel_all ( sdev , IBMVFC_TMF_ABORT_TASK_SET ) ;
abort_rc = ibmvfc_abort_task_set ( sdev ) ;
2008-07-01 22:14:30 +04:00
2008-10-29 16:46:41 +03:00
if ( ! cancel_rc & & ! abort_rc )
rc = ibmvfc_wait_for_ops ( vhost , sdev , ibmvfc_match_lun ) ;
2008-07-01 22:14:30 +04:00
LEAVE ;
2008-10-29 16:46:41 +03:00
return rc ;
2008-07-01 22:14:30 +04:00
}
/**
* ibmvfc_eh_device_reset_handler - Reset a single LUN
* @ cmd : scsi command struct
*
* Returns :
* SUCCESS / FAILED
* */
static int ibmvfc_eh_device_reset_handler ( struct scsi_cmnd * cmd )
{
2008-10-29 16:46:41 +03:00
struct scsi_device * sdev = cmd - > device ;
struct ibmvfc_host * vhost = shost_priv ( sdev - > host ) ;
2008-07-01 22:14:30 +04:00
int cancel_rc , reset_rc ;
2008-10-29 16:46:41 +03:00
int rc = FAILED ;
2008-07-01 22:14:30 +04:00
ENTER ;
ibmvfc_wait_while_resetting ( vhost ) ;
2008-10-29 16:46:41 +03:00
cancel_rc = ibmvfc_cancel_all ( sdev , IBMVFC_TMF_LUN_RESET ) ;
reset_rc = ibmvfc_reset_device ( sdev , IBMVFC_LUN_RESET , " LUN " ) ;
2008-07-01 22:14:30 +04:00
2008-10-29 16:46:41 +03:00
if ( ! cancel_rc & & ! reset_rc )
rc = ibmvfc_wait_for_ops ( vhost , sdev , ibmvfc_match_lun ) ;
2008-07-01 22:14:30 +04:00
LEAVE ;
2008-10-29 16:46:41 +03:00
return rc ;
2008-07-01 22:14:30 +04:00
}
/**
* ibmvfc_dev_cancel_all - Device iterated cancel all function
* @ sdev : scsi device struct
* @ data : return code
*
* */
static void ibmvfc_dev_cancel_all ( struct scsi_device * sdev , void * data )
{
unsigned long * rc = data ;
* rc | = ibmvfc_cancel_all ( sdev , IBMVFC_TMF_TGT_RESET ) ;
}
/**
* ibmvfc_dev_abort_all - Device iterated abort task set function
* @ sdev : scsi device struct
* @ data : return code
*
* */
static void ibmvfc_dev_abort_all ( struct scsi_device * sdev , void * data )
{
unsigned long * rc = data ;
* rc | = ibmvfc_abort_task_set ( sdev ) ;
}
/**
* ibmvfc_eh_target_reset_handler - Reset the target
* @ cmd : scsi command struct
*
* Returns :
* SUCCESS / FAILED
* */
static int ibmvfc_eh_target_reset_handler ( struct scsi_cmnd * cmd )
{
2008-10-29 16:46:41 +03:00
struct scsi_device * sdev = cmd - > device ;
struct ibmvfc_host * vhost = shost_priv ( sdev - > host ) ;
struct scsi_target * starget = scsi_target ( sdev ) ;
2008-07-01 22:14:30 +04:00
int reset_rc ;
2008-10-29 16:46:41 +03:00
int rc = FAILED ;
2008-07-01 22:14:30 +04:00
unsigned long cancel_rc = 0 ;
ENTER ;
ibmvfc_wait_while_resetting ( vhost ) ;
starget_for_each_device ( starget , & cancel_rc , ibmvfc_dev_cancel_all ) ;
2008-10-29 16:46:41 +03:00
reset_rc = ibmvfc_reset_device ( sdev , IBMVFC_TARGET_RESET , " target " ) ;
2008-07-01 22:14:30 +04:00
2008-10-29 16:46:41 +03:00
if ( ! cancel_rc & & ! reset_rc )
rc = ibmvfc_wait_for_ops ( vhost , starget , ibmvfc_match_target ) ;
2008-07-01 22:14:30 +04:00
LEAVE ;
2008-10-29 16:46:41 +03:00
return rc ;
2008-07-01 22:14:30 +04:00
}
/**
* ibmvfc_eh_host_reset_handler - Reset the connection to the server
* @ cmd : struct scsi_cmnd having problems
*
* */
static int ibmvfc_eh_host_reset_handler ( struct scsi_cmnd * cmd )
{
int rc ;
struct ibmvfc_host * vhost = shost_priv ( cmd - > device - > host ) ;
dev_err ( vhost - > dev , " Resetting connection due to error recovery \n " ) ;
rc = ibmvfc_issue_fc_host_lip ( vhost - > host ) ;
return rc ? FAILED : SUCCESS ;
}
/**
* ibmvfc_terminate_rport_io - Terminate all pending I / O to the rport .
* @ rport : rport struct
*
* Return value :
* none
* */
static void ibmvfc_terminate_rport_io ( struct fc_rport * rport )
{
struct scsi_target * starget = to_scsi_target ( & rport - > dev ) ;
struct Scsi_Host * shost = dev_to_shost ( starget - > dev . parent ) ;
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
unsigned long cancel_rc = 0 ;
unsigned long abort_rc = 0 ;
2008-10-29 16:46:41 +03:00
int rc = FAILED ;
2008-07-01 22:14:30 +04:00
ENTER ;
starget_for_each_device ( starget , & cancel_rc , ibmvfc_dev_cancel_all ) ;
starget_for_each_device ( starget , & abort_rc , ibmvfc_dev_abort_all ) ;
2008-10-29 16:46:41 +03:00
if ( ! cancel_rc & & ! abort_rc )
rc = ibmvfc_wait_for_ops ( vhost , starget , ibmvfc_match_target ) ;
if ( rc = = FAILED )
2008-07-01 22:14:30 +04:00
ibmvfc_issue_fc_host_lip ( shost ) ;
LEAVE ;
}
static const struct {
enum ibmvfc_async_event ae ;
const char * desc ;
} ae_desc [ ] = {
{ IBMVFC_AE_ELS_PLOGI , " PLOGI " } ,
{ IBMVFC_AE_ELS_LOGO , " LOGO " } ,
{ IBMVFC_AE_ELS_PRLO , " PRLO " } ,
{ IBMVFC_AE_SCN_NPORT , " N-Port SCN " } ,
{ IBMVFC_AE_SCN_GROUP , " Group SCN " } ,
{ IBMVFC_AE_SCN_DOMAIN , " Domain SCN " } ,
{ IBMVFC_AE_SCN_FABRIC , " Fabric SCN " } ,
{ IBMVFC_AE_LINK_UP , " Link Up " } ,
{ IBMVFC_AE_LINK_DOWN , " Link Down " } ,
{ IBMVFC_AE_LINK_DEAD , " Link Dead " } ,
{ IBMVFC_AE_HALT , " Halt " } ,
{ IBMVFC_AE_RESUME , " Resume " } ,
{ IBMVFC_AE_ADAPTER_FAILED , " Adapter Failed " } ,
} ;
static const char * unknown_ae = " Unknown async " ;
/**
* ibmvfc_get_ae_desc - Get text description for async event
* @ ae : async event
*
* */
static const char * ibmvfc_get_ae_desc ( u64 ae )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( ae_desc ) ; i + + )
if ( ae_desc [ i ] . ae = = ae )
return ae_desc [ i ] . desc ;
return unknown_ae ;
}
/**
* ibmvfc_handle_async - Handle an async event from the adapter
* @ crq : crq to process
* @ vhost : ibmvfc host struct
*
* */
static void ibmvfc_handle_async ( struct ibmvfc_async_crq * crq ,
struct ibmvfc_host * vhost )
{
const char * desc = ibmvfc_get_ae_desc ( crq - > event ) ;
2009-05-29 01:17:33 +04:00
struct ibmvfc_target * tgt ;
2008-07-01 22:14:30 +04:00
2009-01-06 17:59:00 +03:00
ibmvfc_log ( vhost , 3 , " %s event received. scsi_id: %llx, wwpn: %llx, "
" node_name: %llx \n " , desc , crq - > scsi_id , crq - > wwpn , crq - > node_name ) ;
2008-07-01 22:14:30 +04:00
switch ( crq - > event ) {
case IBMVFC_AE_RESUME :
2009-05-29 01:17:30 +04:00
switch ( crq - > link_state ) {
case IBMVFC_AE_LS_LINK_DOWN :
ibmvfc_link_down ( vhost , IBMVFC_LINK_DOWN ) ;
break ;
case IBMVFC_AE_LS_LINK_DEAD :
ibmvfc_link_down ( vhost , IBMVFC_LINK_DEAD ) ;
break ;
case IBMVFC_AE_LS_LINK_UP :
case IBMVFC_AE_LS_LINK_BOUNCED :
default :
vhost - > events_to_log | = IBMVFC_AE_LINKUP ;
vhost - > delay_init = 1 ;
__ibmvfc_reset_host ( vhost ) ;
break ;
} ;
break ;
case IBMVFC_AE_LINK_UP :
2008-07-01 22:14:30 +04:00
vhost - > events_to_log | = IBMVFC_AE_LINKUP ;
2008-12-18 18:26:51 +03:00
vhost - > delay_init = 1 ;
__ibmvfc_reset_host ( vhost ) ;
2008-07-01 22:14:30 +04:00
break ;
case IBMVFC_AE_SCN_FABRIC :
2008-12-18 18:26:51 +03:00
case IBMVFC_AE_SCN_DOMAIN :
2008-07-01 22:14:30 +04:00
vhost - > events_to_log | = IBMVFC_AE_RSCN ;
2008-12-18 18:26:51 +03:00
vhost - > delay_init = 1 ;
__ibmvfc_reset_host ( vhost ) ;
2008-07-01 22:14:30 +04:00
break ;
case IBMVFC_AE_SCN_NPORT :
case IBMVFC_AE_SCN_GROUP :
vhost - > events_to_log | = IBMVFC_AE_RSCN ;
2009-05-29 01:17:33 +04:00
ibmvfc_reinit_host ( vhost ) ;
break ;
2008-07-01 22:14:30 +04:00
case IBMVFC_AE_ELS_LOGO :
case IBMVFC_AE_ELS_PRLO :
case IBMVFC_AE_ELS_PLOGI :
2009-05-29 01:17:33 +04:00
list_for_each_entry ( tgt , & vhost - > targets , queue ) {
if ( ! crq - > scsi_id & & ! crq - > wwpn & & ! crq - > node_name )
break ;
if ( crq - > scsi_id & & tgt - > scsi_id ! = crq - > scsi_id )
continue ;
if ( crq - > wwpn & & tgt - > ids . port_name ! = crq - > wwpn )
continue ;
if ( crq - > node_name & & tgt - > ids . node_name ! = crq - > node_name )
continue ;
2009-06-18 18:06:55 +04:00
if ( tgt - > need_login & & crq - > event = = IBMVFC_AE_ELS_LOGO )
tgt - > logo_rcvd = 1 ;
if ( ! tgt - > need_login | | crq - > event = = IBMVFC_AE_ELS_PLOGI ) {
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_DEL_RPORT ) ;
ibmvfc_reinit_host ( vhost ) ;
}
2009-05-29 01:17:33 +04:00
}
2008-07-01 22:14:30 +04:00
break ;
case IBMVFC_AE_LINK_DOWN :
case IBMVFC_AE_ADAPTER_FAILED :
ibmvfc_link_down ( vhost , IBMVFC_LINK_DOWN ) ;
break ;
case IBMVFC_AE_LINK_DEAD :
ibmvfc_link_down ( vhost , IBMVFC_LINK_DEAD ) ;
break ;
case IBMVFC_AE_HALT :
ibmvfc_link_down ( vhost , IBMVFC_HALTED ) ;
break ;
default :
2009-01-06 17:59:00 +03:00
dev_err ( vhost - > dev , " Unknown async event received: %lld \n " , crq - > event ) ;
2008-07-01 22:14:30 +04:00
break ;
} ;
}
/**
* ibmvfc_handle_crq - Handles and frees received events in the CRQ
* @ crq : Command / Response queue
* @ vhost : ibmvfc host struct
*
* */
static void ibmvfc_handle_crq ( struct ibmvfc_crq * crq , struct ibmvfc_host * vhost )
{
long rc ;
struct ibmvfc_event * evt = ( struct ibmvfc_event * ) crq - > ioba ;
switch ( crq - > valid ) {
case IBMVFC_CRQ_INIT_RSP :
switch ( crq - > format ) {
case IBMVFC_CRQ_INIT :
dev_info ( vhost - > dev , " Partner initialized \n " ) ;
/* Send back a response */
rc = ibmvfc_send_crq_init_complete ( vhost ) ;
if ( rc = = 0 )
2009-10-20 00:07:49 +04:00
ibmvfc_init_host ( vhost ) ;
2008-07-01 22:14:30 +04:00
else
dev_err ( vhost - > dev , " Unable to send init rsp. rc=%ld \n " , rc ) ;
break ;
case IBMVFC_CRQ_INIT_COMPLETE :
dev_info ( vhost - > dev , " Partner initialization complete \n " ) ;
2009-10-20 00:07:49 +04:00
ibmvfc_init_host ( vhost ) ;
2008-07-01 22:14:30 +04:00
break ;
default :
dev_err ( vhost - > dev , " Unknown crq message type: %d \n " , crq - > format ) ;
}
return ;
case IBMVFC_CRQ_XPORT_EVENT :
vhost - > state = IBMVFC_NO_CRQ ;
2009-05-29 01:17:29 +04:00
vhost - > logged_in = 0 ;
2008-07-01 22:14:30 +04:00
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_NONE ) ;
if ( crq - > format = = IBMVFC_PARTITION_MIGRATED ) {
/* We need to re-setup the interpartition connection */
dev_info ( vhost - > dev , " Re-enabling adapter \n " ) ;
vhost - > client_migrated = 1 ;
ibmvfc_purge_requests ( vhost , DID_REQUEUE ) ;
if ( ( rc = ibmvfc_reenable_crq_queue ( vhost ) ) | |
( rc = ibmvfc_send_crq_init ( vhost ) ) ) {
ibmvfc_link_down ( vhost , IBMVFC_LINK_DEAD ) ;
dev_err ( vhost - > dev , " Error after enable (rc=%ld) \n " , rc ) ;
} else
ibmvfc_link_down ( vhost , IBMVFC_LINK_DOWN ) ;
} else {
dev_err ( vhost - > dev , " Virtual adapter failed (rc=%d) \n " , crq - > format ) ;
ibmvfc_purge_requests ( vhost , DID_ERROR ) ;
if ( ( rc = ibmvfc_reset_crq ( vhost ) ) | |
( rc = ibmvfc_send_crq_init ( vhost ) ) ) {
ibmvfc_link_down ( vhost , IBMVFC_LINK_DEAD ) ;
dev_err ( vhost - > dev , " Error after reset (rc=%ld) \n " , rc ) ;
} else
ibmvfc_link_down ( vhost , IBMVFC_LINK_DOWN ) ;
}
return ;
case IBMVFC_CRQ_CMD_RSP :
break ;
default :
dev_err ( vhost - > dev , " Got an invalid message type 0x%02x \n " , crq - > valid ) ;
return ;
}
if ( crq - > format = = IBMVFC_ASYNC_EVENT )
return ;
/* The only kind of payload CRQs we should get are responses to
* things we send . Make sure this response is to something we
* actually sent
*/
if ( unlikely ( ! ibmvfc_valid_event ( & vhost - > pool , evt ) ) ) {
2009-01-06 17:59:00 +03:00
dev_err ( vhost - > dev , " Returned correlation_token 0x%08llx is invalid! \n " ,
2008-07-01 22:14:30 +04:00
crq - > ioba ) ;
return ;
}
if ( unlikely ( atomic_read ( & evt - > free ) ) ) {
2009-01-06 17:59:00 +03:00
dev_err ( vhost - > dev , " Received duplicate correlation_token 0x%08llx! \n " ,
2008-07-01 22:14:30 +04:00
crq - > ioba ) ;
return ;
}
del_timer ( & evt - > timer ) ;
list_del ( & evt - > queue ) ;
ibmvfc_trc_end ( evt ) ;
evt - > done ( evt ) ;
}
/**
* ibmvfc_scan_finished - Check if the device scan is done .
* @ shost : scsi host struct
* @ time : current elapsed time
*
* Returns :
* 0 if scan is not done / 1 if scan is done
* */
static int ibmvfc_scan_finished ( struct Scsi_Host * shost , unsigned long time )
{
unsigned long flags ;
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
int done = 0 ;
spin_lock_irqsave ( shost - > host_lock , flags ) ;
if ( time > = ( init_timeout * HZ ) ) {
dev_info ( vhost - > dev , " Scan taking longer than %d seconds, "
" continuing initialization \n " , init_timeout ) ;
done = 1 ;
}
2009-05-29 01:17:28 +04:00
if ( vhost - > scan_complete )
2008-07-01 22:14:30 +04:00
done = 1 ;
spin_unlock_irqrestore ( shost - > host_lock , flags ) ;
return done ;
}
/**
* ibmvfc_slave_alloc - Setup the device ' s task set value
* @ sdev : struct scsi_device device to configure
*
* Set the device ' s task set value so that error handling works as
* expected .
*
* Returns :
* 0 on success / - ENXIO if device does not exist
* */
static int ibmvfc_slave_alloc ( struct scsi_device * sdev )
{
struct Scsi_Host * shost = sdev - > host ;
struct fc_rport * rport = starget_to_rport ( scsi_target ( sdev ) ) ;
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
unsigned long flags = 0 ;
if ( ! rport | | fc_remote_port_chkready ( rport ) )
return - ENXIO ;
spin_lock_irqsave ( shost - > host_lock , flags ) ;
sdev - > hostdata = ( void * ) ( unsigned long ) vhost - > task_set + + ;
spin_unlock_irqrestore ( shost - > host_lock , flags ) ;
return 0 ;
}
2008-10-29 16:46:41 +03:00
/**
* ibmvfc_target_alloc - Setup the target ' s task set value
* @ starget : struct scsi_target
*
* Set the target ' s task set value so that error handling works as
* expected .
*
* Returns :
* 0 on success / - ENXIO if device does not exist
* */
static int ibmvfc_target_alloc ( struct scsi_target * starget )
{
struct Scsi_Host * shost = dev_to_shost ( starget - > dev . parent ) ;
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
unsigned long flags = 0 ;
spin_lock_irqsave ( shost - > host_lock , flags ) ;
starget - > hostdata = ( void * ) ( unsigned long ) vhost - > task_set + + ;
spin_unlock_irqrestore ( shost - > host_lock , flags ) ;
return 0 ;
}
2008-07-01 22:14:30 +04:00
/**
* ibmvfc_slave_configure - Configure the device
* @ sdev : struct scsi_device device to configure
*
* Enable allow_restart for a device if it is a disk . Adjust the
* queue_depth here also .
*
* Returns :
* 0
* */
static int ibmvfc_slave_configure ( struct scsi_device * sdev )
{
struct Scsi_Host * shost = sdev - > host ;
struct fc_rport * rport = starget_to_rport ( sdev - > sdev_target ) ;
unsigned long flags = 0 ;
spin_lock_irqsave ( shost - > host_lock , flags ) ;
if ( sdev - > type = = TYPE_DISK )
sdev - > allow_restart = 1 ;
if ( sdev - > tagged_supported ) {
scsi_set_tag_type ( sdev , MSG_SIMPLE_TAG ) ;
scsi_activate_tcq ( sdev , sdev - > queue_depth ) ;
} else
scsi_deactivate_tcq ( sdev , sdev - > queue_depth ) ;
rport - > dev_loss_tmo = dev_loss_tmo ;
spin_unlock_irqrestore ( shost - > host_lock , flags ) ;
return 0 ;
}
/**
* ibmvfc_change_queue_depth - Change the device ' s queue depth
* @ sdev : scsi device struct
* @ qdepth : depth to set
*
* Return value :
* actual depth set
* */
static int ibmvfc_change_queue_depth ( struct scsi_device * sdev , int qdepth )
{
if ( qdepth > IBMVFC_MAX_CMDS_PER_LUN )
qdepth = IBMVFC_MAX_CMDS_PER_LUN ;
scsi_adjust_queue_depth ( sdev , 0 , qdepth ) ;
return sdev - > queue_depth ;
}
/**
* ibmvfc_change_queue_type - Change the device ' s queue type
* @ sdev : scsi device struct
* @ tag_type : type of tags to use
*
* Return value :
* actual queue type set
* */
static int ibmvfc_change_queue_type ( struct scsi_device * sdev , int tag_type )
{
if ( sdev - > tagged_supported ) {
scsi_set_tag_type ( sdev , tag_type ) ;
if ( tag_type )
scsi_activate_tcq ( sdev , sdev - > queue_depth ) ;
else
scsi_deactivate_tcq ( sdev , sdev - > queue_depth ) ;
} else
tag_type = 0 ;
return tag_type ;
}
static ssize_t ibmvfc_show_host_partition_name ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct Scsi_Host * shost = class_to_shost ( dev ) ;
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
return snprintf ( buf , PAGE_SIZE , " %s \n " ,
vhost - > login_buf - > resp . partition_name ) ;
}
static ssize_t ibmvfc_show_host_device_name ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct Scsi_Host * shost = class_to_shost ( dev ) ;
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
return snprintf ( buf , PAGE_SIZE , " %s \n " ,
vhost - > login_buf - > resp . device_name ) ;
}
static ssize_t ibmvfc_show_host_loc_code ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct Scsi_Host * shost = class_to_shost ( dev ) ;
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
return snprintf ( buf , PAGE_SIZE , " %s \n " ,
vhost - > login_buf - > resp . port_loc_code ) ;
}
static ssize_t ibmvfc_show_host_drc_name ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct Scsi_Host * shost = class_to_shost ( dev ) ;
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
return snprintf ( buf , PAGE_SIZE , " %s \n " ,
vhost - > login_buf - > resp . drc_name ) ;
}
static ssize_t ibmvfc_show_host_npiv_version ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct Scsi_Host * shost = class_to_shost ( dev ) ;
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
return snprintf ( buf , PAGE_SIZE , " %d \n " , vhost - > login_buf - > resp . version ) ;
}
2009-05-29 01:17:30 +04:00
static ssize_t ibmvfc_show_host_capabilities ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct Scsi_Host * shost = class_to_shost ( dev ) ;
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
return snprintf ( buf , PAGE_SIZE , " %llx \n " , vhost - > login_buf - > resp . capabilities ) ;
}
2008-07-01 22:14:30 +04:00
/**
* ibmvfc_show_log_level - Show the adapter ' s error logging level
* @ dev : class device struct
* @ buf : buffer
*
* Return value :
* number of bytes printed to buffer
* */
static ssize_t ibmvfc_show_log_level ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct Scsi_Host * shost = class_to_shost ( dev ) ;
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
unsigned long flags = 0 ;
int len ;
spin_lock_irqsave ( shost - > host_lock , flags ) ;
len = snprintf ( buf , PAGE_SIZE , " %d \n " , vhost - > log_level ) ;
spin_unlock_irqrestore ( shost - > host_lock , flags ) ;
return len ;
}
/**
* ibmvfc_store_log_level - Change the adapter ' s error logging level
* @ dev : class device struct
* @ buf : buffer
*
* Return value :
* number of bytes printed to buffer
* */
static ssize_t ibmvfc_store_log_level ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct Scsi_Host * shost = class_to_shost ( dev ) ;
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
unsigned long flags = 0 ;
spin_lock_irqsave ( shost - > host_lock , flags ) ;
vhost - > log_level = simple_strtoul ( buf , NULL , 10 ) ;
spin_unlock_irqrestore ( shost - > host_lock , flags ) ;
return strlen ( buf ) ;
}
2009-05-29 01:17:25 +04:00
static DEVICE_ATTR ( partition_name , S_IRUGO , ibmvfc_show_host_partition_name , NULL ) ;
static DEVICE_ATTR ( device_name , S_IRUGO , ibmvfc_show_host_device_name , NULL ) ;
static DEVICE_ATTR ( port_loc_code , S_IRUGO , ibmvfc_show_host_loc_code , NULL ) ;
static DEVICE_ATTR ( drc_name , S_IRUGO , ibmvfc_show_host_drc_name , NULL ) ;
static DEVICE_ATTR ( npiv_version , S_IRUGO , ibmvfc_show_host_npiv_version , NULL ) ;
2009-05-29 01:17:30 +04:00
static DEVICE_ATTR ( capabilities , S_IRUGO , ibmvfc_show_host_capabilities , NULL ) ;
2009-05-29 01:17:25 +04:00
static DEVICE_ATTR ( log_level , S_IRUGO | S_IWUSR ,
ibmvfc_show_log_level , ibmvfc_store_log_level ) ;
2008-07-01 22:14:30 +04:00
# ifdef CONFIG_SCSI_IBMVFC_TRACE
/**
* ibmvfc_read_trace - Dump the adapter trace
* @ kobj : kobject struct
* @ bin_attr : bin_attribute struct
* @ buf : buffer
* @ off : offset
* @ count : buffer size
*
* Return value :
* number of bytes printed to buffer
* */
static ssize_t ibmvfc_read_trace ( struct kobject * kobj ,
struct bin_attribute * bin_attr ,
char * buf , loff_t off , size_t count )
{
struct device * dev = container_of ( kobj , struct device , kobj ) ;
struct Scsi_Host * shost = class_to_shost ( dev ) ;
struct ibmvfc_host * vhost = shost_priv ( shost ) ;
unsigned long flags = 0 ;
int size = IBMVFC_TRACE_SIZE ;
char * src = ( char * ) vhost - > trace ;
if ( off > size )
return 0 ;
if ( off + count > size ) {
size - = off ;
count = size ;
}
spin_lock_irqsave ( shost - > host_lock , flags ) ;
memcpy ( buf , & src [ off ] , count ) ;
spin_unlock_irqrestore ( shost - > host_lock , flags ) ;
return count ;
}
static struct bin_attribute ibmvfc_trace_attr = {
. attr = {
. name = " trace " ,
. mode = S_IRUGO ,
} ,
. size = 0 ,
. read = ibmvfc_read_trace ,
} ;
# endif
static struct device_attribute * ibmvfc_attrs [ ] = {
2009-05-29 01:17:25 +04:00
& dev_attr_partition_name ,
& dev_attr_device_name ,
& dev_attr_port_loc_code ,
& dev_attr_drc_name ,
& dev_attr_npiv_version ,
2009-05-29 01:17:30 +04:00
& dev_attr_capabilities ,
2009-05-29 01:17:25 +04:00
& dev_attr_log_level ,
2008-07-01 22:14:30 +04:00
NULL
} ;
static struct scsi_host_template driver_template = {
. module = THIS_MODULE ,
. name = " IBM POWER Virtual FC Adapter " ,
. proc_name = IBMVFC_NAME ,
. queuecommand = ibmvfc_queuecommand ,
. eh_abort_handler = ibmvfc_eh_abort_handler ,
. eh_device_reset_handler = ibmvfc_eh_device_reset_handler ,
. eh_target_reset_handler = ibmvfc_eh_target_reset_handler ,
. eh_host_reset_handler = ibmvfc_eh_host_reset_handler ,
. slave_alloc = ibmvfc_slave_alloc ,
. slave_configure = ibmvfc_slave_configure ,
2008-10-29 16:46:41 +03:00
. target_alloc = ibmvfc_target_alloc ,
2008-07-01 22:14:30 +04:00
. scan_finished = ibmvfc_scan_finished ,
. change_queue_depth = ibmvfc_change_queue_depth ,
. change_queue_type = ibmvfc_change_queue_type ,
. cmd_per_lun = 16 ,
. can_queue = IBMVFC_MAX_REQUESTS_DEFAULT ,
. this_id = - 1 ,
. sg_tablesize = SG_ALL ,
. max_sectors = IBMVFC_MAX_SECTORS ,
. use_clustering = ENABLE_CLUSTERING ,
. shost_attrs = ibmvfc_attrs ,
} ;
/**
* ibmvfc_next_async_crq - Returns the next entry in async queue
* @ vhost : ibmvfc host struct
*
* Returns :
* Pointer to next entry in queue / NULL if empty
* */
static struct ibmvfc_async_crq * ibmvfc_next_async_crq ( struct ibmvfc_host * vhost )
{
struct ibmvfc_async_crq_queue * async_crq = & vhost - > async_crq ;
struct ibmvfc_async_crq * crq ;
crq = & async_crq - > msgs [ async_crq - > cur ] ;
if ( crq - > valid & 0x80 ) {
if ( + + async_crq - > cur = = async_crq - > size )
async_crq - > cur = 0 ;
} else
crq = NULL ;
return crq ;
}
/**
* ibmvfc_next_crq - Returns the next entry in message queue
* @ vhost : ibmvfc host struct
*
* Returns :
* Pointer to next entry in queue / NULL if empty
* */
static struct ibmvfc_crq * ibmvfc_next_crq ( struct ibmvfc_host * vhost )
{
struct ibmvfc_crq_queue * queue = & vhost - > crq ;
struct ibmvfc_crq * crq ;
crq = & queue - > msgs [ queue - > cur ] ;
if ( crq - > valid & 0x80 ) {
if ( + + queue - > cur = = queue - > size )
queue - > cur = 0 ;
} else
crq = NULL ;
return crq ;
}
/**
* ibmvfc_interrupt - Interrupt handler
* @ irq : number of irq to handle , not used
* @ dev_instance : ibmvfc_host that received interrupt
*
* Returns :
* IRQ_HANDLED
* */
static irqreturn_t ibmvfc_interrupt ( int irq , void * dev_instance )
{
struct ibmvfc_host * vhost = ( struct ibmvfc_host * ) dev_instance ;
2009-03-20 23:44:35 +03:00
unsigned long flags ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
vio_disable_interrupts ( to_vio_dev ( vhost - > dev ) ) ;
tasklet_schedule ( & vhost - > tasklet ) ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
return IRQ_HANDLED ;
}
/**
* ibmvfc_tasklet - Interrupt handler tasklet
* @ data : ibmvfc host struct
*
* Returns :
* Nothing
* */
static void ibmvfc_tasklet ( void * data )
{
struct ibmvfc_host * vhost = data ;
2008-07-01 22:14:30 +04:00
struct vio_dev * vdev = to_vio_dev ( vhost - > dev ) ;
struct ibmvfc_crq * crq ;
struct ibmvfc_async_crq * async ;
unsigned long flags ;
int done = 0 ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
while ( ! done ) {
/* Pull all the valid messages off the async CRQ */
while ( ( async = ibmvfc_next_async_crq ( vhost ) ) ! = NULL ) {
ibmvfc_handle_async ( async , vhost ) ;
async - > valid = 0 ;
}
2009-06-18 18:06:52 +04:00
/* Pull all the valid messages off the CRQ */
while ( ( crq = ibmvfc_next_crq ( vhost ) ) ! = NULL ) {
2008-07-01 22:14:30 +04:00
ibmvfc_handle_crq ( crq , vhost ) ;
crq - > valid = 0 ;
2009-06-18 18:06:52 +04:00
}
vio_enable_interrupts ( vdev ) ;
if ( ( async = ibmvfc_next_async_crq ( vhost ) ) ! = NULL ) {
2008-07-01 22:14:30 +04:00
vio_disable_interrupts ( vdev ) ;
ibmvfc_handle_async ( async , vhost ) ;
2008-11-14 22:33:50 +03:00
async - > valid = 0 ;
2009-06-18 18:06:52 +04:00
} else if ( ( crq = ibmvfc_next_crq ( vhost ) ) ! = NULL ) {
vio_disable_interrupts ( vdev ) ;
ibmvfc_handle_crq ( crq , vhost ) ;
crq - > valid = 0 ;
2008-07-01 22:14:30 +04:00
} else
done = 1 ;
}
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
}
/**
* ibmvfc_init_tgt - Set the next init job step for the target
* @ tgt : ibmvfc target struct
* @ job_step : job step to perform
*
* */
static void ibmvfc_init_tgt ( struct ibmvfc_target * tgt ,
void ( * job_step ) ( struct ibmvfc_target * ) )
{
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_INIT ) ;
tgt - > job_step = job_step ;
wake_up ( & tgt - > vhost - > work_wait_q ) ;
}
/**
* ibmvfc_retry_tgt_init - Attempt to retry a step in target initialization
* @ tgt : ibmvfc target struct
* @ job_step : initialization job step
*
2009-05-29 01:17:26 +04:00
* Returns : 1 if step will be retried / 0 if not
*
2008-07-01 22:14:30 +04:00
* */
2009-05-29 01:17:26 +04:00
static int ibmvfc_retry_tgt_init ( struct ibmvfc_target * tgt ,
2008-07-01 22:14:30 +04:00
void ( * job_step ) ( struct ibmvfc_target * ) )
{
2008-12-03 20:02:54 +03:00
if ( + + tgt - > init_retries > IBMVFC_MAX_TGT_INIT_RETRIES ) {
2008-07-01 22:14:30 +04:00
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_DEL_RPORT ) ;
wake_up ( & tgt - > vhost - > work_wait_q ) ;
2009-05-29 01:17:26 +04:00
return 0 ;
2008-07-01 22:14:30 +04:00
} else
ibmvfc_init_tgt ( tgt , job_step ) ;
2009-05-29 01:17:26 +04:00
return 1 ;
2008-07-01 22:14:30 +04:00
}
2009-02-03 03:39:40 +03:00
/* Defined in FC-LS */
static const struct {
int code ;
int retry ;
int logged_in ;
} prli_rsp [ ] = {
{ 0 , 1 , 0 } ,
{ 1 , 0 , 1 } ,
{ 2 , 1 , 0 } ,
{ 3 , 1 , 0 } ,
{ 4 , 0 , 0 } ,
{ 5 , 0 , 0 } ,
{ 6 , 0 , 1 } ,
{ 7 , 0 , 0 } ,
{ 8 , 1 , 0 } ,
} ;
/**
* ibmvfc_get_prli_rsp - Find PRLI response index
* @ flags : PRLI response flags
*
* */
static int ibmvfc_get_prli_rsp ( u16 flags )
{
int i ;
int code = ( flags & 0x0f00 ) > > 8 ;
for ( i = 0 ; i < ARRAY_SIZE ( prli_rsp ) ; i + + )
if ( prli_rsp [ i ] . code = = code )
return i ;
return 0 ;
}
2008-07-01 22:14:30 +04:00
/**
* ibmvfc_tgt_prli_done - Completion handler for Process Login
* @ evt : ibmvfc event struct
*
* */
static void ibmvfc_tgt_prli_done ( struct ibmvfc_event * evt )
{
struct ibmvfc_target * tgt = evt - > tgt ;
struct ibmvfc_host * vhost = evt - > vhost ;
struct ibmvfc_process_login * rsp = & evt - > xfer_iu - > prli ;
2009-02-03 03:39:40 +03:00
struct ibmvfc_prli_svc_parms * parms = & rsp - > parms ;
2008-07-01 22:14:30 +04:00
u32 status = rsp - > common . status ;
2009-05-29 01:17:26 +04:00
int index , level = IBMVFC_DEFAULT_LOG_LEVEL ;
2008-07-01 22:14:30 +04:00
vhost - > discovery_threads - - ;
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_NONE ) ;
switch ( status ) {
case IBMVFC_MAD_SUCCESS :
2009-02-03 03:39:40 +03:00
tgt_dbg ( tgt , " Process Login succeeded: %X %02X %04X \n " ,
parms - > type , parms - > flags , parms - > service_parms ) ;
if ( parms - > type = = IBMVFC_SCSI_FCP_TYPE ) {
index = ibmvfc_get_prli_rsp ( parms - > flags ) ;
if ( prli_rsp [ index ] . logged_in ) {
if ( parms - > flags & IBMVFC_PRLI_EST_IMG_PAIR ) {
tgt - > need_login = 0 ;
tgt - > ids . roles = 0 ;
if ( parms - > service_parms & IBMVFC_PRLI_TARGET_FUNC )
tgt - > ids . roles | = FC_PORT_ROLE_FCP_TARGET ;
if ( parms - > service_parms & IBMVFC_PRLI_INITIATOR_FUNC )
tgt - > ids . roles | = FC_PORT_ROLE_FCP_INITIATOR ;
2009-05-29 01:17:28 +04:00
tgt - > add_rport = 1 ;
2009-02-03 03:39:40 +03:00
} else
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_DEL_RPORT ) ;
} else if ( prli_rsp [ index ] . retry )
ibmvfc_retry_tgt_init ( tgt , ibmvfc_tgt_send_prli ) ;
else
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_DEL_RPORT ) ;
} else
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_DEL_RPORT ) ;
2008-07-01 22:14:30 +04:00
break ;
case IBMVFC_MAD_DRIVER_FAILED :
break ;
case IBMVFC_MAD_CRQ_ERROR :
ibmvfc_retry_tgt_init ( tgt , ibmvfc_tgt_send_prli ) ;
break ;
case IBMVFC_MAD_FAILED :
default :
2009-06-18 18:06:55 +04:00
if ( ( rsp - > status & IBMVFC_VIOS_FAILURE ) & & rsp - > error = = IBMVFC_PLOGI_REQUIRED )
level + = ibmvfc_retry_tgt_init ( tgt , ibmvfc_tgt_send_plogi ) ;
else if ( tgt - > logo_rcvd )
level + = ibmvfc_retry_tgt_init ( tgt , ibmvfc_tgt_send_plogi ) ;
else if ( ibmvfc_retry_cmd ( rsp - > status , rsp - > error ) )
2009-05-29 01:17:26 +04:00
level + = ibmvfc_retry_tgt_init ( tgt , ibmvfc_tgt_send_prli ) ;
2008-10-29 16:46:45 +03:00
else
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_DEL_RPORT ) ;
2009-05-29 01:17:26 +04:00
tgt_log ( tgt , level , " Process Login failed: %s (%x:%x) rc=0x%02X \n " ,
ibmvfc_get_cmd_error ( rsp - > status , rsp - > error ) ,
rsp - > status , rsp - > error , status ) ;
2008-07-01 22:14:30 +04:00
break ;
} ;
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
ibmvfc_free_event ( evt ) ;
wake_up ( & vhost - > work_wait_q ) ;
}
/**
* ibmvfc_tgt_send_prli - Send a process login
* @ tgt : ibmvfc target struct
*
* */
static void ibmvfc_tgt_send_prli ( struct ibmvfc_target * tgt )
{
struct ibmvfc_process_login * prli ;
struct ibmvfc_host * vhost = tgt - > vhost ;
struct ibmvfc_event * evt ;
if ( vhost - > discovery_threads > = disc_threads )
return ;
kref_get ( & tgt - > kref ) ;
evt = ibmvfc_get_event ( vhost ) ;
vhost - > discovery_threads + + ;
ibmvfc_init_event ( evt , ibmvfc_tgt_prli_done , IBMVFC_MAD_FORMAT ) ;
evt - > tgt = tgt ;
prli = & evt - > iu . prli ;
memset ( prli , 0 , sizeof ( * prli ) ) ;
prli - > common . version = 1 ;
prli - > common . opcode = IBMVFC_PROCESS_LOGIN ;
prli - > common . length = sizeof ( * prli ) ;
prli - > scsi_id = tgt - > scsi_id ;
prli - > parms . type = IBMVFC_SCSI_FCP_TYPE ;
prli - > parms . flags = IBMVFC_PRLI_EST_IMG_PAIR ;
prli - > parms . service_parms = IBMVFC_PRLI_INITIATOR_FUNC ;
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_INIT_WAIT ) ;
if ( ibmvfc_send_event ( evt , vhost , default_timeout ) ) {
vhost - > discovery_threads - - ;
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_NONE ) ;
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
} else
tgt_dbg ( tgt , " Sent process login \n " ) ;
}
/**
* ibmvfc_tgt_plogi_done - Completion handler for Port Login
* @ evt : ibmvfc event struct
*
* */
static void ibmvfc_tgt_plogi_done ( struct ibmvfc_event * evt )
{
struct ibmvfc_target * tgt = evt - > tgt ;
struct ibmvfc_host * vhost = evt - > vhost ;
struct ibmvfc_port_login * rsp = & evt - > xfer_iu - > plogi ;
u32 status = rsp - > common . status ;
2009-05-29 01:17:26 +04:00
int level = IBMVFC_DEFAULT_LOG_LEVEL ;
2008-07-01 22:14:30 +04:00
vhost - > discovery_threads - - ;
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_NONE ) ;
switch ( status ) {
case IBMVFC_MAD_SUCCESS :
tgt_dbg ( tgt , " Port Login succeeded \n " ) ;
if ( tgt - > ids . port_name & &
tgt - > ids . port_name ! = wwn_to_u64 ( rsp - > service_parms . port_name ) ) {
vhost - > reinit = 1 ;
tgt_dbg ( tgt , " Port re-init required \n " ) ;
break ;
}
tgt - > ids . node_name = wwn_to_u64 ( rsp - > service_parms . node_name ) ;
tgt - > ids . port_name = wwn_to_u64 ( rsp - > service_parms . port_name ) ;
tgt - > ids . port_id = tgt - > scsi_id ;
memcpy ( & tgt - > service_parms , & rsp - > service_parms ,
sizeof ( tgt - > service_parms ) ) ;
memcpy ( & tgt - > service_parms_change , & rsp - > service_parms_change ,
sizeof ( tgt - > service_parms_change ) ) ;
ibmvfc_init_tgt ( tgt , ibmvfc_tgt_send_prli ) ;
break ;
case IBMVFC_MAD_DRIVER_FAILED :
break ;
case IBMVFC_MAD_CRQ_ERROR :
ibmvfc_retry_tgt_init ( tgt , ibmvfc_tgt_send_plogi ) ;
break ;
case IBMVFC_MAD_FAILED :
default :
if ( ibmvfc_retry_cmd ( rsp - > status , rsp - > error ) )
2009-05-29 01:17:26 +04:00
level + = ibmvfc_retry_tgt_init ( tgt , ibmvfc_tgt_send_plogi ) ;
2008-10-29 16:46:45 +03:00
else
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_DEL_RPORT ) ;
2009-05-29 01:17:26 +04:00
tgt_log ( tgt , level , " Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X \n " ,
ibmvfc_get_cmd_error ( rsp - > status , rsp - > error ) , rsp - > status , rsp - > error ,
ibmvfc_get_fc_type ( rsp - > fc_type ) , rsp - > fc_type ,
ibmvfc_get_ls_explain ( rsp - > fc_explain ) , rsp - > fc_explain , status ) ;
2008-07-01 22:14:30 +04:00
break ;
} ;
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
ibmvfc_free_event ( evt ) ;
wake_up ( & vhost - > work_wait_q ) ;
}
/**
* ibmvfc_tgt_send_plogi - Send PLOGI to the specified target
* @ tgt : ibmvfc target struct
*
* */
static void ibmvfc_tgt_send_plogi ( struct ibmvfc_target * tgt )
{
struct ibmvfc_port_login * plogi ;
struct ibmvfc_host * vhost = tgt - > vhost ;
struct ibmvfc_event * evt ;
if ( vhost - > discovery_threads > = disc_threads )
return ;
kref_get ( & tgt - > kref ) ;
2009-06-18 18:06:55 +04:00
tgt - > logo_rcvd = 0 ;
2008-07-01 22:14:30 +04:00
evt = ibmvfc_get_event ( vhost ) ;
vhost - > discovery_threads + + ;
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_INIT_WAIT ) ;
ibmvfc_init_event ( evt , ibmvfc_tgt_plogi_done , IBMVFC_MAD_FORMAT ) ;
evt - > tgt = tgt ;
plogi = & evt - > iu . plogi ;
memset ( plogi , 0 , sizeof ( * plogi ) ) ;
plogi - > common . version = 1 ;
plogi - > common . opcode = IBMVFC_PORT_LOGIN ;
plogi - > common . length = sizeof ( * plogi ) ;
plogi - > scsi_id = tgt - > scsi_id ;
if ( ibmvfc_send_event ( evt , vhost , default_timeout ) ) {
vhost - > discovery_threads - - ;
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_NONE ) ;
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
} else
tgt_dbg ( tgt , " Sent port login \n " ) ;
}
/**
* ibmvfc_tgt_implicit_logout_done - Completion handler for Implicit Logout MAD
* @ evt : ibmvfc event struct
*
* */
static void ibmvfc_tgt_implicit_logout_done ( struct ibmvfc_event * evt )
{
struct ibmvfc_target * tgt = evt - > tgt ;
struct ibmvfc_host * vhost = evt - > vhost ;
struct ibmvfc_implicit_logout * rsp = & evt - > xfer_iu - > implicit_logout ;
u32 status = rsp - > common . status ;
vhost - > discovery_threads - - ;
ibmvfc_free_event ( evt ) ;
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_NONE ) ;
switch ( status ) {
case IBMVFC_MAD_SUCCESS :
tgt_dbg ( tgt , " Implicit Logout succeeded \n " ) ;
break ;
case IBMVFC_MAD_DRIVER_FAILED :
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
wake_up ( & vhost - > work_wait_q ) ;
return ;
case IBMVFC_MAD_FAILED :
default :
tgt_err ( tgt , " Implicit Logout failed: rc=0x%02X \n " , status ) ;
break ;
} ;
if ( vhost - > action = = IBMVFC_HOST_ACTION_TGT_INIT )
ibmvfc_init_tgt ( tgt , ibmvfc_tgt_send_plogi ) ;
else if ( vhost - > action = = IBMVFC_HOST_ACTION_QUERY_TGTS & &
tgt - > scsi_id ! = tgt - > new_scsi_id )
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_DEL_RPORT ) ;
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
wake_up ( & vhost - > work_wait_q ) ;
}
/**
* ibmvfc_tgt_implicit_logout - Initiate an Implicit Logout for specified target
* @ tgt : ibmvfc target struct
*
* */
static void ibmvfc_tgt_implicit_logout ( struct ibmvfc_target * tgt )
{
struct ibmvfc_implicit_logout * mad ;
struct ibmvfc_host * vhost = tgt - > vhost ;
struct ibmvfc_event * evt ;
if ( vhost - > discovery_threads > = disc_threads )
return ;
kref_get ( & tgt - > kref ) ;
evt = ibmvfc_get_event ( vhost ) ;
vhost - > discovery_threads + + ;
ibmvfc_init_event ( evt , ibmvfc_tgt_implicit_logout_done , IBMVFC_MAD_FORMAT ) ;
evt - > tgt = tgt ;
mad = & evt - > iu . implicit_logout ;
memset ( mad , 0 , sizeof ( * mad ) ) ;
mad - > common . version = 1 ;
mad - > common . opcode = IBMVFC_IMPLICIT_LOGOUT ;
mad - > common . length = sizeof ( * mad ) ;
mad - > old_scsi_id = tgt - > scsi_id ;
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_INIT_WAIT ) ;
if ( ibmvfc_send_event ( evt , vhost , default_timeout ) ) {
vhost - > discovery_threads - - ;
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_NONE ) ;
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
} else
tgt_dbg ( tgt , " Sent Implicit Logout \n " ) ;
}
2008-07-22 17:31:47 +04:00
/**
* ibmvfc_adisc_needs_plogi - Does device need PLOGI ?
* @ mad : ibmvfc passthru mad struct
* @ tgt : ibmvfc target struct
*
* Returns :
* 1 if PLOGI needed / 0 if PLOGI not needed
* */
static int ibmvfc_adisc_needs_plogi ( struct ibmvfc_passthru_mad * mad ,
struct ibmvfc_target * tgt )
{
if ( memcmp ( & mad - > fc_iu . response [ 2 ] , & tgt - > ids . port_name ,
sizeof ( tgt - > ids . port_name ) ) )
return 1 ;
if ( memcmp ( & mad - > fc_iu . response [ 4 ] , & tgt - > ids . node_name ,
sizeof ( tgt - > ids . node_name ) ) )
return 1 ;
if ( mad - > fc_iu . response [ 6 ] ! = tgt - > scsi_id )
return 1 ;
return 0 ;
}
/**
* ibmvfc_tgt_adisc_done - Completion handler for ADISC
* @ evt : ibmvfc event struct
*
* */
static void ibmvfc_tgt_adisc_done ( struct ibmvfc_event * evt )
{
struct ibmvfc_target * tgt = evt - > tgt ;
struct ibmvfc_host * vhost = evt - > vhost ;
struct ibmvfc_passthru_mad * mad = & evt - > xfer_iu - > passthru ;
u32 status = mad - > common . status ;
u8 fc_reason , fc_explain ;
vhost - > discovery_threads - - ;
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_NONE ) ;
2009-03-20 23:44:39 +03:00
del_timer ( & tgt - > timer ) ;
2008-07-22 17:31:47 +04:00
switch ( status ) {
case IBMVFC_MAD_SUCCESS :
tgt_dbg ( tgt , " ADISC succeeded \n " ) ;
if ( ibmvfc_adisc_needs_plogi ( mad , tgt ) )
2009-05-29 01:17:32 +04:00
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_DEL_RPORT ) ;
2008-07-22 17:31:47 +04:00
break ;
case IBMVFC_MAD_DRIVER_FAILED :
break ;
case IBMVFC_MAD_FAILED :
default :
2009-05-29 01:17:32 +04:00
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_DEL_RPORT ) ;
2008-07-22 17:31:47 +04:00
fc_reason = ( mad - > fc_iu . response [ 1 ] & 0x00ff0000 ) > > 16 ;
fc_explain = ( mad - > fc_iu . response [ 1 ] & 0x0000ff00 ) > > 8 ;
tgt_info ( tgt , " ADISC failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X \n " ,
ibmvfc_get_cmd_error ( mad - > iu . status , mad - > iu . error ) ,
mad - > iu . status , mad - > iu . error ,
ibmvfc_get_fc_type ( fc_reason ) , fc_reason ,
ibmvfc_get_ls_explain ( fc_explain ) , fc_explain , status ) ;
break ;
} ;
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
ibmvfc_free_event ( evt ) ;
wake_up ( & vhost - > work_wait_q ) ;
}
/**
* ibmvfc_init_passthru - Initialize an event struct for FC passthru
* @ evt : ibmvfc event struct
*
* */
static void ibmvfc_init_passthru ( struct ibmvfc_event * evt )
{
struct ibmvfc_passthru_mad * mad = & evt - > iu . passthru ;
memset ( mad , 0 , sizeof ( * mad ) ) ;
mad - > common . version = 1 ;
mad - > common . opcode = IBMVFC_PASSTHRU ;
mad - > common . length = sizeof ( * mad ) - sizeof ( mad - > fc_iu ) - sizeof ( mad - > iu ) ;
mad - > cmd_ioba . va = ( u64 ) evt - > crq . ioba +
offsetof ( struct ibmvfc_passthru_mad , iu ) ;
mad - > cmd_ioba . len = sizeof ( mad - > iu ) ;
mad - > iu . cmd_len = sizeof ( mad - > fc_iu . payload ) ;
mad - > iu . rsp_len = sizeof ( mad - > fc_iu . response ) ;
mad - > iu . cmd . va = ( u64 ) evt - > crq . ioba +
offsetof ( struct ibmvfc_passthru_mad , fc_iu ) +
offsetof ( struct ibmvfc_passthru_fc_iu , payload ) ;
mad - > iu . cmd . len = sizeof ( mad - > fc_iu . payload ) ;
mad - > iu . rsp . va = ( u64 ) evt - > crq . ioba +
offsetof ( struct ibmvfc_passthru_mad , fc_iu ) +
offsetof ( struct ibmvfc_passthru_fc_iu , response ) ;
mad - > iu . rsp . len = sizeof ( mad - > fc_iu . response ) ;
}
2009-03-20 23:44:39 +03:00
/**
* ibmvfc_tgt_adisc_cancel_done - Completion handler when cancelling an ADISC
* @ evt : ibmvfc event struct
*
* Just cleanup this event struct . Everything else is handled by
* the ADISC completion handler . If the ADISC never actually comes
* back , we still have the timer running on the ADISC event struct
* which will fire and cause the CRQ to get reset .
*
* */
static void ibmvfc_tgt_adisc_cancel_done ( struct ibmvfc_event * evt )
{
struct ibmvfc_host * vhost = evt - > vhost ;
struct ibmvfc_target * tgt = evt - > tgt ;
tgt_dbg ( tgt , " ADISC cancel complete \n " ) ;
vhost - > abort_threads - - ;
ibmvfc_free_event ( evt ) ;
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
wake_up ( & vhost - > work_wait_q ) ;
}
/**
* ibmvfc_adisc_timeout - Handle an ADISC timeout
* @ tgt : ibmvfc target struct
*
* If an ADISC times out , send a cancel . If the cancel times
* out , reset the CRQ . When the ADISC comes back as cancelled ,
* log back into the target .
* */
static void ibmvfc_adisc_timeout ( struct ibmvfc_target * tgt )
{
struct ibmvfc_host * vhost = tgt - > vhost ;
struct ibmvfc_event * evt ;
struct ibmvfc_tmf * tmf ;
unsigned long flags ;
int rc ;
tgt_dbg ( tgt , " ADISC timeout \n " ) ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
if ( vhost - > abort_threads > = disc_threads | |
tgt - > action ! = IBMVFC_TGT_ACTION_INIT_WAIT | |
vhost - > state ! = IBMVFC_INITIALIZING | |
vhost - > action ! = IBMVFC_HOST_ACTION_QUERY_TGTS ) {
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
return ;
}
vhost - > abort_threads + + ;
kref_get ( & tgt - > kref ) ;
evt = ibmvfc_get_event ( vhost ) ;
ibmvfc_init_event ( evt , ibmvfc_tgt_adisc_cancel_done , IBMVFC_MAD_FORMAT ) ;
evt - > tgt = tgt ;
tmf = & evt - > iu . tmf ;
memset ( tmf , 0 , sizeof ( * tmf ) ) ;
tmf - > common . version = 1 ;
tmf - > common . opcode = IBMVFC_TMF_MAD ;
tmf - > common . length = sizeof ( * tmf ) ;
tmf - > scsi_id = tgt - > scsi_id ;
tmf - > cancel_key = tgt - > cancel_key ;
rc = ibmvfc_send_event ( evt , vhost , default_timeout ) ;
if ( rc ) {
tgt_err ( tgt , " Failed to send cancel event for ADISC. rc=%d \n " , rc ) ;
vhost - > abort_threads - - ;
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
__ibmvfc_reset_host ( vhost ) ;
} else
tgt_dbg ( tgt , " Attempting to cancel ADISC \n " ) ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
}
2008-07-22 17:31:47 +04:00
/**
* ibmvfc_tgt_adisc - Initiate an ADISC for specified target
* @ tgt : ibmvfc target struct
*
2009-03-20 23:44:39 +03:00
* When sending an ADISC we end up with two timers running . The
* first timer is the timer in the ibmvfc target struct . If this
* fires , we send a cancel to the target . The second timer is the
* timer on the ibmvfc event for the ADISC , which is longer . If that
* fires , it means the ADISC timed out and our attempt to cancel it
* also failed , so we need to reset the CRQ .
2008-07-22 17:31:47 +04:00
* */
static void ibmvfc_tgt_adisc ( struct ibmvfc_target * tgt )
{
struct ibmvfc_passthru_mad * mad ;
struct ibmvfc_host * vhost = tgt - > vhost ;
struct ibmvfc_event * evt ;
if ( vhost - > discovery_threads > = disc_threads )
return ;
kref_get ( & tgt - > kref ) ;
evt = ibmvfc_get_event ( vhost ) ;
vhost - > discovery_threads + + ;
ibmvfc_init_event ( evt , ibmvfc_tgt_adisc_done , IBMVFC_MAD_FORMAT ) ;
evt - > tgt = tgt ;
ibmvfc_init_passthru ( evt ) ;
mad = & evt - > iu . passthru ;
mad - > iu . flags = IBMVFC_FC_ELS ;
mad - > iu . scsi_id = tgt - > scsi_id ;
2009-03-20 23:44:39 +03:00
mad - > iu . cancel_key = tgt - > cancel_key ;
2008-07-22 17:31:47 +04:00
mad - > fc_iu . payload [ 0 ] = IBMVFC_ADISC ;
memcpy ( & mad - > fc_iu . payload [ 2 ] , & vhost - > login_buf - > resp . port_name ,
sizeof ( vhost - > login_buf - > resp . port_name ) ) ;
memcpy ( & mad - > fc_iu . payload [ 4 ] , & vhost - > login_buf - > resp . node_name ,
sizeof ( vhost - > login_buf - > resp . node_name ) ) ;
mad - > fc_iu . payload [ 6 ] = vhost - > login_buf - > resp . scsi_id & 0x00ffffff ;
2009-03-20 23:44:39 +03:00
if ( timer_pending ( & tgt - > timer ) )
mod_timer ( & tgt - > timer , jiffies + ( IBMVFC_ADISC_TIMEOUT * HZ ) ) ;
else {
tgt - > timer . data = ( unsigned long ) tgt ;
tgt - > timer . expires = jiffies + ( IBMVFC_ADISC_TIMEOUT * HZ ) ;
tgt - > timer . function = ( void ( * ) ( unsigned long ) ) ibmvfc_adisc_timeout ;
add_timer ( & tgt - > timer ) ;
}
2008-07-22 17:31:47 +04:00
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_INIT_WAIT ) ;
2009-03-20 23:44:39 +03:00
if ( ibmvfc_send_event ( evt , vhost , IBMVFC_ADISC_PLUS_CANCEL_TIMEOUT ) ) {
2008-07-22 17:31:47 +04:00
vhost - > discovery_threads - - ;
2009-03-20 23:44:39 +03:00
del_timer ( & tgt - > timer ) ;
2008-07-22 17:31:47 +04:00
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_NONE ) ;
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
} else
tgt_dbg ( tgt , " Sent ADISC \n " ) ;
}
2008-07-01 22:14:30 +04:00
/**
* ibmvfc_tgt_query_target_done - Completion handler for Query Target MAD
* @ evt : ibmvfc event struct
*
* */
static void ibmvfc_tgt_query_target_done ( struct ibmvfc_event * evt )
{
struct ibmvfc_target * tgt = evt - > tgt ;
struct ibmvfc_host * vhost = evt - > vhost ;
struct ibmvfc_query_tgt * rsp = & evt - > xfer_iu - > query_tgt ;
u32 status = rsp - > common . status ;
2009-05-29 01:17:26 +04:00
int level = IBMVFC_DEFAULT_LOG_LEVEL ;
2008-07-01 22:14:30 +04:00
vhost - > discovery_threads - - ;
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_NONE ) ;
switch ( status ) {
case IBMVFC_MAD_SUCCESS :
tgt_dbg ( tgt , " Query Target succeeded \n " ) ;
tgt - > new_scsi_id = rsp - > scsi_id ;
if ( rsp - > scsi_id ! = tgt - > scsi_id )
ibmvfc_init_tgt ( tgt , ibmvfc_tgt_implicit_logout ) ;
2008-07-22 17:31:47 +04:00
else
ibmvfc_init_tgt ( tgt , ibmvfc_tgt_adisc ) ;
2008-07-01 22:14:30 +04:00
break ;
case IBMVFC_MAD_DRIVER_FAILED :
break ;
case IBMVFC_MAD_CRQ_ERROR :
ibmvfc_retry_tgt_init ( tgt , ibmvfc_tgt_query_target ) ;
break ;
case IBMVFC_MAD_FAILED :
default :
if ( ( rsp - > status & IBMVFC_FABRIC_MAPPED ) = = IBMVFC_FABRIC_MAPPED & &
rsp - > error = = IBMVFC_UNABLE_TO_PERFORM_REQ & &
rsp - > fc_explain = = IBMVFC_PORT_NAME_NOT_REG )
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_DEL_RPORT ) ;
else if ( ibmvfc_retry_cmd ( rsp - > status , rsp - > error ) )
2009-05-29 01:17:26 +04:00
level + = ibmvfc_retry_tgt_init ( tgt , ibmvfc_tgt_query_target ) ;
2008-10-29 16:46:45 +03:00
else
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_DEL_RPORT ) ;
2009-05-29 01:17:26 +04:00
tgt_log ( tgt , level , " Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X \n " ,
ibmvfc_get_cmd_error ( rsp - > status , rsp - > error ) , rsp - > status , rsp - > error ,
ibmvfc_get_fc_type ( rsp - > fc_type ) , rsp - > fc_type ,
ibmvfc_get_gs_explain ( rsp - > fc_explain ) , rsp - > fc_explain , status ) ;
2008-07-01 22:14:30 +04:00
break ;
} ;
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
ibmvfc_free_event ( evt ) ;
wake_up ( & vhost - > work_wait_q ) ;
}
/**
* ibmvfc_tgt_query_target - Initiate a Query Target for specified target
* @ tgt : ibmvfc target struct
*
* */
static void ibmvfc_tgt_query_target ( struct ibmvfc_target * tgt )
{
struct ibmvfc_query_tgt * query_tgt ;
struct ibmvfc_host * vhost = tgt - > vhost ;
struct ibmvfc_event * evt ;
if ( vhost - > discovery_threads > = disc_threads )
return ;
kref_get ( & tgt - > kref ) ;
evt = ibmvfc_get_event ( vhost ) ;
vhost - > discovery_threads + + ;
evt - > tgt = tgt ;
ibmvfc_init_event ( evt , ibmvfc_tgt_query_target_done , IBMVFC_MAD_FORMAT ) ;
query_tgt = & evt - > iu . query_tgt ;
memset ( query_tgt , 0 , sizeof ( * query_tgt ) ) ;
query_tgt - > common . version = 1 ;
query_tgt - > common . opcode = IBMVFC_QUERY_TARGET ;
query_tgt - > common . length = sizeof ( * query_tgt ) ;
query_tgt - > wwpn = tgt - > ids . port_name ;
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_INIT_WAIT ) ;
if ( ibmvfc_send_event ( evt , vhost , default_timeout ) ) {
vhost - > discovery_threads - - ;
ibmvfc_set_tgt_action ( tgt , IBMVFC_TGT_ACTION_NONE ) ;
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
} else
tgt_dbg ( tgt , " Sent Query Target \n " ) ;
}
/**
* ibmvfc_alloc_target - Allocate and initialize an ibmvfc target
* @ vhost : ibmvfc host struct
* @ scsi_id : SCSI ID to allocate target for
*
* Returns :
* 0 on success / other on failure
* */
static int ibmvfc_alloc_target ( struct ibmvfc_host * vhost , u64 scsi_id )
{
struct ibmvfc_target * tgt ;
unsigned long flags ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
list_for_each_entry ( tgt , & vhost - > targets , queue ) {
if ( tgt - > scsi_id = = scsi_id ) {
if ( tgt - > need_login )
ibmvfc_init_tgt ( tgt , ibmvfc_tgt_implicit_logout ) ;
goto unlock_out ;
}
}
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
2009-05-29 01:17:24 +04:00
tgt = mempool_alloc ( vhost - > tgt_pool , GFP_NOIO ) ;
2008-07-01 22:14:30 +04:00
if ( ! tgt ) {
2009-01-06 17:59:00 +03:00
dev_err ( vhost - > dev , " Target allocation failure for scsi id %08llx \n " ,
2008-07-01 22:14:30 +04:00
scsi_id ) ;
return - ENOMEM ;
}
2009-02-05 01:13:12 +03:00
memset ( tgt , 0 , sizeof ( * tgt ) ) ;
2008-07-01 22:14:30 +04:00
tgt - > scsi_id = scsi_id ;
tgt - > new_scsi_id = scsi_id ;
tgt - > vhost = vhost ;
tgt - > need_login = 1 ;
2009-03-20 23:44:39 +03:00
tgt - > cancel_key = vhost - > task_set + + ;
init_timer ( & tgt - > timer ) ;
2008-07-01 22:14:30 +04:00
kref_init ( & tgt - > kref ) ;
ibmvfc_init_tgt ( tgt , ibmvfc_tgt_implicit_logout ) ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
list_add_tail ( & tgt - > queue , & vhost - > targets ) ;
unlock_out :
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
return 0 ;
}
/**
* ibmvfc_alloc_targets - Allocate and initialize ibmvfc targets
* @ vhost : ibmvfc host struct
*
* Returns :
* 0 on success / other on failure
* */
static int ibmvfc_alloc_targets ( struct ibmvfc_host * vhost )
{
int i , rc ;
for ( i = 0 , rc = 0 ; ! rc & & i < vhost - > num_targets ; i + + )
rc = ibmvfc_alloc_target ( vhost ,
vhost - > disc_buf - > scsi_id [ i ] & IBMVFC_DISC_TGT_SCSI_ID_MASK ) ;
return rc ;
}
/**
* ibmvfc_discover_targets_done - Completion handler for discover targets MAD
* @ evt : ibmvfc event struct
*
* */
static void ibmvfc_discover_targets_done ( struct ibmvfc_event * evt )
{
struct ibmvfc_host * vhost = evt - > vhost ;
struct ibmvfc_discover_targets * rsp = & evt - > xfer_iu - > discover_targets ;
u32 mad_status = rsp - > common . status ;
2009-05-29 01:17:26 +04:00
int level = IBMVFC_DEFAULT_LOG_LEVEL ;
2008-07-01 22:14:30 +04:00
switch ( mad_status ) {
case IBMVFC_MAD_SUCCESS :
ibmvfc_dbg ( vhost , " Discover Targets succeeded \n " ) ;
vhost - > num_targets = rsp - > num_written ;
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_ALLOC_TGTS ) ;
break ;
case IBMVFC_MAD_FAILED :
2009-05-29 01:17:26 +04:00
level + = ibmvfc_retry_host_init ( vhost ) ;
ibmvfc_log ( vhost , level , " Discover Targets failed: %s (%x:%x) \n " ,
ibmvfc_get_cmd_error ( rsp - > status , rsp - > error ) , rsp - > status , rsp - > error ) ;
2008-07-01 22:14:30 +04:00
break ;
case IBMVFC_MAD_DRIVER_FAILED :
break ;
default :
dev_err ( vhost - > dev , " Invalid Discover Targets response: 0x%x \n " , mad_status ) ;
ibmvfc_link_down ( vhost , IBMVFC_LINK_DEAD ) ;
break ;
}
ibmvfc_free_event ( evt ) ;
wake_up ( & vhost - > work_wait_q ) ;
}
/**
* ibmvfc_discover_targets - Send Discover Targets MAD
* @ vhost : ibmvfc host struct
*
* */
static void ibmvfc_discover_targets ( struct ibmvfc_host * vhost )
{
struct ibmvfc_discover_targets * mad ;
struct ibmvfc_event * evt = ibmvfc_get_event ( vhost ) ;
ibmvfc_init_event ( evt , ibmvfc_discover_targets_done , IBMVFC_MAD_FORMAT ) ;
mad = & evt - > iu . discover_targets ;
memset ( mad , 0 , sizeof ( * mad ) ) ;
mad - > common . version = 1 ;
mad - > common . opcode = IBMVFC_DISC_TARGETS ;
mad - > common . length = sizeof ( * mad ) ;
mad - > bufflen = vhost - > disc_buf_sz ;
mad - > buffer . va = vhost - > disc_buf_dma ;
mad - > buffer . len = vhost - > disc_buf_sz ;
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_INIT_WAIT ) ;
if ( ! ibmvfc_send_event ( evt , vhost , default_timeout ) )
ibmvfc_dbg ( vhost , " Sent discover targets \n " ) ;
else
ibmvfc_link_down ( vhost , IBMVFC_LINK_DEAD ) ;
}
/**
* ibmvfc_npiv_login_done - Completion handler for NPIV Login
* @ evt : ibmvfc event struct
*
* */
static void ibmvfc_npiv_login_done ( struct ibmvfc_event * evt )
{
struct ibmvfc_host * vhost = evt - > vhost ;
u32 mad_status = evt - > xfer_iu - > npiv_login . common . status ;
struct ibmvfc_npiv_login_resp * rsp = & vhost - > login_buf - > resp ;
unsigned int npiv_max_sectors ;
2009-05-29 01:17:26 +04:00
int level = IBMVFC_DEFAULT_LOG_LEVEL ;
2008-07-01 22:14:30 +04:00
switch ( mad_status ) {
case IBMVFC_MAD_SUCCESS :
ibmvfc_free_event ( evt ) ;
break ;
case IBMVFC_MAD_FAILED :
if ( ibmvfc_retry_cmd ( rsp - > status , rsp - > error ) )
2009-05-29 01:17:26 +04:00
level + = ibmvfc_retry_host_init ( vhost ) ;
2008-07-01 22:14:30 +04:00
else
ibmvfc_link_down ( vhost , IBMVFC_LINK_DEAD ) ;
2009-05-29 01:17:26 +04:00
ibmvfc_log ( vhost , level , " NPIV Login failed: %s (%x:%x) \n " ,
ibmvfc_get_cmd_error ( rsp - > status , rsp - > error ) , rsp - > status , rsp - > error ) ;
2008-07-01 22:14:30 +04:00
ibmvfc_free_event ( evt ) ;
return ;
case IBMVFC_MAD_CRQ_ERROR :
ibmvfc_retry_host_init ( vhost ) ;
case IBMVFC_MAD_DRIVER_FAILED :
ibmvfc_free_event ( evt ) ;
return ;
default :
dev_err ( vhost - > dev , " Invalid NPIV Login response: 0x%x \n " , mad_status ) ;
ibmvfc_link_down ( vhost , IBMVFC_LINK_DEAD ) ;
ibmvfc_free_event ( evt ) ;
return ;
}
vhost - > client_migrated = 0 ;
if ( ! ( rsp - > flags & IBMVFC_NATIVE_FC ) ) {
dev_err ( vhost - > dev , " Virtual adapter does not support FC. %x \n " ,
rsp - > flags ) ;
ibmvfc_link_down ( vhost , IBMVFC_LINK_DEAD ) ;
wake_up ( & vhost - > work_wait_q ) ;
return ;
}
if ( rsp - > max_cmds < = IBMVFC_NUM_INTERNAL_REQ ) {
dev_err ( vhost - > dev , " Virtual adapter supported queue depth too small: %d \n " ,
rsp - > max_cmds ) ;
ibmvfc_link_down ( vhost , IBMVFC_LINK_DEAD ) ;
wake_up ( & vhost - > work_wait_q ) ;
return ;
}
2009-05-29 01:17:29 +04:00
vhost - > logged_in = 1 ;
2008-07-01 22:14:30 +04:00
npiv_max_sectors = min ( ( uint ) ( rsp - > max_dma_len > > 9 ) , IBMVFC_MAX_SECTORS ) ;
dev_info ( vhost - > dev , " Host partition: %s, device: %s %s %s max sectors %u \n " ,
rsp - > partition_name , rsp - > device_name , rsp - > port_loc_code ,
rsp - > drc_name , npiv_max_sectors ) ;
fc_host_fabric_name ( vhost - > host ) = rsp - > node_name ;
fc_host_node_name ( vhost - > host ) = rsp - > node_name ;
fc_host_port_name ( vhost - > host ) = rsp - > port_name ;
fc_host_port_id ( vhost - > host ) = rsp - > scsi_id ;
fc_host_port_type ( vhost - > host ) = FC_PORTTYPE_NPIV ;
fc_host_supported_classes ( vhost - > host ) = 0 ;
if ( rsp - > service_parms . class1_parms [ 0 ] & 0x80000000 )
fc_host_supported_classes ( vhost - > host ) | = FC_COS_CLASS1 ;
if ( rsp - > service_parms . class2_parms [ 0 ] & 0x80000000 )
fc_host_supported_classes ( vhost - > host ) | = FC_COS_CLASS2 ;
if ( rsp - > service_parms . class3_parms [ 0 ] & 0x80000000 )
fc_host_supported_classes ( vhost - > host ) | = FC_COS_CLASS3 ;
fc_host_maxframe_size ( vhost - > host ) =
rsp - > service_parms . common . bb_rcv_sz & 0x0fff ;
vhost - > host - > can_queue = rsp - > max_cmds - IBMVFC_NUM_INTERNAL_REQ ;
vhost - > host - > max_sectors = npiv_max_sectors ;
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_QUERY ) ;
wake_up ( & vhost - > work_wait_q ) ;
}
/**
* ibmvfc_npiv_login - Sends NPIV login
* @ vhost : ibmvfc host struct
*
* */
static void ibmvfc_npiv_login ( struct ibmvfc_host * vhost )
{
struct ibmvfc_npiv_login_mad * mad ;
struct ibmvfc_event * evt = ibmvfc_get_event ( vhost ) ;
ibmvfc_gather_partition_info ( vhost ) ;
ibmvfc_set_login_info ( vhost ) ;
ibmvfc_init_event ( evt , ibmvfc_npiv_login_done , IBMVFC_MAD_FORMAT ) ;
memcpy ( vhost - > login_buf , & vhost - > login_info , sizeof ( vhost - > login_info ) ) ;
mad = & evt - > iu . npiv_login ;
memset ( mad , 0 , sizeof ( struct ibmvfc_npiv_login_mad ) ) ;
mad - > common . version = 1 ;
mad - > common . opcode = IBMVFC_NPIV_LOGIN ;
mad - > common . length = sizeof ( struct ibmvfc_npiv_login_mad ) ;
mad - > buffer . va = vhost - > login_buf_dma ;
mad - > buffer . len = sizeof ( * vhost - > login_buf ) ;
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_INIT_WAIT ) ;
if ( ! ibmvfc_send_event ( evt , vhost , default_timeout ) )
ibmvfc_dbg ( vhost , " Sent NPIV login \n " ) ;
else
ibmvfc_link_down ( vhost , IBMVFC_LINK_DEAD ) ;
} ;
2009-05-29 01:17:29 +04:00
/**
* ibmvfc_npiv_logout_done - Completion handler for NPIV Logout
* @ vhost : ibmvfc host struct
*
* */
static void ibmvfc_npiv_logout_done ( struct ibmvfc_event * evt )
{
struct ibmvfc_host * vhost = evt - > vhost ;
u32 mad_status = evt - > xfer_iu - > npiv_logout . common . status ;
ibmvfc_free_event ( evt ) ;
switch ( mad_status ) {
case IBMVFC_MAD_SUCCESS :
if ( list_empty ( & vhost - > sent ) & &
vhost - > action = = IBMVFC_HOST_ACTION_LOGO_WAIT ) {
2009-10-20 00:07:49 +04:00
ibmvfc_init_host ( vhost ) ;
2009-05-29 01:17:29 +04:00
return ;
}
break ;
case IBMVFC_MAD_FAILED :
case IBMVFC_MAD_NOT_SUPPORTED :
case IBMVFC_MAD_CRQ_ERROR :
case IBMVFC_MAD_DRIVER_FAILED :
default :
ibmvfc_dbg ( vhost , " NPIV Logout failed. 0x%X \n " , mad_status ) ;
break ;
}
ibmvfc_hard_reset_host ( vhost ) ;
}
/**
* ibmvfc_npiv_logout - Issue an NPIV Logout
* @ vhost : ibmvfc host struct
*
* */
static void ibmvfc_npiv_logout ( struct ibmvfc_host * vhost )
{
struct ibmvfc_npiv_logout_mad * mad ;
struct ibmvfc_event * evt ;
evt = ibmvfc_get_event ( vhost ) ;
ibmvfc_init_event ( evt , ibmvfc_npiv_logout_done , IBMVFC_MAD_FORMAT ) ;
mad = & evt - > iu . npiv_logout ;
memset ( mad , 0 , sizeof ( * mad ) ) ;
mad - > common . version = 1 ;
mad - > common . opcode = IBMVFC_NPIV_LOGOUT ;
mad - > common . length = sizeof ( struct ibmvfc_npiv_logout_mad ) ;
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_LOGO_WAIT ) ;
if ( ! ibmvfc_send_event ( evt , vhost , default_timeout ) )
ibmvfc_dbg ( vhost , " Sent NPIV logout \n " ) ;
else
ibmvfc_link_down ( vhost , IBMVFC_LINK_DEAD ) ;
}
2008-07-01 22:14:30 +04:00
/**
* ibmvfc_dev_init_to_do - Is there target initialization work to do ?
* @ vhost : ibmvfc host struct
*
* Returns :
* 1 if work to do / 0 if not
* */
static int ibmvfc_dev_init_to_do ( struct ibmvfc_host * vhost )
{
struct ibmvfc_target * tgt ;
list_for_each_entry ( tgt , & vhost - > targets , queue ) {
if ( tgt - > action = = IBMVFC_TGT_ACTION_INIT | |
tgt - > action = = IBMVFC_TGT_ACTION_INIT_WAIT )
return 1 ;
}
return 0 ;
}
/**
* __ibmvfc_work_to_do - Is there task level work to do ? ( no locking )
* @ vhost : ibmvfc host struct
*
* Returns :
* 1 if work to do / 0 if not
* */
static int __ibmvfc_work_to_do ( struct ibmvfc_host * vhost )
{
struct ibmvfc_target * tgt ;
if ( kthread_should_stop ( ) )
return 1 ;
switch ( vhost - > action ) {
case IBMVFC_HOST_ACTION_NONE :
case IBMVFC_HOST_ACTION_INIT_WAIT :
2009-05-29 01:17:29 +04:00
case IBMVFC_HOST_ACTION_LOGO_WAIT :
2008-07-01 22:14:30 +04:00
return 0 ;
case IBMVFC_HOST_ACTION_TGT_INIT :
case IBMVFC_HOST_ACTION_QUERY_TGTS :
if ( vhost - > discovery_threads = = disc_threads )
return 0 ;
list_for_each_entry ( tgt , & vhost - > targets , queue )
if ( tgt - > action = = IBMVFC_TGT_ACTION_INIT )
return 1 ;
list_for_each_entry ( tgt , & vhost - > targets , queue )
if ( tgt - > action = = IBMVFC_TGT_ACTION_INIT_WAIT )
return 0 ;
return 1 ;
2009-05-29 01:17:29 +04:00
case IBMVFC_HOST_ACTION_LOGO :
2008-07-01 22:14:30 +04:00
case IBMVFC_HOST_ACTION_INIT :
case IBMVFC_HOST_ACTION_ALLOC_TGTS :
case IBMVFC_HOST_ACTION_TGT_DEL :
2008-10-29 16:46:45 +03:00
case IBMVFC_HOST_ACTION_TGT_DEL_FAILED :
2008-07-01 22:14:30 +04:00
case IBMVFC_HOST_ACTION_QUERY :
default :
break ;
} ;
return 1 ;
}
/**
* ibmvfc_work_to_do - Is there task level work to do ?
* @ vhost : ibmvfc host struct
*
* Returns :
* 1 if work to do / 0 if not
* */
static int ibmvfc_work_to_do ( struct ibmvfc_host * vhost )
{
unsigned long flags ;
int rc ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
rc = __ibmvfc_work_to_do ( vhost ) ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
return rc ;
}
/**
* ibmvfc_log_ae - Log async events if necessary
* @ vhost : ibmvfc host struct
* @ events : events to log
*
* */
static void ibmvfc_log_ae ( struct ibmvfc_host * vhost , int events )
{
if ( events & IBMVFC_AE_RSCN )
fc_host_post_event ( vhost - > host , fc_get_event_number ( ) , FCH_EVT_RSCN , 0 ) ;
if ( ( events & IBMVFC_AE_LINKDOWN ) & &
vhost - > state > = IBMVFC_HALTED )
fc_host_post_event ( vhost - > host , fc_get_event_number ( ) , FCH_EVT_LINKDOWN , 0 ) ;
if ( ( events & IBMVFC_AE_LINKUP ) & &
vhost - > state = = IBMVFC_INITIALIZING )
fc_host_post_event ( vhost - > host , fc_get_event_number ( ) , FCH_EVT_LINKUP , 0 ) ;
}
/**
* ibmvfc_tgt_add_rport - Tell the FC transport about a new remote port
* @ tgt : ibmvfc target struct
*
* */
static void ibmvfc_tgt_add_rport ( struct ibmvfc_target * tgt )
{
struct ibmvfc_host * vhost = tgt - > vhost ;
2009-05-29 01:17:28 +04:00
struct fc_rport * rport ;
2008-07-01 22:14:30 +04:00
unsigned long flags ;
2009-05-29 01:17:28 +04:00
tgt_dbg ( tgt , " Adding rport \n " ) ;
rport = fc_remote_port_add ( vhost - > host , 0 , & tgt - > ids ) ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
if ( rport & & tgt - > action = = IBMVFC_TGT_ACTION_DEL_RPORT ) {
tgt_dbg ( tgt , " Deleting rport \n " ) ;
list_del ( & tgt - > queue ) ;
2009-02-05 01:13:12 +03:00
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
2009-05-29 01:17:28 +04:00
fc_remote_port_delete ( rport ) ;
del_timer_sync ( & tgt - > timer ) ;
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
2009-02-05 01:13:12 +03:00
return ;
}
2008-07-01 22:14:30 +04:00
if ( rport ) {
tgt_dbg ( tgt , " rport add succeeded \n " ) ;
2009-05-29 01:17:28 +04:00
tgt - > rport = rport ;
2008-07-01 22:14:30 +04:00
rport - > maxframe_size = tgt - > service_parms . common . bb_rcv_sz & 0x0fff ;
rport - > supported_classes = 0 ;
2008-07-22 17:31:46 +04:00
tgt - > target_id = rport - > scsi_target_id ;
2008-07-01 22:14:30 +04:00
if ( tgt - > service_parms . class1_parms [ 0 ] & 0x80000000 )
rport - > supported_classes | = FC_COS_CLASS1 ;
if ( tgt - > service_parms . class2_parms [ 0 ] & 0x80000000 )
rport - > supported_classes | = FC_COS_CLASS2 ;
if ( tgt - > service_parms . class3_parms [ 0 ] & 0x80000000 )
rport - > supported_classes | = FC_COS_CLASS3 ;
} else
tgt_dbg ( tgt , " rport add failed \n " ) ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
}
/**
* ibmvfc_do_work - Do task level work
* @ vhost : ibmvfc host struct
*
* */
static void ibmvfc_do_work ( struct ibmvfc_host * vhost )
{
struct ibmvfc_target * tgt ;
unsigned long flags ;
struct fc_rport * rport ;
ibmvfc_log_ae ( vhost , vhost - > events_to_log ) ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
vhost - > events_to_log = 0 ;
switch ( vhost - > action ) {
case IBMVFC_HOST_ACTION_NONE :
2009-05-29 01:17:29 +04:00
case IBMVFC_HOST_ACTION_LOGO_WAIT :
2008-07-01 22:14:30 +04:00
case IBMVFC_HOST_ACTION_INIT_WAIT :
break ;
2009-05-29 01:17:29 +04:00
case IBMVFC_HOST_ACTION_LOGO :
vhost - > job_step ( vhost ) ;
break ;
2008-07-01 22:14:30 +04:00
case IBMVFC_HOST_ACTION_INIT :
BUG_ON ( vhost - > state ! = IBMVFC_INITIALIZING ) ;
2008-12-03 20:02:54 +03:00
if ( vhost - > delay_init ) {
vhost - > delay_init = 0 ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
2008-12-18 18:26:51 +03:00
ssleep ( 15 ) ;
2008-12-03 20:02:54 +03:00
return ;
} else
vhost - > job_step ( vhost ) ;
2008-07-01 22:14:30 +04:00
break ;
case IBMVFC_HOST_ACTION_QUERY :
list_for_each_entry ( tgt , & vhost - > targets , queue )
ibmvfc_init_tgt ( tgt , ibmvfc_tgt_query_target ) ;
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_QUERY_TGTS ) ;
break ;
case IBMVFC_HOST_ACTION_QUERY_TGTS :
list_for_each_entry ( tgt , & vhost - > targets , queue ) {
if ( tgt - > action = = IBMVFC_TGT_ACTION_INIT ) {
tgt - > job_step ( tgt ) ;
break ;
}
}
if ( ! ibmvfc_dev_init_to_do ( vhost ) )
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_TGT_DEL ) ;
break ;
case IBMVFC_HOST_ACTION_TGT_DEL :
2008-10-29 16:46:45 +03:00
case IBMVFC_HOST_ACTION_TGT_DEL_FAILED :
2008-07-01 22:14:30 +04:00
list_for_each_entry ( tgt , & vhost - > targets , queue ) {
if ( tgt - > action = = IBMVFC_TGT_ACTION_DEL_RPORT ) {
tgt_dbg ( tgt , " Deleting rport \n " ) ;
rport = tgt - > rport ;
tgt - > rport = NULL ;
list_del ( & tgt - > queue ) ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
if ( rport )
fc_remote_port_delete ( rport ) ;
2009-03-20 23:44:39 +03:00
del_timer_sync ( & tgt - > timer ) ;
2008-07-01 22:14:30 +04:00
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
return ;
}
}
if ( vhost - > state = = IBMVFC_INITIALIZING ) {
2008-10-29 16:46:45 +03:00
if ( vhost - > action = = IBMVFC_HOST_ACTION_TGT_DEL_FAILED ) {
2009-05-29 01:17:28 +04:00
if ( vhost - > reinit ) {
vhost - > reinit = 0 ;
scsi_block_requests ( vhost - > host ) ;
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_QUERY ) ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
} else {
ibmvfc_set_host_state ( vhost , IBMVFC_ACTIVE ) ;
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_NONE ) ;
wake_up ( & vhost - > init_wait_q ) ;
schedule_work ( & vhost - > rport_add_work_q ) ;
vhost - > init_retries = 0 ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
scsi_unblock_requests ( vhost - > host ) ;
}
2008-10-29 16:46:45 +03:00
return ;
} else {
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_INIT ) ;
vhost - > job_step = ibmvfc_discover_targets ;
}
2008-07-01 22:14:30 +04:00
} else {
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_NONE ) ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
scsi_unblock_requests ( vhost - > host ) ;
wake_up ( & vhost - > init_wait_q ) ;
return ;
}
break ;
case IBMVFC_HOST_ACTION_ALLOC_TGTS :
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_TGT_INIT ) ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
ibmvfc_alloc_targets ( vhost ) ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
break ;
case IBMVFC_HOST_ACTION_TGT_INIT :
list_for_each_entry ( tgt , & vhost - > targets , queue ) {
if ( tgt - > action = = IBMVFC_TGT_ACTION_INIT ) {
tgt - > job_step ( tgt ) ;
break ;
}
}
2008-10-29 16:46:45 +03:00
if ( ! ibmvfc_dev_init_to_do ( vhost ) )
ibmvfc_set_host_action ( vhost , IBMVFC_HOST_ACTION_TGT_DEL_FAILED ) ;
2008-07-01 22:14:30 +04:00
break ;
default :
break ;
} ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
}
/**
* ibmvfc_work - Do task level work
* @ data : ibmvfc host struct
*
* Returns :
* zero
* */
static int ibmvfc_work ( void * data )
{
struct ibmvfc_host * vhost = data ;
int rc ;
set_user_nice ( current , - 20 ) ;
while ( 1 ) {
rc = wait_event_interruptible ( vhost - > work_wait_q ,
ibmvfc_work_to_do ( vhost ) ) ;
BUG_ON ( rc ) ;
if ( kthread_should_stop ( ) )
break ;
ibmvfc_do_work ( vhost ) ;
}
ibmvfc_dbg ( vhost , " ibmvfc kthread exiting... \n " ) ;
return 0 ;
}
/**
* ibmvfc_init_crq - Initializes and registers CRQ with hypervisor
* @ vhost : ibmvfc host struct
*
* Allocates a page for messages , maps it for dma , and registers
* the crq with the hypervisor .
*
* Return value :
* zero on success / other on failure
* */
static int ibmvfc_init_crq ( struct ibmvfc_host * vhost )
{
int rc , retrc = - ENOMEM ;
struct device * dev = vhost - > dev ;
struct vio_dev * vdev = to_vio_dev ( dev ) ;
struct ibmvfc_crq_queue * crq = & vhost - > crq ;
ENTER ;
crq - > msgs = ( struct ibmvfc_crq * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! crq - > msgs )
return - ENOMEM ;
crq - > size = PAGE_SIZE / sizeof ( * crq - > msgs ) ;
crq - > msg_token = dma_map_single ( dev , crq - > msgs ,
PAGE_SIZE , DMA_BIDIRECTIONAL ) ;
2008-07-26 06:44:49 +04:00
if ( dma_mapping_error ( dev , crq - > msg_token ) )
2008-07-01 22:14:30 +04:00
goto map_failed ;
retrc = rc = plpar_hcall_norets ( H_REG_CRQ , vdev - > unit_address ,
crq - > msg_token , PAGE_SIZE ) ;
if ( rc = = H_RESOURCE )
/* maybe kexecing and resource is busy. try a reset */
retrc = rc = ibmvfc_reset_crq ( vhost ) ;
if ( rc = = H_CLOSED )
dev_warn ( dev , " Partner adapter not ready \n " ) ;
else if ( rc ) {
dev_warn ( dev , " Error %d opening adapter \n " , rc ) ;
goto reg_crq_failed ;
}
retrc = 0 ;
2009-03-20 23:44:35 +03:00
tasklet_init ( & vhost - > tasklet , ( void * ) ibmvfc_tasklet , ( unsigned long ) vhost ) ;
2008-07-01 22:14:30 +04:00
if ( ( rc = request_irq ( vdev - > irq , ibmvfc_interrupt , 0 , IBMVFC_NAME , vhost ) ) ) {
dev_err ( dev , " Couldn't register irq 0x%x. rc=%d \n " , vdev - > irq , rc ) ;
goto req_irq_failed ;
}
if ( ( rc = vio_enable_interrupts ( vdev ) ) ) {
dev_err ( dev , " Error %d enabling interrupts \n " , rc ) ;
goto req_irq_failed ;
}
crq - > cur = 0 ;
LEAVE ;
return retrc ;
req_irq_failed :
2009-03-20 23:44:35 +03:00
tasklet_kill ( & vhost - > tasklet ) ;
2008-07-01 22:14:30 +04:00
do {
rc = plpar_hcall_norets ( H_FREE_CRQ , vdev - > unit_address ) ;
} while ( rc = = H_BUSY | | H_IS_LONG_BUSY ( rc ) ) ;
reg_crq_failed :
dma_unmap_single ( dev , crq - > msg_token , PAGE_SIZE , DMA_BIDIRECTIONAL ) ;
map_failed :
free_page ( ( unsigned long ) crq - > msgs ) ;
return retrc ;
}
/**
* ibmvfc_free_mem - Free memory for vhost
* @ vhost : ibmvfc host struct
*
* Return value :
* none
* */
static void ibmvfc_free_mem ( struct ibmvfc_host * vhost )
{
struct ibmvfc_async_crq_queue * async_q = & vhost - > async_crq ;
ENTER ;
mempool_destroy ( vhost - > tgt_pool ) ;
kfree ( vhost - > trace ) ;
dma_free_coherent ( vhost - > dev , vhost - > disc_buf_sz , vhost - > disc_buf ,
vhost - > disc_buf_dma ) ;
dma_free_coherent ( vhost - > dev , sizeof ( * vhost - > login_buf ) ,
vhost - > login_buf , vhost - > login_buf_dma ) ;
dma_pool_destroy ( vhost - > sg_pool ) ;
dma_unmap_single ( vhost - > dev , async_q - > msg_token ,
async_q - > size * sizeof ( * async_q - > msgs ) , DMA_BIDIRECTIONAL ) ;
free_page ( ( unsigned long ) async_q - > msgs ) ;
LEAVE ;
}
/**
* ibmvfc_alloc_mem - Allocate memory for vhost
* @ vhost : ibmvfc host struct
*
* Return value :
* 0 on success / non - zero on failure
* */
static int ibmvfc_alloc_mem ( struct ibmvfc_host * vhost )
{
struct ibmvfc_async_crq_queue * async_q = & vhost - > async_crq ;
struct device * dev = vhost - > dev ;
ENTER ;
async_q - > msgs = ( struct ibmvfc_async_crq * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! async_q - > msgs ) {
dev_err ( dev , " Couldn't allocate async queue. \n " ) ;
goto nomem ;
}
async_q - > size = PAGE_SIZE / sizeof ( struct ibmvfc_async_crq ) ;
async_q - > msg_token = dma_map_single ( dev , async_q - > msgs ,
async_q - > size * sizeof ( * async_q - > msgs ) ,
DMA_BIDIRECTIONAL ) ;
2008-07-26 06:44:49 +04:00
if ( dma_mapping_error ( dev , async_q - > msg_token ) ) {
2008-07-01 22:14:30 +04:00
dev_err ( dev , " Failed to map async queue \n " ) ;
goto free_async_crq ;
}
vhost - > sg_pool = dma_pool_create ( IBMVFC_NAME , dev ,
SG_ALL * sizeof ( struct srp_direct_buf ) ,
sizeof ( struct srp_direct_buf ) , 0 ) ;
if ( ! vhost - > sg_pool ) {
dev_err ( dev , " Failed to allocate sg pool \n " ) ;
goto unmap_async_crq ;
}
vhost - > login_buf = dma_alloc_coherent ( dev , sizeof ( * vhost - > login_buf ) ,
& vhost - > login_buf_dma , GFP_KERNEL ) ;
if ( ! vhost - > login_buf ) {
dev_err ( dev , " Couldn't allocate NPIV login buffer \n " ) ;
goto free_sg_pool ;
}
vhost - > disc_buf_sz = sizeof ( vhost - > disc_buf - > scsi_id [ 0 ] ) * max_targets ;
vhost - > disc_buf = dma_alloc_coherent ( dev , vhost - > disc_buf_sz ,
& vhost - > disc_buf_dma , GFP_KERNEL ) ;
if ( ! vhost - > disc_buf ) {
dev_err ( dev , " Couldn't allocate Discover Targets buffer \n " ) ;
goto free_login_buffer ;
}
vhost - > trace = kcalloc ( IBMVFC_NUM_TRACE_ENTRIES ,
sizeof ( struct ibmvfc_trace_entry ) , GFP_KERNEL ) ;
if ( ! vhost - > trace )
goto free_disc_buffer ;
2009-08-04 00:54:47 +04:00
vhost - > tgt_pool = mempool_create_kmalloc_pool ( IBMVFC_TGT_MEMPOOL_SZ ,
2008-07-01 22:14:30 +04:00
sizeof ( struct ibmvfc_target ) ) ;
if ( ! vhost - > tgt_pool ) {
dev_err ( dev , " Couldn't allocate target memory pool \n " ) ;
goto free_trace ;
}
LEAVE ;
return 0 ;
free_trace :
kfree ( vhost - > trace ) ;
free_disc_buffer :
dma_free_coherent ( dev , vhost - > disc_buf_sz , vhost - > disc_buf ,
vhost - > disc_buf_dma ) ;
free_login_buffer :
dma_free_coherent ( dev , sizeof ( * vhost - > login_buf ) ,
vhost - > login_buf , vhost - > login_buf_dma ) ;
free_sg_pool :
dma_pool_destroy ( vhost - > sg_pool ) ;
unmap_async_crq :
dma_unmap_single ( dev , async_q - > msg_token ,
async_q - > size * sizeof ( * async_q - > msgs ) , DMA_BIDIRECTIONAL ) ;
free_async_crq :
free_page ( ( unsigned long ) async_q - > msgs ) ;
nomem :
LEAVE ;
return - ENOMEM ;
}
2009-05-29 01:17:28 +04:00
/**
* ibmvfc_rport_add_thread - Worker thread for rport adds
* @ work : work struct
*
* */
static void ibmvfc_rport_add_thread ( struct work_struct * work )
{
struct ibmvfc_host * vhost = container_of ( work , struct ibmvfc_host ,
rport_add_work_q ) ;
struct ibmvfc_target * tgt ;
struct fc_rport * rport ;
unsigned long flags ;
int did_work ;
ENTER ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
do {
did_work = 0 ;
if ( vhost - > state ! = IBMVFC_ACTIVE )
break ;
list_for_each_entry ( tgt , & vhost - > targets , queue ) {
if ( tgt - > add_rport ) {
did_work = 1 ;
tgt - > add_rport = 0 ;
kref_get ( & tgt - > kref ) ;
rport = tgt - > rport ;
if ( ! rport ) {
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
ibmvfc_tgt_add_rport ( tgt ) ;
} else if ( get_device ( & rport - > dev ) ) {
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
tgt_dbg ( tgt , " Setting rport roles \n " ) ;
fc_remote_port_rolechg ( rport , tgt - > ids . roles ) ;
put_device ( & rport - > dev ) ;
}
kref_put ( & tgt - > kref , ibmvfc_release_tgt ) ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
break ;
}
}
} while ( did_work ) ;
if ( vhost - > state = = IBMVFC_ACTIVE )
vhost - > scan_complete = 1 ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
LEAVE ;
}
2008-07-01 22:14:30 +04:00
/**
* ibmvfc_probe - Adapter hot plug add entry point
* @ vdev : vio device struct
* @ id : vio device id struct
*
* Return value :
* 0 on success / non - zero on failure
* */
static int ibmvfc_probe ( struct vio_dev * vdev , const struct vio_device_id * id )
{
struct ibmvfc_host * vhost ;
struct Scsi_Host * shost ;
struct device * dev = & vdev - > dev ;
int rc = - ENOMEM ;
ENTER ;
shost = scsi_host_alloc ( & driver_template , sizeof ( * vhost ) ) ;
if ( ! shost ) {
dev_err ( dev , " Couldn't allocate host data \n " ) ;
goto out ;
}
shost - > transportt = ibmvfc_transport_template ;
shost - > can_queue = max_requests ;
shost - > max_lun = max_lun ;
shost - > max_id = max_targets ;
shost - > max_sectors = IBMVFC_MAX_SECTORS ;
shost - > max_cmd_len = IBMVFC_MAX_CDB_LEN ;
shost - > unique_id = shost - > host_no ;
vhost = shost_priv ( shost ) ;
INIT_LIST_HEAD ( & vhost - > sent ) ;
INIT_LIST_HEAD ( & vhost - > free ) ;
INIT_LIST_HEAD ( & vhost - > targets ) ;
sprintf ( vhost - > name , IBMVFC_NAME ) ;
vhost - > host = shost ;
vhost - > dev = dev ;
vhost - > partition_number = - 1 ;
vhost - > log_level = log_level ;
2009-03-20 23:44:39 +03:00
vhost - > task_set = 1 ;
2008-07-01 22:14:30 +04:00
strcpy ( vhost - > partition_name , " UNKNOWN " ) ;
init_waitqueue_head ( & vhost - > work_wait_q ) ;
init_waitqueue_head ( & vhost - > init_wait_q ) ;
2009-05-29 01:17:28 +04:00
INIT_WORK ( & vhost - > rport_add_work_q , ibmvfc_rport_add_thread ) ;
2008-07-01 22:14:30 +04:00
if ( ( rc = ibmvfc_alloc_mem ( vhost ) ) )
goto free_scsi_host ;
vhost - > work_thread = kthread_run ( ibmvfc_work , vhost , " %s_%d " , IBMVFC_NAME ,
shost - > host_no ) ;
if ( IS_ERR ( vhost - > work_thread ) ) {
dev_err ( dev , " Couldn't create kernel thread: %ld \n " ,
PTR_ERR ( vhost - > work_thread ) ) ;
goto free_host_mem ;
}
if ( ( rc = ibmvfc_init_crq ( vhost ) ) ) {
dev_err ( dev , " Couldn't initialize crq. rc=%d \n " , rc ) ;
goto kill_kthread ;
}
if ( ( rc = ibmvfc_init_event_pool ( vhost ) ) ) {
dev_err ( dev , " Couldn't initialize event pool. rc=%d \n " , rc ) ;
goto release_crq ;
}
if ( ( rc = scsi_add_host ( shost , dev ) ) )
goto release_event_pool ;
if ( ( rc = ibmvfc_create_trace_file ( & shost - > shost_dev . kobj ,
& ibmvfc_trace_attr ) ) ) {
dev_err ( dev , " Failed to create trace file. rc=%d \n " , rc ) ;
goto remove_shost ;
}
dev_set_drvdata ( dev , vhost ) ;
spin_lock ( & ibmvfc_driver_lock ) ;
list_add_tail ( & vhost - > queue , & ibmvfc_head ) ;
spin_unlock ( & ibmvfc_driver_lock ) ;
ibmvfc_send_crq_init ( vhost ) ;
scsi_scan_host ( shost ) ;
return 0 ;
remove_shost :
scsi_remove_host ( shost ) ;
release_event_pool :
ibmvfc_free_event_pool ( vhost ) ;
release_crq :
ibmvfc_release_crq_queue ( vhost ) ;
kill_kthread :
kthread_stop ( vhost - > work_thread ) ;
free_host_mem :
ibmvfc_free_mem ( vhost ) ;
free_scsi_host :
scsi_host_put ( shost ) ;
out :
LEAVE ;
return rc ;
}
/**
* ibmvfc_remove - Adapter hot plug remove entry point
* @ vdev : vio device struct
*
* Return value :
* 0
* */
static int ibmvfc_remove ( struct vio_dev * vdev )
{
struct ibmvfc_host * vhost = dev_get_drvdata ( & vdev - > dev ) ;
unsigned long flags ;
ENTER ;
ibmvfc_remove_trace_file ( & vhost - > host - > shost_dev . kobj , & ibmvfc_trace_attr ) ;
2009-10-20 00:07:48 +04:00
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
2008-07-22 17:31:42 +04:00
ibmvfc_link_down ( vhost , IBMVFC_HOST_OFFLINE ) ;
2009-10-20 00:07:48 +04:00
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
2008-07-22 17:31:42 +04:00
ibmvfc_wait_while_resetting ( vhost ) ;
ibmvfc_release_crq_queue ( vhost ) ;
2008-07-01 22:14:30 +04:00
kthread_stop ( vhost - > work_thread ) ;
fc_remove_host ( vhost - > host ) ;
scsi_remove_host ( vhost - > host ) ;
spin_lock_irqsave ( vhost - > host - > host_lock , flags ) ;
ibmvfc_purge_requests ( vhost , DID_ERROR ) ;
ibmvfc_free_event_pool ( vhost ) ;
spin_unlock_irqrestore ( vhost - > host - > host_lock , flags ) ;
ibmvfc_free_mem ( vhost ) ;
spin_lock ( & ibmvfc_driver_lock ) ;
list_del ( & vhost - > queue ) ;
spin_unlock ( & ibmvfc_driver_lock ) ;
scsi_host_put ( vhost - > host ) ;
LEAVE ;
return 0 ;
}
2008-07-23 22:35:48 +04:00
/**
* ibmvfc_get_desired_dma - Calculate DMA resources needed by the driver
* @ vdev : vio device struct
*
* Return value :
* Number of bytes the driver will need to DMA map at the same time in
* order to perform well .
*/
static unsigned long ibmvfc_get_desired_dma ( struct vio_dev * vdev )
{
unsigned long pool_dma = max_requests * sizeof ( union ibmvfc_iu ) ;
return pool_dma + ( ( 512 * 1024 ) * driver_template . cmd_per_lun ) ;
}
2008-07-01 22:14:30 +04:00
static struct vio_device_id ibmvfc_device_table [ ] __devinitdata = {
{ " fcp " , " IBM,vfc-client " } ,
{ " " , " " }
} ;
MODULE_DEVICE_TABLE ( vio , ibmvfc_device_table ) ;
static struct vio_driver ibmvfc_driver = {
. id_table = ibmvfc_device_table ,
. probe = ibmvfc_probe ,
. remove = ibmvfc_remove ,
2008-07-23 22:35:48 +04:00
. get_desired_dma = ibmvfc_get_desired_dma ,
2008-07-01 22:14:30 +04:00
. driver = {
. name = IBMVFC_NAME ,
. owner = THIS_MODULE ,
}
} ;
static struct fc_function_template ibmvfc_transport_functions = {
. show_host_fabric_name = 1 ,
. show_host_node_name = 1 ,
. show_host_port_name = 1 ,
. show_host_supported_classes = 1 ,
. show_host_port_type = 1 ,
. show_host_port_id = 1 ,
2009-03-20 23:44:38 +03:00
. show_host_maxframe_size = 1 ,
2008-07-01 22:14:30 +04:00
. get_host_port_state = ibmvfc_get_host_port_state ,
. show_host_port_state = 1 ,
. get_host_speed = ibmvfc_get_host_speed ,
. show_host_speed = 1 ,
. issue_fc_host_lip = ibmvfc_issue_fc_host_lip ,
. terminate_rport_io = ibmvfc_terminate_rport_io ,
. show_rport_maxframe_size = 1 ,
. show_rport_supported_classes = 1 ,
. set_rport_dev_loss_tmo = ibmvfc_set_rport_dev_loss_tmo ,
. show_rport_dev_loss_tmo = 1 ,
. get_starget_node_name = ibmvfc_get_starget_node_name ,
. show_starget_node_name = 1 ,
. get_starget_port_name = ibmvfc_get_starget_port_name ,
. show_starget_port_name = 1 ,
. get_starget_port_id = ibmvfc_get_starget_port_id ,
. show_starget_port_id = 1 ,
} ;
/**
* ibmvfc_module_init - Initialize the ibmvfc module
*
* Return value :
* 0 on success / other on failure
* */
static int __init ibmvfc_module_init ( void )
{
int rc ;
if ( ! firmware_has_feature ( FW_FEATURE_VIO ) )
return - ENODEV ;
printk ( KERN_INFO IBMVFC_NAME " : IBM Virtual Fibre Channel Driver version: %s %s \n " ,
IBMVFC_DRIVER_VERSION , IBMVFC_DRIVER_DATE ) ;
ibmvfc_transport_template = fc_attach_transport ( & ibmvfc_transport_functions ) ;
if ( ! ibmvfc_transport_template )
return - ENOMEM ;
rc = vio_register_driver ( & ibmvfc_driver ) ;
if ( rc )
fc_release_transport ( ibmvfc_transport_template ) ;
return rc ;
}
/**
* ibmvfc_module_exit - Teardown the ibmvfc module
*
* Return value :
* nothing
* */
static void __exit ibmvfc_module_exit ( void )
{
vio_unregister_driver ( & ibmvfc_driver ) ;
fc_release_transport ( ibmvfc_transport_template ) ;
}
module_init ( ibmvfc_module_init ) ;
module_exit ( ibmvfc_module_exit ) ;