@@ -67,6 +67,8 @@ struct vhost_scsi {
/* Local pointer to allocated TCM configfs fabric module */
static struct target_fabric_configfs *tcm_vhost_fabric_configfs;
+static struct workqueue_struct *tcm_vhost_workqueue;
+
/* Global spinlock to protect tcm_vhost TPG list for vhost IOCTL access */
static DEFINE_MUTEX(tcm_vhost_mutex);
static LIST_HEAD(tcm_vhost_list);
@@ -247,55 +249,6 @@ static u32 tcm_vhost_tpg_get_inst_index(struct se_portal_group *se_tpg)
return 1;
}
-/*
- * Called by struct target_core_fabric_ops->new_cmd_map()
- *
- * Always called in process context. A non zero return value
- * here will signal to handle an exception based on the return code.
- */
-static int tcm_vhost_new_cmd_map(struct se_cmd *se_cmd)
-{
- struct tcm_vhost_cmd *tv_cmd = container_of(se_cmd,
- struct tcm_vhost_cmd, tvc_se_cmd);
- struct scatterlist *sg_ptr, *sg_bidi_ptr = NULL;
- u32 sg_no_bidi = 0;
- int ret;
- /*
- * Allocate the necessary tasks to complete the received CDB+data
- */
- ret = target_setup_cmd_from_cdb(se_cmd, tv_cmd->tvc_cdb);
- if (ret != 0)
- return ret;
- /*
- * Setup the struct scatterlist memory from the received
- * struct tcm_vhost_cmd..
- */
- if (tv_cmd->tvc_sgl_count) {
- sg_ptr = tv_cmd->tvc_sgl;
- /*
- * For BIDI commands, pass in the extra READ buffer
- * to transport_generic_map_mem_to_cmd() below..
- */
-/* FIXME: Fix BIDI operation in tcm_vhost_new_cmd_map() */
-#if 0
- if (se_cmd->se_cmd_flags & SCF_BIDI) {
- mem_bidi_ptr = NULL;
- sg_no_bidi = 0;
- }
-#endif
- } else {
- /*
- * Used for DMA_NONE
- */
- sg_ptr = NULL;
- }
-
- /* Tell the core about our preallocated memory */
- return transport_generic_map_mem_to_cmd(se_cmd, sg_ptr,
- tv_cmd->tvc_sgl_count, sg_bidi_ptr,
- sg_no_bidi);
-}
-
static void tcm_vhost_release_cmd(struct se_cmd *se_cmd)
{
return;
@@ -509,12 +462,6 @@ static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd(
if (bidi)
se_cmd->se_cmd_flags |= SCF_BIDI;
#endif
- /*
- * From here the rest of the se_cmd will be setup and dispatched
- * via tcm_vhost_new_cmd_map() from TCM backend thread context
- * after transport_generic_handle_cdb_map() has been called from
- * vhost_scsi_handle_vq() below..
- */
return tv_cmd;
}
@@ -611,6 +558,71 @@ static int vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *tv_cmd,
return 0;
}
+static void tcm_vhost_submission_work(struct work_struct *work)
+{
+ struct tcm_vhost_cmd *tv_cmd =
+ container_of(work, struct tcm_vhost_cmd, work);
+ struct se_cmd *se_cmd = &tv_cmd->tvc_se_cmd;
+ struct scatterlist *sg_ptr, *sg_bidi_ptr = NULL;
+ int rc, sg_no_bidi = 0;
+ /*
+ * Locate the struct se_lun pointer based on v_req->lun, and
+ * attach it to struct se_cmd
+ */
+ rc = transport_lookup_cmd_lun(&tv_cmd->tvc_se_cmd, tv_cmd->tvc_lun);
+ if (rc < 0) {
+ pr_err("Failed to look up lun: %d\n", tv_cmd->tvc_lun);
+ transport_send_check_condition_and_sense(&tv_cmd->tvc_se_cmd,
+ tv_cmd->tvc_se_cmd.scsi_sense_reason, 0);
+ transport_generic_free_cmd(se_cmd, 0);
+ return;
+ }
+
+ rc = target_setup_cmd_from_cdb(se_cmd, tv_cmd->tvc_cdb);
+ if (rc == -ENOMEM) {
+ transport_send_check_condition_and_sense(se_cmd,
+ TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
+ transport_generic_free_cmd(se_cmd, 0);
+ return;
+ } else if (rc < 0) {
+ if (se_cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT)
+ tcm_vhost_queue_status(se_cmd);
+ else
+ transport_send_check_condition_and_sense(se_cmd,
+ se_cmd->scsi_sense_reason, 0);
+ transport_generic_free_cmd(se_cmd, 0);
+ return;
+ }
+
+ if (tv_cmd->tvc_sgl_count) {
+ sg_ptr = tv_cmd->tvc_sgl;
+ /*
+ * For BIDI commands, pass in the extra READ buffer
+ * to transport_generic_map_mem_to_cmd() below..
+ */
+/* FIXME: Fix BIDI operation in tcm_vhost_submission_work() */
+#if 0
+ if (se_cmd->se_cmd_flags & SCF_BIDI) {
+ sg_bidi_ptr = NULL;
+ sg_no_bidi = 0;
+ }
+#endif
+ } else {
+ sg_ptr = NULL;
+ }
+
+ rc = transport_generic_map_mem_to_cmd(se_cmd, sg_ptr,
+ tv_cmd->tvc_sgl_count, sg_bidi_ptr,
+ sg_no_bidi);
+ if (rc < 0) {
+ transport_send_check_condition_and_sense(se_cmd,
+ se_cmd->scsi_sense_reason, 0);
+ transport_generic_free_cmd(se_cmd, 0);
+ return;
+ }
+ transport_handle_cdb_direct(se_cmd);
+}
+
static void vhost_scsi_handle_vq(struct vhost_scsi *vs)
{
struct vhost_virtqueue *vq = &vs->vqs[2];
@@ -619,7 +631,7 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs)
struct tcm_vhost_cmd *tv_cmd;
u32 exp_data_len, data_first, data_num, data_direction;
unsigned out, in, i;
- int head, ret, lun;
+ int head, ret;
/* Must use ioctl VHOST_SCSI_SET_ENDPOINT */
tv_tpg = vs->vs_tpg;
@@ -732,10 +744,10 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs)
scsi_command_size(tv_cmd->tvc_cdb), TCM_VHOST_MAX_CDB_SIZE);
break; /* TODO */
}
- lun = ((v_req.lun[2] << 8) | v_req.lun[3]) & 0x3FFF;
+ tv_cmd->tvc_lun = ((v_req.lun[2] << 8) | v_req.lun[3]) & 0x3FFF;
pr_debug("vhost_scsi got command opcode: %#02x, lun: %d\n",
- tv_cmd->tvc_cdb[0], lun);
+ tv_cmd->tvc_cdb[0], tv_cmd->tvc_lun);
if (data_direction != DMA_NONE) {
ret = vhost_scsi_map_iov_to_sgl(tv_cmd, &vq->iov[data_first],
@@ -753,22 +765,13 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs)
*/
tv_cmd->tvc_vq_desc = head;
/*
- * Locate the struct se_lun pointer based on v_req->lun, and
- * attach it to struct se_cmd
- */
- if (transport_lookup_cmd_lun(&tv_cmd->tvc_se_cmd, lun) < 0) {
- pr_err("Failed to look up lun: %d\n", lun);
- /* NON_EXISTENT_LUN */
- transport_send_check_condition_and_sense(&tv_cmd->tvc_se_cmd,
- tv_cmd->tvc_se_cmd.scsi_sense_reason, 0);
- continue;
- }
- /*
- * Now queue up the newly allocated se_cmd to be processed
- * within TCM thread context to finish the setup and dispatched
- * into a TCM backend struct se_device.
+ * Dispatch tv_cmd descriptor for cmwq execution in process
+ * context provided by tcm_vhost_workqueue. This also ensures
+ * tv_cmd is executed on the same kworker CPU as this vhost
+ * thread to gain positive L2 cache locality effects..
*/
- transport_generic_handle_cdb_map(&tv_cmd->tvc_se_cmd);
+ INIT_WORK(&tv_cmd->work, tcm_vhost_submission_work);
+ queue_work(tcm_vhost_workqueue, &tv_cmd->work);
}
mutex_unlock(&vq->mutex);
@@ -1478,7 +1481,6 @@ static struct target_core_fabric_ops tcm_vhost_ops = {
.tpg_alloc_fabric_acl = tcm_vhost_alloc_fabric_acl,
.tpg_release_fabric_acl = tcm_vhost_release_fabric_acl,
.tpg_get_inst_index = tcm_vhost_tpg_get_inst_index,
- .new_cmd_map = tcm_vhost_new_cmd_map,
.release_cmd = tcm_vhost_release_cmd,
.shutdown_session = tcm_vhost_shutdown_session,
.close_session = tcm_vhost_close_session,
@@ -1570,23 +1572,35 @@ static void tcm_vhost_deregister_configfs(void)
static int __init tcm_vhost_init(void)
{
- int ret;
+ int ret = -ENOMEM;
+
+ tcm_vhost_workqueue = alloc_workqueue("tcm_vhost", 0, 0);
+ if (!tcm_vhost_workqueue)
+ goto out;
ret = vhost_scsi_register();
if (ret < 0)
- return ret;
+ goto out_destroy_workqueue;
ret = tcm_vhost_register_configfs();
if (ret < 0)
- return ret;
+ goto out_vhost_scsi_deregister;
return 0;
+
+out_vhost_scsi_deregister:
+ vhost_scsi_deregister();
+out_destroy_workqueue:
+ destroy_workqueue(tcm_vhost_workqueue);
+out:
+ return ret;
};
static void tcm_vhost_exit(void)
{
tcm_vhost_deregister_configfs();
vhost_scsi_deregister();
+ destroy_workqueue(tcm_vhost_workqueue);
};
MODULE_DESCRIPTION("TCM_VHOST series fabric driver");
@@ -9,14 +9,18 @@ struct tcm_vhost_cmd {
u64 tvc_tag;
/* The number of scatterlists associated with this cmd */
u32 tvc_sgl_count;
+ /* Saved unpacked SCSI LUN for tcm_vhost_submission_work() */
+ u32 tvc_lun;
/* Pointer to the SGL formatted memory from virtio-scsi */
struct scatterlist *tvc_sgl;
/* Pointer to response */
struct virtio_scsi_cmd_resp __user *tvc_resp;
/* Pointer to vhost_scsi for our device */
struct vhost_scsi *tvc_vhost;
- /* The TCM I/O descriptor that is accessed via container_of() */
+ /* The TCM I/O descriptor that is accessed via container_of() */
struct se_cmd tvc_se_cmd;
+ /* work item used for cmwq dispatch to tcm_vhost_submission_work() */
+ struct work_struct work;
/* Copy of the incoming SCSI command descriptor block (CDB) */
unsigned char tvc_cdb[TCM_VHOST_MAX_CDB_SIZE];
/* Sense buffer that will be mapped into outgoing status */