2019-06-01 10:08:54 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2016-12-01 00:21:08 -08:00
/*
* QLogic iSCSI Offload Driver
* Copyright ( c ) 2016 Cavium Inc .
*/
# include <linux/blkdev.h>
# include <linux/etherdevice.h>
# include <linux/if_ether.h>
# include <linux/if_vlan.h>
# include <scsi/scsi_tcq.h>
# include "qedi.h"
# include "qedi_iscsi.h"
# include "qedi_gbl.h"
int qedi_recover_all_conns ( struct qedi_ctx * qedi )
{
struct qedi_conn * qedi_conn ;
int i ;
for ( i = 0 ; i < qedi - > max_active_conns ; i + + ) {
qedi_conn = qedi_get_conn_from_id ( qedi , i ) ;
if ( ! qedi_conn )
continue ;
qedi_start_conn_recovery ( qedi , qedi_conn ) ;
}
return SUCCESS ;
}
static int qedi_eh_host_reset ( struct scsi_cmnd * cmd )
{
struct Scsi_Host * shost = cmd - > device - > host ;
struct qedi_ctx * qedi ;
qedi = iscsi_host_priv ( shost ) ;
return qedi_recover_all_conns ( qedi ) ;
}
struct scsi_host_template qedi_host_template = {
. module = THIS_MODULE ,
. name = " QLogic QEDI 25/40/100Gb iSCSI Initiator Driver " ,
. proc_name = QEDI_MODULE_NAME ,
. queuecommand = iscsi_queuecommand ,
2017-01-30 13:18:58 +01:00
. eh_timed_out = iscsi_eh_cmd_timed_out ,
2016-12-01 00:21:08 -08:00
. eh_abort_handler = iscsi_eh_abort ,
. eh_device_reset_handler = iscsi_eh_device_reset ,
. eh_target_reset_handler = iscsi_eh_recover_target ,
. eh_host_reset_handler = qedi_eh_host_reset ,
. target_alloc = iscsi_target_alloc ,
. change_queue_depth = scsi_change_queue_depth ,
. can_queue = QEDI_MAX_ISCSI_TASK ,
. this_id = - 1 ,
. sg_tablesize = QEDI_ISCSI_MAX_BDS_PER_CMD ,
. max_sectors = 0xffff ,
2017-05-19 01:33:17 -07:00
. dma_boundary = QEDI_HW_DMA_BOUNDARY ,
2016-12-01 00:21:08 -08:00
. cmd_per_lun = 128 ,
. shost_attrs = qedi_shost_attrs ,
} ;
static void qedi_conn_free_login_resources ( struct qedi_ctx * qedi ,
struct qedi_conn * qedi_conn )
{
if ( qedi_conn - > gen_pdu . resp_bd_tbl ) {
dma_free_coherent ( & qedi - > pdev - > dev , QEDI_PAGE_SIZE ,
qedi_conn - > gen_pdu . resp_bd_tbl ,
qedi_conn - > gen_pdu . resp_bd_dma ) ;
qedi_conn - > gen_pdu . resp_bd_tbl = NULL ;
}
if ( qedi_conn - > gen_pdu . req_bd_tbl ) {
dma_free_coherent ( & qedi - > pdev - > dev , QEDI_PAGE_SIZE ,
qedi_conn - > gen_pdu . req_bd_tbl ,
qedi_conn - > gen_pdu . req_bd_dma ) ;
qedi_conn - > gen_pdu . req_bd_tbl = NULL ;
}
if ( qedi_conn - > gen_pdu . resp_buf ) {
dma_free_coherent ( & qedi - > pdev - > dev ,
ISCSI_DEF_MAX_RECV_SEG_LEN ,
qedi_conn - > gen_pdu . resp_buf ,
qedi_conn - > gen_pdu . resp_dma_addr ) ;
qedi_conn - > gen_pdu . resp_buf = NULL ;
}
if ( qedi_conn - > gen_pdu . req_buf ) {
dma_free_coherent ( & qedi - > pdev - > dev ,
ISCSI_DEF_MAX_RECV_SEG_LEN ,
qedi_conn - > gen_pdu . req_buf ,
qedi_conn - > gen_pdu . req_dma_addr ) ;
qedi_conn - > gen_pdu . req_buf = NULL ;
}
}
static int qedi_conn_alloc_login_resources ( struct qedi_ctx * qedi ,
struct qedi_conn * qedi_conn )
{
qedi_conn - > gen_pdu . req_buf =
dma_alloc_coherent ( & qedi - > pdev - > dev ,
ISCSI_DEF_MAX_RECV_SEG_LEN ,
& qedi_conn - > gen_pdu . req_dma_addr ,
GFP_KERNEL ) ;
if ( ! qedi_conn - > gen_pdu . req_buf )
goto login_req_buf_failure ;
qedi_conn - > gen_pdu . req_buf_size = 0 ;
qedi_conn - > gen_pdu . req_wr_ptr = qedi_conn - > gen_pdu . req_buf ;
qedi_conn - > gen_pdu . resp_buf =
dma_alloc_coherent ( & qedi - > pdev - > dev ,
ISCSI_DEF_MAX_RECV_SEG_LEN ,
& qedi_conn - > gen_pdu . resp_dma_addr ,
GFP_KERNEL ) ;
if ( ! qedi_conn - > gen_pdu . resp_buf )
goto login_resp_buf_failure ;
qedi_conn - > gen_pdu . resp_buf_size = ISCSI_DEF_MAX_RECV_SEG_LEN ;
qedi_conn - > gen_pdu . resp_wr_ptr = qedi_conn - > gen_pdu . resp_buf ;
qedi_conn - > gen_pdu . req_bd_tbl =
dma_alloc_coherent ( & qedi - > pdev - > dev , QEDI_PAGE_SIZE ,
& qedi_conn - > gen_pdu . req_bd_dma , GFP_KERNEL ) ;
if ( ! qedi_conn - > gen_pdu . req_bd_tbl )
goto login_req_bd_tbl_failure ;
qedi_conn - > gen_pdu . resp_bd_tbl =
dma_alloc_coherent ( & qedi - > pdev - > dev , QEDI_PAGE_SIZE ,
& qedi_conn - > gen_pdu . resp_bd_dma ,
GFP_KERNEL ) ;
if ( ! qedi_conn - > gen_pdu . resp_bd_tbl )
goto login_resp_bd_tbl_failure ;
QEDI_INFO ( & qedi - > dbg_ctx , QEDI_LOG_SESS ,
" Allocation successful, cid=0x%x \n " ,
qedi_conn - > iscsi_conn_id ) ;
return 0 ;
login_resp_bd_tbl_failure :
dma_free_coherent ( & qedi - > pdev - > dev , QEDI_PAGE_SIZE ,
qedi_conn - > gen_pdu . req_bd_tbl ,
qedi_conn - > gen_pdu . req_bd_dma ) ;
qedi_conn - > gen_pdu . req_bd_tbl = NULL ;
login_req_bd_tbl_failure :
dma_free_coherent ( & qedi - > pdev - > dev , ISCSI_DEF_MAX_RECV_SEG_LEN ,
qedi_conn - > gen_pdu . resp_buf ,
qedi_conn - > gen_pdu . resp_dma_addr ) ;
qedi_conn - > gen_pdu . resp_buf = NULL ;
login_resp_buf_failure :
dma_free_coherent ( & qedi - > pdev - > dev , ISCSI_DEF_MAX_RECV_SEG_LEN ,
qedi_conn - > gen_pdu . req_buf ,
qedi_conn - > gen_pdu . req_dma_addr ) ;
qedi_conn - > gen_pdu . req_buf = NULL ;
login_req_buf_failure :
iscsi_conn_printk ( KERN_ERR , qedi_conn - > cls_conn - > dd_data ,
" login resource alloc failed!! \n " ) ;
return - ENOMEM ;
}
static void qedi_destroy_cmd_pool ( struct qedi_ctx * qedi ,
struct iscsi_session * session )
{
int i ;
for ( i = 0 ; i < session - > cmds_max ; i + + ) {
struct iscsi_task * task = session - > cmds [ i ] ;
struct qedi_cmd * cmd = task - > dd_data ;
if ( cmd - > io_tbl . sge_tbl )
dma_free_coherent ( & qedi - > pdev - > dev ,
QEDI_ISCSI_MAX_BDS_PER_CMD *
2017-03-11 18:39:18 +02:00
sizeof ( struct scsi_sge ) ,
2016-12-01 00:21:08 -08:00
cmd - > io_tbl . sge_tbl ,
cmd - > io_tbl . sge_tbl_dma ) ;
if ( cmd - > sense_buffer )
dma_free_coherent ( & qedi - > pdev - > dev ,
SCSI_SENSE_BUFFERSIZE ,
cmd - > sense_buffer ,
cmd - > sense_buffer_dma ) ;
}
}
static int qedi_alloc_sget ( struct qedi_ctx * qedi , struct iscsi_session * session ,
struct qedi_cmd * cmd )
{
struct qedi_io_bdt * io = & cmd - > io_tbl ;
2017-03-11 18:39:18 +02:00
struct scsi_sge * sge ;
2016-12-01 00:21:08 -08:00
io - > sge_tbl = dma_alloc_coherent ( & qedi - > pdev - > dev ,
QEDI_ISCSI_MAX_BDS_PER_CMD *
sizeof ( * sge ) ,
& io - > sge_tbl_dma , GFP_KERNEL ) ;
if ( ! io - > sge_tbl ) {
iscsi_session_printk ( KERN_ERR , session ,
" Could not allocate BD table. \n " ) ;
return - ENOMEM ;
}
io - > sge_valid = 0 ;
return 0 ;
}
static int qedi_setup_cmd_pool ( struct qedi_ctx * qedi ,
struct iscsi_session * session )
{
int i ;
for ( i = 0 ; i < session - > cmds_max ; i + + ) {
struct iscsi_task * task = session - > cmds [ i ] ;
struct qedi_cmd * cmd = task - > dd_data ;
task - > hdr = & cmd - > hdr ;
task - > hdr_max = sizeof ( struct iscsi_hdr ) ;
if ( qedi_alloc_sget ( qedi , session , cmd ) )
goto free_sgets ;
cmd - > sense_buffer = dma_alloc_coherent ( & qedi - > pdev - > dev ,
SCSI_SENSE_BUFFERSIZE ,
& cmd - > sense_buffer_dma ,
GFP_KERNEL ) ;
if ( ! cmd - > sense_buffer )
goto free_sgets ;
}
return 0 ;
free_sgets :
qedi_destroy_cmd_pool ( qedi , session ) ;
return - ENOMEM ;
}
static struct iscsi_cls_session *
qedi_session_create ( struct iscsi_endpoint * ep , u16 cmds_max ,
u16 qdepth , uint32_t initial_cmdsn )
{
struct Scsi_Host * shost ;
struct iscsi_cls_session * cls_session ;
struct qedi_ctx * qedi ;
struct qedi_endpoint * qedi_ep ;
if ( ! ep )
return NULL ;
qedi_ep = ep - > dd_data ;
shost = qedi_ep - > qedi - > shost ;
qedi = iscsi_host_priv ( shost ) ;
if ( cmds_max > qedi - > max_sqes )
cmds_max = qedi - > max_sqes ;
else if ( cmds_max < QEDI_SQ_WQES_MIN )
cmds_max = QEDI_SQ_WQES_MIN ;
cls_session = iscsi_session_setup ( & qedi_iscsi_transport , shost ,
cmds_max , 0 , sizeof ( struct qedi_cmd ) ,
initial_cmdsn , ISCSI_MAX_TARGET ) ;
if ( ! cls_session ) {
QEDI_ERR ( & qedi - > dbg_ctx ,
" Failed to setup session for ep=%p \n " , qedi_ep ) ;
return NULL ;
}
if ( qedi_setup_cmd_pool ( qedi , cls_session - > dd_data ) ) {
QEDI_ERR ( & qedi - > dbg_ctx ,
" Failed to setup cmd pool for ep=%p \n " , qedi_ep ) ;
goto session_teardown ;
}
return cls_session ;
session_teardown :
iscsi_session_teardown ( cls_session ) ;
return NULL ;
}
static void qedi_session_destroy ( struct iscsi_cls_session * cls_session )
{
struct iscsi_session * session = cls_session - > dd_data ;
struct Scsi_Host * shost = iscsi_session_to_shost ( cls_session ) ;
struct qedi_ctx * qedi = iscsi_host_priv ( shost ) ;
qedi_destroy_cmd_pool ( qedi , session ) ;
iscsi_session_teardown ( cls_session ) ;
}
static struct iscsi_cls_conn *
qedi_conn_create ( struct iscsi_cls_session * cls_session , uint32_t cid )
{
struct Scsi_Host * shost = iscsi_session_to_shost ( cls_session ) ;
struct qedi_ctx * qedi = iscsi_host_priv ( shost ) ;
struct iscsi_cls_conn * cls_conn ;
struct qedi_conn * qedi_conn ;
struct iscsi_conn * conn ;
cls_conn = iscsi_conn_setup ( cls_session , sizeof ( * qedi_conn ) ,
cid ) ;
if ( ! cls_conn ) {
QEDI_ERR ( & qedi - > dbg_ctx ,
" conn_new: iscsi conn setup failed, cid=0x%x, cls_sess=%p! \n " ,
cid , cls_session ) ;
return NULL ;
}
conn = cls_conn - > dd_data ;
qedi_conn = conn - > dd_data ;
qedi_conn - > cls_conn = cls_conn ;
qedi_conn - > qedi = qedi ;
qedi_conn - > ep = NULL ;
qedi_conn - > active_cmd_count = 0 ;
INIT_LIST_HEAD ( & qedi_conn - > active_cmd_list ) ;
spin_lock_init ( & qedi_conn - > list_lock ) ;
if ( qedi_conn_alloc_login_resources ( qedi , qedi_conn ) ) {
iscsi_conn_printk ( KERN_ALERT , conn ,
" conn_new: login resc alloc failed, cid=0x%x, cls_sess=%p!! \n " ,
cid , cls_session ) ;
goto free_conn ;
}
return cls_conn ;
free_conn :
iscsi_conn_teardown ( cls_conn ) ;
return NULL ;
}
void qedi_mark_device_missing ( struct iscsi_cls_session * cls_session )
{
iscsi_block_session ( cls_session ) ;
}
void qedi_mark_device_available ( struct iscsi_cls_session * cls_session )
{
iscsi_unblock_session ( cls_session ) ;
}
static int qedi_bind_conn_to_iscsi_cid ( struct qedi_ctx * qedi ,
struct qedi_conn * qedi_conn )
{
u32 iscsi_cid = qedi_conn - > iscsi_conn_id ;
if ( qedi - > cid_que . conn_cid_tbl [ iscsi_cid ] ) {
iscsi_conn_printk ( KERN_ALERT , qedi_conn - > cls_conn - > dd_data ,
" conn bind - entry #%d not free \n " ,
iscsi_cid ) ;
return - EBUSY ;
}
qedi - > cid_que . conn_cid_tbl [ iscsi_cid ] = qedi_conn ;
return 0 ;
}
struct qedi_conn * qedi_get_conn_from_id ( struct qedi_ctx * qedi , u32 iscsi_cid )
{
if ( ! qedi - > cid_que . conn_cid_tbl ) {
QEDI_ERR ( & qedi - > dbg_ctx , " missing conn<->cid table \n " ) ;
return NULL ;
} else if ( iscsi_cid > = qedi - > max_active_conns ) {
QEDI_ERR ( & qedi - > dbg_ctx , " wrong cid #%d \n " , iscsi_cid ) ;
return NULL ;
}
return qedi - > cid_que . conn_cid_tbl [ iscsi_cid ] ;
}
static int qedi_conn_bind ( struct iscsi_cls_session * cls_session ,
struct iscsi_cls_conn * cls_conn ,
u64 transport_fd , int is_leading )
{
struct iscsi_conn * conn = cls_conn - > dd_data ;
struct qedi_conn * qedi_conn = conn - > dd_data ;
struct Scsi_Host * shost = iscsi_session_to_shost ( cls_session ) ;
struct qedi_ctx * qedi = iscsi_host_priv ( shost ) ;
struct qedi_endpoint * qedi_ep ;
struct iscsi_endpoint * ep ;
ep = iscsi_lookup_endpoint ( transport_fd ) ;
if ( ! ep )
return - EINVAL ;
qedi_ep = ep - > dd_data ;
if ( ( qedi_ep - > state = = EP_STATE_TCP_FIN_RCVD ) | |
( qedi_ep - > state = = EP_STATE_TCP_RST_RCVD ) )
return - EINVAL ;
if ( iscsi_conn_bind ( cls_session , cls_conn , is_leading ) )
return - EINVAL ;
qedi_ep - > conn = qedi_conn ;
qedi_conn - > ep = qedi_ep ;
qedi_conn - > iscsi_conn_id = qedi_ep - > iscsi_cid ;
qedi_conn - > fw_cid = qedi_ep - > fw_cid ;
qedi_conn - > cmd_cleanup_req = 0 ;
qedi_conn - > cmd_cleanup_cmpl = 0 ;
if ( qedi_bind_conn_to_iscsi_cid ( qedi , qedi_conn ) )
return - EINVAL ;
spin_lock_init ( & qedi_conn - > tmf_work_lock ) ;
INIT_LIST_HEAD ( & qedi_conn - > tmf_work_list ) ;
init_waitqueue_head ( & qedi_conn - > wait_queue ) ;
return 0 ;
}
static int qedi_iscsi_update_conn ( struct qedi_ctx * qedi ,
struct qedi_conn * qedi_conn )
{
struct qed_iscsi_params_update * conn_info ;
struct iscsi_cls_conn * cls_conn = qedi_conn - > cls_conn ;
struct iscsi_conn * conn = cls_conn - > dd_data ;
struct qedi_endpoint * qedi_ep ;
int rval ;
qedi_ep = qedi_conn - > ep ;
conn_info = kzalloc ( sizeof ( * conn_info ) , GFP_KERNEL ) ;
if ( ! conn_info ) {
QEDI_ERR ( & qedi - > dbg_ctx , " memory alloc failed \n " ) ;
return - ENOMEM ;
}
conn_info - > update_flag = 0 ;
if ( conn - > hdrdgst_en )
SET_FIELD ( conn_info - > update_flag ,
ISCSI_CONN_UPDATE_RAMROD_PARAMS_HD_EN , true ) ;
if ( conn - > datadgst_en )
SET_FIELD ( conn_info - > update_flag ,
ISCSI_CONN_UPDATE_RAMROD_PARAMS_DD_EN , true ) ;
if ( conn - > session - > initial_r2t_en )
SET_FIELD ( conn_info - > update_flag ,
ISCSI_CONN_UPDATE_RAMROD_PARAMS_INITIAL_R2T ,
true ) ;
if ( conn - > session - > imm_data_en )
SET_FIELD ( conn_info - > update_flag ,
ISCSI_CONN_UPDATE_RAMROD_PARAMS_IMMEDIATE_DATA ,
true ) ;
conn_info - > max_seq_size = conn - > session - > max_burst ;
conn_info - > max_recv_pdu_length = conn - > max_recv_dlength ;
conn_info - > max_send_pdu_length = conn - > max_xmit_dlength ;
conn_info - > first_seq_length = conn - > session - > first_burst ;
conn_info - > exp_stat_sn = conn - > exp_statsn ;
rval = qedi_ops - > update_conn ( qedi - > cdev , qedi_ep - > handle ,
conn_info ) ;
if ( rval ) {
rval = - ENXIO ;
QEDI_ERR ( & qedi - > dbg_ctx , " Could not update connection \n " ) ;
}
kfree ( conn_info ) ;
return rval ;
}
static u16 qedi_calc_mss ( u16 pmtu , u8 is_ipv6 , u8 tcp_ts_en , u8 vlan_en )
{
u16 mss = 0 ;
u16 hdrs = TCP_HDR_LEN ;
if ( is_ipv6 )
hdrs + = IPV6_HDR_LEN ;
else
hdrs + = IPV4_HDR_LEN ;
mss = pmtu - hdrs ;
if ( ! mss )
mss = DEF_MSS ;
return mss ;
}
static int qedi_iscsi_offload_conn ( struct qedi_endpoint * qedi_ep )
{
struct qedi_ctx * qedi = qedi_ep - > qedi ;
struct qed_iscsi_params_offload * conn_info ;
int rval ;
int i ;
conn_info = kzalloc ( sizeof ( * conn_info ) , GFP_KERNEL ) ;
if ( ! conn_info ) {
QEDI_ERR ( & qedi - > dbg_ctx ,
" Failed to allocate memory ep=%p \n " , qedi_ep ) ;
return - ENOMEM ;
}
ether_addr_copy ( conn_info - > src . mac , qedi_ep - > src_mac ) ;
ether_addr_copy ( conn_info - > dst . mac , qedi_ep - > dst_mac ) ;
conn_info - > src . ip [ 0 ] = ntohl ( qedi_ep - > src_addr [ 0 ] ) ;
conn_info - > dst . ip [ 0 ] = ntohl ( qedi_ep - > dst_addr [ 0 ] ) ;
if ( qedi_ep - > ip_type = = TCP_IPV4 ) {
conn_info - > ip_version = 0 ;
QEDI_INFO ( & qedi - > dbg_ctx , QEDI_LOG_CONN ,
" After ntohl: src_addr=%pI4, dst_addr=%pI4 \n " ,
qedi_ep - > src_addr , qedi_ep - > dst_addr ) ;
} else {
for ( i = 1 ; i < 4 ; i + + ) {
conn_info - > src . ip [ i ] = ntohl ( qedi_ep - > src_addr [ i ] ) ;
conn_info - > dst . ip [ i ] = ntohl ( qedi_ep - > dst_addr [ i ] ) ;
}
conn_info - > ip_version = 1 ;
QEDI_INFO ( & qedi - > dbg_ctx , QEDI_LOG_CONN ,
" After ntohl: src_addr=%pI6, dst_addr=%pI6 \n " ,
qedi_ep - > src_addr , qedi_ep - > dst_addr ) ;
}
conn_info - > src . port = qedi_ep - > src_port ;
conn_info - > dst . port = qedi_ep - > dst_port ;
conn_info - > layer_code = ISCSI_SLOW_PATH_LAYER_CODE ;
conn_info - > sq_pbl_addr = qedi_ep - > sq_pbl_dma ;
conn_info - > vlan_id = qedi_ep - > vlan_id ;
SET_FIELD ( conn_info - > tcp_flags , TCP_OFFLOAD_PARAMS_TS_EN , 1 ) ;
SET_FIELD ( conn_info - > tcp_flags , TCP_OFFLOAD_PARAMS_DA_EN , 1 ) ;
SET_FIELD ( conn_info - > tcp_flags , TCP_OFFLOAD_PARAMS_DA_CNT_EN , 1 ) ;
SET_FIELD ( conn_info - > tcp_flags , TCP_OFFLOAD_PARAMS_KA_EN , 1 ) ;
2017-08-10 06:32:17 -07:00
conn_info - > default_cq = ( qedi_ep - > fw_cid % qedi - > num_queues ) ;
2016-12-01 00:21:08 -08:00
conn_info - > ka_max_probe_cnt = DEF_KA_MAX_PROBE_COUNT ;
conn_info - > dup_ack_theshold = 3 ;
conn_info - > rcv_wnd = 65535 ;
conn_info - > ss_thresh = 65535 ;
conn_info - > srtt = 300 ;
conn_info - > rtt_var = 150 ;
conn_info - > flow_label = 0 ;
conn_info - > ka_timeout = DEF_KA_TIMEOUT ;
conn_info - > ka_interval = DEF_KA_INTERVAL ;
conn_info - > max_rt_time = DEF_MAX_RT_TIME ;
conn_info - > ttl = DEF_TTL ;
conn_info - > tos_or_tc = DEF_TOS ;
conn_info - > remote_port = qedi_ep - > dst_port ;
conn_info - > local_port = qedi_ep - > src_port ;
conn_info - > mss = qedi_calc_mss ( qedi_ep - > pmtu ,
( qedi_ep - > ip_type = = TCP_IPV6 ) ,
1 , ( qedi_ep - > vlan_id ! = 0 ) ) ;
2017-12-27 19:30:07 +02:00
conn_info - > cwnd = DEF_MAX_CWND * conn_info - > mss ;
2016-12-01 00:21:08 -08:00
conn_info - > rcv_wnd_scale = 4 ;
conn_info - > da_timeout_value = 200 ;
conn_info - > ack_frequency = 2 ;
QEDI_INFO ( & qedi - > dbg_ctx , QEDI_LOG_INFO ,
" Default cq index [%d], mss [%d] \n " ,
conn_info - > default_cq , conn_info - > mss ) ;
rval = qedi_ops - > offload_conn ( qedi - > cdev , qedi_ep - > handle , conn_info ) ;
if ( rval )
QEDI_ERR ( & qedi - > dbg_ctx , " offload_conn returned %d, ep=%p \n " ,
rval , qedi_ep ) ;
kfree ( conn_info ) ;
return rval ;
}
static int qedi_conn_start ( struct iscsi_cls_conn * cls_conn )
{
struct iscsi_conn * conn = cls_conn - > dd_data ;
struct qedi_conn * qedi_conn = conn - > dd_data ;
struct qedi_ctx * qedi ;
int rval ;
qedi = qedi_conn - > qedi ;
rval = qedi_iscsi_update_conn ( qedi , qedi_conn ) ;
if ( rval ) {
iscsi_conn_printk ( KERN_ALERT , conn ,
2019-04-17 14:43:04 +01:00
" conn_start: FW offload conn failed. \n " ) ;
2016-12-01 00:21:08 -08:00
rval = - EINVAL ;
goto start_err ;
}
clear_bit ( QEDI_CONN_FW_CLEANUP , & qedi_conn - > flags ) ;
qedi_conn - > abrt_conn = 0 ;
rval = iscsi_conn_start ( cls_conn ) ;
if ( rval ) {
iscsi_conn_printk ( KERN_ALERT , conn ,
2019-04-17 14:43:04 +01:00
" iscsi_conn_start: FW offload conn failed!! \n " ) ;
2016-12-01 00:21:08 -08:00
}
start_err :
return rval ;
}
static void qedi_conn_destroy ( struct iscsi_cls_conn * cls_conn )
{
struct iscsi_conn * conn = cls_conn - > dd_data ;
struct qedi_conn * qedi_conn = conn - > dd_data ;
struct Scsi_Host * shost ;
struct qedi_ctx * qedi ;
shost = iscsi_session_to_shost ( iscsi_conn_to_session ( cls_conn ) ) ;
qedi = iscsi_host_priv ( shost ) ;
qedi_conn_free_login_resources ( qedi , qedi_conn ) ;
iscsi_conn_teardown ( cls_conn ) ;
}
static int qedi_ep_get_param ( struct iscsi_endpoint * ep ,
enum iscsi_param param , char * buf )
{
struct qedi_endpoint * qedi_ep = ep - > dd_data ;
int len ;
if ( ! qedi_ep )
return - ENOTCONN ;
switch ( param ) {
case ISCSI_PARAM_CONN_PORT :
len = sprintf ( buf , " %hu \n " , qedi_ep - > dst_port ) ;
break ;
case ISCSI_PARAM_CONN_ADDRESS :
if ( qedi_ep - > ip_type = = TCP_IPV4 )
len = sprintf ( buf , " %pI4 \n " , qedi_ep - > dst_addr ) ;
else
len = sprintf ( buf , " %pI6 \n " , qedi_ep - > dst_addr ) ;
break ;
default :
return - ENOTCONN ;
}
return len ;
}
static int qedi_host_get_param ( struct Scsi_Host * shost ,
enum iscsi_host_param param , char * buf )
{
struct qedi_ctx * qedi ;
int len ;
qedi = iscsi_host_priv ( shost ) ;
switch ( param ) {
case ISCSI_HOST_PARAM_HWADDRESS :
len = sysfs_format_mac ( buf , qedi - > mac , 6 ) ;
break ;
case ISCSI_HOST_PARAM_NETDEV_NAME :
len = sprintf ( buf , " host%d \n " , shost - > host_no ) ;
break ;
case ISCSI_HOST_PARAM_IPADDRESS :
if ( qedi - > ip_type = = TCP_IPV4 )
len = sprintf ( buf , " %pI4 \n " , qedi - > src_ip ) ;
else
len = sprintf ( buf , " %pI6 \n " , qedi - > src_ip ) ;
break ;
default :
return iscsi_host_get_param ( shost , param , buf ) ;
}
return len ;
}
static void qedi_conn_get_stats ( struct iscsi_cls_conn * cls_conn ,
struct iscsi_stats * stats )
{
struct iscsi_conn * conn = cls_conn - > dd_data ;
struct qed_iscsi_stats iscsi_stats ;
struct Scsi_Host * shost ;
struct qedi_ctx * qedi ;
shost = iscsi_session_to_shost ( iscsi_conn_to_session ( cls_conn ) ) ;
qedi = iscsi_host_priv ( shost ) ;
qedi_ops - > get_stats ( qedi - > cdev , & iscsi_stats ) ;
conn - > txdata_octets = iscsi_stats . iscsi_tx_bytes_cnt ;
conn - > rxdata_octets = iscsi_stats . iscsi_rx_bytes_cnt ;
conn - > dataout_pdus_cnt = ( uint32_t ) iscsi_stats . iscsi_tx_data_pdu_cnt ;
conn - > datain_pdus_cnt = ( uint32_t ) iscsi_stats . iscsi_rx_data_pdu_cnt ;
conn - > r2t_pdus_cnt = ( uint32_t ) iscsi_stats . iscsi_rx_r2t_pdu_cnt ;
stats - > txdata_octets = conn - > txdata_octets ;
stats - > rxdata_octets = conn - > rxdata_octets ;
stats - > scsicmd_pdus = conn - > scsicmd_pdus_cnt ;
stats - > dataout_pdus = conn - > dataout_pdus_cnt ;
stats - > scsirsp_pdus = conn - > scsirsp_pdus_cnt ;
stats - > datain_pdus = conn - > datain_pdus_cnt ;
stats - > r2t_pdus = conn - > r2t_pdus_cnt ;
stats - > tmfcmd_pdus = conn - > tmfcmd_pdus_cnt ;
stats - > tmfrsp_pdus = conn - > tmfrsp_pdus_cnt ;
stats - > digest_err = 0 ;
stats - > timeout_err = 0 ;
strcpy ( stats - > custom [ 0 ] . desc , " eh_abort_cnt " ) ;
stats - > custom [ 0 ] . value = conn - > eh_abort_cnt ;
stats - > custom_length = 1 ;
}
static void qedi_iscsi_prep_generic_pdu_bd ( struct qedi_conn * qedi_conn )
{
2017-03-11 18:39:18 +02:00
struct scsi_sge * bd_tbl ;
2016-12-01 00:21:08 -08:00
2017-03-11 18:39:18 +02:00
bd_tbl = ( struct scsi_sge * ) qedi_conn - > gen_pdu . req_bd_tbl ;
2016-12-01 00:21:08 -08:00
bd_tbl - > sge_addr . hi =
( u32 ) ( ( u64 ) qedi_conn - > gen_pdu . req_dma_addr > > 32 ) ;
bd_tbl - > sge_addr . lo = ( u32 ) qedi_conn - > gen_pdu . req_dma_addr ;
bd_tbl - > sge_len = qedi_conn - > gen_pdu . req_wr_ptr -
qedi_conn - > gen_pdu . req_buf ;
2017-03-11 18:39:18 +02:00
bd_tbl = ( struct scsi_sge * ) qedi_conn - > gen_pdu . resp_bd_tbl ;
2016-12-01 00:21:08 -08:00
bd_tbl - > sge_addr . hi =
( u32 ) ( ( u64 ) qedi_conn - > gen_pdu . resp_dma_addr > > 32 ) ;
bd_tbl - > sge_addr . lo = ( u32 ) qedi_conn - > gen_pdu . resp_dma_addr ;
bd_tbl - > sge_len = ISCSI_DEF_MAX_RECV_SEG_LEN ;
}
static int qedi_iscsi_send_generic_request ( struct iscsi_task * task )
{
struct qedi_cmd * cmd = task - > dd_data ;
struct qedi_conn * qedi_conn = cmd - > conn ;
char * buf ;
int data_len ;
int rc = 0 ;
qedi_iscsi_prep_generic_pdu_bd ( qedi_conn ) ;
switch ( task - > hdr - > opcode & ISCSI_OPCODE_MASK ) {
case ISCSI_OP_LOGIN :
qedi_send_iscsi_login ( qedi_conn , task ) ;
break ;
case ISCSI_OP_NOOP_OUT :
data_len = qedi_conn - > gen_pdu . req_buf_size ;
buf = qedi_conn - > gen_pdu . req_buf ;
if ( data_len )
rc = qedi_send_iscsi_nopout ( qedi_conn , task ,
buf , data_len , 1 ) ;
else
rc = qedi_send_iscsi_nopout ( qedi_conn , task ,
NULL , 0 , 1 ) ;
break ;
case ISCSI_OP_LOGOUT :
rc = qedi_send_iscsi_logout ( qedi_conn , task ) ;
break ;
case ISCSI_OP_SCSI_TMFUNC :
rc = qedi_iscsi_abort_work ( qedi_conn , task ) ;
break ;
case ISCSI_OP_TEXT :
rc = qedi_send_iscsi_text ( qedi_conn , task ) ;
break ;
default :
iscsi_conn_printk ( KERN_ALERT , qedi_conn - > cls_conn - > dd_data ,
" unsupported op 0x%x \n " , task - > hdr - > opcode ) ;
}
return rc ;
}
static int qedi_mtask_xmit ( struct iscsi_conn * conn , struct iscsi_task * task )
{
struct qedi_conn * qedi_conn = conn - > dd_data ;
struct qedi_cmd * cmd = task - > dd_data ;
memset ( qedi_conn - > gen_pdu . req_buf , 0 , ISCSI_DEF_MAX_RECV_SEG_LEN ) ;
qedi_conn - > gen_pdu . req_buf_size = task - > data_count ;
if ( task - > data_count ) {
memcpy ( qedi_conn - > gen_pdu . req_buf , task - > data ,
task - > data_count ) ;
qedi_conn - > gen_pdu . req_wr_ptr =
qedi_conn - > gen_pdu . req_buf + task - > data_count ;
}
cmd - > conn = conn - > dd_data ;
cmd - > scsi_cmd = NULL ;
return qedi_iscsi_send_generic_request ( task ) ;
}
static int qedi_task_xmit ( struct iscsi_task * task )
{
struct iscsi_conn * conn = task - > conn ;
struct qedi_conn * qedi_conn = conn - > dd_data ;
struct qedi_cmd * cmd = task - > dd_data ;
struct scsi_cmnd * sc = task - > sc ;
cmd - > state = 0 ;
cmd - > task = NULL ;
cmd - > use_slowpath = false ;
cmd - > conn = qedi_conn ;
cmd - > task = task ;
cmd - > io_cmd_in_list = false ;
INIT_LIST_HEAD ( & cmd - > io_cmd ) ;
if ( ! sc )
return qedi_mtask_xmit ( conn , task ) ;
cmd - > scsi_cmd = sc ;
return qedi_iscsi_send_ioreq ( task ) ;
}
static struct iscsi_endpoint *
qedi_ep_connect ( struct Scsi_Host * shost , struct sockaddr * dst_addr ,
int non_blocking )
{
struct qedi_ctx * qedi ;
struct iscsi_endpoint * ep ;
struct qedi_endpoint * qedi_ep ;
struct sockaddr_in * addr ;
struct sockaddr_in6 * addr6 ;
struct iscsi_path path_req ;
u32 msg_type = ISCSI_KEVENT_IF_DOWN ;
u32 iscsi_cid = QEDI_CID_RESERVED ;
u16 len = 0 ;
char * buf = NULL ;
2017-07-12 10:31:21 +03:00
int ret , tmp ;
2016-12-01 00:21:08 -08:00
if ( ! shost ) {
ret = - ENXIO ;
QEDI_ERR ( NULL , " shost is NULL \n " ) ;
return ERR_PTR ( ret ) ;
}
2017-03-02 15:58:03 +01:00
if ( qedi_do_not_recover ) {
2016-12-01 00:21:08 -08:00
ret = - ENOMEM ;
return ERR_PTR ( ret ) ;
}
qedi = iscsi_host_priv ( shost ) ;
if ( test_bit ( QEDI_IN_OFFLINE , & qedi - > flags ) | |
test_bit ( QEDI_IN_RECOVERY , & qedi - > flags ) ) {
ret = - ENOMEM ;
return ERR_PTR ( ret ) ;
}
ep = iscsi_create_endpoint ( sizeof ( struct qedi_endpoint ) ) ;
if ( ! ep ) {
QEDI_ERR ( & qedi - > dbg_ctx , " endpoint create fail \n " ) ;
ret = - ENOMEM ;
return ERR_PTR ( ret ) ;
}
qedi_ep = ep - > dd_data ;
memset ( qedi_ep , 0 , sizeof ( struct qedi_endpoint ) ) ;
qedi_ep - > state = EP_STATE_IDLE ;
qedi_ep - > iscsi_cid = ( u32 ) - 1 ;
qedi_ep - > qedi = qedi ;
if ( dst_addr - > sa_family = = AF_INET ) {
addr = ( struct sockaddr_in * ) dst_addr ;
memcpy ( qedi_ep - > dst_addr , & addr - > sin_addr . s_addr ,
sizeof ( struct in_addr ) ) ;
qedi_ep - > dst_port = ntohs ( addr - > sin_port ) ;
qedi_ep - > ip_type = TCP_IPV4 ;
QEDI_INFO ( & qedi - > dbg_ctx , QEDI_LOG_CONN ,
" dst_addr=%pI4, dst_port=%u \n " ,
qedi_ep - > dst_addr , qedi_ep - > dst_port ) ;
} else if ( dst_addr - > sa_family = = AF_INET6 ) {
addr6 = ( struct sockaddr_in6 * ) dst_addr ;
memcpy ( qedi_ep - > dst_addr , & addr6 - > sin6_addr ,
sizeof ( struct in6_addr ) ) ;
qedi_ep - > dst_port = ntohs ( addr6 - > sin6_port ) ;
qedi_ep - > ip_type = TCP_IPV6 ;
QEDI_INFO ( & qedi - > dbg_ctx , QEDI_LOG_CONN ,
" dst_addr=%pI6, dst_port=%u \n " ,
qedi_ep - > dst_addr , qedi_ep - > dst_port ) ;
} else {
QEDI_ERR ( & qedi - > dbg_ctx , " Invalid endpoint \n " ) ;
}
if ( atomic_read ( & qedi - > link_state ) ! = QEDI_LINK_UP ) {
QEDI_WARN ( & qedi - > dbg_ctx , " qedi link down \n " ) ;
ret = - ENXIO ;
goto ep_conn_exit ;
}
ret = qedi_alloc_sq ( qedi , qedi_ep ) ;
if ( ret )
goto ep_conn_exit ;
ret = qedi_ops - > acquire_conn ( qedi - > cdev , & qedi_ep - > handle ,
& qedi_ep - > fw_cid , & qedi_ep - > p_doorbell ) ;
if ( ret ) {
QEDI_ERR ( & qedi - > dbg_ctx , " Could not acquire connection \n " ) ;
ret = - ENXIO ;
goto ep_free_sq ;
}
iscsi_cid = qedi_ep - > handle ;
qedi_ep - > iscsi_cid = iscsi_cid ;
init_waitqueue_head ( & qedi_ep - > ofld_wait ) ;
init_waitqueue_head ( & qedi_ep - > tcp_ofld_wait ) ;
qedi_ep - > state = EP_STATE_OFLDCONN_START ;
qedi - > ep_tbl [ iscsi_cid ] = qedi_ep ;
buf = ( char * ) & path_req ;
len = sizeof ( path_req ) ;
memset ( & path_req , 0 , len ) ;
msg_type = ISCSI_KEVENT_PATH_REQ ;
path_req . handle = ( u64 ) qedi_ep - > iscsi_cid ;
path_req . pmtu = qedi - > ll2_mtu ;
qedi_ep - > pmtu = qedi - > ll2_mtu ;
if ( qedi_ep - > ip_type = = TCP_IPV4 ) {
memcpy ( & path_req . dst . v4_addr , & qedi_ep - > dst_addr ,
sizeof ( struct in_addr ) ) ;
path_req . ip_addr_len = 4 ;
} else {
memcpy ( & path_req . dst . v6_addr , & qedi_ep - > dst_addr ,
sizeof ( struct in6_addr ) ) ;
path_req . ip_addr_len = 16 ;
}
ret = iscsi_offload_mesg ( shost , & qedi_iscsi_transport , msg_type , buf ,
len ) ;
if ( ret ) {
QEDI_ERR ( & qedi - > dbg_ctx ,
" iscsi_offload_mesg() failed for cid=0x%x ret=%d \n " ,
iscsi_cid , ret ) ;
goto ep_rel_conn ;
}
atomic_inc ( & qedi - > num_offloads ) ;
return ep ;
ep_rel_conn :
qedi - > ep_tbl [ iscsi_cid ] = NULL ;
2017-07-12 10:31:21 +03:00
tmp = qedi_ops - > release_conn ( qedi - > cdev , qedi_ep - > handle ) ;
if ( tmp )
2016-12-01 00:21:08 -08:00
QEDI_WARN ( & qedi - > dbg_ctx , " release_conn returned %d \n " ,
2017-07-12 10:31:21 +03:00
tmp ) ;
2016-12-01 00:21:08 -08:00
ep_free_sq :
qedi_free_sq ( qedi , qedi_ep ) ;
ep_conn_exit :
iscsi_destroy_endpoint ( ep ) ;
return ERR_PTR ( ret ) ;
}
static int qedi_ep_poll ( struct iscsi_endpoint * ep , int timeout_ms )
{
struct qedi_endpoint * qedi_ep ;
int ret = 0 ;
2017-03-02 15:58:03 +01:00
if ( qedi_do_not_recover )
2016-12-01 00:21:08 -08:00
return 1 ;
qedi_ep = ep - > dd_data ;
if ( qedi_ep - > state = = EP_STATE_IDLE | |
2019-01-09 01:39:07 -08:00
qedi_ep - > state = = EP_STATE_OFLDCONN_NONE | |
2016-12-01 00:21:08 -08:00
qedi_ep - > state = = EP_STATE_OFLDCONN_FAILED )
return - 1 ;
if ( qedi_ep - > state = = EP_STATE_OFLDCONN_COMPL )
ret = 1 ;
ret = wait_event_interruptible_timeout ( qedi_ep - > ofld_wait ,
QEDI_OFLD_WAIT_STATE ( qedi_ep ) ,
msecs_to_jiffies ( timeout_ms ) ) ;
if ( qedi_ep - > state = = EP_STATE_OFLDCONN_FAILED )
ret = - 1 ;
if ( ret > 0 )
return 1 ;
else if ( ! ret )
return 0 ;
else
return ret ;
}
static void qedi_cleanup_active_cmd_list ( struct qedi_conn * qedi_conn )
{
struct qedi_cmd * cmd , * cmd_tmp ;
list_for_each_entry_safe ( cmd , cmd_tmp , & qedi_conn - > active_cmd_list ,
io_cmd ) {
list_del_init ( & cmd - > io_cmd ) ;
qedi_conn - > active_cmd_count - - ;
}
}
static void qedi_ep_disconnect ( struct iscsi_endpoint * ep )
{
struct qedi_endpoint * qedi_ep ;
struct qedi_conn * qedi_conn = NULL ;
struct iscsi_conn * conn = NULL ;
struct qedi_ctx * qedi ;
int ret = 0 ;
2019-04-26 03:55:46 -07:00
int wait_delay ;
2016-12-01 00:21:08 -08:00
int abrt_conn = 0 ;
int count = 10 ;
2019-04-26 03:55:46 -07:00
wait_delay = 60 * HZ + DEF_MAX_RT_TIME ;
2016-12-01 00:21:08 -08:00
qedi_ep = ep - > dd_data ;
qedi = qedi_ep - > qedi ;
2019-04-26 03:55:45 -07:00
if ( qedi_ep - > state = = EP_STATE_OFLDCONN_START )
goto ep_exit_recover ;
2016-12-01 00:21:08 -08:00
flush_work ( & qedi_ep - > offload_work ) ;
if ( qedi_ep - > conn ) {
qedi_conn = qedi_ep - > conn ;
conn = qedi_conn - > cls_conn - > dd_data ;
iscsi_suspend_queue ( conn ) ;
abrt_conn = qedi_conn - > abrt_conn ;
while ( count - - ) {
if ( ! test_bit ( QEDI_CONN_FW_CLEANUP ,
& qedi_conn - > flags ) ) {
break ;
}
msleep ( 1000 ) ;
}
if ( test_bit ( QEDI_IN_RECOVERY , & qedi - > flags ) ) {
2017-03-02 15:58:03 +01:00
if ( qedi_do_not_recover ) {
2016-12-01 00:21:08 -08:00
QEDI_INFO ( & qedi - > dbg_ctx , QEDI_LOG_INFO ,
" Do not recover cid=0x%x \n " ,
qedi_ep - > iscsi_cid ) ;
goto ep_exit_recover ;
}
QEDI_INFO ( & qedi - > dbg_ctx , QEDI_LOG_INFO ,
" Reset recovery cid=0x%x, qedi_ep=%p, state=0x%x \n " ,
qedi_ep - > iscsi_cid , qedi_ep , qedi_ep - > state ) ;
qedi_cleanup_active_cmd_list ( qedi_conn ) ;
goto ep_release_conn ;
}
}
2017-03-02 15:58:03 +01:00
if ( qedi_do_not_recover )
2016-12-01 00:21:08 -08:00
goto ep_exit_recover ;
switch ( qedi_ep - > state ) {
case EP_STATE_OFLDCONN_START :
2019-01-09 01:39:07 -08:00
case EP_STATE_OFLDCONN_NONE :
2016-12-01 00:21:08 -08:00
goto ep_release_conn ;
case EP_STATE_OFLDCONN_FAILED :
break ;
case EP_STATE_OFLDCONN_COMPL :
if ( unlikely ( ! qedi_conn ) )
break ;
QEDI_INFO ( & qedi - > dbg_ctx , QEDI_LOG_INFO ,
" Active cmd count=%d, abrt_conn=%d, ep state=0x%x, cid=0x%x, qedi_conn=%p \n " ,
qedi_conn - > active_cmd_count , abrt_conn ,
qedi_ep - > state ,
qedi_ep - > iscsi_cid ,
qedi_ep - > conn
) ;
if ( ! qedi_conn - > active_cmd_count )
abrt_conn = 0 ;
else
abrt_conn = 1 ;
if ( abrt_conn )
qedi_clearsq ( qedi , qedi_conn , NULL ) ;
break ;
default :
break ;
}
qedi_ep - > state = EP_STATE_DISCONN_START ;
ret = qedi_ops - > destroy_conn ( qedi - > cdev , qedi_ep - > handle , abrt_conn ) ;
if ( ret ) {
QEDI_WARN ( & qedi - > dbg_ctx ,
" destroy_conn failed returned %d \n " , ret ) ;
} else {
ret = wait_event_interruptible_timeout (
qedi_ep - > tcp_ofld_wait ,
( qedi_ep - > state ! =
EP_STATE_DISCONN_START ) ,
wait_delay ) ;
if ( ( ret < = 0 ) | | ( qedi_ep - > state = = EP_STATE_DISCONN_START ) ) {
QEDI_WARN ( & qedi - > dbg_ctx ,
" Destroy conn timedout or interrupted, ret=%d, delay=%d, cid=0x%x \n " ,
ret , wait_delay , qedi_ep - > iscsi_cid ) ;
}
}
ep_release_conn :
ret = qedi_ops - > release_conn ( qedi - > cdev , qedi_ep - > handle ) ;
if ( ret )
QEDI_WARN ( & qedi - > dbg_ctx ,
" release_conn returned %d, cid=0x%x \n " ,
ret , qedi_ep - > iscsi_cid ) ;
ep_exit_recover :
qedi_ep - > state = EP_STATE_IDLE ;
qedi - > ep_tbl [ qedi_ep - > iscsi_cid ] = NULL ;
qedi - > cid_que . conn_cid_tbl [ qedi_ep - > iscsi_cid ] = NULL ;
qedi_free_id ( & qedi - > lcl_port_tbl , qedi_ep - > src_port ) ;
qedi_free_sq ( qedi , qedi_ep ) ;
if ( qedi_conn )
qedi_conn - > ep = NULL ;
qedi_ep - > conn = NULL ;
qedi_ep - > qedi = NULL ;
atomic_dec ( & qedi - > num_offloads ) ;
iscsi_destroy_endpoint ( ep ) ;
}
static int qedi_data_avail ( struct qedi_ctx * qedi , u16 vlanid )
{
struct qed_dev * cdev = qedi - > cdev ;
struct qedi_uio_dev * udev ;
struct qedi_uio_ctrl * uctrl ;
struct sk_buff * skb ;
u32 len ;
int rc = 0 ;
udev = qedi - > udev ;
if ( ! udev ) {
QEDI_ERR ( & qedi - > dbg_ctx , " udev is NULL. \n " ) ;
return - EINVAL ;
}
uctrl = ( struct qedi_uio_ctrl * ) udev - > uctrl ;
if ( ! uctrl ) {
QEDI_ERR ( & qedi - > dbg_ctx , " uctlr is NULL. \n " ) ;
return - EINVAL ;
}
len = uctrl - > host_tx_pkt_len ;
if ( ! len ) {
QEDI_ERR ( & qedi - > dbg_ctx , " Invalid len %u \n " , len ) ;
return - EINVAL ;
}
skb = alloc_skb ( len , GFP_ATOMIC ) ;
if ( ! skb ) {
QEDI_ERR ( & qedi - > dbg_ctx , " alloc_skb failed \n " ) ;
return - EINVAL ;
}
skb_put ( skb , len ) ;
memcpy ( skb - > data , udev - > tx_pkt , len ) ;
skb - > ip_summed = CHECKSUM_NONE ;
if ( vlanid )
__vlan_hwaccel_put_tag ( skb , htons ( ETH_P_8021Q ) , vlanid ) ;
2018-05-05 18:43:02 -07:00
rc = qedi_ops - > ll2 - > start_xmit ( cdev , skb , 0 ) ;
2016-12-01 00:21:08 -08:00
if ( rc ) {
QEDI_ERR ( & qedi - > dbg_ctx , " ll2 start_xmit returned %d \n " ,
rc ) ;
kfree_skb ( skb ) ;
}
uctrl - > host_tx_pkt_len = 0 ;
uctrl - > hw_tx_cons + + ;
return rc ;
}
static void qedi_offload_work ( struct work_struct * work )
{
struct qedi_endpoint * qedi_ep =
container_of ( work , struct qedi_endpoint , offload_work ) ;
struct qedi_ctx * qedi ;
2019-04-26 03:55:46 -07:00
int wait_delay = 5 * HZ ;
2016-12-01 00:21:08 -08:00
int ret ;
qedi = qedi_ep - > qedi ;
ret = qedi_iscsi_offload_conn ( qedi_ep ) ;
if ( ret ) {
QEDI_ERR ( & qedi - > dbg_ctx ,
" offload error: iscsi_cid=%u, qedi_ep=%p, ret=%d \n " ,
qedi_ep - > iscsi_cid , qedi_ep , ret ) ;
qedi_ep - > state = EP_STATE_OFLDCONN_FAILED ;
return ;
}
ret = wait_event_interruptible_timeout ( qedi_ep - > tcp_ofld_wait ,
( qedi_ep - > state = =
EP_STATE_OFLDCONN_COMPL ) ,
wait_delay ) ;
if ( ( ret < = 0 ) | | ( qedi_ep - > state ! = EP_STATE_OFLDCONN_COMPL ) ) {
qedi_ep - > state = EP_STATE_OFLDCONN_FAILED ;
QEDI_ERR ( & qedi - > dbg_ctx ,
" Offload conn TIMEOUT iscsi_cid=%u, qedi_ep=%p \n " ,
qedi_ep - > iscsi_cid , qedi_ep ) ;
}
}
static int qedi_set_path ( struct Scsi_Host * shost , struct iscsi_path * path_data )
{
struct qedi_ctx * qedi ;
struct qedi_endpoint * qedi_ep ;
int ret = 0 ;
u32 iscsi_cid ;
u16 port_id = 0 ;
if ( ! shost ) {
ret = - ENXIO ;
QEDI_ERR ( NULL , " shost is NULL \n " ) ;
return ret ;
}
if ( strcmp ( shost - > hostt - > proc_name , " qedi " ) ) {
ret = - ENXIO ;
QEDI_ERR ( NULL , " shost %s is invalid \n " ,
shost - > hostt - > proc_name ) ;
return ret ;
}
qedi = iscsi_host_priv ( shost ) ;
if ( path_data - > handle = = QEDI_PATH_HANDLE ) {
ret = qedi_data_avail ( qedi , path_data - > vlan_id ) ;
goto set_path_exit ;
}
iscsi_cid = ( u32 ) path_data - > handle ;
qedi_ep = qedi - > ep_tbl [ iscsi_cid ] ;
2017-05-19 01:33:18 -07:00
QEDI_INFO ( & qedi - > dbg_ctx , QEDI_LOG_INFO ,
2016-12-01 00:21:08 -08:00
" iscsi_cid=0x%x, qedi_ep=%p \n " , iscsi_cid , qedi_ep ) ;
2017-05-19 01:33:18 -07:00
if ( ! qedi_ep ) {
ret = - EINVAL ;
goto set_path_exit ;
}
2016-12-01 00:21:08 -08:00
if ( ! is_valid_ether_addr ( & path_data - > mac_addr [ 0 ] ) ) {
QEDI_NOTICE ( & qedi - > dbg_ctx , " dst mac NOT VALID \n " ) ;
2019-01-09 01:39:07 -08:00
qedi_ep - > state = EP_STATE_OFLDCONN_NONE ;
2016-12-01 00:21:08 -08:00
ret = - EIO ;
goto set_path_exit ;
}
ether_addr_copy ( & qedi_ep - > src_mac [ 0 ] , & qedi - > mac [ 0 ] ) ;
ether_addr_copy ( & qedi_ep - > dst_mac [ 0 ] , & path_data - > mac_addr [ 0 ] ) ;
qedi_ep - > vlan_id = path_data - > vlan_id ;
if ( path_data - > pmtu < DEF_PATH_MTU ) {
qedi_ep - > pmtu = qedi - > ll2_mtu ;
QEDI_INFO ( & qedi - > dbg_ctx , QEDI_LOG_INFO ,
" MTU cannot be %u, using default MTU %u \n " ,
path_data - > pmtu , qedi_ep - > pmtu ) ;
}
if ( path_data - > pmtu ! = qedi - > ll2_mtu ) {
if ( path_data - > pmtu > JUMBO_MTU ) {
ret = - EINVAL ;
QEDI_ERR ( NULL , " Invalid MTU %u \n " , path_data - > pmtu ) ;
goto set_path_exit ;
}
qedi_reset_host_mtu ( qedi , path_data - > pmtu ) ;
qedi_ep - > pmtu = qedi - > ll2_mtu ;
}
port_id = qedi_ep - > src_port ;
if ( port_id > = QEDI_LOCAL_PORT_MIN & &
port_id < QEDI_LOCAL_PORT_MAX ) {
if ( qedi_alloc_id ( & qedi - > lcl_port_tbl , port_id ) )
port_id = 0 ;
} else {
port_id = 0 ;
}
if ( ! port_id ) {
port_id = qedi_alloc_new_id ( & qedi - > lcl_port_tbl ) ;
if ( port_id = = QEDI_LOCAL_PORT_INVALID ) {
QEDI_ERR ( & qedi - > dbg_ctx ,
" Failed to allocate port id for iscsi_cid=0x%x \n " ,
iscsi_cid ) ;
ret = - ENOMEM ;
goto set_path_exit ;
}
}
qedi_ep - > src_port = port_id ;
if ( qedi_ep - > ip_type = = TCP_IPV4 ) {
memcpy ( & qedi_ep - > src_addr [ 0 ] , & path_data - > src . v4_addr ,
sizeof ( struct in_addr ) ) ;
memcpy ( & qedi - > src_ip [ 0 ] , & path_data - > src . v4_addr ,
sizeof ( struct in_addr ) ) ;
qedi - > ip_type = TCP_IPV4 ;
QEDI_INFO ( & qedi - > dbg_ctx , QEDI_LOG_CONN ,
" src addr:port=%pI4:%u, dst addr:port=%pI4:%u \n " ,
qedi_ep - > src_addr , qedi_ep - > src_port ,
qedi_ep - > dst_addr , qedi_ep - > dst_port ) ;
} else {
memcpy ( & qedi_ep - > src_addr [ 0 ] , & path_data - > src . v6_addr ,
sizeof ( struct in6_addr ) ) ;
memcpy ( & qedi - > src_ip [ 0 ] , & path_data - > src . v6_addr ,
sizeof ( struct in6_addr ) ) ;
qedi - > ip_type = TCP_IPV6 ;
QEDI_INFO ( & qedi - > dbg_ctx , QEDI_LOG_CONN ,
" src addr:port=%pI6:%u, dst addr:port=%pI6:%u \n " ,
qedi_ep - > src_addr , qedi_ep - > src_port ,
qedi_ep - > dst_addr , qedi_ep - > dst_port ) ;
}
INIT_WORK ( & qedi_ep - > offload_work , qedi_offload_work ) ;
queue_work ( qedi - > offload_thread , & qedi_ep - > offload_work ) ;
ret = 0 ;
set_path_exit :
return ret ;
}
static umode_t qedi_attr_is_visible ( int param_type , int param )
{
switch ( param_type ) {
case ISCSI_HOST_PARAM :
switch ( param ) {
case ISCSI_HOST_PARAM_NETDEV_NAME :
case ISCSI_HOST_PARAM_HWADDRESS :
case ISCSI_HOST_PARAM_IPADDRESS :
return 0444 ;
default :
return 0 ;
}
case ISCSI_PARAM :
switch ( param ) {
case ISCSI_PARAM_MAX_RECV_DLENGTH :
case ISCSI_PARAM_MAX_XMIT_DLENGTH :
case ISCSI_PARAM_HDRDGST_EN :
case ISCSI_PARAM_DATADGST_EN :
case ISCSI_PARAM_CONN_ADDRESS :
case ISCSI_PARAM_CONN_PORT :
case ISCSI_PARAM_EXP_STATSN :
case ISCSI_PARAM_PERSISTENT_ADDRESS :
case ISCSI_PARAM_PERSISTENT_PORT :
case ISCSI_PARAM_PING_TMO :
case ISCSI_PARAM_RECV_TMO :
case ISCSI_PARAM_INITIAL_R2T_EN :
case ISCSI_PARAM_MAX_R2T :
case ISCSI_PARAM_IMM_DATA_EN :
case ISCSI_PARAM_FIRST_BURST :
case ISCSI_PARAM_MAX_BURST :
case ISCSI_PARAM_PDU_INORDER_EN :
case ISCSI_PARAM_DATASEQ_INORDER_EN :
case ISCSI_PARAM_ERL :
case ISCSI_PARAM_TARGET_NAME :
case ISCSI_PARAM_TPGT :
case ISCSI_PARAM_USERNAME :
case ISCSI_PARAM_PASSWORD :
case ISCSI_PARAM_USERNAME_IN :
case ISCSI_PARAM_PASSWORD_IN :
case ISCSI_PARAM_FAST_ABORT :
case ISCSI_PARAM_ABORT_TMO :
case ISCSI_PARAM_LU_RESET_TMO :
case ISCSI_PARAM_TGT_RESET_TMO :
case ISCSI_PARAM_IFACE_NAME :
case ISCSI_PARAM_INITIATOR_NAME :
case ISCSI_PARAM_BOOT_ROOT :
case ISCSI_PARAM_BOOT_NIC :
case ISCSI_PARAM_BOOT_TARGET :
return 0444 ;
default :
return 0 ;
}
}
return 0 ;
}
static void qedi_cleanup_task ( struct iscsi_task * task )
{
if ( ! task - > sc | | task - > state = = ISCSI_TASK_PENDING ) {
QEDI_INFO ( NULL , QEDI_LOG_IO , " Returning ref_cnt=%d \n " ,
2017-03-09 13:46:58 +02:00
refcount_read ( & task - > refcount ) ) ;
2016-12-01 00:21:08 -08:00
return ;
}
qedi_iscsi_unmap_sg_list ( task - > dd_data ) ;
}
struct iscsi_transport qedi_iscsi_transport = {
. owner = THIS_MODULE ,
. name = QEDI_MODULE_NAME ,
. caps = CAP_RECOVERY_L0 | CAP_HDRDGST | CAP_MULTI_R2T | CAP_DATADGST |
CAP_DATA_PATH_OFFLOAD | CAP_TEXT_NEGO ,
. create_session = qedi_session_create ,
. destroy_session = qedi_session_destroy ,
. create_conn = qedi_conn_create ,
. bind_conn = qedi_conn_bind ,
. start_conn = qedi_conn_start ,
. stop_conn = iscsi_conn_stop ,
. destroy_conn = qedi_conn_destroy ,
. set_param = iscsi_set_param ,
. get_ep_param = qedi_ep_get_param ,
. get_conn_param = iscsi_conn_get_param ,
. get_session_param = iscsi_session_get_param ,
. get_host_param = qedi_host_get_param ,
. send_pdu = iscsi_conn_send_pdu ,
. get_stats = qedi_conn_get_stats ,
. xmit_task = qedi_task_xmit ,
. cleanup_task = qedi_cleanup_task ,
. session_recovery_timedout = iscsi_session_recovery_timedout ,
. ep_connect = qedi_ep_connect ,
. ep_poll = qedi_ep_poll ,
. ep_disconnect = qedi_ep_disconnect ,
. set_path = qedi_set_path ,
. attr_is_visible = qedi_attr_is_visible ,
} ;
void qedi_start_conn_recovery ( struct qedi_ctx * qedi ,
struct qedi_conn * qedi_conn )
{
struct iscsi_cls_session * cls_sess ;
struct iscsi_cls_conn * cls_conn ;
struct iscsi_conn * conn ;
cls_conn = qedi_conn - > cls_conn ;
conn = cls_conn - > dd_data ;
cls_sess = iscsi_conn_to_session ( cls_conn ) ;
if ( iscsi_is_session_online ( cls_sess ) ) {
qedi_conn - > abrt_conn = 1 ;
QEDI_ERR ( & qedi - > dbg_ctx ,
" Failing connection, state=0x%x, cid=0x%x \n " ,
conn - > session - > state , qedi_conn - > iscsi_conn_id ) ;
iscsi_conn_failure ( qedi_conn - > cls_conn - > dd_data ,
ISCSI_ERR_CONN_FAILED ) ;
}
}
static const struct {
enum iscsi_error_types error_code ;
char * err_string ;
} qedi_iscsi_error [ ] = {
{ ISCSI_STATUS_NONE ,
" tcp_error none "
} ,
{ ISCSI_CONN_ERROR_TASK_CID_MISMATCH ,
" task cid mismatch "
} ,
{ ISCSI_CONN_ERROR_TASK_NOT_VALID ,
" invalid task "
} ,
{ ISCSI_CONN_ERROR_RQ_RING_IS_FULL ,
" rq ring full "
} ,
{ ISCSI_CONN_ERROR_CMDQ_RING_IS_FULL ,
" cmdq ring full "
} ,
{ ISCSI_CONN_ERROR_HQE_CACHING_FAILED ,
" sge caching failed "
} ,
{ ISCSI_CONN_ERROR_HEADER_DIGEST_ERROR ,
" hdr digest error "
} ,
{ ISCSI_CONN_ERROR_LOCAL_COMPLETION_ERROR ,
" local cmpl error "
} ,
{ ISCSI_CONN_ERROR_DATA_OVERRUN ,
" invalid task "
} ,
{ ISCSI_CONN_ERROR_OUT_OF_SGES_ERROR ,
" out of sge error "
} ,
{ ISCSI_CONN_ERROR_TCP_IP_FRAGMENT_ERROR ,
" tcp ip fragment error "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_AHS_LEN ,
" AHS len protocol error "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_ITT_OUT_OF_RANGE ,
" itt out of range error "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_DATA_SEG_LEN_EXCEEDS_PDU_SIZE ,
" data seg more than pdu size "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_INVALID_OPCODE ,
" invalid opcode "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_INVALID_OPCODE_BEFORE_UPDATE ,
" invalid opcode before update "
} ,
{ ISCSI_CONN_ERROR_UNVALID_NOPIN_DSL ,
" unexpected opcode "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_R2T_CARRIES_NO_DATA ,
" r2t carries no data "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_DATA_SN ,
" data sn error "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_DATA_IN_TTT ,
" data TTT error "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_R2T_TTT ,
" r2t TTT error "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_R2T_BUFFER_OFFSET ,
" buffer offset error "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_BUFFER_OFFSET_OOO ,
" buffer offset ooo "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_R2T_SN ,
" data seg len 0 "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_0 ,
" data xer len error "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_1 ,
" data xer len1 error "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_2 ,
" data xer len2 error "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_LUN ,
" protocol lun error "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_F_BIT_ZERO ,
" f bit zero error "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_EXP_STAT_SN ,
" exp stat sn error "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_DSL_NOT_ZERO ,
" dsl not zero error "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_INVALID_DSL ,
" invalid dsl "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_DATA_SEG_LEN_TOO_BIG ,
" data seg len too big "
} ,
{ ISCSI_CONN_ERROR_PROTOCOL_ERR_OUTSTANDING_R2T_COUNT ,
" outstanding r2t count error "
} ,
{ ISCSI_CONN_ERROR_SENSE_DATA_LENGTH ,
" sense datalen error "
} ,
} ;
char * qedi_get_iscsi_error ( enum iscsi_error_types err_code )
{
int i ;
char * msg = NULL ;
for ( i = 0 ; i < ARRAY_SIZE ( qedi_iscsi_error ) ; i + + ) {
if ( qedi_iscsi_error [ i ] . error_code = = err_code ) {
msg = qedi_iscsi_error [ i ] . err_string ;
break ;
}
}
return msg ;
}
2017-12-27 19:30:07 +02:00
void qedi_process_iscsi_error ( struct qedi_endpoint * ep ,
struct iscsi_eqe_data * data )
2016-12-01 00:21:08 -08:00
{
struct qedi_conn * qedi_conn ;
struct qedi_ctx * qedi ;
char warn_notice [ ] = " iscsi_warning " ;
char error_notice [ ] = " iscsi_error " ;
char unknown_msg [ ] = " Unknown error " ;
char * message ;
int need_recovery = 0 ;
u32 err_mask = 0 ;
char * msg ;
if ( ! ep )
return ;
qedi_conn = ep - > conn ;
if ( ! qedi_conn )
return ;
qedi = ep - > qedi ;
QEDI_ERR ( & qedi - > dbg_ctx , " async event iscsi error:0x%x \n " ,
data - > error_code ) ;
if ( err_mask ) {
need_recovery = 0 ;
message = warn_notice ;
} else {
need_recovery = 1 ;
message = error_notice ;
}
msg = qedi_get_iscsi_error ( data - > error_code ) ;
if ( ! msg ) {
need_recovery = 0 ;
msg = unknown_msg ;
}
iscsi_conn_printk ( KERN_ALERT ,
qedi_conn - > cls_conn - > dd_data ,
" qedi: %s - %s \n " , message , msg ) ;
if ( need_recovery )
qedi_start_conn_recovery ( qedi_conn - > qedi , qedi_conn ) ;
}
2017-12-27 19:30:07 +02:00
void qedi_process_tcp_error ( struct qedi_endpoint * ep ,
struct iscsi_eqe_data * data )
2016-12-01 00:21:08 -08:00
{
struct qedi_conn * qedi_conn ;
if ( ! ep )
return ;
qedi_conn = ep - > conn ;
if ( ! qedi_conn )
return ;
QEDI_ERR ( & ep - > qedi - > dbg_ctx , " async event TCP error:0x%x \n " ,
data - > error_code ) ;
qedi_start_conn_recovery ( qedi_conn - > qedi , qedi_conn ) ;
}