diff options
Diffstat (limited to 'drivers/scsi/libiscsi_tcp.c')
| -rw-r--r-- | drivers/scsi/libiscsi_tcp.c | 304 |
1 files changed, 178 insertions, 126 deletions
diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index e7705d3532c..60cb6dc3c6f 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -29,12 +29,14 @@ #include <linux/types.h> #include <linux/list.h> #include <linux/inet.h> +#include <linux/slab.h> #include <linux/file.h> #include <linux/blkdev.h> #include <linux/crypto.h> #include <linux/delay.h> #include <linux/kfifo.h> #include <linux/scatterlist.h> +#include <linux/module.h> #include <net/tcp.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> @@ -49,13 +51,21 @@ MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, " "Alex Aizman <itn780@yahoo.com>"); MODULE_DESCRIPTION("iSCSI/TCP data-path"); MODULE_LICENSE("GPL"); -#undef DEBUG_TCP -#ifdef DEBUG_TCP -#define debug_tcp(fmt...) printk(KERN_INFO "tcp: " fmt) -#else -#define debug_tcp(fmt...) -#endif +static int iscsi_dbg_libtcp; +module_param_named(debug_libiscsi_tcp, iscsi_dbg_libtcp, int, + S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug_libiscsi_tcp, "Turn on debugging for libiscsi_tcp " + "module. Set to 1 to turn on, and zero to turn off. Default " + "is off."); + +#define ISCSI_DBG_TCP(_conn, dbg_fmt, arg...) \ + do { \ + if (iscsi_dbg_libtcp) \ + iscsi_conn_printk(KERN_INFO, _conn, \ + "%s " dbg_fmt, \ + __func__, ##arg); \ + } while (0); static int iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn, struct iscsi_segment *segment); @@ -123,19 +133,25 @@ static void iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv) if (page_count(sg_page(sg)) >= 1 && !recv) return; - debug_tcp("iscsi_tcp_segment_map %s %p\n", recv ? "recv" : "xmit", - segment); - segment->sg_mapped = kmap_atomic(sg_page(sg), KM_SOFTIRQ0); + if (recv) { + segment->atomic_mapped = true; + segment->sg_mapped = kmap_atomic(sg_page(sg)); + } else { + segment->atomic_mapped = false; + /* the xmit path can sleep with the page mapped so use kmap */ + segment->sg_mapped = kmap(sg_page(sg)); + } + segment->data = segment->sg_mapped + sg->offset + segment->sg_offset; } void iscsi_tcp_segment_unmap(struct iscsi_segment *segment) { - debug_tcp("iscsi_tcp_segment_unmap %p\n", segment); - if (segment->sg_mapped) { - debug_tcp("iscsi_tcp_segment_unmap valid\n"); - kunmap_atomic(segment->sg_mapped, KM_SOFTIRQ0); + if (segment->atomic_mapped) + kunmap_atomic(segment->sg_mapped); + else + kunmap(sg_page(segment->sg)); segment->sg_mapped = NULL; segment->data = NULL; } @@ -180,8 +196,9 @@ int iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn, struct scatterlist sg; unsigned int pad; - debug_tcp("copied %u %u size %u %s\n", segment->copied, copied, - segment->size, recv ? "recv" : "xmit"); + ISCSI_DBG_TCP(tcp_conn->iscsi_conn, "copied %u %u size %u %s\n", + segment->copied, copied, segment->size, + recv ? "recv" : "xmit"); if (segment->hash && copied) { /* * If a segment is kmapd we must unmap it before sending @@ -214,8 +231,8 @@ int iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn, iscsi_tcp_segment_unmap(segment); /* Do we have more scatterlist entries? */ - debug_tcp("total copied %u total size %u\n", segment->total_copied, - segment->total_size); + ISCSI_DBG_TCP(tcp_conn->iscsi_conn, "total copied %u total size %u\n", + segment->total_copied, segment->total_size); if (segment->total_copied < segment->total_size) { /* Proceed to the next entry in the scatterlist. */ iscsi_tcp_segment_init_sg(segment, sg_next(segment->sg), @@ -229,7 +246,8 @@ int iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn, if (!(tcp_conn->iscsi_conn->session->tt->caps & CAP_PADDING_OFFLOAD)) { pad = iscsi_padding(segment->total_copied); if (pad != 0) { - debug_tcp("consume %d pad bytes\n", pad); + ISCSI_DBG_TCP(tcp_conn->iscsi_conn, + "consume %d pad bytes\n", pad); segment->total_size += pad; segment->size = pad; segment->data = segment->padbuf; @@ -278,13 +296,13 @@ iscsi_tcp_segment_recv(struct iscsi_tcp_conn *tcp_conn, while (!iscsi_tcp_segment_done(tcp_conn, segment, 1, copy)) { if (copied == len) { - debug_tcp("iscsi_tcp_segment_recv copied %d bytes\n", - len); + ISCSI_DBG_TCP(tcp_conn->iscsi_conn, + "copied %d bytes\n", len); break; } copy = min(len - copied, segment->size - segment->copied); - debug_tcp("iscsi_tcp_segment_recv copying %d\n", copy); + ISCSI_DBG_TCP(tcp_conn->iscsi_conn, "copying %d\n", copy); memcpy(segment->data + segment->copied, ptr + copied, copy); copied += copy; } @@ -311,7 +329,7 @@ iscsi_tcp_dgst_verify(struct iscsi_tcp_conn *tcp_conn, if (memcmp(segment->recv_digest, segment->digest, segment->digest_len)) { - debug_scsi("digest mismatch\n"); + ISCSI_DBG_TCP(tcp_conn->iscsi_conn, "digest mismatch\n"); return 0; } @@ -355,12 +373,8 @@ iscsi_segment_seek_sg(struct iscsi_segment *segment, struct scatterlist *sg; unsigned int i; - debug_scsi("iscsi_segment_seek_sg offset %u size %llu\n", - offset, size); __iscsi_segment_init(segment, size, done, hash); for_each_sg(sg_list, sg, sg_count, i) { - debug_scsi("sg %d, len %u offset %u\n", i, sg->length, - sg->offset); if (offset < sg->length) { iscsi_tcp_segment_init_sg(segment, sg, offset); return 0; @@ -382,8 +396,9 @@ EXPORT_SYMBOL_GPL(iscsi_segment_seek_sg); */ 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_DBG_TCP(tcp_conn->iscsi_conn, + "(%s)\n", tcp_conn->iscsi_conn->hdrdgst_en ? + "digest enabled" : "digest disabled"); iscsi_segment_init_linear(&tcp_conn->in.segment, tcp_conn->in.hdr_buf, sizeof(struct iscsi_hdr), iscsi_tcp_hdr_recv_done, NULL); @@ -418,7 +433,7 @@ 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 & + if (conn->datadgst_en && !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD)) rx_hash = tcp_conn->rx_hash; @@ -431,30 +446,32 @@ iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn) * iscsi_tcp_cleanup_task - free tcp_task resources * @task: iscsi task * - * must be called with session lock + * must be called with session back_lock */ void iscsi_tcp_cleanup_task(struct iscsi_task *task) { struct iscsi_tcp_task *tcp_task = task->dd_data; struct iscsi_r2t_info *r2t; - /* nothing to do for mgmt or pending tasks */ - if (!task->sc || task->state == ISCSI_TASK_PENDING) + /* nothing to do for mgmt */ + if (!task->sc) return; + spin_lock_bh(&tcp_task->queue2pool); /* flush task's r2t queues */ - while (__kfifo_get(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*))) { - __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t, + while (kfifo_out(&tcp_task->r2tqueue, (void*)&r2t, sizeof(void*))) { + kfifo_in(&tcp_task->r2tpool.queue, (void*)&r2t, sizeof(void*)); - debug_scsi("iscsi_tcp_cleanup_task pending r2t dropped\n"); + ISCSI_DBG_TCP(task->conn, "pending r2t dropped\n"); } r2t = tcp_task->r2t; if (r2t != NULL) { - __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t, + kfifo_in(&tcp_task->r2tpool.queue, (void*)&r2t, sizeof(void*)); tcp_task->r2t = NULL; } + spin_unlock_bh(&tcp_task->queue2pool); } EXPORT_SYMBOL_GPL(iscsi_tcp_cleanup_task); @@ -471,13 +488,19 @@ static int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task) int datasn = be32_to_cpu(rhdr->datasn); unsigned total_in_length = scsi_in(task->sc)->length; - iscsi_update_cmdsn(conn->session, (struct iscsi_nopin*)rhdr); + /* + * lib iscsi will update this in the completion handling if there + * is status. + */ + if (!(rhdr->flags & ISCSI_FLAG_DATA_STATUS)) + iscsi_update_cmdsn(conn->session, (struct iscsi_nopin*)rhdr); + if (tcp_conn->in.datalen == 0) return 0; if (tcp_task->exp_datasn != datasn) { - debug_tcp("%s: task->exp_datasn(%d) != rhdr->datasn(%d)\n", - __func__, tcp_task->exp_datasn, datasn); + ISCSI_DBG_TCP(conn, "task->exp_datasn(%d) != rhdr->datasn(%d)" + "\n", tcp_task->exp_datasn, datasn); return ISCSI_ERR_DATASN; } @@ -485,9 +508,9 @@ static int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task) tcp_task->data_offset = be32_to_cpu(rhdr->offset); if (tcp_task->data_offset + tcp_conn->in.datalen > total_in_length) { - debug_tcp("%s: data_offset(%d) + data_len(%d) > total_length_in(%d)\n", - __func__, tcp_task->data_offset, - tcp_conn->in.datalen, total_in_length); + ISCSI_DBG_TCP(conn, "data_offset(%d) + data_len(%d) > " + "total_length_in(%d)\n", tcp_task->data_offset, + tcp_conn->in.datalen, total_in_length); return ISCSI_ERR_DATA_OFFSET; } @@ -508,6 +531,8 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task) struct iscsi_r2t_rsp *rhdr = (struct iscsi_r2t_rsp *)tcp_conn->in.hdr; struct iscsi_r2t_info *r2t; int r2tsn = be32_to_cpu(rhdr->r2tsn); + u32 data_length; + u32 data_offset; int rc; if (tcp_conn->in.datalen) { @@ -518,8 +543,8 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task) } if (tcp_task->exp_datasn != r2tsn){ - debug_tcp("%s: task->exp_datasn(%d) != rhdr->r2tsn(%d)\n", - __func__, tcp_task->exp_datasn, r2tsn); + ISCSI_DBG_TCP(conn, "task->exp_datasn(%d) != rhdr->r2tsn(%d)\n", + tcp_task->exp_datasn, r2tsn); return ISCSI_ERR_R2TSN; } @@ -533,47 +558,49 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task) return 0; } - rc = __kfifo_get(tcp_task->r2tpool.queue, (void*)&r2t, sizeof(void*)); - if (!rc) { - iscsi_conn_printk(KERN_ERR, conn, "Could not allocate R2T. " - "Target has sent more R2Ts than it " - "negotiated for or driver has has leaked.\n"); - return ISCSI_ERR_PROTO; - } - - r2t->exp_statsn = rhdr->statsn; - r2t->data_length = be32_to_cpu(rhdr->data_length); - if (r2t->data_length == 0) { + data_length = be32_to_cpu(rhdr->data_length); + if (data_length == 0) { iscsi_conn_printk(KERN_ERR, conn, "invalid R2T with zero data len\n"); - __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t, - sizeof(void*)); return ISCSI_ERR_DATALEN; } - if (r2t->data_length > session->max_burst) - debug_scsi("invalid R2T with data len %u and max burst %u." - "Attempting to execute request.\n", - r2t->data_length, session->max_burst); + if (data_length > session->max_burst) + ISCSI_DBG_TCP(conn, "invalid R2T with data len %u and max " + "burst %u. Attempting to execute request.\n", + data_length, session->max_burst); - r2t->data_offset = be32_to_cpu(rhdr->data_offset); - if (r2t->data_offset + r2t->data_length > scsi_out(task->sc)->length) { + data_offset = be32_to_cpu(rhdr->data_offset); + if (data_offset + data_length > scsi_out(task->sc)->length) { iscsi_conn_printk(KERN_ERR, conn, "invalid R2T with data len %u at offset %u " - "and total length %d\n", r2t->data_length, - r2t->data_offset, scsi_out(task->sc)->length); - __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t, - sizeof(void*)); + "and total length %d\n", data_length, + data_offset, scsi_out(task->sc)->length); return ISCSI_ERR_DATALEN; } + spin_lock(&tcp_task->pool2queue); + rc = kfifo_out(&tcp_task->r2tpool.queue, (void *)&r2t, sizeof(void *)); + if (!rc) { + iscsi_conn_printk(KERN_ERR, conn, "Could not allocate R2T. " + "Target has sent more R2Ts than it " + "negotiated for or driver has leaked.\n"); + spin_unlock(&tcp_task->pool2queue); + return ISCSI_ERR_PROTO; + } + + r2t->exp_statsn = rhdr->statsn; + r2t->data_length = data_length; + r2t->data_offset = data_offset; + r2t->ttt = rhdr->ttt; /* no flip */ r2t->datasn = 0; r2t->sent = 0; tcp_task->exp_datasn = r2tsn + 1; - __kfifo_put(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*)); + kfifo_in(&tcp_task->r2tqueue, (void*)&r2t, sizeof(void*)); conn->r2t_pdus_cnt++; + spin_unlock(&tcp_task->pool2queue); iscsi_requeue_task(task); return 0; @@ -641,19 +668,19 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) if (rc) return rc; - debug_tcp("opcode 0x%x ahslen %d datalen %d\n", - opcode, ahslen, tcp_conn->in.datalen); + ISCSI_DBG_TCP(conn, "opcode 0x%x ahslen %d datalen %d\n", + opcode, ahslen, tcp_conn->in.datalen); switch(opcode) { case ISCSI_OP_SCSI_DATA_IN: - spin_lock(&conn->session->lock); + spin_lock(&conn->session->back_lock); task = iscsi_itt_to_ctask(conn, hdr->itt); if (!task) rc = ISCSI_ERR_BAD_ITT; else rc = iscsi_tcp_data_in(conn, task); if (rc) { - spin_unlock(&conn->session->lock); + spin_unlock(&conn->session->back_lock); break; } @@ -674,10 +701,11 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD)) rx_hash = tcp_conn->rx_hash; - debug_tcp("iscsi_tcp_begin_data_in(%p, offset=%d, " - "datalen=%d)\n", tcp_conn, - tcp_task->data_offset, - tcp_conn->in.datalen); + ISCSI_DBG_TCP(conn, "iscsi_tcp_begin_data_in( " + "offset=%d, datalen=%d)\n", + tcp_task->data_offset, + tcp_conn->in.datalen); + task->last_xfer = jiffies; rc = iscsi_segment_seek_sg(&tcp_conn->in.segment, sdb->table.sgl, sdb->table.nents, @@ -685,11 +713,11 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) tcp_conn->in.datalen, iscsi_tcp_process_data_in, rx_hash); - spin_unlock(&conn->session->lock); + spin_unlock(&conn->session->back_lock); return rc; } rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); - spin_unlock(&conn->session->lock); + spin_unlock(&conn->session->back_lock); break; case ISCSI_OP_SCSI_CMD_RSP: if (tcp_conn->in.datalen) { @@ -699,17 +727,20 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) rc = iscsi_complete_pdu(conn, hdr, NULL, 0); break; case ISCSI_OP_R2T: - spin_lock(&conn->session->lock); + spin_lock(&conn->session->back_lock); task = iscsi_itt_to_ctask(conn, hdr->itt); + spin_unlock(&conn->session->back_lock); if (!task) rc = ISCSI_ERR_BAD_ITT; else if (ahslen) rc = ISCSI_ERR_AHSLEN; - else if (task->sc->sc_data_direction == DMA_TO_DEVICE) + else if (task->sc->sc_data_direction == DMA_TO_DEVICE) { + task->last_xfer = jiffies; + spin_lock(&conn->session->frwd_lock); rc = iscsi_tcp_r2t_rsp(conn, task); - else + spin_unlock(&conn->session->frwd_lock); + } else rc = ISCSI_ERR_PROTO; - spin_unlock(&conn->session->lock); break; case ISCSI_OP_LOGIN_RSP: case ISCSI_OP_TEXT_RSP: @@ -854,10 +885,16 @@ int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb, unsigned int consumed = 0; int rc = 0; - debug_tcp("in %d bytes\n", skb->len - offset); + ISCSI_DBG_TCP(conn, "in %d bytes\n", skb->len - offset); + /* + * Update for each skb instead of pdu, because over slow networks a + * data_in's data could take a while to read in. We also want to + * account for r2ts. + */ + conn->last_recv = jiffies; if (unlikely(conn->suspend_rx)) { - debug_tcp("conn %d Rx suspended!\n", conn->id); + ISCSI_DBG_TCP(conn, "Rx suspended!\n"); *status = ISCSI_TCP_SUSPENDED; return 0; } @@ -874,15 +911,15 @@ int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb, avail = skb_seq_read(consumed, &ptr, &seq); if (avail == 0) { - debug_tcp("no more data avail. Consumed %d\n", - consumed); + ISCSI_DBG_TCP(conn, "no more data avail. Consumed %d\n", + consumed); *status = ISCSI_TCP_SKB_DONE; - skb_abort_seq_read(&seq); goto skb_done; } BUG_ON(segment->copied >= segment->size); - debug_tcp("skb %p ptr=%p avail=%u\n", skb, ptr, avail); + ISCSI_DBG_TCP(conn, "skb %p ptr=%p avail=%u\n", skb, ptr, + avail); rc = iscsi_tcp_segment_recv(tcp_conn, segment, ptr, avail); BUG_ON(rc == 0); consumed += rc; @@ -895,11 +932,11 @@ int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb, segment_done: *status = ISCSI_TCP_SEGMENT_DONE; - debug_tcp("segment done\n"); + ISCSI_DBG_TCP(conn, "segment done\n"); rc = segment->done(tcp_conn, segment); if (rc != 0) { *status = ISCSI_TCP_CONN_ERR; - debug_tcp("Error receiving PDU, errno=%d\n", rc); + ISCSI_DBG_TCP(conn, "Error receiving PDU, errno=%d\n", rc); iscsi_conn_failure(conn, rc); return 0; } @@ -929,19 +966,17 @@ int iscsi_tcp_task_init(struct iscsi_task *task) * mgmt tasks do not have a scatterlist since they come * in from the iscsi interface. */ - debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id, - task->itt); + ISCSI_DBG_TCP(conn, "mtask deq [itt 0x%x]\n", task->itt); return conn->session->tt->init_pdu(task, 0, task->data_count); } - BUG_ON(__kfifo_len(tcp_task->r2tqueue)); + BUG_ON(kfifo_len(&tcp_task->r2tqueue)); tcp_task->exp_datasn = 0; /* Prepare PDU, optionally w/ immediate data */ - debug_scsi("task deq [cid %d itt 0x%x imm %d unsol %d]\n", - conn->id, task->itt, task->imm_count, - task->unsol_r2t.data_length); + ISCSI_DBG_TCP(conn, "task deq [itt 0x%x imm %d unsol %d]\n", + task->itt, task->imm_count, task->unsol_r2t.data_length); err = conn->session->tt->init_pdu(task, 0, task->imm_count); if (err) @@ -953,20 +988,20 @@ EXPORT_SYMBOL_GPL(iscsi_tcp_task_init); static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task) { - struct iscsi_session *session = task->conn->session; struct iscsi_tcp_task *tcp_task = task->dd_data; struct iscsi_r2t_info *r2t = NULL; if (iscsi_task_has_unsol_data(task)) r2t = &task->unsol_r2t; else { - spin_lock_bh(&session->lock); + spin_lock_bh(&tcp_task->queue2pool); if (tcp_task->r2t) { r2t = tcp_task->r2t; /* Continue with this R2T? */ if (r2t->data_length <= r2t->sent) { - debug_scsi(" done with r2t %p\n", r2t); - __kfifo_put(tcp_task->r2tpool.queue, + ISCSI_DBG_TCP(task->conn, + " done with r2t %p\n", r2t); + kfifo_in(&tcp_task->r2tpool.queue, (void *)&tcp_task->r2t, sizeof(void *)); tcp_task->r2t = r2t = NULL; @@ -974,11 +1009,14 @@ static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task) } if (r2t == NULL) { - __kfifo_get(tcp_task->r2tqueue, - (void *)&tcp_task->r2t, sizeof(void *)); - r2t = tcp_task->r2t; + if (kfifo_out(&tcp_task->r2tqueue, + (void *)&tcp_task->r2t, sizeof(void *)) != + sizeof(void *)) + r2t = NULL; + else + r2t = tcp_task->r2t; } - spin_unlock_bh(&session->lock); + spin_unlock_bh(&tcp_task->queue2pool); } return r2t; @@ -988,7 +1026,7 @@ static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task) * iscsi_tcp_task_xmit - xmit normal PDU task * @task: iscsi command task * - * We're expected to return 0 when everything was transmitted succesfully, + * We're expected to return 0 when everything was transmitted successfully, * -EAGAIN if there's still data in the queue, or != 0 for any other kind * of error. */ @@ -1019,7 +1057,7 @@ flush: r2t = iscsi_tcp_get_curr_r2t(task); if (r2t == NULL) { /* Waiting for more R2Ts to arrive. */ - debug_tcp("no R2Ts yet\n"); + ISCSI_DBG_TCP(conn, "no R2Ts yet\n"); return 0; } @@ -1028,14 +1066,17 @@ flush: return rc; iscsi_prep_data_out_pdu(task, r2t, (struct iscsi_data *) task->hdr); - debug_scsi("sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n", - r2t, r2t->datasn - 1, task->hdr->itt, - r2t->data_offset + r2t->sent, r2t->data_count); + ISCSI_DBG_TCP(conn, "sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n", + r2t, r2t->datasn - 1, task->hdr->itt, + r2t->data_offset + r2t->sent, r2t->data_count); rc = conn->session->tt->init_pdu(task, r2t->data_offset + r2t->sent, r2t->data_count); - if (rc) + if (rc) { + iscsi_conn_failure(conn, ISCSI_ERR_XMIT_FAILED); return rc; + } + r2t->sent += r2t->data_count; goto flush; } @@ -1050,7 +1091,8 @@ iscsi_tcp_conn_setup(struct iscsi_cls_session *cls_session, int dd_data_size, struct iscsi_cls_conn *cls_conn; struct iscsi_tcp_conn *tcp_conn; - cls_conn = iscsi_conn_setup(cls_session, sizeof(*tcp_conn), conn_idx); + cls_conn = iscsi_conn_setup(cls_session, + sizeof(*tcp_conn) + dd_data_size, conn_idx); if (!cls_conn) return NULL; conn = cls_conn->dd_data; @@ -1062,22 +1104,13 @@ iscsi_tcp_conn_setup(struct iscsi_cls_session *cls_session, int dd_data_size, tcp_conn = conn->dd_data; tcp_conn->iscsi_conn = conn; - - tcp_conn->dd_data = kzalloc(dd_data_size, GFP_KERNEL); - if (!tcp_conn->dd_data) { - iscsi_conn_teardown(cls_conn); - return NULL; - } + tcp_conn->dd_data = conn->dd_data + sizeof(*tcp_conn); return cls_conn; } EXPORT_SYMBOL_GPL(iscsi_tcp_conn_setup); void iscsi_tcp_conn_teardown(struct iscsi_cls_conn *cls_conn) { - struct iscsi_conn *conn = cls_conn->dd_data; - struct iscsi_tcp_conn *tcp_conn = conn->dd_data; - - kfree(tcp_conn->dd_data); iscsi_conn_teardown(cls_conn); } EXPORT_SYMBOL_GPL(iscsi_tcp_conn_teardown); @@ -1108,12 +1141,13 @@ int iscsi_tcp_r2tpool_alloc(struct iscsi_session *session) } /* R2T xmit queue */ - tcp_task->r2tqueue = kfifo_alloc( - session->max_r2t * 4 * sizeof(void*), GFP_KERNEL, NULL); - if (tcp_task->r2tqueue == ERR_PTR(-ENOMEM)) { + if (kfifo_alloc(&tcp_task->r2tqueue, + session->max_r2t * 4 * sizeof(void*), GFP_KERNEL)) { iscsi_pool_free(&tcp_task->r2tpool); goto r2t_alloc_fail; } + spin_lock_init(&tcp_task->pool2queue); + spin_lock_init(&tcp_task->queue2pool); } return 0; @@ -1123,7 +1157,7 @@ r2t_alloc_fail: struct iscsi_task *task = session->cmds[i]; struct iscsi_tcp_task *tcp_task = task->dd_data; - kfifo_free(tcp_task->r2tqueue); + kfifo_free(&tcp_task->r2tqueue); iscsi_pool_free(&tcp_task->r2tpool); } return -ENOMEM; @@ -1138,12 +1172,30 @@ void iscsi_tcp_r2tpool_free(struct iscsi_session *session) struct iscsi_task *task = session->cmds[i]; struct iscsi_tcp_task *tcp_task = task->dd_data; - kfifo_free(tcp_task->r2tqueue); + kfifo_free(&tcp_task->r2tqueue); iscsi_pool_free(&tcp_task->r2tpool); } } EXPORT_SYMBOL_GPL(iscsi_tcp_r2tpool_free); +int iscsi_tcp_set_max_r2t(struct iscsi_conn *conn, char *buf) +{ + struct iscsi_session *session = conn->session; + unsigned short r2ts = 0; + + sscanf(buf, "%hu", &r2ts); + if (session->max_r2t == r2ts) + return 0; + + if (!r2ts || !is_power_of_2(r2ts)) + return -EINVAL; + + session->max_r2t = r2ts; + iscsi_tcp_r2tpool_free(session); + return iscsi_tcp_r2tpool_alloc(session); +} +EXPORT_SYMBOL_GPL(iscsi_tcp_set_max_r2t); + void iscsi_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats) { |
