diff options
author | jhb <jhb@FreeBSD.org> | 2008-07-04 21:13:18 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2008-07-04 21:13:18 +0000 |
commit | ab5a1a3be383b4ffe9b6cdd9d2652c8c195a0e29 (patch) | |
tree | a4f37019ce62ea10512d972dd4cb9d3dbb1fc6fb /sys | |
parent | a495f456d1f1492b415b437aaf9f5ec5f676c239 (diff) | |
download | FreeBSD-src-ab5a1a3be383b4ffe9b6cdd9d2652c8c195a0e29.zip FreeBSD-src-ab5a1a3be383b4ffe9b6cdd9d2652c8c195a0e29.tar.gz |
Make sbsh(4) MPSAFE:
- Add a mutex to the softc and use it to protect the softc and device
hardware.
- Setup interrupt handler after ether_ifattach().
- Remove unused sbsh_watchdog() routine.
- Protect against concurrent attempts to load firmware.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/sbsh/if_sbsh.c | 199 |
1 files changed, 109 insertions, 90 deletions
diff --git a/sys/dev/sbsh/if_sbsh.c b/sys/dev/sbsh/if_sbsh.c index 27565a7..a5b5684 100644 --- a/sys/dev/sbsh/if_sbsh.c +++ b/sys/dev/sbsh/if_sbsh.c @@ -98,6 +98,7 @@ struct cx28975_cmdarea { struct sbsh_softc { struct ifnet *ifp; + struct mtx lock; struct resource *mem_res; struct resource *irq_res; @@ -123,8 +124,13 @@ struct sbsh_softc { /* the descriptors mapped onto the first buffers in xq and rq */ unsigned head_tdesc, head_rdesc; u_int8_t state; + int loading_firmware; }; +#define SBSH_LOCK(sc) mtx_lock(&(sc)->lock) +#define SBSH_UNLOCK(sc) mtx_unlock(&(sc)->lock) +#define SBSH_ASSERT_LOCKED(sc) mtx_assert(&(sc)->lock, MA_OWNED) + struct cx28975_cfg { u_int8_t *firmw_image; u_int32_t firmw_len; @@ -159,10 +165,11 @@ static int sbsh_ioctl(struct ifnet *, u_long, caddr_t); static void sbsh_shutdown(device_t); static int sbsh_suspend(device_t); static int sbsh_resume(device_t); -static void sbsh_watchdog(struct ifnet *); static void sbsh_start(struct ifnet *); +static void sbsh_start_locked(struct ifnet *); static void sbsh_init(void *); +static void sbsh_init_locked(struct sbsh_softc *); static void sbsh_stop(struct sbsh_softc *); static void init_card(struct sbsh_softc *); static void sbsh_intr(void *); @@ -222,20 +229,17 @@ sbsh_attach(device_t dev) { struct sbsh_softc *sc; struct ifnet *ifp; - int unit, error = 0, rid, s; + int error = 0, rid; u_char eaddr[6]; - s = splimp(); - sc = device_get_softc(dev); - unit = device_get_unit(dev); rid = PCIR_BAR(1); sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, - 0, ~0, 4096, RF_ACTIVE); + 0ul, ~0ul, 4096, RF_ACTIVE); if (sc->mem_res == NULL) { - printf ("sbsh%d: couldn't map memory\n", unit); + device_printf(dev, "couldn't map memory\n"); error = ENXIO; goto fail; } @@ -245,9 +249,7 @@ sbsh_attach(device_t dev) RF_SHAREABLE | RF_ACTIVE); if (sc->irq_res == NULL) { - printf("sbsh%d: couldn't map interrupt\n", unit); - bus_release_resource(dev, SYS_RES_MEMORY, - PCIR_BAR(1), sc->mem_res); + device_printf(dev, "couldn't map interrupt\n"); error = ENXIO; goto fail; } @@ -255,45 +257,46 @@ sbsh_attach(device_t dev) sc->mem_base = rman_get_virtual(sc->mem_res); init_card(sc); - error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET, - NULL, sbsh_intr, sc, &sc->intr_hand); - if (error) { - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); - bus_release_resource(dev, SYS_RES_MEMORY, - PCIR_BAR(1), sc->mem_res); - printf("sbsh%d: couldn't set up irq\n", unit); - goto fail; - } - /* generate ethernet MAC address */ *(u_int32_t *)eaddr = htonl(0x00ff0192); read_random(eaddr + 4, 2); ifp = sc->ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); - bus_release_resource(dev, SYS_RES_MEMORY, - PCIR_BAR(1), sc->mem_res); - bus_teardown_intr(dev, sc->irq_res, sc->intr_hand); - printf("sbsh%d: can not if_alloc()\n", unit); + device_printf(dev, "can not if_alloc()\n"); goto fail; } ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | - IFF_NEEDSGIANT; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = sbsh_ioctl; ifp->if_start = sbsh_start; - ifp->if_watchdog = sbsh_watchdog; ifp->if_init = sbsh_init; ifp->if_baudrate = 4600000; - ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + mtx_init(&sc->lock, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); ether_ifattach(ifp, eaddr); + error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE, + NULL, sbsh_intr, sc, &sc->intr_hand); + if (error) { + ether_ifdetach(ifp); + mtx_destroy(&sc->lock); + device_printf(dev, "couldn't set up irq\n"); + goto fail; + } + fail: - splx(s); + if (sc->ifp) + if_free(sc->ifp); + if (sc->irq_res) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res) + bus_release_resource(dev, SYS_RES_MEMORY, + PCIR_BAR(1), sc->mem_res); return (error); } @@ -302,14 +305,13 @@ sbsh_detach(device_t dev) { struct sbsh_softc *sc; struct ifnet *ifp; - int s; - - s = splimp(); sc = device_get_softc(dev); ifp = sc->ifp; + SBSH_LOCK(sc); sbsh_stop(sc); + SBSH_UNLOCK(sc); ether_ifdetach(ifp); bus_teardown_intr(dev, sc->irq_res, sc->intr_hand); @@ -317,40 +319,51 @@ sbsh_detach(device_t dev) bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(1), sc->mem_res); if_free(ifp); + mtx_destroy(&sc->lock); - splx(s); return (0); } - static void sbsh_start(struct ifnet *ifp) { struct sbsh_softc *sc = ifp->if_softc; - int s; + + SBSH_LOCK(sc); + sbsh_start_locked(ifp); + SBSH_UNLOCK(sc); +} + +static void +sbsh_start_locked(struct ifnet *ifp) +{ + struct sbsh_softc *sc = ifp->if_softc; if (sc->state != ACTIVE) return; - s = splimp(); start_xmit_frames(ifp->if_softc); - splx(s); } - static void sbsh_init(void *xsc) { struct sbsh_softc *sc = xsc; + + SBSH_LOCK(sc); + sbsh_init_locked(sc); + SBSH_UNLOCK(sc); +} + +static void +sbsh_init_locked(struct sbsh_softc *sc) +{ struct ifnet *ifp = sc->ifp; - int s; u_int8_t t; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) || sc->state == NOT_LOADED) return; - s = splimp(); - bzero(&sc->in_stats, sizeof(struct sbni16_stats)); sc->head_xq = sc->tail_xq = sc->head_rq = sc->tail_rq = 0; sc->head_tdesc = sc->head_rdesc = 0; @@ -364,18 +377,15 @@ sbsh_init(void *xsc) ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } - - splx(s); } static void sbsh_stop(struct sbsh_softc *sc) { - int s; u_int8_t t; - s = splimp(); + SBSH_ASSERT_LOCKED(sc); sc->regs->IMR = EXT; t = 0; @@ -391,7 +401,7 @@ sbsh_stop(struct sbsh_softc *sc) sc->regs->IMR = 0; sc->state = DOWN; - splx(s); + sc->ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } @@ -416,21 +426,26 @@ sbsh_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) struct sbsh_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; struct cx28975_cfg cfg; + struct sbni16_stats stats; struct dsl_stats ds; - int s, error = 0; + int error = 0; u_int8_t t; - s = splimp(); - switch(cmd) { case SIOCLOADFIRMW: if ((error = priv_check(curthread, PRIV_DRIVER)) != 0) break; - if (ifp->if_flags & IFF_UP) + error = copyin(ifr->ifr_data, &cfg, sizeof(cfg)); + if (error) + break; + SBSH_LOCK(sc); + if (ifp->if_flags & IFF_UP || sc->loading_firmware) { error = EBUSY; - - bcopy((caddr_t)ifr->ifr_data, (caddr_t)&cfg, sizeof cfg); + SBSH_UNLOCK(sc); + break; + } + sc->loading_firmware = 1; if (start_cx28975(sc, cfg) == 0) { static char *modstr[] = { "TCPAM32", "TCPAM16", "TCPAM8", "TCPAM4" }; @@ -442,12 +457,15 @@ sbsh_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) "unable to load firmware\n"); error = EIO; } + sc->loading_firmware = 0; + SBSH_UNLOCK(sc); break; case SIOCGETSTATS : if ((error = priv_check(curthread, PRIV_DRIVER)) != 0) break; + SBSH_LOCK(sc); t = 0; if (issue_cx28975_cmd(sc, _DSL_FAR_END_ATTEN, &t, 1)) error = EIO; @@ -472,36 +490,43 @@ sbsh_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) ds.status_1 = ((volatile u_int8_t *)sc->cmdp)[0x3c0]; ds.status_3 = ((volatile u_int8_t *)sc->cmdp)[0x3c2]; + bcopy(&sc->in_stats, &stats, sizeof(struct sbni16_stats)); + SBSH_UNLOCK(sc); - bcopy(&sc->in_stats, ifr->ifr_data, sizeof(struct sbni16_stats)); - bcopy(&ds, ifr->ifr_data + sizeof(struct sbni16_stats), - sizeof(struct dsl_stats)); + error = copyout(&stats, ifr->ifr_data, + sizeof(struct sbni16_stats)); + if (error) + break; + error = copyout(&ds, ifr->ifr_data + + sizeof(struct sbni16_stats), sizeof(struct dsl_stats)); break; case SIOCCLRSTATS : if (!(error = priv_check(curthread, PRIV_DRIVER))) { + SBSH_LOCK(sc); bzero(&sc->in_stats, sizeof(struct sbni16_stats)); t = 2; if (issue_cx28975_cmd(sc, _DSL_CLEAR_ERROR_CTRS, &t, 1)) error = EIO; + SBSH_UNLOCK(sc); } break; case SIOCSIFFLAGS: + SBSH_LOCK(sc); if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { if (sc->state == NOT_LOADED) { if_printf(ifp, "firmware wasn't loaded\n"); error = EBUSY; } else - sbsh_init(sc); + sbsh_init_locked(sc); } } else { - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) sbsh_stop(sc); - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - } } + SBSH_UNLOCK(sc); break; case SIOCADDMULTI: @@ -514,7 +539,6 @@ sbsh_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) break; } - splx(s); return (error); } @@ -524,18 +548,19 @@ sbsh_shutdown(device_t dev) { struct sbsh_softc *sc = device_get_softc(dev); + SBSH_LOCK(sc); sbsh_stop(sc); + SBSH_UNLOCK(sc); } static int sbsh_suspend(device_t dev) { struct sbsh_softc *sc = device_get_softc(dev); - int s; - s = splimp(); + SBSH_LOCK(sc); sbsh_stop(sc); - splx(s); + SBSH_UNLOCK(sc); return (0); } @@ -545,43 +570,30 @@ sbsh_resume(device_t dev) { struct sbsh_softc *sc = device_get_softc(dev); struct ifnet *ifp; - int s; - s = splimp(); + SBSH_LOCK(sc); ifp = sc->ifp; - if (ifp->if_flags & IFF_UP) - sbsh_init(sc); + sbsh_init_locked(sc); + SBSH_UNLOCK(sc); - splx(s); return (0); } - -static void -sbsh_watchdog(struct ifnet *ifp) -{ - struct sbsh_softc *sc = ifp->if_softc; - - if_printf(ifp, "transmit timeout\n"); - - if (sc->regs->SR & TXS) { - sc->regs->SR = TXS; - if_printf(ifp, "interrupt posted but not delivered\n"); - } - free_sent_buffers(sc); -} - /* -------------------------------------------------------------------------- */ static void sbsh_intr(void *arg) { struct sbsh_softc *sc = (struct sbsh_softc *)arg; - u_int8_t status = sc->regs->SR; + u_int8_t status; - if (status == 0) + SBSH_LOCK(sc); + status = sc->regs->SR; + if (status == 0) { + SBSH_UNLOCK(sc); return; + } if (status & EXT) { cx28975_interrupt(sc); @@ -617,6 +629,7 @@ sbsh_intr(void *arg) ++sc->ifp->if_ierrors; sc->regs->SR = OFL; } + SBSH_UNLOCK(sc); } /* @@ -817,11 +830,13 @@ indicate_frames(struct sbsh_softc *sc) sc->rbd[sc->head_rdesc].length & 0x7ff; m->m_pkthdr.rcvif = sc->ifp; - (*sc->ifp->if_input)(sc->ifp, m); ++sc->in_stats.rcvd_pkts; ++sc->ifp->if_ipackets; sc->head_rdesc = (sc->head_rdesc + 1) & 0x7f; + SBSH_UNLOCK(sc); + (*sc->ifp->if_input)(sc->ifp, m); + SBSH_LOCK(sc); } } @@ -943,14 +958,14 @@ start_cx28975(struct sbsh_softc *sc, struct cx28975_cfg cfg) if (cfg.wburst) sc->regs->CRB |= WTBE; - tsleep(sc, PWAIT, "sbsh", 0); + mtx_sleep(sc, &sc->lock, PWAIT, "sbsh", 0); if ((p->out_ack & 0x1f) != _ACK_BOOT_WAKE_UP) return (-1); if (download_firmware(sc, cfg.firmw_image, cfg.firmw_len)) return (-1); - tsleep(sc, PWAIT, "sbsh", 0); + mtx_sleep(sc, &sc->lock, PWAIT, "sbsh", 0); if ((p->out_ack & 0x1f) != _ACK_OPER_WAKE_UP) return (-1); @@ -1059,7 +1074,11 @@ issue_cx28975_cmd(struct sbsh_softc *sc, u_int8_t cmd, p->out_ack = _ACK_NOT_COMPLETE; p->intr_8051 = 0xfe; - if (tsleep(sc, PWAIT, "sbsh", hz << 3)) + /* + * XXX: Sleeping here probably breaks lots of things. This should + * probably be doing a spin loop with a DELAY or some such. + */ + if (mtx_sleep(sc, &sc->lock, PWAIT, "sbsh", hz << 3)) return (-1); while (p->out_ack == _ACK_NOT_COMPLETE) |