diff options
Diffstat (limited to 'drivers/hv')
-rw-r--r-- | drivers/hv/channel.c | 50 | ||||
-rw-r--r-- | drivers/hv/channel_mgmt.c | 119 | ||||
-rw-r--r-- | drivers/hv/connection.c | 14 |
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, unsigned long flags; int ret, t, err = 0; + spin_lock_irqsave(&newchannel->sc_lock, flags); + if (newchannel->state == CHANNEL_OPEN_STATE) { + newchannel->state = CHANNEL_OPENING_STATE; + } else { + spin_unlock_irqrestore(&newchannel->sc_lock, flags); + return -EINVAL; + } + spin_unlock_irqrestore(&newchannel->sc_lock, flags); + newchannel->onchannel_callback = onchannelcallback; newchannel->channel_callback_context = context; @@ -216,6 +225,9 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, list_del(&open_info->msglistentry); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + if (err == 0) + newchannel->state = CHANNEL_OPENED_STATE; + kfree(open_info); return err; @@ -500,15 +512,14 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) } EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl); -/* - * vmbus_close - Close the specified channel - */ -void vmbus_close(struct vmbus_channel *channel) +static void vmbus_close_internal(struct vmbus_channel *channel) { struct vmbus_channel_close_channel *msg; int ret; unsigned long flags; + channel->state = CHANNEL_OPEN_STATE; + channel->sc_creation_callback = NULL; /* Stop callback and cancel the timer asap */ spin_lock_irqsave(&channel->inbound_lock, flags); channel->onchannel_callback = NULL; @@ -538,6 +549,37 @@ void vmbus_close(struct vmbus_channel *channel) } + +/* + * vmbus_close - Close the specified channel + */ +void vmbus_close(struct vmbus_channel *channel) +{ + struct list_head *cur, *tmp; + struct vmbus_channel *cur_channel; + + if (channel->primary_channel != NULL) { + /* + * We will only close sub-channels when + * the primary is closed. + */ + return; + } + /* + * Close all the sub-channels first and then close the + * primary channel. + */ + list_for_each_safe(cur, tmp, &channel->sc_list) { + cur_channel = list_entry(cur, struct vmbus_channel, sc_list); + if (cur_channel->state != CHANNEL_OPENED_STATE) + continue; + vmbus_close_internal(cur_channel); + } + /* + * Now close the primary. + */ + vmbus_close_internal(channel); +} EXPORT_SYMBOL_GPL(vmbus_close); /** 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) return NULL; spin_lock_init(&channel->inbound_lock); + spin_lock_init(&channel->sc_lock); + + INIT_LIST_HEAD(&channel->sc_list); channel->controlwq = create_workqueue("hv_vmbus_ctl"); if (!channel->controlwq) { @@ -166,6 +169,7 @@ static void vmbus_process_rescind_offer(struct work_struct *work) struct vmbus_channel, work); unsigned long flags; + struct vmbus_channel *primary_channel; struct vmbus_channel_relid_released msg; vmbus_device_unregister(channel->device_obj); @@ -174,9 +178,16 @@ static void vmbus_process_rescind_offer(struct work_struct *work) msg.header.msgtype = CHANNELMSG_RELID_RELEASED; vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released)); - spin_lock_irqsave(&vmbus_connection.channel_lock, flags); - list_del(&channel->listentry); - spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); + if (channel->primary_channel == NULL) { + spin_lock_irqsave(&vmbus_connection.channel_lock, flags); + list_del(&channel->listentry); + spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); + } else { + primary_channel = channel->primary_channel; + spin_lock_irqsave(&primary_channel->sc_lock, flags); + list_del(&channel->listentry); + spin_unlock_irqrestore(&primary_channel->sc_lock, flags); + } free_channel(channel); } @@ -228,6 +239,24 @@ static void vmbus_process_offer(struct work_struct *work) spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); if (!fnew) { + /* + * Check to see if this is a sub-channel. + */ + if (newchannel->offermsg.offer.sub_channel_index != 0) { + /* + * Process the sub-channel. + */ + newchannel->primary_channel = channel; + spin_lock_irqsave(&channel->sc_lock, flags); + list_add_tail(&newchannel->sc_list, &channel->sc_list); + spin_unlock_irqrestore(&channel->sc_lock, flags); + newchannel->state = CHANNEL_OPEN_STATE; + if (channel->sc_creation_callback != NULL) + channel->sc_creation_callback(newchannel); + + return; + } + free_channel(newchannel); return; } @@ -685,4 +714,86 @@ cleanup: return ret; } -/* eof */ +/* + * Retrieve the (sub) channel on which to send an outgoing request. + * When a primary channel has multiple sub-channels, we choose a + * channel whose VCPU binding is closest to the VCPU on which + * this call is being made. + */ +struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary) +{ + struct list_head *cur, *tmp; + int cur_cpu = hv_context.vp_index[smp_processor_id()]; + struct vmbus_channel *cur_channel; + struct vmbus_channel *outgoing_channel = primary; + int cpu_distance, new_cpu_distance; + + if (list_empty(&primary->sc_list)) + return outgoing_channel; + + list_for_each_safe(cur, tmp, &primary->sc_list) { + cur_channel = list_entry(cur, struct vmbus_channel, sc_list); + if (cur_channel->state != CHANNEL_OPENED_STATE) + continue; + + if (cur_channel->target_vp == cur_cpu) + return cur_channel; + + cpu_distance = ((outgoing_channel->target_vp > cur_cpu) ? + (outgoing_channel->target_vp - cur_cpu) : + (cur_cpu - outgoing_channel->target_vp)); + + new_cpu_distance = ((cur_channel->target_vp > cur_cpu) ? + (cur_channel->target_vp - cur_cpu) : + (cur_cpu - cur_channel->target_vp)); + + if (cpu_distance < new_cpu_distance) + continue; + + outgoing_channel = cur_channel; + } + + return outgoing_channel; +} +EXPORT_SYMBOL_GPL(vmbus_get_outgoing_channel); + +static void invoke_sc_cb(struct vmbus_channel *primary_channel) +{ + struct list_head *cur, *tmp; + struct vmbus_channel *cur_channel; + + if (primary_channel->sc_creation_callback == NULL) + return; + + list_for_each_safe(cur, tmp, &primary_channel->sc_list) { + cur_channel = list_entry(cur, struct vmbus_channel, sc_list); + + primary_channel->sc_creation_callback(cur_channel); + } +} + +void vmbus_set_sc_create_callback(struct vmbus_channel *primary_channel, + void (*sc_cr_cb)(struct vmbus_channel *new_sc)) +{ + primary_channel->sc_creation_callback = sc_cr_cb; +} +EXPORT_SYMBOL_GPL(vmbus_set_sc_create_callback); + +bool vmbus_are_subchannels_present(struct vmbus_channel *primary) +{ + bool ret; + + ret = !list_empty(&primary->sc_list); + + if (ret) { + /* + * Invoke the callback on sub-channel creation. + * This will present a uniform interface to the + * clients. + */ + invoke_sc_cb(primary); + } + + return ret; +} +EXPORT_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) struct vmbus_channel *channel; struct vmbus_channel *found_channel = NULL; unsigned long flags; + struct list_head *cur, *tmp; + struct vmbus_channel *cur_sc; spin_lock_irqsave(&vmbus_connection.channel_lock, flags); list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { if (channel->offermsg.child_relid == relid) { found_channel = channel; break; + } else if (!list_empty(&channel->sc_list)) { + /* + * Deal with sub-channels. + */ + list_for_each_safe(cur, tmp, &channel->sc_list) { + cur_sc = list_entry(cur, struct vmbus_channel, + sc_list); + if (cur_sc->offermsg.child_relid == relid) { + found_channel = cur_sc; + break; + } + } } } spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); |