forked from Minki/linux
tcm_loop: TCQ and command abort support
Implement TCQ support, which enables us to do proper command abort, too. Signed-off-by: Hannes Reinecke <hare@suse.de> Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
This commit is contained in:
parent
a314d7003c
commit
969871cdc2
@ -135,6 +135,21 @@ static int tcm_loop_change_queue_depth(
|
||||
return sdev->queue_depth;
|
||||
}
|
||||
|
||||
static int tcm_loop_change_queue_type(struct scsi_device *sdev, int tag)
|
||||
{
|
||||
if (sdev->tagged_supported) {
|
||||
scsi_set_tag_type(sdev, tag);
|
||||
|
||||
if (tag)
|
||||
scsi_activate_tcq(sdev, sdev->queue_depth);
|
||||
else
|
||||
scsi_deactivate_tcq(sdev, sdev->queue_depth);
|
||||
} else
|
||||
tag = 0;
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
/*
|
||||
* Locate the SAM Task Attr from struct scsi_cmnd *
|
||||
*/
|
||||
@ -236,6 +251,7 @@ static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc)
|
||||
}
|
||||
|
||||
tl_cmd->sc = sc;
|
||||
tl_cmd->sc_cmd_tag = sc->tag;
|
||||
INIT_WORK(&tl_cmd->work, tcm_loop_submission_work);
|
||||
queue_work(tcm_loop_workqueue, &tl_cmd->work);
|
||||
return 0;
|
||||
@ -247,7 +263,7 @@ static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc)
|
||||
*/
|
||||
static int tcm_loop_issue_tmr(struct tcm_loop_tpg *tl_tpg,
|
||||
struct tcm_loop_nexus *tl_nexus,
|
||||
int lun, enum tcm_tmreq_table tmr)
|
||||
int lun, int task, enum tcm_tmreq_table tmr)
|
||||
{
|
||||
struct se_cmd *se_cmd = NULL;
|
||||
struct se_session *se_sess;
|
||||
@ -283,6 +299,9 @@ static int tcm_loop_issue_tmr(struct tcm_loop_tpg *tl_tpg,
|
||||
if (rc < 0)
|
||||
goto release;
|
||||
|
||||
if (tmr == TMR_ABORT_TASK)
|
||||
se_cmd->se_tmr_req->ref_task_tag = task;
|
||||
|
||||
/*
|
||||
* Locate the underlying TCM struct se_lun
|
||||
*/
|
||||
@ -310,6 +329,36 @@ release:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tcm_loop_abort_task(struct scsi_cmnd *sc)
|
||||
{
|
||||
struct tcm_loop_hba *tl_hba;
|
||||
struct tcm_loop_nexus *tl_nexus;
|
||||
struct tcm_loop_tpg *tl_tpg;
|
||||
int ret = FAILED;
|
||||
|
||||
/*
|
||||
* Locate the tcm_loop_hba_t pointer
|
||||
*/
|
||||
tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
|
||||
/*
|
||||
* Locate the tl_nexus and se_sess pointers
|
||||
*/
|
||||
tl_nexus = tl_hba->tl_nexus;
|
||||
if (!tl_nexus) {
|
||||
pr_err("Unable to perform device reset without"
|
||||
" active I_T Nexus\n");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Locate the tl_tpg pointer from TargetID in sc->device->id
|
||||
*/
|
||||
tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
|
||||
ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun,
|
||||
sc->tag, TMR_ABORT_TASK);
|
||||
return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from SCSI EH process context to issue a LUN_RESET TMR
|
||||
* to struct scsi_device
|
||||
@ -338,8 +387,8 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc)
|
||||
* Locate the tl_tpg pointer from TargetID in sc->device->id
|
||||
*/
|
||||
tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
|
||||
ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus,
|
||||
sc->device->lun, TMR_LUN_RESET);
|
||||
ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun,
|
||||
0, TMR_LUN_RESET);
|
||||
return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED;
|
||||
}
|
||||
|
||||
@ -351,6 +400,15 @@ static int tcm_loop_slave_alloc(struct scsi_device *sd)
|
||||
|
||||
static int tcm_loop_slave_configure(struct scsi_device *sd)
|
||||
{
|
||||
if (sd->tagged_supported) {
|
||||
scsi_activate_tcq(sd, sd->queue_depth);
|
||||
scsi_adjust_queue_depth(sd, MSG_SIMPLE_TAG,
|
||||
sd->host->cmd_per_lun);
|
||||
} else {
|
||||
scsi_adjust_queue_depth(sd, 0,
|
||||
sd->host->cmd_per_lun);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -360,6 +418,8 @@ static struct scsi_host_template tcm_loop_driver_template = {
|
||||
.name = "TCM_Loopback",
|
||||
.queuecommand = tcm_loop_queuecommand,
|
||||
.change_queue_depth = tcm_loop_change_queue_depth,
|
||||
.change_queue_type = tcm_loop_change_queue_type,
|
||||
.eh_abort_handler = tcm_loop_abort_task,
|
||||
.eh_device_reset_handler = tcm_loop_device_reset,
|
||||
.can_queue = 1024,
|
||||
.this_id = -1,
|
||||
@ -719,7 +779,10 @@ static void tcm_loop_set_default_node_attributes(struct se_node_acl *se_acl)
|
||||
|
||||
static u32 tcm_loop_get_task_tag(struct se_cmd *se_cmd)
|
||||
{
|
||||
return 1;
|
||||
struct tcm_loop_cmd *tl_cmd = container_of(se_cmd,
|
||||
struct tcm_loop_cmd, tl_se_cmd);
|
||||
|
||||
return tl_cmd->sc_cmd_tag;
|
||||
}
|
||||
|
||||
static int tcm_loop_get_cmd_state(struct se_cmd *se_cmd)
|
||||
|
@ -10,6 +10,8 @@
|
||||
struct tcm_loop_cmd {
|
||||
/* State of Linux/SCSI CDB+Data descriptor */
|
||||
u32 sc_cmd_state;
|
||||
/* Tagged command queueing */
|
||||
u32 sc_cmd_tag;
|
||||
/* Pointer to the CDB+Data descriptor from Linux/SCSI subsystem */
|
||||
struct scsi_cmnd *sc;
|
||||
/* The TCM I/O descriptor that is accessed via container_of() */
|
||||
|
Loading…
Reference in New Issue
Block a user