diff options
author | K. Y. Srinivasan <kys@microsoft.com> | 2013-06-04 15:05:07 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2013-07-08 02:11:19 -0400 |
commit | 6f94d5de090947eb41efe9d3841d75ad91fd6dc7 (patch) | |
tree | bcc6f710e2cfd0c8a1c9e4986ca7c21cf894afe5 /drivers/scsi/storvsc_drv.c | |
parent | c8a2ba3f50f7445b3d1b822ba1a8168b4234baca (diff) |
[SCSI] storvsc: Implement multi-channel support
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/storvsc_drv.c')
-rw-r--r-- | drivers/scsi/storvsc_drv.c | 131 |
1 files changed, 127 insertions, 4 deletions
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 945198910460..1d4d42506ef8 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c | |||
@@ -328,6 +328,8 @@ static int storvsc_timeout = 180; | |||
328 | 328 | ||
329 | #define STORVSC_MAX_IO_REQUESTS 128 | 329 | #define STORVSC_MAX_IO_REQUESTS 128 |
330 | 330 | ||
331 | static void storvsc_on_channel_callback(void *context); | ||
332 | |||
331 | /* | 333 | /* |
332 | * In Hyper-V, each port/path/target maps to 1 scsi host adapter. In | 334 | * In Hyper-V, each port/path/target maps to 1 scsi host adapter. In |
333 | * reality, the path/target is not used (ie always set to 0) so our | 335 | * reality, the path/target is not used (ie always set to 0) so our |
@@ -364,6 +366,7 @@ struct storvsc_device { | |||
364 | 366 | ||
365 | bool destroy; | 367 | bool destroy; |
366 | bool drain_notify; | 368 | bool drain_notify; |
369 | bool open_sub_channel; | ||
367 | atomic_t num_outstanding_req; | 370 | atomic_t num_outstanding_req; |
368 | struct Scsi_Host *host; | 371 | struct Scsi_Host *host; |
369 | 372 | ||
@@ -755,12 +758,104 @@ static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, | |||
755 | return total_copied; | 758 | return total_copied; |
756 | } | 759 | } |
757 | 760 | ||
761 | static void handle_sc_creation(struct vmbus_channel *new_sc) | ||
762 | { | ||
763 | struct hv_device *device = new_sc->primary_channel->device_obj; | ||
764 | struct storvsc_device *stor_device; | ||
765 | struct vmstorage_channel_properties props; | ||
766 | |||
767 | stor_device = get_out_stor_device(device); | ||
768 | if (!stor_device) | ||
769 | return; | ||
770 | |||
771 | if (stor_device->open_sub_channel == false) | ||
772 | return; | ||
773 | |||
774 | memset(&props, 0, sizeof(struct vmstorage_channel_properties)); | ||
775 | |||
776 | vmbus_open(new_sc, | ||
777 | storvsc_ringbuffer_size, | ||
778 | storvsc_ringbuffer_size, | ||
779 | (void *)&props, | ||
780 | sizeof(struct vmstorage_channel_properties), | ||
781 | storvsc_on_channel_callback, new_sc); | ||
782 | } | ||
783 | |||
784 | static void handle_multichannel_storage(struct hv_device *device, int max_chns) | ||
785 | { | ||
786 | struct storvsc_device *stor_device; | ||
787 | int num_cpus = num_online_cpus(); | ||
788 | int num_sc; | ||
789 | struct storvsc_cmd_request *request; | ||
790 | struct vstor_packet *vstor_packet; | ||
791 | int ret, t; | ||
792 | |||
793 | num_sc = ((max_chns > num_cpus) ? num_cpus : max_chns); | ||
794 | stor_device = get_out_stor_device(device); | ||
795 | if (!stor_device) | ||
796 | return; | ||
797 | |||
798 | request = &stor_device->init_request; | ||
799 | vstor_packet = &request->vstor_packet; | ||
800 | |||
801 | stor_device->open_sub_channel = true; | ||
802 | /* | ||
803 | * Establish a handler for dealing with subchannels. | ||
804 | */ | ||
805 | vmbus_set_sc_create_callback(device->channel, handle_sc_creation); | ||
806 | |||
807 | /* | ||
808 | * Check to see if sub-channels have already been created. This | ||
809 | * can happen when this driver is re-loaded after unloading. | ||
810 | */ | ||
811 | |||
812 | if (vmbus_are_subchannels_present(device->channel)) | ||
813 | return; | ||
814 | |||
815 | stor_device->open_sub_channel = false; | ||
816 | /* | ||
817 | * Request the host to create sub-channels. | ||
818 | */ | ||
819 | memset(request, 0, sizeof(struct storvsc_cmd_request)); | ||
820 | init_completion(&request->wait_event); | ||
821 | vstor_packet->operation = VSTOR_OPERATION_CREATE_SUB_CHANNELS; | ||
822 | vstor_packet->flags = REQUEST_COMPLETION_FLAG; | ||
823 | vstor_packet->sub_channel_count = num_sc; | ||
824 | |||
825 | ret = vmbus_sendpacket(device->channel, vstor_packet, | ||
826 | (sizeof(struct vstor_packet) - | ||
827 | vmscsi_size_delta), | ||
828 | (unsigned long)request, | ||
829 | VM_PKT_DATA_INBAND, | ||
830 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | ||
831 | |||
832 | if (ret != 0) | ||
833 | return; | ||
834 | |||
835 | t = wait_for_completion_timeout(&request->wait_event, 10*HZ); | ||
836 | if (t == 0) | ||
837 | return; | ||
838 | |||
839 | if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || | ||
840 | vstor_packet->status != 0) | ||
841 | return; | ||
842 | |||
843 | /* | ||
844 | * Now that we created the sub-channels, invoke the check; this | ||
845 | * may trigger the callback. | ||
846 | */ | ||
847 | stor_device->open_sub_channel = true; | ||
848 | vmbus_are_subchannels_present(device->channel); | ||
849 | } | ||
850 | |||
758 | static int storvsc_channel_init(struct hv_device *device) | 851 | static int storvsc_channel_init(struct hv_device *device) |
759 | { | 852 | { |
760 | struct storvsc_device *stor_device; | 853 | struct storvsc_device *stor_device; |
761 | struct storvsc_cmd_request *request; | 854 | struct storvsc_cmd_request *request; |
762 | struct vstor_packet *vstor_packet; | 855 | struct vstor_packet *vstor_packet; |
763 | int ret, t; | 856 | int ret, t; |
857 | int max_chns; | ||
858 | bool process_sub_channels = false; | ||
764 | 859 | ||
765 | stor_device = get_out_stor_device(device); | 860 | stor_device = get_out_stor_device(device); |
766 | if (!stor_device) | 861 | if (!stor_device) |
@@ -855,6 +950,19 @@ static int storvsc_channel_init(struct hv_device *device) | |||
855 | vstor_packet->status != 0) | 950 | vstor_packet->status != 0) |
856 | goto cleanup; | 951 | goto cleanup; |
857 | 952 | ||
953 | /* | ||
954 | * Check to see if multi-channel support is there. | ||
955 | * Hosts that implement protocol version of 5.1 and above | ||
956 | * support multi-channel. | ||
957 | */ | ||
958 | max_chns = vstor_packet->storage_channel_properties.max_channel_cnt; | ||
959 | if ((vmbus_proto_version != VERSION_WIN7) && | ||
960 | (vmbus_proto_version != VERSION_WS2008)) { | ||
961 | if (vstor_packet->storage_channel_properties.flags & | ||
962 | STORAGE_CHANNEL_SUPPORTS_MULTI_CHANNEL) | ||
963 | process_sub_channels = true; | ||
964 | } | ||
965 | |||
858 | memset(vstor_packet, 0, sizeof(struct vstor_packet)); | 966 | memset(vstor_packet, 0, sizeof(struct vstor_packet)); |
859 | vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION; | 967 | vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION; |
860 | vstor_packet->flags = REQUEST_COMPLETION_FLAG; | 968 | vstor_packet->flags = REQUEST_COMPLETION_FLAG; |
@@ -879,6 +987,9 @@ static int storvsc_channel_init(struct hv_device *device) | |||
879 | vstor_packet->status != 0) | 987 | vstor_packet->status != 0) |
880 | goto cleanup; | 988 | goto cleanup; |
881 | 989 | ||
990 | if (process_sub_channels) | ||
991 | handle_multichannel_storage(device, max_chns); | ||
992 | |||
882 | 993 | ||
883 | cleanup: | 994 | cleanup: |
884 | return ret; | 995 | return ret; |
@@ -1100,7 +1211,8 @@ static void storvsc_on_receive(struct hv_device *device, | |||
1100 | 1211 | ||
1101 | static void storvsc_on_channel_callback(void *context) | 1212 | static void storvsc_on_channel_callback(void *context) |
1102 | { | 1213 | { |
1103 | struct hv_device *device = (struct hv_device *)context; | 1214 | struct vmbus_channel *channel = (struct vmbus_channel *)context; |
1215 | struct hv_device *device; | ||
1104 | struct storvsc_device *stor_device; | 1216 | struct storvsc_device *stor_device; |
1105 | u32 bytes_recvd; | 1217 | u32 bytes_recvd; |
1106 | u64 request_id; | 1218 | u64 request_id; |
@@ -1108,13 +1220,17 @@ static void storvsc_on_channel_callback(void *context) | |||
1108 | struct storvsc_cmd_request *request; | 1220 | struct storvsc_cmd_request *request; |
1109 | int ret; | 1221 | int ret; |
1110 | 1222 | ||
1223 | if (channel->primary_channel != NULL) | ||
1224 | device = channel->primary_channel->device_obj; | ||
1225 | else | ||
1226 | device = channel->device_obj; | ||
1111 | 1227 | ||
1112 | stor_device = get_in_stor_device(device); | 1228 | stor_device = get_in_stor_device(device); |
1113 | if (!stor_device) | 1229 | if (!stor_device) |
1114 | return; | 1230 | return; |
1115 | 1231 | ||
1116 | do { | 1232 | do { |
1117 | ret = vmbus_recvpacket(device->channel, packet, | 1233 | ret = vmbus_recvpacket(channel, packet, |
1118 | ALIGN((sizeof(struct vstor_packet) - | 1234 | ALIGN((sizeof(struct vstor_packet) - |
1119 | vmscsi_size_delta), 8), | 1235 | vmscsi_size_delta), 8), |
1120 | &bytes_recvd, &request_id); | 1236 | &bytes_recvd, &request_id); |
@@ -1155,7 +1271,7 @@ static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size) | |||
1155 | ring_size, | 1271 | ring_size, |
1156 | (void *)&props, | 1272 | (void *)&props, |
1157 | sizeof(struct vmstorage_channel_properties), | 1273 | sizeof(struct vmstorage_channel_properties), |
1158 | storvsc_on_channel_callback, device); | 1274 | storvsc_on_channel_callback, device->channel); |
1159 | 1275 | ||
1160 | if (ret != 0) | 1276 | if (ret != 0) |
1161 | return ret; | 1277 | return ret; |
@@ -1207,6 +1323,7 @@ static int storvsc_do_io(struct hv_device *device, | |||
1207 | { | 1323 | { |
1208 | struct storvsc_device *stor_device; | 1324 | struct storvsc_device *stor_device; |
1209 | struct vstor_packet *vstor_packet; | 1325 | struct vstor_packet *vstor_packet; |
1326 | struct vmbus_channel *outgoing_channel; | ||
1210 | int ret = 0; | 1327 | int ret = 0; |
1211 | 1328 | ||
1212 | vstor_packet = &request->vstor_packet; | 1329 | vstor_packet = &request->vstor_packet; |
@@ -1217,6 +1334,11 @@ static int storvsc_do_io(struct hv_device *device, | |||
1217 | 1334 | ||
1218 | 1335 | ||
1219 | request->device = device; | 1336 | request->device = device; |
1337 | /* | ||
1338 | * Select an an appropriate channel to send the request out. | ||
1339 | */ | ||
1340 | |||
1341 | outgoing_channel = vmbus_get_outgoing_channel(device->channel); | ||
1220 | 1342 | ||
1221 | 1343 | ||
1222 | vstor_packet->flags |= REQUEST_COMPLETION_FLAG; | 1344 | vstor_packet->flags |= REQUEST_COMPLETION_FLAG; |
@@ -1234,7 +1356,7 @@ static int storvsc_do_io(struct hv_device *device, | |||
1234 | vstor_packet->operation = VSTOR_OPERATION_EXECUTE_SRB; | 1356 | vstor_packet->operation = VSTOR_OPERATION_EXECUTE_SRB; |
1235 | 1357 | ||
1236 | if (request->data_buffer.len) { | 1358 | if (request->data_buffer.len) { |
1237 | ret = vmbus_sendpacket_multipagebuffer(device->channel, | 1359 | ret = vmbus_sendpacket_multipagebuffer(outgoing_channel, |
1238 | &request->data_buffer, | 1360 | &request->data_buffer, |
1239 | vstor_packet, | 1361 | vstor_packet, |
1240 | (sizeof(struct vstor_packet) - | 1362 | (sizeof(struct vstor_packet) - |
@@ -1643,6 +1765,7 @@ static int storvsc_probe(struct hv_device *device, | |||
1643 | } | 1765 | } |
1644 | 1766 | ||
1645 | stor_device->destroy = false; | 1767 | stor_device->destroy = false; |
1768 | stor_device->open_sub_channel = false; | ||
1646 | init_waitqueue_head(&stor_device->waiting_to_drain); | 1769 | init_waitqueue_head(&stor_device->waiting_to_drain); |
1647 | stor_device->device = device; | 1770 | stor_device->device = device; |
1648 | stor_device->host = host; | 1771 | stor_device->host = host; |