aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/storvsc_drv.c
diff options
context:
space:
mode:
authorK. Y. Srinivasan <kys@microsoft.com>2013-06-04 15:05:07 -0400
committerJames Bottomley <JBottomley@Parallels.com>2013-07-08 02:11:19 -0400
commit6f94d5de090947eb41efe9d3841d75ad91fd6dc7 (patch)
treebcc6f710e2cfd0c8a1c9e4986ca7c21cf894afe5 /drivers/scsi/storvsc_drv.c
parentc8a2ba3f50f7445b3d1b822ba1a8168b4234baca (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.c131
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
331static 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
761static 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
784static 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
758static int storvsc_channel_init(struct hv_device *device) 851static 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
883cleanup: 994cleanup:
884 return ret; 995 return ret;
@@ -1100,7 +1211,8 @@ static void storvsc_on_receive(struct hv_device *device,
1100 1211
1101static void storvsc_on_channel_callback(void *context) 1212static 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;