From 34f682c19ef1604defbc92fe7185b6e1bea94ff3 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Sat, 26 Apr 2014 01:56:36 +0000 Subject: [PATCH] Processing of QErr and TMF_ONLY added git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@5481 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/include/scst.h | 35 +++- scst/include/scst_const.h | 14 +- scst/include/scst_user.h | 2 + scst/src/dev_handlers/scst_user.c | 30 ++++ scst/src/dev_handlers/scst_vdisk.c | 122 ++++++++++++-- scst/src/scst_lib.c | 256 +++++++++++++++++++++++++++-- scst/src/scst_priv.h | 2 + scst/src/scst_targ.c | 109 ++++++++---- usr/fileio/fileio.c | 2 + 9 files changed, 503 insertions(+), 69 deletions(-) diff --git a/scst/include/scst.h b/scst/include/scst.h index e7063b0e8..a393956c8 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -225,6 +225,8 @@ static inline bool list_entry_in_list(const struct list_head *entry) ** !! as well! *************************************************************/ enum { + /** Active states **/ + /* Dev handler's parse() is going to be called */ SCST_CMD_STATE_PARSE = 0, @@ -258,6 +260,12 @@ enum { /* Checks before target driver's xmit_response() is called */ SCST_CMD_STATE_PRE_XMIT_RESP, + /* Checks 1 before target driver's xmit_response() is called */ + SCST_CMD_STATE_PRE_XMIT_RESP1, + + /* Checks 2 before target driver's xmit_response() is called */ + SCST_CMD_STATE_PRE_XMIT_RESP2, + /* Target driver's xmit_response() is going to be called */ SCST_CMD_STATE_XMIT_RESP, @@ -269,6 +277,8 @@ enum { SCST_CMD_STATE_LAST_ACTIVE = (SCST_CMD_STATE_FINISHED_INTERNAL+100), + /** Passive states **/ + /* A cmd is created, but scst_cmd_init_done() not called */ SCST_CMD_STATE_INIT_WAIT, @@ -568,7 +578,11 @@ enum scst_exec_context { /* Set if the cmd is aborted by other initiator */ #define SCST_CMD_ABORTED_OTHER 1 -/* Set if no response should be sent to the target about this cmd */ +/* + * Set if no response should be sent to the target about this cmd. + * Must be set together with SCST_CMD_ABORTED for better processing + * in scst_pre_xmit_response2(). + */ #define SCST_CMD_NO_RESP 2 /* Set if the cmd is dead and can be destroyed at any time */ @@ -2403,13 +2417,17 @@ struct scst_device { /**************************************************************/ /************************************************************* - ** Dev's control mode page related values. Updates serialized - ** by scst_block_dev(). Modified independently to the above - ** fields, hence the alignment. - *************************************************************/ + ** Dev's control mode page related values. Updates serialized + ** by device blocking. Since device blocking protects only + ** commands on the execution stage, in all other read cases + ** use ACCESS_ONCE(), if necessary. Modified independently + ** to the above fields, hence the alignment. + *************************************************************/ unsigned int queue_alg:4 __aligned(sizeof(long)); unsigned int tst:3; + unsigned int qerr:2; + unsigned int tmf_only:1; unsigned int tas:1; unsigned int swp:1; unsigned int d_sense:1; @@ -2426,6 +2444,12 @@ struct scst_device { unsigned int queue_alg_saved:4; unsigned int queue_alg_default:4; + unsigned int tmf_only_saved:1; + unsigned int tmf_only_default:1; + + unsigned int qerr_saved:2; + unsigned int qerr_default:2; + unsigned int tas_saved:1; unsigned int tas_default:1; @@ -2906,7 +2930,6 @@ struct scst_tg_tgt { uint16_t rel_tgt_id; }; - /* * Used to store per-session UNIT ATTENTIONs */ diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index a4322819c..3ce1393d8 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -495,8 +495,8 @@ enum { /************************************************************* ** Values for the control mode page TST field *************************************************************/ -#define SCST_TST_0_SINGLE_TASK_SET 0 -#define SCST_TST_1_SEP_TASK_SETS 1 +#define SCST_TST_0_SINGLE_TASK_SET 0 +#define SCST_TST_1_SEP_TASK_SETS 1 /******************************************************************* ** Values for the control mode page QUEUE ALGORITHM MODIFIER field @@ -508,7 +508,15 @@ enum { ** Values for the control mode page D_SENSE field *************************************************************/ #define SCST_D_SENSE_0_FIXED_SENSE 0 -#define SCST_D_SENSE_1_DESCR_SENSE 1 +#define SCST_D_SENSE_1_DESCR_SENSE 1 + +/************************************************************* + ** Values for the control mode page QErr field + *************************************************************/ +#define SCST_QERR_0_ALL_RESUME 0 +#define SCST_QERR_1_ABORT_ALL 1 +#define SCST_QERR_2_RESERVED 2 +#define SCST_QERR_3_ABORT_THIS_NEXUS_ONLY 3 /************************************************************* ** TransportID protocol identifiers diff --git a/scst/include/scst_user.h b/scst/include/scst_user.h index 0e1306de5..1cda6c214 100644 --- a/scst/include/scst_user.h +++ b/scst/include/scst_user.h @@ -88,7 +88,9 @@ struct scst_user_opt { /* SCSI control mode page parameters, see SPC */ uint8_t tst; + uint8_t tmf_only; uint8_t queue_alg; + uint8_t qerr; uint8_t tas; uint8_t swp; uint8_t d_sense; diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c index bc6e33bb6..206644246 100644 --- a/scst/src/dev_handlers/scst_user.c +++ b/scst/src/dev_handlers/scst_user.c @@ -62,7 +62,9 @@ struct scst_user_dev { unsigned int blocking:1; unsigned int cleanup_done:1; unsigned int tst:3; + unsigned int tmf_only:1; unsigned int queue_alg:4; + unsigned int qerr:2; unsigned int tas:1; unsigned int swp:1; unsigned int d_sense:1; @@ -2618,7 +2620,13 @@ static int dev_user_attach(struct scst_device *sdev) sdev->dh_priv = dev; sdev->tst = dev->tst; + sdev->tmf_only = dev->tmf_only; + sdev->tmf_only_saved = dev->tmf_only; + sdev->tmf_only_default = dev->tmf_only; sdev->queue_alg = dev->queue_alg; + sdev->qerr = dev->qerr; + sdev->qerr_saved = dev->qerr; + sdev->qerr_default = dev->qerr; sdev->swp = dev->swp; sdev->swp_saved = dev->swp; sdev->swp_default = dev->swp; @@ -3370,6 +3378,22 @@ static int __dev_user_set_opt(struct scst_user_dev *dev, goto out; } + if (((opt->tst != SCST_TST_0_SINGLE_TASK_SET) && + (opt->tst != SCST_TST_1_SEP_TASK_SETS)) || + (opt->tmf_only > 1) || + ((opt->queue_alg != SCST_QUEUE_ALG_0_RESTRICTED_REORDER) && + (opt->queue_alg != SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER)) || + ((opt->qerr == SCST_QERR_2_RESERVED) || + (opt->qerr > SCST_QERR_3_ABORT_THIS_NEXUS_ONLY)) || + (opt->swp > 1) || (opt->tas > 1) || (opt->has_own_order_mgmt > 1) || + (opt->d_sense > 1)) { + PRINT_ERROR("Invalid SCSI option (tst %x, tmf_only %x, " + "queue_alg %x, qerr %x, swp %x, tas %x, d_sense %d, " + "has_own_order_mgmt %x)", + opt->tst, opt->tmf_only, opt->queue_alg, opt->qerr, + opt->swp, opt->tas, opt->d_sense, opt->has_own_order_mgmt); + } + #if 1 if ((dev->tst != opt->tst) && (dev->sdev != NULL) && !list_empty(&dev->sdev->dev_tgt_dev_list)) { @@ -3387,14 +3411,18 @@ static int __dev_user_set_opt(struct scst_user_dev *dev, dev->partial_len = opt->partial_len; dev->tst = opt->tst; + dev->tmf_only = opt->tmf_only; dev->queue_alg = opt->queue_alg; + dev->qerr = opt->qerr; dev->swp = opt->swp; dev->tas = opt->tas; dev->d_sense = opt->d_sense; dev->has_own_order_mgmt = opt->has_own_order_mgmt; if (dev->sdev != NULL) { dev->sdev->tst = opt->tst; + dev->sdev->tmf_only = opt->tmf_only; dev->sdev->queue_alg = opt->queue_alg; + dev->sdev->qerr = opt->qerr; dev->sdev->swp = opt->swp; dev->sdev->tas = opt->tas; dev->sdev->d_sense = opt->d_sense; @@ -3465,7 +3493,9 @@ static int dev_user_get_opt(struct file *file, void __user *arg) opt.partial_transfers_type = dev->partial_transfers_type; opt.partial_len = dev->partial_len; opt.tst = dev->tst; + opt.tmf_only = dev->tmf_only; opt.queue_alg = dev->queue_alg; + opt.qerr = dev->qerr; opt.tas = dev->tas; opt.swp = dev->swp; opt.d_sense = dev->d_sense; diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 5519476bd..a72732ece 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -116,6 +116,7 @@ static struct scst_trace_log vdisk_local_trace_tbl[] = { #define VDISK_NULLIO_SIZE (5LL*1024*1024*1024*1024/2) #define DEF_TST SCST_TST_1_SEP_TASK_SETS +#define DEF_TMF_ONLY 0 /* * Since we can't control backstorage device's reordering, we have to always @@ -123,9 +124,10 @@ static struct scst_trace_log vdisk_local_trace_tbl[] = { */ #define DEF_QUEUE_ALG_WT SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER #define DEF_QUEUE_ALG SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER + +#define DEF_QERR SCST_QERR_0_ALL_RESUME #define DEF_SWP 0 #define DEF_TAS 0 - #define DEF_DSENSE SCST_D_SENSE_0_FIXED_SENSE #ifdef CONFIG_SCST_PROC @@ -1294,6 +1296,9 @@ static int vdisk_attach(struct scst_device *dev) dev->dh_priv = virt_dev; dev->tst = virt_dev->tst; + dev->tmf_only = DEF_TMF_ONLY; + dev->tmf_only_saved = DEF_TMF_ONLY; + dev->tmf_only_default = DEF_TMF_ONLY; dev->d_sense = DEF_DSENSE; dev->d_sense_saved = DEF_DSENSE; dev->d_sense_default = DEF_DSENSE; @@ -1303,6 +1308,9 @@ static int vdisk_attach(struct scst_device *dev) dev->queue_alg = DEF_QUEUE_ALG; dev->queue_alg_saved = dev->queue_alg; dev->queue_alg_default = dev->queue_alg; + dev->qerr = DEF_QERR; + dev->qerr_saved = DEF_QERR; + dev->qerr_default = DEF_QERR; dev->swp = DEF_SWP; dev->swp_saved = DEF_SWP; dev->swp_default = DEF_SWP; @@ -3711,8 +3719,10 @@ static int vdisk_ctrl_m_pg(unsigned char *p, int pcontrol, switch (pcontrol) { case 0: /* current */ p[2] |= virt_dev->dev->tst << 5; + p[2] |= virt_dev->dev->tmf_only << 4; p[2] |= virt_dev->dev->d_sense << 2; p[3] |= virt_dev->dev->queue_alg << 4; + p[3] |= virt_dev->dev->qerr << 1; p[4] |= virt_dev->dev->swp << 3; p[5] |= virt_dev->dev->tas << 6; break; @@ -3726,21 +3736,27 @@ static int vdisk_ctrl_m_pg(unsigned char *p, int pcontrol, p[2] |= 7 << 5; /* TST */ #endif p[2] |= 1 << 2; /* D_SENSE */ + p[2] |= 1 << 4; /* TMF_ONLY */ p[3] |= 0xF << 4; /* QUEUE ALGORITHM MODIFIER */ + p[3] |= 3 << 1; /* QErr */ p[4] |= 1 << 3; /* SWP */ p[5] |= 1 << 6; /* TAS */ break; case 2: /* default */ p[2] |= virt_dev->tst << 5; p[2] |= virt_dev->dev->d_sense_default << 2; + p[2] |= virt_dev->dev->tmf_only_default << 4; p[3] |= virt_dev->dev->queue_alg_default << 4; + p[3] |= virt_dev->dev->qerr_default << 1; p[4] |= virt_dev->dev->swp_default << 3; p[5] |= virt_dev->dev->tas_default << 6; break; case 3: /* saved */ p[2] |= virt_dev->dev->tst << 5; p[2] |= virt_dev->dev->d_sense_saved << 2; + p[2] |= virt_dev->dev->tmf_only_default << 4; p[3] |= virt_dev->dev->queue_alg_saved << 4; + p[3] |= virt_dev->dev->qerr_saved << 1; p[4] |= virt_dev->dev->swp_saved << 3; p[5] |= virt_dev->dev->tas_saved << 6; break; @@ -3960,12 +3976,14 @@ out: } static void vdisk_ctrl_m_pg_select(unsigned char *p, - struct scst_vdisk_dev *virt_dev, struct scst_cmd *cmd, bool save) + struct scst_vdisk_dev *virt_dev, struct scst_cmd *cmd, bool save, + int param_offset) { struct scst_device *dev = virt_dev->dev; int old_swp = dev->swp, old_tas = dev->tas, old_dsense = dev->d_sense; int old_queue_alg = dev->queue_alg; - int rc; + int rc, old_tmf_only = dev->tmf_only, old_qerr = dev->qerr; + int queue_alg, swp, tas, tmf_only, qerr, d_sense; TRACE_ENTRY(); @@ -3977,6 +3995,11 @@ static void vdisk_ctrl_m_pg_select(unsigned char *p, goto out; } + /* + * MODE SELECT is a strictly serialized cmd, so it is safe to + * perform direct assignment here. + */ + #if 0 /* Not implemented yet, see comment in struct scst_device */ dev->tst = (p[2] >> 5) & 1; #else @@ -3988,13 +4011,79 @@ static void vdisk_ctrl_m_pg_select(unsigned char *p, goto out; } #endif - dev->queue_alg = p[3] >> 4; - dev->swp = (p[4] & 0x8) >> 3; - dev->tas = (p[5] & 0x40) >> 6; - dev->d_sense = (p[2] & 0x4) >> 2; + + queue_alg = p[3] >> 4; + if ((queue_alg != SCST_QUEUE_ALG_0_RESTRICTED_REORDER) && + (queue_alg != SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER)) { + PRINT_WARNING("Attempt to set invalid Control mode page QUEUE " + "ALGORITHM MODIFIER value %d (initiator %s, dev %s)", + queue_alg, cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 3, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 4); + goto out; + } + + swp = (p[4] & 0x8) >> 3; + if (swp > 1) { + PRINT_WARNING("Attempt to set invalid Control mode page SWP " + "value %d (initiator %s, dev %s)", swp, + cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 4, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 3); + goto out; + } + + tas = (p[5] & 0x40) >> 6; + if (tas > 1) { + PRINT_WARNING("Attempt to set invalid Control mode page TAS " + "value %d (initiator %s, dev %s)", tas, + cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 5, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 6); + goto out; + } + + tmf_only = (p[2] & 0x10) >> 4; + if (tmf_only > 1) { + PRINT_WARNING("Attempt to set invalid Control mode page " + "TMF_ONLY value %d (initiator %s, dev %s)", tmf_only, + cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 4); + goto out; + } + + qerr = (p[3] & 0x6) >> 1; + if ((qerr == SCST_QERR_2_RESERVED) || + (qerr > SCST_QERR_3_ABORT_THIS_NEXUS_ONLY)) { + PRINT_WARNING("Attempt to set invalid Control mode page QErr " + "value %d (initiator %s, dev %s)", qerr, + cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 3, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 1); + goto out; + } + + d_sense = (p[2] & 0x4) >> 2; + if (d_sense > 1) { + PRINT_WARNING("Attempt to set invalid Control mode page D_SENSE " + "value %d (initiator %s, dev %s)", d_sense, + cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 2); + goto out; + } + + dev->queue_alg = queue_alg; + dev->swp = swp; + dev->tas = tas; + dev->tmf_only = tmf_only; + dev->qerr = qerr; + dev->d_sense = d_sense; if ((dev->swp == old_swp) && (dev->tas == old_tas) && - (dev->d_sense == old_dsense) && (dev->queue_alg == old_queue_alg)) + (dev->d_sense == old_dsense) && (dev->queue_alg == old_queue_alg) && + (dev->qerr == old_qerr) && dev->tmf_only == old_tmf_only) goto out; if (!save) @@ -4006,6 +4095,8 @@ static void vdisk_ctrl_m_pg_select(unsigned char *p, dev->tas = old_tas; dev->d_sense = old_dsense; dev->queue_alg = old_queue_alg; + dev->tmf_only = old_tmf_only; + dev->qerr = old_qerr; /* Hopefully, the error is temporary */ scst_set_busy(cmd); goto out; @@ -4015,13 +4106,16 @@ static void vdisk_ctrl_m_pg_select(unsigned char *p, dev->tas_saved = dev->tas; dev->d_sense_saved = dev->d_sense; dev->queue_alg_saved = dev->queue_alg; + dev->tmf_only_saved = dev->tmf_only; + dev->qerr_saved = dev->qerr; out_ok: PRINT_INFO("Device %s: new control mode page parameters: SWP %x " - "(was %x), TAS %x (was %x), D_SENSE %d (was %d), " - "QUEUE ALG %d (was %d)", virt_dev->name, dev->swp, - old_swp, dev->tas, old_tas, dev->d_sense, old_dsense, - dev->queue_alg, old_queue_alg); + "(was %x), TAS %x (was %x), TMF_ONLY %d (was %x), QErr %x " + "(was %x), D_SENSE %d (was %d), QUEUE ALG %d (was %d)", + virt_dev->name, dev->swp, old_swp, dev->tas, old_tas, + dev->tmf_only, old_tmf_only, dev->qerr, old_qerr, + dev->d_sense, old_dsense, dev->queue_alg, old_queue_alg); out: TRACE_EXIT(); @@ -4141,7 +4235,7 @@ static enum compl_status_e vdisk_exec_mode_select(struct vdisk_cmd_params *p) goto out_put; } vdisk_ctrl_m_pg_select(&address[offset], virt_dev, cmd, - cmd->cdb[1] & SP); + cmd->cdb[1] & SP, offset); } else { TRACE(TRACE_MINOR, "MODE SELECT: Invalid request %x", address[offset] & 0x3f); @@ -5617,10 +5711,12 @@ static void vdisk_task_mgmt_fn_done(struct scst_mgmt_cmd *mcmd, struct scst_vdisk_dev *virt_dev = dev->dh_priv; int rc; + dev->tmf_only = dev->tmf_only_saved; dev->d_sense = dev->d_sense_saved; dev->swp = dev->swp_saved; dev->tas = dev->tas_saved; dev->queue_alg = dev->queue_alg_saved; + dev->qerr = dev->qerr_saved; dev->tst = virt_dev->tst; diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 9f42faca6..5da420247 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -2912,7 +2912,7 @@ int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd) if (cmd->internal) res = SCST_CMD_STATE_FINISHED_INTERNAL; else - res = SCST_CMD_STATE_PRE_XMIT_RESP; + res = SCST_CMD_STATE_PRE_XMIT_RESP1; break; case SCST_CMD_STATE_PRE_DEV_DONE: @@ -2920,14 +2920,18 @@ int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd) res = SCST_CMD_STATE_DEV_DONE; break; - case SCST_CMD_STATE_PRE_XMIT_RESP: + case SCST_CMD_STATE_PRE_XMIT_RESP1: + res = SCST_CMD_STATE_PRE_XMIT_RESP2; + break; + + case SCST_CMD_STATE_PRE_XMIT_RESP2: res = SCST_CMD_STATE_XMIT_RESP; break; case SCST_CMD_STATE_PREPROCESSING_DONE: case SCST_CMD_STATE_PREPROCESSING_DONE_CALLED: if (cmd->tgt_dev == NULL) - res = SCST_CMD_STATE_PRE_XMIT_RESP; + res = SCST_CMD_STATE_PRE_XMIT_RESP1; else res = SCST_CMD_STATE_PRE_DEV_DONE; break; @@ -3008,7 +3012,8 @@ void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd) case SCST_CMD_STATE_DEV_DONE: case SCST_CMD_STATE_PRE_DEV_DONE: case SCST_CMD_STATE_MODE_SELECT_CHECKS: - case SCST_CMD_STATE_PRE_XMIT_RESP: + case SCST_CMD_STATE_PRE_XMIT_RESP1: + case SCST_CMD_STATE_PRE_XMIT_RESP2: case SCST_CMD_STATE_FINISHED_INTERNAL: break; default: @@ -3019,7 +3024,7 @@ void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd) } #ifdef CONFIG_SCST_EXTRACHECKS - if (((cmd->state != SCST_CMD_STATE_PRE_XMIT_RESP) && + if (((cmd->state != SCST_CMD_STATE_PRE_XMIT_RESP1) && (cmd->state != SCST_CMD_STATE_PREPROCESSING_DONE)) && (cmd->tgt_dev == NULL) && !cmd->internal) { PRINT_CRIT_ERROR("Wrong not inited cmd state %d (cmd %p, " @@ -8737,12 +8742,16 @@ int scst_obtain_device_parameters(struct scst_device *dev, "page data", buffer, sizeof(buffer)); dev->tst = buffer[4+2] >> 5; + dev->tmf_only = (buffer[4+2] & 0x10) >> 4; q = buffer[4+3] >> 4; if (q > SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER) { - PRINT_ERROR("Too big QUEUE ALG %x, dev %s", + PRINT_ERROR("Too big QUEUE ALG %x, dev %s, " + "using default: unrestricted reorder", dev->queue_alg, dev->virt_name); + q = SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER; } dev->queue_alg = q; + dev->qerr = (buffer[4+3] & 0x6) >> 1; dev->swp = (buffer[4+4] & 0x8) >> 3; dev->tas = (buffer[4+5] & 0x40) >> 6; dev->d_sense = (buffer[4+2] & 0x4) >> 2; @@ -8755,10 +8764,11 @@ int scst_obtain_device_parameters(struct scst_device *dev, */ dev->has_own_order_mgmt = !dev->queue_alg; - PRINT_INFO("Device %s: TST %x, QUEUE ALG %x, SWP %x, " - "TAS %x, D_SENSE %d, has_own_order_mgmt %d", - dev->virt_name, dev->tst, dev->queue_alg, - dev->swp, dev->tas, dev->d_sense, + PRINT_INFO("Device %s: TST %x, TMF_ONLY %x, QUEUE ALG %x, " + "QErr %x, SWP %x, TAS %x, D_SENSE %d, " + "has_own_order_mgmt %d", dev->virt_name, + dev->tst, dev->tmf_only, dev->queue_alg, + dev->qerr, dev->swp, dev->tas, dev->d_sense, dev->has_own_order_mgmt); goto out; @@ -8820,9 +8830,10 @@ int scst_obtain_device_parameters(struct scst_device *dev, } brk: PRINT_WARNING("Unable to get device's %s control mode page, using " - "existing values/defaults: TST %x, QUEUE ALG %x, SWP %x, " - "TAS %x, D_SENSE %d, has_own_order_mgmt %d", dev->virt_name, - dev->tst, dev->queue_alg, dev->swp, dev->tas, dev->d_sense, + "existing values/defaults: TST %x, TMF_ONLY %x, QUEUE ALG %x, " + "QErr %x, SWP %x, TAS %x, D_SENSE %d, has_own_order_mgmt %d", + dev->virt_name, dev->tst, dev->tmf_only, dev->queue_alg, + dev->qerr, dev->swp, dev->tas, dev->d_sense, dev->has_own_order_mgmt); out: @@ -8890,6 +8901,152 @@ void scst_store_sense(struct scst_cmd *cmd) return; } +/* dev_lock supposed to be locked and BHs off */ +static void scst_abort_cmds_tgt_dev(struct scst_tgt_dev *tgt_dev, + struct scst_cmd *exclude_cmd) +{ + struct scst_session *sess = tgt_dev->sess; + struct scst_cmd *cmd; + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("Aborting commands for tgt_dev %p (exclude_cmd %p)", + tgt_dev, exclude_cmd); + + /* IRQs supposed to be already locked */ + spin_lock(&sess->sess_list_lock); + + list_for_each_entry(cmd, &sess->sess_cmd_list, sess_cmd_list_entry) { + if (cmd == exclude_cmd) + continue; + if ((cmd->tgt_dev == tgt_dev) || + ((cmd->tgt_dev == NULL) && + (cmd->lun == tgt_dev->lun))) { + scst_abort_cmd(cmd, NULL, (tgt_dev != exclude_cmd->tgt_dev), 0); + } + } + spin_unlock(&sess->sess_list_lock); + + TRACE_EXIT(); + return; +} + +/* dev_lock supposed to be locked and BHs off */ +static void scst_abort_cmds_dev(struct scst_device *dev, + struct scst_cmd *exclude_cmd) +{ + struct scst_tgt_dev *tgt_dev; + uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; + int sl = 0; + bool set_ua = (dev->tas == 0); + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("Aborting commands for dev %p (exclude_cmd %p, set_ua %d)", + dev, exclude_cmd, set_ua); + + if (set_ua) + sl = scst_set_sense(sense_buffer, sizeof(sense_buffer), dev->d_sense, + SCST_LOAD_SENSE(scst_sense_cleared_by_another_ini_UA)); + + list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list, dev_tgt_dev_list_entry) { + scst_abort_cmds_tgt_dev(tgt_dev, exclude_cmd); + /* + * Potentially, setting UA here, when the aborted commands are + * still running, can lead to a situation that one of them could + * take it, then that would be detected and the UA requeued. + * But, meanwhile, one or more subsequent, i.e. not aborted, + * commands can "leak" executed normally. So, as result, the + * UA would be delivered one or more commands "later". However, + * that should be OK, because, if multiple commands are being + * executed in parallel, you can't control exact order of UA + * delivery anyway. + */ + if (set_ua && (tgt_dev != exclude_cmd->tgt_dev)) + scst_check_set_UA(tgt_dev, sense_buffer, sl, 0); + } + + TRACE_EXIT(); + return; +} + +/* No locks */ +static void scst_process_qerr(struct scst_cmd *cmd) +{ + bool unblock = false; + struct scst_device *dev = cmd->dev; + unsigned int qerr, q; + + TRACE_ENTRY(); + + /* dev->qerr can be changed behind our back */ + q = dev->qerr; + qerr = ACCESS_ONCE(q); /* ACCESS_ONCE doesn't work for bit bilds */ + + TRACE_DBG("Processing QErr %d for cmd %p", qerr, cmd); + + spin_lock_bh(&dev->dev_lock); + + switch (qerr) { + case SCST_QERR_2_RESERVED: + default: + PRINT_WARNING("Invalid QErr value %x for device %s, process as " + "0", qerr, dev->virt_name); + /* go through */ + case SCST_QERR_0_ALL_RESUME: + /* Nothing to do */ + break; + case SCST_QERR_1_ABORT_ALL: + if (dev->tst == SCST_TST_0_SINGLE_TASK_SET) + scst_abort_cmds_dev(dev, cmd); + else + scst_abort_cmds_tgt_dev(cmd->tgt_dev, cmd); + unblock = true; + break; + case SCST_QERR_3_ABORT_THIS_NEXUS_ONLY: + scst_abort_cmds_tgt_dev(cmd->tgt_dev, cmd); + unblock = true; + break; + } + + spin_unlock_bh(&dev->dev_lock); + + if (unblock) + scst_unblock_aborted_cmds(cmd->tgt, cmd->sess, dev, false); + + TRACE_EXIT(); + return; +} + +/* + * No locks. Returns -1, if processing should be switched to another cmd, 1 + * if cmd was aborted, 0 if cmd processing should continue. + */ +int scst_process_check_condition(struct scst_cmd *cmd) +{ + int res; + struct scst_order_data *order_data; + struct scst_device *dev; + + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(test_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags)); + + order_data = cmd->cur_order_data; + dev = cmd->dev; + + TRACE_DBG("CHECK CONDITION for cmd %p (tgt_dev %p)", cmd, cmd->tgt_dev); + + scst_process_qerr(cmd); + + scst_store_sense(cmd); + + res = 0; + + TRACE_EXIT_RES(res); + return res; +} + void scst_xmit_process_aborted_cmd(struct scst_cmd *cmd) { TRACE_ENTRY(); @@ -9273,6 +9430,8 @@ static void scst_free_descriptors(struct scst_cmd *cmd) **/ #define SCST_TAS_LABEL "TAS" +#define SCST_QERR_LABEL "QERR" +#define SCST_TMF_ONLY_LABEL "TMF_ONLY" #define SCST_SWP_LABEL "SWP" #define SCST_DSENSE_LABEL "D_SENSE" #define SCST_QUEUE_ALG_LABEL "QUEUE_ALG" @@ -9291,6 +9450,20 @@ int scst_save_global_mode_pages(const struct scst_device *dev, goto out_overflow; } + if (dev->qerr != dev->qerr_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_QERR_LABEL, dev->qerr); + if (res >= size-1) + goto out_overflow; + } + + if (dev->tmf_only != dev->tmf_only_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_TMF_ONLY_LABEL, dev->tmf_only); + if (res >= size-1) + goto out_overflow; + } + if (dev->swp != dev->swp_default) { res += scnprintf(&buf[res], size - res, "%s=%d\n", SCST_SWP_LABEL, dev->swp); @@ -9349,6 +9522,59 @@ out: return res; } +static int scst_restore_qerr(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if ((val == SCST_QERR_2_RESERVED) || + (val > SCST_QERR_3_ABORT_THIS_NEXUS_ONLY)) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_QERR_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->qerr = val; + dev->qerr_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_QERR_LABEL, + dev->qerr, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_restore_tmf_only(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if (val > 1) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_TMF_ONLY_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->tmf_only = val; + dev->tmf_only_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_TMF_ONLY_LABEL, + dev->tmf_only, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + static int scst_restore_swp(struct scst_device *dev, unsigned int val) { int res; @@ -9464,6 +9690,10 @@ int scst_restore_global_mode_pages(struct scst_device *dev, char *params, if (strcasecmp(SCST_TAS_LABEL, p) == 0) res = scst_restore_tas(dev, val); + else if (strcasecmp(SCST_QERR_LABEL, p) == 0) + res = scst_restore_qerr(dev, val); + else if (strcasecmp(SCST_TMF_ONLY_LABEL, p) == 0) + res = scst_restore_tmf_only(dev, val); else if (strcasecmp(SCST_SWP_LABEL, p) == 0) res = scst_restore_swp(dev, val); else if (strcasecmp(SCST_DSENSE_LABEL, p) == 0) diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index fb7c92fbb..9f5a3c6ed 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -375,6 +375,8 @@ int scst_set_cmd_error_sense(struct scst_cmd *cmd, uint8_t *sense, unsigned int len); void scst_store_sense(struct scst_cmd *cmd); +int scst_process_check_condition(struct scst_cmd *cmd); + int scst_assign_dev_handler(struct scst_device *dev, struct scst_dev_type *handler); diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index b68e646b4..402a9de3d 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -577,12 +577,12 @@ int scst_pre_parse(struct scst_cmd *cmd) TRACE_DBG("op_name <%s> (cmd %p), direction=%d " "(expected %d, set %s), lba %lld, bufflen=%d, data_len %lld, " "out_bufflen=%d (expected len %d, out expected len %d), " - "flags=0x%x", cmd->op_name, cmd, cmd->data_direction, + "flags=0x%x, naca %d", cmd->op_name, cmd, cmd->data_direction, cmd->expected_data_direction, scst_cmd_is_expected_set(cmd) ? "yes" : "no", (long long)cmd->lba, cmd->bufflen, (long long)cmd->data_len, cmd->out_bufflen, cmd->expected_transfer_len, - cmd->expected_out_transfer_len, cmd->op_flags); + cmd->expected_out_transfer_len, cmd->op_flags, cmd->cmd_naca); res = 0; @@ -893,7 +893,8 @@ set_res: case SCST_CMD_STATE_REAL_EXEC: case SCST_CMD_STATE_PRE_DEV_DONE: case SCST_CMD_STATE_DEV_DONE: - case SCST_CMD_STATE_PRE_XMIT_RESP: + case SCST_CMD_STATE_PRE_XMIT_RESP1: + case SCST_CMD_STATE_PRE_XMIT_RESP2: case SCST_CMD_STATE_XMIT_RESP: case SCST_CMD_STATE_FINISHED: case SCST_CMD_STATE_FINISHED_INTERNAL: @@ -1304,7 +1305,9 @@ void scst_restart_cmd(struct scst_cmd *cmd, int status, break; case SCST_PREPROCESS_STATUS_ERROR_FATAL: + set_bit(SCST_CMD_ABORTED, &cmd->cmd_flags); set_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags); + cmd->delivery_status = SCST_CMD_DELIVERY_FAILED; /* go through */ case SCST_PREPROCESS_STATUS_ERROR: if (cmd->sense != NULL) @@ -1559,7 +1562,9 @@ void scst_rx_data(struct scst_cmd *cmd, int status, break; case SCST_RX_STATUS_ERROR_FATAL: + set_bit(SCST_CMD_ABORTED, &cmd->cmd_flags); set_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags); + cmd->delivery_status = SCST_CMD_DELIVERY_FAILED; /* go through */ case SCST_RX_STATUS_ERROR: if (!cmd->write_not_received_set) @@ -1679,7 +1684,9 @@ static int scst_tgt_pre_exec(struct scst_cmd *cmd) scst_set_cmd_abnormal_done_state(cmd); goto out; case SCST_PREPROCESS_STATUS_ERROR_FATAL: + set_bit(SCST_CMD_ABORTED, &cmd->cmd_flags); set_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags); + cmd->delivery_status = SCST_CMD_DELIVERY_FAILED; /* go through */ case SCST_PREPROCESS_STATUS_ERROR: scst_set_cmd_error(cmd, @@ -1808,7 +1815,8 @@ static void scst_cmd_done_local(struct scst_cmd *cmd, int next_state, #ifdef CONFIG_SCST_EXTRACHECKS if ((next_state != SCST_CMD_STATE_PRE_DEV_DONE) && - (next_state != SCST_CMD_STATE_PRE_XMIT_RESP) && + (next_state != SCST_CMD_STATE_PRE_XMIT_RESP1) && + (next_state != SCST_CMD_STATE_PRE_XMIT_RESP2) && (next_state != SCST_CMD_STATE_FINISHED) && (next_state != SCST_CMD_STATE_FINISHED_INTERNAL)) { PRINT_ERROR("%s() received invalid cmd state %d (opcode %d)", @@ -3723,7 +3731,7 @@ static int scst_dev_done(struct scst_cmd *cmd) TRACE_ENTRY(); - state = SCST_CMD_STATE_PRE_XMIT_RESP; + state = SCST_CMD_STATE_PRE_XMIT_RESP1; if (likely((cmd->op_flags & SCST_FULLY_LOCAL_CMD) == 0) && likely(devt->dev_done != NULL)) { @@ -3754,7 +3762,8 @@ static int scst_dev_done(struct scst_cmd *cmd) switch (state) { #ifdef CONFIG_SCST_EXTRACHECKS - case SCST_CMD_STATE_PRE_XMIT_RESP: + case SCST_CMD_STATE_PRE_XMIT_RESP1: + case SCST_CMD_STATE_PRE_XMIT_RESP2: case SCST_CMD_STATE_PARSE: case SCST_CMD_STATE_PREPARE_SPACE: case SCST_CMD_STATE_RDY_TO_XFER: @@ -3809,7 +3818,8 @@ static int scst_dev_done(struct scst_cmd *cmd) cmd->state = SCST_CMD_STATE_FINISHED_INTERNAL; #ifndef CONFIG_SCST_TEST_IO_IN_SIRQ - if (cmd->state != SCST_CMD_STATE_PRE_XMIT_RESP) { +#ifdef CONFIG_SCST_EXTRACHECKS + if (cmd->state != SCST_CMD_STATE_PRE_XMIT_RESP1) { /* We can't allow atomic command on the exec stages */ if (scst_cmd_atomic(cmd)) { switch (state) { @@ -3826,13 +3836,58 @@ static int scst_dev_done(struct scst_cmd *cmd) } } #endif +#endif out: TRACE_EXIT_HRES(res); return res; } -static int scst_pre_xmit_response(struct scst_cmd *cmd) +static int scst_pre_xmit_response2(struct scst_cmd *cmd) +{ + int res; + + TRACE_ENTRY(); + +again: + if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) + scst_xmit_process_aborted_cmd(cmd); + else if (unlikely(cmd->status == SAM_STAT_CHECK_CONDITION)) { + if (cmd->tgt_dev != NULL) { + int rc = scst_process_check_condition(cmd); + /* !! At this point cmd can be already dead !! */ + if (rc == -1) { + res = SCST_CMD_STATE_RES_CONT_NEXT; + goto out; + } else if (rc == 1) + goto again; + } + } + + if (unlikely(test_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags))) { + EXTRACHECKS_BUG_ON(!test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)); + TRACE_MGMT_DBG("Flag NO_RESP set for cmd %p (tag %llu), " + "skipping", cmd, (long long unsigned int)cmd->tag); + cmd->state = SCST_CMD_STATE_FINISHED; + goto out_same; + } + + if (unlikely(cmd->resid_possible)) + scst_adjust_resp_data_len(cmd); + else + cmd->adjusted_resp_data_len = cmd->resp_data_len; + + cmd->state = SCST_CMD_STATE_XMIT_RESP; + +out_same: + res = SCST_CMD_STATE_RES_CONT_SAME; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_pre_xmit_response1(struct scst_cmd *cmd) { int res; @@ -3880,29 +3935,10 @@ static int scst_pre_xmit_response(struct scst_cmd *cmd) cmd->done = 1; smp_mb(); /* to sync with scst_abort_cmd() */ - if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) - scst_xmit_process_aborted_cmd(cmd); - else if (unlikely(cmd->status == SAM_STAT_CHECK_CONDITION)) - scst_store_sense(cmd); + cmd->state = SCST_CMD_STATE_PRE_XMIT_RESP2; + res = scst_pre_xmit_response2(cmd); - if (unlikely(test_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags))) { - TRACE_MGMT_DBG("Flag NO_RESP set for cmd %p (tag %llu), " - "skipping", cmd, (long long unsigned int)cmd->tag); - cmd->state = SCST_CMD_STATE_FINISHED; - res = SCST_CMD_STATE_RES_CONT_SAME; - goto out; - } - - if (unlikely(cmd->resid_possible)) - scst_adjust_resp_data_len(cmd); - else - cmd->adjusted_resp_data_len = cmd->resp_data_len; - - cmd->state = SCST_CMD_STATE_XMIT_RESP; - res = SCST_CMD_STATE_RES_CONT_SAME; - -out: - TRACE_EXIT_HRES(res); + TRACE_EXIT_RES(res); return res; } @@ -4073,6 +4109,7 @@ static int scst_finish_cmd(struct scst_cmd *cmd) if (unlikely(cmd->delivery_status != SCST_CMD_DELIVERY_SUCCESS)) { if ((cmd->tgt_dev != NULL) && + (cmd->status == SAM_STAT_CHECK_CONDITION) && scst_is_ua_sense(cmd->sense, cmd->sense_valid_len)) { /* This UA delivery failed, so we need to requeue it */ if (scst_cmd_atomic(cmd) && @@ -4766,10 +4803,14 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic) res = scst_dev_done(cmd); break; - case SCST_CMD_STATE_PRE_XMIT_RESP: - res = scst_pre_xmit_response(cmd); - EXTRACHECKS_BUG_ON(res == - SCST_CMD_STATE_RES_NEED_THREAD); + case SCST_CMD_STATE_PRE_XMIT_RESP1: + res = scst_pre_xmit_response1(cmd); + EXTRACHECKS_BUG_ON(res == SCST_CMD_STATE_RES_NEED_THREAD); + break; + + case SCST_CMD_STATE_PRE_XMIT_RESP2: + res = scst_pre_xmit_response2(cmd); + EXTRACHECKS_BUG_ON(res == SCST_CMD_STATE_RES_NEED_THREAD); break; case SCST_CMD_STATE_XMIT_RESP: diff --git a/usr/fileio/fileio.c b/usr/fileio/fileio.c index 43b314fd9..083337c2e 100644 --- a/usr/fileio/fileio.c +++ b/usr/fileio/fileio.c @@ -384,7 +384,9 @@ int start(int argc, char **argv) desc.opt.memory_reuse_type = memory_reuse_type; desc.opt.tst = SCST_TST_1_SEP_TASK_SETS; + desc.opt.tmf_only = 0; desc.opt.queue_alg = SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER; + desc.opt.qerr = 0; desc.opt.d_sense = SCST_D_SENSE_0_FIXED_SENSE; res = ioctl(devs[i].scst_usr_fd, SCST_USER_REGISTER_DEVICE, &desc);