iscsi update for 2.6.25
Subsystems (5)
This patchset was made over scsi-rc-fixes (it also applies to Linus's git tree). It will not apply over scsi-misc, because there are some patches which went into scsi-rc-fixes/2.6.24-rc4 and scsi-misc has not been updated for the scsi-rc-fixes patches.
Sorry for the bigger than normal update. There are patches from Olaf and Boaz which do the following:
- Rewrite iscsi_tcp data path (from Olaf), so we do not have two ulgy abstractions for the send and recv path. Now we have one nice iscsi_segment struct that works for both paths.
- Add basic iscsi spec handling we will need for Bidi support (from Boaz). This does not add bidi support. It just adds code that is common.
- Device reset support. Some clustering software needed at least device reset support, so I cleaned up that code and added device logical unit reset support. I will add warm target reset support later.
- Lots of fixes for things like shutdown, r2t leaks, and lots of clean up from Olaf.
- sg chaining support.
PATCH 1/23 libiscsi, iscsi_tcp: add device support
From: Mike Christie <[email protected]>
This patch adds logical unit reset support. This should work for ib_iser, but I have not finished testing that driver so it is not hooked in yet.
This patch also temporarily reverts the iscsi_tcp r2t write out patch. That code is completely rewritten in this patchset.
--- drivers/infiniband/ulp/iser/iscsi_iser.c | 6 - drivers/scsi/iscsi_tcp.c | 151 +++++----- drivers/scsi/iscsi_tcp.h | 34 +- drivers/scsi/libiscsi.c | 494 +++++++++++++++++------------- drivers/scsi/scsi_transport_iscsi.c | 4 +- include/scsi/iscsi_if.h | 2 + include/scsi/iscsi_proto.h | 2 + include/scsi/libiscsi.h | 25 +- 8 files changed, 396 insertions(+), 322 deletions(-)
drivers/infiniband/ulp/iser/iscsi_iser.c +0 -6
@@ -220,12 +220,6 @@ iscsi_iser_ctask_xmit(struct iscsi_conn *conn,debug_scsi ("ctask deq [cid %d itt 0x%x]\n" , conn->id, ctask->itt); -/* -* serialize with TMF AbortTask -*/ -if (ctask->mtask) -return error; -/* Send the cmd PDU */ if (!iser_ctask->command_sent) { error =iser_send_command (conn, ctask);
drivers/scsi/iscsi_tcp.c +76 -75
408 lines not on this page · raw message
drivers/scsi/iscsi_tcp.h +17 -17
@@ -32,21 +32,21 @@#define IN_PROGRESS_PAD_RECV 0x4/* xmit state machine */ -#define XMSTATE_VALUE_IDLE 0 -#define XMSTATE_BIT_CMD_HDR_INIT 0 -#define XMSTATE_BIT_CMD_HDR_XMIT 1 -#define XMSTATE_BIT_IMM_HDR 2 -#define XMSTATE_BIT_IMM_DATA 3 -#define XMSTATE_BIT_UNS_INIT 4 -#define XMSTATE_BIT_UNS_HDR 5 -#define XMSTATE_BIT_UNS_DATA 6 -#define XMSTATE_BIT_SOL_HDR 7 -#define XMSTATE_BIT_SOL_DATA 8 -#define XMSTATE_BIT_W_PAD 9 -#define XMSTATE_BIT_W_RESEND_PAD 10 -#define XMSTATE_BIT_W_RESEND_DATA_DIGEST 11 -#define XMSTATE_BIT_IMM_HDR_INIT 12 -#define XMSTATE_BIT_SOL_HDR_INIT 13 +#define XMSTATE_IDLE 0x0 +#define XMSTATE_CMD_HDR_INIT 0x1 +#define XMSTATE_CMD_HDR_XMIT 0x2 +#define XMSTATE_IMM_HDR 0x4 +#define XMSTATE_IMM_DATA 0x8 +#define XMSTATE_UNS_INIT 0x10 +#define XMSTATE_UNS_HDR 0x20 +#define XMSTATE_UNS_DATA 0x40 +#define XMSTATE_SOL_HDR 0x80 +#define XMSTATE_SOL_DATA 0x100 +#define XMSTATE_W_PAD 0x200 +#define XMSTATE_W_RESEND_PAD 0x400 +#define XMSTATE_W_RESEND_DATA_DIGEST 0x800 +#define XMSTATE_IMM_HDR_INIT 0x1000 +#define XMSTATE_SOL_HDR_INIT 0x2000#define ISCSI_PAD_LEN 4#define ISCSI_SG_TABLESIZE SG_ALL
@@ -122,7 +122,7 @@ struct iscsi_data_task {struct iscsi_tcp_mgmt_task {struct iscsi_hdr hdr;char hdrext[sizeof (__u32)];/* Header-Digest */ -unsigned long xmstate;/* mgmt xmit progress */ +int xmstate;/* mgmt xmit progress */ struct iscsi_buf headbuf;/* header buffer */ struct iscsi_buf sendbuf;/* in progress buffer */ int sent;
@@ -150,7 +150,7 @@ struct iscsi_tcp_cmd_task {int pad_count;/* padded bytes */ struct iscsi_buf headbuf;/* header buf (xmit) */ struct iscsi_buf sendbuf;/* in progress buffer*/ -unsigned long xmstate;/* xmit xtate machine */ +int xmstate;/* xmit xtate machine */ int sent;struct scatterlist *sg;/* per-cmd SG list */ struct scatterlist *bad_sg;/* assert statement */
drivers/scsi/libiscsi.c +281 -213
806 lines not on this page · raw message
drivers/scsi/scsi_transport_iscsi.c +3 -1
@@ -30,7 +30,7 @@#include <scsi/scsi_transport_iscsi.h> #include <scsi/iscsi_if.h> -#define ISCSI_SESSION_ATTRS 15 +#define ISCSI_SESSION_ATTRS 16#define ISCSI_CONN_ATTRS 11#define ISCSI_HOST_ATTRS 4#define ISCSI_TRANSPORT_VERSION "2.0-724"
@@ -1217,6 +1217,7 @@ iscsi_session_attr(username, ISCSI_PARAM_USERNAME, 1);iscsi_session_attr (username_in,ISCSI_PARAM_USERNAME_IN ,1 );iscsi_session_attr (password,ISCSI_PARAM_PASSWORD ,1 );iscsi_session_attr (password_in,ISCSI_PARAM_PASSWORD_IN ,1 ); +iscsi_session_attr (fast_abort,ISCSI_PARAM_FAST_ABORT ,1 );#define iscsi_priv_session_attr_show (field, format) \ static ssize_t \
@@ -1438,6 +1439,7 @@ iscsi_register_transport(struct iscsi_transport *tt)SETUP_SESSION_RD_ATTR (password_in,ISCSI_USERNAME_IN );SETUP_SESSION_RD_ATTR (username,ISCSI_PASSWORD );SETUP_SESSION_RD_ATTR (username_in,ISCSI_PASSWORD_IN ); +SETUP_SESSION_RD_ATTR (fast_abort,ISCSI_FAST_ABORT );SETUP_PRIV_SESSION_RD_ATTR (recovery_tmo);BUG_ON (count >ISCSI_SESSION_ATTRS );
include/scsi/iscsi_if.h +2 -0
@@ -236,6 +236,7 @@ enum iscsi_param {ISCSI_PARAM_PASSWORD ,ISCSI_PARAM_PASSWORD_IN , +ISCSI_PARAM_FAST_ABORT ,/* must always be last */ ISCSI_PARAM_MAX, };
@@ -266,6 +267,7 @@ enum iscsi_param {#define ISCSI_USERNAME_IN (1 << ISCSI_PARAM_USERNAME_IN)#define ISCSI_PASSWORD (1 << ISCSI_PARAM_PASSWORD)#define ISCSI_PASSWORD_IN (1 << ISCSI_PARAM_PASSWORD_IN) +#define ISCSI_FAST_ABORT (1 << ISCSI_PARAM_FAST_ABORT)/* iSCSI HBA params */ enum iscsi_host_param {
include/scsi/iscsi_proto.h +2 -0
@@ -600,6 +600,8 @@ struct iscsi_reject {#define ISCSI_MIN_MAX_BURST_LEN 512#define ISCSI_MAX_MAX_BURST_LEN 16777215 +#define ISCSI_DEF_TIME2WAIT 2 +/************************* RFC 3720 End *****************************/ #endif /* ISCSI_PROTO_H */
include/scsi/libiscsi.h +15 -10
@@ -57,11 +57,14 @@ struct iscsi_nopin;#define ISCSI_MAX_CMD_PER_LUN 128/* Task Mgmt states */ -#define TMABORT_INITIAL 0x0 -#define TMABORT_SUCCESS 0x1 -#define TMABORT_FAILED 0x2 -#define TMABORT_TIMEDOUT 0x3 -#define TMABORT_NOT_FOUND 0x4 +enum { +TMF_INITIAL , +TMF_QUEUED , +TMF_SUCCESS , +TMF_FAILED , +TMF_TIMEDOUT , +TMF_NOT_FOUND , +};/* Connection suspend "bit" */ #define ISCSI_SUSPEND_BIT 1
@@ -91,7 +94,6 @@ enum {ISCSI_TASK_COMPLETED ,ISCSI_TASK_PENDING , ISCSI_TASK_RUNNING, - ISCSI_TASK_ABORTING, }; struct iscsi_cmd_task {
@@ -110,7 +112,6 @@ struct iscsi_cmd_task {unsigned data_count;/* remaining Data-Out */ struct scsi_cmnd *sc;/* associated SCSI cmd*/ struct iscsi_conn *conn;/* used connection */ -struct iscsi_mgmt_task *mtask;/* tmf mtask in progr */ /* state set/tested under session->lock */ int state;
@@ -152,10 +153,11 @@ struct iscsi_conn {struct iscsi_cmd_task *ctask;/* xmit ctask in progress */ /* xmit */ -struct kfifo *mgmtqueue;/* mgmt (control) xmit queue */ +struct list_head mgmtqueue;/* mgmt (control) xmit queue */ struct list_head mgmt_run_list;/* list of control tasks */ struct list_head xmitqueue;/* data-path cmd queue */ struct list_head run_list;/* list of cmds in progress */ +struct list_head requeue;/* tasks needing another run */ struct work_struct xmitwork;/* per-conn. xmit workqueue */ unsigned long suspend_tx;/* suspend Tx */ unsigned long suspend_rx;/* suspend Rx */
@@ -163,8 +165,8 @@ struct iscsi_conn {/* abort */ wait_queue_head_t ehwait;/* used in eh_abort() */ struct iscsi_tm tmhdr; -struct timer_list tmabort_timer; -int tmabort_state;/* see TMABORT_INITIAL, etc.*/ +struct timer_list tmf_timer; +int tmf_state;/* see TMF_INITIAL, etc.*/ /* negotiated params */ unsigned max_recv_dlength;/* initiator_max_recv_dsl*/
@@ -231,6 +233,7 @@ struct iscsi_session {int pdu_inorder_en;int dataseq_inorder_en;int erl; +int fast_abort;int tpgt;char *username;char *username_in;
@@ -268,6 +271,7 @@ struct iscsi_session {extern int iscsi_change_queue_depth (struct scsi_device *sdev,int depth);extern int iscsi_eh_abort (struct scsi_cmnd *sc);extern int iscsi_eh_host_reset (struct scsi_cmnd *sc); +extern int iscsi_eh_device_reset (struct scsi_cmnd *sc);extern int iscsi_queuecommand (struct scsi_cmnd *sc,void (*done)(struct scsi_cmnd *));
@@ -326,6 +330,7 @@ extern int __iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *, char *, int);extern int iscsi_verify_itt (struct iscsi_conn *,struct iscsi_hdr *,uint32_t *); +extern void iscsi_requeue_ctask (struct iscsi_cmd_task *ctask);/* * generic helpers
Signature · 1 line
1.5.1.2
PATCH 2/23 iscsi_tcp: rewrite recv path
From: Mike Christie <[email protected]>
Quoted 1 line
From Olaf Kirch:
Rewrite recv path. Fixes:
- data digest processing and error handling.
- ahs support.
Some fixups by Mike Christie
--- drivers/scsi/iscsi_tcp.c | 1018 +++++++++++++++++++++++----------------------- drivers/scsi/iscsi_tcp.h | 66 ++-- include/scsi/libiscsi.h | 4 + 3 files changed, 552 insertions(+), 536 deletions(-)
drivers/scsi/iscsi_tcp.c +507 -511
@@ -48,7 +48,7 @@ MODULE_AUTHOR("Dmitry Yusupov <[email protected]>, " "Alex Aizman <[email protected]>");MODULE_DESCRIPTION ("iSCSI/TCP data-path" );MODULE_LICENSE ("GPL" ); -/* #define DEBUG_TCP */ +#undef DEBUG_TCP#define DEBUG_ASSERT #ifdef DEBUG_TCP
@@ -67,10 +67,15 @@ MODULE_LICENSE("GPL");static unsigned int iscsi_max_lun =512 ;module_param_named (max_lun, iscsi_max_lun, uint,S_IRUGO ); +static int iscsi_tcp_hdr_recv_done (struct iscsi_tcp_conn *tcp_conn, +struct iscsi_chunk *chunk); +static inline void iscsi_buf_init_iov (struct iscsi_buf *ibuf,char *vbuf,int size) { -sg_init_one (&ibuf->sg, vbuf, size); + ibuf->sg.page =virt_to_page (vbuf); + ibuf->sg.offset =offset_in_page (vbuf); + ibuf->sg.length = size; ibuf->sent =0 ; ibuf->use_sendmsg =1 ; }
@@ -78,12 +83,13 @@ iscsi_buf_init_iov(struct iscsi_buf *ibuf, char *vbuf, int size)static inline void iscsi_buf_init_sg (struct iscsi_buf *ibuf,struct scatterlist *sg) { -sg_init_table (&ibuf->sg,1 ); -sg_set_page (&ibuf->sg,sg_page (sg), sg->length, sg->offset); + ibuf->sg.page = sg->page; + ibuf->sg.offset = sg->offset; + ibuf->sg.length = sg->length;/* * Fastpath: sg element fits into single page */ -if (sg->length + sg->offset <=PAGE_SIZE && !PageSlab (sg_page (sg))) +if (sg->length + sg->offset <=PAGE_SIZE && !PageSlab (sg->page)) ibuf->use_sendmsg =0 ;else ibuf->use_sendmsg =1 ;
@@ -110,72 +116,331 @@ iscsi_hdr_digest(struct iscsi_conn *conn, struct iscsi_buf *buf, buf->sg.length +=sizeof (u32); } +/* +* Scatterlist handling: inside the iscsi_chunk, we +* remember an index into the scatterlist, and set data/size +* to the current scatterlist entry. For highmem pages, we +* kmap as needed. +* +* Note that the page is unmapped when we return from +* TCP's data_ready handler, so we may end up mapping and +* unmapping the same page repeatedly. The whole reason +* for this is that we shouldn't keep the page mapped +* outside the softirq. +*/ + +/** +* iscsi_tcp_chunk_init_sg - init indicated scatterlist entry +* @chunk: the buffer object +* @idx: index into scatterlist +* @offset: byte offset into that sg entry +* +* This function sets up the chunk so that subsequent +* data is copied to the indicated sg entry, at the given +* offset. +*/ +static inline void +iscsi_tcp_chunk_init_sg (struct iscsi_chunk *chunk, +unsigned int idx,unsigned int offset) +{ +struct scatterlist *sg; + +BUG_ON (chunk->sg ==NULL ); + + sg = &chunk->sg[idx]; + chunk->sg_index = idx; + chunk->sg_offset = offset; + chunk->size =min (sg->length - offset, chunk->total_size); + chunk->data =NULL ; +} + +/** +* iscsi_tcp_chunk_map - map the current S/G page +* @chunk: iscsi chunk +* +* We only need to possibly kmap data if scatter lists are being used, +* because the iscsi passthrough and internal IO paths will never use high +* mem pages. +*/ +static inline void +iscsi_tcp_chunk_map (struct iscsi_chunk *chunk) +{ +struct scatterlist *sg; + +if (chunk->data !=NULL || !chunk->sg) +return ; + + sg = &chunk->sg[chunk->sg_index]; +BUG_ON (chunk->sg_mapped); +BUG_ON (sg->length ==0 ); + chunk->sg_mapped =kmap_atomic (sg->page,KM_SOFTIRQ0 ); + chunk->data = chunk->sg_mapped + sg->offset + chunk->sg_offset; +} + +static inline void +iscsi_tcp_chunk_unmap (struct iscsi_chunk *chunk) +{ +if (chunk->sg_mapped) { +kunmap_atomic (chunk->sg_mapped,KM_SOFTIRQ0 ); + chunk->sg_mapped =NULL ; + chunk->data =NULL ; + } +} + +/* +* Splice the digest buffer into the buffer +*/ +static inline void +iscsi_tcp_chunk_splice_digest (struct iscsi_chunk *chunk,void *digest) +{ + chunk->data = digest; + chunk->digest_len =ISCSI_DIGEST_SIZE ; + chunk->total_size +=ISCSI_DIGEST_SIZE ; + chunk->size =ISCSI_DIGEST_SIZE ; + chunk->copied =0 ; + chunk->sg =NULL ; + chunk->sg_index =0 ; + chunk->hash =NULL ; +} + +/** +* iscsi_tcp_chunk_done - check whether the chunk is complete +* @chunk: iscsi chunk to check +* +* Check if we're done receiving this chunk. If the receive +* buffer is full but we expect more data, move on to the +* next entry in the scatterlist. +* +* If the amount of data we received isn't a multiple of 4, +* we will transparently receive the pad bytes, too. +* +* This function must be re-entrant. +*/ static inline int -iscsi_hdr_extract (struct iscsi_tcp_conn *tcp_conn) +iscsi_tcp_chunk_done (struct iscsi_chunk *chunk) { -struct sk_buff *skb = tcp_conn->in.skb; +static unsigned char padbuf[ISCSI_PAD_LEN ]; + +if (chunk->copied < chunk->size) { +iscsi_tcp_chunk_map (chunk); +return 0 ; + } - tcp_conn->in.zero_copy_hdr =0 ; + chunk->total_copied += chunk->copied; + chunk->copied =0 ; + chunk->size =0 ; -if (tcp_conn->in.copy >= tcp_conn->hdr_size && - tcp_conn->in_progress ==IN_PROGRESS_WAIT_HEADER ) { -/* -* Zero-copy PDU Header: using connection context -* to store header pointer. -*/ -if (skb_shinfo (skb)->frag_list ==NULL && - !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 { -/* ignoring return code since we checked -* in.copy before */ -skb_copy_bits (skb, tcp_conn->in.offset, - &tcp_conn->hdr, tcp_conn->hdr_size); - tcp_conn->in.hdr = &tcp_conn->hdr; +/* Unmap the current scatterlist page, if there is one. */ +iscsi_tcp_chunk_unmap (chunk); + +/* Do we have more scatterlist entries? */ +if (chunk->total_copied < chunk->total_size) { +/* Proceed to the next entry in the scatterlist. */ +iscsi_tcp_chunk_init_sg (chunk, chunk->sg_index +1 ,0 ); +iscsi_tcp_chunk_map (chunk); +BUG_ON (chunk->size ==0 ); +return 0 ; + } + +/* Do we need to handle padding? */ +if (chunk->total_copied & (ISCSI_PAD_LEN -1 )) { +unsigned int pad; + + pad =ISCSI_PAD_LEN - (chunk->total_copied & (ISCSI_PAD_LEN -1 )); +debug_tcp ("consume %d pad bytes\n" , pad); + chunk->total_size += pad; + chunk->size = pad; + chunk->data = padbuf; +return 0 ; + } + +/* +* Set us up for receiving the data digest. hdr digest +* is completely handled in hdr done function. +*/ +if (chunk->hash) { +if (chunk->digest_len ==0 ) { +crypto_hash_final (chunk->hash, chunk->digest); +iscsi_tcp_chunk_splice_digest (chunk, + chunk->recv_digest); +return 0 ; } - tcp_conn->in.offset += tcp_conn->hdr_size; - tcp_conn->in.copy -= tcp_conn->hdr_size; - }else { -int hdr_remains; -int copylen; + } -/* -* PDU header scattered across SKB's, -* copying it... This'll happen quite rarely. -*/ +return 1 ; +} + +/** +* iscsi_tcp_chunk_recv - copy data to chunk +* @tcp_conn: the iSCSI TCP connection +* @chunk: the buffer to copy to +* @ptr: data pointer +* @len: amount of data available +* +* This function copies up to @len bytes to the +* given buffer, and returns the number of bytes +* consumed, which can actually be less than @len. +* +* If hash digest is enabled, the function will update the +* hash while copying. +* Combining these two operations doesn't buy us a lot (yet), +* but in the future we could implement combined copy+crc, +* just way we do for network layer checksums. +*/ +static int +iscsi_tcp_chunk_recv (struct iscsi_tcp_conn *tcp_conn, +struct iscsi_chunk *chunk,const void *ptr, +unsigned int len) +{ +struct scatterlist sg; +unsigned int copy, copied =0 ; + +while (!iscsi_tcp_chunk_done (chunk)) { +if (copied == len) + goto out; + + copy =min (len - copied, chunk->size - chunk->copied); +memcpy (chunk->data + chunk->copied, ptr + copied, copy); + +if (chunk->hash) { +sg_init_one (&sg, ptr + copied, copy); +crypto_hash_update (chunk->hash, &sg, copy); + } + chunk->copied += copy; + copied += copy; + } + +out: +return copied; +} -if (tcp_conn->in_progress ==IN_PROGRESS_WAIT_HEADER ) - tcp_conn->in.hdr_offset =0 ; +static inline void +iscsi_tcp_dgst_header (struct hash_desc *hash,const void *hdr,size_t hdrlen, +unsigned char digest[ISCSI_DIGEST_SIZE ]) +{ +struct scatterlist sg; + +sg_init_one (&sg, hdr, hdrlen); +crypto_hash_digest (hash, &sg, hdrlen, digest); +} + +static inline int +iscsi_tcp_dgst_verify (struct iscsi_tcp_conn *tcp_conn, +struct iscsi_chunk *chunk) +{ +if (!chunk->digest_len) +return 1 ; + +if (memcmp (chunk->recv_digest, chunk->digest, chunk->digest_len)) { +debug_scsi ("digest mismatch\n" ); +return 0 ; + } - hdr_remains = tcp_conn->hdr_size - tcp_conn->in.hdr_offset; -BUG_ON (hdr_remains <=0 ); +return 1 ; +} - 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); +/* +* Helper function to set up chunk buffer +*/ +static inline void +__iscsi_chunk_init (struct iscsi_chunk *chunk,size_t size, +iscsi_chunk_done_fn_t *done,struct hash_desc *hash) +{ +memset (chunk,0 ,sizeof (*chunk)); + chunk->total_size = size; + chunk->done = done; -debug_tcp ("PDU gather offset %d bytes %d in.offset %d " -"in.copy %d\n" , tcp_conn->in.hdr_offset, copylen, - tcp_conn->in.offset, tcp_conn->in.copy); +if (hash) { + chunk->hash = hash; +crypto_hash_init (hash); + } +} - tcp_conn->in.offset += copylen; - tcp_conn->in.copy -= copylen; -if (copylen < hdr_remains) { - tcp_conn->in_progress =IN_PROGRESS_HEADER_GATHER ; - tcp_conn->in.hdr_offset += copylen; -return -EAGAIN ; +static inline void +iscsi_chunk_init_linear (struct iscsi_chunk *chunk,void *data,size_t size, +iscsi_chunk_done_fn_t *done,struct hash_desc *hash) +{ +__iscsi_chunk_init (chunk, size, done, hash); + chunk->data = data; + chunk->size = size; +} + +static inline int +iscsi_chunk_seek_sg (struct iscsi_chunk *chunk, +struct scatterlist *sg,unsigned int sg_count, +unsigned int offset,size_t size, +iscsi_chunk_done_fn_t *done,struct hash_desc *hash) +{ +unsigned int i; + +__iscsi_chunk_init (chunk, size, done, hash); +for (i =0 ; i < sg_count; ++i) { +if (offset < sg[i].length) { + chunk->sg = sg; + chunk->sg_count = sg_count; +iscsi_tcp_chunk_init_sg (chunk, i, offset); +return 0 ; } - tcp_conn->in.hdr = &tcp_conn->hdr; - tcp_conn->discontiguous_hdr_cnt++; - tcp_conn->in_progress =IN_PROGRESS_WAIT_HEADER ; + offset -= sg[i].length; } +return ISCSI_ERR_DATA_OFFSET ; +} + +/** +* iscsi_tcp_hdr_recv_prep - prep chunk for hdr reception +* @tcp_conn: iscsi connection to prep for +* +* This function always passes NULL for the hash argument, because when this +* function is called we do not yet know the final size of the header and want +* to delay the digest processing until we know that. +*/ +static void +iscsi_tcp_hdr_recv_prep (struct iscsi_tcp_conn *tcp_conn) +{ +debug_tcp ("iscsi_tcp_hdr_recv_prep(%p%s)\n" , tcp_conn, + tcp_conn->iscsi_conn->hdrdgst_en ?", digest enabled" :"" ); +iscsi_chunk_init_linear (&tcp_conn->in.chunk, + tcp_conn->in.hdr_buf,sizeof (struct iscsi_hdr ), + iscsi_tcp_hdr_recv_done,NULL ); +} + +/* +* Handle incoming reply to any other type of command +*/ +static int +iscsi_tcp_data_recv_done (struct iscsi_tcp_conn *tcp_conn, +struct iscsi_chunk *chunk) +{ +struct iscsi_conn *conn = tcp_conn->iscsi_conn; +int rc =0 ; + +if (!iscsi_tcp_dgst_verify (tcp_conn, chunk)) +return ISCSI_ERR_DATA_DGST ; + + rc =iscsi_complete_pdu (conn, tcp_conn->in.hdr, + conn->data, tcp_conn->in.datalen); +if (rc) +return rc; + +iscsi_tcp_hdr_recv_prep (tcp_conn);return 0 ; } +static void +iscsi_tcp_data_recv_prep (struct iscsi_tcp_conn *tcp_conn) +{ +struct iscsi_conn *conn = tcp_conn->iscsi_conn; +struct hash_desc *rx_hash =NULL ; + +if (conn->datadgst_en) + rx_hash = &tcp_conn->rx_hash; + +iscsi_chunk_init_linear (&tcp_conn->in.chunk, + conn->data, tcp_conn->in.datalen, + iscsi_tcp_data_recv_done, rx_hash); +} +/* * must be called with session lock */
@@ -417,16 +682,49 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)return 0 ; } +/* +* Handle incoming reply to DataIn command +*/ static int -iscsi_tcp_hdr_recv (struct iscsi_conn *conn) +iscsi_tcp_process_data_in (struct iscsi_tcp_conn *tcp_conn, +struct iscsi_chunk *chunk) +{ +struct iscsi_conn *conn = tcp_conn->iscsi_conn; +struct iscsi_hdr *hdr = tcp_conn->in.hdr; +int rc; + +if (!iscsi_tcp_dgst_verify (tcp_conn, chunk)) +return ISCSI_ERR_DATA_DGST ; + +/* check for non-exceptional status */ +if (hdr->flags &ISCSI_FLAG_DATA_STATUS ) { + rc =iscsi_complete_pdu (conn, tcp_conn->in.hdr,NULL ,0 ); +if (rc) +return rc; + } + +iscsi_tcp_hdr_recv_prep (tcp_conn); +return 0 ; +} + +/** +* iscsi_tcp_hdr_dissect - process PDU header +* @conn: iSCSI connection +* @hdr: PDU header +* +* This function analyzes the header of the PDU received, +* and performs several sanity checks. If the PDU is accompanied +* by data, the receive buffer is set up to copy the incoming data +* to the correct location. +*/ +static int +iscsi_tcp_hdr_dissect (struct iscsi_conn *conn,struct iscsi_hdr *hdr) {int rc =0 , opcode, ahslen; -struct iscsi_hdr *hdr;struct iscsi_session *session = conn->session;struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -uint32_t cdgst, rdgst =0 , itt; - - hdr = tcp_conn->in.hdr; +struct iscsi_cmd_task *ctask; +uint32_t itt;/* verify PDU length */ tcp_conn->in.datalen =ntoh24 (hdr->dlength);
@@ -435,77 +733,72 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn) tcp_conn->in.datalen, conn->max_recv_dlength);return ISCSI_ERR_DATALEN ; } - tcp_conn->data_copied =0 ; -/* read AHS */ +/* Additional header segments. So far, we don't +* process additional headers. +*/ ahslen = hdr->hlength <<2 ; - tcp_conn->in.offset += ahslen; - tcp_conn->in.copy -= ahslen; -if (tcp_conn->in.copy <0 ) { -printk (KERN_ERR "iscsi_tcp: can't handle AHS with length " -"%d bytes\n" , ahslen); -return ISCSI_ERR_AHSLEN ; - } - -/* calculate read padding */ - 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); - } - -if (conn->hdrdgst_en) { -struct scatterlist sg; - -sg_init_one (&sg, (u8 *)hdr, -sizeof (struct iscsi_hdr ) + ahslen); -crypto_hash_digest (&tcp_conn->rx_hash, &sg, sg.length, - (u8 *)&cdgst); - rdgst = *(uint32_t *)((char *)hdr +sizeof (struct iscsi_hdr ) + - ahslen); -if (cdgst != rdgst) { -printk (KERN_ERR "iscsi_tcp: hdrdgst error " -"recv 0x%x calc 0x%x\n" , rdgst, cdgst); -return ISCSI_ERR_HDR_DGST ; - } - } opcode = hdr->opcode &ISCSI_OPCODE_MASK ;/* verify itt (itt encoding: age+cid+itt) */ rc =iscsi_verify_itt (conn, hdr, &itt);if (rc ==ISCSI_ERR_NO_SCSI_CMD ) { +/* XXX: what does this do? */ tcp_conn->in.datalen =0 ;/* force drop */ return 0 ; }else if (rc)return rc; -debug_tcp ("opcode 0x%x offset %d copy %d ahslen %d datalen %d\n" , - opcode, tcp_conn->in.offset, tcp_conn->in.copy, - ahslen, tcp_conn->in.datalen); +debug_tcp ("opcode 0x%x ahslen %d datalen %d\n" , + opcode, ahslen, tcp_conn->in.datalen);switch (opcode) {case ISCSI_OP_SCSI_DATA_IN : - tcp_conn->in.ctask = session->cmds[itt]; - rc =iscsi_data_rsp (conn, tcp_conn->in.ctask); + ctask = session->cmds[itt]; + rc =iscsi_data_rsp (conn, ctask);if (rc)return rc; +if (tcp_conn->in.datalen) { +struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; +struct hash_desc *rx_hash =NULL ; + +/* +* Setup copy of Data-In into the Scsi_Cmnd +* Scatterlist case: +* We set up the iscsi_chunk to point to the next +* scatterlist entry to copy to. As we go along, +* we move on to the next scatterlist entry and +* update the digest per-entry. +*/ +if (conn->datadgst_en) + rx_hash = &tcp_conn->rx_hash; + +debug_tcp ("iscsi_tcp_begin_data_in(%p, offset=%d, " +"datalen=%d)\n" , tcp_conn, + tcp_ctask->data_offset, + tcp_conn->in.datalen); +return iscsi_chunk_seek_sg (&tcp_conn->in.chunk, +scsi_sglist (ctask->sc), +scsi_sg_count (ctask->sc), + tcp_ctask->data_offset, + tcp_conn->in.datalen, + iscsi_tcp_process_data_in, + rx_hash); + }/* fall through */ case ISCSI_OP_SCSI_CMD_RSP : - tcp_conn->in.ctask = session->cmds[itt]; -if (tcp_conn->in.datalen) - goto copy_hdr; - -spin_lock (&session->lock); - rc =__iscsi_complete_pdu (conn, hdr,NULL ,0 ); -spin_unlock (&session->lock); +if (tcp_conn->in.datalen) { +iscsi_tcp_data_recv_prep (tcp_conn); +return 0 ; + } + rc =iscsi_complete_pdu (conn, hdr,NULL ,0 );break ;case ISCSI_OP_R2T : - tcp_conn->in.ctask = session->cmds[itt]; + 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 if (ctask->sc->sc_data_direction ==DMA_TO_DEVICE ) + rc =iscsi_r2t_rsp (conn, ctask);else rc =ISCSI_ERR_PROTO ;break ;
@@ -518,8 +811,7 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn)* than 8K, but there are no targets that currently do this. * For now we fail until we find a vendor that needs it */ -if (ISCSI_DEF_MAX_RECV_SEG_LEN < - tcp_conn->in.datalen) { +if (ISCSI_DEF_MAX_RECV_SEG_LEN < tcp_conn->in.datalen) { printk(KERN_ERR "iscsi_tcp: received buffer of len %u " "but conn buffer is only %u (opcode %0x)\n", tcp_conn->in.datalen,
@@ -528,8 +820,13 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn)break ; } -if (tcp_conn->in.datalen) - goto copy_hdr; +/* If there's data coming in with the response, +* receive it to the connection's buffer. +*/ +if (tcp_conn->in.datalen) { +iscsi_tcp_data_recv_prep (tcp_conn); +return 0 ; + }/* fall through */ case ISCSI_OP_LOGOUT_RSP :case ISCSI_OP_NOOP_IN :
@@ -541,129 +838,15 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn)break ; } -return rc; - -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 ; -} - -/** -* iscsi_ctask_copy - copy skb bits to the destanation cmd task -* @conn: iscsi tcp connection -* @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 -iscsi_ctask_copy (struct iscsi_tcp_conn *tcp_conn,struct iscsi_cmd_task *ctask, -void *buf,int buf_size,int offset) -{ -struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; -int buf_left = buf_size - (tcp_conn->data_copied + offset); -unsigned size =min (tcp_conn->in.copy, buf_left); -int rc; - - size =min (size, ctask->data_count); - -debug_tcp ("ctask_copy %d bytes at offset %d copied %d\n" , - size, tcp_conn->in.offset, tcp_conn->in.copied); - -BUG_ON (size <=0 ); -BUG_ON (tcp_ctask->sent + size >scsi_bufflen (ctask->sc)); - - rc =skb_copy_bits (tcp_conn->in.skb, tcp_conn->in.offset, - (char *)buf + (offset + tcp_conn->data_copied), size); -/* must fit into skb->len */ -BUG_ON (rc); - - tcp_conn->in.offset += size; - tcp_conn->in.copy -= size; - tcp_conn->in.copied += size; - tcp_conn->data_copied += size; - tcp_ctask->sent += size; - ctask->data_count -= size; - -BUG_ON (tcp_conn->in.copy <0 ); -BUG_ON (ctask->data_count <0 ); - -if (buf_size != (tcp_conn->data_copied + offset)) { -if (!ctask->data_count) { -BUG_ON (buf_size - tcp_conn->data_copied <0 ); -/* done with this PDU */ -return buf_size - tcp_conn->data_copied; - } -return -EAGAIN ; +if (rc ==0 ) { +/* Anything that comes with data should have +* been handled above. */ +if (tcp_conn->in.datalen) +return ISCSI_ERR_PROTO ; +iscsi_tcp_hdr_recv_prep (tcp_conn); } -/* done with this buffer or with both - PDU and buffer */ - tcp_conn->data_copied =0 ; -return 0 ; -} - -/** -* iscsi_tcp_copy - copy skb bits to the destanation buffer -* @conn: iscsi tcp connection -* -* Notes: -* The function calls skb_copy_bits() and updates per-connection -* byte counters. -**/ -static inline int -iscsi_tcp_copy (struct iscsi_conn *conn,int buf_size) -{ -struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -int buf_left = buf_size - tcp_conn->data_copied; -int size =min (tcp_conn->in.copy, buf_left); -int rc; - -debug_tcp ("tcp_copy %d bytes at offset %d copied %d\n" , - size, tcp_conn->in.offset, tcp_conn->data_copied); -BUG_ON (size <=0 ); - - rc =skb_copy_bits (tcp_conn->in.skb, tcp_conn->in.offset, - (char *)conn->data + tcp_conn->data_copied, size); -BUG_ON (rc); - - tcp_conn->in.offset += size; - tcp_conn->in.copy -= size; - tcp_conn->in.copied += size; - tcp_conn->data_copied += size; - -if (buf_size != tcp_conn->data_copied) -return -EAGAIN ; - -return 0 ; +return rc; } static inline void
@@ -677,325 +860,146 @@ partial_sg_digest_update(struct hash_desc *desc, struct scatterlist *sg,crypto_hash_update (desc, &temp, length); } -static void -iscsi_recv_digest_update (struct iscsi_tcp_conn *tcp_conn,char * buf,int len) -{ -struct scatterlist tmp; - -sg_init_one (&tmp, buf, len); -crypto_hash_update (&tcp_conn->rx_hash, &tmp, len); -} - -static int iscsi_scsi_data_in (struct iscsi_conn *conn) +/** +* iscsi_tcp_hdr_recv_done - process PDU header +* +* This is the callback invoked when the PDU header has +* been received. If the header is followed by additional +* header segments, we go back for more data. +*/ +static int +iscsi_tcp_hdr_recv_done (struct iscsi_tcp_conn *tcp_conn, +struct iscsi_chunk *chunk) { -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; -struct scsi_cmnd *sc = ctask->sc; -struct scatterlist *sg; -int i, offset, rc =0 ; - -BUG_ON ((void *)ctask != sc->SCp.ptr); - - offset = tcp_ctask->data_offset; - sg =scsi_sglist (sc); - -if (tcp_ctask->data_offset) -for (i =0 ; i < tcp_ctask->sg_count; i++) - offset -= sg[i].length; -/* we've passed through partial sg*/ -if (offset <0 ) - offset =0 ; - -for (i = tcp_ctask->sg_count; i <scsi_sg_count (sc); i++) { -char *dest; - - dest =kmap_atomic (sg_page (&sg[i]),KM_SOFTIRQ0 ); - rc =iscsi_ctask_copy (tcp_conn, ctask, dest + sg[i].offset, - 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) -crypto_hash_update ( - &tcp_conn->rx_hash, - &sg[i], sg[i].length); -else -partial_sg_digest_update ( - &tcp_conn->rx_hash, - &sg[i], - sg[i].offset + offset, - sg[i].length - offset); - } - offset =0 ; - tcp_ctask->sg_count++; - } - -if (!ctask->data_count) { -if (rc && conn->datadgst_en) -/* -* data-in is complete, but buffer not... -*/ -partial_sg_digest_update (&tcp_conn->rx_hash, - &sg[i], - sg[i].offset, - sg[i].length-rc); - rc =0 ; -break ; - } - -if (!tcp_conn->in.copy) -return -EAGAIN ; - } -BUG_ON (ctask->data_count); +struct iscsi_conn *conn = tcp_conn->iscsi_conn; +struct iscsi_hdr *hdr; -/* check for non-exceptional status */ -if (tcp_conn->in.hdr->flags &ISCSI_FLAG_DATA_STATUS ) { -debug_scsi ("done [sc %lx res %d itt 0x%x flags 0x%x]\n" , - (long )sc, sc->result, ctask->itt, - tcp_conn->in.hdr->flags); -spin_lock (&conn->session->lock); -__iscsi_complete_pdu (conn, tcp_conn->in.hdr,NULL ,0 ); -spin_unlock (&conn->session->lock); +/* Check if there are additional header segments +* *prior* to computing the digest, because we +* may need to go back to the caller for more. +*/ + hdr = (struct iscsi_hdr *) tcp_conn->in.hdr_buf; +if (chunk->copied ==sizeof (struct iscsi_hdr ) && hdr->hlength) { +/* Bump the header length - the caller will +* just loop around and get the AHS for us, and +* call again. */ +unsigned int ahslen = hdr->hlength <<2 ; + +/* Make sure we don't overflow */ +if (sizeof (*hdr) + ahslen >sizeof (tcp_conn->in.hdr_buf)) +return ISCSI_ERR_AHSLEN ; + + chunk->total_size += ahslen; + chunk->size += ahslen; +return 0 ; } -return rc; -} - -static int -iscsi_data_recv (struct iscsi_conn *conn) -{ -struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -int rc =0 , opcode; - - opcode = tcp_conn->in.hdr->opcode &ISCSI_OPCODE_MASK ; -switch (opcode) { -case ISCSI_OP_SCSI_DATA_IN : - rc =iscsi_scsi_data_in (conn); -break ; -case ISCSI_OP_SCSI_CMD_RSP : -case ISCSI_OP_TEXT_RSP : -case ISCSI_OP_LOGIN_RSP : -case ISCSI_OP_ASYNC_EVENT : -case ISCSI_OP_REJECT : -/* -* Collect data segment to the connection's data -* placeholder -*/ -if (iscsi_tcp_copy (conn, tcp_conn->in.datalen)) { - rc = -EAGAIN ; - goto exit; +/* We're done processing the header. See if we're doing +* header digests; if so, set up the recv_digest buffer +* and go back for more. */ +if (conn->hdrdgst_en) { +if (chunk->digest_len ==0 ) { +iscsi_tcp_chunk_splice_digest (chunk, + chunk->recv_digest); +return 0 ; } +iscsi_tcp_dgst_header (&tcp_conn->rx_hash, hdr, + chunk->total_copied -ISCSI_DIGEST_SIZE , + chunk->digest); - rc =iscsi_complete_pdu (conn, tcp_conn->in.hdr, conn->data, - tcp_conn->in.datalen); -if (!rc && conn->datadgst_en && opcode !=ISCSI_OP_LOGIN_RSP ) -iscsi_recv_digest_update (tcp_conn, conn->data, - tcp_conn->in.datalen); -break ; -default : -BUG_ON (1 ); +if (!iscsi_tcp_dgst_verify (tcp_conn, chunk)) +return ISCSI_ERR_HDR_DGST ; } -exit: -return rc; + + tcp_conn->in.hdr = hdr; +return iscsi_tcp_hdr_dissect (conn, hdr); }/** -* iscsi_tcp_data_recv - TCP receive in sendfile fashion +* iscsi_tcp_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) +iscsi_tcp_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;struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -int processed; -char pad[ISCSI_PAD_LEN ]; -struct scatterlist sg; - -/* -* Save current SKB and its offset in the corresponding -* connection context. -*/ - 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); +struct iscsi_chunk *chunk = &tcp_conn->in.chunk; +struct skb_seq_state seq; +unsigned int consumed =0 ; +int rc =0 ; -more: - tcp_conn->in.copied =0 ; - rc =0 ; +debug_tcp ("in %d bytes\n" , skb->len - offset);if (unlikely (conn->suspend_rx)) {debug_tcp ("conn %d Rx suspended!\n" , conn->id);return 0 ; } -if (tcp_conn->in_progress ==IN_PROGRESS_WAIT_HEADER || - tcp_conn->in_progress ==IN_PROGRESS_HEADER_GATHER ) { - rc =iscsi_hdr_extract (tcp_conn); -if (rc) { -if (rc == -EAGAIN ) - goto nomore; -else { -iscsi_conn_failure (conn,ISCSI_ERR_CONN_FAILED ); -return 0 ; - } - } - -/* -* Verify and process incoming PDU header. -*/ - rc =iscsi_tcp_hdr_recv (conn); -if (!rc && tcp_conn->in.datalen) { -if (conn->datadgst_en) -crypto_hash_init (&tcp_conn->rx_hash); - tcp_conn->in_progress =IN_PROGRESS_DATA_RECV ; - }else if (rc) { -iscsi_conn_failure (conn, rc); -return 0 ; - } - } - -if (tcp_conn->in_progress ==IN_PROGRESS_DDIGEST_RECV && - tcp_conn->in.copy) { -uint32_t recv_digest; +skb_prepare_seq_read (skb, offset, skb->len, &seq); +while (1 ) { +unsigned int avail; +const u8 *ptr; -debug_tcp ("extra data_recv offset %d copy %d\n" , - tcp_conn->in.offset, tcp_conn->in.copy); - -if (!tcp_conn->data_copied) { -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_hash_update (&tcp_conn->rx_hash, - &sg, sg.length); + avail =skb_seq_read (consumed, &ptr, &seq); +if (avail ==0 ) +break ; +BUG_ON (chunk->copied >= chunk->size); + +debug_tcp ("skb %p ptr=%p avail=%u\n" , skb, ptr, avail); + rc =iscsi_tcp_chunk_recv (tcp_conn, chunk, ptr, avail); +BUG_ON (rc ==0 ); + consumed += rc; + +if (chunk->total_copied >= chunk->total_size) { + rc = chunk->done (tcp_conn, chunk); +if (rc !=0 ) { +skb_abort_seq_read (&seq); + goto error; } -crypto_hash_final (&tcp_conn->rx_hash, - (u8 *) &tcp_conn->in.datadgst); -debug_tcp ("rx digest 0x%x\n" , tcp_conn->in.datadgst); - } - - rc =iscsi_tcp_copy (conn,sizeof (uint32_t )); -if (rc) { -if (rc == -EAGAIN ) - goto again; -iscsi_conn_failure (conn,ISCSI_ERR_CONN_FAILED ); -return 0 ; - } - -memcpy (&recv_digest, conn->data,sizeof (uint32_t )); -if (recv_digest != tcp_conn->in.datadgst) { -debug_tcp ("iscsi_tcp: data digest error!" -"0x%x != 0x%x\n" , recv_digest, - tcp_conn->in.datadgst); -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, - tcp_conn->in.datadgst); - tcp_conn->in_progress =IN_PROGRESS_WAIT_HEADER ; - } - } - -if (tcp_conn->in_progress ==IN_PROGRESS_DATA_RECV && - tcp_conn->in.copy) { -debug_tcp ("data_recv offset %d copy %d\n" , - tcp_conn->in.offset, tcp_conn->in.copy); - rc =iscsi_data_recv (conn); -if (rc) { -if (rc == -EAGAIN ) - goto again; -iscsi_conn_failure (conn,ISCSI_ERR_CONN_FAILED ); -return 0 ; +/* The done() functions sets up the +* next chunk. */ } - -if (tcp_conn->in.padding) - tcp_conn->in_progress =IN_PROGRESS_PAD_RECV ; -else if (conn->datadgst_en) - tcp_conn->in_progress =IN_PROGRESS_DDIGEST_RECV ; -else - tcp_conn->in_progress =IN_PROGRESS_WAIT_HEADER ; - tcp_conn->data_copied =0 ; - } - -if (tcp_conn->in_progress ==IN_PROGRESS_PAD_RECV && - tcp_conn->in.copy) { -int copylen =min (tcp_conn->in.padding - tcp_conn->data_copied, - tcp_conn->in.copy); - - tcp_conn->in.copy -= copylen; - tcp_conn->in.offset += copylen; - tcp_conn->data_copied += copylen; - -if (tcp_conn->data_copied != tcp_conn->in.padding) - tcp_conn->in_progress =IN_PROGRESS_PAD_RECV ; -else if (conn->datadgst_en) - tcp_conn->in_progress =IN_PROGRESS_DDIGEST_RECV ; -else - tcp_conn->in_progress =IN_PROGRESS_WAIT_HEADER ; - tcp_conn->data_copied =0 ; } -debug_tcp ("f, processed %d from out of %d padding %d\n" , - tcp_conn->in.offset - offset, (int )len, tcp_conn->in.padding); -BUG_ON (tcp_conn->in.offset - offset > len); + conn->rxdata_octets += consumed; +return consumed; -if (tcp_conn->in.offset - offset != len) { -debug_tcp ("continue to process %d bytes\n" , - (int )len - (tcp_conn->in.offset - offset)); - goto more; - } - -nomore: - processed = tcp_conn->in.offset - offset; -BUG_ON (processed ==0 ); -return processed; - -again: - processed = tcp_conn->in.offset - offset; -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; +error: +debug_tcp ("Error receiving PDU, errno=%d\n" , rc); +iscsi_conn_failure (conn,ISCSI_ERR_CONN_FAILED ); +return 0 ; }static void iscsi_tcp_data_ready (struct sock *sk,int flag) {struct iscsi_conn *conn = sk->sk_user_data; +struct iscsi_tcp_conn *tcp_conn = conn->dd_data;read_descriptor_t rd_desc;read_lock (&sk->sk_callback_lock);/* -* Use rd_desc to pass 'conn' to iscsi_tcp_data_recv. +* Use rd_desc to pass 'conn' to iscsi_tcp_recv. * We set count to 1 because we want the network layer to -* hand us all the skbs that are available. iscsi_tcp_data_recv +* hand us all the skbs that are available. iscsi_tcp_recv * handled pdus that cross buffers or pdus that still need data. */ rd_desc.arg.data = conn; rd_desc.count =1 ; -tcp_read_sock (sk, &rd_desc, iscsi_tcp_data_recv); +tcp_read_sock (sk, &rd_desc, iscsi_tcp_recv);read_unlock (&sk->sk_callback_lock); + +/* If we had to (atomically) map a highmem page, +* unmap it now. */ +iscsi_tcp_chunk_unmap (&tcp_conn->in.chunk); } static void
@@ -1097,9 +1101,9 @@ iscsi_send(struct iscsi_conn *conn, struct iscsi_buf *buf, int size, int flags)* slab case. */ if (buf->use_sendmsg) - res =sock_no_sendpage (sk,sg_page (&buf->sg), offset, size, flags); + res =sock_no_sendpage (sk, buf->sg.page, offset, size, flags);else - res = tcp_conn->sendpage (sk,sg_page (&buf->sg), offset, size, flags); + res = tcp_conn->sendpage (sk, buf->sg.page, offset, size, flags);if (res >=0 ) { conn->txdata_octets += res;
@@ -1783,9 +1787,6 @@ iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx) 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->tx_hash.tfm =crypto_alloc_hash ("crc32c" ,0 ,CRYPTO_ALG_ASYNC );
@@ -1862,11 +1863,9 @@ static void iscsi_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) {struct iscsi_conn *conn = cls_conn->dd_data; -struct iscsi_tcp_conn *tcp_conn = conn->dd_data;iscsi_conn_stop (cls_conn, flag);iscsi_tcp_release_conn (conn); - tcp_conn->hdr_size =sizeof (struct iscsi_hdr ); } static int iscsi_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock,
@@ -1966,7 +1965,7 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,/* * set receive state machine into initial state */ - tcp_conn->in_progress =IN_PROGRESS_WAIT_HEADER ; +iscsi_tcp_hdr_recv_prep (tcp_conn);return 0 ; free_socket:
@@ -2059,9 +2058,6 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,switch (param) {case ISCSI_PARAM_HDRDGST_EN :iscsi_set_param (cls_conn, param, buf, buflen); - tcp_conn->hdr_size =sizeof (struct iscsi_hdr ); -if (conn->hdrdgst_en) - tcp_conn->hdr_size +=sizeof (__u32);break ;case ISCSI_PARAM_DATADGST_EN :iscsi_set_param (cls_conn, param, buf, buflen);
drivers/scsi/iscsi_tcp.h +41 -25
95 lines not on this page · raw message
include/scsi/libiscsi.h +4 -0
@@ -77,6 +77,10 @@ enum {#define ISCSI_ADDRESS_BUF_LEN 64 +enum { +ISCSI_DIGEST_SIZE =sizeof (__u32), +}; +struct iscsi_mgmt_task {/* * Becuae LLDs allocate their hdr differently, this is a pointer to
Signature · 1 line
1.5.1.2
PATCH 3/23 Prettify resid handling and some extra checks
From: Mike Christie <[email protected]>
from Boaz Harrosh:
- Check to see that OVERFLOW is not negative indicating a bug.
- Unify handling of UNDERFLOW and OVERFLOW to the same code.
- Also handle BIDI_OVERFLOW.
--- drivers/scsi/iscsi_tcp.c | 16 +++++++--------- drivers/scsi/libiscsi.c | 12 +++++++----- 2 files changed, 14 insertions(+), 14 deletions(-)
drivers/scsi/iscsi_tcp.c +7 -9
@@ -507,22 +507,20 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) }if (rhdr->flags &ISCSI_FLAG_DATA_STATUS ) { + sc->result = (DID_OK <<16 ) | rhdr->cmd_status; conn->exp_statsn =be32_to_cpu (rhdr->statsn) +1 ; -if (rhdr->flags &ISCSI_FLAG_DATA_UNDERFLOW ) { +if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW | +ISCSI_FLAG_DATA_OVERFLOW )) {int res_count =be32_to_cpu (rhdr->residual_count);if (res_count >0 && - res_count <=scsi_bufflen (sc)) { + (rhdr->flags &ISCSI_FLAG_CMD_OVERFLOW || + res_count <=scsi_bufflen (sc)))scsi_set_resid (sc, res_count); - sc->result = (DID_OK <<16 ) | rhdr->cmd_status; - }else +else sc->result = (DID_BAD_TARGET <<16 ) | rhdr->cmd_status; - }else if (rhdr->flags &ISCSI_FLAG_DATA_OVERFLOW ) { -scsi_set_resid (sc,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++;
drivers/scsi/libiscsi.c +7 -5
@@ -291,17 +291,19 @@ invalid_datalen: min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); } -if (rhdr->flags &ISCSI_FLAG_CMD_UNDERFLOW ) { +if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW | +ISCSI_FLAG_CMD_OVERFLOW )) {int res_count =be32_to_cpu (rhdr->residual_count); -if (res_count >0 && res_count <=scsi_bufflen (sc)) +if (res_count >0 && + (rhdr->flags &ISCSI_FLAG_CMD_OVERFLOW || + res_count <=scsi_bufflen (sc)))scsi_set_resid (sc, res_count);else sc->result = (DID_BAD_TARGET <<16 ) | rhdr->cmd_status; - }else if (rhdr->flags &ISCSI_FLAG_CMD_BIDI_UNDERFLOW ) + }else if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW | +ISCSI_FLAG_CMD_BIDI_OVERFLOW )) sc->result = (DID_BAD_TARGET <<16 ) | rhdr->cmd_status; -else if (rhdr->flags &ISCSI_FLAG_CMD_OVERFLOW ) -scsi_set_resid (sc,be32_to_cpu (rhdr->residual_count)); out: debug_scsi("done [sc %lx res %d itt 0x%x]\n",
Signature · 1 line
1.5.1.2
PATCH 4/23 iscsi_tcp, libiscsi: initial AHS Support
From: Mike Christie <[email protected]>
at libiscsi generic code
- currently code assumes a storage space of pdu header is allocated at llds ctask and is pointed to by iscsi_cmd_task->hdr. Here I add a hdr_max field pertaining to that storage, and an hdr_len that accumulates the current use of the pdu-header.
- Add an iscsi_next_hdr() inline which returns the next free space to write new Header at. Also iscsi_next_hdr() is used to retrieve the address at which to write the header-digest.
- Add iscsi_add_hdr(length). What the user do is calls iscsi_next_hdr() for address of the new header, than calls iscsi_add_hdr(length) with the size of the new header. iscsi_add_hdr() will check if space is available and update to the new size. length must be padded according to standard.
- Add 2 padding inline helpers thanks to Olaf. Current patch does not use them but Following patches will.
Also moved definition of ISCSI_PAD_LEN to iscsi_proto.h which had PAD_WORD_LEN that was never used anywhere.
- Let iscsi_prep_scsi_cmd_pdu() signal an Error return since now it is possible that it will fail.
- I was tired of yet again writing a "this is a digest" comment next to sizeof(__u32) so I defined a new ISCSI_DIGEST_SIZE. Now I don't need any comments. Changed all places that used sizeof(__u32) or "4" in connection to a digest.
iscsi_tcp specific code
- At struct iscsi_tcp_cmd_task allocate maximum space allowed in standard for all headers following the iscsi_cmd header. and mark it so in iscsi_tcp_session_create()
- At iscsi_send_cmd_hdr() retrieve the correct headers size and write header digest at iscsi_next_hdr().
--- drivers/scsi/iscsi_tcp.c | 16 ++++++++-------- drivers/scsi/iscsi_tcp.h | 13 +++++++------ drivers/scsi/libiscsi.c | 41 +++++++++++++++++++++++++++++++++++++++-- include/scsi/iscsi_proto.h | 10 +++++++++- include/scsi/libiscsi.h | 33 +++++++++++++++++++++++++++++++-- 5 files changed, 94 insertions(+), 19 deletions(-)
drivers/scsi/iscsi_tcp.c +8 -8
@@ -113,7 +113,7 @@ iscsi_hdr_digest(struct iscsi_conn *conn, struct iscsi_buf *buf,struct iscsi_tcp_conn *tcp_conn = conn->dd_data;crypto_hash_digest (&tcp_conn->tx_hash, &buf->sg, buf->sg.length, crc); - buf->sg.length +=sizeof (u32); + buf->sg.length +=ISCSI_DIGEST_SIZE ; }/*
@@ -220,6 +220,7 @@ static inline int iscsi_tcp_chunk_done(struct iscsi_chunk *chunk) {static unsigned char padbuf[ISCSI_PAD_LEN ]; +unsigned int pad;if (chunk->copied < chunk->size) {iscsi_tcp_chunk_map (chunk);
@@ -243,10 +244,8 @@ iscsi_tcp_chunk_done(struct iscsi_chunk *chunk) }/* Do we need to handle padding? */ -if (chunk->total_copied & (ISCSI_PAD_LEN -1 )) { -unsigned int pad; - - pad =ISCSI_PAD_LEN - (chunk->total_copied & (ISCSI_PAD_LEN -1 )); + pad =iscsi_padding (chunk->total_copied); +if (pad !=0 ) {debug_tcp ("consume %d pad bytes\n" , pad); chunk->total_size += pad; chunk->size = pad;
@@ -1385,11 +1384,11 @@ iscsi_send_cmd_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) }iscsi_buf_init_iov (&tcp_ctask->headbuf, (char *)ctask->hdr, -sizeof (struct iscsi_hdr )); + ctask->hdr_len);if (conn->hdrdgst_en)iscsi_hdr_digest (conn, &tcp_ctask->headbuf, - (u8 *)tcp_ctask->hdrext); +iscsi_next_hdr (ctask)); tcp_ctask->xmstate &= ~XMSTATE_CMD_HDR_INIT ; tcp_ctask->xmstate |=XMSTATE_CMD_HDR_XMIT ; }
@@ -2176,7 +2175,8 @@ iscsi_tcp_session_create(struct iscsi_transport *iscsit,struct iscsi_cmd_task *ctask = session->cmds[cmd_i];struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; - ctask->hdr = &tcp_ctask->hdr; + ctask->hdr = &tcp_ctask->hdr.cmd_hdr; + ctask->hdr_max =sizeof (tcp_ctask->hdr) -ISCSI_DIGEST_SIZE ; }for (cmd_i =0 ; cmd_i < session->mgmtpool_max; cmd_i++) {
drivers/scsi/iscsi_tcp.h +7 -6
@@ -41,7 +41,6 @@#define XMSTATE_IMM_HDR_INIT 0x1000#define XMSTATE_SOL_HDR_INIT 0x2000 -#define ISCSI_PAD_LEN 4#define ISCSI_SG_TABLESIZE SG_ALL#define ISCSI_TCP_MAX_CMD_LEN 16
@@ -130,14 +129,14 @@ struct iscsi_buf {struct iscsi_data_task {struct iscsi_data hdr;/* PDU */ -char hdrext[sizeof (__u32)];/* Header-Digest */ +char hdrext[ISCSI_DIGEST_SIZE ];/* Header-Digest */ struct iscsi_buf digestbuf;/* digest buffer */ uint32_t digest;/* data digest */ };struct iscsi_tcp_mgmt_task {struct iscsi_hdr hdr; -char hdrext[sizeof (__u32)];/* Header-Digest */ +char hdrext[ISCSI_DIGEST_SIZE ];/* Header-Digest */ int xmstate;/* mgmt xmit progress */ struct iscsi_buf headbuf;/* header buffer */ struct iscsi_buf sendbuf;/* in progress buffer */
@@ -159,9 +158,11 @@ struct iscsi_r2t_info { };struct iscsi_tcp_cmd_task { -struct iscsi_cmd hdr; -char hdrext[4 *sizeof (__u16)+/* AHS */ -sizeof (__u32)];/* HeaderDigest */ +struct iscsi_hdr_buff { +struct iscsi_cmd cmd_hdr; +char hdrextbuf[ISCSI_MAX_AHS_SIZE + +ISCSI_DIGEST_SIZE ]; + } hdr;char pad[ISCSI_PAD_LEN ];int pad_count;/* padded bytes */ struct iscsi_buf headbuf;/* header buf (xmit) */
drivers/scsi/libiscsi.c +39 -2
89 lines not on this page · raw message
include/scsi/iscsi_proto.h +9 -1
@@ -27,7 +27,7 @@#define ISCSI_LISTEN_PORT 3260/* Padding word length */ -#define PAD_WORD_LEN 4 +#define ISCSI_PAD_LEN 4/* * useful common(control and data pathes) macro
@@ -147,6 +147,14 @@ struct iscsi_rlength_ahdr {__be32 read_length; }; +/* Extended CDB AHS */ +struct iscsi_ecdb_ahdr { +__be16 ahslength;/* CDB length - 15, including reserved byte */ +uint8_t ahstype; +uint8_t reserved; +uint8_t ecdb[260 -16 ];/* 4-byte aligned extended CDB spillover */ +}; +/* SCSI Response Header */ struct iscsi_cmd_rsp {uint8_t opcode;
include/scsi/libiscsi.h +31 -2
@@ -78,6 +78,9 @@ enum {#define ISCSI_ADDRESS_BUF_LEN 64enum { +/* this is the maximum possible storage for AHSs */ +ISCSI_MAX_AHS_SIZE =sizeof (struct iscsi_ecdb_ahdr ) + +sizeof (struct iscsi_rlength_ahdr ),ISCSI_DIGEST_SIZE =sizeof (__u32), };
@@ -102,10 +105,13 @@ enum {struct iscsi_cmd_task {/* -* Becuae LLDs allocate their hdr differently, this is a pointer to -* that storage. It must be setup at session creation time. +* Because LLDs allocate their hdr differently, this is a pointer +* and length to that storage. It must be setup at session +* creation time. */ struct iscsi_cmd *hdr; +unsigned short hdr_max; +unsigned short hdr_len;/* accumulated size of hdr used */ int itt;/* this ITT */ uint32_t unsol_datasn;
@@ -124,6 +130,11 @@ struct iscsi_cmd_task {void *dd_data;/* driver/transport data */ }; +static inline void *iscsi_next_hdr (struct iscsi_cmd_task *ctask) +{ +return (void *)ctask->hdr + ctask->hdr_len; +} +struct iscsi_conn {struct iscsi_cls_conn *cls_conn;/* ptr to class connection */ void *dd_data;/* iscsi_transport data */
@@ -342,4 +353,22 @@ extern void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask);extern void iscsi_pool_free (struct iscsi_queue *,void **);extern int iscsi_pool_init (struct iscsi_queue *,int ,void ***,int ); +/* +* inline functions to deal with padding. +*/ +static inline unsigned int +iscsi_padded (unsigned int len) +{ +return (len +ISCSI_PAD_LEN -1 ) & ~(ISCSI_PAD_LEN -1 ); +} + +static inline unsigned int +iscsi_padding (unsigned int len) +{ + len &= (ISCSI_PAD_LEN -1 ); +if (len) + len =ISCSI_PAD_LEN - len; +return len; +} +#endif
Signature · 1 line
1.5.1.2
PATCH 5/23 iser patching for AHS support
From: Mike Christie <[email protected]>
from Boaz Harrosh <[email protected]>
- The default initialization of hdr_max is the minimum - sizeof(struct iscsi_cmd) - Once this patch goes into iser the default initialization at libiscsi can be removed.
- This is not yet full support for AHSs at iser end. But it should be easy. Just allocate more space at iser_desc right after iscsi_hdr. Than at transmission time use ctask->hdr_len to retrieve the total size of all iscsi pdu headers. See previous patch at iscsi_tcp.[ch]
--- drivers/infiniband/ulp/iser/iscsi_iser.c | 1 + drivers/scsi/libiscsi.c | 1 - 2 files changed, 1 insertions(+), 1 deletions(-)
drivers/infiniband/ulp/iser/iscsi_iser.c +1 -0
@@ -400,6 +400,7 @@ iscsi_iser_session_create(struct iscsi_transport *iscsit, ctask = session->cmds[i]; iser_ctask = ctask->dd_data; ctask->hdr = (struct iscsi_cmd *)&iser_ctask->desc.iscsi_header; + ctask->hdr_max =sizeof (iser_ctask->desc.iscsi_header); }for (i =0 ; i < session->mgmtpool_max; i++) {
drivers/scsi/libiscsi.c +0 -1
@@ -1570,7 +1570,6 @@ iscsi_session_setup(struct iscsi_transport *iscsit,if (cmd_task_size) ctask->dd_data = &ctask[1 ]; ctask->itt = cmd_i; - ctask->hdr_max =sizeof (struct iscsi_cmd );INIT_LIST_HEAD (&ctask->running); }
Signature · 1 line
1.5.1.2
PATCH 6/23 libiscsi, iscsi_tcp: iscsi pool cleanup
From: Mike Christie <[email protected]>
from [email protected]
iscsi_pool_init simplified
iscsi_pool_init currently has a lot of duplicate kfree() calls it does when some allocation fails. This patch simplifies the code a little by using iscsi_pool_free to tear down the pool in case of an error.
iscsi_pool_init also returns a copy of the item array to the caller. Not all callers use this array, so we make it optional.
Instead of allocating a second array and return that, allocate just one array, of twice the size.
Update users of iscsi_pool_{init,free}
This patch drops the (now useless) second argument to iscsi_pool_free, and updates all callers.
It also removes the ctask->r2ts array, which was never used anyway. Since the items argument to iscsi_pool_init is now optional, we can pass NULL instead.
--- drivers/scsi/iscsi_tcp.c | 12 ++----- drivers/scsi/iscsi_tcp.h | 3 +- drivers/scsi/libiscsi.c | 75 ++++++++++++++++++++++++--------------------- include/scsi/libiscsi.h | 10 +++--- 4 files changed, 50 insertions(+), 50 deletions(-)
drivers/scsi/iscsi_tcp.c +4 -8
@@ -1998,8 +1998,7 @@ iscsi_r2tpool_alloc(struct iscsi_session *session)*/ /* R2T pool */ -if (iscsi_pool_init (&tcp_ctask->r2tpool, session->max_r2t *4 , - (void ***)&tcp_ctask->r2ts, +if (iscsi_pool_init (&tcp_ctask->r2tpool, session->max_r2t *4 ,NULL ,sizeof (struct iscsi_r2t_info ))) { goto r2t_alloc_fail; }
@@ -2008,8 +2007,7 @@ iscsi_r2tpool_alloc(struct iscsi_session *session) tcp_ctask->r2tqueue =kfifo_alloc ( session->max_r2t *4 *sizeof (void *),GFP_KERNEL ,NULL );if (tcp_ctask->r2tqueue ==ERR_PTR (-ENOMEM )) { -iscsi_pool_free (&tcp_ctask->r2tpool, - (void **)tcp_ctask->r2ts); +iscsi_pool_free (&tcp_ctask->r2tpool); goto r2t_alloc_fail; } }
@@ -2022,8 +2020,7 @@ r2t_alloc_fail:struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;kfifo_free (tcp_ctask->r2tqueue); -iscsi_pool_free (&tcp_ctask->r2tpool, - (void **)tcp_ctask->r2ts); +iscsi_pool_free (&tcp_ctask->r2tpool); }return -ENOMEM ; }
@@ -2038,8 +2035,7 @@ iscsi_r2tpool_free(struct iscsi_session *session)struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;kfifo_free (tcp_ctask->r2tqueue); -iscsi_pool_free (&tcp_ctask->r2tpool, - (void **)tcp_ctask->r2ts); +iscsi_pool_free (&tcp_ctask->r2tpool); } }
drivers/scsi/iscsi_tcp.h +1 -2
@@ -175,9 +175,8 @@ struct iscsi_tcp_cmd_task {uint32_t exp_datasn;/* expected target's R2TSN/DataSN */ int data_offset;struct iscsi_r2t_info *r2t;/* in progress R2T */ -struct iscsi_queue r2tpool; +struct iscsi_pool r2tpool;struct kfifo *r2tqueue; -struct iscsi_r2t_info **r2ts;int digest_count;uint32_t immdigest;/* for imm data */ struct iscsi_buf immbuf;/* for imm data digest */
drivers/scsi/libiscsi.c +40 -35
116 lines not on this page · raw message
include/scsi/libiscsi.h +5 -5
@@ -215,7 +215,7 @@ struct iscsi_conn {uint32_t eh_abort_cnt; }; -struct iscsi_queue { +struct iscsi_pool {struct kfifo *queue;/* FIFO Queue */ void **pool;/* Pool of elements */ int max;/* Max number of elements */
@@ -274,10 +274,10 @@ struct iscsi_session {int cmds_max;/* size of cmds array */ struct iscsi_cmd_task **cmds;/* Original Cmds arr */ -struct iscsi_queue cmdpool;/* PDU's pool */ +struct iscsi_pool cmdpool;/* PDU's pool */ int mgmtpool_max;/* size of mgmt array */ struct iscsi_mgmt_task **mgmt_cmds;/* Original mgmt arr */ -struct iscsi_queue mgmtpool;/* Mgmt PDU's pool */ +struct iscsi_pool mgmtpool;/* Mgmt PDU's pool */ };/*
@@ -350,8 +350,8 @@ extern void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask);/* * generic helpers */ -extern void iscsi_pool_free (struct iscsi_queue *,void **); -extern int iscsi_pool_init (struct iscsi_queue *,int ,void ***,int ); +extern void iscsi_pool_free (struct iscsi_pool *); +extern int iscsi_pool_init (struct iscsi_pool *,int ,void ***,int );/* * inline functions to deal with padding.
Signature · 1 line
1.5.1.2
PATCH 7/23 libiscsi: do not block session during logout
From: Mike Christie <[email protected]>
There is not need to block the session during logout. Since we are going to fail the commands that were blocked just fail them immediately instead.
--- drivers/infiniband/ulp/iser/iser_initiator.c | 11 +-- drivers/scsi/iscsi_tcp.c | 4 +- drivers/scsi/libiscsi.c | 153 ++++++++++++++------------ include/scsi/libiscsi.h | 2 + include/scsi/scsi_transport_iscsi.h | 1 + 5 files changed, 89 insertions(+), 82 deletions(-)
drivers/infiniband/ulp/iser/iser_initiator.c +2 -9
@@ -617,15 +617,8 @@ void iser_snd_completion(struct iser_desc *tx_desc)/* this arithmetic is legal by libiscsi dd_data allocation */ mtask = (void *) ((long )(void *)tx_desc -sizeof (struct iscsi_mgmt_task )); -if (mtask->hdr->itt ==RESERVED_ITT ) { -struct iscsi_session *session = conn->session; - -spin_lock (&conn->session->lock); -list_del (&mtask->running); -__kfifo_put (session->mgmtpool.queue, (void *)&mtask, -sizeof (void *)); -spin_unlock (&session->lock); - } +if (mtask->hdr->itt ==RESERVED_ITT ) +iscsi_free_mgmt_task (conn, mtask); } }
drivers/scsi/iscsi_tcp.c +1 -3
@@ -1349,9 +1349,7 @@ iscsi_tcp_mtask_xmit(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)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 *)); +iscsi_free_mgmt_task (conn, mtask);spin_unlock_bh (&session->lock); }return 0 ;
drivers/scsi/libiscsi.c +83 -70
251 lines not on this page · raw message
include/scsi/libiscsi.h +2 -0
@@ -346,6 +346,8 @@ extern int __iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *,extern int iscsi_verify_itt (struct iscsi_conn *,struct iscsi_hdr *,uint32_t *);extern void iscsi_requeue_ctask (struct iscsi_cmd_task *ctask); +extern void iscsi_free_mgmt_task (struct iscsi_conn *conn, +struct iscsi_mgmt_task *mtask);/* * generic helpers
include/scsi/scsi_transport_iscsi.h +1 -0
@@ -176,6 +176,7 @@ struct iscsi_cls_conn {#define ISCSI_STATE_TERMINATE 4#define ISCSI_STATE_IN_RECOVERY 5#define ISCSI_STATE_RECOVERY_FAILED 6 +#define ISCSI_STATE_LOGGING_OUT 7struct iscsi_cls_session {struct list_head sess_list;/* item in session_list */
Signature · 1 line
1.5.1.2
PATCH 8/23 iscsi class: Use our own workq instead of common system one.
From: Mike Christie <[email protected]>
There is just too much going on through the common workq and something like a scsi device removal through sysfs affects how long it will take to recover the transport, mark it as failed, or shut it down gracefully.
--- drivers/scsi/scsi_transport_iscsi.c | 16 ++++++++++++---- 1 files changed, 12 insertions(+), 4 deletions(-)
drivers/scsi/scsi_transport_iscsi.c +12 -4
@@ -50,6 +50,7 @@ struct iscsi_internal { };static atomic_t iscsi_session_nr;/* sysfs session id for next new session */ +static struct workqueue_struct *iscsi_eh_timer_workq;/* * list of registered transports and lock that must
@@ -252,7 +253,7 @@ static void session_recovery_timedout(struct work_struct *work)void iscsi_unblock_session (struct iscsi_cls_session *session) {if (!cancel_delayed_work (&session->recovery_work)) -flush_scheduled_work (); +flush_workqueue (iscsi_eh_timer_workq);scsi_target_unblock (&session->dev); }EXPORT_SYMBOL_GPL (iscsi_unblock_session);
@@ -260,8 +261,8 @@ EXPORT_SYMBOL_GPL(iscsi_unblock_session);void iscsi_block_session (struct iscsi_cls_session *session) {scsi_target_block (&session->dev); -schedule_delayed_work (&session->recovery_work, - session->recovery_tmo *HZ ); +queue_delayed_work (iscsi_eh_timer_workq, &session->recovery_work, + session->recovery_tmo *HZ ); }EXPORT_SYMBOL_GPL (iscsi_block_session);
@@ -356,7 +357,7 @@ void iscsi_remove_session(struct iscsi_cls_session *session)struct iscsi_host *ihost = shost->shost_data;if (!cancel_delayed_work (&session->recovery_work)) -flush_scheduled_work (); +flush_workqueue (iscsi_eh_timer_workq);mutex_lock (&ihost->mutex);list_del (&session->host_list);
@@ -1520,8 +1521,14 @@ static __init int iscsi_transport_init(void) goto unregister_session_class; } + iscsi_eh_timer_workq =create_singlethread_workqueue ("iscsi_eh" ); +if (!iscsi_eh_timer_workq) + goto release_nls; +return 0 ; +release_nls: +sock_release (nls->sk_socket); unregister_session_class:transport_class_unregister (&iscsi_session_class); unregister_conn_class:
@@ -1535,6 +1542,7 @@ unregister_transport_class: static void __exit iscsi_transport_exit(void) { +destroy_workqueue (iscsi_eh_timer_workq);sock_release (nls->sk_socket);transport_class_unregister (&iscsi_connection_class);transport_class_unregister (&iscsi_session_class);
Signature · 1 line
1.5.1.2
PATCH 9/23 libiscsi: grab eh_mutex during host reset
From: Mike Christie <[email protected]>
I thought we may not need the eh mutex during host reset, but that is wrong with the new shutdown code. When start_session_recovery sets the state to terminate then drops the session lock. The scsi eh thread could then grab the session lock see that we are terminating and then return failed to scsi-ml. scsi-ml's eh then owns the command and will do whatever it wants with it. But then the iscsi eh thread could grab the session lock and want to complete the scsi commands that we in the LLD, but
it no longer owns them and kaboom.
--- drivers/scsi/libiscsi.c | 7 +++++-- 1 files changed, 5 insertions(+), 2 deletions(-)
drivers/scsi/libiscsi.c +5 -2
@@ -1079,17 +1079,19 @@ int iscsi_eh_host_reset(struct scsi_cmnd *sc)struct iscsi_session *session =iscsi_hostdata (host->hostdata);struct iscsi_conn *conn = session->leadconn; +mutex_lock (&session->eh_mutex);spin_lock_bh (&session->lock);if (session->state ==ISCSI_STATE_TERMINATE ) { failed:debug_scsi ("failing host reset: session terminated " "[CID %d age %d]\n" , conn->id, session->age);spin_unlock_bh (&session->lock); +mutex_unlock (&session->eh_mutex);return FAILED ; }spin_unlock_bh (&session->lock); - +mutex_unlock (&session->eh_mutex);/* * we drop the lock here but the leadconn cannot be destoyed while * we are in the scsi eh
@@ -1104,13 +1106,14 @@ failed:if (signal_pending (current))flush_signals (current); +mutex_lock (&session->eh_mutex);spin_lock_bh (&session->lock);if (session->state ==ISCSI_STATE_LOGGED_IN )printk (KERN_INFO "iscsi: host reset succeeded\n" );else goto failed;spin_unlock_bh (&session->lock); - +mutex_unlock (&session->eh_mutex);return SUCCESS ; }EXPORT_SYMBOL_GPL (iscsi_eh_host_reset);
Signature · 1 line
1.5.1.2
PATCH 10/23 libiscsi: fix shutdown
From: Mike Christie <[email protected]>
We were using the device delete sysfs file to remove each device then logout. Now in 2.6.21 and .22 this will not work because the sysfs delete file returns immediately and does not wait for the device removal to complete. This causes a hang if a cache sync is needed during shutdown. Before .21, that approach had other problems, so this patch fixes the shutdown code so that we remove the target and unbind the session before logging out and shut down the session.
--- drivers/scsi/libiscsi.c | 4 +- drivers/scsi/qla4xxx/ql4_init.c | 4 +- drivers/scsi/qla4xxx/ql4_os.c | 7 +- drivers/scsi/scsi_transport_iscsi.c | 287 +++++++++++++++++++---------------- include/scsi/iscsi_if.h | 7 + include/scsi/iscsi_proto.h | 2 + include/scsi/scsi_transport_iscsi.h | 7 +- 7 files changed, 175 insertions(+), 143 deletions(-)
drivers/scsi/libiscsi.c +2 -2
@@ -1662,7 +1662,7 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session)struct iscsi_session *session =iscsi_hostdata (shost->hostdata);struct module *owner = cls_session->transport->owner; -iscsi_unblock_session (cls_session); +iscsi_remove_session (cls_session);scsi_remove_host (shost);iscsi_pool_free (&session->mgmtpool);
@@ -1677,7 +1677,7 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session)kfree (session->hwaddress);kfree (session->initiatorname); -iscsi_destroy_session (cls_session); +iscsi_free_session (cls_session);scsi_host_put (shost);module_put (owner); }
drivers/scsi/qla4xxx/ql4_init.c +3 -1
@@ -5,6 +5,7 @@* See LICENSE.qla4xxx for copyright and licensing details. */ +#include <scsi/iscsi_if.h> #include "ql4_def.h" #include "ql4_glbl.h" #include "ql4_dbg.h"
@@ -1305,7 +1306,8 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha,atomic_set (&ddb_entry->relogin_timer,0 );clear_bit (DF_RELOGIN , &ddb_entry->flags);clear_bit (DF_NO_RELOGIN , &ddb_entry->flags); -iscsi_if_create_session_done (ddb_entry->conn); +iscsi_session_event (ddb_entry->sess, +ISCSI_KEVENT_CREATE_SESSION );/* * Change the lun state to READY in case the lun TIMEOUT before * the device came back.
drivers/scsi/qla4xxx/ql4_os.c +2 -5
@@ -298,8 +298,7 @@ void qla4xxx_destroy_sess(struct ddb_entry *ddb_entry)return ;if (ddb_entry->conn) { -iscsi_if_destroy_session_done (ddb_entry->conn); -iscsi_destroy_conn (ddb_entry->conn); +atomic_set (&ddb_entry->state,DDB_STATE_DEAD );iscsi_remove_session (ddb_entry->sess); }iscsi_free_session (ddb_entry->sess);
@@ -309,6 +308,7 @@ int qla4xxx_add_sess(struct ddb_entry *ddb_entry) {int err; + ddb_entry->sess->recovery_tmo = ddb_entry->ha->port_down_retry_count; err =iscsi_add_session (ddb_entry->sess, ddb_entry->fw_ddb_index);if (err) {DEBUG2 (printk (KERN_ERR "Could not add session.\n" ));
@@ -321,9 +321,6 @@ int qla4xxx_add_sess(struct ddb_entry *ddb_entry)DEBUG2 (printk (KERN_ERR "Could not add connection.\n" ));return -ENOMEM ; } - - ddb_entry->sess->recovery_tmo = ddb_entry->ha->port_down_retry_count; -iscsi_if_create_session_done (ddb_entry->conn);return 0 ; }
drivers/scsi/scsi_transport_iscsi.c +154 -133
468 lines not on this page · raw message
include/scsi/iscsi_if.h +7 -0
@@ -49,12 +49,15 @@ enum iscsi_uevent_e {ISCSI_UEVENT_TGT_DSCVR =UEVENT_BASE +15 ,ISCSI_UEVENT_SET_HOST_PARAM =UEVENT_BASE +16 , +ISCSI_UEVENT_UNBIND_SESSION =UEVENT_BASE +17 ,/* up events */ ISCSI_KEVENT_RECV_PDU =KEVENT_BASE +1 ,ISCSI_KEVENT_CONN_ERROR =KEVENT_BASE +2 ,ISCSI_KEVENT_IF_ERROR =KEVENT_BASE +3 ,ISCSI_KEVENT_DESTROY_SESSION =KEVENT_BASE +4 , +ISCSI_KEVENT_UNBIND_SESSION =KEVENT_BASE +5 , + ISCSI_KEVENT_CREATE_SESSION = KEVENT_BASE + 6, }; enum iscsi_tgt_dscvr {
@@ -156,6 +159,10 @@ struct iscsi_uevent {uint32_t sid;uint32_t cid; } c_conn_ret; +struct msg_unbind_session { +uint32_t sid; +uint32_t host_no; + } unbind_session;struct msg_recv_req {uint32_t sid;uint32_t cid;
include/scsi/iscsi_proto.h +2 -0
@@ -21,6 +21,8 @@#ifndef ISCSI_PROTO_H #define ISCSI_PROTO_H +#include <linux/types.h> +#define ISCSI_DRAFT20_VERSION 0x00/* default iSCSI listen port for incoming connections */
include/scsi/scsi_transport_iscsi.h +5 -2
@@ -186,6 +186,7 @@ struct iscsi_cls_session {/* recovery fields */ int recovery_tmo;struct delayed_work recovery_work; +struct work_struct unbind_work;int target_id;
@@ -206,6 +207,8 @@ struct iscsi_cls_session {struct iscsi_host {struct list_head sessions;struct mutex mutex; +struct workqueue_struct *unbind_workq; +char unbind_workq_name[KOBJ_NAME_LEN ]; };/*
@@ -215,8 +218,8 @@ extern struct iscsi_cls_session *iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport);extern int iscsi_add_session (struct iscsi_cls_session *session,unsigned int target_id); -extern int iscsi_if_create_session_done (struct iscsi_cls_conn *conn); -extern int iscsi_if_destroy_session_done (struct iscsi_cls_conn *conn); +extern int iscsi_session_event (struct iscsi_cls_session *session, +enum iscsi_uevent_e event);extern struct iscsi_cls_session *iscsi_create_session (struct Scsi_Host *shost,struct iscsi_transport *t,unsigned int target_id);
Signature · 1 line
1.5.1.2
PATCH 11/23 libiscsi: fix nop handling
From: Mike Christie <[email protected]>
During root boot and shutdown the target could send us nops. At this time iscsid cannot be running, so the target will drop the session and the boot or shutdown will hang.
To handle this and allow us to better control when to check the network this patch moves the nop handling to the kernel.
--- drivers/infiniband/ulp/iser/iscsi_iser.c | 4 +- drivers/scsi/iscsi_tcp.c | 4 +- drivers/scsi/libiscsi.c | 331 ++++++++++++++++++++++++------ drivers/scsi/scsi_transport_iscsi.c | 4 + include/scsi/iscsi_if.h | 11 + include/scsi/libiscsi.h | 8 + 6 files changed, 294 insertions(+), 68 deletions(-)
drivers/infiniband/ulp/iser/iscsi_iser.c +3 -1
@@ -577,7 +577,9 @@ static struct iscsi_transport iscsi_iser_transport = {ISCSI_PERSISTENT_ADDRESS |ISCSI_TARGET_NAME |ISCSI_TPGT |ISCSI_USERNAME |ISCSI_PASSWORD | - ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN, +ISCSI_USERNAME_IN |ISCSI_PASSWORD_IN | +ISCSI_FAST_ABORT |ISCSI_ABORT_TMO | + ISCSI_PING_TMO | ISCSI_RECV_TMO, .host_param_mask =ISCSI_HOST_HWADDRESS |ISCSI_HOST_NETDEV_NAME | ISCSI_HOST_INITIATOR_NAME,
drivers/scsi/iscsi_tcp.c +3 -1
@@ -2246,7 +2246,9 @@ static struct iscsi_transport iscsi_tcp_transport = {ISCSI_TARGET_NAME |ISCSI_TPGT |ISCSI_USERNAME |ISCSI_PASSWORD |ISCSI_USERNAME_IN |ISCSI_PASSWORD_IN | - ISCSI_FAST_ABORT, +ISCSI_FAST_ABORT |ISCSI_ABORT_TMO | +ISCSI_LU_RESET_TMO | + ISCSI_PING_TMO | ISCSI_RECV_TMO, .host_param_mask =ISCSI_HOST_HWADDRESS |ISCSI_HOST_IPADDRESS |ISCSI_HOST_INITIATOR_NAME | ISCSI_HOST_NETDEV_NAME,
drivers/scsi/libiscsi.c +265 -66
472 lines not on this page · raw message
drivers/scsi/scsi_transport_iscsi.c +4 -0
@@ -1201,6 +1201,8 @@ iscsi_conn_attr(port, ISCSI_PARAM_CONN_PORT);iscsi_conn_attr (exp_statsn,ISCSI_PARAM_EXP_STATSN );iscsi_conn_attr (persistent_address,ISCSI_PARAM_PERSISTENT_ADDRESS );iscsi_conn_attr (address,ISCSI_PARAM_CONN_ADDRESS ); +iscsi_conn_attr (ping_tmo,ISCSI_PARAM_PING_TMO ); +iscsi_conn_attr (recv_tmo,ISCSI_PARAM_RECV_TMO );#define iscsi_cdev_to_session (_cdev) \ iscsi_dev_to_session(_cdev->dev)
@@ -1436,6 +1438,8 @@ iscsi_register_transport(struct iscsi_transport *tt)SETUP_CONN_RD_ATTR (exp_statsn,ISCSI_EXP_STATSN );SETUP_CONN_RD_ATTR (persistent_address,ISCSI_PERSISTENT_ADDRESS );SETUP_CONN_RD_ATTR (persistent_port,ISCSI_PERSISTENT_PORT ); +SETUP_CONN_RD_ATTR (ping_tmo,ISCSI_PING_TMO ); +SETUP_CONN_RD_ATTR (recv_tmo,ISCSI_RECV_TMO );BUG_ON (count >ISCSI_CONN_ATTRS ); priv->conn_attrs[count] =NULL ;
include/scsi/iscsi_if.h +11 -0
@@ -244,6 +244,12 @@ enum iscsi_param {ISCSI_PARAM_PASSWORD_IN ,ISCSI_PARAM_FAST_ABORT , +ISCSI_PARAM_ABORT_TMO , +ISCSI_PARAM_LU_RESET_TMO , +ISCSI_PARAM_HOST_RESET_TMO , + +ISCSI_PARAM_PING_TMO , +ISCSI_PARAM_RECV_TMO ,/* must always be last */ ISCSI_PARAM_MAX, };
@@ -275,6 +281,11 @@ enum iscsi_param {#define ISCSI_PASSWORD (1 << ISCSI_PARAM_PASSWORD)#define ISCSI_PASSWORD_IN (1 << ISCSI_PARAM_PASSWORD_IN)#define ISCSI_FAST_ABORT (1 << ISCSI_PARAM_FAST_ABORT) +#define ISCSI_ABORT_TMO (1 << ISCSI_PARAM_ABORT_TMO) +#define ISCSI_LU_RESET_TMO (1 << ISCSI_PARAM_LU_RESET_TMO) +#define ISCSI_HOST_RESET_TMO (1 << ISCSI_PARAM_HOST_RESET_TMO) +#define ISCSI_PING_TMO (1 << ISCSI_PARAM_PING_TMO) +#define ISCSI_RECV_TMO (1 << ISCSI_PARAM_RECV_TMO)/* iSCSI HBA params */ enum iscsi_host_param {
include/scsi/libiscsi.h +8 -0
@@ -148,6 +148,12 @@ struct iscsi_conn {* conn_stop() flag: stop to recover, stop to terminate */ int stop_stage; +struct timer_list transport_timer; +unsigned long last_recv; +unsigned long last_ping; +int ping_timeout; +int recv_timeout; +struct iscsi_mgmt_task *ping_mtask;/* iSCSI connection-wide sequencing */ uint32_t exp_statsn;
@@ -238,6 +244,8 @@ struct iscsi_session {uint32_t queued_cmdsn;/* configuration */ +int abort_timeout; +int lu_reset_timeout;int initial_r2t_en;unsigned max_r2t;int imm_data_en;
Signature · 1 line
1.5.1.2
PATCH 12/23 iscsi_tcp: update the website URL
From: FUJITA Tomonori <[email protected]>
Use open-iscsi.org instead of linux-iscsi.sf.net, which hasn't been updated for ages.
--- drivers/scsi/Kconfig | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/scsi/Kconfig +1 -1
@@ -341,7 +341,7 @@ config ISCSI_TCP The userspace component needed to initialize the driver, documentation, and sample configuration files can be found here: - http://linux-iscsi.sf.net + http://open-iscsi.org config SGIWD93_SCSI tristate "SGI WD93C93 SCSI Driver"
Signature · 1 line
1.5.1.2
PATCH 13/23 Do not fail commands immediately during logout
From: Mike Christie <[email protected]>
If the target requests a logout, then we do not want
to fail commands to scsi-ml right away. This patch just fails in pending commands for a requeue immediately, and then lets iscsid handle running commands like normal recovery.
--- drivers/scsi/libiscsi.c | 14 ++++++-------- 1 files changed, 6 insertions(+), 8 deletions(-)
drivers/scsi/libiscsi.c +6 -8
@@ -917,7 +917,7 @@ check_mgmt: conn->ctask =list_entry (conn->xmitqueue.next, struct iscsi_cmd_task, running);if (conn->session->state ==ISCSI_STATE_LOGGING_OUT ) { -fail_command (conn, conn->ctask,DID_NO_CONNECT <<16 ); +fail_command (conn, conn->ctask,DID_IMM_RETRY <<16 );continue ; }if (iscsi_prep_scsi_cmd_pdu (conn->ctask)) {
@@ -1024,21 +1024,19 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))* be entering our queuecommand while a block is starting * up because the block code is not locked) */ -if (session->state ==ISCSI_STATE_IN_RECOVERY ) { +switch (session->state) { +case ISCSI_STATE_IN_RECOVERY : reason =FAILURE_SESSION_IN_RECOVERY ; goto reject; - } - -switch (session->state) { +case ISCSI_STATE_LOGGING_OUT : + reason =FAILURE_SESSION_LOGGING_OUT ; + goto reject;case ISCSI_STATE_RECOVERY_FAILED : reason =FAILURE_SESSION_RECOVERY_TIMEOUT ;break ;case ISCSI_STATE_TERMINATE : reason =FAILURE_SESSION_TERMINATE ;break ; -case ISCSI_STATE_LOGGING_OUT : - reason =FAILURE_SESSION_LOGGING_OUT ; -break ;default : reason =FAILURE_SESSION_FREED ; }
Signature · 1 line
1.5.1.2
PATCH 14/23 clear conn->ctask when task is completed early
From: Mike Christie <[email protected]>
If the current ctask is failed early, we legt the conn->ctask pointer pointing to a invalid task. When the xmit thread would send data for it, we would then oops.
--- drivers/scsi/libiscsi.c | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-)
drivers/scsi/libiscsi.c +4 -1
@@ -248,13 +248,16 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)*/ static void iscsi_complete_command (struct iscsi_cmd_task *ctask) { -struct iscsi_session *session = ctask->conn->session; +struct iscsi_conn *conn = ctask->conn; +struct iscsi_session *session = conn->session;struct scsi_cmnd *sc = ctask->sc; ctask->state =ISCSI_TASK_COMPLETED ; ctask->sc =NULL ;/* SCSI eh reuses commands to verify us */ sc->SCp.ptr =NULL ; +if (conn->ctask == ctask) + conn->ctask =NULL ;list_del_init (&ctask->running);__kfifo_put (session->cmdpool.queue, (void *)&ctask,sizeof (void *)); sc->scsi_done (sc);
Signature · 1 line
1.5.1.2
PATCH 15/23 Drop host lock in queuecommand
From: Mike Christie <[email protected]>
The driver does not need the host lock in queuecommand so drop it.
--- drivers/scsi/libiscsi.c | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-)
drivers/scsi/libiscsi.c +5 -1
@@ -1010,8 +1010,9 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) sc->SCp.ptr =NULL ; host = sc->device->host; - session =iscsi_hostdata (host->hostdata); +spin_unlock (host->host_lock); + session =iscsi_hostdata (host->hostdata);spin_lock (&session->lock);/*
@@ -1077,11 +1078,13 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))spin_unlock (&session->lock);scsi_queue_work (host, &conn->xmitwork); +spin_lock (host->host_lock);return 0 ; reject:spin_unlock (&session->lock);debug_scsi ("cmd 0x%x rejected (%d)\n" , sc->cmnd[0 ], reason); +spin_lock (host->host_lock);return SCSI_MLQUEUE_HOST_BUSY ; fault:
@@ -1091,6 +1094,7 @@ fault: sc->result = (DID_NO_CONNECT <<16 );scsi_set_resid (sc,scsi_bufflen (sc)); sc->scsi_done (sc); +spin_lock (host->host_lock);return 0 ; }EXPORT_SYMBOL_GPL (iscsi_queuecommand);
Signature · 1 line
1.5.1.2
PATCH 16/23 convert xmit path to iscsi chunks
From: Mike Christie <[email protected]>
from [email protected]:
Convert xmit to iscsi chunks.
from [email protected]:
Bug fixes, more digest integration, sg chaining conversion and other sg wrapper changes, coding style sync up, and removal of io fields, like pdu_sent, that are not needed.
--- drivers/infiniband/ulp/iser/iscsi_iser.c | 3 +- drivers/scsi/iscsi_tcp.c | 1333 ++++++++++++------------------ drivers/scsi/iscsi_tcp.h | 75 +-- drivers/scsi/libiscsi.c | 37 +- include/scsi/scsi_transport_iscsi.h | 2 +- 5 files changed, 563 insertions(+), 887 deletions(-)
drivers/infiniband/ulp/iser/iscsi_iser.c +2 -1
@@ -129,7 +129,7 @@ error:* iscsi_iser_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands * **/ -static void +static int iscsi_iser_cmd_init (struct iscsi_cmd_task *ctask) {struct iscsi_iser_conn *iser_conn = ctask->conn->dd_data;
@@ -138,6 +138,7 @@ iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask) iser_ctask->command_sent =0 ; iser_ctask->iser_conn = iser_conn;iser_ctask_rdma_init (iser_ctask); +return 0 ; }/**
drivers/scsi/iscsi_tcp.c +526 -807
1,741 lines not on this page · raw message
drivers/scsi/iscsi_tcp.h +15 -60
143 lines not on this page · raw message
drivers/scsi/libiscsi.c +19 -18
@@ -156,20 +156,19 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) rc =iscsi_add_hdr (ctask,sizeof (*hdr));if (rc)return rc; - hdr->opcode =ISCSI_OP_SCSI_CMD ; - hdr->flags =ISCSI_ATTR_SIMPLE ; -int_to_scsilun (sc->device->lun, (struct scsi_lun *)hdr->lun); - hdr->itt =build_itt (ctask->itt, conn->id, session->age); - hdr->data_length =cpu_to_be32 (scsi_bufflen (sc)); - hdr->cmdsn =cpu_to_be32 (session->cmdsn); - session->cmdsn++; - hdr->exp_statsn =cpu_to_be32 (conn->exp_statsn); -memcpy (hdr->cdb, sc->cmnd, sc->cmd_len); + hdr->opcode =ISCSI_OP_SCSI_CMD ; + hdr->flags =ISCSI_ATTR_SIMPLE ; +int_to_scsilun (sc->device->lun, (struct scsi_lun *)hdr->lun); + hdr->itt =build_itt (ctask->itt, conn->id, session->age); + hdr->data_length =cpu_to_be32 (scsi_bufflen (sc)); + hdr->cmdsn =cpu_to_be32 (session->cmdsn); + session->cmdsn++; + hdr->exp_statsn =cpu_to_be32 (conn->exp_statsn); +memcpy (hdr->cdb, sc->cmnd, sc->cmd_len);if (sc->cmd_len <MAX_COMMAND_SIZE )memset (&hdr->cdb[sc->cmd_len],0 ,MAX_COMMAND_SIZE - sc->cmd_len); - ctask->data_count =0 ; ctask->imm_count =0 ;if (sc->sc_data_direction ==DMA_TO_DEVICE ) { hdr->flags |=ISCSI_FLAG_CMD_WRITE ;
@@ -198,9 +197,9 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)else ctask->imm_count =min (scsi_bufflen (sc), conn->max_xmit_dlength); -hton24 (ctask->hdr->dlength, ctask->imm_count); +hton24 (hdr->dlength, ctask->imm_count); }else -zero_data (ctask->hdr->dlength); +zero_data (hdr->dlength);if (!session->initial_r2t_en) { ctask->unsol_count = min((session->first_burst),
@@ -210,7 +209,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)if (!ctask->unsol_count)/* No unsolicit Data-Out's */ - ctask->hdr->flags |=ISCSI_FLAG_CMD_FINAL ; + hdr->flags |=ISCSI_FLAG_CMD_FINAL ; }else { hdr->flags |=ISCSI_FLAG_CMD_FINAL ;zero_data (hdr->dlength);
@@ -228,13 +227,15 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)WARN_ON (hdrlength >=256 ); hdr->hlength = hdrlength &0xFF ; - conn->scsicmd_pdus_cnt++; +if (conn->session->tt->init_cmd_task (conn->ctask)) +return EIO ; -debug_scsi ("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x len %d " + conn->scsicmd_pdus_cnt++; +debug_scsi ("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x len %d " "cmdsn %d win %d]\n" , - sc->sc_data_direction ==DMA_TO_DEVICE ?"write" :"read" , + sc->sc_data_direction ==DMA_TO_DEVICE ?"write" :"read" , conn->id, sc, sc->cmnd[0 ], ctask->itt,scsi_bufflen (sc), - session->cmdsn, session->max_cmdsn - session->exp_cmdsn +1 ); + session->cmdsn, session->max_cmdsn - session->exp_cmdsn +1 );return 0 ; }
@@ -927,7 +928,7 @@ check_mgmt:fail_command (conn, conn->ctask,DID_ABORT <<16 );continue ; } - conn->session->tt->init_cmd_task (conn->ctask); + conn->ctask->state =ISCSI_TASK_RUNNING ;list_move_tail (conn->xmitqueue.next, &conn->run_list); rc =iscsi_xmit_ctask (conn);
include/scsi/scsi_transport_iscsi.h +1 -1
@@ -118,7 +118,7 @@ struct iscsi_transport { char *data, uint32_t data_size);void (*get_stats) (struct iscsi_cls_conn *conn,struct iscsi_stats *stats); -void (*init_cmd_task) (struct iscsi_cmd_task *ctask); +int (*init_cmd_task) (struct iscsi_cmd_task *ctask);void (*init_mgmt_task) (struct iscsi_conn *conn,struct iscsi_mgmt_task *mtask); int (*xmit_cmd_task) (struct iscsi_conn *conn,
Signature · 1 line
1.5.1.2
PATCH 17/23 iscsi_tcp: stop leaking r2t_info's when the incoming R2T is bad
From: Mike Christie <[email protected]>
from [email protected]:
iscsi_r2t_rsp checks the incoming R2T for sanity, and if it thinks it's fishy, it will drop it silently. In this case, we leaked an r2t_info object. If we do this often enough, we run into a BUG_ON some time later.
Removed r2t wrappers and update patch by Mike Christie
--- drivers/scsi/iscsi_tcp.c | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-)
drivers/scsi/iscsi_tcp.c +5 -1
@@ -658,6 +658,8 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) r2t->data_length =be32_to_cpu (rhdr->data_length);if (r2t->data_length ==0 ) {printk (KERN_ERR "iscsi_tcp: invalid R2T with zero data len\n" ); +__kfifo_put (tcp_ctask->r2tpool.queue, (void *)&r2t, +sizeof (void *));spin_unlock (&session->lock);return ISCSI_ERR_DATALEN ; }
@@ -669,10 +671,12 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) r2t->data_offset =be32_to_cpu (rhdr->data_offset);if (r2t->data_offset + r2t->data_length >scsi_bufflen (ctask->sc)) { -spin_unlock (&session->lock);printk (KERN_ERR "iscsi_tcp: invalid R2T with data len %u at " "offset %u and total length %d\n" , r2t->data_length, r2t->data_offset,scsi_bufflen (ctask->sc)); +__kfifo_put (tcp_ctask->r2tpool.queue, (void *)&r2t, +sizeof (void *)); +spin_unlock (&session->lock);return ISCSI_ERR_DATALEN ; }
Signature · 1 line
1.5.1.2
PATCH 18/23 iscsi_tcp: drop session when itt does not match any command
From: Mike Christie <[email protected]>
A target should never send us a itt that does not match a running task. If it does we do not really know what is coming down after the header, unless we evaluate the hdr and do some guessing sometimes. However, even if we know what is coming we probably do not have buffers for it or we cannot respond (if it is a r2t for example), so just drop the session.
--- drivers/scsi/iscsi_tcp.c | 6 +----- 1 files changed, 1 insertions(+), 5 deletions(-)
drivers/scsi/iscsi_tcp.c +1 -5
@@ -755,11 +755,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) opcode = hdr->opcode &ISCSI_OPCODE_MASK ;/* verify itt (itt encoding: age+cid+itt) */ rc =iscsi_verify_itt (conn, hdr, &itt); -if (rc ==ISCSI_ERR_NO_SCSI_CMD ) { -/* XXX: what does this do? */ - tcp_conn->in.datalen =0 ;/* force drop */ -return 0 ; - }else if (rc) +if (rc)return rc; debug_tcp("opcode 0x%x ahslen %d datalen %d\n",
Signature · 1 line
1.5.1.2
PATCH 19/23 libiscsi, iscsi class: set tmf to a safe default and export in sysfs
From: Mike Christie <[email protected]>
Older tools will not be setting the tmf time outs since they did not exists, so set them to a safe default.
And export abort and lu reset timeout values in sysfs.
--- drivers/scsi/libiscsi.c | 2 ++ drivers/scsi/scsi_transport_iscsi.c | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-)
drivers/scsi/libiscsi.c +2 -0
@@ -1732,6 +1732,8 @@ iscsi_session_setup(struct iscsi_transport *iscsit, session->host = shost; session->state =ISCSI_STATE_FREE ; session->fast_abort =1 ; + session->lu_reset_timeout =15 ; + session->abort_timeout =10 ; session->mgmtpool_max =ISCSI_MGMT_CMDS_MAX ; session->cmds_max = cmds_max; session->queued_cmdsn = session->cmdsn = initial_cmdsn;
drivers/scsi/scsi_transport_iscsi.c +6 -2
@@ -30,7 +30,7 @@#include <scsi/scsi_transport_iscsi.h> #include <scsi/iscsi_if.h> -#define ISCSI_SESSION_ATTRS 16 +#define ISCSI_SESSION_ATTRS 18#define ISCSI_CONN_ATTRS 11#define ISCSI_HOST_ATTRS 4#define ISCSI_TRANSPORT_VERSION "2.0-724"
@@ -1241,7 +1241,9 @@ iscsi_session_attr(username, ISCSI_PARAM_USERNAME, 1);iscsi_session_attr (username_in,ISCSI_PARAM_USERNAME_IN ,1 );iscsi_session_attr (password,ISCSI_PARAM_PASSWORD ,1 );iscsi_session_attr (password_in,ISCSI_PARAM_PASSWORD_IN ,1 ); -iscsi_session_attr (fast_abort,ISCSI_PARAM_FAST_ABORT ,1 ); +iscsi_session_attr (fast_abort,ISCSI_PARAM_FAST_ABORT ,0 ); +iscsi_session_attr (abort_tmo,ISCSI_PARAM_ABORT_TMO ,0 ); +iscsi_session_attr (lu_reset_tmo,ISCSI_PARAM_LU_RESET_TMO ,0 );#define iscsi_priv_session_attr_show (field, format) \ static ssize_t \
@@ -1466,6 +1468,8 @@ iscsi_register_transport(struct iscsi_transport *tt)SETUP_SESSION_RD_ATTR (username,ISCSI_PASSWORD );SETUP_SESSION_RD_ATTR (username_in,ISCSI_PASSWORD_IN );SETUP_SESSION_RD_ATTR (fast_abort,ISCSI_FAST_ABORT ); +SETUP_SESSION_RD_ATTR (abort_tmo,ISCSI_ABORT_TMO ); +SETUP_SESSION_RD_ATTR (lu_reset_tmo,ISCSI_LU_RESET_TMO );SETUP_PRIV_SESSION_RD_ATTR (recovery_tmo);BUG_ON (count >ISCSI_SESSION_ATTRS );
Signature · 1 line
1.5.1.2
PATCH 20/23 iscsi_tcp: enable sg chaining
From: Mike Christie <[email protected]>
The previous patches converted iscsi_tcp to support sg chaining. This patch sets the proper flags and sets sg_table size to 4096. This allows fs io to be capped at max_sectors, but passthrough IO to be limited by some other part of the kernel.
--- drivers/scsi/iscsi_tcp.c | 5 +++-- drivers/scsi/iscsi_tcp.h | 3 --- 2 files changed, 3 insertions(+), 5 deletions(-)
drivers/scsi/iscsi_tcp.c +3 -2
@@ -1928,13 +1928,14 @@ static struct scsi_host_template iscsi_sht = { .queuecommand = iscsi_queuecommand, .change_queue_depth = iscsi_change_queue_depth, .can_queue = ISCSI_DEF_XMIT_CMDS_MAX - 1, - .sg_tablesize = ISCSI_SG_TABLESIZE, + .sg_tablesize = 4096, .max_sectors = 0xFFFF, .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN, .eh_abort_handler = iscsi_eh_abort, .eh_device_reset_handler= iscsi_eh_device_reset, .eh_host_reset_handler = iscsi_eh_host_reset, .use_clustering = DISABLE_CLUSTERING, + .use_sg_chaining = ENABLE_SG_CHAINING, .slave_configure = iscsi_tcp_slave_configure, .proc_name = "iscsi_tcp", .this_id = -1,
@@ -1974,7 +1975,7 @@ static struct iscsi_transport iscsi_tcp_transport = { .host_template = &iscsi_sht, .conndata_size = sizeof(struct iscsi_conn), .max_conn = 1, - .max_cmd_len = ISCSI_TCP_MAX_CMD_LEN, + .max_cmd_len = 16,/* session management */ .create_session = iscsi_tcp_session_create, .destroy_session = iscsi_tcp_session_destroy,
drivers/scsi/iscsi_tcp.h +0 -3
@@ -24,9 +24,6 @@#include <scsi/libiscsi.h> -#define ISCSI_SG_TABLESIZE SG_ALL -#define ISCSI_TCP_MAX_CMD_LEN 16 -struct crypto_hash ;struct socket ;struct iscsi_tcp_conn ;
Signature · 1 line
1.5.1.2
PATCH 21/23 iscsi_tcp: hold lock during data rsp processing
From: Mike Christie <[email protected]>
iscsi_data_rsp needs to hold the sesison lock when it calls iscsi_update_cmdsn.
--- drivers/scsi/iscsi_tcp.c | 14 ++++++-------- 1 files changed, 6 insertions(+), 8 deletions(-)
drivers/scsi/iscsi_tcp.c +6 -8
@@ -641,13 +641,11 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) }/* fill-in new R2T associated with the task */ -spin_lock (&session->lock);iscsi_update_cmdsn (session, (struct iscsi_nopin *)rhdr);if (!ctask->sc || 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 ; }
@@ -660,7 +658,6 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)printk (KERN_ERR "iscsi_tcp: invalid R2T with zero data len\n" );__kfifo_put (tcp_ctask->r2tpool.queue, (void *)&r2t,sizeof (void *)); -spin_unlock (&session->lock);return ISCSI_ERR_DATALEN ; }
@@ -676,7 +673,6 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) r2t->data_offset, scsi_bufflen(ctask->sc));__kfifo_put (tcp_ctask->r2tpool.queue, (void *)&r2t,sizeof (void *)); -spin_unlock (&session->lock);return ISCSI_ERR_DATALEN ; }
@@ -690,8 +686,6 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) conn->r2t_pdus_cnt++;iscsi_requeue_ctask (ctask); -spin_unlock (&session->lock); -return 0 ; }
@@ -764,7 +758,9 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)switch (opcode) {case ISCSI_OP_SCSI_DATA_IN : ctask = session->cmds[itt]; +spin_lock (&conn->session->lock); rc =iscsi_data_rsp (conn, ctask); +spin_unlock (&conn->session->lock);if (rc)return rc;if (tcp_conn->in.datalen) {
@@ -806,9 +802,11 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) ctask = session->cmds[itt];if (ahslen) rc =ISCSI_ERR_AHSLEN ; -else if (ctask->sc->sc_data_direction ==DMA_TO_DEVICE ) +else if (ctask->sc->sc_data_direction ==DMA_TO_DEVICE ) { +spin_lock (&session->lock); rc =iscsi_r2t_rsp (conn, ctask); -else +spin_unlock (&session->lock); + }else rc =ISCSI_ERR_PROTO ;break ;case ISCSI_OP_LOGIN_RSP :
Signature · 1 line
1.5.1.2
PATCH 22/23 libiscsi: use is_power_of_2
From: Mike Christie <[email protected]>
Patch from vignesh babu <[email protected]>:
Replacing n & (n - 1) for power of 2 check by is_power_of_2(n)
--- drivers/scsi/libiscsi.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-)
drivers/scsi/libiscsi.c +2 -1
@@ -24,6 +24,7 @@#include <linux/types.h> #include <linux/kfifo.h> #include <linux/delay.h> +#include <linux/log2.h> #include <asm/unaligned.h> #include <net/tcp.h> #include <scsi/scsi_cmnd.h>
@@ -1700,7 +1701,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, qdepth =ISCSI_DEF_CMD_PER_LUN ; } -if (cmds_max <2 || (cmds_max & (cmds_max -1 )) || +if (!is_power_of_2 (cmds_max) || cmds_max >=ISCSI_MGMT_ITT_OFFSET ) {if (cmds_max !=0 ) printk(KERN_ERR "iscsi: invalid can_queue of %d. "
Signature · 1 line
1.5.1.2
PATCH 23/23 iscsi_tcp: fix setting of r2t
From: Mike Christie <[email protected]>
If we negotiate for X r2ts we have to use only X r2ts. We cannot round up (we could send less though). It is ok to fail if it is not something the driver can handle, so this patch just does that.
--- drivers/scsi/iscsi_tcp.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-)
drivers/scsi/iscsi_tcp.c +3 -3
@@ -1774,12 +1774,12 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,break ;case ISCSI_PARAM_MAX_R2T :sscanf (buf,"%d" , &value); -if (session->max_r2t ==roundup_pow_of_two (value)) +if (value <=0 || !is_power_of_2 (value)) +return -EINVAL ; +if (session->max_r2t == value)break ;iscsi_r2tpool_free (session);iscsi_set_param (cls_conn, param, buf, buflen); -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 ;
Signature · 1 line
1.5.1.2