Fix possible deadlock on waiting on blocking. As a side effect it leaded to a nice cleanup.

git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@2272 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2010-09-25 18:25:32 +00:00
parent f14cd777f7
commit 864d4b38c7
6 changed files with 73 additions and 161 deletions
+2 -6
View File
@@ -82,12 +82,8 @@ void ft_cmd_dump(struct scst_cmd *cmd, const char *caller)
ft_cmd_flag(buf, sizeof(buf), "retry");
if (cmd->internal)
ft_cmd_flag(buf, sizeof(buf), "internal");
if (cmd->inc_blocking)
ft_cmd_flag(buf, sizeof(buf), "inc_blk");
if (cmd->needs_unblocking)
ft_cmd_flag(buf, sizeof(buf), "needs_unblk");
if (cmd->dec_on_dev_needed)
ft_cmd_flag(buf, sizeof(buf), "dec_on_dev");
if (cmd->unblock_dev)
ft_cmd_flag(buf, sizeof(buf), "unblock_dev");
if (cmd->cmd_hw_pending)
ft_cmd_flag(buf, sizeof(buf), "hw_pend");
if (cmd->tgt_need_alloc_data_buf)
+11 -26
View File
@@ -1636,14 +1636,8 @@ struct scst_cmd {
/* Set if cmd is internally generated */
unsigned int internal:1;
/* Set if the device was blocked by scst_inc_on_dev_cmd() (for debug) */
unsigned int inc_blocking:1;
/* Set if the device should be unblocked after cmd's finish */
unsigned int needs_unblocking:1;
/* Set if scst_dec_on_dev_cmd() call is needed on the cmd's finish */
unsigned int dec_on_dev_needed:1;
/* Set if the device was blocked by scst_check_blocked_dev() */
unsigned int unblock_dev:1;
/* Set if cmd is queued as hw pending */
unsigned int cmd_hw_pending:1;
@@ -2068,6 +2062,15 @@ struct scst_device {
/**************************************************************/
/*
* How many times device was blocked for new cmds execution.
* Protected by dev_lock
*/
int block_count;
/* How many cmds alive on this dev */
atomic_t dev_cmd_count;
/*
* Set if dev is persistently reserved. Protected by dev_pr_mutex.
* Modified independently to the above field, hence the alignment.
@@ -2105,21 +2108,6 @@ struct scst_device {
/* Memory limits for this device */
struct scst_mem_lim dev_mem_lim;
/* How many cmds alive on this dev */
atomic_t dev_cmd_count;
/*
* How many there are "on_dev" commands, i.e. ones those are being
* executed by the underlying SCSI/virtual device.
*/
atomic_t on_dev_count;
/*
* How many times device was blocked for new cmds execution.
* Protected by dev_lock
*/
int block_count;
/* How many write cmds alive on this dev. Temporary, ToDo */
atomic_t write_cmd_count;
@@ -2168,9 +2156,6 @@ struct scst_device {
struct list_head blocked_cmd_list; /* protected by dev_lock */
/* Used to wait for requested amount of "on_dev" commands */
wait_queue_head_t on_dev_waitQ;
/* A list entry used during TM, protected by scst_mutex */
struct list_head tm_dev_list_entry;
+12 -7
View File
@@ -141,6 +141,10 @@ enum scst_cmd_queue_type {
/*************************************************************
** CDB flags
**
** Implicit ordered used for commands which need calm environment
** without any simultaneous activities. For instance, for MODE
** SELECT it is needed to correctly generate its UA.
*************************************************************/
enum scst_cdb_flags {
SCST_TRANSFER_LEN_TYPE_FIXED = 0x0001,
@@ -150,13 +154,14 @@ enum scst_cdb_flags {
SCST_INFO_VALID = 0x0010, /* must be single bit */
SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED = 0x0020,
SCST_IMPLICIT_HQ = 0x0040,
SCST_SKIP_UA = 0x0080,
SCST_WRITE_MEDIUM = 0x0100,
SCST_LOCAL_CMD = 0x0200,
SCST_FULLY_LOCAL_CMD = 0x0400,
SCST_REG_RESERVE_ALLOWED = 0x0800,
SCST_WRITE_EXCL_ALLOWED = 0x1000,
SCST_EXCL_ACCESS_ALLOWED = 0x2000,
SCST_IMPLICIT_ORDERED = 0x0080,
SCST_SKIP_UA = 0x0100,
SCST_WRITE_MEDIUM = 0x0200,
SCST_LOCAL_CMD = 0x0400,
SCST_FULLY_LOCAL_CMD = 0x0800,
SCST_REG_RESERVE_ALLOWED = 0x1000,
SCST_WRITE_EXCL_ALLOWED = 0x2000,
SCST_EXCL_ACCESS_ALLOWED = 0x4000,
#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ
SCST_TEST_IO_IN_SIRQ_ALLOWED = 0x8000,
#endif
+14 -63
View File
@@ -271,7 +271,7 @@ static const struct scst_sdbops scst_scsi_op_table[] = {
SCST_WRITE_EXCL_ALLOWED,
2, get_trans_len_3},
{0x15, "OMOOOOOOOOOOOOOO", "MODE SELECT(6)",
SCST_DATA_WRITE, SCST_LOCAL_CMD, 4, get_trans_len_1},
SCST_DATA_WRITE, SCST_IMPLICIT_ORDERED, 4, get_trans_len_1},
{0x16, "MMMMMMMMMMMMMMMM", "RESERVE",
SCST_DATA_NONE, SCST_SMALL_TIMEOUT|SCST_LOCAL_CMD|
SCST_WRITE_EXCL_ALLOWED|SCST_EXCL_ACCESS_ALLOWED,
@@ -432,7 +432,7 @@ static const struct scst_sdbops scst_scsi_op_table[] = {
{0x4B, " O ", "PAUSE/RESUME",
SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none},
{0x4C, "OOOOOOOOOOOOOOOO", "LOG SELECT",
SCST_DATA_WRITE, SCST_SMALL_TIMEOUT, 7, get_trans_len_2},
SCST_DATA_WRITE, SCST_IMPLICIT_ORDERED, 7, get_trans_len_2},
{0x4D, "OOOOOOOOOOOOOOOO", "LOG SENSE",
SCST_DATA_READ, SCST_SMALL_TIMEOUT|
SCST_REG_RESERVE_ALLOWED|
@@ -458,7 +458,7 @@ static const struct scst_sdbops scst_scsi_op_table[] = {
{0x54, " O ", "SEND OPC INFORMATION",
SCST_DATA_WRITE, FLAG_NONE, 7, get_trans_len_2},
{0x55, "OOOOOOOOOOOOOOOO", "MODE SELECT(10)",
SCST_DATA_WRITE, SCST_LOCAL_CMD, 7, get_trans_len_2},
SCST_DATA_WRITE, SCST_IMPLICIT_ORDERED, 7, get_trans_len_2},
{0x56, "OOOOOOOOOOOOOOOO", "RESERVE(10)",
SCST_DATA_NONE, SCST_SMALL_TIMEOUT|SCST_LOCAL_CMD,
0, get_trans_len_none},
@@ -2579,11 +2579,9 @@ int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev)
atomic_set(&dev->write_cmd_count, 0);
scst_init_mem_lim(&dev->dev_mem_lim);
spin_lock_init(&dev->dev_lock);
atomic_set(&dev->on_dev_count, 0);
INIT_LIST_HEAD(&dev->blocked_cmd_list);
INIT_LIST_HEAD(&dev->dev_tgt_dev_list);
INIT_LIST_HEAD(&dev->dev_acg_dev_list);
init_waitqueue_head(&dev->on_dev_waitQ);
dev->dev_double_ua_possible = 1;
dev->queue_alg = SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER;
@@ -4231,8 +4229,7 @@ void scst_free_cmd(struct scst_cmd *cmd)
cmd, atomic_read(&scst_cmd_count));
}
sBUG_ON(cmd->inc_blocking || cmd->needs_unblocking ||
cmd->dec_on_dev_needed);
sBUG_ON(cmd->unblock_dev);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
#if defined(CONFIG_SCST_EXTRACHECKS)
@@ -6685,47 +6682,12 @@ bool scst_del_thr_data(struct scst_tgt_dev *tgt_dev, struct task_struct *tsk)
}
/* dev_lock supposed to be held and BH disabled */
void __scst_block_dev(struct scst_device *dev)
void scst_block_dev(struct scst_device *dev)
{
dev->block_count++;
TRACE_MGMT_DBG("Device BLOCK(new %d), dev %p", dev->block_count, dev);
}
/* No locks */
static void scst_block_dev(struct scst_device *dev, int outstanding)
{
spin_lock_bh(&dev->dev_lock);
__scst_block_dev(dev);
spin_unlock_bh(&dev->dev_lock);
/*
* Memory barrier is necessary here, because we need to read
* on_dev_count in wait_event() below after we increased block_count.
* Otherwise, we can miss wake up in scst_dec_on_dev_cmd().
* We use the explicit barrier, because spin_unlock_bh() doesn't
* provide the necessary memory barrier functionality.
*/
smp_mb();
TRACE_MGMT_DBG("Waiting during blocking outstanding %d (on_dev_count "
"%d)", outstanding, atomic_read(&dev->on_dev_count));
wait_event(dev->on_dev_waitQ,
atomic_read(&dev->on_dev_count) <= outstanding);
TRACE_MGMT_DBG("%s", "wait_event() returned");
}
/* No locks */
void scst_block_dev_cmd(struct scst_cmd *cmd, int outstanding)
{
sBUG_ON(cmd->needs_unblocking);
cmd->needs_unblocking = 1;
TRACE_MGMT_DBG("Needs unblocking cmd %p (tag %llu)",
cmd, (long long unsigned int)cmd->tag);
scst_block_dev(cmd->dev, outstanding);
}
/* No locks */
void scst_unblock_dev(struct scst_device *dev)
{
@@ -6739,25 +6701,14 @@ void scst_unblock_dev(struct scst_device *dev)
}
/* No locks */
void scst_unblock_dev_cmd(struct scst_cmd *cmd)
bool __scst_check_blocked_dev(struct scst_cmd *cmd)
{
scst_unblock_dev(cmd->dev);
cmd->needs_unblocking = 0;
}
/* No locks */
int scst_inc_on_dev_cmd(struct scst_cmd *cmd)
{
int res = 0;
int res = false;
struct scst_device *dev = cmd->dev;
TRACE_ENTRY();
sBUG_ON(cmd->inc_blocking || cmd->dec_on_dev_needed);
atomic_inc(&dev->on_dev_count);
cmd->dec_on_dev_needed = 1;
TRACE_DBG("New on_dev_count %d", atomic_read(&dev->on_dev_count));
EXTRACHECKS_BUG_ON(cmd->unblock_dev);
if (unlikely(cmd->internal) && (cmd->cdb[0] == REQUEST_SENSE)) {
/*
@@ -6768,18 +6719,17 @@ int scst_inc_on_dev_cmd(struct scst_cmd *cmd)
}
repeat:
if (unlikely(dev->block_count > 0)) {
if (dev->block_count > 0) {
spin_lock_bh(&dev->dev_lock);
if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)))
goto out_unlock;
if (dev->block_count > 0) {
scst_dec_on_dev_cmd(cmd);
TRACE_MGMT_DBG("Delaying cmd %p due to blocking "
"(tag %llu, dev %p)", cmd,
(long long unsigned int)cmd->tag, dev);
list_add_tail(&cmd->blocked_cmd_list_entry,
&dev->blocked_cmd_list);
res = 1;
res = true;
spin_unlock_bh(&dev->dev_lock);
goto out;
} else {
@@ -6788,14 +6738,15 @@ repeat:
}
spin_unlock_bh(&dev->dev_lock);
}
if (unlikely(dev->dev_double_ua_possible)) {
if (dev->dev_double_ua_possible) {
spin_lock_bh(&dev->dev_lock);
if (dev->block_count == 0) {
TRACE_MGMT_DBG("cmd %p (tag %llu), blocking further "
"cmds due to possible double reset UA (dev %p)",
cmd, (long long unsigned int)cmd->tag, dev);
__scst_block_dev(dev);
cmd->inc_blocking = 1;
scst_block_dev(dev);
cmd->unblock_dev = 1;
} else {
spin_unlock_bh(&dev->dev_lock);
TRACE_MGMT_DBG("Somebody blocked the device, "
+21 -27
View File
@@ -558,6 +558,11 @@ static inline bool scst_is_implicit_hq(struct scst_cmd *cmd)
return (cmd->op_flags & SCST_IMPLICIT_HQ) != 0;
}
static inline bool scst_is_implicit_ordered(struct scst_cmd *cmd)
{
return (cmd->op_flags & SCST_IMPLICIT_ORDERED) != 0;
}
/*
* Some notes on devices "blocking". Blocking means that no
* commands will go from SCST to underlying SCSI device until it
@@ -565,40 +570,29 @@ static inline bool scst_is_implicit_hq(struct scst_cmd *cmd)
* already on the device.
*/
extern int scst_inc_on_dev_cmd(struct scst_cmd *cmd);
extern void __scst_block_dev(struct scst_device *dev);
extern void scst_block_dev_cmd(struct scst_cmd *cmd, int outstanding);
extern void scst_block_dev(struct scst_device *dev);
extern void scst_unblock_dev(struct scst_device *dev);
extern void scst_unblock_dev_cmd(struct scst_cmd *cmd);
extern bool __scst_check_blocked_dev(struct scst_cmd *cmd);
static inline bool scst_check_blocked_dev(struct scst_cmd *cmd)
{
if (unlikely(cmd->dev->block_count > 0) ||
unlikely(cmd->dev->dev_double_ua_possible))
return __scst_check_blocked_dev(cmd);
else
return false;
}
/* No locks */
static inline void scst_dec_on_dev_cmd(struct scst_cmd *cmd)
static inline void scst_check_unblock_dev(struct scst_cmd *cmd)
{
struct scst_device *dev = cmd->dev;
bool unblock_dev = cmd->inc_blocking;
if (cmd->inc_blocking) {
if (unlikely(cmd->unblock_dev)) {
TRACE_MGMT_DBG("cmd %p (tag %llu): unblocking dev %p", cmd,
(long long unsigned int)cmd->tag, cmd->dev);
cmd->inc_blocking = 0;
cmd->unblock_dev = 0;
scst_unblock_dev(cmd->dev);
}
cmd->dec_on_dev_needed = 0;
if (unblock_dev)
scst_unblock_dev(dev);
atomic_dec(&dev->on_dev_count);
/* See comment in scst_block_dev() */
smp_mb__after_atomic_dec();
TRACE_DBG("New on_dev_count %d", atomic_read(&dev->on_dev_count));
sBUG_ON(atomic_read(&dev->on_dev_count) < 0);
if (unlikely(dev->block_count != 0))
wake_up_all(&dev->on_dev_waitQ);
return;
}
+13 -32
View File
@@ -414,9 +414,10 @@ static int scst_pre_parse(struct scst_cmd *cmd)
cmd->inc_expected_sn_on_done = 1;
#else
cmd->inc_expected_sn_on_done = dev->handler->exec_sync ||
(!dev->has_own_order_mgmt &&
(dev->queue_alg == SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER ||
cmd->queue_type == SCST_CMD_QUEUE_ORDERED));
scst_is_implicit_ordered(cmd) ||
(!dev->has_own_order_mgmt &&
(dev->queue_alg == SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER ||
cmd->queue_type == SCST_CMD_QUEUE_ORDERED));
#endif
/*
@@ -1927,20 +1928,6 @@ out_unlock_compl:
goto out_compl;
}
static int scst_pre_select(struct scst_cmd *cmd)
{
int res = SCST_EXEC_NOT_COMPLETED;
TRACE_ENTRY();
scst_block_dev_cmd(cmd, 1);
/* Check for local events will be done when cmd will be executed */
TRACE_EXIT_RES(res);
return res;
}
static int scst_reserve_local(struct scst_cmd *cmd)
{
int res = SCST_EXEC_NOT_COMPLETED, rc;
@@ -2704,11 +2691,6 @@ static int scst_do_local_exec(struct scst_cmd *cmd)
}
switch (cmd->cdb[0]) {
case MODE_SELECT:
case MODE_SELECT_10:
case LOG_SELECT:
res = scst_pre_select(cmd);
break;
case RESERVE:
case RESERVE_10:
res = scst_reserve_local(cmd);
@@ -2780,7 +2762,7 @@ static int scst_exec(struct scst_cmd **active_cmd)
TRACE_ENTRY();
if (unlikely(scst_inc_on_dev_cmd(cmd) != 0))
if (unlikely(scst_check_blocked_dev(cmd)))
goto out;
/* To protect tgt_dev */
@@ -2823,7 +2805,7 @@ done:
if (cmd == NULL)
break;
if (unlikely(scst_inc_on_dev_cmd(cmd) != 0))
if (unlikely(scst_check_blocked_dev(cmd)))
break;
__scst_cmd_put(ref_cmd);
@@ -3372,11 +3354,7 @@ static int scst_dev_done(struct scst_cmd *cmd)
#endif
}
if (cmd->needs_unblocking)
scst_unblock_dev_cmd(cmd);
if (likely(cmd->dec_on_dev_needed))
scst_dec_on_dev_cmd(cmd);
scst_check_unblock_dev(cmd);
if (cmd->inc_expected_sn_on_done && cmd->sent_for_exec)
scst_inc_check_expected_sn(cmd);
@@ -3694,6 +3672,9 @@ static void scst_cmd_set_sn(struct scst_cmd *cmd)
cmd->queue_type = SCST_CMD_QUEUE_HEAD_OF_QUEUE;
}
if (unlikely(scst_is_implicit_ordered(cmd)))
cmd->queue_type = SCST_CMD_QUEUE_ORDERED;
EXTRACHECKS_BUG_ON(cmd->sn_set || cmd->hq_cmd_inced);
/* Optimized for lockless fast path */
@@ -5047,7 +5028,7 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd)
*/
mcmd->needs_unblocking = 1;
spin_lock_bh(&dev->dev_lock);
__scst_block_dev(dev);
scst_block_dev(dev);
spin_unlock_bh(&dev->dev_lock);
#endif
@@ -5207,7 +5188,7 @@ static int scst_target_reset(struct scst_mgmt_cmd *mcmd)
dev = acg_dev->dev;
spin_lock_bh(&dev->dev_lock);
__scst_block_dev(dev);
scst_block_dev(dev);
scst_process_reset(dev, mcmd->sess, NULL, mcmd, true);
spin_unlock_bh(&dev->dev_lock);
@@ -5311,7 +5292,7 @@ static int scst_lun_reset(struct scst_mgmt_cmd *mcmd)
mcmd->needs_unblocking = 1;
spin_lock_bh(&dev->dev_lock);
__scst_block_dev(dev);
scst_block_dev(dev);
scst_process_reset(dev, mcmd->sess, NULL, mcmd, true);
spin_unlock_bh(&dev->dev_lock);