Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/hyperv/vmbus/vmbus_chan.c
Show First 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | static void vmbus_chan_close_internal( | ||||
struct vmbus_channel *); | struct vmbus_channel *); | ||||
static int vmbus_chan_sysctl_mnf(SYSCTL_HANDLER_ARGS); | static int vmbus_chan_sysctl_mnf(SYSCTL_HANDLER_ARGS); | ||||
static void vmbus_chan_sysctl_create( | static void vmbus_chan_sysctl_create( | ||||
struct vmbus_channel *); | struct vmbus_channel *); | ||||
static struct vmbus_channel *vmbus_chan_alloc(struct vmbus_softc *); | static struct vmbus_channel *vmbus_chan_alloc(struct vmbus_softc *); | ||||
static void vmbus_chan_free(struct vmbus_channel *); | static void vmbus_chan_free(struct vmbus_channel *); | ||||
static int vmbus_chan_add(struct vmbus_channel *); | static int vmbus_chan_add(struct vmbus_channel *); | ||||
static void vmbus_chan_cpu_default(struct vmbus_channel *); | static void vmbus_chan_cpu_default(struct vmbus_channel *); | ||||
static int vmbus_chan_release(struct vmbus_channel *); | |||||
static void vmbus_chan_set_chmap(struct vmbus_channel *); | |||||
static void vmbus_chan_clear_chmap(struct vmbus_channel *); | |||||
static void vmbus_chan_ins_prilist(struct vmbus_softc *, | |||||
struct vmbus_channel *); | |||||
static void vmbus_chan_rem_prilist(struct vmbus_softc *, | |||||
struct vmbus_channel *); | |||||
static void vmbus_chan_ins_list(struct vmbus_softc *, | |||||
struct vmbus_channel *); | |||||
static void vmbus_chan_rem_list(struct vmbus_softc *, | |||||
struct vmbus_channel *); | |||||
static void vmbus_chan_ins_sublist(struct vmbus_channel *, | |||||
struct vmbus_channel *); | |||||
static void vmbus_chan_rem_sublist(struct vmbus_channel *, | |||||
struct vmbus_channel *); | |||||
static void vmbus_chan_task(void *, int); | static void vmbus_chan_task(void *, int); | ||||
static void vmbus_chan_task_nobatch(void *, int); | static void vmbus_chan_task_nobatch(void *, int); | ||||
static void vmbus_chan_detach_task(void *, int); | static void vmbus_chan_clrchmap_task(void *, int); | ||||
static void vmbus_prichan_attach_task(void *, int); | |||||
static void vmbus_subchan_attach_task(void *, int); | |||||
static void vmbus_prichan_detach_task(void *, int); | |||||
static void vmbus_subchan_detach_task(void *, int); | |||||
static void vmbus_chan_msgproc_choffer(struct vmbus_softc *, | static void vmbus_chan_msgproc_choffer(struct vmbus_softc *, | ||||
const struct vmbus_message *); | const struct vmbus_message *); | ||||
static void vmbus_chan_msgproc_chrescind( | static void vmbus_chan_msgproc_chrescind( | ||||
struct vmbus_softc *, | struct vmbus_softc *, | ||||
const struct vmbus_message *); | const struct vmbus_message *); | ||||
/* | /* | ||||
Show All 17 Lines | |||||
{ | { | ||||
atomic_set_long(chan->ch_evtflag, chan->ch_evtflag_mask); | atomic_set_long(chan->ch_evtflag, chan->ch_evtflag_mask); | ||||
if (chan->ch_txflags & VMBUS_CHAN_TXF_HASMNF) | if (chan->ch_txflags & VMBUS_CHAN_TXF_HASMNF) | ||||
atomic_set_int(chan->ch_montrig, chan->ch_montrig_mask); | atomic_set_int(chan->ch_montrig, chan->ch_montrig_mask); | ||||
else | else | ||||
hypercall_signal_event(chan->ch_monprm_dma.hv_paddr); | hypercall_signal_event(chan->ch_monprm_dma.hv_paddr); | ||||
} | } | ||||
static void | |||||
vmbus_chan_ins_prilist(struct vmbus_softc *sc, struct vmbus_channel *chan) | |||||
{ | |||||
mtx_assert(&sc->vmbus_prichan_lock, MA_OWNED); | |||||
if (atomic_testandset_int(&chan->ch_stflags, | |||||
VMBUS_CHAN_ST_ONPRIL_SHIFT)) | |||||
panic("channel is already on the prilist"); | |||||
TAILQ_INSERT_TAIL(&sc->vmbus_prichans, chan, ch_prilink); | |||||
} | |||||
static void | |||||
vmbus_chan_rem_prilist(struct vmbus_softc *sc, struct vmbus_channel *chan) | |||||
{ | |||||
mtx_assert(&sc->vmbus_prichan_lock, MA_OWNED); | |||||
if (atomic_testandclear_int(&chan->ch_stflags, | |||||
VMBUS_CHAN_ST_ONPRIL_SHIFT) == 0) | |||||
panic("channel is not on the prilist"); | |||||
TAILQ_REMOVE(&sc->vmbus_prichans, chan, ch_prilink); | |||||
} | |||||
static void | |||||
vmbus_chan_ins_sublist(struct vmbus_channel *prichan, | |||||
struct vmbus_channel *chan) | |||||
{ | |||||
mtx_assert(&prichan->ch_subchan_lock, MA_OWNED); | |||||
if (atomic_testandset_int(&chan->ch_stflags, | |||||
VMBUS_CHAN_ST_ONSUBL_SHIFT)) | |||||
panic("channel is already on the sublist"); | |||||
TAILQ_INSERT_TAIL(&prichan->ch_subchans, chan, ch_sublink); | |||||
/* Bump sub-channel count. */ | |||||
prichan->ch_subchan_cnt++; | |||||
} | |||||
static void | |||||
vmbus_chan_rem_sublist(struct vmbus_channel *prichan, | |||||
struct vmbus_channel *chan) | |||||
{ | |||||
mtx_assert(&prichan->ch_subchan_lock, MA_OWNED); | |||||
KASSERT(prichan->ch_subchan_cnt > 0, | |||||
("invalid subchan_cnt %d", prichan->ch_subchan_cnt)); | |||||
prichan->ch_subchan_cnt--; | |||||
if (atomic_testandclear_int(&chan->ch_stflags, | |||||
VMBUS_CHAN_ST_ONSUBL_SHIFT) == 0) | |||||
panic("channel is not on the sublist"); | |||||
TAILQ_REMOVE(&prichan->ch_subchans, chan, ch_sublink); | |||||
} | |||||
static void | |||||
vmbus_chan_ins_list(struct vmbus_softc *sc, struct vmbus_channel *chan) | |||||
{ | |||||
mtx_assert(&sc->vmbus_chan_lock, MA_OWNED); | |||||
if (atomic_testandset_int(&chan->ch_stflags, | |||||
VMBUS_CHAN_ST_ONLIST_SHIFT)) | |||||
panic("channel is already on the list"); | |||||
TAILQ_INSERT_TAIL(&sc->vmbus_chans, chan, ch_link); | |||||
} | |||||
static void | |||||
vmbus_chan_rem_list(struct vmbus_softc *sc, struct vmbus_channel *chan) | |||||
{ | |||||
mtx_assert(&sc->vmbus_chan_lock, MA_OWNED); | |||||
if (atomic_testandclear_int(&chan->ch_stflags, | |||||
VMBUS_CHAN_ST_ONLIST_SHIFT) == 0) | |||||
panic("channel is not on the list"); | |||||
TAILQ_REMOVE(&sc->vmbus_chans, chan, ch_link); | |||||
} | |||||
static int | static int | ||||
vmbus_chan_sysctl_mnf(SYSCTL_HANDLER_ARGS) | vmbus_chan_sysctl_mnf(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct vmbus_channel *chan = arg1; | struct vmbus_channel *chan = arg1; | ||||
int mnf = 0; | int mnf = 0; | ||||
if (chan->ch_txflags & VMBUS_CHAN_TXF_HASMNF) | if (chan->ch_txflags & VMBUS_CHAN_TXF_HASMNF) | ||||
mnf = 1; | mnf = 1; | ||||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
struct vmbus_softc *sc = chan->ch_vmbus; | struct vmbus_softc *sc = chan->ch_vmbus; | ||||
const struct vmbus_chanmsg_chopen_resp *resp; | const struct vmbus_chanmsg_chopen_resp *resp; | ||||
const struct vmbus_message *msg; | const struct vmbus_message *msg; | ||||
struct vmbus_chanmsg_chopen *req; | struct vmbus_chanmsg_chopen *req; | ||||
struct vmbus_msghc *mh; | struct vmbus_msghc *mh; | ||||
uint32_t status; | uint32_t status; | ||||
int error, txbr_size, rxbr_size; | int error, txbr_size, rxbr_size; | ||||
task_fn_t *task_fn; | |||||
uint8_t *br; | uint8_t *br; | ||||
if (udlen > VMBUS_CHANMSG_CHOPEN_UDATA_SIZE) { | if (udlen > VMBUS_CHANMSG_CHOPEN_UDATA_SIZE) { | ||||
device_printf(sc->vmbus_dev, | device_printf(sc->vmbus_dev, | ||||
"invalid udata len %d for chan%u\n", udlen, chan->ch_id); | "invalid udata len %d for chan%u\n", udlen, chan->ch_id); | ||||
return EINVAL; | return EINVAL; | ||||
} | } | ||||
Show All 18 Lines | vmbus_chan_open_br(struct vmbus_channel *chan, const struct vmbus_chan_br *cbr, | ||||
chan->ch_cb = cb; | chan->ch_cb = cb; | ||||
chan->ch_cbarg = cbarg; | chan->ch_cbarg = cbarg; | ||||
vmbus_chan_update_evtflagcnt(sc, chan); | vmbus_chan_update_evtflagcnt(sc, chan); | ||||
chan->ch_tq = VMBUS_PCPU_GET(chan->ch_vmbus, event_tq, chan->ch_cpuid); | chan->ch_tq = VMBUS_PCPU_GET(chan->ch_vmbus, event_tq, chan->ch_cpuid); | ||||
if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD) | if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD) | ||||
TASK_INIT(&chan->ch_task, 0, vmbus_chan_task, chan); | task_fn = vmbus_chan_task; | ||||
else | else | ||||
TASK_INIT(&chan->ch_task, 0, vmbus_chan_task_nobatch, chan); | task_fn = vmbus_chan_task_nobatch; | ||||
TASK_INIT(&chan->ch_task, 0, task_fn, chan); | |||||
/* TX bufring comes first */ | /* TX bufring comes first */ | ||||
vmbus_txbr_setup(&chan->ch_txbr, br, txbr_size); | vmbus_txbr_setup(&chan->ch_txbr, br, txbr_size); | ||||
/* RX bufring immediately follows TX bufring */ | /* RX bufring immediately follows TX bufring */ | ||||
vmbus_rxbr_setup(&chan->ch_rxbr, br + txbr_size, rxbr_size); | vmbus_rxbr_setup(&chan->ch_rxbr, br + txbr_size, rxbr_size); | ||||
/* Create sysctl tree for this channel */ | /* Create sysctl tree for this channel */ | ||||
vmbus_chan_sysctl_create(chan); | vmbus_chan_sysctl_create(chan); | ||||
/* | /* | ||||
* Connect the bufrings, both RX and TX, to this channel. | * Connect the bufrings, both RX and TX, to this channel. | ||||
*/ | */ | ||||
error = vmbus_chan_gpadl_connect(chan, cbr->cbr_paddr, | error = vmbus_chan_gpadl_connect(chan, cbr->cbr_paddr, | ||||
txbr_size + rxbr_size, &chan->ch_bufring_gpadl); | txbr_size + rxbr_size, &chan->ch_bufring_gpadl); | ||||
if (error) { | if (error) { | ||||
device_printf(sc->vmbus_dev, | device_printf(sc->vmbus_dev, | ||||
"failed to connect bufring GPADL to chan%u\n", chan->ch_id); | "failed to connect bufring GPADL to chan%u\n", chan->ch_id); | ||||
goto failed; | goto failed; | ||||
} | } | ||||
/* | /* | ||||
* Install this channel, before it is opened, but after everything | |||||
* else has been setup. | |||||
*/ | |||||
vmbus_chan_set_chmap(chan); | |||||
/* | |||||
* Open channel w/ the bufring GPADL on the target CPU. | * Open channel w/ the bufring GPADL on the target CPU. | ||||
*/ | */ | ||||
mh = vmbus_msghc_get(sc, sizeof(*req)); | mh = vmbus_msghc_get(sc, sizeof(*req)); | ||||
if (mh == NULL) { | if (mh == NULL) { | ||||
device_printf(sc->vmbus_dev, | device_printf(sc->vmbus_dev, | ||||
"can not get msg hypercall for chopen(chan%u)\n", | "can not get msg hypercall for chopen(chan%u)\n", | ||||
chan->ch_id); | chan->ch_id); | ||||
error = ENXIO; | error = ENXIO; | ||||
Show All 32 Lines | if (status == 0) { | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
device_printf(sc->vmbus_dev, "failed to open chan%u\n", chan->ch_id); | device_printf(sc->vmbus_dev, "failed to open chan%u\n", chan->ch_id); | ||||
error = ENXIO; | error = ENXIO; | ||||
failed: | failed: | ||||
vmbus_chan_clear_chmap(chan); | |||||
if (chan->ch_bufring_gpadl) { | if (chan->ch_bufring_gpadl) { | ||||
vmbus_chan_gpadl_disconnect(chan, chan->ch_bufring_gpadl); | vmbus_chan_gpadl_disconnect(chan, chan->ch_bufring_gpadl); | ||||
chan->ch_bufring_gpadl = 0; | chan->ch_bufring_gpadl = 0; | ||||
} | } | ||||
atomic_clear_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED); | atomic_clear_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED); | ||||
return error; | return error; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 160 Lines • ▼ Show 20 Lines | vmbus_chan_gpadl_disconnect(struct vmbus_channel *chan, uint32_t gpadl) | ||||
vmbus_msghc_wait_result(sc, mh); | vmbus_msghc_wait_result(sc, mh); | ||||
/* Discard result; no useful information */ | /* Discard result; no useful information */ | ||||
vmbus_msghc_put(sc, mh); | vmbus_msghc_put(sc, mh); | ||||
return 0; | return 0; | ||||
} | } | ||||
static void | static void | ||||
vmbus_chan_clrchmap_task(void *xchan, int pending __unused) | |||||
{ | |||||
struct vmbus_channel *chan = xchan; | |||||
critical_enter(); | |||||
chan->ch_vmbus->vmbus_chmap[chan->ch_id] = NULL; | |||||
critical_exit(); | |||||
} | |||||
static void | |||||
vmbus_chan_clear_chmap(struct vmbus_channel *chan) | |||||
{ | |||||
struct task chmap_task; | |||||
TASK_INIT(&chmap_task, 0, vmbus_chan_clrchmap_task, chan); | |||||
taskqueue_enqueue(chan->ch_tq, &chmap_task); | |||||
taskqueue_drain(chan->ch_tq, &chmap_task); | |||||
} | |||||
static void | |||||
vmbus_chan_set_chmap(struct vmbus_channel *chan) | |||||
{ | |||||
__compiler_membar(); | |||||
chan->ch_vmbus->vmbus_chmap[chan->ch_id] = chan; | |||||
} | |||||
static void | |||||
vmbus_chan_close_internal(struct vmbus_channel *chan) | vmbus_chan_close_internal(struct vmbus_channel *chan) | ||||
{ | { | ||||
struct vmbus_softc *sc = chan->ch_vmbus; | struct vmbus_softc *sc = chan->ch_vmbus; | ||||
struct vmbus_msghc *mh; | struct vmbus_msghc *mh; | ||||
struct vmbus_chanmsg_chclose *req; | struct vmbus_chanmsg_chclose *req; | ||||
struct taskqueue *tq = chan->ch_tq; | |||||
int error; | int error; | ||||
/* TODO: stringent check */ | /* TODO: stringent check */ | ||||
atomic_clear_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED); | atomic_clear_int(&chan->ch_stflags, VMBUS_CHAN_ST_OPENED); | ||||
/* | /* | ||||
* Free this channel's sysctl tree attached to its device's | * Free this channel's sysctl tree attached to its device's | ||||
* sysctl tree. | * sysctl tree. | ||||
*/ | */ | ||||
sysctl_ctx_free(&chan->ch_sysctl_ctx); | sysctl_ctx_free(&chan->ch_sysctl_ctx); | ||||
/* | /* | ||||
* Set ch_tq to NULL to avoid more requests be scheduled. | * NOTE: | ||||
* XXX pretty broken; need rework. | * Order is critical. This channel _must_ be uninstalled first, | ||||
* else the channel task may be enqueued by the IDT after it has | |||||
* been drained. | |||||
*/ | */ | ||||
vmbus_chan_clear_chmap(chan); | |||||
taskqueue_drain(chan->ch_tq, &chan->ch_task); | |||||
chan->ch_tq = NULL; | chan->ch_tq = NULL; | ||||
taskqueue_drain(tq, &chan->ch_task); | |||||
chan->ch_cb = NULL; | |||||
/* | /* | ||||
* Close this channel. | * Close this channel. | ||||
*/ | */ | ||||
mh = vmbus_msghc_get(sc, sizeof(*req)); | mh = vmbus_msghc_get(sc, sizeof(*req)); | ||||
if (mh == NULL) { | if (mh == NULL) { | ||||
device_printf(sc->vmbus_dev, | device_printf(sc->vmbus_dev, | ||||
"can not get msg hypercall for chclose(chan%u)\n", | "can not get msg hypercall for chclose(chan%u)\n", | ||||
▲ Show 20 Lines • Show All 327 Lines • ▼ Show 20 Lines | for (f = 0; f < flag_cnt; ++f) { | ||||
while ((chid_ofs = ffsl(flags)) != 0) { | while ((chid_ofs = ffsl(flags)) != 0) { | ||||
struct vmbus_channel *chan; | struct vmbus_channel *chan; | ||||
--chid_ofs; /* NOTE: ffsl is 1-based */ | --chid_ofs; /* NOTE: ffsl is 1-based */ | ||||
flags &= ~(1UL << chid_ofs); | flags &= ~(1UL << chid_ofs); | ||||
chan = sc->vmbus_chmap[chid_base + chid_ofs]; | chan = sc->vmbus_chmap[chid_base + chid_ofs]; | ||||
if (__predict_false(chan == NULL)) { | |||||
/* if channel is closed or closing */ | /* Channel is closed. */ | ||||
if (chan == NULL || chan->ch_tq == NULL) | |||||
continue; | continue; | ||||
} | |||||
__compiler_membar(); | |||||
if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD) | if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD) | ||||
vmbus_rxbr_intr_mask(&chan->ch_rxbr); | vmbus_rxbr_intr_mask(&chan->ch_rxbr); | ||||
taskqueue_enqueue(chan->ch_tq, &chan->ch_task); | taskqueue_enqueue(chan->ch_tq, &chan->ch_task); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | if (chan->ch_monprm == NULL) { | ||||
device_printf(sc->vmbus_dev, "monprm alloc failed\n"); | device_printf(sc->vmbus_dev, "monprm alloc failed\n"); | ||||
free(chan, M_DEVBUF); | free(chan, M_DEVBUF); | ||||
return NULL; | return NULL; | ||||
} | } | ||||
chan->ch_vmbus = sc; | chan->ch_vmbus = sc; | ||||
mtx_init(&chan->ch_subchan_lock, "vmbus subchan", NULL, MTX_DEF); | mtx_init(&chan->ch_subchan_lock, "vmbus subchan", NULL, MTX_DEF); | ||||
TAILQ_INIT(&chan->ch_subchans); | TAILQ_INIT(&chan->ch_subchans); | ||||
TASK_INIT(&chan->ch_detach_task, 0, vmbus_chan_detach_task, chan); | |||||
vmbus_rxbr_init(&chan->ch_rxbr); | vmbus_rxbr_init(&chan->ch_rxbr); | ||||
vmbus_txbr_init(&chan->ch_txbr); | vmbus_txbr_init(&chan->ch_txbr); | ||||
return chan; | return chan; | ||||
} | } | ||||
static void | static void | ||||
vmbus_chan_free(struct vmbus_channel *chan) | vmbus_chan_free(struct vmbus_channel *chan) | ||||
{ | { | ||||
/* TODO: assert sub-channel list is empty */ | |||||
/* TODO: asset no longer on the primary channel's sub-channel list */ | KASSERT(TAILQ_EMPTY(&chan->ch_subchans) && chan->ch_subchan_cnt == 0, | ||||
/* TODO: asset no longer on the vmbus channel list */ | ("still owns sub-channels")); | ||||
KASSERT((chan->ch_stflags & | |||||
(VMBUS_CHAN_ST_OPENED | | |||||
VMBUS_CHAN_ST_ONPRIL | | |||||
VMBUS_CHAN_ST_ONSUBL | | |||||
VMBUS_CHAN_ST_ONLIST)) == 0, ("free busy channel")); | |||||
hyperv_dmamem_free(&chan->ch_monprm_dma, chan->ch_monprm); | hyperv_dmamem_free(&chan->ch_monprm_dma, chan->ch_monprm); | ||||
mtx_destroy(&chan->ch_subchan_lock); | mtx_destroy(&chan->ch_subchan_lock); | ||||
vmbus_rxbr_deinit(&chan->ch_rxbr); | vmbus_rxbr_deinit(&chan->ch_rxbr); | ||||
vmbus_txbr_deinit(&chan->ch_txbr); | vmbus_txbr_deinit(&chan->ch_txbr); | ||||
free(chan, M_DEVBUF); | free(chan, M_DEVBUF); | ||||
} | } | ||||
static int | static int | ||||
Show All 10 Lines | if (newchan->ch_id == 0) { | ||||
*/ | */ | ||||
device_printf(sc->vmbus_dev, "got chan0 offer, discard\n"); | device_printf(sc->vmbus_dev, "got chan0 offer, discard\n"); | ||||
return EINVAL; | return EINVAL; | ||||
} else if (newchan->ch_id >= VMBUS_CHAN_MAX) { | } else if (newchan->ch_id >= VMBUS_CHAN_MAX) { | ||||
device_printf(sc->vmbus_dev, "invalid chan%u offer\n", | device_printf(sc->vmbus_dev, "invalid chan%u offer\n", | ||||
newchan->ch_id); | newchan->ch_id); | ||||
return EINVAL; | return EINVAL; | ||||
} | } | ||||
sc->vmbus_chmap[newchan->ch_id] = newchan; | |||||
if (bootverbose) { | if (bootverbose) { | ||||
device_printf(sc->vmbus_dev, "chan%u subidx%u offer\n", | device_printf(sc->vmbus_dev, "chan%u subidx%u offer\n", | ||||
newchan->ch_id, newchan->ch_subidx); | newchan->ch_id, newchan->ch_subidx); | ||||
} | } | ||||
mtx_lock(&sc->vmbus_prichan_lock); | mtx_lock(&sc->vmbus_prichan_lock); | ||||
TAILQ_FOREACH(prichan, &sc->vmbus_prichans, ch_prilink) { | TAILQ_FOREACH(prichan, &sc->vmbus_prichans, ch_prilink) { | ||||
/* | /* | ||||
* Sub-channel will have the same type GUID and instance | * Sub-channel will have the same type GUID and instance | ||||
* GUID as its primary channel. | * GUID as its primary channel. | ||||
*/ | */ | ||||
if (memcmp(&prichan->ch_guid_type, &newchan->ch_guid_type, | if (memcmp(&prichan->ch_guid_type, &newchan->ch_guid_type, | ||||
sizeof(struct hyperv_guid)) == 0 && | sizeof(struct hyperv_guid)) == 0 && | ||||
memcmp(&prichan->ch_guid_inst, &newchan->ch_guid_inst, | memcmp(&prichan->ch_guid_inst, &newchan->ch_guid_inst, | ||||
sizeof(struct hyperv_guid)) == 0) | sizeof(struct hyperv_guid)) == 0) | ||||
break; | break; | ||||
} | } | ||||
if (VMBUS_CHAN_ISPRIMARY(newchan)) { | if (VMBUS_CHAN_ISPRIMARY(newchan)) { | ||||
if (prichan == NULL) { | if (prichan == NULL) { | ||||
/* Install the new primary channel */ | /* Install the new primary channel */ | ||||
TAILQ_INSERT_TAIL(&sc->vmbus_prichans, newchan, | vmbus_chan_ins_prilist(sc, newchan); | ||||
ch_prilink); | |||||
mtx_unlock(&sc->vmbus_prichan_lock); | mtx_unlock(&sc->vmbus_prichan_lock); | ||||
return 0; | goto done; | ||||
} else { | } else { | ||||
mtx_unlock(&sc->vmbus_prichan_lock); | mtx_unlock(&sc->vmbus_prichan_lock); | ||||
device_printf(sc->vmbus_dev, "duplicated primary " | device_printf(sc->vmbus_dev, "duplicated primary " | ||||
"chan%u\n", newchan->ch_id); | "chan%u\n", newchan->ch_id); | ||||
return EINVAL; | return EINVAL; | ||||
} | } | ||||
} else { /* Sub-channel */ | } else { /* Sub-channel */ | ||||
if (prichan == NULL) { | if (prichan == NULL) { | ||||
Show All 17 Lines | vmbus_chan_add(struct vmbus_channel *newchan) | ||||
KASSERT(!VMBUS_CHAN_ISPRIMARY(newchan), | KASSERT(!VMBUS_CHAN_ISPRIMARY(newchan), | ||||
("new channel is not sub-channel")); | ("new channel is not sub-channel")); | ||||
KASSERT(prichan != NULL, ("no primary channel")); | KASSERT(prichan != NULL, ("no primary channel")); | ||||
newchan->ch_prichan = prichan; | newchan->ch_prichan = prichan; | ||||
newchan->ch_dev = prichan->ch_dev; | newchan->ch_dev = prichan->ch_dev; | ||||
mtx_lock(&prichan->ch_subchan_lock); | mtx_lock(&prichan->ch_subchan_lock); | ||||
TAILQ_INSERT_TAIL(&prichan->ch_subchans, newchan, ch_sublink); | vmbus_chan_ins_sublist(prichan, newchan); | ||||
mtx_unlock(&prichan->ch_subchan_lock); | |||||
/* | /* | ||||
* Bump up sub-channel count and notify anyone that is | * Notify anyone that is interested in this sub-channel, | ||||
* interested in this sub-channel, after this sub-channel | * after this sub-channel is setup. | ||||
* is setup. | |||||
*/ | */ | ||||
prichan->ch_subchan_cnt++; | |||||
mtx_unlock(&prichan->ch_subchan_lock); | |||||
wakeup(prichan); | wakeup(prichan); | ||||
done: | |||||
/* | |||||
* Hook this channel up for later rescind. | |||||
*/ | |||||
mtx_lock(&sc->vmbus_chan_lock); | |||||
vmbus_chan_ins_list(sc, newchan); | |||||
mtx_unlock(&sc->vmbus_chan_lock); | |||||
return 0; | return 0; | ||||
} | } | ||||
void | void | ||||
vmbus_chan_cpu_set(struct vmbus_channel *chan, int cpu) | vmbus_chan_cpu_set(struct vmbus_channel *chan, int cpu) | ||||
{ | { | ||||
KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu)); | KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu)); | ||||
Show All 34 Lines | |||||
} | } | ||||
static void | static void | ||||
vmbus_chan_msgproc_choffer(struct vmbus_softc *sc, | vmbus_chan_msgproc_choffer(struct vmbus_softc *sc, | ||||
const struct vmbus_message *msg) | const struct vmbus_message *msg) | ||||
{ | { | ||||
const struct vmbus_chanmsg_choffer *offer; | const struct vmbus_chanmsg_choffer *offer; | ||||
struct vmbus_channel *chan; | struct vmbus_channel *chan; | ||||
task_fn_t *detach_fn, *attach_fn; | |||||
int error; | int error; | ||||
offer = (const struct vmbus_chanmsg_choffer *)msg->msg_data; | offer = (const struct vmbus_chanmsg_choffer *)msg->msg_data; | ||||
chan = vmbus_chan_alloc(sc); | chan = vmbus_chan_alloc(sc); | ||||
if (chan == NULL) { | if (chan == NULL) { | ||||
device_printf(sc->vmbus_dev, "allocate chan%u failed\n", | device_printf(sc->vmbus_dev, "allocate chan%u failed\n", | ||||
offer->chm_chanid); | offer->chm_chanid); | ||||
Show All 32 Lines | vmbus_chan_msgproc_choffer(struct vmbus_softc *sc, | ||||
/* | /* | ||||
* Setup event flag. | * Setup event flag. | ||||
*/ | */ | ||||
chan->ch_evtflag = | chan->ch_evtflag = | ||||
&sc->vmbus_tx_evtflags[chan->ch_id >> VMBUS_EVTFLAG_SHIFT]; | &sc->vmbus_tx_evtflags[chan->ch_id >> VMBUS_EVTFLAG_SHIFT]; | ||||
chan->ch_evtflag_mask = 1UL << (chan->ch_id & VMBUS_EVTFLAG_MASK); | chan->ch_evtflag_mask = 1UL << (chan->ch_id & VMBUS_EVTFLAG_MASK); | ||||
/* | |||||
* Setup attach and detach tasks. | |||||
*/ | |||||
if (VMBUS_CHAN_ISPRIMARY(chan)) { | |||||
chan->ch_mgmt_tq = sc->vmbus_devtq; | |||||
attach_fn = vmbus_prichan_attach_task; | |||||
detach_fn = vmbus_prichan_detach_task; | |||||
} else { | |||||
chan->ch_mgmt_tq = sc->vmbus_subchtq; | |||||
attach_fn = vmbus_subchan_attach_task; | |||||
detach_fn = vmbus_subchan_detach_task; | |||||
} | |||||
TASK_INIT(&chan->ch_attach_task, 0, attach_fn, chan); | |||||
TASK_INIT(&chan->ch_detach_task, 0, detach_fn, chan); | |||||
/* Select default cpu for this channel. */ | /* Select default cpu for this channel. */ | ||||
vmbus_chan_cpu_default(chan); | vmbus_chan_cpu_default(chan); | ||||
error = vmbus_chan_add(chan); | error = vmbus_chan_add(chan); | ||||
if (error) { | if (error) { | ||||
device_printf(sc->vmbus_dev, "add chan%u failed: %d\n", | device_printf(sc->vmbus_dev, "add chan%u failed: %d\n", | ||||
chan->ch_id, error); | chan->ch_id, error); | ||||
vmbus_chan_free(chan); | vmbus_chan_free(chan); | ||||
return; | return; | ||||
} | } | ||||
taskqueue_enqueue(chan->ch_mgmt_tq, &chan->ch_attach_task); | |||||
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. | |||||
*/ | |||||
vmbus_add_child(chan); | |||||
} | } | ||||
} | |||||
/* | |||||
* XXX pretty broken; need rework. | |||||
*/ | |||||
static void | static void | ||||
vmbus_chan_msgproc_chrescind(struct vmbus_softc *sc, | vmbus_chan_msgproc_chrescind(struct vmbus_softc *sc, | ||||
const struct vmbus_message *msg) | const struct vmbus_message *msg) | ||||
{ | { | ||||
const struct vmbus_chanmsg_chrescind *note; | const struct vmbus_chanmsg_chrescind *note; | ||||
struct vmbus_channel *chan; | struct vmbus_channel *chan; | ||||
note = (const struct vmbus_chanmsg_chrescind *)msg->msg_data; | note = (const struct vmbus_chanmsg_chrescind *)msg->msg_data; | ||||
if (note->chm_chanid > VMBUS_CHAN_MAX) { | if (note->chm_chanid > VMBUS_CHAN_MAX) { | ||||
device_printf(sc->vmbus_dev, "invalid rescinded chan%u\n", | device_printf(sc->vmbus_dev, "invalid rescinded chan%u\n", | ||||
note->chm_chanid); | note->chm_chanid); | ||||
return; | return; | ||||
} | } | ||||
if (bootverbose) { | if (bootverbose) { | ||||
device_printf(sc->vmbus_dev, "chan%u rescinded\n", | device_printf(sc->vmbus_dev, "chan%u rescinded\n", | ||||
note->chm_chanid); | note->chm_chanid); | ||||
} | } | ||||
chan = sc->vmbus_chmap[note->chm_chanid]; | /* | ||||
if (chan == NULL) | * Find and remove the target channel from the channel list. | ||||
*/ | |||||
mtx_lock(&sc->vmbus_chan_lock); | |||||
TAILQ_FOREACH(chan, &sc->vmbus_chans, ch_link) { | |||||
if (chan->ch_id == note->chm_chanid) | |||||
break; | |||||
} | |||||
if (chan == NULL) { | |||||
mtx_unlock(&sc->vmbus_chan_lock); | |||||
device_printf(sc->vmbus_dev, "chan%u is not offered\n", | |||||
note->chm_chanid); | |||||
return; | return; | ||||
sc->vmbus_chmap[note->chm_chanid] = NULL; | } | ||||
vmbus_chan_rem_list(sc, chan); | |||||
mtx_unlock(&sc->vmbus_chan_lock); | |||||
taskqueue_enqueue(taskqueue_thread, &chan->ch_detach_task); | if (VMBUS_CHAN_ISPRIMARY(chan)) { | ||||
/* | |||||
* The target channel is a primary channel; remove the | |||||
* target channel from the primary channel list now, | |||||
* instead of later, so that it will not be found by | |||||
* other sub-channel offers, which are processed in | |||||
* this thread. | |||||
*/ | |||||
mtx_lock(&sc->vmbus_prichan_lock); | |||||
vmbus_chan_rem_prilist(sc, chan); | |||||
mtx_unlock(&sc->vmbus_prichan_lock); | |||||
} | } | ||||
static void | /* Detach the target channel. */ | ||||
vmbus_chan_detach_task(void *xchan, int pending __unused) | taskqueue_enqueue(chan->ch_mgmt_tq, &chan->ch_detach_task); | ||||
{ | } | ||||
struct vmbus_channel *chan = xchan; | |||||
if (VMBUS_CHAN_ISPRIMARY(chan)) { | static int | ||||
/* Only primary channel owns the device */ | vmbus_chan_release(struct vmbus_channel *chan) | ||||
vmbus_delete_child(chan); | { | ||||
/* NOTE: DO NOT free primary channel for now */ | |||||
} else { | |||||
struct vmbus_softc *sc = chan->ch_vmbus; | struct vmbus_softc *sc = chan->ch_vmbus; | ||||
struct vmbus_channel *pri_chan = chan->ch_prichan; | |||||
struct vmbus_chanmsg_chfree *req; | struct vmbus_chanmsg_chfree *req; | ||||
struct vmbus_msghc *mh; | struct vmbus_msghc *mh; | ||||
int error; | int error; | ||||
mh = vmbus_msghc_get(sc, sizeof(*req)); | mh = vmbus_msghc_get(sc, sizeof(*req)); | ||||
if (mh == NULL) { | if (mh == NULL) { | ||||
device_printf(sc->vmbus_dev, | device_printf(sc->vmbus_dev, "can not get msg hypercall for " | ||||
"can not get msg hypercall for chfree(chan%u)\n", | "chfree(chan%u)\n", chan->ch_id); | ||||
chan->ch_id); | return (ENXIO); | ||||
goto remove; | |||||
} | } | ||||
req = vmbus_msghc_dataptr(mh); | req = vmbus_msghc_dataptr(mh); | ||||
req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE; | req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE; | ||||
req->chm_chanid = chan->ch_id; | req->chm_chanid = chan->ch_id; | ||||
error = vmbus_msghc_exec_noresult(mh); | error = vmbus_msghc_exec_noresult(mh); | ||||
vmbus_msghc_put(sc, mh); | vmbus_msghc_put(sc, mh); | ||||
if (error) { | if (error) { | ||||
device_printf(sc->vmbus_dev, | device_printf(sc->vmbus_dev, "chfree(chan%u) failed: %d", | ||||
"chfree(chan%u) failed: %d", | |||||
chan->ch_id, error); | chan->ch_id, error); | ||||
/* NOTE: Move on! */ | |||||
} else { | } else { | ||||
if (bootverbose) { | if (bootverbose) { | ||||
device_printf(sc->vmbus_dev, "chan%u freed\n", | device_printf(sc->vmbus_dev, "chan%u freed\n", | ||||
chan->ch_id); | chan->ch_id); | ||||
} | } | ||||
} | } | ||||
remove: | return (error); | ||||
} | |||||
static void | |||||
vmbus_prichan_detach_task(void *xchan, int pending __unused) | |||||
{ | |||||
struct vmbus_channel *chan = xchan; | |||||
KASSERT(VMBUS_CHAN_ISPRIMARY(chan), | |||||
("chan%u is not primary channel", chan->ch_id)); | |||||
/* Delete and detach the device associated with this channel. */ | |||||
vmbus_delete_child(chan); | |||||
/* Release this channel (back to vmbus). */ | |||||
vmbus_chan_release(chan); | |||||
/* Free this channel's resource. */ | |||||
vmbus_chan_free(chan); | |||||
} | |||||
static void | |||||
vmbus_subchan_detach_task(void *xchan, int pending __unused) | |||||
{ | |||||
struct vmbus_channel *chan = xchan; | |||||
struct vmbus_channel *pri_chan = chan->ch_prichan; | |||||
KASSERT(!VMBUS_CHAN_ISPRIMARY(chan), | |||||
("chan%u is primary channel", chan->ch_id)); | |||||
/* Release this channel (back to vmbus). */ | |||||
vmbus_chan_release(chan); | |||||
/* Unlink from its primary channel's sub-channel list. */ | |||||
mtx_lock(&pri_chan->ch_subchan_lock); | mtx_lock(&pri_chan->ch_subchan_lock); | ||||
TAILQ_REMOVE(&pri_chan->ch_subchans, chan, ch_sublink); | vmbus_chan_rem_sublist(pri_chan, chan); | ||||
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); | mtx_unlock(&pri_chan->ch_subchan_lock); | ||||
/* Notify anyone that is waiting for this sub-channel to vanish. */ | |||||
wakeup(pri_chan); | wakeup(pri_chan); | ||||
/* Free this channel's resource. */ | |||||
vmbus_chan_free(chan); | vmbus_chan_free(chan); | ||||
} | } | ||||
} | |||||
static void | |||||
vmbus_prichan_attach_task(void *xchan, int pending __unused) | |||||
{ | |||||
/* | /* | ||||
* Detach all devices and destroy the corresponding primary channels. | * Add device for this primary channel. | ||||
*/ | */ | ||||
vmbus_add_child(xchan); | |||||
} | |||||
static void | |||||
vmbus_subchan_attach_task(void *xchan __unused, int pending __unused) | |||||
{ | |||||
/* Nothing */ | |||||
} | |||||
void | void | ||||
vmbus_chan_destroy_all(struct vmbus_softc *sc) | vmbus_chan_destroy_all(struct vmbus_softc *sc) | ||||
{ | { | ||||
/* | |||||
* Detach all devices and destroy the corresponding primary | |||||
* channels. | |||||
*/ | |||||
for (;;) { | |||||
struct vmbus_channel *chan; | struct vmbus_channel *chan; | ||||
mtx_lock(&sc->vmbus_chan_lock); | |||||
TAILQ_FOREACH(chan, &sc->vmbus_chans, ch_link) { | |||||
if (VMBUS_CHAN_ISPRIMARY(chan)) | |||||
break; | |||||
} | |||||
if (chan == NULL) { | |||||
/* No more primary channels; done. */ | |||||
mtx_unlock(&sc->vmbus_chan_lock); | |||||
break; | |||||
} | |||||
vmbus_chan_rem_list(sc, chan); | |||||
mtx_unlock(&sc->vmbus_chan_lock); | |||||
mtx_lock(&sc->vmbus_prichan_lock); | mtx_lock(&sc->vmbus_prichan_lock); | ||||
while ((chan = TAILQ_FIRST(&sc->vmbus_prichans)) != NULL) { | vmbus_chan_rem_prilist(sc, chan); | ||||
KASSERT(VMBUS_CHAN_ISPRIMARY(chan), ("not primary channel")); | |||||
TAILQ_REMOVE(&sc->vmbus_prichans, chan, ch_prilink); | |||||
mtx_unlock(&sc->vmbus_prichan_lock); | mtx_unlock(&sc->vmbus_prichan_lock); | ||||
vmbus_delete_child(chan); | taskqueue_enqueue(chan->ch_mgmt_tq, &chan->ch_detach_task); | ||||
vmbus_chan_free(chan); | |||||
mtx_lock(&sc->vmbus_prichan_lock); | |||||
} | } | ||||
bzero(sc->vmbus_chmap, | |||||
sizeof(struct vmbus_channel *) * VMBUS_CHAN_MAX); | |||||
mtx_unlock(&sc->vmbus_prichan_lock); | |||||
} | } | ||||
/* | /* | ||||
* The channel whose vcpu binding is closest to the currect vcpu will | * The channel whose vcpu binding is closest to the currect vcpu will | ||||
* be selected. | * be selected. | ||||
* If no multi-channel, always select primary channel. | * If no multi-channel, always select primary channel. | ||||
*/ | */ | ||||
struct vmbus_channel * | struct vmbus_channel * | ||||
▲ Show 20 Lines • Show All 165 Lines • Show Last 20 Lines |