Index: if_fe.c =================================================================== RCS file: /usr/cvs/src/sys/dev/fe/if_fe.c,v retrieving revision 1.98 diff -u -r1.98 if_fe.c --- if_fe.c 23 Feb 2007 12:18:41 -0000 1.98 +++ if_fe.c 29 May 2008 16:05:51 -0000 @@ -71,6 +71,7 @@ */ #include +#include #include #include #include @@ -135,10 +136,12 @@ /* Standard driver entry points. These can be static. */ static void fe_init (void *); +static void fe_init_locked (struct fe_softc *); static driver_intr_t fe_intr; static int fe_ioctl (struct ifnet *, u_long, caddr_t); static void fe_start (struct ifnet *); -static void fe_watchdog (struct ifnet *); +static void fe_start_locked (struct ifnet *); +static void fe_watchdog (void *); static int fe_medchange (struct ifnet *); static void fe_medstat (struct ifnet *, struct ifmediareq *); @@ -737,16 +740,13 @@ ifp = sc->ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(dev, "can not ifalloc\n"); + fe_release_resource(dev); return (ENOSPC); } - error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET, - NULL, fe_intr, sc, &sc->irq_handle); - if (error) { - if_free(ifp); - fe_release_resource(dev); - return ENXIO; - } + mtx_init(&sc->lock, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + callout_init_mtx(&sc->timer, &sc->lock, 0); /* * Initialize ifnet structure @@ -755,7 +755,6 @@ if_initname(sc->ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_start = fe_start; ifp->if_ioctl = fe_ioctl; - ifp->if_watchdog = fe_watchdog; ifp->if_init = fe_init; ifp->if_linkmib = &sc->mibdata; ifp->if_linkmiblen = sizeof (sc->mibdata); @@ -767,8 +766,7 @@ /* * Set fixed interface flags. */ - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | - IFF_NEEDSGIANT; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; #if 1 /* @@ -830,9 +828,21 @@ #endif /* Attach and stop the interface. */ - ether_ifattach(sc->ifp, sc->enaddr); + FE_LOCK(sc); fe_stop(sc); - + FE_UNLOCK(sc); + ether_ifattach(sc->ifp, sc->enaddr); + + error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE, + NULL, fe_intr, sc, &sc->irq_handle); + if (error) { + ether_ifdetach(ifp); + mtx_destroy(&sc->lock); + if_free(ifp); + fe_release_resource(dev); + return ENXIO; + } + /* Print additional info when attached. */ device_printf(dev, "type %s%s\n", sc->typestr, (sc->proto_dlcr4 & FE_D4_DSC) ? ", full duplex" : ""); @@ -942,7 +952,7 @@ /* Put the interface into known initial state. */ fe_stop(sc); if (sc->ifp->if_flags & IFF_UP) - fe_init(sc); + fe_init_locked(sc); } /* @@ -954,9 +964,8 @@ void fe_stop (struct fe_softc *sc) { - int s; - s = splimp(); + FE_ASSERT_LOCKED(sc); /* Disable interrupts. */ fe_outb(sc, FE_DLCR2, 0x00); @@ -978,7 +987,7 @@ /* Reset transmitter variables and interface flags. */ sc->ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE | IFF_DRV_RUNNING); - sc->ifp->if_timer = 0; + callout_stop(&sc->timer); sc->txb_free = sc->txb_size; sc->txb_count = 0; sc->txb_sched = 0; @@ -989,8 +998,6 @@ /* Call a device-specific hook. */ if (sc->stop) sc->stop(sc); - - (void) splx(s); } /* @@ -998,16 +1005,18 @@ * generate an interrupt after a transmit has been started on it. */ static void -fe_watchdog ( struct ifnet *ifp ) +fe_watchdog (void *arg) { - struct fe_softc *sc = ifp->if_softc; + struct fe_softc *sc = arg; + + FE_ASSERT_LOCKED(sc); /* A "debug" message. */ - if_printf(ifp, "transmission timeout (%d+%d)%s\n", + if_printf(sc->ifp, "transmission timeout (%d+%d)%s\n", sc->txb_sched, sc->txb_count, - (ifp->if_flags & IFF_UP) ? "" : " when down"); + (sc->ifp->if_flags & IFF_UP) ? "" : " when down"); if (sc->ifp->if_opackets == 0 && sc->ifp->if_ipackets == 0) - if_printf(ifp, "wrong IRQ setting in config?\n"); + if_printf(sc->ifp, "wrong IRQ setting in config?\n"); fe_reset(sc); } @@ -1018,10 +1027,17 @@ fe_init (void * xsc) { struct fe_softc *sc = xsc; - int s; + + FE_LOCK(sc); + fe_init_locked(sc); + FE_UNLOCK(sc); +} + +static void +fe_init_locked (struct fe_softc *sc) +{ /* Start initializing 86960. */ - s = splimp(); /* Call a hook before we start initializing the chip. */ if (sc->init) @@ -1128,10 +1144,8 @@ the interface keeping it idle. The upper layer will soon start the interface anyway, and there are no significant delay. */ - fe_start(sc->ifp); + fe_start_locked(sc->ifp); #endif - - (void) splx(s); } /* @@ -1145,7 +1159,7 @@ * We use longer timeout for multiple packet transmission. * I'm not sure this timer value is appropriate. FIXME. */ - sc->ifp->if_timer = 1 + sc->txb_count; + callout_reset(&sc->timer, (1 + sc->txb_count) * hz, fe_watchdog, sc); /* Update txb variables. */ sc->txb_sched = sc->txb_count; @@ -1159,17 +1173,24 @@ /* * Start output on interface. - * We make two assumptions here: - * 1) that the current priority is set to splimp _before_ this code - * is called *and* is returned to the appropriate priority after - * return - * 2) that the IFF_DRV_OACTIVE flag is checked before this code is called + * We make one assumption here: + * 1) that the IFF_DRV_OACTIVE flag is checked before this code is called * (i.e. that the output part of the interface is idle) */ static void fe_start (struct ifnet *ifp) { struct fe_softc *sc = ifp->if_softc; + + FE_LOCK(sc); + fe_start_locked(ifp); + FE_UNLOCK(sc); +} + +static void +fe_start_locked (struct ifnet *ifp) +{ + struct fe_softc *sc = ifp->if_softc; struct mbuf *m; #ifdef DIAGNOSTIC @@ -1534,7 +1555,7 @@ * Reset output active flag and watchdog timer. */ sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - sc->ifp->if_timer = 0; + callout_stop(&sc->timer); /* * If more data is ready to transmit in the buffer, start @@ -1685,6 +1706,8 @@ u_char tstat, rstat; int loop_count = FE_MAX_LOOP; + FE_LOCK(sc); + /* Loop until there are no more new interrupt conditions. */ while (loop_count-- > 0) { /* @@ -1692,8 +1715,10 @@ */ tstat = fe_inb(sc, FE_DLCR0) & FE_TMASK; rstat = fe_inb(sc, FE_DLCR1) & FE_RMASK; - if (tstat == 0 && rstat == 0) + if (tstat == 0 && rstat == 0) { + FE_UNLOCK(sc); return; + } /* * Reset the conditions we are acknowledging. @@ -1741,8 +1766,9 @@ * interrupt when the transmission buffer is full. */ if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) - fe_start(sc->ifp); + fe_start_locked(sc->ifp); } + FE_UNLOCK(sc); if_printf(sc->ifp, "too many loops\n"); } @@ -1756,9 +1782,7 @@ { struct fe_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; - int s, error = 0; - - s = splimp(); + int error = 0; switch (command) { @@ -1767,9 +1791,10 @@ * Switch interface state between "running" and * "stopped", reflecting the UP flag. */ + FE_LOCK(sc); if (sc->ifp->if_flags & IFF_UP) { if ((sc->ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) - fe_init(sc); + fe_init_locked(sc); } else { if ((sc->ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) fe_stop(sc); @@ -1780,6 +1805,7 @@ * so reprogram the multicast filter and/or receive mode. */ fe_setmode(sc); + FE_UNLOCK(sc); /* Done. */ break; @@ -1790,7 +1816,9 @@ * Multicast list has changed; set the hardware filter * accordingly. */ + FE_LOCK(sc); fe_setmode(sc); + FE_UNLOCK(sc); break; case SIOCSIFMEDIA: @@ -1805,7 +1833,6 @@ break; } - (void) splx(s); return (error); } @@ -1821,6 +1848,8 @@ struct ether_header *eh; struct mbuf *m; + FE_ASSERT_LOCKED(sc); + /* * NFS wants the data be aligned to the word (4 byte) * boundary. Ethernet header has 14 bytes. There is a @@ -1890,7 +1919,9 @@ } /* Feed the packet to upper layer. */ + FE_UNLOCK(sc); (*ifp->if_input)(ifp, m); + FE_LOCK(sc); return 0; } @@ -2164,7 +2195,7 @@ /* * Load a new multicast address filter into MARs. * - * The caller must have splimp'ed before fe_loadmar. + * The caller must have acquired the softc lock before fe_loadmar. * This function starts the DLC upon return. So it can be called only * when the chip is working, i.e., from the driver's point of view, when * a device is RUNNING. (I mistook the point in previous versions.) @@ -2223,9 +2254,11 @@ until the transmission buffer being empty? Changing the media when we are sending a frame will cause two garbages on wires, one on old media and another on new. FIXME */ + FE_LOCK(sc); if (sc->ifp->if_flags & IFF_UP) { if (sc->msel) sc->msel(sc); } + FE_UNLOCK(sc); return 0; } Index: if_fe_pccard.c =================================================================== RCS file: /usr/cvs/src/sys/dev/fe/if_fe_pccard.c,v retrieving revision 1.32 diff -u -r1.32 if_fe_pccard.c --- if_fe_pccard.c 19 Nov 2005 23:26:57 -0000 1.32 +++ if_fe_pccard.c 29 May 2008 15:46:11 -0000 @@ -175,11 +175,15 @@ struct fe_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->ifp; + FE_LOCK(sc); fe_stop(sc); + FE_UNLOCK(sc); + callout_drain(&sc->timer); ether_ifdetach(ifp); bus_teardown_intr(dev, sc->irq_res, sc->irq_handle); if_free(ifp); fe_release_resource(dev); + mtx_destroy(&sc->lock); return 0; } Index: if_fevar.h =================================================================== RCS file: /usr/cvs/src/sys/dev/fe/if_fevar.h,v retrieving revision 1.6 diff -u -r1.6 if_fevar.h --- if_fevar.h 10 Jun 2005 16:49:08 -0000 1.6 +++ if_fevar.h 29 May 2008 15:43:47 -0000 @@ -117,6 +117,8 @@ int defmedia; /* default media */ void (* msel)(struct fe_softc *); /* media selector. */ + struct mtx lock; + struct callout timer; }; struct fe_simple_probe_struct { @@ -125,6 +127,9 @@ u_char bits; /* Values to be compared against. */ }; +#define FE_LOCK(sc) mtx_lock(&(sc)->lock) +#define FE_UNLOCK(sc) mtx_unlock(&(sc)->lock) +#define FE_ASSERT_LOCKED(sc) mtx_assert(&(sc)->lock, MA_OWNED) extern devclass_t fe_devclass;