Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/e1000/if_em.c
Show First 20 Lines • Show All 358 Lines • ▼ Show 20 Lines | |||||
#define MAX_INTS_PER_SEC 8000 | #define MAX_INTS_PER_SEC 8000 | ||||
#define DEFAULT_ITR (1000000000/(MAX_INTS_PER_SEC * 256)) | #define DEFAULT_ITR (1000000000/(MAX_INTS_PER_SEC * 256)) | ||||
/* Allow common code without TSO */ | /* Allow common code without TSO */ | ||||
#ifndef CSUM_TSO | #ifndef CSUM_TSO | ||||
#define CSUM_TSO 0 | #define CSUM_TSO 0 | ||||
#endif | #endif | ||||
#define TSO_WORKAROUND 4 | |||||
static SYSCTL_NODE(_hw, OID_AUTO, em, CTLFLAG_RD, 0, "EM driver parameters"); | static SYSCTL_NODE(_hw, OID_AUTO, em, CTLFLAG_RD, 0, "EM driver parameters"); | ||||
static int em_disable_crc_stripping = 0; | static int em_disable_crc_stripping = 0; | ||||
SYSCTL_INT(_hw_em, OID_AUTO, disable_crc_stripping, CTLFLAG_RDTUN, | SYSCTL_INT(_hw_em, OID_AUTO, disable_crc_stripping, CTLFLAG_RDTUN, | ||||
&em_disable_crc_stripping, 0, "Disable CRC Stripping"); | &em_disable_crc_stripping, 0, "Disable CRC Stripping"); | ||||
static int em_tx_int_delay_dflt = EM_TICKS_TO_USECS(EM_TIDV); | static int em_tx_int_delay_dflt = EM_TICKS_TO_USECS(EM_TIDV); | ||||
static int em_rx_int_delay_dflt = EM_TICKS_TO_USECS(EM_RDTR); | static int em_rx_int_delay_dflt = EM_TICKS_TO_USECS(EM_RDTR); | ||||
▲ Show 20 Lines • Show All 1,496 Lines • ▼ Show 20 Lines | em_xmit(struct tx_ring *txr, struct mbuf **m_headp) | ||||
bus_dma_segment_t segs[EM_MAX_SCATTER]; | bus_dma_segment_t segs[EM_MAX_SCATTER]; | ||||
bus_dmamap_t map; | bus_dmamap_t map; | ||||
struct em_buffer *tx_buffer, *tx_buffer_mapped; | struct em_buffer *tx_buffer, *tx_buffer_mapped; | ||||
struct e1000_tx_desc *ctxd = NULL; | struct e1000_tx_desc *ctxd = NULL; | ||||
struct mbuf *m_head; | struct mbuf *m_head; | ||||
struct ether_header *eh; | struct ether_header *eh; | ||||
struct ip *ip = NULL; | struct ip *ip = NULL; | ||||
struct tcphdr *tp = NULL; | struct tcphdr *tp = NULL; | ||||
u32 txd_upper = 0, txd_lower = 0, txd_used = 0; | u32 txd_upper = 0, txd_lower = 0; | ||||
int ip_off, poff; | int ip_off, poff; | ||||
int nsegs, i, j, first, last = 0; | int nsegs, i, j, first, last = 0; | ||||
int error, do_tso, tso_desc = 0, remap = 1; | int error; | ||||
bool do_tso, tso_desc, remap = TRUE; | |||||
m_head = *m_headp; | m_head = *m_headp; | ||||
do_tso = ((m_head->m_pkthdr.csum_flags & CSUM_TSO) != 0); | do_tso = (m_head->m_pkthdr.csum_flags & CSUM_TSO); | ||||
tso_desc = FALSE; | |||||
ip_off = poff = 0; | ip_off = poff = 0; | ||||
/* | /* | ||||
* Intel recommends entire IP/TCP header length reside in a single | * Intel recommends entire IP/TCP header length reside in a single | ||||
* buffer. If multiple descriptors are used to describe the IP and | * buffer. If multiple descriptors are used to describe the IP and | ||||
* TCP header, each descriptor should describe one or more | * TCP header, each descriptor should describe one or more | ||||
* complete headers; descriptors referencing only parts of headers | * complete headers; descriptors referencing only parts of headers | ||||
* are not supported. If all layer headers are not coalesced into | * are not supported. If all layer headers are not coalesced into | ||||
Show All 19 Lines | if (do_tso || (m_head->m_next != NULL && | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* XXX | * XXX | ||||
* Assume IPv4, we don't have TSO/checksum offload support | * Assume IPv4, we don't have TSO/checksum offload support | ||||
* for IPv6 yet. | * for IPv6 yet. | ||||
*/ | */ | ||||
ip_off = sizeof(struct ether_header); | ip_off = sizeof(struct ether_header); | ||||
if (m_head->m_len < ip_off) { | |||||
m_head = m_pullup(m_head, ip_off); | m_head = m_pullup(m_head, ip_off); | ||||
if (m_head == NULL) { | if (m_head == NULL) { | ||||
*m_headp = NULL; | *m_headp = NULL; | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
} | } | ||||
} | |||||
eh = mtod(m_head, struct ether_header *); | eh = mtod(m_head, struct ether_header *); | ||||
if (eh->ether_type == htons(ETHERTYPE_VLAN)) { | if (eh->ether_type == htons(ETHERTYPE_VLAN)) { | ||||
ip_off = sizeof(struct ether_vlan_header); | ip_off = sizeof(struct ether_vlan_header); | ||||
if (m_head->m_len < ip_off) { | |||||
m_head = m_pullup(m_head, ip_off); | m_head = m_pullup(m_head, ip_off); | ||||
if (m_head == NULL) { | if (m_head == NULL) { | ||||
*m_headp = NULL; | *m_headp = NULL; | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
} | } | ||||
} | } | ||||
} | |||||
if (m_head->m_len < ip_off + sizeof(struct ip)) { | |||||
m_head = m_pullup(m_head, ip_off + sizeof(struct ip)); | m_head = m_pullup(m_head, ip_off + sizeof(struct ip)); | ||||
if (m_head == NULL) { | if (m_head == NULL) { | ||||
*m_headp = NULL; | *m_headp = NULL; | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
} | } | ||||
} | |||||
ip = (struct ip *)(mtod(m_head, char *) + ip_off); | ip = (struct ip *)(mtod(m_head, char *) + ip_off); | ||||
poff = ip_off + (ip->ip_hl << 2); | poff = ip_off + (ip->ip_hl << 2); | ||||
if (do_tso) { | |||||
m_head = m_pullup(m_head, poff + sizeof(struct tcphdr)); | if (do_tso || (m_head->m_pkthdr.csum_flags & CSUM_TCP)) { | ||||
if (m_head->m_len < poff + sizeof(struct tcphdr)) { | |||||
m_head = m_pullup(m_head, poff + | |||||
sizeof(struct tcphdr)); | |||||
if (m_head == NULL) { | if (m_head == NULL) { | ||||
*m_headp = NULL; | *m_headp = NULL; | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
} | } | ||||
} | |||||
tp = (struct tcphdr *)(mtod(m_head, char *) + poff); | tp = (struct tcphdr *)(mtod(m_head, char *) + poff); | ||||
/* | /* | ||||
* TSO workaround: | * TSO workaround: | ||||
* pull 4 more bytes of data into it. | * pull 4 more bytes of data into it. | ||||
*/ | */ | ||||
m_head = m_pullup(m_head, poff + (tp->th_off << 2) + 4); | if (m_head->m_len < poff + (tp->th_off << 2)) { | ||||
m_head = m_pullup(m_head, poff + | |||||
(tp->th_off << 2) + | |||||
TSO_WORKAROUND); | |||||
if (m_head == NULL) { | if (m_head == NULL) { | ||||
*m_headp = NULL; | *m_headp = NULL; | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
} | } | ||||
} | |||||
ip = (struct ip *)(mtod(m_head, char *) + ip_off); | ip = (struct ip *)(mtod(m_head, char *) + ip_off); | ||||
ip->ip_len = 0; | tp = (struct tcphdr *)(mtod(m_head, char *) + poff); | ||||
if (do_tso) { | |||||
ip->ip_len = htons(m_head->m_pkthdr.tso_segsz + | |||||
(ip->ip_hl << 2) + | |||||
(tp->th_off << 2)); | |||||
ip->ip_sum = 0; | ip->ip_sum = 0; | ||||
/* | /* | ||||
* The pseudo TCP checksum does not include TCP payload | * The pseudo TCP checksum does not include TCP | ||||
* length so driver should recompute the checksum here | * payload length so driver should recompute | ||||
* what hardware expect to see. This is adherence of | * the checksum here what hardware expect to | ||||
* Microsoft's Large Send specification. | * see. This is adherence of Microsoft's Large | ||||
* Send specification. | |||||
*/ | */ | ||||
tp = (struct tcphdr *)(mtod(m_head, char *) + poff); | |||||
tp->th_sum = in_pseudo(ip->ip_src.s_addr, | tp->th_sum = in_pseudo(ip->ip_src.s_addr, | ||||
ip->ip_dst.s_addr, htons(IPPROTO_TCP)); | ip->ip_dst.s_addr, htons(IPPROTO_TCP)); | ||||
} else if (m_head->m_pkthdr.csum_flags & CSUM_TCP) { | |||||
m_head = m_pullup(m_head, poff + sizeof(struct tcphdr)); | |||||
if (m_head == NULL) { | |||||
*m_headp = NULL; | |||||
return (ENOBUFS); | |||||
} | } | ||||
tp = (struct tcphdr *)(mtod(m_head, char *) + poff); | |||||
m_head = m_pullup(m_head, poff + (tp->th_off << 2)); | |||||
if (m_head == NULL) { | |||||
*m_headp = NULL; | |||||
return (ENOBUFS); | |||||
} | |||||
ip = (struct ip *)(mtod(m_head, char *) + ip_off); | |||||
tp = (struct tcphdr *)(mtod(m_head, char *) + poff); | |||||
} else if (m_head->m_pkthdr.csum_flags & CSUM_UDP) { | } else if (m_head->m_pkthdr.csum_flags & CSUM_UDP) { | ||||
m_head = m_pullup(m_head, poff + sizeof(struct udphdr)); | if (m_head->m_len < poff + sizeof(struct udphdr)) { | ||||
m_head = m_pullup(m_head, poff + | |||||
sizeof(struct udphdr)); | |||||
if (m_head == NULL) { | if (m_head == NULL) { | ||||
*m_headp = NULL; | *m_headp = NULL; | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
} | } | ||||
} | |||||
ip = (struct ip *)(mtod(m_head, char *) + ip_off); | ip = (struct ip *)(mtod(m_head, char *) + ip_off); | ||||
} | } | ||||
*m_headp = m_head; | *m_headp = m_head; | ||||
} | } | ||||
/* | /* | ||||
* Map the packet for DMA | * Map the packet for DMA | ||||
* | * | ||||
Show All 28 Lines | if (m == NULL) { | ||||
adapter->mbuf_alloc_failed++; | adapter->mbuf_alloc_failed++; | ||||
m_freem(*m_headp); | m_freem(*m_headp); | ||||
*m_headp = NULL; | *m_headp = NULL; | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
} | } | ||||
*m_headp = m; | *m_headp = m; | ||||
/* Try it again, but only once */ | /* Try it again, but only once */ | ||||
remap = 0; | remap = FALSE; | ||||
goto retry; | goto retry; | ||||
} else if (error != 0) { | } else if (error != 0) { | ||||
adapter->no_tx_dma_setup++; | adapter->no_tx_dma_setup++; | ||||
m_freem(*m_headp); | m_freem(*m_headp); | ||||
*m_headp = NULL; | *m_headp = NULL; | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* TSO Hardware workaround, if this packet is not | * TSO Hardware workaround, if this packet is not | ||||
* TSO, and is only a single descriptor long, and | * TSO, and is only a single descriptor long, and | ||||
* it follows a TSO burst, then we need to add a | * it follows a TSO burst, then we need to add a | ||||
* sentinel descriptor to prevent premature writeback. | * sentinel descriptor to prevent premature writeback. | ||||
*/ | */ | ||||
if ((do_tso == 0) && (txr->tx_tso == TRUE)) { | if ((!do_tso) && (txr->tx_tso == TRUE)) { | ||||
if (nsegs == 1) | if (nsegs == 1) | ||||
tso_desc = TRUE; | tso_desc = TRUE; | ||||
txr->tx_tso = FALSE; | txr->tx_tso = FALSE; | ||||
} | } | ||||
if (nsegs > (txr->tx_avail - 2)) { | if (nsegs > (txr->tx_avail - EM_MAX_SCATTER)) { | ||||
txr->no_desc_avail++; | txr->no_desc_avail++; | ||||
bus_dmamap_unload(txr->txtag, map); | bus_dmamap_unload(txr->txtag, map); | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
} | } | ||||
m_head = *m_headp; | m_head = *m_headp; | ||||
/* Do hardware assists */ | /* Do hardware assists */ | ||||
if (m_head->m_pkthdr.csum_flags & CSUM_TSO) { | if (m_head->m_pkthdr.csum_flags & CSUM_TSO) { | ||||
Show All 23 Lines | for (j = 0; j < nsegs; j++) { | ||||
ctxd = &txr->tx_base[i]; | ctxd = &txr->tx_base[i]; | ||||
seg_addr = segs[j].ds_addr; | seg_addr = segs[j].ds_addr; | ||||
seg_len = segs[j].ds_len; | seg_len = segs[j].ds_len; | ||||
/* | /* | ||||
** TSO Workaround: | ** TSO Workaround: | ||||
** If this is the last descriptor, we want to | ** If this is the last descriptor, we want to | ||||
** split it so we have a small final sentinel | ** split it so we have a small final sentinel | ||||
*/ | */ | ||||
if (tso_desc && (j == (nsegs -1)) && (seg_len > 8)) { | if (tso_desc && (j == (nsegs - 1)) && (seg_len > 8)) { | ||||
seg_len -= 4; | seg_len -= TSO_WORKAROUND; | ||||
ctxd->buffer_addr = htole64(seg_addr); | ctxd->buffer_addr = htole64(seg_addr); | ||||
ctxd->lower.data = htole32( | ctxd->lower.data = htole32( | ||||
adapter->txd_cmd | txd_lower | seg_len); | adapter->txd_cmd | txd_lower | seg_len); | ||||
ctxd->upper.data = | ctxd->upper.data = htole32(txd_upper); | ||||
htole32(txd_upper); | |||||
if (++i == adapter->num_tx_desc) | if (++i == adapter->num_tx_desc) | ||||
i = 0; | i = 0; | ||||
/* Now make the sentinel */ | /* Now make the sentinel */ | ||||
++txd_used; /* using an extra txd */ | txr->tx_avail--; | ||||
ctxd = &txr->tx_base[i]; | ctxd = &txr->tx_base[i]; | ||||
tx_buffer = &txr->tx_buffers[i]; | tx_buffer = &txr->tx_buffers[i]; | ||||
ctxd->buffer_addr = | ctxd->buffer_addr = | ||||
htole64(seg_addr + seg_len); | htole64(seg_addr + seg_len); | ||||
ctxd->lower.data = htole32( | ctxd->lower.data = htole32( | ||||
adapter->txd_cmd | txd_lower | 4); | adapter->txd_cmd | txd_lower | TSO_WORKAROUND); | ||||
ctxd->upper.data = | ctxd->upper.data = | ||||
htole32(txd_upper); | htole32(txd_upper); | ||||
last = i; | last = i; | ||||
if (++i == adapter->num_tx_desc) | if (++i == adapter->num_tx_desc) | ||||
i = 0; | i = 0; | ||||
} else { | } else { | ||||
ctxd->buffer_addr = htole64(seg_addr); | ctxd->buffer_addr = htole64(seg_addr); | ||||
ctxd->lower.data = htole32( | ctxd->lower.data = htole32( | ||||
adapter->txd_cmd | txd_lower | seg_len); | adapter->txd_cmd | txd_lower | seg_len); | ||||
ctxd->upper.data = | ctxd->upper.data = htole32(txd_upper); | ||||
htole32(txd_upper); | |||||
last = i; | last = i; | ||||
if (++i == adapter->num_tx_desc) | if (++i == adapter->num_tx_desc) | ||||
i = 0; | i = 0; | ||||
} | } | ||||
tx_buffer->m_head = NULL; | tx_buffer->m_head = NULL; | ||||
tx_buffer->next_eop = -1; | tx_buffer->next_eop = -1; | ||||
} | } | ||||
txr->next_avail_desc = i; | txr->next_avail_desc = i; | ||||
txr->tx_avail -= nsegs; | txr->tx_avail -= nsegs; | ||||
if (tso_desc) /* TSO used an extra for sentinel */ | |||||
txr->tx_avail -= txd_used; | |||||
tx_buffer->m_head = m_head; | tx_buffer->m_head = m_head; | ||||
/* | /* | ||||
** Here we swap the map so the last descriptor, | ** Here we swap the map so the last descriptor, | ||||
** which gets the completion interrupt has the | ** which gets the completion interrupt has the | ||||
** real map, and the first descriptor gets the | ** real map, and the first descriptor gets the | ||||
** unused map from this descriptor. | ** unused map from this descriptor. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 886 Lines • ▼ Show 20 Lines | em_setup_interface(device_t dev, struct adapter *adapter) | ||||
} | } | ||||
if_initname(ifp, device_get_name(dev), device_get_unit(dev)); | if_initname(ifp, device_get_name(dev), device_get_unit(dev)); | ||||
if_setdev(ifp, dev); | if_setdev(ifp, dev); | ||||
if_setinitfn(ifp, em_init); | if_setinitfn(ifp, em_init); | ||||
if_setsoftc(ifp, adapter); | if_setsoftc(ifp, adapter); | ||||
if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); | if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); | ||||
if_setioctlfn(ifp, em_ioctl); | if_setioctlfn(ifp, em_ioctl); | ||||
if_setgetcounterfn(ifp, em_get_counter); | if_setgetcounterfn(ifp, em_get_counter); | ||||
/* TSO parameters */ | |||||
ifp->if_hw_tsomax = EM_TSO_SIZE; | |||||
ifp->if_hw_tsomaxsegcount = EM_MAX_SCATTER; | |||||
ifp->if_hw_tsomaxsegsize = EM_TSO_SEG_SIZE; | |||||
#ifdef EM_MULTIQUEUE | #ifdef EM_MULTIQUEUE | ||||
/* Multiqueue stack interface */ | /* Multiqueue stack interface */ | ||||
if_settransmitfn(ifp, em_mq_start); | if_settransmitfn(ifp, em_mq_start); | ||||
if_setqflushfn(ifp, em_qflush); | if_setqflushfn(ifp, em_qflush); | ||||
#else | #else | ||||
if_setstartfn(ifp, em_start); | if_setstartfn(ifp, em_start); | ||||
if_setsendqlen(ifp, adapter->num_tx_desc - 1); | if_setsendqlen(ifp, adapter->num_tx_desc - 1); | ||||
if_setsendqready(ifp); | if_setsendqready(ifp); | ||||
▲ Show 20 Lines • Show All 1,843 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
em_enable_intr(struct adapter *adapter) | em_enable_intr(struct adapter *adapter) | ||||
{ | { | ||||
struct e1000_hw *hw = &adapter->hw; | struct e1000_hw *hw = &adapter->hw; | ||||
u32 ims_mask = IMS_ENABLE_MASK; | u32 ims_mask = IMS_ENABLE_MASK; | ||||
if (hw->mac.type == e1000_82574) { | if (hw->mac.type == e1000_82574) { | ||||
E1000_WRITE_REG(hw, EM_EIAC, EM_MSIX_MASK); | E1000_WRITE_REG(hw, EM_EIAC, adapter->ims); | ||||
ims_mask |= EM_MSIX_MASK; | ims_mask |= adapter->ims; | ||||
} | } | ||||
E1000_WRITE_REG(hw, E1000_IMS, ims_mask); | E1000_WRITE_REG(hw, E1000_IMS, ims_mask); | ||||
} | } | ||||
static void | static void | ||||
em_disable_intr(struct adapter *adapter) | em_disable_intr(struct adapter *adapter) | ||||
{ | { | ||||
struct e1000_hw *hw = &adapter->hw; | struct e1000_hw *hw = &adapter->hw; | ||||
▲ Show 20 Lines • Show All 1,119 Lines • Show Last 20 Lines |