xref: /freebsd/sys/dev/e1000/em_txrx.c (revision f7926a6d)
1d37cece2SSean Bruno /*-
27021bf05SStephen Hurd  * Copyright (c) 2016 Nicole Graziano <nicole@nextbsd.org>
37021bf05SStephen Hurd  * Copyright (c) 2017 Matthew Macy <mmacy@mattmacy.io>
4d37cece2SSean Bruno  * All rights reserved.
5d37cece2SSean Bruno  *
6d37cece2SSean Bruno  * Redistribution and use in source and binary forms, with or without
7d37cece2SSean Bruno  * modification, are permitted provided that the following conditions
8d37cece2SSean Bruno  * are met:
9d37cece2SSean Bruno  * 1. Redistributions of source code must retain the above copyright
10d37cece2SSean Bruno  *    notice, this list of conditions and the following disclaimer.
11d37cece2SSean Bruno  * 2. Redistributions in binary form must reproduce the above copyright
12d37cece2SSean Bruno  *    notice, this list of conditions and the following disclaimer in the
13d37cece2SSean Bruno  *    documentation and/or other materials provided with the distribution.
14d37cece2SSean Bruno  *
15d37cece2SSean Bruno  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16d37cece2SSean Bruno  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17d37cece2SSean Bruno  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18d37cece2SSean Bruno  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19d37cece2SSean Bruno  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20d37cece2SSean Bruno  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21d37cece2SSean Bruno  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22d37cece2SSean Bruno  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23d37cece2SSean Bruno  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24d37cece2SSean Bruno  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25d37cece2SSean Bruno  * SUCH DAMAGE.
26d37cece2SSean Bruno  */
27d37cece2SSean Bruno 
28f2d6ace4SSean Bruno /* $FreeBSD$ */
29f2d6ace4SSean Bruno #include "if_em.h"
30f2d6ace4SSean Bruno 
31f2d6ace4SSean Bruno #ifdef RSS
32f2d6ace4SSean Bruno #include <net/rss_config.h>
33f2d6ace4SSean Bruno #include <netinet/in_rss.h>
34f2d6ace4SSean Bruno #endif
35f2d6ace4SSean Bruno 
36f2d6ace4SSean Bruno #ifdef VERBOSE_DEBUG
37f2d6ace4SSean Bruno #define DPRINTF device_printf
38f2d6ace4SSean Bruno #else
39f2d6ace4SSean Bruno #define DPRINTF(...)
40f2d6ace4SSean Bruno #endif
41f2d6ace4SSean Bruno 
42f2d6ace4SSean Bruno /*********************************************************************
43f2d6ace4SSean Bruno  *  Local Function prototypes
44f2d6ace4SSean Bruno  *********************************************************************/
45dc926051SKevin Bowling static int em_tso_setup(struct e1000_softc *sc, if_pkt_info_t pi, u32 *txd_upper,
4695246abbSSean Bruno     u32 *txd_lower);
47dc926051SKevin Bowling static int em_transmit_checksum_setup(struct e1000_softc *sc, if_pkt_info_t pi,
4895246abbSSean Bruno     u32 *txd_upper, u32 *txd_lower);
49f2d6ace4SSean Bruno static int em_isc_txd_encap(void *arg, if_pkt_info_t pi);
5095246abbSSean Bruno static void em_isc_txd_flush(void *arg, uint16_t txqid, qidx_t pidx);
5195246abbSSean Bruno static int em_isc_txd_credits_update(void *arg, uint16_t txqid, bool clear);
5295246abbSSean Bruno static void em_isc_rxd_refill(void *arg, if_rxd_update_t iru);
5395246abbSSean Bruno static void em_isc_rxd_flush(void *arg, uint16_t rxqid, uint8_t flid __unused,
5495246abbSSean Bruno     qidx_t pidx);
5595246abbSSean Bruno static int em_isc_rxd_available(void *arg, uint16_t rxqid, qidx_t idx,
5695246abbSSean Bruno     qidx_t budget);
57f2d6ace4SSean Bruno static int em_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri);
58f2d6ace4SSean Bruno 
5995246abbSSean Bruno static void lem_isc_rxd_refill(void *arg, if_rxd_update_t iru);
60f2d6ace4SSean Bruno 
6195246abbSSean Bruno static int lem_isc_rxd_available(void *arg, uint16_t rxqid, qidx_t idx,
6295246abbSSean Bruno    qidx_t budget);
63f2d6ace4SSean Bruno static int lem_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri);
64f2d6ace4SSean Bruno 
65015075f3SKevin Bowling static void em_receive_checksum(uint16_t, uint8_t, if_rxd_info_t);
66cbf1505dSSean Bruno static int em_determine_rsstype(u32 pkt_info);
67f2d6ace4SSean Bruno extern int em_intr(void *arg);
68f2d6ace4SSean Bruno 
69f2d6ace4SSean Bruno struct if_txrx em_txrx = {
70fbf8b74cSMark Johnston 	.ift_txd_encap = em_isc_txd_encap,
71fbf8b74cSMark Johnston 	.ift_txd_flush = em_isc_txd_flush,
72fbf8b74cSMark Johnston 	.ift_txd_credits_update = em_isc_txd_credits_update,
73fbf8b74cSMark Johnston 	.ift_rxd_available = em_isc_rxd_available,
74fbf8b74cSMark Johnston 	.ift_rxd_pkt_get = em_isc_rxd_pkt_get,
75fbf8b74cSMark Johnston 	.ift_rxd_refill = em_isc_rxd_refill,
76fbf8b74cSMark Johnston 	.ift_rxd_flush = em_isc_rxd_flush,
77fbf8b74cSMark Johnston 	.ift_legacy_intr = em_intr
78f2d6ace4SSean Bruno };
79f2d6ace4SSean Bruno 
80f2d6ace4SSean Bruno struct if_txrx lem_txrx = {
81fbf8b74cSMark Johnston 	.ift_txd_encap = em_isc_txd_encap,
82fbf8b74cSMark Johnston 	.ift_txd_flush = em_isc_txd_flush,
83fbf8b74cSMark Johnston 	.ift_txd_credits_update = em_isc_txd_credits_update,
84fbf8b74cSMark Johnston 	.ift_rxd_available = lem_isc_rxd_available,
85fbf8b74cSMark Johnston 	.ift_rxd_pkt_get = lem_isc_rxd_pkt_get,
86fbf8b74cSMark Johnston 	.ift_rxd_refill = lem_isc_rxd_refill,
87fbf8b74cSMark Johnston 	.ift_rxd_flush = em_isc_rxd_flush,
88fbf8b74cSMark Johnston 	.ift_legacy_intr = em_intr
89f2d6ace4SSean Bruno };
90f2d6ace4SSean Bruno 
91f2d6ace4SSean Bruno extern if_shared_ctx_t em_sctx;
92f2d6ace4SSean Bruno 
9395246abbSSean Bruno void
94dc926051SKevin Bowling em_dump_rs(struct e1000_softc *sc)
9595246abbSSean Bruno {
96dc926051SKevin Bowling 	if_softc_ctx_t scctx = sc->shared;
9795246abbSSean Bruno 	struct em_tx_queue *que;
9895246abbSSean Bruno 	struct tx_ring *txr;
9995246abbSSean Bruno 	qidx_t i, ntxd, qid, cur;
10095246abbSSean Bruno 	int16_t rs_cidx;
10195246abbSSean Bruno 	uint8_t status;
10295246abbSSean Bruno 
10395246abbSSean Bruno 	printf("\n");
10495246abbSSean Bruno 	ntxd = scctx->isc_ntxd[0];
105dc926051SKevin Bowling 	for (qid = 0; qid < sc->tx_num_queues; qid++) {
106dc926051SKevin Bowling 		que = &sc->tx_queues[qid];
10795246abbSSean Bruno 		txr =  &que->txr;
10895246abbSSean Bruno 		rs_cidx = txr->tx_rs_cidx;
10995246abbSSean Bruno 		if (rs_cidx != txr->tx_rs_pidx) {
11095246abbSSean Bruno 			cur = txr->tx_rsq[rs_cidx];
11195246abbSSean Bruno 			status = txr->tx_base[cur].upper.fields.status;
11295246abbSSean Bruno 			if (!(status & E1000_TXD_STAT_DD))
11395246abbSSean Bruno 				printf("qid[%d]->tx_rsq[%d]: %d clear ", qid, rs_cidx, cur);
11495246abbSSean Bruno 		} else {
11595246abbSSean Bruno 			rs_cidx = (rs_cidx-1)&(ntxd-1);
11695246abbSSean Bruno 			cur = txr->tx_rsq[rs_cidx];
11795246abbSSean Bruno 			printf("qid[%d]->tx_rsq[rs_cidx-1=%d]: %d  ", qid, rs_cidx, cur);
11895246abbSSean Bruno 		}
11995246abbSSean Bruno 		printf("cidx_prev=%d rs_pidx=%d ",txr->tx_cidx_processed, txr->tx_rs_pidx);
12095246abbSSean Bruno 		for (i = 0; i < ntxd; i++) {
12195246abbSSean Bruno 			if (txr->tx_base[i].upper.fields.status & E1000_TXD_STAT_DD)
12295246abbSSean Bruno 				printf("%d set ", i);
12395246abbSSean Bruno 		}
12495246abbSSean Bruno 		printf("\n");
12595246abbSSean Bruno 	}
12695246abbSSean Bruno }
12795246abbSSean Bruno 
128f2d6ace4SSean Bruno /**********************************************************************
129f2d6ace4SSean Bruno  *
130f2d6ace4SSean Bruno  *  Setup work for hardware segmentation offload (TSO) on
131f2d6ace4SSean Bruno  *  adapters using advanced tx descriptors
132f2d6ace4SSean Bruno  *
133f2d6ace4SSean Bruno  **********************************************************************/
134f2d6ace4SSean Bruno static int
135dc926051SKevin Bowling em_tso_setup(struct e1000_softc *sc, if_pkt_info_t pi, u32 *txd_upper, u32 *txd_lower)
136f2d6ace4SSean Bruno {
137dc926051SKevin Bowling 	if_softc_ctx_t scctx = sc->shared;
138dc926051SKevin Bowling 	struct em_tx_queue *que = &sc->tx_queues[pi->ipi_qsidx];
139f2d6ace4SSean Bruno 	struct tx_ring *txr = &que->txr;
140f2d6ace4SSean Bruno 	struct e1000_context_desc *TXD;
141f2d6ace4SSean Bruno 	int cur, hdr_len;
142f2d6ace4SSean Bruno 
143f2d6ace4SSean Bruno 	hdr_len = pi->ipi_ehdrlen + pi->ipi_ip_hlen + pi->ipi_tcp_hlen;
144f2d6ace4SSean Bruno 	*txd_lower = (E1000_TXD_CMD_DEXT |	/* Extended descr type */
145f2d6ace4SSean Bruno 		      E1000_TXD_DTYP_D |	/* Data descr type */
146f2d6ace4SSean Bruno 		      E1000_TXD_CMD_TSE);	/* Do TSE on this packet */
147f2d6ace4SSean Bruno 
148f2d6ace4SSean Bruno 	/* IP and/or TCP header checksum calculation and insertion. */
149f2d6ace4SSean Bruno 	*txd_upper = (E1000_TXD_POPTS_IXSM | E1000_TXD_POPTS_TXSM) << 8;
150f2d6ace4SSean Bruno 
151f2d6ace4SSean Bruno 	cur = pi->ipi_pidx;
152f2d6ace4SSean Bruno 	TXD = (struct e1000_context_desc *)&txr->tx_base[cur];
153f2d6ace4SSean Bruno 
154f2d6ace4SSean Bruno 	/*
155f2d6ace4SSean Bruno 	 * Start offset for header checksum calculation.
156f2d6ace4SSean Bruno 	 * End offset for header checksum calculation.
157f2d6ace4SSean Bruno 	 * Offset of place put the checksum.
158f2d6ace4SSean Bruno 	 */
159f2d6ace4SSean Bruno 	TXD->lower_setup.ip_fields.ipcss = pi->ipi_ehdrlen;
160f2d6ace4SSean Bruno 	TXD->lower_setup.ip_fields.ipcse =
161f2d6ace4SSean Bruno 	    htole16(pi->ipi_ehdrlen + pi->ipi_ip_hlen - 1);
162f2d6ace4SSean Bruno 	TXD->lower_setup.ip_fields.ipcso = pi->ipi_ehdrlen + offsetof(struct ip, ip_sum);
163f2d6ace4SSean Bruno 
164f2d6ace4SSean Bruno 	/*
165f2d6ace4SSean Bruno 	 * Start offset for payload checksum calculation.
166f2d6ace4SSean Bruno 	 * End offset for payload checksum calculation.
167f2d6ace4SSean Bruno 	 * Offset of place to put the checksum.
168f2d6ace4SSean Bruno 	 */
169f2d6ace4SSean Bruno 	TXD->upper_setup.tcp_fields.tucss = pi->ipi_ehdrlen + pi->ipi_ip_hlen;
170f2d6ace4SSean Bruno 	TXD->upper_setup.tcp_fields.tucse = 0;
171f2d6ace4SSean Bruno 	TXD->upper_setup.tcp_fields.tucso =
172f2d6ace4SSean Bruno 	    pi->ipi_ehdrlen + pi->ipi_ip_hlen + offsetof(struct tcphdr, th_sum);
173f2d6ace4SSean Bruno 
174f2d6ace4SSean Bruno 	/*
175f2d6ace4SSean Bruno 	 * Payload size per packet w/o any headers.
176f2d6ace4SSean Bruno 	 * Length of all headers up to payload.
177f2d6ace4SSean Bruno 	 */
178f2d6ace4SSean Bruno 	TXD->tcp_seg_setup.fields.mss = htole16(pi->ipi_tso_segsz);
179f2d6ace4SSean Bruno 	TXD->tcp_seg_setup.fields.hdr_len = hdr_len;
180f2d6ace4SSean Bruno 
181dc926051SKevin Bowling 	TXD->cmd_and_length = htole32(sc->txd_cmd |
182f2d6ace4SSean Bruno 				E1000_TXD_CMD_DEXT |	/* Extended descr */
183f2d6ace4SSean Bruno 				E1000_TXD_CMD_TSE |	/* TSE context */
184f2d6ace4SSean Bruno 				E1000_TXD_CMD_IP |	/* Do IP csum */
185f2d6ace4SSean Bruno 				E1000_TXD_CMD_TCP |	/* Do TCP checksum */
186f2d6ace4SSean Bruno 				      (pi->ipi_len - hdr_len)); /* Total len */
1871bbdc25fSKevin Bowling 	txr->tx_tso = true;
188f2d6ace4SSean Bruno 
189f2d6ace4SSean Bruno 	if (++cur == scctx->isc_ntxd[0]) {
190f2d6ace4SSean Bruno 		cur = 0;
191f2d6ace4SSean Bruno 	}
192dc926051SKevin Bowling 	DPRINTF(iflib_get_dev(sc->ctx), "%s: pidx: %d cur: %d\n", __FUNCTION__, pi->ipi_pidx, cur);
193f2d6ace4SSean Bruno 	return (cur);
194f2d6ace4SSean Bruno }
195f2d6ace4SSean Bruno 
196f2d6ace4SSean Bruno #define TSO_WORKAROUND 4
197f2d6ace4SSean Bruno #define DONT_FORCE_CTX 1
198f2d6ace4SSean Bruno 
199f2d6ace4SSean Bruno 
200f2d6ace4SSean Bruno /*********************************************************************
201f2d6ace4SSean Bruno  *  The offload context is protocol specific (TCP/UDP) and thus
202f2d6ace4SSean Bruno  *  only needs to be set when the protocol changes. The occasion
203f2d6ace4SSean Bruno  *  of a context change can be a performance detriment, and
204f2d6ace4SSean Bruno  *  might be better just disabled. The reason arises in the way
205f2d6ace4SSean Bruno  *  in which the controller supports pipelined requests from the
206f2d6ace4SSean Bruno  *  Tx data DMA. Up to four requests can be pipelined, and they may
207f2d6ace4SSean Bruno  *  belong to the same packet or to multiple packets. However all
208f2d6ace4SSean Bruno  *  requests for one packet are issued before a request is issued
209f2d6ace4SSean Bruno  *  for a subsequent packet and if a request for the next packet
210f2d6ace4SSean Bruno  *  requires a context change, that request will be stalled
211f2d6ace4SSean Bruno  *  until the previous request completes. This means setting up
212f2d6ace4SSean Bruno  *  a new context effectively disables pipelined Tx data DMA which
213f2d6ace4SSean Bruno  *  in turn greatly slow down performance to send small sized
214f2d6ace4SSean Bruno  *  frames.
215f2d6ace4SSean Bruno  **********************************************************************/
216f2d6ace4SSean Bruno 
217f2d6ace4SSean Bruno static int
218dc926051SKevin Bowling em_transmit_checksum_setup(struct e1000_softc *sc, if_pkt_info_t pi, u32 *txd_upper, u32 *txd_lower)
219f2d6ace4SSean Bruno {
220f2d6ace4SSean Bruno 	 struct e1000_context_desc *TXD = NULL;
221dc926051SKevin Bowling 	if_softc_ctx_t scctx = sc->shared;
222dc926051SKevin Bowling 	struct em_tx_queue *que = &sc->tx_queues[pi->ipi_qsidx];
223f2d6ace4SSean Bruno 	struct tx_ring *txr = &que->txr;
224f2d6ace4SSean Bruno 	int csum_flags = pi->ipi_csum_flags;
225f2d6ace4SSean Bruno 	int cur, hdr_len;
226f2d6ace4SSean Bruno 	u32 cmd;
227f2d6ace4SSean Bruno 
228f2d6ace4SSean Bruno 	cur = pi->ipi_pidx;
229f2d6ace4SSean Bruno 	hdr_len = pi->ipi_ehdrlen + pi->ipi_ip_hlen;
230dc926051SKevin Bowling 	cmd = sc->txd_cmd;
231f2d6ace4SSean Bruno 
232f2d6ace4SSean Bruno 	/*
233f2d6ace4SSean Bruno 	 * The 82574L can only remember the *last* context used
234f2d6ace4SSean Bruno 	 * regardless of queue that it was use for.  We cannot reuse
235f2d6ace4SSean Bruno 	 * contexts on this hardware platform and must generate a new
236f2d6ace4SSean Bruno 	 * context every time.  82574L hardware spec, section 7.2.6,
237f2d6ace4SSean Bruno 	 * second note.
238f2d6ace4SSean Bruno 	 */
239f2d6ace4SSean Bruno 	if (DONT_FORCE_CTX &&
240dc926051SKevin Bowling 	    sc->tx_num_queues == 1 &&
241f2d6ace4SSean Bruno 	    txr->csum_lhlen == pi->ipi_ehdrlen &&
242f2d6ace4SSean Bruno 	    txr->csum_iphlen == pi->ipi_ip_hlen &&
243f2d6ace4SSean Bruno 	    txr->csum_flags == csum_flags) {
244f2d6ace4SSean Bruno 		/*
245f2d6ace4SSean Bruno 		 * Same csum offload context as the previous packets;
246f2d6ace4SSean Bruno 		 * just return.
247f2d6ace4SSean Bruno 		 */
248f2d6ace4SSean Bruno 		*txd_upper = txr->csum_txd_upper;
249f2d6ace4SSean Bruno 		*txd_lower = txr->csum_txd_lower;
250f2d6ace4SSean Bruno 		return (cur);
251f2d6ace4SSean Bruno 	}
252f2d6ace4SSean Bruno 
253f2d6ace4SSean Bruno 	TXD = (struct e1000_context_desc *)&txr->tx_base[cur];
254f2d6ace4SSean Bruno 	if (csum_flags & CSUM_IP) {
255f2d6ace4SSean Bruno 		*txd_upper |= E1000_TXD_POPTS_IXSM << 8;
256f2d6ace4SSean Bruno 		/*
257f2d6ace4SSean Bruno 		 * Start offset for header checksum calculation.
258f2d6ace4SSean Bruno 		 * End offset for header checksum calculation.
259f2d6ace4SSean Bruno 		 * Offset of place to put the checksum.
260f2d6ace4SSean Bruno 		 */
261f2d6ace4SSean Bruno 		TXD->lower_setup.ip_fields.ipcss = pi->ipi_ehdrlen;
262f2d6ace4SSean Bruno 		TXD->lower_setup.ip_fields.ipcse = htole16(hdr_len);
263f2d6ace4SSean Bruno 		TXD->lower_setup.ip_fields.ipcso = pi->ipi_ehdrlen + offsetof(struct ip, ip_sum);
264f2d6ace4SSean Bruno 		cmd |= E1000_TXD_CMD_IP;
265f2d6ace4SSean Bruno 	}
266f2d6ace4SSean Bruno 
267f2d6ace4SSean Bruno 	if (csum_flags & (CSUM_TCP|CSUM_UDP)) {
268f2d6ace4SSean Bruno 		uint8_t tucso;
269f2d6ace4SSean Bruno 
270f2d6ace4SSean Bruno 		*txd_upper |= E1000_TXD_POPTS_TXSM << 8;
271f2d6ace4SSean Bruno 		*txd_lower = E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D;
272f2d6ace4SSean Bruno 
273f2d6ace4SSean Bruno 		if (csum_flags & CSUM_TCP) {
274f2d6ace4SSean Bruno 			tucso = hdr_len + offsetof(struct tcphdr, th_sum);
275f2d6ace4SSean Bruno 			cmd |= E1000_TXD_CMD_TCP;
276f2d6ace4SSean Bruno 		} else
277f2d6ace4SSean Bruno 			tucso = hdr_len + offsetof(struct udphdr, uh_sum);
278f2d6ace4SSean Bruno 		TXD->upper_setup.tcp_fields.tucss = hdr_len;
279f2d6ace4SSean Bruno 		TXD->upper_setup.tcp_fields.tucse = htole16(0);
280f2d6ace4SSean Bruno 		TXD->upper_setup.tcp_fields.tucso = tucso;
281f2d6ace4SSean Bruno 	}
282f2d6ace4SSean Bruno 
283f2d6ace4SSean Bruno 	txr->csum_lhlen = pi->ipi_ehdrlen;
284f2d6ace4SSean Bruno 	txr->csum_iphlen = pi->ipi_ip_hlen;
285f2d6ace4SSean Bruno 	txr->csum_flags = csum_flags;
286f2d6ace4SSean Bruno 	txr->csum_txd_upper = *txd_upper;
287f2d6ace4SSean Bruno 	txr->csum_txd_lower = *txd_lower;
288f2d6ace4SSean Bruno 
289f2d6ace4SSean Bruno 	TXD->tcp_seg_setup.data = htole32(0);
290f2d6ace4SSean Bruno 	TXD->cmd_and_length =
291f2d6ace4SSean Bruno 		htole32(E1000_TXD_CMD_IFCS | E1000_TXD_CMD_DEXT | cmd);
292f2d6ace4SSean Bruno 
293f2d6ace4SSean Bruno 	if (++cur == scctx->isc_ntxd[0]) {
294f2d6ace4SSean Bruno 		cur = 0;
295f2d6ace4SSean Bruno 	}
296dc926051SKevin Bowling 	DPRINTF(iflib_get_dev(sc->ctx), "checksum_setup csum_flags=%x txd_upper=%x txd_lower=%x hdr_len=%d cmd=%x\n",
297f2d6ace4SSean Bruno 		      csum_flags, *txd_upper, *txd_lower, hdr_len, cmd);
298f2d6ace4SSean Bruno 	return (cur);
299f2d6ace4SSean Bruno }
300f2d6ace4SSean Bruno 
301f2d6ace4SSean Bruno static int
302f2d6ace4SSean Bruno em_isc_txd_encap(void *arg, if_pkt_info_t pi)
303f2d6ace4SSean Bruno {
304dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
305f2d6ace4SSean Bruno 	if_softc_ctx_t scctx = sc->shared;
306f2d6ace4SSean Bruno 	struct em_tx_queue *que = &sc->tx_queues[pi->ipi_qsidx];
307f2d6ace4SSean Bruno 	struct tx_ring *txr = &que->txr;
308f2d6ace4SSean Bruno 	bus_dma_segment_t *segs = pi->ipi_segs;
309f2d6ace4SSean Bruno 	int nsegs = pi->ipi_nsegs;
310f2d6ace4SSean Bruno 	int csum_flags = pi->ipi_csum_flags;
311f2d6ace4SSean Bruno 	int i, j, first, pidx_last;
31295246abbSSean Bruno 	u32 txd_flags, txd_upper = 0, txd_lower = 0;
313f2d6ace4SSean Bruno 
314f2d6ace4SSean Bruno 	struct e1000_tx_desc *ctxd = NULL;
315f2d6ace4SSean Bruno 	bool do_tso, tso_desc;
31695246abbSSean Bruno 	qidx_t ntxd;
317f2d6ace4SSean Bruno 
31895246abbSSean Bruno 	txd_flags = pi->ipi_flags & IPI_TX_INTR ? E1000_TXD_CMD_RS : 0;
319f2d6ace4SSean Bruno 	i = first = pi->ipi_pidx;
320f2d6ace4SSean Bruno 	do_tso = (csum_flags & CSUM_TSO);
3211bbdc25fSKevin Bowling 	tso_desc = false;
32295246abbSSean Bruno 	ntxd = scctx->isc_ntxd[0];
323f2d6ace4SSean Bruno 	/*
324f2d6ace4SSean Bruno 	 * TSO Hardware workaround, if this packet is not
325f2d6ace4SSean Bruno 	 * TSO, and is only a single descriptor long, and
326f2d6ace4SSean Bruno 	 * it follows a TSO burst, then we need to add a
327f2d6ace4SSean Bruno 	 * sentinel descriptor to prevent premature writeback.
328f2d6ace4SSean Bruno 	 */
3291bbdc25fSKevin Bowling 	if ((!do_tso) && (txr->tx_tso == true)) {
330f2d6ace4SSean Bruno 		if (nsegs == 1)
3311bbdc25fSKevin Bowling 			tso_desc = true;
3321bbdc25fSKevin Bowling 		txr->tx_tso = false;
333f2d6ace4SSean Bruno 	}
334f2d6ace4SSean Bruno 
335f2d6ace4SSean Bruno 	/* Do hardware assists */
336f2d6ace4SSean Bruno 	if (do_tso) {
337f2d6ace4SSean Bruno 		i = em_tso_setup(sc, pi, &txd_upper, &txd_lower);
3381bbdc25fSKevin Bowling 		tso_desc = true;
33982379056SSean Bruno 	} else if (csum_flags & EM_CSUM_OFFLOAD) {
340f2d6ace4SSean Bruno 		i = em_transmit_checksum_setup(sc, pi, &txd_upper, &txd_lower);
341f2d6ace4SSean Bruno 	}
342f2d6ace4SSean Bruno 
343f2d6ace4SSean Bruno 	if (pi->ipi_mflags & M_VLANTAG) {
344f2d6ace4SSean Bruno 		/* Set the vlan id. */
345f2d6ace4SSean Bruno 		txd_upper |= htole16(pi->ipi_vtag) << 16;
346f2d6ace4SSean Bruno 		/* Tell hardware to add tag */
347f2d6ace4SSean Bruno 		txd_lower |= htole32(E1000_TXD_CMD_VLE);
348f2d6ace4SSean Bruno 	}
349f2d6ace4SSean Bruno 
350f2d6ace4SSean Bruno 	DPRINTF(iflib_get_dev(sc->ctx), "encap: set up tx: nsegs=%d first=%d i=%d\n", nsegs, first, i);
351dc926051SKevin Bowling 	/* XXX sc->pcix_82544 -- lem_fill_descriptors */
352f2d6ace4SSean Bruno 
353f2d6ace4SSean Bruno 	/* Set up our transmit descriptors */
354f2d6ace4SSean Bruno 	for (j = 0; j < nsegs; j++) {
355f2d6ace4SSean Bruno 		bus_size_t seg_len;
356f2d6ace4SSean Bruno 		bus_addr_t seg_addr;
357f2d6ace4SSean Bruno 		uint32_t cmd;
358f2d6ace4SSean Bruno 
359f2d6ace4SSean Bruno 		ctxd = &txr->tx_base[i];
360f2d6ace4SSean Bruno 		seg_addr = segs[j].ds_addr;
361f2d6ace4SSean Bruno 		seg_len = segs[j].ds_len;
362f2d6ace4SSean Bruno 		cmd = E1000_TXD_CMD_IFCS | sc->txd_cmd;
363f2d6ace4SSean Bruno 
364f2d6ace4SSean Bruno 		/*
36595246abbSSean Bruno 		 * TSO Workaround:
36695246abbSSean Bruno 		 * If this is the last descriptor, we want to
36795246abbSSean Bruno 		 * split it so we have a small final sentinel
368f2d6ace4SSean Bruno 		 */
369f2d6ace4SSean Bruno 		if (tso_desc && (j == (nsegs - 1)) && (seg_len > 8)) {
370f2d6ace4SSean Bruno 			seg_len -= TSO_WORKAROUND;
371f2d6ace4SSean Bruno 			ctxd->buffer_addr = htole64(seg_addr);
372f2d6ace4SSean Bruno 			ctxd->lower.data = htole32(cmd | txd_lower | seg_len);
373f2d6ace4SSean Bruno 			ctxd->upper.data = htole32(txd_upper);
374f2d6ace4SSean Bruno 
375f2d6ace4SSean Bruno 			if (++i == scctx->isc_ntxd[0])
376f2d6ace4SSean Bruno 				i = 0;
377f2d6ace4SSean Bruno 
378f2d6ace4SSean Bruno 			/* Now make the sentinel */
379f2d6ace4SSean Bruno 			ctxd = &txr->tx_base[i];
380f2d6ace4SSean Bruno 			ctxd->buffer_addr = htole64(seg_addr + seg_len);
381f2d6ace4SSean Bruno 			ctxd->lower.data = htole32(cmd | txd_lower | TSO_WORKAROUND);
382f2d6ace4SSean Bruno 			ctxd->upper.data = htole32(txd_upper);
383f2d6ace4SSean Bruno 			pidx_last = i;
384f2d6ace4SSean Bruno 			if (++i == scctx->isc_ntxd[0])
385f2d6ace4SSean Bruno 				i = 0;
386f2d6ace4SSean Bruno 			DPRINTF(iflib_get_dev(sc->ctx), "TSO path pidx_last=%d i=%d ntxd[0]=%d\n", pidx_last, i, scctx->isc_ntxd[0]);
387f2d6ace4SSean Bruno 		} else {
388f2d6ace4SSean Bruno 			ctxd->buffer_addr = htole64(seg_addr);
389f2d6ace4SSean Bruno 			ctxd->lower.data = htole32(cmd | txd_lower | seg_len);
390f2d6ace4SSean Bruno 			ctxd->upper.data = htole32(txd_upper);
391f2d6ace4SSean Bruno 			pidx_last = i;
392f2d6ace4SSean Bruno 			if (++i == scctx->isc_ntxd[0])
393f2d6ace4SSean Bruno 				i = 0;
394f2d6ace4SSean Bruno 			DPRINTF(iflib_get_dev(sc->ctx), "pidx_last=%d i=%d ntxd[0]=%d\n", pidx_last, i, scctx->isc_ntxd[0]);
395f2d6ace4SSean Bruno 		}
396f2d6ace4SSean Bruno 	}
397f2d6ace4SSean Bruno 
398f2d6ace4SSean Bruno 	/*
399f2d6ace4SSean Bruno 	 * Last Descriptor of Packet
400f2d6ace4SSean Bruno 	 * needs End Of Packet (EOP)
401f2d6ace4SSean Bruno 	 * and Report Status (RS)
402f2d6ace4SSean Bruno 	 */
4038fd222ebSMatt Macy 	if (txd_flags && nsegs) {
40495246abbSSean Bruno 		txr->tx_rsq[txr->tx_rs_pidx] = pidx_last;
40595246abbSSean Bruno 		DPRINTF(iflib_get_dev(sc->ctx), "setting to RS on %d rs_pidx %d first: %d\n", pidx_last, txr->tx_rs_pidx, first);
40695246abbSSean Bruno 		txr->tx_rs_pidx = (txr->tx_rs_pidx+1) & (ntxd-1);
40795246abbSSean Bruno 		MPASS(txr->tx_rs_pidx != txr->tx_rs_cidx);
40895246abbSSean Bruno 	}
40995246abbSSean Bruno 	ctxd->lower.data |= htole32(E1000_TXD_CMD_EOP | txd_flags);
410f2d6ace4SSean Bruno 	DPRINTF(iflib_get_dev(sc->ctx), "tx_buffers[%d]->eop = %d ipi_new_pidx=%d\n", first, pidx_last, i);
411f2d6ace4SSean Bruno 	pi->ipi_new_pidx = i;
412f2d6ace4SSean Bruno 
413f2d6ace4SSean Bruno 	return (0);
414f2d6ace4SSean Bruno }
415f2d6ace4SSean Bruno 
416f2d6ace4SSean Bruno static void
41795246abbSSean Bruno em_isc_txd_flush(void *arg, uint16_t txqid, qidx_t pidx)
418f2d6ace4SSean Bruno {
419dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
420dc926051SKevin Bowling 	struct em_tx_queue *que = &sc->tx_queues[txqid];
421f2d6ace4SSean Bruno 	struct tx_ring *txr = &que->txr;
422f2d6ace4SSean Bruno 
423dc926051SKevin Bowling 	E1000_WRITE_REG(&sc->hw, E1000_TDT(txr->me), pidx);
424f2d6ace4SSean Bruno }
425f2d6ace4SSean Bruno 
426f2d6ace4SSean Bruno static int
42795246abbSSean Bruno em_isc_txd_credits_update(void *arg, uint16_t txqid, bool clear)
428f2d6ace4SSean Bruno {
429dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
430dc926051SKevin Bowling 	if_softc_ctx_t scctx = sc->shared;
431dc926051SKevin Bowling 	struct em_tx_queue *que = &sc->tx_queues[txqid];
432f2d6ace4SSean Bruno 	struct tx_ring *txr = &que->txr;
433f2d6ace4SSean Bruno 
43495246abbSSean Bruno 	qidx_t processed = 0;
43595246abbSSean Bruno 	int updated;
43695246abbSSean Bruno 	qidx_t cur, prev, ntxd, rs_cidx;
43795246abbSSean Bruno 	int32_t delta;
43895246abbSSean Bruno 	uint8_t status;
439f2d6ace4SSean Bruno 
44095246abbSSean Bruno 	rs_cidx = txr->tx_rs_cidx;
44195246abbSSean Bruno 	if (rs_cidx == txr->tx_rs_pidx)
44295246abbSSean Bruno 		return (0);
44395246abbSSean Bruno 	cur = txr->tx_rsq[rs_cidx];
44495246abbSSean Bruno 	MPASS(cur != QIDX_INVALID);
44595246abbSSean Bruno 	status = txr->tx_base[cur].upper.fields.status;
44695246abbSSean Bruno 	updated = !!(status & E1000_TXD_STAT_DD);
447f2d6ace4SSean Bruno 
448adf93b56SEric Joyner 	if (!updated)
449adf93b56SEric Joyner 		return (0);
450adf93b56SEric Joyner 
451adf93b56SEric Joyner 	/* If clear is false just let caller know that there
452adf93b56SEric Joyner 	 * are descriptors to reclaim */
453adf93b56SEric Joyner 	if (!clear)
454adf93b56SEric Joyner 		return (1);
45595246abbSSean Bruno 
45695246abbSSean Bruno 	prev = txr->tx_cidx_processed;
45795246abbSSean Bruno 	ntxd = scctx->isc_ntxd[0];
45895246abbSSean Bruno 	do {
459088a0b27SEric Joyner 		MPASS(prev != cur);
46095246abbSSean Bruno 		delta = (int32_t)cur - (int32_t)prev;
46195246abbSSean Bruno 		if (delta < 0)
46295246abbSSean Bruno 			delta += ntxd;
463088a0b27SEric Joyner 		MPASS(delta > 0);
464dc926051SKevin Bowling 		DPRINTF(iflib_get_dev(sc->ctx),
46595246abbSSean Bruno 			      "%s: cidx_processed=%u cur=%u clear=%d delta=%d\n",
46695246abbSSean Bruno 			      __FUNCTION__, prev, cur, clear, delta);
467f2d6ace4SSean Bruno 
46895246abbSSean Bruno 		processed += delta;
46995246abbSSean Bruno 		prev  = cur;
47095246abbSSean Bruno 		rs_cidx = (rs_cidx + 1) & (ntxd-1);
47195246abbSSean Bruno 		if (rs_cidx  == txr->tx_rs_pidx)
472f2d6ace4SSean Bruno 			break;
47395246abbSSean Bruno 		cur = txr->tx_rsq[rs_cidx];
47495246abbSSean Bruno 		MPASS(cur != QIDX_INVALID);
47595246abbSSean Bruno 		status = txr->tx_base[cur].upper.fields.status;
47695246abbSSean Bruno 	} while ((status & E1000_TXD_STAT_DD));
477f2d6ace4SSean Bruno 
47895246abbSSean Bruno 	txr->tx_rs_cidx = rs_cidx;
47995246abbSSean Bruno 	txr->tx_cidx_processed = prev;
480f2d6ace4SSean Bruno 	return(processed);
481f2d6ace4SSean Bruno }
482f2d6ace4SSean Bruno 
483f2d6ace4SSean Bruno static void
48495246abbSSean Bruno lem_isc_rxd_refill(void *arg, if_rxd_update_t iru)
485f2d6ace4SSean Bruno {
486dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
487f2d6ace4SSean Bruno 	if_softc_ctx_t scctx = sc->shared;
48895246abbSSean Bruno 	struct em_rx_queue *que = &sc->rx_queues[iru->iru_qsidx];
489f2d6ace4SSean Bruno 	struct rx_ring *rxr = &que->rxr;
490f2d6ace4SSean Bruno 	struct e1000_rx_desc *rxd;
49195246abbSSean Bruno 	uint64_t *paddrs;
49295246abbSSean Bruno 	uint32_t next_pidx, pidx;
49395246abbSSean Bruno 	uint16_t count;
494f2d6ace4SSean Bruno 	int i;
49595246abbSSean Bruno 
49695246abbSSean Bruno 	paddrs = iru->iru_paddrs;
49795246abbSSean Bruno 	pidx = iru->iru_pidx;
49895246abbSSean Bruno 	count = iru->iru_count;
499f2d6ace4SSean Bruno 
500f2d6ace4SSean Bruno 	for (i = 0, next_pidx = pidx; i < count; i++) {
501f2d6ace4SSean Bruno 		rxd = (struct e1000_rx_desc *)&rxr->rx_base[next_pidx];
502f2d6ace4SSean Bruno 		rxd->buffer_addr = htole64(paddrs[i]);
503f2d6ace4SSean Bruno 		/* status bits must be cleared */
504f2d6ace4SSean Bruno 		rxd->status = 0;
505f2d6ace4SSean Bruno 
506f2d6ace4SSean Bruno 		if (++next_pidx == scctx->isc_nrxd[0])
507f2d6ace4SSean Bruno 			next_pidx = 0;
508f2d6ace4SSean Bruno 	}
509f2d6ace4SSean Bruno }
510f2d6ace4SSean Bruno 
511f2d6ace4SSean Bruno static void
51295246abbSSean Bruno em_isc_rxd_refill(void *arg, if_rxd_update_t iru)
513f2d6ace4SSean Bruno {
514dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
515f2d6ace4SSean Bruno 	if_softc_ctx_t scctx = sc->shared;
51695246abbSSean Bruno 	uint16_t rxqid = iru->iru_qsidx;
517f2d6ace4SSean Bruno 	struct em_rx_queue *que = &sc->rx_queues[rxqid];
518f2d6ace4SSean Bruno 	struct rx_ring *rxr = &que->rxr;
519f2d6ace4SSean Bruno 	union e1000_rx_desc_extended *rxd;
52095246abbSSean Bruno 	uint64_t *paddrs;
52195246abbSSean Bruno 	uint32_t next_pidx, pidx;
52295246abbSSean Bruno 	uint16_t count;
523f2d6ace4SSean Bruno 	int i;
52495246abbSSean Bruno 
52595246abbSSean Bruno 	paddrs = iru->iru_paddrs;
52695246abbSSean Bruno 	pidx = iru->iru_pidx;
52795246abbSSean Bruno 	count = iru->iru_count;
528f2d6ace4SSean Bruno 
529f2d6ace4SSean Bruno 	for (i = 0, next_pidx = pidx; i < count; i++) {
530f2d6ace4SSean Bruno 		rxd = &rxr->rx_base[next_pidx];
531f2d6ace4SSean Bruno 		rxd->read.buffer_addr = htole64(paddrs[i]);
532ab2e3f79SStephen Hurd 		/* DD bits must be cleared */
533ab2e3f79SStephen Hurd 		rxd->wb.upper.status_error = 0;
534f2d6ace4SSean Bruno 
535f2d6ace4SSean Bruno 		if (++next_pidx == scctx->isc_nrxd[0])
536f2d6ace4SSean Bruno 			next_pidx = 0;
537f2d6ace4SSean Bruno 	}
538f2d6ace4SSean Bruno }
539f2d6ace4SSean Bruno 
540f2d6ace4SSean Bruno static void
54195246abbSSean Bruno em_isc_rxd_flush(void *arg, uint16_t rxqid, uint8_t flid __unused, qidx_t pidx)
542f2d6ace4SSean Bruno {
543dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
544f2d6ace4SSean Bruno 	struct em_rx_queue *que = &sc->rx_queues[rxqid];
545f2d6ace4SSean Bruno 	struct rx_ring *rxr = &que->rxr;
546f2d6ace4SSean Bruno 
547f2d6ace4SSean Bruno 	E1000_WRITE_REG(&sc->hw, E1000_RDT(rxr->me), pidx);
548f2d6ace4SSean Bruno }
549f2d6ace4SSean Bruno 
550f2d6ace4SSean Bruno static int
55195246abbSSean Bruno lem_isc_rxd_available(void *arg, uint16_t rxqid, qidx_t idx, qidx_t budget)
552f2d6ace4SSean Bruno {
553dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
554f2d6ace4SSean Bruno 	if_softc_ctx_t scctx = sc->shared;
555f2d6ace4SSean Bruno 	struct em_rx_queue *que = &sc->rx_queues[rxqid];
556f2d6ace4SSean Bruno 	struct rx_ring *rxr = &que->rxr;
557f2d6ace4SSean Bruno 	struct e1000_rx_desc *rxd;
558f2d6ace4SSean Bruno 	u32 staterr = 0;
559f2d6ace4SSean Bruno 	int cnt, i;
560f2d6ace4SSean Bruno 
561ab2e3f79SStephen Hurd 	for (cnt = 0, i = idx; cnt < scctx->isc_nrxd[0] && cnt <= budget;) {
562f2d6ace4SSean Bruno 		rxd = (struct e1000_rx_desc *)&rxr->rx_base[i];
563f2d6ace4SSean Bruno 		staterr = rxd->status;
564f2d6ace4SSean Bruno 
565f2d6ace4SSean Bruno 		if ((staterr & E1000_RXD_STAT_DD) == 0)
566f2d6ace4SSean Bruno 			break;
567f2d6ace4SSean Bruno 		if (++i == scctx->isc_nrxd[0])
568f2d6ace4SSean Bruno 			i = 0;
569f2d6ace4SSean Bruno 		if (staterr & E1000_RXD_STAT_EOP)
570f2d6ace4SSean Bruno 			cnt++;
571f2d6ace4SSean Bruno 	}
572f2d6ace4SSean Bruno 	return (cnt);
573f2d6ace4SSean Bruno }
574f2d6ace4SSean Bruno 
575f2d6ace4SSean Bruno static int
57695246abbSSean Bruno em_isc_rxd_available(void *arg, uint16_t rxqid, qidx_t idx, qidx_t budget)
577f2d6ace4SSean Bruno {
578dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
579f2d6ace4SSean Bruno 	if_softc_ctx_t scctx = sc->shared;
580f2d6ace4SSean Bruno 	struct em_rx_queue *que = &sc->rx_queues[rxqid];
581f2d6ace4SSean Bruno 	struct rx_ring *rxr = &que->rxr;
582f2d6ace4SSean Bruno 	union e1000_rx_desc_extended *rxd;
583f2d6ace4SSean Bruno 	u32 staterr = 0;
584f2d6ace4SSean Bruno 	int cnt, i;
585f2d6ace4SSean Bruno 
586ab2e3f79SStephen Hurd 	for (cnt = 0, i = idx; cnt < scctx->isc_nrxd[0] && cnt <= budget;) {
587f2d6ace4SSean Bruno 		rxd = &rxr->rx_base[i];
588f2d6ace4SSean Bruno 		staterr = le32toh(rxd->wb.upper.status_error);
589f2d6ace4SSean Bruno 
590f2d6ace4SSean Bruno 		if ((staterr & E1000_RXD_STAT_DD) == 0)
591f2d6ace4SSean Bruno 			break;
592adf93b56SEric Joyner 		if (++i == scctx->isc_nrxd[0])
593f2d6ace4SSean Bruno 			i = 0;
594f2d6ace4SSean Bruno 		if (staterr & E1000_RXD_STAT_EOP)
595f2d6ace4SSean Bruno 			cnt++;
596f2d6ace4SSean Bruno 	}
597f2d6ace4SSean Bruno 	return (cnt);
598f2d6ace4SSean Bruno }
599f2d6ace4SSean Bruno 
600f2d6ace4SSean Bruno static int
601f2d6ace4SSean Bruno lem_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri)
602f2d6ace4SSean Bruno {
603dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
604dc926051SKevin Bowling 	if_softc_ctx_t scctx = sc->shared;
605dc926051SKevin Bowling 	struct em_rx_queue *que = &sc->rx_queues[ri->iri_qsidx];
606f2d6ace4SSean Bruno 	struct rx_ring *rxr = &que->rxr;
607f2d6ace4SSean Bruno 	struct e1000_rx_desc *rxd;
608f2d6ace4SSean Bruno 	u16 len;
609f2d6ace4SSean Bruno 	u32 status, errors;
610f2d6ace4SSean Bruno 	bool eop;
611f2d6ace4SSean Bruno 	int i, cidx;
612f2d6ace4SSean Bruno 
613f2d6ace4SSean Bruno 	status = errors = i = 0;
614f2d6ace4SSean Bruno 	cidx = ri->iri_cidx;
615f2d6ace4SSean Bruno 
616f2d6ace4SSean Bruno 	do {
617f2d6ace4SSean Bruno 		rxd = (struct e1000_rx_desc *)&rxr->rx_base[cidx];
618f2d6ace4SSean Bruno 		status = rxd->status;
619f2d6ace4SSean Bruno 		errors = rxd->errors;
620f2d6ace4SSean Bruno 
621f2d6ace4SSean Bruno 		/* Error Checking then decrement count */
622f2d6ace4SSean Bruno 		MPASS ((status & E1000_RXD_STAT_DD) != 0);
623f2d6ace4SSean Bruno 
624f2d6ace4SSean Bruno 		len = le16toh(rxd->length);
625f2d6ace4SSean Bruno 		ri->iri_len += len;
626f2d6ace4SSean Bruno 
627f2d6ace4SSean Bruno 		eop = (status & E1000_RXD_STAT_EOP) != 0;
628f2d6ace4SSean Bruno 
629f2d6ace4SSean Bruno 		/* Make sure bad packets are discarded */
630f2d6ace4SSean Bruno 		if (errors & E1000_RXD_ERR_FRAME_ERR_MASK) {
631dc926051SKevin Bowling 			sc->dropped_pkts++;
632f2d6ace4SSean Bruno 			/* XXX fixup if common */
633f2d6ace4SSean Bruno 			return (EBADMSG);
634f2d6ace4SSean Bruno 		}
635f2d6ace4SSean Bruno 
636f2d6ace4SSean Bruno 		ri->iri_frags[i].irf_flid = 0;
637f2d6ace4SSean Bruno 		ri->iri_frags[i].irf_idx = cidx;
638f2d6ace4SSean Bruno 		ri->iri_frags[i].irf_len = len;
639f2d6ace4SSean Bruno 		/* Zero out the receive descriptors status. */
640f2d6ace4SSean Bruno 		rxd->status = 0;
641f2d6ace4SSean Bruno 
642f2d6ace4SSean Bruno 		if (++cidx == scctx->isc_nrxd[0])
643f2d6ace4SSean Bruno 			cidx = 0;
644f2d6ace4SSean Bruno 		i++;
645f2d6ace4SSean Bruno 	} while (!eop);
646f2d6ace4SSean Bruno 
647f2d6ace4SSean Bruno 	/* XXX add a faster way to look this up */
648dc926051SKevin Bowling 	if (sc->hw.mac.type >= e1000_82543)
649015075f3SKevin Bowling 		em_receive_checksum(status, errors, ri);
650f2d6ace4SSean Bruno 
651f2d6ace4SSean Bruno 	if (status & E1000_RXD_STAT_VP) {
652f2d6ace4SSean Bruno 		ri->iri_vtag = le16toh(rxd->special);
653f2d6ace4SSean Bruno 		ri->iri_flags |= M_VLANTAG;
654f2d6ace4SSean Bruno 	}
655f2d6ace4SSean Bruno 
656f2d6ace4SSean Bruno 	ri->iri_nfrags = i;
657f2d6ace4SSean Bruno 
658f2d6ace4SSean Bruno 	return (0);
659f2d6ace4SSean Bruno }
660f2d6ace4SSean Bruno 
661f2d6ace4SSean Bruno static int
662f2d6ace4SSean Bruno em_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri)
663f2d6ace4SSean Bruno {
664dc926051SKevin Bowling 	struct e1000_softc *sc = arg;
665dc926051SKevin Bowling 	if_softc_ctx_t scctx = sc->shared;
666dc926051SKevin Bowling 	struct em_rx_queue *que = &sc->rx_queues[ri->iri_qsidx];
667f2d6ace4SSean Bruno 	struct rx_ring *rxr = &que->rxr;
668f2d6ace4SSean Bruno 	union e1000_rx_desc_extended *rxd;
669f2d6ace4SSean Bruno 
670f2d6ace4SSean Bruno 	u16 len;
671cbf1505dSSean Bruno 	u32 pkt_info;
672f2d6ace4SSean Bruno 	u32 staterr = 0;
673f2d6ace4SSean Bruno 	bool eop;
674f7926a6dSVincenzo Maffione 	int i, cidx;
675f2d6ace4SSean Bruno 
676f7926a6dSVincenzo Maffione 	i = 0;
677f2d6ace4SSean Bruno 	cidx = ri->iri_cidx;
678f2d6ace4SSean Bruno 
679f2d6ace4SSean Bruno 	do {
680f2d6ace4SSean Bruno 		rxd = &rxr->rx_base[cidx];
681f2d6ace4SSean Bruno 		staterr = le32toh(rxd->wb.upper.status_error);
682cbf1505dSSean Bruno 		pkt_info = le32toh(rxd->wb.lower.mrq);
683f2d6ace4SSean Bruno 
684f2d6ace4SSean Bruno 		/* Error Checking then decrement count */
685ab2e3f79SStephen Hurd 		MPASS ((staterr & E1000_RXD_STAT_DD) != 0);
686f2d6ace4SSean Bruno 
687f2d6ace4SSean Bruno 		len = le16toh(rxd->wb.upper.length);
688f2d6ace4SSean Bruno 		ri->iri_len += len;
689f2d6ace4SSean Bruno 
690f2d6ace4SSean Bruno 		eop = (staterr & E1000_RXD_STAT_EOP) != 0;
691f2d6ace4SSean Bruno 
692f2d6ace4SSean Bruno 		/* Make sure bad packets are discarded */
693f2d6ace4SSean Bruno 		if (staterr & E1000_RXDEXT_ERR_FRAME_ERR_MASK) {
694dc926051SKevin Bowling 			sc->dropped_pkts++;
695f2d6ace4SSean Bruno 			return EBADMSG;
696f2d6ace4SSean Bruno 		}
697f2d6ace4SSean Bruno 
698f2d6ace4SSean Bruno 		ri->iri_frags[i].irf_flid = 0;
699f2d6ace4SSean Bruno 		ri->iri_frags[i].irf_idx = cidx;
700f2d6ace4SSean Bruno 		ri->iri_frags[i].irf_len = len;
701f2d6ace4SSean Bruno 		/* Zero out the receive descriptors status. */
702f2d6ace4SSean Bruno 		rxd->wb.upper.status_error &= htole32(~0xFF);
703f2d6ace4SSean Bruno 
704f2d6ace4SSean Bruno 		if (++cidx == scctx->isc_nrxd[0])
705f2d6ace4SSean Bruno 			cidx = 0;
706f2d6ace4SSean Bruno 		i++;
707f2d6ace4SSean Bruno 	} while (!eop);
708f2d6ace4SSean Bruno 
70952f45d8aSVincenzo Maffione 	if (scctx->isc_capenable & IFCAP_RXCSUM)
710015075f3SKevin Bowling 		em_receive_checksum(staterr, staterr >> 24, ri);
711f2d6ace4SSean Bruno 
712f2d6ace4SSean Bruno 	if (staterr & E1000_RXD_STAT_VP) {
713f7926a6dSVincenzo Maffione 		ri->iri_vtag = le16toh(rxd->wb.upper.vlan);
714f2d6ace4SSean Bruno 		ri->iri_flags |= M_VLANTAG;
715f7926a6dSVincenzo Maffione 	}
716f2d6ace4SSean Bruno 
71795246abbSSean Bruno 	ri->iri_flowid = le32toh(rxd->wb.lower.hi_dword.rss);
718cbf1505dSSean Bruno 	ri->iri_rsstype = em_determine_rsstype(pkt_info);
719cbf1505dSSean Bruno 
720cbf1505dSSean Bruno 	ri->iri_nfrags = i;
721f2d6ace4SSean Bruno 	return (0);
722f2d6ace4SSean Bruno }
723f2d6ace4SSean Bruno 
724f2d6ace4SSean Bruno /*********************************************************************
725f2d6ace4SSean Bruno  *
726f2d6ace4SSean Bruno  *  Verify that the hardware indicated that the checksum is valid.
727f2d6ace4SSean Bruno  *  Inform the stack about the status of checksum so that stack
728f2d6ace4SSean Bruno  *  doesn't spend time verifying the checksum.
729f2d6ace4SSean Bruno  *
730f2d6ace4SSean Bruno  *********************************************************************/
731f2d6ace4SSean Bruno static void
732015075f3SKevin Bowling em_receive_checksum(uint16_t status, uint8_t errors, if_rxd_info_t ri)
733f2d6ace4SSean Bruno {
734015075f3SKevin Bowling 	if (__predict_false(status & E1000_RXD_STAT_IXSM))
735015075f3SKevin Bowling 		return;
736015075f3SKevin Bowling 
737015075f3SKevin Bowling 	/* If there is a layer 3 or 4 error we are done */
738015075f3SKevin Bowling 	if (__predict_false(errors & (E1000_RXD_ERR_IPE | E1000_RXD_ERR_TCPE)))
739015075f3SKevin Bowling 		return;
740015075f3SKevin Bowling 
741015075f3SKevin Bowling 	/* IP Checksum Good */
742015075f3SKevin Bowling 	if (status & E1000_RXD_STAT_IPCS)
743f2d6ace4SSean Bruno 		ri->iri_csum_flags = (CSUM_IP_CHECKED | CSUM_IP_VALID);
744f2d6ace4SSean Bruno 
745015075f3SKevin Bowling 	/* Valid L4E checksum */
746015075f3SKevin Bowling 	if (__predict_true(status &
747015075f3SKevin Bowling 	    (E1000_RXD_STAT_TCPCS | E1000_RXD_STAT_UDPCS))) {
748015075f3SKevin Bowling 		ri->iri_csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
749f2d6ace4SSean Bruno 		ri->iri_csum_data = htons(0xffff);
750f2d6ace4SSean Bruno 	}
751f2d6ace4SSean Bruno }
752f2d6ace4SSean Bruno 
753cbf1505dSSean Bruno /********************************************************************
754cbf1505dSSean Bruno  *
755cbf1505dSSean Bruno  *  Parse the packet type to determine the appropriate hash
756cbf1505dSSean Bruno  *
757cbf1505dSSean Bruno  ******************************************************************/
758cbf1505dSSean Bruno static int
759cbf1505dSSean Bruno em_determine_rsstype(u32 pkt_info)
760cbf1505dSSean Bruno {
761cbf1505dSSean Bruno 	switch (pkt_info & E1000_RXDADV_RSSTYPE_MASK) {
762cbf1505dSSean Bruno 	case E1000_RXDADV_RSSTYPE_IPV4_TCP:
763cbf1505dSSean Bruno 		return M_HASHTYPE_RSS_TCP_IPV4;
764cbf1505dSSean Bruno 	case E1000_RXDADV_RSSTYPE_IPV4:
765cbf1505dSSean Bruno 		return M_HASHTYPE_RSS_IPV4;
766cbf1505dSSean Bruno 	case E1000_RXDADV_RSSTYPE_IPV6_TCP:
767cbf1505dSSean Bruno 		return M_HASHTYPE_RSS_TCP_IPV6;
768cbf1505dSSean Bruno 	case E1000_RXDADV_RSSTYPE_IPV6_EX:
769cbf1505dSSean Bruno 		return M_HASHTYPE_RSS_IPV6_EX;
770cbf1505dSSean Bruno 	case E1000_RXDADV_RSSTYPE_IPV6:
771cbf1505dSSean Bruno 		return M_HASHTYPE_RSS_IPV6;
772cbf1505dSSean Bruno 	case E1000_RXDADV_RSSTYPE_IPV6_TCP_EX:
773cbf1505dSSean Bruno 		return M_HASHTYPE_RSS_TCP_IPV6_EX;
774cbf1505dSSean Bruno 	default:
775cbf1505dSSean Bruno 		return M_HASHTYPE_OPAQUE;
776cbf1505dSSean Bruno 	}
777cbf1505dSSean Bruno }
778