diff options
Diffstat (limited to 'drivers/scsi/iscsi_tcp.c')
-rw-r--r-- | drivers/scsi/iscsi_tcp.c | 249 |
1 files changed, 119 insertions, 130 deletions
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 464de780d953..97bad5ce5834 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -57,13 +57,6 @@ MODULE_LICENSE("GPL"); #define debug_tcp(fmt...) #endif -#ifndef DEBUG_ASSERT -#ifdef BUG_ON -#undef BUG_ON -#endif -#define BUG_ON(expr) -#endif - static struct scsi_transport_template *iscsi_tcp_scsi_transport; static struct scsi_host_template iscsi_sht; static struct iscsi_transport iscsi_tcp_transport; @@ -498,14 +491,13 @@ iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn) /* * must be called with session lock */ -static void -iscsi_tcp_cleanup_task(struct iscsi_conn *conn, struct iscsi_task *task) +static 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 tasks */ - if (!task->sc) + /* nothing to do for mgmt or pending tasks */ + if (!task->sc || task->state == ISCSI_TASK_PENDING) return; /* flush task's r2t queues */ @@ -611,11 +603,11 @@ iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_task *task, static int iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task) { - struct iscsi_r2t_info *r2t; struct iscsi_session *session = conn->session; struct iscsi_tcp_task *tcp_task = task->dd_data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 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); int rc; @@ -643,7 +635,12 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task) } rc = __kfifo_get(tcp_task->r2tpool.queue, (void*)&r2t, sizeof(void*)); - BUG_ON(!rc); + 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); @@ -672,9 +669,8 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task) } r2t->ttt = rhdr->ttt; /* no flip */ - r2t->solicit_datasn = 0; - - iscsi_solicit_data_init(conn, task, r2t); + r2t->datasn = 0; + r2t->sent = 0; tcp_task->exp_datasn = r2tsn + 1; __kfifo_put(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*)); @@ -1199,9 +1195,9 @@ iscsi_tcp_xmit_qlen(struct iscsi_conn *conn) return segment->total_copied - segment->total_size; } -static inline int -iscsi_tcp_flush(struct iscsi_conn *conn) +static int iscsi_tcp_flush(struct iscsi_task *task) { + struct iscsi_conn *conn = task->conn; int rc; while (iscsi_tcp_xmit_qlen(conn)) { @@ -1364,14 +1360,49 @@ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_task *task, return 1; } +static int iscsi_tcp_pdu_init(struct iscsi_task *task, + unsigned int offset, unsigned int count) +{ + struct iscsi_conn *conn = task->conn; + int err = 0; + + iscsi_tcp_send_hdr_prep(conn, task->hdr, task->hdr_len); + + if (!count) + return 0; + + if (!task->sc) + iscsi_tcp_send_linear_data_prepare(conn, task->data, count); + else { + struct scsi_data_buffer *sdb = scsi_out(task->sc); + + err = iscsi_tcp_send_data_prep(conn, sdb->table.sgl, + sdb->table.nents, offset, count); + } + + if (err) { + iscsi_conn_failure(conn, err); + return -EIO; + } + return 0; +} + +static int iscsi_tcp_pdu_alloc(struct iscsi_task *task) +{ + struct iscsi_tcp_task *tcp_task = task->dd_data; + + task->hdr = &tcp_task->hdr.hdrbuf; + task->hdr_max = sizeof(tcp_task->hdr) - ISCSI_DIGEST_SIZE; + return 0; +} + /** * iscsi_tcp_task - Initialize iSCSI SCSI_READ or SCSI_WRITE commands * @conn: iscsi connection * @task: scsi command task * @sc: scsi command **/ -static int -iscsi_tcp_task_init(struct iscsi_task *task) +static int iscsi_tcp_task_init(struct iscsi_task *task) { struct iscsi_tcp_task *tcp_task = task->dd_data; struct iscsi_conn *conn = task->conn; @@ -1386,40 +1417,57 @@ iscsi_tcp_task_init(struct iscsi_task *task) debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id, task->itt); - /* Prepare PDU, optionally w/ immediate data */ - iscsi_tcp_send_hdr_prep(conn, task->hdr, sizeof(*task->hdr)); - - /* If we have immediate data, attach a payload */ - if (task->data_count) - iscsi_tcp_send_linear_data_prepare(conn, task->data, - task->data_count); - return 0; + return conn->session->tt->init_pdu(task, 0, task->data_count); } BUG_ON(__kfifo_len(tcp_task->r2tqueue)); - tcp_task->sent = 0; 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_count); - iscsi_tcp_send_hdr_prep(conn, task->hdr, task->hdr_len); + task->unsol_r2t.data_length); - if (!task->imm_count) - return 0; - - /* If we have immediate data, attach a payload */ - err = iscsi_tcp_send_data_prep(conn, scsi_out(sc)->table.sgl, - scsi_out(sc)->table.nents, - 0, task->imm_count); + err = conn->session->tt->init_pdu(task, 0, task->imm_count); if (err) return err; - tcp_task->sent += task->imm_count; task->imm_count = 0; return 0; } +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); + 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, + (void *)&tcp_task->r2t, + sizeof(void *)); + tcp_task->r2t = r2t = NULL; + } + } + + if (r2t == NULL) { + __kfifo_get(tcp_task->r2tqueue, + (void *)&tcp_task->r2t, sizeof(void *)); + r2t = tcp_task->r2t; + } + spin_unlock_bh(&session->lock); + } + + return r2t; +} + /* * iscsi_tcp_task_xmit - xmit normal PDU task * @task: iscsi command task @@ -1428,108 +1476,52 @@ iscsi_tcp_task_init(struct iscsi_task *task) * -EAGAIN if there's still data in the queue, or != 0 for any other kind * of error. */ -static int -iscsi_tcp_task_xmit(struct iscsi_task *task) +static int iscsi_tcp_task_xmit(struct iscsi_task *task) { struct iscsi_conn *conn = task->conn; - struct iscsi_tcp_task *tcp_task = task->dd_data; - struct scsi_cmnd *sc = task->sc; - struct scsi_data_buffer *sdb; + struct iscsi_session *session = conn->session; + struct iscsi_r2t_info *r2t; int rc = 0; flush: /* Flush any pending data first. */ - rc = iscsi_tcp_flush(conn); + rc = session->tt->xmit_pdu(task); if (rc < 0) return rc; /* mgmt command */ - if (!sc) { + if (!task->sc) { if (task->hdr->itt == RESERVED_ITT) iscsi_put_task(task); return 0; } /* Are we done already? */ - if (sc->sc_data_direction != DMA_TO_DEVICE) + if (task->sc->sc_data_direction != DMA_TO_DEVICE) return 0; - sdb = scsi_out(sc); - if (task->unsol_count != 0) { - struct iscsi_data *hdr = &tcp_task->unsol_dtask.hdr; - - /* Prepare a header for the unsolicited PDU. - * The amount of data we want to send will be - * in task->data_count. - * FIXME: return the data count instead. - */ - iscsi_prep_unsolicit_data_pdu(task, hdr); - - debug_tcp("unsol dout [itt 0x%x doff %d dlen %d]\n", - task->itt, tcp_task->sent, task->data_count); - - iscsi_tcp_send_hdr_prep(conn, hdr, sizeof(*hdr)); - rc = iscsi_tcp_send_data_prep(conn, sdb->table.sgl, - sdb->table.nents, tcp_task->sent, - task->data_count); - if (rc) - goto fail; - tcp_task->sent += task->data_count; - task->unsol_count -= task->data_count; - goto flush; - } else { - struct iscsi_session *session = conn->session; - struct iscsi_r2t_info *r2t; - - /* All unsolicited PDUs sent. Check for solicited PDUs. - */ - spin_lock_bh(&session->lock); - r2t = tcp_task->r2t; - if (r2t != NULL) { - /* Continue with this R2T? */ - if (!iscsi_solicit_data_cont(conn, task, r2t)) { - debug_scsi(" done with r2t %p\n", r2t); - - __kfifo_put(tcp_task->r2tpool.queue, - (void*)&r2t, sizeof(void*)); - tcp_task->r2t = r2t = NULL; - } - } - - if (r2t == NULL) { - __kfifo_get(tcp_task->r2tqueue, (void*)&tcp_task->r2t, - sizeof(void*)); - r2t = tcp_task->r2t; - } - spin_unlock_bh(&session->lock); - + r2t = iscsi_tcp_get_curr_r2t(task); + if (r2t == NULL) { /* Waiting for more R2Ts to arrive. */ - if (r2t == NULL) { - debug_tcp("no R2Ts yet\n"); - return 0; - } + debug_tcp("no R2Ts yet\n"); + return 0; + } - debug_scsi("sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n", - r2t, r2t->solicit_datasn - 1, task->itt, - r2t->data_offset + r2t->sent, r2t->data_count); + rc = conn->session->tt->alloc_pdu(task); + if (rc) + return rc; + iscsi_prep_data_out_pdu(task, r2t, (struct iscsi_data *) task->hdr); - iscsi_tcp_send_hdr_prep(conn, &r2t->dtask.hdr, - sizeof(struct iscsi_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); - rc = iscsi_tcp_send_data_prep(conn, sdb->table.sgl, - sdb->table.nents, - r2t->data_offset + r2t->sent, - r2t->data_count); - if (rc) - goto fail; - tcp_task->sent += r2t->data_count; - r2t->sent += r2t->data_count; - goto flush; - } - return 0; -fail: - iscsi_conn_failure(conn, rc); - return -EIO; + rc = conn->session->tt->init_pdu(task, r2t->data_offset + r2t->sent, + r2t->data_count); + if (rc) + return rc; + r2t->sent += r2t->data_count; + goto flush; } static struct iscsi_cls_conn * @@ -1751,13 +1743,14 @@ iscsi_r2tpool_alloc(struct iscsi_session *session) struct iscsi_tcp_task *tcp_task = task->dd_data; /* - * pre-allocated x4 as much r2ts to handle race when + * pre-allocated x2 as much r2ts to handle race when * target acks DataOut faster than we data_xmit() queues * could replenish r2tqueue. */ /* R2T pool */ - if (iscsi_pool_init(&tcp_task->r2tpool, session->max_r2t * 4, NULL, + if (iscsi_pool_init(&tcp_task->r2tpool, + session->max_r2t * 2, NULL, sizeof(struct iscsi_r2t_info))) { goto r2t_alloc_fail; } @@ -1891,7 +1884,6 @@ iscsi_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max, struct iscsi_cls_session *cls_session; struct iscsi_session *session; struct Scsi_Host *shost; - int cmd_i; if (ep) { printk(KERN_ERR "iscsi_tcp: invalid ep %p.\n", ep); @@ -1919,14 +1911,6 @@ iscsi_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max, session = cls_session->dd_data; shost->can_queue = session->scsi_cmds_max; - for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) { - struct iscsi_task *task = session->cmds[cmd_i]; - struct iscsi_tcp_task *tcp_task = task->dd_data; - - task->hdr = &tcp_task->hdr.cmd_hdr; - task->hdr_max = sizeof(tcp_task->hdr) - ISCSI_DIGEST_SIZE; - } - if (iscsi_r2tpool_alloc(session)) goto remove_session; return cls_session; @@ -2026,9 +2010,14 @@ static struct iscsi_transport iscsi_tcp_transport = { /* IO */ .send_pdu = iscsi_conn_send_pdu, .get_stats = iscsi_conn_get_stats, + /* iscsi task/cmd helpers */ .init_task = iscsi_tcp_task_init, .xmit_task = iscsi_tcp_task_xmit, .cleanup_task = iscsi_tcp_cleanup_task, + /* low level pdu helpers */ + .xmit_pdu = iscsi_tcp_flush, + .init_pdu = iscsi_tcp_pdu_init, + .alloc_pdu = iscsi_tcp_pdu_alloc, /* recovery */ .session_recovery_timedout = iscsi_session_recovery_timedout, }; |