diff options
Diffstat (limited to 'drivers/target/loopback/tcm_loop.c')
| -rw-r--r-- | drivers/target/loopback/tcm_loop.c | 292 | 
1 files changed, 240 insertions, 52 deletions
diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 0f6d69dabca..8c64b8776a9 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(  	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 *   */ @@ -164,7 +179,7 @@ static void tcm_loop_submission_work(struct work_struct *work)  	struct tcm_loop_hba *tl_hba;  	struct tcm_loop_tpg *tl_tpg;  	struct scatterlist *sgl_bidi = NULL; -	u32 sgl_bidi_count = 0; +	u32 sgl_bidi_count = 0, transfer_length;  	int rc;  	tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); @@ -178,7 +193,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" @@ -194,12 +212,26 @@ static void tcm_loop_submission_work(struct work_struct *work)  		se_cmd->se_cmd_flags |= SCF_BIDI;  	} + +	transfer_length = scsi_transfer_length(sc); +	if (!scsi_prot_sg_count(sc) && +	    scsi_get_prot_op(sc) != SCSI_PROT_NORMAL) { +		se_cmd->prot_pto = true; +		/* +		 * loopback transport doesn't support +		 * WRITE_GENERATE, READ_STRIP protection +		 * information operations, go ahead unprotected. +		 */ +		transfer_length = scsi_bufflen(sc); +	} +  	rc = target_submit_cmd_map_sgls(se_cmd, tl_nexus->se_sess, sc->cmnd,  			&tl_cmd->tl_sense_buf[0], tl_cmd->sc->device->lun, -			scsi_bufflen(sc), tcm_loop_sam_attr(sc), +			transfer_length, tcm_loop_sam_attr(sc),  			sc->sc_data_direction, 0,  			scsi_sglist(sc), scsi_sg_count(sc), -			sgl_bidi, sgl_bidi_count); +			sgl_bidi, sgl_bidi_count, +			scsi_prot_sglist(sc), scsi_prot_sg_count(sc));  	if (rc < 0) {  		set_host_byte(sc, DID_NO_CONNECT);  		goto out_done; @@ -207,6 +239,7 @@ static void tcm_loop_submission_work(struct work_struct *work)  	return;  out_done: +	kmem_cache_free(tcm_loop_cmd_cache, tl_cmd);  	sc->scsi_done(sc);  	return;  } @@ -233,6 +266,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; @@ -242,41 +276,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, int task, 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); @@ -287,6 +301,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  	 */ @@ -294,17 +310,23 @@ 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; + +	if (tmr == TMR_ABORT_TASK) +		se_cmd->se_tmr_req->ref_task_tag = task; +  	/* -	 * 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)); @@ -312,8 +334,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); @@ -323,6 +344,94 @@ 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 + */ +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, +				 0, TMR_LUN_RESET); +	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; + +	/* +	 * 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); @@ -331,6 +440,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;  } @@ -340,7 +458,10 @@ 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, +	.eh_target_reset_handler = tcm_loop_target_reset,  	.can_queue		= 1024,  	.this_id		= -1,  	.sg_tablesize		= 256, @@ -356,7 +477,7 @@ static int tcm_loop_driver_probe(struct device *dev)  {  	struct tcm_loop_hba *tl_hba;  	struct Scsi_Host *sh; -	int error; +	int error, host_prot;  	tl_hba = to_tcm_loop_hba(dev); @@ -380,6 +501,13 @@ static int tcm_loop_driver_probe(struct device *dev)  	sh->max_channel = 0;  	sh->max_cmd_len = TL_SCSI_MAX_CMD_LEN; +	host_prot = SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION | +		    SHOST_DIF_TYPE3_PROTECTION | SHOST_DIX_TYPE1_PROTECTION | +		    SHOST_DIX_TYPE2_PROTECTION | SHOST_DIX_TYPE3_PROTECTION; + +	scsi_host_set_prot(sh, host_prot); +	scsi_host_set_guard(sh, SHOST_DIX_GUARD_CRC); +  	error = scsi_add_host(sh, &tl_hba->dev);  	if (error) {  		pr_err("%s: scsi_add_host failed\n", __func__); @@ -699,7 +827,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) @@ -798,6 +929,11 @@ static void tcm_loop_queue_tm_rsp(struct se_cmd *se_cmd)  	wake_up(&tl_tmr->tl_tmr_wait);  } +static void tcm_loop_aborted_task(struct se_cmd *se_cmd) +{ +	return; +} +  static char *tcm_loop_dump_proto_id(struct tcm_loop_hba *tl_hba)  {  	switch (tl_hba->tl_proto_id) { @@ -825,7 +961,7 @@ static int tcm_loop_port_link(  	struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba;  	atomic_inc(&tl_tpg->tl_tpg_port_count); -	smp_mb__after_atomic_inc(); +	smp_mb__after_atomic();  	/*  	 * Add Linux/SCSI struct scsi_device by HCTL  	 */ @@ -860,7 +996,7 @@ static void tcm_loop_port_unlink(  	scsi_device_put(sd);  	atomic_dec(&tl_tpg->tl_tpg_port_count); -	smp_mb__after_atomic_dec(); +	smp_mb__after_atomic();  	pr_debug("TCM_Loop_ConfigFS: Port Unlink Successful\n");  } @@ -892,7 +1028,7 @@ static int tcm_loop_make_nexus(  	/*  	 * Initialize the struct se_session pointer  	 */ -	tl_nexus->se_sess = transport_init_session(); +	tl_nexus->se_sess = transport_init_session(TARGET_PROT_ALL);  	if (IS_ERR(tl_nexus->se_sess)) {  		ret = PTR_ERR(tl_nexus->se_sess);  		goto out; @@ -932,7 +1068,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; @@ -1061,14 +1200,62 @@ 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,  };  /* Start items for tcm_loop_naa_cit */ -struct se_portal_group *tcm_loop_make_naa_tpg( +static struct se_portal_group *tcm_loop_make_naa_tpg(  	struct se_wwn *wwn,  	struct config_group *group,  	const char *name) @@ -1113,7 +1300,7 @@ struct se_portal_group *tcm_loop_make_naa_tpg(  	return &tl_tpg->tl_se_tpg;  } -void tcm_loop_drop_naa_tpg( +static void tcm_loop_drop_naa_tpg(  	struct se_portal_group *se_tpg)  {  	struct se_wwn *wwn = se_tpg->se_tpg_wwn; @@ -1145,7 +1332,7 @@ void tcm_loop_drop_naa_tpg(  /* Start items for tcm_loop_cit */ -struct se_wwn *tcm_loop_make_scsi_hba( +static struct se_wwn *tcm_loop_make_scsi_hba(  	struct target_fabric_configfs *tf,  	struct config_group *group,  	const char *name) @@ -1215,7 +1402,7 @@ out:  	return ERR_PTR(ret);  } -void tcm_loop_drop_scsi_hba( +static void tcm_loop_drop_scsi_hba(  	struct se_wwn *wwn)  {  	struct tcm_loop_hba *tl_hba = container_of(wwn, @@ -1315,6 +1502,7 @@ static int tcm_loop_register_configfs(void)  	fabric->tf_ops.queue_data_in = &tcm_loop_queue_data_in;  	fabric->tf_ops.queue_status = &tcm_loop_queue_status;  	fabric->tf_ops.queue_tm_rsp = &tcm_loop_queue_tm_rsp; +	fabric->tf_ops.aborted_task = &tcm_loop_aborted_task;  	/*  	 * Setup function pointers for generic logic in target_core_fabric_configfs.c @@ -1334,11 +1522,11 @@ static int tcm_loop_register_configfs(void)  	/*  	 * Setup default attribute lists for various fabric->tf_cit_tmpl  	 */ -	TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = tcm_loop_wwn_attrs; -	TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = tcm_loop_tpg_attrs; -	TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL; -	TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL; -	TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL; +	fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = tcm_loop_wwn_attrs; +	fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = tcm_loop_tpg_attrs; +	fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL; +	fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL; +	fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL;  	/*  	 * Once fabric->tf_ops has been setup, now register the fabric for  	 * use within TCM  | 
