aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/hv/channel.c50
-rw-r--r--drivers/hv/channel_mgmt.c119
-rw-r--r--drivers/hv/connection.c14
-rw-r--r--include/linux/hyperv.h62
4 files changed, 236 insertions, 9 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);
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index c2559847d7ee..405e05a28e65 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -909,6 +909,7 @@ enum vmbus_channel_state {
909 CHANNEL_OFFER_STATE, 909 CHANNEL_OFFER_STATE,
910 CHANNEL_OPENING_STATE, 910 CHANNEL_OPENING_STATE,
911 CHANNEL_OPEN_STATE, 911 CHANNEL_OPEN_STATE,
912 CHANNEL_OPENED_STATE,
912}; 913};
913 914
914struct vmbus_channel_debug_info { 915struct vmbus_channel_debug_info {
@@ -1046,6 +1047,38 @@ struct vmbus_channel {
1046 * preserve the earlier behavior. 1047 * preserve the earlier behavior.
1047 */ 1048 */
1048 u32 target_vp; 1049 u32 target_vp;
1050 /*
1051 * Support for sub-channels. For high performance devices,
1052 * it will be useful to have multiple sub-channels to support
1053 * a scalable communication infrastructure with the host.
1054 * The support for sub-channels is implemented as an extention
1055 * to the current infrastructure.
1056 * The initial offer is considered the primary channel and this
1057 * offer message will indicate if the host supports sub-channels.
1058 * The guest is free to ask for sub-channels to be offerred and can
1059 * open these sub-channels as a normal "primary" channel. However,
1060 * all sub-channels will have the same type and instance guids as the
1061 * primary channel. Requests sent on a given channel will result in a
1062 * response on the same channel.
1063 */
1064
1065 /*
1066 * Sub-channel creation callback. This callback will be called in
1067 * process context when a sub-channel offer is received from the host.
1068 * The guest can open the sub-channel in the context of this callback.
1069 */
1070 void (*sc_creation_callback)(struct vmbus_channel *new_sc);
1071
1072 spinlock_t sc_lock;
1073 /*
1074 * All Sub-channels of a primary channel are linked here.
1075 */
1076 struct list_head sc_list;
1077 /*
1078 * The primary channel this sub-channel belongs to.
1079 * This will be NULL for the primary channel.
1080 */
1081 struct vmbus_channel *primary_channel;
1049}; 1082};
1050 1083
1051static inline void set_channel_read_state(struct vmbus_channel *c, bool state) 1084static inline void set_channel_read_state(struct vmbus_channel *c, bool state)
@@ -1057,6 +1090,34 @@ void vmbus_onmessage(void *context);
1057 1090
1058int vmbus_request_offers(void); 1091int vmbus_request_offers(void);
1059 1092
1093/*
1094 * APIs for managing sub-channels.
1095 */
1096
1097void vmbus_set_sc_create_callback(struct vmbus_channel *primary_channel,
1098 void (*sc_cr_cb)(struct vmbus_channel *new_sc));
1099
1100/*
1101 * Retrieve the (sub) channel on which to send an outgoing request.
1102 * When a primary channel has multiple sub-channels, we choose a
1103 * channel whose VCPU binding is closest to the VCPU on which
1104 * this call is being made.
1105 */
1106struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary);
1107
1108/*
1109 * Check if sub-channels have already been offerred. This API will be useful
1110 * when the driver is unloaded after establishing sub-channels. In this case,
1111 * when the driver is re-loaded, the driver would have to check if the
1112 * subchannels have already been established before attempting to request
1113 * the creation of sub-channels.
1114 * This function returns TRUE to indicate that subchannels have already been
1115 * created.
1116 * This function should be invoked after setting the callback function for
1117 * sub-channel creation.
1118 */
1119bool vmbus_are_subchannels_present(struct vmbus_channel *primary);
1120
1060/* The format must be the same as struct vmdata_gpa_direct */ 1121/* The format must be the same as struct vmdata_gpa_direct */
1061struct vmbus_channel_packet_page_buffer { 1122struct vmbus_channel_packet_page_buffer {
1062 u16 type; 1123 u16 type;
@@ -1327,7 +1388,6 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver);
1327 0x8e, 0x77, 0x05, 0x58, 0xeb, 0x10, 0x73, 0xf8 \ 1388 0x8e, 0x77, 0x05, 0x58, 0xeb, 0x10, 0x73, 0xf8 \
1328 } 1389 }
1329 1390
1330
1331/* 1391/*
1332 * Common header for Hyper-V ICs 1392 * Common header for Hyper-V ICs
1333 */ 1393 */