Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/e1000/if_em.c
Show First 20 Lines • Show All 267 Lines • ▼ Show 20 Lines | |||||
static void em_set_promisc(struct adapter *); | static void em_set_promisc(struct adapter *); | ||||
static void em_disable_promisc(struct adapter *); | static void em_disable_promisc(struct adapter *); | ||||
static void em_set_multi(struct adapter *); | static void em_set_multi(struct adapter *); | ||||
static void em_update_link_status(struct adapter *); | static void em_update_link_status(struct adapter *); | ||||
static void em_refresh_mbufs(struct rx_ring *, int); | static void em_refresh_mbufs(struct rx_ring *, int); | ||||
static void em_register_vlan(void *, if_t, u16); | static void em_register_vlan(void *, if_t, u16); | ||||
static void em_unregister_vlan(void *, if_t, u16); | static void em_unregister_vlan(void *, if_t, u16); | ||||
static void em_setup_vlan_hw_support(struct adapter *); | static void em_setup_vlan_hw_support(struct adapter *); | ||||
static int em_xmit(struct tx_ring *, struct mbuf **); | static int __noinline em_xmit(struct tx_ring *, struct mbuf **); | ||||
static int em_dma_malloc(struct adapter *, bus_size_t, | static int em_dma_malloc(struct adapter *, bus_size_t, | ||||
struct em_dma_alloc *, int); | struct em_dma_alloc *, int); | ||||
static void em_dma_free(struct adapter *, struct em_dma_alloc *); | static void em_dma_free(struct adapter *, struct em_dma_alloc *); | ||||
static int em_sysctl_nvm_info(SYSCTL_HANDLER_ARGS); | static int em_sysctl_nvm_info(SYSCTL_HANDLER_ARGS); | ||||
static void em_print_nvm_info(struct adapter *); | static void em_print_nvm_info(struct adapter *); | ||||
static int em_sysctl_debug_info(SYSCTL_HANDLER_ARGS); | static int em_sysctl_debug_info(SYSCTL_HANDLER_ARGS); | ||||
static void em_print_debug_info(struct adapter *); | static void em_print_debug_info(struct adapter *); | ||||
static int em_is_valid_ether_addr(u8 *); | static int em_is_valid_ether_addr(u8 *); | ||||
▲ Show 20 Lines • Show All 1,570 Lines • ▼ Show 20 Lines | |||||
/********************************************************************* | /********************************************************************* | ||||
* | * | ||||
* This routine maps the mbufs to tx descriptors. | * This routine maps the mbufs to tx descriptors. | ||||
* | * | ||||
* return 0 on success, positive on failure | * return 0 on success, positive on failure | ||||
**********************************************************************/ | **********************************************************************/ | ||||
static int | static int __noinline | ||||
em_xmit(struct tx_ring *txr, struct mbuf **m_headp) | em_xmit(struct tx_ring *txr, struct mbuf **m_headp) | ||||
{ | { | ||||
struct adapter *adapter = txr->adapter; | struct adapter *adapter = txr->adapter; | ||||
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, txd_lower, txd_used, txd_saved; | 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, do_tso, remap = 1; | ||||
m_head = *m_headp; | m_head = *m_headp; | ||||
txd_upper = txd_lower = txd_used = txd_saved = 0; | do_tso = (m_head->m_pkthdr.csum_flags & CSUM_TSO); | ||||
do_tso = ((m_head->m_pkthdr.csum_flags & CSUM_TSO) != 0); | |||||
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) { | if (do_tso || (m_head->m_pkthdr.csum_flags & CSUM_TCP)) { | ||||
m_head = m_pullup(m_head, poff + sizeof(struct tcphdr)); | 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); | ||||
/* | if (m_head->m_len < poff + (tp->th_off << 2)) { | ||||
* TSO workaround: | m_head = m_pullup(m_head, poff + (tp->th_off << 2)); | ||||
* pull 4 more bytes of data into it. | |||||
*/ | |||||
m_head = m_pullup(m_head, poff + (tp->th_off << 2) + 4); | |||||
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); | ||||
tp = (struct tcphdr *)(mtod(m_head, char *) + poff); | |||||
if (do_tso) { | |||||
ip->ip_len = 0; | ip->ip_len = 0; | ||||
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 payload | ||||
* length so driver should recompute the checksum here | * length so driver should recompute the checksum here | ||||
* what hardware expect to see. This is adherence of | * what hardware expect to see. This is adherence of | ||||
* Microsoft's Large Send specification. | * 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 40 Lines | if (error == EFBIG && remap) { | ||||
return (error); | return (error); | ||||
} 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); | ||||
} | } | ||||
#if 0 | |||||
/* | /* | ||||
* 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 == 0) && (txr->tx_tso == TRUE)) { | ||||
if (nsegs == 1) | |||||
tso_desc = TRUE; | |||||
txr->tx_tso = FALSE; | txr->tx_tso = FALSE; | ||||
} | } | ||||
#endif | |||||
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) { | ||||
em_tso_setup(txr, m_head, ip_off, ip, tp, | em_tso_setup(txr, m_head, ip_off, ip, tp, | ||||
&txd_upper, &txd_lower); | &txd_upper, &txd_lower); | ||||
/* we need to make a final sentinel transmit desc */ | |||||
tso_desc = TRUE; | |||||
} else if (m_head->m_pkthdr.csum_flags & CSUM_OFFLOAD) | } else if (m_head->m_pkthdr.csum_flags & CSUM_OFFLOAD) | ||||
em_transmit_checksum_setup(txr, m_head, | em_transmit_checksum_setup(txr, m_head, | ||||
ip_off, ip, &txd_upper, &txd_lower); | ip_off, ip, &txd_upper, &txd_lower); | ||||
if (m_head->m_flags & M_VLANTAG) { | if (m_head->m_flags & M_VLANTAG) { | ||||
/* Set the vlan id. */ | /* Set the vlan id. */ | ||||
txd_upper |= htole16(if_getvtag(m_head)) << 16; | txd_upper |= htole16(if_getvtag(m_head)) << 16; | ||||
/* Tell hardware to add tag */ | /* Tell hardware to add tag */ | ||||
txd_lower |= htole32(E1000_TXD_CMD_VLE); | txd_lower |= htole32(E1000_TXD_CMD_VLE); | ||||
} | } | ||||
i = txr->next_avail_desc; | i = txr->next_avail_desc; | ||||
/* Set up our transmit descriptors */ | /* Set up our transmit descriptors */ | ||||
for (j = 0; j < nsegs; j++) { | for (j = 0; j < nsegs; j++) { | ||||
bus_size_t seg_len; | bus_size_t seg_len; | ||||
bus_addr_t seg_addr; | bus_addr_t seg_addr; | ||||
tx_buffer = &txr->tx_buffers[i]; | tx_buffer = &txr->tx_buffers[i]; | ||||
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; | ||||
#if 0 | |||||
/* | /* | ||||
** 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 -= 4; | ||||
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 */ | ++txd_used; /* using an extra txd */ | ||||
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 = | |||||
htole64(seg_addr + seg_len); | ctxd->buffer_addr = htole64(seg_addr + seg_len); | ||||
ctxd->lower.data = htole32( | ctxd->lower.data = htole32( | ||||
adapter->txd_cmd | txd_lower | 4); | adapter->txd_cmd | txd_lower | 4); | ||||
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 { | ||||
#endif | |||||
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; | ||||
#if 0 | |||||
} | } | ||||
#endif | |||||
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--; | ||||
} | |||||
#if 0 | |||||
if (tso_desc) /* TSO used an extra for sentinel */ | if (tso_desc) /* TSO used an extra for sentinel */ | ||||
txr->tx_avail -= txd_used; | txr->tx_avail -= txd_used; | ||||
#endif | |||||
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 826 Lines • ▼ Show 20 Lines | em_tso_setup(struct tx_ring *txr, struct mbuf *mp, int ip_off, | ||||
TXD->cmd_and_length = htole32(adapter->txd_cmd | | TXD->cmd_and_length = htole32(adapter->txd_cmd | | ||||
E1000_TXD_CMD_DEXT | /* Extended descr */ | E1000_TXD_CMD_DEXT | /* Extended descr */ | ||||
E1000_TXD_CMD_TSE | /* TSE context */ | E1000_TXD_CMD_TSE | /* TSE context */ | ||||
E1000_TXD_CMD_IP | /* Do IP csum */ | E1000_TXD_CMD_IP | /* Do IP csum */ | ||||
E1000_TXD_CMD_TCP | /* Do TCP checksum */ | E1000_TXD_CMD_TCP | /* Do TCP checksum */ | ||||
(mp->m_pkthdr.len - (hdr_len))); /* Total len */ | (mp->m_pkthdr.len - (hdr_len))); /* Total len */ | ||||
if (!(adapter->txd_cmd | E1000_TXD_CMD_IFCS)) | |||||
device_printf(adapter->dev, "IFCS NOT SET\n"); | |||||
tx_buffer->m_head = NULL; | tx_buffer->m_head = NULL; | ||||
tx_buffer->next_eop = -1; | tx_buffer->next_eop = -1; | ||||
if (++cur == adapter->num_tx_desc) | if (++cur == adapter->num_tx_desc) | ||||
cur = 0; | cur = 0; | ||||
txr->tx_avail--; | txr->tx_avail--; | ||||
txr->next_avail_desc = cur; | txr->next_avail_desc = cur; | ||||
#if 0 | |||||
txr->tx_tso = TRUE; | txr->tx_tso = TRUE; | ||||
#endif | |||||
} | } | ||||
/********************************************************************** | /********************************************************************** | ||||
* | * | ||||
* Examine each tx_buffer in the used queue. If the hardware is done | * Examine each tx_buffer in the used queue. If the hardware is done | ||||
* processing the packet then free associated resources. The | * processing the packet then free associated resources. The | ||||
* tx_buffer is put back on the free queue. | * tx_buffer is put back on the free queue. | ||||
▲ Show 20 Lines • Show All 2,128 Lines • Show Last 20 Lines |