X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fscsi%2Flibiscsi.c;h=d37048c96eab5163231f04917408a6c418c25b79;hb=refs%2Fheads%2Fvserver;hp=2673a11a9495ef2297ada1591cf65cfcbd94c0a1;hpb=16cf0ec7408f389279d413869e94c1a351392f97;p=linux-2.6.git diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 2673a11a9..d37048c96 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -68,8 +68,7 @@ iscsi_check_assign_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr) EXPORT_SYMBOL_GPL(iscsi_check_assign_cmdsn); void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask, - struct iscsi_data *hdr, - int transport_data_cnt) + struct iscsi_data *hdr) { struct iscsi_conn *conn = ctask->conn; @@ -82,14 +81,12 @@ void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask, hdr->itt = ctask->hdr->itt; hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); - - hdr->offset = cpu_to_be32(ctask->total_length - - transport_data_cnt - - ctask->unsol_count); + hdr->offset = cpu_to_be32(ctask->unsol_offset); if (ctask->unsol_count > conn->max_xmit_dlength) { hton24(hdr->dlength, conn->max_xmit_dlength); ctask->data_count = conn->max_xmit_dlength; + ctask->unsol_offset += ctask->data_count; hdr->flags = 0; } else { hton24(hdr->dlength, ctask->unsol_count); @@ -125,6 +122,7 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) memcpy(hdr->cdb, sc->cmnd, sc->cmd_len); memset(&hdr->cdb[sc->cmd_len], 0, MAX_COMMAND_SIZE - sc->cmd_len); + ctask->data_count = 0; if (sc->sc_data_direction == DMA_TO_DEVICE) { hdr->flags |= ISCSI_FLAG_CMD_WRITE; /* @@ -143,6 +141,7 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) */ ctask->imm_count = 0; ctask->unsol_count = 0; + ctask->unsol_offset = 0; ctask->unsol_datasn = 0; if (session->imm_data_en) { @@ -156,9 +155,12 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) } else zero_data(ctask->hdr->dlength); - if (!session->initial_r2t_en) + if (!session->initial_r2t_en) { ctask->unsol_count = min(session->first_burst, ctask->total_length) - ctask->imm_count; + ctask->unsol_offset = ctask->imm_count; + } + if (!ctask->unsol_count) /* No unsolicit Data-Out's */ ctask->hdr->flags |= ISCSI_FLAG_CMD_FINAL; @@ -177,24 +179,51 @@ EXPORT_SYMBOL_GPL(iscsi_prep_scsi_cmd_pdu); /** * iscsi_complete_command - return command back to scsi-ml - * @session: iscsi session * @ctask: iscsi cmd task * * Must be called with session lock. * This function returns the scsi command to scsi-ml and returns * the cmd task to the pool of available cmd tasks. */ -static void iscsi_complete_command(struct iscsi_session *session, - struct iscsi_cmd_task *ctask) +static void iscsi_complete_command(struct iscsi_cmd_task *ctask) { + struct iscsi_session *session = ctask->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; list_del_init(&ctask->running); __kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*)); sc->scsi_done(sc); } +static void __iscsi_get_ctask(struct iscsi_cmd_task *ctask) +{ + atomic_inc(&ctask->refcount); +} + +static void iscsi_get_ctask(struct iscsi_cmd_task *ctask) +{ + spin_lock_bh(&ctask->conn->session->lock); + __iscsi_get_ctask(ctask); + spin_unlock_bh(&ctask->conn->session->lock); +} + +static void __iscsi_put_ctask(struct iscsi_cmd_task *ctask) +{ + if (atomic_dec_and_test(&ctask->refcount)) + iscsi_complete_command(ctask); +} + +static void iscsi_put_ctask(struct iscsi_cmd_task *ctask) +{ + spin_lock_bh(&ctask->conn->session->lock); + __iscsi_put_ctask(ctask); + spin_unlock_bh(&ctask->conn->session->lock); +} + /** * iscsi_cmd_rsp - SCSI Command Response processing * @conn: iscsi connection @@ -231,7 +260,7 @@ static int iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, } if (rhdr->cmd_status == SAM_STAT_CHECK_CONDITION) { - int senselen; + uint16_t senselen; if (datalen < 2) { invalid_datalen: @@ -241,12 +270,12 @@ invalid_datalen: goto out; } - senselen = (data[0] << 8) | data[1]; + senselen = be16_to_cpu(*(uint16_t *)data); if (datalen < senselen) goto invalid_datalen; memcpy(sc->sense_buffer, data + 2, - min(senselen, SCSI_SENSE_BUFFERSIZE)); + min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); debug_scsi("copied %d bytes of sense\n", min(senselen, SCSI_SENSE_BUFFERSIZE)); } @@ -271,10 +300,53 @@ out: (long)sc, sc->result, ctask->itt); conn->scsirsp_pdus_cnt++; - iscsi_complete_command(conn->session, ctask); + __iscsi_put_ctask(ctask); return rc; } +static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr) +{ + struct iscsi_tm_rsp *tmf = (struct iscsi_tm_rsp *)hdr; + + conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; + conn->tmfrsp_pdus_cnt++; + + if (conn->tmabort_state != TMABORT_INITIAL) + return; + + if (tmf->response == ISCSI_TMF_RSP_COMPLETE) + conn->tmabort_state = TMABORT_SUCCESS; + else if (tmf->response == ISCSI_TMF_RSP_NO_TASK) + conn->tmabort_state = TMABORT_NOT_FOUND; + else + conn->tmabort_state = TMABORT_FAILED; + wake_up(&conn->ehwait); +} + +static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, + char *data, int datalen) +{ + struct iscsi_reject *reject = (struct iscsi_reject *)hdr; + struct iscsi_hdr rejected_pdu; + uint32_t itt; + + conn->exp_statsn = be32_to_cpu(reject->statsn) + 1; + + if (reject->reason == ISCSI_REASON_DATA_DIGEST_ERROR) { + if (ntoh24(reject->dlength) > datalen) + return ISCSI_ERR_PROTO; + + if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) { + memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr)); + itt = rejected_pdu.itt & ISCSI_ITT_MASK; + printk(KERN_ERR "itt 0x%x had pdu (op 0x%x) rejected " + "due to DataDigest error.\n", itt, + rejected_pdu.opcode); + } + } + return 0; +} + /** * __iscsi_complete_pdu - complete pdu * @conn: iscsi conn @@ -316,7 +388,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, BUG_ON((void*)ctask != ctask->sc->SCp.ptr); if (hdr->flags & ISCSI_FLAG_DATA_STATUS) { conn->scsirsp_pdus_cnt++; - iscsi_complete_command(session, ctask); + __iscsi_put_ctask(ctask); } break; case ISCSI_OP_R2T: @@ -340,6 +412,10 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, switch(opcode) { case ISCSI_OP_LOGOUT_RSP: + if (datalen) { + rc = ISCSI_ERR_PROTO; + break; + } conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; /* fall through */ case ISCSI_OP_LOGIN_RSP: @@ -348,7 +424,8 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, * login related PDU's exp_statsn is handled in * userspace */ - rc = iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen); + if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen)) + rc = ISCSI_ERR_CONN_FAILED; list_del(&mtask->running); if (conn->login_mtask != mtask) __kfifo_put(session->mgmtpool.queue, @@ -360,25 +437,17 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, break; } - conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; - conn->tmfrsp_pdus_cnt++; - if (conn->tmabort_state == TMABORT_INITIAL) { - conn->tmabort_state = - ((struct iscsi_tm_rsp *)hdr)-> - response == ISCSI_TMF_RSP_COMPLETE ? - TMABORT_SUCCESS:TMABORT_FAILED; - /* unblock eh_abort() */ - wake_up(&conn->ehwait); - } + iscsi_tmf_rsp(conn, hdr); break; case ISCSI_OP_NOOP_IN: - if (hdr->ttt != ISCSI_RESERVED_TAG) { + if (hdr->ttt != ISCSI_RESERVED_TAG || datalen) { rc = ISCSI_ERR_PROTO; break; } conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; - rc = iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen); + if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen)) + rc = ISCSI_ERR_CONN_FAILED; list_del(&mtask->running); if (conn->login_mtask != mtask) __kfifo_put(session->mgmtpool.queue, @@ -389,23 +458,31 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, break; } } else if (itt == ISCSI_RESERVED_TAG) { + rc = iscsi_check_assign_cmdsn(session, + (struct iscsi_nopin*)hdr); + if (rc) + goto done; + switch(opcode) { case ISCSI_OP_NOOP_IN: - if (!datalen) { - rc = iscsi_check_assign_cmdsn(session, - (struct iscsi_nopin*)hdr); - if (!rc && hdr->ttt != ISCSI_RESERVED_TAG) - rc = iscsi_recv_pdu(conn->cls_conn, - hdr, NULL, 0); - } else + if (datalen) { rc = ISCSI_ERR_PROTO; + break; + } + + if (hdr->ttt == ISCSI_RESERVED_TAG) + break; + + if (iscsi_recv_pdu(conn->cls_conn, hdr, NULL, 0)) + rc = ISCSI_ERR_CONN_FAILED; break; case ISCSI_OP_REJECT: - /* we need sth like iscsi_reject_rsp()*/ + rc = iscsi_handle_reject(conn, hdr, data, datalen); + break; case ISCSI_OP_ASYNC_EVENT: conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; - /* we need sth like iscsi_async_event_rsp() */ - rc = ISCSI_ERR_BAD_OPCODE; + if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen)) + rc = ISCSI_ERR_CONN_FAILED; break; default: rc = ISCSI_ERR_BAD_OPCODE; @@ -501,6 +578,27 @@ void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err) } EXPORT_SYMBOL_GPL(iscsi_conn_failure); +static int iscsi_xmit_imm_task(struct iscsi_conn *conn) +{ + struct iscsi_hdr *hdr = conn->mtask->hdr; + int rc, was_logout = 0; + + if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT) { + conn->session->state = ISCSI_STATE_IN_RECOVERY; + iscsi_block_session(session_to_cls(conn->session)); + was_logout = 1; + } + rc = conn->session->tt->xmit_mgmt_task(conn, conn->mtask); + if (rc) + return rc; + + if (was_logout) { + set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); + return -ENODATA; + } + return 0; +} + /** * iscsi_data_xmit - xmit any command into the scheduled connection * @conn: iscsi connection @@ -537,14 +635,16 @@ static int iscsi_data_xmit(struct iscsi_conn *conn) BUG_ON(conn->ctask && conn->mtask); if (conn->ctask) { + iscsi_get_ctask(conn->ctask); rc = tt->xmit_cmd_task(conn, conn->ctask); + iscsi_put_ctask(conn->ctask); if (rc) goto again; /* done with this in-progress ctask */ conn->ctask = NULL; } if (conn->mtask) { - rc = tt->xmit_mgmt_task(conn, conn->mtask); + rc = iscsi_xmit_imm_task(conn); if (rc) goto again; /* done with this in-progress mtask */ @@ -559,7 +659,7 @@ static int iscsi_data_xmit(struct iscsi_conn *conn) list_add_tail(&conn->mtask->running, &conn->mgmt_run_list); spin_unlock_bh(&conn->session->lock); - rc = tt->xmit_mgmt_task(conn, conn->mtask); + rc = iscsi_xmit_imm_task(conn); if (rc) goto again; } @@ -568,20 +668,29 @@ static int iscsi_data_xmit(struct iscsi_conn *conn) } /* process command queue */ - while (__kfifo_get(conn->xmitqueue, (void*)&conn->ctask, - sizeof(void*))) { + spin_lock_bh(&conn->session->lock); + while (!list_empty(&conn->xmitqueue)) { /* * iscsi tcp may readd the task to the xmitqueue to send * write data */ - spin_lock_bh(&conn->session->lock); - if (list_empty(&conn->ctask->running)) - list_add_tail(&conn->ctask->running, &conn->run_list); + conn->ctask = list_entry(conn->xmitqueue.next, + struct iscsi_cmd_task, running); + conn->ctask->state = ISCSI_TASK_RUNNING; + list_move_tail(conn->xmitqueue.next, &conn->run_list); + __iscsi_get_ctask(conn->ctask); spin_unlock_bh(&conn->session->lock); + rc = tt->xmit_cmd_task(conn, conn->ctask); - if (rc) + + spin_lock_bh(&conn->session->lock); + __iscsi_put_ctask(conn->ctask); + if (rc) { + spin_unlock_bh(&conn->session->lock); goto again; + } } + spin_unlock_bh(&conn->session->lock); /* done with this ctask */ conn->ctask = NULL; @@ -610,9 +719,10 @@ again: return rc; } -static void iscsi_xmitworker(void *data) +static void iscsi_xmitworker(struct work_struct *work) { - struct iscsi_conn *conn = data; + struct iscsi_conn *conn = + container_of(work, struct iscsi_conn, xmitwork); int rc; /* * serialize Xmit worker on a per-connection basis. @@ -629,6 +739,7 @@ enum { FAILURE_SESSION_FAILED, FAILURE_SESSION_FREED, FAILURE_WINDOW_CLOSED, + FAILURE_OOM, FAILURE_SESSION_TERMINATE, FAILURE_SESSION_IN_RECOVERY, FAILURE_SESSION_RECOVERY_TIMEOUT, @@ -644,6 +755,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) sc->scsi_done = done; sc->result = 0; + sc->SCp.ptr = NULL; host = sc->device->host; session = iscsi_hostdata(host->hostdata); @@ -686,11 +798,21 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) } conn = session->leadconn; + if (!conn) { + reason = FAILURE_SESSION_FREED; + goto fault; + } - __kfifo_get(session->cmdpool.queue, (void*)&ctask, sizeof(void*)); + if (!__kfifo_get(session->cmdpool.queue, (void*)&ctask, + sizeof(void*))) { + reason = FAILURE_OOM; + goto reject; + } sc->SCp.phase = session->age; sc->SCp.ptr = (char *)ctask; + atomic_set(&ctask->refcount, 1); + ctask->state = ISCSI_TASK_PENDING; ctask->mtask = NULL; ctask->conn = conn; ctask->sc = sc; @@ -700,11 +822,12 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) session->tt->init_cmd_task(ctask); - __kfifo_put(conn->xmitqueue, (void*)&ctask, sizeof(void*)); + list_add_tail(&ctask->running, &conn->xmitqueue); debug_scsi( - "ctask enq [%s cid %d sc %lx itt 0x%x len %d cmdsn %d win %d]\n", + "ctask enq [%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", - conn->id, (long)sc, ctask->itt, sc->request_bufflen, + conn->id, sc, sc->cmnd[0], ctask->itt, sc->request_bufflen, session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1); spin_unlock(&session->lock); @@ -853,13 +976,13 @@ int iscsi_eh_host_reset(struct scsi_cmnd *sc) if (session->state == ISCSI_STATE_TERMINATE) { failed: debug_scsi("failing host reset: session terminated " - "[CID %d age %d]", conn->id, session->age); + "[CID %d age %d]\n", conn->id, session->age); spin_unlock_bh(&session->lock); return FAILED; } if (sc->SCp.phase == session->age) { - debug_scsi("failing connection CID %d due to SCSI host reset", + debug_scsi("failing connection CID %d due to SCSI host reset\n", conn->id); fail_session = 1; } @@ -932,7 +1055,8 @@ static int iscsi_exec_abort_task(struct scsi_cmnd *sc, NULL, 0); if (rc) { iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); - debug_scsi("abort sent failure [itt 0x%x] %d", ctask->itt, rc); + debug_scsi("abort sent failure [itt 0x%x] %d\n", ctask->itt, + rc); return rc; } @@ -949,7 +1073,7 @@ static int iscsi_exec_abort_task(struct scsi_cmnd *sc, conn->tmabort_timer.function = iscsi_tmabort_timedout; conn->tmabort_timer.data = (unsigned long)ctask; add_timer(&conn->tmabort_timer); - debug_scsi("abort set timeout [itt 0x%x]", ctask->itt); + debug_scsi("abort set timeout [itt 0x%x]\n", ctask->itt); } spin_unlock_bh(&session->lock); mutex_unlock(&conn->xmitmutex); @@ -977,31 +1101,27 @@ static int iscsi_exec_abort_task(struct scsi_cmnd *sc, /* * xmit mutex and session lock must be held */ -#define iscsi_remove_task(tasktype) \ -static struct iscsi_##tasktype * \ -iscsi_remove_##tasktype(struct kfifo *fifo, uint32_t itt) \ -{ \ - int i, nr_tasks = __kfifo_len(fifo) / sizeof(void*); \ - struct iscsi_##tasktype *task; \ - \ - debug_scsi("searching %d tasks\n", nr_tasks); \ - \ - for (i = 0; i < nr_tasks; i++) { \ - __kfifo_get(fifo, (void*)&task, sizeof(void*)); \ - debug_scsi("check task %u\n", task->itt); \ - \ - if (task->itt == itt) { \ - debug_scsi("matched task\n"); \ - return task; \ - } \ - \ - __kfifo_put(fifo, (void*)&task, sizeof(void*)); \ - } \ - return NULL; \ -} +static struct iscsi_mgmt_task * +iscsi_remove_mgmt_task(struct kfifo *fifo, uint32_t itt) +{ + int i, nr_tasks = __kfifo_len(fifo) / sizeof(void*); + struct iscsi_mgmt_task *task; -iscsi_remove_task(mgmt_task); -iscsi_remove_task(cmd_task); + debug_scsi("searching %d tasks\n", nr_tasks); + + for (i = 0; i < nr_tasks; i++) { + __kfifo_get(fifo, (void*)&task, sizeof(void*)); + debug_scsi("check task %u\n", task->itt); + + if (task->itt == itt) { + debug_scsi("matched task\n"); + return task; + } + + __kfifo_put(fifo, (void*)&task, sizeof(void*)); + } + return NULL; +} static int iscsi_ctask_mtask_cleanup(struct iscsi_cmd_task *ctask) { @@ -1027,25 +1147,39 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, { struct scsi_cmnd *sc; - conn->session->tt->cleanup_cmd_task(conn, ctask); - iscsi_ctask_mtask_cleanup(ctask); - sc = ctask->sc; if (!sc) return; + + conn->session->tt->cleanup_cmd_task(conn, ctask); + iscsi_ctask_mtask_cleanup(ctask); + sc->result = err; sc->resid = sc->request_bufflen; - iscsi_complete_command(conn->session, ctask); + /* release ref from queuecommand */ + __iscsi_put_ctask(ctask); } int iscsi_eh_abort(struct scsi_cmnd *sc) { - struct iscsi_cmd_task *ctask = (struct iscsi_cmd_task *)sc->SCp.ptr; - struct iscsi_conn *conn = ctask->conn; - struct iscsi_session *session = conn->session; - struct iscsi_cmd_task *pending_ctask; + struct iscsi_cmd_task *ctask; + struct iscsi_conn *conn; + struct iscsi_session *session; int rc; + /* + * if session was ISCSI_STATE_IN_RECOVERY then we may not have + * got the command. + */ + if (!sc->SCp.ptr) { + debug_scsi("sc never reached iscsi layer or it completed.\n"); + return SUCCESS; + } + + ctask = (struct iscsi_cmd_task *)sc->SCp.ptr; + conn = ctask->conn; + session = conn->session; + conn->eh_abort_cnt++; debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt); @@ -1061,8 +1195,11 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) goto failed; /* ctask completed before time out */ - if (!ctask->sc) - goto success; + if (!ctask->sc) { + spin_unlock_bh(&session->lock); + debug_scsi("sc completed while abort in progress\n"); + goto success_rel_mutex; + } /* what should we do here ? */ if (conn->ctask == ctask) { @@ -1071,17 +1208,8 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) goto failed; } - /* check for the easy pending cmd abort */ - pending_ctask = iscsi_remove_cmd_task(conn->xmitqueue, ctask->itt); - if (pending_ctask) { - /* iscsi_tcp queues write transfers on the xmitqueue */ - if (list_empty(&pending_ctask->running)) { - debug_scsi("found pending task\n"); - goto success; - } else - __kfifo_put(conn->xmitqueue, (void*)&pending_ctask, - sizeof(void*)); - } + if (ctask->state == ISCSI_TASK_PENDING) + goto success_cleanup; conn->tmabort_state = TMABORT_INITIAL; @@ -1089,25 +1217,31 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) rc = iscsi_exec_abort_task(sc, ctask); spin_lock_bh(&session->lock); - iscsi_ctask_mtask_cleanup(ctask); if (rc || sc->SCp.phase != session->age || session->state != ISCSI_STATE_LOGGED_IN) goto failed; + iscsi_ctask_mtask_cleanup(ctask); - /* ctask completed before tmf abort response */ - if (!ctask->sc) { - debug_scsi("sc completed while abort in progress\n"); - goto success; - } - - if (conn->tmabort_state != TMABORT_SUCCESS) { + switch (conn->tmabort_state) { + case TMABORT_SUCCESS: + goto success_cleanup; + case TMABORT_NOT_FOUND: + if (!ctask->sc) { + /* ctask completed before tmf abort response */ + spin_unlock_bh(&session->lock); + debug_scsi("sc completed while abort in progress\n"); + goto success_rel_mutex; + } + /* fall through */ + default: + /* timedout or failed */ spin_unlock_bh(&session->lock); iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); spin_lock_bh(&session->lock); goto failed; } -success: +success_cleanup: debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt); spin_unlock_bh(&session->lock); @@ -1121,6 +1255,7 @@ success: spin_unlock(&session->lock); write_unlock_bh(conn->recv_lock); +success_rel_mutex: mutex_unlock(&conn->xmitmutex); return SUCCESS; @@ -1263,10 +1398,10 @@ iscsi_session_setup(struct iscsi_transport *iscsit, if (cmd_task_size) ctask->dd_data = &ctask[1]; ctask->itt = cmd_i; + INIT_LIST_HEAD(&ctask->running); } spin_lock_init(&session->lock); - INIT_LIST_HEAD(&session->connections); /* initialize immediate command pool */ if (iscsi_pool_init(&session->mgmtpool, session->mgmtpool_max, @@ -1282,18 +1417,24 @@ iscsi_session_setup(struct iscsi_transport *iscsit, if (mgmt_task_size) mtask->dd_data = &mtask[1]; mtask->itt = ISCSI_MGMT_ITT_OFFSET + cmd_i; + INIT_LIST_HEAD(&mtask->running); } if (scsi_add_host(shost, NULL)) goto add_host_fail; + if (!try_module_get(iscsit->owner)) + goto cls_session_fail; + cls_session = iscsi_create_session(shost, iscsit, 0); if (!cls_session) - goto cls_session_fail; + goto module_put; *(unsigned long*)shost->hostdata = (unsigned long)cls_session; return cls_session; +module_put: + module_put(iscsit->owner); cls_session_fail: scsi_remove_host(shost); add_host_fail: @@ -1317,14 +1458,18 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session) { struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); struct iscsi_session *session = iscsi_hostdata(shost->hostdata); + struct module *owner = cls_session->transport->owner; scsi_remove_host(shost); iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds); iscsi_pool_free(&session->cmdpool, (void**)session->cmds); + kfree(session->targetname); + iscsi_destroy_session(cls_session); scsi_host_put(shost); + module_put(owner); } EXPORT_SYMBOL_GPL(iscsi_session_teardown); @@ -1355,12 +1500,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx) conn->tmabort_state = TMABORT_INITIAL; INIT_LIST_HEAD(&conn->run_list); INIT_LIST_HEAD(&conn->mgmt_run_list); - - /* initialize general xmit PDU commands queue */ - conn->xmitqueue = kfifo_alloc(session->cmds_max * sizeof(void*), - GFP_KERNEL, NULL); - if (conn->xmitqueue == ERR_PTR(-ENOMEM)) - goto xmitqueue_alloc_fail; + INIT_LIST_HEAD(&conn->xmitqueue); /* initialize general immediate & non-immediate PDU commands queue */ conn->immqueue = kfifo_alloc(session->mgmtpool_max * sizeof(void*), @@ -1373,7 +1513,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx) if (conn->mgmtqueue == ERR_PTR(-ENOMEM)) goto mgmtqueue_alloc_fail; - INIT_WORK(&conn->xmitwork, iscsi_xmitworker, conn); + INIT_WORK(&conn->xmitwork, iscsi_xmitworker); /* allocate login_mtask used for the login/text sequences */ spin_lock_bh(&session->lock); @@ -1388,7 +1528,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx) data = kmalloc(DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH, GFP_KERNEL); if (!data) goto login_mtask_data_alloc_fail; - conn->login_mtask->data = data; + conn->login_mtask->data = conn->data = data; init_timer(&conn->tmabort_timer); mutex_init(&conn->xmitmutex); @@ -1404,8 +1544,6 @@ login_mtask_alloc_fail: mgmtqueue_alloc_fail: kfifo_free(conn->immqueue); immqueue_alloc_fail: - kfifo_free(conn->xmitqueue); -xmitqueue_alloc_fail: iscsi_destroy_conn(cls_conn); return NULL; } @@ -1426,12 +1564,6 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); mutex_lock(&conn->xmitmutex); - if (conn->c_stage == ISCSI_CONN_INITIAL_STAGE) { - if (session->tt->suspend_conn_recv) - session->tt->suspend_conn_recv(conn); - - session->tt->terminate_conn(conn); - } spin_lock_bh(&session->lock); conn->c_stage = ISCSI_CONN_CLEANUP_WAIT; @@ -1468,22 +1600,17 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) } spin_lock_bh(&session->lock); - kfree(conn->login_mtask->data); + kfree(conn->data); + kfree(conn->persistent_address); __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask, sizeof(void*)); - list_del(&conn->item); - if (list_empty(&session->connections)) + if (session->leadconn == conn) { session->leadconn = NULL; - if (session->leadconn && session->leadconn == conn) - session->leadconn = container_of(session->connections.next, - struct iscsi_conn, item); - - if (session->leadconn == NULL) /* no connections exits.. reset sequencing */ session->cmdsn = session->max_cmdsn = session->exp_cmdsn = 1; + } spin_unlock_bh(&session->lock); - kfifo_free(conn->xmitqueue); kfifo_free(conn->immqueue); kfifo_free(conn->mgmtqueue); @@ -1496,11 +1623,19 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_session *session = conn->session; - if (session == NULL) { + if (!session) { printk(KERN_ERR "iscsi: can't start unbound connection\n"); return -EPERM; } + if ((session->imm_data_en || !session->initial_r2t_en) && + session->first_burst > session->max_burst) { + printk("iscsi: invalid burst lengths: " + "first_burst %d max_burst %d\n", + session->first_burst, session->max_burst); + return -EINVAL; + } + spin_lock_bh(&session->lock); conn->c_stage = ISCSI_CONN_STARTED; session->state = ISCSI_STATE_LOGGED_IN; @@ -1566,7 +1701,7 @@ static void fail_all_commands(struct iscsi_conn *conn) struct iscsi_cmd_task *ctask, *tmp; /* flush pending */ - while (__kfifo_get(conn->xmitqueue, (void*)&ctask, sizeof(void*))) { + list_for_each_entry_safe(ctask, tmp, &conn->xmitqueue, running) { debug_scsi("failing pending sc %p itt 0x%x\n", ctask->sc, ctask->itt); fail_command(conn, ctask, DID_BUS_BUSY << 16); @@ -1609,8 +1744,9 @@ static void iscsi_start_session_recovery(struct iscsi_session *session, set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); spin_unlock_bh(&session->lock); - if (session->tt->suspend_conn_recv) - session->tt->suspend_conn_recv(conn); + write_lock_bh(conn->recv_lock); + set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); + write_unlock_bh(conn->recv_lock); mutex_lock(&conn->xmitmutex); /* @@ -1629,7 +1765,6 @@ static void iscsi_start_session_recovery(struct iscsi_session *session, } } - session->tt->terminate_conn(conn); /* * flush queues. */ @@ -1661,32 +1796,12 @@ int iscsi_conn_bind(struct iscsi_cls_session *cls_session, struct iscsi_cls_conn *cls_conn, int is_leading) { struct iscsi_session *session = class_to_transport_session(cls_session); - struct iscsi_conn *tmp = ERR_PTR(-EEXIST), *conn = cls_conn->dd_data; + struct iscsi_conn *conn = cls_conn->dd_data; - /* lookup for existing connection */ spin_lock_bh(&session->lock); - list_for_each_entry(tmp, &session->connections, item) { - if (tmp == conn) { - if (conn->c_stage != ISCSI_CONN_STOPPED || - conn->stop_stage == STOP_CONN_TERM) { - printk(KERN_ERR "iscsi: can't bind " - "non-stopped connection (%d:%d)\n", - conn->c_stage, conn->stop_stage); - spin_unlock_bh(&session->lock); - return -EIO; - } - break; - } - } - if (tmp != conn) { - /* bind new iSCSI connection to session */ - conn->session = session; - list_add(&conn->item, &session->connections); - } - spin_unlock_bh(&session->lock); - if (is_leading) session->leadconn = conn; + spin_unlock_bh(&session->lock); /* * Unblock xmitworker(), Login Phase will pass through. @@ -1697,6 +1812,185 @@ int iscsi_conn_bind(struct iscsi_cls_session *cls_session, } EXPORT_SYMBOL_GPL(iscsi_conn_bind); + +int iscsi_set_param(struct iscsi_cls_conn *cls_conn, + enum iscsi_param param, char *buf, int buflen) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + struct iscsi_session *session = conn->session; + uint32_t value; + + switch(param) { + case ISCSI_PARAM_MAX_RECV_DLENGTH: + sscanf(buf, "%d", &conn->max_recv_dlength); + break; + case ISCSI_PARAM_MAX_XMIT_DLENGTH: + sscanf(buf, "%d", &conn->max_xmit_dlength); + break; + case ISCSI_PARAM_HDRDGST_EN: + sscanf(buf, "%d", &conn->hdrdgst_en); + break; + case ISCSI_PARAM_DATADGST_EN: + sscanf(buf, "%d", &conn->datadgst_en); + break; + case ISCSI_PARAM_INITIAL_R2T_EN: + sscanf(buf, "%d", &session->initial_r2t_en); + break; + case ISCSI_PARAM_MAX_R2T: + sscanf(buf, "%d", &session->max_r2t); + break; + case ISCSI_PARAM_IMM_DATA_EN: + sscanf(buf, "%d", &session->imm_data_en); + break; + case ISCSI_PARAM_FIRST_BURST: + sscanf(buf, "%d", &session->first_burst); + break; + case ISCSI_PARAM_MAX_BURST: + sscanf(buf, "%d", &session->max_burst); + break; + case ISCSI_PARAM_PDU_INORDER_EN: + sscanf(buf, "%d", &session->pdu_inorder_en); + break; + case ISCSI_PARAM_DATASEQ_INORDER_EN: + sscanf(buf, "%d", &session->dataseq_inorder_en); + break; + case ISCSI_PARAM_ERL: + sscanf(buf, "%d", &session->erl); + break; + case ISCSI_PARAM_IFMARKER_EN: + sscanf(buf, "%d", &value); + BUG_ON(value); + break; + case ISCSI_PARAM_OFMARKER_EN: + sscanf(buf, "%d", &value); + BUG_ON(value); + break; + case ISCSI_PARAM_EXP_STATSN: + sscanf(buf, "%u", &conn->exp_statsn); + break; + case ISCSI_PARAM_TARGET_NAME: + /* this should not change between logins */ + if (session->targetname) + break; + + session->targetname = kstrdup(buf, GFP_KERNEL); + if (!session->targetname) + return -ENOMEM; + break; + case ISCSI_PARAM_TPGT: + sscanf(buf, "%d", &session->tpgt); + break; + case ISCSI_PARAM_PERSISTENT_PORT: + sscanf(buf, "%d", &conn->persistent_port); + break; + case ISCSI_PARAM_PERSISTENT_ADDRESS: + /* + * this is the address returned in discovery so it should + * not change between logins. + */ + if (conn->persistent_address) + break; + + conn->persistent_address = kstrdup(buf, GFP_KERNEL); + if (!conn->persistent_address) + return -ENOMEM; + break; + default: + return -ENOSYS; + } + + return 0; +} +EXPORT_SYMBOL_GPL(iscsi_set_param); + +int iscsi_session_get_param(struct iscsi_cls_session *cls_session, + enum iscsi_param param, char *buf) +{ + struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); + struct iscsi_session *session = iscsi_hostdata(shost->hostdata); + int len; + + switch(param) { + case ISCSI_PARAM_INITIAL_R2T_EN: + len = sprintf(buf, "%d\n", session->initial_r2t_en); + break; + case ISCSI_PARAM_MAX_R2T: + len = sprintf(buf, "%hu\n", session->max_r2t); + break; + case ISCSI_PARAM_IMM_DATA_EN: + len = sprintf(buf, "%d\n", session->imm_data_en); + break; + case ISCSI_PARAM_FIRST_BURST: + len = sprintf(buf, "%u\n", session->first_burst); + break; + case ISCSI_PARAM_MAX_BURST: + len = sprintf(buf, "%u\n", session->max_burst); + break; + case ISCSI_PARAM_PDU_INORDER_EN: + len = sprintf(buf, "%d\n", session->pdu_inorder_en); + break; + case ISCSI_PARAM_DATASEQ_INORDER_EN: + len = sprintf(buf, "%d\n", session->dataseq_inorder_en); + break; + case ISCSI_PARAM_ERL: + len = sprintf(buf, "%d\n", session->erl); + break; + case ISCSI_PARAM_TARGET_NAME: + len = sprintf(buf, "%s\n", session->targetname); + break; + case ISCSI_PARAM_TPGT: + len = sprintf(buf, "%d\n", session->tpgt); + break; + default: + return -ENOSYS; + } + + return len; +} +EXPORT_SYMBOL_GPL(iscsi_session_get_param); + +int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, + enum iscsi_param param, char *buf) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + int len; + + switch(param) { + case ISCSI_PARAM_MAX_RECV_DLENGTH: + len = sprintf(buf, "%u\n", conn->max_recv_dlength); + break; + case ISCSI_PARAM_MAX_XMIT_DLENGTH: + len = sprintf(buf, "%u\n", conn->max_xmit_dlength); + break; + case ISCSI_PARAM_HDRDGST_EN: + len = sprintf(buf, "%d\n", conn->hdrdgst_en); + break; + case ISCSI_PARAM_DATADGST_EN: + len = sprintf(buf, "%d\n", conn->datadgst_en); + break; + case ISCSI_PARAM_IFMARKER_EN: + len = sprintf(buf, "%d\n", conn->ifmarker_en); + break; + case ISCSI_PARAM_OFMARKER_EN: + len = sprintf(buf, "%d\n", conn->ofmarker_en); + break; + case ISCSI_PARAM_EXP_STATSN: + len = sprintf(buf, "%u\n", conn->exp_statsn); + break; + case ISCSI_PARAM_PERSISTENT_PORT: + len = sprintf(buf, "%d\n", conn->persistent_port); + break; + case ISCSI_PARAM_PERSISTENT_ADDRESS: + len = sprintf(buf, "%s\n", conn->persistent_address); + break; + default: + return -ENOSYS; + } + + return len; +} +EXPORT_SYMBOL_GPL(iscsi_conn_get_param); + MODULE_AUTHOR("Mike Christie"); MODULE_DESCRIPTION("iSCSI library functions"); MODULE_LICENSE("GPL");