diff options
Diffstat (limited to 'drivers/xen/xen-scsiback.c')
| -rw-r--r-- | drivers/xen/xen-scsiback.c | 281 | 
1 files changed, 139 insertions, 142 deletions
| diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index c46ee189466f..ff932624eaad 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -141,6 +141,8 @@ struct scsiback_tmr {  	wait_queue_head_t tmr_wait;  }; +#define VSCSI_DEFAULT_SESSION_TAGS	128 +  struct scsiback_nexus {  	/* Pointer to TCM session for I_T Nexus */  	struct se_session *tvn_se_sess; @@ -190,7 +192,6 @@ module_param_named(max_buffer_pages, scsiback_max_buffer_pages, int, 0644);  MODULE_PARM_DESC(max_buffer_pages,  "Maximum number of free pages to keep in backend buffer"); -static struct kmem_cache *scsiback_cachep;  static DEFINE_SPINLOCK(free_pages_lock);  static int free_pages_num;  static LIST_HEAD(scsiback_free_pages); @@ -321,11 +322,11 @@ static void scsiback_free_translation_entry(struct kref *kref)  	kfree(entry);  } -static void scsiback_do_resp_with_sense(char *sense_buffer, int32_t result, -			uint32_t resid, struct vscsibk_pend *pending_req) +static void scsiback_send_response(struct vscsibk_info *info, +			char *sense_buffer, int32_t result, uint32_t resid, +			uint16_t rqid)  {  	struct vscsiif_response *ring_res; -	struct vscsibk_info *info = pending_req->info;  	int notify;  	struct scsi_sense_hdr sshdr;  	unsigned long flags; @@ -337,7 +338,7 @@ static void scsiback_do_resp_with_sense(char *sense_buffer, int32_t result,  	info->ring.rsp_prod_pvt++;  	ring_res->rslt   = result; -	ring_res->rqid   = pending_req->rqid; +	ring_res->rqid   = rqid;  	if (sense_buffer != NULL &&  	    scsi_normalize_sense(sense_buffer, VSCSIIF_SENSE_BUFFERSIZE, @@ -357,6 +358,13 @@ static void scsiback_do_resp_with_sense(char *sense_buffer, int32_t result,  	if (notify)  		notify_remote_via_irq(info->irq); +} + +static void scsiback_do_resp_with_sense(char *sense_buffer, int32_t result, +			uint32_t resid, struct vscsibk_pend *pending_req) +{ +	scsiback_send_response(pending_req->info, sense_buffer, result, +			       resid, pending_req->rqid);  	if (pending_req->v2p)  		kref_put(&pending_req->v2p->kref, @@ -380,6 +388,12 @@ static void scsiback_cmd_done(struct vscsibk_pend *pending_req)  	scsiback_fast_flush_area(pending_req);  	scsiback_do_resp_with_sense(sense_buffer, errors, resid, pending_req);  	scsiback_put(info); +	/* +	 * Drop the extra KREF_ACK reference taken by target_submit_cmd_map_sgls() +	 * ahead of scsiback_check_stop_free() ->  transport_generic_free_cmd() +	 * final se_cmd->cmd_kref put. +	 */ +	target_put_sess_cmd(&pending_req->se_cmd);  }  static void scsiback_cmd_exec(struct vscsibk_pend *pending_req) @@ -388,16 +402,12 @@ static void scsiback_cmd_exec(struct vscsibk_pend *pending_req)  	struct se_session *sess = pending_req->v2p->tpg->tpg_nexus->tvn_se_sess;  	int rc; -	memset(pending_req->sense_buffer, 0, VSCSIIF_SENSE_BUFFERSIZE); - -	memset(se_cmd, 0, sizeof(*se_cmd)); -  	scsiback_get(pending_req->info);  	se_cmd->tag = pending_req->rqid;  	rc = target_submit_cmd_map_sgls(se_cmd, sess, pending_req->cmnd,  			pending_req->sense_buffer, pending_req->v2p->lun,  			pending_req->data_len, 0, -			pending_req->sc_data_direction, 0, +			pending_req->sc_data_direction, TARGET_SCF_ACK_KREF,  			pending_req->sgl, pending_req->n_sg,  			NULL, 0, NULL, 0);  	if (rc < 0) { @@ -586,45 +596,40 @@ static void scsiback_disconnect(struct vscsibk_info *info)  static void scsiback_device_action(struct vscsibk_pend *pending_req,  	enum tcm_tmreq_table act, int tag)  { -	int rc, err = FAILED;  	struct scsiback_tpg *tpg = pending_req->v2p->tpg; +	struct scsiback_nexus *nexus = tpg->tpg_nexus;  	struct se_cmd *se_cmd = &pending_req->se_cmd;  	struct scsiback_tmr *tmr; +	u64 unpacked_lun = pending_req->v2p->lun; +	int rc, err = FAILED;  	tmr = kzalloc(sizeof(struct scsiback_tmr), GFP_KERNEL); -	if (!tmr) -		goto out; +	if (!tmr) { +		target_put_sess_cmd(se_cmd); +		goto err; +	}  	init_waitqueue_head(&tmr->tmr_wait); -	transport_init_se_cmd(se_cmd, tpg->se_tpg.se_tpg_tfo, -		tpg->tpg_nexus->tvn_se_sess, 0, DMA_NONE, TCM_SIMPLE_TAG, -		&pending_req->sense_buffer[0]); - -	rc = core_tmr_alloc_req(se_cmd, tmr, act, GFP_KERNEL); -	if (rc < 0) -		goto out; - -	se_cmd->se_tmr_req->ref_task_tag = tag; - -	if (transport_lookup_tmr_lun(se_cmd, pending_req->v2p->lun) < 0) -		goto out; +	rc = target_submit_tmr(&pending_req->se_cmd, nexus->tvn_se_sess, +			       &pending_req->sense_buffer[0], +			       unpacked_lun, tmr, act, GFP_KERNEL, +			       tag, TARGET_SCF_ACK_KREF); +	if (rc) +		goto err; -	transport_generic_handle_tmr(se_cmd);  	wait_event(tmr->tmr_wait, atomic_read(&tmr->tmr_complete));  	err = (se_cmd->se_tmr_req->response == TMR_FUNCTION_COMPLETE) ?  		SUCCESS : FAILED; -out: -	if (tmr) { -		transport_generic_free_cmd(&pending_req->se_cmd, 1); +	scsiback_do_resp_with_sense(NULL, err, 0, pending_req); +	transport_generic_free_cmd(&pending_req->se_cmd, 1); +	return; +err: +	if (tmr)  		kfree(tmr); -	} -  	scsiback_do_resp_with_sense(NULL, err, 0, pending_req); - -	kmem_cache_free(scsiback_cachep, pending_req);  }  /* @@ -653,15 +658,53 @@ out:  	return entry;  } -static int prepare_pending_reqs(struct vscsibk_info *info, -				struct vscsiif_request *ring_req, -				struct vscsibk_pend *pending_req) +static struct vscsibk_pend *scsiback_get_pend_req(struct vscsiif_back_ring *ring, +				struct v2p_entry *v2p) +{ +	struct scsiback_tpg *tpg = v2p->tpg; +	struct scsiback_nexus *nexus = tpg->tpg_nexus; +	struct se_session *se_sess = nexus->tvn_se_sess; +	struct vscsibk_pend *req; +	int tag, i; + +	tag = percpu_ida_alloc(&se_sess->sess_tag_pool, TASK_RUNNING); +	if (tag < 0) { +		pr_err("Unable to obtain tag for vscsiif_request\n"); +		return ERR_PTR(-ENOMEM); +	} + +	req = &((struct vscsibk_pend *)se_sess->sess_cmd_map)[tag]; +	memset(req, 0, sizeof(*req)); +	req->se_cmd.map_tag = tag; + +	for (i = 0; i < VSCSI_MAX_GRANTS; i++) +		req->grant_handles[i] = SCSIBACK_INVALID_HANDLE; + +	return req; +} + +static struct vscsibk_pend *prepare_pending_reqs(struct vscsibk_info *info, +				struct vscsiif_back_ring *ring, +				struct vscsiif_request *ring_req)  { +	struct vscsibk_pend *pending_req;  	struct v2p_entry *v2p;  	struct ids_tuple vir; -	pending_req->rqid       = ring_req->rqid; -	pending_req->info       = info; +	/* request range check from frontend */ +	if ((ring_req->sc_data_direction != DMA_BIDIRECTIONAL) && +		(ring_req->sc_data_direction != DMA_TO_DEVICE) && +		(ring_req->sc_data_direction != DMA_FROM_DEVICE) && +		(ring_req->sc_data_direction != DMA_NONE)) { +		pr_debug("invalid parameter data_dir = %d\n", +			ring_req->sc_data_direction); +		return ERR_PTR(-EINVAL); +	} +	if (ring_req->cmd_len > VSCSIIF_MAX_COMMAND_SIZE) { +		pr_debug("invalid parameter cmd_len = %d\n", +			ring_req->cmd_len); +		return ERR_PTR(-EINVAL); +	}  	vir.chn = ring_req->channel;  	vir.tgt = ring_req->id; @@ -669,33 +712,24 @@ static int prepare_pending_reqs(struct vscsibk_info *info,  	v2p = scsiback_do_translation(info, &vir);  	if (!v2p) { -		pending_req->v2p = NULL;  		pr_debug("the v2p of (chn:%d, tgt:%d, lun:%d) doesn't exist.\n", -			vir.chn, vir.tgt, vir.lun); -		return -ENODEV; +			 vir.chn, vir.tgt, vir.lun); +		return ERR_PTR(-ENODEV);  	} -	pending_req->v2p = v2p; -	/* request range check from frontend */ -	pending_req->sc_data_direction = ring_req->sc_data_direction; -	if ((pending_req->sc_data_direction != DMA_BIDIRECTIONAL) && -		(pending_req->sc_data_direction != DMA_TO_DEVICE) && -		(pending_req->sc_data_direction != DMA_FROM_DEVICE) && -		(pending_req->sc_data_direction != DMA_NONE)) { -		pr_debug("invalid parameter data_dir = %d\n", -			pending_req->sc_data_direction); -		return -EINVAL; +	pending_req = scsiback_get_pend_req(ring, v2p); +	if (IS_ERR(pending_req)) { +		kref_put(&v2p->kref, scsiback_free_translation_entry); +		return ERR_PTR(-ENOMEM);  	} - +	pending_req->rqid = ring_req->rqid; +	pending_req->info = info; +	pending_req->v2p = v2p; +	pending_req->sc_data_direction = ring_req->sc_data_direction;  	pending_req->cmd_len = ring_req->cmd_len; -	if (pending_req->cmd_len > VSCSIIF_MAX_COMMAND_SIZE) { -		pr_debug("invalid parameter cmd_len = %d\n", -			pending_req->cmd_len); -		return -EINVAL; -	}  	memcpy(pending_req->cmnd, ring_req->cmnd, pending_req->cmd_len); -	return 0; +	return pending_req;  }  static int scsiback_do_cmd_fn(struct vscsibk_info *info) @@ -704,7 +738,7 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info)  	struct vscsiif_request ring_req;  	struct vscsibk_pend *pending_req;  	RING_IDX rc, rp; -	int err, more_to_do; +	int more_to_do;  	uint32_t result;  	rc = ring->req_cons; @@ -722,16 +756,13 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info)  	while ((rc != rp)) {  		if (RING_REQUEST_CONS_OVERFLOW(ring, rc))  			break; -		pending_req = kmem_cache_alloc(scsiback_cachep, GFP_KERNEL); -		if (!pending_req) -			return 1;  		RING_COPY_REQUEST(ring, rc, &ring_req);  		ring->req_cons = ++rc; -		err = prepare_pending_reqs(info, &ring_req, pending_req); -		if (err) { -			switch (err) { +		pending_req = prepare_pending_reqs(info, ring, &ring_req); +		if (IS_ERR(pending_req)) { +			switch (PTR_ERR(pending_req)) {  			case -ENODEV:  				result = DID_NO_CONNECT;  				break; @@ -739,9 +770,8 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info)  				result = DRIVER_ERROR;  				break;  			} -			scsiback_do_resp_with_sense(NULL, result << 24, 0, -						    pending_req); -			kmem_cache_free(scsiback_cachep, pending_req); +			scsiback_send_response(info, NULL, result << 24, 0, +					       ring_req.rqid);  			return 1;  		} @@ -750,8 +780,8 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info)  			if (scsiback_gnttab_data_map(&ring_req, pending_req)) {  				scsiback_fast_flush_area(pending_req);  				scsiback_do_resp_with_sense(NULL, -					DRIVER_ERROR << 24, 0, pending_req); -				kmem_cache_free(scsiback_cachep, pending_req); +						DRIVER_ERROR << 24, 0, pending_req); +				transport_generic_free_cmd(&pending_req->se_cmd, 0);  			} else {  				scsiback_cmd_exec(pending_req);  			} @@ -765,9 +795,9 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info)  			break;  		default:  			pr_err_ratelimited("invalid request\n"); -			scsiback_do_resp_with_sense(NULL, DRIVER_ERROR << 24, -						    0, pending_req); -			kmem_cache_free(scsiback_cachep, pending_req); +			scsiback_do_resp_with_sense(NULL, DRIVER_ERROR << 24, 0, +						    pending_req); +			transport_generic_free_cmd(&pending_req->se_cmd, 0);  			break;  		} @@ -1353,24 +1383,20 @@ static u32 scsiback_tpg_get_inst_index(struct se_portal_group *se_tpg)  static int scsiback_check_stop_free(struct se_cmd *se_cmd)  { -	/* -	 * Do not release struct se_cmd's containing a valid TMR pointer. -	 * These will be released directly in scsiback_device_action() -	 * with transport_generic_free_cmd(). -	 */ -	if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) -		return 0; - -	transport_generic_free_cmd(se_cmd, 0); -	return 1; +	return transport_generic_free_cmd(se_cmd, 0);  }  static void scsiback_release_cmd(struct se_cmd *se_cmd)  { -	struct vscsibk_pend *pending_req = container_of(se_cmd, -				struct vscsibk_pend, se_cmd); +	struct se_session *se_sess = se_cmd->se_sess; +	struct se_tmr_req *se_tmr = se_cmd->se_tmr_req; + +	if (se_tmr && se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) { +		struct scsiback_tmr *tmr = se_tmr->fabric_tmr_ptr; +		kfree(tmr); +	} -	kmem_cache_free(scsiback_cachep, pending_req); +	percpu_ida_free(&se_sess->sess_tag_pool, se_cmd->map_tag);  }  static int scsiback_shutdown_session(struct se_session *se_sess) @@ -1494,61 +1520,49 @@ static struct configfs_attribute *scsiback_param_attrs[] = {  	NULL,  }; +static int scsiback_alloc_sess_cb(struct se_portal_group *se_tpg, +				  struct se_session *se_sess, void *p) +{ +	struct scsiback_tpg *tpg = container_of(se_tpg, +				struct scsiback_tpg, se_tpg); + +	tpg->tpg_nexus = p; +	return 0; +} +  static int scsiback_make_nexus(struct scsiback_tpg *tpg,  				const char *name)  { -	struct se_portal_group *se_tpg; -	struct se_session *se_sess;  	struct scsiback_nexus *tv_nexus; +	int ret = 0;  	mutex_lock(&tpg->tv_tpg_mutex);  	if (tpg->tpg_nexus) { -		mutex_unlock(&tpg->tv_tpg_mutex);  		pr_debug("tpg->tpg_nexus already exists\n"); -		return -EEXIST; +		ret = -EEXIST; +		goto out_unlock;  	} -	se_tpg = &tpg->se_tpg;  	tv_nexus = kzalloc(sizeof(struct scsiback_nexus), GFP_KERNEL);  	if (!tv_nexus) { -		mutex_unlock(&tpg->tv_tpg_mutex); -		return -ENOMEM; +		ret = -ENOMEM; +		goto out_unlock;  	} -	/* -	 * Initialize the struct se_session pointer -	 */ -	tv_nexus->tvn_se_sess = transport_init_session(TARGET_PROT_NORMAL); + +	tv_nexus->tvn_se_sess = target_alloc_session(&tpg->se_tpg, +						     VSCSI_DEFAULT_SESSION_TAGS, +						     sizeof(struct vscsibk_pend), +						     TARGET_PROT_NORMAL, name, +						     tv_nexus, scsiback_alloc_sess_cb);  	if (IS_ERR(tv_nexus->tvn_se_sess)) { -		mutex_unlock(&tpg->tv_tpg_mutex);  		kfree(tv_nexus); -		return -ENOMEM; +		ret = -ENOMEM; +		goto out_unlock;  	} -	se_sess = tv_nexus->tvn_se_sess; -	/* -	 * Since we are running in 'demo mode' this call with generate a -	 * struct se_node_acl for the scsiback struct se_portal_group with -	 * the SCSI Initiator port name of the passed configfs group 'name'. -	 */ -	tv_nexus->tvn_se_sess->se_node_acl = core_tpg_check_initiator_node_acl( -				se_tpg, (unsigned char *)name); -	if (!tv_nexus->tvn_se_sess->se_node_acl) { -		mutex_unlock(&tpg->tv_tpg_mutex); -		pr_debug("core_tpg_check_initiator_node_acl() failed for %s\n", -			 name); -		goto out; -	} -	/* Now register the TCM pvscsi virtual I_T Nexus as active. */ -	transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl, -			tv_nexus->tvn_se_sess, tv_nexus); -	tpg->tpg_nexus = tv_nexus; +out_unlock:  	mutex_unlock(&tpg->tv_tpg_mutex); -	return 0; - -out: -	transport_free_session(se_sess); -	kfree(tv_nexus); -	return -ENOMEM; +	return ret;  }  static int scsiback_drop_nexus(struct scsiback_tpg *tpg) @@ -1866,16 +1880,6 @@ static struct xenbus_driver scsiback_driver = {  	.otherend_changed	= scsiback_frontend_changed  }; -static void scsiback_init_pend(void *p) -{ -	struct vscsibk_pend *pend = p; -	int i; - -	memset(pend, 0, sizeof(*pend)); -	for (i = 0; i < VSCSI_MAX_GRANTS; i++) -		pend->grant_handles[i] = SCSIBACK_INVALID_HANDLE; -} -  static int __init scsiback_init(void)  {  	int ret; @@ -1886,14 +1890,9 @@ static int __init scsiback_init(void)  	pr_debug("xen-pvscsi: fabric module %s on %s/%s on "UTS_RELEASE"\n",  		 VSCSI_VERSION, utsname()->sysname, utsname()->machine); -	scsiback_cachep = kmem_cache_create("vscsiif_cache", -		sizeof(struct vscsibk_pend), 0, 0, scsiback_init_pend); -	if (!scsiback_cachep) -		return -ENOMEM; -  	ret = xenbus_register_backend(&scsiback_driver);  	if (ret) -		goto out_cache_destroy; +		goto out;  	ret = target_register_template(&scsiback_ops);  	if (ret) @@ -1903,8 +1902,7 @@ static int __init scsiback_init(void)  out_unregister_xenbus:  	xenbus_unregister_driver(&scsiback_driver); -out_cache_destroy: -	kmem_cache_destroy(scsiback_cachep); +out:  	pr_err("%s: error %d\n", __func__, ret);  	return ret;  } @@ -1920,7 +1918,6 @@ static void __exit scsiback_exit(void)  	}  	target_unregister_template(&scsiback_ops);  	xenbus_unregister_driver(&scsiback_driver); -	kmem_cache_destroy(scsiback_cachep);  }  module_init(scsiback_init); | 
