diff options
author | gallatin <gallatin@FreeBSD.org> | 2007-01-31 19:53:36 +0000 |
---|---|---|
committer | gallatin <gallatin@FreeBSD.org> | 2007-01-31 19:53:36 +0000 |
commit | dd7403e0de050efaf4124919bd6a3875fdcf19ee (patch) | |
tree | c20b923e8fef79013beec96769f479fdd335d43f /sys/dev/mxge | |
parent | 11d416de5c231b3b270290e5cc257554053a2826 (diff) | |
download | FreeBSD-src-dd7403e0de050efaf4124919bd6a3875fdcf19ee.zip FreeBSD-src-dd7403e0de050efaf4124919bd6a3875fdcf19ee.tar.gz |
- Add 99% of a callout based watchdog. The remaining 1% is waiting
for pci_cfg_restore() to be exported. It was tested using a
hackily accessed pci_cfg_restore().
- Add ifmedia_removeall() to mxge_detach() in order to stop leaking
an ifaddr
- Fix a small acounting bug introduced by the locking code shuffle
which could cause spurious watchdog resets now that we have a
watchdog.
Sponsored by: Myricom
Diffstat (limited to 'sys/dev/mxge')
-rw-r--r-- | sys/dev/mxge/if_mxge.c | 194 | ||||
-rw-r--r-- | sys/dev/mxge/if_mxge_var.h | 4 |
2 files changed, 168 insertions, 30 deletions
diff --git a/sys/dev/mxge/if_mxge.c b/sys/dev/mxge/if_mxge.c index 25eafdd..be8ab0e 100644 --- a/sys/dev/mxge/if_mxge.c +++ b/sys/dev/mxge/if_mxge.c @@ -90,6 +90,7 @@ static int mxge_intr_coal_delay = 30; static int mxge_deassert_wait = 1; static int mxge_flow_control = 1; static int mxge_verbose = 0; +static int mxge_ticks; static char *mxge_fw_unaligned = "mxge_ethp_z8e"; static char *mxge_fw_aligned = "mxge_eth_z8e"; @@ -2104,12 +2105,6 @@ mxge_intr(void *arg) } static void -mxge_watchdog(struct ifnet *ifp) -{ - printf("%s called\n", __FUNCTION__); -} - -static void mxge_init(void *arg) { } @@ -2140,6 +2135,7 @@ mxge_free_mbufs(mxge_softc_t *sc) } for (i = 0; i <= sc->tx.mask; i++) { + sc->tx.info[i].flag = 0; if (sc->tx.info[i].m == NULL) continue; bus_dmamap_unload(sc->tx.dmat, @@ -2401,7 +2397,7 @@ mxge_open(mxge_softc_t *sc) } bzero(sc->rx_done.entry, mxge_max_intr_slots * sizeof(*sc->rx_done.entry)); - + if (MCLBYTES >= sc->ifp->if_mtu + ETHER_HDR_LEN + MXGEFW_PAD) sc->big_bytes = MCLBYTES; @@ -2533,7 +2529,7 @@ mxge_close(mxge_softc_t *sc) } if (old_down_cnt == sc->down_cnt) { /* wait for down irq */ - DELAY(2 * sc->intr_coal_delay); + DELAY(10 * sc->intr_coal_delay); } if (old_down_cnt == sc->down_cnt) { device_printf(sc->dev, "never got down irq\n"); @@ -2544,6 +2540,149 @@ mxge_close(mxge_softc_t *sc) return 0; } +static void +mxge_setup_cfg_space(mxge_softc_t *sc) +{ + device_t dev = sc->dev; + int reg; + uint16_t cmd, lnk, pectl; + + /* find the PCIe link width and set max read request to 4KB*/ + if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { + lnk = pci_read_config(dev, reg + 0x12, 2); + sc->link_width = (lnk >> 4) & 0x3f; + + pectl = pci_read_config(dev, reg + 0x8, 2); + pectl = (pectl & ~0x7000) | (5 << 12); + pci_write_config(dev, reg + 0x8, pectl, 2); + } + + /* Enable DMA and Memory space access */ + pci_enable_busmaster(dev); + cmd = pci_read_config(dev, PCIR_COMMAND, 2); + cmd |= PCIM_CMD_MEMEN; + pci_write_config(dev, PCIR_COMMAND, cmd, 2); +} + +static uint32_t +mxge_read_reboot(mxge_softc_t *sc) +{ + device_t dev = sc->dev; + uint32_t vs; + + /* find the vendor specific offset */ + if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) { + device_printf(sc->dev, + "could not find vendor specific offset\n"); + return (uint32_t)-1; + } + /* enable read32 mode */ + pci_write_config(dev, vs + 0x10, 0x3, 1); + /* tell NIC which register to read */ + pci_write_config(dev, vs + 0x18, 0xfffffff0, 4); + return (pci_read_config(dev, vs + 0x14, 4)); +} + +static void +mxge_watchdog_reset(mxge_softc_t *sc) +{ + int err; + uint32_t reboot; + uint16_t cmd; + + err = ENXIO; + + device_printf(sc->dev, "Watchdog reset!\n"); + + /* + * check to see if the NIC rebooted. If it did, then all of + * PCI config space has been reset, and things like the + * busmaster bit will be zero. If this is the case, then we + * must restore PCI config space before the NIC can be used + * again + */ + cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); + if (cmd == 0xffff) { + /* + * maybe the watchdog caught the NIC rebooting; wait + * up to 100ms for it to finish. If it does not come + * back, then give up + */ + DELAY(1000*100); + cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); + if (cmd == 0xffff) { + device_printf(sc->dev, "NIC disappeared!\n"); + goto abort; + } + } + if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) { + /* print the reboot status */ + reboot = mxge_read_reboot(sc); + device_printf(sc->dev, "NIC rebooted, status = 0x%x\n", + reboot); + /* restore PCI configuration space */ + + /* XXXX waiting for pci_cfg_restore() to be exported */ + goto abort; /* just abort for now */ + + /* and redo any changes we made to our config space */ + mxge_setup_cfg_space(sc); + } else { + device_printf(sc->dev, "NIC did not reboot, ring state:\n"); + device_printf(sc->dev, "tx.req=%d tx.done=%d\n", + sc->tx.req, sc->tx.done); + device_printf(sc->dev, "pkt_done=%d fw=%d\n", + sc->tx.pkt_done, + be32toh(sc->fw_stats->send_done_count)); + } + + if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) { + mxge_close(sc); + err = mxge_open(sc); + } + +abort: + /* + * stop the watchdog if the nic is dead, to avoid spamming the + * console + */ + if (err != 0) { + callout_stop(&sc->co_hdl); + } +} + +static void +mxge_watchdog(mxge_softc_t *sc) +{ + mxge_tx_buf_t *tx = &sc->tx; + + /* see if we have outstanding transmits, which + have been pending for more than mxge_ticks */ + if (tx->req != tx->done && + tx->watchdog_req != tx->watchdog_done && + tx->done == tx->watchdog_done) + mxge_watchdog_reset(sc); + + tx->watchdog_req = tx->req; + tx->watchdog_done = tx->done; +} + +static void +mxge_tick(void *arg) +{ + mxge_softc_t *sc = arg; + + + /* Synchronize with possible callout reset/stop. */ + if (callout_pending(&sc->co_hdl) || + !callout_active(&sc->co_hdl)) { + mtx_unlock(&sc->driver_mtx); + return; + } + + callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); + mxge_watchdog(sc); +} static int mxge_media_change(struct ifnet *ifp) @@ -2567,6 +2706,7 @@ mxge_change_mtu(mxge_softc_t *sc, int mtu) old_mtu = ifp->if_mtu; ifp->if_mtu = mtu; if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + callout_stop(&sc->co_hdl); mxge_close(sc); err = mxge_open(sc); if (err != 0) { @@ -2574,6 +2714,7 @@ mxge_change_mtu(mxge_softc_t *sc, int mtu) mxge_close(sc); (void) mxge_open(sc); } + callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); } mtx_unlock(&sc->driver_mtx); return err; @@ -2614,9 +2755,11 @@ mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) case SIOCSIFFLAGS: mtx_lock(&sc->driver_mtx); if (ifp->if_flags & IFF_UP) { - if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { err = mxge_open(sc); - else { + callout_reset(&sc->co_hdl, mxge_ticks, + mxge_tick, sc); + } else { /* take care of promis can allmulti flag chages */ mxge_change_promisc(sc, @@ -2624,8 +2767,10 @@ mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) mxge_set_multicast_list(sc); } } else { - if (ifp->if_drv_flags & IFF_DRV_RUNNING) + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { mxge_close(sc); + callout_stop(&sc->co_hdl); + } } mtx_unlock(&sc->driver_mtx); break; @@ -2701,11 +2846,14 @@ mxge_fetch_tunables(mxge_softc_t *sc) &mxge_deassert_wait); TUNABLE_INT_FETCH("hw.mxge.verbose", &mxge_verbose); + TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks); if (bootverbose) mxge_verbose = 1; if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000) mxge_intr_coal_delay = 30; + if (mxge_ticks == 0) + mxge_ticks = hz; sc->pause = mxge_flow_control; } @@ -2715,8 +2863,7 @@ mxge_attach(device_t dev) mxge_softc_t *sc = device_get_softc(dev); struct ifnet *ifp; size_t bytes; - int count, rid, err, reg; - uint16_t cmd, pectl, lnk; + int count, rid, err; sc->dev = dev; mxge_fetch_tunables(sc); @@ -2757,22 +2904,10 @@ mxge_attach(device_t dev) mtx_init(&sc->driver_mtx, sc->driver_mtx_name, MTX_NETWORK_LOCK, MTX_DEF); - /* find the PCIe link width and set max read request to 4KB*/ - if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { - lnk = pci_read_config(dev, reg + 0x12, 2); - sc->link_width = (lnk >> 4) & 0x3f; - - pectl = pci_read_config(dev, reg + 0x8, 2); - pectl = (pectl & ~0x7000) | (5 << 12); - pci_write_config(dev, reg + 0x8, pectl, 2); - } - - /* Enable DMA and Memory space access */ - pci_enable_busmaster(dev); - cmd = pci_read_config(dev, PCIR_COMMAND, 2); - cmd |= PCIM_CMD_MEMEN; - pci_write_config(dev, PCIR_COMMAND, cmd, 2); + callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0); + mxge_setup_cfg_space(sc); + /* Map the board into the kernel */ rid = PCIR_BARS; sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, @@ -2888,7 +3023,6 @@ mxge_attach(device_t dev) ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = mxge_ioctl; ifp->if_start = mxge_start; - ifp->if_watchdog = mxge_watchdog; ether_ifattach(ifp, sc->mac_addr); /* ether_ifattach sets mtu to 1500 */ ifp->if_mtu = MXGE_MAX_ETHER_MTU - ETHER_HDR_LEN; @@ -2941,8 +3075,10 @@ mxge_detach(device_t dev) mtx_lock(&sc->driver_mtx); if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) mxge_close(sc); + callout_stop(&sc->co_hdl); mtx_unlock(&sc->driver_mtx); ether_ifdetach(sc->ifp); + ifmedia_removeall(&sc->media); mxge_dummy_rdma(sc, 0); bus_teardown_intr(sc->dev, sc->irq_res, sc->ih); mxge_free_rings(sc); diff --git a/sys/dev/mxge/if_mxge_var.h b/sys/dev/mxge/if_mxge_var.h index cf3c0d4..9b5d3bc 100644 --- a/sys/dev/mxge/if_mxge_var.h +++ b/sys/dev/mxge/if_mxge_var.h @@ -108,7 +108,8 @@ typedef struct int boundary; /* boundary transmits cannot cross*/ int stall; /* #times hw queue exhausted */ int wake; /* #times irq re-enabled xmit */ - + int watchdog_req; /* cache of req */ + int watchdog_done; /* cache of done */ } mxge_tx_buf_t; typedef struct { @@ -160,6 +161,7 @@ typedef struct { int fw_multicast_support; int link_width; mxge_dma_t dmabench_dma; + struct callout co_hdl; char *mac_addr_string; char product_code_string[64]; char serial_number_string[64]; |