diff options
author | Hannes Reinecke <hare@suse.de> | 2013-10-16 03:12:55 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2013-10-16 18:41:50 -0400 |
commit | 969871cdc2d0f5a81d6733450b5334d14fce2b75 (patch) | |
tree | 3f6f35aaa3cc3f10f3e31f68a47a72bfa1b17f3d | |
parent | a314d7003ccd4f4886d1280ff4f6c00217fc9ae3 (diff) |
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>
-rw-r--r-- | drivers/target/loopback/tcm_loop.c | 71 | ||||
-rw-r--r-- | drivers/target/loopback/tcm_loop.h | 2 |
2 files changed, 69 insertions, 4 deletions
diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 783675f7cdab..04811caac487 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c | |||
@@ -135,6 +135,21 @@ static int tcm_loop_change_queue_depth( | |||
135 | return sdev->queue_depth; | 135 | return sdev->queue_depth; |
136 | } | 136 | } |
137 | 137 | ||
138 | static int tcm_loop_change_queue_type(struct scsi_device *sdev, int tag) | ||
139 | { | ||
140 | if (sdev->tagged_supported) { | ||
141 | scsi_set_tag_type(sdev, tag); | ||
142 | |||
143 | if (tag) | ||
144 | scsi_activate_tcq(sdev, sdev->queue_depth); | ||
145 | else | ||
146 | scsi_deactivate_tcq(sdev, sdev->queue_depth); | ||
147 | } else | ||
148 | tag = 0; | ||
149 | |||
150 | return tag; | ||
151 | } | ||
152 | |||
138 | /* | 153 | /* |
139 | * Locate the SAM Task Attr from struct scsi_cmnd * | 154 | * Locate the SAM Task Attr from struct scsi_cmnd * |
140 | */ | 155 | */ |
@@ -236,6 +251,7 @@ static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc) | |||
236 | } | 251 | } |
237 | 252 | ||
238 | tl_cmd->sc = sc; | 253 | tl_cmd->sc = sc; |
254 | tl_cmd->sc_cmd_tag = sc->tag; | ||
239 | INIT_WORK(&tl_cmd->work, tcm_loop_submission_work); | 255 | INIT_WORK(&tl_cmd->work, tcm_loop_submission_work); |
240 | queue_work(tcm_loop_workqueue, &tl_cmd->work); | 256 | queue_work(tcm_loop_workqueue, &tl_cmd->work); |
241 | return 0; | 257 | return 0; |
@@ -247,7 +263,7 @@ static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc) | |||
247 | */ | 263 | */ |
248 | static int tcm_loop_issue_tmr(struct tcm_loop_tpg *tl_tpg, | 264 | static int tcm_loop_issue_tmr(struct tcm_loop_tpg *tl_tpg, |
249 | struct tcm_loop_nexus *tl_nexus, | 265 | struct tcm_loop_nexus *tl_nexus, |
250 | int lun, enum tcm_tmreq_table tmr) | 266 | int lun, int task, enum tcm_tmreq_table tmr) |
251 | { | 267 | { |
252 | struct se_cmd *se_cmd = NULL; | 268 | struct se_cmd *se_cmd = NULL; |
253 | struct se_session *se_sess; | 269 | struct se_session *se_sess; |
@@ -283,6 +299,9 @@ static int tcm_loop_issue_tmr(struct tcm_loop_tpg *tl_tpg, | |||
283 | if (rc < 0) | 299 | if (rc < 0) |
284 | goto release; | 300 | goto release; |
285 | 301 | ||
302 | if (tmr == TMR_ABORT_TASK) | ||
303 | se_cmd->se_tmr_req->ref_task_tag = task; | ||
304 | |||
286 | /* | 305 | /* |
287 | * Locate the underlying TCM struct se_lun | 306 | * Locate the underlying TCM struct se_lun |
288 | */ | 307 | */ |
@@ -310,6 +329,36 @@ release: | |||
310 | return ret; | 329 | return ret; |
311 | } | 330 | } |
312 | 331 | ||
332 | static int tcm_loop_abort_task(struct scsi_cmnd *sc) | ||
333 | { | ||
334 | struct tcm_loop_hba *tl_hba; | ||
335 | struct tcm_loop_nexus *tl_nexus; | ||
336 | struct tcm_loop_tpg *tl_tpg; | ||
337 | int ret = FAILED; | ||
338 | |||
339 | /* | ||
340 | * Locate the tcm_loop_hba_t pointer | ||
341 | */ | ||
342 | tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); | ||
343 | /* | ||
344 | * Locate the tl_nexus and se_sess pointers | ||
345 | */ | ||
346 | tl_nexus = tl_hba->tl_nexus; | ||
347 | if (!tl_nexus) { | ||
348 | pr_err("Unable to perform device reset without" | ||
349 | " active I_T Nexus\n"); | ||
350 | return FAILED; | ||
351 | } | ||
352 | |||
353 | /* | ||
354 | * Locate the tl_tpg pointer from TargetID in sc->device->id | ||
355 | */ | ||
356 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; | ||
357 | ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun, | ||
358 | sc->tag, TMR_ABORT_TASK); | ||
359 | return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; | ||
360 | } | ||
361 | |||
313 | /* | 362 | /* |
314 | * Called from SCSI EH process context to issue a LUN_RESET TMR | 363 | * Called from SCSI EH process context to issue a LUN_RESET TMR |
315 | * to struct scsi_device | 364 | * to struct scsi_device |
@@ -338,8 +387,8 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc) | |||
338 | * Locate the tl_tpg pointer from TargetID in sc->device->id | 387 | * Locate the tl_tpg pointer from TargetID in sc->device->id |
339 | */ | 388 | */ |
340 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; | 389 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; |
341 | ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, | 390 | ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun, |
342 | sc->device->lun, TMR_LUN_RESET); | 391 | 0, TMR_LUN_RESET); |
343 | return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; | 392 | return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; |
344 | } | 393 | } |
345 | 394 | ||
@@ -351,6 +400,15 @@ static int tcm_loop_slave_alloc(struct scsi_device *sd) | |||
351 | 400 | ||
352 | static int tcm_loop_slave_configure(struct scsi_device *sd) | 401 | static int tcm_loop_slave_configure(struct scsi_device *sd) |
353 | { | 402 | { |
403 | if (sd->tagged_supported) { | ||
404 | scsi_activate_tcq(sd, sd->queue_depth); | ||
405 | scsi_adjust_queue_depth(sd, MSG_SIMPLE_TAG, | ||
406 | sd->host->cmd_per_lun); | ||
407 | } else { | ||
408 | scsi_adjust_queue_depth(sd, 0, | ||
409 | sd->host->cmd_per_lun); | ||
410 | } | ||
411 | |||
354 | return 0; | 412 | return 0; |
355 | } | 413 | } |
356 | 414 | ||
@@ -360,6 +418,8 @@ static struct scsi_host_template tcm_loop_driver_template = { | |||
360 | .name = "TCM_Loopback", | 418 | .name = "TCM_Loopback", |
361 | .queuecommand = tcm_loop_queuecommand, | 419 | .queuecommand = tcm_loop_queuecommand, |
362 | .change_queue_depth = tcm_loop_change_queue_depth, | 420 | .change_queue_depth = tcm_loop_change_queue_depth, |
421 | .change_queue_type = tcm_loop_change_queue_type, | ||
422 | .eh_abort_handler = tcm_loop_abort_task, | ||
363 | .eh_device_reset_handler = tcm_loop_device_reset, | 423 | .eh_device_reset_handler = tcm_loop_device_reset, |
364 | .can_queue = 1024, | 424 | .can_queue = 1024, |
365 | .this_id = -1, | 425 | .this_id = -1, |
@@ -719,7 +779,10 @@ static void tcm_loop_set_default_node_attributes(struct se_node_acl *se_acl) | |||
719 | 779 | ||
720 | static u32 tcm_loop_get_task_tag(struct se_cmd *se_cmd) | 780 | static u32 tcm_loop_get_task_tag(struct se_cmd *se_cmd) |
721 | { | 781 | { |
722 | return 1; | 782 | struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, |
783 | struct tcm_loop_cmd, tl_se_cmd); | ||
784 | |||
785 | return tl_cmd->sc_cmd_tag; | ||
723 | } | 786 | } |
724 | 787 | ||
725 | static int tcm_loop_get_cmd_state(struct se_cmd *se_cmd) | 788 | static int tcm_loop_get_cmd_state(struct se_cmd *se_cmd) |
diff --git a/drivers/target/loopback/tcm_loop.h b/drivers/target/loopback/tcm_loop.h index 56528f7dafae..54c59d0b6608 100644 --- a/drivers/target/loopback/tcm_loop.h +++ b/drivers/target/loopback/tcm_loop.h | |||
@@ -10,6 +10,8 @@ | |||
10 | struct tcm_loop_cmd { | 10 | struct tcm_loop_cmd { |
11 | /* State of Linux/SCSI CDB+Data descriptor */ | 11 | /* State of Linux/SCSI CDB+Data descriptor */ |
12 | u32 sc_cmd_state; | 12 | u32 sc_cmd_state; |
13 | /* Tagged command queueing */ | ||
14 | u32 sc_cmd_tag; | ||
13 | /* Pointer to the CDB+Data descriptor from Linux/SCSI subsystem */ | 15 | /* Pointer to the CDB+Data descriptor from Linux/SCSI subsystem */ |
14 | struct scsi_cmnd *sc; | 16 | struct scsi_cmnd *sc; |
15 | /* The TCM I/O descriptor that is accessed via container_of() */ | 17 | /* The TCM I/O descriptor that is accessed via container_of() */ |