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