#include "vhost.h"
#include "tcm_vhost.h"
+enum {
+ VHOST_SCSI_VQ_CTL = 0,
+ VHOST_SCSI_VQ_EVT = 1,
+ VHOST_SCSI_VQ_IO = 2,
+};
+
struct vhost_scsi {
- atomic_t vhost_ref_cnt;
- struct tcm_vhost_tpg *vs_tpg;
+ struct tcm_vhost_tpg *vs_tpg; /* Protected by vhost_scsi->dev.mutex */
struct vhost_dev dev;
struct vhost_virtqueue vqs[3];
return 1;
}
-static u32 tcm_vhost_get_pr_transport_id(
- struct se_portal_group *se_tpg,
+static u32 tcm_vhost_get_pr_transport_id(struct se_portal_group *se_tpg,
struct se_node_acl *se_nacl,
struct t10_pr_registration *pr_reg,
int *format_code,
format_code, buf);
}
-static u32 tcm_vhost_get_pr_transport_id_len(
- struct se_portal_group *se_tpg,
+static u32 tcm_vhost_get_pr_transport_id_len(struct se_portal_group *se_tpg,
struct se_node_acl *se_nacl,
struct t10_pr_registration *pr_reg,
int *format_code)
format_code);
}
-static char *tcm_vhost_parse_pr_out_transport_id(
- struct se_portal_group *se_tpg,
+static char *tcm_vhost_parse_pr_out_transport_id(struct se_portal_group *se_tpg,
const char *buf,
u32 *out_tid_len,
char **port_nexus_ptr)
return &nacl->se_node_acl;
}
-static void tcm_vhost_release_fabric_acl(
- struct se_portal_group *se_tpg,
+static void tcm_vhost_release_fabric_acl(struct se_portal_group *se_tpg,
struct se_node_acl *se_nacl)
{
struct tcm_vhost_nacl *nacl = container_of(se_nacl,
return 0;
}
-static void vhost_scsi_complete_cmd(struct tcm_vhost_cmd *);
+static void vhost_scsi_complete_cmd(struct tcm_vhost_cmd *tv_cmd)
+{
+ struct vhost_scsi *vs = tv_cmd->tvc_vhost;
+
+ spin_lock_bh(&vs->vs_completion_lock);
+ list_add_tail(&tv_cmd->tvc_completion_list, &vs->vs_completion_list);
+ spin_unlock_bh(&vs->vs_completion_lock);
+
+ vhost_work_queue(&vs->dev, &vs->vs_completion_work);
+}
static int tcm_vhost_queue_data_in(struct se_cmd *se_cmd)
{
vs_completion_work);
struct tcm_vhost_cmd *tv_cmd;
- while ((tv_cmd = vhost_scsi_get_cmd_from_completion(vs)) != NULL) {
+ while ((tv_cmd = vhost_scsi_get_cmd_from_completion(vs))) {
struct virtio_scsi_cmd_resp v_rsp;
struct se_cmd *se_cmd = &tv_cmd->tvc_se_cmd;
int ret;
vhost_signal(&vs->dev, &vs->vqs[2]);
}
-static void vhost_scsi_complete_cmd(struct tcm_vhost_cmd *tv_cmd)
-{
- struct vhost_scsi *vs = tv_cmd->tvc_vhost;
-
- pr_debug("%s tv_cmd %p\n", __func__, tv_cmd);
-
- spin_lock_bh(&vs->vs_completion_lock);
- list_add_tail(&tv_cmd->tvc_completion_list, &vs->vs_completion_list);
- spin_unlock_bh(&vs->vs_completion_lock);
-
- vhost_work_queue(&vs->dev, &vs->vs_completion_work);
-}
-
static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd(
struct tcm_vhost_tpg *tv_tpg,
struct virtio_scsi_cmd_req *v_req,
static void vhost_scsi_ctl_handle_kick(struct vhost_work *work)
{
- pr_err("%s: The handling func for control queue.\n", __func__);
+ pr_debug("%s: The handling func for control queue.\n", __func__);
}
static void vhost_scsi_evt_handle_kick(struct vhost_work *work)
{
- pr_err("%s: The handling func for event queue.\n", __func__);
+ pr_debug("%s: The handling func for event queue.\n", __func__);
}
static void vhost_scsi_handle_kick(struct vhost_work *work)
return -EFAULT;
}
}
-
- if (vs->vs_tpg) {
- mutex_unlock(&vs->dev.mutex);
- return -EEXIST;
- }
mutex_unlock(&vs->dev.mutex);
mutex_lock(&tcm_vhost_mutex);
mutex_unlock(&tv_tpg->tv_tpg_mutex);
continue;
}
- if (atomic_read(&tv_tpg->tv_tpg_vhost_count)) {
+ if (tv_tpg->tv_tpg_vhost_count != 0) {
mutex_unlock(&tv_tpg->tv_tpg_mutex);
continue;
}
if (!strcmp(tv_tport->tport_name, t->vhost_wwpn) &&
(tv_tpg->tport_tpgt == t->vhost_tpgt)) {
- atomic_inc(&tv_tpg->tv_tpg_vhost_count);
- smp_mb__after_atomic_inc();
+ tv_tpg->tv_tpg_vhost_count++;
mutex_unlock(&tv_tpg->tv_tpg_mutex);
mutex_unlock(&tcm_vhost_mutex);
mutex_lock(&vs->dev.mutex);
+ if (vs->vs_tpg) {
+ mutex_unlock(&vs->dev.mutex);
+ mutex_lock(&tv_tpg->tv_tpg_mutex);
+ tv_tpg->tv_tpg_vhost_count--;
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ return -EEXIST;
+ }
+
vs->vs_tpg = tv_tpg;
- atomic_inc(&vs->vhost_ref_cnt);
smp_mb__after_atomic_inc();
mutex_unlock(&vs->dev.mutex);
return 0;
{
struct tcm_vhost_tport *tv_tport;
struct tcm_vhost_tpg *tv_tpg;
- int index;
+ int index, ret;
mutex_lock(&vs->dev.mutex);
/* Verify that ring has been setup correctly. */
for (index = 0; index < vs->dev.nvqs; ++index) {
if (!vhost_vq_access_ok(&vs->vqs[index])) {
- mutex_unlock(&vs->dev.mutex);
- return -EFAULT;
+ ret = -EFAULT;
+ goto err;
}
}
if (!vs->vs_tpg) {
- mutex_unlock(&vs->dev.mutex);
- return -ENODEV;
+ ret = -ENODEV;
+ goto err;
}
tv_tpg = vs->vs_tpg;
tv_tport = tv_tpg->tport;
if (strcmp(tv_tport->tport_name, t->vhost_wwpn) ||
(tv_tpg->tport_tpgt != t->vhost_tpgt)) {
- mutex_unlock(&vs->dev.mutex);
pr_warn("tv_tport->tport_name: %s, tv_tpg->tport_tpgt: %hu"
" does not match t->vhost_wwpn: %s, t->vhost_tpgt: %hu\n",
tv_tport->tport_name, tv_tpg->tport_tpgt,
t->vhost_wwpn, t->vhost_tpgt);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
- atomic_dec(&tv_tpg->tv_tpg_vhost_count);
+ tv_tpg->tv_tpg_vhost_count--;
vs->vs_tpg = NULL;
mutex_unlock(&vs->dev.mutex);
return 0;
+
+err:
+ mutex_unlock(&vs->dev.mutex);
+ return ret;
}
static int vhost_scsi_open(struct inode *inode, struct file *f)
INIT_LIST_HEAD(&s->vs_completion_list);
spin_lock_init(&s->vs_completion_lock);
- s->vqs[0].handle_kick = vhost_scsi_ctl_handle_kick;
- s->vqs[1].handle_kick = vhost_scsi_evt_handle_kick;
- s->vqs[2].handle_kick = vhost_scsi_handle_kick;
+ s->vqs[VHOST_SCSI_VQ_CTL].handle_kick = vhost_scsi_ctl_handle_kick;
+ s->vqs[VHOST_SCSI_VQ_EVT].handle_kick = vhost_scsi_evt_handle_kick;
+ s->vqs[VHOST_SCSI_VQ_IO].handle_kick = vhost_scsi_handle_kick;
r = vhost_dev_init(&s->dev, s->vqs, 3);
if (r < 0) {
kfree(s);
return 0;
}
+static void vhost_scsi_flush_vq(struct vhost_scsi *vs, int index)
+{
+ vhost_poll_flush(&vs->dev.vqs[index].poll);
+}
+
+static void vhost_scsi_flush(struct vhost_scsi *vs)
+{
+ vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_CTL);
+ vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_EVT);
+ vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_IO);
+}
+
static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features)
{
if (features & ~VHOST_FEATURES)
return -EFAULT;
}
vs->dev.acked_features = features;
- /* TODO possibly smp_wmb() and flush vqs */
+ smp_wmb();
+ vhost_scsi_flush(vs);
mutex_unlock(&vs->dev.mutex);
return 0;
}
void __user *argp = (void __user *)arg;
u64 __user *featurep = argp;
u64 features;
- int r;
+ int r, abi_version = VHOST_SCSI_ABI_VERSION;
switch (ioctl) {
case VHOST_SCSI_SET_ENDPOINT:
return vhost_scsi_clear_endpoint(vs, &backend);
case VHOST_SCSI_GET_ABI_VERSION:
- if (copy_from_user(&backend, argp, sizeof backend))
- return -EFAULT;
-
- backend.abi_version = VHOST_SCSI_ABI_VERSION;
-
- if (copy_to_user(argp, &backend, sizeof backend))
+ if (copy_to_user(argp, &abi_version, sizeof abi_version))
return -EFAULT;
return 0;
case VHOST_GET_FEATURES:
}
}
+#ifdef CONFIG_COMPAT
+static long vhost_scsi_compat_ioctl(struct file *f, unsigned int ioctl,
+ unsigned long arg)
+{
+ return vhost_scsi_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
+}
+#endif
+
static const struct file_operations vhost_scsi_fops = {
.owner = THIS_MODULE,
.release = vhost_scsi_release,
.unlocked_ioctl = vhost_scsi_ioctl,
- /* TODO compat ioctl? */
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vhost_scsi_compat_ioctl,
+#endif
.open = vhost_scsi_open,
.llseek = noop_llseek,
};
return "Unknown";
}
-static int tcm_vhost_port_link(
- struct se_portal_group *se_tpg,
+static int tcm_vhost_port_link(struct se_portal_group *se_tpg,
struct se_lun *lun)
{
struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
struct tcm_vhost_tpg, se_tpg);
- atomic_inc(&tv_tpg->tv_tpg_port_count);
- smp_mb__after_atomic_inc();
+ mutex_lock(&tv_tpg->tv_tpg_mutex);
+ tv_tpg->tv_tpg_port_count++;
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
return 0;
}
-static void tcm_vhost_port_unlink(
- struct se_portal_group *se_tpg,
+static void tcm_vhost_port_unlink(struct se_portal_group *se_tpg,
struct se_lun *se_lun)
{
struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
struct tcm_vhost_tpg, se_tpg);
- atomic_dec(&tv_tpg->tv_tpg_port_count);
- smp_mb__after_atomic_dec();
+ mutex_lock(&tv_tpg->tv_tpg_mutex);
+ tv_tpg->tv_tpg_port_count--;
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
}
static struct se_node_acl *tcm_vhost_make_nodeacl(
kfree(nacl);
}
-static int tcm_vhost_make_nexus(
- struct tcm_vhost_tpg *tv_tpg,
+static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tv_tpg,
const char *name)
{
struct se_portal_group *se_tpg;
return -ENOMEM;
}
/*
- * Now register the TCM vHost virtual I_T Nexus as active with the
+ * Now register the TCM vhost virtual I_T Nexus as active with the
* call to __transport_register_session()
*/
__transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
return 0;
}
-static int tcm_vhost_drop_nexus(
- struct tcm_vhost_tpg *tpg)
+static int tcm_vhost_drop_nexus(struct tcm_vhost_tpg *tpg)
{
struct se_session *se_sess;
struct tcm_vhost_nexus *tv_nexus;
return -ENODEV;
}
- if (atomic_read(&tpg->tv_tpg_port_count)) {
+ if (tpg->tv_tpg_port_count != 0) {
mutex_unlock(&tpg->tv_tpg_mutex);
- pr_err("Unable to remove TCM_vHost I_T Nexus with"
+ pr_err("Unable to remove TCM_vhost I_T Nexus with"
" active TPG port count: %d\n",
- atomic_read(&tpg->tv_tpg_port_count));
- return -EPERM;
+ tpg->tv_tpg_port_count);
+ return -EBUSY;
}
- if (atomic_read(&tpg->tv_tpg_vhost_count)) {
+ if (tpg->tv_tpg_vhost_count != 0) {
mutex_unlock(&tpg->tv_tpg_mutex);
- pr_err("Unable to remove TCM_vHost I_T Nexus with"
+ pr_err("Unable to remove TCM_vhost I_T Nexus with"
" active TPG vhost count: %d\n",
- atomic_read(&tpg->tv_tpg_vhost_count));
- return -EPERM;
+ tpg->tv_tpg_vhost_count);
+ return -EBUSY;
}
- pr_debug("TCM_vHost_ConfigFS: Removing I_T Nexus to emulated"
+ pr_debug("TCM_vhost_ConfigFS: Removing I_T Nexus to emulated"
" %s Initiator Port: %s\n", tcm_vhost_dump_proto_id(tpg->tport),
tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
/*
- * Release the SCSI I_T Nexus to the emulated vHost Target Port
+ * Release the SCSI I_T Nexus to the emulated vhost Target Port
*/
transport_deregister_session(tv_nexus->tvn_se_sess);
tpg->tpg_nexus = NULL;
return 0;
}
-static ssize_t tcm_vhost_tpg_show_nexus(
- struct se_portal_group *se_tpg,
+static ssize_t tcm_vhost_tpg_show_nexus(struct se_portal_group *se_tpg,
char *page)
{
struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
return ret;
}
-static ssize_t tcm_vhost_tpg_store_nexus(
- struct se_portal_group *se_tpg,
+static ssize_t tcm_vhost_tpg_store_nexus(struct se_portal_group *se_tpg,
const char *page,
size_t count)
{
NULL,
};
-static struct se_portal_group *tcm_vhost_make_tpg(
- struct se_wwn *wwn,
+static struct se_portal_group *tcm_vhost_make_tpg(struct se_wwn *wwn,
struct config_group *group,
const char *name)
{
list_del(&tpg->tv_tpg_list);
mutex_unlock(&tcm_vhost_mutex);
/*
- * Release the virtual I_T Nexus for this vHost TPG
+ * Release the virtual I_T Nexus for this vhost TPG
*/
tcm_vhost_drop_nexus(tpg);
/*
kfree(tpg);
}
-static struct se_wwn *tcm_vhost_make_tport(
- struct target_fabric_configfs *tf,
+static struct se_wwn *tcm_vhost_make_tport(struct target_fabric_configfs *tf,
struct config_group *group,
const char *name)
{
static int __init tcm_vhost_init(void)
{
int ret = -ENOMEM;
-
+ /*
+ * Use our own dedicated workqueue for submitting I/O into
+ * target core to avoid contention within system_wq.
+ */
tcm_vhost_workqueue = alloc_workqueue("tcm_vhost", 0, 0);
if (!tcm_vhost_workqueue)
goto out;