[SCSI] storvsc: Implement multi-channel support
authorK. Y. Srinivasan <kys@microsoft.com>
Tue, 4 Jun 2013 19:05:07 +0000 (12:05 -0700)
committerJames Bottomley <JBottomley@Parallels.com>
Mon, 8 Jul 2013 06:11:19 +0000 (07:11 +0100)
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
drivers/scsi/storvsc_drv.c

index 9451989..1d4d425 100644 (file)
@@ -328,6 +328,8 @@ static int storvsc_timeout = 180;
 
 #define STORVSC_MAX_IO_REQUESTS                                128
 
+static void storvsc_on_channel_callback(void *context);
+
 /*
  * In Hyper-V, each port/path/target maps to 1 scsi host adapter.  In
  * reality, the path/target is not used (ie always set to 0) so our
@@ -364,6 +366,7 @@ struct storvsc_device {
 
        bool     destroy;
        bool     drain_notify;
+       bool     open_sub_channel;
        atomic_t num_outstanding_req;
        struct Scsi_Host *host;
 
@@ -755,12 +758,104 @@ static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl,
        return total_copied;
 }
 
+static void handle_sc_creation(struct vmbus_channel *new_sc)
+{
+       struct hv_device *device = new_sc->primary_channel->device_obj;
+       struct storvsc_device *stor_device;
+       struct vmstorage_channel_properties props;
+
+       stor_device = get_out_stor_device(device);
+       if (!stor_device)
+               return;
+
+       if (stor_device->open_sub_channel == false)
+               return;
+
+       memset(&props, 0, sizeof(struct vmstorage_channel_properties));
+
+       vmbus_open(new_sc,
+                  storvsc_ringbuffer_size,
+                  storvsc_ringbuffer_size,
+                  (void *)&props,
+                  sizeof(struct vmstorage_channel_properties),
+                  storvsc_on_channel_callback, new_sc);
+}
+
+static void  handle_multichannel_storage(struct hv_device *device, int max_chns)
+{
+       struct storvsc_device *stor_device;
+       int num_cpus = num_online_cpus();
+       int num_sc;
+       struct storvsc_cmd_request *request;
+       struct vstor_packet *vstor_packet;
+       int ret, t;
+
+       num_sc = ((max_chns > num_cpus) ? num_cpus : max_chns);
+       stor_device = get_out_stor_device(device);
+       if (!stor_device)
+               return;
+
+       request = &stor_device->init_request;
+       vstor_packet = &request->vstor_packet;
+
+       stor_device->open_sub_channel = true;
+       /*
+        * Establish a handler for dealing with subchannels.
+        */
+       vmbus_set_sc_create_callback(device->channel, handle_sc_creation);
+
+       /*
+        * Check to see if sub-channels have already been created. This
+        * can happen when this driver is re-loaded after unloading.
+        */
+
+       if (vmbus_are_subchannels_present(device->channel))
+               return;
+
+       stor_device->open_sub_channel = false;
+       /*
+        * Request the host to create sub-channels.
+        */
+       memset(request, 0, sizeof(struct storvsc_cmd_request));
+       init_completion(&request->wait_event);
+       vstor_packet->operation = VSTOR_OPERATION_CREATE_SUB_CHANNELS;
+       vstor_packet->flags = REQUEST_COMPLETION_FLAG;
+       vstor_packet->sub_channel_count = num_sc;
+
+       ret = vmbus_sendpacket(device->channel, vstor_packet,
+                              (sizeof(struct vstor_packet) -
+                              vmscsi_size_delta),
+                              (unsigned long)request,
+                              VM_PKT_DATA_INBAND,
+                              VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+
+       if (ret != 0)
+               return;
+
+       t = wait_for_completion_timeout(&request->wait_event, 10*HZ);
+       if (t == 0)
+               return;
+
+       if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO ||
+           vstor_packet->status != 0)
+               return;
+
+       /*
+        * Now that we created the sub-channels, invoke the check; this
+        * may trigger the callback.
+        */
+       stor_device->open_sub_channel = true;
+       vmbus_are_subchannels_present(device->channel);
+}
+
 static int storvsc_channel_init(struct hv_device *device)
 {
        struct storvsc_device *stor_device;
        struct storvsc_cmd_request *request;
        struct vstor_packet *vstor_packet;
        int ret, t;
+       int max_chns;
+       bool process_sub_channels = false;
 
        stor_device = get_out_stor_device(device);
        if (!stor_device)
@@ -855,6 +950,19 @@ static int storvsc_channel_init(struct hv_device *device)
            vstor_packet->status != 0)
                goto cleanup;
 
+       /*
+        * Check to see if multi-channel support is there.
+        * Hosts that implement protocol version of 5.1 and above
+        * support multi-channel.
+        */
+       max_chns = vstor_packet->storage_channel_properties.max_channel_cnt;
+       if ((vmbus_proto_version != VERSION_WIN7) &&
+          (vmbus_proto_version != VERSION_WS2008))  {
+               if (vstor_packet->storage_channel_properties.flags &
+                   STORAGE_CHANNEL_SUPPORTS_MULTI_CHANNEL)
+                       process_sub_channels = true;
+       }
+
        memset(vstor_packet, 0, sizeof(struct vstor_packet));
        vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION;
        vstor_packet->flags = REQUEST_COMPLETION_FLAG;
@@ -879,6 +987,9 @@ static int storvsc_channel_init(struct hv_device *device)
            vstor_packet->status != 0)
                goto cleanup;
 
+       if (process_sub_channels)
+               handle_multichannel_storage(device, max_chns);
+
 
 cleanup:
        return ret;
@@ -1100,7 +1211,8 @@ static void storvsc_on_receive(struct hv_device *device,
 
 static void storvsc_on_channel_callback(void *context)
 {
-       struct hv_device *device = (struct hv_device *)context;
+       struct vmbus_channel *channel = (struct vmbus_channel *)context;
+       struct hv_device *device;
        struct storvsc_device *stor_device;
        u32 bytes_recvd;
        u64 request_id;
@@ -1108,13 +1220,17 @@ static void storvsc_on_channel_callback(void *context)
        struct storvsc_cmd_request *request;
        int ret;
 
+       if (channel->primary_channel != NULL)
+               device = channel->primary_channel->device_obj;
+       else
+               device = channel->device_obj;
 
        stor_device = get_in_stor_device(device);
        if (!stor_device)
                return;
 
        do {
-               ret = vmbus_recvpacket(device->channel, packet,
+               ret = vmbus_recvpacket(channel, packet,
                                       ALIGN((sizeof(struct vstor_packet) -
                                             vmscsi_size_delta), 8),
                                       &bytes_recvd, &request_id);
@@ -1155,7 +1271,7 @@ static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size)
                         ring_size,
                         (void *)&props,
                         sizeof(struct vmstorage_channel_properties),
-                        storvsc_on_channel_callback, device);
+                        storvsc_on_channel_callback, device->channel);
 
        if (ret != 0)
                return ret;
@@ -1207,6 +1323,7 @@ static int storvsc_do_io(struct hv_device *device,
 {
        struct storvsc_device *stor_device;
        struct vstor_packet *vstor_packet;
+       struct vmbus_channel *outgoing_channel;
        int ret = 0;
 
        vstor_packet = &request->vstor_packet;
@@ -1217,6 +1334,11 @@ static int storvsc_do_io(struct hv_device *device,
 
 
        request->device  = device;
+       /*
+        * Select an an appropriate channel to send the request out.
+        */
+
+       outgoing_channel = vmbus_get_outgoing_channel(device->channel);
 
 
        vstor_packet->flags |= REQUEST_COMPLETION_FLAG;
@@ -1234,7 +1356,7 @@ static int storvsc_do_io(struct hv_device *device,
        vstor_packet->operation = VSTOR_OPERATION_EXECUTE_SRB;
 
        if (request->data_buffer.len) {
-               ret = vmbus_sendpacket_multipagebuffer(device->channel,
+               ret = vmbus_sendpacket_multipagebuffer(outgoing_channel,
                                &request->data_buffer,
                                vstor_packet,
                                (sizeof(struct vstor_packet) -
@@ -1643,6 +1765,7 @@ static int storvsc_probe(struct hv_device *device,
        }
 
        stor_device->destroy = false;
+       stor_device->open_sub_channel = false;
        init_waitqueue_head(&stor_device->waiting_to_drain);
        stor_device->device = device;
        stor_device->host = host;