aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hv
diff options
context:
space:
mode:
authorK. Y. Srinivasan <kys@microsoft.com>2013-05-23 15:02:32 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-06-03 17:09:14 -0400
commite68d2971d26577b932a16333ce165af98a96e068 (patch)
tree641b8143d41993366c4d0d0b6546a1695acba83b /drivers/hv
parent8d7bda51888d14c07cbebacc5a10be776477bb63 (diff)
Drivers: hv: vmbus: Implement multi-channel support
Starting with Win8, the host supports multiple sub-channels for a given device. As in the past, the initial channel offer specifies the device and is associated with both the type and the instance GUIDs. For performance critical devices, the host may support multiple sub-channels. The sub-channels share the same type and instance GUID as the primary channel. The number of sub-channels offerrred to the guest depends on the number of virtual CPUs assigned to the guest. The guest can request the creation of these sub-channels and once created and opened, the guest can distribute the traffic across all the channels (the primary and the sub-channels). A request sent on a sub-channel will have the response delivered on the same sub-channel. At channel (sub-channel) creation we bind the channel interrupt to a CPU and with this sub-channel support we will be able to spread the interrupt load of a given device across all available CPUs. Signed-off-by: K. Y. Srinivasan <kys@microsoft.com> Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/hv')
-rw-r--r--drivers/hv/channel.c50
-rw-r--r--drivers/hv/channel_mgmt.c119
-rw-r--r--drivers/hv/connection.c14
3 files changed, 175 insertions, 8 deletions
diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c
index 0b122f8c7005..6de6c98ce6eb 100644
--- a/drivers/hv/channel.c
+++ b/drivers/hv/channel.c
@@ -116,6 +116,15 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
116 unsigned long flags; 116 unsigned long flags;
117 int ret, t, err = 0; 117 int ret, t, err = 0;
118 118
119 spin_lock_irqsave(&newchannel->sc_lock, flags);
120 if (newchannel->state == CHANNEL_OPEN_STATE) {
121 newchannel->state = CHANNEL_OPENING_STATE;
122 } else {
123 spin_unlock_irqrestore(&newchannel->sc_lock, flags);
124 return -EINVAL;
125 }
126 spin_unlock_irqrestore(&newchannel->sc_lock, flags);
127
119 newchannel->onchannel_callback = onchannelcallback; 128 newchannel->onchannel_callback = onchannelcallback;
120 newchannel->channel_callback_context = context; 129 newchannel->channel_callback_context = context;
121 130
@@ -216,6 +225,9 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
216 list_del(&open_info->msglistentry); 225 list_del(&open_info->msglistentry);
217 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 226 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
218 227
228 if (err == 0)
229 newchannel->state = CHANNEL_OPENED_STATE;
230
219 kfree(open_info); 231 kfree(open_info);
220 return err; 232 return err;
221 233
@@ -500,15 +512,14 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)
500} 512}
501EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl); 513EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl);
502 514
503/* 515static void vmbus_close_internal(struct vmbus_channel *channel)
504 * vmbus_close - Close the specified channel
505 */
506void vmbus_close(struct vmbus_channel *channel)
507{ 516{
508 struct vmbus_channel_close_channel *msg; 517 struct vmbus_channel_close_channel *msg;
509 int ret; 518 int ret;
510 unsigned long flags; 519 unsigned long flags;
511 520
521 channel->state = CHANNEL_OPEN_STATE;
522 channel->sc_creation_callback = NULL;
512 /* Stop callback and cancel the timer asap */ 523 /* Stop callback and cancel the timer asap */
513 spin_lock_irqsave(&channel->inbound_lock, flags); 524 spin_lock_irqsave(&channel->inbound_lock, flags);
514 channel->onchannel_callback = NULL; 525 channel->onchannel_callback = NULL;
@@ -538,6 +549,37 @@ void vmbus_close(struct vmbus_channel *channel)
538 549
539 550
540} 551}
552
553/*
554 * vmbus_close - Close the specified channel
555 */
556void vmbus_close(struct vmbus_channel *channel)
557{
558 struct list_head *cur, *tmp;
559 struct vmbus_channel *cur_channel;
560
561 if (channel->primary_channel != NULL) {
562 /*
563 * We will only close sub-channels when
564 * the primary is closed.
565 */
566 return;
567 }
568 /*
569 * Close all the sub-channels first and then close the
570 * primary channel.
571 */
572 list_for_each_safe(cur, tmp, &channel->sc_list) {
573 cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
574 if (cur_channel->state != CHANNEL_OPENED_STATE)
575 continue;
576 vmbus_close_internal(cur_channel);
577 }
578 /*
579 * Now close the primary.
580 */
581 vmbus_close_internal(channel);
582}
541EXPORT_SYMBOL_GPL(vmbus_close); 583EXPORT_SYMBOL_GPL(vmbus_close);
542 584
543/** 585/**
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index 21ef68934a20..0df75908200e 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -115,6 +115,9 @@ static struct vmbus_channel *alloc_channel(void)
115 return NULL; 115 return NULL;
116 116
117 spin_lock_init(&channel->inbound_lock); 117 spin_lock_init(&channel->inbound_lock);
118 spin_lock_init(&channel->sc_lock);
119
120 INIT_LIST_HEAD(&channel->sc_list);
118 121
119 channel->controlwq = create_workqueue("hv_vmbus_ctl"); 122 channel->controlwq = create_workqueue("hv_vmbus_ctl");
120 if (!channel->controlwq) { 123 if (!channel->controlwq) {
@@ -166,6 +169,7 @@ static void vmbus_process_rescind_offer(struct work_struct *work)
166 struct vmbus_channel, 169 struct vmbus_channel,
167 work); 170 work);
168 unsigned long flags; 171 unsigned long flags;
172 struct vmbus_channel *primary_channel;
169 struct vmbus_channel_relid_released msg; 173 struct vmbus_channel_relid_released msg;
170 174
171 vmbus_device_unregister(channel->device_obj); 175 vmbus_device_unregister(channel->device_obj);
@@ -174,9 +178,16 @@ static void vmbus_process_rescind_offer(struct work_struct *work)
174 msg.header.msgtype = CHANNELMSG_RELID_RELEASED; 178 msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
175 vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released)); 179 vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released));
176 180
177 spin_lock_irqsave(&vmbus_connection.channel_lock, flags); 181 if (channel->primary_channel == NULL) {
178 list_del(&channel->listentry); 182 spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
179 spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); 183 list_del(&channel->listentry);
184 spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
185 } else {
186 primary_channel = channel->primary_channel;
187 spin_lock_irqsave(&primary_channel->sc_lock, flags);
188 list_del(&channel->listentry);
189 spin_unlock_irqrestore(&primary_channel->sc_lock, flags);
190 }
180 free_channel(channel); 191 free_channel(channel);
181} 192}
182 193
@@ -228,6 +239,24 @@ static void vmbus_process_offer(struct work_struct *work)
228 spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); 239 spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
229 240
230 if (!fnew) { 241 if (!fnew) {
242 /*
243 * Check to see if this is a sub-channel.
244 */
245 if (newchannel->offermsg.offer.sub_channel_index != 0) {
246 /*
247 * Process the sub-channel.
248 */
249 newchannel->primary_channel = channel;
250 spin_lock_irqsave(&channel->sc_lock, flags);
251 list_add_tail(&newchannel->sc_list, &channel->sc_list);
252 spin_unlock_irqrestore(&channel->sc_lock, flags);
253 newchannel->state = CHANNEL_OPEN_STATE;
254 if (channel->sc_creation_callback != NULL)
255 channel->sc_creation_callback(newchannel);
256
257 return;
258 }
259
231 free_channel(newchannel); 260 free_channel(newchannel);
232 return; 261 return;
233 } 262 }
@@ -685,4 +714,86 @@ cleanup:
685 return ret; 714 return ret;
686} 715}
687 716
688/* eof */ 717/*
718 * Retrieve the (sub) channel on which to send an outgoing request.
719 * When a primary channel has multiple sub-channels, we choose a
720 * channel whose VCPU binding is closest to the VCPU on which
721 * this call is being made.
722 */
723struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary)
724{
725 struct list_head *cur, *tmp;
726 int cur_cpu = hv_context.vp_index[smp_processor_id()];
727 struct vmbus_channel *cur_channel;
728 struct vmbus_channel *outgoing_channel = primary;
729 int cpu_distance, new_cpu_distance;
730
731 if (list_empty(&primary->sc_list))
732 return outgoing_channel;
733
734 list_for_each_safe(cur, tmp, &primary->sc_list) {
735 cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
736 if (cur_channel->state != CHANNEL_OPENED_STATE)
737 continue;
738
739 if (cur_channel->target_vp == cur_cpu)
740 return cur_channel;
741
742 cpu_distance = ((outgoing_channel->target_vp > cur_cpu) ?
743 (outgoing_channel->target_vp - cur_cpu) :
744 (cur_cpu - outgoing_channel->target_vp));
745
746 new_cpu_distance = ((cur_channel->target_vp > cur_cpu) ?
747 (cur_channel->target_vp - cur_cpu) :
748 (cur_cpu - cur_channel->target_vp));
749
750 if (cpu_distance < new_cpu_distance)
751 continue;
752
753 outgoing_channel = cur_channel;
754 }
755
756 return outgoing_channel;
757}
758EXPORT_SYMBOL_GPL(vmbus_get_outgoing_channel);
759
760static void invoke_sc_cb(struct vmbus_channel *primary_channel)
761{
762 struct list_head *cur, *tmp;
763 struct vmbus_channel *cur_channel;
764
765 if (primary_channel->sc_creation_callback == NULL)
766 return;
767
768 list_for_each_safe(cur, tmp, &primary_channel->sc_list) {
769 cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
770
771 primary_channel->sc_creation_callback(cur_channel);
772 }
773}
774
775void vmbus_set_sc_create_callback(struct vmbus_channel *primary_channel,
776 void (*sc_cr_cb)(struct vmbus_channel *new_sc))
777{
778 primary_channel->sc_creation_callback = sc_cr_cb;
779}
780EXPORT_SYMBOL_GPL(vmbus_set_sc_create_callback);
781
782bool vmbus_are_subchannels_present(struct vmbus_channel *primary)
783{
784 bool ret;
785
786 ret = !list_empty(&primary->sc_list);
787
788 if (ret) {
789 /*
790 * Invoke the callback on sub-channel creation.
791 * This will present a uniform interface to the
792 * clients.
793 */
794 invoke_sc_cb(primary);
795 }
796
797 return ret;
798}
799EXPORT_SYMBOL_GPL(vmbus_are_subchannels_present);
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index 253a74ba245c..ec3b8cdf1e04 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -246,12 +246,26 @@ struct vmbus_channel *relid2channel(u32 relid)
246 struct vmbus_channel *channel; 246 struct vmbus_channel *channel;
247 struct vmbus_channel *found_channel = NULL; 247 struct vmbus_channel *found_channel = NULL;
248 unsigned long flags; 248 unsigned long flags;
249 struct list_head *cur, *tmp;
250 struct vmbus_channel *cur_sc;
249 251
250 spin_lock_irqsave(&vmbus_connection.channel_lock, flags); 252 spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
251 list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { 253 list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
252 if (channel->offermsg.child_relid == relid) { 254 if (channel->offermsg.child_relid == relid) {
253 found_channel = channel; 255 found_channel = channel;
254 break; 256 break;
257 } else if (!list_empty(&channel->sc_list)) {
258 /*
259 * Deal with sub-channels.
260 */
261 list_for_each_safe(cur, tmp, &channel->sc_list) {
262 cur_sc = list_entry(cur, struct vmbus_channel,
263 sc_list);
264 if (cur_sc->offermsg.child_relid == relid) {
265 found_channel = cur_sc;
266 break;
267 }
268 }
255 } 269 }
256 } 270 }
257 spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); 271 spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);