[SCSI] target: Fix bug with task_sg chained transport_free_dev_tasks release
This patch addresses a bug in the target core release path for HW operation where transport_free_dev_tasks() was incorrectly being called from transport_lun_remove_cmd() while releasing a se_cmd reference and calling struct target_core_fabric_ops->queue_data_in(). This would result in a OOPs with HW target mode when the release of se_task->task_sg[] would happen before pci_unmap_sg() can be called in HW target mode fabric module code. This patch addresses the issue by moving transport_free_dev_tasks() from transport_lun_remove_cmd() into transport_generic_free_cmd(), and adding TRANSPORT_FREE_CMD_INTR and transport_generic_free_cmd_intr() to allow se_cmd descriptor release to happen fromfrom within transport_processing_thread() process context when release of se_cmd is not possible from HW interrupt context. Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org> Cc: stable@kernel.org Signed-off-by: James Bottomley <jbottomley@parallels.com>
This commit is contained in:
parent
53ab6709b4
commit
f436677262
|
@ -762,7 +762,6 @@ static void transport_lun_remove_cmd(struct se_cmd *cmd)
|
||||||
transport_all_task_dev_remove_state(cmd);
|
transport_all_task_dev_remove_state(cmd);
|
||||||
spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
|
spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
|
||||||
|
|
||||||
transport_free_dev_tasks(cmd);
|
|
||||||
|
|
||||||
check_lun:
|
check_lun:
|
||||||
spin_lock_irqsave(&lun->lun_cmd_lock, flags);
|
spin_lock_irqsave(&lun->lun_cmd_lock, flags);
|
||||||
|
@ -2058,6 +2057,13 @@ int transport_generic_handle_tmr(
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(transport_generic_handle_tmr);
|
EXPORT_SYMBOL(transport_generic_handle_tmr);
|
||||||
|
|
||||||
|
void transport_generic_free_cmd_intr(
|
||||||
|
struct se_cmd *cmd)
|
||||||
|
{
|
||||||
|
transport_add_cmd_to_queue(cmd, TRANSPORT_FREE_CMD_INTR);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(transport_generic_free_cmd_intr);
|
||||||
|
|
||||||
static int transport_stop_tasks_for_cmd(struct se_cmd *cmd)
|
static int transport_stop_tasks_for_cmd(struct se_cmd *cmd)
|
||||||
{
|
{
|
||||||
struct se_task *task, *task_tmp;
|
struct se_task *task, *task_tmp;
|
||||||
|
@ -5301,6 +5307,8 @@ void transport_generic_free_cmd(
|
||||||
if (wait_for_tasks && cmd->transport_wait_for_tasks)
|
if (wait_for_tasks && cmd->transport_wait_for_tasks)
|
||||||
cmd->transport_wait_for_tasks(cmd, 0, 0);
|
cmd->transport_wait_for_tasks(cmd, 0, 0);
|
||||||
|
|
||||||
|
transport_free_dev_tasks(cmd);
|
||||||
|
|
||||||
transport_generic_remove(cmd, release_to_pool,
|
transport_generic_remove(cmd, release_to_pool,
|
||||||
session_reinstatement);
|
session_reinstatement);
|
||||||
}
|
}
|
||||||
|
@ -6136,6 +6144,9 @@ get_cmd:
|
||||||
case TRANSPORT_REMOVE:
|
case TRANSPORT_REMOVE:
|
||||||
transport_generic_remove(cmd, 1, 0);
|
transport_generic_remove(cmd, 1, 0);
|
||||||
break;
|
break;
|
||||||
|
case TRANSPORT_FREE_CMD_INTR:
|
||||||
|
transport_generic_free_cmd(cmd, 0, 1, 0);
|
||||||
|
break;
|
||||||
case TRANSPORT_PROCESS_TMR:
|
case TRANSPORT_PROCESS_TMR:
|
||||||
transport_generic_do_tmr(cmd);
|
transport_generic_do_tmr(cmd);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -98,6 +98,7 @@ enum transport_state_table {
|
||||||
TRANSPORT_REMOVE = 14,
|
TRANSPORT_REMOVE = 14,
|
||||||
TRANSPORT_FREE = 15,
|
TRANSPORT_FREE = 15,
|
||||||
TRANSPORT_NEW_CMD_MAP = 16,
|
TRANSPORT_NEW_CMD_MAP = 16,
|
||||||
|
TRANSPORT_FREE_CMD_INTR = 17,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Used for struct se_cmd->se_cmd_flags */
|
/* Used for struct se_cmd->se_cmd_flags */
|
||||||
|
|
|
@ -172,6 +172,7 @@ extern int transport_generic_handle_cdb_map(struct se_cmd *);
|
||||||
extern int transport_generic_handle_data(struct se_cmd *);
|
extern int transport_generic_handle_data(struct se_cmd *);
|
||||||
extern void transport_new_cmd_failure(struct se_cmd *);
|
extern void transport_new_cmd_failure(struct se_cmd *);
|
||||||
extern int transport_generic_handle_tmr(struct se_cmd *);
|
extern int transport_generic_handle_tmr(struct se_cmd *);
|
||||||
|
extern void transport_generic_free_cmd_intr(struct se_cmd *);
|
||||||
extern void __transport_stop_task_timer(struct se_task *, unsigned long *);
|
extern void __transport_stop_task_timer(struct se_task *, unsigned long *);
|
||||||
extern unsigned char transport_asciihex_to_binaryhex(unsigned char val[2]);
|
extern unsigned char transport_asciihex_to_binaryhex(unsigned char val[2]);
|
||||||
extern int transport_generic_map_mem_to_cmd(struct se_cmd *cmd, struct scatterlist *, u32,
|
extern int transport_generic_map_mem_to_cmd(struct se_cmd *cmd, struct scatterlist *, u32,
|
||||||
|
|
Loading…
Reference in New Issue