From 90fd36aac8600a1fd235bbc1ca2f010b0a89013a Mon Sep 17 00:00:00 2001 From: Prafulla Deuskar Date: Mon, 26 Aug 2002 18:59:16 +0000 Subject: [PATCH] Back out TX/RX descriptor/buffer management changes from earier commit. We are having panics with the driver under stress with jumbo frames. Unfortunately we didnot catch it during our regular test cycle. I am going to MFC the backout immediately. --- sys/dev/em/if_em.c | 464 +++++++++++++++++++++++++++------------------ sys/dev/em/if_em.h | 58 +++--- 2 files changed, 309 insertions(+), 213 deletions(-) diff --git a/sys/dev/em/if_em.c b/sys/dev/em/if_em.c index d430a12b947e..f28143a13b79 100644 --- a/sys/dev/em/if_em.c +++ b/sys/dev/em/if_em.c @@ -53,7 +53,7 @@ struct adapter *em_adapter_list = NULL; * Driver version *********************************************************************/ -char em_driver_version[] = "1.3.15"; +char em_driver_version[] = "1.3.14"; /********************************************************************* @@ -130,8 +130,9 @@ static void em_process_receive_interrupts(struct adapter *); static void em_receive_checksum(struct adapter *, struct em_rx_desc * rx_desc, struct mbuf *); -static int em_transmit_checksum_setup(struct adapter *, +static void em_transmit_checksum_setup(struct adapter *, struct mbuf *, + struct em_tx_buffer *, u_int32_t *, u_int32_t *); static void em_set_promisc(struct adapter *); @@ -139,7 +140,7 @@ static void em_disable_promisc(struct adapter *); static void em_set_multi(struct adapter *); static void em_print_hw_stats(struct adapter *); static void em_print_link_status(struct adapter *); -static int em_get_buf(int i, struct adapter *, +static int em_get_buf(struct em_rx_buffer *, struct adapter *, struct mbuf *); static void em_enable_vlans(struct adapter *adapter); @@ -275,10 +276,10 @@ em_attach(device_t dev) /* Set the max frame size assuming standard ethernet sized frames */ adapter->hw.max_frame_size = - ETHERMTU + ETHER_HDR_LEN + ETHER_CRC_LEN; + ETHERMTU + ETHER_HDR_LEN + ETHER_CRC_LEN; adapter->hw.min_frame_size = - MINIMUM_ETHERNET_PACKET_SIZE + ETHER_CRC_LEN; + MINIMUM_ETHERNET_PACKET_SIZE + ETHER_CRC_LEN; /* This controls when hardware reports transmit completion status. */ if ((EM_REPORT_TX_EARLY == 0) || (EM_REPORT_TX_EARLY == 1)) { @@ -468,22 +469,15 @@ em_start(struct ifnet *ifp) s = splimp(); while (ifp->if_snd.ifq_head != NULL) { - int i, count = 0; struct ifvlan *ifv = NULL; IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; - /* If num_tx_desc_avail is less than threshold, call - * clean_transmit_interrupts to reclaim TX descriptors - */ if (adapter->num_tx_desc_avail <= TX_CLEANUP_THRESHOLD) em_clean_transmit_interrupts(adapter); - /* If num_tx_desc_avail is still less than threshold, - * prepend the mbuf to the head of the queue. - */ if (adapter->num_tx_desc_avail <= TX_CLEANUP_THRESHOLD) { ifp->if_flags |= IFF_OACTIVE; IF_PREPEND(&ifp->if_snd, m_head); @@ -491,53 +485,67 @@ em_start(struct ifnet *ifp) break; } - i = adapter->next_avail_tx_desc; - tx_buffer = &adapter->tx_buffer_area[i]; - tx_buffer->m_head = m_head; - tx_buffer->num_tx_desc_used = 0; - - if (ifp->if_hwassist > 0) { - if (em_transmit_checksum_setup(adapter, m_head, - &txd_upper, &txd_lower)) { - /* - * if we change context, one tx_desc is used, - * so count it and advance pointer (i) - */ - count = 1; - if (++i == adapter->num_tx_desc) - i = 0; - + tx_buffer = STAILQ_FIRST(&adapter->free_tx_buffer_list); + if (!tx_buffer) { + adapter->no_tx_buffer_avail1++; + /* + * OK so we should not get here but I've seen it so let + * us try to clean up and then try to get a tx_buffer + * again and only break if we still don't get one. + */ + em_clean_transmit_interrupts(adapter); + tx_buffer = STAILQ_FIRST(&adapter->free_tx_buffer_list); + if (!tx_buffer) { + ifp->if_flags |= IFF_OACTIVE; + IF_PREPEND(&ifp->if_snd, m_head); + adapter->no_tx_buffer_avail2++; + break; } } - else - txd_upper = txd_lower = 0; - - for (mp = m_head; mp != NULL; mp = mp->m_next) { - if (mp->m_len == 0) - continue; - current_tx_desc = &adapter->tx_desc_base[i]; - virtual_addr = mtod(mp, vm_offset_t); - current_tx_desc->buffer_addr = vtophys(virtual_addr); + STAILQ_REMOVE_HEAD(&adapter->free_tx_buffer_list, em_tx_entry); - current_tx_desc->lower.data = (txd_lower | mp->m_len); - current_tx_desc->upper.data = (txd_upper); - - if (++i == adapter->num_tx_desc) - i = 0; - count++; + tx_buffer->num_tx_desc_used = 0; + tx_buffer->m_head = m_head; + if (ifp->if_hwassist > 0) { + em_transmit_checksum_setup(adapter, m_head, tx_buffer, + &txd_upper, &txd_lower); + } else { + txd_upper = 0; + txd_lower = 0; } - tx_buffer->num_tx_desc_used = count; - adapter->num_tx_desc_avail -= count; - adapter->next_avail_tx_desc = i; - /* Find out if we are in vlan mode */ if ((m_head->m_flags & (M_PROTO1|M_PKTHDR)) == (M_PROTO1|M_PKTHDR) && m_head->m_pkthdr.rcvif != NULL && m_head->m_pkthdr.rcvif->if_type == IFT_L2VLAN) ifv = m_head->m_pkthdr.rcvif->if_softc; + + for (mp = m_head; mp != NULL; mp = mp->m_next) { + if (mp->m_len == 0) + continue; + current_tx_desc = adapter->next_avail_tx_desc; + virtual_addr = mtod(mp, vm_offset_t); + current_tx_desc->buffer_addr = vtophys(virtual_addr); + + current_tx_desc->lower.data = (txd_lower | mp->m_len); + current_tx_desc->upper.data = (txd_upper); + + if (current_tx_desc == adapter->last_tx_desc) + adapter->next_avail_tx_desc = + adapter->first_tx_desc; + else + adapter->next_avail_tx_desc++; + + adapter->num_tx_desc_avail--; + tx_buffer->num_tx_desc_used++; + } + + /* Put this tx_buffer at the end in the "in use" list */ + STAILQ_INSERT_TAIL(&adapter->used_tx_buffer_list, tx_buffer, + em_tx_entry); + if (ifv != NULL) { /* Tell hardware to add tag */ current_tx_desc->lower.data |= E1000_TXD_CMD_VLE; @@ -560,7 +568,9 @@ em_start(struct ifnet *ifp) * Advance the Transmit Descriptor Tail (Tdt), this tells the E1000 * that this frame is available to transmit. */ - E1000_WRITE_REG(&adapter->hw, TDT, i); + E1000_WRITE_REG(&adapter->hw, TDT, + (((uintptr_t) adapter->next_avail_tx_desc - + (uintptr_t) adapter->first_tx_desc) >> 4)); } /* end of while loop */ splx(s); @@ -1429,13 +1439,35 @@ em_allocate_transmit_structures(struct adapter * adapter) static int em_setup_transmit_structures(struct adapter * adapter) { + struct em_tx_buffer *tx_buffer; + int i; + if (em_allocate_transmit_structures(adapter)) return ENOMEM; - bzero((void *) adapter->tx_desc_base, - (sizeof(struct em_tx_desc)) * adapter->num_tx_desc); - - adapter->next_avail_tx_desc = 0; + adapter->first_tx_desc = adapter->tx_desc_base; + adapter->last_tx_desc = + adapter->first_tx_desc + (adapter->num_tx_desc - 1); + + + STAILQ_INIT(&adapter->free_tx_buffer_list); + STAILQ_INIT(&adapter->used_tx_buffer_list); + + tx_buffer = adapter->tx_buffer_area; + + /* Setup the linked list of the tx_buffer's */ + for (i = 0; i < adapter->num_tx_desc; i++, tx_buffer++) { + bzero((void *) tx_buffer, sizeof(struct em_tx_buffer)); + STAILQ_INSERT_TAIL(&adapter->free_tx_buffer_list, + tx_buffer, em_tx_entry); + } + + bzero((void *) adapter->first_tx_desc, + (sizeof(struct em_tx_desc)) * adapter->num_tx_desc); + + /* Setup TX descriptor pointers */ + adapter->next_avail_tx_desc = adapter->first_tx_desc; + adapter->oldest_used_tx_desc = adapter->first_tx_desc; /* Set number of descriptors available */ adapter->num_tx_desc_avail = adapter->num_tx_desc; @@ -1558,16 +1590,17 @@ em_free_transmit_structures(struct adapter * adapter) * The offload context needs to be set when we transfer the first * packet of a particular protocol (TCP/UDP). We change the * context only if the protocol type changes. - * Return 1 on context change, 0 otherwise. * **********************************************************************/ -static int +static void em_transmit_checksum_setup(struct adapter * adapter, struct mbuf *mp, + struct em_tx_buffer *tx_buffer, u_int32_t *txd_upper, u_int32_t *txd_lower) { struct em_context_desc *TXD; + struct em_tx_desc * current_tx_desc; if (mp->m_pkthdr.csum_flags) { @@ -1575,7 +1608,7 @@ em_transmit_checksum_setup(struct adapter * adapter, *txd_upper = E1000_TXD_POPTS_TXSM << 8; *txd_lower = E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D; if (adapter->active_checksum_context == OFFLOAD_TCP_IP) - return (0); + return; else adapter->active_checksum_context = OFFLOAD_TCP_IP; @@ -1583,25 +1616,25 @@ em_transmit_checksum_setup(struct adapter * adapter, *txd_upper = E1000_TXD_POPTS_TXSM << 8; *txd_lower = E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D; if (adapter->active_checksum_context == OFFLOAD_UDP_IP) - return(0); + return; else adapter->active_checksum_context = OFFLOAD_UDP_IP; } else { *txd_upper = 0; *txd_lower = 0; - return (0); + return; } } else { *txd_upper = 0; *txd_lower = 0; - return (0); + return; } /* If we reach this point, the checksum offload context * needs to be reset. */ - TXD = (struct em_context_desc *) - &adapter->tx_desc_base[adapter->next_avail_tx_desc]; + current_tx_desc = adapter->next_avail_tx_desc; + TXD = (struct em_context_desc *)current_tx_desc; TXD->lower_setup.ip_fields.ipcss = ETHER_HDR_LEN; TXD->lower_setup.ip_fields.ipcso = @@ -1626,7 +1659,15 @@ em_transmit_checksum_setup(struct adapter * adapter, TXD->tcp_seg_setup.data = 0; TXD->cmd_and_length = E1000_TXD_CMD_DEXT; - return(1); + if (current_tx_desc == adapter->last_tx_desc) + adapter->next_avail_tx_desc = adapter->first_tx_desc; + else + adapter->next_avail_tx_desc++; + + adapter->num_tx_desc_avail--; + + tx_buffer->num_tx_desc_used++; + return; } @@ -1636,40 +1677,40 @@ em_transmit_checksum_setup(struct adapter * adapter, * **********************************************************************/ static int -em_get_buf(int i, struct adapter *adapter, - struct mbuf *nmp) +em_get_buf(struct em_rx_buffer *rx_buffer, struct adapter *adapter, + struct mbuf *mp) { - register struct mbuf *mp = nmp; + struct mbuf *nmp; struct ifnet *ifp; ifp = &adapter->interface_data.ac_if; if (mp == NULL) { - MGETHDR(mp, M_DONTWAIT, MT_DATA); - if (mp == NULL) { + MGETHDR(nmp, M_DONTWAIT, MT_DATA); + if (nmp == NULL) { adapter->mbuf_alloc_failed++; return(ENOBUFS); } - MCLGET(mp, M_DONTWAIT); - if ((mp->m_flags & M_EXT) == 0) { - m_freem(mp); + MCLGET(nmp, M_DONTWAIT); + if ((nmp->m_flags & M_EXT) == 0) { + m_freem(nmp); adapter->mbuf_cluster_failed++; return(ENOBUFS); } - mp->m_len = mp->m_pkthdr.len = MCLBYTES; + nmp->m_len = nmp->m_pkthdr.len = MCLBYTES; } else { - mp->m_len = mp->m_pkthdr.len = MCLBYTES; - mp->m_data = mp->m_ext.ext_buf; - mp->m_next = NULL; + nmp = mp; + nmp->m_len = nmp->m_pkthdr.len = MCLBYTES; + nmp->m_data = nmp->m_ext.ext_buf; + nmp->m_next = NULL; } if (ifp->if_mtu <= ETHERMTU) { - m_adj(mp, ETHER_ALIGN); + m_adj(nmp, ETHER_ALIGN); } - - adapter->rx_buffer_area[i].m_head = mp; - adapter->rx_desc_base[i].buffer_addr = - vtophys(mtod(mp, vm_offset_t)); + + rx_buffer->m_head = nmp; + rx_buffer->buffer_addr = vtophys(mtod(nmp, vm_offset_t)); return(0); } @@ -1686,6 +1727,7 @@ static int em_allocate_receive_structures(struct adapter * adapter) { int i; + struct em_rx_buffer *rx_buffer; if (!(adapter->rx_buffer_area = (struct em_rx_buffer *) malloc(sizeof(struct em_rx_buffer) * @@ -1699,10 +1741,11 @@ em_allocate_receive_structures(struct adapter * adapter) bzero(adapter->rx_buffer_area, sizeof(struct em_rx_buffer) * adapter->num_rx_desc); - for (i = 0; i < adapter->num_rx_desc; i++) { - if (em_get_buf(i, adapter, NULL) == ENOBUFS) { - adapter->rx_buffer_area[i].m_head = NULL; - adapter->rx_desc_base[i].buffer_addr = 0; + for (i = 0, rx_buffer = adapter->rx_buffer_area; + i < adapter->num_rx_desc; i++, rx_buffer++) { + + if (em_get_buf(rx_buffer, adapter, NULL) == ENOBUFS) { + rx_buffer->m_head = NULL; return(ENOBUFS); } } @@ -1718,14 +1761,42 @@ em_allocate_receive_structures(struct adapter * adapter) static int em_setup_receive_structures(struct adapter * adapter) { - bzero((void *) adapter->rx_desc_base, - (sizeof(struct em_rx_desc)) * adapter->num_rx_desc); + struct em_rx_buffer *rx_buffer; + struct em_rx_desc *rx_desc; + int i; if (em_allocate_receive_structures(adapter)) return ENOMEM; + STAILQ_INIT(&adapter->rx_buffer_list); + + adapter->first_rx_desc = + (struct em_rx_desc *) adapter->rx_desc_base; + adapter->last_rx_desc = + adapter->first_rx_desc + (adapter->num_rx_desc - 1); + + rx_buffer = (struct em_rx_buffer *) adapter->rx_buffer_area; + + bzero((void *) adapter->first_rx_desc, + (sizeof(struct em_rx_desc)) * adapter->num_rx_desc); + + /* Build a linked list of rx_buffer's */ + for (i = 0, rx_desc = adapter->first_rx_desc; + i < adapter->num_rx_desc; + i++, rx_buffer++, rx_desc++) { + if (rx_buffer->m_head == NULL) + printf("em%d: Receive buffer memory not allocated", + adapter->unit); + else { + rx_desc->buffer_addr = rx_buffer->buffer_addr; + STAILQ_INSERT_TAIL(&adapter->rx_buffer_list, + rx_buffer, em_rx_entry); + } + } + /* Setup our descriptor pointers */ - adapter->next_rx_desc_to_check = 0; + adapter->next_rx_desc_to_check = adapter->first_rx_desc; + return(0); } @@ -1759,7 +1830,9 @@ em_initialize_receive_unit(struct adapter * adapter) /* Setup the HW Rx Head and Tail Descriptor Pointers */ E1000_WRITE_REG(&adapter->hw, RDH, 0); - E1000_WRITE_REG(&adapter->hw, RDT, adapter->num_rx_desc - 1); + E1000_WRITE_REG(&adapter->hw, RDT, + (((uintptr_t) adapter->last_rx_desc - + (uintptr_t) adapter->first_rx_desc) >> 4)); /* Setup the Receive Control Register */ reg_rctl = E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_LBM_NO | @@ -1841,17 +1914,22 @@ em_free_receive_structures(struct adapter * adapter) static void em_process_receive_interrupts(struct adapter * adapter) { + struct mbuf *mp; struct ifnet *ifp; struct ether_header *eh; + u_int16_t len; + u_int8_t last_byte; u_int8_t accept_frame = 0; - int i; + u_int8_t eop = 0; + u_int32_t pkt_len = 0; /* Pointer to the receive descriptor being examined. */ struct em_rx_desc *current_desc; + struct em_rx_desc *last_desc_processed; + struct em_rx_buffer *rx_buffer; ifp = &adapter->interface_data.ac_if; - i = adapter->next_rx_desc_to_check; - current_desc = &adapter->rx_desc_base[i]; + current_desc = adapter->next_rx_desc_to_check; if (!((current_desc->status) & E1000_RXD_STAT_DD)) { #ifdef DBG_STATS @@ -1861,10 +1939,18 @@ em_process_receive_interrupts(struct adapter * adapter) } while (current_desc->status & E1000_RXD_STAT_DD) { - struct mbuf *mp = adapter->rx_buffer_area[i].m_head; - int eop, len; + /* Get a pointer to the actual receive buffer */ + rx_buffer = STAILQ_FIRST(&adapter->rx_buffer_list); + + if (rx_buffer == NULL) { + printf("em%d: Found null rx_buffer\n", adapter->unit); + return; + } + + mp = rx_buffer->m_head; accept_frame = 1; + if (current_desc->status & E1000_RXD_STAT_EOP) { eop = 1; len = current_desc->length - ETHER_CRC_LEN; @@ -1874,14 +1960,15 @@ em_process_receive_interrupts(struct adapter * adapter) } if (current_desc->errors & E1000_RXD_ERR_FRAME_ERR_MASK) { - u_int8_t last_byte; - u_int32_t pkt_len = current_desc->length; - if (adapter->fmp != NULL) + /* Compute packet length for tbi_accept macro */ + pkt_len = current_desc->length; + if (adapter->fmp != NULL) { pkt_len += adapter->fmp->m_pkthdr.len; - - last_byte = *(mtod(mp, caddr_t) + - current_desc->length - 1); + } + + last_byte = *(mtod(rx_buffer->m_head,caddr_t) + + current_desc->length - 1); if (TBI_ACCEPT(&adapter->hw, current_desc->status, current_desc->errors, @@ -1891,19 +1978,17 @@ em_process_receive_interrupts(struct adapter * adapter) pkt_len, adapter->hw.mac_addr); len--; - } - else { + } else { accept_frame = 0; } } if (accept_frame) { - if (em_get_buf(i, adapter, NULL) == ENOBUFS) { + if (em_get_buf(rx_buffer, adapter, NULL) == ENOBUFS) { adapter->dropped_pkts++; - em_get_buf(i, adapter, mp); - if (adapter->fmp != NULL) - m_freem(adapter->fmp); + em_get_buf(rx_buffer, adapter, mp); + if (adapter->fmp != NULL) m_freem(adapter->fmp); adapter->fmp = NULL; adapter->lmp = NULL; break; @@ -1944,27 +2029,39 @@ em_process_receive_interrupts(struct adapter * adapter) } } else { adapter->dropped_pkts++; - em_get_buf(i, adapter, mp); - if (adapter->fmp != NULL) - m_freem(adapter->fmp); + em_get_buf(rx_buffer, adapter, mp); + if (adapter->fmp != NULL) m_freem(adapter->fmp); adapter->fmp = NULL; adapter->lmp = NULL; } /* Zero out the receive descriptors status */ current_desc->status = 0; - - /* Advance the E1000's Receive Queue #0 "Tail Pointer". */ - E1000_WRITE_REG(&adapter->hw, RDT, i); - /* Advance our pointers to the next descriptor */ - if (++i == adapter->num_rx_desc) { - i = 0; - current_desc = adapter->rx_desc_base; - } else - current_desc++; + if (rx_buffer->m_head != NULL) { + current_desc->buffer_addr = rx_buffer->buffer_addr; + } + + /* Advance our pointers to the next descriptor (checking for wrap). */ + if (current_desc == adapter->last_rx_desc) + adapter->next_rx_desc_to_check = adapter->first_rx_desc; + else + ((adapter)->next_rx_desc_to_check)++; + + last_desc_processed = current_desc; + current_desc = adapter->next_rx_desc_to_check; + /* + * Put the buffer that we just indicated back at the end of our list + */ + STAILQ_REMOVE_HEAD(&adapter->rx_buffer_list, em_rx_entry); + STAILQ_INSERT_TAIL(&adapter->rx_buffer_list, + rx_buffer, em_rx_entry); + + /* Advance the E1000's Receive Queue #0 "Tail Pointer". */ + E1000_WRITE_REG(&adapter->hw, RDT, + (((u_long) last_desc_processed - + (u_long) adapter->first_rx_desc) >> 4)); } - adapter->next_rx_desc_to_check = i; return; } @@ -2250,88 +2347,87 @@ em_print_hw_stats(struct adapter *adapter) * * Examine each tx_buffer in the used queue. If the hardware is done * 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. * **********************************************************************/ static void em_clean_transmit_interrupts(struct adapter * adapter) { - int s; - int i, num_avail; + struct em_tx_buffer *tx_buffer; + struct em_tx_desc *tx_desc; + int s; + struct ifnet *ifp; - if (adapter->num_tx_desc_avail == adapter->num_tx_desc) - return; - - s = splimp(); + s = splimp(); #ifdef DBG_STATS - adapter->clean_tx_interrupts++; + adapter->clean_tx_interrupts++; #endif - /* - * Keep the number of descriptors available in a local - * variable (to ease updates and also check if some descriptors - * have been freed). - * Set i to the oldest used buffer (and descriptor), which is - * i = (next_avail + num_avail) % num_tx_desc - * This is the index of the buffer we look at. - */ - num_avail = adapter->num_tx_desc_avail; - i = adapter->next_avail_tx_desc + num_avail; - if (i >= adapter->num_tx_desc) - i -= adapter->num_tx_desc; - for (;;) { - struct em_tx_buffer *tx_buffer = &adapter->tx_buffer_area[i]; + for (tx_buffer = STAILQ_FIRST(&adapter->used_tx_buffer_list); + tx_buffer; + tx_buffer = STAILQ_FIRST(&adapter->used_tx_buffer_list)) { - /* - * Exit from the loop if the buffer is not in use. - * Otherwise locate the last descriptor of the buffer, and - * check its status as reported by the hardware. If the - * hardware is done with it (E1000_TXD_STAT_DD is set) - * we can free the buffer and all of its resources (mbuf - * and descriptors), otherwise we exit from the loop. - */ - if (tx_buffer->num_tx_desc_used == 0) - break; + /* + * Get hold of the next descriptor that the em will report status + * back to (this will be the last descriptor of a given tx_buffer). We + * only want to free the tx_buffer (and it resources) if the driver is + * done with ALL of the descriptors. If the driver is done with the + * last one then it is done with all of them. + */ - i += (tx_buffer->num_tx_desc_used - 1); - if (i >= adapter->num_tx_desc) - i -= adapter->num_tx_desc; + tx_desc = adapter->oldest_used_tx_desc + + (tx_buffer->num_tx_desc_used - 1); - if (!(adapter->tx_desc_base[i].upper.fields.status - & E1000_TXD_STAT_DD)) - break; + /* Check for wrap case */ + if (tx_desc > adapter->last_tx_desc) + tx_desc -= adapter->num_tx_desc; - /* - * Free tx_buffer, mbuf and descriptors. - * Advance index (i) to oldest used tx buffer. - */ - if (tx_buffer->m_head) { - m_freem(tx_buffer->m_head); - tx_buffer->m_head = NULL; - } - num_avail += tx_buffer->num_tx_desc_used; - tx_buffer->num_tx_desc_used = 0; - if (++i == adapter->num_tx_desc) - i = 0; - } - /* - * If we have enough room, clear IFF_OACTIVE to tell the stack - * that it is OK to send packets. - * If there are no pending descriptors, clear the timeout. Otherwise, - * if some descriptors have been freed, restart the timeout. - */ - if (num_avail > TX_CLEANUP_THRESHOLD) { - struct ifnet *ifp = &adapter->interface_data.ac_if; + /* + * If the descriptor done bit is set free tx_buffer and associated + * resources + */ + if (tx_desc->upper.fields.status & E1000_TXD_STAT_DD) { - ifp->if_flags &= ~IFF_OACTIVE; - if (num_avail == adapter->num_tx_desc) - ifp->if_timer = 0; - else if (num_avail == adapter->num_tx_desc_avail) - ifp->if_timer = EM_TX_TIMEOUT; - } - adapter->num_tx_desc_avail = num_avail; - splx(s); - return; + STAILQ_REMOVE_HEAD(&adapter->used_tx_buffer_list, + em_tx_entry); + + if ((tx_desc == adapter->last_tx_desc)) + adapter->oldest_used_tx_desc = + adapter->first_tx_desc; + else + adapter->oldest_used_tx_desc = (tx_desc + 1); + + /* Make available the descriptors that were previously used */ + adapter->num_tx_desc_avail += + tx_buffer->num_tx_desc_used; + + tx_buffer->num_tx_desc_used = 0; + + if (tx_buffer->m_head) { + m_freem(tx_buffer->m_head); + tx_buffer->m_head = NULL; + } + /* Return this "Software packet" back to the "free" list */ + STAILQ_INSERT_TAIL(&adapter->free_tx_buffer_list, + tx_buffer, em_tx_entry); + } else { + /* + * Found a tx_buffer that the em is not done with then there is + * no reason to check the rest of the queue. + */ + break; + } + } /* end for each tx_buffer */ + + ifp = &adapter->interface_data.ac_if; + + /* Tell the stack that it is OK to send packets */ + if (adapter->num_tx_desc_avail > TX_CLEANUP_THRESHOLD) { + ifp->if_timer = 0; + ifp->if_flags &= ~IFF_OACTIVE; + } + splx(s); + return; } diff --git a/sys/dev/em/if_em.h b/sys/dev/em/if_em.h index 3eba5b87bc90..d548437fd191 100644 --- a/sys/dev/em/if_em.h +++ b/sys/dev/em/if_em.h @@ -151,12 +151,20 @@ typedef struct _em_vendor_info_t struct em_tx_buffer { + STAILQ_ENTRY(em_tx_buffer) em_tx_entry; struct mbuf *m_head; u_int32_t num_tx_desc_used; }; + +/* ****************************************************************************** + * This structure stores information about the 2k aligned receive buffer + * into which the E1000 DMA's frames. + * ******************************************************************************/ struct em_rx_buffer { + STAILQ_ENTRY(em_rx_buffer) em_rx_entry; struct mbuf *m_head; + u_int64_t buffer_addr; }; typedef enum _XSUM_CONTEXT_T { @@ -194,36 +202,28 @@ struct adapter { XSUM_CONTEXT_T active_checksum_context; - /* - * Transmit definitions - * - * We have an array of num_tx_desc descriptors (handled - * by the controller) paired with an array of tx_buffers - * (at tx_buffer_area). - * The index of the next available descriptor is next_avail_tx_desc. - * The number of remaining tx_desc is num_tx_desc_avail. - */ - struct em_tx_desc *tx_desc_base; - u_int32_t next_avail_tx_desc; - volatile u_int16_t num_tx_desc_avail; - u_int16_t num_tx_desc; - u_int32_t txd_cmd; - struct em_tx_buffer *tx_buffer_area; - - /* - * Receive definitions - * - * we have an array of num_rx_desc rx_desc (handled by the - * controller), and paired with an array of rx_buffers - * (at rx_buffer_area). - * The next pair to check on receive is at offset next_rx_desc_to_check - */ - struct em_rx_desc *rx_desc_base; - u_int32_t next_rx_desc_to_check; - u_int16_t num_rx_desc; - u_int32_t rx_buffer_len; - struct em_rx_buffer *rx_buffer_area; + /* Transmit definitions */ + struct em_tx_desc *first_tx_desc; + struct em_tx_desc *last_tx_desc; + struct em_tx_desc *next_avail_tx_desc; + struct em_tx_desc *oldest_used_tx_desc; + struct em_tx_desc *tx_desc_base; + volatile u_int16_t num_tx_desc_avail; + u_int16_t num_tx_desc; + u_int32_t txd_cmd; + struct em_tx_buffer *tx_buffer_area; + STAILQ_HEAD(__em_tx_buffer_free, em_tx_buffer) free_tx_buffer_list; + STAILQ_HEAD(__em_tx_buffer_used, em_tx_buffer) used_tx_buffer_list; + /* Receive definitions */ + struct em_rx_desc *first_rx_desc; + struct em_rx_desc *last_rx_desc; + struct em_rx_desc *next_rx_desc_to_check; + struct em_rx_desc *rx_desc_base; + u_int16_t num_rx_desc; + u_int32_t rx_buffer_len; + struct em_rx_buffer *rx_buffer_area; + STAILQ_HEAD(__em_rx_buffer, em_rx_buffer) rx_buffer_list; /* Jumbo frame */ struct mbuf *fmp;