diff options
author | Asias He <asias@redhat.com> | 2013-02-06 00:20:59 -0500 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2013-02-13 14:30:14 -0500 |
commit | 1b7f390eb3bfc197c979c5478eadbc2a90f07667 (patch) | |
tree | 969efecaf717e4d94b55eaaa5a3fc2f678956459 /drivers/vhost | |
parent | 67e18cf9ab21648a477e91e0d3cb6dbdb1330262 (diff) |
tcm_vhost: Multi-queue support
This adds virtio-scsi multi-queue support to tcm_vhost. In order to use
multi-queue, guest side multi-queue support is need. It can
be found here:
https://lkml.org/lkml/2012/12/18/166
Currently, only one thread is created by vhost core code for each
vhost_scsi instance. Even if there are multi-queues, all the handling of
guest kick (vhost_scsi_handle_kick) are processed in one thread. This is
not optimal. Luckily, most of the work is offloaded to the tcm_vhost
workqueue.
Some initial perf numbers:
1 queue, 4 targets, 1 lun per target
4K request size, 50% randread + 50% randwrite: 127K/127k IOPS
4 queues, 4 targets, 1 lun per target
4K request size, 50% randread + 50% randwrite: 181K/181k IOPS
Signed-off-by: Asias He <asias@redhat.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/vhost')
-rw-r--r-- | drivers/vhost/tcm_vhost.c | 46 | ||||
-rw-r--r-- | drivers/vhost/tcm_vhost.h | 2 |
2 files changed, 31 insertions, 17 deletions
diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index 81ecda54b923..9951297b2427 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c | |||
@@ -48,6 +48,7 @@ | |||
48 | #include <linux/virtio_net.h> /* TODO vhost.h currently depends on this */ | 48 | #include <linux/virtio_net.h> /* TODO vhost.h currently depends on this */ |
49 | #include <linux/virtio_scsi.h> | 49 | #include <linux/virtio_scsi.h> |
50 | #include <linux/llist.h> | 50 | #include <linux/llist.h> |
51 | #include <linux/bitmap.h> | ||
51 | 52 | ||
52 | #include "vhost.c" | 53 | #include "vhost.c" |
53 | #include "vhost.h" | 54 | #include "vhost.h" |
@@ -59,7 +60,8 @@ enum { | |||
59 | VHOST_SCSI_VQ_IO = 2, | 60 | VHOST_SCSI_VQ_IO = 2, |
60 | }; | 61 | }; |
61 | 62 | ||
62 | #define VHOST_SCSI_MAX_TARGET 256 | 63 | #define VHOST_SCSI_MAX_TARGET 256 |
64 | #define VHOST_SCSI_MAX_VQ 128 | ||
63 | 65 | ||
64 | struct vhost_scsi { | 66 | struct vhost_scsi { |
65 | /* Protected by vhost_scsi->dev.mutex */ | 67 | /* Protected by vhost_scsi->dev.mutex */ |
@@ -68,7 +70,7 @@ struct vhost_scsi { | |||
68 | bool vs_endpoint; | 70 | bool vs_endpoint; |
69 | 71 | ||
70 | struct vhost_dev dev; | 72 | struct vhost_dev dev; |
71 | struct vhost_virtqueue vqs[3]; | 73 | struct vhost_virtqueue vqs[VHOST_SCSI_MAX_VQ]; |
72 | 74 | ||
73 | struct vhost_work vs_completion_work; /* cmd completion work item */ | 75 | struct vhost_work vs_completion_work; /* cmd completion work item */ |
74 | struct llist_head vs_completion_list; /* cmd completion queue */ | 76 | struct llist_head vs_completion_list; /* cmd completion queue */ |
@@ -366,12 +368,14 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) | |||
366 | { | 368 | { |
367 | struct vhost_scsi *vs = container_of(work, struct vhost_scsi, | 369 | struct vhost_scsi *vs = container_of(work, struct vhost_scsi, |
368 | vs_completion_work); | 370 | vs_completion_work); |
371 | DECLARE_BITMAP(signal, VHOST_SCSI_MAX_VQ); | ||
369 | struct virtio_scsi_cmd_resp v_rsp; | 372 | struct virtio_scsi_cmd_resp v_rsp; |
370 | struct tcm_vhost_cmd *tv_cmd; | 373 | struct tcm_vhost_cmd *tv_cmd; |
371 | struct llist_node *llnode; | 374 | struct llist_node *llnode; |
372 | struct se_cmd *se_cmd; | 375 | struct se_cmd *se_cmd; |
373 | int ret; | 376 | int ret, vq; |
374 | 377 | ||
378 | bitmap_zero(signal, VHOST_SCSI_MAX_VQ); | ||
375 | llnode = llist_del_all(&vs->vs_completion_list); | 379 | llnode = llist_del_all(&vs->vs_completion_list); |
376 | while (llnode) { | 380 | while (llnode) { |
377 | tv_cmd = llist_entry(llnode, struct tcm_vhost_cmd, | 381 | tv_cmd = llist_entry(llnode, struct tcm_vhost_cmd, |
@@ -390,15 +394,20 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) | |||
390 | memcpy(v_rsp.sense, tv_cmd->tvc_sense_buf, | 394 | memcpy(v_rsp.sense, tv_cmd->tvc_sense_buf, |
391 | v_rsp.sense_len); | 395 | v_rsp.sense_len); |
392 | ret = copy_to_user(tv_cmd->tvc_resp, &v_rsp, sizeof(v_rsp)); | 396 | ret = copy_to_user(tv_cmd->tvc_resp, &v_rsp, sizeof(v_rsp)); |
393 | if (likely(ret == 0)) | 397 | if (likely(ret == 0)) { |
394 | vhost_add_used(&vs->vqs[2], tv_cmd->tvc_vq_desc, 0); | 398 | vhost_add_used(tv_cmd->tvc_vq, tv_cmd->tvc_vq_desc, 0); |
395 | else | 399 | vq = tv_cmd->tvc_vq - vs->vqs; |
400 | __set_bit(vq, signal); | ||
401 | } else | ||
396 | pr_err("Faulted on virtio_scsi_cmd_resp\n"); | 402 | pr_err("Faulted on virtio_scsi_cmd_resp\n"); |
397 | 403 | ||
398 | vhost_scsi_free_cmd(tv_cmd); | 404 | vhost_scsi_free_cmd(tv_cmd); |
399 | } | 405 | } |
400 | 406 | ||
401 | vhost_signal(&vs->dev, &vs->vqs[2]); | 407 | vq = -1; |
408 | while ((vq = find_next_bit(signal, VHOST_SCSI_MAX_VQ, vq + 1)) | ||
409 | < VHOST_SCSI_MAX_VQ) | ||
410 | vhost_signal(&vs->dev, &vs->vqs[vq]); | ||
402 | } | 411 | } |
403 | 412 | ||
404 | static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd( | 413 | static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd( |
@@ -561,9 +570,9 @@ static void tcm_vhost_submission_work(struct work_struct *work) | |||
561 | } | 570 | } |
562 | } | 571 | } |
563 | 572 | ||
564 | static void vhost_scsi_handle_vq(struct vhost_scsi *vs) | 573 | static void vhost_scsi_handle_vq(struct vhost_scsi *vs, |
574 | struct vhost_virtqueue *vq) | ||
565 | { | 575 | { |
566 | struct vhost_virtqueue *vq = &vs->vqs[2]; | ||
567 | struct virtio_scsi_cmd_req v_req; | 576 | struct virtio_scsi_cmd_req v_req; |
568 | struct tcm_vhost_tpg *tv_tpg; | 577 | struct tcm_vhost_tpg *tv_tpg; |
569 | struct tcm_vhost_cmd *tv_cmd; | 578 | struct tcm_vhost_cmd *tv_cmd; |
@@ -656,7 +665,7 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs) | |||
656 | ret = __copy_to_user(resp, &rsp, sizeof(rsp)); | 665 | ret = __copy_to_user(resp, &rsp, sizeof(rsp)); |
657 | if (!ret) | 666 | if (!ret) |
658 | vhost_add_used_and_signal(&vs->dev, | 667 | vhost_add_used_and_signal(&vs->dev, |
659 | &vs->vqs[2], head, 0); | 668 | vq, head, 0); |
660 | else | 669 | else |
661 | pr_err("Faulted on virtio_scsi_cmd_resp\n"); | 670 | pr_err("Faulted on virtio_scsi_cmd_resp\n"); |
662 | 671 | ||
@@ -678,6 +687,7 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs) | |||
678 | ": %d\n", tv_cmd, exp_data_len, data_direction); | 687 | ": %d\n", tv_cmd, exp_data_len, data_direction); |
679 | 688 | ||
680 | tv_cmd->tvc_vhost = vs; | 689 | tv_cmd->tvc_vhost = vs; |
690 | tv_cmd->tvc_vq = vq; | ||
681 | 691 | ||
682 | if (unlikely(vq->iov[out].iov_len != | 692 | if (unlikely(vq->iov[out].iov_len != |
683 | sizeof(struct virtio_scsi_cmd_resp))) { | 693 | sizeof(struct virtio_scsi_cmd_resp))) { |
@@ -758,7 +768,7 @@ static void vhost_scsi_handle_kick(struct vhost_work *work) | |||
758 | poll.work); | 768 | poll.work); |
759 | struct vhost_scsi *vs = container_of(vq->dev, struct vhost_scsi, dev); | 769 | struct vhost_scsi *vs = container_of(vq->dev, struct vhost_scsi, dev); |
760 | 770 | ||
761 | vhost_scsi_handle_vq(vs); | 771 | vhost_scsi_handle_vq(vs, vq); |
762 | } | 772 | } |
763 | 773 | ||
764 | /* | 774 | /* |
@@ -879,7 +889,7 @@ err: | |||
879 | static int vhost_scsi_open(struct inode *inode, struct file *f) | 889 | static int vhost_scsi_open(struct inode *inode, struct file *f) |
880 | { | 890 | { |
881 | struct vhost_scsi *s; | 891 | struct vhost_scsi *s; |
882 | int r; | 892 | int r, i; |
883 | 893 | ||
884 | s = kzalloc(sizeof(*s), GFP_KERNEL); | 894 | s = kzalloc(sizeof(*s), GFP_KERNEL); |
885 | if (!s) | 895 | if (!s) |
@@ -889,8 +899,9 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) | |||
889 | 899 | ||
890 | s->vqs[VHOST_SCSI_VQ_CTL].handle_kick = vhost_scsi_ctl_handle_kick; | 900 | s->vqs[VHOST_SCSI_VQ_CTL].handle_kick = vhost_scsi_ctl_handle_kick; |
891 | s->vqs[VHOST_SCSI_VQ_EVT].handle_kick = vhost_scsi_evt_handle_kick; | 901 | s->vqs[VHOST_SCSI_VQ_EVT].handle_kick = vhost_scsi_evt_handle_kick; |
892 | s->vqs[VHOST_SCSI_VQ_IO].handle_kick = vhost_scsi_handle_kick; | 902 | for (i = VHOST_SCSI_VQ_IO; i < VHOST_SCSI_MAX_VQ; i++) |
893 | r = vhost_dev_init(&s->dev, s->vqs, 3); | 903 | s->vqs[i].handle_kick = vhost_scsi_handle_kick; |
904 | r = vhost_dev_init(&s->dev, s->vqs, VHOST_SCSI_MAX_VQ); | ||
894 | if (r < 0) { | 905 | if (r < 0) { |
895 | kfree(s); | 906 | kfree(s); |
896 | return r; | 907 | return r; |
@@ -922,9 +933,10 @@ static void vhost_scsi_flush_vq(struct vhost_scsi *vs, int index) | |||
922 | 933 | ||
923 | static void vhost_scsi_flush(struct vhost_scsi *vs) | 934 | static void vhost_scsi_flush(struct vhost_scsi *vs) |
924 | { | 935 | { |
925 | vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_CTL); | 936 | int i; |
926 | vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_EVT); | 937 | |
927 | vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_IO); | 938 | for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) |
939 | vhost_scsi_flush_vq(vs, i); | ||
928 | } | 940 | } |
929 | 941 | ||
930 | static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features) | 942 | static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features) |
diff --git a/drivers/vhost/tcm_vhost.h b/drivers/vhost/tcm_vhost.h index 519a5504d347..1d2ae7a60e11 100644 --- a/drivers/vhost/tcm_vhost.h +++ b/drivers/vhost/tcm_vhost.h | |||
@@ -23,6 +23,8 @@ struct tcm_vhost_cmd { | |||
23 | struct virtio_scsi_cmd_resp __user *tvc_resp; | 23 | struct virtio_scsi_cmd_resp __user *tvc_resp; |
24 | /* Pointer to vhost_scsi for our device */ | 24 | /* Pointer to vhost_scsi for our device */ |
25 | struct vhost_scsi *tvc_vhost; | 25 | struct vhost_scsi *tvc_vhost; |
26 | /* Pointer to vhost_virtqueue for the cmd */ | ||
27 | struct vhost_virtqueue *tvc_vq; | ||
26 | /* Pointer to vhost nexus memory */ | 28 | /* Pointer to vhost nexus memory */ |
27 | struct tcm_vhost_nexus *tvc_nexus; | 29 | struct tcm_vhost_nexus *tvc_nexus; |
28 | /* The TCM I/O descriptor that is accessed via container_of() */ | 30 | /* The TCM I/O descriptor that is accessed via container_of() */ |