Here's a diff, which is a little icky: it's from a version of netbsd-6 in may to a version of netbsd-6 with the bnx fixes from march, but I think it's pretty much what you want. This diff is: Approved for Public Release, Distribution Unlimited This material is based upon work supported by the Defense Advanced Research Projects Agency and Space and Naval Warfare Systems Center, Pacific, under Contract No. N66001-09-C-2073. and is the work of Bev Schwartz of BBN. A commit message is some blend of: fix potential livelock problem If the bnx driver receives packets while there are no mbuf clusters to be obtained, the packet is thrown away and the mbuf cluster is recycled. This change allows the packet to be processed and lets the receive ring slot go empty, to be refilled later. The slot can be filled either by bnx_rx_intr processing a future packet, or by bnx_tick, which periodically tries to fill any empty slots in the receive ring. Fix memory overwrite in bnx receive ring In bnx_rx_intr, there are two places where the producer can get pushed ahead of the consumer. In the while (sw_cons != hw_cons) loop, there is a check for a packet whose layer 2 data has been corrupted, and a check for being able to allocate a new mbuf cluster to be placed in the receive queue. In both cases, if the test fails, the mbuf cluster that has just been removed from the receive ring buffer is put back, and the producer index is pushed forward. However, the consumer index is not pushed forward, so the next time around, the loop, the mbuf cluster is removed from where the consumer points, but a new (or recycled) mbuf cluster is put where the producer points. This overwrites the pointer to another mbuf cluster. The mbuf cluster is lost forever, and the receive buffer ends up filled with NULLs because the consumer and producer indices stay out of sync. Eventually all mbuf clusters are lost, and no more communication can happen over any interface. Instead of calling "continue" in each of these cases, a "goto" is used to leave the if conditional, but continue on with the processing in the loop. This remaining processing in the loop is what pushes the consumer index forward. A KASSERT has been added to bnx_add_buf to check that any mbuf cluster put into the receive ring is actually going into an available slot. This conditional was essential to finding this problem. bnx_tick now informs chip when the producer pointer has moved bnx_tick calls bnx_get_buf in case any slots in the receiver ring need to be filled with an mbuf cluster. Before this fix, bnx_tick never communicated the change to the chip itself. This can cause an interface freeze when the chip and the cpu get out of sync on where they each think the producer is pointing. (The chip will think there are no buffers for receiving packets, and the cpu doesn't move along because its waiting for the chip to signal that there are new packets.) Added a counter to track how many times bnx_tick successfully acquires mbuf clusters. Update hw consumer index In bnx_rx_intr, there is a check to see if the chip has detected new incoming packets, and if so, update software counters and continue processing. However, this code never actually pulled in the value from the chip which lets the cpu know if there's new work. This fix adds a single statement which gets values from the chip. diff --git a/src/sys/dev/pci/if_bnx.c b/src/sys/dev/pci/if_bnx.c index 711ec34..3d029ec 100644 --- a/src/sys/dev/pci/if_bnx.c +++ b/src/sys/dev/pci/if_bnx.c @@ -3730,6 +3730,7 @@ bnx_add_buf(struct bnx_softc *sc, struct mbuf *m_new, u_int16_t *prod, * last rx_bd entry to that rx_mbuf_ptr and rx_mbuf_map matches) * and update counter. */ + KASSERT(sc->rx_mbuf_ptr[*chain_prod] == NULL); sc->rx_mbuf_ptr[*chain_prod] = m_new; sc->rx_mbuf_map[first_chain_prod] = sc->rx_mbuf_map[*chain_prod]; sc->rx_mbuf_map[*chain_prod] = map; @@ -4436,7 +4437,7 @@ bnx_rx_intr(struct bnx_softc *sc) len > (BNX_MAX_JUMBO_ETHER_MTU_VLAN - ETHER_CRC_LEN)) { ifp->if_ierrors++; - DBRUNIF(1, sc->l2fhdr_status_errors++); + sc->l2fhdr_status_errors++; /* Reuse the mbuf for a new frame. */ if (bnx_add_buf(sc, m, &sw_prod, @@ -4445,7 +4446,7 @@ bnx_rx_intr(struct bnx_softc *sc) panic("%s: Can't reuse RX mbuf!\n", device_xname(sc->bnx_dev)); } - continue; + goto bnx_rx_receive_error; } /* @@ -4460,17 +4461,7 @@ bnx_rx_intr(struct bnx_softc *sc) "Failed to allocate " "new mbuf, incoming frame dropped!\n")); - ifp->if_ierrors++; - - /* Try and reuse the exisitng mbuf. */ - if (bnx_add_buf(sc, m, &sw_prod, - &sw_chain_prod, &sw_prod_bseq)) { - DBRUNIF(1, bnx_breakpoint(sc)); - panic("%s: Double mbuf allocation " - "failure!", - device_xname(sc->bnx_dev)); - } - continue; + sc->bnx_rx_intr_allocation_failure++; } /* Skip over the l2_fhdr when passing the data up @@ -4557,10 +4548,13 @@ bnx_rx_intr(struct bnx_softc *sc) } +bnx_rx_receive_error: sw_cons = NEXT_RX_BD(sw_cons); /* Refresh hw_cons to see if there's new work */ if (sw_cons == hw_cons) { + bus_dmamap_sync(sc->bnx_dmatag, sc->status_map, 0, + BNX_STATUS_BLK_SZ, BUS_DMASYNC_POSTREAD); hw_cons = sc->hw_rx_cons = sblk->status_rx_quick_consumer_index0; if ((hw_cons & USABLE_RX_BD_PER_PAGE) == @@ -5615,8 +5609,13 @@ bnx_tick(void *xsc) prod_bseq = sc->rx_prod_bseq; chain_prod = RX_CHAIN_IDX(prod); bnx_get_buf(sc, &prod, &chain_prod, &prod_bseq); - sc->rx_prod = prod; - sc->rx_prod_bseq = prod_bseq; + if (prod != sc->rx_prod || prod_bseq != sc->rx_prod_bseq) { + sc->bnx_tick_allocation_success++; + sc->rx_prod = prod; + sc->rx_prod_bseq = prod_bseq; + REG_WR16(sc, MB_RX_CID_ADDR + BNX_L2CTX_HOST_BDIDX, prod); + REG_WR(sc, MB_RX_CID_ADDR + BNX_L2CTX_HOST_BSEQ, prod_bseq); + } splx(s); return; } diff --git a/src/sys/dev/pci/if_bnxvar.h b/src/sys/dev/pci/if_bnxvar.h index 6545c76..fef5230 100644 --- a/src/sys/dev/pci/if_bnxvar.h +++ b/src/sys/dev/pci/if_bnxvar.h @@ -220,8 +220,8 @@ struct bnx_softc bus_addr_t tx_bd_chain_paddr[TX_PAGES]; /* H/W maintained RX buffer descriptor chain structure. */ - bus_dma_segment_t rx_bd_chain_seg[RX_PAGES]; - int rx_bd_chain_rseg[RX_PAGES]; + bus_dma_segment_t rx_bd_chain_seg[TX_PAGES]; + int rx_bd_chain_rseg[TX_PAGES]; bus_dmamap_t rx_bd_chain_map[RX_PAGES]; struct rx_bd *rx_bd_chain[RX_PAGES]; bus_addr_t rx_bd_chain_paddr[RX_PAGES]; @@ -337,6 +337,15 @@ struct bnx_softc /* TX DMA mapping failure counter. */ u_int32_t tx_dma_map_failures; + /* bnx_tick rx buffer allocation counter */ + u_int32_t bnx_tick_allocation_success; + + /* l2 fhdr status error counter */ + u_int32_t l2fhdr_status_errors; + + /* number of times bnx_rx_intr fails to allocate an mbuf cluster */ + u_int32_t bnx_rx_intr_allocation_failure; + #ifdef BNX_DEBUG /* Track the number of enqueued mbufs. */ int tx_mbuf_alloc; @@ -356,7 +365,6 @@ struct bnx_softc 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_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; #endif
Attachment:
pgpqttOqYDiix.pgp
Description: PGP signature