Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/dev/hyperv vmbus(4): Do not call hyperv_dma_alloc() in i...
details: https://anonhg.NetBSD.org/src/rev/3f5529261d82
branches: trunk
changeset: 933531:3f5529261d82
user: nonaka <nonaka%NetBSD.org@localhost>
date: Tue May 26 16:08:55 2020 +0000
description:
vmbus(4): Do not call hyperv_dma_alloc() in interrupt context.
The channel offer and rescind process is performed on another context.
diffstat:
sys/dev/hyperv/vmbus.c | 328 +++++++++++++++++++++++++++++++++++++--------
sys/dev/hyperv/vmbusvar.h | 23 +++-
2 files changed, 289 insertions(+), 62 deletions(-)
diffs (truncated from 569 to 300 lines):
diff -r f8131e0d5806 -r 3f5529261d82 sys/dev/hyperv/vmbus.c
--- a/sys/dev/hyperv/vmbus.c Tue May 26 16:00:06 2020 +0000
+++ b/sys/dev/hyperv/vmbus.c Tue May 26 16:08:55 2020 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: vmbus.c,v 1.10 2020/05/26 16:00:06 nonaka Exp $ */
+/* $NetBSD: vmbus.c,v 1.11 2020/05/26 16:08:55 nonaka Exp $ */
/* $OpenBSD: hyperv.c,v 1.43 2017/06/27 13:56:15 mikeb Exp $ */
/*-
@@ -35,7 +35,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vmbus.c,v 1.10 2020/05/26 16:00:06 nonaka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vmbus.c,v 1.11 2020/05/26 16:08:55 nonaka Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -103,10 +103,14 @@
static void vmbus_channel_pause(struct vmbus_channel *);
static uint32_t vmbus_channel_unpause(struct vmbus_channel *);
static uint32_t vmbus_channel_ready(struct vmbus_channel *);
+static void vmbus_chevq_enqueue(struct vmbus_softc *, int, void *);
+static void vmbus_process_chevq(void *);
+static void vmbus_chevq_thread(void *);
static void vmbus_devq_enqueue(struct vmbus_softc *, int,
struct vmbus_channel *);
static void vmbus_process_devq(void *);
static void vmbus_devq_thread(void *);
+static void vmbus_subchannel_devq_thread(void *);
static struct vmbus_softc *vmbus_sc;
@@ -841,15 +845,33 @@
static void
vmbus_channel_offer(struct vmbus_softc *sc, struct vmbus_chanmsg_hdr *hdr)
{
+ struct vmbus_chanmsg_choffer *co;
- vmbus_process_offer(sc, (struct vmbus_chanmsg_choffer *)hdr);
+ co = kmem_intr_alloc(sizeof(*co), KM_NOSLEEP);
+ if (co == NULL) {
+ device_printf(sc->sc_dev,
+ "failed to allocate an offer object\n");
+ return;
+ }
+
+ memcpy(co, hdr, sizeof(*co));
+ vmbus_chevq_enqueue(sc, VMBUS_CHEV_TYPE_OFFER, co);
}
static void
vmbus_channel_rescind(struct vmbus_softc *sc, struct vmbus_chanmsg_hdr *hdr)
{
+ struct vmbus_chanmsg_chrescind *cr;
+
+ cr = kmem_intr_alloc(sizeof(*cr), KM_NOSLEEP);
+ if (cr == NULL) {
+ device_printf(sc->sc_dev,
+ "failed to allocate an rescind object\n");
+ return;
+ }
- vmbus_process_rescind(sc, (struct vmbus_chanmsg_chrescind *)hdr);
+ memcpy(cr, hdr, sizeof(*cr));
+ vmbus_chevq_enqueue(sc, VMBUS_CHEV_TYPE_RESCIND, cr);
}
static void
@@ -906,16 +928,48 @@
struct vmbus_chanmsg_hdr hdr;
struct vmbus_chanmsg_choffer rsp;
+ TAILQ_INIT(&sc->sc_prichans);
+ mutex_init(&sc->sc_prichan_lock, MUTEX_DEFAULT, IPL_NET);
TAILQ_INIT(&sc->sc_channels);
mutex_init(&sc->sc_channel_lock, MUTEX_DEFAULT, IPL_NET);
+ /*
+ * This queue serializes vmbus channel offer and rescind messages.
+ */
+ SIMPLEQ_INIT(&sc->sc_chevq);
+ mutex_init(&sc->sc_chevq_lock, MUTEX_DEFAULT, IPL_NET);
+ cv_init(&sc->sc_chevq_cv, "hvchevcv");
+ if (kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL,
+ vmbus_chevq_thread, sc, NULL, "hvchevq") != 0) {
+ DPRINTF("%s: failed to create prich chevq thread\n",
+ device_xname(sc->sc_dev));
+ return -1;
+ }
+
+ /*
+ * This queue serializes vmbus devices' attach and detach
+ * for channel offer and rescind messages.
+ */
SIMPLEQ_INIT(&sc->sc_devq);
mutex_init(&sc->sc_devq_lock, MUTEX_DEFAULT, IPL_NET);
cv_init(&sc->sc_devq_cv, "hvdevqcv");
-
if (kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL,
- vmbus_devq_thread, sc, NULL, "hvoffer") != 0) {
- DPRINTF("%s: failed to create offer thread\n",
+ vmbus_devq_thread, sc, NULL, "hvdevq") != 0) {
+ DPRINTF("%s: failed to create prich devq thread\n",
+ device_xname(sc->sc_dev));
+ return -1;
+ }
+
+ /*
+ * This queue handles sub-channel detach, so that vmbus
+ * device's detach running in sc_devq can drain its sub-channels.
+ */
+ SIMPLEQ_INIT(&sc->sc_subch_devq);
+ mutex_init(&sc->sc_subch_devq_lock, MUTEX_DEFAULT, IPL_NET);
+ cv_init(&sc->sc_subch_devq_cv, "hvsdvqcv");
+ if (kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL,
+ vmbus_subchannel_devq_thread, sc, NULL, "hvsdevq") != 0) {
+ DPRINTF("%s: failed to create subch devq thread\n",
device_xname(sc->sc_dev));
return -1;
}
@@ -932,6 +986,9 @@
while (!ISSET(sc->sc_flags, VMBUS_SCFLAG_OFFERS_DELIVERED))
tsleep(&sc->sc_devq, PRIBIO, "hvscan", 1);
+ mutex_enter(&sc->sc_chevq_lock);
+ vmbus_process_chevq(sc);
+ mutex_exit(&sc->sc_chevq_lock);
mutex_enter(&sc->sc_devq_lock);
vmbus_process_devq(sc);
mutex_exit(&sc->sc_devq_lock);
@@ -944,10 +1001,10 @@
{
struct vmbus_channel *ch;
- ch = kmem_intr_zalloc(sizeof(*ch), KM_NOSLEEP);
+ ch = kmem_zalloc(sizeof(*ch), KM_SLEEP);
ch->ch_monprm = hyperv_dma_alloc(sc->sc_dmat, &ch->ch_monprm_dma,
- sizeof(*ch->ch_monprm), 8, 0, 1, HYPERV_DMA_NOSLEEP);
+ sizeof(*ch->ch_monprm), 8, 0, 1, HYPERV_DMA_SLEEPOK);
if (ch->ch_monprm == NULL) {
device_printf(sc->sc_dev, "monprm alloc failed\n");
kmem_free(ch, sizeof(*ch));
@@ -1000,32 +1057,32 @@
return EINVAL;
}
- mutex_enter(&sc->sc_channel_lock);
- TAILQ_FOREACH(ch, &sc->sc_channels, ch_entry) {
+ mutex_enter(&sc->sc_prichan_lock);
+ TAILQ_FOREACH(ch, &sc->sc_prichans, ch_prientry) {
if (!memcmp(&ch->ch_type, &nch->ch_type, sizeof(ch->ch_type)) &&
!memcmp(&ch->ch_inst, &nch->ch_inst, sizeof(ch->ch_inst)))
break;
}
if (VMBUS_CHAN_ISPRIMARY(nch)) {
if (ch == NULL) {
- TAILQ_INSERT_TAIL(&sc->sc_channels, nch, ch_entry);
- mutex_exit(&sc->sc_channel_lock);
+ TAILQ_INSERT_TAIL(&sc->sc_prichans, nch, ch_prientry);
+ mutex_exit(&sc->sc_prichan_lock);
goto done;
} else {
- mutex_exit(&sc->sc_channel_lock);
+ mutex_exit(&sc->sc_prichan_lock);
device_printf(sc->sc_dev,
"duplicated primary channel%u\n", nch->ch_id);
return EINVAL;
}
} else {
if (ch == NULL) {
- mutex_exit(&sc->sc_channel_lock);
+ mutex_exit(&sc->sc_prichan_lock);
device_printf(sc->sc_dev, "no primary channel%u\n",
nch->ch_id);
return EINVAL;
}
}
- mutex_exit(&sc->sc_channel_lock);
+ mutex_exit(&sc->sc_prichan_lock);
KASSERT(!VMBUS_CHAN_ISPRIMARY(nch));
KASSERT(ch != NULL);
@@ -1043,6 +1100,10 @@
wakeup(ch);
done:
+ mutex_enter(&sc->sc_channel_lock);
+ TAILQ_INSERT_TAIL(&sc->sc_channels, nch, ch_entry);
+ mutex_exit(&sc->sc_channel_lock);
+
vmbus_channel_cpu_default(nch);
return 0;
@@ -1094,7 +1155,6 @@
return (ch->ch_flags & CHF_REVOKED) ? true : false;
}
-
static void
vmbus_process_offer(struct vmbus_softc *sc, struct vmbus_chanmsg_choffer *co)
{
@@ -1176,6 +1236,12 @@
TAILQ_REMOVE(&sc->sc_channels, ch, ch_entry);
mutex_exit(&sc->sc_channel_lock);
+ if (VMBUS_CHAN_ISPRIMARY(ch)) {
+ mutex_enter(&sc->sc_prichan_lock);
+ TAILQ_REMOVE(&sc->sc_prichans, ch, ch_prientry);
+ mutex_exit(&sc->sc_prichan_lock);
+ }
+
KASSERTMSG(!(ch->ch_flags & CHF_REVOKED),
"channel%u has already been revoked", ch->ch_id);
atomic_or_uint(&ch->ch_flags, CHF_REVOKED);
@@ -1206,20 +1272,32 @@
struct vmbus_channel **
vmbus_subchannel_get(struct vmbus_channel *prich, int cnt)
{
+ struct vmbus_softc *sc = prich->ch_sc;
struct vmbus_channel **ret, *ch;
- int i;
+ int i, s;
- KASSERT(cnt > 0);
+ KASSERTMSG(cnt > 0, "invalid sub-channel count %d", cnt);
- ret = kmem_alloc(sizeof(struct vmbus_channel *) * cnt,
- cold ? KM_NOSLEEP : KM_SLEEP);
+ ret = kmem_zalloc(sizeof(struct vmbus_channel *) * cnt, KM_SLEEP);
mutex_enter(&prich->ch_subchannel_lock);
- while (prich->ch_subchannel_count < cnt)
- /* XXX use condvar(9) instead of mtsleep */
- mtsleep(prich, PRIBIO, "hvvmsubch", 0,
- &prich->ch_subchannel_lock);
+ while (prich->ch_subchannel_count < cnt) {
+ if (cold) {
+ mutex_exit(&prich->ch_subchannel_lock);
+ delay(1000);
+ s = splnet();
+ hyperv_intr();
+ splx(s);
+ mutex_enter(&sc->sc_chevq_lock);
+ vmbus_process_chevq(sc);
+ mutex_exit(&sc->sc_chevq_lock);
+ mutex_enter(&prich->ch_subchannel_lock);
+ } else {
+ mtsleep(prich, PRIBIO, "hvsubch", 1,
+ &prich->ch_subchannel_lock);
+ }
+ }
i = 0;
TAILQ_FOREACH(ch, &prich->ch_subchannels, ch_subentry) {
@@ -1229,6 +1307,9 @@
break;
}
+ KASSERTMSG(i == cnt, "invalid subchan count %d, should be %d",
+ prich->ch_subchannel_count, cnt);
+
mutex_exit(&prich->ch_subchannel_lock);
return ret;
@@ -1991,12 +2072,90 @@
}
}
+ static void
+vmbus_chevq_enqueue(struct vmbus_softc *sc, int type, void *arg)
+{
+ struct vmbus_chev *vce;
+
+ vce = kmem_intr_alloc(sizeof(*vce), KM_NOSLEEP);
+ if (vce == NULL) {
+ device_printf(sc->sc_dev, "failed to allocate chev\n");
+ return;
+ }
+
+ vce->vce_type = type;
+ vce->vce_arg = arg;
+
+ mutex_enter(&sc->sc_chevq_lock);
+ SIMPLEQ_INSERT_TAIL(&sc->sc_chevq, vce, vce_entry);
+ cv_broadcast(&sc->sc_chevq_cv);
+ mutex_exit(&sc->sc_chevq_lock);
+}
+
+static void
+vmbus_process_chevq(void *arg)
+{
+ struct vmbus_softc *sc = arg;
+ struct vmbus_chev *vce;
+ struct vmbus_chanmsg_choffer *co;
+ struct vmbus_chanmsg_chrescind *cr;
+
+ KASSERT(mutex_owned(&sc->sc_chevq_lock));
Home |
Main Index |
Thread Index |
Old Index