Source-Changes-HG archive

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

[src/trunk]: src/sys/dev/pci age_init() is called from age_watchdog() which i...



details:   https://anonhg.NetBSD.org/src/rev/962974fedfdf
branches:  trunk
changeset: 770620:962974fedfdf
user:      bouyer <bouyer%NetBSD.org@localhost>
date:      Tue Oct 25 21:47:38 2011 +0000

description:
age_init() is called from age_watchdog() which is interrupt context, we
can't sleep here or we get a DIAGNOSTIC panic when age_watchdog() fires.
More correct bus_dma(9) usage in age_encap()
Introduce a age_shutdown() to be called by pmf(9) at shutdown time,
to stop the DMA engine. My system would't properly reboot without this.
Be consistent in WAIT/NOWAIT use in init routines
Use BUS_DMA_COHERENT where appropriate
Rework the interrupt routine a bit, and ACK but do not disable interrupts
here. There seems to be a race where interrupts would not be properly
reenabled after this, leading do watchdog timeouts.

With these changes, the on-board age(4) on ftp.fr.netbsd.org seems to
be finally working properly.

diffstat:

 sys/dev/pci/if_age.c |  227 ++++++++++++++++++++++++++++----------------------
 1 files changed, 129 insertions(+), 98 deletions(-)

diffs (truncated from 479 to 300 lines):

diff -r 3b0843543877 -r 962974fedfdf sys/dev/pci/if_age.c
--- a/sys/dev/pci/if_age.c      Tue Oct 25 18:26:09 2011 +0000
+++ b/sys/dev/pci/if_age.c      Tue Oct 25 21:47:38 2011 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: if_age.c,v 1.39 2010/07/20 09:17:24 cegger Exp $ */
+/*     $NetBSD: if_age.c,v 1.40 2011/10/25 21:47:38 bouyer Exp $ */
 /*     $OpenBSD: if_age.c,v 1.1 2009/01/16 05:00:34 kevlo Exp $        */
 
 /*-
@@ -31,7 +31,7 @@
 /* Driver for Attansic Technology Corp. L1 Gigabit Ethernet. */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_age.c,v 1.39 2010/07/20 09:17:24 cegger Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_age.c,v 1.40 2011/10/25 21:47:38 bouyer Exp $");
 
 #include "vlan.h"
 
@@ -90,6 +90,7 @@
 static int     age_ioctl(struct ifnet *, u_long, void *);
 static void    age_start(struct ifnet *);
 static void    age_watchdog(struct ifnet *);
+static bool    age_shutdown(device_t, int);
 static void    age_mediastatus(struct ifnet *, struct ifmediareq *);
 static int     age_mediachange(struct ifnet *);
 
@@ -287,7 +288,7 @@
        if_attach(ifp);
        ether_ifattach(ifp, sc->sc_enaddr);
 
-       if (pmf_device_register(self, NULL, age_resume))
+       if (pmf_device_register1(self, NULL, age_resume, age_shutdown))
                pmf_class_network_register(self, ifp);
        else
                aprint_error_dev(self, "couldn't establish power handler\n");
@@ -495,65 +496,61 @@
        cmb = sc->age_rdata.age_cmb_block;
        if (cmb == NULL) {
                /* Happens when bringing up the interface
-                * w/o having a carrier. Ack. the interrupt.
+                * w/o having a carrier. Ack the interrupt.
                 */
                CSR_WRITE_4(sc, AGE_INTR_STATUS, status);
                return 0;
        }
 
-       /* Disable interrupts. */
-       CSR_WRITE_4(sc, AGE_INTR_STATUS, status | INTR_DIS_INT);
-               
-       bus_dmamap_sync(sc->sc_dmat, sc->age_cdata.age_cmb_block_map, 0,
-           sc->age_cdata.age_cmb_block_map->dm_mapsize, BUS_DMASYNC_POSTREAD);
-       status = le32toh(cmb->intr_status);
-       if ((status & AGE_INTRS) == 0)
-               goto back;
-
-       sc->age_tpd_cons = (le32toh(cmb->tpd_cons) & TPD_CONS_MASK) >>
-           TPD_CONS_SHIFT;
-       sc->age_rr_prod = (le32toh(cmb->rprod_cons) & RRD_PROD_MASK) >>
-           RRD_PROD_SHIFT;
-
-       /* Let hardware know CMB was served. */
-       cmb->intr_status = 0;
        bus_dmamap_sync(sc->sc_dmat, sc->age_cdata.age_cmb_block_map, 0,
            sc->age_cdata.age_cmb_block_map->dm_mapsize,
-           BUS_DMASYNC_PREWRITE);
+           BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+       status = le32toh(cmb->intr_status);
+       /* ACK/reenable interrupts */
+       CSR_WRITE_4(sc, AGE_INTR_STATUS, status);
+       while ((status & AGE_INTRS) != 0) {
+               sc->age_tpd_cons = (le32toh(cmb->tpd_cons) & TPD_CONS_MASK) >>
+                   TPD_CONS_SHIFT;
+               sc->age_rr_prod = (le32toh(cmb->rprod_cons) & RRD_PROD_MASK) >>
+                   RRD_PROD_SHIFT;
 
-       if (ifp->if_flags & IFF_RUNNING) {
-               if (status & INTR_CMB_RX)
-                       age_rxintr(sc, sc->age_rr_prod);
-
-               if (status & INTR_CMB_TX)
-                       age_txintr(sc, sc->age_tpd_cons);
+               /* Let hardware know CMB was served. */
+               cmb->intr_status = 0;
+               bus_dmamap_sync(sc->sc_dmat, sc->age_cdata.age_cmb_block_map, 0,
+                   sc->age_cdata.age_cmb_block_map->dm_mapsize,
+                   BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
 
-               if (status & (INTR_DMA_RD_TO_RST | INTR_DMA_WR_TO_RST)) {
-                       if (status & INTR_DMA_RD_TO_RST)
-                               printf("%s: DMA read error! -- resetting\n",
-                                   device_xname(sc->sc_dev));
-                       if (status & INTR_DMA_WR_TO_RST)
-                               printf("%s: DMA write error! -- resetting\n",
-                                   device_xname(sc->sc_dev));
-                       age_init(ifp);
-               }
+               if (ifp->if_flags & IFF_RUNNING) {
+                       if (status & INTR_CMB_RX)
+                               age_rxintr(sc, sc->age_rr_prod);
+
+                       if (status & INTR_CMB_TX)
+                               age_txintr(sc, sc->age_tpd_cons);
 
-               if (!IFQ_IS_EMPTY(&ifp->if_snd))
+                       if (status & (INTR_DMA_RD_TO_RST | INTR_DMA_WR_TO_RST)) {
+                               if (status & INTR_DMA_RD_TO_RST)
+                                       printf("%s: DMA read error! -- "
+                                           "resetting\n",
+                                           device_xname(sc->sc_dev));
+                               if (status & INTR_DMA_WR_TO_RST)
+                                       printf("%s: DMA write error! -- "
+                                           "resetting\n",
+                                           device_xname(sc->sc_dev));
+                               age_init(ifp);
+                       }
+
                        age_start(ifp);
 
-               if (status & INTR_SMB)
-                       age_stats_update(sc);
+                       if (status & INTR_SMB)
+                               age_stats_update(sc);
+               }
+               /* check if more interrupts did came in */
+               bus_dmamap_sync(sc->sc_dmat, sc->age_cdata.age_cmb_block_map, 0,
+                   sc->age_cdata.age_cmb_block_map->dm_mapsize,
+                   BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+               status = le32toh(cmb->intr_status);
        }
 
-       /* Check whether CMB was updated while serving Tx/Rx/SMB handler. */
-       bus_dmamap_sync(sc->sc_dmat, sc->age_cdata.age_cmb_block_map, 0,
-           sc->age_cdata.age_cmb_block_map->dm_mapsize,
-           BUS_DMASYNC_POSTREAD);
-
-back:
-       /* Re-enable interrupts. */
-       CSR_WRITE_4(sc, AGE_INTR_STATUS, 0);
-
        return 1;
 }
 
@@ -702,7 +699,7 @@
        /* Allocate DMA'able memory for TX ring */
        error = bus_dmamem_alloc(sc->sc_dmat, AGE_TX_RING_SZ, 
            ETHER_ALIGN, 0, &sc->age_rdata.age_tx_ring_seg, 1, 
-           &nsegs, BUS_DMA_WAITOK);
+           &nsegs, BUS_DMA_NOWAIT);
        if (error) {
                printf("%s: could not allocate DMA'able memory for Tx ring, "
                    "error = %i\n", device_xname(sc->sc_dev), error);
@@ -711,7 +708,7 @@
 
        error = bus_dmamem_map(sc->sc_dmat, &sc->age_rdata.age_tx_ring_seg,
            nsegs, AGE_TX_RING_SZ, (void **)&sc->age_rdata.age_tx_ring,
-           BUS_DMA_NOWAIT);
+           BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
        if (error) 
                return ENOBUFS;
 
@@ -719,7 +716,7 @@
 
        /*  Load the DMA map for Tx ring. */
        error = bus_dmamap_load(sc->sc_dmat, sc->age_cdata.age_tx_ring_map,
-           sc->age_rdata.age_tx_ring, AGE_TX_RING_SZ, NULL, BUS_DMA_WAITOK);
+           sc->age_rdata.age_tx_ring, AGE_TX_RING_SZ, NULL, BUS_DMA_NOWAIT);
        if (error) {
                printf("%s: could not load DMA'able memory for Tx ring, "
                    "error = %i\n", device_xname(sc->sc_dev), error);
@@ -744,7 +741,7 @@
        /* Allocate DMA'able memory for RX ring */
        error = bus_dmamem_alloc(sc->sc_dmat, AGE_RX_RING_SZ, 
            ETHER_ALIGN, 0, &sc->age_rdata.age_rx_ring_seg, 1, 
-           &nsegs, BUS_DMA_WAITOK);
+           &nsegs, BUS_DMA_NOWAIT);
        if (error) {
                printf("%s: could not allocate DMA'able memory for Rx ring, "
                    "error = %i.\n", device_xname(sc->sc_dev), error);
@@ -753,7 +750,7 @@
 
        error = bus_dmamem_map(sc->sc_dmat, &sc->age_rdata.age_rx_ring_seg,
            nsegs, AGE_RX_RING_SZ, (void **)&sc->age_rdata.age_rx_ring,
-           BUS_DMA_NOWAIT);
+           BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
        if (error)
                return ENOBUFS;
 
@@ -761,7 +758,7 @@
 
        /* Load the DMA map for Rx ring. */
        error = bus_dmamap_load(sc->sc_dmat, sc->age_cdata.age_rx_ring_map,
-           sc->age_rdata.age_rx_ring, AGE_RX_RING_SZ, NULL, BUS_DMA_WAITOK);
+           sc->age_rdata.age_rx_ring, AGE_RX_RING_SZ, NULL, BUS_DMA_NOWAIT);
        if (error) {
                printf("%s: could not load DMA'able memory for Rx ring, "
                    "error = %i.\n", device_xname(sc->sc_dev), error);
@@ -786,7 +783,7 @@
        /* Allocate DMA'able memory for RX return ring */
        error = bus_dmamem_alloc(sc->sc_dmat, AGE_RR_RING_SZ, 
            ETHER_ALIGN, 0, &sc->age_rdata.age_rr_ring_seg, 1, 
-           &nsegs, BUS_DMA_WAITOK);
+           &nsegs, BUS_DMA_NOWAIT);
        if (error) {
                printf("%s: could not allocate DMA'able memory for Rx "
                    "return ring, error = %i.\n",
@@ -796,7 +793,7 @@
 
        error = bus_dmamem_map(sc->sc_dmat, &sc->age_rdata.age_rr_ring_seg,
            nsegs, AGE_RR_RING_SZ, (void **)&sc->age_rdata.age_rr_ring,
-           BUS_DMA_NOWAIT);
+           BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
        if (error)
                return ENOBUFS;
 
@@ -804,7 +801,7 @@
 
        /*  Load the DMA map for Rx return ring. */
        error = bus_dmamap_load(sc->sc_dmat, sc->age_cdata.age_rr_ring_map,
-           sc->age_rdata.age_rr_ring, AGE_RR_RING_SZ, NULL, BUS_DMA_WAITOK);
+           sc->age_rdata.age_rr_ring, AGE_RR_RING_SZ, NULL, BUS_DMA_NOWAIT);
        if (error) {
                printf("%s: could not load DMA'able memory for Rx return ring, "
                    "error = %i\n", device_xname(sc->sc_dev), error);
@@ -830,7 +827,7 @@
        /* Allocate DMA'able memory for CMB block */
        error = bus_dmamem_alloc(sc->sc_dmat, AGE_CMB_BLOCK_SZ, 
            ETHER_ALIGN, 0, &sc->age_rdata.age_cmb_block_seg, 1, 
-           &nsegs, BUS_DMA_WAITOK);
+           &nsegs, BUS_DMA_NOWAIT);
        if (error) {
                printf("%s: could not allocate DMA'able memory for "
                    "CMB block, error = %i\n", device_xname(sc->sc_dev), error);
@@ -839,7 +836,7 @@
 
        error = bus_dmamem_map(sc->sc_dmat, &sc->age_rdata.age_cmb_block_seg,
            nsegs, AGE_CMB_BLOCK_SZ, (void **)&sc->age_rdata.age_cmb_block,
-           BUS_DMA_NOWAIT);
+           BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
        if (error)
                return ENOBUFS;
 
@@ -848,7 +845,7 @@
        /*  Load the DMA map for CMB block. */
        error = bus_dmamap_load(sc->sc_dmat, sc->age_cdata.age_cmb_block_map,
            sc->age_rdata.age_cmb_block, AGE_CMB_BLOCK_SZ, NULL, 
-           BUS_DMA_WAITOK);
+           BUS_DMA_NOWAIT);
        if (error) {
                printf("%s: could not load DMA'able memory for CMB block, "
                    "error = %i\n", device_xname(sc->sc_dev), error);
@@ -874,7 +871,7 @@
        /* Allocate DMA'able memory for SMB block */
        error = bus_dmamem_alloc(sc->sc_dmat, AGE_SMB_BLOCK_SZ, 
            ETHER_ALIGN, 0, &sc->age_rdata.age_smb_block_seg, 1, 
-           &nsegs, BUS_DMA_WAITOK);
+           &nsegs, BUS_DMA_NOWAIT);
        if (error) {
                printf("%s: could not allocate DMA'able memory for "
                    "SMB block, error = %i\n", device_xname(sc->sc_dev), error);
@@ -883,7 +880,7 @@
 
        error = bus_dmamem_map(sc->sc_dmat, &sc->age_rdata.age_smb_block_seg,
            nsegs, AGE_SMB_BLOCK_SZ, (void **)&sc->age_rdata.age_smb_block,
-           BUS_DMA_NOWAIT);
+           BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
        if (error)
                return ENOBUFS;
 
@@ -892,7 +889,7 @@
        /*  Load the DMA map for SMB block */
        error = bus_dmamap_load(sc->sc_dmat, sc->age_cdata.age_smb_block_map,
            sc->age_rdata.age_smb_block, AGE_SMB_BLOCK_SZ, NULL, 
-           BUS_DMA_WAITOK);
+           BUS_DMA_NOWAIT);
        if (error) {
                printf("%s: could not load DMA'able memory for SMB block, "
                    "error = %i\n", device_xname(sc->sc_dev), error);
@@ -1034,6 +1031,10 @@
 
        if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
                return;
+       if ((sc->age_flags & AGE_FLAG_LINK) == 0)
+               return;
+       if (IFQ_IS_EMPTY(&ifp->if_snd))
+               return;
 
        enq = 0;
        for (;;) {
@@ -1086,18 +1087,29 @@
        if (sc->age_cdata.age_tx_cnt == 0) {
                printf("%s: watchdog timeout (missed Tx interrupts) "
                    "-- recovering\n", device_xname(sc->sc_dev));
-               if (!IFQ_IS_EMPTY(&ifp->if_snd))
-                       age_start(ifp);
+               age_start(ifp);
                return;
        }
 
        printf("%s: watchdog timeout\n", device_xname(sc->sc_dev));
        ifp->if_oerrors++;



Home | Main Index | Thread Index | Old Index