diff options
author | Brad Smith <brad@cvs.openbsd.org> | 2008-06-24 23:02:43 +0000 |
---|---|---|
committer | Brad Smith <brad@cvs.openbsd.org> | 2008-06-24 23:02:43 +0000 |
commit | 29bb8e08307aa1d8f81991447c687dc9f664582c (patch) | |
tree | 93a1fbb0e1cd37437c998956cf47fb0c8a09a983 | |
parent | eb30d8959a1448ff7a7b73c410533fd80508f0af (diff) |
Fixed a problem that would cause errors (especially when in low memory
systems) because the RX chain was corrupted when an mbuf was mapped to
an unexpected number of buffers.
From davidch@FreeBSD
-rw-r--r-- | sys/dev/pci/if_bnx.c | 259 | ||||
-rw-r--r-- | sys/dev/pci/if_bnxreg.h | 16 |
2 files changed, 177 insertions, 98 deletions
diff --git a/sys/dev/pci/if_bnx.c b/sys/dev/pci/if_bnx.c index 7048f52554c..0df4f6bd894 100644 --- a/sys/dev/pci/if_bnx.c +++ b/sys/dev/pci/if_bnx.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_bnx.c,v 1.63 2008/06/13 21:40:21 brad Exp $ */ +/* $OpenBSD: if_bnx.c,v 1.64 2008/06/24 23:02:42 brad Exp $ */ /*- * Copyright (c) 2006 Broadcom Corporation @@ -374,6 +374,7 @@ int bnx_get_buf(struct bnx_softc *, struct mbuf *, u_int16_t *, u_int16_t *, u_int32_t *); int bnx_init_tx_chain(struct bnx_softc *); +void bnx_fill_rx_chain(struct bnx_softc *); int bnx_init_rx_chain(struct bnx_softc *); void bnx_free_rx_chain(struct bnx_softc *); void bnx_free_tx_chain(struct bnx_softc *); @@ -2929,7 +2930,7 @@ bnx_stop(struct bnx_softc *sc) /* Tell firmware that the driver is going away. */ bnx_reset(sc, BNX_DRV_MSG_CODE_SUSPEND_NO_WOL); - /* Free the RX lists. */ + /* Free RX buffers. */ bnx_free_rx_chain(sc); /* Free TX buffers. */ @@ -3275,11 +3276,12 @@ bnx_get_buf(struct bnx_softc *sc, struct mbuf *m, u_int16_t *prod, "0x%04X, prod_bseq = 0x%08X\n", __FUNCTION__, *prod, *chain_prod, *prod_bseq); + /* Check whether this is a new mbuf allocation. */ if (m == NULL) { + /* Simulate an mbuf allocation failure. */ DBRUNIF(DB_RANDOMTRUE(bnx_debug_mbuf_allocation_failure), - BNX_PRINTF(sc, "Simulating mbuf allocation failure.\n"); - sc->mbuf_alloc_failed++; + sc->mbuf_sim_alloc_failed++; rc = ENOBUFS; goto bnx_get_buf_exit); @@ -3290,13 +3292,24 @@ bnx_get_buf(struct bnx_softc *sc, struct mbuf *m, u_int16_t *prod, "%s(%d): RX mbuf header allocation failed!\n", __FILE__, __LINE__); - DBRUNIF(1, sc->mbuf_alloc_failed++); + sc->mbuf_alloc_failed++; rc = ENOBUFS; goto bnx_get_buf_exit; } DBRUNIF(1, sc->rx_mbuf_alloc++); + + /* Simulate an mbuf cluster allocation failure. */ + DBRUNIF(DB_RANDOMTRUE(bnx_debug_mbuf_allocation_failure), + m_freem(m_new); + sc->rx_mbuf_alloc--; + sc->mbuf_alloc_failed++; + sc->mbuf_sim_alloc_failed++; + rc = ENOBUFS; + goto bnx_get_buf_exit); + + /* Attach a cluster to the mbuf. */ MCLGET(m_new, M_DONTWAIT); if (!(m_new->m_flags & M_EXT)) { DBPRINT(sc, BNX_WARN, @@ -3304,16 +3317,17 @@ bnx_get_buf(struct bnx_softc *sc, struct mbuf *m, u_int16_t *prod, __FILE__, __LINE__); m_freem(m_new); - DBRUNIF(1, sc->rx_mbuf_alloc--); - DBRUNIF(1, sc->mbuf_alloc_failed++); + sc->mbuf_alloc_failed++; rc = ENOBUFS; goto bnx_get_buf_exit; } + /* Initialize the mbuf cluster. */ m_new->m_len = m_new->m_pkthdr.len = sc->mbuf_alloc_size; } else { + /* Reuse an existing mbuf. */ m_new = m; m_new->m_len = m_new->m_pkthdr.len = sc->mbuf_alloc_size; m_new->m_data = m_new->m_ext.ext_buf; @@ -3327,22 +3341,32 @@ bnx_get_buf(struct bnx_softc *sc, struct mbuf *m, u_int16_t *prod, __FILE__, __LINE__); m_freem(m_new); - DBRUNIF(1, sc->rx_mbuf_alloc--); rc = ENOBUFS; goto bnx_get_buf_exit; } - /* Watch for overflow. */ - DBRUNIF((sc->free_rx_bd > USABLE_RX_BD), - printf("%s: Too many free rx_bd (0x%04X > 0x%04X)!\n", - sc->free_rx_bd, (u_int16_t) USABLE_RX_BD)); + /* Make sure there is room in the receive chain. */ + if (map->dm_nsegs > sc->free_rx_bd) { + bus_dmamap_unload(sc->bnx_dmatag, map); + + m_freem(m_new); + DBRUNIF(1, sc->rx_mbuf_alloc--); + + rc = EFBIG; + goto bnx_get_buf_exit; + } + +#ifdef BNX_DEBUG + /* Track the distribution of buffer segments. */ + sc->rx_mbuf_segs[map->dm_nsegs]++; +#endif /* Update some debug statistics counters */ DBRUNIF((sc->free_rx_bd < sc->rx_low_watermark), sc->rx_low_watermark = sc->free_rx_bd); - DBRUNIF((sc->free_rx_bd == 0), sc->rx_empty_count++); + DBRUNIF((sc->free_rx_bd == sc->max_rx_bd), sc->rx_empty_count++); /* Setup the rx_bd for the first segment. */ rxbd = &sc->rx_bd_chain[RX_PAGE(*chain_prod)][RX_IDX(*chain_prod)]; @@ -3503,6 +3527,8 @@ bnx_free_tx_chain(struct bnx_softc *sc) for (i = 0; i < TX_PAGES; i++) bzero((char *)sc->tx_bd_chain[i], BNX_TX_CHAIN_PAGE_SZ); + sc->used_tx_bd = 0; + /* Check if we lost any mbufs in the process. */ DBRUNIF((sc->tx_mbuf_alloc), printf("%s: Memory leak! Lost %d mbufs from tx chain!\n", @@ -3512,6 +3538,60 @@ bnx_free_tx_chain(struct bnx_softc *sc) } /****************************************************************************/ +/* Add mbufs to the RX chain until its full or an mbuf allocation error */ +/* occurs. */ +/* */ +/* Returns: */ +/* Nothing */ +/****************************************************************************/ +void +bnx_fill_rx_chain(struct bnx_softc *sc) +{ + u_int16_t prod, chain_prod; + u_int32_t prod_bseq; +#ifdef BNX_DEBUG + int rx_mbuf_alloc_before, free_rx_bd_before; +#endif + + DBPRINT(sc, BNX_EXCESSIVE_RECV, "Entering %s()\n", __FUNCTION__); + + prod = sc->rx_prod; + prod_bseq = sc->rx_prod_bseq; + +#ifdef BNX_DEBUG + rx_mbuf_alloc_before = sc->rx_mbuf_alloc; + free_rx_bd_before = sc->free_rx_bd; +#endif + + /* Keep filling the RX chain until it's full. */ + while (sc->free_rx_bd > 0) { + chain_prod = RX_CHAIN_IDX(prod); + if (bnx_get_buf(sc, NULL, &prod, &chain_prod, &prod_bseq)) { + /* Bail out if we can't add an mbuf to the chain. */ + break; + } + prod = NEXT_RX_BD(prod); + } + +#if 0 + DBRUNIF((sc->rx_mbuf_alloc - rx_mbuf_alloc_before), + BNX_PRINTF(sc, "%s(): Installed %d mbufs in %d rx_bd entries.\n", + __FUNCTION__, (sc->rx_mbuf_alloc - rx_mbuf_alloc_before), + (free_rx_bd_before - sc->free_rx_bd))); +#endif + + /* Save the RX chain producer index. */ + sc->rx_prod = prod; + sc->rx_prod_bseq = prod_bseq; + + /* Tell the chip about the waiting rx_bd's. */ + REG_WR16(sc, MB_RX_CID_ADDR + BNX_L2CTX_HOST_BDIDX, sc->rx_prod); + REG_WR(sc, MB_RX_CID_ADDR + BNX_L2CTX_HOST_BSEQ, sc->rx_prod_bseq); + + DBPRINT(sc, BNX_EXCESSIVE_RECV, "Exiting %s()\n", __FUNCTION__); +} + +/****************************************************************************/ /* Allocate memory and initialize the RX data structures. */ /* */ /* Returns: */ @@ -3522,8 +3602,7 @@ bnx_init_rx_chain(struct bnx_softc *sc) { struct rx_bd *rxbd; int i, rc = 0; - u_int16_t prod, chain_prod; - u_int32_t prod_bseq, val, addr; + u_int32_t val, addr; DBPRINT(sc, BNX_VERBOSE_RESET, "Entering %s()\n", __FUNCTION__); @@ -3567,32 +3646,14 @@ bnx_init_rx_chain(struct bnx_softc *sc) val = (u_int32_t)(sc->rx_bd_chain_paddr[0]); CTX_WR(sc, GET_CID_ADDR(RX_CID), BNX_L2CTX_NX_BDHADDR_LO, val); - /* Allocate mbuf clusters for the rx_bd chain. */ - prod = prod_bseq = 0; - while (prod < TOTAL_RX_BD) { - chain_prod = RX_CHAIN_IDX(prod); - if (bnx_get_buf(sc, NULL, &prod, &chain_prod, &prod_bseq)) { - BNX_PRINTF(sc, "Error filling RX chain: rx_bd[0x%04X]!\n", - chain_prod); - rc = ENOBUFS; - break; - } - prod = NEXT_RX_BD(prod); - } - - /* Save the RX chain producer index. */ - sc->rx_prod = prod; - sc->rx_prod_bseq = prod_bseq; + /* Fill up the RX chain. */ + bnx_fill_rx_chain(sc); for (i = 0; i < RX_PAGES; i++) bus_dmamap_sync(sc->bnx_dmatag, sc->rx_bd_chain_map[i], 0, sc->rx_bd_chain_map[i]->dm_mapsize, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); - /* Tell the chip about the waiting rx_bd's. */ - REG_WR16(sc, MB_RX_CID_ADDR + BNX_L2CTX_HOST_BDIDX, sc->rx_prod); - REG_WR(sc, MB_RX_CID_ADDR + BNX_L2CTX_HOST_BSEQ, sc->rx_prod_bseq); - DBRUN(BNX_VERBOSE_RECV, bnx_dump_rx_chain(sc, 0, TOTAL_RX_BD)); DBPRINT(sc, BNX_VERBOSE_RESET, "Exiting %s()\n", __FUNCTION__); @@ -3610,9 +3671,16 @@ void bnx_free_rx_chain(struct bnx_softc *sc) { int i; +#ifdef BNX_DEBUG + int rx_mbuf_alloc_before; +#endif DBPRINT(sc, BNX_VERBOSE_RESET, "Entering %s()\n", __FUNCTION__); +#ifdef BNX_DEBUG + rx_mbuf_alloc_before = sc->rx_mbuf_alloc; +#endif + /* Free any mbufs still in the RX mbuf chain. */ for (i = 0; i < TOTAL_RX_BD; i++) { if (sc->rx_mbuf_ptr[i] != NULL) { @@ -3627,10 +3695,16 @@ bnx_free_rx_chain(struct bnx_softc *sc) } } + DBRUNIF((rx_mbuf_alloc_before - sc->rx_mbuf_alloc), + BNX_PRINTF(sc, "%s(): Released %d mbufs.\n", + __FUNCTION__, (rx_mbuf_alloc_before - sc->rx_mbuf_alloc))); + /* Clear each RX chain page. */ for (i = 0; i < RX_PAGES; i++) bzero((char *)sc->rx_bd_chain[i], BNX_RX_CHAIN_PAGE_SZ); + sc->free_rx_bd = sc->max_rx_bd; + /* Check if we lost any mbufs in the process. */ DBRUNIF((sc->rx_mbuf_alloc), printf("%s: Memory leak! Lost %d mbufs from rx chain!\n", @@ -3779,7 +3853,7 @@ bnx_rx_intr(struct bnx_softc *sc) /* Update some debug statistics counters */ DBRUNIF((sc->free_rx_bd < sc->rx_low_watermark), sc->rx_low_watermark = sc->free_rx_bd); - DBRUNIF((sc->free_rx_bd == 0), sc->rx_empty_count++); + DBRUNIF((sc->free_rx_bd == USABLE_RX_BD), sc->rx_empty_count++); /* * Scan through the receive chain as long @@ -3791,6 +3865,9 @@ bnx_rx_intr(struct bnx_softc *sc) unsigned int len; u_int32_t status; + /* Clear the mbuf pointer. */ + m = NULL; + /* Convert the producer/consumer indices to an actual * rx_bd index. */ @@ -3826,7 +3903,7 @@ bnx_rx_intr(struct bnx_softc *sc) bus_dmamap_unload(sc->bnx_dmatag, sc->rx_mbuf_map[sw_chain_cons]); - /* Remove the mbuf from the driver's chain. */ + /* Remove the mbuf from RX chain. */ m = sc->rx_mbuf_ptr[sw_chain_cons]; sc->rx_mbuf_ptr[sw_chain_cons] = NULL; @@ -3866,39 +3943,12 @@ bnx_rx_intr(struct bnx_softc *sc) L2_FHDR_ERRORS_ALIGNMENT | L2_FHDR_ERRORS_TOO_SHORT | L2_FHDR_ERRORS_GIANT_FRAME)) { + /* Log the error and release the mbuf. */ ifp->if_ierrors++; DBRUNIF(1, sc->l2fhdr_status_errors++); - /* Reuse the mbuf for a new frame. */ - if (bnx_get_buf(sc, m, &sw_prod, - &sw_chain_prod, &sw_prod_bseq)) { - DBRUNIF(1, bnx_breakpoint(sc)); - panic("%s: Can't reuse RX mbuf!\n", - sc->bnx_dev.dv_xname); - } - goto bnx_rx_int_next_rx; - } - - /* - * Get a new mbuf for the rx_bd. If no new - * mbufs are available then reuse the current mbuf, - * log an ierror on the interface, and generate - * an error in the system log. - */ - if (bnx_get_buf(sc, NULL, &sw_prod, &sw_chain_prod, - &sw_prod_bseq)) { - DBRUN(BNX_WARN, BNX_PRINTF(sc, "Failed to allocate " - "new mbuf, incoming frame dropped!\n")); - - ifp->if_ierrors++; - - /* Try and reuse the exisitng mbuf. */ - if (bnx_get_buf(sc, m, &sw_prod, - &sw_chain_prod, &sw_prod_bseq)) { - DBRUNIF(1, bnx_breakpoint(sc)); - panic("%s: Double mbuf allocation " - "failure!", sc->bnx_dev.dv_xname); - } + m_freem(m); + m = NULL; goto bnx_rx_int_next_rx; } @@ -3990,6 +4040,19 @@ bnx_rx_intr(struct bnx_softc *sc) #endif } + /* Pass the mbuf off to the upper layers. */ + ifp->if_ipackets++; + +bnx_rx_int_next_rx: + sw_prod = NEXT_RX_BD(sw_prod); + } + + sw_cons = NEXT_RX_BD(sw_cons); + + /* If we have a packet, pass it up the stack */ + if (m) { + sc->rx_cons = sw_cons; + #if NBPFILTER > 0 /* * Handle BPF listeners. Let the BPF @@ -3999,19 +4062,14 @@ bnx_rx_intr(struct bnx_softc *sc) bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN); #endif - /* Pass the mbuf off to the upper layers. */ - ifp->if_ipackets++; DBPRINT(sc, BNX_VERBOSE_RECV, "%s(): Passing received frame up.\n", __FUNCTION__); ether_input_mbuf(ifp, m); DBRUNIF(1, sc->rx_mbuf_alloc--); -bnx_rx_int_next_rx: - sw_prod = NEXT_RX_BD(sw_prod); + sw_cons = sc->rx_cons; } - sw_cons = NEXT_RX_BD(sw_cons); - /* Refresh hw_cons to see if there's new work */ if (sw_cons == hw_cons) { hw_cons = sc->hw_rx_cons = @@ -4028,19 +4086,16 @@ bnx_rx_int_next_rx: BUS_SPACE_BARRIER_READ); } + /* No new packets to process. Refill the RX chain and exit. */ + sc->rx_cons = sw_cons; + bnx_fill_rx_chain(sc); + for (i = 0; i < RX_PAGES; i++) bus_dmamap_sync(sc->bnx_dmatag, sc->rx_bd_chain_map[i], 0, sc->rx_bd_chain_map[i]->dm_mapsize, BUS_DMASYNC_PREWRITE); - sc->rx_cons = sw_cons; - sc->rx_prod = sw_prod; - sc->rx_prod_bseq = sw_prod_bseq; - - REG_WR16(sc, MB_RX_CID_ADDR + BNX_L2CTX_HOST_BDIDX, sc->rx_prod); - REG_WR(sc, MB_RX_CID_ADDR + BNX_L2CTX_HOST_BSEQ, sc->rx_prod_bseq); - DBPRINT(sc, BNX_INFO_RECV, "%s(exit): rx_prod = 0x%04X, " "rx_cons = 0x%04X, rx_prod_bseq = 0x%08X\n", __FUNCTION__, sc->rx_prod, sc->rx_cons, sc->rx_prod_bseq); @@ -4365,6 +4420,7 @@ bnx_tx_encap(struct bnx_softc *sc, struct mbuf **m_head) sc->bnx_dev.dv_xname); m_freem(m0); *m_head = NULL; + sc->tx_dma_map_failures++; return (error); } @@ -4635,6 +4691,13 @@ bnx_watchdog(struct ifnet *ifp) DBRUN(BNX_WARN_SEND, bnx_dump_driver_state(sc); bnx_dump_status_block(sc)); + /* + * If we are in this routine because of pause frames, then + * don't reset the hardware. + */ + if (REG_RD(sc, BNX_EMAC_TX_STATUS) & BNX_EMAC_TX_STATUS_XOFFED) + return; + printf("%s: Watchdog timeout occurred, resetting!\n", ifp->if_xname); @@ -5715,6 +5778,18 @@ bnx_dump_driver_state(struct bnx_softc *sc) " 0x%08X - (sc->tx_prod_bseq) tx producer bseq index\n", sc->tx_prod_bseq); + BNX_PRINTF(sc, + " 0x%08X - (sc->tx_mbuf_alloc) tx mbufs allocated\n", + sc->tx_mbuf_alloc); + + BNX_PRINTF(sc, + " 0x%08X - (sc->used_tx_bd) used tx_bd's\n", + sc->used_tx_bd); + + BNX_PRINTF(sc, + " 0x%08X/%08X - (sc->tx_hi_watermark) tx hi watermark\n", + sc->tx_hi_watermark, sc->max_tx_bd); + BNX_PRINTF(sc, " 0x%08X - (sc->rx_prod) rx producer index\n", sc->rx_prod); @@ -5737,22 +5812,14 @@ bnx_dump_driver_state(struct bnx_softc *sc) sc->rx_low_watermark, sc->max_rx_bd); BNX_PRINTF(sc, - " 0x%08X - (sc->txmbuf_alloc) tx mbufs allocated\n", - sc->tx_mbuf_alloc); - - BNX_PRINTF(sc, - " 0x%08X - (sc->rx_mbuf_alloc) rx mbufs allocated\n", - sc->rx_mbuf_alloc); - - BNX_PRINTF(sc, " 0x%08X - (sc->used_tx_bd) used tx_bd's\n", - sc->used_tx_bd); - - BNX_PRINTF(sc, "0x%08X/%08X - (sc->tx_hi_watermark) tx hi watermark\n", - sc->tx_hi_watermark, sc->max_tx_bd); + " 0x%08X - (sc->mbuf_alloc_failed) " + "mbuf alloc failures\n", + sc->mbuf_alloc_failed); BNX_PRINTF(sc, - " 0x%08X - (sc->mbuf_alloc_failed) failed mbuf alloc\n", - sc->mbuf_alloc_failed); + " 0x%0X - (sc->mbuf_sim_allocated_failed) " + "simulated mbuf alloc failures\n", + sc->mbuf_sim_alloc_failed); BNX_PRINTF(sc, "-------------------------------------------" "-----------------------------\n"); @@ -5828,10 +5895,10 @@ bnx_breakpoint(struct bnx_softc *sc) bnx_dump_txbd(sc, 0, NULL); bnx_dump_rxbd(sc, 0, NULL); bnx_dump_tx_mbuf_chain(sc, 0, USABLE_TX_BD); - bnx_dump_rx_mbuf_chain(sc, 0, USABLE_RX_BD); + bnx_dump_rx_mbuf_chain(sc, 0, sc->max_rx_bd); bnx_dump_l2fhdr(sc, 0, NULL); bnx_dump_tx_chain(sc, 0, USABLE_TX_BD); - bnx_dump_rx_chain(sc, 0, USABLE_RX_BD); + bnx_dump_rx_chain(sc, 0, sc->max_rx_bd); bnx_dump_status_block(sc); bnx_dump_stats_block(sc); bnx_dump_driver_state(sc); diff --git a/sys/dev/pci/if_bnxreg.h b/sys/dev/pci/if_bnxreg.h index 3631be964b5..e8763c70666 100644 --- a/sys/dev/pci/if_bnxreg.h +++ b/sys/dev/pci/if_bnxreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_bnxreg.h,v 1.25 2008/06/13 07:40:30 brad Exp $ */ +/* $OpenBSD: if_bnxreg.h,v 1.26 2008/06/24 23:02:42 brad Exp $ */ /*- * Copyright (c) 2006 Broadcom Corporation @@ -424,6 +424,9 @@ struct flash_spec { #define BNX_DRV_PULSE_MB 0x00000010 #define BNX_DRV_PULSE_SEQ_MASK 0x00007fff +#define BNX_MB_ARGS_0 0x00000014 +#define BNX_MB_ARGS_1 0x00000018 + /* Indicate to the firmware not to go into the * OS absent when it is not getting driver pulse. * This is used for debugging. */ @@ -4811,11 +4814,20 @@ struct bnx_softc u_int32_t stat_CatchupInMBUFDiscards; u_int32_t stat_CatchupInRuleCheckerP4Hit; + /* Mbuf allocation failure counter. */ + u_int32_t mbuf_alloc_failed; + + /* TX DMA mapping failure counter. */ + u_int32_t tx_dma_map_failures; + #ifdef BNX_DEBUG /* Track the number of enqueued mbufs. */ int tx_mbuf_alloc; int rx_mbuf_alloc; + /* Track the distribution buffer segments. */ + u_int32_t rx_mbuf_segs[BNX_MAX_SEGMENTS+1]; + /* Track how many and what type of interrupts are generated. */ u_int32_t interrupts_generated; u_int32_t interrupts_handled; @@ -4826,7 +4838,7 @@ struct bnx_softc u_int32_t rx_empty_count; /* Number of times the RX chain was empty. */ u_int32_t tx_hi_watermark; /* Greatest number of tx_bd's used. */ u_int32_t tx_full_count; /* Number of times the TX chain was full. */ - u_int32_t mbuf_alloc_failed; /* Mbuf allocation failure counter. */ + u_int32_t mbuf_sim_alloc_failed; /* Mbuf simulated allocation failure counter. */ u_int32_t l2fhdr_status_errors; u_int32_t unexpected_attentions; u_int32_t lost_status_block_updates; |