vmbus_get_outgoing_channel() implements the following algorithm for selecting an outgoing channel (despite the comment before the function saying it distributes the load equally): 1) If we have no subchannels return the primary channel; 2) If primary->next_oc is grater than primary->num_sc reset the primary->next_oc to 0 and return the primary channel; 3) Aim for the primary->next_oc subchannel, increment primary->next_oc; 4) Loop through all opened subchannels. If we see a channel which has target_cpu == current_cpu return it. If we reached the primary->next_oc'th open subchannel return it; 5) Return the primary channel. The implementation also skips the subchannel No. 0 unless it matches the current cpu as we assign i to 1 in the initialization. Improve the algorithm to do the full scan searching for a (sub)channel with the target_cpu == current_cpu, use round-robin as a fallback. Signed-off-by: Vitaly Kuznetsov <vkuznets@xxxxxxxxxx> --- drivers/hv/channel_mgmt.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 1f1417d..cdc3914 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -823,46 +823,45 @@ cleanup: } /* - * Retrieve the (sub) channel on which to send an outgoing request. - * When a primary channel has multiple sub-channels, we try to - * distribute the load equally amongst all available channels. + * Retrieve the (sub) channel on which to send an outgoing request. When a + * primary channel has multiple sub-channels, we try to find a (sub)channel + * with target_cpu == current CPU and we try to distribute the load equally + * amongst all available channels when we fail. */ struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary) { struct list_head *cur, *tmp; - int cur_cpu; struct vmbus_channel *cur_channel; - struct vmbus_channel *outgoing_channel = primary; - int next_channel; - int i = 1; + struct vmbus_channel *out_channel_rr = NULL; + int i = 0; if (list_empty(&primary->sc_list)) - return outgoing_channel; + return primary; - next_channel = primary->next_oc++; - - if (next_channel > (primary->num_sc)) { + if (primary->next_oc >= primary->num_sc) { primary->next_oc = 0; - return outgoing_channel; + return primary; } - cur_cpu = hv_context.vp_index[get_cpu()]; - put_cpu(); + if (primary->target_cpu == smp_processor_id()) + return primary; + list_for_each_safe(cur, tmp, &primary->sc_list) { + i++; 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) + if (cur_channel->target_cpu == smp_processor_id()) return cur_channel; - if (i == next_channel) - return cur_channel; - - i++; + if (!out_channel_rr && i > primary->next_oc) { + primary->next_oc = i; + out_channel_rr = cur_channel; + } } - return outgoing_channel; + return out_channel_rr ? out_channel_rr : primary; } EXPORT_SYMBOL_GPL(vmbus_get_outgoing_channel); -- 1.9.3 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel