Serialize STPG commands

git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@6584 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2015-11-06 03:51:35 +00:00
parent 55c46e4106
commit ecbe9d17a1
5 changed files with 133 additions and 10 deletions

View File

@@ -2213,6 +2213,12 @@ struct scst_cmd {
/* Set if cmd has LINK bit set in CDB */
unsigned int cmd_linked:1;
/* Set if cmd is on scst_global_stpg_list */
unsigned int cmd_on_global_stpg_list:1;
/* Set if cmd was globally STPG blocked in __scst_check_blocked_dev() */
unsigned int cmd_global_stpg_blocked:1;
/**************************************************************/
/* cmd's async flags */
@@ -2428,6 +2434,11 @@ struct scst_cmd {
void *cmd_data_descriptors;
int cmd_data_descriptors_cnt;
};
/* STPG commands global serialization */
struct {
struct list_head global_stpg_list_entry;
};
};
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
@@ -3707,6 +3718,7 @@ bool scst_alua_configured(struct scst_device *dev);
int scst_tg_get_group_info(void **buf, uint32_t *response_length,
struct scst_device *dev, uint8_t data_format);
int scst_tg_set_group_info(struct scst_cmd *cmd);
void scst_stpg_del_unblock_next(struct scst_cmd *cmd);
/*
* Get/set functions for dev's static DIF APP TAG

View File

@@ -5294,6 +5294,8 @@ static enum compl_status_e vdisk_exec_set_tpgs(struct vdisk_cmd_params *p)
rc = scst_tg_set_group_info(cmd);
if (rc == 0)
res = RUNNING_ASYNC;
else
scst_stpg_del_unblock_next(cmd);
out:
TRACE_EXIT_RES(res);

View File

@@ -61,6 +61,19 @@
#include "scst_mem.h"
#include "scst_pres.h"
/*
* List and IRQ lock to globally serialize all STPG commands. Needed to
* prevent deadlock, if (1) a device group contains multiple devices and
* (2) STPG commands comes to 2 or more of them at about the same time.
* In this case they will be waiting for each other to finish all pending
* commands, i.e. the STPG commands waiting for each other. Strict
* serialization is per device, so can not help here.
*
* ToDo: make it per device group.
*/
static DEFINE_SPINLOCK(scst_global_stpg_list_lock);
static LIST_HEAD(scst_global_stpg_list);
static void scst_put_acg_work(struct work_struct *work);
static void scst_del_acn(struct scst_acn *acn);
static void scst_free_acn(struct scst_acn *acn, bool reassign);
@@ -6472,6 +6485,60 @@ static void scst_destroy_cmd(struct scst_cmd *cmd)
return;
}
/* No locks */
void scst_stpg_del_unblock_next(struct scst_cmd *cmd)
{
struct scst_cmd *c;
TRACE_ENTRY();
spin_lock_irq(&scst_global_stpg_list_lock);
EXTRACHECKS_BUG_ON(!cmd->cmd_on_global_stpg_list);
TRACE_DBG("STPG cmd %p: unblocking next", cmd);
list_del(&cmd->global_stpg_list_entry);
cmd->cmd_on_global_stpg_list = 0;
if (list_empty(&scst_global_stpg_list)) {
TRACE_DBG("No more STPG commands to unblock");
spin_unlock_irq(&scst_global_stpg_list_lock);
goto out;
}
c = list_first_entry(&scst_global_stpg_list, typeof(*c),
global_stpg_list_entry);
spin_unlock_irq(&scst_global_stpg_list_lock);
spin_lock_bh(&c->dev->dev_lock);
if (!c->cmd_global_stpg_blocked) {
TRACE_DBG("STPG cmd %p is not cmd_global_stpg_blocked", c);
spin_unlock_bh(&c->dev->dev_lock);
goto out;
}
TRACE_BLOCK("Unblocking serialized STPG cmd %p", c);
list_del(&c->blocked_cmd_list_entry);
c->cmd_global_stpg_blocked = 0;
spin_unlock_bh(&c->dev->dev_lock);
spin_lock_irq(&c->cmd_threads->cmd_list_lock);
list_add(&c->cmd_list_entry,
&c->cmd_threads->active_cmd_list);
wake_up(&c->cmd_threads->cmd_list_waitQ);
spin_unlock_irq(&c->cmd_threads->cmd_list_lock);
out:
TRACE_EXIT();
return;
}
EXPORT_SYMBOL_GPL(scst_stpg_del_unblock_next);
/* No locks supposed to be held */
void scst_free_cmd(struct scst_cmd *cmd)
{
@@ -10526,10 +10593,18 @@ static int get_cdb_info_mo(struct scst_cmd *cmd,
{
switch (cmd->cdb[1] & 0x1f) {
case MO_SET_TARGET_PGS:
{
unsigned long flags;
cmd->op_name = "SET TARGET PORT GROUPS";
cmd->op_flags |= SCST_STRICTLY_SERIALIZED;
spin_lock_irqsave(&scst_global_stpg_list_lock, flags);
TRACE_DBG("Adding STPG cmd %p to global_stpg_list", cmd);
cmd->cmd_on_global_stpg_list = 1;
list_add_tail(&cmd->global_stpg_list_entry, &scst_global_stpg_list);
spin_unlock_irqrestore(&scst_global_stpg_list_lock, flags);
break;
}
}
return get_cdb_info_len_4(cmd, sdbops);
}
@@ -11963,10 +12038,27 @@ bool __scst_check_blocked_dev(struct scst_cmd *cmd)
scst_get_opcode_name(cmd), dev->virt_name);
goto out_block;
} else if ((cmd->op_flags & SCST_STRICTLY_SERIALIZED) == SCST_STRICTLY_SERIALIZED) {
TRACE_BLOCK("cmd %p (tag %llu, op %s): blocking further "
"cmds on dev %s due to strict serialization", cmd,
(unsigned long long int)cmd->tag,
TRACE_BLOCK("Strictly serialized cmd %p (tag %llu, op %s, dev %s)",
cmd, (unsigned long long int)cmd->tag,
scst_get_opcode_name(cmd), dev->virt_name);
if ((cmd->cdb[0] == MAINTENANCE_OUT) &&
((cmd->cdb[1] & 0x1f) == MO_SET_TARGET_PGS)) {
const struct scst_cmd *c;
/*
* It's OK to do that without lock, because we
* are interested only in comparison
*/
c = list_first_entry(&scst_global_stpg_list, typeof(*c),
global_stpg_list_entry);
if (cmd != c) {
TRACE_BLOCK("Blocking serialized STPG cmd %p "
"(head %p)", cmd, c);
cmd->cmd_global_stpg_blocked = 1;
goto out_block;
}
}
scst_block_dev(dev);
if (dev->on_dev_cmd_count > 1) {
TRACE_BLOCK("Delaying strictly serialized cmd %p "
@@ -12019,10 +12111,17 @@ void scst_unblock_dev(struct scst_device *dev)
local_irq_save_nort(flags);
list_for_each_entry_safe(cmd, tcmd, &dev->blocked_cmd_list,
blocked_cmd_list_entry) {
bool strictly_serialized;
bool strictly_serialized =
((cmd->op_flags & SCST_STRICTLY_SERIALIZED) == SCST_STRICTLY_SERIALIZED);
if (dev->strictly_serialized_cmd_waiting &&
!strictly_serialized)
continue;
list_del(&cmd->blocked_cmd_list_entry);
TRACE_BLOCK("Adding blocked cmd %p to active cmd "
"list", cmd);
cmd->cmd_global_stpg_blocked = 0;
TRACE_BLOCK("Adding blocked cmd %p to active cmd list", cmd);
spin_lock(&cmd->cmd_threads->cmd_list_lock);
if (cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE)
list_add(&cmd->cmd_list_entry,
@@ -12030,9 +12129,9 @@ void scst_unblock_dev(struct scst_device *dev)
else
list_add_tail(&cmd->cmd_list_entry,
&cmd->cmd_threads->active_cmd_list);
strictly_serialized = ((cmd->op_flags & SCST_STRICTLY_SERIALIZED) == SCST_STRICTLY_SERIALIZED);
wake_up(&cmd->cmd_threads->cmd_list_waitQ);
spin_unlock(&cmd->cmd_threads->cmd_list_lock);
if (dev->strictly_serialized_cmd_waiting && strictly_serialized)
break;
}

View File

@@ -4261,6 +4261,12 @@ static int scst_finish_cmd(struct scst_cmd *cmd)
spin_unlock_irq(&sess->sess_list_lock);
if (unlikely(cmd->cmd_on_global_stpg_list)) {
TRACE_DBG("Unlisting being freed STPG cmd %p", cmd);
EXTRACHECKS_BUG_ON(cmd->cmd_global_stpg_blocked);
scst_stpg_del_unblock_next(cmd);
}
if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)))
scst_finish_cmd_mgmt(cmd);
@@ -5620,11 +5626,13 @@ static int scst_set_mcmd_next_state(struct scst_mgmt_cmd *mcmd)
/* IRQs supposed to be disabled */
static bool __scst_check_unblock_aborted_cmd(struct scst_cmd *cmd,
struct list_head *list_entry)
struct list_head *list_entry, bool blocked)
{
bool res;
if (test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) {
list_del(list_entry);
if (blocked)
cmd->cmd_global_stpg_blocked = 0;
spin_lock(&cmd->cmd_threads->cmd_list_lock);
list_add_tail(&cmd->cmd_list_entry,
&cmd->cmd_threads->active_cmd_list);
@@ -5667,7 +5675,7 @@ void scst_unblock_aborted_cmds(const struct scst_tgt *tgt,
continue;
if (__scst_check_unblock_aborted_cmd(cmd,
&cmd->blocked_cmd_list_entry)) {
&cmd->blocked_cmd_list_entry, true)) {
TRACE_MGMT_DBG("Unblock aborted blocked cmd %p", cmd);
}
}
@@ -5689,7 +5697,7 @@ void scst_unblock_aborted_cmds(const struct scst_tgt *tgt,
continue;
if (__scst_check_unblock_aborted_cmd(cmd,
&cmd->deferred_cmd_list_entry)) {
&cmd->deferred_cmd_list_entry, false)) {
TRACE_MGMT_DBG("Unblocked aborted SN "
"cmd %p (sn %u)", cmd, cmd->sn);
order_data->def_cmd_count--;

View File

@@ -919,6 +919,8 @@ out_unlock:
mutex_unlock(&scst_dg_mutex);
mutex_unlock(&scst_mutex);
scst_stpg_del_unblock_next(cmd);
cmd->completed = 1;
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_THREAD);