Date: Fri, 15 Jul 2016 04:42:08 +0000 (UTC) From: Sepherosa Ziehau <sephe@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r302864 - in head/sys: conf dev/hyperv/vmbus modules/hyperv/vmbus Message-ID: <201607150442.u6F4g8F6060118@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: sephe Date: Fri Jul 15 04:42:08 2016 New Revision: 302864 URL: https://svnweb.freebsd.org/changeset/base/302864 Log: hyperv/vmbus: Merge hv_channel_mgmt.c into hv_channel.c MFC after: 1 week Sponsored by: Microsoft OSTC Differential Revision: https://reviews.freebsd.org/D7126 Deleted: head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c Modified: head/sys/conf/files.amd64 head/sys/conf/files.i386 head/sys/dev/hyperv/vmbus/hv_channel.c head/sys/modules/hyperv/vmbus/Makefile Modified: head/sys/conf/files.amd64 ============================================================================== --- head/sys/conf/files.amd64 Fri Jul 15 02:29:10 2016 (r302863) +++ head/sys/conf/files.amd64 Fri Jul 15 04:42:08 2016 (r302864) @@ -271,7 +271,6 @@ dev/hyperv/utilities/hv_shutdown.c opt dev/hyperv/utilities/hv_timesync.c optional hyperv dev/hyperv/utilities/hv_util.c optional hyperv dev/hyperv/vmbus/hv_channel.c optional hyperv -dev/hyperv/vmbus/hv_channel_mgmt.c optional hyperv dev/hyperv/vmbus/hv_ring_buffer.c optional hyperv dev/hyperv/vmbus/hyperv.c optional hyperv dev/hyperv/vmbus/hyperv_busdma.c optional hyperv Modified: head/sys/conf/files.i386 ============================================================================== --- head/sys/conf/files.i386 Fri Jul 15 02:29:10 2016 (r302863) +++ head/sys/conf/files.i386 Fri Jul 15 04:42:08 2016 (r302864) @@ -247,7 +247,6 @@ dev/hyperv/utilities/hv_shutdown.c opt dev/hyperv/utilities/hv_timesync.c optional hyperv dev/hyperv/utilities/hv_util.c optional hyperv dev/hyperv/vmbus/hv_channel.c optional hyperv -dev/hyperv/vmbus/hv_channel_mgmt.c optional hyperv dev/hyperv/vmbus/hv_ring_buffer.c optional hyperv dev/hyperv/vmbus/hyperv.c optional hyperv dev/hyperv/vmbus/hyperv_busdma.c optional hyperv Modified: head/sys/dev/hyperv/vmbus/hv_channel.c ============================================================================== --- head/sys/dev/hyperv/vmbus/hv_channel.c Fri Jul 15 02:29:10 2016 (r302863) +++ head/sys/dev/hyperv/vmbus/hv_channel.c Fri Jul 15 04:42:08 2016 (r302864) @@ -53,8 +53,28 @@ __FBSDID("$FreeBSD$"); static void vmbus_chan_send_event(hv_vmbus_channel* channel); static void vmbus_chan_update_evtflagcnt(struct vmbus_softc *, const struct hv_vmbus_channel *); + static void vmbus_chan_task(void *, int); static void vmbus_chan_task_nobatch(void *, int); +static void vmbus_chan_detach_task(void *, int); + +static void vmbus_chan_msgproc_choffer(struct vmbus_softc *, + const struct vmbus_message *); +static void vmbus_chan_msgproc_chrescind(struct vmbus_softc *, + const struct vmbus_message *); + +/* + * Vmbus channel message processing. + */ +static const vmbus_chanmsg_proc_t +vmbus_chan_msgprocs[VMBUS_CHANMSG_TYPE_MAX] = { + VMBUS_CHANMSG_PROC(CHOFFER, vmbus_chan_msgproc_choffer), + VMBUS_CHANMSG_PROC(CHRESCIND, vmbus_chan_msgproc_chrescind), + + VMBUS_CHANMSG_PROC_WAKEUP(CHOPEN_RESP), + VMBUS_CHANMSG_PROC_WAKEUP(GPADL_CONNRESP), + VMBUS_CHANMSG_PROC_WAKEUP(GPADL_DISCONNRESP) +}; /** * @brief Trigger an event notification on the specified channel @@ -984,3 +1004,463 @@ vmbus_chan_update_evtflagcnt(struct vmbu } } } + +static struct hv_vmbus_channel * +vmbus_chan_alloc(struct vmbus_softc *sc) +{ + struct hv_vmbus_channel *chan; + + chan = malloc(sizeof(*chan), M_DEVBUF, M_WAITOK | M_ZERO); + + chan->ch_monprm = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev), + HYPERCALL_PARAM_ALIGN, 0, sizeof(struct hyperv_mon_param), + &chan->ch_monprm_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO); + if (chan->ch_monprm == NULL) { + device_printf(sc->vmbus_dev, "monprm alloc failed\n"); + free(chan, M_DEVBUF); + return NULL; + } + + chan->vmbus_sc = sc; + mtx_init(&chan->ch_subchan_lock, "vmbus subchan", NULL, MTX_DEF); + TAILQ_INIT(&chan->ch_subchans); + TASK_INIT(&chan->ch_detach_task, 0, vmbus_chan_detach_task, chan); + + return chan; +} + +static void +vmbus_chan_free(struct hv_vmbus_channel *chan) +{ + /* TODO: assert sub-channel list is empty */ + /* TODO: asset no longer on the primary channel's sub-channel list */ + /* TODO: asset no longer on the vmbus channel list */ + hyperv_dmamem_free(&chan->ch_monprm_dma, chan->ch_monprm); + mtx_destroy(&chan->ch_subchan_lock); + free(chan, M_DEVBUF); +} + +static int +vmbus_chan_add(struct hv_vmbus_channel *newchan) +{ + struct vmbus_softc *sc = newchan->vmbus_sc; + struct hv_vmbus_channel *prichan; + + if (newchan->ch_id == 0) { + /* + * XXX + * Chan0 will neither be processed nor should be offered; + * skip it. + */ + device_printf(sc->vmbus_dev, "got chan0 offer, discard\n"); + return EINVAL; + } else if (newchan->ch_id >= VMBUS_CHAN_MAX) { + device_printf(sc->vmbus_dev, "invalid chan%u offer\n", + newchan->ch_id); + return EINVAL; + } + sc->vmbus_chmap[newchan->ch_id] = newchan; + + if (bootverbose) { + device_printf(sc->vmbus_dev, "chan%u subidx%u offer\n", + newchan->ch_id, newchan->ch_subidx); + } + + mtx_lock(&sc->vmbus_prichan_lock); + TAILQ_FOREACH(prichan, &sc->vmbus_prichans, ch_prilink) { + /* + * Sub-channel will have the same type GUID and instance + * GUID as its primary channel. + */ + if (memcmp(&prichan->ch_guid_type, &newchan->ch_guid_type, + sizeof(struct hyperv_guid)) == 0 && + memcmp(&prichan->ch_guid_inst, &newchan->ch_guid_inst, + sizeof(struct hyperv_guid)) == 0) + break; + } + if (VMBUS_CHAN_ISPRIMARY(newchan)) { + if (prichan == NULL) { + /* Install the new primary channel */ + TAILQ_INSERT_TAIL(&sc->vmbus_prichans, newchan, + ch_prilink); + mtx_unlock(&sc->vmbus_prichan_lock); + return 0; + } else { + mtx_unlock(&sc->vmbus_prichan_lock); + device_printf(sc->vmbus_dev, "duplicated primary " + "chan%u\n", newchan->ch_id); + return EINVAL; + } + } else { /* Sub-channel */ + if (prichan == NULL) { + mtx_unlock(&sc->vmbus_prichan_lock); + device_printf(sc->vmbus_dev, "no primary chan for " + "chan%u\n", newchan->ch_id); + return EINVAL; + } + /* + * Found the primary channel for this sub-channel and + * move on. + * + * XXX refcnt prichan + */ + } + mtx_unlock(&sc->vmbus_prichan_lock); + + /* + * This is a sub-channel; link it with the primary channel. + */ + KASSERT(!VMBUS_CHAN_ISPRIMARY(newchan), + ("new channel is not sub-channel")); + KASSERT(prichan != NULL, ("no primary channel")); + + newchan->ch_prichan = prichan; + newchan->ch_dev = prichan->ch_dev; + + mtx_lock(&prichan->ch_subchan_lock); + TAILQ_INSERT_TAIL(&prichan->ch_subchans, newchan, ch_sublink); + /* + * Bump up sub-channel count and notify anyone that is + * interested in this sub-channel, after this sub-channel + * is setup. + */ + prichan->ch_subchan_cnt++; + mtx_unlock(&prichan->ch_subchan_lock); + wakeup(prichan); + + return 0; +} + +void +vmbus_channel_cpu_set(struct hv_vmbus_channel *chan, int cpu) +{ + KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu)); + + if (chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WS2008 || + chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WIN7) { + /* Only cpu0 is supported */ + cpu = 0; + } + + chan->target_cpu = cpu; + chan->target_vcpu = VMBUS_PCPU_GET(chan->vmbus_sc, vcpuid, cpu); + + if (bootverbose) { + printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n", + chan->ch_id, + chan->target_cpu, chan->target_vcpu); + } +} + +void +vmbus_channel_cpu_rr(struct hv_vmbus_channel *chan) +{ + static uint32_t vmbus_chan_nextcpu; + int cpu; + + cpu = atomic_fetchadd_int(&vmbus_chan_nextcpu, 1) % mp_ncpus; + vmbus_channel_cpu_set(chan, cpu); +} + +static void +vmbus_chan_cpu_default(struct hv_vmbus_channel *chan) +{ + /* + * By default, pin the channel to cpu0. Devices having + * special channel-cpu mapping requirement should call + * vmbus_channel_cpu_{set,rr}(). + */ + vmbus_channel_cpu_set(chan, 0); +} + +static void +vmbus_chan_msgproc_choffer(struct vmbus_softc *sc, + const struct vmbus_message *msg) +{ + const struct vmbus_chanmsg_choffer *offer; + struct hv_vmbus_channel *chan; + int error; + + offer = (const struct vmbus_chanmsg_choffer *)msg->msg_data; + + chan = vmbus_chan_alloc(sc); + if (chan == NULL) { + device_printf(sc->vmbus_dev, "allocate chan%u failed\n", + offer->chm_chanid); + return; + } + + chan->ch_id = offer->chm_chanid; + chan->ch_subidx = offer->chm_subidx; + chan->ch_guid_type = offer->chm_chtype; + chan->ch_guid_inst = offer->chm_chinst; + + /* Batch reading is on by default */ + chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD; + + chan->ch_monprm->mp_connid = VMBUS_CONNID_EVENT; + if (sc->vmbus_version != VMBUS_VERSION_WS2008) + chan->ch_monprm->mp_connid = offer->chm_connid; + + if (offer->chm_flags1 & VMBUS_CHOFFER_FLAG1_HASMNF) { + /* + * Setup MNF stuffs. + */ + chan->ch_flags |= VMBUS_CHAN_FLAG_HASMNF; + chan->ch_montrig_idx = offer->chm_montrig / VMBUS_MONTRIG_LEN; + if (chan->ch_montrig_idx >= VMBUS_MONTRIGS_MAX) + panic("invalid monitor trigger %u", offer->chm_montrig); + chan->ch_montrig_mask = + 1 << (offer->chm_montrig % VMBUS_MONTRIG_LEN); + } + + /* Select default cpu for this channel. */ + vmbus_chan_cpu_default(chan); + + error = vmbus_chan_add(chan); + if (error) { + device_printf(sc->vmbus_dev, "add chan%u failed: %d\n", + chan->ch_id, error); + vmbus_chan_free(chan); + return; + } + + if (VMBUS_CHAN_ISPRIMARY(chan)) { + /* + * Add device for this primary channel. + * + * NOTE: + * Error is ignored here; don't have much to do if error + * really happens. + */ + hv_vmbus_child_device_register(chan); + } +} + +/* + * XXX pretty broken; need rework. + */ +static void +vmbus_chan_msgproc_chrescind(struct vmbus_softc *sc, + const struct vmbus_message *msg) +{ + const struct vmbus_chanmsg_chrescind *note; + struct hv_vmbus_channel *chan; + + note = (const struct vmbus_chanmsg_chrescind *)msg->msg_data; + if (note->chm_chanid > VMBUS_CHAN_MAX) { + device_printf(sc->vmbus_dev, "invalid rescinded chan%u\n", + note->chm_chanid); + return; + } + + if (bootverbose) { + device_printf(sc->vmbus_dev, "chan%u rescinded\n", + note->chm_chanid); + } + + chan = sc->vmbus_chmap[note->chm_chanid]; + if (chan == NULL) + return; + sc->vmbus_chmap[note->chm_chanid] = NULL; + + taskqueue_enqueue(taskqueue_thread, &chan->ch_detach_task); +} + +static void +vmbus_chan_detach_task(void *xchan, int pending __unused) +{ + struct hv_vmbus_channel *chan = xchan; + + if (VMBUS_CHAN_ISPRIMARY(chan)) { + /* Only primary channel owns the device */ + hv_vmbus_child_device_unregister(chan); + /* NOTE: DO NOT free primary channel for now */ + } else { + struct vmbus_softc *sc = chan->vmbus_sc; + struct hv_vmbus_channel *pri_chan = chan->ch_prichan; + struct vmbus_chanmsg_chfree *req; + struct vmbus_msghc *mh; + int error; + + mh = vmbus_msghc_get(sc, sizeof(*req)); + if (mh == NULL) { + device_printf(sc->vmbus_dev, + "can not get msg hypercall for chfree(chan%u)\n", + chan->ch_id); + goto remove; + } + + req = vmbus_msghc_dataptr(mh); + req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE; + req->chm_chanid = chan->ch_id; + + error = vmbus_msghc_exec_noresult(mh); + vmbus_msghc_put(sc, mh); + + if (error) { + device_printf(sc->vmbus_dev, + "chfree(chan%u) failed: %d", + chan->ch_id, error); + /* NOTE: Move on! */ + } else { + if (bootverbose) { + device_printf(sc->vmbus_dev, "chan%u freed\n", + chan->ch_id); + } + } +remove: + mtx_lock(&pri_chan->ch_subchan_lock); + TAILQ_REMOVE(&pri_chan->ch_subchans, chan, ch_sublink); + KASSERT(pri_chan->ch_subchan_cnt > 0, + ("invalid subchan_cnt %d", pri_chan->ch_subchan_cnt)); + pri_chan->ch_subchan_cnt--; + mtx_unlock(&pri_chan->ch_subchan_lock); + wakeup(pri_chan); + + vmbus_chan_free(chan); + } +} + +/* + * Detach all devices and destroy the corresponding primary channels. + */ +void +vmbus_chan_destroy_all(struct vmbus_softc *sc) +{ + struct hv_vmbus_channel *chan; + + mtx_lock(&sc->vmbus_prichan_lock); + while ((chan = TAILQ_FIRST(&sc->vmbus_prichans)) != NULL) { + KASSERT(VMBUS_CHAN_ISPRIMARY(chan), ("not primary channel")); + TAILQ_REMOVE(&sc->vmbus_prichans, chan, ch_prilink); + mtx_unlock(&sc->vmbus_prichan_lock); + + hv_vmbus_child_device_unregister(chan); + vmbus_chan_free(chan); + + mtx_lock(&sc->vmbus_prichan_lock); + } + bzero(sc->vmbus_chmap, + sizeof(struct hv_vmbus_channel *) * VMBUS_CHAN_MAX); + 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 + */ +struct hv_vmbus_channel * +vmbus_select_outgoing_channel(struct hv_vmbus_channel *primary) +{ + 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; + } + + if (smp_pro_id >= MAXCPU) { + return outgoing_channel; + } + + 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; + } + + if (new_channel->target_vcpu == cur_vcpu){ + return new_channel; + } + + old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ? + (outgoing_channel->target_vcpu - cur_vcpu) : + (cur_vcpu - outgoing_channel->target_vcpu)); + + new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ? + (new_channel->target_vcpu - cur_vcpu) : + (cur_vcpu - new_channel->target_vcpu)); + + if (old_cpu_distance < new_cpu_distance) { + continue; + } + + outgoing_channel = new_channel; + } + + return(outgoing_channel); +} + +struct hv_vmbus_channel ** +vmbus_get_subchan(struct hv_vmbus_channel *pri_chan, int subchan_cnt) +{ + struct hv_vmbus_channel **ret, *chan; + int i; + + ret = malloc(subchan_cnt * sizeof(struct hv_vmbus_channel *), M_TEMP, + M_WAITOK); + + mtx_lock(&pri_chan->ch_subchan_lock); + + while (pri_chan->ch_subchan_cnt < subchan_cnt) + mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "subch", 0); + + i = 0; + TAILQ_FOREACH(chan, &pri_chan->ch_subchans, ch_sublink) { + /* TODO: refcnt chan */ + ret[i] = chan; + + ++i; + if (i == subchan_cnt) + break; + } + KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d", + pri_chan->ch_subchan_cnt, subchan_cnt)); + + mtx_unlock(&pri_chan->ch_subchan_lock); + + return ret; +} + +void +vmbus_rel_subchan(struct hv_vmbus_channel **subchan, int subchan_cnt __unused) +{ + + free(subchan, M_TEMP); +} + +void +vmbus_drain_subchan(struct hv_vmbus_channel *pri_chan) +{ + mtx_lock(&pri_chan->ch_subchan_lock); + while (pri_chan->ch_subchan_cnt > 0) + mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "dsubch", 0); + mtx_unlock(&pri_chan->ch_subchan_lock); +} + +void +vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg) +{ + vmbus_chanmsg_proc_t msg_proc; + uint32_t msg_type; + + msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type; + KASSERT(msg_type < VMBUS_CHANMSG_TYPE_MAX, + ("invalid message type %u", msg_type)); + + msg_proc = vmbus_chan_msgprocs[msg_type]; + if (msg_proc != NULL) + msg_proc(sc, msg); +} Modified: head/sys/modules/hyperv/vmbus/Makefile ============================================================================== --- head/sys/modules/hyperv/vmbus/Makefile Fri Jul 15 02:29:10 2016 (r302863) +++ head/sys/modules/hyperv/vmbus/Makefile Fri Jul 15 04:42:08 2016 (r302864) @@ -5,7 +5,6 @@ KMOD= hv_vmbus SRCS= hv_channel.c \ - hv_channel_mgmt.c \ hv_ring_buffer.c \ hyperv.c \ hyperv_busdma.c \
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201607150442.u6F4g8F6060118>