xref: /freebsd/sys/dev/sfxge/sfxge_rx.c (revision 453130d9bfc1c6d68b366dfcb041689d69f81295)
1e948693eSPhilip Paeps /*-
23c838a9fSAndrew Rybchenko  * Copyright (c) 2010-2015 Solarflare Communications Inc.
3e948693eSPhilip Paeps  * All rights reserved.
4e948693eSPhilip Paeps  *
5e948693eSPhilip Paeps  * This software was developed in part by Philip Paeps under contract for
6e948693eSPhilip Paeps  * Solarflare Communications, Inc.
7e948693eSPhilip Paeps  *
8e948693eSPhilip Paeps  * Redistribution and use in source and binary forms, with or without
93c838a9fSAndrew Rybchenko  * modification, are permitted provided that the following conditions are met:
10e948693eSPhilip Paeps  *
113c838a9fSAndrew Rybchenko  * 1. Redistributions of source code must retain the above copyright notice,
123c838a9fSAndrew Rybchenko  *    this list of conditions and the following disclaimer.
133c838a9fSAndrew Rybchenko  * 2. Redistributions in binary form must reproduce the above copyright notice,
143c838a9fSAndrew Rybchenko  *    this list of conditions and the following disclaimer in the documentation
153c838a9fSAndrew Rybchenko  *    and/or other materials provided with the distribution.
163c838a9fSAndrew Rybchenko  *
173c838a9fSAndrew Rybchenko  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
183c838a9fSAndrew Rybchenko  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
193c838a9fSAndrew Rybchenko  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
203c838a9fSAndrew Rybchenko  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
213c838a9fSAndrew Rybchenko  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
223c838a9fSAndrew Rybchenko  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
233c838a9fSAndrew Rybchenko  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
243c838a9fSAndrew Rybchenko  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
253c838a9fSAndrew Rybchenko  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
263c838a9fSAndrew Rybchenko  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
273c838a9fSAndrew Rybchenko  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
283c838a9fSAndrew Rybchenko  *
293c838a9fSAndrew Rybchenko  * The views and conclusions contained in the software and documentation are
303c838a9fSAndrew Rybchenko  * those of the authors and should not be interpreted as representing official
313c838a9fSAndrew Rybchenko  * policies, either expressed or implied, of the FreeBSD Project.
32e948693eSPhilip Paeps  */
33e948693eSPhilip Paeps 
34e948693eSPhilip Paeps #include <sys/cdefs.h>
35e948693eSPhilip Paeps __FBSDID("$FreeBSD$");
36e948693eSPhilip Paeps 
378ec07310SGleb Smirnoff #include <sys/param.h>
388ec07310SGleb Smirnoff #include <sys/malloc.h>
39e948693eSPhilip Paeps #include <sys/mbuf.h>
40e948693eSPhilip Paeps #include <sys/smp.h>
41e948693eSPhilip Paeps #include <sys/socket.h>
42e948693eSPhilip Paeps #include <sys/sysctl.h>
433c838a9fSAndrew Rybchenko #include <sys/syslog.h>
44e948693eSPhilip Paeps #include <sys/limits.h>
45245d1576SAndrew Rybchenko #include <sys/syslog.h>
46e948693eSPhilip Paeps 
47e948693eSPhilip Paeps #include <net/ethernet.h>
48e948693eSPhilip Paeps #include <net/if.h>
49e948693eSPhilip Paeps #include <net/if_vlan_var.h>
50e948693eSPhilip Paeps 
51e948693eSPhilip Paeps #include <netinet/in.h>
52e948693eSPhilip Paeps #include <netinet/ip.h>
53e948693eSPhilip Paeps #include <netinet/ip6.h>
54e948693eSPhilip Paeps #include <netinet/tcp.h>
55e948693eSPhilip Paeps 
56e948693eSPhilip Paeps #include <machine/in_cksum.h>
57e948693eSPhilip Paeps 
58e948693eSPhilip Paeps #include "common/efx.h"
59e948693eSPhilip Paeps 
60e948693eSPhilip Paeps 
61e948693eSPhilip Paeps #include "sfxge.h"
62e948693eSPhilip Paeps #include "sfxge_rx.h"
63e948693eSPhilip Paeps 
64385b1d8eSGeorge V. Neville-Neil #define	RX_REFILL_THRESHOLD(_entries)	(EFX_RXQ_LIMIT(_entries) * 9 / 10)
65e948693eSPhilip Paeps 
6618daa0eeSAndrew Rybchenko #ifdef SFXGE_LRO
6718daa0eeSAndrew Rybchenko 
68245d1576SAndrew Rybchenko SYSCTL_NODE(_hw_sfxge, OID_AUTO, lro, CTLFLAG_RD, NULL,
69245d1576SAndrew Rybchenko 	    "Large receive offload (LRO) parameters");
70245d1576SAndrew Rybchenko 
71245d1576SAndrew Rybchenko #define	SFXGE_LRO_PARAM(_param)	SFXGE_PARAM(lro._param)
72245d1576SAndrew Rybchenko 
73e948693eSPhilip Paeps /* Size of the LRO hash table.  Must be a power of 2.  A larger table
74e948693eSPhilip Paeps  * means we can accelerate a larger number of streams.
75e948693eSPhilip Paeps  */
76e948693eSPhilip Paeps static unsigned lro_table_size = 128;
77245d1576SAndrew Rybchenko TUNABLE_INT(SFXGE_LRO_PARAM(table_size), &lro_table_size);
78245d1576SAndrew Rybchenko SYSCTL_UINT(_hw_sfxge_lro, OID_AUTO, table_size, CTLFLAG_RDTUN,
79245d1576SAndrew Rybchenko 	    &lro_table_size, 0,
80245d1576SAndrew Rybchenko 	    "Size of the LRO hash table (must be a power of 2)");
81e948693eSPhilip Paeps 
82e948693eSPhilip Paeps /* Maximum length of a hash chain.  If chains get too long then the lookup
83e948693eSPhilip Paeps  * time increases and may exceed the benefit of LRO.
84e948693eSPhilip Paeps  */
85e948693eSPhilip Paeps static unsigned lro_chain_max = 20;
86245d1576SAndrew Rybchenko TUNABLE_INT(SFXGE_LRO_PARAM(chain_max), &lro_chain_max);
87245d1576SAndrew Rybchenko SYSCTL_UINT(_hw_sfxge_lro, OID_AUTO, chain_max, CTLFLAG_RDTUN,
88245d1576SAndrew Rybchenko 	    &lro_chain_max, 0,
89245d1576SAndrew Rybchenko 	    "The maximum length of a hash chain");
90e948693eSPhilip Paeps 
91e948693eSPhilip Paeps /* Maximum time (in ticks) that a connection can be idle before it's LRO
92e948693eSPhilip Paeps  * state is discarded.
93e948693eSPhilip Paeps  */
94e948693eSPhilip Paeps static unsigned lro_idle_ticks; /* initialised in sfxge_rx_init() */
95245d1576SAndrew Rybchenko TUNABLE_INT(SFXGE_LRO_PARAM(idle_ticks), &lro_idle_ticks);
96245d1576SAndrew Rybchenko SYSCTL_UINT(_hw_sfxge_lro, OID_AUTO, idle_ticks, CTLFLAG_RDTUN,
97245d1576SAndrew Rybchenko 	    &lro_idle_ticks, 0,
98245d1576SAndrew Rybchenko 	    "The maximum time (in ticks) that a connection can be idle "
99245d1576SAndrew Rybchenko 	    "before it's LRO state is discarded");
100e948693eSPhilip Paeps 
101e948693eSPhilip Paeps /* Number of packets with payload that must arrive in-order before a
102e948693eSPhilip Paeps  * connection is eligible for LRO.  The idea is we should avoid coalescing
103e948693eSPhilip Paeps  * segments when the sender is in slow-start because reducing the ACK rate
104e948693eSPhilip Paeps  * can damage performance.
105e948693eSPhilip Paeps  */
106e948693eSPhilip Paeps static int lro_slow_start_packets = 2000;
107245d1576SAndrew Rybchenko TUNABLE_INT(SFXGE_LRO_PARAM(slow_start_packets), &lro_slow_start_packets);
108245d1576SAndrew Rybchenko SYSCTL_UINT(_hw_sfxge_lro, OID_AUTO, slow_start_packets, CTLFLAG_RDTUN,
109245d1576SAndrew Rybchenko 	    &lro_slow_start_packets, 0,
110245d1576SAndrew Rybchenko 	    "Number of packets with payload that must arrive in-order before "
111245d1576SAndrew Rybchenko 	    "a connection is eligible for LRO");
112e948693eSPhilip Paeps 
113e948693eSPhilip Paeps /* Number of packets with payload that must arrive in-order following loss
114e948693eSPhilip Paeps  * before a connection is eligible for LRO.  The idea is we should avoid
115e948693eSPhilip Paeps  * coalescing segments when the sender is recovering from loss, because
116e948693eSPhilip Paeps  * reducing the ACK rate can damage performance.
117e948693eSPhilip Paeps  */
118e948693eSPhilip Paeps static int lro_loss_packets = 20;
119245d1576SAndrew Rybchenko TUNABLE_INT(SFXGE_LRO_PARAM(loss_packets), &lro_loss_packets);
120245d1576SAndrew Rybchenko SYSCTL_UINT(_hw_sfxge_lro, OID_AUTO, loss_packets, CTLFLAG_RDTUN,
121245d1576SAndrew Rybchenko 	    &lro_loss_packets, 0,
122245d1576SAndrew Rybchenko 	    "Number of packets with payload that must arrive in-order "
123245d1576SAndrew Rybchenko 	    "following loss before a connection is eligible for LRO");
124e948693eSPhilip Paeps 
125e948693eSPhilip Paeps /* Flags for sfxge_lro_conn::l2_id; must not collide with EVL_VLID_MASK */
126e948693eSPhilip Paeps #define	SFXGE_LRO_L2_ID_VLAN 0x4000
127e948693eSPhilip Paeps #define	SFXGE_LRO_L2_ID_IPV6 0x8000
128e948693eSPhilip Paeps #define	SFXGE_LRO_CONN_IS_VLAN_ENCAP(c) ((c)->l2_id & SFXGE_LRO_L2_ID_VLAN)
129e948693eSPhilip Paeps #define	SFXGE_LRO_CONN_IS_TCPIPV4(c) (!((c)->l2_id & SFXGE_LRO_L2_ID_IPV6))
130e948693eSPhilip Paeps 
131e948693eSPhilip Paeps /* Compare IPv6 addresses, avoiding conditional branches */
1320b28bbdcSAndrew Rybchenko static unsigned long ipv6_addr_cmp(const struct in6_addr *left,
133e948693eSPhilip Paeps 				   const struct in6_addr *right)
134e948693eSPhilip Paeps {
135e948693eSPhilip Paeps #if LONG_BIT == 64
136e948693eSPhilip Paeps 	const uint64_t *left64 = (const uint64_t *)left;
137e948693eSPhilip Paeps 	const uint64_t *right64 = (const uint64_t *)right;
138e948693eSPhilip Paeps 	return (left64[0] - right64[0]) | (left64[1] - right64[1]);
139e948693eSPhilip Paeps #else
140e948693eSPhilip Paeps 	return (left->s6_addr32[0] - right->s6_addr32[0]) |
141e948693eSPhilip Paeps 	       (left->s6_addr32[1] - right->s6_addr32[1]) |
142e948693eSPhilip Paeps 	       (left->s6_addr32[2] - right->s6_addr32[2]) |
143e948693eSPhilip Paeps 	       (left->s6_addr32[3] - right->s6_addr32[3]);
144e948693eSPhilip Paeps #endif
145e948693eSPhilip Paeps }
146e948693eSPhilip Paeps 
14718daa0eeSAndrew Rybchenko #endif	/* SFXGE_LRO */
14818daa0eeSAndrew Rybchenko 
149e948693eSPhilip Paeps void
150e948693eSPhilip Paeps sfxge_rx_qflush_done(struct sfxge_rxq *rxq)
151e948693eSPhilip Paeps {
152e948693eSPhilip Paeps 
153e948693eSPhilip Paeps 	rxq->flush_state = SFXGE_FLUSH_DONE;
154e948693eSPhilip Paeps }
155e948693eSPhilip Paeps 
156e948693eSPhilip Paeps void
157e948693eSPhilip Paeps sfxge_rx_qflush_failed(struct sfxge_rxq *rxq)
158e948693eSPhilip Paeps {
159e948693eSPhilip Paeps 
160e948693eSPhilip Paeps 	rxq->flush_state = SFXGE_FLUSH_FAILED;
161e948693eSPhilip Paeps }
162e948693eSPhilip Paeps 
163e948693eSPhilip Paeps static uint8_t toep_key[] = {
164e948693eSPhilip Paeps 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
165e948693eSPhilip Paeps 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
166e948693eSPhilip Paeps 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
167e948693eSPhilip Paeps 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
168e948693eSPhilip Paeps 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
169e948693eSPhilip Paeps };
170e948693eSPhilip Paeps 
171e948693eSPhilip Paeps static void
172e948693eSPhilip Paeps sfxge_rx_post_refill(void *arg)
173e948693eSPhilip Paeps {
174e948693eSPhilip Paeps 	struct sfxge_rxq *rxq = arg;
175e948693eSPhilip Paeps 	struct sfxge_softc *sc;
176e948693eSPhilip Paeps 	unsigned int index;
177e948693eSPhilip Paeps 	struct sfxge_evq *evq;
178e948693eSPhilip Paeps 	uint16_t magic;
179e948693eSPhilip Paeps 
180e948693eSPhilip Paeps 	sc = rxq->sc;
181e948693eSPhilip Paeps 	index = rxq->index;
182e948693eSPhilip Paeps 	evq = sc->evq[index];
183e948693eSPhilip Paeps 
184e948693eSPhilip Paeps 	magic = SFXGE_MAGIC_RX_QREFILL | index;
185e948693eSPhilip Paeps 
186e948693eSPhilip Paeps 	/* This is guaranteed due to the start/stop order of rx and ev */
187e948693eSPhilip Paeps 	KASSERT(evq->init_state == SFXGE_EVQ_STARTED,
188e948693eSPhilip Paeps 	    ("evq not started"));
189e948693eSPhilip Paeps 	KASSERT(rxq->init_state == SFXGE_RXQ_STARTED,
190e948693eSPhilip Paeps 	    ("rxq not started"));
191e948693eSPhilip Paeps 	efx_ev_qpost(evq->common, magic);
192e948693eSPhilip Paeps }
193e948693eSPhilip Paeps 
194e948693eSPhilip Paeps static void
195e948693eSPhilip Paeps sfxge_rx_schedule_refill(struct sfxge_rxq *rxq, boolean_t retrying)
196e948693eSPhilip Paeps {
197e948693eSPhilip Paeps 	/* Initially retry after 100 ms, but back off in case of
198e948693eSPhilip Paeps 	 * repeated failures as we probably have to wait for the
199e948693eSPhilip Paeps 	 * administrator to raise the pool limit. */
200e948693eSPhilip Paeps 	if (retrying)
201e948693eSPhilip Paeps 		rxq->refill_delay = min(rxq->refill_delay * 2, 10 * hz);
202e948693eSPhilip Paeps 	else
203e948693eSPhilip Paeps 		rxq->refill_delay = hz / 10;
204e948693eSPhilip Paeps 
205e948693eSPhilip Paeps 	callout_reset_curcpu(&rxq->refill_callout, rxq->refill_delay,
206e948693eSPhilip Paeps 			     sfxge_rx_post_refill, rxq);
207e948693eSPhilip Paeps }
208e948693eSPhilip Paeps 
209e948693eSPhilip Paeps #define	SFXGE_REFILL_BATCH  64
210e948693eSPhilip Paeps 
211e948693eSPhilip Paeps static void
212e948693eSPhilip Paeps sfxge_rx_qfill(struct sfxge_rxq *rxq, unsigned int target, boolean_t retrying)
213e948693eSPhilip Paeps {
214e948693eSPhilip Paeps 	struct sfxge_softc *sc;
215e948693eSPhilip Paeps 	unsigned int index;
216e948693eSPhilip Paeps 	struct sfxge_evq *evq;
217e948693eSPhilip Paeps 	unsigned int batch;
218e948693eSPhilip Paeps 	unsigned int rxfill;
219e948693eSPhilip Paeps 	unsigned int mblksize;
220e948693eSPhilip Paeps 	int ntodo;
221e948693eSPhilip Paeps 	efsys_dma_addr_t addr[SFXGE_REFILL_BATCH];
222e948693eSPhilip Paeps 
223e948693eSPhilip Paeps 	sc = rxq->sc;
224e948693eSPhilip Paeps 	index = rxq->index;
225e948693eSPhilip Paeps 	evq = sc->evq[index];
226e948693eSPhilip Paeps 
227e948693eSPhilip Paeps 	prefetch_read_many(sc->enp);
228e948693eSPhilip Paeps 	prefetch_read_many(rxq->common);
229e948693eSPhilip Paeps 
230763cab71SAndrew Rybchenko 	SFXGE_EVQ_LOCK_ASSERT_OWNED(evq);
231e948693eSPhilip Paeps 
232851128b8SAndrew Rybchenko 	if (__predict_false(rxq->init_state != SFXGE_RXQ_STARTED))
233e948693eSPhilip Paeps 		return;
234e948693eSPhilip Paeps 
235e948693eSPhilip Paeps 	rxfill = rxq->added - rxq->completed;
236385b1d8eSGeorge V. Neville-Neil 	KASSERT(rxfill <= EFX_RXQ_LIMIT(rxq->entries),
237385b1d8eSGeorge V. Neville-Neil 	    ("rxfill > EFX_RXQ_LIMIT(rxq->entries)"));
238385b1d8eSGeorge V. Neville-Neil 	ntodo = min(EFX_RXQ_LIMIT(rxq->entries) - rxfill, target);
239385b1d8eSGeorge V. Neville-Neil 	KASSERT(ntodo <= EFX_RXQ_LIMIT(rxq->entries),
240385b1d8eSGeorge V. Neville-Neil 	    ("ntodo > EFX_RQX_LIMIT(rxq->entries)"));
241e948693eSPhilip Paeps 
242e948693eSPhilip Paeps 	if (ntodo == 0)
243e948693eSPhilip Paeps 		return;
244e948693eSPhilip Paeps 
245e948693eSPhilip Paeps 	batch = 0;
2463c838a9fSAndrew Rybchenko 	mblksize = sc->rx_buffer_size - sc->rx_buffer_align;
247e948693eSPhilip Paeps 	while (ntodo-- > 0) {
248e948693eSPhilip Paeps 		unsigned int id;
249e948693eSPhilip Paeps 		struct sfxge_rx_sw_desc *rx_desc;
250e948693eSPhilip Paeps 		bus_dma_segment_t seg;
251e948693eSPhilip Paeps 		struct mbuf *m;
252e948693eSPhilip Paeps 
253385b1d8eSGeorge V. Neville-Neil 		id = (rxq->added + batch) & rxq->ptr_mask;
254e948693eSPhilip Paeps 		rx_desc = &rxq->queue[id];
255e948693eSPhilip Paeps 		KASSERT(rx_desc->mbuf == NULL, ("rx_desc->mbuf != NULL"));
256e948693eSPhilip Paeps 
257e948693eSPhilip Paeps 		rx_desc->flags = EFX_DISCARD;
258009d75e7SGleb Smirnoff 		m = rx_desc->mbuf = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR,
259009d75e7SGleb Smirnoff 		    sc->rx_cluster_size);
260e948693eSPhilip Paeps 		if (m == NULL)
261e948693eSPhilip Paeps 			break;
2623c838a9fSAndrew Rybchenko 
2633c838a9fSAndrew Rybchenko 		/* m_len specifies length of area to be mapped for DMA */
2643c838a9fSAndrew Rybchenko 		m->m_len  = mblksize;
2653c838a9fSAndrew Rybchenko 		m->m_data = (caddr_t)P2ROUNDUP((uintptr_t)m->m_data, CACHE_LINE_SIZE);
2663c838a9fSAndrew Rybchenko 		m->m_data += sc->rx_buffer_align;
2673c838a9fSAndrew Rybchenko 
268e948693eSPhilip Paeps 		sfxge_map_mbuf_fast(rxq->mem.esm_tag, rxq->mem.esm_map, m, &seg);
269e948693eSPhilip Paeps 		addr[batch++] = seg.ds_addr;
270e948693eSPhilip Paeps 
271e948693eSPhilip Paeps 		if (batch == SFXGE_REFILL_BATCH) {
272e948693eSPhilip Paeps 			efx_rx_qpost(rxq->common, addr, mblksize, batch,
273e948693eSPhilip Paeps 			    rxq->completed, rxq->added);
274e948693eSPhilip Paeps 			rxq->added += batch;
275e948693eSPhilip Paeps 			batch = 0;
276e948693eSPhilip Paeps 		}
277e948693eSPhilip Paeps 	}
278e948693eSPhilip Paeps 
279e948693eSPhilip Paeps 	if (ntodo != 0)
280e948693eSPhilip Paeps 		sfxge_rx_schedule_refill(rxq, retrying);
281e948693eSPhilip Paeps 
282e948693eSPhilip Paeps 	if (batch != 0) {
283e948693eSPhilip Paeps 		efx_rx_qpost(rxq->common, addr, mblksize, batch,
284e948693eSPhilip Paeps 		    rxq->completed, rxq->added);
285e948693eSPhilip Paeps 		rxq->added += batch;
286e948693eSPhilip Paeps 	}
287e948693eSPhilip Paeps 
288e948693eSPhilip Paeps 	/* Make the descriptors visible to the hardware */
289e948693eSPhilip Paeps 	bus_dmamap_sync(rxq->mem.esm_tag, rxq->mem.esm_map,
290e948693eSPhilip Paeps 			BUS_DMASYNC_PREWRITE);
291e948693eSPhilip Paeps 
2923c838a9fSAndrew Rybchenko 	efx_rx_qpush(rxq->common, rxq->added, &rxq->pushed);
2933c838a9fSAndrew Rybchenko 
2943c838a9fSAndrew Rybchenko 	/* The queue could still be empty if no descriptors were actually
2953c838a9fSAndrew Rybchenko 	 * pushed, in which case there will be no event to cause the next
2963c838a9fSAndrew Rybchenko 	 * refill, so we must schedule a refill ourselves.
2973c838a9fSAndrew Rybchenko 	 */
2983c838a9fSAndrew Rybchenko 	if(rxq->pushed == rxq->completed) {
2993c838a9fSAndrew Rybchenko 		sfxge_rx_schedule_refill(rxq, retrying);
3003c838a9fSAndrew Rybchenko 	}
301e948693eSPhilip Paeps }
302e948693eSPhilip Paeps 
303e948693eSPhilip Paeps void
304e948693eSPhilip Paeps sfxge_rx_qrefill(struct sfxge_rxq *rxq)
305e948693eSPhilip Paeps {
306e948693eSPhilip Paeps 
307851128b8SAndrew Rybchenko 	if (__predict_false(rxq->init_state != SFXGE_RXQ_STARTED))
308e948693eSPhilip Paeps 		return;
309e948693eSPhilip Paeps 
310e948693eSPhilip Paeps 	/* Make sure the queue is full */
311385b1d8eSGeorge V. Neville-Neil 	sfxge_rx_qfill(rxq, EFX_RXQ_LIMIT(rxq->entries), B_TRUE);
312e948693eSPhilip Paeps }
313e948693eSPhilip Paeps 
314e948693eSPhilip Paeps static void __sfxge_rx_deliver(struct sfxge_softc *sc, struct mbuf *m)
315e948693eSPhilip Paeps {
316e948693eSPhilip Paeps 	struct ifnet *ifp = sc->ifnet;
317e948693eSPhilip Paeps 
318e948693eSPhilip Paeps 	m->m_pkthdr.rcvif = ifp;
319e948693eSPhilip Paeps 	m->m_pkthdr.csum_data = 0xffff;
320e948693eSPhilip Paeps 	ifp->if_input(ifp, m);
321e948693eSPhilip Paeps }
322e948693eSPhilip Paeps 
323e948693eSPhilip Paeps static void
324e948693eSPhilip Paeps sfxge_rx_deliver(struct sfxge_softc *sc, struct sfxge_rx_sw_desc *rx_desc)
325e948693eSPhilip Paeps {
326e948693eSPhilip Paeps 	struct mbuf *m = rx_desc->mbuf;
327588644a4SAndrew Rybchenko 	int flags = rx_desc->flags;
328e948693eSPhilip Paeps 	int csum_flags;
329e948693eSPhilip Paeps 
330e948693eSPhilip Paeps 	/* Convert checksum flags */
331588644a4SAndrew Rybchenko 	csum_flags = (flags & EFX_CKSUM_IPV4) ?
332e948693eSPhilip Paeps 		(CSUM_IP_CHECKED | CSUM_IP_VALID) : 0;
333588644a4SAndrew Rybchenko 	if (flags & EFX_CKSUM_TCPUDP)
334e948693eSPhilip Paeps 		csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
335e948693eSPhilip Paeps 
336588644a4SAndrew Rybchenko 	if (flags & (EFX_PKT_IPV4 | EFX_PKT_IPV6)) {
3373c838a9fSAndrew Rybchenko 		m->m_pkthdr.flowid =
3383c838a9fSAndrew Rybchenko 			efx_psuedo_hdr_hash_get(sc->enp,
3393c838a9fSAndrew Rybchenko 						EFX_RX_HASHALG_TOEPLITZ,
340e948693eSPhilip Paeps 						mtod(m, uint8_t *));
341dcf08586SAndrew Rybchenko 		/* The hash covers a 4-tuple for TCP only */
342dcf08586SAndrew Rybchenko 		M_HASHTYPE_SET(m,
343588644a4SAndrew Rybchenko 		    (flags & EFX_PKT_IPV4) ?
344588644a4SAndrew Rybchenko 			((flags & EFX_PKT_TCP) ?
345dcf08586SAndrew Rybchenko 			    M_HASHTYPE_RSS_TCP_IPV4 : M_HASHTYPE_RSS_IPV4) :
346588644a4SAndrew Rybchenko 			((flags & EFX_PKT_TCP) ?
347dcf08586SAndrew Rybchenko 			    M_HASHTYPE_RSS_TCP_IPV6 : M_HASHTYPE_RSS_IPV6));
348e948693eSPhilip Paeps 	}
349e948693eSPhilip Paeps 	m->m_data += sc->rx_prefix_size;
350e948693eSPhilip Paeps 	m->m_len = rx_desc->size - sc->rx_prefix_size;
351e948693eSPhilip Paeps 	m->m_pkthdr.len = m->m_len;
352e948693eSPhilip Paeps 	m->m_pkthdr.csum_flags = csum_flags;
353e948693eSPhilip Paeps 	__sfxge_rx_deliver(sc, rx_desc->mbuf);
354e948693eSPhilip Paeps 
355e948693eSPhilip Paeps 	rx_desc->flags = EFX_DISCARD;
356e948693eSPhilip Paeps 	rx_desc->mbuf = NULL;
357e948693eSPhilip Paeps }
358e948693eSPhilip Paeps 
35918daa0eeSAndrew Rybchenko #ifdef SFXGE_LRO
36018daa0eeSAndrew Rybchenko 
361e948693eSPhilip Paeps static void
362e948693eSPhilip Paeps sfxge_lro_deliver(struct sfxge_lro_state *st, struct sfxge_lro_conn *c)
363e948693eSPhilip Paeps {
364e948693eSPhilip Paeps 	struct sfxge_softc *sc = st->sc;
365e948693eSPhilip Paeps 	struct mbuf *m = c->mbuf;
366e948693eSPhilip Paeps 	struct tcphdr *c_th;
367e948693eSPhilip Paeps 	int csum_flags;
368e948693eSPhilip Paeps 
369e948693eSPhilip Paeps 	KASSERT(m, ("no mbuf to deliver"));
370e948693eSPhilip Paeps 
371e948693eSPhilip Paeps 	++st->n_bursts;
372e948693eSPhilip Paeps 
373e948693eSPhilip Paeps 	/* Finish off packet munging and recalculate IP header checksum. */
374e948693eSPhilip Paeps 	if (SFXGE_LRO_CONN_IS_TCPIPV4(c)) {
375e948693eSPhilip Paeps 		struct ip *iph = c->nh;
376e948693eSPhilip Paeps 		iph->ip_len = htons(iph->ip_len);
377e948693eSPhilip Paeps 		iph->ip_sum = 0;
378e948693eSPhilip Paeps 		iph->ip_sum = in_cksum_hdr(iph);
379e948693eSPhilip Paeps 		c_th = (struct tcphdr *)(iph + 1);
380e948693eSPhilip Paeps 		csum_flags = (CSUM_DATA_VALID | CSUM_PSEUDO_HDR |
381e948693eSPhilip Paeps 			      CSUM_IP_CHECKED | CSUM_IP_VALID);
382e948693eSPhilip Paeps 	} else {
383e948693eSPhilip Paeps 		struct ip6_hdr *iph = c->nh;
384e948693eSPhilip Paeps 		iph->ip6_plen = htons(iph->ip6_plen);
385e948693eSPhilip Paeps 		c_th = (struct tcphdr *)(iph + 1);
386e948693eSPhilip Paeps 		csum_flags = CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
387e948693eSPhilip Paeps 	}
388e948693eSPhilip Paeps 
389e948693eSPhilip Paeps 	c_th->th_win = c->th_last->th_win;
390e948693eSPhilip Paeps 	c_th->th_ack = c->th_last->th_ack;
391e948693eSPhilip Paeps 	if (c_th->th_off == c->th_last->th_off) {
392e948693eSPhilip Paeps 		/* Copy TCP options (take care to avoid going negative). */
393e948693eSPhilip Paeps 		int optlen = ((c_th->th_off - 5) & 0xf) << 2u;
394e948693eSPhilip Paeps 		memcpy(c_th + 1, c->th_last + 1, optlen);
395e948693eSPhilip Paeps 	}
396e948693eSPhilip Paeps 
397e948693eSPhilip Paeps 	m->m_pkthdr.flowid = c->conn_hash;
398dcf08586SAndrew Rybchenko 	M_HASHTYPE_SET(m,
399dcf08586SAndrew Rybchenko 	    SFXGE_LRO_CONN_IS_TCPIPV4(c) ?
400dcf08586SAndrew Rybchenko 		M_HASHTYPE_RSS_TCP_IPV4 : M_HASHTYPE_RSS_TCP_IPV6);
401a411fe4eSAndrew Rybchenko 
402e948693eSPhilip Paeps 	m->m_pkthdr.csum_flags = csum_flags;
403e948693eSPhilip Paeps 	__sfxge_rx_deliver(sc, m);
404e948693eSPhilip Paeps 
405e948693eSPhilip Paeps 	c->mbuf = NULL;
406e948693eSPhilip Paeps 	c->delivered = 1;
407e948693eSPhilip Paeps }
408e948693eSPhilip Paeps 
409e948693eSPhilip Paeps /* Drop the given connection, and add it to the free list. */
410e948693eSPhilip Paeps static void sfxge_lro_drop(struct sfxge_rxq *rxq, struct sfxge_lro_conn *c)
411e948693eSPhilip Paeps {
412e948693eSPhilip Paeps 	unsigned bucket;
413e948693eSPhilip Paeps 
414e948693eSPhilip Paeps 	KASSERT(!c->mbuf, ("found orphaned mbuf"));
415e948693eSPhilip Paeps 
416b7b0edd1SGeorge V. Neville-Neil 	if (c->next_buf.mbuf != NULL) {
417e948693eSPhilip Paeps 		sfxge_rx_deliver(rxq->sc, &c->next_buf);
418e948693eSPhilip Paeps 		LIST_REMOVE(c, active_link);
419e948693eSPhilip Paeps 	}
420e948693eSPhilip Paeps 
421e948693eSPhilip Paeps 	bucket = c->conn_hash & rxq->lro.conns_mask;
422e948693eSPhilip Paeps 	KASSERT(rxq->lro.conns_n[bucket] > 0, ("LRO: bucket fill level wrong"));
423e948693eSPhilip Paeps 	--rxq->lro.conns_n[bucket];
424e948693eSPhilip Paeps 	TAILQ_REMOVE(&rxq->lro.conns[bucket], c, link);
425e948693eSPhilip Paeps 	TAILQ_INSERT_HEAD(&rxq->lro.free_conns, c, link);
426e948693eSPhilip Paeps }
427e948693eSPhilip Paeps 
428e948693eSPhilip Paeps /* Stop tracking connections that have gone idle in order to keep hash
429e948693eSPhilip Paeps  * chains short.
430e948693eSPhilip Paeps  */
431e948693eSPhilip Paeps static void sfxge_lro_purge_idle(struct sfxge_rxq *rxq, unsigned now)
432e948693eSPhilip Paeps {
433e948693eSPhilip Paeps 	struct sfxge_lro_conn *c;
434e948693eSPhilip Paeps 	unsigned i;
435e948693eSPhilip Paeps 
436e948693eSPhilip Paeps 	KASSERT(LIST_EMPTY(&rxq->lro.active_conns),
437e948693eSPhilip Paeps 		("found active connections"));
438e948693eSPhilip Paeps 
439e948693eSPhilip Paeps 	rxq->lro.last_purge_ticks = now;
440e948693eSPhilip Paeps 	for (i = 0; i <= rxq->lro.conns_mask; ++i) {
441e948693eSPhilip Paeps 		if (TAILQ_EMPTY(&rxq->lro.conns[i]))
442e948693eSPhilip Paeps 			continue;
443e948693eSPhilip Paeps 
444e948693eSPhilip Paeps 		c = TAILQ_LAST(&rxq->lro.conns[i], sfxge_lro_tailq);
445e948693eSPhilip Paeps 		if (now - c->last_pkt_ticks > lro_idle_ticks) {
446e948693eSPhilip Paeps 			++rxq->lro.n_drop_idle;
447e948693eSPhilip Paeps 			sfxge_lro_drop(rxq, c);
448e948693eSPhilip Paeps 		}
449e948693eSPhilip Paeps 	}
450e948693eSPhilip Paeps }
451e948693eSPhilip Paeps 
452e948693eSPhilip Paeps static void
453e948693eSPhilip Paeps sfxge_lro_merge(struct sfxge_lro_state *st, struct sfxge_lro_conn *c,
454e948693eSPhilip Paeps 		struct mbuf *mbuf, struct tcphdr *th)
455e948693eSPhilip Paeps {
456e948693eSPhilip Paeps 	struct tcphdr *c_th;
457e948693eSPhilip Paeps 
458e948693eSPhilip Paeps 	/* Tack the new mbuf onto the chain. */
459e948693eSPhilip Paeps 	KASSERT(!mbuf->m_next, ("mbuf already chained"));
460e948693eSPhilip Paeps 	c->mbuf_tail->m_next = mbuf;
461e948693eSPhilip Paeps 	c->mbuf_tail = mbuf;
462e948693eSPhilip Paeps 
463e948693eSPhilip Paeps 	/* Increase length appropriately */
464e948693eSPhilip Paeps 	c->mbuf->m_pkthdr.len += mbuf->m_len;
465e948693eSPhilip Paeps 
466e948693eSPhilip Paeps 	/* Update the connection state flags */
467e948693eSPhilip Paeps 	if (SFXGE_LRO_CONN_IS_TCPIPV4(c)) {
468e948693eSPhilip Paeps 		struct ip *iph = c->nh;
469e948693eSPhilip Paeps 		iph->ip_len += mbuf->m_len;
470e948693eSPhilip Paeps 		c_th = (struct tcphdr *)(iph + 1);
471e948693eSPhilip Paeps 	} else {
472e948693eSPhilip Paeps 		struct ip6_hdr *iph = c->nh;
473e948693eSPhilip Paeps 		iph->ip6_plen += mbuf->m_len;
474e948693eSPhilip Paeps 		c_th = (struct tcphdr *)(iph + 1);
475e948693eSPhilip Paeps 	}
476e948693eSPhilip Paeps 	c_th->th_flags |= (th->th_flags & TH_PUSH);
477e948693eSPhilip Paeps 	c->th_last = th;
478e948693eSPhilip Paeps 	++st->n_merges;
479e948693eSPhilip Paeps 
480e948693eSPhilip Paeps 	/* Pass packet up now if another segment could overflow the IP
481e948693eSPhilip Paeps 	 * length.
482e948693eSPhilip Paeps 	 */
483e948693eSPhilip Paeps 	if (c->mbuf->m_pkthdr.len > 65536 - 9200)
484e948693eSPhilip Paeps 		sfxge_lro_deliver(st, c);
485e948693eSPhilip Paeps }
486e948693eSPhilip Paeps 
487e948693eSPhilip Paeps static void
488e948693eSPhilip Paeps sfxge_lro_start(struct sfxge_lro_state *st, struct sfxge_lro_conn *c,
489e948693eSPhilip Paeps 		struct mbuf *mbuf, void *nh, struct tcphdr *th)
490e948693eSPhilip Paeps {
491e948693eSPhilip Paeps 	/* Start the chain */
492e948693eSPhilip Paeps 	c->mbuf = mbuf;
493e948693eSPhilip Paeps 	c->mbuf_tail = c->mbuf;
494e948693eSPhilip Paeps 	c->nh = nh;
495e948693eSPhilip Paeps 	c->th_last = th;
496e948693eSPhilip Paeps 
497e948693eSPhilip Paeps 	mbuf->m_pkthdr.len = mbuf->m_len;
498e948693eSPhilip Paeps 
499e948693eSPhilip Paeps 	/* Mangle header fields for later processing */
500e948693eSPhilip Paeps 	if (SFXGE_LRO_CONN_IS_TCPIPV4(c)) {
501e948693eSPhilip Paeps 		struct ip *iph = nh;
502e948693eSPhilip Paeps 		iph->ip_len = ntohs(iph->ip_len);
503e948693eSPhilip Paeps 	} else {
504e948693eSPhilip Paeps 		struct ip6_hdr *iph = nh;
505e948693eSPhilip Paeps 		iph->ip6_plen = ntohs(iph->ip6_plen);
506e948693eSPhilip Paeps 	}
507e948693eSPhilip Paeps }
508e948693eSPhilip Paeps 
509e948693eSPhilip Paeps /* Try to merge or otherwise hold or deliver (as appropriate) the
510e948693eSPhilip Paeps  * packet buffered for this connection (c->next_buf).  Return a flag
511e948693eSPhilip Paeps  * indicating whether the connection is still active for LRO purposes.
512e948693eSPhilip Paeps  */
513e948693eSPhilip Paeps static int
514e948693eSPhilip Paeps sfxge_lro_try_merge(struct sfxge_rxq *rxq, struct sfxge_lro_conn *c)
515e948693eSPhilip Paeps {
516e948693eSPhilip Paeps 	struct sfxge_rx_sw_desc *rx_buf = &c->next_buf;
517e948693eSPhilip Paeps 	char *eh = c->next_eh;
518e948693eSPhilip Paeps 	int data_length, hdr_length, dont_merge;
519e948693eSPhilip Paeps 	unsigned th_seq, pkt_length;
520e948693eSPhilip Paeps 	struct tcphdr *th;
521e948693eSPhilip Paeps 	unsigned now;
522e948693eSPhilip Paeps 
523e948693eSPhilip Paeps 	if (SFXGE_LRO_CONN_IS_TCPIPV4(c)) {
524e948693eSPhilip Paeps 		struct ip *iph = c->next_nh;
525e948693eSPhilip Paeps 		th = (struct tcphdr *)(iph + 1);
526e948693eSPhilip Paeps 		pkt_length = ntohs(iph->ip_len) + (char *) iph - eh;
527e948693eSPhilip Paeps 	} else {
528e948693eSPhilip Paeps 		struct ip6_hdr *iph = c->next_nh;
529e948693eSPhilip Paeps 		th = (struct tcphdr *)(iph + 1);
530e948693eSPhilip Paeps 		pkt_length = ntohs(iph->ip6_plen) + (char *) th - eh;
531e948693eSPhilip Paeps 	}
532e948693eSPhilip Paeps 
533e948693eSPhilip Paeps 	hdr_length = (char *) th + th->th_off * 4 - eh;
534e948693eSPhilip Paeps 	data_length = (min(pkt_length, rx_buf->size - rxq->sc->rx_prefix_size) -
535e948693eSPhilip Paeps 		       hdr_length);
536e948693eSPhilip Paeps 	th_seq = ntohl(th->th_seq);
537e948693eSPhilip Paeps 	dont_merge = ((data_length <= 0)
538e948693eSPhilip Paeps 		      | (th->th_flags & (TH_URG | TH_SYN | TH_RST | TH_FIN)));
539e948693eSPhilip Paeps 
540e948693eSPhilip Paeps 	/* Check for options other than aligned timestamp. */
541e948693eSPhilip Paeps 	if (th->th_off != 5) {
542e948693eSPhilip Paeps 		const uint32_t *opt_ptr = (const uint32_t *) (th + 1);
543e948693eSPhilip Paeps 		if (th->th_off == 8 &&
544e948693eSPhilip Paeps 		    opt_ptr[0] == ntohl((TCPOPT_NOP << 24) |
545e948693eSPhilip Paeps 					(TCPOPT_NOP << 16) |
546e948693eSPhilip Paeps 					(TCPOPT_TIMESTAMP << 8) |
547e948693eSPhilip Paeps 					TCPOLEN_TIMESTAMP)) {
548e948693eSPhilip Paeps 			/* timestamp option -- okay */
549e948693eSPhilip Paeps 		} else {
550e948693eSPhilip Paeps 			dont_merge = 1;
551e948693eSPhilip Paeps 		}
552e948693eSPhilip Paeps 	}
553e948693eSPhilip Paeps 
554e948693eSPhilip Paeps 	if (__predict_false(th_seq != c->next_seq)) {
555e948693eSPhilip Paeps 		/* Out-of-order, so start counting again. */
556b7b0edd1SGeorge V. Neville-Neil 		if (c->mbuf != NULL)
557e948693eSPhilip Paeps 			sfxge_lro_deliver(&rxq->lro, c);
558e948693eSPhilip Paeps 		c->n_in_order_pkts -= lro_loss_packets;
559e948693eSPhilip Paeps 		c->next_seq = th_seq + data_length;
560e948693eSPhilip Paeps 		++rxq->lro.n_misorder;
561e948693eSPhilip Paeps 		goto deliver_buf_out;
562e948693eSPhilip Paeps 	}
563e948693eSPhilip Paeps 	c->next_seq = th_seq + data_length;
564e948693eSPhilip Paeps 
565e948693eSPhilip Paeps 	now = ticks;
566e948693eSPhilip Paeps 	if (now - c->last_pkt_ticks > lro_idle_ticks) {
567e948693eSPhilip Paeps 		++rxq->lro.n_drop_idle;
568b7b0edd1SGeorge V. Neville-Neil 		if (c->mbuf != NULL)
569e948693eSPhilip Paeps 			sfxge_lro_deliver(&rxq->lro, c);
570e948693eSPhilip Paeps 		sfxge_lro_drop(rxq, c);
571b7b0edd1SGeorge V. Neville-Neil 		return (0);
572e948693eSPhilip Paeps 	}
573e948693eSPhilip Paeps 	c->last_pkt_ticks = ticks;
574e948693eSPhilip Paeps 
575e948693eSPhilip Paeps 	if (c->n_in_order_pkts < lro_slow_start_packets) {
576e948693eSPhilip Paeps 		/* May be in slow-start, so don't merge. */
577e948693eSPhilip Paeps 		++rxq->lro.n_slow_start;
578e948693eSPhilip Paeps 		++c->n_in_order_pkts;
579e948693eSPhilip Paeps 		goto deliver_buf_out;
580e948693eSPhilip Paeps 	}
581e948693eSPhilip Paeps 
582e948693eSPhilip Paeps 	if (__predict_false(dont_merge)) {
583b7b0edd1SGeorge V. Neville-Neil 		if (c->mbuf != NULL)
584e948693eSPhilip Paeps 			sfxge_lro_deliver(&rxq->lro, c);
585e948693eSPhilip Paeps 		if (th->th_flags & (TH_FIN | TH_RST)) {
586e948693eSPhilip Paeps 			++rxq->lro.n_drop_closed;
587e948693eSPhilip Paeps 			sfxge_lro_drop(rxq, c);
588b7b0edd1SGeorge V. Neville-Neil 			return (0);
589e948693eSPhilip Paeps 		}
590e948693eSPhilip Paeps 		goto deliver_buf_out;
591e948693eSPhilip Paeps 	}
592e948693eSPhilip Paeps 
593e948693eSPhilip Paeps 	rx_buf->mbuf->m_data += rxq->sc->rx_prefix_size;
594e948693eSPhilip Paeps 
595e948693eSPhilip Paeps 	if (__predict_true(c->mbuf != NULL)) {
596e948693eSPhilip Paeps 		/* Remove headers and any padding */
597e948693eSPhilip Paeps 		rx_buf->mbuf->m_data += hdr_length;
598e948693eSPhilip Paeps 		rx_buf->mbuf->m_len = data_length;
599e948693eSPhilip Paeps 
600e948693eSPhilip Paeps 		sfxge_lro_merge(&rxq->lro, c, rx_buf->mbuf, th);
601e948693eSPhilip Paeps 	} else {
602e948693eSPhilip Paeps 		/* Remove any padding */
603e948693eSPhilip Paeps 		rx_buf->mbuf->m_len = pkt_length;
604e948693eSPhilip Paeps 
605e948693eSPhilip Paeps 		sfxge_lro_start(&rxq->lro, c, rx_buf->mbuf, c->next_nh, th);
606e948693eSPhilip Paeps 	}
607e948693eSPhilip Paeps 
608e948693eSPhilip Paeps 	rx_buf->mbuf = NULL;
609b7b0edd1SGeorge V. Neville-Neil 	return (1);
610e948693eSPhilip Paeps 
611e948693eSPhilip Paeps  deliver_buf_out:
612e948693eSPhilip Paeps 	sfxge_rx_deliver(rxq->sc, rx_buf);
613b7b0edd1SGeorge V. Neville-Neil 	return (1);
614e948693eSPhilip Paeps }
615e948693eSPhilip Paeps 
616e948693eSPhilip Paeps static void sfxge_lro_new_conn(struct sfxge_lro_state *st, uint32_t conn_hash,
617e948693eSPhilip Paeps 			       uint16_t l2_id, void *nh, struct tcphdr *th)
618e948693eSPhilip Paeps {
619e948693eSPhilip Paeps 	unsigned bucket = conn_hash & st->conns_mask;
620e948693eSPhilip Paeps 	struct sfxge_lro_conn *c;
621e948693eSPhilip Paeps 
622e948693eSPhilip Paeps 	if (st->conns_n[bucket] >= lro_chain_max) {
623e948693eSPhilip Paeps 		++st->n_too_many;
624e948693eSPhilip Paeps 		return;
625e948693eSPhilip Paeps 	}
626e948693eSPhilip Paeps 
627e948693eSPhilip Paeps 	if (!TAILQ_EMPTY(&st->free_conns)) {
628e948693eSPhilip Paeps 		c = TAILQ_FIRST(&st->free_conns);
629e948693eSPhilip Paeps 		TAILQ_REMOVE(&st->free_conns, c, link);
630e948693eSPhilip Paeps 	} else {
631e275c0d3SGleb Smirnoff 		c = malloc(sizeof(*c), M_SFXGE, M_NOWAIT);
632e948693eSPhilip Paeps 		if (c == NULL)
633e948693eSPhilip Paeps 			return;
634e948693eSPhilip Paeps 		c->mbuf = NULL;
635e948693eSPhilip Paeps 		c->next_buf.mbuf = NULL;
636e948693eSPhilip Paeps 	}
637e948693eSPhilip Paeps 
638e948693eSPhilip Paeps 	/* Create the connection tracking data */
639e948693eSPhilip Paeps 	++st->conns_n[bucket];
640e948693eSPhilip Paeps 	TAILQ_INSERT_HEAD(&st->conns[bucket], c, link);
641e948693eSPhilip Paeps 	c->l2_id = l2_id;
642e948693eSPhilip Paeps 	c->conn_hash = conn_hash;
643e948693eSPhilip Paeps 	c->source = th->th_sport;
644e948693eSPhilip Paeps 	c->dest = th->th_dport;
645e948693eSPhilip Paeps 	c->n_in_order_pkts = 0;
646e948693eSPhilip Paeps 	c->last_pkt_ticks = *(volatile int *)&ticks;
647e948693eSPhilip Paeps 	c->delivered = 0;
648e948693eSPhilip Paeps 	++st->n_new_stream;
649e948693eSPhilip Paeps 	/* NB. We don't initialise c->next_seq, and it doesn't matter what
650e948693eSPhilip Paeps 	 * value it has.  Most likely the next packet received for this
651e948693eSPhilip Paeps 	 * connection will not match -- no harm done.
652e948693eSPhilip Paeps 	 */
653e948693eSPhilip Paeps }
654e948693eSPhilip Paeps 
655e948693eSPhilip Paeps /* Process mbuf and decide whether to dispatch it to the stack now or
656e948693eSPhilip Paeps  * later.
657e948693eSPhilip Paeps  */
658e948693eSPhilip Paeps static void
659e948693eSPhilip Paeps sfxge_lro(struct sfxge_rxq *rxq, struct sfxge_rx_sw_desc *rx_buf)
660e948693eSPhilip Paeps {
661e948693eSPhilip Paeps 	struct sfxge_softc *sc = rxq->sc;
662e948693eSPhilip Paeps 	struct mbuf *m = rx_buf->mbuf;
663e948693eSPhilip Paeps 	struct ether_header *eh;
664e948693eSPhilip Paeps 	struct sfxge_lro_conn *c;
665e948693eSPhilip Paeps 	uint16_t l2_id;
666e948693eSPhilip Paeps 	uint16_t l3_proto;
667e948693eSPhilip Paeps 	void *nh;
668e948693eSPhilip Paeps 	struct tcphdr *th;
669e948693eSPhilip Paeps 	uint32_t conn_hash;
670e948693eSPhilip Paeps 	unsigned bucket;
671e948693eSPhilip Paeps 
672e948693eSPhilip Paeps 	/* Get the hardware hash */
6733c838a9fSAndrew Rybchenko 	conn_hash = efx_psuedo_hdr_hash_get(sc->enp,
6743c838a9fSAndrew Rybchenko 					    EFX_RX_HASHALG_TOEPLITZ,
675e948693eSPhilip Paeps 					    mtod(m, uint8_t *));
676e948693eSPhilip Paeps 
677e948693eSPhilip Paeps 	eh = (struct ether_header *)(m->m_data + sc->rx_prefix_size);
678e948693eSPhilip Paeps 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
679e948693eSPhilip Paeps 		struct ether_vlan_header *veh = (struct ether_vlan_header *)eh;
680e948693eSPhilip Paeps 		l2_id = EVL_VLANOFTAG(ntohs(veh->evl_tag)) |
681e948693eSPhilip Paeps 			SFXGE_LRO_L2_ID_VLAN;
682e948693eSPhilip Paeps 		l3_proto = veh->evl_proto;
683e948693eSPhilip Paeps 		nh = veh + 1;
684e948693eSPhilip Paeps 	} else {
685e948693eSPhilip Paeps 		l2_id = 0;
686e948693eSPhilip Paeps 		l3_proto = eh->ether_type;
687e948693eSPhilip Paeps 		nh = eh + 1;
688e948693eSPhilip Paeps 	}
689e948693eSPhilip Paeps 
690e948693eSPhilip Paeps 	/* Check whether this is a suitable packet (unfragmented
691e948693eSPhilip Paeps 	 * TCP/IPv4 or TCP/IPv6).  If so, find the TCP header and
692e948693eSPhilip Paeps 	 * length, and compute a hash if necessary.  If not, return.
693e948693eSPhilip Paeps 	 */
694e948693eSPhilip Paeps 	if (l3_proto == htons(ETHERTYPE_IP)) {
695e948693eSPhilip Paeps 		struct ip *iph = nh;
6963b3390c1SAndrew Rybchenko 
6973b3390c1SAndrew Rybchenko 		KASSERT(iph->ip_p == IPPROTO_TCP,
6983b3390c1SAndrew Rybchenko 		    ("IPv4 protocol is not TCP, but packet marker is set"));
6993b3390c1SAndrew Rybchenko 		if ((iph->ip_hl - (sizeof(*iph) >> 2u)) |
700e948693eSPhilip Paeps 		    (iph->ip_off & htons(IP_MF | IP_OFFMASK)))
701e948693eSPhilip Paeps 			goto deliver_now;
702e948693eSPhilip Paeps 		th = (struct tcphdr *)(iph + 1);
703e948693eSPhilip Paeps 	} else if (l3_proto == htons(ETHERTYPE_IPV6)) {
704e948693eSPhilip Paeps 		struct ip6_hdr *iph = nh;
7053b3390c1SAndrew Rybchenko 
7063b3390c1SAndrew Rybchenko 		KASSERT(iph->ip6_nxt == IPPROTO_TCP,
7073b3390c1SAndrew Rybchenko 		    ("IPv6 next header is not TCP, but packet marker is set"));
708e948693eSPhilip Paeps 		l2_id |= SFXGE_LRO_L2_ID_IPV6;
709e948693eSPhilip Paeps 		th = (struct tcphdr *)(iph + 1);
710e948693eSPhilip Paeps 	} else {
711e948693eSPhilip Paeps 		goto deliver_now;
712e948693eSPhilip Paeps 	}
713e948693eSPhilip Paeps 
714e948693eSPhilip Paeps 	bucket = conn_hash & rxq->lro.conns_mask;
715e948693eSPhilip Paeps 
716e948693eSPhilip Paeps 	TAILQ_FOREACH(c, &rxq->lro.conns[bucket], link) {
717e948693eSPhilip Paeps 		if ((c->l2_id - l2_id) | (c->conn_hash - conn_hash))
718e948693eSPhilip Paeps 			continue;
719e948693eSPhilip Paeps 		if ((c->source - th->th_sport) | (c->dest - th->th_dport))
720e948693eSPhilip Paeps 			continue;
721b7b0edd1SGeorge V. Neville-Neil 		if (c->mbuf != NULL) {
722e948693eSPhilip Paeps 			if (SFXGE_LRO_CONN_IS_TCPIPV4(c)) {
723e948693eSPhilip Paeps 				struct ip *c_iph, *iph = nh;
724e948693eSPhilip Paeps 				c_iph = c->nh;
725e948693eSPhilip Paeps 				if ((c_iph->ip_src.s_addr - iph->ip_src.s_addr) |
726e948693eSPhilip Paeps 				    (c_iph->ip_dst.s_addr - iph->ip_dst.s_addr))
727e948693eSPhilip Paeps 					continue;
728e948693eSPhilip Paeps 			} else {
729e948693eSPhilip Paeps 				struct ip6_hdr *c_iph, *iph = nh;
730e948693eSPhilip Paeps 				c_iph = c->nh;
731e948693eSPhilip Paeps 				if (ipv6_addr_cmp(&c_iph->ip6_src, &iph->ip6_src) |
732e948693eSPhilip Paeps 				    ipv6_addr_cmp(&c_iph->ip6_dst, &iph->ip6_dst))
733e948693eSPhilip Paeps 					continue;
734e948693eSPhilip Paeps 			}
735e948693eSPhilip Paeps 		}
736e948693eSPhilip Paeps 
737e948693eSPhilip Paeps 		/* Re-insert at head of list to reduce lookup time. */
738e948693eSPhilip Paeps 		TAILQ_REMOVE(&rxq->lro.conns[bucket], c, link);
739e948693eSPhilip Paeps 		TAILQ_INSERT_HEAD(&rxq->lro.conns[bucket], c, link);
740e948693eSPhilip Paeps 
741b7b0edd1SGeorge V. Neville-Neil 		if (c->next_buf.mbuf != NULL) {
742e948693eSPhilip Paeps 			if (!sfxge_lro_try_merge(rxq, c))
743e948693eSPhilip Paeps 				goto deliver_now;
744e948693eSPhilip Paeps 		} else {
745e948693eSPhilip Paeps 			LIST_INSERT_HEAD(&rxq->lro.active_conns, c,
746e948693eSPhilip Paeps 			    active_link);
747e948693eSPhilip Paeps 		}
748e948693eSPhilip Paeps 		c->next_buf = *rx_buf;
749e948693eSPhilip Paeps 		c->next_eh = eh;
750e948693eSPhilip Paeps 		c->next_nh = nh;
751e948693eSPhilip Paeps 
752e948693eSPhilip Paeps 		rx_buf->mbuf = NULL;
753e948693eSPhilip Paeps 		rx_buf->flags = EFX_DISCARD;
754e948693eSPhilip Paeps 		return;
755e948693eSPhilip Paeps 	}
756e948693eSPhilip Paeps 
757e948693eSPhilip Paeps 	sfxge_lro_new_conn(&rxq->lro, conn_hash, l2_id, nh, th);
758e948693eSPhilip Paeps  deliver_now:
759e948693eSPhilip Paeps 	sfxge_rx_deliver(sc, rx_buf);
760e948693eSPhilip Paeps }
761e948693eSPhilip Paeps 
762e948693eSPhilip Paeps static void sfxge_lro_end_of_burst(struct sfxge_rxq *rxq)
763e948693eSPhilip Paeps {
764e948693eSPhilip Paeps 	struct sfxge_lro_state *st = &rxq->lro;
765e948693eSPhilip Paeps 	struct sfxge_lro_conn *c;
766e948693eSPhilip Paeps 	unsigned t;
767e948693eSPhilip Paeps 
768e948693eSPhilip Paeps 	while (!LIST_EMPTY(&st->active_conns)) {
769e948693eSPhilip Paeps 		c = LIST_FIRST(&st->active_conns);
770b7b0edd1SGeorge V. Neville-Neil 		if (!c->delivered && c->mbuf != NULL)
771e948693eSPhilip Paeps 			sfxge_lro_deliver(st, c);
772e948693eSPhilip Paeps 		if (sfxge_lro_try_merge(rxq, c)) {
773b7b0edd1SGeorge V. Neville-Neil 			if (c->mbuf != NULL)
774e948693eSPhilip Paeps 				sfxge_lro_deliver(st, c);
775e948693eSPhilip Paeps 			LIST_REMOVE(c, active_link);
776e948693eSPhilip Paeps 		}
777e948693eSPhilip Paeps 		c->delivered = 0;
778e948693eSPhilip Paeps 	}
779e948693eSPhilip Paeps 
780e948693eSPhilip Paeps 	t = *(volatile int *)&ticks;
781e948693eSPhilip Paeps 	if (__predict_false(t != st->last_purge_ticks))
782e948693eSPhilip Paeps 		sfxge_lro_purge_idle(rxq, t);
783e948693eSPhilip Paeps }
784e948693eSPhilip Paeps 
78518daa0eeSAndrew Rybchenko #else	/* !SFXGE_LRO */
78618daa0eeSAndrew Rybchenko 
78718daa0eeSAndrew Rybchenko static void
78818daa0eeSAndrew Rybchenko sfxge_lro(struct sfxge_rxq *rxq, struct sfxge_rx_sw_desc *rx_buf)
78918daa0eeSAndrew Rybchenko {
79018daa0eeSAndrew Rybchenko }
79118daa0eeSAndrew Rybchenko 
79218daa0eeSAndrew Rybchenko static void
79318daa0eeSAndrew Rybchenko sfxge_lro_end_of_burst(struct sfxge_rxq *rxq)
79418daa0eeSAndrew Rybchenko {
79518daa0eeSAndrew Rybchenko }
79618daa0eeSAndrew Rybchenko 
79718daa0eeSAndrew Rybchenko #endif	/* SFXGE_LRO */
79818daa0eeSAndrew Rybchenko 
799e948693eSPhilip Paeps void
800e948693eSPhilip Paeps sfxge_rx_qcomplete(struct sfxge_rxq *rxq, boolean_t eop)
801e948693eSPhilip Paeps {
802e948693eSPhilip Paeps 	struct sfxge_softc *sc = rxq->sc;
803b5bae9f4SAndrew Rybchenko 	int if_capenable = sc->ifnet->if_capenable;
804b5bae9f4SAndrew Rybchenko 	int lro_enabled = if_capenable & IFCAP_LRO;
805e948693eSPhilip Paeps 	unsigned int index;
806e948693eSPhilip Paeps 	struct sfxge_evq *evq;
807e948693eSPhilip Paeps 	unsigned int completed;
808e948693eSPhilip Paeps 	unsigned int level;
809e948693eSPhilip Paeps 	struct mbuf *m;
810e948693eSPhilip Paeps 	struct sfxge_rx_sw_desc *prev = NULL;
811e948693eSPhilip Paeps 
812e948693eSPhilip Paeps 	index = rxq->index;
813e948693eSPhilip Paeps 	evq = sc->evq[index];
814e948693eSPhilip Paeps 
815763cab71SAndrew Rybchenko 	SFXGE_EVQ_LOCK_ASSERT_OWNED(evq);
816e948693eSPhilip Paeps 
817e948693eSPhilip Paeps 	completed = rxq->completed;
818e948693eSPhilip Paeps 	while (completed != rxq->pending) {
819e948693eSPhilip Paeps 		unsigned int id;
820e948693eSPhilip Paeps 		struct sfxge_rx_sw_desc *rx_desc;
821e948693eSPhilip Paeps 
822385b1d8eSGeorge V. Neville-Neil 		id = completed++ & rxq->ptr_mask;
823e948693eSPhilip Paeps 		rx_desc = &rxq->queue[id];
824e948693eSPhilip Paeps 		m = rx_desc->mbuf;
825e948693eSPhilip Paeps 
826851128b8SAndrew Rybchenko 		if (__predict_false(rxq->init_state != SFXGE_RXQ_STARTED))
827e948693eSPhilip Paeps 			goto discard;
828e948693eSPhilip Paeps 
829e948693eSPhilip Paeps 		if (rx_desc->flags & (EFX_ADDR_MISMATCH | EFX_DISCARD))
830e948693eSPhilip Paeps 			goto discard;
831e948693eSPhilip Paeps 
832*453130d9SPedro F. Giffuni 		/* Read the length from the pseudo header if required */
8333c838a9fSAndrew Rybchenko 		if (rx_desc->flags & EFX_PKT_PREFIX_LEN) {
8343c838a9fSAndrew Rybchenko 			uint16_t tmp_size;
8353c838a9fSAndrew Rybchenko 			int rc;
8363c838a9fSAndrew Rybchenko 			rc = efx_psuedo_hdr_pkt_length_get(sc->enp,
8373c838a9fSAndrew Rybchenko 							   mtod(m, uint8_t *),
8383c838a9fSAndrew Rybchenko 							   &tmp_size);
8393c838a9fSAndrew Rybchenko 			KASSERT(rc == 0, ("cannot get packet length: %d", rc));
8403c838a9fSAndrew Rybchenko 			rx_desc->size = (int)tmp_size + sc->rx_prefix_size;
8413c838a9fSAndrew Rybchenko 		}
8423c838a9fSAndrew Rybchenko 
843e948693eSPhilip Paeps 		prefetch_read_many(mtod(m, caddr_t));
844e948693eSPhilip Paeps 
845b5bae9f4SAndrew Rybchenko 		switch (rx_desc->flags & (EFX_PKT_IPV4 | EFX_PKT_IPV6)) {
846b5bae9f4SAndrew Rybchenko 		case EFX_PKT_IPV4:
847b5bae9f4SAndrew Rybchenko 			if (~if_capenable & IFCAP_RXCSUM)
848b5bae9f4SAndrew Rybchenko 				rx_desc->flags &=
849b5bae9f4SAndrew Rybchenko 				    ~(EFX_CKSUM_IPV4 | EFX_CKSUM_TCPUDP);
850b5bae9f4SAndrew Rybchenko 			break;
851b5bae9f4SAndrew Rybchenko 		case EFX_PKT_IPV6:
852b5bae9f4SAndrew Rybchenko 			if (~if_capenable & IFCAP_RXCSUM_IPV6)
853b5bae9f4SAndrew Rybchenko 				rx_desc->flags &= ~EFX_CKSUM_TCPUDP;
854b5bae9f4SAndrew Rybchenko 			break;
855b5bae9f4SAndrew Rybchenko 		case 0:
856e948693eSPhilip Paeps 			/* Check for loopback packets */
857b5bae9f4SAndrew Rybchenko 			{
858e948693eSPhilip Paeps 				struct ether_header *etherhp;
859e948693eSPhilip Paeps 
860e948693eSPhilip Paeps 				/*LINTED*/
861e948693eSPhilip Paeps 				etherhp = mtod(m, struct ether_header *);
862e948693eSPhilip Paeps 
863e948693eSPhilip Paeps 				if (etherhp->ether_type ==
864e948693eSPhilip Paeps 				    htons(SFXGE_ETHERTYPE_LOOPBACK)) {
865e948693eSPhilip Paeps 					EFSYS_PROBE(loopback);
866e948693eSPhilip Paeps 
867e948693eSPhilip Paeps 					rxq->loopback++;
868e948693eSPhilip Paeps 					goto discard;
869e948693eSPhilip Paeps 				}
870e948693eSPhilip Paeps 			}
871b5bae9f4SAndrew Rybchenko 			break;
872b5bae9f4SAndrew Rybchenko 		default:
873b5bae9f4SAndrew Rybchenko 			KASSERT(B_FALSE,
874b5bae9f4SAndrew Rybchenko 			    ("Rx descriptor with both IPv4 and IPv6 flags"));
875b5bae9f4SAndrew Rybchenko 			goto discard;
876b5bae9f4SAndrew Rybchenko 		}
877e948693eSPhilip Paeps 
878e948693eSPhilip Paeps 		/* Pass packet up the stack or into LRO (pipelined) */
879e948693eSPhilip Paeps 		if (prev != NULL) {
8803b3390c1SAndrew Rybchenko 			if (lro_enabled &&
8813b3390c1SAndrew Rybchenko 			    ((prev->flags & (EFX_PKT_TCP | EFX_CKSUM_TCPUDP)) ==
8823b3390c1SAndrew Rybchenko 			     (EFX_PKT_TCP | EFX_CKSUM_TCPUDP)))
883e948693eSPhilip Paeps 				sfxge_lro(rxq, prev);
884e948693eSPhilip Paeps 			else
885e948693eSPhilip Paeps 				sfxge_rx_deliver(sc, prev);
886e948693eSPhilip Paeps 		}
887e948693eSPhilip Paeps 		prev = rx_desc;
888e948693eSPhilip Paeps 		continue;
889e948693eSPhilip Paeps 
890e948693eSPhilip Paeps discard:
891e948693eSPhilip Paeps 		/* Return the packet to the pool */
892e948693eSPhilip Paeps 		m_free(m);
893e948693eSPhilip Paeps 		rx_desc->mbuf = NULL;
894e948693eSPhilip Paeps 	}
895e948693eSPhilip Paeps 	rxq->completed = completed;
896e948693eSPhilip Paeps 
897e948693eSPhilip Paeps 	level = rxq->added - rxq->completed;
898e948693eSPhilip Paeps 
899e948693eSPhilip Paeps 	/* Pass last packet up the stack or into LRO */
900e948693eSPhilip Paeps 	if (prev != NULL) {
9013b3390c1SAndrew Rybchenko 		if (lro_enabled &&
9023b3390c1SAndrew Rybchenko 		    ((prev->flags & (EFX_PKT_TCP | EFX_CKSUM_TCPUDP)) ==
9033b3390c1SAndrew Rybchenko 		     (EFX_PKT_TCP | EFX_CKSUM_TCPUDP)))
904e948693eSPhilip Paeps 			sfxge_lro(rxq, prev);
905e948693eSPhilip Paeps 		else
906e948693eSPhilip Paeps 			sfxge_rx_deliver(sc, prev);
907e948693eSPhilip Paeps 	}
908e948693eSPhilip Paeps 
909e948693eSPhilip Paeps 	/*
910e948693eSPhilip Paeps 	 * If there are any pending flows and this is the end of the
911e948693eSPhilip Paeps 	 * poll then they must be completed.
912e948693eSPhilip Paeps 	 */
913e948693eSPhilip Paeps 	if (eop)
914e948693eSPhilip Paeps 		sfxge_lro_end_of_burst(rxq);
915e948693eSPhilip Paeps 
916e948693eSPhilip Paeps 	/* Top up the queue if necessary */
917385b1d8eSGeorge V. Neville-Neil 	if (level < rxq->refill_threshold)
918385b1d8eSGeorge V. Neville-Neil 		sfxge_rx_qfill(rxq, EFX_RXQ_LIMIT(rxq->entries), B_FALSE);
919e948693eSPhilip Paeps }
920e948693eSPhilip Paeps 
921e948693eSPhilip Paeps static void
922e948693eSPhilip Paeps sfxge_rx_qstop(struct sfxge_softc *sc, unsigned int index)
923e948693eSPhilip Paeps {
924e948693eSPhilip Paeps 	struct sfxge_rxq *rxq;
925e948693eSPhilip Paeps 	struct sfxge_evq *evq;
926e948693eSPhilip Paeps 	unsigned int count;
9273c838a9fSAndrew Rybchenko 	unsigned int retry = 3;
9283c838a9fSAndrew Rybchenko 
9293c838a9fSAndrew Rybchenko 	SFXGE_ADAPTER_LOCK_ASSERT_OWNED(sc);
930e948693eSPhilip Paeps 
931e948693eSPhilip Paeps 	rxq = sc->rxq[index];
932e948693eSPhilip Paeps 	evq = sc->evq[index];
933e948693eSPhilip Paeps 
934763cab71SAndrew Rybchenko 	SFXGE_EVQ_LOCK(evq);
935e948693eSPhilip Paeps 
936e948693eSPhilip Paeps 	KASSERT(rxq->init_state == SFXGE_RXQ_STARTED,
937e948693eSPhilip Paeps 	    ("rxq not started"));
938e948693eSPhilip Paeps 
939e948693eSPhilip Paeps 	rxq->init_state = SFXGE_RXQ_INITIALIZED;
940e948693eSPhilip Paeps 
941e948693eSPhilip Paeps 	callout_stop(&rxq->refill_callout);
942e948693eSPhilip Paeps 
9433c838a9fSAndrew Rybchenko 	while (rxq->flush_state != SFXGE_FLUSH_DONE && retry != 0) {
944e948693eSPhilip Paeps 		rxq->flush_state = SFXGE_FLUSH_PENDING;
945e948693eSPhilip Paeps 
946763cab71SAndrew Rybchenko 		SFXGE_EVQ_UNLOCK(evq);
947e948693eSPhilip Paeps 
9483c838a9fSAndrew Rybchenko 		/* Flush the receive queue */
9493c838a9fSAndrew Rybchenko 		if (efx_rx_qflush(rxq->common) != 0) {
9503c838a9fSAndrew Rybchenko 			SFXGE_EVQ_LOCK(evq);
9513c838a9fSAndrew Rybchenko 			rxq->flush_state = SFXGE_FLUSH_FAILED;
9523c838a9fSAndrew Rybchenko 			break;
9533c838a9fSAndrew Rybchenko 		}
9543c838a9fSAndrew Rybchenko 
955e948693eSPhilip Paeps 		count = 0;
956e948693eSPhilip Paeps 		do {
957e948693eSPhilip Paeps 			/* Spin for 100 ms */
958e948693eSPhilip Paeps 			DELAY(100000);
959e948693eSPhilip Paeps 
960e948693eSPhilip Paeps 			if (rxq->flush_state != SFXGE_FLUSH_PENDING)
961e948693eSPhilip Paeps 				break;
962e948693eSPhilip Paeps 
963e948693eSPhilip Paeps 		} while (++count < 20);
964e948693eSPhilip Paeps 
965763cab71SAndrew Rybchenko 		SFXGE_EVQ_LOCK(evq);
966e948693eSPhilip Paeps 
9673c838a9fSAndrew Rybchenko 		if (rxq->flush_state == SFXGE_FLUSH_PENDING) {
9683c838a9fSAndrew Rybchenko 			/* Flush timeout - neither done nor failed */
9693c838a9fSAndrew Rybchenko 			log(LOG_ERR, "%s: Cannot flush Rx queue %u\n",
9703c838a9fSAndrew Rybchenko 			    device_get_nameunit(sc->dev), index);
971e948693eSPhilip Paeps 			rxq->flush_state = SFXGE_FLUSH_DONE;
9723c838a9fSAndrew Rybchenko 		}
9733c838a9fSAndrew Rybchenko 		retry--;
9743c838a9fSAndrew Rybchenko 	}
9753c838a9fSAndrew Rybchenko 	if (rxq->flush_state == SFXGE_FLUSH_FAILED) {
9763c838a9fSAndrew Rybchenko 		log(LOG_ERR, "%s: Flushing Rx queue %u failed\n",
9773c838a9fSAndrew Rybchenko 		    device_get_nameunit(sc->dev), index);
9783c838a9fSAndrew Rybchenko 		rxq->flush_state = SFXGE_FLUSH_DONE;
9793c838a9fSAndrew Rybchenko 	}
980e948693eSPhilip Paeps 
981e948693eSPhilip Paeps 	rxq->pending = rxq->added;
982e948693eSPhilip Paeps 	sfxge_rx_qcomplete(rxq, B_TRUE);
983e948693eSPhilip Paeps 
984e948693eSPhilip Paeps 	KASSERT(rxq->completed == rxq->pending,
985e948693eSPhilip Paeps 	    ("rxq->completed != rxq->pending"));
986e948693eSPhilip Paeps 
987e948693eSPhilip Paeps 	rxq->added = 0;
9883c838a9fSAndrew Rybchenko 	rxq->pushed = 0;
989e948693eSPhilip Paeps 	rxq->pending = 0;
990e948693eSPhilip Paeps 	rxq->completed = 0;
991e948693eSPhilip Paeps 	rxq->loopback = 0;
992e948693eSPhilip Paeps 
993e948693eSPhilip Paeps 	/* Destroy the common code receive queue. */
994e948693eSPhilip Paeps 	efx_rx_qdestroy(rxq->common);
995e948693eSPhilip Paeps 
996e948693eSPhilip Paeps 	efx_sram_buf_tbl_clear(sc->enp, rxq->buf_base_id,
997385b1d8eSGeorge V. Neville-Neil 	    EFX_RXQ_NBUFS(sc->rxq_entries));
998e948693eSPhilip Paeps 
999763cab71SAndrew Rybchenko 	SFXGE_EVQ_UNLOCK(evq);
1000e948693eSPhilip Paeps }
1001e948693eSPhilip Paeps 
1002e948693eSPhilip Paeps static int
1003e948693eSPhilip Paeps sfxge_rx_qstart(struct sfxge_softc *sc, unsigned int index)
1004e948693eSPhilip Paeps {
1005e948693eSPhilip Paeps 	struct sfxge_rxq *rxq;
1006e948693eSPhilip Paeps 	efsys_mem_t *esmp;
1007e948693eSPhilip Paeps 	struct sfxge_evq *evq;
1008e948693eSPhilip Paeps 	int rc;
1009e948693eSPhilip Paeps 
10103c838a9fSAndrew Rybchenko 	SFXGE_ADAPTER_LOCK_ASSERT_OWNED(sc);
10113c838a9fSAndrew Rybchenko 
1012e948693eSPhilip Paeps 	rxq = sc->rxq[index];
1013e948693eSPhilip Paeps 	esmp = &rxq->mem;
1014e948693eSPhilip Paeps 	evq = sc->evq[index];
1015e948693eSPhilip Paeps 
1016e948693eSPhilip Paeps 	KASSERT(rxq->init_state == SFXGE_RXQ_INITIALIZED,
1017e948693eSPhilip Paeps 	    ("rxq->init_state != SFXGE_RXQ_INITIALIZED"));
1018e948693eSPhilip Paeps 	KASSERT(evq->init_state == SFXGE_EVQ_STARTED,
1019e948693eSPhilip Paeps 	    ("evq->init_state != SFXGE_EVQ_STARTED"));
1020e948693eSPhilip Paeps 
1021e948693eSPhilip Paeps 	/* Program the buffer table. */
1022e948693eSPhilip Paeps 	if ((rc = efx_sram_buf_tbl_set(sc->enp, rxq->buf_base_id, esmp,
1023385b1d8eSGeorge V. Neville-Neil 	    EFX_RXQ_NBUFS(sc->rxq_entries))) != 0)
1024385b1d8eSGeorge V. Neville-Neil 		return (rc);
1025e948693eSPhilip Paeps 
1026e948693eSPhilip Paeps 	/* Create the common code receive queue. */
102734113442SAndrew Rybchenko 	if ((rc = efx_rx_qcreate(sc->enp, index, 0, EFX_RXQ_TYPE_DEFAULT,
1028385b1d8eSGeorge V. Neville-Neil 	    esmp, sc->rxq_entries, rxq->buf_base_id, evq->common,
1029e948693eSPhilip Paeps 	    &rxq->common)) != 0)
1030e948693eSPhilip Paeps 		goto fail;
1031e948693eSPhilip Paeps 
1032763cab71SAndrew Rybchenko 	SFXGE_EVQ_LOCK(evq);
1033e948693eSPhilip Paeps 
1034e948693eSPhilip Paeps 	/* Enable the receive queue. */
1035e948693eSPhilip Paeps 	efx_rx_qenable(rxq->common);
1036e948693eSPhilip Paeps 
1037e948693eSPhilip Paeps 	rxq->init_state = SFXGE_RXQ_STARTED;
10383c838a9fSAndrew Rybchenko 	rxq->flush_state = SFXGE_FLUSH_REQUIRED;
1039e948693eSPhilip Paeps 
1040e948693eSPhilip Paeps 	/* Try to fill the queue from the pool. */
1041385b1d8eSGeorge V. Neville-Neil 	sfxge_rx_qfill(rxq, EFX_RXQ_LIMIT(sc->rxq_entries), B_FALSE);
1042e948693eSPhilip Paeps 
1043763cab71SAndrew Rybchenko 	SFXGE_EVQ_UNLOCK(evq);
1044e948693eSPhilip Paeps 
1045e948693eSPhilip Paeps 	return (0);
1046e948693eSPhilip Paeps 
1047e948693eSPhilip Paeps fail:
1048e948693eSPhilip Paeps 	efx_sram_buf_tbl_clear(sc->enp, rxq->buf_base_id,
1049385b1d8eSGeorge V. Neville-Neil 	    EFX_RXQ_NBUFS(sc->rxq_entries));
1050385b1d8eSGeorge V. Neville-Neil 	return (rc);
1051e948693eSPhilip Paeps }
1052e948693eSPhilip Paeps 
1053e948693eSPhilip Paeps void
1054e948693eSPhilip Paeps sfxge_rx_stop(struct sfxge_softc *sc)
1055e948693eSPhilip Paeps {
1056e948693eSPhilip Paeps 	int index;
1057e948693eSPhilip Paeps 
10583c838a9fSAndrew Rybchenko 	efx_mac_filter_default_rxq_clear(sc->enp);
10593c838a9fSAndrew Rybchenko 
1060e948693eSPhilip Paeps 	/* Stop the receive queue(s) */
1061133366a6SAndrew Rybchenko 	index = sc->rxq_count;
1062e948693eSPhilip Paeps 	while (--index >= 0)
1063e948693eSPhilip Paeps 		sfxge_rx_qstop(sc, index);
1064e948693eSPhilip Paeps 
1065e948693eSPhilip Paeps 	sc->rx_prefix_size = 0;
1066e948693eSPhilip Paeps 	sc->rx_buffer_size = 0;
1067e948693eSPhilip Paeps 
1068e948693eSPhilip Paeps 	efx_rx_fini(sc->enp);
1069e948693eSPhilip Paeps }
1070e948693eSPhilip Paeps 
1071e948693eSPhilip Paeps int
1072e948693eSPhilip Paeps sfxge_rx_start(struct sfxge_softc *sc)
1073e948693eSPhilip Paeps {
1074e948693eSPhilip Paeps 	struct sfxge_intr *intr;
10753c838a9fSAndrew Rybchenko 	const efx_nic_cfg_t *encp;
10763c838a9fSAndrew Rybchenko 	size_t hdrlen, align, reserved;
1077e948693eSPhilip Paeps 	int index;
1078e948693eSPhilip Paeps 	int rc;
1079e948693eSPhilip Paeps 
1080e948693eSPhilip Paeps 	intr = &sc->intr;
1081e948693eSPhilip Paeps 
1082e948693eSPhilip Paeps 	/* Initialize the common code receive module. */
1083e948693eSPhilip Paeps 	if ((rc = efx_rx_init(sc->enp)) != 0)
1084e948693eSPhilip Paeps 		return (rc);
1085e948693eSPhilip Paeps 
10863c838a9fSAndrew Rybchenko 	encp = efx_nic_cfg_get(sc->enp);
10873c838a9fSAndrew Rybchenko 	sc->rx_buffer_size = EFX_MAC_PDU(sc->ifnet->if_mtu);
10883c838a9fSAndrew Rybchenko 
1089e948693eSPhilip Paeps 	/* Calculate the receive packet buffer size. */
10903c838a9fSAndrew Rybchenko 	sc->rx_prefix_size = encp->enc_rx_prefix_size;
10913c838a9fSAndrew Rybchenko 
10923c838a9fSAndrew Rybchenko 	/* Ensure IP headers are 32bit aligned */
10933c838a9fSAndrew Rybchenko 	hdrlen = sc->rx_prefix_size + sizeof (struct ether_header);
10943c838a9fSAndrew Rybchenko 	sc->rx_buffer_align = P2ROUNDUP(hdrlen, 4) - hdrlen;
10953c838a9fSAndrew Rybchenko 
10963c838a9fSAndrew Rybchenko 	sc->rx_buffer_size += sc->rx_buffer_align;
10973c838a9fSAndrew Rybchenko 
10983c838a9fSAndrew Rybchenko 	/* Align end of packet buffer for RX DMA end padding */
10993c838a9fSAndrew Rybchenko 	align = MAX(1, encp->enc_rx_buf_align_end);
11003c838a9fSAndrew Rybchenko 	EFSYS_ASSERT(ISP2(align));
11013c838a9fSAndrew Rybchenko 	sc->rx_buffer_size = P2ROUNDUP(sc->rx_buffer_size, align);
11023c838a9fSAndrew Rybchenko 
11033c838a9fSAndrew Rybchenko 	/*
11043c838a9fSAndrew Rybchenko 	 * Standard mbuf zones only guarantee pointer-size alignment;
11053c838a9fSAndrew Rybchenko 	 * we need extra space to align to the cache line
11063c838a9fSAndrew Rybchenko 	 */
11073c838a9fSAndrew Rybchenko 	reserved = sc->rx_buffer_size + CACHE_LINE_SIZE;
1108e948693eSPhilip Paeps 
1109e948693eSPhilip Paeps 	/* Select zone for packet buffers */
11103c838a9fSAndrew Rybchenko 	if (reserved <= MCLBYTES)
1111009d75e7SGleb Smirnoff 		sc->rx_cluster_size = MCLBYTES;
11123c838a9fSAndrew Rybchenko 	else if (reserved <= MJUMPAGESIZE)
1113009d75e7SGleb Smirnoff 		sc->rx_cluster_size = MJUMPAGESIZE;
11143c838a9fSAndrew Rybchenko 	else if (reserved <= MJUM9BYTES)
1115009d75e7SGleb Smirnoff 		sc->rx_cluster_size = MJUM9BYTES;
1116e948693eSPhilip Paeps 	else
1117009d75e7SGleb Smirnoff 		sc->rx_cluster_size = MJUM16BYTES;
1118e948693eSPhilip Paeps 
1119e948693eSPhilip Paeps 	/*
1120e948693eSPhilip Paeps 	 * Set up the scale table.  Enable all hash types and hash insertion.
1121e948693eSPhilip Paeps 	 */
1122e948693eSPhilip Paeps 	for (index = 0; index < SFXGE_RX_SCALE_MAX; index++)
1123133366a6SAndrew Rybchenko 		sc->rx_indir_table[index] = index % sc->rxq_count;
1124e948693eSPhilip Paeps 	if ((rc = efx_rx_scale_tbl_set(sc->enp, sc->rx_indir_table,
1125e948693eSPhilip Paeps 				       SFXGE_RX_SCALE_MAX)) != 0)
1126e948693eSPhilip Paeps 		goto fail;
1127e948693eSPhilip Paeps 	(void)efx_rx_scale_mode_set(sc->enp, EFX_RX_HASHALG_TOEPLITZ,
1128e948693eSPhilip Paeps 	    (1 << EFX_RX_HASH_IPV4) | (1 << EFX_RX_HASH_TCPIPV4) |
1129e948693eSPhilip Paeps 	    (1 << EFX_RX_HASH_IPV6) | (1 << EFX_RX_HASH_TCPIPV6), B_TRUE);
1130e948693eSPhilip Paeps 
11313c838a9fSAndrew Rybchenko 	if ((rc = efx_rx_scale_key_set(sc->enp, toep_key,
1132e948693eSPhilip Paeps 				       sizeof(toep_key))) != 0)
1133e948693eSPhilip Paeps 		goto fail;
1134e948693eSPhilip Paeps 
1135e948693eSPhilip Paeps 	/* Start the receive queue(s). */
1136133366a6SAndrew Rybchenko 	for (index = 0; index < sc->rxq_count; index++) {
1137e948693eSPhilip Paeps 		if ((rc = sfxge_rx_qstart(sc, index)) != 0)
1138e948693eSPhilip Paeps 			goto fail2;
1139e948693eSPhilip Paeps 	}
1140e948693eSPhilip Paeps 
11413c838a9fSAndrew Rybchenko 	rc = efx_mac_filter_default_rxq_set(sc->enp, sc->rxq[0]->common,
11423c838a9fSAndrew Rybchenko 					    sc->intr.n_alloc > 1);
11433c838a9fSAndrew Rybchenko 	if (rc != 0)
11443c838a9fSAndrew Rybchenko 		goto fail3;
11453c838a9fSAndrew Rybchenko 
1146e948693eSPhilip Paeps 	return (0);
1147e948693eSPhilip Paeps 
11483c838a9fSAndrew Rybchenko fail3:
1149e948693eSPhilip Paeps fail2:
1150e948693eSPhilip Paeps 	while (--index >= 0)
1151e948693eSPhilip Paeps 		sfxge_rx_qstop(sc, index);
1152e948693eSPhilip Paeps 
1153e948693eSPhilip Paeps fail:
1154e948693eSPhilip Paeps 	efx_rx_fini(sc->enp);
1155e948693eSPhilip Paeps 
1156e948693eSPhilip Paeps 	return (rc);
1157e948693eSPhilip Paeps }
1158e948693eSPhilip Paeps 
115918daa0eeSAndrew Rybchenko #ifdef SFXGE_LRO
116018daa0eeSAndrew Rybchenko 
1161e948693eSPhilip Paeps static void sfxge_lro_init(struct sfxge_rxq *rxq)
1162e948693eSPhilip Paeps {
1163e948693eSPhilip Paeps 	struct sfxge_lro_state *st = &rxq->lro;
1164e948693eSPhilip Paeps 	unsigned i;
1165e948693eSPhilip Paeps 
1166e948693eSPhilip Paeps 	st->conns_mask = lro_table_size - 1;
1167e948693eSPhilip Paeps 	KASSERT(!((st->conns_mask + 1) & st->conns_mask),
1168e948693eSPhilip Paeps 		("lro_table_size must be a power of 2"));
1169e948693eSPhilip Paeps 	st->sc = rxq->sc;
1170e948693eSPhilip Paeps 	st->conns = malloc((st->conns_mask + 1) * sizeof(st->conns[0]),
1171e948693eSPhilip Paeps 			   M_SFXGE, M_WAITOK);
1172e948693eSPhilip Paeps 	st->conns_n = malloc((st->conns_mask + 1) * sizeof(st->conns_n[0]),
1173e948693eSPhilip Paeps 			     M_SFXGE, M_WAITOK);
1174e948693eSPhilip Paeps 	for (i = 0; i <= st->conns_mask; ++i) {
1175e948693eSPhilip Paeps 		TAILQ_INIT(&st->conns[i]);
1176e948693eSPhilip Paeps 		st->conns_n[i] = 0;
1177e948693eSPhilip Paeps 	}
1178e948693eSPhilip Paeps 	LIST_INIT(&st->active_conns);
1179e948693eSPhilip Paeps 	TAILQ_INIT(&st->free_conns);
1180e948693eSPhilip Paeps }
1181e948693eSPhilip Paeps 
1182e948693eSPhilip Paeps static void sfxge_lro_fini(struct sfxge_rxq *rxq)
1183e948693eSPhilip Paeps {
1184e948693eSPhilip Paeps 	struct sfxge_lro_state *st = &rxq->lro;
1185e948693eSPhilip Paeps 	struct sfxge_lro_conn *c;
1186e948693eSPhilip Paeps 	unsigned i;
1187e948693eSPhilip Paeps 
1188e948693eSPhilip Paeps 	/* Return cleanly if sfxge_lro_init() has not been called. */
1189e948693eSPhilip Paeps 	if (st->conns == NULL)
1190e948693eSPhilip Paeps 		return;
1191e948693eSPhilip Paeps 
1192e948693eSPhilip Paeps 	KASSERT(LIST_EMPTY(&st->active_conns), ("found active connections"));
1193e948693eSPhilip Paeps 
1194e948693eSPhilip Paeps 	for (i = 0; i <= st->conns_mask; ++i) {
1195e948693eSPhilip Paeps 		while (!TAILQ_EMPTY(&st->conns[i])) {
1196e948693eSPhilip Paeps 			c = TAILQ_LAST(&st->conns[i], sfxge_lro_tailq);
1197e948693eSPhilip Paeps 			sfxge_lro_drop(rxq, c);
1198e948693eSPhilip Paeps 		}
1199e948693eSPhilip Paeps 	}
1200e948693eSPhilip Paeps 
1201e948693eSPhilip Paeps 	while (!TAILQ_EMPTY(&st->free_conns)) {
1202e948693eSPhilip Paeps 		c = TAILQ_FIRST(&st->free_conns);
1203e948693eSPhilip Paeps 		TAILQ_REMOVE(&st->free_conns, c, link);
1204e948693eSPhilip Paeps 		KASSERT(!c->mbuf, ("found orphaned mbuf"));
1205e948693eSPhilip Paeps 		free(c, M_SFXGE);
1206e948693eSPhilip Paeps 	}
1207e948693eSPhilip Paeps 
1208e948693eSPhilip Paeps 	free(st->conns_n, M_SFXGE);
1209e948693eSPhilip Paeps 	free(st->conns, M_SFXGE);
1210e948693eSPhilip Paeps 	st->conns = NULL;
1211e948693eSPhilip Paeps }
1212e948693eSPhilip Paeps 
121318daa0eeSAndrew Rybchenko #else
121418daa0eeSAndrew Rybchenko 
121518daa0eeSAndrew Rybchenko static void
121618daa0eeSAndrew Rybchenko sfxge_lro_init(struct sfxge_rxq *rxq)
121718daa0eeSAndrew Rybchenko {
121818daa0eeSAndrew Rybchenko }
121918daa0eeSAndrew Rybchenko 
122018daa0eeSAndrew Rybchenko static void
122118daa0eeSAndrew Rybchenko sfxge_lro_fini(struct sfxge_rxq *rxq)
122218daa0eeSAndrew Rybchenko {
122318daa0eeSAndrew Rybchenko }
122418daa0eeSAndrew Rybchenko 
122518daa0eeSAndrew Rybchenko #endif	/* SFXGE_LRO */
122618daa0eeSAndrew Rybchenko 
1227e948693eSPhilip Paeps static void
1228e948693eSPhilip Paeps sfxge_rx_qfini(struct sfxge_softc *sc, unsigned int index)
1229e948693eSPhilip Paeps {
1230e948693eSPhilip Paeps 	struct sfxge_rxq *rxq;
1231e948693eSPhilip Paeps 
1232e948693eSPhilip Paeps 	rxq = sc->rxq[index];
1233e948693eSPhilip Paeps 
1234e948693eSPhilip Paeps 	KASSERT(rxq->init_state == SFXGE_RXQ_INITIALIZED,
1235e948693eSPhilip Paeps 	    ("rxq->init_state != SFXGE_RXQ_INITIALIZED"));
1236e948693eSPhilip Paeps 
1237e948693eSPhilip Paeps 	/* Free the context array and the flow table. */
1238e948693eSPhilip Paeps 	free(rxq->queue, M_SFXGE);
1239e948693eSPhilip Paeps 	sfxge_lro_fini(rxq);
1240e948693eSPhilip Paeps 
1241e948693eSPhilip Paeps 	/* Release DMA memory. */
1242e948693eSPhilip Paeps 	sfxge_dma_free(&rxq->mem);
1243e948693eSPhilip Paeps 
1244e948693eSPhilip Paeps 	sc->rxq[index] = NULL;
1245e948693eSPhilip Paeps 
1246e948693eSPhilip Paeps 	free(rxq, M_SFXGE);
1247e948693eSPhilip Paeps }
1248e948693eSPhilip Paeps 
1249e948693eSPhilip Paeps static int
1250e948693eSPhilip Paeps sfxge_rx_qinit(struct sfxge_softc *sc, unsigned int index)
1251e948693eSPhilip Paeps {
1252e948693eSPhilip Paeps 	struct sfxge_rxq *rxq;
1253e948693eSPhilip Paeps 	struct sfxge_evq *evq;
1254e948693eSPhilip Paeps 	efsys_mem_t *esmp;
1255e948693eSPhilip Paeps 	int rc;
1256e948693eSPhilip Paeps 
1257133366a6SAndrew Rybchenko 	KASSERT(index < sc->rxq_count, ("index >= %d", sc->rxq_count));
1258e948693eSPhilip Paeps 
1259e948693eSPhilip Paeps 	rxq = malloc(sizeof(struct sfxge_rxq), M_SFXGE, M_ZERO | M_WAITOK);
1260e948693eSPhilip Paeps 	rxq->sc = sc;
1261e948693eSPhilip Paeps 	rxq->index = index;
1262385b1d8eSGeorge V. Neville-Neil 	rxq->entries = sc->rxq_entries;
1263385b1d8eSGeorge V. Neville-Neil 	rxq->ptr_mask = rxq->entries - 1;
1264385b1d8eSGeorge V. Neville-Neil 	rxq->refill_threshold = RX_REFILL_THRESHOLD(rxq->entries);
1265e948693eSPhilip Paeps 
1266e948693eSPhilip Paeps 	sc->rxq[index] = rxq;
1267e948693eSPhilip Paeps 	esmp = &rxq->mem;
1268e948693eSPhilip Paeps 
1269e948693eSPhilip Paeps 	evq = sc->evq[index];
1270e948693eSPhilip Paeps 
1271e948693eSPhilip Paeps 	/* Allocate and zero DMA space. */
1272385b1d8eSGeorge V. Neville-Neil 	if ((rc = sfxge_dma_alloc(sc, EFX_RXQ_SIZE(sc->rxq_entries), esmp)) != 0)
1273e948693eSPhilip Paeps 		return (rc);
1274e948693eSPhilip Paeps 
1275e948693eSPhilip Paeps 	/* Allocate buffer table entries. */
1276385b1d8eSGeorge V. Neville-Neil 	sfxge_sram_buf_tbl_alloc(sc, EFX_RXQ_NBUFS(sc->rxq_entries),
1277e948693eSPhilip Paeps 				 &rxq->buf_base_id);
1278e948693eSPhilip Paeps 
1279e948693eSPhilip Paeps 	/* Allocate the context array and the flow table. */
1280385b1d8eSGeorge V. Neville-Neil 	rxq->queue = malloc(sizeof(struct sfxge_rx_sw_desc) * sc->rxq_entries,
1281e948693eSPhilip Paeps 	    M_SFXGE, M_WAITOK | M_ZERO);
1282e948693eSPhilip Paeps 	sfxge_lro_init(rxq);
1283e948693eSPhilip Paeps 
1284fd90e2edSJung-uk Kim 	callout_init(&rxq->refill_callout, 1);
1285e948693eSPhilip Paeps 
1286e948693eSPhilip Paeps 	rxq->init_state = SFXGE_RXQ_INITIALIZED;
1287e948693eSPhilip Paeps 
1288e948693eSPhilip Paeps 	return (0);
1289e948693eSPhilip Paeps }
1290e948693eSPhilip Paeps 
1291e948693eSPhilip Paeps static const struct {
1292e948693eSPhilip Paeps 	const char *name;
1293e948693eSPhilip Paeps 	size_t offset;
1294e948693eSPhilip Paeps } sfxge_rx_stats[] = {
1295e948693eSPhilip Paeps #define	SFXGE_RX_STAT(name, member) \
1296e948693eSPhilip Paeps 	{ #name, offsetof(struct sfxge_rxq, member) }
129718daa0eeSAndrew Rybchenko #ifdef SFXGE_LRO
1298e948693eSPhilip Paeps 	SFXGE_RX_STAT(lro_merges, lro.n_merges),
1299e948693eSPhilip Paeps 	SFXGE_RX_STAT(lro_bursts, lro.n_bursts),
1300e948693eSPhilip Paeps 	SFXGE_RX_STAT(lro_slow_start, lro.n_slow_start),
1301e948693eSPhilip Paeps 	SFXGE_RX_STAT(lro_misorder, lro.n_misorder),
1302e948693eSPhilip Paeps 	SFXGE_RX_STAT(lro_too_many, lro.n_too_many),
1303e948693eSPhilip Paeps 	SFXGE_RX_STAT(lro_new_stream, lro.n_new_stream),
1304e948693eSPhilip Paeps 	SFXGE_RX_STAT(lro_drop_idle, lro.n_drop_idle),
1305e948693eSPhilip Paeps 	SFXGE_RX_STAT(lro_drop_closed, lro.n_drop_closed)
130618daa0eeSAndrew Rybchenko #endif
1307e948693eSPhilip Paeps };
1308e948693eSPhilip Paeps 
1309e948693eSPhilip Paeps static int
1310e948693eSPhilip Paeps sfxge_rx_stat_handler(SYSCTL_HANDLER_ARGS)
1311e948693eSPhilip Paeps {
1312e948693eSPhilip Paeps 	struct sfxge_softc *sc = arg1;
1313e948693eSPhilip Paeps 	unsigned int id = arg2;
1314e948693eSPhilip Paeps 	unsigned int sum, index;
1315e948693eSPhilip Paeps 
1316e948693eSPhilip Paeps 	/* Sum across all RX queues */
1317e948693eSPhilip Paeps 	sum = 0;
1318133366a6SAndrew Rybchenko 	for (index = 0; index < sc->rxq_count; index++)
1319e948693eSPhilip Paeps 		sum += *(unsigned int *)((caddr_t)sc->rxq[index] +
1320e948693eSPhilip Paeps 					 sfxge_rx_stats[id].offset);
1321e948693eSPhilip Paeps 
1322b7b0edd1SGeorge V. Neville-Neil 	return (SYSCTL_OUT(req, &sum, sizeof(sum)));
1323e948693eSPhilip Paeps }
1324e948693eSPhilip Paeps 
1325e948693eSPhilip Paeps static void
1326e948693eSPhilip Paeps sfxge_rx_stat_init(struct sfxge_softc *sc)
1327e948693eSPhilip Paeps {
1328e948693eSPhilip Paeps 	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev);
1329e948693eSPhilip Paeps 	struct sysctl_oid_list *stat_list;
1330e948693eSPhilip Paeps 	unsigned int id;
1331e948693eSPhilip Paeps 
1332e948693eSPhilip Paeps 	stat_list = SYSCTL_CHILDREN(sc->stats_node);
1333e948693eSPhilip Paeps 
1334612d8e28SAndrew Rybchenko 	for (id = 0; id < nitems(sfxge_rx_stats); id++) {
1335e948693eSPhilip Paeps 		SYSCTL_ADD_PROC(
1336e948693eSPhilip Paeps 			ctx, stat_list,
1337e948693eSPhilip Paeps 			OID_AUTO, sfxge_rx_stats[id].name,
1338e948693eSPhilip Paeps 			CTLTYPE_UINT|CTLFLAG_RD,
1339e948693eSPhilip Paeps 			sc, id, sfxge_rx_stat_handler, "IU",
1340e948693eSPhilip Paeps 			"");
1341e948693eSPhilip Paeps 	}
1342e948693eSPhilip Paeps }
1343e948693eSPhilip Paeps 
1344e948693eSPhilip Paeps void
1345e948693eSPhilip Paeps sfxge_rx_fini(struct sfxge_softc *sc)
1346e948693eSPhilip Paeps {
1347e948693eSPhilip Paeps 	int index;
1348e948693eSPhilip Paeps 
1349133366a6SAndrew Rybchenko 	index = sc->rxq_count;
1350e948693eSPhilip Paeps 	while (--index >= 0)
1351e948693eSPhilip Paeps 		sfxge_rx_qfini(sc, index);
1352133366a6SAndrew Rybchenko 
1353133366a6SAndrew Rybchenko 	sc->rxq_count = 0;
1354e948693eSPhilip Paeps }
1355e948693eSPhilip Paeps 
1356e948693eSPhilip Paeps int
1357e948693eSPhilip Paeps sfxge_rx_init(struct sfxge_softc *sc)
1358e948693eSPhilip Paeps {
1359e948693eSPhilip Paeps 	struct sfxge_intr *intr;
1360e948693eSPhilip Paeps 	int index;
1361e948693eSPhilip Paeps 	int rc;
1362e948693eSPhilip Paeps 
136318daa0eeSAndrew Rybchenko #ifdef SFXGE_LRO
1364245d1576SAndrew Rybchenko 	if (!ISP2(lro_table_size)) {
1365245d1576SAndrew Rybchenko 		log(LOG_ERR, "%s=%u must be power of 2",
1366245d1576SAndrew Rybchenko 		    SFXGE_LRO_PARAM(table_size), lro_table_size);
1367245d1576SAndrew Rybchenko 		rc = EINVAL;
1368245d1576SAndrew Rybchenko 		goto fail_lro_table_size;
1369245d1576SAndrew Rybchenko 	}
1370245d1576SAndrew Rybchenko 
1371e948693eSPhilip Paeps 	if (lro_idle_ticks == 0)
1372e948693eSPhilip Paeps 		lro_idle_ticks = hz / 10 + 1; /* 100 ms */
137318daa0eeSAndrew Rybchenko #endif
1374e948693eSPhilip Paeps 
1375e948693eSPhilip Paeps 	intr = &sc->intr;
1376e948693eSPhilip Paeps 
1377133366a6SAndrew Rybchenko 	sc->rxq_count = intr->n_alloc;
1378133366a6SAndrew Rybchenko 
1379e948693eSPhilip Paeps 	KASSERT(intr->state == SFXGE_INTR_INITIALIZED,
1380e948693eSPhilip Paeps 	    ("intr->state != SFXGE_INTR_INITIALIZED"));
1381e948693eSPhilip Paeps 
1382e948693eSPhilip Paeps 	/* Initialize the receive queue(s) - one per interrupt. */
1383133366a6SAndrew Rybchenko 	for (index = 0; index < sc->rxq_count; index++) {
1384e948693eSPhilip Paeps 		if ((rc = sfxge_rx_qinit(sc, index)) != 0)
1385e948693eSPhilip Paeps 			goto fail;
1386e948693eSPhilip Paeps 	}
1387e948693eSPhilip Paeps 
1388e948693eSPhilip Paeps 	sfxge_rx_stat_init(sc);
1389e948693eSPhilip Paeps 
1390e948693eSPhilip Paeps 	return (0);
1391e948693eSPhilip Paeps 
1392e948693eSPhilip Paeps fail:
1393e948693eSPhilip Paeps 	/* Tear down the receive queue(s). */
1394e948693eSPhilip Paeps 	while (--index >= 0)
1395e948693eSPhilip Paeps 		sfxge_rx_qfini(sc, index);
1396e948693eSPhilip Paeps 
1397133366a6SAndrew Rybchenko 	sc->rxq_count = 0;
1398245d1576SAndrew Rybchenko 
1399245d1576SAndrew Rybchenko #ifdef SFXGE_LRO
1400245d1576SAndrew Rybchenko fail_lro_table_size:
1401245d1576SAndrew Rybchenko #endif
1402e948693eSPhilip Paeps 	return (rc);
1403e948693eSPhilip Paeps }
1404