xref: /freebsd-src/sys/dev/e1000/em_txrx.c (revision 7390daf87c53073da5e74b2d73ac7320c1a44475)
1d37cece2SSean Bruno /*-
2034f38cdSKevin Bowling  * SPDX-License-Identifier: BSD-2-Clause
3034f38cdSKevin Bowling  *
47021bf05SStephen Hurd  * Copyright (c) 2016 Nicole Graziano <nicole@nextbsd.org>
57021bf05SStephen Hurd  * Copyright (c) 2017 Matthew Macy <mmacy@mattmacy.io>
6d37cece2SSean Bruno  * All rights reserved.
7d37cece2SSean Bruno  *
8d37cece2SSean Bruno  * Redistribution and use in source and binary forms, with or without
9d37cece2SSean Bruno  * modification, are permitted provided that the following conditions
10d37cece2SSean Bruno  * are met:
11d37cece2SSean Bruno  * 1. Redistributions of source code must retain the above copyright
12d37cece2SSean Bruno  *    notice, this list of conditions and the following disclaimer.
13d37cece2SSean Bruno  * 2. Redistributions in binary form must reproduce the above copyright
14d37cece2SSean Bruno  *    notice, this list of conditions and the following disclaimer in the
15d37cece2SSean Bruno  *    documentation and/or other materials provided with the distribution.
16d37cece2SSean Bruno  *
17d37cece2SSean Bruno  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18d37cece2SSean Bruno  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19d37cece2SSean Bruno  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20d37cece2SSean Bruno  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21d37cece2SSean Bruno  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22d37cece2SSean Bruno  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23d37cece2SSean Bruno  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24d37cece2SSean Bruno  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25d37cece2SSean Bruno  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26d37cece2SSean Bruno  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27d37cece2SSean Bruno  * SUCH DAMAGE.
28d37cece2SSean Bruno  */
29d37cece2SSean Bruno 
30f2d6ace4SSean Bruno #include "if_em.h"
31f2d6ace4SSean Bruno 
32f2d6ace4SSean Bruno #ifdef RSS
33f2d6ace4SSean Bruno #include <net/rss_config.h>
34f2d6ace4SSean Bruno #include <netinet/in_rss.h>
35f2d6ace4SSean Bruno #endif
36f2d6ace4SSean Bruno 
37f2d6ace4SSean Bruno #ifdef VERBOSE_DEBUG
38f2d6ace4SSean Bruno #define DPRINTF device_printf
39f2d6ace4SSean Bruno #else
40f2d6ace4SSean Bruno #define DPRINTF(...)
41f2d6ace4SSean Bruno #endif
42f2d6ace4SSean Bruno 
43f2d6ace4SSean Bruno /*********************************************************************
44f2d6ace4SSean Bruno  *  Local Function prototypes
45f2d6ace4SSean Bruno  *********************************************************************/
469dc452b9SKevin Bowling static int em_tso_setup(struct e1000_softc *, if_pkt_info_t, uint32_t *,
479dc452b9SKevin Bowling     uint32_t *);
489dc452b9SKevin Bowling static int em_transmit_checksum_setup(struct e1000_softc *, if_pkt_info_t,
499dc452b9SKevin Bowling     uint32_t *, uint32_t *);
509dc452b9SKevin Bowling static int em_isc_txd_encap(void *, if_pkt_info_t);
519dc452b9SKevin Bowling static void em_isc_txd_flush(void *, uint16_t, qidx_t);
529dc452b9SKevin Bowling static int em_isc_txd_credits_update(void *, uint16_t, bool);
539dc452b9SKevin Bowling static void em_isc_rxd_refill(void *, if_rxd_update_t);
549dc452b9SKevin Bowling static void em_isc_rxd_flush(void *, uint16_t, uint8_t, qidx_t);
559dc452b9SKevin Bowling static int em_isc_rxd_available(void *, uint16_t, qidx_t, qidx_t);
569dc452b9SKevin Bowling static int em_isc_rxd_pkt_get(void *, if_rxd_info_t);
57f2d6ace4SSean Bruno 
589dc452b9SKevin Bowling static void lem_isc_rxd_refill(void *, if_rxd_update_t);
59f2d6ace4SSean Bruno 
609dc452b9SKevin Bowling static int lem_isc_rxd_available(void *, uint16_t, qidx_t, qidx_t);
619dc452b9SKevin Bowling static int lem_isc_rxd_pkt_get(void *, if_rxd_info_t);
62f2d6ace4SSean Bruno 
63015075f3SKevin Bowling static void em_receive_checksum(uint16_t, uint8_t, if_rxd_info_t);
649dc452b9SKevin Bowling static int em_determine_rsstype(uint32_t);
659dc452b9SKevin Bowling extern int em_intr(void *);
66f2d6ace4SSean Bruno 
67f2d6ace4SSean Bruno struct if_txrx em_txrx = {
68fbf8b74cSMark Johnston 	.ift_txd_encap = em_isc_txd_encap,
69fbf8b74cSMark Johnston 	.ift_txd_flush = em_isc_txd_flush,
70fbf8b74cSMark Johnston 	.ift_txd_credits_update = em_isc_txd_credits_update,
71fbf8b74cSMark Johnston 	.ift_rxd_available = em_isc_rxd_available,
72fbf8b74cSMark Johnston 	.ift_rxd_pkt_get = em_isc_rxd_pkt_get,
73fbf8b74cSMark Johnston 	.ift_rxd_refill = em_isc_rxd_refill,
74fbf8b74cSMark Johnston 	.ift_rxd_flush = em_isc_rxd_flush,
75fbf8b74cSMark Johnston 	.ift_legacy_intr = em_intr
76f2d6ace4SSean Bruno };
77f2d6ace4SSean Bruno 
78f2d6ace4SSean Bruno struct if_txrx lem_txrx = {
79fbf8b74cSMark Johnston 	.ift_txd_encap = em_isc_txd_encap,
80fbf8b74cSMark Johnston 	.ift_txd_flush = em_isc_txd_flush,
81fbf8b74cSMark Johnston 	.ift_txd_credits_update = em_isc_txd_credits_update,
82fbf8b74cSMark Johnston 	.ift_rxd_available = lem_isc_rxd_available,
83fbf8b74cSMark Johnston 	.ift_rxd_pkt_get = lem_isc_rxd_pkt_get,
84fbf8b74cSMark Johnston 	.ift_rxd_refill = lem_isc_rxd_refill,
85fbf8b74cSMark Johnston 	.ift_rxd_flush = em_isc_rxd_flush,
86fbf8b74cSMark Johnston 	.ift_legacy_intr = em_intr
87f2d6ace4SSean Bruno };
88f2d6ace4SSean Bruno 
89f2d6ace4SSean Bruno extern if_shared_ctx_t em_sctx;
90f2d6ace4SSean Bruno 
9195246abbSSean Bruno void
92dc926051SKevin Bowling em_dump_rs(struct e1000_softc *sc)
9395246abbSSean Bruno {
94dc926051SKevin Bowling 	if_softc_ctx_t scctx = sc->shared;
9595246abbSSean Bruno 	struct em_tx_queue *que;
9695246abbSSean Bruno 	struct tx_ring *txr;
9795246abbSSean Bruno 	qidx_t i, ntxd, qid, cur;
9895246abbSSean Bruno 	int16_t rs_cidx;
9995246abbSSean Bruno 	uint8_t status;
10095246abbSSean Bruno 
10195246abbSSean Bruno 	printf("\n");
10295246abbSSean Bruno 	ntxd = scctx->isc_ntxd[0];
103dc926051SKevin Bowling 	for (qid = 0; qid < sc->tx_num_queues; qid++) {
104dc926051SKevin Bowling 		que = &sc->tx_queues[qid];
10595246abbSSean Bruno 		txr =  &que->txr;
10695246abbSSean Bruno 		rs_cidx = txr->tx_rs_cidx;
10795246abbSSean Bruno 		if (rs_cidx != txr->tx_rs_pidx) {
10895246abbSSean Bruno 			cur = txr->tx_rsq[rs_cidx];
10995246abbSSean Bruno 			status = txr->tx_base[cur].upper.fields.status;
11095246abbSSean Bruno 			if (!(status & E1000_TXD_STAT_DD))
111*7390daf8SKevin Bowling 				printf("qid[%d]->tx_rsq[%d]: %d clear ",
112*7390daf8SKevin Bowling 				    qid, rs_cidx, cur);
11395246abbSSean Bruno 		} else {
11495246abbSSean Bruno 			rs_cidx = (rs_cidx-1)&(ntxd-1);
11595246abbSSean Bruno 			cur = txr->tx_rsq[rs_cidx];
116*7390daf8SKevin Bowling 			printf("qid[%d]->tx_rsq[rs_cidx-1=%d]: %d  ",
117*7390daf8SKevin Bowling 			    qid, rs_cidx, cur);
11895246abbSSean Bruno 		}
1195253d74eSKevin Bowling 		printf("cidx_prev=%d rs_pidx=%d ",txr->tx_cidx_processed,
1205253d74eSKevin Bowling 		    txr->tx_rs_pidx);
12195246abbSSean Bruno 		for (i = 0; i < ntxd; i++) {
122*7390daf8SKevin Bowling 			if (txr->tx_base[i].upper.fields.status &
123*7390daf8SKevin Bowling 			    E1000_TXD_STAT_DD)
12495246abbSSean Bruno 				printf("%d set ", i);
12595246abbSSean Bruno 		}
12695246abbSSean Bruno 		printf("\n");
12795246abbSSean Bruno 	}
12895246abbSSean Bruno }
12995246abbSSean Bruno 
130f2d6ace4SSean Bruno /**********************************************************************
131f2d6ace4SSean Bruno  *
132f2d6ace4SSean Bruno  *  Setup work for hardware segmentation offload (TSO) on
133f2d6ace4SSean Bruno  *  adapters using advanced tx descriptors
134f2d6ace4SSean Bruno  *
135f2d6ace4SSean Bruno  **********************************************************************/
136f2d6ace4SSean Bruno static int
1375253d74eSKevin Bowling em_tso_setup(struct e1000_softc *sc, if_pkt_info_t pi, uint32_t *txd_upper,
1385253d74eSKevin Bowling     uint32_t *txd_lower)
139f2d6ace4SSean Bruno {
140dc926051SKevin Bowling 	if_softc_ctx_t scctx = sc->shared;
141dc926051SKevin Bowling 	struct em_tx_queue *que = &sc->tx_queues[pi->ipi_qsidx];
142f2d6ace4SSean Bruno 	struct tx_ring *txr = &que->txr;
143f2d6ace4SSean Bruno 	struct e1000_context_desc *TXD;
144f2d6ace4SSean Bruno 	int cur, hdr_len;
145201c4b7cSKevin Bowling 	uint32_t cmd_type_len;
146f2d6ace4SSean Bruno 
147f2d6ace4SSean Bruno 	hdr_len = pi->ipi_ehdrlen + pi->ipi_ip_hlen + pi->ipi_tcp_hlen;
148f2d6ace4SSean Bruno 	*txd_lower = (E1000_TXD_CMD_DEXT |	/* Extended descr type */
149f2d6ace4SSean Bruno 	    E1000_TXD_DTYP_D |			/* Data descr type */
150f2d6ace4SSean Bruno 	    E1000_TXD_CMD_TSE);			/* Do TSE on this packet */
151f2d6ace4SSean Bruno 
152f2d6ace4SSean Bruno 	cur = pi->ipi_pidx;
153f2d6ace4SSean Bruno 	TXD = (struct e1000_context_desc *)&txr->tx_base[cur];
154f2d6ace4SSean Bruno 
155f2d6ace4SSean Bruno 	/*
156e1353dcbSKevin Bowling 	 * ipcss - Start offset for header checksum calculation.
157e1353dcbSKevin Bowling 	 * ipcse - End offset for header checksum calculation.
158e1353dcbSKevin Bowling 	 * ipcso - Offset of place to put the checksum.
159f2d6ace4SSean Bruno 	 */
160e1353dcbSKevin Bowling 	switch(pi->ipi_etype) {
161e1353dcbSKevin Bowling 	case ETHERTYPE_IP:
162e1353dcbSKevin Bowling 		/* IP and/or TCP header checksum calculation and insertion. */
163*7390daf8SKevin Bowling 		*txd_upper =
164*7390daf8SKevin Bowling 		    (E1000_TXD_POPTS_IXSM | E1000_TXD_POPTS_TXSM) << 8;
165e1353dcbSKevin Bowling 
166f2d6ace4SSean Bruno 		TXD->lower_setup.ip_fields.ipcse =
167f2d6ace4SSean Bruno 		    htole16(pi->ipi_ehdrlen + pi->ipi_ip_hlen - 1);
168e1353dcbSKevin Bowling 		break;
169e1353dcbSKevin Bowling 	case ETHERTYPE_IPV6:
170e1353dcbSKevin Bowling 		/* TCP header checksum calculation and insertion. */
171e1353dcbSKevin Bowling 		*txd_upper = E1000_TXD_POPTS_TXSM << 8;
172e1353dcbSKevin Bowling 
173e1353dcbSKevin Bowling 		TXD->lower_setup.ip_fields.ipcse = htole16(0);
174e1353dcbSKevin Bowling 		break;
175e1353dcbSKevin Bowling 	default:
176e1353dcbSKevin Bowling 		break;
177e1353dcbSKevin Bowling 	}
178e1353dcbSKevin Bowling 	TXD->lower_setup.ip_fields.ipcss = pi->ipi_ehdrlen;
1795253d74eSKevin Bowling 	TXD->lower_setup.ip_fields.ipcso =
1805253d74eSKevin Bowling 	    pi->ipi_ehdrlen + offsetof(struct ip, ip_sum);
181f2d6ace4SSean Bruno 
182f2d6ace4SSean Bruno 	/*
183e1353dcbSKevin Bowling 	 * tucss - Start offset for payload checksum calculation.
184e1353dcbSKevin Bowling 	 * tucse - End offset for payload checksum calculation.
185e1353dcbSKevin Bowling 	 * tucso - Offset of place to put the checksum.
186f2d6ace4SSean Bruno 	 */
187f2d6ace4SSean Bruno 	TXD->upper_setup.tcp_fields.tucss = pi->ipi_ehdrlen + pi->ipi_ip_hlen;
188f2d6ace4SSean Bruno 	TXD->upper_setup.tcp_fields.tucse = 0;
189f2d6ace4SSean Bruno 	TXD->upper_setup.tcp_fields.tucso =
190*7390daf8SKevin Bowling 	    pi->ipi_ehdrlen + pi->ipi_ip_hlen +
191*7390daf8SKevin Bowling 	    offsetof(struct tcphdr, th_sum);
192f2d6ace4SSean Bruno 
193f2d6ace4SSean Bruno 	/*
194f2d6ace4SSean Bruno 	 * Payload size per packet w/o any headers.
195f2d6ace4SSean Bruno 	 * Length of all headers up to payload.
196f2d6ace4SSean Bruno 	 */
197f2d6ace4SSean Bruno 	TXD->tcp_seg_setup.fields.mss = htole16(pi->ipi_tso_segsz);
198f2d6ace4SSean Bruno 	TXD->tcp_seg_setup.fields.hdr_len = hdr_len;
199f2d6ace4SSean Bruno 
200201c4b7cSKevin Bowling 	/*
201cbcab907SKevin Bowling 	 * "PCI/PCI-X SDM 4.0" page 45, and "PCIe GbE SDM 2.5" page 63
202201c4b7cSKevin Bowling 	 * - Set up basic TUCMDs
203201c4b7cSKevin Bowling 	 * - For others IP bit on indicates IPv4, while off indicates IPv6
204201c4b7cSKevin Bowling 	*/
205201c4b7cSKevin Bowling 	cmd_type_len = sc->txd_cmd |
206f2d6ace4SSean Bruno 	    E1000_TXD_CMD_DEXT | /* Extended descr */
207f2d6ace4SSean Bruno 	    E1000_TXD_CMD_TSE |  /* TSE context */
208201c4b7cSKevin Bowling 	    E1000_TXD_CMD_TCP;   /* Do TCP checksum */
209e1353dcbSKevin Bowling 	if (pi->ipi_etype == ETHERTYPE_IP)
210201c4b7cSKevin Bowling 		cmd_type_len |= E1000_TXD_CMD_IP;
211201c4b7cSKevin Bowling 	TXD->cmd_and_length = htole32(cmd_type_len |
212f2d6ace4SSean Bruno 	    (pi->ipi_len - hdr_len)); /* Total len */
213201c4b7cSKevin Bowling 
2141bbdc25fSKevin Bowling 	txr->tx_tso = true;
215f2d6ace4SSean Bruno 
216f2d6ace4SSean Bruno 	if (++cur == scctx->isc_ntxd[0]) {
217f2d6ace4SSean Bruno 		cur = 0;
218f2d6ace4SSean Bruno 	}
219*7390daf8SKevin Bowling 	DPRINTF(iflib_get_dev(sc->ctx), "%s: pidx: %d cur: %d\n",
220*7390daf8SKevin Bowling 	    __FUNCTION__, pi->ipi_pidx, cur);
221f2d6ace4SSean Bruno 	return (cur);
222f2d6ace4SSean Bruno }
223f2d6ace4SSean Bruno 
224f2d6ace4SSean Bruno /*********************************************************************
225f2d6ace4SSean Bruno  *  The offload context is protocol specific (TCP/UDP) and thus
226f2d6ace4SSean Bruno  *  only needs to be set when the protocol changes. The occasion
227f2d6ace4SSean Bruno  *  of a context change can be a performance detriment, and
228f2d6ace4SSean Bruno  *  might be better just disabled. The reason arises in the way
229f2d6ace4SSean Bruno  *  in which the controller supports pipelined requests from the
230f2d6ace4SSean Bruno  *  Tx data DMA. Up to four requests can be pipelined, and they may
231f2d6ace4SSean Bruno  *  belong to the same packet or to multiple packets. However all
232f2d6ace4SSean Bruno  *  requests for one packet are issued before a request is issued
233f2d6ace4SSean Bruno  *  for a subsequent packet and if a request for the next packet
234f2d6ace4SSean Bruno  *  requires a context change, that request will be stalled
235f2d6ace4SSean Bruno  *  until the previous request completes. This means setting up
236f2d6ace4SSean Bruno  *  a new context effectively disables pipelined Tx data DMA which
237f2d6ace4SSean Bruno  *  in turn greatly slow down performance to send small sized
238f2d6ace4SSean Bruno  *  frames.
239f2d6ace4SSean Bruno  **********************************************************************/
240cbcab907SKevin Bowling #define DONT_FORCE_CTX 1
241f2d6ace4SSean Bruno 
242f2d6ace4SSean Bruno static int
2435253d74eSKevin Bowling em_transmit_checksum_setup(struct e1000_softc *sc, if_pkt_info_t pi,
2445253d74eSKevin Bowling     uint32_t *txd_upper, uint32_t *txd_lower)
245f2d6ace4SSean Bruno {
246f2d6ace4SSean Bruno 	struct e1000_context_desc *TXD = NULL;
247dc926051SKevin Bowling 	if_softc_ctx_t scctx = sc->shared;
248dc926051SKevin Bowling 	struct em_tx_queue *que = &sc->tx_queues[pi->ipi_qsidx];
249f2d6ace4SSean Bruno 	struct tx_ring *txr = &que->txr;
250f2d6ace4SSean Bruno 	int csum_flags = pi->ipi_csum_flags;
251f2d6ace4SSean Bruno 	int cur, hdr_len;
2525253d74eSKevin Bowling 	uint32_t cmd;
253f2d6ace4SSean Bruno 
254f2d6ace4SSean Bruno 	cur = pi->ipi_pidx;
255f2d6ace4SSean Bruno 	hdr_len = pi->ipi_ehdrlen + pi->ipi_ip_hlen;
256dc926051SKevin Bowling 	cmd = sc->txd_cmd;
257f2d6ace4SSean Bruno 
258f2d6ace4SSean Bruno 	/*
259f2d6ace4SSean Bruno 	 * The 82574L can only remember the *last* context used
260f2d6ace4SSean Bruno 	 * regardless of queue that it was use for.  We cannot reuse
261f2d6ace4SSean Bruno 	 * contexts on this hardware platform and must generate a new
262f2d6ace4SSean Bruno 	 * context every time.  82574L hardware spec, section 7.2.6,
263f2d6ace4SSean Bruno 	 * second note.
264f2d6ace4SSean Bruno 	 */
265f2d6ace4SSean Bruno 	if (DONT_FORCE_CTX &&
266dc926051SKevin Bowling 	    sc->tx_num_queues == 1 &&
267f2d6ace4SSean Bruno 	    txr->csum_lhlen == pi->ipi_ehdrlen &&
268f2d6ace4SSean Bruno 	    txr->csum_iphlen == pi->ipi_ip_hlen &&
269f2d6ace4SSean Bruno 	    txr->csum_flags == csum_flags) {
270f2d6ace4SSean Bruno 		/*
271f2d6ace4SSean Bruno 		 * Same csum offload context as the previous packets;
272f2d6ace4SSean Bruno 		 * just return.
273f2d6ace4SSean Bruno 		 */
274f2d6ace4SSean Bruno 		*txd_upper = txr->csum_txd_upper;
275f2d6ace4SSean Bruno 		*txd_lower = txr->csum_txd_lower;
276f2d6ace4SSean Bruno 		return (cur);
277f2d6ace4SSean Bruno 	}
278f2d6ace4SSean Bruno 
279f2d6ace4SSean Bruno 	TXD = (struct e1000_context_desc *)&txr->tx_base[cur];
280f2d6ace4SSean Bruno 	/*
281cbcab907SKevin Bowling 	 * ipcss - Start offset for header checksum calculation.
282cbcab907SKevin Bowling 	 * ipcse - End offset for header checksum calculation.
283cbcab907SKevin Bowling 	 * ipcso - Offset of place to put the checksum.
284cbcab907SKevin Bowling 	 *
285*7390daf8SKevin Bowling 	 * We set ipcsX values regardless of IP version to work around HW
286*7390daf8SKevin Bowling 	 * issues and ipcse must be 0 for IPv6 per "PCIe GbE SDM 2.5" page 61.
287cbcab907SKevin Bowling 	 * IXSM controls whether it's inserted.
288f2d6ace4SSean Bruno 	 */
289f2d6ace4SSean Bruno 	TXD->lower_setup.ip_fields.ipcss = pi->ipi_ehdrlen;
2905253d74eSKevin Bowling 	TXD->lower_setup.ip_fields.ipcso = pi->ipi_ehdrlen +
2915253d74eSKevin Bowling 	    offsetof(struct ip, ip_sum);
292cbcab907SKevin Bowling 	if (csum_flags & CSUM_IP) {
293cbcab907SKevin Bowling 		*txd_upper |= E1000_TXD_POPTS_IXSM << 8;
294eac761e9SKevin Bowling 		TXD->lower_setup.ip_fields.ipcse = htole16(hdr_len - 1);
295f2d6ace4SSean Bruno 		cmd |= E1000_TXD_CMD_IP;
296cbcab907SKevin Bowling 	} else if (csum_flags & (CSUM_IP6_TCP | CSUM_IP6_UDP))
297cbcab907SKevin Bowling 		TXD->lower_setup.ip_fields.ipcse = htole16(0);
298f2d6ace4SSean Bruno 
299cbcab907SKevin Bowling 	/*
300cbcab907SKevin Bowling 	 * tucss - Start offset for payload checksum calculation.
301cbcab907SKevin Bowling 	 * tucse - End offset for payload checksum calculation.
302cbcab907SKevin Bowling 	 * tucso - Offset of place to put the checksum.
303cbcab907SKevin Bowling 	 */
304*7390daf8SKevin Bowling 	if (csum_flags & (CSUM_TCP | CSUM_UDP | CSUM_IP6_TCP |
305*7390daf8SKevin Bowling 	    CSUM_IP6_UDP)) {
306f2d6ace4SSean Bruno 		uint8_t tucso;
307f2d6ace4SSean Bruno 
308f2d6ace4SSean Bruno 		*txd_upper |= E1000_TXD_POPTS_TXSM << 8;
309f2d6ace4SSean Bruno 		*txd_lower = E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D;
310f2d6ace4SSean Bruno 
31192fd2f39SKevin Bowling 		if (csum_flags & (CSUM_TCP | CSUM_IP6_TCP)) {
312f2d6ace4SSean Bruno 			tucso = hdr_len + offsetof(struct tcphdr, th_sum);
313f2d6ace4SSean Bruno 			cmd |= E1000_TXD_CMD_TCP;
314f2d6ace4SSean Bruno 		} else
315f2d6ace4SSean Bruno 			tucso = hdr_len + offsetof(struct udphdr, uh_sum);
316f2d6ace4SSean Bruno 		TXD->upper_setup.tcp_fields.tucss = hdr_len;
317f2d6ace4SSean Bruno 		TXD->upper_setup.tcp_fields.tucse = htole16(0);
318f2d6ace4SSean Bruno 		TXD->upper_setup.tcp_fields.tucso = tucso;
319f2d6ace4SSean Bruno 	}
320f2d6ace4SSean Bruno 
321f2d6ace4SSean Bruno 	txr->csum_lhlen = pi->ipi_ehdrlen;
322f2d6ace4SSean Bruno 	txr->csum_iphlen = pi->ipi_ip_hlen;
323f2d6ace4SSean Bruno 	txr->csum_flags = csum_flags;
324f2d6ace4SSean Bruno 	txr->csum_txd_upper = *txd_upper;
325f2d6ace4SSean Bruno 	txr->csum_txd_lower = *txd_lower;
326f2d6ace4SSean Bruno 
327f2d6ace4SSean Bruno 	TXD->tcp_seg_setup.data = htole32(0);
328f2d6ace4SSean Bruno 	TXD->cmd_and_length =
329f2d6ace4SSean Bruno 		htole32(E1000_TXD_CMD_IFCS | E1000_TXD_CMD_DEXT | cmd);
330f2d6ace4SSean Bruno 
331f2d6ace4SSean Bruno 	if (++cur == scctx->isc_ntxd[0]) {
332f2d6ace4SSean Bruno 		cur = 0;
333f2d6ace4SSean Bruno 	}
3345253d74eSKevin Bowling 	DPRINTF(iflib_get_dev(sc->ctx),
335*7390daf8SKevin Bowling 	    "checksum_setup csum_flags=%x txd_upper=%x txd_lower=%x"
336*7390daf8SKevin Bowling 	    " hdr_len=%d cmd=%x\n",
337f2d6ace4SSean Bruno 	    csum_flags, *txd_upper, *txd_lower, hdr_len, cmd);
338f2d6ace4SSean Bruno 	return (cur);
339f2d6ace4SSean Bruno }
340f2d6ace4SSean Bruno 
341cbcab907SKevin Bowling #define TSO_WORKAROUND 4 /* TSO sentinel descriptor */
342cbcab907SKevin Bowling 
343f2d6ace4SSean Bruno static int
344f2d6ace4SSean Bruno em_isc_txd_encap(void *arg, if_pkt_info_t pi)
345f2d6ace4SSean Bruno {
346dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
347f2d6ace4SSean Bruno 	if_softc_ctx_t scctx = sc->shared;
348f2d6ace4SSean Bruno 	struct em_tx_queue *que = &sc->tx_queues[pi->ipi_qsidx];
349f2d6ace4SSean Bruno 	struct tx_ring *txr = &que->txr;
350f2d6ace4SSean Bruno 	bus_dma_segment_t *segs = pi->ipi_segs;
351f2d6ace4SSean Bruno 	int nsegs = pi->ipi_nsegs;
352f2d6ace4SSean Bruno 	int csum_flags = pi->ipi_csum_flags;
353f2d6ace4SSean Bruno 	int i, j, first, pidx_last;
3545253d74eSKevin Bowling 	uint32_t txd_flags, txd_upper = 0, txd_lower = 0;
355f2d6ace4SSean Bruno 
356f2d6ace4SSean Bruno 	struct e1000_tx_desc *ctxd = NULL;
357f2d6ace4SSean Bruno 	bool do_tso, tso_desc;
35895246abbSSean Bruno 	qidx_t ntxd;
359f2d6ace4SSean Bruno 
36095246abbSSean Bruno 	txd_flags = pi->ipi_flags & IPI_TX_INTR ? E1000_TXD_CMD_RS : 0;
361f2d6ace4SSean Bruno 	i = first = pi->ipi_pidx;
362f2d6ace4SSean Bruno 	do_tso = (csum_flags & CSUM_TSO);
3631bbdc25fSKevin Bowling 	tso_desc = false;
36495246abbSSean Bruno 	ntxd = scctx->isc_ntxd[0];
365f2d6ace4SSean Bruno 	/*
366f2d6ace4SSean Bruno 	 * TSO Hardware workaround, if this packet is not
367f2d6ace4SSean Bruno 	 * TSO, and is only a single descriptor long, and
368f2d6ace4SSean Bruno 	 * it follows a TSO burst, then we need to add a
369f2d6ace4SSean Bruno 	 * sentinel descriptor to prevent premature writeback.
370f2d6ace4SSean Bruno 	 */
3711bbdc25fSKevin Bowling 	if ((!do_tso) && (txr->tx_tso == true)) {
372f2d6ace4SSean Bruno 		if (nsegs == 1)
3731bbdc25fSKevin Bowling 			tso_desc = true;
3741bbdc25fSKevin Bowling 		txr->tx_tso = false;
375f2d6ace4SSean Bruno 	}
376f2d6ace4SSean Bruno 
377f2d6ace4SSean Bruno 	/* Do hardware assists */
378f2d6ace4SSean Bruno 	if (do_tso) {
379f2d6ace4SSean Bruno 		i = em_tso_setup(sc, pi, &txd_upper, &txd_lower);
3801bbdc25fSKevin Bowling 		tso_desc = true;
38182379056SSean Bruno 	} else if (csum_flags & EM_CSUM_OFFLOAD) {
382*7390daf8SKevin Bowling 		i = em_transmit_checksum_setup(sc, pi, &txd_upper,
383*7390daf8SKevin Bowling 		    &txd_lower);
384f2d6ace4SSean Bruno 	}
385f2d6ace4SSean Bruno 
386f2d6ace4SSean Bruno 	if (pi->ipi_mflags & M_VLANTAG) {
387f2d6ace4SSean Bruno 		/* Set the vlan id. */
388f2d6ace4SSean Bruno 		txd_upper |= htole16(pi->ipi_vtag) << 16;
389f2d6ace4SSean Bruno 		/* Tell hardware to add tag */
390f2d6ace4SSean Bruno 		txd_lower |= htole32(E1000_TXD_CMD_VLE);
391f2d6ace4SSean Bruno 	}
392f2d6ace4SSean Bruno 
3935253d74eSKevin Bowling 	DPRINTF(iflib_get_dev(sc->ctx),
3945253d74eSKevin Bowling 	    "encap: set up tx: nsegs=%d first=%d i=%d\n", nsegs, first, i);
395dc926051SKevin Bowling 	/* XXX sc->pcix_82544 -- lem_fill_descriptors */
396f2d6ace4SSean Bruno 
397f2d6ace4SSean Bruno 	/* Set up our transmit descriptors */
398f2d6ace4SSean Bruno 	for (j = 0; j < nsegs; j++) {
399f2d6ace4SSean Bruno 		bus_size_t seg_len;
400f2d6ace4SSean Bruno 		bus_addr_t seg_addr;
401f2d6ace4SSean Bruno 		uint32_t cmd;
402f2d6ace4SSean Bruno 
403f2d6ace4SSean Bruno 		ctxd = &txr->tx_base[i];
404f2d6ace4SSean Bruno 		seg_addr = segs[j].ds_addr;
405f2d6ace4SSean Bruno 		seg_len = segs[j].ds_len;
406f2d6ace4SSean Bruno 		cmd = E1000_TXD_CMD_IFCS | sc->txd_cmd;
407f2d6ace4SSean Bruno 
408f2d6ace4SSean Bruno 		/*
40995246abbSSean Bruno 		 * TSO Workaround:
41095246abbSSean Bruno 		 * If this is the last descriptor, we want to
41195246abbSSean Bruno 		 * split it so we have a small final sentinel
412f2d6ace4SSean Bruno 		 */
413f2d6ace4SSean Bruno 		if (tso_desc && (j == (nsegs - 1)) && (seg_len > 8)) {
414f2d6ace4SSean Bruno 			seg_len -= TSO_WORKAROUND;
415f2d6ace4SSean Bruno 			ctxd->buffer_addr = htole64(seg_addr);
416f2d6ace4SSean Bruno 			ctxd->lower.data = htole32(cmd | txd_lower | seg_len);
417f2d6ace4SSean Bruno 			ctxd->upper.data = htole32(txd_upper);
418f2d6ace4SSean Bruno 
419f2d6ace4SSean Bruno 			if (++i == scctx->isc_ntxd[0])
420f2d6ace4SSean Bruno 				i = 0;
421f2d6ace4SSean Bruno 
422f2d6ace4SSean Bruno 			/* Now make the sentinel */
423f2d6ace4SSean Bruno 			ctxd = &txr->tx_base[i];
424f2d6ace4SSean Bruno 			ctxd->buffer_addr = htole64(seg_addr + seg_len);
425*7390daf8SKevin Bowling 			ctxd->lower.data =
426*7390daf8SKevin Bowling 			    htole32(cmd | txd_lower | TSO_WORKAROUND);
427f2d6ace4SSean Bruno 			ctxd->upper.data = htole32(txd_upper);
428f2d6ace4SSean Bruno 			pidx_last = i;
429f2d6ace4SSean Bruno 			if (++i == scctx->isc_ntxd[0])
430f2d6ace4SSean Bruno 				i = 0;
4315253d74eSKevin Bowling 			DPRINTF(iflib_get_dev(sc->ctx),
4325253d74eSKevin Bowling 			    "TSO path pidx_last=%d i=%d ntxd[0]=%d\n",
4335253d74eSKevin Bowling 			    pidx_last, i, scctx->isc_ntxd[0]);
434f2d6ace4SSean Bruno 		} else {
435f2d6ace4SSean Bruno 			ctxd->buffer_addr = htole64(seg_addr);
436f2d6ace4SSean Bruno 			ctxd->lower.data = htole32(cmd | txd_lower | seg_len);
437f2d6ace4SSean Bruno 			ctxd->upper.data = htole32(txd_upper);
438f2d6ace4SSean Bruno 			pidx_last = i;
439f2d6ace4SSean Bruno 			if (++i == scctx->isc_ntxd[0])
440f2d6ace4SSean Bruno 				i = 0;
441*7390daf8SKevin Bowling 			DPRINTF(iflib_get_dev(sc->ctx),
442*7390daf8SKevin Bowling 			    "pidx_last=%d i=%d ntxd[0]=%d\n",
4435253d74eSKevin Bowling 			    pidx_last, i, scctx->isc_ntxd[0]);
444f2d6ace4SSean Bruno 		}
445f2d6ace4SSean Bruno 	}
446f2d6ace4SSean Bruno 
447f2d6ace4SSean Bruno 	/*
448f2d6ace4SSean Bruno 	 * Last Descriptor of Packet
449f2d6ace4SSean Bruno 	 * needs End Of Packet (EOP)
450f2d6ace4SSean Bruno 	 * and Report Status (RS)
451f2d6ace4SSean Bruno 	 */
4528fd222ebSMatt Macy 	if (txd_flags && nsegs) {
45395246abbSSean Bruno 		txr->tx_rsq[txr->tx_rs_pidx] = pidx_last;
4545253d74eSKevin Bowling 		DPRINTF(iflib_get_dev(sc->ctx),
4555253d74eSKevin Bowling 		    "setting to RS on %d rs_pidx %d first: %d\n",
4565253d74eSKevin Bowling 		    pidx_last, txr->tx_rs_pidx, first);
45795246abbSSean Bruno 		txr->tx_rs_pidx = (txr->tx_rs_pidx+1) & (ntxd-1);
45895246abbSSean Bruno 		MPASS(txr->tx_rs_pidx != txr->tx_rs_cidx);
45995246abbSSean Bruno 	}
46095246abbSSean Bruno 	ctxd->lower.data |= htole32(E1000_TXD_CMD_EOP | txd_flags);
4615253d74eSKevin Bowling 	DPRINTF(iflib_get_dev(sc->ctx),
462*7390daf8SKevin Bowling 	    "tx_buffers[%d]->eop = %d ipi_new_pidx=%d\n",
463*7390daf8SKevin Bowling 	    first, pidx_last, i);
464f2d6ace4SSean Bruno 	pi->ipi_new_pidx = i;
465f2d6ace4SSean Bruno 
4663e501ef8SKevin Bowling 	/* Sent data accounting for AIM */
4673e501ef8SKevin Bowling 	txr->tx_bytes += pi->ipi_len;
4683e501ef8SKevin Bowling 	++txr->tx_packets;
4693e501ef8SKevin Bowling 
470f2d6ace4SSean Bruno 	return (0);
471f2d6ace4SSean Bruno }
472f2d6ace4SSean Bruno 
473f2d6ace4SSean Bruno static void
47495246abbSSean Bruno em_isc_txd_flush(void *arg, uint16_t txqid, qidx_t pidx)
475f2d6ace4SSean Bruno {
476dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
477dc926051SKevin Bowling 	struct em_tx_queue *que = &sc->tx_queues[txqid];
478f2d6ace4SSean Bruno 	struct tx_ring *txr = &que->txr;
479f2d6ace4SSean Bruno 
480dc926051SKevin Bowling 	E1000_WRITE_REG(&sc->hw, E1000_TDT(txr->me), pidx);
481f2d6ace4SSean Bruno }
482f2d6ace4SSean Bruno 
483f2d6ace4SSean Bruno static int
48495246abbSSean Bruno em_isc_txd_credits_update(void *arg, uint16_t txqid, bool clear)
485f2d6ace4SSean Bruno {
486dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
487dc926051SKevin Bowling 	if_softc_ctx_t scctx = sc->shared;
488dc926051SKevin Bowling 	struct em_tx_queue *que = &sc->tx_queues[txqid];
489f2d6ace4SSean Bruno 	struct tx_ring *txr = &que->txr;
490f2d6ace4SSean Bruno 
49195246abbSSean Bruno 	qidx_t processed = 0;
49295246abbSSean Bruno 	int updated;
49395246abbSSean Bruno 	qidx_t cur, prev, ntxd, rs_cidx;
49495246abbSSean Bruno 	int32_t delta;
49595246abbSSean Bruno 	uint8_t status;
496f2d6ace4SSean Bruno 
49795246abbSSean Bruno 	rs_cidx = txr->tx_rs_cidx;
49895246abbSSean Bruno 	if (rs_cidx == txr->tx_rs_pidx)
49995246abbSSean Bruno 		return (0);
50095246abbSSean Bruno 	cur = txr->tx_rsq[rs_cidx];
50195246abbSSean Bruno 	MPASS(cur != QIDX_INVALID);
50295246abbSSean Bruno 	status = txr->tx_base[cur].upper.fields.status;
50395246abbSSean Bruno 	updated = !!(status & E1000_TXD_STAT_DD);
504f2d6ace4SSean Bruno 
505adf93b56SEric Joyner 	if (!updated)
506adf93b56SEric Joyner 		return (0);
507adf93b56SEric Joyner 
508adf93b56SEric Joyner 	/* If clear is false just let caller know that there
509adf93b56SEric Joyner 	 * are descriptors to reclaim */
510adf93b56SEric Joyner 	if (!clear)
511adf93b56SEric Joyner 		return (1);
51295246abbSSean Bruno 
51395246abbSSean Bruno 	prev = txr->tx_cidx_processed;
51495246abbSSean Bruno 	ntxd = scctx->isc_ntxd[0];
51595246abbSSean Bruno 	do {
516088a0b27SEric Joyner 		MPASS(prev != cur);
51795246abbSSean Bruno 		delta = (int32_t)cur - (int32_t)prev;
51895246abbSSean Bruno 		if (delta < 0)
51995246abbSSean Bruno 			delta += ntxd;
520088a0b27SEric Joyner 		MPASS(delta > 0);
521dc926051SKevin Bowling 		DPRINTF(iflib_get_dev(sc->ctx),
52295246abbSSean Bruno 		    "%s: cidx_processed=%u cur=%u clear=%d delta=%d\n",
52395246abbSSean Bruno 		    __FUNCTION__, prev, cur, clear, delta);
524f2d6ace4SSean Bruno 
52595246abbSSean Bruno 		processed += delta;
52695246abbSSean Bruno 		prev  = cur;
52795246abbSSean Bruno 		rs_cidx = (rs_cidx + 1) & (ntxd-1);
52895246abbSSean Bruno 		if (rs_cidx  == txr->tx_rs_pidx)
529f2d6ace4SSean Bruno 			break;
53095246abbSSean Bruno 		cur = txr->tx_rsq[rs_cidx];
53195246abbSSean Bruno 		MPASS(cur != QIDX_INVALID);
53295246abbSSean Bruno 		status = txr->tx_base[cur].upper.fields.status;
53395246abbSSean Bruno 	} while ((status & E1000_TXD_STAT_DD));
534f2d6ace4SSean Bruno 
53595246abbSSean Bruno 	txr->tx_rs_cidx = rs_cidx;
53695246abbSSean Bruno 	txr->tx_cidx_processed = prev;
537f2d6ace4SSean Bruno 	return(processed);
538f2d6ace4SSean Bruno }
539f2d6ace4SSean Bruno 
540f2d6ace4SSean Bruno static void
54195246abbSSean Bruno lem_isc_rxd_refill(void *arg, if_rxd_update_t iru)
542f2d6ace4SSean Bruno {
543dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
544f2d6ace4SSean Bruno 	if_softc_ctx_t scctx = sc->shared;
54595246abbSSean Bruno 	struct em_rx_queue *que = &sc->rx_queues[iru->iru_qsidx];
546f2d6ace4SSean Bruno 	struct rx_ring *rxr = &que->rxr;
547f2d6ace4SSean Bruno 	struct e1000_rx_desc *rxd;
54895246abbSSean Bruno 	uint64_t *paddrs;
54995246abbSSean Bruno 	uint32_t next_pidx, pidx;
55095246abbSSean Bruno 	uint16_t count;
551f2d6ace4SSean Bruno 	int i;
55295246abbSSean Bruno 
55395246abbSSean Bruno 	paddrs = iru->iru_paddrs;
55495246abbSSean Bruno 	pidx = iru->iru_pidx;
55595246abbSSean Bruno 	count = iru->iru_count;
556f2d6ace4SSean Bruno 
557f2d6ace4SSean Bruno 	for (i = 0, next_pidx = pidx; i < count; i++) {
558f2d6ace4SSean Bruno 		rxd = (struct e1000_rx_desc *)&rxr->rx_base[next_pidx];
559f2d6ace4SSean Bruno 		rxd->buffer_addr = htole64(paddrs[i]);
560f2d6ace4SSean Bruno 		/* status bits must be cleared */
561f2d6ace4SSean Bruno 		rxd->status = 0;
562f2d6ace4SSean Bruno 
563f2d6ace4SSean Bruno 		if (++next_pidx == scctx->isc_nrxd[0])
564f2d6ace4SSean Bruno 			next_pidx = 0;
565f2d6ace4SSean Bruno 	}
566f2d6ace4SSean Bruno }
567f2d6ace4SSean Bruno 
568f2d6ace4SSean Bruno static void
56995246abbSSean Bruno em_isc_rxd_refill(void *arg, if_rxd_update_t iru)
570f2d6ace4SSean Bruno {
571dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
572f2d6ace4SSean Bruno 	if_softc_ctx_t scctx = sc->shared;
57395246abbSSean Bruno 	uint16_t rxqid = iru->iru_qsidx;
574f2d6ace4SSean Bruno 	struct em_rx_queue *que = &sc->rx_queues[rxqid];
575f2d6ace4SSean Bruno 	struct rx_ring *rxr = &que->rxr;
576f2d6ace4SSean Bruno 	union e1000_rx_desc_extended *rxd;
57795246abbSSean Bruno 	uint64_t *paddrs;
57895246abbSSean Bruno 	uint32_t next_pidx, pidx;
57995246abbSSean Bruno 	uint16_t count;
580f2d6ace4SSean Bruno 	int i;
58195246abbSSean Bruno 
58295246abbSSean Bruno 	paddrs = iru->iru_paddrs;
58395246abbSSean Bruno 	pidx = iru->iru_pidx;
58495246abbSSean Bruno 	count = iru->iru_count;
585f2d6ace4SSean Bruno 
586f2d6ace4SSean Bruno 	for (i = 0, next_pidx = pidx; i < count; i++) {
587f2d6ace4SSean Bruno 		rxd = &rxr->rx_base[next_pidx];
588f2d6ace4SSean Bruno 		rxd->read.buffer_addr = htole64(paddrs[i]);
589ab2e3f79SStephen Hurd 		/* DD bits must be cleared */
590ab2e3f79SStephen Hurd 		rxd->wb.upper.status_error = 0;
591f2d6ace4SSean Bruno 
592f2d6ace4SSean Bruno 		if (++next_pidx == scctx->isc_nrxd[0])
593f2d6ace4SSean Bruno 			next_pidx = 0;
594f2d6ace4SSean Bruno 	}
595f2d6ace4SSean Bruno }
596f2d6ace4SSean Bruno 
597f2d6ace4SSean Bruno static void
5985253d74eSKevin Bowling em_isc_rxd_flush(void *arg, uint16_t rxqid, uint8_t flid __unused,
5995253d74eSKevin Bowling     qidx_t pidx)
600f2d6ace4SSean Bruno {
601dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
602f2d6ace4SSean Bruno 	struct em_rx_queue *que = &sc->rx_queues[rxqid];
603f2d6ace4SSean Bruno 	struct rx_ring *rxr = &que->rxr;
604f2d6ace4SSean Bruno 
605f2d6ace4SSean Bruno 	E1000_WRITE_REG(&sc->hw, E1000_RDT(rxr->me), pidx);
606f2d6ace4SSean Bruno }
607f2d6ace4SSean Bruno 
608f2d6ace4SSean Bruno static int
60995246abbSSean Bruno lem_isc_rxd_available(void *arg, uint16_t rxqid, qidx_t idx, qidx_t budget)
610f2d6ace4SSean Bruno {
611dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
612f2d6ace4SSean Bruno 	if_softc_ctx_t scctx = sc->shared;
613f2d6ace4SSean Bruno 	struct em_rx_queue *que = &sc->rx_queues[rxqid];
614f2d6ace4SSean Bruno 	struct rx_ring *rxr = &que->rxr;
615f2d6ace4SSean Bruno 	struct e1000_rx_desc *rxd;
6165253d74eSKevin Bowling 	uint32_t staterr = 0;
617f2d6ace4SSean Bruno 	int cnt, i;
618f2d6ace4SSean Bruno 
619ab2e3f79SStephen Hurd 	for (cnt = 0, i = idx; cnt < scctx->isc_nrxd[0] && cnt <= budget;) {
620f2d6ace4SSean Bruno 		rxd = (struct e1000_rx_desc *)&rxr->rx_base[i];
621f2d6ace4SSean Bruno 		staterr = rxd->status;
622f2d6ace4SSean Bruno 
623f2d6ace4SSean Bruno 		if ((staterr & E1000_RXD_STAT_DD) == 0)
624f2d6ace4SSean Bruno 			break;
625f2d6ace4SSean Bruno 		if (++i == scctx->isc_nrxd[0])
626f2d6ace4SSean Bruno 			i = 0;
627f2d6ace4SSean Bruno 		if (staterr & E1000_RXD_STAT_EOP)
628f2d6ace4SSean Bruno 			cnt++;
629f2d6ace4SSean Bruno 	}
630f2d6ace4SSean Bruno 	return (cnt);
631f2d6ace4SSean Bruno }
632f2d6ace4SSean Bruno 
633f2d6ace4SSean Bruno static int
63495246abbSSean Bruno em_isc_rxd_available(void *arg, uint16_t rxqid, qidx_t idx, qidx_t budget)
635f2d6ace4SSean Bruno {
636dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
637f2d6ace4SSean Bruno 	if_softc_ctx_t scctx = sc->shared;
638f2d6ace4SSean Bruno 	struct em_rx_queue *que = &sc->rx_queues[rxqid];
639f2d6ace4SSean Bruno 	struct rx_ring *rxr = &que->rxr;
640f2d6ace4SSean Bruno 	union e1000_rx_desc_extended *rxd;
6415253d74eSKevin Bowling 	uint32_t staterr = 0;
642f2d6ace4SSean Bruno 	int cnt, i;
643f2d6ace4SSean Bruno 
644ab2e3f79SStephen Hurd 	for (cnt = 0, i = idx; cnt < scctx->isc_nrxd[0] && cnt <= budget;) {
645f2d6ace4SSean Bruno 		rxd = &rxr->rx_base[i];
646f2d6ace4SSean Bruno 		staterr = le32toh(rxd->wb.upper.status_error);
647f2d6ace4SSean Bruno 
648f2d6ace4SSean Bruno 		if ((staterr & E1000_RXD_STAT_DD) == 0)
649f2d6ace4SSean Bruno 			break;
650adf93b56SEric Joyner 		if (++i == scctx->isc_nrxd[0])
651f2d6ace4SSean Bruno 			i = 0;
652f2d6ace4SSean Bruno 		if (staterr & E1000_RXD_STAT_EOP)
653f2d6ace4SSean Bruno 			cnt++;
654f2d6ace4SSean Bruno 	}
655f2d6ace4SSean Bruno 	return (cnt);
656f2d6ace4SSean Bruno }
657f2d6ace4SSean Bruno 
658f2d6ace4SSean Bruno static int
659f2d6ace4SSean Bruno lem_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri)
660f2d6ace4SSean Bruno {
661dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
662dc926051SKevin Bowling 	if_softc_ctx_t scctx = sc->shared;
663dc926051SKevin Bowling 	struct em_rx_queue *que = &sc->rx_queues[ri->iri_qsidx];
664f2d6ace4SSean Bruno 	struct rx_ring *rxr = &que->rxr;
665f2d6ace4SSean Bruno 	struct e1000_rx_desc *rxd;
6665253d74eSKevin Bowling 	uint16_t len;
6675253d74eSKevin Bowling 	uint32_t status, errors;
668f2d6ace4SSean Bruno 	bool eop;
669f2d6ace4SSean Bruno 	int i, cidx;
670f2d6ace4SSean Bruno 
671f2d6ace4SSean Bruno 	status = errors = i = 0;
672f2d6ace4SSean Bruno 	cidx = ri->iri_cidx;
673f2d6ace4SSean Bruno 
674f2d6ace4SSean Bruno 	do {
675f2d6ace4SSean Bruno 		rxd = (struct e1000_rx_desc *)&rxr->rx_base[cidx];
676f2d6ace4SSean Bruno 		status = rxd->status;
677f2d6ace4SSean Bruno 		errors = rxd->errors;
678f2d6ace4SSean Bruno 
679f2d6ace4SSean Bruno 		/* Error Checking then decrement count */
680f2d6ace4SSean Bruno 		MPASS ((status & E1000_RXD_STAT_DD) != 0);
681f2d6ace4SSean Bruno 
682f2d6ace4SSean Bruno 		len = le16toh(rxd->length);
683f2d6ace4SSean Bruno 		ri->iri_len += len;
6843e501ef8SKevin Bowling 		rxr->rx_bytes += ri->iri_len;
685f2d6ace4SSean Bruno 
686f2d6ace4SSean Bruno 		eop = (status & E1000_RXD_STAT_EOP) != 0;
687f2d6ace4SSean Bruno 
688f2d6ace4SSean Bruno 		/* Make sure bad packets are discarded */
689f2d6ace4SSean Bruno 		if (errors & E1000_RXD_ERR_FRAME_ERR_MASK) {
690dc926051SKevin Bowling 			sc->dropped_pkts++;
691f2d6ace4SSean Bruno 			/* XXX fixup if common */
692f2d6ace4SSean Bruno 			return (EBADMSG);
693f2d6ace4SSean Bruno 		}
694f2d6ace4SSean Bruno 
695f2d6ace4SSean Bruno 		ri->iri_frags[i].irf_flid = 0;
696f2d6ace4SSean Bruno 		ri->iri_frags[i].irf_idx = cidx;
697f2d6ace4SSean Bruno 		ri->iri_frags[i].irf_len = len;
698f2d6ace4SSean Bruno 		/* Zero out the receive descriptors status. */
699f2d6ace4SSean Bruno 		rxd->status = 0;
700f2d6ace4SSean Bruno 
701f2d6ace4SSean Bruno 		if (++cidx == scctx->isc_nrxd[0])
702f2d6ace4SSean Bruno 			cidx = 0;
703f2d6ace4SSean Bruno 		i++;
704f2d6ace4SSean Bruno 	} while (!eop);
705f2d6ace4SSean Bruno 
7063e501ef8SKevin Bowling 	rxr->rx_packets++;
7073e501ef8SKevin Bowling 
708918c2567SKevin Bowling 	if (scctx->isc_capenable & IFCAP_RXCSUM)
709015075f3SKevin Bowling 		em_receive_checksum(status, errors, ri);
710f2d6ace4SSean Bruno 
711918c2567SKevin Bowling 	if (scctx->isc_capenable & IFCAP_VLAN_HWTAGGING &&
712918c2567SKevin Bowling 	    status & E1000_RXD_STAT_VP) {
713*7390daf8SKevin Bowling 		ri->iri_vtag =
714*7390daf8SKevin Bowling 		    le16toh(rxd->special & E1000_RXD_SPC_VLAN_MASK);
715f2d6ace4SSean Bruno 		ri->iri_flags |= M_VLANTAG;
716f2d6ace4SSean Bruno 	}
717f2d6ace4SSean Bruno 
718f2d6ace4SSean Bruno 	ri->iri_nfrags = i;
719f2d6ace4SSean Bruno 
720f2d6ace4SSean Bruno 	return (0);
721f2d6ace4SSean Bruno }
722f2d6ace4SSean Bruno 
723f2d6ace4SSean Bruno static int
724f2d6ace4SSean Bruno em_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri)
725f2d6ace4SSean Bruno {
726dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
727dc926051SKevin Bowling 	if_softc_ctx_t scctx = sc->shared;
728dc926051SKevin Bowling 	struct em_rx_queue *que = &sc->rx_queues[ri->iri_qsidx];
729f2d6ace4SSean Bruno 	struct rx_ring *rxr = &que->rxr;
730f2d6ace4SSean Bruno 	union e1000_rx_desc_extended *rxd;
731f2d6ace4SSean Bruno 
7325253d74eSKevin Bowling 	uint16_t len;
7335253d74eSKevin Bowling 	uint32_t pkt_info;
734918c2567SKevin Bowling 	uint32_t staterr;
735f2d6ace4SSean Bruno 	bool eop;
736f7926a6dSVincenzo Maffione 	int i, cidx;
737f2d6ace4SSean Bruno 
738918c2567SKevin Bowling 	staterr = i = 0;
739f2d6ace4SSean Bruno 	cidx = ri->iri_cidx;
740f2d6ace4SSean Bruno 
741f2d6ace4SSean Bruno 	do {
742f2d6ace4SSean Bruno 		rxd = &rxr->rx_base[cidx];
743f2d6ace4SSean Bruno 		staterr = le32toh(rxd->wb.upper.status_error);
744cbf1505dSSean Bruno 		pkt_info = le32toh(rxd->wb.lower.mrq);
745f2d6ace4SSean Bruno 
746f2d6ace4SSean Bruno 		/* Error Checking then decrement count */
747ab2e3f79SStephen Hurd 		MPASS ((staterr & E1000_RXD_STAT_DD) != 0);
748f2d6ace4SSean Bruno 
749f2d6ace4SSean Bruno 		len = le16toh(rxd->wb.upper.length);
750f2d6ace4SSean Bruno 		ri->iri_len += len;
7513e501ef8SKevin Bowling 		rxr->rx_bytes += ri->iri_len;
752f2d6ace4SSean Bruno 
753f2d6ace4SSean Bruno 		eop = (staterr & E1000_RXD_STAT_EOP) != 0;
754f2d6ace4SSean Bruno 
755f2d6ace4SSean Bruno 		/* Make sure bad packets are discarded */
756f2d6ace4SSean Bruno 		if (staterr & E1000_RXDEXT_ERR_FRAME_ERR_MASK) {
757dc926051SKevin Bowling 			sc->dropped_pkts++;
758f2d6ace4SSean Bruno 			return EBADMSG;
759f2d6ace4SSean Bruno 		}
760f2d6ace4SSean Bruno 
761f2d6ace4SSean Bruno 		ri->iri_frags[i].irf_flid = 0;
762f2d6ace4SSean Bruno 		ri->iri_frags[i].irf_idx = cidx;
763f2d6ace4SSean Bruno 		ri->iri_frags[i].irf_len = len;
764f2d6ace4SSean Bruno 		/* Zero out the receive descriptors status. */
765f2d6ace4SSean Bruno 		rxd->wb.upper.status_error &= htole32(~0xFF);
766f2d6ace4SSean Bruno 
767f2d6ace4SSean Bruno 		if (++cidx == scctx->isc_nrxd[0])
768f2d6ace4SSean Bruno 			cidx = 0;
769f2d6ace4SSean Bruno 		i++;
770f2d6ace4SSean Bruno 	} while (!eop);
771f2d6ace4SSean Bruno 
7723e501ef8SKevin Bowling 	rxr->rx_packets++;
7733e501ef8SKevin Bowling 
77452f45d8aSVincenzo Maffione 	if (scctx->isc_capenable & IFCAP_RXCSUM)
775015075f3SKevin Bowling 		em_receive_checksum(staterr, staterr >> 24, ri);
776f2d6ace4SSean Bruno 
777918c2567SKevin Bowling 	if (scctx->isc_capenable & IFCAP_VLAN_HWTAGGING &&
778918c2567SKevin Bowling 	    staterr & E1000_RXD_STAT_VP) {
779f7926a6dSVincenzo Maffione 		ri->iri_vtag = le16toh(rxd->wb.upper.vlan);
780f2d6ace4SSean Bruno 		ri->iri_flags |= M_VLANTAG;
781f7926a6dSVincenzo Maffione 	}
782f2d6ace4SSean Bruno 
78395246abbSSean Bruno 	ri->iri_flowid = le32toh(rxd->wb.lower.hi_dword.rss);
784cbf1505dSSean Bruno 	ri->iri_rsstype = em_determine_rsstype(pkt_info);
785cbf1505dSSean Bruno 
786cbf1505dSSean Bruno 	ri->iri_nfrags = i;
787f2d6ace4SSean Bruno 	return (0);
788f2d6ace4SSean Bruno }
789f2d6ace4SSean Bruno 
790f2d6ace4SSean Bruno /*********************************************************************
791f2d6ace4SSean Bruno  *
792f2d6ace4SSean Bruno  *  Verify that the hardware indicated that the checksum is valid.
793f2d6ace4SSean Bruno  *  Inform the stack about the status of checksum so that stack
794f2d6ace4SSean Bruno  *  doesn't spend time verifying the checksum.
795f2d6ace4SSean Bruno  *
796f2d6ace4SSean Bruno  *********************************************************************/
797f2d6ace4SSean Bruno static void
798015075f3SKevin Bowling em_receive_checksum(uint16_t status, uint8_t errors, if_rxd_info_t ri)
799f2d6ace4SSean Bruno {
800015075f3SKevin Bowling 	if (__predict_false(status & E1000_RXD_STAT_IXSM))
801015075f3SKevin Bowling 		return;
802015075f3SKevin Bowling 
803015075f3SKevin Bowling 	/* If there is a layer 3 or 4 error we are done */
804*7390daf8SKevin Bowling 	if (__predict_false(errors & (E1000_RXD_ERR_IPE |
805*7390daf8SKevin Bowling 	    E1000_RXD_ERR_TCPE)))
806015075f3SKevin Bowling 		return;
807015075f3SKevin Bowling 
808015075f3SKevin Bowling 	/* IP Checksum Good */
809015075f3SKevin Bowling 	if (status & E1000_RXD_STAT_IPCS)
810f2d6ace4SSean Bruno 		ri->iri_csum_flags = (CSUM_IP_CHECKED | CSUM_IP_VALID);
811f2d6ace4SSean Bruno 
812015075f3SKevin Bowling 	/* Valid L4E checksum */
813015075f3SKevin Bowling 	if (__predict_true(status &
814015075f3SKevin Bowling 	    (E1000_RXD_STAT_TCPCS | E1000_RXD_STAT_UDPCS))) {
815015075f3SKevin Bowling 		ri->iri_csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
816f2d6ace4SSean Bruno 		ri->iri_csum_data = htons(0xffff);
817f2d6ace4SSean Bruno 	}
818f2d6ace4SSean Bruno }
819f2d6ace4SSean Bruno 
820cbf1505dSSean Bruno /********************************************************************
821cbf1505dSSean Bruno  *
822cbf1505dSSean Bruno  *  Parse the packet type to determine the appropriate hash
823cbf1505dSSean Bruno  *
824cbf1505dSSean Bruno  ******************************************************************/
825cbf1505dSSean Bruno static int
8265253d74eSKevin Bowling em_determine_rsstype(uint32_t pkt_info)
827cbf1505dSSean Bruno {
828cbf1505dSSean Bruno 	switch (pkt_info & E1000_RXDADV_RSSTYPE_MASK) {
829cbf1505dSSean Bruno 	case E1000_RXDADV_RSSTYPE_IPV4_TCP:
830cbf1505dSSean Bruno 		return M_HASHTYPE_RSS_TCP_IPV4;
831cbf1505dSSean Bruno 	case E1000_RXDADV_RSSTYPE_IPV4:
832cbf1505dSSean Bruno 		return M_HASHTYPE_RSS_IPV4;
833cbf1505dSSean Bruno 	case E1000_RXDADV_RSSTYPE_IPV6_TCP:
834cbf1505dSSean Bruno 		return M_HASHTYPE_RSS_TCP_IPV6;
835cbf1505dSSean Bruno 	case E1000_RXDADV_RSSTYPE_IPV6_EX:
836cbf1505dSSean Bruno 		return M_HASHTYPE_RSS_IPV6_EX;
837cbf1505dSSean Bruno 	case E1000_RXDADV_RSSTYPE_IPV6:
838cbf1505dSSean Bruno 		return M_HASHTYPE_RSS_IPV6;
839cbf1505dSSean Bruno 	case E1000_RXDADV_RSSTYPE_IPV6_TCP_EX:
840cbf1505dSSean Bruno 		return M_HASHTYPE_RSS_TCP_IPV6_EX;
841cbf1505dSSean Bruno 	default:
842cbf1505dSSean Bruno 		return M_HASHTYPE_OPAQUE;
843cbf1505dSSean Bruno 	}
844cbf1505dSSean Bruno }
845