From 80ba881d461a274f00a1fcc5aed0d1d0bb887231 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 13 Aug 2013 18:43:26 +0000 Subject: [PATCH] fcst: Fix ft_abort_cmd() git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@4968 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- fcst/fcst.h | 28 ++++++++++++ fcst/ft_cmd.c | 116 ++++++++++++++++++++++++++++++++++++++++++-------- fcst/ft_io.c | 11 ++++- 3 files changed, 136 insertions(+), 19 deletions(-) diff --git a/fcst/fcst.h b/fcst/fcst.h index 4ff9d1ec1..072f045e3 100644 --- a/fcst/fcst.h +++ b/fcst/fcst.h @@ -91,6 +91,30 @@ struct ft_tport { struct scst_tgt *tgt; }; +/** + * enum ft_cmd_state - SCSI command state managed by fcst + * @FT_STATE_NEW: New command arrived and is being processed. + * @FT_STATE_NEED_DATA: Processing a write or bidir command and waiting + * for data arrival. + * @FT_STATE_DATA_IN: Data for the write or bidir command arrived and is + * being processed. + * @FT_STATE_CMD_RSP_SENT: Response with SCSI status has been sent. + * @FT_STATE_MGMT: Processing a SCSI task management function. + * @FT_STATE_MGMT_RSP_SENT: Response for task management function has been sent. + * @FT_STATE_DONE: Command processing finished successfully, command + * processing has been aborted or command processing + * failed. + */ +enum ft_cmd_state { + FT_STATE_NEW = 0, + FT_STATE_NEED_DATA = 1, + FT_STATE_DATA_IN = 2, + FT_STATE_CMD_RSP_SENT = 3, + FT_STATE_MGMT = 4, + FT_STATE_MGMT_RSP_SENT = 5, + FT_STATE_DONE = 6, +}; + /* * Commands */ @@ -102,6 +126,8 @@ struct ft_cmd { u32 max_lso_payload; /* max offloaded (LSO) data payload */ u16 max_payload; /* max transmitted data payload */ struct scst_cmd *scst_cmd; + struct spinlock lock; /* protects state */ + enum ft_cmd_state state; }; extern struct list_head ft_lport_list; @@ -139,6 +165,8 @@ void ft_lport_del(struct fc_lport *, void *); * other internal functions. */ int ft_thread(void *); +bool ft_test_and_set_cmd_state(struct ft_cmd *fcmd, enum ft_cmd_state old, + enum ft_cmd_state new); void ft_recv_req(struct ft_sess *, struct fc_frame *); void ft_recv_write_data(struct scst_cmd *, struct fc_frame *); int ft_send_read_data(struct scst_cmd *); diff --git a/fcst/ft_cmd.c b/fcst/ft_cmd.c index a7695e493..0cf8b52db 100644 --- a/fcst/ft_cmd.c +++ b/fcst/ft_cmd.c @@ -168,6 +168,89 @@ static void ft_cmd_tm_dump(struct scst_mgmt_cmd *mcmd, const char *caller) ft_cmd_dump(mcmd->cmd_to_abort, caller); } +/** + * ft_set_cmd_state() - set the state of a command + */ +static enum ft_cmd_state ft_set_cmd_state(struct ft_cmd *fcmd, + enum ft_cmd_state new) +{ + enum ft_cmd_state previous; + + spin_lock(&fcmd->lock); + previous = fcmd->state; + if (previous != FT_STATE_DONE) + fcmd->state = new; + spin_unlock(&fcmd->lock); + + return previous; +} + +/** + * ft_test_and_set_cmd_state() - test and set the state of a command + * + * Returns true if and only if the previous command state was equal to 'old'. + */ +bool ft_test_and_set_cmd_state(struct ft_cmd *fcmd, enum ft_cmd_state old, + enum ft_cmd_state new) +{ + enum ft_cmd_state previous; + + WARN_ON(old == FT_STATE_DONE); + WARN_ON(new == FT_STATE_NEW); + + spin_lock(&fcmd->lock); + previous = fcmd->state; + if (previous == old) + fcmd->state = new; + spin_unlock(&fcmd->lock); + + return previous == old; +} + +static void ft_abort_cmd(struct scst_cmd *cmd) +{ + struct ft_cmd *fcmd = scst_cmd_get_tgt_priv(cmd); + struct fc_seq *sp = fcmd->seq; + struct fc_exch *ep = fc_seq_exch(sp); + struct fc_lport *lport = ep->lp; + + pr_err("%s: cmd %p ox_id %#x rx_id %#x state %d\n", __func__, cmd, + ep->oxid, ep->rxid, fcmd->state); + + lport->tt.exch_done(sp); + + spin_lock(&fcmd->lock); + switch (fcmd->state) { + case FT_STATE_NEW: + case FT_STATE_DATA_IN: + case FT_STATE_MGMT: + /* + * Do nothing - defer abort processing until + * srpt_xmit_response() is invoked. + */ + break; + case FT_STATE_NEED_DATA: + /* SCST_DATA_WRITE */ + fcmd->state = FT_STATE_DATA_IN; + scst_rx_data(cmd, SCST_RX_STATUS_ERROR_FATAL, + SCST_CONTEXT_THREAD); + break; + case FT_STATE_CMD_RSP_SENT: + /* + * ft_send_response() is either in progress or has finished. + * Wait until the SCST core has invoked ft_cmd_done(). + */ + break; + case FT_STATE_MGMT_RSP_SENT: + default: + pr_info("Unexpected command state %d\n", fcmd->state); + __WARN(); + fcmd->state = FT_STATE_DONE; + break; + } + spin_unlock(&fcmd->lock); +} + /* * Free command and associated frame. */ @@ -219,9 +302,12 @@ int ft_send_response(struct scst_cmd *cmd) ep = fc_seq_exch(fcmd->seq); lport = ep->lp; + WARN_ON(fcmd->state != FT_STATE_NEW && fcmd->state != FT_STATE_DATA_IN); + ft_set_cmd_state(fcmd, FT_STATE_CMD_RSP_SENT); + if (scst_cmd_aborted_on_xmit(cmd)) { FT_IO_DBG("cmd aborted did %x oxid %x\n", ep->did, ep->oxid); - scst_set_delivery_status(cmd, SCST_CMD_DELIVERY_ABORTED); + ft_abort_cmd(cmd); goto done; } @@ -326,8 +412,7 @@ static void ft_recv_seq(struct fc_seq *sp, struct fc_frame *fp, void *arg) if (IS_ERR(fp)) { pr_err("exchange error %ld - aborting cmd %p / tag %lld\n", -PTR_ERR(fp), cmd, cmd->tag); - scst_rx_mgmt_fn_tag(cmd->sess, SCST_ABORT_TASK, cmd->tag, - SCST_ATOMIC, NULL); + ft_abort_cmd(cmd); return; } @@ -347,19 +432,6 @@ static void ft_recv_seq(struct fc_seq *sp, struct fc_frame *fp, void *arg) } } -static void ft_abort_cmd(struct scst_cmd *cmd, enum scst_exec_context context) -{ - scst_data_direction dir; - - dir = scst_cmd_get_data_direction(cmd); - if (dir & SCST_DATA_WRITE) - scst_rx_data(cmd, SCST_RX_STATUS_ERROR, context); - if (dir & SCST_DATA_READ) { - scst_set_delivery_status(cmd, SCST_CMD_DELIVERY_ABORTED); - scst_tgt_cmd_done(cmd, context); - } -} - /* * Command timeout. * SCST calls this when the command has taken too long in the device handler. @@ -367,7 +439,7 @@ static void ft_abort_cmd(struct scst_cmd *cmd, enum scst_exec_context context) void ft_cmd_timeout(struct scst_cmd *cmd) { FT_IO_DBG("%p: timeout\n", cmd); - ft_abort_cmd(cmd, SCST_CONTEXT_DIRECT); + ft_abort_cmd(cmd); } /* @@ -389,6 +461,9 @@ int ft_send_xfer_rdy(struct scst_cmd *cmd) if (!fp) return SCST_TGT_RES_QUEUE_FULL; + WARN_ON(!ft_test_and_set_cmd_state(fcmd, FT_STATE_NEW, + FT_STATE_NEED_DATA)); + txrdy = fc_frame_payload_get(fp, sizeof(*txrdy)); memset(txrdy, 0, sizeof(*txrdy)); txrdy->ft_data_ro = 0; @@ -485,6 +560,9 @@ void ft_cmd_tm_done(struct scst_mgmt_cmd *mcmd) fcmd = scst_mgmt_cmd_get_tgt_priv(mcmd); if (!fcmd) return; + + ft_set_cmd_state(fcmd, FT_STATE_MGMT_RSP_SENT); + switch (scst_mgmt_cmd_get_status(mcmd)) { case SCST_MGMT_STATUS_SUCCESS: code = FCP_TMF_CMPL; @@ -516,6 +594,8 @@ static void ft_recv_tm(struct scst_session *scst_sess, struct scst_rx_mgmt_params params; int ret; + ft_set_cmd_state(fcmd, FT_STATE_MGMT); + scst_rx_mgmt_params_init(¶ms); #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) @@ -583,6 +663,7 @@ static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp) fcmd->max_payload = sess->max_payload; fcmd->max_lso_payload = sess->max_lso_payload; fcmd->req_frame = fp; + spin_lock_init(&fcmd->lock); fcp = fc_frame_payload_get(fp, sizeof(*fcp)); if (!fcp) @@ -615,6 +696,7 @@ static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp) goto busy; fcmd->scst_cmd = cmd; scst_cmd_set_tgt_priv(cmd, fcmd); + cmd->state = FT_STATE_NEW; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) sp = fr_seq(fp); diff --git a/fcst/ft_io.c b/fcst/ft_io.c index f1682cb20..49013413a 100644 --- a/fcst/ft_io.c +++ b/fcst/ft_io.c @@ -265,8 +265,15 @@ void ft_recv_write_data(struct scst_cmd *cmd, struct fc_frame *fp) else scst_put_buf(cmd, buf); } - if (fcmd->write_data_len == bufflen) - scst_rx_data(cmd, SCST_RX_STATUS_SUCCESS, SCST_CONTEXT_THREAD); + if (fcmd->write_data_len == bufflen) { + spin_lock(&fcmd->lock); + if (fcmd->state == FT_STATE_NEED_DATA) { + fcmd->state = FT_STATE_DATA_IN; + scst_rx_data(cmd, SCST_RX_STATUS_SUCCESS, + SCST_CONTEXT_THREAD); + } + spin_unlock(&fcmd->lock); + } drop: fc_frame_free(fp); }