2005-08-05 06:30:08 +04:00
/*
* iSCSI Initiator over TCP / IP Data - Path
*
* Copyright ( C ) 2004 Dmitry Yusupov
* Copyright ( C ) 2004 Alex Aizman
2006-04-07 06:26:46 +04:00
* Copyright ( C ) 2005 - 2006 Mike Christie
* Copyright ( C ) 2006 Red Hat , Inc . All rights reserved .
2005-08-05 06:30:08 +04:00
* maintained by open - iscsi @ googlegroups . com
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* See the file COPYING included with this distribution for more details .
*
* Credits :
* Christoph Hellwig
* FUJITA Tomonori
* Arne Redlich
* Zhenyu Wang
*/
# include <linux/types.h>
# include <linux/list.h>
# include <linux/inet.h>
# include <linux/blkdev.h>
# include <linux/crypto.h>
# include <linux/delay.h>
# include <linux/kfifo.h>
# include <linux/scatterlist.h>
2006-01-11 15:16:10 +03:00
# include <linux/mutex.h>
2005-08-05 06:30:08 +04:00
# include <net/tcp.h>
# include <scsi/scsi_cmnd.h>
# include <scsi/scsi_host.h>
# include <scsi/scsi.h>
# include <scsi/scsi_transport_iscsi.h>
# include "iscsi_tcp.h"
MODULE_AUTHOR ( " Dmitry Yusupov <dmitry_yus@yahoo.com>, "
" Alex Aizman <itn780@yahoo.com> " ) ;
MODULE_DESCRIPTION ( " iSCSI/TCP data-path " ) ;
MODULE_LICENSE ( " GPL " ) ;
2005-11-30 08:13:01 +03:00
MODULE_VERSION ( " 0:4.445 " ) ;
2005-08-05 06:30:08 +04:00
/* #define DEBUG_TCP */
# define DEBUG_ASSERT
# ifdef DEBUG_TCP
2006-04-07 06:26:46 +04:00
# define debug_tcp(fmt...) printk(KERN_INFO "tcp: " fmt)
2005-08-05 06:30:08 +04:00
# else
# define debug_tcp(fmt...)
# endif
# ifndef DEBUG_ASSERT
# ifdef BUG_ON
# undef BUG_ON
# endif
# define BUG_ON(expr)
# endif
static unsigned int iscsi_max_lun = 512 ;
module_param_named ( max_lun , iscsi_max_lun , uint , S_IRUGO ) ;
/* global data */
static kmem_cache_t * taskcache ;
static inline void
iscsi_buf_init_virt ( struct iscsi_buf * ibuf , char * vbuf , int size )
{
sg_init_one ( & ibuf - > sg , ( u8 * ) vbuf , size ) ;
ibuf - > sent = 0 ;
2006-01-14 03:05:47 +03:00
ibuf - > use_sendmsg = 0 ;
2005-08-05 06:30:08 +04:00
}
static inline void
iscsi_buf_init_iov ( struct iscsi_buf * ibuf , char * vbuf , int size )
{
2006-01-14 03:05:47 +03:00
ibuf - > sg . page = virt_to_page ( vbuf ) ;
ibuf - > sg . offset = offset_in_page ( vbuf ) ;
2005-08-05 06:30:08 +04:00
ibuf - > sg . length = size ;
ibuf - > sent = 0 ;
2006-01-14 03:05:47 +03:00
ibuf - > use_sendmsg = 1 ;
2005-08-05 06:30:08 +04:00
}
static inline void
iscsi_buf_init_sg ( struct iscsi_buf * ibuf , struct scatterlist * sg )
{
2006-01-14 03:05:47 +03:00
ibuf - > sg . page = sg - > page ;
ibuf - > sg . offset = sg - > offset ;
ibuf - > sg . length = sg - > length ;
2005-08-05 06:30:08 +04:00
/*
* Fastpath : sg element fits into single page
*/
2006-01-14 03:05:56 +03:00
if ( sg - > length + sg - > offset < = PAGE_SIZE & & ! PageSlab ( sg - > page ) )
2006-01-14 03:05:47 +03:00
ibuf - > use_sendmsg = 0 ;
else
ibuf - > use_sendmsg = 1 ;
2005-08-05 06:30:08 +04:00
ibuf - > sent = 0 ;
}
static inline int
iscsi_buf_left ( struct iscsi_buf * ibuf )
{
int rc ;
rc = ibuf - > sg . length - ibuf - > sent ;
BUG_ON ( rc < 0 ) ;
return rc ;
}
static inline void
2005-09-13 06:01:32 +04:00
iscsi_hdr_digest ( struct iscsi_conn * conn , struct iscsi_buf * buf ,
u8 * crc )
2005-08-05 06:30:08 +04:00
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
crypto_digest_digest ( tcp_conn - > tx_tfm , & buf - > sg , 1 , crc ) ;
buf - > sg . length + = sizeof ( uint32_t ) ;
2005-08-05 06:30:08 +04:00
}
static inline int
2006-04-07 06:26:46 +04:00
iscsi_hdr_extract ( struct iscsi_tcp_conn * tcp_conn )
2005-08-05 06:30:08 +04:00
{
2006-04-07 06:26:46 +04:00
struct sk_buff * skb = tcp_conn - > in . skb ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
tcp_conn - > in . zero_copy_hdr = 0 ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
if ( tcp_conn - > in . copy > = tcp_conn - > hdr_size & &
tcp_conn - > in_progress = = IN_PROGRESS_WAIT_HEADER ) {
2005-08-05 06:30:08 +04:00
/*
* Zero - copy PDU Header : using connection context
* to store header pointer .
*/
if ( skb_shinfo ( skb ) - > frag_list = = NULL & &
2006-04-07 06:26:46 +04:00
! skb_shinfo ( skb ) - > nr_frags ) {
tcp_conn - > in . hdr = ( struct iscsi_hdr * )
( ( char * ) skb - > data + tcp_conn - > in . offset ) ;
tcp_conn - > in . zero_copy_hdr = 1 ;
} else {
2005-08-05 06:30:08 +04:00
/* ignoring return code since we checked
* in . copy before */
2006-04-07 06:26:46 +04:00
skb_copy_bits ( skb , tcp_conn - > in . offset ,
& tcp_conn - > hdr , tcp_conn - > hdr_size ) ;
tcp_conn - > in . hdr = & tcp_conn - > hdr ;
2005-08-05 06:30:08 +04:00
}
2006-04-07 06:26:46 +04:00
tcp_conn - > in . offset + = tcp_conn - > hdr_size ;
tcp_conn - > in . copy - = tcp_conn - > hdr_size ;
2005-08-05 06:30:08 +04:00
} else {
int hdr_remains ;
int copylen ;
/*
* PDU header scattered across SKB ' s ,
* copying it . . . This ' ll happen quite rarely .
*/
2006-04-07 06:26:46 +04:00
if ( tcp_conn - > in_progress = = IN_PROGRESS_WAIT_HEADER )
tcp_conn - > in . hdr_offset = 0 ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
hdr_remains = tcp_conn - > hdr_size - tcp_conn - > in . hdr_offset ;
2005-08-05 06:30:08 +04:00
BUG_ON ( hdr_remains < = 0 ) ;
2006-04-07 06:26:46 +04:00
copylen = min ( tcp_conn - > in . copy , hdr_remains ) ;
skb_copy_bits ( skb , tcp_conn - > in . offset ,
( char * ) & tcp_conn - > hdr + tcp_conn - > in . hdr_offset ,
copylen ) ;
2005-08-05 06:30:08 +04:00
debug_tcp ( " PDU gather offset %d bytes %d in.offset %d "
2006-04-07 06:26:46 +04:00
" in.copy %d \n " , tcp_conn - > in . hdr_offset , copylen ,
tcp_conn - > in . offset , tcp_conn - > in . copy ) ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
tcp_conn - > in . offset + = copylen ;
tcp_conn - > in . copy - = copylen ;
2005-08-05 06:30:08 +04:00
if ( copylen < hdr_remains ) {
2006-04-07 06:26:46 +04:00
tcp_conn - > in_progress = IN_PROGRESS_HEADER_GATHER ;
tcp_conn - > in . hdr_offset + = copylen ;
2005-08-05 06:30:08 +04:00
return - EAGAIN ;
}
2006-04-07 06:26:46 +04:00
tcp_conn - > in . hdr = & tcp_conn - > hdr ;
tcp_conn - > discontiguous_hdr_cnt + + ;
tcp_conn - > in_progress = IN_PROGRESS_WAIT_HEADER ;
2005-08-05 06:30:08 +04:00
}
return 0 ;
}
2006-04-07 06:13:39 +04:00
/*
* must be called with session lock
*/
static void
__iscsi_ctask_cleanup ( struct iscsi_conn * conn , struct iscsi_cmd_task * ctask )
2005-08-05 06:30:08 +04:00
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
2006-04-07 06:13:39 +04:00
struct scsi_cmnd * sc ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:13:39 +04:00
sc = ctask - > sc ;
if ( unlikely ( ! sc ) )
2005-08-05 06:30:08 +04:00
return ;
2006-04-07 06:13:39 +04:00
2005-08-05 06:30:08 +04:00
if ( sc - > sc_data_direction = = DMA_TO_DEVICE ) {
struct iscsi_data_task * dtask , * n ;
2006-04-07 06:26:46 +04:00
2005-08-05 06:30:08 +04:00
/* WRITE: cleanup Data-Out's if any */
2006-04-07 06:26:46 +04:00
list_for_each_entry_safe ( dtask , n , & tcp_ctask - > dataqueue ,
item ) {
2005-08-05 06:30:08 +04:00
list_del ( & dtask - > item ) ;
2006-04-07 06:26:46 +04:00
mempool_free ( dtask , tcp_ctask - > datapool ) ;
2005-08-05 06:30:08 +04:00
}
}
2006-04-07 06:26:46 +04:00
tcp_ctask - > xmstate = XMSTATE_IDLE ;
tcp_ctask - > r2t = NULL ;
2005-08-05 06:30:08 +04:00
}
/**
* iscsi_data_rsp - SCSI Data - In Response processing
* @ conn : iscsi connection
* @ ctask : scsi command task
* */
static int
iscsi_data_rsp ( struct iscsi_conn * conn , struct iscsi_cmd_task * ctask )
{
int rc ;
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
struct iscsi_data_rsp * rhdr = ( struct iscsi_data_rsp * ) tcp_conn - > in . hdr ;
2005-08-05 06:30:08 +04:00
struct iscsi_session * session = conn - > session ;
int datasn = be32_to_cpu ( rhdr - > datasn ) ;
rc = iscsi_check_assign_cmdsn ( session , ( struct iscsi_nopin * ) rhdr ) ;
if ( rc )
return rc ;
/*
* setup Data - In byte counter ( gets decremented . . )
*/
2006-04-07 06:26:46 +04:00
ctask - > data_count = tcp_conn - > in . datalen ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
if ( tcp_conn - > in . datalen = = 0 )
2005-08-05 06:30:08 +04:00
return 0 ;
if ( ctask - > datasn ! = datasn )
return ISCSI_ERR_DATASN ;
ctask - > datasn + + ;
2006-04-07 06:26:46 +04:00
tcp_ctask - > data_offset = be32_to_cpu ( rhdr - > offset ) ;
if ( tcp_ctask - > data_offset + tcp_conn - > in . datalen > ctask - > total_length )
2005-08-05 06:30:08 +04:00
return ISCSI_ERR_DATA_OFFSET ;
if ( rhdr - > flags & ISCSI_FLAG_DATA_STATUS ) {
struct scsi_cmnd * sc = ctask - > sc ;
conn - > exp_statsn = be32_to_cpu ( rhdr - > statsn ) + 1 ;
2006-01-14 03:05:38 +03:00
if ( rhdr - > flags & ISCSI_FLAG_DATA_UNDERFLOW ) {
2005-08-05 06:30:08 +04:00
int res_count = be32_to_cpu ( rhdr - > residual_count ) ;
if ( res_count > 0 & &
res_count < = sc - > request_bufflen ) {
sc - > resid = res_count ;
sc - > result = ( DID_OK < < 16 ) | rhdr - > cmd_status ;
} else
sc - > result = ( DID_BAD_TARGET < < 16 ) |
rhdr - > cmd_status ;
2006-01-14 03:05:38 +03:00
} else if ( rhdr - > flags & ISCSI_FLAG_DATA_OVERFLOW ) {
2005-08-05 06:30:08 +04:00
sc - > resid = be32_to_cpu ( rhdr - > residual_count ) ;
sc - > result = ( DID_OK < < 16 ) | rhdr - > cmd_status ;
} else
sc - > result = ( DID_OK < < 16 ) | rhdr - > cmd_status ;
}
conn - > datain_pdus_cnt + + ;
return 0 ;
}
/**
* iscsi_solicit_data_init - initialize first Data - Out
* @ conn : iscsi connection
* @ ctask : scsi command task
* @ r2t : R2T info
*
* Notes :
* Initialize first Data - Out within this R2T sequence and finds
* proper data_offset within this SCSI command .
*
* This function is called with connection lock taken .
* */
static void
iscsi_solicit_data_init ( struct iscsi_conn * conn , struct iscsi_cmd_task * ctask ,
struct iscsi_r2t_info * r2t )
{
struct iscsi_data * hdr ;
struct iscsi_data_task * dtask ;
struct scsi_cmnd * sc = ctask - > sc ;
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
dtask = mempool_alloc ( tcp_ctask - > datapool , GFP_ATOMIC ) ;
2005-08-05 06:30:08 +04:00
BUG_ON ( ! dtask ) ;
2006-04-07 06:13:39 +04:00
INIT_LIST_HEAD ( & dtask - > item ) ;
2005-08-05 06:30:08 +04:00
hdr = & dtask - > hdr ;
memset ( hdr , 0 , sizeof ( struct iscsi_data ) ) ;
hdr - > ttt = r2t - > ttt ;
hdr - > datasn = cpu_to_be32 ( r2t - > solicit_datasn ) ;
r2t - > solicit_datasn + + ;
hdr - > opcode = ISCSI_OP_SCSI_DATA_OUT ;
2006-04-07 06:26:46 +04:00
memcpy ( hdr - > lun , ctask - > hdr - > lun , sizeof ( hdr - > lun ) ) ;
hdr - > itt = ctask - > hdr - > itt ;
2005-08-05 06:30:08 +04:00
hdr - > exp_statsn = r2t - > exp_statsn ;
hdr - > offset = cpu_to_be32 ( r2t - > data_offset ) ;
if ( r2t - > data_length > conn - > max_xmit_dlength ) {
hton24 ( hdr - > dlength , conn - > max_xmit_dlength ) ;
r2t - > data_count = conn - > max_xmit_dlength ;
hdr - > flags = 0 ;
} else {
hton24 ( hdr - > dlength , r2t - > data_length ) ;
r2t - > data_count = r2t - > data_length ;
hdr - > flags = ISCSI_FLAG_CMD_FINAL ;
}
conn - > dataout_pdus_cnt + + ;
r2t - > sent = 0 ;
2005-09-13 06:01:32 +04:00
iscsi_buf_init_virt ( & r2t - > headbuf , ( char * ) hdr ,
sizeof ( struct iscsi_hdr ) ) ;
2005-08-05 06:30:08 +04:00
r2t - > dtask = dtask ;
if ( sc - > use_sg ) {
int i , sg_count = 0 ;
struct scatterlist * sg = sc - > request_buffer ;
r2t - > sg = NULL ;
for ( i = 0 ; i < sc - > use_sg ; i + + , sg + = 1 ) {
/* FIXME: prefetch ? */
if ( sg_count + sg - > length > r2t - > data_offset ) {
int page_offset ;
/* sg page found! */
/* offset within this page */
page_offset = r2t - > data_offset - sg_count ;
/* fill in this buffer */
iscsi_buf_init_sg ( & r2t - > sendbuf , sg ) ;
r2t - > sendbuf . sg . offset + = page_offset ;
r2t - > sendbuf . sg . length - = page_offset ;
/* xmit logic will continue with next one */
r2t - > sg = sg + 1 ;
break ;
}
sg_count + = sg - > length ;
}
BUG_ON ( r2t - > sg = = NULL ) ;
} else
2006-04-07 06:26:46 +04:00
iscsi_buf_init_iov ( & tcp_ctask - > sendbuf ,
2005-08-05 06:30:08 +04:00
( char * ) sc - > request_buffer + r2t - > data_offset ,
r2t - > data_count ) ;
2006-04-07 06:26:46 +04:00
list_add ( & dtask - > item , & tcp_ctask - > dataqueue ) ;
2005-08-05 06:30:08 +04:00
}
/**
* iscsi_r2t_rsp - iSCSI R2T Response processing
* @ conn : iscsi connection
* @ ctask : scsi command task
* */
static int
iscsi_r2t_rsp ( struct iscsi_conn * conn , struct iscsi_cmd_task * ctask )
{
struct iscsi_r2t_info * r2t ;
struct iscsi_session * session = conn - > session ;
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
struct iscsi_r2t_rsp * rhdr = ( struct iscsi_r2t_rsp * ) tcp_conn - > in . hdr ;
2005-08-05 06:30:08 +04:00
int r2tsn = be32_to_cpu ( rhdr - > r2tsn ) ;
int rc ;
2006-04-07 06:26:46 +04:00
if ( tcp_conn - > in . datalen )
2005-08-05 06:30:08 +04:00
return ISCSI_ERR_DATALEN ;
2006-04-07 06:26:46 +04:00
if ( tcp_ctask - > exp_r2tsn & & tcp_ctask - > exp_r2tsn ! = r2tsn )
2005-08-05 06:30:08 +04:00
return ISCSI_ERR_R2TSN ;
rc = iscsi_check_assign_cmdsn ( session , ( struct iscsi_nopin * ) rhdr ) ;
if ( rc )
return rc ;
/* FIXME: use R2TSN to detect missing R2T */
/* fill-in new R2T associated with the task */
spin_lock ( & session - > lock ) ;
if ( ! ctask - > sc | | ctask - > mtask | |
session - > state ! = ISCSI_STATE_LOGGED_IN ) {
printk ( KERN_INFO " iscsi_tcp: dropping R2T itt %d in "
" recovery... \n " , ctask - > itt ) ;
spin_unlock ( & session - > lock ) ;
return 0 ;
}
2006-04-07 06:26:46 +04:00
rc = __kfifo_get ( tcp_ctask - > r2tpool . queue , ( void * ) & r2t , sizeof ( void * ) ) ;
2005-08-05 06:30:08 +04:00
BUG_ON ( ! rc ) ;
r2t - > exp_statsn = rhdr - > statsn ;
r2t - > data_length = be32_to_cpu ( rhdr - > data_length ) ;
if ( r2t - > data_length = = 0 | |
r2t - > data_length > session - > max_burst ) {
spin_unlock ( & session - > lock ) ;
return ISCSI_ERR_DATALEN ;
}
r2t - > data_offset = be32_to_cpu ( rhdr - > data_offset ) ;
if ( r2t - > data_offset + r2t - > data_length > ctask - > total_length ) {
spin_unlock ( & session - > lock ) ;
return ISCSI_ERR_DATALEN ;
}
r2t - > ttt = rhdr - > ttt ; /* no flip */
r2t - > solicit_datasn = 0 ;
iscsi_solicit_data_init ( conn , ctask , r2t ) ;
2006-04-07 06:26:46 +04:00
tcp_ctask - > exp_r2tsn = r2tsn + 1 ;
tcp_ctask - > xmstate | = XMSTATE_SOL_HDR ;
__kfifo_put ( tcp_ctask - > r2tqueue , ( void * ) & r2t , sizeof ( void * ) ) ;
__kfifo_put ( conn - > xmitqueue , ( void * ) & ctask , sizeof ( void * ) ) ;
2005-08-05 06:30:08 +04:00
2006-01-14 03:05:53 +03:00
scsi_queue_work ( session - > host , & conn - > xmitwork ) ;
2005-08-05 06:30:08 +04:00
conn - > r2t_pdus_cnt + + ;
spin_unlock ( & session - > lock ) ;
return 0 ;
}
static int
2006-04-07 06:26:46 +04:00
iscsi_tcp_hdr_recv ( struct iscsi_conn * conn )
2005-08-05 06:30:08 +04:00
{
2006-04-07 06:26:46 +04:00
int rc = 0 , opcode , ahslen ;
2005-08-05 06:30:08 +04:00
struct iscsi_hdr * hdr ;
struct iscsi_session * session = conn - > session ;
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
uint32_t cdgst , rdgst = 0 , itt ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
hdr = tcp_conn - > in . hdr ;
2005-08-05 06:30:08 +04:00
/* verify PDU length */
2006-04-07 06:26:46 +04:00
tcp_conn - > in . datalen = ntoh24 ( hdr - > dlength ) ;
if ( tcp_conn - > in . datalen > conn - > max_recv_dlength ) {
2005-08-05 06:30:08 +04:00
printk ( KERN_ERR " iscsi_tcp: datalen %d > %d \n " ,
2006-04-07 06:26:46 +04:00
tcp_conn - > in . datalen , conn - > max_recv_dlength ) ;
2005-08-05 06:30:08 +04:00
return ISCSI_ERR_DATALEN ;
}
2006-04-07 06:26:46 +04:00
tcp_conn - > data_copied = 0 ;
2005-08-05 06:30:08 +04:00
/* read AHS */
2006-04-07 06:26:46 +04:00
ahslen = hdr - > hlength < < 2 ;
tcp_conn - > in . offset + = ahslen ;
tcp_conn - > in . copy - = ahslen ;
if ( tcp_conn - > in . copy < 0 ) {
2005-08-05 06:30:08 +04:00
printk ( KERN_ERR " iscsi_tcp: can't handle AHS with length "
2006-04-07 06:26:46 +04:00
" %d bytes \n " , ahslen ) ;
2005-08-05 06:30:08 +04:00
return ISCSI_ERR_AHSLEN ;
}
/* calculate read padding */
2006-04-07 06:26:46 +04:00
tcp_conn - > in . padding = tcp_conn - > in . datalen & ( ISCSI_PAD_LEN - 1 ) ;
if ( tcp_conn - > in . padding ) {
tcp_conn - > in . padding = ISCSI_PAD_LEN - tcp_conn - > in . padding ;
debug_scsi ( " read padding %d bytes \n " , tcp_conn - > in . padding ) ;
2005-08-05 06:30:08 +04:00
}
if ( conn - > hdrdgst_en ) {
struct scatterlist sg ;
sg_init_one ( & sg , ( u8 * ) hdr ,
2006-04-07 06:26:46 +04:00
sizeof ( struct iscsi_hdr ) + ahslen ) ;
crypto_digest_digest ( tcp_conn - > rx_tfm , & sg , 1 , ( u8 * ) & cdgst ) ;
2005-08-05 06:30:08 +04:00
rdgst = * ( uint32_t * ) ( ( char * ) hdr + sizeof ( struct iscsi_hdr ) +
2006-04-07 06:26:46 +04:00
ahslen ) ;
2005-11-30 11:27:19 +03:00
if ( cdgst ! = rdgst ) {
2006-04-07 06:26:46 +04:00
printk ( KERN_ERR " iscsi_tcp: hdrdgst error "
" recv 0x%x calc 0x%x \n " , rdgst , cdgst ) ;
2005-11-30 11:27:19 +03:00
return ISCSI_ERR_HDR_DGST ;
}
2005-08-05 06:30:08 +04:00
}
2006-04-07 06:26:46 +04:00
opcode = hdr - > opcode & ISCSI_OPCODE_MASK ;
2005-08-05 06:30:08 +04:00
/* verify itt (itt encoding: age+cid+itt) */
2006-04-07 06:26:46 +04:00
rc = iscsi_verify_itt ( conn , hdr , & itt ) ;
if ( rc = = ISCSI_ERR_NO_SCSI_CMD ) {
tcp_conn - > in . datalen = 0 ; /* force drop */
return 0 ;
} else if ( rc )
return rc ;
2005-08-05 06:30:08 +04:00
debug_tcp ( " opcode 0x%x offset %d copy %d ahslen %d datalen %d \n " ,
2006-04-07 06:26:46 +04:00
opcode , tcp_conn - > in . offset , tcp_conn - > in . copy ,
ahslen , tcp_conn - > in . datalen ) ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
switch ( opcode ) {
case ISCSI_OP_SCSI_DATA_IN :
tcp_conn - > in . ctask = session - > cmds [ itt ] ;
rc = iscsi_data_rsp ( conn , tcp_conn - > in . ctask ) ;
/* fall through */
case ISCSI_OP_SCSI_CMD_RSP :
tcp_conn - > in . ctask = session - > cmds [ itt ] ;
if ( tcp_conn - > in . datalen )
goto copy_hdr ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
spin_lock ( & session - > lock ) ;
__iscsi_ctask_cleanup ( conn , tcp_conn - > in . ctask ) ;
rc = __iscsi_complete_pdu ( conn , hdr , NULL , 0 ) ;
spin_unlock ( & session - > lock ) ;
break ;
case ISCSI_OP_R2T :
tcp_conn - > in . ctask = session - > cmds [ itt ] ;
if ( ahslen )
rc = ISCSI_ERR_AHSLEN ;
else if ( tcp_conn - > in . ctask - > sc - > sc_data_direction = =
DMA_TO_DEVICE )
rc = iscsi_r2t_rsp ( conn , tcp_conn - > in . ctask ) ;
else
rc = ISCSI_ERR_PROTO ;
break ;
case ISCSI_OP_LOGIN_RSP :
case ISCSI_OP_TEXT_RSP :
case ISCSI_OP_LOGOUT_RSP :
case ISCSI_OP_NOOP_IN :
case ISCSI_OP_REJECT :
case ISCSI_OP_ASYNC_EVENT :
if ( tcp_conn - > in . datalen )
goto copy_hdr ;
/* fall through */
case ISCSI_OP_SCSI_TMFUNC_RSP :
rc = iscsi_complete_pdu ( conn , hdr , NULL , 0 ) ;
break ;
default :
rc = ISCSI_ERR_BAD_OPCODE ;
break ;
}
2005-08-05 06:30:08 +04:00
return rc ;
2006-04-07 06:26:46 +04:00
copy_hdr :
/*
* if we did zero copy for the header but we will need multiple
* skbs to complete the command then we have to copy the header
* for later use
*/
if ( tcp_conn - > in . zero_copy_hdr & & tcp_conn - > in . copy <
( tcp_conn - > in . datalen + tcp_conn - > in . padding +
( conn - > datadgst_en ? 4 : 0 ) ) ) {
debug_tcp ( " Copying header for later use. in.copy %d in.datalen "
" %d \n " , tcp_conn - > in . copy , tcp_conn - > in . datalen ) ;
memcpy ( & tcp_conn - > hdr , tcp_conn - > in . hdr ,
sizeof ( struct iscsi_hdr ) ) ;
tcp_conn - > in . hdr = & tcp_conn - > hdr ;
tcp_conn - > in . zero_copy_hdr = 0 ;
}
return 0 ;
2005-08-05 06:30:08 +04:00
}
/**
* iscsi_ctask_copy - copy skb bits to the destanation cmd task
2006-04-07 06:26:46 +04:00
* @ conn : iscsi tcp connection
2005-08-05 06:30:08 +04:00
* @ ctask : scsi command task
* @ buf : buffer to copy to
* @ buf_size : size of buffer
* @ offset : offset within the buffer
*
* Notes :
* The function calls skb_copy_bits ( ) and updates per - connection and
* per - cmd byte counters .
*
* Read counters ( in bytes ) :
*
* conn - > in . offset offset within in progress SKB
* conn - > in . copy left to copy from in progress SKB
* including padding
* conn - > in . copied copied already from in progress SKB
* conn - > data_copied copied already from in progress buffer
* ctask - > sent total bytes sent up to the MidLayer
* ctask - > data_count left to copy from in progress Data - In
* buf_left left to copy from in progress buffer
* */
static inline int
2006-04-07 06:26:46 +04:00
iscsi_ctask_copy ( struct iscsi_tcp_conn * tcp_conn , struct iscsi_cmd_task * ctask ,
2005-08-05 06:30:08 +04:00
void * buf , int buf_size , int offset )
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
int buf_left = buf_size - ( tcp_conn - > data_copied + offset ) ;
int size = min ( tcp_conn - > in . copy , buf_left ) ;
2005-08-05 06:30:08 +04:00
int rc ;
size = min ( size , ctask - > data_count ) ;
debug_tcp ( " ctask_copy %d bytes at offset %d copied %d \n " ,
2006-04-07 06:26:46 +04:00
size , tcp_conn - > in . offset , tcp_conn - > in . copied ) ;
2005-08-05 06:30:08 +04:00
BUG_ON ( size < = 0 ) ;
2006-04-07 06:26:46 +04:00
BUG_ON ( tcp_ctask - > sent + size > ctask - > total_length ) ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
rc = skb_copy_bits ( tcp_conn - > in . skb , tcp_conn - > in . offset ,
( char * ) buf + ( offset + tcp_conn - > data_copied ) , size ) ;
2005-08-05 06:30:08 +04:00
/* must fit into skb->len */
BUG_ON ( rc ) ;
2006-04-07 06:26:46 +04:00
tcp_conn - > in . offset + = size ;
tcp_conn - > in . copy - = size ;
tcp_conn - > in . copied + = size ;
tcp_conn - > data_copied + = size ;
tcp_ctask - > sent + = size ;
2005-08-05 06:30:08 +04:00
ctask - > data_count - = size ;
2006-04-07 06:26:46 +04:00
BUG_ON ( tcp_conn - > in . copy < 0 ) ;
2005-08-05 06:30:08 +04:00
BUG_ON ( ctask - > data_count < 0 ) ;
2006-04-07 06:26:46 +04:00
if ( buf_size ! = ( tcp_conn - > data_copied + offset ) ) {
2005-08-05 06:30:08 +04:00
if ( ! ctask - > data_count ) {
2006-04-07 06:26:46 +04:00
BUG_ON ( buf_size - tcp_conn - > data_copied < 0 ) ;
2005-08-05 06:30:08 +04:00
/* done with this PDU */
2006-04-07 06:26:46 +04:00
return buf_size - tcp_conn - > data_copied ;
2005-08-05 06:30:08 +04:00
}
return - EAGAIN ;
}
/* done with this buffer or with both - PDU and buffer */
2006-04-07 06:26:46 +04:00
tcp_conn - > data_copied = 0 ;
2005-08-05 06:30:08 +04:00
return 0 ;
}
/**
* iscsi_tcp_copy - copy skb bits to the destanation buffer
2006-04-07 06:26:46 +04:00
* @ conn : iscsi tcp connection
2005-08-05 06:30:08 +04:00
*
* Notes :
* The function calls skb_copy_bits ( ) and updates per - connection
* byte counters .
* */
static inline int
2006-04-07 06:26:46 +04:00
iscsi_tcp_copy ( struct iscsi_tcp_conn * tcp_conn )
2005-08-05 06:30:08 +04:00
{
2006-04-07 06:26:46 +04:00
void * buf = tcp_conn - > data ;
int buf_size = tcp_conn - > in . datalen ;
int buf_left = buf_size - tcp_conn - > data_copied ;
int size = min ( tcp_conn - > in . copy , buf_left ) ;
2005-08-05 06:30:08 +04:00
int rc ;
debug_tcp ( " tcp_copy %d bytes at offset %d copied %d \n " ,
2006-04-07 06:26:46 +04:00
size , tcp_conn - > in . offset , tcp_conn - > data_copied ) ;
2005-08-05 06:30:08 +04:00
BUG_ON ( size < = 0 ) ;
2006-04-07 06:26:46 +04:00
rc = skb_copy_bits ( tcp_conn - > in . skb , tcp_conn - > in . offset ,
( char * ) buf + tcp_conn - > data_copied , size ) ;
2005-08-05 06:30:08 +04:00
BUG_ON ( rc ) ;
2006-04-07 06:26:46 +04:00
tcp_conn - > in . offset + = size ;
tcp_conn - > in . copy - = size ;
tcp_conn - > in . copied + = size ;
tcp_conn - > data_copied + = size ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
if ( buf_size ! = tcp_conn - > data_copied )
2005-08-05 06:30:08 +04:00
return - EAGAIN ;
return 0 ;
}
static inline void
2006-04-07 06:26:46 +04:00
partial_sg_digest_update ( struct iscsi_tcp_conn * tcp_conn ,
struct scatterlist * sg , int offset , int length )
2005-08-05 06:30:08 +04:00
{
struct scatterlist temp ;
memcpy ( & temp , sg , sizeof ( struct scatterlist ) ) ;
temp . offset = offset ;
temp . length = length ;
2006-04-07 06:26:46 +04:00
crypto_digest_update ( tcp_conn - > data_rx_tfm , & temp , 1 ) ;
2005-08-05 06:30:08 +04:00
}
2005-11-30 08:12:57 +03:00
static void
2006-04-07 06:26:46 +04:00
iscsi_recv_digest_update ( struct iscsi_tcp_conn * tcp_conn , char * buf , int len )
2005-11-30 08:12:57 +03:00
{
struct scatterlist tmp ;
sg_init_one ( & tmp , buf , len ) ;
2006-04-07 06:26:46 +04:00
crypto_digest_update ( tcp_conn - > data_rx_tfm , & tmp , 1 ) ;
2005-11-30 08:12:57 +03:00
}
2005-08-05 06:30:08 +04:00
static int iscsi_scsi_data_in ( struct iscsi_conn * conn )
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
struct iscsi_cmd_task * ctask = tcp_conn - > in . ctask ;
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
2005-08-05 06:30:08 +04:00
struct scsi_cmnd * sc = ctask - > sc ;
2005-11-30 08:12:57 +03:00
struct scatterlist * sg ;
2005-08-05 06:30:08 +04:00
int i , offset , rc = 0 ;
BUG_ON ( ( void * ) ctask ! = sc - > SCp . ptr ) ;
/*
* copying Data - In into the Scsi_Cmnd
*/
if ( ! sc - > use_sg ) {
i = ctask - > data_count ;
2006-04-07 06:26:46 +04:00
rc = iscsi_ctask_copy ( tcp_conn , ctask , sc - > request_buffer ,
sc - > request_bufflen ,
tcp_ctask - > data_offset ) ;
2005-08-05 06:30:08 +04:00
if ( rc = = - EAGAIN )
return rc ;
2006-01-14 03:05:35 +03:00
if ( conn - > datadgst_en )
2006-04-07 06:26:46 +04:00
iscsi_recv_digest_update ( tcp_conn , sc - > request_buffer ,
i ) ;
2005-08-05 06:30:08 +04:00
rc = 0 ;
goto done ;
}
2006-04-07 06:26:46 +04:00
offset = tcp_ctask - > data_offset ;
2005-08-05 06:30:08 +04:00
sg = sc - > request_buffer ;
2006-04-07 06:26:46 +04:00
if ( tcp_ctask - > data_offset )
for ( i = 0 ; i < tcp_ctask - > sg_count ; i + + )
2005-08-05 06:30:08 +04:00
offset - = sg [ i ] . length ;
/* we've passed through partial sg*/
if ( offset < 0 )
offset = 0 ;
2006-04-07 06:26:46 +04:00
for ( i = tcp_ctask - > sg_count ; i < sc - > use_sg ; i + + ) {
2005-08-05 06:30:08 +04:00
char * dest ;
dest = kmap_atomic ( sg [ i ] . page , KM_SOFTIRQ0 ) ;
2006-04-07 06:26:46 +04:00
rc = iscsi_ctask_copy ( tcp_conn , ctask , dest + sg [ i ] . offset ,
2005-08-05 06:30:08 +04:00
sg [ i ] . length , offset ) ;
kunmap_atomic ( dest , KM_SOFTIRQ0 ) ;
if ( rc = = - EAGAIN )
/* continue with the next SKB/PDU */
return rc ;
if ( ! rc ) {
if ( conn - > datadgst_en ) {
if ( ! offset )
2006-04-07 06:26:46 +04:00
crypto_digest_update (
tcp_conn - > data_rx_tfm ,
& sg [ i ] , 1 ) ;
2005-08-05 06:30:08 +04:00
else
2006-04-07 06:26:46 +04:00
partial_sg_digest_update ( tcp_conn ,
& sg [ i ] ,
2005-08-05 06:30:08 +04:00
sg [ i ] . offset + offset ,
sg [ i ] . length - offset ) ;
}
offset = 0 ;
2006-04-07 06:26:46 +04:00
tcp_ctask - > sg_count + + ;
2005-08-05 06:30:08 +04:00
}
if ( ! ctask - > data_count ) {
if ( rc & & conn - > datadgst_en )
/*
* data - in is complete , but buffer not . . .
*/
2006-04-07 06:26:46 +04:00
partial_sg_digest_update ( tcp_conn , & sg [ i ] ,
2005-08-05 06:30:08 +04:00
sg [ i ] . offset , sg [ i ] . length - rc ) ;
rc = 0 ;
break ;
}
2006-04-07 06:26:46 +04:00
if ( ! tcp_conn - > in . copy )
2005-08-05 06:30:08 +04:00
return - EAGAIN ;
}
BUG_ON ( ctask - > data_count ) ;
done :
/* check for non-exceptional status */
2006-04-07 06:26:46 +04:00
if ( tcp_conn - > in . hdr - > flags & ISCSI_FLAG_DATA_STATUS ) {
2005-08-05 06:30:08 +04:00
debug_scsi ( " done [sc %lx res %d itt 0x%x] \n " ,
( long ) sc , sc - > result , ctask - > itt ) ;
2006-04-07 06:26:46 +04:00
spin_lock ( & conn - > session - > lock ) ;
__iscsi_ctask_cleanup ( conn , ctask ) ;
__iscsi_complete_pdu ( conn , tcp_conn - > in . hdr , NULL , 0 ) ;
spin_unlock ( & conn - > session - > lock ) ;
2005-08-05 06:30:08 +04:00
}
return rc ;
}
static int
iscsi_data_recv ( struct iscsi_conn * conn )
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
int rc = 0 , opcode ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
opcode = tcp_conn - > in . hdr - > opcode & ISCSI_OPCODE_MASK ;
switch ( opcode ) {
2005-08-05 06:30:08 +04:00
case ISCSI_OP_SCSI_DATA_IN :
rc = iscsi_scsi_data_in ( conn ) ;
break ;
2006-04-07 06:26:46 +04:00
case ISCSI_OP_SCSI_CMD_RSP :
spin_lock ( & conn - > session - > lock ) ;
__iscsi_ctask_cleanup ( conn , tcp_conn - > in . ctask ) ;
spin_unlock ( & conn - > session - > lock ) ;
2005-08-05 06:30:08 +04:00
case ISCSI_OP_TEXT_RSP :
case ISCSI_OP_LOGIN_RSP :
2006-04-07 06:26:46 +04:00
case ISCSI_OP_NOOP_IN :
case ISCSI_OP_ASYNC_EVENT :
case ISCSI_OP_REJECT :
2005-08-05 06:30:08 +04:00
/*
* Collect data segment to the connection ' s data
* placeholder
*/
2006-04-07 06:26:46 +04:00
if ( iscsi_tcp_copy ( tcp_conn ) ) {
2005-08-05 06:30:08 +04:00
rc = - EAGAIN ;
goto exit ;
}
2006-04-07 06:26:46 +04:00
rc = iscsi_complete_pdu ( conn , tcp_conn - > in . hdr , tcp_conn - > data ,
tcp_conn - > in . datalen ) ;
if ( ! rc & & conn - > datadgst_en & & opcode ! = ISCSI_OP_LOGIN_RSP )
iscsi_recv_digest_update ( tcp_conn , tcp_conn - > data ,
tcp_conn - > in . datalen ) ;
break ;
2005-08-05 06:30:08 +04:00
default :
BUG_ON ( 1 ) ;
}
exit :
return rc ;
}
/**
* iscsi_tcp_data_recv - TCP receive in sendfile fashion
* @ rd_desc : read descriptor
* @ skb : socket buffer
* @ offset : offset in skb
* @ len : skb - > len - offset
* */
static int
iscsi_tcp_data_recv ( read_descriptor_t * rd_desc , struct sk_buff * skb ,
unsigned int offset , size_t len )
{
int rc ;
struct iscsi_conn * conn = rd_desc - > arg . data ;
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
2005-08-05 06:30:08 +04:00
int processed ;
char pad [ ISCSI_PAD_LEN ] ;
struct scatterlist sg ;
/*
* Save current SKB and its offset in the corresponding
* connection context .
*/
2006-04-07 06:26:46 +04:00
tcp_conn - > in . copy = skb - > len - offset ;
tcp_conn - > in . offset = offset ;
tcp_conn - > in . skb = skb ;
tcp_conn - > in . len = tcp_conn - > in . copy ;
BUG_ON ( tcp_conn - > in . copy < = 0 ) ;
debug_tcp ( " in %d bytes \n " , tcp_conn - > in . copy ) ;
2005-08-05 06:30:08 +04:00
more :
2006-04-07 06:26:46 +04:00
tcp_conn - > in . copied = 0 ;
2005-08-05 06:30:08 +04:00
rc = 0 ;
if ( unlikely ( conn - > suspend_rx ) ) {
debug_tcp ( " conn %d Rx suspended! \n " , conn - > id ) ;
return 0 ;
}
2006-04-07 06:26:46 +04:00
if ( tcp_conn - > in_progress = = IN_PROGRESS_WAIT_HEADER | |
tcp_conn - > in_progress = = IN_PROGRESS_HEADER_GATHER ) {
rc = iscsi_hdr_extract ( tcp_conn ) ;
2005-08-05 06:30:08 +04:00
if ( rc ) {
if ( rc = = - EAGAIN )
goto nomore ;
else {
iscsi_conn_failure ( conn , rc ) ;
return 0 ;
}
}
/*
* Verify and process incoming PDU header .
*/
2006-04-07 06:26:46 +04:00
rc = iscsi_tcp_hdr_recv ( conn ) ;
if ( ! rc & & tcp_conn - > in . datalen ) {
2005-11-30 11:27:19 +03:00
if ( conn - > datadgst_en ) {
2006-04-07 06:26:46 +04:00
BUG_ON ( ! tcp_conn - > data_rx_tfm ) ;
crypto_digest_init ( tcp_conn - > data_rx_tfm ) ;
2005-08-05 06:30:08 +04:00
}
2006-04-07 06:26:46 +04:00
tcp_conn - > in_progress = IN_PROGRESS_DATA_RECV ;
2005-08-05 06:30:08 +04:00
} else if ( rc ) {
iscsi_conn_failure ( conn , rc ) ;
return 0 ;
}
}
2006-04-07 06:26:46 +04:00
if ( tcp_conn - > in_progress = = IN_PROGRESS_DDIGEST_RECV ) {
2005-11-30 08:12:57 +03:00
uint32_t recv_digest ;
2006-04-07 06:26:46 +04:00
2005-08-05 06:30:08 +04:00
debug_tcp ( " extra data_recv offset %d copy %d \n " ,
2006-04-07 06:26:46 +04:00
tcp_conn - > in . offset , tcp_conn - > in . copy ) ;
skb_copy_bits ( tcp_conn - > in . skb , tcp_conn - > in . offset ,
2005-11-30 08:12:57 +03:00
& recv_digest , 4 ) ;
2006-04-07 06:26:46 +04:00
tcp_conn - > in . offset + = 4 ;
tcp_conn - > in . copy - = 4 ;
if ( recv_digest ! = tcp_conn - > in . datadgst ) {
2005-11-30 08:12:57 +03:00
debug_tcp ( " iscsi_tcp: data digest error! "
" 0x%x != 0x%x \n " , recv_digest ,
2006-04-07 06:26:46 +04:00
tcp_conn - > in . datadgst ) ;
2005-11-30 08:12:57 +03:00
iscsi_conn_failure ( conn , ISCSI_ERR_DATA_DGST ) ;
return 0 ;
} else {
debug_tcp ( " iscsi_tcp: data digest match! "
" 0x%x == 0x%x \n " , recv_digest ,
2006-04-07 06:26:46 +04:00
tcp_conn - > in . datadgst ) ;
tcp_conn - > in_progress = IN_PROGRESS_WAIT_HEADER ;
2005-08-05 06:30:08 +04:00
}
}
2006-04-07 06:26:46 +04:00
if ( tcp_conn - > in_progress = = IN_PROGRESS_DATA_RECV & &
tcp_conn - > in . copy ) {
2005-08-05 06:30:08 +04:00
debug_tcp ( " data_recv offset %d copy %d \n " ,
2006-04-07 06:26:46 +04:00
tcp_conn - > in . offset , tcp_conn - > in . copy ) ;
2005-08-05 06:30:08 +04:00
rc = iscsi_data_recv ( conn ) ;
if ( rc ) {
if ( rc = = - EAGAIN ) {
2006-04-07 06:26:46 +04:00
rd_desc - > count = tcp_conn - > in . datalen -
tcp_conn - > in . ctask - > data_count ;
2005-08-05 06:30:08 +04:00
goto again ;
}
iscsi_conn_failure ( conn , rc ) ;
return 0 ;
}
2006-04-07 06:26:46 +04:00
tcp_conn - > in . copy - = tcp_conn - > in . padding ;
tcp_conn - > in . offset + = tcp_conn - > in . padding ;
2005-11-30 11:27:19 +03:00
if ( conn - > datadgst_en ) {
2006-04-07 06:26:46 +04:00
if ( tcp_conn - > in . padding ) {
debug_tcp ( " padding -> %d \n " ,
tcp_conn - > in . padding ) ;
memset ( pad , 0 , tcp_conn - > in . padding ) ;
sg_init_one ( & sg , pad , tcp_conn - > in . padding ) ;
crypto_digest_update ( tcp_conn - > data_rx_tfm ,
& sg , 1 ) ;
2005-08-05 06:30:08 +04:00
}
2006-04-07 06:26:46 +04:00
crypto_digest_final ( tcp_conn - > data_rx_tfm ,
( u8 * ) & tcp_conn - > in . datadgst ) ;
debug_tcp ( " rx digest 0x%x \n " , tcp_conn - > in . datadgst ) ;
tcp_conn - > in_progress = IN_PROGRESS_DDIGEST_RECV ;
2005-08-05 06:30:08 +04:00
} else
2006-04-07 06:26:46 +04:00
tcp_conn - > in_progress = IN_PROGRESS_WAIT_HEADER ;
2005-08-05 06:30:08 +04:00
}
debug_tcp ( " f, processed %d from out of %d padding %d \n " ,
2006-04-07 06:26:46 +04:00
tcp_conn - > in . offset - offset , ( int ) len , tcp_conn - > in . padding ) ;
BUG_ON ( tcp_conn - > in . offset - offset > len ) ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
if ( tcp_conn - > in . offset - offset ! = len ) {
2005-08-05 06:30:08 +04:00
debug_tcp ( " continue to process %d bytes \n " ,
2006-04-07 06:26:46 +04:00
( int ) len - ( tcp_conn - > in . offset - offset ) ) ;
2005-08-05 06:30:08 +04:00
goto more ;
}
nomore :
2006-04-07 06:26:46 +04:00
processed = tcp_conn - > in . offset - offset ;
2005-08-05 06:30:08 +04:00
BUG_ON ( processed = = 0 ) ;
return processed ;
again :
2006-04-07 06:26:46 +04:00
processed = tcp_conn - > in . offset - offset ;
2005-08-05 06:30:08 +04:00
debug_tcp ( " c, processed %d from out of %d rd_desc_cnt %d \n " ,
processed , ( int ) len , ( int ) rd_desc - > count ) ;
BUG_ON ( processed = = 0 ) ;
BUG_ON ( processed > len ) ;
conn - > rxdata_octets + = processed ;
return processed ;
}
static void
iscsi_tcp_data_ready ( struct sock * sk , int flag )
{
struct iscsi_conn * conn = sk - > sk_user_data ;
read_descriptor_t rd_desc ;
read_lock ( & sk - > sk_callback_lock ) ;
/* use rd_desc to pass 'conn' to iscsi_tcp_data_recv */
rd_desc . arg . data = conn ;
rd_desc . count = 0 ;
tcp_read_sock ( sk , & rd_desc , iscsi_tcp_data_recv ) ;
read_unlock ( & sk - > sk_callback_lock ) ;
}
static void
iscsi_tcp_state_change ( struct sock * sk )
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn ;
2005-08-05 06:30:08 +04:00
struct iscsi_conn * conn ;
struct iscsi_session * session ;
void ( * old_state_change ) ( struct sock * ) ;
read_lock ( & sk - > sk_callback_lock ) ;
conn = ( struct iscsi_conn * ) sk - > sk_user_data ;
session = conn - > session ;
2005-11-30 08:12:49 +03:00
if ( ( sk - > sk_state = = TCP_CLOSE_WAIT | |
sk - > sk_state = = TCP_CLOSE ) & &
! atomic_read ( & sk - > sk_rmem_alloc ) ) {
2005-08-05 06:30:08 +04:00
debug_tcp ( " iscsi_tcp_state_change: TCP_CLOSE|TCP_CLOSE_WAIT \n " ) ;
iscsi_conn_failure ( conn , ISCSI_ERR_CONN_FAILED ) ;
}
2006-04-07 06:26:46 +04:00
tcp_conn = conn - > dd_data ;
old_state_change = tcp_conn - > old_state_change ;
2005-08-05 06:30:08 +04:00
read_unlock ( & sk - > sk_callback_lock ) ;
old_state_change ( sk ) ;
}
/**
* iscsi_write_space - Called when more output buffer space is available
* @ sk : socket space is available for
* */
static void
iscsi_write_space ( struct sock * sk )
{
struct iscsi_conn * conn = ( struct iscsi_conn * ) sk - > sk_user_data ;
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
tcp_conn - > old_write_space ( sk ) ;
2005-08-05 06:30:08 +04:00
debug_tcp ( " iscsi_write_space: cid %d \n " , conn - > id ) ;
2006-04-07 06:26:46 +04:00
clear_bit ( ISCSI_SUSPEND_BIT , & conn - > suspend_tx ) ;
2006-01-14 03:05:53 +03:00
scsi_queue_work ( conn - > session - > host , & conn - > xmitwork ) ;
2005-08-05 06:30:08 +04:00
}
static void
iscsi_conn_set_callbacks ( struct iscsi_conn * conn )
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
struct sock * sk = tcp_conn - > sock - > sk ;
2005-08-05 06:30:08 +04:00
/* assign new callbacks */
write_lock_bh ( & sk - > sk_callback_lock ) ;
sk - > sk_user_data = conn ;
2006-04-07 06:26:46 +04:00
tcp_conn - > old_data_ready = sk - > sk_data_ready ;
tcp_conn - > old_state_change = sk - > sk_state_change ;
tcp_conn - > old_write_space = sk - > sk_write_space ;
2005-08-05 06:30:08 +04:00
sk - > sk_data_ready = iscsi_tcp_data_ready ;
sk - > sk_state_change = iscsi_tcp_state_change ;
sk - > sk_write_space = iscsi_write_space ;
write_unlock_bh ( & sk - > sk_callback_lock ) ;
}
static void
iscsi_conn_restore_callbacks ( struct iscsi_conn * conn )
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
struct sock * sk = tcp_conn - > sock - > sk ;
2005-08-05 06:30:08 +04:00
/* restore socket callbacks, see also: iscsi_conn_set_callbacks() */
write_lock_bh ( & sk - > sk_callback_lock ) ;
sk - > sk_user_data = NULL ;
2006-04-07 06:26:46 +04:00
sk - > sk_data_ready = tcp_conn - > old_data_ready ;
sk - > sk_state_change = tcp_conn - > old_state_change ;
sk - > sk_write_space = tcp_conn - > old_write_space ;
2005-08-05 06:30:08 +04:00
sk - > sk_no_check = 0 ;
write_unlock_bh ( & sk - > sk_callback_lock ) ;
}
/**
* iscsi_send - generic send routine
* @ sk : kernel ' s socket
* @ buf : buffer to write from
* @ size : actual size to write
* @ flags : socket ' s flags
*/
static inline int
2006-01-14 03:05:44 +03:00
iscsi_send ( struct iscsi_conn * conn , struct iscsi_buf * buf , int size , int flags )
2005-08-05 06:30:08 +04:00
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
struct socket * sk = tcp_conn - > sock ;
2006-01-14 03:05:47 +03:00
int offset = buf - > sg . offset + buf - > sent ;
2005-08-05 06:30:08 +04:00
2006-01-14 03:05:47 +03:00
/*
* if we got use_sg = 0 or are sending something we kmallocd
* then we did not have to do kmap ( kmap returns page_address )
*
* if we got use_sg > 0 , but had to drop down , we do not
* set clustering so this should only happen for that
* slab case .
*/
if ( buf - > use_sendmsg )
return sock_no_sendpage ( sk , buf - > sg . page , offset , size , flags ) ;
else
2006-04-07 06:26:46 +04:00
return tcp_conn - > sendpage ( sk , buf - > sg . page , offset , size ,
flags ) ;
2005-08-05 06:30:08 +04:00
}
/**
* iscsi_sendhdr - send PDU Header via tcp_sendpage ( )
* @ conn : iscsi connection
* @ buf : buffer to write from
* @ datalen : lenght of data to be sent after the header
*
* Notes :
* ( Tx , Fast Path )
* */
static inline int
iscsi_sendhdr ( struct iscsi_conn * conn , struct iscsi_buf * buf , int datalen )
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn ;
2005-08-05 06:30:08 +04:00
int flags = 0 ; /* MSG_DONTWAIT; */
int res , size ;
size = buf - > sg . length - buf - > sent ;
BUG_ON ( buf - > sent + size > buf - > sg . length ) ;
if ( buf - > sent + size ! = buf - > sg . length | | datalen )
flags | = MSG_MORE ;
2006-01-14 03:05:44 +03:00
res = iscsi_send ( conn , buf , size , flags ) ;
2005-08-05 06:30:08 +04:00
debug_tcp ( " sendhdr %d bytes, sent %d res %d \n " , size , buf - > sent , res ) ;
if ( res > = 0 ) {
conn - > txdata_octets + = res ;
buf - > sent + = res ;
if ( size ! = res )
return - EAGAIN ;
return 0 ;
} else if ( res = = - EAGAIN ) {
2006-04-07 06:26:46 +04:00
tcp_conn = conn - > dd_data ;
tcp_conn - > sendpage_failures_cnt + + ;
set_bit ( ISCSI_SUSPEND_BIT , & conn - > suspend_tx ) ;
2005-08-05 06:30:08 +04:00
} else if ( res = = - EPIPE )
iscsi_conn_failure ( conn , ISCSI_ERR_CONN_FAILED ) ;
return res ;
}
/**
* iscsi_sendpage - send one page of iSCSI Data - Out .
* @ conn : iscsi connection
* @ buf : buffer to write from
* @ count : remaining data
* @ sent : number of bytes sent
*
* Notes :
* ( Tx , Fast Path )
* */
static inline int
iscsi_sendpage ( struct iscsi_conn * conn , struct iscsi_buf * buf ,
int * count , int * sent )
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn ;
2005-08-05 06:30:08 +04:00
int flags = 0 ; /* MSG_DONTWAIT; */
int res , size ;
size = buf - > sg . length - buf - > sent ;
BUG_ON ( buf - > sent + size > buf - > sg . length ) ;
if ( size > * count )
size = * count ;
2005-09-13 06:01:28 +04:00
if ( buf - > sent + size ! = buf - > sg . length | | * count ! = size )
2005-08-05 06:30:08 +04:00
flags | = MSG_MORE ;
2006-01-14 03:05:44 +03:00
res = iscsi_send ( conn , buf , size , flags ) ;
2005-08-05 06:30:08 +04:00
debug_tcp ( " sendpage: %d bytes, sent %d left %d sent %d res %d \n " ,
size , buf - > sent , * count , * sent , res ) ;
if ( res > = 0 ) {
conn - > txdata_octets + = res ;
buf - > sent + = res ;
* count - = res ;
* sent + = res ;
if ( size ! = res )
return - EAGAIN ;
return 0 ;
} else if ( res = = - EAGAIN ) {
2006-04-07 06:26:46 +04:00
tcp_conn = conn - > dd_data ;
tcp_conn - > sendpage_failures_cnt + + ;
set_bit ( ISCSI_SUSPEND_BIT , & conn - > suspend_tx ) ;
2005-08-05 06:30:08 +04:00
} else if ( res = = - EPIPE )
iscsi_conn_failure ( conn , ISCSI_ERR_CONN_FAILED ) ;
return res ;
}
static inline void
2006-04-07 06:26:46 +04:00
iscsi_data_digest_init ( struct iscsi_tcp_conn * tcp_conn ,
struct iscsi_cmd_task * ctask )
2005-08-05 06:30:08 +04:00
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
BUG_ON ( ! tcp_conn - > data_tx_tfm ) ;
crypto_digest_init ( tcp_conn - > data_tx_tfm ) ;
tcp_ctask - > digest_count = 4 ;
2005-08-05 06:30:08 +04:00
}
2006-01-15 00:20:43 +03:00
static int
2005-08-05 06:30:08 +04:00
iscsi_digest_final_send ( struct iscsi_conn * conn , struct iscsi_cmd_task * ctask ,
struct iscsi_buf * buf , uint32_t * digest , int final )
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
2005-08-05 06:30:08 +04:00
int rc = 0 ;
int sent = 0 ;
if ( final )
2006-04-07 06:26:46 +04:00
crypto_digest_final ( tcp_conn - > data_tx_tfm , ( u8 * ) digest ) ;
2005-08-05 06:30:08 +04:00
iscsi_buf_init_virt ( buf , ( char * ) digest , 4 ) ;
2006-04-07 06:26:46 +04:00
rc = iscsi_sendpage ( conn , buf , & tcp_ctask - > digest_count , & sent ) ;
2005-08-05 06:30:08 +04:00
if ( rc ) {
2006-04-07 06:26:46 +04:00
tcp_ctask - > datadigest = * digest ;
tcp_ctask - > xmstate | = XMSTATE_DATA_DIGEST ;
2005-08-05 06:30:08 +04:00
} else
2006-04-07 06:26:46 +04:00
tcp_ctask - > digest_count = 4 ;
2005-08-05 06:30:08 +04:00
return rc ;
}
/**
* iscsi_solicit_data_cont - initialize next Data - Out
* @ conn : iscsi connection
* @ ctask : scsi command task
* @ r2t : R2T info
* @ left : bytes left to transfer
*
* Notes :
* Initialize next Data - Out within this R2T sequence and continue
* to process next Scatter - Gather element ( if any ) of this SCSI command .
*
* Called under connection lock .
* */
static void
iscsi_solicit_data_cont ( struct iscsi_conn * conn , struct iscsi_cmd_task * ctask ,
struct iscsi_r2t_info * r2t , int left )
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
2005-08-05 06:30:08 +04:00
struct iscsi_data * hdr ;
struct iscsi_data_task * dtask ;
struct scsi_cmnd * sc = ctask - > sc ;
int new_offset ;
2006-04-07 06:26:46 +04:00
dtask = mempool_alloc ( tcp_ctask - > datapool , GFP_ATOMIC ) ;
2005-08-05 06:30:08 +04:00
BUG_ON ( ! dtask ) ;
2006-04-07 06:13:39 +04:00
INIT_LIST_HEAD ( & dtask - > item ) ;
2005-08-05 06:30:08 +04:00
hdr = & dtask - > hdr ;
memset ( hdr , 0 , sizeof ( struct iscsi_data ) ) ;
hdr - > ttt = r2t - > ttt ;
hdr - > datasn = cpu_to_be32 ( r2t - > solicit_datasn ) ;
r2t - > solicit_datasn + + ;
hdr - > opcode = ISCSI_OP_SCSI_DATA_OUT ;
2006-04-07 06:26:46 +04:00
memcpy ( hdr - > lun , ctask - > hdr - > lun , sizeof ( hdr - > lun ) ) ;
hdr - > itt = ctask - > hdr - > itt ;
2005-08-05 06:30:08 +04:00
hdr - > exp_statsn = r2t - > exp_statsn ;
new_offset = r2t - > data_offset + r2t - > sent ;
hdr - > offset = cpu_to_be32 ( new_offset ) ;
if ( left > conn - > max_xmit_dlength ) {
hton24 ( hdr - > dlength , conn - > max_xmit_dlength ) ;
r2t - > data_count = conn - > max_xmit_dlength ;
} else {
hton24 ( hdr - > dlength , left ) ;
r2t - > data_count = left ;
hdr - > flags = ISCSI_FLAG_CMD_FINAL ;
}
conn - > dataout_pdus_cnt + + ;
2005-09-13 06:01:32 +04:00
iscsi_buf_init_virt ( & r2t - > headbuf , ( char * ) hdr ,
sizeof ( struct iscsi_hdr ) ) ;
2005-08-05 06:30:08 +04:00
r2t - > dtask = dtask ;
if ( sc - > use_sg & & ! iscsi_buf_left ( & r2t - > sendbuf ) ) {
2006-04-07 06:26:46 +04:00
BUG_ON ( tcp_ctask - > bad_sg = = r2t - > sg ) ;
2005-08-05 06:30:08 +04:00
iscsi_buf_init_sg ( & r2t - > sendbuf , r2t - > sg ) ;
r2t - > sg + = 1 ;
} else
2006-04-07 06:26:46 +04:00
iscsi_buf_init_iov ( & tcp_ctask - > sendbuf ,
2005-08-05 06:30:08 +04:00
( char * ) sc - > request_buffer + new_offset ,
r2t - > data_count ) ;
2006-04-07 06:26:46 +04:00
list_add ( & dtask - > item , & tcp_ctask - > dataqueue ) ;
2005-08-05 06:30:08 +04:00
}
static void
iscsi_unsolicit_data_init ( struct iscsi_conn * conn , struct iscsi_cmd_task * ctask )
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
2005-08-05 06:30:08 +04:00
struct iscsi_data_task * dtask ;
2006-04-07 06:26:46 +04:00
dtask = mempool_alloc ( tcp_ctask - > datapool , GFP_ATOMIC ) ;
2005-08-05 06:30:08 +04:00
BUG_ON ( ! dtask ) ;
2006-04-07 06:13:39 +04:00
INIT_LIST_HEAD ( & dtask - > item ) ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
iscsi_prep_unsolicit_data_pdu ( ctask , & dtask - > hdr ,
tcp_ctask - > r2t_data_count ) ;
iscsi_buf_init_virt ( & tcp_ctask - > headbuf , ( char * ) & dtask - > hdr ,
2005-09-13 06:01:32 +04:00
sizeof ( struct iscsi_hdr ) ) ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
list_add ( & dtask - > item , & tcp_ctask - > dataqueue ) ;
tcp_ctask - > dtask = dtask ;
2005-08-05 06:30:08 +04:00
}
/**
2006-04-07 06:26:46 +04:00
* iscsi_tcp_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
2005-08-05 06:30:08 +04:00
* @ conn : iscsi connection
* @ ctask : scsi command task
* @ sc : scsi command
* */
static void
2006-04-07 06:26:46 +04:00
iscsi_tcp_cmd_init ( struct iscsi_cmd_task * ctask )
2005-08-05 06:30:08 +04:00
{
2006-04-07 06:26:46 +04:00
struct scsi_cmnd * sc = ctask - > sc ;
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
BUG_ON ( __kfifo_len ( tcp_ctask - > r2tqueue ) ) ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
tcp_ctask - > sent = 0 ;
tcp_ctask - > sg_count = 0 ;
2005-08-05 06:30:08 +04:00
if ( sc - > sc_data_direction = = DMA_TO_DEVICE ) {
2006-04-07 06:26:46 +04:00
tcp_ctask - > xmstate = XMSTATE_W_HDR ;
tcp_ctask - > exp_r2tsn = 0 ;
2005-08-05 06:30:08 +04:00
BUG_ON ( ctask - > total_length = = 0 ) ;
2006-04-07 06:26:46 +04:00
2005-08-05 06:30:08 +04:00
if ( sc - > use_sg ) {
struct scatterlist * sg = sc - > request_buffer ;
2006-04-07 06:26:46 +04:00
iscsi_buf_init_sg ( & tcp_ctask - > sendbuf ,
& sg [ tcp_ctask - > sg_count + + ] ) ;
tcp_ctask - > sg = sg ;
tcp_ctask - > bad_sg = sg + sc - > use_sg ;
} else
iscsi_buf_init_iov ( & tcp_ctask - > sendbuf ,
sc - > request_buffer ,
sc - > request_bufflen ) ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
if ( ctask - > imm_count )
tcp_ctask - > xmstate | = XMSTATE_IMM_DATA ;
tcp_ctask - > pad_count = ctask - > total_length & ( ISCSI_PAD_LEN - 1 ) ;
if ( tcp_ctask - > pad_count ) {
tcp_ctask - > pad_count = ISCSI_PAD_LEN -
tcp_ctask - > pad_count ;
2005-08-05 06:30:08 +04:00
debug_scsi ( " write padding %d bytes \n " ,
2006-04-07 06:26:46 +04:00
tcp_ctask - > pad_count ) ;
tcp_ctask - > xmstate | = XMSTATE_W_PAD ;
2005-08-05 06:30:08 +04:00
}
2006-04-07 06:26:46 +04:00
if ( ctask - > unsol_count )
tcp_ctask - > xmstate | = XMSTATE_UNS_HDR |
XMSTATE_UNS_INIT ;
tcp_ctask - > r2t_data_count = ctask - > total_length -
2005-08-05 06:30:08 +04:00
ctask - > imm_count -
ctask - > unsol_count ;
debug_scsi ( " cmd [itt %x total %d imm %d imm_data %d "
" r2t_data %d] \n " ,
ctask - > itt , ctask - > total_length , ctask - > imm_count ,
2006-04-07 06:26:46 +04:00
ctask - > unsol_count , tcp_ctask - > r2t_data_count ) ;
} else
tcp_ctask - > xmstate = XMSTATE_R_HDR ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
iscsi_buf_init_virt ( & tcp_ctask - > headbuf , ( char * ) ctask - > hdr ,
2005-09-13 06:01:32 +04:00
sizeof ( struct iscsi_hdr ) ) ;
2005-08-05 06:30:08 +04:00
}
/**
2006-04-07 06:26:46 +04:00
* iscsi_tcp_mtask_xmit - xmit management ( immediate ) task
2005-08-05 06:30:08 +04:00
* @ conn : iscsi connection
* @ mtask : task management task
*
* Notes :
* The function can return - EAGAIN in which case caller must
* call it again later , or recover . ' 0 ' return code means successful
* xmit .
*
* Management xmit state machine consists of two states :
* IN_PROGRESS_IMM_HEAD - PDU Header xmit in progress
* IN_PROGRESS_IMM_DATA - PDU Data xmit in progress
* */
static int
2006-04-07 06:26:46 +04:00
iscsi_tcp_mtask_xmit ( struct iscsi_conn * conn , struct iscsi_mgmt_task * mtask )
2005-08-05 06:30:08 +04:00
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_mgmt_task * tcp_mtask = mtask - > dd_data ;
2005-08-05 06:30:08 +04:00
debug_scsi ( " mtask deq [cid %d state %x itt 0x%x] \n " ,
2006-04-07 06:26:46 +04:00
conn - > id , tcp_mtask - > xmstate , mtask - > itt ) ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
if ( tcp_mtask - > xmstate & XMSTATE_IMM_HDR ) {
tcp_mtask - > xmstate & = ~ XMSTATE_IMM_HDR ;
2005-08-05 06:30:08 +04:00
if ( mtask - > data_count )
2006-04-07 06:26:46 +04:00
tcp_mtask - > xmstate | = XMSTATE_IMM_DATA ;
2005-09-13 06:01:32 +04:00
if ( conn - > c_stage ! = ISCSI_CONN_INITIAL_STAGE & &
2006-04-07 06:13:39 +04:00
conn - > stop_stage ! = STOP_CONN_RECOVER & &
2005-09-13 06:01:32 +04:00
conn - > hdrdgst_en )
2006-04-07 06:26:46 +04:00
iscsi_hdr_digest ( conn , & tcp_mtask - > headbuf ,
( u8 * ) tcp_mtask - > hdrext ) ;
if ( iscsi_sendhdr ( conn , & tcp_mtask - > headbuf ,
mtask - > data_count ) ) {
tcp_mtask - > xmstate | = XMSTATE_IMM_HDR ;
2005-08-05 06:30:08 +04:00
if ( mtask - > data_count )
2006-04-07 06:26:46 +04:00
tcp_mtask - > xmstate & = ~ XMSTATE_IMM_DATA ;
2005-08-05 06:30:08 +04:00
return - EAGAIN ;
}
}
2006-04-07 06:26:46 +04:00
if ( tcp_mtask - > xmstate & XMSTATE_IMM_DATA ) {
2005-08-05 06:30:08 +04:00
BUG_ON ( ! mtask - > data_count ) ;
2006-04-07 06:26:46 +04:00
tcp_mtask - > xmstate & = ~ XMSTATE_IMM_DATA ;
2005-08-05 06:30:08 +04:00
/* FIXME: implement.
* Virtual buffer could be spreaded across multiple pages . . .
*/
do {
2006-04-07 06:26:46 +04:00
if ( iscsi_sendpage ( conn , & tcp_mtask - > sendbuf ,
& mtask - > data_count , & tcp_mtask - > sent ) ) {
tcp_mtask - > xmstate | = XMSTATE_IMM_DATA ;
2005-08-05 06:30:08 +04:00
return - EAGAIN ;
}
} while ( mtask - > data_count ) ;
}
2006-04-07 06:26:46 +04:00
BUG_ON ( tcp_mtask - > xmstate ! = XMSTATE_IDLE ) ;
if ( mtask - > hdr - > itt = = cpu_to_be32 ( ISCSI_RESERVED_TAG ) ) {
struct iscsi_session * session = conn - > session ;
spin_lock_bh ( & session - > lock ) ;
list_del ( & conn - > mtask - > running ) ;
__kfifo_put ( session - > mgmtpool . queue , ( void * ) & conn - > mtask ,
sizeof ( void * ) ) ;
spin_unlock_bh ( & session - > lock ) ;
}
2005-08-05 06:30:08 +04:00
return 0 ;
}
static inline int
2006-04-07 06:26:46 +04:00
handle_xmstate_r_hdr ( struct iscsi_conn * conn ,
struct iscsi_tcp_cmd_task * tcp_ctask )
2005-08-05 06:30:08 +04:00
{
2006-04-07 06:26:46 +04:00
tcp_ctask - > xmstate & = ~ XMSTATE_R_HDR ;
2006-01-14 03:05:35 +03:00
if ( conn - > hdrdgst_en )
2006-04-07 06:26:46 +04:00
iscsi_hdr_digest ( conn , & tcp_ctask - > headbuf ,
( u8 * ) tcp_ctask - > hdrext ) ;
if ( ! iscsi_sendhdr ( conn , & tcp_ctask - > headbuf , 0 ) ) {
BUG_ON ( tcp_ctask - > xmstate ! = XMSTATE_IDLE ) ;
2005-08-05 06:30:08 +04:00
return 0 ; /* wait for Data-In */
}
2006-04-07 06:26:46 +04:00
tcp_ctask - > xmstate | = XMSTATE_R_HDR ;
2005-08-05 06:30:08 +04:00
return - EAGAIN ;
}
static inline int
2006-04-07 06:26:46 +04:00
handle_xmstate_w_hdr ( struct iscsi_conn * conn ,
struct iscsi_cmd_task * ctask )
2005-08-05 06:30:08 +04:00
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
tcp_ctask - > xmstate & = ~ XMSTATE_W_HDR ;
2006-01-14 03:05:35 +03:00
if ( conn - > hdrdgst_en )
2006-04-07 06:26:46 +04:00
iscsi_hdr_digest ( conn , & tcp_ctask - > headbuf ,
( u8 * ) tcp_ctask - > hdrext ) ;
if ( iscsi_sendhdr ( conn , & tcp_ctask - > headbuf , ctask - > imm_count ) ) {
tcp_ctask - > xmstate | = XMSTATE_W_HDR ;
2005-08-05 06:30:08 +04:00
return - EAGAIN ;
}
return 0 ;
}
static inline int
handle_xmstate_data_digest ( struct iscsi_conn * conn ,
struct iscsi_cmd_task * ctask )
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
tcp_ctask - > xmstate & = ~ XMSTATE_DATA_DIGEST ;
debug_tcp ( " resent data digest 0x%x \n " , tcp_ctask - > datadigest ) ;
if ( iscsi_digest_final_send ( conn , ctask , & tcp_ctask - > immbuf ,
& tcp_ctask - > datadigest , 0 ) ) {
tcp_ctask - > xmstate | = XMSTATE_DATA_DIGEST ;
2005-08-05 06:30:08 +04:00
debug_tcp ( " resent data digest 0x%x fail! \n " ,
2006-04-07 06:26:46 +04:00
tcp_ctask - > datadigest ) ;
2005-08-05 06:30:08 +04:00
return - EAGAIN ;
}
return 0 ;
}
static inline int
handle_xmstate_imm_data ( struct iscsi_conn * conn , struct iscsi_cmd_task * ctask )
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
2005-08-05 06:30:08 +04:00
BUG_ON ( ! ctask - > imm_count ) ;
2006-04-07 06:26:46 +04:00
tcp_ctask - > xmstate & = ~ XMSTATE_IMM_DATA ;
2005-08-05 06:30:08 +04:00
if ( conn - > datadgst_en ) {
2006-04-07 06:26:46 +04:00
iscsi_data_digest_init ( tcp_conn , ctask ) ;
tcp_ctask - > immdigest = 0 ;
2005-08-05 06:30:08 +04:00
}
for ( ; ; ) {
2006-04-07 06:26:46 +04:00
if ( iscsi_sendpage ( conn , & tcp_ctask - > sendbuf , & ctask - > imm_count ,
& tcp_ctask - > sent ) ) {
tcp_ctask - > xmstate | = XMSTATE_IMM_DATA ;
2005-08-05 06:30:08 +04:00
if ( conn - > datadgst_en ) {
2006-04-07 06:26:46 +04:00
crypto_digest_final ( tcp_conn - > data_tx_tfm ,
( u8 * ) & tcp_ctask - > immdigest ) ;
2005-08-05 06:30:08 +04:00
debug_tcp ( " tx imm sendpage fail 0x%x \n " ,
2006-04-07 06:26:46 +04:00
tcp_ctask - > datadigest ) ;
2005-08-05 06:30:08 +04:00
}
return - EAGAIN ;
}
if ( conn - > datadgst_en )
2006-04-07 06:26:46 +04:00
crypto_digest_update ( tcp_conn - > data_tx_tfm ,
& tcp_ctask - > sendbuf . sg , 1 ) ;
2005-08-05 06:30:08 +04:00
if ( ! ctask - > imm_count )
break ;
2006-04-07 06:26:46 +04:00
iscsi_buf_init_sg ( & tcp_ctask - > sendbuf ,
& tcp_ctask - > sg [ tcp_ctask - > sg_count + + ] ) ;
2005-08-05 06:30:08 +04:00
}
2006-04-07 06:26:46 +04:00
if ( conn - > datadgst_en & & ! ( tcp_ctask - > xmstate & XMSTATE_W_PAD ) ) {
if ( iscsi_digest_final_send ( conn , ctask , & tcp_ctask - > immbuf ,
& tcp_ctask - > immdigest , 1 ) ) {
2005-08-05 06:30:08 +04:00
debug_tcp ( " sending imm digest 0x%x fail! \n " ,
2006-04-07 06:26:46 +04:00
tcp_ctask - > immdigest ) ;
2005-08-05 06:30:08 +04:00
return - EAGAIN ;
}
2006-04-07 06:26:46 +04:00
debug_tcp ( " sending imm digest 0x%x \n " , tcp_ctask - > immdigest ) ;
2005-08-05 06:30:08 +04:00
}
return 0 ;
}
static inline int
handle_xmstate_uns_hdr ( struct iscsi_conn * conn , struct iscsi_cmd_task * ctask )
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
2005-08-05 06:30:08 +04:00
struct iscsi_data_task * dtask ;
2006-04-07 06:26:46 +04:00
tcp_ctask - > xmstate | = XMSTATE_UNS_DATA ;
if ( tcp_ctask - > xmstate & XMSTATE_UNS_INIT ) {
2005-08-05 06:30:08 +04:00
iscsi_unsolicit_data_init ( conn , ctask ) ;
2006-04-07 06:26:46 +04:00
BUG_ON ( ! tcp_ctask - > dtask ) ;
dtask = tcp_ctask - > dtask ;
2005-09-13 06:01:32 +04:00
if ( conn - > hdrdgst_en )
2006-04-07 06:26:46 +04:00
iscsi_hdr_digest ( conn , & tcp_ctask - > headbuf ,
2005-09-13 06:01:32 +04:00
( u8 * ) dtask - > hdrext ) ;
2006-04-07 06:26:46 +04:00
tcp_ctask - > xmstate & = ~ XMSTATE_UNS_INIT ;
2005-08-05 06:30:08 +04:00
}
2006-04-07 06:26:46 +04:00
if ( iscsi_sendhdr ( conn , & tcp_ctask - > headbuf , ctask - > data_count ) ) {
tcp_ctask - > xmstate & = ~ XMSTATE_UNS_DATA ;
tcp_ctask - > xmstate | = XMSTATE_UNS_HDR ;
2005-08-05 06:30:08 +04:00
return - EAGAIN ;
}
debug_scsi ( " uns dout [itt 0x%x dlen %d sent %d] \n " ,
2006-04-07 06:26:46 +04:00
ctask - > itt , ctask - > unsol_count , tcp_ctask - > sent ) ;
2005-08-05 06:30:08 +04:00
return 0 ;
}
static inline int
handle_xmstate_uns_data ( struct iscsi_conn * conn , struct iscsi_cmd_task * ctask )
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
struct iscsi_data_task * dtask = tcp_ctask - > dtask ;
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
2005-08-05 06:30:08 +04:00
BUG_ON ( ! ctask - > data_count ) ;
2006-04-07 06:26:46 +04:00
tcp_ctask - > xmstate & = ~ XMSTATE_UNS_DATA ;
2005-08-05 06:30:08 +04:00
if ( conn - > datadgst_en ) {
2006-04-07 06:26:46 +04:00
iscsi_data_digest_init ( tcp_conn , ctask ) ;
2005-08-05 06:30:08 +04:00
dtask - > digest = 0 ;
}
for ( ; ; ) {
2006-04-07 06:26:46 +04:00
int start = tcp_ctask - > sent ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
if ( iscsi_sendpage ( conn , & tcp_ctask - > sendbuf ,
& ctask - > data_count , & tcp_ctask - > sent ) ) {
ctask - > unsol_count - = tcp_ctask - > sent - start ;
tcp_ctask - > xmstate | = XMSTATE_UNS_DATA ;
2005-08-05 06:30:08 +04:00
/* will continue with this ctask later.. */
if ( conn - > datadgst_en ) {
2006-04-07 06:26:46 +04:00
crypto_digest_final ( tcp_conn - > data_tx_tfm ,
2005-08-05 06:30:08 +04:00
( u8 * ) & dtask - > digest ) ;
debug_tcp ( " tx uns data fail 0x%x \n " ,
dtask - > digest ) ;
}
return - EAGAIN ;
}
2006-04-07 06:26:46 +04:00
BUG_ON ( tcp_ctask - > sent > ctask - > total_length ) ;
ctask - > unsol_count - = tcp_ctask - > sent - start ;
2005-08-05 06:30:08 +04:00
/*
* XXX : we may run here with un - initial sendbuf .
* so pass it
*/
2006-04-07 06:26:46 +04:00
if ( conn - > datadgst_en & & tcp_ctask - > sent - start > 0 )
crypto_digest_update ( tcp_conn - > data_tx_tfm ,
& tcp_ctask - > sendbuf . sg , 1 ) ;
2005-08-05 06:30:08 +04:00
if ( ! ctask - > data_count )
break ;
2006-04-07 06:26:46 +04:00
iscsi_buf_init_sg ( & tcp_ctask - > sendbuf ,
& tcp_ctask - > sg [ tcp_ctask - > sg_count + + ] ) ;
2005-08-05 06:30:08 +04:00
}
BUG_ON ( ctask - > unsol_count < 0 ) ;
/*
* Done with the Data - Out . Next , check if we need
* to send another unsolicited Data - Out .
*/
if ( ctask - > unsol_count ) {
if ( conn - > datadgst_en ) {
if ( iscsi_digest_final_send ( conn , ctask ,
& dtask - > digestbuf ,
& dtask - > digest , 1 ) ) {
debug_tcp ( " send uns digest 0x%x fail \n " ,
dtask - > digest ) ;
return - EAGAIN ;
}
debug_tcp ( " sending uns digest 0x%x, more uns \n " ,
dtask - > digest ) ;
}
2006-04-07 06:26:46 +04:00
tcp_ctask - > xmstate | = XMSTATE_UNS_INIT ;
2005-08-05 06:30:08 +04:00
return 1 ;
}
2006-04-07 06:26:46 +04:00
if ( conn - > datadgst_en & & ! ( tcp_ctask - > xmstate & XMSTATE_W_PAD ) ) {
2005-08-05 06:30:08 +04:00
if ( iscsi_digest_final_send ( conn , ctask ,
& dtask - > digestbuf ,
& dtask - > digest , 1 ) ) {
debug_tcp ( " send last uns digest 0x%x fail \n " ,
dtask - > digest ) ;
return - EAGAIN ;
}
debug_tcp ( " sending uns digest 0x%x \n " , dtask - > digest ) ;
}
return 0 ;
}
static inline int
handle_xmstate_sol_data ( struct iscsi_conn * conn , struct iscsi_cmd_task * ctask )
{
struct iscsi_session * session = conn - > session ;
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
struct iscsi_r2t_info * r2t = tcp_ctask - > r2t ;
2005-08-05 06:30:08 +04:00
struct iscsi_data_task * dtask = r2t - > dtask ;
int left ;
2006-04-07 06:26:46 +04:00
tcp_ctask - > xmstate & = ~ XMSTATE_SOL_DATA ;
tcp_ctask - > dtask = dtask ;
2005-08-05 06:30:08 +04:00
if ( conn - > datadgst_en ) {
2006-04-07 06:26:46 +04:00
iscsi_data_digest_init ( tcp_conn , ctask ) ;
2005-08-05 06:30:08 +04:00
dtask - > digest = 0 ;
}
solicit_again :
/*
* send Data - Out whitnin this R2T sequence .
*/
if ( ! r2t - > data_count )
goto data_out_done ;
if ( iscsi_sendpage ( conn , & r2t - > sendbuf , & r2t - > data_count , & r2t - > sent ) ) {
2006-04-07 06:26:46 +04:00
tcp_ctask - > xmstate | = XMSTATE_SOL_DATA ;
2005-08-05 06:30:08 +04:00
/* will continue with this ctask later.. */
if ( conn - > datadgst_en ) {
2006-04-07 06:26:46 +04:00
crypto_digest_final ( tcp_conn - > data_tx_tfm ,
2005-08-05 06:30:08 +04:00
( u8 * ) & dtask - > digest ) ;
debug_tcp ( " r2t data send fail 0x%x \n " , dtask - > digest ) ;
}
return - EAGAIN ;
}
BUG_ON ( r2t - > data_count < 0 ) ;
if ( conn - > datadgst_en )
2006-04-07 06:26:46 +04:00
crypto_digest_update ( tcp_conn - > data_tx_tfm , & r2t - > sendbuf . sg ,
1 ) ;
2005-08-05 06:30:08 +04:00
if ( r2t - > data_count ) {
BUG_ON ( ctask - > sc - > use_sg = = 0 ) ;
if ( ! iscsi_buf_left ( & r2t - > sendbuf ) ) {
2006-04-07 06:26:46 +04:00
BUG_ON ( tcp_ctask - > bad_sg = = r2t - > sg ) ;
2005-08-05 06:30:08 +04:00
iscsi_buf_init_sg ( & r2t - > sendbuf , r2t - > sg ) ;
r2t - > sg + = 1 ;
}
goto solicit_again ;
}
data_out_done :
/*
* Done with this Data - Out . Next , check if we have
* to send another Data - Out for this R2T .
*/
BUG_ON ( r2t - > data_length - r2t - > sent < 0 ) ;
left = r2t - > data_length - r2t - > sent ;
if ( left ) {
if ( conn - > datadgst_en ) {
if ( iscsi_digest_final_send ( conn , ctask ,
& dtask - > digestbuf ,
& dtask - > digest , 1 ) ) {
debug_tcp ( " send r2t data digest 0x%x "
" fail \n " , dtask - > digest ) ;
return - EAGAIN ;
}
debug_tcp ( " r2t data send digest 0x%x \n " ,
dtask - > digest ) ;
}
iscsi_solicit_data_cont ( conn , ctask , r2t , left ) ;
2006-04-07 06:26:46 +04:00
tcp_ctask - > xmstate | = XMSTATE_SOL_DATA ;
tcp_ctask - > xmstate & = ~ XMSTATE_SOL_HDR ;
2005-08-05 06:30:08 +04:00
return 1 ;
}
/*
* Done with this R2T . Check if there are more
* outstanding R2Ts ready to be processed .
*/
2006-04-07 06:26:46 +04:00
BUG_ON ( tcp_ctask - > r2t_data_count - r2t - > data_length < 0 ) ;
2005-08-05 06:30:08 +04:00
if ( conn - > datadgst_en ) {
if ( iscsi_digest_final_send ( conn , ctask , & dtask - > digestbuf ,
& dtask - > digest , 1 ) ) {
debug_tcp ( " send last r2t data digest 0x%x "
" fail \n " , dtask - > digest ) ;
return - EAGAIN ;
}
debug_tcp ( " r2t done dout digest 0x%x \n " , dtask - > digest ) ;
}
2006-04-07 06:26:46 +04:00
tcp_ctask - > r2t_data_count - = r2t - > data_length ;
tcp_ctask - > r2t = NULL ;
2005-08-05 06:30:08 +04:00
spin_lock_bh ( & session - > lock ) ;
2006-04-07 06:26:46 +04:00
__kfifo_put ( tcp_ctask - > r2tpool . queue , ( void * ) & r2t , sizeof ( void * ) ) ;
2005-08-05 06:30:08 +04:00
spin_unlock_bh ( & session - > lock ) ;
2006-04-07 06:26:46 +04:00
if ( __kfifo_get ( tcp_ctask - > r2tqueue , ( void * ) & r2t , sizeof ( void * ) ) ) {
tcp_ctask - > r2t = r2t ;
tcp_ctask - > xmstate | = XMSTATE_SOL_DATA ;
tcp_ctask - > xmstate & = ~ XMSTATE_SOL_HDR ;
2005-08-05 06:30:08 +04:00
return 1 ;
}
return 0 ;
}
static inline int
handle_xmstate_w_pad ( struct iscsi_conn * conn , struct iscsi_cmd_task * ctask )
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
struct iscsi_data_task * dtask = tcp_ctask - > dtask ;
2005-08-05 06:30:08 +04:00
int sent ;
2006-04-07 06:26:46 +04:00
tcp_ctask - > xmstate & = ~ XMSTATE_W_PAD ;
iscsi_buf_init_virt ( & tcp_ctask - > sendbuf , ( char * ) & tcp_ctask - > pad ,
tcp_ctask - > pad_count ) ;
if ( iscsi_sendpage ( conn , & tcp_ctask - > sendbuf , & tcp_ctask - > pad_count ,
& sent ) ) {
tcp_ctask - > xmstate | = XMSTATE_W_PAD ;
2005-08-05 06:30:08 +04:00
return - EAGAIN ;
}
if ( conn - > datadgst_en ) {
2006-04-07 06:26:46 +04:00
crypto_digest_update ( tcp_conn - > data_tx_tfm ,
& tcp_ctask - > sendbuf . sg , 1 ) ;
2005-08-05 06:30:08 +04:00
/* imm data? */
if ( ! dtask ) {
2006-04-07 06:26:46 +04:00
if ( iscsi_digest_final_send ( conn , ctask ,
& tcp_ctask - > immbuf ,
& tcp_ctask - > immdigest , 1 ) ) {
2005-08-05 06:30:08 +04:00
debug_tcp ( " send padding digest 0x%x "
2006-04-07 06:26:46 +04:00
" fail! \n " , tcp_ctask - > immdigest ) ;
2005-08-05 06:30:08 +04:00
return - EAGAIN ;
}
debug_tcp ( " done with padding, digest 0x%x \n " ,
2006-04-07 06:26:46 +04:00
tcp_ctask - > datadigest ) ;
2005-08-05 06:30:08 +04:00
} else {
if ( iscsi_digest_final_send ( conn , ctask ,
& dtask - > digestbuf ,
& dtask - > digest , 1 ) ) {
debug_tcp ( " send padding digest 0x%x "
" fail \n " , dtask - > digest ) ;
return - EAGAIN ;
}
debug_tcp ( " done with padding, digest 0x%x \n " ,
dtask - > digest ) ;
}
}
return 0 ;
}
static int
2006-04-07 06:26:46 +04:00
iscsi_tcp_ctask_xmit ( struct iscsi_conn * conn , struct iscsi_cmd_task * ctask )
2005-08-05 06:30:08 +04:00
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
2005-08-05 06:30:08 +04:00
int rc = 0 ;
debug_scsi ( " ctask deq [cid %d xmstate %x itt 0x%x] \n " ,
2006-04-07 06:26:46 +04:00
conn - > id , tcp_ctask - > xmstate , ctask - > itt ) ;
2005-08-05 06:30:08 +04:00
/*
* serialize with TMF AbortTask
*/
if ( ctask - > mtask )
return rc ;
2006-04-07 06:26:46 +04:00
if ( tcp_ctask - > xmstate & XMSTATE_R_HDR ) {
rc = handle_xmstate_r_hdr ( conn , tcp_ctask ) ;
2005-08-05 06:30:08 +04:00
return rc ;
}
2006-04-07 06:26:46 +04:00
if ( tcp_ctask - > xmstate & XMSTATE_W_HDR ) {
2005-08-05 06:30:08 +04:00
rc = handle_xmstate_w_hdr ( conn , ctask ) ;
if ( rc )
return rc ;
}
/* XXX: for data digest xmit recover */
2006-04-07 06:26:46 +04:00
if ( tcp_ctask - > xmstate & XMSTATE_DATA_DIGEST ) {
2005-08-05 06:30:08 +04:00
rc = handle_xmstate_data_digest ( conn , ctask ) ;
if ( rc )
return rc ;
}
2006-04-07 06:26:46 +04:00
if ( tcp_ctask - > xmstate & XMSTATE_IMM_DATA ) {
2005-08-05 06:30:08 +04:00
rc = handle_xmstate_imm_data ( conn , ctask ) ;
if ( rc )
return rc ;
}
2006-04-07 06:26:46 +04:00
if ( tcp_ctask - > xmstate & XMSTATE_UNS_HDR ) {
2005-08-05 06:30:08 +04:00
BUG_ON ( ! ctask - > unsol_count ) ;
2006-04-07 06:26:46 +04:00
tcp_ctask - > xmstate & = ~ XMSTATE_UNS_HDR ;
2005-08-05 06:30:08 +04:00
unsolicit_head_again :
rc = handle_xmstate_uns_hdr ( conn , ctask ) ;
if ( rc )
return rc ;
}
2006-04-07 06:26:46 +04:00
if ( tcp_ctask - > xmstate & XMSTATE_UNS_DATA ) {
2005-08-05 06:30:08 +04:00
rc = handle_xmstate_uns_data ( conn , ctask ) ;
if ( rc = = 1 )
goto unsolicit_head_again ;
else if ( rc )
return rc ;
goto done ;
}
2006-04-07 06:26:46 +04:00
if ( tcp_ctask - > xmstate & XMSTATE_SOL_HDR ) {
2005-08-05 06:30:08 +04:00
struct iscsi_r2t_info * r2t ;
2006-04-07 06:26:46 +04:00
tcp_ctask - > xmstate & = ~ XMSTATE_SOL_HDR ;
tcp_ctask - > xmstate | = XMSTATE_SOL_DATA ;
if ( ! tcp_ctask - > r2t )
__kfifo_get ( tcp_ctask - > r2tqueue , ( void * ) & tcp_ctask - > r2t ,
2005-08-05 06:30:08 +04:00
sizeof ( void * ) ) ;
solicit_head_again :
2006-04-07 06:26:46 +04:00
r2t = tcp_ctask - > r2t ;
2005-09-13 06:01:32 +04:00
if ( conn - > hdrdgst_en )
2006-01-14 03:05:35 +03:00
iscsi_hdr_digest ( conn , & r2t - > headbuf ,
2005-09-13 06:01:32 +04:00
( u8 * ) r2t - > dtask - > hdrext ) ;
2005-08-05 06:30:08 +04:00
if ( iscsi_sendhdr ( conn , & r2t - > headbuf , r2t - > data_count ) ) {
2006-04-07 06:26:46 +04:00
tcp_ctask - > xmstate & = ~ XMSTATE_SOL_DATA ;
tcp_ctask - > xmstate | = XMSTATE_SOL_HDR ;
2005-08-05 06:30:08 +04:00
return - EAGAIN ;
}
debug_scsi ( " sol dout [dsn %d itt 0x%x dlen %d sent %d] \n " ,
r2t - > solicit_datasn - 1 , ctask - > itt , r2t - > data_count ,
r2t - > sent ) ;
}
2006-04-07 06:26:46 +04:00
if ( tcp_ctask - > xmstate & XMSTATE_SOL_DATA ) {
2005-08-05 06:30:08 +04:00
rc = handle_xmstate_sol_data ( conn , ctask ) ;
if ( rc = = 1 )
goto solicit_head_again ;
if ( rc )
return rc ;
}
done :
/*
* Last thing to check is whether we need to send write
* padding . Note that we check for xmstate equality , not just the bit .
*/
2006-04-07 06:26:46 +04:00
if ( tcp_ctask - > xmstate = = XMSTATE_W_PAD )
2005-08-05 06:30:08 +04:00
rc = handle_xmstate_w_pad ( conn , ctask ) ;
return rc ;
}
2006-04-07 06:26:46 +04:00
static struct iscsi_cls_conn *
iscsi_tcp_conn_create ( struct iscsi_cls_session * cls_session , uint32_t conn_idx )
2005-08-05 06:30:08 +04:00
{
2006-04-07 06:26:46 +04:00
struct iscsi_conn * conn ;
struct iscsi_cls_conn * cls_conn ;
struct iscsi_tcp_conn * tcp_conn ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
cls_conn = iscsi_conn_setup ( cls_session , conn_idx ) ;
if ( ! cls_conn )
return NULL ;
conn = cls_conn - > dd_data ;
2005-08-05 06:30:08 +04:00
/*
2006-04-07 06:26:46 +04:00
* due to strange issues with iser these are not set
* in iscsi_conn_setup
2005-08-05 06:30:08 +04:00
*/
2006-04-07 06:26:46 +04:00
conn - > max_recv_dlength = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
tcp_conn = kzalloc ( sizeof ( * tcp_conn ) , GFP_KERNEL ) ;
if ( ! tcp_conn )
goto tcp_conn_alloc_fail ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
conn - > dd_data = tcp_conn ;
tcp_conn - > iscsi_conn = conn ;
tcp_conn - > in_progress = IN_PROGRESS_WAIT_HEADER ;
/* initial operational parameters */
tcp_conn - > hdr_size = sizeof ( struct iscsi_hdr ) ;
tcp_conn - > data_size = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
/* allocate initial PDU receive place holder */
if ( tcp_conn - > data_size < = PAGE_SIZE )
tcp_conn - > data = kmalloc ( tcp_conn - > data_size , GFP_KERNEL ) ;
else
tcp_conn - > data = ( void * ) __get_free_pages ( GFP_KERNEL ,
get_order ( tcp_conn - > data_size ) ) ;
if ( ! tcp_conn - > data )
goto max_recv_dlenght_alloc_fail ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
return cls_conn ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
max_recv_dlenght_alloc_fail :
kfree ( tcp_conn ) ;
tcp_conn_alloc_fail :
iscsi_conn_teardown ( cls_conn ) ;
return NULL ;
2005-08-05 06:30:08 +04:00
}
static void
2006-04-07 06:26:46 +04:00
iscsi_tcp_conn_destroy ( struct iscsi_cls_conn * cls_conn )
2005-08-05 06:30:08 +04:00
{
2006-04-07 06:26:46 +04:00
struct iscsi_conn * conn = cls_conn - > dd_data ;
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
int digest = 0 ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
if ( conn - > hdrdgst_en | | conn - > datadgst_en )
digest = 1 ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
iscsi_conn_teardown ( cls_conn ) ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
/* now free tcp_conn */
if ( digest ) {
if ( tcp_conn - > tx_tfm )
crypto_free_tfm ( tcp_conn - > tx_tfm ) ;
if ( tcp_conn - > rx_tfm )
crypto_free_tfm ( tcp_conn - > rx_tfm ) ;
if ( tcp_conn - > data_tx_tfm )
crypto_free_tfm ( tcp_conn - > data_tx_tfm ) ;
if ( tcp_conn - > data_rx_tfm )
crypto_free_tfm ( tcp_conn - > data_rx_tfm ) ;
}
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
/* free conn->data, size = MaxRecvDataSegmentLength */
if ( tcp_conn - > data_size < = PAGE_SIZE )
kfree ( tcp_conn - > data ) ;
else
free_pages ( ( unsigned long ) tcp_conn - > data ,
get_order ( tcp_conn - > data_size ) ) ;
kfree ( tcp_conn ) ;
}
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
static int
iscsi_tcp_conn_bind ( struct iscsi_cls_session * cls_session ,
2006-05-03 04:46:36 +04:00
struct iscsi_cls_conn * cls_conn , uint64_t transport_eph ,
2006-04-07 06:26:46 +04:00
int is_leading )
{
struct iscsi_conn * conn = cls_conn - > dd_data ;
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
struct sock * sk ;
struct socket * sock ;
int err ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
/* lookup for existing socket */
2006-05-03 04:46:36 +04:00
sock = sockfd_lookup ( ( int ) transport_eph , & err ) ;
2006-04-07 06:26:46 +04:00
if ( ! sock ) {
printk ( KERN_ERR " iscsi_tcp: sockfd_lookup failed %d \n " , err ) ;
return - EEXIST ;
2005-08-05 06:30:08 +04:00
}
2006-04-07 06:26:46 +04:00
err = iscsi_conn_bind ( cls_session , cls_conn , is_leading ) ;
if ( err )
return err ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
if ( conn - > stop_stage ! = STOP_CONN_SUSPEND ) {
/* bind iSCSI connection and socket */
tcp_conn - > sock = sock ;
2005-08-05 06:30:08 +04:00
/* setup Socket parameters */
sk = sock - > sk ;
sk - > sk_reuse = 1 ;
sk - > sk_sndtimeo = 15 * HZ ; /* FIXME: make it configurable */
sk - > sk_allocation = GFP_ATOMIC ;
/* FIXME: disable Nagle's algorithm */
/*
* Intercept TCP callbacks for sendfile like receive
* processing .
*/
2006-04-07 06:26:46 +04:00
conn - > recv_lock = & sk - > sk_callback_lock ;
2005-08-05 06:30:08 +04:00
iscsi_conn_set_callbacks ( conn ) ;
2006-04-07 06:26:46 +04:00
tcp_conn - > sendpage = tcp_conn - > sock - > ops - > sendpage ;
2005-08-05 06:30:08 +04:00
/*
* set receive state machine into initial state
*/
2006-04-07 06:26:46 +04:00
tcp_conn - > in_progress = IN_PROGRESS_WAIT_HEADER ;
2005-08-05 06:30:08 +04:00
}
return 0 ;
}
2006-04-07 06:13:39 +04:00
static void
2006-04-07 06:26:46 +04:00
iscsi_tcp_cleanup_ctask ( struct iscsi_conn * conn , struct iscsi_cmd_task * ctask )
2006-04-07 06:13:39 +04:00
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
2006-04-07 06:13:39 +04:00
struct iscsi_r2t_info * r2t ;
/* flush ctask's r2t queues */
2006-04-07 06:26:46 +04:00
while ( __kfifo_get ( tcp_ctask - > r2tqueue , ( void * ) & r2t , sizeof ( void * ) ) )
__kfifo_put ( tcp_ctask - > r2tpool . queue , ( void * ) & r2t ,
2006-04-07 06:13:39 +04:00
sizeof ( void * ) ) ;
2006-04-07 06:26:46 +04:00
__iscsi_ctask_cleanup ( conn , ctask ) ;
2006-04-07 06:13:39 +04:00
}
static void
2006-04-07 06:26:46 +04:00
iscsi_tcp_suspend_conn_rx ( struct iscsi_conn * conn )
2006-04-07 06:13:39 +04:00
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
2005-08-05 06:30:08 +04:00
struct sock * sk ;
2006-04-07 06:26:46 +04:00
if ( ! tcp_conn - > sock )
return ;
sk = tcp_conn - > sock - > sk ;
2005-08-05 06:30:08 +04:00
write_lock_bh ( & sk - > sk_callback_lock ) ;
2006-04-07 06:26:46 +04:00
set_bit ( ISCSI_SUSPEND_BIT , & conn - > suspend_rx ) ;
2005-08-05 06:30:08 +04:00
write_unlock_bh ( & sk - > sk_callback_lock ) ;
2006-04-07 06:13:39 +04:00
}
2005-08-05 06:30:08 +04:00
2006-04-07 06:13:39 +04:00
static void
2006-04-07 06:26:46 +04:00
iscsi_tcp_terminate_conn ( struct iscsi_conn * conn )
2006-04-07 06:13:39 +04:00
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
if ( ! tcp_conn - > sock )
return ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
sock_hold ( tcp_conn - > sock - > sk ) ;
2006-04-07 06:13:39 +04:00
iscsi_conn_restore_callbacks ( conn ) ;
2006-04-07 06:26:46 +04:00
sock_put ( tcp_conn - > sock - > sk ) ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
sock_release ( tcp_conn - > sock ) ;
tcp_conn - > sock = NULL ;
conn - > recv_lock = NULL ;
2005-08-05 06:30:08 +04:00
}
2006-04-07 06:26:46 +04:00
/* called with host lock */
2006-04-07 06:13:39 +04:00
static void
2006-04-07 06:26:46 +04:00
iscsi_tcp_mgmt_init ( struct iscsi_conn * conn , struct iscsi_mgmt_task * mtask ,
char * data , uint32_t data_size )
2005-08-05 06:30:08 +04:00
{
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_mgmt_task * tcp_mtask = mtask - > dd_data ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
iscsi_buf_init_virt ( & tcp_mtask - > headbuf , ( char * ) mtask - > hdr ,
2005-08-05 06:30:08 +04:00
sizeof ( struct iscsi_hdr ) ) ;
2006-04-07 06:26:46 +04:00
tcp_mtask - > xmstate = XMSTATE_IMM_HDR ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
if ( mtask - > data_count )
iscsi_buf_init_iov ( & tcp_mtask - > sendbuf , ( char * ) mtask - > data ,
2005-08-05 06:30:08 +04:00
mtask - > data_count ) ;
}
static int
iscsi_r2tpool_alloc ( struct iscsi_session * session )
{
int i ;
int cmd_i ;
/*
* initialize per - task : R2T pool and xmit queue
*/
for ( cmd_i = 0 ; cmd_i < session - > cmds_max ; cmd_i + + ) {
struct iscsi_cmd_task * ctask = session - > cmds [ cmd_i ] ;
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
2005-08-05 06:30:08 +04:00
/*
* pre - allocated x4 as much r2ts to handle race when
* target acks DataOut faster than we data_xmit ( ) queues
* could replenish r2tqueue .
*/
/* R2T pool */
2006-04-07 06:26:46 +04:00
if ( iscsi_pool_init ( & tcp_ctask - > r2tpool , session - > max_r2t * 4 ,
( void * * * ) & tcp_ctask - > r2ts ,
sizeof ( struct iscsi_r2t_info ) ) ) {
2005-08-05 06:30:08 +04:00
goto r2t_alloc_fail ;
}
/* R2T xmit queue */
2006-04-07 06:26:46 +04:00
tcp_ctask - > r2tqueue = kfifo_alloc (
2005-08-05 06:30:08 +04:00
session - > max_r2t * 4 * sizeof ( void * ) , GFP_KERNEL , NULL ) ;
2006-04-07 06:26:46 +04:00
if ( tcp_ctask - > r2tqueue = = ERR_PTR ( - ENOMEM ) ) {
iscsi_pool_free ( & tcp_ctask - > r2tpool ,
( void * * ) tcp_ctask - > r2ts ) ;
2005-08-05 06:30:08 +04:00
goto r2t_alloc_fail ;
}
/*
* number of
* Data - Out PDU ' s within R2T - sequence can be quite big ;
* using mempool
*/
2006-04-07 06:26:46 +04:00
tcp_ctask - > datapool = mempool_create_slab_pool ( ISCSI_DTASK_DEFAULT_MAX ,
taskcache ) ;
if ( tcp_ctask - > datapool = = NULL ) {
kfifo_free ( tcp_ctask - > r2tqueue ) ;
iscsi_pool_free ( & tcp_ctask - > r2tpool ,
( void * * ) tcp_ctask - > r2ts ) ;
2005-08-05 06:30:08 +04:00
goto r2t_alloc_fail ;
}
2006-04-07 06:26:46 +04:00
INIT_LIST_HEAD ( & tcp_ctask - > dataqueue ) ;
2005-08-05 06:30:08 +04:00
}
return 0 ;
r2t_alloc_fail :
for ( i = 0 ; i < cmd_i ; i + + ) {
2006-04-07 06:26:46 +04:00
struct iscsi_cmd_task * ctask = session - > cmds [ i ] ;
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
mempool_destroy ( tcp_ctask - > datapool ) ;
kfifo_free ( tcp_ctask - > r2tqueue ) ;
iscsi_pool_free ( & tcp_ctask - > r2tpool ,
( void * * ) tcp_ctask - > r2ts ) ;
2005-08-05 06:30:08 +04:00
}
return - ENOMEM ;
}
static void
iscsi_r2tpool_free ( struct iscsi_session * session )
{
int i ;
for ( i = 0 ; i < session - > cmds_max ; i + + ) {
2006-04-07 06:26:46 +04:00
struct iscsi_cmd_task * ctask = session - > cmds [ i ] ;
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
mempool_destroy ( tcp_ctask - > datapool ) ;
kfifo_free ( tcp_ctask - > r2tqueue ) ;
iscsi_pool_free ( & tcp_ctask - > r2tpool ,
( void * * ) tcp_ctask - > r2ts ) ;
2005-08-05 06:30:08 +04:00
}
}
static int
2006-02-02 06:06:49 +03:00
iscsi_conn_set_param ( struct iscsi_cls_conn * cls_conn , enum iscsi_param param ,
2005-08-05 06:30:08 +04:00
uint32_t value )
{
2006-02-02 06:06:49 +03:00
struct iscsi_conn * conn = cls_conn - > dd_data ;
2005-08-05 06:30:08 +04:00
struct iscsi_session * session = conn - > session ;
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
2005-08-05 06:30:08 +04:00
switch ( param ) {
case ISCSI_PARAM_MAX_RECV_DLENGTH : {
2006-04-07 06:26:46 +04:00
char * saveptr = tcp_conn - > data ;
2005-12-15 12:17:19 +03:00
gfp_t flags = GFP_KERNEL ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
if ( tcp_conn - > data_size > = value ) {
2005-08-05 06:30:08 +04:00
conn - > max_recv_dlength = value ;
break ;
}
spin_lock_bh ( & session - > lock ) ;
if ( conn - > stop_stage = = STOP_CONN_RECOVER )
flags = GFP_ATOMIC ;
spin_unlock_bh ( & session - > lock ) ;
if ( value < = PAGE_SIZE )
2006-04-07 06:26:46 +04:00
tcp_conn - > data = kmalloc ( value , flags ) ;
2005-08-05 06:30:08 +04:00
else
2006-04-07 06:26:46 +04:00
tcp_conn - > data = ( void * ) __get_free_pages ( flags ,
2005-08-05 06:30:08 +04:00
get_order ( value ) ) ;
2006-04-07 06:26:46 +04:00
if ( tcp_conn - > data = = NULL ) {
tcp_conn - > data = saveptr ;
2005-08-05 06:30:08 +04:00
return - ENOMEM ;
}
2006-04-07 06:26:46 +04:00
if ( tcp_conn - > data_size < = PAGE_SIZE )
2005-08-05 06:30:08 +04:00
kfree ( saveptr ) ;
else
free_pages ( ( unsigned long ) saveptr ,
2006-04-07 06:26:46 +04:00
get_order ( tcp_conn - > data_size ) ) ;
2005-08-05 06:30:08 +04:00
conn - > max_recv_dlength = value ;
2006-04-07 06:26:46 +04:00
tcp_conn - > data_size = value ;
2005-08-05 06:30:08 +04:00
}
break ;
case ISCSI_PARAM_MAX_XMIT_DLENGTH :
conn - > max_xmit_dlength = value ;
break ;
case ISCSI_PARAM_HDRDGST_EN :
conn - > hdrdgst_en = value ;
2006-04-07 06:26:46 +04:00
tcp_conn - > hdr_size = sizeof ( struct iscsi_hdr ) ;
2005-08-05 06:30:08 +04:00
if ( conn - > hdrdgst_en ) {
2006-04-07 06:26:46 +04:00
tcp_conn - > hdr_size + = sizeof ( __u32 ) ;
if ( ! tcp_conn - > tx_tfm )
tcp_conn - > tx_tfm = crypto_alloc_tfm ( " crc32c " ,
0 ) ;
if ( ! tcp_conn - > tx_tfm )
2005-08-05 06:30:08 +04:00
return - ENOMEM ;
2006-04-07 06:26:46 +04:00
if ( ! tcp_conn - > rx_tfm )
tcp_conn - > rx_tfm = crypto_alloc_tfm ( " crc32c " ,
0 ) ;
if ( ! tcp_conn - > rx_tfm ) {
crypto_free_tfm ( tcp_conn - > tx_tfm ) ;
2005-08-05 06:30:08 +04:00
return - ENOMEM ;
}
} else {
2006-04-07 06:26:46 +04:00
if ( tcp_conn - > tx_tfm )
crypto_free_tfm ( tcp_conn - > tx_tfm ) ;
if ( tcp_conn - > rx_tfm )
crypto_free_tfm ( tcp_conn - > rx_tfm ) ;
2005-08-05 06:30:08 +04:00
}
break ;
case ISCSI_PARAM_DATADGST_EN :
conn - > datadgst_en = value ;
if ( conn - > datadgst_en ) {
2006-04-07 06:26:46 +04:00
if ( ! tcp_conn - > data_tx_tfm )
tcp_conn - > data_tx_tfm =
2005-08-05 06:30:08 +04:00
crypto_alloc_tfm ( " crc32c " , 0 ) ;
2006-04-07 06:26:46 +04:00
if ( ! tcp_conn - > data_tx_tfm )
2005-08-05 06:30:08 +04:00
return - ENOMEM ;
2006-04-07 06:26:46 +04:00
if ( ! tcp_conn - > data_rx_tfm )
tcp_conn - > data_rx_tfm =
2005-08-05 06:30:08 +04:00
crypto_alloc_tfm ( " crc32c " , 0 ) ;
2006-04-07 06:26:46 +04:00
if ( ! tcp_conn - > data_rx_tfm ) {
crypto_free_tfm ( tcp_conn - > data_tx_tfm ) ;
2005-08-05 06:30:08 +04:00
return - ENOMEM ;
}
} else {
2006-04-07 06:26:46 +04:00
if ( tcp_conn - > data_tx_tfm )
crypto_free_tfm ( tcp_conn - > data_tx_tfm ) ;
if ( tcp_conn - > data_rx_tfm )
crypto_free_tfm ( tcp_conn - > data_rx_tfm ) ;
2005-08-05 06:30:08 +04:00
}
2006-04-07 06:26:46 +04:00
tcp_conn - > sendpage = conn - > datadgst_en ?
sock_no_sendpage : tcp_conn - > sock - > ops - > sendpage ;
2005-08-05 06:30:08 +04:00
break ;
case ISCSI_PARAM_INITIAL_R2T_EN :
session - > initial_r2t_en = value ;
break ;
case ISCSI_PARAM_MAX_R2T :
if ( session - > max_r2t = = roundup_pow_of_two ( value ) )
break ;
iscsi_r2tpool_free ( session ) ;
session - > max_r2t = value ;
if ( session - > max_r2t & ( session - > max_r2t - 1 ) )
session - > max_r2t = roundup_pow_of_two ( session - > max_r2t ) ;
if ( iscsi_r2tpool_alloc ( session ) )
return - ENOMEM ;
break ;
case ISCSI_PARAM_IMM_DATA_EN :
session - > imm_data_en = value ;
break ;
case ISCSI_PARAM_FIRST_BURST :
session - > first_burst = value ;
break ;
case ISCSI_PARAM_MAX_BURST :
session - > max_burst = value ;
break ;
case ISCSI_PARAM_PDU_INORDER_EN :
session - > pdu_inorder_en = value ;
break ;
case ISCSI_PARAM_DATASEQ_INORDER_EN :
session - > dataseq_inorder_en = value ;
break ;
case ISCSI_PARAM_ERL :
session - > erl = value ;
break ;
case ISCSI_PARAM_IFMARKER_EN :
BUG_ON ( value ) ;
session - > ifmarker_en = value ;
break ;
case ISCSI_PARAM_OFMARKER_EN :
BUG_ON ( value ) ;
session - > ofmarker_en = value ;
break ;
default :
break ;
}
return 0 ;
}
static int
2006-02-02 06:06:49 +03:00
iscsi_session_get_param ( struct iscsi_cls_session * cls_session ,
2006-01-14 03:05:50 +03:00
enum iscsi_param param , uint32_t * value )
2005-08-05 06:30:08 +04:00
{
2006-02-02 06:06:49 +03:00
struct Scsi_Host * shost = iscsi_session_to_shost ( cls_session ) ;
2006-01-14 03:05:50 +03:00
struct iscsi_session * session = iscsi_hostdata ( shost - > hostdata ) ;
2005-08-05 06:30:08 +04:00
switch ( param ) {
case ISCSI_PARAM_INITIAL_R2T_EN :
* value = session - > initial_r2t_en ;
break ;
case ISCSI_PARAM_MAX_R2T :
* value = session - > max_r2t ;
break ;
case ISCSI_PARAM_IMM_DATA_EN :
* value = session - > imm_data_en ;
break ;
case ISCSI_PARAM_FIRST_BURST :
* value = session - > first_burst ;
break ;
case ISCSI_PARAM_MAX_BURST :
* value = session - > max_burst ;
break ;
case ISCSI_PARAM_PDU_INORDER_EN :
* value = session - > pdu_inorder_en ;
break ;
case ISCSI_PARAM_DATASEQ_INORDER_EN :
* value = session - > dataseq_inorder_en ;
break ;
case ISCSI_PARAM_ERL :
* value = session - > erl ;
break ;
case ISCSI_PARAM_IFMARKER_EN :
* value = session - > ifmarker_en ;
break ;
case ISCSI_PARAM_OFMARKER_EN :
* value = session - > ofmarker_en ;
break ;
default :
2006-04-07 06:13:36 +04:00
return - EINVAL ;
2005-08-05 06:30:08 +04:00
}
return 0 ;
}
2006-01-14 03:05:50 +03:00
static int
2006-02-02 06:06:49 +03:00
iscsi_conn_get_param ( struct iscsi_cls_conn * cls_conn ,
enum iscsi_param param , uint32_t * value )
2006-01-14 03:05:50 +03:00
{
2006-02-02 06:06:49 +03:00
struct iscsi_conn * conn = cls_conn - > dd_data ;
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
2006-04-07 06:13:36 +04:00
struct inet_sock * inet ;
2006-01-14 03:05:50 +03:00
switch ( param ) {
case ISCSI_PARAM_MAX_RECV_DLENGTH :
* value = conn - > max_recv_dlength ;
break ;
case ISCSI_PARAM_MAX_XMIT_DLENGTH :
* value = conn - > max_xmit_dlength ;
break ;
case ISCSI_PARAM_HDRDGST_EN :
* value = conn - > hdrdgst_en ;
break ;
case ISCSI_PARAM_DATADGST_EN :
* value = conn - > datadgst_en ;
break ;
2006-04-07 06:13:36 +04:00
case ISCSI_PARAM_CONN_PORT :
mutex_lock ( & conn - > xmitmutex ) ;
2006-04-07 06:26:46 +04:00
if ( ! tcp_conn - > sock ) {
2006-04-07 06:13:36 +04:00
mutex_unlock ( & conn - > xmitmutex ) ;
return - EINVAL ;
}
2006-04-07 06:26:46 +04:00
inet = inet_sk ( tcp_conn - > sock - > sk ) ;
2006-04-07 06:13:36 +04:00
* value = be16_to_cpu ( inet - > dport ) ;
mutex_unlock ( & conn - > xmitmutex ) ;
2006-01-14 03:05:50 +03:00
default :
2006-04-07 06:13:36 +04:00
return - EINVAL ;
2006-01-14 03:05:50 +03:00
}
return 0 ;
}
2006-04-07 06:13:36 +04:00
static int
iscsi_conn_get_str_param ( struct iscsi_cls_conn * cls_conn ,
enum iscsi_param param , char * buf )
{
struct iscsi_conn * conn = cls_conn - > dd_data ;
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
2006-04-07 06:13:36 +04:00
struct sock * sk ;
struct inet_sock * inet ;
struct ipv6_pinfo * np ;
int len = 0 ;
switch ( param ) {
case ISCSI_PARAM_CONN_ADDRESS :
mutex_lock ( & conn - > xmitmutex ) ;
2006-04-07 06:26:46 +04:00
if ( ! tcp_conn - > sock ) {
2006-04-07 06:13:36 +04:00
mutex_unlock ( & conn - > xmitmutex ) ;
return - EINVAL ;
}
2006-04-07 06:26:46 +04:00
sk = tcp_conn - > sock - > sk ;
2006-04-07 06:13:36 +04:00
if ( sk - > sk_family = = PF_INET ) {
inet = inet_sk ( sk ) ;
len = sprintf ( buf , " %u.%u.%u.%u \n " ,
NIPQUAD ( inet - > daddr ) ) ;
} else {
np = inet6_sk ( sk ) ;
len = sprintf ( buf ,
" %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x \n " ,
NIP6 ( np - > daddr ) ) ;
}
mutex_unlock ( & conn - > xmitmutex ) ;
break ;
default :
return - EINVAL ;
}
return len ;
}
2005-08-05 06:30:08 +04:00
static void
2006-02-02 06:06:49 +03:00
iscsi_conn_get_stats ( struct iscsi_cls_conn * cls_conn , struct iscsi_stats * stats )
2005-08-05 06:30:08 +04:00
{
2006-02-02 06:06:49 +03:00
struct iscsi_conn * conn = cls_conn - > dd_data ;
2006-04-07 06:26:46 +04:00
struct iscsi_tcp_conn * tcp_conn = conn - > dd_data ;
2005-08-05 06:30:08 +04:00
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 - > custom_length = 3 ;
strcpy ( stats - > custom [ 0 ] . desc , " tx_sendpage_failures " ) ;
2006-04-07 06:26:46 +04:00
stats - > custom [ 0 ] . value = tcp_conn - > sendpage_failures_cnt ;
2005-08-05 06:30:08 +04:00
strcpy ( stats - > custom [ 1 ] . desc , " rx_discontiguous_hdr " ) ;
2006-04-07 06:26:46 +04:00
stats - > custom [ 1 ] . value = tcp_conn - > discontiguous_hdr_cnt ;
2005-08-05 06:30:08 +04:00
strcpy ( stats - > custom [ 2 ] . desc , " eh_abort_cnt " ) ;
stats - > custom [ 2 ] . value = conn - > eh_abort_cnt ;
}
2006-04-07 06:26:46 +04:00
static struct iscsi_cls_session *
iscsi_tcp_session_create ( struct iscsi_transport * iscsit ,
struct scsi_transport_template * scsit ,
uint32_t initial_cmdsn , uint32_t * hostno )
2005-08-05 06:30:08 +04:00
{
2006-04-07 06:26:46 +04:00
struct iscsi_cls_session * cls_session ;
struct iscsi_session * session ;
uint32_t hn ;
int cmd_i ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
cls_session = iscsi_session_setup ( iscsit , scsit ,
sizeof ( struct iscsi_tcp_cmd_task ) ,
sizeof ( struct iscsi_tcp_mgmt_task ) ,
initial_cmdsn , & hn ) ;
if ( ! cls_session )
return NULL ;
* hostno = hn ;
2005-08-05 06:30:08 +04:00
2006-04-07 06:26:46 +04:00
session = class_to_transport_session ( cls_session ) ;
for ( cmd_i = 0 ; cmd_i < session - > cmds_max ; cmd_i + + ) {
struct iscsi_cmd_task * ctask = session - > cmds [ cmd_i ] ;
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
ctask - > hdr = & tcp_ctask - > hdr ;
}
for ( cmd_i = 0 ; cmd_i < session - > mgmtpool_max ; cmd_i + + ) {
struct iscsi_mgmt_task * mtask = session - > mgmt_cmds [ cmd_i ] ;
struct iscsi_tcp_mgmt_task * tcp_mtask = mtask - > dd_data ;
mtask - > hdr = & tcp_mtask - > hdr ;
}
if ( iscsi_r2tpool_alloc ( class_to_transport_session ( cls_session ) ) )
goto r2tpool_alloc_fail ;
return cls_session ;
r2tpool_alloc_fail :
iscsi_session_teardown ( cls_session ) ;
return NULL ;
}
static void iscsi_tcp_session_destroy ( struct iscsi_cls_session * cls_session )
{
struct iscsi_session * session = class_to_transport_session ( cls_session ) ;
struct iscsi_data_task * dtask , * n ;
int cmd_i ;
for ( cmd_i = 0 ; cmd_i < session - > cmds_max ; cmd_i + + ) {
struct iscsi_cmd_task * ctask = session - > cmds [ cmd_i ] ;
struct iscsi_tcp_cmd_task * tcp_ctask = ctask - > dd_data ;
list_for_each_entry_safe ( dtask , n , & tcp_ctask - > dataqueue ,
item ) {
list_del ( & dtask - > item ) ;
mempool_free ( dtask , tcp_ctask - > datapool ) ;
}
}
iscsi_r2tpool_free ( class_to_transport_session ( cls_session ) ) ;
iscsi_session_teardown ( cls_session ) ;
2005-08-05 06:30:08 +04:00
}
2006-04-07 06:26:46 +04:00
static struct scsi_host_template iscsi_sht = {
. name = " iSCSI Initiator over TCP/IP, v. "
ISCSI_VERSION_STR ,
. queuecommand = iscsi_queuecommand ,
. change_queue_depth = iscsi_change_queue_depth ,
. can_queue = ISCSI_XMIT_CMDS_MAX - 1 ,
. sg_tablesize = ISCSI_SG_TABLESIZE ,
. cmd_per_lun = ISCSI_DEF_CMD_PER_LUN ,
. eh_abort_handler = iscsi_eh_abort ,
. eh_host_reset_handler = iscsi_eh_host_reset ,
. use_clustering = DISABLE_CLUSTERING ,
. proc_name = " iscsi_tcp " ,
. this_id = - 1 ,
} ;
2005-08-05 06:30:08 +04:00
static struct iscsi_transport iscsi_tcp_transport = {
. owner = THIS_MODULE ,
. name = " tcp " ,
. caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST
| CAP_DATADGST ,
2006-04-07 06:13:36 +04:00
. param_mask = ISCSI_MAX_RECV_DLENGTH |
ISCSI_MAX_XMIT_DLENGTH |
ISCSI_HDRDGST_EN |
ISCSI_DATADGST_EN |
ISCSI_INITIAL_R2T_EN |
ISCSI_MAX_R2T |
ISCSI_IMM_DATA_EN |
ISCSI_FIRST_BURST |
ISCSI_MAX_BURST |
ISCSI_PDU_INORDER_EN |
ISCSI_DATASEQ_INORDER_EN |
ISCSI_ERL |
ISCSI_CONN_PORT |
ISCSI_CONN_ADDRESS ,
2005-08-05 06:30:08 +04:00
. host_template = & iscsi_sht ,
2006-01-14 03:05:50 +03:00
. conndata_size = sizeof ( struct iscsi_conn ) ,
2005-08-05 06:30:08 +04:00
. max_conn = 1 ,
. max_cmd_len = ISCSI_TCP_MAX_CMD_LEN ,
2006-04-07 06:26:46 +04:00
/* session management */
. create_session = iscsi_tcp_session_create ,
. destroy_session = iscsi_tcp_session_destroy ,
/* connection management */
. create_conn = iscsi_tcp_conn_create ,
. bind_conn = iscsi_tcp_conn_bind ,
. destroy_conn = iscsi_tcp_conn_destroy ,
2005-08-05 06:30:08 +04:00
. set_param = iscsi_conn_set_param ,
2006-01-14 03:05:50 +03:00
. get_conn_param = iscsi_conn_get_param ,
2006-04-07 06:13:36 +04:00
. get_conn_str_param = iscsi_conn_get_str_param ,
2006-01-14 03:05:50 +03:00
. get_session_param = iscsi_session_get_param ,
2005-08-05 06:30:08 +04:00
. start_conn = iscsi_conn_start ,
. stop_conn = iscsi_conn_stop ,
2006-04-07 06:26:46 +04:00
/* these are called as part of conn recovery */
. suspend_conn_recv = iscsi_tcp_suspend_conn_rx ,
. terminate_conn = iscsi_tcp_terminate_conn ,
/* IO */
2005-08-05 06:30:08 +04:00
. send_pdu = iscsi_conn_send_pdu ,
. get_stats = iscsi_conn_get_stats ,
2006-04-07 06:26:46 +04:00
. init_cmd_task = iscsi_tcp_cmd_init ,
. init_mgmt_task = iscsi_tcp_mgmt_init ,
. xmit_cmd_task = iscsi_tcp_ctask_xmit ,
. xmit_mgmt_task = iscsi_tcp_mtask_xmit ,
. cleanup_cmd_task = iscsi_tcp_cleanup_ctask ,
/* recovery */
2006-04-07 06:13:39 +04:00
. session_recovery_timedout = iscsi_session_recovery_timedout ,
2005-08-05 06:30:08 +04:00
} ;
static int __init
iscsi_tcp_init ( void )
{
if ( iscsi_max_lun < 1 ) {
printk ( KERN_ERR " Invalid max_lun value of %u \n " , iscsi_max_lun ) ;
return - EINVAL ;
}
iscsi_tcp_transport . max_lun = iscsi_max_lun ;
taskcache = kmem_cache_create ( " iscsi_taskcache " ,
sizeof ( struct iscsi_data_task ) , 0 ,
2006-03-22 11:08:15 +03:00
SLAB_HWCACHE_ALIGN , NULL , NULL ) ;
2005-08-05 06:30:08 +04:00
if ( ! taskcache )
return - ENOMEM ;
2006-01-14 03:05:50 +03:00
if ( ! iscsi_register_transport ( & iscsi_tcp_transport ) )
2005-08-05 06:30:08 +04:00
kmem_cache_destroy ( taskcache ) ;
2006-01-14 03:05:50 +03:00
return 0 ;
2005-08-05 06:30:08 +04:00
}
static void __exit
iscsi_tcp_exit ( void )
{
iscsi_unregister_transport ( & iscsi_tcp_transport ) ;
kmem_cache_destroy ( taskcache ) ;
}
module_init ( iscsi_tcp_init ) ;
module_exit ( iscsi_tcp_exit ) ;