diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index b80f20d60..445c74a23 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -653,6 +653,11 @@ again: goto cont; } } + if (cmnd->data_out_in_data_receiving) { + TRACE_MGMT_DBG("Aborted cmnd %p is waiting for " + "DATA OUT data, keep waiting", cmnd); + goto cont; + } if (((cmnd == conn->read_cmnd) || (cmnd->r2t_len_to_receive != 0)) && (time_after_eq(j, cmnd->write_start + ISCSI_TM_DATA_WAIT_TIMEOUT) || force)) { diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index f8129ef6a..cb35249f5 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -417,6 +417,7 @@ void cmnd_done(struct iscsi_cmnd *cmnd) EXTRACHECKS_BUG_ON(cmnd->on_rx_digest_list); EXTRACHECKS_BUG_ON(cmnd->hashed); EXTRACHECKS_BUG_ON(cmnd->cmd_req); + EXTRACHECKS_BUG_ON(cmnd->data_out_in_data_receiving); req_del_from_write_timeout_list(cmnd); @@ -625,11 +626,14 @@ static void req_cmnd_pre_release(struct iscsi_cmnd *req) if (unlikely(req->hashed)) { /* It sometimes can happen during errors recovery */ + TRACE_MGMT_DBG("Removing req %p from hash", req); cmnd_remove_data_wait_hash(req); } if (unlikely(req->cmd_req)) { /* It sometimes can happen during errors recovery */ + TRACE_MGMT_DBG("Putting cmd_req %p (req %p)", req->cmd_req, req); + req->cmd_req->data_out_in_data_receiving = 0; cmnd_put(req->cmd_req); req->cmd_req = NULL; } @@ -1271,8 +1275,6 @@ static struct iscsi_cmnd *cmnd_find_data_wait_hash(struct iscsi_conn *conn, spin_lock(&session->cmnd_data_wait_hash_lock); res = __cmnd_find_data_wait_hash(conn, itt); - if (cmnd_get_check(res) != 0) - res = NULL; spin_unlock(&session->cmnd_data_wait_hash_lock); return res; @@ -2048,8 +2050,9 @@ static int data_out_start(struct iscsi_cmnd *cmnd) TRACE_ENTRY(); /* - * There is no race with send_r2t() and conn_abort(), since - * all functions called from single read thread + * There is no race with send_r2t(), conn_abort() and + * iscsi_check_tm_data_wait_timeouts(), since + * all the functions called from single read thread */ iscsi_extracheck_is_rd_thread(cmnd->conn); @@ -2069,6 +2072,8 @@ static int data_out_start(struct iscsi_cmnd *cmnd) goto out; } + cmnd_get(orig_req); + if (unlikely(orig_req->r2t_len_to_receive < cmnd->pdu.datasize)) { if (orig_req->prelim_compl_flags != 0) { /* We can have fake r2t_len_to_receive */ @@ -2102,6 +2107,9 @@ go: goto out; } + EXTRACHECKS_BUG_ON(orig_req->data_out_in_data_receiving); + orig_req->data_out_in_data_receiving = 1; + TRACE_WRITE("cmnd %p, orig_req %p, offset %u, datasize %u", cmnd, orig_req, offset, cmnd->pdu.datasize); @@ -2129,6 +2137,8 @@ static void data_out_end(struct iscsi_cmnd *cmnd) iscsi_extracheck_is_rd_thread(cmnd->conn); + req->data_out_in_data_receiving = 0; + if (!(cmnd->conn->ddigest_type & DIGEST_NONE) && !cmnd->ddigest_checked) { cmd_add_on_rx_ddigest_list(req, cmnd); @@ -2968,9 +2978,9 @@ static void iscsi_push_cmnd(struct iscsi_cmnd *cmnd) if (unlikely(before(cmd_sn, session->exp_cmd_sn))) { TRACE_MGMT_DBG("Ignoring out of expected range cmd_sn " - "(sn %u, exp_sn %u, op %x, CDB op %x)", cmd_sn, - session->exp_cmd_sn, cmnd_opcode(cmnd), - cmnd_scsicode(cmnd)); + "(sn %u, exp_sn %u, cmd %p, op %x, CDB op %x)", + cmd_sn, session->exp_cmd_sn, cmnd, + cmnd_opcode(cmnd), cmnd_scsicode(cmnd)); drop = 1; } diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index 4d9644a7f..c61913835 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -393,6 +393,13 @@ struct iscsi_cmnd { unsigned int force_cleanup_done:1; unsigned int dec_active_cmds:1; unsigned int ddigest_checked:1; + /* + * Used to prevent release of original req while its related DATA OUT + * cmd is receiving data, i.e. stays between data_out_start() and + * data_out_end(). Ref counting can't be used for that, because + * req_cmnd_release() supposed to be called only once. + */ + unsigned int data_out_in_data_receiving:1; #ifdef CONFIG_SCST_EXTRACHECKS unsigned int on_rx_digest_list:1; unsigned int release_called:1;