diff options
author | Christoph Hellwig <hch@lst.de> | 2017-02-05 12:15:26 -0500 |
---|---|---|
committer | Michael S. Tsirkin <mst@redhat.com> | 2017-02-27 13:54:06 -0500 |
commit | 0d9f0a52c8b9f7a003fe1650b7d5fb8518efabe0 (patch) | |
tree | fab497b3fc1a878f5a79e7512e95c90fb36713b5 | |
parent | ad71473d9c43725c917fc5a86d54ceb7001ee28c (diff) |
virtio_scsi: use virtio IRQ affinity
Use automatic IRQ affinity assignment in the virtio layer if available,
and build the blk-mq queues based on it.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
-rw-r--r-- | drivers/scsi/virtio_scsi.c | 126 | ||||
-rw-r--r-- | include/linux/cpuhotplug.h | 1 |
2 files changed, 12 insertions, 115 deletions
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index c9c5ea0611e9..939c47df73fa 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/mempool.h> | 20 | #include <linux/mempool.h> |
21 | #include <linux/interrupt.h> | ||
21 | #include <linux/virtio.h> | 22 | #include <linux/virtio.h> |
22 | #include <linux/virtio_ids.h> | 23 | #include <linux/virtio_ids.h> |
23 | #include <linux/virtio_config.h> | 24 | #include <linux/virtio_config.h> |
@@ -29,6 +30,7 @@ | |||
29 | #include <scsi/scsi_cmnd.h> | 30 | #include <scsi/scsi_cmnd.h> |
30 | #include <scsi/scsi_tcq.h> | 31 | #include <scsi/scsi_tcq.h> |
31 | #include <linux/seqlock.h> | 32 | #include <linux/seqlock.h> |
33 | #include <linux/blk-mq-virtio.h> | ||
32 | 34 | ||
33 | #define VIRTIO_SCSI_MEMPOOL_SZ 64 | 35 | #define VIRTIO_SCSI_MEMPOOL_SZ 64 |
34 | #define VIRTIO_SCSI_EVENT_LEN 8 | 36 | #define VIRTIO_SCSI_EVENT_LEN 8 |
@@ -108,7 +110,6 @@ struct virtio_scsi { | |||
108 | bool affinity_hint_set; | 110 | bool affinity_hint_set; |
109 | 111 | ||
110 | struct hlist_node node; | 112 | struct hlist_node node; |
111 | struct hlist_node node_dead; | ||
112 | 113 | ||
113 | /* Protected by event_vq lock */ | 114 | /* Protected by event_vq lock */ |
114 | bool stop_events; | 115 | bool stop_events; |
@@ -118,7 +119,6 @@ struct virtio_scsi { | |||
118 | struct virtio_scsi_vq req_vqs[]; | 119 | struct virtio_scsi_vq req_vqs[]; |
119 | }; | 120 | }; |
120 | 121 | ||
121 | static enum cpuhp_state virtioscsi_online; | ||
122 | static struct kmem_cache *virtscsi_cmd_cache; | 122 | static struct kmem_cache *virtscsi_cmd_cache; |
123 | static mempool_t *virtscsi_cmd_pool; | 123 | static mempool_t *virtscsi_cmd_pool; |
124 | 124 | ||
@@ -766,6 +766,13 @@ static void virtscsi_target_destroy(struct scsi_target *starget) | |||
766 | kfree(tgt); | 766 | kfree(tgt); |
767 | } | 767 | } |
768 | 768 | ||
769 | static int virtscsi_map_queues(struct Scsi_Host *shost) | ||
770 | { | ||
771 | struct virtio_scsi *vscsi = shost_priv(shost); | ||
772 | |||
773 | return blk_mq_virtio_map_queues(&shost->tag_set, vscsi->vdev, 2); | ||
774 | } | ||
775 | |||
769 | static struct scsi_host_template virtscsi_host_template_single = { | 776 | static struct scsi_host_template virtscsi_host_template_single = { |
770 | .module = THIS_MODULE, | 777 | .module = THIS_MODULE, |
771 | .name = "Virtio SCSI HBA", | 778 | .name = "Virtio SCSI HBA", |
@@ -801,6 +808,7 @@ static struct scsi_host_template virtscsi_host_template_multi = { | |||
801 | .use_clustering = ENABLE_CLUSTERING, | 808 | .use_clustering = ENABLE_CLUSTERING, |
802 | .target_alloc = virtscsi_target_alloc, | 809 | .target_alloc = virtscsi_target_alloc, |
803 | .target_destroy = virtscsi_target_destroy, | 810 | .target_destroy = virtscsi_target_destroy, |
811 | .map_queues = virtscsi_map_queues, | ||
804 | .track_queue_depth = 1, | 812 | .track_queue_depth = 1, |
805 | }; | 813 | }; |
806 | 814 | ||
@@ -817,80 +825,6 @@ static struct scsi_host_template virtscsi_host_template_multi = { | |||
817 | virtio_cwrite(vdev, struct virtio_scsi_config, fld, &__val); \ | 825 | virtio_cwrite(vdev, struct virtio_scsi_config, fld, &__val); \ |
818 | } while(0) | 826 | } while(0) |
819 | 827 | ||
820 | static void __virtscsi_set_affinity(struct virtio_scsi *vscsi, bool affinity) | ||
821 | { | ||
822 | int i; | ||
823 | int cpu; | ||
824 | |||
825 | /* In multiqueue mode, when the number of cpu is equal | ||
826 | * to the number of request queues, we let the qeueues | ||
827 | * to be private to one cpu by setting the affinity hint | ||
828 | * to eliminate the contention. | ||
829 | */ | ||
830 | if ((vscsi->num_queues == 1 || | ||
831 | vscsi->num_queues != num_online_cpus()) && affinity) { | ||
832 | if (vscsi->affinity_hint_set) | ||
833 | affinity = false; | ||
834 | else | ||
835 | return; | ||
836 | } | ||
837 | |||
838 | if (affinity) { | ||
839 | i = 0; | ||
840 | for_each_online_cpu(cpu) { | ||
841 | virtqueue_set_affinity(vscsi->req_vqs[i].vq, cpu); | ||
842 | i++; | ||
843 | } | ||
844 | |||
845 | vscsi->affinity_hint_set = true; | ||
846 | } else { | ||
847 | for (i = 0; i < vscsi->num_queues; i++) { | ||
848 | if (!vscsi->req_vqs[i].vq) | ||
849 | continue; | ||
850 | |||
851 | virtqueue_set_affinity(vscsi->req_vqs[i].vq, -1); | ||
852 | } | ||
853 | |||
854 | vscsi->affinity_hint_set = false; | ||
855 | } | ||
856 | } | ||
857 | |||
858 | static void virtscsi_set_affinity(struct virtio_scsi *vscsi, bool affinity) | ||
859 | { | ||
860 | get_online_cpus(); | ||
861 | __virtscsi_set_affinity(vscsi, affinity); | ||
862 | put_online_cpus(); | ||
863 | } | ||
864 | |||
865 | static int virtscsi_cpu_online(unsigned int cpu, struct hlist_node *node) | ||
866 | { | ||
867 | struct virtio_scsi *vscsi = hlist_entry_safe(node, struct virtio_scsi, | ||
868 | node); | ||
869 | __virtscsi_set_affinity(vscsi, true); | ||
870 | return 0; | ||
871 | } | ||
872 | |||
873 | static int virtscsi_cpu_notif_add(struct virtio_scsi *vi) | ||
874 | { | ||
875 | int ret; | ||
876 | |||
877 | ret = cpuhp_state_add_instance(virtioscsi_online, &vi->node); | ||
878 | if (ret) | ||
879 | return ret; | ||
880 | |||
881 | ret = cpuhp_state_add_instance(CPUHP_VIRT_SCSI_DEAD, &vi->node_dead); | ||
882 | if (ret) | ||
883 | cpuhp_state_remove_instance(virtioscsi_online, &vi->node); | ||
884 | return ret; | ||
885 | } | ||
886 | |||
887 | static void virtscsi_cpu_notif_remove(struct virtio_scsi *vi) | ||
888 | { | ||
889 | cpuhp_state_remove_instance_nocalls(virtioscsi_online, &vi->node); | ||
890 | cpuhp_state_remove_instance_nocalls(CPUHP_VIRT_SCSI_DEAD, | ||
891 | &vi->node_dead); | ||
892 | } | ||
893 | |||
894 | static void virtscsi_init_vq(struct virtio_scsi_vq *virtscsi_vq, | 828 | static void virtscsi_init_vq(struct virtio_scsi_vq *virtscsi_vq, |
895 | struct virtqueue *vq) | 829 | struct virtqueue *vq) |
896 | { | 830 | { |
@@ -900,14 +834,8 @@ static void virtscsi_init_vq(struct virtio_scsi_vq *virtscsi_vq, | |||
900 | 834 | ||
901 | static void virtscsi_remove_vqs(struct virtio_device *vdev) | 835 | static void virtscsi_remove_vqs(struct virtio_device *vdev) |
902 | { | 836 | { |
903 | struct Scsi_Host *sh = virtio_scsi_host(vdev); | ||
904 | struct virtio_scsi *vscsi = shost_priv(sh); | ||
905 | |||
906 | virtscsi_set_affinity(vscsi, false); | ||
907 | |||
908 | /* Stop all the virtqueues. */ | 837 | /* Stop all the virtqueues. */ |
909 | vdev->config->reset(vdev); | 838 | vdev->config->reset(vdev); |
910 | |||
911 | vdev->config->del_vqs(vdev); | 839 | vdev->config->del_vqs(vdev); |
912 | } | 840 | } |
913 | 841 | ||
@@ -920,6 +848,7 @@ static int virtscsi_init(struct virtio_device *vdev, | |||
920 | vq_callback_t **callbacks; | 848 | vq_callback_t **callbacks; |
921 | const char **names; | 849 | const char **names; |
922 | struct virtqueue **vqs; | 850 | struct virtqueue **vqs; |
851 | struct irq_affinity desc = { .pre_vectors = 2 }; | ||
923 | 852 | ||
924 | num_vqs = vscsi->num_queues + VIRTIO_SCSI_VQ_BASE; | 853 | num_vqs = vscsi->num_queues + VIRTIO_SCSI_VQ_BASE; |
925 | vqs = kmalloc(num_vqs * sizeof(struct virtqueue *), GFP_KERNEL); | 854 | vqs = kmalloc(num_vqs * sizeof(struct virtqueue *), GFP_KERNEL); |
@@ -942,7 +871,7 @@ static int virtscsi_init(struct virtio_device *vdev, | |||
942 | 871 | ||
943 | /* Discover virtqueues and write information to configuration. */ | 872 | /* Discover virtqueues and write information to configuration. */ |
944 | err = vdev->config->find_vqs(vdev, num_vqs, vqs, callbacks, names, | 873 | err = vdev->config->find_vqs(vdev, num_vqs, vqs, callbacks, names, |
945 | NULL); | 874 | &desc); |
946 | if (err) | 875 | if (err) |
947 | goto out; | 876 | goto out; |
948 | 877 | ||
@@ -1008,10 +937,6 @@ static int virtscsi_probe(struct virtio_device *vdev) | |||
1008 | if (err) | 937 | if (err) |
1009 | goto virtscsi_init_failed; | 938 | goto virtscsi_init_failed; |
1010 | 939 | ||
1011 | err = virtscsi_cpu_notif_add(vscsi); | ||
1012 | if (err) | ||
1013 | goto scsi_add_host_failed; | ||
1014 | |||
1015 | cmd_per_lun = virtscsi_config_get(vdev, cmd_per_lun) ?: 1; | 940 | cmd_per_lun = virtscsi_config_get(vdev, cmd_per_lun) ?: 1; |
1016 | shost->cmd_per_lun = min_t(u32, cmd_per_lun, shost->can_queue); | 941 | shost->cmd_per_lun = min_t(u32, cmd_per_lun, shost->can_queue); |
1017 | shost->max_sectors = virtscsi_config_get(vdev, max_sectors) ?: 0xFFFF; | 942 | shost->max_sectors = virtscsi_config_get(vdev, max_sectors) ?: 0xFFFF; |
@@ -1066,9 +991,6 @@ static void virtscsi_remove(struct virtio_device *vdev) | |||
1066 | virtscsi_cancel_event_work(vscsi); | 991 | virtscsi_cancel_event_work(vscsi); |
1067 | 992 | ||
1068 | scsi_remove_host(shost); | 993 | scsi_remove_host(shost); |
1069 | |||
1070 | virtscsi_cpu_notif_remove(vscsi); | ||
1071 | |||
1072 | virtscsi_remove_vqs(vdev); | 994 | virtscsi_remove_vqs(vdev); |
1073 | scsi_host_put(shost); | 995 | scsi_host_put(shost); |
1074 | } | 996 | } |
@@ -1076,10 +998,6 @@ static void virtscsi_remove(struct virtio_device *vdev) | |||
1076 | #ifdef CONFIG_PM_SLEEP | 998 | #ifdef CONFIG_PM_SLEEP |
1077 | static int virtscsi_freeze(struct virtio_device *vdev) | 999 | static int virtscsi_freeze(struct virtio_device *vdev) |
1078 | { | 1000 | { |
1079 | struct Scsi_Host *sh = virtio_scsi_host(vdev); | ||
1080 | struct virtio_scsi *vscsi = shost_priv(sh); | ||
1081 | |||
1082 | virtscsi_cpu_notif_remove(vscsi); | ||
1083 | virtscsi_remove_vqs(vdev); | 1001 | virtscsi_remove_vqs(vdev); |
1084 | return 0; | 1002 | return 0; |
1085 | } | 1003 | } |
@@ -1094,11 +1012,6 @@ static int virtscsi_restore(struct virtio_device *vdev) | |||
1094 | if (err) | 1012 | if (err) |
1095 | return err; | 1013 | return err; |
1096 | 1014 | ||
1097 | err = virtscsi_cpu_notif_add(vscsi); | ||
1098 | if (err) { | ||
1099 | vdev->config->del_vqs(vdev); | ||
1100 | return err; | ||
1101 | } | ||
1102 | virtio_device_ready(vdev); | 1015 | virtio_device_ready(vdev); |
1103 | 1016 | ||
1104 | if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) | 1017 | if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) |
@@ -1153,16 +1066,6 @@ static int __init init(void) | |||
1153 | pr_err("mempool_create() for virtscsi_cmd_pool failed\n"); | 1066 | pr_err("mempool_create() for virtscsi_cmd_pool failed\n"); |
1154 | goto error; | 1067 | goto error; |
1155 | } | 1068 | } |
1156 | ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, | ||
1157 | "scsi/virtio:online", | ||
1158 | virtscsi_cpu_online, NULL); | ||
1159 | if (ret < 0) | ||
1160 | goto error; | ||
1161 | virtioscsi_online = ret; | ||
1162 | ret = cpuhp_setup_state_multi(CPUHP_VIRT_SCSI_DEAD, "scsi/virtio:dead", | ||
1163 | NULL, virtscsi_cpu_online); | ||
1164 | if (ret) | ||
1165 | goto error; | ||
1166 | ret = register_virtio_driver(&virtio_scsi_driver); | 1069 | ret = register_virtio_driver(&virtio_scsi_driver); |
1167 | if (ret < 0) | 1070 | if (ret < 0) |
1168 | goto error; | 1071 | goto error; |
@@ -1178,17 +1081,12 @@ error: | |||
1178 | kmem_cache_destroy(virtscsi_cmd_cache); | 1081 | kmem_cache_destroy(virtscsi_cmd_cache); |
1179 | virtscsi_cmd_cache = NULL; | 1082 | virtscsi_cmd_cache = NULL; |
1180 | } | 1083 | } |
1181 | if (virtioscsi_online) | ||
1182 | cpuhp_remove_multi_state(virtioscsi_online); | ||
1183 | cpuhp_remove_multi_state(CPUHP_VIRT_SCSI_DEAD); | ||
1184 | return ret; | 1084 | return ret; |
1185 | } | 1085 | } |
1186 | 1086 | ||
1187 | static void __exit fini(void) | 1087 | static void __exit fini(void) |
1188 | { | 1088 | { |
1189 | unregister_virtio_driver(&virtio_scsi_driver); | 1089 | unregister_virtio_driver(&virtio_scsi_driver); |
1190 | cpuhp_remove_multi_state(virtioscsi_online); | ||
1191 | cpuhp_remove_multi_state(CPUHP_VIRT_SCSI_DEAD); | ||
1192 | mempool_destroy(virtscsi_cmd_pool); | 1090 | mempool_destroy(virtscsi_cmd_pool); |
1193 | kmem_cache_destroy(virtscsi_cmd_cache); | 1091 | kmem_cache_destroy(virtscsi_cmd_cache); |
1194 | } | 1092 | } |
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 921acaaa1601..01aea80a503e 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h | |||
@@ -26,7 +26,6 @@ enum cpuhp_state { | |||
26 | CPUHP_ARM_OMAP_WAKE_DEAD, | 26 | CPUHP_ARM_OMAP_WAKE_DEAD, |
27 | CPUHP_IRQ_POLL_DEAD, | 27 | CPUHP_IRQ_POLL_DEAD, |
28 | CPUHP_BLOCK_SOFTIRQ_DEAD, | 28 | CPUHP_BLOCK_SOFTIRQ_DEAD, |
29 | CPUHP_VIRT_SCSI_DEAD, | ||
30 | CPUHP_ACPI_CPUDRV_DEAD, | 29 | CPUHP_ACPI_CPUDRV_DEAD, |
31 | CPUHP_S390_PFAULT_DEAD, | 30 | CPUHP_S390_PFAULT_DEAD, |
32 | CPUHP_BLK_MQ_DEAD, | 31 | CPUHP_BLK_MQ_DEAD, |