Commit 77a23c21 authored by Mike Christie's avatar Mike Christie Committed by James Bottomley

[SCSI] libiscsi: fix iscsi cmdsn allocation

The cmdsn allocation and pdu transmit code can race, and we can end
up sending a pdu with cmdsn 10 before a pdu with 5. The target will
then fail the connection/session. This patch fixes the problem by
delaying the cmdsn allocation until we are about to send the pdu.

This also removes the xmitmutex. We were using the connection xmitmutex
during error handling to handle races with mtask and ctask cleanup and
completion. For ctasks we now have nice refcounting and for the mtask,
if we hit the case where the mtask timesout and it is floating
around somewhere in the driver, we end up dropping the session.
And to handle session level cleanup, we use the xmit suspend bit
along with scsi_flush_queue and the session lock to make sure
that the xmit thread is not possibly transmitting a task while
we are trying to kill it.
Signed-off-by: default avatarMike Christie <michaelc@cs.wisc.edu>
Cc: Roland Dreier <rdreier@cisco.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 218432c6
...@@ -134,19 +134,9 @@ iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask) ...@@ -134,19 +134,9 @@ iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
{ {
struct iscsi_iser_conn *iser_conn = ctask->conn->dd_data; struct iscsi_iser_conn *iser_conn = ctask->conn->dd_data;
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data; struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
struct scsi_cmnd *sc = ctask->sc;
iser_ctask->command_sent = 0; iser_ctask->command_sent = 0;
iser_ctask->iser_conn = iser_conn; iser_ctask->iser_conn = iser_conn;
if (sc->sc_data_direction == DMA_TO_DEVICE) {
BUG_ON(sc->request_bufflen == 0);
debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
ctask->itt, sc->request_bufflen, ctask->imm_count,
ctask->unsol_count);
}
iser_ctask_rdma_init(iser_ctask); iser_ctask_rdma_init(iser_ctask);
} }
...@@ -219,6 +209,14 @@ iscsi_iser_ctask_xmit(struct iscsi_conn *conn, ...@@ -219,6 +209,14 @@ iscsi_iser_ctask_xmit(struct iscsi_conn *conn,
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data; struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
int error = 0; int error = 0;
if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
BUG_ON(ctask->sc->request_bufflen == 0);
debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
ctask->itt, ctask->sc->request_bufflen,
ctask->imm_count, ctask->unsol_count);
}
debug_scsi("ctask deq [cid %d itt 0x%x]\n", debug_scsi("ctask deq [cid %d itt 0x%x]\n",
conn->id, ctask->itt); conn->id, ctask->itt);
......
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/kfifo.h> #include <linux/kfifo.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/mutex.h>
#include <net/tcp.h> #include <net/tcp.h>
#include <scsi/scsi_cmnd.h> #include <scsi/scsi_cmnd.h>
#include <scsi/scsi_host.h> #include <scsi/scsi_host.h>
...@@ -211,7 +210,6 @@ iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) ...@@ -211,7 +210,6 @@ iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
static int static int
iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
{ {
int rc;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr; struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr;
...@@ -219,9 +217,7 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) ...@@ -219,9 +217,7 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
struct scsi_cmnd *sc = ctask->sc; struct scsi_cmnd *sc = ctask->sc;
int datasn = be32_to_cpu(rhdr->datasn); int datasn = be32_to_cpu(rhdr->datasn);
rc = iscsi_check_assign_cmdsn(session, (struct iscsi_nopin*)rhdr); iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
if (rc)
return rc;
/* /*
* setup Data-In byte counter (gets decremented..) * setup Data-In byte counter (gets decremented..)
*/ */
...@@ -377,12 +373,10 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) ...@@ -377,12 +373,10 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
return ISCSI_ERR_R2TSN; return ISCSI_ERR_R2TSN;
} }
rc = iscsi_check_assign_cmdsn(session, (struct iscsi_nopin*)rhdr);
if (rc)
return rc;
/* fill-in new R2T associated with the task */ /* fill-in new R2T associated with the task */
spin_lock(&session->lock); spin_lock(&session->lock);
iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
if (!ctask->sc || ctask->mtask || if (!ctask->sc || ctask->mtask ||
session->state != ISCSI_STATE_LOGGED_IN) { session->state != ISCSI_STATE_LOGGED_IN) {
printk(KERN_INFO "iscsi_tcp: dropping R2T itt %d in " printk(KERN_INFO "iscsi_tcp: dropping R2T itt %d in "
...@@ -1762,12 +1756,6 @@ iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) ...@@ -1762,12 +1756,6 @@ iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
debug_scsi("ctask deq [cid %d xmstate %x itt 0x%x]\n", debug_scsi("ctask deq [cid %d xmstate %x itt 0x%x]\n",
conn->id, tcp_ctask->xmstate, ctask->itt); conn->id, tcp_ctask->xmstate, ctask->itt);
/*
* serialize with TMF AbortTask
*/
if (ctask->mtask)
return rc;
rc = iscsi_send_cmd_hdr(conn, ctask); rc = iscsi_send_cmd_hdr(conn, ctask);
if (rc) if (rc)
return rc; return rc;
...@@ -1949,8 +1937,7 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session, ...@@ -1949,8 +1937,7 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
/* called with host lock */ /* called with host lock */
static void static void
iscsi_tcp_mgmt_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask, iscsi_tcp_mgmt_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
char *data, uint32_t data_size)
{ {
struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data; struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data;
tcp_mtask->xmstate = XMSTATE_IMM_HDR_INIT; tcp_mtask->xmstate = XMSTATE_IMM_HDR_INIT;
...@@ -2073,22 +2060,15 @@ iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn, ...@@ -2073,22 +2060,15 @@ iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
switch(param) { switch(param) {
case ISCSI_PARAM_CONN_PORT: case ISCSI_PARAM_CONN_PORT:
mutex_lock(&conn->xmitmutex); if (!tcp_conn->sock)
if (!tcp_conn->sock) {
mutex_unlock(&conn->xmitmutex);
return -EINVAL; return -EINVAL;
}
inet = inet_sk(tcp_conn->sock->sk); inet = inet_sk(tcp_conn->sock->sk);
len = sprintf(buf, "%hu\n", be16_to_cpu(inet->dport)); len = sprintf(buf, "%hu\n", be16_to_cpu(inet->dport));
mutex_unlock(&conn->xmitmutex);
break; break;
case ISCSI_PARAM_CONN_ADDRESS: case ISCSI_PARAM_CONN_ADDRESS:
mutex_lock(&conn->xmitmutex); if (!tcp_conn->sock)
if (!tcp_conn->sock) {
mutex_unlock(&conn->xmitmutex);
return -EINVAL; return -EINVAL;
}
sk = tcp_conn->sock->sk; sk = tcp_conn->sock->sk;
if (sk->sk_family == PF_INET) { if (sk->sk_family == PF_INET) {
...@@ -2099,7 +2079,6 @@ iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn, ...@@ -2099,7 +2079,6 @@ iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
np = inet6_sk(sk); np = inet6_sk(sk);
len = sprintf(buf, NIP6_FMT "\n", NIP6(np->daddr)); len = sprintf(buf, NIP6_FMT "\n", NIP6(np->daddr));
} }
mutex_unlock(&conn->xmitmutex);
break; break;
default: default:
return iscsi_conn_get_param(cls_conn, param, buf); return iscsi_conn_get_param(cls_conn, param, buf);
......
This diff is collapsed.
...@@ -90,6 +90,7 @@ enum { ...@@ -90,6 +90,7 @@ enum {
ISCSI_TASK_COMPLETED, ISCSI_TASK_COMPLETED,
ISCSI_TASK_PENDING, ISCSI_TASK_PENDING,
ISCSI_TASK_RUNNING, ISCSI_TASK_RUNNING,
ISCSI_TASK_ABORTING,
}; };
struct iscsi_cmd_task { struct iscsi_cmd_task {
...@@ -150,18 +151,11 @@ struct iscsi_conn { ...@@ -150,18 +151,11 @@ struct iscsi_conn {
struct iscsi_cmd_task *ctask; /* xmit ctask in progress */ struct iscsi_cmd_task *ctask; /* xmit ctask in progress */
/* xmit */ /* xmit */
struct kfifo *immqueue; /* immediate xmit queue */
struct kfifo *mgmtqueue; /* mgmt (control) xmit queue */ struct kfifo *mgmtqueue; /* mgmt (control) xmit queue */
struct list_head mgmt_run_list; /* list of control tasks */ struct list_head mgmt_run_list; /* list of control tasks */
struct list_head xmitqueue; /* data-path cmd queue */ struct list_head xmitqueue; /* data-path cmd queue */
struct list_head run_list; /* list of cmds in progress */ struct list_head run_list; /* list of cmds in progress */
struct work_struct xmitwork; /* per-conn. xmit workqueue */ struct work_struct xmitwork; /* per-conn. xmit workqueue */
/*
* serializes connection xmit, access to kfifos:
* xmitqueue, immqueue, mgmtqueue
*/
struct mutex xmitmutex;
unsigned long suspend_tx; /* suspend Tx */ unsigned long suspend_tx; /* suspend Tx */
unsigned long suspend_rx; /* suspend Rx */ unsigned long suspend_rx; /* suspend Rx */
...@@ -303,8 +297,7 @@ extern int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, ...@@ -303,8 +297,7 @@ extern int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
/* /*
* pdu and task processing * pdu and task processing
*/ */
extern int iscsi_check_assign_cmdsn(struct iscsi_session *, extern void iscsi_update_cmdsn(struct iscsi_session *, struct iscsi_nopin *);
struct iscsi_nopin *);
extern void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *, extern void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *,
struct iscsi_data *hdr); struct iscsi_data *hdr);
extern int iscsi_conn_send_pdu(struct iscsi_cls_conn *, struct iscsi_hdr *, extern int iscsi_conn_send_pdu(struct iscsi_cls_conn *, struct iscsi_hdr *,
......
...@@ -117,8 +117,7 @@ struct iscsi_transport { ...@@ -117,8 +117,7 @@ struct iscsi_transport {
struct iscsi_stats *stats); struct iscsi_stats *stats);
void (*init_cmd_task) (struct iscsi_cmd_task *ctask); void (*init_cmd_task) (struct iscsi_cmd_task *ctask);
void (*init_mgmt_task) (struct iscsi_conn *conn, void (*init_mgmt_task) (struct iscsi_conn *conn,
struct iscsi_mgmt_task *mtask, struct iscsi_mgmt_task *mtask);
char *data, uint32_t data_size);
int (*xmit_cmd_task) (struct iscsi_conn *conn, int (*xmit_cmd_task) (struct iscsi_conn *conn,
struct iscsi_cmd_task *ctask); struct iscsi_cmd_task *ctask);
void (*cleanup_cmd_task) (struct iscsi_conn *conn, void (*cleanup_cmd_task) (struct iscsi_conn *conn,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment