Index: sys/dev/hyperv/include/hyperv.h =================================================================== --- sys/dev/hyperv/include/hyperv.h +++ sys/dev/hyperv/include/hyperv.h @@ -281,8 +281,6 @@ vmbus_chan_callback_t cb, void *cbarg); void hv_vmbus_channel_close(hv_vmbus_channel *channel); -struct hv_vmbus_channel* vmbus_select_outgoing_channel(struct hv_vmbus_channel *promary); - /** * @brief Get physical address from virtual */ Index: sys/dev/hyperv/include/vmbus.h =================================================================== --- sys/dev/hyperv/include/vmbus.h +++ sys/dev/hyperv/include/vmbus.h @@ -96,13 +96,14 @@ void vmbus_chan_cpu_set(struct hv_vmbus_channel *chan, int cpu); void vmbus_chan_cpu_rr(struct hv_vmbus_channel *chan); +struct hv_vmbus_channel * + vmbus_chan_cpu2chan(struct hv_vmbus_channel *chan, int cpu); struct hv_vmbus_channel ** vmbus_subchan_get(struct hv_vmbus_channel *pri_chan, int subchan_cnt); void vmbus_subchan_rel(struct hv_vmbus_channel **subchan, int subchan_cnt); void vmbus_subchan_drain(struct hv_vmbus_channel *pri_chan); - int vmbus_chan_recv(struct hv_vmbus_channel *chan, void *data, int *dlen, uint64_t *xactid); int vmbus_chan_recv_pkt(struct hv_vmbus_channel *chan, Index: sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c =================================================================== --- sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c +++ sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c @@ -147,6 +147,8 @@ struct hv_storvsc_request hs_init_req; struct hv_storvsc_request hs_reset_req; device_t hs_dev; + + struct hv_vmbus_channel *hs_cpu2chan[MAXCPU]; }; @@ -664,7 +666,7 @@ vstor_packet->operation = VSTOR_OPERATION_EXECUTESRB; - outgoing_channel = vmbus_select_outgoing_channel(sc->hs_chan); + outgoing_channel = sc->hs_cpu2chan[curcpu]; mtx_unlock(&request->softc->hs_lock); if (request->prp_list.gpa_range.gpa_len) { @@ -863,6 +865,20 @@ return (ret); } +static void +storvsc_create_cpu2chan(struct storvsc_softc *sc) +{ + int cpu; + + CPU_FOREACH(cpu) { + sc->hs_cpu2chan[cpu] = vmbus_chan_cpu2chan(sc->hs_chan, cpu); + if (bootverbose) { + device_printf(sc->hs_dev, "cpu%d -> chan%u\n", + cpu, sc->hs_cpu2chan[cpu]->ch_id); + } + } +} + /** * @brief StorVSC attach function * @@ -960,6 +976,9 @@ goto cleanup; } + /* Construct cpu to channel mapping */ + storvsc_create_cpu2chan(sc); + /* * Create the device queue. * Hyper-V maps each target to one SCSI HBA Index: sys/dev/hyperv/vmbus/hv_channel.c =================================================================== --- sys/dev/hyperv/vmbus/hv_channel.c +++ sys/dev/hyperv/vmbus/hv_channel.c @@ -1250,61 +1250,63 @@ mtx_unlock(&sc->vmbus_prichan_lock); } -/** - * @brief Select the best outgoing channel - * +/* * The channel whose vcpu binding is closest to the currect vcpu will * be selected. - * If no multi-channel, always select primary channel - * - * @param primary - primary channel + * If no multi-channel, always select primary channel. */ struct hv_vmbus_channel * -vmbus_select_outgoing_channel(struct hv_vmbus_channel *primary) +vmbus_chan_cpu2chan(struct hv_vmbus_channel *prichan, int cpu) { - hv_vmbus_channel *new_channel = NULL; - hv_vmbus_channel *outgoing_channel = primary; - int old_cpu_distance = 0; - int new_cpu_distance = 0; - int cur_vcpu = 0; - int smp_pro_id = PCPU_GET(cpuid); - - if (TAILQ_EMPTY(&primary->ch_subchans)) { - return outgoing_channel; - } + struct hv_vmbus_channel *sel, *chan; + uint32_t vcpu, sel_dist; - if (smp_pro_id >= MAXCPU) { - return outgoing_channel; - } + KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpuid %d", cpu)); + if (TAILQ_EMPTY(&prichan->ch_subchans)) + return prichan; - cur_vcpu = VMBUS_PCPU_GET(primary->vmbus_sc, vcpuid, smp_pro_id); - - /* XXX need lock */ - TAILQ_FOREACH(new_channel, &primary->ch_subchans, ch_sublink) { - if ((new_channel->ch_stflags & VMBUS_CHAN_ST_OPENED) == 0) { - continue; - } + vcpu = VMBUS_PCPU_GET(prichan->vmbus_sc, vcpuid, cpu); - if (new_channel->ch_vcpuid == cur_vcpu){ - return new_channel; - } +#define CHAN_VCPU_DIST(ch, vcpu) \ + (((ch)->ch_vcpuid > (vcpu)) ? \ + ((ch)->ch_vcpuid - (vcpu)) : ((vcpu) - (ch)->ch_vcpuid)) - old_cpu_distance = ((outgoing_channel->ch_vcpuid > cur_vcpu) ? - (outgoing_channel->ch_vcpuid - cur_vcpu) : - (cur_vcpu - outgoing_channel->ch_vcpuid)); +#define CHAN_SELECT(ch) \ +do { \ + sel = ch; \ + sel_dist = CHAN_VCPU_DIST(ch, vcpu); \ +} while (0) - new_cpu_distance = ((new_channel->ch_vcpuid > cur_vcpu) ? - (new_channel->ch_vcpuid - cur_vcpu) : - (cur_vcpu - new_channel->ch_vcpuid)); + CHAN_SELECT(prichan); - if (old_cpu_distance < new_cpu_distance) { + mtx_lock(&prichan->ch_subchan_lock); + TAILQ_FOREACH(chan, &prichan->ch_subchans, ch_sublink) { + uint32_t dist; + + KASSERT(chan->ch_stflags & VMBUS_CHAN_ST_OPENED, + ("chan%u is not opened", chan->ch_id)); + + if (chan->ch_vcpuid == vcpu) { + /* Exact match; done */ + CHAN_SELECT(chan); + break; + } + + dist = CHAN_VCPU_DIST(chan, vcpu); + if (sel_dist <= dist) { + /* Far or same distance; skip */ continue; } - outgoing_channel = new_channel; + /* Select the closer channel. */ + CHAN_SELECT(chan); } + mtx_unlock(&prichan->ch_subchan_lock); + +#undef CHAN_SELECT +#undef CHAN_VCPU_DIST - return(outgoing_channel); + return sel; } struct hv_vmbus_channel **