mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-05 20:24:09 +08:00
scsi: target: Make the session shutdown code also wait for commands that are being aborted
Target drivers must call target_sess_cmd_list_set_waiting() and target_wait_for_sess_cmds() before freeing a session. Since freeing a session is only safe after all commands that are associated with a session have finished, make target_wait_for_sess_cmds() also wait for commands that are being aborted. Instead of setting a flag in each pending command from target_sess_cmd_list_set_waiting() and waiting in target_wait_for_sess_cmds() on a per-command completion, only set a per-session flag in the former function and wait on a per-session completion in the latter function. This change is safe because once a SCSI initiator system has submitted a command a target system is always allowed to execute it to completion. See also commit0f4a943168
("target: Fix remote-port TMR ABORT + se_cmd fabric stop"). This patch is based on the following two patches: * Bart Van Assche, target: Simplify session shutdown code, February 19, 2015 (8df5463d7d
). * Christoph Hellwig, target: Rework session shutdown code, December 7, 2015 (http://thread.gmane.org/gmane.linux.scsi.target.devel/10695). Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com> Reviewed-by: Mike Christie <mchristi@redhat.com> Cc: Hannes Reinecke <hare@suse.com> Cc: Christoph Hellwig <hch@lst.de> Cc: Sagi Grimberg <sagig@mellanox.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
d1bff07f38
commit
00d909a107
@ -142,7 +142,7 @@ static bool __target_check_io_state(struct se_cmd *se_cmd,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sess->sess_tearing_down || se_cmd->cmd_wait_set) {
|
if (sess->sess_tearing_down) {
|
||||||
pr_debug("Attempted to abort io tag: %llu already shutdown,"
|
pr_debug("Attempted to abort io tag: %llu already shutdown,"
|
||||||
" skipping\n", se_cmd->tag);
|
" skipping\n", se_cmd->tag);
|
||||||
spin_unlock(&se_cmd->t_state_lock);
|
spin_unlock(&se_cmd->t_state_lock);
|
||||||
@ -187,7 +187,6 @@ void core_tmr_abort_task(
|
|||||||
if (!__target_check_io_state(se_cmd, se_sess, 0))
|
if (!__target_check_io_state(se_cmd, se_sess, 0))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
list_del_init(&se_cmd->se_cmd_list);
|
|
||||||
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
|
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
|
||||||
|
|
||||||
cancel_work_sync(&se_cmd->work);
|
cancel_work_sync(&se_cmd->work);
|
||||||
@ -259,7 +258,7 @@ static void core_tmr_drain_tmr_list(
|
|||||||
spin_unlock(&sess->sess_cmd_lock);
|
spin_unlock(&sess->sess_cmd_lock);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (sess->sess_tearing_down || cmd->cmd_wait_set) {
|
if (sess->sess_tearing_down) {
|
||||||
spin_unlock(&cmd->t_state_lock);
|
spin_unlock(&cmd->t_state_lock);
|
||||||
spin_unlock(&sess->sess_cmd_lock);
|
spin_unlock(&sess->sess_cmd_lock);
|
||||||
continue;
|
continue;
|
||||||
|
@ -235,8 +235,8 @@ void transport_init_session(struct se_session *se_sess)
|
|||||||
INIT_LIST_HEAD(&se_sess->sess_list);
|
INIT_LIST_HEAD(&se_sess->sess_list);
|
||||||
INIT_LIST_HEAD(&se_sess->sess_acl_list);
|
INIT_LIST_HEAD(&se_sess->sess_acl_list);
|
||||||
INIT_LIST_HEAD(&se_sess->sess_cmd_list);
|
INIT_LIST_HEAD(&se_sess->sess_cmd_list);
|
||||||
INIT_LIST_HEAD(&se_sess->sess_wait_list);
|
|
||||||
spin_lock_init(&se_sess->sess_cmd_lock);
|
spin_lock_init(&se_sess->sess_cmd_lock);
|
||||||
|
init_waitqueue_head(&se_sess->cmd_list_wq);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(transport_init_session);
|
EXPORT_SYMBOL(transport_init_session);
|
||||||
|
|
||||||
@ -2728,13 +2728,15 @@ static void target_release_cmd_kref(struct kref *kref)
|
|||||||
if (se_sess) {
|
if (se_sess) {
|
||||||
spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
|
spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
|
||||||
list_del_init(&se_cmd->se_cmd_list);
|
list_del_init(&se_cmd->se_cmd_list);
|
||||||
|
if (list_empty(&se_sess->sess_cmd_list))
|
||||||
|
wake_up(&se_sess->cmd_list_wq);
|
||||||
|
|
||||||
spin_lock(&se_cmd->t_state_lock);
|
spin_lock(&se_cmd->t_state_lock);
|
||||||
fabric_stop = (se_cmd->transport_state & CMD_T_FABRIC_STOP) &&
|
fabric_stop = (se_cmd->transport_state & CMD_T_FABRIC_STOP) &&
|
||||||
(se_cmd->transport_state & CMD_T_ABORTED);
|
(se_cmd->transport_state & CMD_T_ABORTED);
|
||||||
spin_unlock(&se_cmd->t_state_lock);
|
spin_unlock(&se_cmd->t_state_lock);
|
||||||
|
|
||||||
if (se_cmd->cmd_wait_set || fabric_stop) {
|
if (fabric_stop) {
|
||||||
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
|
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
|
||||||
target_free_cmd_mem(se_cmd);
|
target_free_cmd_mem(se_cmd);
|
||||||
complete(&se_cmd->cmd_wait_comp);
|
complete(&se_cmd->cmd_wait_comp);
|
||||||
@ -2863,78 +2865,41 @@ void target_show_cmd(const char *pfx, struct se_cmd *cmd)
|
|||||||
EXPORT_SYMBOL(target_show_cmd);
|
EXPORT_SYMBOL(target_show_cmd);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* target_sess_cmd_list_set_waiting - Flag all commands in
|
* target_sess_cmd_list_set_waiting - Set sess_tearing_down so no new commands are queued.
|
||||||
* sess_cmd_list to complete cmd_wait_comp. Set
|
|
||||||
* sess_tearing_down so no more commands are queued.
|
|
||||||
* @se_sess: session to flag
|
* @se_sess: session to flag
|
||||||
*/
|
*/
|
||||||
void target_sess_cmd_list_set_waiting(struct se_session *se_sess)
|
void target_sess_cmd_list_set_waiting(struct se_session *se_sess)
|
||||||
{
|
{
|
||||||
struct se_cmd *se_cmd, *tmp_cmd;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int rc;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
|
spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
|
||||||
if (se_sess->sess_tearing_down) {
|
|
||||||
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
se_sess->sess_tearing_down = 1;
|
se_sess->sess_tearing_down = 1;
|
||||||
list_splice_init(&se_sess->sess_cmd_list, &se_sess->sess_wait_list);
|
|
||||||
|
|
||||||
list_for_each_entry_safe(se_cmd, tmp_cmd,
|
|
||||||
&se_sess->sess_wait_list, se_cmd_list) {
|
|
||||||
rc = kref_get_unless_zero(&se_cmd->cmd_kref);
|
|
||||||
if (rc) {
|
|
||||||
se_cmd->cmd_wait_set = 1;
|
|
||||||
spin_lock(&se_cmd->t_state_lock);
|
|
||||||
se_cmd->transport_state |= CMD_T_FABRIC_STOP;
|
|
||||||
spin_unlock(&se_cmd->t_state_lock);
|
|
||||||
} else
|
|
||||||
list_del_init(&se_cmd->se_cmd_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
|
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(target_sess_cmd_list_set_waiting);
|
EXPORT_SYMBOL(target_sess_cmd_list_set_waiting);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* target_wait_for_sess_cmds - Wait for outstanding descriptors
|
* target_wait_for_sess_cmds - Wait for outstanding commands
|
||||||
* @se_sess: session to wait for active I/O
|
* @se_sess: session to wait for active I/O
|
||||||
*/
|
*/
|
||||||
void target_wait_for_sess_cmds(struct se_session *se_sess)
|
void target_wait_for_sess_cmds(struct se_session *se_sess)
|
||||||
{
|
{
|
||||||
struct se_cmd *se_cmd, *tmp_cmd;
|
struct se_cmd *cmd;
|
||||||
unsigned long flags;
|
int ret;
|
||||||
bool tas;
|
|
||||||
|
|
||||||
list_for_each_entry_safe(se_cmd, tmp_cmd,
|
WARN_ON_ONCE(!se_sess->sess_tearing_down);
|
||||||
&se_sess->sess_wait_list, se_cmd_list) {
|
|
||||||
pr_debug("Waiting for se_cmd: %p t_state: %d, fabric state:"
|
|
||||||
" %d\n", se_cmd, se_cmd->t_state,
|
|
||||||
se_cmd->se_tfo->get_cmd_state(se_cmd));
|
|
||||||
|
|
||||||
spin_lock_irqsave(&se_cmd->t_state_lock, flags);
|
|
||||||
tas = (se_cmd->transport_state & CMD_T_TAS);
|
|
||||||
spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
|
|
||||||
|
|
||||||
if (!target_put_sess_cmd(se_cmd)) {
|
|
||||||
if (tas)
|
|
||||||
target_put_sess_cmd(se_cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_for_completion(&se_cmd->cmd_wait_comp);
|
|
||||||
pr_debug("After cmd_wait_comp: se_cmd: %p t_state: %d"
|
|
||||||
" fabric state: %d\n", se_cmd, se_cmd->t_state,
|
|
||||||
se_cmd->se_tfo->get_cmd_state(se_cmd));
|
|
||||||
|
|
||||||
se_cmd->se_tfo->release_cmd(se_cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
|
|
||||||
WARN_ON(!list_empty(&se_sess->sess_cmd_list));
|
|
||||||
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
|
|
||||||
|
|
||||||
|
spin_lock_irq(&se_sess->sess_cmd_lock);
|
||||||
|
do {
|
||||||
|
ret = wait_event_interruptible_lock_irq_timeout(
|
||||||
|
se_sess->cmd_list_wq,
|
||||||
|
list_empty(&se_sess->sess_cmd_list),
|
||||||
|
se_sess->sess_cmd_lock, 180 * HZ);
|
||||||
|
list_for_each_entry(cmd, &se_sess->sess_cmd_list, se_cmd_list)
|
||||||
|
target_show_cmd("session shutdown: still waiting for ",
|
||||||
|
cmd);
|
||||||
|
} while (ret <= 0);
|
||||||
|
spin_unlock_irq(&se_sess->sess_cmd_lock);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(target_wait_for_sess_cmds);
|
EXPORT_SYMBOL(target_wait_for_sess_cmds);
|
||||||
|
|
||||||
|
@ -443,7 +443,6 @@ struct se_cmd {
|
|||||||
u8 scsi_asc;
|
u8 scsi_asc;
|
||||||
u8 scsi_ascq;
|
u8 scsi_ascq;
|
||||||
u16 scsi_sense_length;
|
u16 scsi_sense_length;
|
||||||
unsigned cmd_wait_set:1;
|
|
||||||
unsigned unknown_data_length:1;
|
unsigned unknown_data_length:1;
|
||||||
bool state_active:1;
|
bool state_active:1;
|
||||||
u64 tag; /* SAM command identifier aka task tag */
|
u64 tag; /* SAM command identifier aka task tag */
|
||||||
@ -606,8 +605,8 @@ struct se_session {
|
|||||||
struct list_head sess_list;
|
struct list_head sess_list;
|
||||||
struct list_head sess_acl_list;
|
struct list_head sess_acl_list;
|
||||||
struct list_head sess_cmd_list;
|
struct list_head sess_cmd_list;
|
||||||
struct list_head sess_wait_list;
|
|
||||||
spinlock_t sess_cmd_lock;
|
spinlock_t sess_cmd_lock;
|
||||||
|
wait_queue_head_t cmd_list_wq;
|
||||||
void *sess_cmd_map;
|
void *sess_cmd_map;
|
||||||
struct sbitmap_queue sess_tag_pool;
|
struct sbitmap_queue sess_tag_pool;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user