diff options
author | Asias He <asias@redhat.com> | 2013-02-04 23:31:57 -0500 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2013-02-13 14:29:53 -0500 |
commit | 67e18cf9ab21648a477e91e0d3cb6dbdb1330262 (patch) | |
tree | 2f39e32dfe7c2794082d61aea95dcdee38e5e4ae /drivers/vhost | |
parent | adfa9570a56c3dbfc2a28baab77ff6f0b8f480d3 (diff) |
tcm_vhost: Multi-target support
In order to take advantages of Paolo's multi-queue virito-scsi, we need
multi-target support in tcm_vhost first. Otherwise all the requests go
to one queue and other queues are idle.
This patch makes:
1. All the targets under the wwpn is seen and can be used by guest.
2. No need to pass the tpgt number in struct vhost_scsi_target to
tcm_vhost.ko. Only wwpn is needed.
3. We can always pass max_target = 255 to guest now, since we abort the
request who's target id does not exist.
Changes in v2:
- Handle non-contiguous tpgt
Changes in v3:
- Simplfy lock in vhost_scsi_set_endpoint
- Return -EEXIST when does not match
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 | 131 | ||||
-rw-r--r-- | drivers/vhost/tcm_vhost.h | 4 |
2 files changed, 85 insertions, 50 deletions
diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index 704e4f674776..81ecda54b923 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c | |||
@@ -59,8 +59,14 @@ enum { | |||
59 | VHOST_SCSI_VQ_IO = 2, | 59 | VHOST_SCSI_VQ_IO = 2, |
60 | }; | 60 | }; |
61 | 61 | ||
62 | #define VHOST_SCSI_MAX_TARGET 256 | ||
63 | |||
62 | struct vhost_scsi { | 64 | struct vhost_scsi { |
63 | struct tcm_vhost_tpg *vs_tpg; /* Protected by vhost_scsi->dev.mutex */ | 65 | /* Protected by vhost_scsi->dev.mutex */ |
66 | struct tcm_vhost_tpg *vs_tpg[VHOST_SCSI_MAX_TARGET]; | ||
67 | char vs_vhost_wwpn[TRANSPORT_IQN_LEN]; | ||
68 | bool vs_endpoint; | ||
69 | |||
64 | struct vhost_dev dev; | 70 | struct vhost_dev dev; |
65 | struct vhost_virtqueue vqs[3]; | 71 | struct vhost_virtqueue vqs[3]; |
66 | 72 | ||
@@ -564,10 +570,10 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs) | |||
564 | u32 exp_data_len, data_first, data_num, data_direction; | 570 | u32 exp_data_len, data_first, data_num, data_direction; |
565 | unsigned out, in, i; | 571 | unsigned out, in, i; |
566 | int head, ret; | 572 | int head, ret; |
573 | u8 target; | ||
567 | 574 | ||
568 | /* Must use ioctl VHOST_SCSI_SET_ENDPOINT */ | 575 | /* Must use ioctl VHOST_SCSI_SET_ENDPOINT */ |
569 | tv_tpg = vs->vs_tpg; | 576 | if (unlikely(!vs->vs_endpoint)) |
570 | if (unlikely(!tv_tpg)) | ||
571 | return; | 577 | return; |
572 | 578 | ||
573 | mutex_lock(&vq->mutex); | 579 | mutex_lock(&vq->mutex); |
@@ -635,6 +641,28 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs) | |||
635 | break; | 641 | break; |
636 | } | 642 | } |
637 | 643 | ||
644 | /* Extract the tpgt */ | ||
645 | target = v_req.lun[1]; | ||
646 | tv_tpg = vs->vs_tpg[target]; | ||
647 | |||
648 | /* Target does not exist, fail the request */ | ||
649 | if (unlikely(!tv_tpg)) { | ||
650 | struct virtio_scsi_cmd_resp __user *resp; | ||
651 | struct virtio_scsi_cmd_resp rsp; | ||
652 | |||
653 | memset(&rsp, 0, sizeof(rsp)); | ||
654 | rsp.response = VIRTIO_SCSI_S_BAD_TARGET; | ||
655 | resp = vq->iov[out].iov_base; | ||
656 | ret = __copy_to_user(resp, &rsp, sizeof(rsp)); | ||
657 | if (!ret) | ||
658 | vhost_add_used_and_signal(&vs->dev, | ||
659 | &vs->vqs[2], head, 0); | ||
660 | else | ||
661 | pr_err("Faulted on virtio_scsi_cmd_resp\n"); | ||
662 | |||
663 | continue; | ||
664 | } | ||
665 | |||
638 | exp_data_len = 0; | 666 | exp_data_len = 0; |
639 | for (i = 0; i < data_num; i++) | 667 | for (i = 0; i < data_num; i++) |
640 | exp_data_len += vq->iov[data_first + i].iov_len; | 668 | exp_data_len += vq->iov[data_first + i].iov_len; |
@@ -743,7 +771,8 @@ static int vhost_scsi_set_endpoint( | |||
743 | { | 771 | { |
744 | struct tcm_vhost_tport *tv_tport; | 772 | struct tcm_vhost_tport *tv_tport; |
745 | struct tcm_vhost_tpg *tv_tpg; | 773 | struct tcm_vhost_tpg *tv_tpg; |
746 | int index; | 774 | bool match = false; |
775 | int index, ret; | ||
747 | 776 | ||
748 | mutex_lock(&vs->dev.mutex); | 777 | mutex_lock(&vs->dev.mutex); |
749 | /* Verify that ring has been setup correctly. */ | 778 | /* Verify that ring has been setup correctly. */ |
@@ -754,7 +783,6 @@ static int vhost_scsi_set_endpoint( | |||
754 | return -EFAULT; | 783 | return -EFAULT; |
755 | } | 784 | } |
756 | } | 785 | } |
757 | mutex_unlock(&vs->dev.mutex); | ||
758 | 786 | ||
759 | mutex_lock(&tcm_vhost_mutex); | 787 | mutex_lock(&tcm_vhost_mutex); |
760 | list_for_each_entry(tv_tpg, &tcm_vhost_list, tv_tpg_list) { | 788 | list_for_each_entry(tv_tpg, &tcm_vhost_list, tv_tpg_list) { |
@@ -769,30 +797,33 @@ static int vhost_scsi_set_endpoint( | |||
769 | } | 797 | } |
770 | tv_tport = tv_tpg->tport; | 798 | tv_tport = tv_tpg->tport; |
771 | 799 | ||
772 | if (!strcmp(tv_tport->tport_name, t->vhost_wwpn) && | 800 | if (!strcmp(tv_tport->tport_name, t->vhost_wwpn)) { |
773 | (tv_tpg->tport_tpgt == t->vhost_tpgt)) { | 801 | if (vs->vs_tpg[tv_tpg->tport_tpgt]) { |
774 | tv_tpg->tv_tpg_vhost_count++; | ||
775 | mutex_unlock(&tv_tpg->tv_tpg_mutex); | ||
776 | mutex_unlock(&tcm_vhost_mutex); | ||
777 | |||
778 | mutex_lock(&vs->dev.mutex); | ||
779 | if (vs->vs_tpg) { | ||
780 | mutex_unlock(&vs->dev.mutex); | ||
781 | mutex_lock(&tv_tpg->tv_tpg_mutex); | ||
782 | tv_tpg->tv_tpg_vhost_count--; | ||
783 | mutex_unlock(&tv_tpg->tv_tpg_mutex); | 802 | mutex_unlock(&tv_tpg->tv_tpg_mutex); |
803 | mutex_unlock(&tcm_vhost_mutex); | ||
804 | mutex_unlock(&vs->dev.mutex); | ||
784 | return -EEXIST; | 805 | return -EEXIST; |
785 | } | 806 | } |
786 | 807 | tv_tpg->tv_tpg_vhost_count++; | |
787 | vs->vs_tpg = tv_tpg; | 808 | vs->vs_tpg[tv_tpg->tport_tpgt] = tv_tpg; |
788 | smp_mb__after_atomic_inc(); | 809 | smp_mb__after_atomic_inc(); |
789 | mutex_unlock(&vs->dev.mutex); | 810 | match = true; |
790 | return 0; | ||
791 | } | 811 | } |
792 | mutex_unlock(&tv_tpg->tv_tpg_mutex); | 812 | mutex_unlock(&tv_tpg->tv_tpg_mutex); |
793 | } | 813 | } |
794 | mutex_unlock(&tcm_vhost_mutex); | 814 | mutex_unlock(&tcm_vhost_mutex); |
795 | return -EINVAL; | 815 | |
816 | if (match) { | ||
817 | memcpy(vs->vs_vhost_wwpn, t->vhost_wwpn, | ||
818 | sizeof(vs->vs_vhost_wwpn)); | ||
819 | vs->vs_endpoint = true; | ||
820 | ret = 0; | ||
821 | } else { | ||
822 | ret = -EEXIST; | ||
823 | } | ||
824 | |||
825 | mutex_unlock(&vs->dev.mutex); | ||
826 | return ret; | ||
796 | } | 827 | } |
797 | 828 | ||
798 | static int vhost_scsi_clear_endpoint( | 829 | static int vhost_scsi_clear_endpoint( |
@@ -801,7 +832,8 @@ static int vhost_scsi_clear_endpoint( | |||
801 | { | 832 | { |
802 | struct tcm_vhost_tport *tv_tport; | 833 | struct tcm_vhost_tport *tv_tport; |
803 | struct tcm_vhost_tpg *tv_tpg; | 834 | struct tcm_vhost_tpg *tv_tpg; |
804 | int index, ret; | 835 | int index, ret, i; |
836 | u8 target; | ||
805 | 837 | ||
806 | mutex_lock(&vs->dev.mutex); | 838 | mutex_lock(&vs->dev.mutex); |
807 | /* Verify that ring has been setup correctly. */ | 839 | /* Verify that ring has been setup correctly. */ |
@@ -811,27 +843,32 @@ static int vhost_scsi_clear_endpoint( | |||
811 | goto err; | 843 | goto err; |
812 | } | 844 | } |
813 | } | 845 | } |
846 | for (i = 0; i < VHOST_SCSI_MAX_TARGET; i++) { | ||
847 | target = i; | ||
814 | 848 | ||
815 | if (!vs->vs_tpg) { | 849 | tv_tpg = vs->vs_tpg[target]; |
816 | ret = -ENODEV; | 850 | if (!tv_tpg) |
817 | goto err; | 851 | continue; |
818 | } | 852 | |
819 | tv_tpg = vs->vs_tpg; | 853 | tv_tport = tv_tpg->tport; |
820 | tv_tport = tv_tpg->tport; | 854 | if (!tv_tport) { |
821 | 855 | ret = -ENODEV; | |
822 | if (strcmp(tv_tport->tport_name, t->vhost_wwpn) || | 856 | goto err; |
823 | (tv_tpg->tport_tpgt != t->vhost_tpgt)) { | 857 | } |
824 | pr_warn("tv_tport->tport_name: %s, tv_tpg->tport_tpgt: %hu" | 858 | |
825 | " does not match t->vhost_wwpn: %s, t->vhost_tpgt: %hu\n", | 859 | if (strcmp(tv_tport->tport_name, t->vhost_wwpn)) { |
826 | tv_tport->tport_name, tv_tpg->tport_tpgt, | 860 | pr_warn("tv_tport->tport_name: %s, tv_tpg->tport_tpgt: %hu" |
827 | t->vhost_wwpn, t->vhost_tpgt); | 861 | " does not match t->vhost_wwpn: %s, t->vhost_tpgt: %hu\n", |
828 | ret = -EINVAL; | 862 | tv_tport->tport_name, tv_tpg->tport_tpgt, |
829 | goto err; | 863 | t->vhost_wwpn, t->vhost_tpgt); |
864 | ret = -EINVAL; | ||
865 | goto err; | ||
866 | } | ||
867 | tv_tpg->tv_tpg_vhost_count--; | ||
868 | vs->vs_tpg[target] = NULL; | ||
869 | vs->vs_endpoint = false; | ||
830 | } | 870 | } |
831 | tv_tpg->tv_tpg_vhost_count--; | ||
832 | vs->vs_tpg = NULL; | ||
833 | mutex_unlock(&vs->dev.mutex); | 871 | mutex_unlock(&vs->dev.mutex); |
834 | |||
835 | return 0; | 872 | return 0; |
836 | 873 | ||
837 | err: | 874 | err: |
@@ -866,16 +903,12 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) | |||
866 | static int vhost_scsi_release(struct inode *inode, struct file *f) | 903 | static int vhost_scsi_release(struct inode *inode, struct file *f) |
867 | { | 904 | { |
868 | struct vhost_scsi *s = f->private_data; | 905 | struct vhost_scsi *s = f->private_data; |
906 | struct vhost_scsi_target t; | ||
869 | 907 | ||
870 | if (s->vs_tpg && s->vs_tpg->tport) { | 908 | mutex_lock(&s->dev.mutex); |
871 | struct vhost_scsi_target backend; | 909 | memcpy(t.vhost_wwpn, s->vs_vhost_wwpn, sizeof(t.vhost_wwpn)); |
872 | 910 | mutex_unlock(&s->dev.mutex); | |
873 | memcpy(backend.vhost_wwpn, s->vs_tpg->tport->tport_name, | 911 | vhost_scsi_clear_endpoint(s, &t); |
874 | sizeof(backend.vhost_wwpn)); | ||
875 | backend.vhost_tpgt = s->vs_tpg->tport_tpgt; | ||
876 | vhost_scsi_clear_endpoint(s, &backend); | ||
877 | } | ||
878 | |||
879 | vhost_dev_stop(&s->dev); | 912 | vhost_dev_stop(&s->dev); |
880 | vhost_dev_cleanup(&s->dev, false); | 913 | vhost_dev_cleanup(&s->dev, false); |
881 | kfree(s); | 914 | kfree(s); |
diff --git a/drivers/vhost/tcm_vhost.h b/drivers/vhost/tcm_vhost.h index 47ee80b3adee..519a5504d347 100644 --- a/drivers/vhost/tcm_vhost.h +++ b/drivers/vhost/tcm_vhost.h | |||
@@ -93,9 +93,11 @@ struct tcm_vhost_tport { | |||
93 | * | 93 | * |
94 | * ABI Rev 0: July 2012 version starting point for v3.6-rc merge candidate + | 94 | * ABI Rev 0: July 2012 version starting point for v3.6-rc merge candidate + |
95 | * RFC-v2 vhost-scsi userspace. Add GET_ABI_VERSION ioctl usage | 95 | * RFC-v2 vhost-scsi userspace. Add GET_ABI_VERSION ioctl usage |
96 | * ABI Rev 1: January 2013. Ignore vhost_tpgt filed in struct vhost_scsi_target. | ||
97 | * All the targets under vhost_wwpn can be seen and used by guset. | ||
96 | */ | 98 | */ |
97 | 99 | ||
98 | #define VHOST_SCSI_ABI_VERSION 0 | 100 | #define VHOST_SCSI_ABI_VERSION 1 |
99 | 101 | ||
100 | struct vhost_scsi_target { | 102 | struct vhost_scsi_target { |
101 | int abi_version; | 103 | int abi_version; |