Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/pci Make if_wm MPSAFE



details:   https://anonhg.NetBSD.org/src/rev/aabd67e5fb4c
branches:  trunk
changeset: 330251:aabd67e5fb4c
user:      ozaki-r <ozaki-r%NetBSD.org@localhost>
date:      Tue Jul 01 10:35:18 2014 +0000

description:
Make if_wm MPSAFE

- Make it MPSAFE only when NET_MPSAFE
  - otherwise, its instructions are almost same as before
  - the only change is IFQ_POLL/IFQ_DEQUEUE which
    is now single IFQ_DEQUEUE
- Protect driver operations with a lock
  - further work would make it separate
- Apply MPSAFE flag to
  - callout_init
  - pci_intr_establish
- Stop proceeding packets when the driver is likely
  to stop for graceful ifconfig down

Tested on Rangeley (I354) and KVM (e1000)
Reviewed by msaitoh@

diffstat:

 sys/dev/pci/if_wm.c |  273 ++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 234 insertions(+), 39 deletions(-)

diffs (truncated from 690 to 300 lines):

diff -r 41d974914889 -r aabd67e5fb4c sys/dev/pci/if_wm.c
--- a/sys/dev/pci/if_wm.c       Tue Jul 01 10:16:02 2014 +0000
+++ b/sys/dev/pci/if_wm.c       Tue Jul 01 10:35:18 2014 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: if_wm.c,v 1.271 2014/06/30 06:09:44 ozaki-r Exp $      */
+/*     $NetBSD: if_wm.c,v 1.272 2014/07/01 10:35:18 ozaki-r Exp $      */
 
 /*
  * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc.
@@ -76,7 +76,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.271 2014/06/30 06:09:44 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.272 2014/07/01 10:35:18 ozaki-r Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -141,6 +141,10 @@
 #define        DPRINTF(x, y)   /* nothing */
 #endif /* WM_DEBUG */
 
+#ifdef NET_MPSAFE
+#define WM_MPSAFE      1
+#endif
+
 /*
  * Transmit descriptor list size.  Due to errata, we can only have
  * 256 hardware descriptors in the ring on < 82544, but we use 4096
@@ -275,6 +279,7 @@
 
        void *sc_ih;                    /* interrupt cookie */
        callout_t sc_tick_ch;           /* tick callout */
+       bool sc_stopping;
 
        int sc_ee_addrbits;             /* EEPROM address bits */
        int sc_ich8_flash_base;
@@ -380,8 +385,21 @@
        int sc_mchash_type;             /* multicast filter offset */
 
        krndsource_t rnd_source;        /* random source */
+
+       kmutex_t *sc_txrx_lock;         /* lock for tx/rx operations */
+                                       /* XXX need separation? */
 };
 
+#define WM_LOCK(_sc)   if ((_sc)->sc_txrx_lock) mutex_enter((_sc)->sc_txrx_lock)
+#define WM_UNLOCK(_sc) if ((_sc)->sc_txrx_lock) mutex_exit((_sc)->sc_txrx_lock)
+#define WM_LOCKED(_sc) (!(_sc)->sc_txrx_lock || mutex_owned((_sc)->sc_txrx_lock))
+
+#ifdef WM_MPSAFE
+#define CALLOUT_FLAGS  CALLOUT_MPSAFE
+#else
+#define CALLOUT_FLAGS  0
+#endif
+
 #define        WM_RXCHAIN_RESET(sc)                                            \
 do {                                                                   \
        (sc)->sc_rxtailp = &(sc)->sc_rxhead;                            \
@@ -495,12 +513,16 @@
 } while (/*CONSTCOND*/0)
 
 static void    wm_start(struct ifnet *);
+static void    wm_start_locked(struct ifnet *);
 static void    wm_nq_start(struct ifnet *);
+static void    wm_nq_start_locked(struct ifnet *);
 static void    wm_watchdog(struct ifnet *);
 static int     wm_ifflags_cb(struct ethercom *);
 static int     wm_ioctl(struct ifnet *, u_long, void *);
 static int     wm_init(struct ifnet *);
+static int     wm_init_locked(struct ifnet *);
 static void    wm_stop(struct ifnet *, int);
+static void    wm_stop_locked(struct ifnet *, int);
 static bool    wm_suspend(device_t, const pmf_qual_t *);
 static bool    wm_resume(device_t, const pmf_qual_t *);
 
@@ -1184,7 +1206,8 @@
        char intrbuf[PCI_INTRSTR_LEN];
 
        sc->sc_dev = self;
-       callout_init(&sc->sc_tick_ch, 0);
+       callout_init(&sc->sc_tick_ch, CALLOUT_FLAGS);
+       sc->sc_stopping = false;
 
        sc->sc_wmp = wmp = wm_lookup(pa);
        if (wmp == NULL) {
@@ -1315,6 +1338,9 @@
                return;
        }
        intrstr = pci_intr_string(pc, ih, intrbuf, sizeof(intrbuf));
+#ifdef WM_MPSAFE
+       pci_intr_setattr(pc, &ih, PCI_INTR_MPSAFE, true);
+#endif
        sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, wm_intr, sc);
        if (sc->sc_ih == NULL) {
                aprint_error_dev(sc->sc_dev, "unable to establish interrupt");
@@ -1354,7 +1380,7 @@
                aprint_verbose_dev(sc->sc_dev,
                    "Communication Streaming Architecture\n");
                if (sc->sc_type == WM_T_82547) {
-                       callout_init(&sc->sc_txfifo_ch, 0);
+                       callout_init(&sc->sc_txfifo_ch, CALLOUT_FLAGS);
                        callout_setfunc(&sc->sc_txfifo_ch,
                                        wm_82547_txfifo_stall, sc);
                        aprint_verbose_dev(sc->sc_dev,
@@ -2051,6 +2077,12 @@
                ifp->if_capabilities |= IFCAP_TSOv6;
        }
 
+#ifdef WM_MPSAFE
+       sc->sc_txrx_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
+#else
+       sc->sc_txrx_lock = NULL;
+#endif
+
        /*
         * Attach the interface.
         */
@@ -2159,18 +2191,26 @@
 {
        struct wm_softc *sc = device_private(self);
        struct ifnet *ifp = &sc->sc_ethercom.ec_if;
-       int i, s;
+       int i;
+#ifndef WM_MPSAFE
+       int s;
 
        s = splnet();
+#endif
        /* Stop the interface. Callouts are stopped in it. */
        wm_stop(ifp, 1);
+
+#ifndef WM_MPSAFE
        splx(s);
+#endif
 
        pmf_device_deregister(self);
 
        /* Tell the firmware about the release */
+       WM_LOCK(sc);
        wm_release_manageability(sc);
        wm_release_hw_control(sc);
+       WM_UNLOCK(sc);
 
        mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY);
 
@@ -2182,7 +2222,10 @@
 
 
        /* Unload RX dmamaps and free mbufs */
+       WM_LOCK(sc);
        wm_rxdrain(sc);
+       WM_UNLOCK(sc);
+       /* Must unlock here */
 
        /* Free dmamap. It's the same as the end of the wm_attach() function */
        for (i = 0; i < WM_NRXDESC; i++) {
@@ -2218,6 +2261,9 @@
                sc->sc_ios = 0;
        }
 
+       if (sc->sc_txrx_lock)
+               mutex_obj_free(sc->sc_txrx_lock);
+
        return 0;
 }
 
@@ -2441,9 +2487,15 @@
 wm_82547_txfifo_stall(void *arg)
 {
        struct wm_softc *sc = arg;
+#ifndef WM_MPSAFE
        int s;
 
        s = splnet();
+#endif
+       WM_LOCK(sc);
+
+       if (sc->sc_stopping)
+               goto out;
 
        if (sc->sc_txfifo_stall) {
                if (CSR_READ(sc, WMREG_TDT) == CSR_READ(sc, WMREG_TDH) &&
@@ -2465,7 +2517,7 @@
 
                        sc->sc_txfifo_head = 0;
                        sc->sc_txfifo_stall = 0;
-                       wm_start(&sc->sc_ethercom.ec_if);
+                       wm_start_locked(&sc->sc_ethercom.ec_if);
                } else {
                        /*
                         * Still waiting for packets to drain; try again in
@@ -2475,7 +2527,11 @@
                }
        }
 
+out:
+       WM_UNLOCK(sc);
+#ifndef WM_MPSAFE
        splx(s);
+#endif
 }
 
 static void
@@ -2546,6 +2602,17 @@
 wm_start(struct ifnet *ifp)
 {
        struct wm_softc *sc = ifp->if_softc;
+
+       WM_LOCK(sc);
+       if (!sc->sc_stopping)
+               wm_start_locked(ifp);
+       WM_UNLOCK(sc);
+}
+
+static void
+wm_start_locked(struct ifnet *ifp)
+{
+       struct wm_softc *sc = ifp->if_softc;
        struct mbuf *m0;
        struct m_tag *mtag;
        struct wm_txsoft *txs;
@@ -2556,6 +2623,8 @@
        uint32_t cksumcmd;
        uint8_t cksumfields;
 
+       KASSERT(WM_LOCKED(sc));
+
        if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING)
                return;
 
@@ -2570,14 +2639,7 @@
         * descriptors.
         */
        for (;;) {
-               /* Grab a packet off the queue. */
-               IFQ_POLL(&ifp->if_snd, m0);
-               if (m0 == NULL)
-                       break;
-
-               DPRINTF(WM_DEBUG_TX,
-                   ("%s: TX: have packet to transmit: %p\n",
-                   device_xname(sc->sc_dev), m0));
+               m0 = NULL;
 
                /* Get a work queue entry. */
                if (sc->sc_txsfree < WM_TXQUEUE_GC(sc)) {
@@ -2591,6 +2653,15 @@
                        }
                }
 
+               /* Grab a packet off the queue. */
+               IFQ_DEQUEUE(&ifp->if_snd, m0);
+               if (m0 == NULL)
+                       break;
+
+               DPRINTF(WM_DEBUG_TX,
+                   ("%s: TX: have packet to transmit: %p\n",
+                   device_xname(sc->sc_dev), m0));
+
                txs = &sc->sc_txsoft[sc->sc_txsnext];
                dmamap = txs->txs_dmamap;
 
@@ -2627,7 +2698,6 @@
                                log(LOG_ERR, "%s: Tx packet consumes too many "
                                    "DMA segments, dropping...\n",
                                    device_xname(sc->sc_dev));
-                               IFQ_DEQUEUE(&ifp->if_snd, m0);
                                wm_dump_mbuf_chain(sc, m0);
                                m_freem(m0);
                                continue;
@@ -2688,8 +2758,6 @@
                        break;
                }
 
-               IFQ_DEQUEUE(&ifp->if_snd, m0);
-
                /*
                 * WE ARE NOW COMMITTED TO TRANSMITTING THE PACKET.
                 */
@@ -2833,6 +2901,13 @@
                bpf_mtap(ifp, m0);
        }
 
+       if (m0 != NULL) {
+               ifp->if_flags |= IFF_OACTIVE;
+               WM_EVCNT_INCR(&sc->sc_ev_txdrop);
+               DPRINTF(WM_DEBUG_TX, ("%s: TX: error after IFQ_DEQUEUE\n", __func__));
+               m_freem(m0);
+       }
+
        if (sc->sc_txsfree == 0 || sc->sc_txfree <= 2) {
                /* No more slots; notify upper layer. */
                ifp->if_flags |= IFF_OACTIVE;
@@ -3053,6 +3128,17 @@
 wm_nq_start(struct ifnet *ifp)
 {
        struct wm_softc *sc = ifp->if_softc;
+
+       WM_LOCK(sc);



Home | Main Index | Thread Index | Old Index