From 42459a6725afa4d692644b5806ef19c1de2f135d Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 20 Dec 2016 06:10:56 +0000 Subject: [PATCH] scst: add ACA support ACA (Auto Contingent Allegiance) is a way to handle CHECK CONDITION status. It significantly improves performance of AIX systems. git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@7072 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/include/scst.h | 27 +- scst/include/scst_const.h | 7 - scst/src/dev_handlers/scst_vdisk.c | 3 +- scst/src/scst_lib.c | 114 +++++++- scst/src/scst_priv.h | 1 + scst/src/scst_targ.c | 426 ++++++++++++++++++++++++----- 6 files changed, 493 insertions(+), 85 deletions(-) diff --git a/scst/include/scst.h b/scst/include/scst.h index 33b5c6159..3c7fd2716 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -469,7 +469,7 @@ enum scst_exec_context { /* * Set if no response should be sent to the target about this cmd. - * Must be set together with SCST_CMD_ABORTED for better processing + * Must be set together with SCST_CMD_ABORTED for better ACA processing * in scst_pre_xmit_response2(). */ #define SCST_CMD_NO_RESP 2 @@ -790,13 +790,6 @@ struct scst_tgt_template { unsigned enabled_attr_not_needed:1; #endif - /* - * True if SCST should report that it supports ACA although it does - * not yet support ACA. Necessary for the IBM virtual SCSI target - * driver. - */ - unsigned fake_aca:1; - /* * True, if this target adapter can call scst_cmd_init_done() from * several threads at the same time. @@ -2107,13 +2100,22 @@ struct scst_order_data { struct list_head skipped_sn_list; struct list_head deferred_cmd_list; - spinlock_t sn_lock; + spinlock_t sn_lock; /* IRQ lock */ int hq_cmd_count; /* Set if the prev cmd was ORDERED */ bool prev_cmd_ordered; + /* + * tgt_dev initiated ACA, if any, or 0 otherwise. It can be deleted + * and freed during LUN deletion, so must not be dereferenced. + */ + unsigned long aca_tgt_dev; + + /* Active ACA cmd, if any */ + struct scst_cmd *aca_cmd; + int def_cmd_count; unsigned int expected_sn; unsigned int curr_sn; @@ -2224,6 +2226,9 @@ struct scst_cmd { /* Set if cmd has NACA bit set in CDB */ unsigned int cmd_naca:1; + /* Set if cmd was allowed during ACA */ + unsigned int cmd_aca_allowed:1; + /* * Set if the target driver wants to alloc data buffers on its own. * In this case tgt_alloc_data_buf() must be provided in the target @@ -2356,7 +2361,7 @@ struct scst_cmd { unsigned long start_time; - /* List entry for tgt_dev's deferred (SN, etc.) lists */ + /* List entry for tgt_dev's deferred (SN, ACA, etc.) lists */ struct list_head deferred_cmd_list_entry; /* Cmd's serial number, used to execute cmd's in order of arrival */ @@ -3480,7 +3485,7 @@ struct scst_aen { int delivery_status; }; -#define SCST_OD_DEFAULT_CONTROL_BYTE 0 +#define SCST_OD_DEFAULT_CONTROL_BYTE 4 /* NACA */ struct scst_opcode_descriptor { uint16_t od_serv_action; diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index a8f2824a7..aa9e17208 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -513,13 +513,6 @@ static inline int scst_sense_response_code(const uint8_t *sense) *************************************************************/ #define SCST_INQ_EVPD 0x01 -/************************************************************* - ** Byte 3 in Standard INQUIRY data - *************************************************************/ -#define SCST_INQ_BYTE3 3 - -#define SCST_INQ_NORMACA_BIT 0x20 - /************************************************************* ** TPGS field in byte 5 of the INQUIRY response (SPC-4). *************************************************************/ diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 7e6fb39dc..356eb1a0a 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -4322,8 +4322,7 @@ static int vdisk_inq(uint8_t *buf, struct scst_cmd *cmd, buf[1] = 0x80; /* removable */ buf[2] = 6; /* Device complies to SPC-4 */ buf[3] = 0x02; /* Data in format specified in SPC */ - if (cmd->tgtt->fake_aca) - buf[3] |= 0x20; + buf[3] |= 0x20; /* ACA supported */ buf[4] = 31;/* n - 4 = 35 - 4 = 31 for full 36 byte data */ if (cmd->dev->dev_dif_mode != SCST_DIF_MODE_NONE) buf[5] |= 1; /* PROTECT */ diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 9daf5843c..83eae90a3 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -12473,6 +12473,15 @@ void scst_process_reset(struct scst_device *dev, spin_unlock_irq(&sess->sess_list_lock); } + /* + * We need at first abort all affected commands and only then + * release them as part of clearing ACA + */ + list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list, + dev_tgt_dev_list_entry) { + scst_clear_aca(tgt_dev, (tgt_dev->sess != originator)); + } + if (setUA) { uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; int sl = scst_set_sense(sense_buffer, sizeof(sense_buffer), @@ -12872,9 +12881,27 @@ struct scst_cmd *__scst_check_deferred_commands_locked( restart: list_for_each_entry_safe(cmd, t, &order_data->deferred_cmd_list, deferred_cmd_list_entry) { - EXTRACHECKS_BUG_ON((cmd->queue_type != SCST_CMD_QUEUE_SIMPLE) && - (cmd->queue_type != SCST_CMD_QUEUE_ORDERED)); - if (cmd->sn == expected_sn) { + EXTRACHECKS_BUG_ON(cmd->queue_type == SCST_CMD_QUEUE_ACA); + + if (unlikely(order_data->aca_tgt_dev != 0)) { + if (!test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) { + /* To prevent defer/release storms during ACA */ + continue; + } + } + + if (unlikely(cmd->done)) { + TRACE_MGMT_DBG("Releasing deferred done cmd %p", cmd); + order_data->def_cmd_count--; + list_del(&cmd->deferred_cmd_list_entry); + + spin_lock(&cmd->cmd_threads->cmd_list_lock); + TRACE_DBG("Adding cmd %p to active cmd list", cmd); + list_add_tail(&cmd->cmd_list_entry, + &cmd->cmd_threads->active_cmd_list); + wake_up(&cmd->cmd_threads->cmd_list_waitQ); + spin_unlock(&cmd->cmd_threads->cmd_list_lock); + } else if ((cmd->sn == expected_sn) || !cmd->sn_set) { bool stop = (cmd->sn_slot == NULL); TRACE_SN("Deferred command %p (sn %d, set %d) found", @@ -13480,21 +13507,100 @@ static void scst_process_qerr(struct scst_cmd *cmd) 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)); - TRACE_DBG("CHECK CONDITION for cmd %p (tgt_dev %p)", cmd, cmd->tgt_dev); + order_data = cmd->cur_order_data; + dev = cmd->dev; + TRACE((order_data->aca_tgt_dev != 0) ? TRACE_MGMT_DEBUG : TRACE_DEBUG, + "CHECK CONDITION for cmd %p (naca %d, cmd_aca_allowed %d, " + "ACA allowed cmd %d, tgt_dev %p, aca_tgt_dev %lu, aca_cmd %p)", + cmd, cmd->cmd_naca, cmd->cmd_aca_allowed, + cmd->queue_type == SCST_CMD_QUEUE_ACA, cmd->tgt_dev, + order_data->aca_tgt_dev, order_data->aca_cmd); + + spin_lock_irq(&order_data->sn_lock); + + if (order_data->aca_tgt_dev != 0) { + if (((cmd->queue_type == SCST_CMD_QUEUE_ACA) && + (order_data->aca_tgt_dev == (unsigned long)cmd->tgt_dev)) || + ((cmd->cdb[0] == PERSISTENT_RESERVE_OUT) && + ((cmd->cdb[1] & 0x1f) == PR_PREEMPT_AND_ABORT))) { + if (!cmd->cmd_naca) { + if (order_data->aca_cmd == cmd) { + /* + * Clear it to prevent from be + * aborted during ACA clearing + */ + TRACE_MGMT_DBG("Check condition of ACA " + "cmd %p", cmd); + order_data->aca_cmd = NULL; + } + spin_unlock_irq(&order_data->sn_lock); + scst_clear_aca(cmd->tgt_dev, + (order_data->aca_tgt_dev != (unsigned long)cmd->tgt_dev)); + /* + * Goto directly to avoid race when ACA + * reestablished during retaking sn_lock + * once again. + */ + goto process_qerr; + } + } + if (test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) { + /* + * cmd can be aborted and the unblock + * procedure finished while we were + * entering here. I.e. cmd can not be + * blocked anymore for any case. + */ + res = 1; + goto out_unlock; + } else if (!cmd->cmd_aca_allowed) { + TRACE_MGMT_DBG("Deferring CHECK CONDITION " + "cmd %p due to ACA active (tgt_dev %p)", + cmd, cmd->tgt_dev); + order_data->def_cmd_count++; + /* + * Put cmd in the head to let restart earlier: + * it is already completed and completed with + * CHECK CONDITION + */ + list_add(&cmd->deferred_cmd_list_entry, + &order_data->deferred_cmd_list); + res = -1; + goto out_unlock; + } + } + + if (cmd->cmd_naca) { + TRACE_MGMT_DBG("Establishing ACA for dev %s (lun %lld, cmd %p, " + "tgt_dev %p)", dev->virt_name, (unsigned long long)cmd->lun, + cmd, cmd->tgt_dev); + order_data->aca_tgt_dev = (unsigned long)cmd->tgt_dev; + } + + spin_unlock_irq(&order_data->sn_lock); + +process_qerr: scst_process_qerr(cmd); scst_store_sense(cmd); res = 0; +out: TRACE_EXIT_RES(res); return res; + +out_unlock: + spin_unlock_irq(&order_data->sn_lock); + goto out; } void scst_xmit_process_aborted_cmd(struct scst_cmd *cmd) diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index 9c5638ba3..6f31b3135 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -712,6 +712,7 @@ void scst_process_reset(struct scst_device *dev, void scst_unblock_aborted_cmds(const struct scst_tgt *tgt, const struct scst_session *sess, const struct scst_device *device, bool scst_mutex_held); +void scst_clear_aca(struct scst_tgt_dev *tgt_dev, bool other_ini); bool scst_is_ua_global(const uint8_t *sense, int len); void scst_requeue_ua(struct scst_cmd *cmd, const uint8_t *buf, int size); diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 5d27607e9..ee376680e 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -877,7 +877,7 @@ void scst_cmd_init_done(struct scst_cmd *cmd, spin_unlock_irqrestore(&sess->sess_list_lock, flags); - if (unlikely(cmd->queue_type >= SCST_CMD_QUEUE_ACA)) { + if (unlikely(cmd->queue_type > SCST_CMD_QUEUE_ACA)) { PRINT_ERROR("Unsupported queue type %d", cmd->queue_type); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_message)); @@ -976,7 +976,7 @@ int scst_pre_parse(struct scst_cmd *cmd) #ifdef CONFIG_SCST_STRICT_SERIALIZING cmd->inc_expected_sn_on_done = 1; #else - cmd->inc_expected_sn_on_done = devt->exec_sync || + cmd->inc_expected_sn_on_done = devt->exec_sync || cmd->cmd_naca || (!dev->has_own_order_mgmt && (dev->queue_alg == SCST_QUEUE_ALG_0_RESTRICTED_REORDER || cmd->queue_type == SCST_CMD_QUEUE_ORDERED)); @@ -1162,14 +1162,6 @@ static int scst_parse_cmd(struct scst_cmd *cmd) cmd->op_flags &= ~SCST_UNKNOWN_LENGTH; } - if (unlikely(cmd->cmd_naca)) { - PRINT_ERROR("NACA bit in control byte CDB is not supported " - "(opcode 0x%02x)", cmd->cdb[0]); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_message)); - goto out_done; - } - if (unlikely(cmd->cmd_linked)) { PRINT_ERROR("Linked commands are not supported " "(opcode %s)", scst_get_opcode_name(cmd)); @@ -2553,7 +2545,7 @@ static int scst_report_supported_tm_fns(struct scst_cmd *cmd) memset(buf, 0, sizeof(buf)); - buf[0] = 0xD8; /* ATS, ATSS, CTSS, LURS */ + buf[0] = 0xF8; /* ATS, ATSS, CACAS, CTSS, LURS */ buf[1] = 0; if ((cmd->cdb[2] & 0x80) == 0) resp_len = 4; @@ -2561,7 +2553,7 @@ static int scst_report_supported_tm_fns(struct scst_cmd *cmd) buf[3] = 0x0C; #if 1 buf[4] = 1; /* TMFTMOV */ - buf[6] = 0x80; /* ATTS */ + buf[6] = 0xA0; /* ATTS, CACATS */ put_unaligned_be32(300, &buf[8]); /* long timeout - 30 sec. */ put_unaligned_be32(150, &buf[12]); /* short timeout - 15 sec. */ #endif @@ -3811,9 +3803,79 @@ static int scst_exec_check_sn(struct scst_cmd **active_cmd) if (unlikely(cmd->internal)) goto exec; + if (unlikely(order_data->aca_tgt_dev != 0)) { + if (!cmd->cmd_aca_allowed) { + spin_lock_irq(&order_data->sn_lock); + if (test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) { + /* + * cmd can be aborted and the unblock + * procedure finished while we were + * entering here. I.e. cmd can not be ACA + * blocked/deferred anymore for any case, + * hence let it pass through. + */ + } else if (order_data->aca_tgt_dev != 0) { + unsigned int qerr, q; + bool this_nex = ((unsigned long)cmd->tgt_dev == order_data->aca_tgt_dev); + + /* + * Commands can potentially "leak" from + * scst_process_check_condition() after + * establishing ACA due to separate locks, so + * let's catch such "leaked" commands here. + * In any case, if QErr requests them to be + * aborted, they must not be deferred/blocked. + */ + + /* dev->qerr can be changed behind our back */ + q = cmd->dev->qerr; + /* ACCESS_ONCE doesn't work for bit fields */ + qerr = ACCESS_ONCE(q); + + switch (qerr) { + case SCST_QERR_2_RESERVED: + default: + case SCST_QERR_0_ALL_RESUME: +defer: + TRACE_MGMT_DBG("Deferring cmd %p due to " + "ACA active (tgt_dev %p)", cmd, + cmd->tgt_dev); + order_data->def_cmd_count++; + list_add_tail(&cmd->deferred_cmd_list_entry, + &order_data->deferred_cmd_list); + spin_unlock_irq(&order_data->sn_lock); + res = SCST_CMD_STATE_RES_CONT_NEXT; + goto out; + case SCST_QERR_3_ABORT_THIS_NEXUS_ONLY: + if (!this_nex) + goto defer; + /* else go through */ + case SCST_QERR_1_ABORT_ALL: + TRACE_MGMT_DBG("Aborting cmd %p due to " + "ACA active (tgt_dev %p)", cmd, + cmd->tgt_dev); + scst_abort_cmd(cmd, NULL, !this_nex, 0); + scst_set_cmd_abnormal_done_state(cmd); + res = SCST_CMD_STATE_RES_CONT_SAME; + spin_unlock_irq(&order_data->sn_lock); + goto out; + } + } + spin_unlock_irq(&order_data->sn_lock); + } else + goto exec; + } + if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE)) goto exec; + /* Must check here to catch ACA cmds after just cleared ACA */ + if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) { + scst_set_cmd_abnormal_done_state(cmd); + res = SCST_CMD_STATE_RES_CONT_SAME; + goto out; + } + EXTRACHECKS_BUG_ON(!cmd->sn_set); expected_sn = READ_ONCE(order_data->expected_sn); @@ -4110,44 +4172,6 @@ next: scst_put_buf_full(cmd, address); } - /* - * Check and clear NormACA option for the device, if necessary, - * since we don't support ACA - */ - if (unlikely((cmd->cdb[0] == INQUIRY)) && - /* Std INQUIRY data (no EVPD) */ - !(cmd->cdb[1] & SCST_INQ_EVPD) && - (cmd->resp_data_len > SCST_INQ_BYTE3)) { - uint8_t *buffer; - int buflen; - bool err = false; - - buflen = scst_get_buf_full(cmd, &buffer); - if (buflen > SCST_INQ_BYTE3 && !cmd->tgtt->fake_aca) { -#ifdef CONFIG_SCST_EXTRACHECKS - if (buffer[SCST_INQ_BYTE3] & SCST_INQ_NORMACA_BIT) { - PRINT_INFO("NormACA set for device: " - "lun=%lld, type 0x%02x. Clear it, " - "since it's unsupported.", - (unsigned long long int)cmd->lun, - buffer[0]); - } -#endif - buffer[SCST_INQ_BYTE3] &= ~SCST_INQ_NORMACA_BIT; - } else if (buflen <= SCST_INQ_BYTE3 && buflen != 0) { - PRINT_ERROR("%s", "Unable to get INQUIRY " - "buffer"); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_internal_failure)); - err = true; - } - if (buflen > 0) - scst_put_buf_full(cmd, buffer); - - if (err) - goto out; - } - if (unlikely((cmd->cdb[0] == MODE_SELECT) || (cmd->cdb[0] == MODE_SELECT_10) || (cmd->cdb[0] == LOG_SELECT))) { @@ -4403,6 +4427,50 @@ again: } else if (rc == 1) goto again; } + } else if (likely(cmd->tgt_dev != NULL)) { + struct scst_order_data *order_data = cmd->cur_order_data; + if (unlikely(order_data->aca_tgt_dev != 0)) { + if (!cmd->cmd_aca_allowed) { + spin_lock_irq(&order_data->sn_lock); + if (test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) { + /* + * cmd can be aborted and the unblock + * procedure finished while we were + * entering here. I.e. cmd can not be + * blocked anymore for any case. + */ + spin_unlock_irq(&order_data->sn_lock); + goto again; + } + if (order_data->aca_tgt_dev != 0) { + TRACE_MGMT_DBG("Deferring done cmd %p due " + "to ACA active (tgt_dev %p)", + cmd, cmd->tgt_dev); + order_data->def_cmd_count++; + /* + * Put cmd in the head to let restart + * earlier, because it's already completed + */ + list_add(&cmd->deferred_cmd_list_entry, + &order_data->deferred_cmd_list); + spin_unlock_irq(&order_data->sn_lock); + res = SCST_CMD_STATE_RES_CONT_NEXT; + goto out; + } + spin_unlock_irq(&order_data->sn_lock); + } + } + } + + if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_ACA) && + (cmd->tgt_dev != NULL)) { + struct scst_order_data *order_data = cmd->cur_order_data; + spin_lock_irq(&order_data->sn_lock); + if (order_data->aca_cmd == cmd) { + TRACE_MGMT_DBG("ACA cmd %p finished", cmd); + order_data->aca_cmd = NULL; + } + spin_unlock_irq(&order_data->sn_lock); } if (unlikely(test_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags))) { @@ -4882,6 +4950,10 @@ ordered: cmd->hq_cmd_inced = 1; goto out; + case SCST_CMD_QUEUE_ACA: + /* Nothing to do */ + goto out; + default: sBUG(); } @@ -4986,6 +5058,8 @@ static int scst_translate_lun(struct scst_cmd *cmd) static int __scst_init_cmd(struct scst_cmd *cmd) { int res = 0; + unsigned long flags; + struct scst_order_data *order_data; TRACE_ENTRY(); @@ -4994,6 +5068,8 @@ static int __scst_init_cmd(struct scst_cmd *cmd) int cnt; bool failure = false; + order_data = cmd->cur_order_data; + cmd->state = SCST_CMD_STATE_PARSE; cnt = atomic_inc_return(&cmd->tgt_dev->tgt_dev_cmd_count); @@ -5022,8 +5098,13 @@ static int __scst_init_cmd(struct scst_cmd *cmd) } #endif - if (unlikely(failure)) - goto out_busy; + if (unlikely(failure)) { + /* + * Better to delivery BUSY ASAP, than to delay + * it due to ACA + */ + goto out_busy_bypass_aca; + } /* * SCST_IMPLICIT_HQ for unknown commands not implemented for @@ -5034,6 +5115,73 @@ static int __scst_init_cmd(struct scst_cmd *cmd) * queue_type to change it if needed. ToDo. */ scst_pre_parse(cmd); +again: + if (unlikely(order_data->aca_tgt_dev != 0)) { + spin_lock_irqsave(&order_data->sn_lock, flags); + + if (order_data->aca_tgt_dev == 0) { + spin_unlock_irqrestore(&order_data->sn_lock, flags); + goto again; + } + + if (order_data->aca_tgt_dev == (unsigned long)cmd->tgt_dev) { + if ((cmd->queue_type != SCST_CMD_QUEUE_ACA) || + (order_data->aca_cmd != NULL) || + cmd->dev->tmf_only) { + TRACE_DBG("Refusing cmd %p, because ACA " + "active (aca_cmd %p, tgt_dev %p)", + cmd, order_data->aca_cmd, + cmd->tgt_dev); + goto out_unlock_aca_active; + } else { + TRACE_MGMT_DBG("ACA cmd %p (tgt_dev %p)", + cmd, cmd->tgt_dev); + order_data->aca_cmd = cmd; + /* allow it */ + } + } else { + /* Non-faulted I_T nexus */ + EXTRACHECKS_BUG_ON(cmd->dev->tst != SCST_TST_0_SINGLE_TASK_SET); + if (cmd->queue_type == SCST_CMD_QUEUE_ACA) { + TRACE_MGMT_DBG("Refusing ACA cmd %p " + "from wrong I_T nexus (aca_tgt_dev %ld, " + "cmd->tgt_dev %p)", cmd, + order_data->aca_tgt_dev, cmd->tgt_dev); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_message)); + goto out_unlock_aca_active; + } else { + if ((cmd->cdb[0] == PERSISTENT_RESERVE_OUT) && + ((cmd->cdb[1] & 0x1f) == PR_PREEMPT_AND_ABORT)) { + TRACE_MGMT_DBG("Allow PR PREEMPT AND " + "ABORT cmd %p during ACA " + "(tgt_dev %p)", cmd, cmd->tgt_dev); + /* allow it */ + } else { + TRACE_DBG("Refusing other IT-nexus " + "cmd %p, because ACA active " + "(tgt_dev %p)", cmd, cmd->tgt_dev); + if (cmd->cmd_naca) + goto out_unlock_aca_active; + else { + spin_unlock_irqrestore(&order_data->sn_lock, flags); + scst_set_cmd_error_status(cmd, SAM_STAT_BUSY); + goto out_bypass_aca; + } + } + } + } + + cmd->cmd_aca_allowed = 1; + + spin_unlock_irqrestore(&order_data->sn_lock, flags); + } else if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_ACA)) { + TRACE_MGMT_DBG("Refusing ACA cmd %p, because there's no ACA, " + "tgt_dev %p", cmd, cmd->tgt_dev); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_message)); + goto out_abnormal; + } if (!cmd->set_sn_on_restart_cmd) { if (!cmd->tgtt->multithreaded_init_done) @@ -5049,20 +5197,28 @@ static int __scst_init_cmd(struct scst_cmd *cmd) } } else if (res < 0) { TRACE_DBG("Finishing cmd %p", cmd); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_lun_not_supported)); - scst_set_cmd_abnormal_done_state(cmd); - } else - goto out; + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_lun_not_supported)); + goto out_abnormal; + } /* else goto out; */ out: TRACE_EXIT_RES(res); return res; -out_busy: +out_busy_bypass_aca: scst_set_busy(cmd); + +out_bypass_aca: + cmd->cmd_aca_allowed = 1; /* for check in scst_pre_xmit_response2() */ + +out_abnormal: scst_set_cmd_abnormal_done_state(cmd); goto out; + +out_unlock_aca_active: + spin_unlock_irqrestore(&order_data->sn_lock, flags); + scst_set_cmd_error_status(cmd, SAM_STAT_ACA_ACTIVE); + goto out_bypass_aca; } /* Called under scst_init_lock and IRQs disabled */ @@ -6370,8 +6526,25 @@ static int scst_abort_task_set(struct scst_mgmt_cmd *mcmd) __scst_abort_task_set(mcmd, tgt_dev); if (mcmd->fn == SCST_PR_ABORT_ALL) { + struct scst_cmd *orig_pr_cmd = mcmd->origin_pr_cmd; struct scst_pr_abort_all_pending_mgmt_cmds_counter *pr_cnt = - mcmd->origin_pr_cmd->pr_abort_counter; + orig_pr_cmd->pr_abort_counter; + + if (tgt_dev->curr_order_data->aca_tgt_dev == (unsigned long)mcmd->mcmd_tgt_dev) { + /* PR cmd is clearing the commands received on the faulted I_T nexus */ + if (orig_pr_cmd->cur_order_data->aca_tgt_dev == (unsigned long)orig_pr_cmd->tgt_dev) { + /* PR cmd received on the faulted I_T nexus */ + if (orig_pr_cmd->queue_type == SCST_CMD_QUEUE_ACA) + scst_clear_aca(tgt_dev, + (tgt_dev != orig_pr_cmd->tgt_dev)); + } else { + /* PR cmd received on a non-faulted I_T nexus */ + if (orig_pr_cmd->queue_type != SCST_CMD_QUEUE_ACA) + scst_clear_aca(tgt_dev, + (tgt_dev != orig_pr_cmd->tgt_dev)); + } + } + if (atomic_dec_and_test(&pr_cnt->pr_aborting_cnt)) complete_all(&pr_cnt->pr_aborting_cmpl); } @@ -6832,6 +7005,16 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd, "ABORT ALL SESS or UNREG SESS", (mcmd->fn == SCST_UNREG_SESS_TM)); } + if (nexus_loss_unreg_sess) { + /* + * We need at first abort all affected commands and + * only then release them as part of clearing ACA + */ + list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) { + scst_clear_aca(tgt_dev, + (tgt_dev != mcmd->mcmd_tgt_dev)); + } + } } scst_unblock_aborted_cmds(NULL, sess, NULL, true); @@ -6905,6 +7088,16 @@ static int scst_abort_all_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd, tm_dbg_task_mgmt(tgt_dev->dev, "NEXUS LOSS or " "ABORT ALL", 0); } + if (nexus_loss) { + /* + * We need at first abort all affected commands and + * only then release them as part of clearing ACA + */ + list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) { + scst_clear_aca(tgt_dev, + (tgt_dev != mcmd->mcmd_tgt_dev)); + } + } } } @@ -6961,6 +7154,118 @@ static int scst_abort_task(struct scst_mgmt_cmd *mcmd) return res; } +/* sn_lock supposed to be held and IRQs off */ +static void __scst_clear_aca(struct scst_tgt_dev *tgt_dev, + struct scst_mgmt_cmd *mcmd, bool other_ini) +{ + struct scst_order_data *order_data = tgt_dev->curr_order_data; + struct scst_cmd *aca_cmd; + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("Clearing ACA for tgt_dev %p (lun %lld)", + tgt_dev, (unsigned long long)tgt_dev->lun); + + aca_cmd = order_data->aca_cmd; + if (aca_cmd != NULL) { + unsigned long flags; + TRACE_MGMT_DBG("Aborting pending ACA cmd %p", aca_cmd); + spin_lock_irqsave(&aca_cmd->sess->sess_list_lock, flags); + scst_abort_cmd(aca_cmd, mcmd, other_ini, (mcmd != NULL)); + spin_unlock_irqrestore(&aca_cmd->sess->sess_list_lock, flags); + } + + order_data->aca_tgt_dev = 0; + order_data->aca_cmd = NULL; + + TRACE_EXIT(); + return; +} + +/* No locks or dev_lock, or scst_mutex */ +void scst_clear_aca(struct scst_tgt_dev *tgt_dev, bool other_ini) +{ + struct scst_order_data *order_data = tgt_dev->curr_order_data; + + TRACE_ENTRY(); + + spin_lock_irq(&order_data->sn_lock); + + if (order_data->aca_tgt_dev == 0) { + TRACE_DBG("No ACA (tgt_dev %p)", tgt_dev); + EXTRACHECKS_BUG_ON(order_data->aca_cmd != NULL); + goto out_unlock; + } + + __scst_clear_aca(tgt_dev, NULL, other_ini); + + spin_unlock_irq(&order_data->sn_lock); + + scst_make_deferred_commands_active(order_data); + +out: + TRACE_EXIT(); + return; + +out_unlock: + spin_unlock_irq(&order_data->sn_lock); + goto out; +} + +/* No locks */ +static int scst_clear_aca_mcmd(struct scst_mgmt_cmd *mcmd) +{ + int res; + struct scst_tgt_dev *mcmd_tgt_dev = mcmd->mcmd_tgt_dev; + struct scst_order_data *order_data = mcmd_tgt_dev->curr_order_data; + unsigned long aca_tgt_dev; + + TRACE_ENTRY(); + + TRACE(TRACE_MGMT, "CLEAR ACA (dev %s, lun=%lld, mcmd %p, tgt_dev %p)", + mcmd_tgt_dev->dev->virt_name, + (long long unsigned int)mcmd_tgt_dev->lun, mcmd, mcmd_tgt_dev); + + spin_lock_irq(&order_data->sn_lock); + + aca_tgt_dev = order_data->aca_tgt_dev; + + if (aca_tgt_dev == 0) { + TRACE(TRACE_MGMT, "CLEAR ACA while there's no ACA (mcmd %p)", mcmd); + goto out_unlock_done; + } + + if ((unsigned long)mcmd_tgt_dev != aca_tgt_dev) { + TRACE(TRACE_MGMT, "CLEAR ACA from not initiated ACA I_T nexus " + "(mcmd %p, mcmd_tgt_dev %p, aca_tgt_dev %ld)", mcmd, + mcmd_tgt_dev, aca_tgt_dev); + goto out_unlock_reject; + } + + __scst_clear_aca(mcmd_tgt_dev, mcmd, false); + + spin_unlock_irq(&order_data->sn_lock); + + scst_make_deferred_commands_active(order_data); + + scst_unblock_aborted_cmds(mcmd_tgt_dev->sess->tgt, mcmd_tgt_dev->sess, + mcmd_tgt_dev->dev, false); + +out_state: + res = scst_set_mcmd_next_state(mcmd); + + TRACE_EXIT_RES(res); + return res; + +out_unlock_reject: + mcmd->status = SCST_MGMT_STATUS_REJECTED; + + +out_unlock_done: + spin_unlock_irq(&order_data->sn_lock); + goto out_state; +} + /* Returns 0 if the command processing should be continued, <0 otherwise */ static int scst_mgmt_cmd_exec(struct scst_mgmt_cmd *mcmd) { @@ -7011,8 +7316,7 @@ static int scst_mgmt_cmd_exec(struct scst_mgmt_cmd *mcmd) break; case SCST_CLEAR_ACA: - /* Nothing to do (yet) */ - scst_mgmt_cmd_set_status(mcmd, SCST_MGMT_STATUS_FN_NOT_SUPPORTED); + res = scst_clear_aca_mcmd(mcmd); goto out_done; default: