static void em_destroy_tx_ring(struct adapter *, int);
static void em_destroy_rx_ring(struct adapter *, int);
static int em_newbuf(struct adapter *, int, int);
-static int em_encap(struct adapter *, struct mbuf **);
+static int em_encap(struct adapter *, struct mbuf **, int *, int *);
static void em_rxcsum(struct adapter *, struct e1000_rx_desc *,
struct mbuf *);
static int em_txcsum(struct adapter *, struct mbuf *,
}
if (adapter->flags & EM_FLAG_TSO)
adapter->spare_tx_desc = EM_TX_SPARE_TSO;
+ adapter->tx_wreg_nsegs = 8;
/*
* Keep following relationship between spare_tx_desc, oact_tx_desc
{
struct adapter *adapter = ifp->if_softc;
struct mbuf *m_head;
+ int idx = -1, nsegs = 0;
ASSERT_SERIALIZED(ifp->if_serializer);
if (m_head == NULL)
break;
- if (em_encap(adapter, &m_head)) {
+ if (em_encap(adapter, &m_head, &nsegs, &idx)) {
ifp->if_oerrors++;
em_tx_collect(adapter);
continue;
}
+ if (nsegs >= adapter->tx_wreg_nsegs && idx >= 0) {
+ E1000_WRITE_REG(&adapter->hw, E1000_TDT(0), idx);
+ nsegs = 0;
+ idx = -1;
+ }
+
/* Send a copy of the frame to the BPF listener */
ETHER_BPF_MTAP(ifp, m_head);
/* Set timeout in case hardware has problems transmitting. */
ifp->if_timer = EM_TX_TIMEOUT;
}
+ if (idx >= 0)
+ E1000_WRITE_REG(&adapter->hw, E1000_TDT(0), idx);
}
static int
}
static int
-em_encap(struct adapter *adapter, struct mbuf **m_headp)
+em_encap(struct adapter *adapter, struct mbuf **m_headp,
+ int *segs_used, int *idx)
{
bus_dma_segment_t segs[EM_MAX_SCATTER];
bus_dmamap_t map;
m_head = *m_headp;
adapter->tx_nsegs += nsegs;
+ *segs_used += nsegs;
if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
/* TSO will consume one TX desc */
- adapter->tx_nsegs += em_tso_setup(adapter, m_head,
- &txd_upper, &txd_lower);
+ i = em_tso_setup(adapter, m_head, &txd_upper, &txd_lower);
+ adapter->tx_nsegs += i;
+ *segs_used += i;
} else if (m_head->m_pkthdr.csum_flags & EM_CSUM_FEATURES) {
/* TX csum offloading will consume one TX desc */
- adapter->tx_nsegs += em_txcsum(adapter, m_head,
- &txd_upper, &txd_lower);
+ i = em_txcsum(adapter, m_head, &txd_upper, &txd_lower);
+ adapter->tx_nsegs += i;
+ *segs_used += i;
}
i = adapter->next_avail_tx_desc;
*/
ctxd->lower.data |= htole32(E1000_TXD_CMD_EOP | cmd);
- /*
- * Advance the Transmit Descriptor Tail (TDT), this tells the E1000
- * that this frame is available to transmit.
- */
- if (adapter->hw.mac.type == e1000_82547 &&
- adapter->link_duplex == HALF_DUPLEX) {
- em_82547_move_tail_serialized(adapter);
- } else {
- E1000_WRITE_REG(&adapter->hw, E1000_TDT(0), i);
- if (adapter->hw.mac.type == e1000_82547) {
+ if (adapter->hw.mac.type == e1000_82547) {
+ /*
+ * Advance the Transmit Descriptor Tail (TDT), this tells the
+ * E1000 that this frame is available to transmit.
+ */
+ if (adapter->link_duplex == HALF_DUPLEX) {
+ em_82547_move_tail_serialized(adapter);
+ } else {
+ E1000_WRITE_REG(&adapter->hw, E1000_TDT(0), i);
em_82547_update_fifo_head(adapter,
m_head->m_pkthdr.len);
}
+ } else {
+ /*
+ * Defer TDT updating, until enough descriptors are setup
+ */
+ *idx = i;
}
return (0);
}
CTLTYPE_INT|CTLFLAG_RW, adapter, 0,
em_sysctl_int_tx_nsegs, "I",
"# segments per TX interrupt");
+ SYSCTL_ADD_INT(&adapter->sysctl_ctx,
+ SYSCTL_CHILDREN(adapter->sysctl_tree),
+ OID_AUTO, "wreg_tx_nsegs", CTLFLAG_RW,
+ &adapter->tx_wreg_nsegs, 0,
+ "# segments before write to hardware register");
}
}