mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-17 10:26:15 +00:00
- Allow fxp_encap() enqueue failed transmissions and set
IFF_DRV_OACTIVE to note resource shortage to upper stack. - Don't count number of mbuf chains. Default 32 DMA segments for a frame is enough for most cases. If bus_dmamap_mbuf_sg fails use m_collapse(9) to collapse the mbuf chain instead of relying on expensive m_defrag(9). - Move bpf handling to fxp_start_body() which is supposed to be more appropriate place. - Always arm watchdog timer whenever a new Tx request is made. Previously fxp(4) used to arm watchdog timer only when FXP_CXINT_THRESH-th Tx request is made. Because fxp(4) does not rely on Tx interrupt to reclaim transmitted mbufs it's better to arm watchdog timer to detect potential lockups. - Add more aggresive Tx buffer reclaiming in fxp_start_body to make room for new Tx requests. Since fxp(4) does not request Tx completion interrupt for every frames it's necessary to clean TXCBs in advance to saturate link. - Make fxp(4) try to start more packets transmitting regardless of interrupt type in fxp_intr_body.
This commit is contained in:
parent
16385727ce
commit
4e53f83744
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=185285
@ -220,7 +220,8 @@ static void fxp_init_body(struct fxp_softc *sc);
|
||||
static void fxp_tick(void *xsc);
|
||||
static void fxp_start(struct ifnet *ifp);
|
||||
static void fxp_start_body(struct ifnet *ifp);
|
||||
static int fxp_encap(struct fxp_softc *sc, struct mbuf *m_head);
|
||||
static int fxp_encap(struct fxp_softc *sc, struct mbuf **m_head);
|
||||
static void fxp_txeof(struct fxp_softc *sc);
|
||||
static void fxp_stop(struct fxp_softc *sc);
|
||||
static void fxp_release(struct fxp_softc *sc);
|
||||
static int fxp_ioctl(struct ifnet *ifp, u_long command,
|
||||
@ -1190,7 +1191,7 @@ fxp_start_body(struct ifnet *ifp)
|
||||
{
|
||||
struct fxp_softc *sc = ifp->if_softc;
|
||||
struct mbuf *mb_head;
|
||||
int error, txqueued;
|
||||
int txqueued;
|
||||
|
||||
FXP_LOCK_ASSERT(sc, MA_OWNED);
|
||||
|
||||
@ -1202,6 +1203,8 @@ fxp_start_body(struct ifnet *ifp)
|
||||
if (sc->need_mcsetup)
|
||||
return;
|
||||
|
||||
if (sc->tx_queued > FXP_NTXCB_HIWAT)
|
||||
fxp_txeof(sc);
|
||||
/*
|
||||
* We're finished if there is nothing more to add to the list or if
|
||||
* we're all filled up with buffers to transmit.
|
||||
@ -1219,32 +1222,44 @@ fxp_start_body(struct ifnet *ifp)
|
||||
if (mb_head == NULL)
|
||||
break;
|
||||
|
||||
error = fxp_encap(sc, mb_head);
|
||||
if (error)
|
||||
if (fxp_encap(sc, &mb_head)) {
|
||||
if (mb_head == NULL)
|
||||
break;
|
||||
txqueued = 1;
|
||||
IFQ_DRV_PREPEND(&ifp->if_snd, mb_head);
|
||||
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
|
||||
}
|
||||
txqueued++;
|
||||
/*
|
||||
* Pass packet to bpf if there is a listener.
|
||||
*/
|
||||
BPF_MTAP(ifp, mb_head);
|
||||
}
|
||||
bus_dmamap_sync(sc->cbl_tag, sc->cbl_map, BUS_DMASYNC_PREWRITE);
|
||||
|
||||
/*
|
||||
* We're finished. If we added to the list, issue a RESUME to get DMA
|
||||
* going again if suspended.
|
||||
*/
|
||||
if (txqueued) {
|
||||
if (txqueued > 0) {
|
||||
bus_dmamap_sync(sc->cbl_tag, sc->cbl_map, BUS_DMASYNC_PREWRITE);
|
||||
fxp_scb_wait(sc);
|
||||
fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_RESUME);
|
||||
/*
|
||||
* Set a 5 second timer just in case we don't hear
|
||||
* from the card again.
|
||||
*/
|
||||
sc->watchdog_timer = 5;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
fxp_encap(struct fxp_softc *sc, struct mbuf *m_head)
|
||||
fxp_encap(struct fxp_softc *sc, struct mbuf **m_head)
|
||||
{
|
||||
struct ifnet *ifp;
|
||||
struct mbuf *m;
|
||||
struct fxp_tx *txp;
|
||||
struct fxp_cb_tx *cbp;
|
||||
bus_dma_segment_t segs[FXP_NTXSEG];
|
||||
int chainlen, error, i, nseg;
|
||||
int error, i, nseg;
|
||||
|
||||
FXP_LOCK_ASSERT(sc, MA_OWNED);
|
||||
ifp = sc->ifp;
|
||||
@ -1271,6 +1286,7 @@ fxp_encap(struct fxp_softc *sc, struct mbuf *m_head)
|
||||
txp->tx_cb->ipcb_ip_activation_high =
|
||||
FXP_IPCB_HARDWAREPARSING_ENABLE;
|
||||
|
||||
m = *m_head;
|
||||
/*
|
||||
* Deal with TCP/IP checksum offload. Note that
|
||||
* in order for TCP checksum offload to work,
|
||||
@ -1279,11 +1295,11 @@ fxp_encap(struct fxp_softc *sc, struct mbuf *m_head)
|
||||
* in the TCP header. The stack should have
|
||||
* already done this for us.
|
||||
*/
|
||||
if (m_head->m_pkthdr.csum_flags) {
|
||||
if (m_head->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
|
||||
if (m->m_pkthdr.csum_flags) {
|
||||
if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
|
||||
txp->tx_cb->ipcb_ip_schedule =
|
||||
FXP_IPCB_TCPUDP_CHECKSUM_ENABLE;
|
||||
if (m_head->m_pkthdr.csum_flags & CSUM_TCP)
|
||||
if (m->m_pkthdr.csum_flags & CSUM_TCP)
|
||||
txp->tx_cb->ipcb_ip_schedule |=
|
||||
FXP_IPCB_TCP_PACKET;
|
||||
}
|
||||
@ -1311,13 +1327,13 @@ fxp_encap(struct fxp_softc *sc, struct mbuf *m_head)
|
||||
* the header sizes/offsets vary.
|
||||
*/
|
||||
|
||||
if (m_head->m_pkthdr.csum_flags & CSUM_IP) {
|
||||
if (m_head->m_pkthdr.len < 38) {
|
||||
if (m->m_pkthdr.csum_flags & CSUM_IP) {
|
||||
if (m->m_pkthdr.len < 38) {
|
||||
struct ip *ip;
|
||||
m_head->m_data += ETHER_HDR_LEN;
|
||||
ip = mtod(m_head, struct ip *);
|
||||
ip->ip_sum = in_cksum(m_head, ip->ip_hl << 2);
|
||||
m_head->m_data -= ETHER_HDR_LEN;
|
||||
m->m_data += ETHER_HDR_LEN;
|
||||
ip = mtod(m, struct ip *);
|
||||
ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
|
||||
m->m_data -= ETHER_HDR_LEN;
|
||||
} else {
|
||||
txp->tx_cb->ipcb_ip_activation_high =
|
||||
FXP_IPCB_HARDWAREPARSING_ENABLE;
|
||||
@ -1328,40 +1344,33 @@ fxp_encap(struct fxp_softc *sc, struct mbuf *m_head)
|
||||
#endif
|
||||
}
|
||||
|
||||
chainlen = 0;
|
||||
for (m = m_head; m != NULL && chainlen <= sc->maxtxseg; m = m->m_next)
|
||||
chainlen++;
|
||||
if (chainlen > sc->maxtxseg) {
|
||||
struct mbuf *mn;
|
||||
|
||||
/*
|
||||
* We ran out of segments. We have to recopy this
|
||||
* mbuf chain first. Bail out if we can't get the
|
||||
* new buffers.
|
||||
*/
|
||||
mn = m_defrag(m_head, M_DONTWAIT);
|
||||
if (mn == NULL) {
|
||||
m_freem(m_head);
|
||||
return (-1);
|
||||
} else {
|
||||
m_head = mn;
|
||||
error = bus_dmamap_load_mbuf_sg(sc->fxp_mtag, txp->tx_map, *m_head,
|
||||
segs, &nseg, 0);
|
||||
if (error == EFBIG) {
|
||||
m = m_collapse(*m_head, M_DONTWAIT, sc->maxtxseg);
|
||||
if (m == NULL) {
|
||||
m_freem(*m_head);
|
||||
*m_head = NULL;
|
||||
return (ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Go through each of the mbufs in the chain and initialize
|
||||
* the transmit buffer descriptors with the physical address
|
||||
* and size of the mbuf.
|
||||
*/
|
||||
*m_head = m;
|
||||
error = bus_dmamap_load_mbuf_sg(sc->fxp_mtag, txp->tx_map,
|
||||
m_head, segs, &nseg, 0);
|
||||
if (error) {
|
||||
device_printf(sc->dev, "can't map mbuf (error %d)\n", error);
|
||||
m_freem(m_head);
|
||||
return (-1);
|
||||
*m_head, segs, &nseg, 0);
|
||||
if (error != 0) {
|
||||
m_freem(*m_head);
|
||||
*m_head = NULL;
|
||||
return (ENOMEM);
|
||||
}
|
||||
} else if (error != 0)
|
||||
return (error);
|
||||
if (nseg == 0) {
|
||||
m_freem(*m_head);
|
||||
*m_head = NULL;
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
KASSERT(nseg <= sc->maxtxseg, ("too many DMA segments"));
|
||||
bus_dmamap_sync(sc->fxp_mtag, txp->tx_map, BUS_DMASYNC_PREWRITE);
|
||||
|
||||
cbp = txp->tx_cb;
|
||||
for (i = 0; i < nseg; i++) {
|
||||
@ -1389,24 +1398,17 @@ fxp_encap(struct fxp_softc *sc, struct mbuf *m_head)
|
||||
}
|
||||
cbp->tbd_number = nseg;
|
||||
|
||||
bus_dmamap_sync(sc->fxp_mtag, txp->tx_map, BUS_DMASYNC_PREWRITE);
|
||||
txp->tx_mbuf = m_head;
|
||||
txp->tx_mbuf = m;
|
||||
txp->tx_cb->cb_status = 0;
|
||||
txp->tx_cb->byte_count = 0;
|
||||
if (sc->tx_queued != FXP_CXINT_THRESH - 1) {
|
||||
if (sc->tx_queued != FXP_CXINT_THRESH - 1)
|
||||
txp->tx_cb->cb_command =
|
||||
htole16(sc->tx_cmd | FXP_CB_COMMAND_SF |
|
||||
FXP_CB_COMMAND_S);
|
||||
} else {
|
||||
else
|
||||
txp->tx_cb->cb_command =
|
||||
htole16(sc->tx_cmd | FXP_CB_COMMAND_SF |
|
||||
FXP_CB_COMMAND_S | FXP_CB_COMMAND_I);
|
||||
/*
|
||||
* Set a 5 second timer just in case we don't hear
|
||||
* from the card again.
|
||||
*/
|
||||
sc->watchdog_timer = 5;
|
||||
}
|
||||
txp->tx_cb->tx_threshold = tx_threshold;
|
||||
|
||||
/*
|
||||
@ -1439,10 +1441,6 @@ fxp_encap(struct fxp_softc *sc, struct mbuf *m_head)
|
||||
|
||||
sc->tx_queued++;
|
||||
|
||||
/*
|
||||
* Pass packet to bpf if there is a listener.
|
||||
*/
|
||||
BPF_MTAP(ifp, m_head);
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -1528,8 +1526,10 @@ fxp_intr(void *xsc)
|
||||
static void
|
||||
fxp_txeof(struct fxp_softc *sc)
|
||||
{
|
||||
struct ifnet *ifp;
|
||||
struct fxp_tx *txp;
|
||||
|
||||
ifp = sc->ifp;
|
||||
bus_dmamap_sync(sc->cbl_tag, sc->cbl_map, BUS_DMASYNC_PREREAD);
|
||||
for (txp = sc->fxp_desc.tx_first; sc->tx_queued &&
|
||||
(le16toh(txp->tx_cb->cb_status) & FXP_CB_STATUS_C) != 0;
|
||||
@ -1544,6 +1544,7 @@ fxp_txeof(struct fxp_softc *sc)
|
||||
txp->tx_cb->tbd[0].tb_addr = 0;
|
||||
}
|
||||
sc->tx_queued--;
|
||||
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
|
||||
}
|
||||
sc->fxp_desc.tx_first = txp;
|
||||
bus_dmamap_sync(sc->cbl_tag, sc->cbl_map, BUS_DMASYNC_PREWRITE);
|
||||
@ -1589,7 +1590,7 @@ fxp_intr_body(struct fxp_softc *sc, struct ifnet *ifp, uint8_t statack,
|
||||
* packets go out onto the wire for about 5 to 10 seconds
|
||||
* after the interface is ifconfig'ed for the first time.
|
||||
*/
|
||||
if (statack & (FXP_SCB_STATACK_CXTNO | FXP_SCB_STATACK_CNA)) {
|
||||
if (statack & (FXP_SCB_STATACK_CXTNO | FXP_SCB_STATACK_CNA))
|
||||
fxp_txeof(sc);
|
||||
|
||||
/*
|
||||
@ -1597,7 +1598,6 @@ fxp_intr_body(struct fxp_softc *sc, struct ifnet *ifp, uint8_t statack,
|
||||
*/
|
||||
if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
|
||||
fxp_start_body(ifp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Just return if nothing happened on the receive side.
|
||||
|
@ -38,6 +38,7 @@
|
||||
* This must be a power of two.
|
||||
*/
|
||||
#define FXP_NTXCB 128
|
||||
#define FXP_NTXCB_HIWAT ((FXP_NTXCB * 7) / 10)
|
||||
|
||||
/*
|
||||
* Size of the TxCB list.
|
||||
|
Loading…
Reference in New Issue
Block a user