From: <vl...@us...> - 2007-12-29 17:04:19
|
Revision: 240 http://scst.svn.sourceforge.net/scst/?rev=240&view=rev Author: vlnb Date: 2007-12-29 09:04:13 -0800 (Sat, 29 Dec 2007) Log Message: ----------- - Fixed huge iSCSI RFC violation, inherited from IET: incorrect commands ordering. - TM fixes related to new commands serialization - Other TM improvements - Minor changes: likely/unlikely and priority of mgmt threads Work not fully completed, still there are some rare issues. Modified Paths: -------------- trunk/iscsi-scst/kernel/conn.c trunk/iscsi-scst/kernel/digest.c trunk/iscsi-scst/kernel/iscsi.c trunk/iscsi-scst/kernel/iscsi.h trunk/iscsi-scst/kernel/nthread.c trunk/qla2x00t/qla_os.c trunk/scst/src/scst_targ.c Modified: trunk/iscsi-scst/kernel/conn.c =================================================================== --- trunk/iscsi-scst/kernel/conn.c 2007-12-24 19:22:02 UTC (rev 239) +++ trunk/iscsi-scst/kernel/conn.c 2007-12-29 17:04:13 UTC (rev 240) @@ -173,7 +173,7 @@ TRACE_ENTRY(); - if (sk->sk_state != TCP_ESTABLISHED) { + if (unlikely(sk->sk_state != TCP_ESTABLISHED)) { if (!conn->closing) { PRINT_ERROR("Connection with initiator %s (%p) " "unexpectedly closed!", @@ -392,3 +392,33 @@ return 0; } + +#ifdef EXTRACHECKS + +void iscsi_extracheck_is_rd_thread(struct iscsi_conn *conn) +{ + if (unlikely(current != conn->rd_task)) { + while(in_softirq()) + local_bh_enable(); + printk(KERN_EMERG "conn %p rd_task != current (%p)", conn, current); + printk(KERN_EMERG "rd_task %p", conn->rd_task); + printk(KERN_EMERG "current->pid %d, rd_task->pid %d", current->pid, + conn->rd_task->pid); + sBUG(); + } +} + +void iscsi_extracheck_is_wr_thread(struct iscsi_conn *conn) +{ + if (unlikely(current != conn->wr_task)) { + while(in_softirq()) + local_bh_enable(); + printk(KERN_EMERG "conn %p wr_task != current (%p)", conn, current); + printk(KERN_EMERG "wr_task %p", conn->wr_task); + printk(KERN_EMERG "current->pid %d, wr_task->pid %d", current->pid, + conn->wr_task->pid); + sBUG(); + } +} + +#endif /* EXTRACHECKS */ Modified: trunk/iscsi-scst/kernel/digest.c =================================================================== --- trunk/iscsi-scst/kernel/digest.c 2007-12-24 19:22:02 UTC (rev 239) +++ trunk/iscsi-scst/kernel/digest.c 2007-12-29 17:04:13 UTC (rev 240) @@ -142,7 +142,7 @@ u32 crc; crc = digest_header(&cmnd->pdu); - if (crc != cmnd->hdigest) { + if (unlikely(crc != cmnd->hdigest)) { PRINT_ERROR("%s", "RX header digest failed"); return -EIO; } else @@ -183,7 +183,7 @@ crc = digest_data(req, cmnd->pdu.datasize, offset); - if (crc != cmnd->ddigest) { + if (unlikely(crc != cmnd->ddigest)) { PRINT_ERROR("%s", "RX data digest failed"); res = -EIO; } else Modified: trunk/iscsi-scst/kernel/iscsi.c =================================================================== --- trunk/iscsi-scst/kernel/iscsi.c 2007-12-24 19:22:02 UTC (rev 239) +++ trunk/iscsi-scst/kernel/iscsi.c 2007-12-29 17:04:13 UTC (rev 240) @@ -63,6 +63,7 @@ static void iscsi_send_task_mgmt_resp(struct iscsi_cmnd *req, int status); static void cmnd_prepare_skip_pdu(struct iscsi_cmnd *cmnd); static void iscsi_check_send_delayed_tm_resp(struct iscsi_session *sess); +static void iscsi_session_push_cmnd(struct iscsi_cmnd *cmnd); static inline u32 cmnd_write_size(struct iscsi_cmnd *cmnd) { @@ -152,7 +153,7 @@ be32_to_cpu(req->data_length), req->cmd_sn, be32_to_cpu(cmnd->pdu.datasize)); - if (cmnd->parent_req) { + if (unlikely(cmnd->parent_req)) { struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd->parent_req); PRINT_ERROR("%p %x %u", req, req->opcode, req->scb[0]); @@ -169,8 +170,9 @@ TRACE_DBG("%p", cmnd); if (unlikely(cmnd->tmfabort)) { - TRACE_MGMT_DBG("Done aborted cmd %p (scst cmd %p, state %d)", - cmnd, cmnd->scst_cmd, cmnd->scst_state); + TRACE_MGMT_DBG("Done aborted cmd %p (scst cmd %p, state %d, " + "parent_req %p)", cmnd, cmnd->scst_cmd, cmnd->scst_state, + cmnd->parent_req); } if (cmnd->parent_req == NULL) { @@ -245,56 +247,54 @@ /* * Corresponding conn may also gets destroyed atfer this function, except only - * if it's called from the read thread! + * if it's called from the read thread! + * + * It can't be called in parallel with iscsi_cmnds_init_write()! */ void req_cmnd_release_force(struct iscsi_cmnd *req, int flags) { - struct iscsi_cmnd *rsp; + struct iscsi_cmnd *rsp, *t; struct iscsi_conn *conn = req->conn; + LIST_HEAD(cmds_list); TRACE_ENTRY(); TRACE_DBG("%p", req); if (flags & ISCSI_FORCE_RELEASE_WRITE) { -again_write: spin_lock(&conn->write_list_lock); - list_for_each_entry(rsp, &conn->write_list, write_list_entry) { + list_for_each_entry_safe(rsp, t, &conn->write_list, + write_list_entry) { if (rsp->parent_req != req) continue; cmd_del_from_write_list(rsp); - spin_unlock(&conn->write_list_lock); + list_add_tail(&rsp->write_list_entry, &cmds_list); + } + spin_unlock(&conn->write_list_lock); + list_for_each_entry_safe(rsp, t, &cmds_list, write_list_entry) { + list_del(&rsp->write_list_entry); cmnd_put(rsp); - goto again_write; } - spin_unlock(&conn->write_list_lock); } again_rsp: spin_lock_bh(&req->rsp_cmd_lock); - list_for_each_entry(rsp, &req->rsp_cmd_list, rsp_cmd_list_entry) { - int f; - + list_for_each_entry_reverse(rsp, &req->rsp_cmd_list, rsp_cmd_list_entry) { + /* + * It's OK to check not under write_list_lock, since + * iscsi_cmnds_init_write() can't be called in parallel with us + * and once on_write_list or write_processing_started get set, + * rsp gets out of this function responsibility. + */ if (rsp->on_write_list || rsp->write_processing_started || rsp->force_cleanup_done) continue; spin_unlock_bh(&req->rsp_cmd_lock); - /* - * Recheck is necessary to not take write_list_lock under - * rsp_cmd_lock. - */ - spin_lock(&conn->write_list_lock); - f = rsp->on_write_list || rsp->write_processing_started || - rsp->force_cleanup_done; - spin_unlock(&conn->write_list_lock); - if (f) - goto again_rsp; - rsp->force_cleanup_done = 1; cmnd_put(rsp); @@ -374,13 +374,6 @@ sBUG_ON(cmnd->hashed); sBUG_ON(cmnd->parent_req == NULL); - if (unlikely(cmnd->tmfabort)) { - TRACE_MGMT_DBG("Release aborted rsp cmd %p (parent req %p, " - "scst cmd %p, state %d)", cmnd, cmnd->parent_req, - cmnd->parent_req->scst_cmd, - cmnd->parent_req->scst_state); - } - cmnd_put(cmnd); return; } @@ -453,7 +446,6 @@ cmd_add_on_write_list(conn, rsp); spin_unlock(&conn->write_list_lock); } - if (flags & ISCSI_INIT_WRITE_WAKE) iscsi_make_conn_wr_active(conn); @@ -707,7 +699,7 @@ cmnd->pdu.bhs.sn = cmd_sn = be32_to_cpu(cmnd->pdu.bhs.sn); TRACE_DBG("%d(%d)", cmd_sn, session->exp_cmd_sn); - if ((s32)(cmd_sn - session->exp_cmd_sn) >= 0) + if (likely((s32)(cmd_sn - session->exp_cmd_sn) >= 0)) return 0; PRINT_ERROR("sequence error (%x,%x)", cmd_sn, session->exp_cmd_sn); return -ISCSI_REASON_PROTOCOL_ERROR; @@ -766,7 +758,7 @@ u32 itt = cmnd->pdu.bhs.itt; TRACE_DBG("%p:%x", cmnd, itt); - if (itt == ISCSI_RESERVED_TAG) { + if (unlikely(itt == ISCSI_RESERVED_TAG)) { PRINT_ERROR("%s", "ITT is RESERVED_TAG"); err = -ISCSI_REASON_PROTOCOL_ERROR; goto out; @@ -777,7 +769,7 @@ head = &session->cmnd_hash[cmnd_hashfn(cmnd->pdu.bhs.itt)]; tmp = __cmnd_find_hash(session, itt, ISCSI_RESERVED_TAG); - if (!tmp) { + if (likely(!tmp)) { list_add_tail(&cmnd->hash_list_entry, head); cmnd->hashed = 1; } else { @@ -787,7 +779,7 @@ spin_unlock(&session->cmnd_hash_lock); - if (!err) { + if (likely(!err)) { spin_lock(&session->sn_lock); __update_stat_sn(cmnd); err = check_cmd_sn(cmnd); @@ -807,7 +799,7 @@ tmp = __cmnd_find_hash(session, cmnd->pdu.bhs.itt, ISCSI_RESERVED_TAG); - if (tmp && tmp == cmnd) { + if (likely(tmp && tmp == cmnd)) { list_del(&cmnd->hash_list_entry); cmnd->hashed = 0; } else { @@ -870,8 +862,8 @@ rsp = get_rsp_cmnd(req); rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs; - if (cmnd_opcode(rsp) != ISCSI_OP_SCSI_RSP) { - PRINT_ERROR("unexpected response command %u", cmnd_opcode(rsp)); + if (unlikely(cmnd_opcode(rsp) != ISCSI_OP_SCSI_RSP)) { + PRINT_ERROR("Unexpected response command %u", cmnd_opcode(rsp)); return; } @@ -909,8 +901,8 @@ iscsi_extracheck_is_rd_thread(conn); - if ((offset >= bufflen) || - (offset + size > bufflen)) { + if (unlikely((offset >= bufflen) || + (offset + size > bufflen))) { PRINT_ERROR("Wrong ltn (%u %u %u)", offset, size, bufflen); mark_conn_closed(conn); res = -EIO; @@ -942,7 +934,7 @@ idx, offset, size, conn->read_iov[i].iov_len, addr); size -= conn->read_iov[i].iov_len; offset = 0; - if (++i >= ISCSI_CONN_IOV_MAX) { + if (unlikely(++i >= ISCSI_CONN_IOV_MAX)) { PRINT_ERROR("Initiator %s violated negotiated " "parameters by sending too much data (size " "left %d)", conn->session->initiator_name, size); @@ -964,13 +956,15 @@ struct iscsi_session *session = req->conn->session; struct iscsi_cmnd *rsp; struct iscsi_r2t_hdr *rsp_hdr; - u32 length, offset, burst; + u32 offset, burst; LIST_HEAD(send); if (unlikely(req->tmfabort)) { - TRACE_MGMT_DBG("req %p (scst_cmd %p) aborted on R2T", req, - req->scst_cmd); - req_cmnd_release_force(req, ISCSI_FORCE_RELEASE_WRITE); + TRACE_MGMT_DBG("req %p (scst_cmd %p) aborted on R2T " + "(r2t_length %d, outstanding_r2t %d)", req, + req->scst_cmd, req->r2t_length, req->outstanding_r2t); + if (req->outstanding_r2t == 0) + iscsi_session_push_cmnd(req); goto out; } @@ -980,9 +974,8 @@ */ iscsi_extracheck_is_rd_thread(req->conn); - length = req->r2t_length; burst = session->sess_param.max_burst_length; - offset = be32_to_cpu(cmnd_hdr(req)->data_length) - length; + offset = be32_to_cpu(cmnd_hdr(req)->data_length) - req->r2t_length; do { rsp = iscsi_cmnd_create_rsp_cmnd(req); @@ -994,13 +987,13 @@ rsp_hdr->itt = cmnd_hdr(req)->itt; rsp_hdr->r2t_sn = cpu_to_be32(req->r2t_sn++); rsp_hdr->buffer_offset = cpu_to_be32(offset); - if (length > burst) { + if (req->r2t_length > burst) { rsp_hdr->data_length = cpu_to_be32(burst); - length -= burst; + req->r2t_length -= burst; offset += burst; } else { - rsp_hdr->data_length = cpu_to_be32(length); - length = 0; + rsp_hdr->data_length = cpu_to_be32(req->r2t_length); + req->r2t_length = 0; } TRACE(TRACE_D_WRITE, "%x %u %u %u %u", cmnd_itt(req), @@ -1013,12 +1006,10 @@ if (++req->outstanding_r2t >= session->sess_param.max_outstanding_r2t) break; - } while (length); + } while(req->r2t_length != 0); iscsi_cmnds_init_write(&send, ISCSI_INIT_WRITE_WAKE); - req->data_waiting = 1; - out: return; } @@ -1058,22 +1049,6 @@ return res; } - -static void scsi_cmnd_exec(struct iscsi_cmnd *cmnd) -{ - if (cmnd->r2t_length) { - if (!cmnd->is_unsolicited_data) - send_r2t(cmnd); - } else { - /* - * There is no race with send_r2t() and __cmnd_abort(), - * since all functions called from single read thread - */ - cmnd->data_waiting = 0; - iscsi_restart_cmnd(cmnd); - } -} - static int noop_out_start(struct iscsi_cmnd *cmnd) { struct iscsi_conn *conn = cmnd->conn; @@ -1084,26 +1059,28 @@ iscsi_extracheck_is_rd_thread(conn); - if (cmnd_ttt(cmnd) != cpu_to_be32(ISCSI_RESERVED_TAG)) { + if (unlikely(cmnd_ttt(cmnd) != cpu_to_be32(ISCSI_RESERVED_TAG))) { /* * We don't request a NOP-Out by sending a NOP-In. * See 10.18.2 in the draft 20. */ - PRINT_ERROR("initiator bug %x", cmnd_itt(cmnd)); + PRINT_ERROR("Initiator sent command with not RESERVED tag and " + "TTT %x", cmnd_itt(cmnd)); err = -ISCSI_REASON_PROTOCOL_ERROR; goto out; } if (cmnd_itt(cmnd) == cpu_to_be32(ISCSI_RESERVED_TAG)) { - if (!(cmnd->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE)) - PRINT_ERROR("%s","initiator bug!"); + if (unlikely(!(cmnd->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE))) + PRINT_ERROR("%s", "Initiator sent RESERVED tag for " + "non-immediate command"); spin_lock(&conn->session->sn_lock); __update_stat_sn(cmnd); err = check_cmd_sn(cmnd); spin_unlock(&conn->session->sn_lock); - if (err) + if (unlikely(err)) goto out; - } else if ((err = cmnd_insert_hash(cmnd)) < 0) { + } else if (unlikely((err = cmnd_insert_hash(cmnd)) < 0)) { PRINT_ERROR("Can't insert in hash: ignore this request %x", cmnd_itt(cmnd)); goto out; @@ -1266,7 +1243,8 @@ dir = scst_cmd_get_data_direction(scst_cmd); if (dir != SCST_DATA_WRITE) { - if (!(req_hdr->flags & ISCSI_CMD_FINAL) || req->pdu.datasize) { + if (unlikely(!(req_hdr->flags & ISCSI_CMD_FINAL) || + req->pdu.datasize)) { PRINT_ERROR("Unexpected unsolicited data (ITT %x " "CDB %x", cmnd_itt(req), req_hdr->scb[0]); create_sense_rsp(req, ABORTED_COMMAND, 0xc, 0xc); @@ -1278,11 +1256,12 @@ if (dir == SCST_DATA_WRITE) { req->is_unsolicited_data = !(req_hdr->flags & ISCSI_CMD_FINAL); req->r2t_length = be32_to_cpu(req_hdr->data_length) - req->pdu.datasize; + req->data_waiting = 1; } req->target_task_tag = get_next_ttt(conn); req->sg = scst_cmd_get_sg(scst_cmd); req->bufflen = scst_cmd_get_bufflen(scst_cmd); - if (req->r2t_length > req->bufflen) { + if (unlikely(req->r2t_length > req->bufflen)) { PRINT_ERROR("req->r2t_length %d > req->bufflen %d", req->r2t_length, req->bufflen); req->r2t_length = req->bufflen; @@ -1292,8 +1271,8 @@ "r2t_length=%d, bufflen=%d", req, dir, req->is_unsolicited_data, req->r2t_length, req->bufflen); - if (!session->sess_param.immediate_data && - req->pdu.datasize) { + if (unlikely(!session->sess_param.immediate_data && + req->pdu.datasize)) { PRINT_ERROR("Initiator %s violated negotiated paremeters: " "forbidden immediate data sent (ITT %x, op %x)", session->initiator_name, cmnd_itt(req), req_hdr->scb[0]); @@ -1301,8 +1280,8 @@ goto out; } - if (session->sess_param.initial_r2t && - !(req_hdr->flags & ISCSI_CMD_FINAL)) { + if (unlikely(session->sess_param.initial_r2t && + !(req_hdr->flags & ISCSI_CMD_FINAL))) { PRINT_ERROR("Initiator %s violated negotiated paremeters: " "initial R2T is required (ITT %x, op %x)", session->initiator_name, cmnd_itt(req), req_hdr->scb[0]); @@ -1311,7 +1290,7 @@ } if (req->pdu.datasize) { - if (dir != SCST_DATA_WRITE) { + if (unlikely(dir != SCST_DATA_WRITE)) { PRINT_ERROR("pdu.datasize(%d) >0, but dir(%x) isn't WRITE", req->pdu.datasize, dir); create_sense_rsp(req, ABORTED_COMMAND, 0xc, 0xc); @@ -1344,36 +1323,28 @@ cmnd->cmd_req = req = cmnd_find_hash(conn->session, req_hdr->itt, req_hdr->ttt); - if (!req) { + if (unlikely(req == NULL)) { /* It might happen if req was aborted and then freed */ TRACE(TRACE_MGMT_MINOR, "Unable to find scsi task %x %x", cmnd_itt(cmnd), cmnd_ttt(cmnd)); goto skip_pdu; } - if (req->r2t_length < cmnd->pdu.datasize) { - PRINT_ERROR("Invalid data len %x %u %u", cmnd_itt(req), - cmnd->pdu.datasize, req->r2t_length); - mark_conn_closed(conn); - res = -EINVAL; - goto out; + if (req->is_unsolicited_data) { + if (unlikely(req->r2t_length < cmnd->pdu.datasize)) { + PRINT_ERROR("Data size (%d) > R2T length (%d)", + cmnd->pdu.datasize, req->r2t_length); + mark_conn_closed(conn); + res = -EINVAL; + goto out; + } + req->r2t_length -= cmnd->pdu.datasize; } - if (req->r2t_length + offset != cmnd_write_size(req)) { - PRINT_ERROR("Wrong cmd lengths (%x %u %u %u)", - cmnd_itt(req), req->r2t_length, - offset, cmnd_write_size(req)); - mark_conn_closed(conn); - res = -EINVAL; - goto out; - } - - req->r2t_length -= cmnd->pdu.datasize; - /* Check unsolicited burst data */ - if ((req_hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) && - (req->pdu.bhs.flags & ISCSI_FLG_FINAL)) { - PRINT_ERROR("unexpected data from %x %x", + if (unlikely((req_hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) && + (req->pdu.bhs.flags & ISCSI_FLG_FINAL))) { + PRINT_ERROR("Unexpected data from %x %x", cmnd_itt(cmnd), cmnd_ttt(cmnd)); mark_conn_closed(conn); res = -EINVAL; @@ -1419,28 +1390,36 @@ if (req_hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) { TRACE_DBG("ISCSI_RESERVED_TAG, FINAL %x", req_hdr->flags & ISCSI_FLG_FINAL); - if (req_hdr->flags & ISCSI_FLG_FINAL) { + + if (req_hdr->flags & ISCSI_FLG_FINAL) req->is_unsolicited_data = 0; - if (!req->pending) - scsi_cmnd_exec(req); - } + else + goto out_put; } else { - TRACE_DBG("FINAL %x, outstanding_r2t %d, " - "r2t_length %d", req_hdr->flags & ISCSI_FLG_FINAL, + TRACE_DBG("FINAL %x, outstanding_r2t %d, r2t_length %d", + req_hdr->flags & ISCSI_FLG_FINAL, req->outstanding_r2t, req->r2t_length); - /* ToDo : proper error handling */ - if (!(req_hdr->flags & ISCSI_FLG_FINAL) && (req->r2t_length == 0)) - PRINT_ERROR("initiator error %x", cmnd_itt(req)); - if (!(req_hdr->flags & ISCSI_FLG_FINAL)) - goto out; + if (req_hdr->flags & ISCSI_FLG_FINAL) { + if (unlikely(req->is_unsolicited_data)) { + PRINT_ERROR("Unexpected unsolicited data " + "(r2t_length %u, outstanding_r2t %d)", + req->r2t_length, req->is_unsolicited_data); + mark_conn_closed(req->conn); + goto out_put; + } + req->outstanding_r2t--; + } else + goto out_put; + } - req->outstanding_r2t--; + if (req->r2t_length != 0) { + if (!req->is_unsolicited_data) + send_r2t(req); + } else + iscsi_session_push_cmnd(req); - scsi_cmnd_exec(req); - } - -out: +out_put: cmnd_put(cmnd); return; } @@ -1451,37 +1430,39 @@ * * Returns >0 if cmd_list_lock was dropped inside, 0 otherwise. */ -static inline int __cmnd_abort(struct iscsi_cmnd *cmnd) +static int __cmnd_abort(struct iscsi_cmnd *cmnd) { int res = 0; + struct iscsi_conn *conn = cmnd->conn; - /* Check to avoid double release */ - if (cmnd->tmfabort) - goto out; - TRACE_MGMT_DBG("Aborting cmd %p, scst_cmd %p (scst state %x, " - "ref_cnt %d, itt %x, sn %d, op %x, r2t_len %x, CDB op %x, " - "size to write %u, is_unsolicited_data %u, " - "outstanding_r2t %u)", cmnd, cmnd->scst_cmd, - cmnd->scst_state, atomic_read(&cmnd->ref_cnt), - cmnd_itt(cmnd), cmnd->pdu.bhs.sn, cmnd_opcode(cmnd), - cmnd->r2t_length, cmnd_scsicode(cmnd), cmnd_write_size(cmnd), - cmnd->is_unsolicited_data, cmnd->outstanding_r2t); + "ref_cnt %d, itt %x, sn %u, op %x, r2t_len %x, CDB op %x, " + "size to write %u, is_unsolicited_data %d, " + "outstanding_r2t %d, data_waiting %d, sess->exp_cmd_sn %u)", + cmnd, cmnd->scst_cmd, cmnd->scst_state, + atomic_read(&cmnd->ref_cnt), cmnd_itt(cmnd), cmnd->pdu.bhs.sn, + cmnd_opcode(cmnd), cmnd->r2t_length, cmnd_scsicode(cmnd), + cmnd_write_size(cmnd), cmnd->is_unsolicited_data, + cmnd->outstanding_r2t, cmnd->data_waiting, + conn->session->exp_cmd_sn); #ifdef NET_PAGE_CALLBACKS_DEFINED TRACE_MGMT_DBG("net_ref_cnt %d", atomic_read(&cmnd->net_ref_cnt)); #endif - iscsi_extracheck_is_rd_thread(cmnd->conn); + iscsi_extracheck_is_rd_thread(conn); cmnd->tmfabort = 1; - if (cmnd->data_waiting) { - struct iscsi_conn *conn = cmnd->conn; + if (cmnd->data_waiting && conn->closing) { + spin_unlock_bh(&conn->cmd_list_lock); + res = 1; - spin_unlock_bh(&conn->cmd_list_lock); - TRACE_MGMT_DBG("Releasing data waiting cmd %p", cmnd); - req_cmnd_release_force(cmnd, ISCSI_FORCE_RELEASE_WRITE); + + /* ToDo: this is racy for MC/S */ + TRACE_MGMT_DBG("Pushing data waiting cmd %p", cmnd); + iscsi_session_push_cmnd(cmnd); + /* * We are in the read thread, so we may not worry that after * cmnd release conn gets released as well. @@ -1489,7 +1470,6 @@ spin_lock_bh(&conn->cmd_list_lock); } -out: return res; } @@ -1502,6 +1482,15 @@ struct iscsi_cmnd *cmnd; int err; + req_hdr->ref_cmd_sn = be32_to_cpu(req_hdr->ref_cmd_sn); + + if (after(req_hdr->ref_cmd_sn, req_hdr->cmd_sn)) { + PRINT_ERROR("ABORT TASK: RefCmdSN(%u) > CmdSN(%u)", + req_hdr->ref_cmd_sn, req_hdr->cmd_sn); + err = ISCSI_RESPONSE_FUNCTION_REJECTED; + goto out; + } + if ((cmnd = cmnd_find_hash_get(session, req_hdr->rtt, ISCSI_RESERVED_TAG))) { struct iscsi_conn *conn = cmnd->conn; struct iscsi_scsi_cmd_hdr *hdr = cmnd_hdr(cmnd); @@ -1514,6 +1503,26 @@ goto out_put; } + if (cmnd->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE) { + if (req_hdr->ref_cmd_sn != req_hdr->cmd_sn) { + PRINT_ERROR("ABORT TASK: RefCmdSN(%u) != TM cmd " + "CmdSN(%u) for immediate command %p", + req_hdr->ref_cmd_sn, req_hdr->cmd_sn, + cmnd); + err = ISCSI_RESPONSE_FUNCTION_REJECTED; + goto out_put; + } + } else { + if (req_hdr->ref_cmd_sn != hdr->cmd_sn) { + PRINT_ERROR("ABORT TASK: RefCmdSN(%u) != " + "CmdSN(%u) for command %p", + req_hdr->ref_cmd_sn, req_hdr->cmd_sn, + cmnd); + err = ISCSI_RESPONSE_FUNCTION_REJECTED; + goto out_put; + } + } + if (before(req_hdr->cmd_sn, hdr->cmd_sn) || (req_hdr->cmd_sn == hdr->cmd_sn)) { PRINT_ERROR("ABORT TASK: SN mismatch: req SN %x, " @@ -1635,7 +1644,7 @@ struct scst_rx_mgmt_params params; TRACE((function == ISCSI_FUNCTION_ABORT_TASK) ? TRACE_MGMT_MINOR : TRACE_MGMT, - "TM cmd: req %p, itt %x, fn %d, rtt %x, sn %d", req, cmnd_itt(req), + "TM cmd: req %p, itt %x, fn %d, rtt %x, sn %u", req, cmnd_itt(req), function, req_hdr->rtt, req_hdr->cmd_sn); spin_lock(&sess->sn_lock); @@ -1822,7 +1831,7 @@ noop_out_exec(cmnd); break; case ISCSI_OP_SCSI_CMD: - scsi_cmnd_exec(cmnd); + iscsi_restart_cmnd(cmnd); break; case ISCSI_OP_SCSI_TASK_MGT_MSG: execute_task_management(cmnd); @@ -1996,9 +2005,19 @@ iscsi_extracheck_is_rd_thread(cmnd->conn); + sBUG_ON(cmnd->parent_req != NULL); + + /* + * There is no race with __cmnd_abort(), since all functions + * called from single read thread + */ + cmnd->data_waiting = 0; + if (cmnd->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE) { + TRACE_DBG("Immediate cmd %p (cmd_sn %u)", cmnd, + cmnd->pdu.bhs.sn); iscsi_cmnd_exec(cmnd); - return; + goto out; } spin_lock(&session->sn_lock); @@ -2033,26 +2052,73 @@ pending_list_entry); if (cmnd->pdu.bhs.sn != cmd_sn) break; + list_del(&cmnd->pending_list_entry); - cmnd->pending = 0; + TRACE_DBG("Processing pending cmd %p (cmd_sn %u)", + cmnd, cmd_sn); + spin_lock(&session->sn_lock); } } else { - cmnd->pending = 1; - if (before(cmd_sn, session->exp_cmd_sn)) { - PRINT_ERROR("unexpected cmd_sn (%u,%u)", cmd_sn, + int drop = 0; + + TRACE_DBG("Pending cmd %p (cmd_sn %u, exp_cmd_sn %u)", + cmnd, cmd_sn, session->exp_cmd_sn); + + /* + * iSCSI RFC 3720: "The target MUST silently ignore any + * non-immediate command outside of [from ExpCmdSN to MaxCmdSN + * inclusive] range". But we won't honor the MaxCmdSN + * requirement, because, since we adjust MaxCmdSN from the + * separate write thread, rarery it is possible that initiator + * can legally send command with CmdSN>MaxSN. But it won't + * hurt anything, in the worst case it will lead to + * additional QUEUE FULL status. + */ + + if (unlikely(before(cmd_sn, session->exp_cmd_sn))) { + PRINT_ERROR("Unexpected cmd_sn (%u,%u)", cmd_sn, session->exp_cmd_sn); + drop = 1; } - if (after(cmd_sn, session->exp_cmd_sn + iscsi_get_allowed_cmds(session))) { - PRINT_INFO("Suspicious: too large cmd_sn %u (exp_cmd_sn %u, " +#if 0 + if (unlikely(after(cmd_sn, session->exp_cmd_sn + + iscsi_get_allowed_cmds(session)))) { + TRACE_MGMT_DBG("Too large cmd_sn %u (exp_cmd_sn %u, " "max_sn %u)", cmd_sn, session->exp_cmd_sn, iscsi_get_allowed_cmds(session)); } +#endif spin_unlock(&session->sn_lock); + if (unlikely(drop)) { + req_cmnd_release_force(cmnd, ISCSI_FORCE_RELEASE_WRITE); + goto out; + } + + if (unlikely(cmnd->tmfabort)) { + struct iscsi_cmnd *tm_clone; + + TRACE_MGMT_DBG("Pending aborted cmnd %p, creating TM " + "clone (scst cmd %p, state %d)", cmnd, + cmnd->scst_cmd, cmnd->scst_state); + + tm_clone = cmnd_alloc(cmnd->conn, NULL); + if (tm_clone != NULL) { + tm_clone->tmfabort = 1; + tm_clone->pdu = cmnd->pdu; + + TRACE_MGMT_DBG("TM clone %p created", tm_clone); + + iscsi_cmnd_exec(cmnd); + cmnd = tm_clone; + } else + PRINT_ERROR("%s", "Unable to create TM clone"); + } + list_for_each(entry, &session->pending_list) { struct iscsi_cmnd *tmp = list_entry(entry, struct iscsi_cmnd, pending_list_entry); @@ -2062,7 +2128,7 @@ list_add_tail(&cmnd->pending_list_entry, entry); } - +out: return; } @@ -2071,7 +2137,7 @@ struct iscsi_conn *conn = cmnd->conn; struct iscsi_session *session = conn->session; - if (cmnd->pdu.datasize > session->sess_param.max_recv_data_length) { + if (unlikely(cmnd->pdu.datasize > session->sess_param.max_recv_data_length)) { PRINT_ERROR("Initiator %s violated negotiated parameters: " "data too long (ITT %x, datasize %u, " "max_recv_data_length %u", session->initiator_name, @@ -2143,12 +2209,20 @@ void cmnd_rx_end(struct iscsi_cmnd *cmnd) { + TRACE_ENTRY(); + TRACE_DBG("%p:%x", cmnd, cmnd_opcode(cmnd)); switch (cmnd_opcode(cmnd)) { + case ISCSI_OP_SCSI_CMD: + if (cmnd->r2t_length != 0) { + if (!cmnd->is_unsolicited_data) + send_r2t(cmnd); + break; + } + /* else go through */ case ISCSI_OP_SCSI_REJECT: case ISCSI_OP_NOOP_OUT: - case ISCSI_OP_SCSI_CMD: case ISCSI_OP_SCSI_TASK_MGT_MSG: case ISCSI_OP_LOGOUT_CMD: iscsi_session_push_cmnd(cmnd); @@ -2169,6 +2243,8 @@ req_cmnd_release(cmnd); break; } + + TRACE_EXIT(); return; } @@ -2323,7 +2399,8 @@ TRACE_DBG("req %p, resp_flags=%x, req->bufflen=%d, req->sg=%p", req, resp_flags, req->bufflen, req->sg); - if ((req->bufflen != 0) && !(resp_flags & SCST_TSC_FLAG_STATUS)) { + if (unlikely((req->bufflen != 0) && + !(resp_flags & SCST_TSC_FLAG_STATUS))) { PRINT_ERROR("%s", "Sending DATA without STATUS is unsupported"); scst_set_cmd_error(scst_cmd, SCST_LOAD_SENSE(scst_sense_hardw_error)); @@ -2474,7 +2551,7 @@ if (iscsi_is_delay_tm_resp(rsp)) { TRACE(TRACE_MGMT_MINOR, "Delaying TM fn %x response %p " "(req %p), because not all affected commands received " - "(cmd sn %d, exp sn %d)", + "(TM cmd sn %u, exp sn %u)", req_hdr->function & ISCSI_FUNCTION_MASK, rsp, req, req_hdr->cmd_sn, sess->exp_cmd_sn); sess->tm_rsp = rsp; Modified: trunk/iscsi-scst/kernel/iscsi.h =================================================================== --- trunk/iscsi-scst/kernel/iscsi.h 2007-12-24 19:22:02 UTC (rev 239) +++ trunk/iscsi-scst/kernel/iscsi.h 2007-12-29 17:04:13 UTC (rev 240) @@ -236,7 +236,6 @@ /* Some flags protected by conn->write_list_lock */ unsigned int hashed:1; unsigned int should_close_conn:1; - unsigned int pending:1; unsigned int own_sg:1; unsigned int on_write_list:1; unsigned int write_processing_started:1; @@ -490,8 +489,8 @@ } #ifdef EXTRACHECKS -#define iscsi_extracheck_is_rd_thread(conn) sBUG_ON(current != (conn)->rd_task) -#define iscsi_extracheck_is_wr_thread(conn) sBUG_ON(current != (conn)->wr_task) +extern void iscsi_extracheck_is_rd_thread(struct iscsi_conn *conn); +extern void iscsi_extracheck_is_wr_thread(struct iscsi_conn *conn); #else static inline void iscsi_extracheck_is_rd_thread(struct iscsi_conn *conn) {} static inline void iscsi_extracheck_is_wr_thread(struct iscsi_conn *conn) {} Modified: trunk/iscsi-scst/kernel/nthread.c =================================================================== --- trunk/iscsi-scst/kernel/nthread.c 2007-12-24 19:22:02 UTC (rev 239) +++ trunk/iscsi-scst/kernel/nthread.c 2007-12-29 17:04:13 UTC (rev 240) @@ -120,9 +120,7 @@ { struct iscsi_session *session = conn->session; struct iscsi_target *target = conn->target; -#ifdef DEBUG unsigned long start_waiting = jiffies; -#endif TRACE_ENTRY(); @@ -131,27 +129,13 @@ iscsi_extracheck_is_rd_thread(conn); + sBUG_ON(!conn->closing); + /* We want all our already send operations to complete */ conn->sock->ops->shutdown(conn->sock, RCV_SHUTDOWN); conn_abort(conn); - mutex_lock(&target->target_mutex); - spin_lock(&session->sn_lock); - if ((session->tm_rsp != NULL) && (session->tm_rsp->conn == conn)) { - struct iscsi_cmnd *tm_rsp = session->tm_rsp; - TRACE(TRACE_MGMT_MINOR, "Dropping delayed TM rsp %p", tm_rsp); - session->tm_rsp = NULL; - session->tm_active = 0; - spin_unlock(&session->sn_lock); - mutex_unlock(&target->target_mutex); - - rsp_cmnd_release(tm_rsp); - } else { - spin_unlock(&session->sn_lock); - mutex_unlock(&target->target_mutex); - } - if (conn->read_state != RX_INIT_BHS) { req_cmnd_release_force(conn->read_cmnd, 0); conn->read_cmnd = NULL; @@ -162,31 +146,108 @@ while(atomic_read(&conn->conn_ref_cnt) != 0) { struct iscsi_cmnd *cmnd; + mutex_lock(&target->target_mutex); + spin_lock(&session->sn_lock); + if ((session->tm_rsp != NULL) && (session->tm_rsp->conn == conn)) { + struct iscsi_cmnd *tm_rsp = session->tm_rsp; + TRACE(TRACE_MGMT_MINOR, "Dropping delayed TM rsp %p", + tm_rsp); + session->tm_rsp = NULL; + session->tm_active = 0; + spin_unlock(&session->sn_lock); + mutex_unlock(&target->target_mutex); + + rsp_cmnd_release(tm_rsp); + } else { + spin_unlock(&session->sn_lock); + mutex_unlock(&target->target_mutex); + } + if (!list_empty(&session->pending_list)) { struct list_head *pending_list = &session->pending_list; - struct iscsi_cmnd *tmp; + int req_freed; TRACE_CONN_CLOSE("Disposing pending commands on " "connection %p (conn_ref_cnt=%d)", conn, atomic_read(&conn->conn_ref_cnt)); - - list_for_each_entry_safe(cmnd, tmp, pending_list, - pending_list_entry) { - if (cmnd->conn == conn) { - TRACE_CONN_CLOSE("Freeing pending cmd %p", - cmnd); - list_del(&cmnd->pending_list_entry); - cmnd->pending = 0; - req_cmnd_release_force(cmnd, 0); + + /* + * Such complicated approach currently isn't necessary, + * but it will be necessary for MC/S, if we won't want + * to reestablish the whole session on a connection + * failure. + */ + + spin_lock(&session->sn_lock); + do { + req_freed = 0; + list_for_each_entry(cmnd, pending_list, + pending_list_entry) { + TRACE_CONN_CLOSE("Pending cmd %p" + "(conn %p, cmd_sn %u, exp_cmd_sn %u)", + cmnd, conn, cmnd->pdu.bhs.sn, + session->exp_cmd_sn); + if ((cmnd->conn == conn) && + (session->exp_cmd_sn == cmnd->pdu.bhs.sn)) { + TRACE_CONN_CLOSE("Freeing pending cmd %p", + cmnd); + + list_del(&cmnd->pending_list_entry); + + session->exp_cmd_sn++; + + spin_unlock(&session->sn_lock); + + req_cmnd_release_force(cmnd, 0); + + req_freed = 1; + spin_lock(&session->sn_lock); + break; + } } + } while(req_freed); + spin_unlock(&session->sn_lock); + + if (time_after(jiffies, start_waiting + 5*HZ)) { + TRACE_CONN_CLOSE("%s", "Wait time expired"); + spin_lock(&session->sn_lock); + do { + req_freed = 0; + list_for_each_entry(cmnd, pending_list, + pending_list_entry) { + TRACE_CONN_CLOSE("Pending cmd %p" + "(conn %p, cmd_sn %u, exp_cmd_sn %u)", + cmnd, conn, cmnd->pdu.bhs.sn, + session->exp_cmd_sn); + if (cmnd->conn == conn) { + PRINT_ERROR("Freeing orphaned " + "pending cmd %p", cmnd); + + list_del(&cmnd->pending_list_entry); + + if (session->exp_cmd_sn == cmnd->pdu.bhs.sn) + session->exp_cmd_sn++; + + spin_unlock(&session->sn_lock); + + req_cmnd_release_force(cmnd, 0); + + req_freed = 1; + spin_lock(&session->sn_lock); + break; + } + } + } while(req_freed); + spin_unlock(&session->sn_lock); } } iscsi_make_conn_wr_active(conn); msleep(50); - TRACE_CONN_CLOSE("conn %p, conn_ref_cnt %d left, wr_state %d", - conn, atomic_read(&conn->conn_ref_cnt), conn->wr_state); + TRACE_CONN_CLOSE("conn %p, conn_ref_cnt %d left, wr_state %d, " + "exp_cmd_sn %u", conn, atomic_read(&conn->conn_ref_cnt), + conn->wr_state, session->exp_cmd_sn); #ifdef DEBUG { #ifdef NET_PAGE_CALLBACKS_DEFINED @@ -198,11 +259,11 @@ spin_lock_bh(&conn->cmd_list_lock); list_for_each_entry(cmnd, &conn->cmd_list, cmd_list_entry) { TRACE_CONN_CLOSE_DBG("cmd %p, scst_state %x, scst_cmd " - "state %d, data_waiting %d, ref_cnt %d, " + "state %d, data_waiting %d, ref_cnt %d, sn %u, " "parent_req %p", cmnd, cmnd->scst_state, (cmnd->scst_cmd != NULL) ? cmnd->scst_cmd->state : -1, cmnd->data_waiting, atomic_read(&cmnd->ref_cnt), - cmnd->parent_req); + cmnd->pdu.bhs.sn, cmnd->parent_req); #ifdef NET_PAGE_CALLBACKS_DEFINED TRACE_CONN_CLOSE_DBG("net_ref_cnt %d, sg %p", atomic_read(&cmnd->net_ref_cnt), cmnd->sg); @@ -493,7 +554,7 @@ if (conn->read_state != RX_END) goto out; - if (conn->read_size) { + if (unlikely(conn->read_size)) { PRINT_ERROR("%d %x %d", res, cmnd_opcode(cmnd), conn->read_size); sBUG(); } @@ -742,7 +803,7 @@ } sg = write_cmnd->sg; - if (sg == NULL) { + if (unlikely(sg == NULL)) { PRINT_ERROR("%s", "warning data missing!"); return 0; } @@ -990,7 +1051,7 @@ if (conn->write_state != TX_END) goto out; - if (conn->write_size) { + if (unlikely(conn->write_size)) { PRINT_ERROR("%d %x %u", res, cmnd_opcode(cmnd), conn->write_size); sBUG(); Modified: trunk/qla2x00t/qla_os.c =================================================================== --- trunk/qla2x00t/qla_os.c 2007-12-24 19:22:02 UTC (rev 239) +++ trunk/qla2x00t/qla_os.c 2007-12-29 17:04:13 UTC (rev 240) @@ -2230,7 +2230,7 @@ ha = (scsi_qla_host_t *)data; - set_user_nice(current, -20); + set_user_nice(current, -10); while (!kthread_should_stop()) { DEBUG3(printk("qla2x00: DPC handler sleeping\n")); Modified: trunk/scst/src/scst_targ.c =================================================================== --- trunk/scst/src/scst_targ.c 2007-12-24 19:22:02 UTC (rev 239) +++ trunk/scst/src/scst_targ.c 2007-12-29 17:04:13 UTC (rev 240) @@ -2866,7 +2866,7 @@ current->flags |= PF_NOFREEZE; - set_user_nice(current, -20); + set_user_nice(current, -10); spin_lock_irq(&scst_init_lock); while(!kthread_should_stop()) { @@ -4141,7 +4141,7 @@ current->flags |= PF_NOFREEZE; - set_user_nice(current, -20); + set_user_nice(current, -10); spin_lock_irq(&scst_mcmd_lock); while(!kthread_should_stop()) { @@ -4649,7 +4649,7 @@ current->flags |= PF_NOFREEZE; - set_user_nice(current, -20); + set_user_nice(current, -10); spin_lock_irq(&scst_mgmt_lock); while(!kthread_should_stop()) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |