diff --git a/scst/include/scst.h b/scst/include/scst.h index 7f1b7ebb2..c7b489ee3 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -2204,6 +2204,9 @@ struct scst_cmd { /* Set if cmd was pre-alloced by target driver */ unsigned int pre_alloced:1; + /* Set if cmd was already ALUA checked in TRANSITIONING state */ + unsigned int already_transitioning:1; + /* Set if scst_cmd_set_write_not_received_data_len() was called */ unsigned int write_not_received_set:1; @@ -3008,7 +3011,10 @@ struct scst_tgt_dev { atomic_t tgt_dev_cmd_count ____cacheline_aligned_in_smp; /* ALUA command filter */ - bool (*alua_filter)(struct scst_cmd *cmd); +#define SCST_ALUA_CHECK_OK 0 +#define SCST_ALUA_CHECK_DELAYED 1 +#define SCST_ALUA_CHECK_ERROR -1 + int (*alua_filter)(struct scst_cmd *cmd); struct scst_order_data *curr_order_data; struct scst_order_data tgt_dev_order_data; diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 4f39cd43d..6732a7649 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -3247,15 +3247,40 @@ out_done: goto out; } +static inline bool scst_check_alua(struct scst_cmd *cmd, int *out_res) +{ + int (*alua_filter)(struct scst_cmd *cmd); + bool res = false; + + alua_filter = ACCESS_ONCE(cmd->tgt_dev->alua_filter); + if (unlikely(alua_filter)) { + int ac = alua_filter(cmd); + if (ac != SCST_ALUA_CHECK_OK) { + if (ac != SCST_ALUA_CHECK_DELAYED) { + EXTRACHECKS_BUG_ON(cmd->status == 0); + scst_set_cmd_abnormal_done_state(cmd); + *out_res = SCST_CMD_STATE_RES_CONT_SAME; + } + res = true; + } + } + + return res; +} + static int scst_exec_check_blocking(struct scst_cmd **active_cmd) { struct scst_cmd *cmd = *active_cmd; struct scst_cmd *ref_cmd; + int res = SCST_CMD_STATE_RES_CONT_NEXT; TRACE_ENTRY(); cmd->state = SCST_CMD_STATE_EXEC_CHECK_BLOCKING; + if (unlikely(scst_check_alua(cmd, &res))) + goto out; + if (unlikely(scst_check_blocked_dev(cmd))) goto out; @@ -3319,6 +3344,9 @@ done: cmd->state = SCST_CMD_STATE_EXEC_CHECK_BLOCKING; + if (unlikely(scst_check_alua(cmd, &res))) + goto out; + if (unlikely(scst_check_blocked_dev(cmd))) break; @@ -3338,8 +3366,8 @@ done: /* !! At this point sess, dev and tgt_dev can be already freed !! */ out: - TRACE_EXIT(); - return SCST_CMD_STATE_RES_CONT_NEXT; + TRACE_EXIT_RES(res); + return res; } static int scst_exec_check_sn(struct scst_cmd **active_cmd) @@ -4514,7 +4542,6 @@ static int scst_translate_lun(struct scst_cmd *cmd) */ static int __scst_init_cmd(struct scst_cmd *cmd) { - bool (*alua_filter)(struct scst_cmd *cmd); int res = 0; TRACE_ENTRY(); @@ -4555,12 +4582,6 @@ static int __scst_init_cmd(struct scst_cmd *cmd) if (unlikely(failure)) goto out_busy; - alua_filter = ACCESS_ONCE(cmd->tgt_dev->alua_filter); - if (unlikely(alua_filter && !alua_filter(cmd))) { - scst_set_cmd_abnormal_done_state(cmd); - goto out; - } - /* * SCST_IMPLICIT_HQ for unknown commands not implemented for * case when set_sn_on_restart_cmd not set, because custom parse diff --git a/scst/src/scst_tg.c b/scst/src/scst_tg.c index 68dc6a668..e2fd4018f 100644 --- a/scst/src/scst_tg.c +++ b/scst/src/scst_tg.c @@ -272,11 +272,14 @@ static struct kobj_type scst_tg_tgt_ktype = { }; /* - * Whether or not to accept a command in the ALUA unavailable and transitioning - * states. + * Whether or not to accept a command in the ALUA standby state. */ -static bool scst_tg_accept(struct scst_cmd *cmd) +static int scst_tg_accept_standby(struct scst_cmd *cmd) { + int res; + + TRACE_ENTRY(); + switch (cmd->cdb[0]) { case TEST_UNIT_READY: case GET_EVENT_STATUS_NOTIFICATION: @@ -292,41 +295,6 @@ static bool scst_tg_accept(struct scst_cmd *cmd) case RESERVE_10: case READ_BUFFER: case WRITE_BUFFER: - return true; - case SERVICE_ACTION_IN_16: - switch (cmd->cdb[1] & 0x1f) { - case SAI_READ_CAPACITY_16: - return true; - } - break; - case MAINTENANCE_IN: - switch (cmd->cdb[1] & 0x1f) { - case MI_REPORT_TARGET_PGS: - return true; - } - break; - case MAINTENANCE_OUT: - switch (cmd->cdb[1] & 0x1f) { - case MO_SET_TARGET_PGS: - return true; - } - break; - } - - return false; -} - -/* - * Whether or not to accept a command in the ALUA standby state. - */ -static bool scst_tg_accept_standby(struct scst_cmd *cmd) -{ - bool process_cmd = scst_tg_accept(cmd); - - if (process_cmd) - return process_cmd; - - switch (cmd->cdb[0]) { case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT: @@ -335,42 +303,187 @@ static bool scst_tg_accept_standby(struct scst_cmd *cmd) case SEND_DIAGNOSTIC: case PERSISTENT_RESERVE_IN: case PERSISTENT_RESERVE_OUT: - return true; + res = SCST_ALUA_CHECK_OK; + goto out; + case SERVICE_ACTION_IN_16: + switch (cmd->cdb[1] & 0x1f) { + case SAI_READ_CAPACITY_16: + res = SCST_ALUA_CHECK_OK; + goto out; + } + break; + case MAINTENANCE_IN: + switch (cmd->cdb[1] & 0x1f) { + case MI_REPORT_TARGET_PGS: + res = SCST_ALUA_CHECK_OK; + goto out; + } + break; + case MAINTENANCE_OUT: + switch (cmd->cdb[1] & 0x1f) { + case MO_SET_TARGET_PGS: + res = SCST_ALUA_CHECK_OK; + goto out; + } + break; } scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_alua_standby)); + res = SCST_ALUA_CHECK_ERROR; - return false; +out: + TRACE_EXIT_RES(res); + return res; } /* * Whether or not to accept a command in the ALUA unavailable state. */ -static bool scst_tg_accept_unav(struct scst_cmd *cmd) +static int scst_tg_accept_unav(struct scst_cmd *cmd) { - bool process_cmd = scst_tg_accept(cmd); + int res; - if (!process_cmd) - scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_alua_unav)); + TRACE_ENTRY(); - return process_cmd; + switch (cmd->cdb[0]) { + case TEST_UNIT_READY: + case GET_EVENT_STATUS_NOTIFICATION: + case INQUIRY: + case MODE_SENSE: + case MODE_SENSE_10: + case READ_CAPACITY: + case REPORT_LUNS: + case REQUEST_SENSE: + case RELEASE: + case RELEASE_10: + case RESERVE: + case RESERVE_10: + case READ_BUFFER: + case WRITE_BUFFER: + res = SCST_ALUA_CHECK_OK; + goto out; + case SERVICE_ACTION_IN_16: + switch (cmd->cdb[1] & 0x1f) { + case SAI_READ_CAPACITY_16: + res = SCST_ALUA_CHECK_OK; + goto out; + } + break; + case MAINTENANCE_IN: + switch (cmd->cdb[1] & 0x1f) { + case MI_REPORT_TARGET_PGS: + res = SCST_ALUA_CHECK_OK; + goto out; + } + break; + case MAINTENANCE_OUT: + switch (cmd->cdb[1] & 0x1f) { + case MO_SET_TARGET_PGS: + res = SCST_ALUA_CHECK_OK; + goto out; + } + break; + } + + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_alua_unav)); + res = SCST_ALUA_CHECK_ERROR; + +out: + TRACE_EXIT_RES(res); + return res; +} + +struct scst_alua_retry { + struct scst_cmd *alua_retry_cmd; + struct delayed_work alua_retry_work; +}; + +static void scst_alua_transitioning_work_fn(struct delayed_work *work) +{ + struct scst_alua_retry *retry = container_of(work, struct scst_alua_retry, + alua_retry_work); + struct scst_cmd *cmd = retry->alua_retry_cmd; + + TRACE_ENTRY(); + + TRACE_DBG("Retrying transitioning cmd %p", cmd); + + spin_lock_irq(&cmd->cmd_threads->cmd_list_lock); + list_add(&cmd->cmd_list_entry, + &cmd->cmd_threads->active_cmd_list); + wake_up(&cmd->cmd_threads->cmd_list_waitQ); + spin_unlock_irq(&cmd->cmd_threads->cmd_list_lock); + + kfree(retry); + + TRACE_EXIT(); + return; } /* * Whether or not to accept a command in the ALUA transitioning state. */ -static bool scst_tg_accept_transitioning(struct scst_cmd *cmd) +static int scst_tg_accept_transitioning(struct scst_cmd *cmd) { - bool process_cmd = scst_tg_accept(cmd); + int res; - if (!process_cmd) - scst_set_cmd_error(cmd, + TRACE_ENTRY(); + + switch (cmd->cdb[0]) { + case INQUIRY: + case READ_CAPACITY: + case REPORT_LUNS: + case REQUEST_SENSE: + case READ_BUFFER: + case WRITE_BUFFER: + res = SCST_ALUA_CHECK_OK; + goto out; + case SERVICE_ACTION_IN_16: + switch (cmd->cdb[1] & 0x1f) { + case SAI_READ_CAPACITY_16: + res = SCST_ALUA_CHECK_OK; + goto out; + } + break; + } + + if (cmd->already_transitioning) + TRACE_DBG("cmd %p already transitioned checked, failing", cmd); + else { + struct scst_alua_retry *retry; + + TRACE_DBG("ALUA transitioning: delaying cmd %p", cmd); + + retry = kzalloc(sizeof(*retry), GFP_KERNEL); + if (retry == NULL) { + TRACE_DBG("Unable to allocate ALUA retry " + "struct, failing cmd %p", cmd); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_alua_transitioning)); + res = SCST_ALUA_CHECK_ERROR; + goto out; + } + + /* No get is needed, because cmd is sync here */ + retry->alua_retry_cmd = cmd; + INIT_DELAYED_WORK(&retry->alua_retry_work, + (void (*)(struct work_struct *))scst_alua_transitioning_work_fn); + cmd->already_transitioning = 1; + schedule_delayed_work(&retry->alua_retry_work, HZ/2); + res = SCST_ALUA_CHECK_DELAYED; + goto out; + } + + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_alua_transitioning)); + res = SCST_ALUA_CHECK_ERROR; - return process_cmd; +out: + TRACE_EXIT_RES(res); + return res; } -static bool (*scst_alua_filter[])(struct scst_cmd *cmd) = { +static int (*scst_alua_filter[])(struct scst_cmd *cmd) = { [SCST_TG_STATE_OPTIMIZED] = NULL, [SCST_TG_STATE_NONOPTIMIZED] = NULL, [SCST_TG_STATE_STANDBY] = scst_tg_accept_standby,