svn commit: r307249 - in stable/10/sys/dev/hyperv: include vmbus
Sepherosa Ziehau
sephe at FreeBSD.org
Fri Oct 14 02:42:10 UTC 2016
Author: sephe
Date: Fri Oct 14 02:42:08 2016
New Revision: 307249
URL: https://svnweb.freebsd.org/changeset/base/307249
Log:
MFC 306360,306387,306389
306360
hyperv/vmbus: Add dynamic device add and remove support
Sponsored by: Microsoft
Differential Revision: https://reviews.freebsd.org/D8008
306387
hyperv/vmbus: Add functions to test RX/TX bufring emptiness
Sponsored by: Microsoft
Differential Revision: https://reviews.freebsd.org/D8044
306389
hyperv/vmbus: Add function to drain channel interrupt task.
Sponsored by: Microsoft
Differential Revision: https://reviews.freebsd.org/D8045
Modified:
stable/10/sys/dev/hyperv/include/vmbus.h
stable/10/sys/dev/hyperv/vmbus/vmbus.c
stable/10/sys/dev/hyperv/vmbus/vmbus_brvar.h
stable/10/sys/dev/hyperv/vmbus/vmbus_chan.c
stable/10/sys/dev/hyperv/vmbus/vmbus_chanvar.h
stable/10/sys/dev/hyperv/vmbus/vmbus_var.h
Directory Properties:
stable/10/ (props changed)
Modified: stable/10/sys/dev/hyperv/include/vmbus.h
==============================================================================
--- stable/10/sys/dev/hyperv/include/vmbus.h Fri Oct 14 02:36:51 2016 (r307248)
+++ stable/10/sys/dev/hyperv/include/vmbus.h Fri Oct 14 02:42:08 2016 (r307249)
@@ -134,6 +134,7 @@ int vmbus_chan_open_br(struct vmbus_cha
const struct vmbus_chan_br *cbr, const void *udata,
int udlen, vmbus_chan_callback_t cb, void *cbarg);
void vmbus_chan_close(struct vmbus_channel *chan);
+void vmbus_chan_intr_drain(struct vmbus_channel *chan);
int vmbus_chan_gpadl_connect(struct vmbus_channel *chan,
bus_addr_t paddr, int size, uint32_t *gpadl);
@@ -174,5 +175,7 @@ const struct hyperv_guid *
vmbus_chan_guid_inst(const struct vmbus_channel *chan);
int vmbus_chan_prplist_nelem(int br_size, int prpcnt_max,
int dlen_max);
+bool vmbus_chan_rx_empty(const struct vmbus_channel *chan);
+bool vmbus_chan_tx_empty(const struct vmbus_channel *chan);
#endif /* !_VMBUS_H_ */
Modified: stable/10/sys/dev/hyperv/vmbus/vmbus.c
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/vmbus.c Fri Oct 14 02:36:51 2016 (r307248)
+++ stable/10/sys/dev/hyperv/vmbus/vmbus.c Fri Oct 14 02:42:08 2016 (r307249)
@@ -85,9 +85,7 @@ static int vmbus_connect(struct vmbus_
static int vmbus_req_channels(struct vmbus_softc *sc);
static void vmbus_disconnect(struct vmbus_softc *);
static int vmbus_scan(struct vmbus_softc *);
-static void vmbus_scan_wait(struct vmbus_softc *);
-static void vmbus_scan_newchan(struct vmbus_softc *);
-static void vmbus_scan_newdev(struct vmbus_softc *);
+static void vmbus_scan_teardown(struct vmbus_softc *);
static void vmbus_scan_done(struct vmbus_softc *,
const struct vmbus_message *);
static void vmbus_chanmsg_handle(struct vmbus_softc *,
@@ -395,50 +393,22 @@ vmbus_req_channels(struct vmbus_softc *s
}
static void
-vmbus_scan_newchan(struct vmbus_softc *sc)
+vmbus_scan_done_task(void *xsc, int pending __unused)
{
- mtx_lock(&sc->vmbus_scan_lock);
- if ((sc->vmbus_scan_chcnt & VMBUS_SCAN_CHCNT_DONE) == 0)
- sc->vmbus_scan_chcnt++;
- mtx_unlock(&sc->vmbus_scan_lock);
+ struct vmbus_softc *sc = xsc;
+
+ mtx_lock(&Giant);
+ sc->vmbus_scandone = true;
+ mtx_unlock(&Giant);
+ wakeup(&sc->vmbus_scandone);
}
static void
vmbus_scan_done(struct vmbus_softc *sc,
const struct vmbus_message *msg __unused)
{
- mtx_lock(&sc->vmbus_scan_lock);
- sc->vmbus_scan_chcnt |= VMBUS_SCAN_CHCNT_DONE;
- mtx_unlock(&sc->vmbus_scan_lock);
- wakeup(&sc->vmbus_scan_chcnt);
-}
-
-static void
-vmbus_scan_newdev(struct vmbus_softc *sc)
-{
- mtx_lock(&sc->vmbus_scan_lock);
- sc->vmbus_scan_devcnt++;
- mtx_unlock(&sc->vmbus_scan_lock);
- wakeup(&sc->vmbus_scan_devcnt);
-}
-
-static void
-vmbus_scan_wait(struct vmbus_softc *sc)
-{
- uint32_t chancnt;
-
- mtx_lock(&sc->vmbus_scan_lock);
- while ((sc->vmbus_scan_chcnt & VMBUS_SCAN_CHCNT_DONE) == 0) {
- mtx_sleep(&sc->vmbus_scan_chcnt, &sc->vmbus_scan_lock, 0,
- "waitch", 0);
- }
- chancnt = sc->vmbus_scan_chcnt & ~VMBUS_SCAN_CHCNT_DONE;
- while (sc->vmbus_scan_devcnt != chancnt) {
- mtx_sleep(&sc->vmbus_scan_devcnt, &sc->vmbus_scan_lock, 0,
- "waitdev", 0);
- }
- mtx_unlock(&sc->vmbus_scan_lock);
+ taskqueue_enqueue(sc->vmbus_devtq, &sc->vmbus_scandone_task);
}
static int
@@ -447,31 +417,71 @@ vmbus_scan(struct vmbus_softc *sc)
int error;
/*
+ * Identify, probe and attach for non-channel devices.
+ */
+ bus_generic_probe(sc->vmbus_dev);
+ bus_generic_attach(sc->vmbus_dev);
+
+ /*
+ * This taskqueue serializes vmbus devices' attach and detach
+ * for channel offer and rescind messages.
+ */
+ sc->vmbus_devtq = taskqueue_create("vmbus dev", M_WAITOK,
+ taskqueue_thread_enqueue, &sc->vmbus_devtq);
+ taskqueue_start_threads(&sc->vmbus_devtq, 1, PI_NET, "vmbusdev");
+ TASK_INIT(&sc->vmbus_scandone_task, 0, vmbus_scan_done_task, sc);
+
+ /*
+ * This taskqueue handles sub-channel detach, so that vmbus
+ * device's detach running in vmbus_devtq can drain its sub-
+ * channels.
+ */
+ sc->vmbus_subchtq = taskqueue_create("vmbus subch", M_WAITOK,
+ taskqueue_thread_enqueue, &sc->vmbus_subchtq);
+ taskqueue_start_threads(&sc->vmbus_subchtq, 1, PI_NET, "vmbussch");
+
+ /*
* Start vmbus scanning.
*/
error = vmbus_req_channels(sc);
if (error) {
device_printf(sc->vmbus_dev, "channel request failed: %d\n",
error);
- return error;
+ return (error);
}
/*
- * Wait for all devices are added to vmbus.
+ * Wait for all vmbus devices from the initial channel offers to be
+ * attached.
*/
- vmbus_scan_wait(sc);
-
- /*
- * Identify, probe and attach.
- */
- bus_generic_probe(sc->vmbus_dev);
- bus_generic_attach(sc->vmbus_dev);
+ GIANT_REQUIRED;
+ while (!sc->vmbus_scandone)
+ mtx_sleep(&sc->vmbus_scandone, &Giant, 0, "vmbusdev", 0);
if (bootverbose) {
device_printf(sc->vmbus_dev, "device scan, probe and attach "
"done\n");
}
- return 0;
+ return (0);
+}
+
+static void
+vmbus_scan_teardown(struct vmbus_softc *sc)
+{
+
+ GIANT_REQUIRED;
+ if (sc->vmbus_devtq != NULL) {
+ mtx_unlock(&Giant);
+ taskqueue_free(sc->vmbus_devtq);
+ mtx_lock(&Giant);
+ sc->vmbus_devtq = NULL;
+ }
+ if (sc->vmbus_subchtq != NULL) {
+ mtx_unlock(&Giant);
+ taskqueue_free(sc->vmbus_subchtq);
+ mtx_lock(&Giant);
+ sc->vmbus_subchtq = NULL;
+ }
}
static void
@@ -1000,45 +1010,35 @@ vmbus_add_child(struct vmbus_channel *ch
{
struct vmbus_softc *sc = chan->ch_vmbus;
device_t parent = sc->vmbus_dev;
- int error = 0;
- /* New channel has been offered */
- vmbus_scan_newchan(sc);
+ mtx_lock(&Giant);
chan->ch_dev = device_add_child(parent, NULL, -1);
if (chan->ch_dev == NULL) {
+ mtx_unlock(&Giant);
device_printf(parent, "device_add_child for chan%u failed\n",
chan->ch_id);
- error = ENXIO;
- goto done;
+ return (ENXIO);
}
device_set_ivars(chan->ch_dev, chan);
+ device_probe_and_attach(chan->ch_dev);
-done:
- /* New device has been/should be added to vmbus. */
- vmbus_scan_newdev(sc);
- return error;
+ mtx_unlock(&Giant);
+ return (0);
}
int
vmbus_delete_child(struct vmbus_channel *chan)
{
- int error;
-
- if (chan->ch_dev == NULL) {
- /* Failed to add a device. */
- return 0;
- }
+ int error = 0;
- /*
- * XXXKYS: Ensure that this is the opposite of
- * device_add_child()
- */
mtx_lock(&Giant);
- error = device_delete_child(chan->ch_vmbus->vmbus_dev, chan->ch_dev);
+ if (chan->ch_dev != NULL) {
+ error = device_delete_child(chan->ch_vmbus->vmbus_dev,
+ chan->ch_dev);
+ }
mtx_unlock(&Giant);
-
- return error;
+ return (error);
}
static int
@@ -1110,10 +1110,11 @@ vmbus_doattach(struct vmbus_softc *sc)
return (0);
sc->vmbus_flags |= VMBUS_FLAG_ATTACHED;
- mtx_init(&sc->vmbus_scan_lock, "vmbus scan", NULL, MTX_DEF);
sc->vmbus_gpadl = VMBUS_GPADL_START;
mtx_init(&sc->vmbus_prichan_lock, "vmbus prichan", NULL, MTX_DEF);
TAILQ_INIT(&sc->vmbus_prichans);
+ mtx_init(&sc->vmbus_chan_lock, "vmbus channel", NULL, MTX_DEF);
+ TAILQ_INIT(&sc->vmbus_chans);
sc->vmbus_chmap = malloc(
sizeof(struct vmbus_channel *) * VMBUS_CHAN_MAX, M_DEVBUF,
M_WAITOK | M_ZERO);
@@ -1177,6 +1178,7 @@ vmbus_doattach(struct vmbus_softc *sc)
return (ret);
cleanup:
+ vmbus_scan_teardown(sc);
vmbus_intr_teardown(sc);
vmbus_dma_free(sc);
if (sc->vmbus_xc != NULL) {
@@ -1184,8 +1186,8 @@ cleanup:
sc->vmbus_xc = NULL;
}
free(sc->vmbus_chmap, M_DEVBUF);
- mtx_destroy(&sc->vmbus_scan_lock);
mtx_destroy(&sc->vmbus_prichan_lock);
+ mtx_destroy(&sc->vmbus_chan_lock);
return (ret);
}
@@ -1225,8 +1227,11 @@ vmbus_detach(device_t dev)
{
struct vmbus_softc *sc = device_get_softc(dev);
+ bus_generic_detach(dev);
vmbus_chan_destroy_all(sc);
+ vmbus_scan_teardown(sc);
+
vmbus_disconnect(sc);
if (sc->vmbus_flags & VMBUS_FLAG_SYNIC) {
@@ -1243,8 +1248,8 @@ vmbus_detach(device_t dev)
}
free(sc->vmbus_chmap, M_DEVBUF);
- mtx_destroy(&sc->vmbus_scan_lock);
mtx_destroy(&sc->vmbus_prichan_lock);
+ mtx_destroy(&sc->vmbus_chan_lock);
return (0);
}
Modified: stable/10/sys/dev/hyperv/vmbus/vmbus_brvar.h
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/vmbus_brvar.h Fri Oct 14 02:36:51 2016 (r307248)
+++ stable/10/sys/dev/hyperv/vmbus/vmbus_brvar.h Fri Oct 14 02:42:08 2016 (r307249)
@@ -83,6 +83,20 @@ vmbus_txbr_maxpktsz(const struct vmbus_t
return (tbr->txbr_dsize - sizeof(uint64_t) - 1);
}
+static __inline bool
+vmbus_txbr_empty(const struct vmbus_txbr *tbr)
+{
+
+ return (tbr->txbr_windex == tbr->txbr_rindex ? true : false);
+}
+
+static __inline bool
+vmbus_rxbr_empty(const struct vmbus_rxbr *rbr)
+{
+
+ return (rbr->rxbr_windex == rbr->rxbr_rindex ? true : false);
+}
+
static __inline int
vmbus_br_nelem(int br_size, int elem_size)
{
Modified: stable/10/sys/dev/hyperv/vmbus/vmbus_chan.c
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/vmbus_chan.c Fri Oct 14 02:36:51 2016 (r307248)
+++ stable/10/sys/dev/hyperv/vmbus/vmbus_chan.c Fri Oct 14 02:42:08 2016 (r307249)
@@ -59,10 +59,30 @@ static struct vmbus_channel *vmbus_chan_
static void vmbus_chan_free(struct vmbus_channel *);
static int vmbus_chan_add(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_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 *,
const struct vmbus_message *);
@@ -96,6 +116,83 @@ vmbus_chan_signal_tx(const struct vmbus_
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
vmbus_chan_sysctl_mnf(SYSCTL_HANDLER_ARGS)
{
@@ -235,6 +332,7 @@ vmbus_chan_open_br(struct vmbus_channel
struct vmbus_msghc *mh;
uint32_t status;
int error, txbr_size, rxbr_size;
+ task_fn_t *task_fn;
uint8_t *br;
if (udlen > VMBUS_CHANMSG_CHOPEN_UDATA_SIZE) {
@@ -269,9 +367,10 @@ vmbus_chan_open_br(struct vmbus_channel
chan->ch_tq = VMBUS_PCPU_GET(chan->ch_vmbus, event_tq, chan->ch_cpuid);
if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD)
- TASK_INIT(&chan->ch_task, 0, vmbus_chan_task, chan);
+ task_fn = vmbus_chan_task;
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 */
vmbus_txbr_setup(&chan->ch_txbr, br, txbr_size);
@@ -293,6 +392,12 @@ vmbus_chan_open_br(struct vmbus_channel
}
/*
+ * 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.
*/
mh = vmbus_msghc_get(sc, sizeof(*req));
@@ -341,6 +446,7 @@ vmbus_chan_open_br(struct vmbus_channel
error = ENXIO;
failed:
+ vmbus_chan_clear_chmap(chan);
if (chan->ch_bufring_gpadl) {
vmbus_chan_gpadl_disconnect(chan, chan->ch_bufring_gpadl);
chan->ch_bufring_gpadl = 0;
@@ -517,12 +623,38 @@ vmbus_chan_gpadl_disconnect(struct vmbus
}
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)
{
struct vmbus_softc *sc = chan->ch_vmbus;
struct vmbus_msghc *mh;
struct vmbus_chanmsg_chclose *req;
- struct taskqueue *tq = chan->ch_tq;
int error;
/* TODO: stringent check */
@@ -535,12 +667,14 @@ vmbus_chan_close_internal(struct vmbus_c
sysctl_ctx_free(&chan->ch_sysctl_ctx);
/*
- * Set ch_tq to NULL to avoid more requests be scheduled.
- * XXX pretty broken; need rework.
+ * NOTE:
+ * 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;
- taskqueue_drain(tq, &chan->ch_task);
- chan->ch_cb = NULL;
/*
* Close this channel.
@@ -622,6 +756,13 @@ vmbus_chan_close(struct vmbus_channel *c
vmbus_chan_close_internal(chan);
}
+void
+vmbus_chan_intr_drain(struct vmbus_channel *chan)
+{
+
+ taskqueue_drain(chan->ch_tq, &chan->ch_task);
+}
+
int
vmbus_chan_send(struct vmbus_channel *chan, uint16_t type, uint16_t flags,
void *data, int dlen, uint64_t xactid)
@@ -884,10 +1025,11 @@ vmbus_event_flags_proc(struct vmbus_soft
flags &= ~(1UL << chid_ofs);
chan = sc->vmbus_chmap[chid_base + chid_ofs];
-
- /* if channel is closed or closing */
- if (chan == NULL || chan->ch_tq == NULL)
+ if (__predict_false(chan == NULL)) {
+ /* Channel is closed. */
continue;
+ }
+ __compiler_membar();
if (chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD)
vmbus_rxbr_intr_mask(&chan->ch_rxbr);
@@ -968,7 +1110,6 @@ vmbus_chan_alloc(struct vmbus_softc *sc)
chan->ch_vmbus = 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);
vmbus_rxbr_init(&chan->ch_rxbr);
vmbus_txbr_init(&chan->ch_txbr);
@@ -978,9 +1119,14 @@ vmbus_chan_alloc(struct vmbus_softc *sc)
static void
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 */
- /* TODO: asset no longer on the vmbus channel list */
+
+ KASSERT(TAILQ_EMPTY(&chan->ch_subchans) && chan->ch_subchan_cnt == 0,
+ ("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);
mtx_destroy(&chan->ch_subchan_lock);
vmbus_rxbr_deinit(&chan->ch_rxbr);
@@ -1007,7 +1153,6 @@ vmbus_chan_add(struct vmbus_channel *new
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",
@@ -1029,10 +1174,9 @@ vmbus_chan_add(struct vmbus_channel *new
if (VMBUS_CHAN_ISPRIMARY(newchan)) {
if (prichan == NULL) {
/* Install the new primary channel */
- TAILQ_INSERT_TAIL(&sc->vmbus_prichans, newchan,
- ch_prilink);
+ vmbus_chan_ins_prilist(sc, newchan);
mtx_unlock(&sc->vmbus_prichan_lock);
- return 0;
+ goto done;
} else {
mtx_unlock(&sc->vmbus_prichan_lock);
device_printf(sc->vmbus_dev, "duplicated primary "
@@ -1066,16 +1210,20 @@ vmbus_chan_add(struct vmbus_channel *new
newchan->ch_dev = prichan->ch_dev;
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
- * interested in this sub-channel, after this sub-channel
- * is setup.
+ * 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);
-
+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;
}
@@ -1126,6 +1274,7 @@ vmbus_chan_msgproc_choffer(struct vmbus_
{
const struct vmbus_chanmsg_choffer *offer;
struct vmbus_channel *chan;
+ task_fn_t *detach_fn, *attach_fn;
int error;
offer = (const struct vmbus_chanmsg_choffer *)msg->msg_data;
@@ -1174,6 +1323,21 @@ vmbus_chan_msgproc_choffer(struct vmbus_
&sc->vmbus_tx_evtflags[chan->ch_id >> VMBUS_EVTFLAG_SHIFT];
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. */
vmbus_chan_cpu_default(chan);
@@ -1184,22 +1348,9 @@ vmbus_chan_msgproc_choffer(struct vmbus_
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.
- */
- vmbus_add_child(chan);
- }
+ taskqueue_enqueue(chan->ch_mgmt_tq, &chan->ch_attach_task);
}
-/*
- * XXX pretty broken; need rework.
- */
static void
vmbus_chan_msgproc_chrescind(struct vmbus_softc *sc,
const struct vmbus_message *msg)
@@ -1219,91 +1370,162 @@ vmbus_chan_msgproc_chrescind(struct vmbu
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;
- sc->vmbus_chmap[note->chm_chanid] = NULL;
+ }
+ vmbus_chan_rem_list(sc, chan);
+ mtx_unlock(&sc->vmbus_chan_lock);
+
+ 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);
+ }
- taskqueue_enqueue(taskqueue_thread, &chan->ch_detach_task);
+ /* Detach the target channel. */
+ taskqueue_enqueue(chan->ch_mgmt_tq, &chan->ch_detach_task);
}
-static void
-vmbus_chan_detach_task(void *xchan, int pending __unused)
+static int
+vmbus_chan_release(struct vmbus_channel *chan)
{
- struct vmbus_channel *chan = xchan;
+ struct vmbus_softc *sc = chan->ch_vmbus;
+ struct vmbus_chanmsg_chfree *req;
+ struct vmbus_msghc *mh;
+ int error;
- if (VMBUS_CHAN_ISPRIMARY(chan)) {
- /* Only primary channel owns the device */
- vmbus_delete_child(chan);
- /* NOTE: DO NOT free primary channel for now */
+ 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);
+ return (ENXIO);
+ }
+
+ 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);
} else {
- struct vmbus_softc *sc = chan->ch_vmbus;
- struct 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",
+ if (bootverbose) {
+ device_printf(sc->vmbus_dev, "chan%u freed\n",
chan->ch_id);
- goto remove;
}
+ }
+ return (error);
+}
- req = vmbus_msghc_dataptr(mh);
- req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE;
- req->chm_chanid = chan->ch_id;
+static void
+vmbus_prichan_detach_task(void *xchan, int pending __unused)
+{
+ struct vmbus_channel *chan = xchan;
- error = vmbus_msghc_exec_noresult(mh);
- vmbus_msghc_put(sc, mh);
+ KASSERT(VMBUS_CHAN_ISPRIMARY(chan),
+ ("chan%u is not primary channel", chan->ch_id));
- 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);
+ /* Delete and detach the device associated with this channel. */
+ vmbus_delete_child(chan);
- vmbus_chan_free(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);
+ vmbus_chan_rem_sublist(pri_chan, chan);
+ mtx_unlock(&pri_chan->ch_subchan_lock);
+ /* Notify anyone that is waiting for this sub-channel to vanish. */
+ wakeup(pri_chan);
+
+ /* Free this channel's resource. */
+ vmbus_chan_free(chan);
+}
+
+static void
+vmbus_prichan_attach_task(void *xchan, int pending __unused)
+{
+
+ /*
+ * Add device for this primary channel.
+ */
+ vmbus_add_child(xchan);
+}
+
+static void
+vmbus_subchan_attach_task(void *xchan __unused, int pending __unused)
+{
+
+ /* Nothing */
}
-/*
- * Detach all devices and destroy the corresponding primary channels.
- */
void
vmbus_chan_destroy_all(struct vmbus_softc *sc)
{
- struct 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);
+ /*
+ * Detach all devices and destroy the corresponding primary
+ * channels.
+ */
+ for (;;) {
+ struct vmbus_channel *chan;
- vmbus_delete_child(chan);
- vmbus_chan_free(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);
+ vmbus_chan_rem_prilist(sc, chan);
+ mtx_unlock(&sc->vmbus_prichan_lock);
+
+ taskqueue_enqueue(chan->ch_mgmt_tq, &chan->ch_detach_task);
}
- bzero(sc->vmbus_chmap,
- sizeof(struct vmbus_channel *) * VMBUS_CHAN_MAX);
- mtx_unlock(&sc->vmbus_prichan_lock);
}
/*
@@ -1477,3 +1699,17 @@ vmbus_chan_prplist_nelem(int br_size, in
return (vmbus_br_nelem(br_size, elem_size));
}
+
+bool
+vmbus_chan_tx_empty(const struct vmbus_channel *chan)
+{
+
+ return (vmbus_txbr_empty(&chan->ch_txbr));
+}
+
+bool
+vmbus_chan_rx_empty(const struct vmbus_channel *chan)
+{
+
+ return (vmbus_rxbr_empty(&chan->ch_rxbr));
+}
Modified: stable/10/sys/dev/hyperv/vmbus/vmbus_chanvar.h
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/vmbus_chanvar.h Fri Oct 14 02:36:51 2016 (r307248)
+++ stable/10/sys/dev/hyperv/vmbus/vmbus_chanvar.h Fri Oct 14 02:42:08 2016 (r307249)
@@ -124,8 +124,14 @@ struct vmbus_channel {
struct hyperv_dma ch_bufring_dma;
uint32_t ch_bufring_gpadl;
- struct task ch_detach_task;
+ struct task ch_attach_task; /* run in ch_mgmt_tq */
+ struct task ch_detach_task; /* run in ch_mgmt_tq */
+ struct taskqueue *ch_mgmt_tq;
+
+ /* If this is a primary channel */
TAILQ_ENTRY(vmbus_channel) ch_prilink; /* primary chan link */
+
+ TAILQ_ENTRY(vmbus_channel) ch_link; /* channel link */
uint32_t ch_subidx; /* subchan index */
volatile uint32_t ch_stflags; /* atomic-op */
/* VMBUS_CHAN_ST_ */
@@ -150,7 +156,13 @@ struct vmbus_channel {
#define VMBUS_CHAN_TXF_HASMNF 0x0001
#define VMBUS_CHAN_ST_OPENED_SHIFT 0
+#define VMBUS_CHAN_ST_ONPRIL_SHIFT 1
+#define VMBUS_CHAN_ST_ONSUBL_SHIFT 2
+#define VMBUS_CHAN_ST_ONLIST_SHIFT 3
#define VMBUS_CHAN_ST_OPENED (1 << VMBUS_CHAN_ST_OPENED_SHIFT)
+#define VMBUS_CHAN_ST_ONPRIL (1 << VMBUS_CHAN_ST_ONPRIL_SHIFT)
+#define VMBUS_CHAN_ST_ONSUBL (1 << VMBUS_CHAN_ST_ONSUBL_SHIFT)
+#define VMBUS_CHAN_ST_ONLIST (1 << VMBUS_CHAN_ST_ONLIST_SHIFT)
struct vmbus_softc;
struct vmbus_message;
Modified: stable/10/sys/dev/hyperv/vmbus/vmbus_var.h
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/vmbus_var.h Fri Oct 14 02:36:51 2016 (r307248)
+++ stable/10/sys/dev/hyperv/vmbus/vmbus_var.h Fri Oct 14 02:42:08 2016 (r307249)
@@ -107,14 +107,19 @@ struct vmbus_softc {
struct hyperv_dma vmbus_mnf1_dma;
struct hyperv_dma vmbus_mnf2_dma;
- struct mtx vmbus_scan_lock;
- uint32_t vmbus_scan_chcnt;
-#define VMBUS_SCAN_CHCNT_DONE 0x80000000
- uint32_t vmbus_scan_devcnt;
+ bool vmbus_scandone;
+ struct task vmbus_scandone_task;
+
+ struct taskqueue *vmbus_devtq; /* for dev attach/detach */
+ struct taskqueue *vmbus_subchtq; /* for sub-chan attach/detach */
/* Primary channels */
struct mtx vmbus_prichan_lock;
TAILQ_HEAD(, vmbus_channel) vmbus_prichans;
+
+ /* Complete channel list */
+ struct mtx vmbus_chan_lock;
+ TAILQ_HEAD(, vmbus_channel) vmbus_chans;
};
#define VMBUS_FLAG_ATTACHED 0x0001 /* vmbus was attached */
More information about the svn-src-stable
mailing list