Completed commands are queued and processed from the vhost worker thread. A lock must be used for mutual exclusion. Signed-off-by: Stefan Hajnoczi <stefanha@xxxxxxxxxxxxxxxxxx> --- drivers/target/tcm_vhost/tcm_vhost_scsi.c | 34 +++++++++++++++++++++------- drivers/target/tcm_vhost/tcm_vhost_scsi.h | 1 + 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/drivers/target/tcm_vhost/tcm_vhost_scsi.c b/drivers/target/tcm_vhost/tcm_vhost_scsi.c index f0900f1..93b3a82 100644 --- a/drivers/target/tcm_vhost/tcm_vhost_scsi.c +++ b/drivers/target/tcm_vhost/tcm_vhost_scsi.c @@ -54,6 +54,26 @@ static void vhost_scsi_free_cmd(struct tcm_vhost_cmd *tv_cmd) kfree(tv_cmd); } +/* Dequeue a command from the completion list */ +static struct tcm_vhost_cmd *vhost_scsi_get_cmd_from_completion(struct vhost_scsi *vs) +{ + struct tcm_vhost_cmd *tv_cmd = NULL; + + spin_lock_bh(&vs->vs_completion_lock); + if (list_empty(&vs->vs_completion_list)) { + spin_unlock_bh(&vs->vs_completion_lock); + return NULL; + } + + list_for_each_entry(tv_cmd, &vs->vs_completion_list, + tvc_completion_list) { + list_del(&tv_cmd->tvc_completion_list); + break; + } + spin_unlock_bh(&vs->vs_completion_lock); + return tv_cmd; +} + /* Fill in status and signal that we are done processing this command * * This is scheduled in the vhost work queue so we are called with the owner @@ -64,11 +84,8 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) struct vhost_scsi *vs = container_of(work, struct vhost_scsi, vs_completion_work); struct tcm_vhost_cmd *tv_cmd; - struct tcm_vhost_cmd *tmp; - /* TODO locking? */ - list_for_each_entry_safe(tv_cmd, tmp, &vs->vs_completion_list, - tvc_completion_list) { + while ((tv_cmd = vhost_scsi_get_cmd_from_completion(vs)) != NULL) { struct virtio_scsi_footer v_footer; struct se_cmd *se_cmd = &tv_cmd->tvc_se_cmd; int ret; @@ -76,8 +93,6 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) printk("%s tv_cmd %p resid %u status %#02x\n", __func__, tv_cmd, se_cmd->residual_count, se_cmd->scsi_status); - list_del(&tv_cmd->tvc_completion_list); - memset(&v_footer, 0, sizeof(v_footer)); v_footer.resid = se_cmd->residual_count; /* TODO is status_qualifier field needed? */ @@ -104,11 +119,11 @@ void vhost_scsi_complete_cmd(struct tcm_vhost_cmd *tv_cmd) printk("%s tv_cmd %p\n", __func__, tv_cmd); - /* TODO lock tvc_completion_list? */ + spin_lock_bh(&vs->vs_completion_lock); list_add_tail(&tv_cmd->tvc_completion_list, &vs->vs_completion_list); - vhost_work_queue(&vs->dev, &vs->vs_completion_work); + spin_unlock_bh(&vs->vs_completion_lock); - /* TODO is tv_cmd freed by called after this? Need to keep hold of reference until vhost worker thread is done */ + vhost_work_queue(&vs->dev, &vs->vs_completion_work); } static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd( @@ -533,6 +548,7 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) vhost_work_init(&s->vs_completion_work, vhost_scsi_complete_cmd_work); INIT_LIST_HEAD(&s->vs_completion_list); + spin_lock_init(&s->vs_completion_lock); s->cmd_vq.handle_kick = vhost_scsi_handle_kick; r = vhost_dev_init(&s->dev, &s->cmd_vq, 1); diff --git a/drivers/target/tcm_vhost/tcm_vhost_scsi.h b/drivers/target/tcm_vhost/tcm_vhost_scsi.h index d6a8aae..441a424 100644 --- a/drivers/target/tcm_vhost/tcm_vhost_scsi.h +++ b/drivers/target/tcm_vhost/tcm_vhost_scsi.h @@ -6,6 +6,7 @@ struct vhost_scsi { struct vhost_work vs_completion_work; /* cmd completion work item */ struct list_head vs_completion_list; /* cmd completion queue */ + spinlock_t vs_completion_lock; /* protects vs_completion_list */ }; extern int __init vhost_scsi_register(void); -- 1.7.9.1 -- To unsubscribe from this list: send the line "unsubscribe target-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html