Updates for tcm_loop
Subsystems (2)
cover Updates for tcm_loop
Hi Nic,
here are some updates to tcm_loop I've done during ALUA testing. I've implemented a 'transport_state' attribute to simulate transport failure and added command abort callbacks.
Hannes Reinecke (5):
tcm_loop: Check for valid hba in tcm_loop_drop_nexus()
tcm_loop: Implement transport offline
tcm_loop: separate out tcm_loop_issue_tmr
tcm_loop: TCQ and command abort support
tcm_loop: Implement target reset
drivers/target/loopback/tcm_loop.c | 233 +++++++++++++++++++++++++++++++------ drivers/target/loopback/tcm_loop.h | 6 + include/target/target_core_base.h | 1 + 3 files changed, 204 insertions(+), 36 deletions(-)
Signature · 1 line
1.7.12.4
PATCH 1/5 tcm_loop: Check for valid hba in tcm_loop_drop_nexus()
--- drivers/target/loopback/tcm_loop.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/target/loopback/tcm_loop.c +4 -1
@@ -932,7 +932,10 @@ static int tcm_loop_drop_nexus(struct tcm_loop_nexus *tl_nexus;struct tcm_loop_hba *tl_hba = tpg->tl_hba; - tl_nexus = tpg->tl_hba->tl_nexus; +if (!tl_hba) +return -ENODEV ; + + tl_nexus = tl_hba->tl_nexus;if (!tl_nexus)return -ENODEV ;
Signature · 1 line
1.7.12.4
PATCH 2/5 tcm_loop: Implement transport offline
In reply to Hannes ReineckeAdd attribute 'transport_status' to simulate link failure.
--- drivers/target/loopback/tcm_loop.c | 53 +++++++++++++++++++++++++++++++++++++- drivers/target/loopback/tcm_loop.h | 4 +++ 2 files changed, 56 insertions(+), 1 deletion(-)
drivers/target/loopback/tcm_loop.c +52 -1
@@ -178,7 +178,10 @@ static void tcm_loop_submission_work(struct work_struct *work)set_host_byte (sc,DID_NO_CONNECT ); goto out_done; } - +if (tl_tpg->tl_transport_status ==TCM_TRANSPORT_OFFLINE ) { +set_host_byte (sc,DID_TRANSPORT_DISRUPTED ); + goto out_done; + } tl_nexus = tl_hba->tl_nexus;if (!tl_nexus) { scmd_printk(KERN_ERR, sc, "TCM_Loop I_T Nexus"
@@ -1064,8 +1067,56 @@ check_newline:TF_TPG_BASE_ATTR (tcm_loop, nexus,S_IRUGO |S_IWUSR ); +static ssize_t tcm_loop_tpg_show_transport_status ( +struct se_portal_group *se_tpg, +char *page) +{ +struct tcm_loop_tpg *tl_tpg =container_of (se_tpg, + struct tcm_loop_tpg, tl_se_tpg); +const char *status =NULL ; +ssize_t ret = -EINVAL ; + +switch (tl_tpg->tl_transport_status) { +case TCM_TRANSPORT_ONLINE : + status ="online" ; +break ; +case TCM_TRANSPORT_OFFLINE : + status ="offline" ; +break ; +default : +break ; + } + +if (status) + ret =snprintf (page,PAGE_SIZE ,"%s\n" , status); + +return ret; +} + +static ssize_t tcm_loop_tpg_store_transport_status ( +struct se_portal_group *se_tpg, +const char *page, +size_t count) +{ +struct tcm_loop_tpg *tl_tpg =container_of (se_tpg, + struct tcm_loop_tpg, tl_se_tpg); + +if (!strncmp (page,"online" ,6 )) { + tl_tpg->tl_transport_status =TCM_TRANSPORT_ONLINE ; +return count; + } +if (!strncmp (page,"offline" ,7 )) { + tl_tpg->tl_transport_status =TCM_TRANSPORT_OFFLINE ; +return count; + } +return -EINVAL ; +} + +TF_TPG_BASE_ATTR (tcm_loop, transport_status,S_IRUGO |S_IWUSR ); +static struct configfs_attribute *tcm_loop_tpg_attrs[] = { &tcm_loop_tpg_nexus.attr, + &tcm_loop_tpg_transport_status.attr,NULL , };
drivers/target/loopback/tcm_loop.h +4 -0
@@ -40,8 +40,12 @@ struct tcm_loop_nacl {struct se_node_acl se_node_acl; }; +#define TCM_TRANSPORT_ONLINE 0 +#define TCM_TRANSPORT_OFFLINE 1 +struct tcm_loop_tpg {unsigned short tl_tpgt; +unsigned short tl_transport_status;atomic_t tl_tpg_port_count;struct se_portal_group tl_se_tpg;struct tcm_loop_hba *tl_hba;
Signature · 1 line
1.7.12.4
PATCH 3/5 tcm_loop: separate out tcm_loop_issue_tmr
In reply to Hannes ReineckeNo functional change.
--- drivers/target/loopback/tcm_loop.c | 83 +++++++++++++++++++++++--------------- include/target/target_core_base.h | 1 + 2 files changed, 51 insertions(+), 33 deletions(-)
drivers/target/loopback/tcm_loop.c +50 -33
@@ -245,41 +245,21 @@ static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc)* Called from SCSI EH process context to issue a LUN_RESET TMR * to struct scsi_device */ -static int tcm_loop_device_reset (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) {struct se_cmd *se_cmd =NULL ; -struct se_portal_group *se_tpg;struct se_session *se_sess; +struct se_portal_group *se_tpg;struct tcm_loop_cmd *tl_cmd =NULL ; -struct tcm_loop_hba *tl_hba; -struct tcm_loop_nexus *tl_nexus;struct tcm_loop_tmr *tl_tmr =NULL ; -struct tcm_loop_tpg *tl_tpg; -int ret =FAILED , rc; -/* -* 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 ; - } - se_sess = tl_nexus->se_sess; -/* -* Locate the tl_tpg and se_tpg pointers from TargetID in sc->device->id -*/ - tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; - se_tpg = &tl_tpg->tl_se_tpg; +int ret =TMR_FUNCTION_FAILED , rc; tl_cmd =kmem_cache_zalloc (tcm_loop_cmd_cache,GFP_KERNEL );if (!tl_cmd) {pr_err ("Unable to allocate memory for tl_cmd\n" ); -return FAILED ; +return ret; } tl_tmr =kzalloc (sizeof (struct tcm_loop_tmr ),GFP_KERNEL );
@@ -290,6 +270,8 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc)init_waitqueue_head (&tl_tmr->tl_tmr_wait); se_cmd = &tl_cmd->tl_se_cmd; + se_tpg = &tl_tpg->tl_se_tpg; + se_sess = tl_nexus->se_sess;/* * Initialize struct se_cmd descriptor from target_core_mod infrastructure */
@@ -297,17 +279,20 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc)DMA_NONE ,MSG_SIMPLE_TAG , &tl_cmd->tl_sense_buf[0]); - rc =core_tmr_alloc_req (se_cmd, tl_tmr,TMR_LUN_RESET ,GFP_KERNEL ); + rc =core_tmr_alloc_req (se_cmd, tl_tmr, tmr,GFP_KERNEL );if (rc <0 ) goto release; +/* -* Locate the underlying TCM struct se_lun from sc->device->lun +* Locate the underlying TCM struct se_lun */ -if (transport_lookup_tmr_lun (se_cmd, sc->device->lun) <0 ) +if (transport_lookup_tmr_lun (se_cmd, lun) <0 ) { + ret =TMR_LUN_DOES_NOT_EXIST ; goto release; + }/* -* Queue the TMR to TCM Core and sleep waiting for tcm_loop_queue_tm_rsp() -* to wake us up. +* Queue the TMR to TCM Core and sleep waiting for +* tcm_loop_queue_tm_rsp() to wake us up. */ transport_generic_handle_tmr (se_cmd);wait_event (tl_tmr->tl_tmr_wait,atomic_read (&tl_tmr->tmr_complete));
@@ -315,8 +300,7 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc)* The TMR LUN_RESET has completed, check the response status and * then release allocations. */ - ret = (se_cmd->se_tmr_req->response ==TMR_FUNCTION_COMPLETE ) ? -SUCCESS :FAILED ; + ret = se_cmd->se_tmr_req->response; release:if (se_cmd)transport_generic_free_cmd (se_cmd,1 );
@@ -326,6 +310,39 @@ release:return ret; } +/* +* Called from SCSI EH process context to issue a LUN_RESET TMR +* to struct scsi_device +*/ +static int tcm_loop_device_reset (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,TMR_LUN_RESET ); +return (ret ==TMR_FUNCTION_COMPLETE ) ?SUCCESS :FAILED ; +} +static int tcm_loop_slave_alloc (struct scsi_device *sd) {set_bit (QUEUE_FLAG_BIDI , &sd->request_queue->queue_flags);
include/target/target_core_base.h +1 -0
@@ -227,6 +227,7 @@ enum tcm_tmreq_table {/* fabric independent task management response values */ enum tcm_tmrsp_table { +TMR_FUNCTION_FAILED =0 ,TMR_FUNCTION_COMPLETE =1 ,TMR_TASK_DOES_NOT_EXIST =2 ,TMR_LUN_DOES_NOT_EXIST =3 ,
Signature · 1 line
1.7.12.4
PATCH 4/5 tcm_loop: TCQ and command abort support
In reply to Hannes ReineckeImplement TCQ support, which enables us to do proper command abort, too.
--- drivers/target/loopback/tcm_loop.c | 71 +++++++++++++++++++++++++++++++++++--- drivers/target/loopback/tcm_loop.h | 2 ++ 2 files changed, 69 insertions(+), 4 deletions(-)
drivers/target/loopback/tcm_loop.c +67 -4
@@ -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)
drivers/target/loopback/tcm_loop.h +2 -0
@@ -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() */
Signature · 1 line
1.7.12.4
PATCH 5/5 tcm_loop: Implement target reset
In reply to Hannes ReineckeImplement target reset by resetting the transport status.
--- drivers/target/loopback/tcm_loop.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
drivers/target/loopback/tcm_loop.c +27 -0
@@ -392,6 +392,32 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc)return (ret ==TMR_FUNCTION_COMPLETE ) ?SUCCESS :FAILED ; } +static int tcm_loop_target_reset (struct scsi_cmnd *sc) +{ +struct tcm_loop_hba *tl_hba; +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); +if (!tl_hba) { +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]; +if (tl_tpg) { + tl_tpg->tl_transport_status =TCM_TRANSPORT_ONLINE ; +return SUCCESS ; + } +return FAILED ; +} +static int tcm_loop_slave_alloc (struct scsi_device *sd) {set_bit (QUEUE_FLAG_BIDI , &sd->request_queue->queue_flags);
@@ -421,6 +447,7 @@ static struct scsi_host_template tcm_loop_driver_template = { .change_queue_type = tcm_loop_change_queue_type, .eh_abort_handler = tcm_loop_abort_task, .eh_device_reset_handler = tcm_loop_device_reset, + .eh_target_reset_handler = tcm_loop_target_reset, .can_queue = 1024, .this_id = -1, .sg_tablesize = 256,
Signature · 1 line
1.7.12.4
Quoted 13 lines · Hannes Reinecke
Hi Nic,
here are some updates to
tcm_loopI've done during ALUA testing. I've implemented a 'transport_state' attribute to simulate transport failure and added command abort callbacks.Hannes Reinecke (5):
tcm_loop: Check for valid hba intcm_loop_drop_nexus()
tcm_loop: Implement transport offline
tcm_loop: separate outtcm_loop_issue_tmr
tcm_loop: TCQ and command abort support
tcm_loop: Implement target reset
Quoted 5 lines
drivers/target/loopback/tcm_loop.c | 233 +++++++++++++++++++++++++++++++------ drivers/target/loopback/tcm_loop.h | 6 + include/target/target_core_base.h | 1 + 3 files changed, 204 insertions(+), 36 deletions(-)
Everything looks reasonable to me.. Applied to target-pending/for-next.
Thanks Hannes!
--nab