xref: /freebsd/sys/dev/sfxge/sfxge_rx.c (revision 0fc7bdc978366abb4351b0b76b50a5848cc5d982)
1e948693eSPhilip Paeps /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
4929c7febSAndrew Rybchenko  * Copyright (c) 2010-2016 Solarflare Communications Inc.
5e948693eSPhilip Paeps  * All rights reserved.
6e948693eSPhilip Paeps  *
7e948693eSPhilip Paeps  * This software was developed in part by Philip Paeps under contract for
8e948693eSPhilip Paeps  * Solarflare Communications, Inc.
9e948693eSPhilip Paeps  *
10e948693eSPhilip Paeps  * Redistribution and use in source and binary forms, with or without
113c838a9fSAndrew Rybchenko  * modification, are permitted provided that the following conditions are met:
12e948693eSPhilip Paeps  *
133c838a9fSAndrew Rybchenko  * 1. Redistributions of source code must retain the above copyright notice,
143c838a9fSAndrew Rybchenko  *    this list of conditions and the following disclaimer.
153c838a9fSAndrew Rybchenko  * 2. Redistributions in binary form must reproduce the above copyright notice,
163c838a9fSAndrew Rybchenko  *    this list of conditions and the following disclaimer in the documentation
173c838a9fSAndrew Rybchenko  *    and/or other materials provided with the distribution.
183c838a9fSAndrew Rybchenko  *
193c838a9fSAndrew Rybchenko  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
203c838a9fSAndrew Rybchenko  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
213c838a9fSAndrew Rybchenko  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
223c838a9fSAndrew Rybchenko  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
233c838a9fSAndrew Rybchenko  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
243c838a9fSAndrew Rybchenko  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
253c838a9fSAndrew Rybchenko  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
263c838a9fSAndrew Rybchenko  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
273c838a9fSAndrew Rybchenko  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
283c838a9fSAndrew Rybchenko  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
293c838a9fSAndrew Rybchenko  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
303c838a9fSAndrew Rybchenko  *
313c838a9fSAndrew Rybchenko  * The views and conclusions contained in the software and documentation are
323c838a9fSAndrew Rybchenko  * those of the authors and should not be interpreted as representing official
333c838a9fSAndrew Rybchenko  * policies, either expressed or implied, of the FreeBSD Project.
34e948693eSPhilip Paeps  */
35e948693eSPhilip Paeps 
36e948693eSPhilip Paeps #include <sys/cdefs.h>
372da88194SAndrew Rybchenko #include "opt_rss.h"
382da88194SAndrew Rybchenko 
398ec07310SGleb Smirnoff #include <sys/param.h>
408ec07310SGleb Smirnoff #include <sys/malloc.h>
41e948693eSPhilip Paeps #include <sys/mbuf.h>
42e948693eSPhilip Paeps #include <sys/smp.h>
43e948693eSPhilip Paeps #include <sys/socket.h>
44e948693eSPhilip Paeps #include <sys/sysctl.h>
453c838a9fSAndrew Rybchenko #include <sys/syslog.h>
46e948693eSPhilip Paeps #include <sys/limits.h>
47245d1576SAndrew Rybchenko #include <sys/syslog.h>
48e948693eSPhilip Paeps 
49e948693eSPhilip Paeps #include <net/ethernet.h>
50e948693eSPhilip Paeps #include <net/if.h>
51e948693eSPhilip Paeps #include <net/if_vlan_var.h>
52e948693eSPhilip Paeps 
53e948693eSPhilip Paeps #include <netinet/in.h>
54e948693eSPhilip Paeps #include <netinet/ip.h>
55e948693eSPhilip Paeps #include <netinet/ip6.h>
56e948693eSPhilip Paeps #include <netinet/tcp.h>
57e948693eSPhilip Paeps 
58e948693eSPhilip Paeps #include <machine/in_cksum.h>
59e948693eSPhilip Paeps 
602da88194SAndrew Rybchenko #ifdef RSS
612da88194SAndrew Rybchenko #include <net/rss_config.h>
622da88194SAndrew Rybchenko #endif
632da88194SAndrew Rybchenko 
64e948693eSPhilip Paeps #include "common/efx.h"
65e948693eSPhilip Paeps 
66e948693eSPhilip Paeps #include "sfxge.h"
67e948693eSPhilip Paeps #include "sfxge_rx.h"
68e948693eSPhilip Paeps 
69385b1d8eSGeorge V. Neville-Neil #define	RX_REFILL_THRESHOLD(_entries)	(EFX_RXQ_LIMIT(_entries) * 9 / 10)
70e948693eSPhilip Paeps 
7118daa0eeSAndrew Rybchenko #ifdef SFXGE_LRO
7218daa0eeSAndrew Rybchenko 
737029da5cSPawel Biernacki SYSCTL_NODE(_hw_sfxge, OID_AUTO, lro, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
74245d1576SAndrew Rybchenko     "Large receive offload (LRO) parameters");
75245d1576SAndrew Rybchenko 
76245d1576SAndrew Rybchenko #define	SFXGE_LRO_PARAM(_param)	SFXGE_PARAM(lro._param)
77245d1576SAndrew Rybchenko 
78e948693eSPhilip Paeps /* Size of the LRO hash table.  Must be a power of 2.  A larger table
79e948693eSPhilip Paeps  * means we can accelerate a larger number of streams.
80e948693eSPhilip Paeps  */
81e948693eSPhilip Paeps static unsigned lro_table_size = 128;
82245d1576SAndrew Rybchenko TUNABLE_INT(SFXGE_LRO_PARAM(table_size), &lro_table_size);
83245d1576SAndrew Rybchenko SYSCTL_UINT(_hw_sfxge_lro, OID_AUTO, table_size, CTLFLAG_RDTUN,
84245d1576SAndrew Rybchenko 	    &lro_table_size, 0,
85245d1576SAndrew Rybchenko 	    "Size of the LRO hash table (must be a power of 2)");
86e948693eSPhilip Paeps 
87e948693eSPhilip Paeps /* Maximum length of a hash chain.  If chains get too long then the lookup
88e948693eSPhilip Paeps  * time increases and may exceed the benefit of LRO.
89e948693eSPhilip Paeps  */
90e948693eSPhilip Paeps static unsigned lro_chain_max = 20;
91245d1576SAndrew Rybchenko TUNABLE_INT(SFXGE_LRO_PARAM(chain_max), &lro_chain_max);
92245d1576SAndrew Rybchenko SYSCTL_UINT(_hw_sfxge_lro, OID_AUTO, chain_max, CTLFLAG_RDTUN,
93245d1576SAndrew Rybchenko 	    &lro_chain_max, 0,
94245d1576SAndrew Rybchenko 	    "The maximum length of a hash chain");
95e948693eSPhilip Paeps 
96e948693eSPhilip Paeps /* Maximum time (in ticks) that a connection can be idle before it's LRO
97e948693eSPhilip Paeps  * state is discarded.
98e948693eSPhilip Paeps  */
99e948693eSPhilip Paeps static unsigned lro_idle_ticks; /* initialised in sfxge_rx_init() */
100245d1576SAndrew Rybchenko TUNABLE_INT(SFXGE_LRO_PARAM(idle_ticks), &lro_idle_ticks);
101245d1576SAndrew Rybchenko SYSCTL_UINT(_hw_sfxge_lro, OID_AUTO, idle_ticks, CTLFLAG_RDTUN,
102245d1576SAndrew Rybchenko 	    &lro_idle_ticks, 0,
103245d1576SAndrew Rybchenko 	    "The maximum time (in ticks) that a connection can be idle "
104245d1576SAndrew Rybchenko 	    "before it's LRO state is discarded");
105e948693eSPhilip Paeps 
106e948693eSPhilip Paeps /* Number of packets with payload that must arrive in-order before a
107e948693eSPhilip Paeps  * connection is eligible for LRO.  The idea is we should avoid coalescing
108e948693eSPhilip Paeps  * segments when the sender is in slow-start because reducing the ACK rate
109e948693eSPhilip Paeps  * can damage performance.
110e948693eSPhilip Paeps  */
111e948693eSPhilip Paeps static int lro_slow_start_packets = 2000;
112245d1576SAndrew Rybchenko TUNABLE_INT(SFXGE_LRO_PARAM(slow_start_packets), &lro_slow_start_packets);
113245d1576SAndrew Rybchenko SYSCTL_UINT(_hw_sfxge_lro, OID_AUTO, slow_start_packets, CTLFLAG_RDTUN,
114245d1576SAndrew Rybchenko 	    &lro_slow_start_packets, 0,
115245d1576SAndrew Rybchenko 	    "Number of packets with payload that must arrive in-order before "
116245d1576SAndrew Rybchenko 	    "a connection is eligible for LRO");
117e948693eSPhilip Paeps 
118e948693eSPhilip Paeps /* Number of packets with payload that must arrive in-order following loss
119e948693eSPhilip Paeps  * before a connection is eligible for LRO.  The idea is we should avoid
120e948693eSPhilip Paeps  * coalescing segments when the sender is recovering from loss, because
121e948693eSPhilip Paeps  * reducing the ACK rate can damage performance.
122e948693eSPhilip Paeps  */
123e948693eSPhilip Paeps static int lro_loss_packets = 20;
124245d1576SAndrew Rybchenko TUNABLE_INT(SFXGE_LRO_PARAM(loss_packets), &lro_loss_packets);
125245d1576SAndrew Rybchenko SYSCTL_UINT(_hw_sfxge_lro, OID_AUTO, loss_packets, CTLFLAG_RDTUN,
126245d1576SAndrew Rybchenko 	    &lro_loss_packets, 0,
127245d1576SAndrew Rybchenko 	    "Number of packets with payload that must arrive in-order "
128245d1576SAndrew Rybchenko 	    "following loss before a connection is eligible for LRO");
129e948693eSPhilip Paeps 
130e948693eSPhilip Paeps /* Flags for sfxge_lro_conn::l2_id; must not collide with EVL_VLID_MASK */
131e948693eSPhilip Paeps #define	SFXGE_LRO_L2_ID_VLAN 0x4000
132e948693eSPhilip Paeps #define	SFXGE_LRO_L2_ID_IPV6 0x8000
133e948693eSPhilip Paeps #define	SFXGE_LRO_CONN_IS_VLAN_ENCAP(c) ((c)->l2_id & SFXGE_LRO_L2_ID_VLAN)
134e948693eSPhilip Paeps #define	SFXGE_LRO_CONN_IS_TCPIPV4(c) (!((c)->l2_id & SFXGE_LRO_L2_ID_IPV6))
135e948693eSPhilip Paeps 
136e948693eSPhilip Paeps /* Compare IPv6 addresses, avoiding conditional branches */
ipv6_addr_cmp(const struct in6_addr * left,const struct in6_addr * right)1370b28bbdcSAndrew Rybchenko static unsigned long ipv6_addr_cmp(const struct in6_addr *left,
138e948693eSPhilip Paeps 				   const struct in6_addr *right)
139e948693eSPhilip Paeps {
140e948693eSPhilip Paeps #if LONG_BIT == 64
141e948693eSPhilip Paeps 	const uint64_t *left64 = (const uint64_t *)left;
142e948693eSPhilip Paeps 	const uint64_t *right64 = (const uint64_t *)right;
143e948693eSPhilip Paeps 	return (left64[0] - right64[0]) | (left64[1] - right64[1]);
144e948693eSPhilip Paeps #else
145e948693eSPhilip Paeps 	return (left->s6_addr32[0] - right->s6_addr32[0]) |
146e948693eSPhilip Paeps 	       (left->s6_addr32[1] - right->s6_addr32[1]) |
147e948693eSPhilip Paeps 	       (left->s6_addr32[2] - right->s6_addr32[2]) |
148e948693eSPhilip Paeps 	       (left->s6_addr32[3] - right->s6_addr32[3]);
149e948693eSPhilip Paeps #endif
150e948693eSPhilip Paeps }
151e948693eSPhilip Paeps 
15218daa0eeSAndrew Rybchenko #endif	/* SFXGE_LRO */
15318daa0eeSAndrew Rybchenko 
154e948693eSPhilip Paeps void
sfxge_rx_qflush_done(struct sfxge_rxq * rxq)155e948693eSPhilip Paeps sfxge_rx_qflush_done(struct sfxge_rxq *rxq)
156e948693eSPhilip Paeps {
157e948693eSPhilip Paeps 
158e948693eSPhilip Paeps 	rxq->flush_state = SFXGE_FLUSH_DONE;
159e948693eSPhilip Paeps }
160e948693eSPhilip Paeps 
161e948693eSPhilip Paeps void
sfxge_rx_qflush_failed(struct sfxge_rxq * rxq)162e948693eSPhilip Paeps sfxge_rx_qflush_failed(struct sfxge_rxq *rxq)
163e948693eSPhilip Paeps {
164e948693eSPhilip Paeps 
165e948693eSPhilip Paeps 	rxq->flush_state = SFXGE_FLUSH_FAILED;
166e948693eSPhilip Paeps }
167e948693eSPhilip Paeps 
1682da88194SAndrew Rybchenko #ifdef RSS
1692da88194SAndrew Rybchenko static uint8_t toep_key[RSS_KEYSIZE];
1702da88194SAndrew Rybchenko #else
171e948693eSPhilip Paeps static uint8_t toep_key[] = {
172e948693eSPhilip Paeps 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
173e948693eSPhilip Paeps 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
174e948693eSPhilip Paeps 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
175e948693eSPhilip Paeps 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
176e948693eSPhilip Paeps 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
177e948693eSPhilip Paeps };
1782da88194SAndrew Rybchenko #endif
179e948693eSPhilip Paeps 
180e948693eSPhilip Paeps static void
sfxge_rx_post_refill(void * arg)181e948693eSPhilip Paeps sfxge_rx_post_refill(void *arg)
182e948693eSPhilip Paeps {
183e948693eSPhilip Paeps 	struct sfxge_rxq *rxq = arg;
184e948693eSPhilip Paeps 	struct sfxge_softc *sc;
185e948693eSPhilip Paeps 	unsigned int index;
186e948693eSPhilip Paeps 	struct sfxge_evq *evq;
187e948693eSPhilip Paeps 	uint16_t magic;
188e948693eSPhilip Paeps 
189e948693eSPhilip Paeps 	sc = rxq->sc;
190e948693eSPhilip Paeps 	index = rxq->index;
191e948693eSPhilip Paeps 	evq = sc->evq[index];
19284bcd65eSAndrew Rybchenko 	magic = sfxge_sw_ev_rxq_magic(SFXGE_SW_EV_RX_QREFILL, rxq);
193e948693eSPhilip Paeps 
194e948693eSPhilip Paeps 	/* This is guaranteed due to the start/stop order of rx and ev */
195e948693eSPhilip Paeps 	KASSERT(evq->init_state == SFXGE_EVQ_STARTED,
196e948693eSPhilip Paeps 	    ("evq not started"));
197e948693eSPhilip Paeps 	KASSERT(rxq->init_state == SFXGE_RXQ_STARTED,
198e948693eSPhilip Paeps 	    ("rxq not started"));
199e948693eSPhilip Paeps 	efx_ev_qpost(evq->common, magic);
200e948693eSPhilip Paeps }
201e948693eSPhilip Paeps 
202e948693eSPhilip Paeps static void
sfxge_rx_schedule_refill(struct sfxge_rxq * rxq,boolean_t retrying)203e948693eSPhilip Paeps sfxge_rx_schedule_refill(struct sfxge_rxq *rxq, boolean_t retrying)
204e948693eSPhilip Paeps {
205e948693eSPhilip Paeps 	/* Initially retry after 100 ms, but back off in case of
206e948693eSPhilip Paeps 	 * repeated failures as we probably have to wait for the
207e948693eSPhilip Paeps 	 * administrator to raise the pool limit. */
208e948693eSPhilip Paeps 	if (retrying)
209e948693eSPhilip Paeps 		rxq->refill_delay = min(rxq->refill_delay * 2, 10 * hz);
210e948693eSPhilip Paeps 	else
211e948693eSPhilip Paeps 		rxq->refill_delay = hz / 10;
212e948693eSPhilip Paeps 
213e948693eSPhilip Paeps 	callout_reset_curcpu(&rxq->refill_callout, rxq->refill_delay,
214e948693eSPhilip Paeps 			     sfxge_rx_post_refill, rxq);
215e948693eSPhilip Paeps }
216e948693eSPhilip Paeps 
217e948693eSPhilip Paeps #define	SFXGE_REFILL_BATCH  64
218e948693eSPhilip Paeps 
219e948693eSPhilip Paeps static void
sfxge_rx_qfill(struct sfxge_rxq * rxq,unsigned int target,boolean_t retrying)220e948693eSPhilip Paeps sfxge_rx_qfill(struct sfxge_rxq *rxq, unsigned int target, boolean_t retrying)
221e948693eSPhilip Paeps {
222e948693eSPhilip Paeps 	struct sfxge_softc *sc;
223e948693eSPhilip Paeps 	unsigned int index;
224e565fa55SJohn Baldwin 	struct sfxge_evq *evq __diagused;
225e948693eSPhilip Paeps 	unsigned int batch;
226e948693eSPhilip Paeps 	unsigned int rxfill;
227e948693eSPhilip Paeps 	unsigned int mblksize;
228e948693eSPhilip Paeps 	int ntodo;
229e948693eSPhilip Paeps 	efsys_dma_addr_t addr[SFXGE_REFILL_BATCH];
230e948693eSPhilip Paeps 
231e948693eSPhilip Paeps 	sc = rxq->sc;
232e948693eSPhilip Paeps 	index = rxq->index;
233e948693eSPhilip Paeps 	evq = sc->evq[index];
234e948693eSPhilip Paeps 
235e948693eSPhilip Paeps 	prefetch_read_many(sc->enp);
236e948693eSPhilip Paeps 	prefetch_read_many(rxq->common);
237e948693eSPhilip Paeps 
238763cab71SAndrew Rybchenko 	SFXGE_EVQ_LOCK_ASSERT_OWNED(evq);
239e948693eSPhilip Paeps 
240851128b8SAndrew Rybchenko 	if (__predict_false(rxq->init_state != SFXGE_RXQ_STARTED))
241e948693eSPhilip Paeps 		return;
242e948693eSPhilip Paeps 
243e948693eSPhilip Paeps 	rxfill = rxq->added - rxq->completed;
244385b1d8eSGeorge V. Neville-Neil 	KASSERT(rxfill <= EFX_RXQ_LIMIT(rxq->entries),
245385b1d8eSGeorge V. Neville-Neil 	    ("rxfill > EFX_RXQ_LIMIT(rxq->entries)"));
246385b1d8eSGeorge V. Neville-Neil 	ntodo = min(EFX_RXQ_LIMIT(rxq->entries) - rxfill, target);
247385b1d8eSGeorge V. Neville-Neil 	KASSERT(ntodo <= EFX_RXQ_LIMIT(rxq->entries),
248385b1d8eSGeorge V. Neville-Neil 	    ("ntodo > EFX_RQX_LIMIT(rxq->entries)"));
249e948693eSPhilip Paeps 
250e948693eSPhilip Paeps 	if (ntodo == 0)
251e948693eSPhilip Paeps 		return;
252e948693eSPhilip Paeps 
253e948693eSPhilip Paeps 	batch = 0;
2543c838a9fSAndrew Rybchenko 	mblksize = sc->rx_buffer_size - sc->rx_buffer_align;
255e948693eSPhilip Paeps 	while (ntodo-- > 0) {
256e948693eSPhilip Paeps 		unsigned int id;
257e948693eSPhilip Paeps 		struct sfxge_rx_sw_desc *rx_desc;
258e948693eSPhilip Paeps 		bus_dma_segment_t seg;
259e948693eSPhilip Paeps 		struct mbuf *m;
260e948693eSPhilip Paeps 
261385b1d8eSGeorge V. Neville-Neil 		id = (rxq->added + batch) & rxq->ptr_mask;
262e948693eSPhilip Paeps 		rx_desc = &rxq->queue[id];
263e948693eSPhilip Paeps 		KASSERT(rx_desc->mbuf == NULL, ("rx_desc->mbuf != NULL"));
264e948693eSPhilip Paeps 
265e948693eSPhilip Paeps 		rx_desc->flags = EFX_DISCARD;
266009d75e7SGleb Smirnoff 		m = rx_desc->mbuf = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR,
267009d75e7SGleb Smirnoff 		    sc->rx_cluster_size);
268e948693eSPhilip Paeps 		if (m == NULL)
269e948693eSPhilip Paeps 			break;
2703c838a9fSAndrew Rybchenko 
2713c838a9fSAndrew Rybchenko 		/* m_len specifies length of area to be mapped for DMA */
2723c838a9fSAndrew Rybchenko 		m->m_len  = mblksize;
273ec30f0beSAndrew Rybchenko 		m->m_data = (caddr_t)EFX_P2ROUNDUP(uintptr_t, m->m_data,
274ec30f0beSAndrew Rybchenko 						   CACHE_LINE_SIZE);
2753c838a9fSAndrew Rybchenko 		m->m_data += sc->rx_buffer_align;
2763c838a9fSAndrew Rybchenko 
277e948693eSPhilip Paeps 		sfxge_map_mbuf_fast(rxq->mem.esm_tag, rxq->mem.esm_map, m, &seg);
278e948693eSPhilip Paeps 		addr[batch++] = seg.ds_addr;
279e948693eSPhilip Paeps 
280e948693eSPhilip Paeps 		if (batch == SFXGE_REFILL_BATCH) {
281e948693eSPhilip Paeps 			efx_rx_qpost(rxq->common, addr, mblksize, batch,
282e948693eSPhilip Paeps 			    rxq->completed, rxq->added);
283e948693eSPhilip Paeps 			rxq->added += batch;
284e948693eSPhilip Paeps 			batch = 0;
285e948693eSPhilip Paeps 		}
286e948693eSPhilip Paeps 	}
287e948693eSPhilip Paeps 
288e948693eSPhilip Paeps 	if (ntodo != 0)
289e948693eSPhilip Paeps 		sfxge_rx_schedule_refill(rxq, retrying);
290e948693eSPhilip Paeps 
291e948693eSPhilip Paeps 	if (batch != 0) {
292e948693eSPhilip Paeps 		efx_rx_qpost(rxq->common, addr, mblksize, batch,
293e948693eSPhilip Paeps 		    rxq->completed, rxq->added);
294e948693eSPhilip Paeps 		rxq->added += batch;
295e948693eSPhilip Paeps 	}
296e948693eSPhilip Paeps 
297e948693eSPhilip Paeps 	/* Make the descriptors visible to the hardware */
298e948693eSPhilip Paeps 	bus_dmamap_sync(rxq->mem.esm_tag, rxq->mem.esm_map,
299e948693eSPhilip Paeps 			BUS_DMASYNC_PREWRITE);
300e948693eSPhilip Paeps 
3013c838a9fSAndrew Rybchenko 	efx_rx_qpush(rxq->common, rxq->added, &rxq->pushed);
3023c838a9fSAndrew Rybchenko 
3033c838a9fSAndrew Rybchenko 	/* The queue could still be empty if no descriptors were actually
3043c838a9fSAndrew Rybchenko 	 * pushed, in which case there will be no event to cause the next
3053c838a9fSAndrew Rybchenko 	 * refill, so we must schedule a refill ourselves.
3063c838a9fSAndrew Rybchenko 	 */
3073c838a9fSAndrew Rybchenko 	if(rxq->pushed == rxq->completed) {
3083c838a9fSAndrew Rybchenko 		sfxge_rx_schedule_refill(rxq, retrying);
3093c838a9fSAndrew Rybchenko 	}
310e948693eSPhilip Paeps }
311e948693eSPhilip Paeps 
312e948693eSPhilip Paeps void
sfxge_rx_qrefill(struct sfxge_rxq * rxq)313e948693eSPhilip Paeps sfxge_rx_qrefill(struct sfxge_rxq *rxq)
314e948693eSPhilip Paeps {
315e948693eSPhilip Paeps 
316851128b8SAndrew Rybchenko 	if (__predict_false(rxq->init_state != SFXGE_RXQ_STARTED))
317e948693eSPhilip Paeps 		return;
318e948693eSPhilip Paeps 
319e948693eSPhilip Paeps 	/* Make sure the queue is full */
320385b1d8eSGeorge V. Neville-Neil 	sfxge_rx_qfill(rxq, EFX_RXQ_LIMIT(rxq->entries), B_TRUE);
321e948693eSPhilip Paeps }
322e948693eSPhilip Paeps 
__sfxge_rx_deliver(struct sfxge_softc * sc,struct mbuf * m)323e948693eSPhilip Paeps static void __sfxge_rx_deliver(struct sfxge_softc *sc, struct mbuf *m)
324e948693eSPhilip Paeps {
32504abf87bSJustin Hibbits 	if_t ifp = sc->ifnet;
326e948693eSPhilip Paeps 
327e948693eSPhilip Paeps 	m->m_pkthdr.rcvif = ifp;
328e948693eSPhilip Paeps 	m->m_pkthdr.csum_data = 0xffff;
32904abf87bSJustin Hibbits 	if_input(ifp, m);
330e948693eSPhilip Paeps }
331e948693eSPhilip Paeps 
332e948693eSPhilip Paeps static void
sfxge_rx_deliver(struct sfxge_rxq * rxq,struct sfxge_rx_sw_desc * rx_desc)333dae57086SAndrew Rybchenko sfxge_rx_deliver(struct sfxge_rxq *rxq, struct sfxge_rx_sw_desc *rx_desc)
334e948693eSPhilip Paeps {
335dae57086SAndrew Rybchenko 	struct sfxge_softc *sc = rxq->sc;
336e948693eSPhilip Paeps 	struct mbuf *m = rx_desc->mbuf;
337588644a4SAndrew Rybchenko 	int flags = rx_desc->flags;
338e948693eSPhilip Paeps 	int csum_flags;
339e948693eSPhilip Paeps 
340e948693eSPhilip Paeps 	/* Convert checksum flags */
341588644a4SAndrew Rybchenko 	csum_flags = (flags & EFX_CKSUM_IPV4) ?
342e948693eSPhilip Paeps 		(CSUM_IP_CHECKED | CSUM_IP_VALID) : 0;
343588644a4SAndrew Rybchenko 	if (flags & EFX_CKSUM_TCPUDP)
344e948693eSPhilip Paeps 		csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
345e948693eSPhilip Paeps 
346588644a4SAndrew Rybchenko 	if (flags & (EFX_PKT_IPV4 | EFX_PKT_IPV6)) {
3473c838a9fSAndrew Rybchenko 		m->m_pkthdr.flowid =
34854c1459cSAndrew Rybchenko 			efx_pseudo_hdr_hash_get(rxq->common,
3493c838a9fSAndrew Rybchenko 						EFX_RX_HASHALG_TOEPLITZ,
350e948693eSPhilip Paeps 						mtod(m, uint8_t *));
351dcf08586SAndrew Rybchenko 		/* The hash covers a 4-tuple for TCP only */
352dcf08586SAndrew Rybchenko 		M_HASHTYPE_SET(m,
353588644a4SAndrew Rybchenko 		    (flags & EFX_PKT_IPV4) ?
354588644a4SAndrew Rybchenko 			((flags & EFX_PKT_TCP) ?
355dcf08586SAndrew Rybchenko 			    M_HASHTYPE_RSS_TCP_IPV4 : M_HASHTYPE_RSS_IPV4) :
356588644a4SAndrew Rybchenko 			((flags & EFX_PKT_TCP) ?
357dcf08586SAndrew Rybchenko 			    M_HASHTYPE_RSS_TCP_IPV6 : M_HASHTYPE_RSS_IPV6));
358e948693eSPhilip Paeps 	}
359e948693eSPhilip Paeps 	m->m_data += sc->rx_prefix_size;
360e948693eSPhilip Paeps 	m->m_len = rx_desc->size - sc->rx_prefix_size;
361e948693eSPhilip Paeps 	m->m_pkthdr.len = m->m_len;
362e948693eSPhilip Paeps 	m->m_pkthdr.csum_flags = csum_flags;
363e948693eSPhilip Paeps 	__sfxge_rx_deliver(sc, rx_desc->mbuf);
364e948693eSPhilip Paeps 
365e948693eSPhilip Paeps 	rx_desc->flags = EFX_DISCARD;
366e948693eSPhilip Paeps 	rx_desc->mbuf = NULL;
367e948693eSPhilip Paeps }
368e948693eSPhilip Paeps 
36918daa0eeSAndrew Rybchenko #ifdef SFXGE_LRO
37018daa0eeSAndrew Rybchenko 
371e948693eSPhilip Paeps static void
sfxge_lro_deliver(struct sfxge_lro_state * st,struct sfxge_lro_conn * c)372e948693eSPhilip Paeps sfxge_lro_deliver(struct sfxge_lro_state *st, struct sfxge_lro_conn *c)
373e948693eSPhilip Paeps {
374e948693eSPhilip Paeps 	struct sfxge_softc *sc = st->sc;
375e948693eSPhilip Paeps 	struct mbuf *m = c->mbuf;
376e948693eSPhilip Paeps 	struct tcphdr *c_th;
377e948693eSPhilip Paeps 	int csum_flags;
378e948693eSPhilip Paeps 
379e948693eSPhilip Paeps 	KASSERT(m, ("no mbuf to deliver"));
380e948693eSPhilip Paeps 
381e948693eSPhilip Paeps 	++st->n_bursts;
382e948693eSPhilip Paeps 
383e948693eSPhilip Paeps 	/* Finish off packet munging and recalculate IP header checksum. */
384e948693eSPhilip Paeps 	if (SFXGE_LRO_CONN_IS_TCPIPV4(c)) {
385e948693eSPhilip Paeps 		struct ip *iph = c->nh;
386e948693eSPhilip Paeps 		iph->ip_len = htons(iph->ip_len);
387e948693eSPhilip Paeps 		iph->ip_sum = 0;
388e948693eSPhilip Paeps 		iph->ip_sum = in_cksum_hdr(iph);
389e948693eSPhilip Paeps 		c_th = (struct tcphdr *)(iph + 1);
390e948693eSPhilip Paeps 		csum_flags = (CSUM_DATA_VALID | CSUM_PSEUDO_HDR |
391e948693eSPhilip Paeps 			      CSUM_IP_CHECKED | CSUM_IP_VALID);
392e948693eSPhilip Paeps 	} else {
393e948693eSPhilip Paeps 		struct ip6_hdr *iph = c->nh;
394e948693eSPhilip Paeps 		iph->ip6_plen = htons(iph->ip6_plen);
395e948693eSPhilip Paeps 		c_th = (struct tcphdr *)(iph + 1);
396e948693eSPhilip Paeps 		csum_flags = CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
397e948693eSPhilip Paeps 	}
398e948693eSPhilip Paeps 
399e948693eSPhilip Paeps 	c_th->th_win = c->th_last->th_win;
400e948693eSPhilip Paeps 	c_th->th_ack = c->th_last->th_ack;
401e948693eSPhilip Paeps 	if (c_th->th_off == c->th_last->th_off) {
402e948693eSPhilip Paeps 		/* Copy TCP options (take care to avoid going negative). */
403e948693eSPhilip Paeps 		int optlen = ((c_th->th_off - 5) & 0xf) << 2u;
404e948693eSPhilip Paeps 		memcpy(c_th + 1, c->th_last + 1, optlen);
405e948693eSPhilip Paeps 	}
406e948693eSPhilip Paeps 
407e948693eSPhilip Paeps 	m->m_pkthdr.flowid = c->conn_hash;
408dcf08586SAndrew Rybchenko 	M_HASHTYPE_SET(m,
409dcf08586SAndrew Rybchenko 	    SFXGE_LRO_CONN_IS_TCPIPV4(c) ?
410dcf08586SAndrew Rybchenko 		M_HASHTYPE_RSS_TCP_IPV4 : M_HASHTYPE_RSS_TCP_IPV6);
411a411fe4eSAndrew Rybchenko 
412e948693eSPhilip Paeps 	m->m_pkthdr.csum_flags = csum_flags;
413e948693eSPhilip Paeps 	__sfxge_rx_deliver(sc, m);
414e948693eSPhilip Paeps 
415e948693eSPhilip Paeps 	c->mbuf = NULL;
416e948693eSPhilip Paeps 	c->delivered = 1;
417e948693eSPhilip Paeps }
418e948693eSPhilip Paeps 
419e948693eSPhilip Paeps /* Drop the given connection, and add it to the free list. */
sfxge_lro_drop(struct sfxge_rxq * rxq,struct sfxge_lro_conn * c)420e948693eSPhilip Paeps static void sfxge_lro_drop(struct sfxge_rxq *rxq, struct sfxge_lro_conn *c)
421e948693eSPhilip Paeps {
422e948693eSPhilip Paeps 	unsigned bucket;
423e948693eSPhilip Paeps 
424e948693eSPhilip Paeps 	KASSERT(!c->mbuf, ("found orphaned mbuf"));
425e948693eSPhilip Paeps 
426b7b0edd1SGeorge V. Neville-Neil 	if (c->next_buf.mbuf != NULL) {
427dae57086SAndrew Rybchenko 		sfxge_rx_deliver(rxq, &c->next_buf);
428e948693eSPhilip Paeps 		LIST_REMOVE(c, active_link);
429e948693eSPhilip Paeps 	}
430e948693eSPhilip Paeps 
431e948693eSPhilip Paeps 	bucket = c->conn_hash & rxq->lro.conns_mask;
432e948693eSPhilip Paeps 	KASSERT(rxq->lro.conns_n[bucket] > 0, ("LRO: bucket fill level wrong"));
433e948693eSPhilip Paeps 	--rxq->lro.conns_n[bucket];
434e948693eSPhilip Paeps 	TAILQ_REMOVE(&rxq->lro.conns[bucket], c, link);
435e948693eSPhilip Paeps 	TAILQ_INSERT_HEAD(&rxq->lro.free_conns, c, link);
436e948693eSPhilip Paeps }
437e948693eSPhilip Paeps 
438e948693eSPhilip Paeps /* Stop tracking connections that have gone idle in order to keep hash
439e948693eSPhilip Paeps  * chains short.
440e948693eSPhilip Paeps  */
sfxge_lro_purge_idle(struct sfxge_rxq * rxq,unsigned now)441e948693eSPhilip Paeps static void sfxge_lro_purge_idle(struct sfxge_rxq *rxq, unsigned now)
442e948693eSPhilip Paeps {
443e948693eSPhilip Paeps 	struct sfxge_lro_conn *c;
444e948693eSPhilip Paeps 	unsigned i;
445e948693eSPhilip Paeps 
446e948693eSPhilip Paeps 	KASSERT(LIST_EMPTY(&rxq->lro.active_conns),
447e948693eSPhilip Paeps 		("found active connections"));
448e948693eSPhilip Paeps 
449e948693eSPhilip Paeps 	rxq->lro.last_purge_ticks = now;
450e948693eSPhilip Paeps 	for (i = 0; i <= rxq->lro.conns_mask; ++i) {
451e948693eSPhilip Paeps 		if (TAILQ_EMPTY(&rxq->lro.conns[i]))
452e948693eSPhilip Paeps 			continue;
453e948693eSPhilip Paeps 
454e948693eSPhilip Paeps 		c = TAILQ_LAST(&rxq->lro.conns[i], sfxge_lro_tailq);
455e948693eSPhilip Paeps 		if (now - c->last_pkt_ticks > lro_idle_ticks) {
456e948693eSPhilip Paeps 			++rxq->lro.n_drop_idle;
457e948693eSPhilip Paeps 			sfxge_lro_drop(rxq, c);
458e948693eSPhilip Paeps 		}
459e948693eSPhilip Paeps 	}
460e948693eSPhilip Paeps }
461e948693eSPhilip Paeps 
462e948693eSPhilip Paeps static void
sfxge_lro_merge(struct sfxge_lro_state * st,struct sfxge_lro_conn * c,struct mbuf * mbuf,struct tcphdr * th)463e948693eSPhilip Paeps sfxge_lro_merge(struct sfxge_lro_state *st, struct sfxge_lro_conn *c,
464e948693eSPhilip Paeps 		struct mbuf *mbuf, struct tcphdr *th)
465e948693eSPhilip Paeps {
466e948693eSPhilip Paeps 	struct tcphdr *c_th;
467e948693eSPhilip Paeps 
468e948693eSPhilip Paeps 	/* Tack the new mbuf onto the chain. */
469e948693eSPhilip Paeps 	KASSERT(!mbuf->m_next, ("mbuf already chained"));
470e948693eSPhilip Paeps 	c->mbuf_tail->m_next = mbuf;
471e948693eSPhilip Paeps 	c->mbuf_tail = mbuf;
472e948693eSPhilip Paeps 
473e948693eSPhilip Paeps 	/* Increase length appropriately */
474e948693eSPhilip Paeps 	c->mbuf->m_pkthdr.len += mbuf->m_len;
475e948693eSPhilip Paeps 
476e948693eSPhilip Paeps 	/* Update the connection state flags */
477e948693eSPhilip Paeps 	if (SFXGE_LRO_CONN_IS_TCPIPV4(c)) {
478e948693eSPhilip Paeps 		struct ip *iph = c->nh;
479e948693eSPhilip Paeps 		iph->ip_len += mbuf->m_len;
480e948693eSPhilip Paeps 		c_th = (struct tcphdr *)(iph + 1);
481e948693eSPhilip Paeps 	} else {
482e948693eSPhilip Paeps 		struct ip6_hdr *iph = c->nh;
483e948693eSPhilip Paeps 		iph->ip6_plen += mbuf->m_len;
484e948693eSPhilip Paeps 		c_th = (struct tcphdr *)(iph + 1);
485e948693eSPhilip Paeps 	}
486*0fc7bdc9SRichard Scheffenegger 	tcp_set_flags(c_th, tcp_get_flags(c_th) | (tcp_get_flags(th) & TH_PUSH));
487e948693eSPhilip Paeps 	c->th_last = th;
488e948693eSPhilip Paeps 	++st->n_merges;
489e948693eSPhilip Paeps 
490e948693eSPhilip Paeps 	/* Pass packet up now if another segment could overflow the IP
491e948693eSPhilip Paeps 	 * length.
492e948693eSPhilip Paeps 	 */
493e948693eSPhilip Paeps 	if (c->mbuf->m_pkthdr.len > 65536 - 9200)
494e948693eSPhilip Paeps 		sfxge_lro_deliver(st, c);
495e948693eSPhilip Paeps }
496e948693eSPhilip Paeps 
497e948693eSPhilip Paeps static void
sfxge_lro_start(struct sfxge_lro_state * st,struct sfxge_lro_conn * c,struct mbuf * mbuf,void * nh,struct tcphdr * th)498e948693eSPhilip Paeps sfxge_lro_start(struct sfxge_lro_state *st, struct sfxge_lro_conn *c,
499e948693eSPhilip Paeps 		struct mbuf *mbuf, void *nh, struct tcphdr *th)
500e948693eSPhilip Paeps {
501e948693eSPhilip Paeps 	/* Start the chain */
502e948693eSPhilip Paeps 	c->mbuf = mbuf;
503e948693eSPhilip Paeps 	c->mbuf_tail = c->mbuf;
504e948693eSPhilip Paeps 	c->nh = nh;
505e948693eSPhilip Paeps 	c->th_last = th;
506e948693eSPhilip Paeps 
507e948693eSPhilip Paeps 	mbuf->m_pkthdr.len = mbuf->m_len;
508e948693eSPhilip Paeps 
509e948693eSPhilip Paeps 	/* Mangle header fields for later processing */
510e948693eSPhilip Paeps 	if (SFXGE_LRO_CONN_IS_TCPIPV4(c)) {
511e948693eSPhilip Paeps 		struct ip *iph = nh;
512e948693eSPhilip Paeps 		iph->ip_len = ntohs(iph->ip_len);
513e948693eSPhilip Paeps 	} else {
514e948693eSPhilip Paeps 		struct ip6_hdr *iph = nh;
515e948693eSPhilip Paeps 		iph->ip6_plen = ntohs(iph->ip6_plen);
516e948693eSPhilip Paeps 	}
517e948693eSPhilip Paeps }
518e948693eSPhilip Paeps 
519e948693eSPhilip Paeps /* Try to merge or otherwise hold or deliver (as appropriate) the
520e948693eSPhilip Paeps  * packet buffered for this connection (c->next_buf).  Return a flag
521e948693eSPhilip Paeps  * indicating whether the connection is still active for LRO purposes.
522e948693eSPhilip Paeps  */
523e948693eSPhilip Paeps static int
sfxge_lro_try_merge(struct sfxge_rxq * rxq,struct sfxge_lro_conn * c)524e948693eSPhilip Paeps sfxge_lro_try_merge(struct sfxge_rxq *rxq, struct sfxge_lro_conn *c)
525e948693eSPhilip Paeps {
526e948693eSPhilip Paeps 	struct sfxge_rx_sw_desc *rx_buf = &c->next_buf;
527e948693eSPhilip Paeps 	char *eh = c->next_eh;
528e948693eSPhilip Paeps 	int data_length, hdr_length, dont_merge;
529e948693eSPhilip Paeps 	unsigned th_seq, pkt_length;
530e948693eSPhilip Paeps 	struct tcphdr *th;
531e948693eSPhilip Paeps 	unsigned now;
532e948693eSPhilip Paeps 
533e948693eSPhilip Paeps 	if (SFXGE_LRO_CONN_IS_TCPIPV4(c)) {
534e948693eSPhilip Paeps 		struct ip *iph = c->next_nh;
535e948693eSPhilip Paeps 		th = (struct tcphdr *)(iph + 1);
536e948693eSPhilip Paeps 		pkt_length = ntohs(iph->ip_len) + (char *) iph - eh;
537e948693eSPhilip Paeps 	} else {
538e948693eSPhilip Paeps 		struct ip6_hdr *iph = c->next_nh;
539e948693eSPhilip Paeps 		th = (struct tcphdr *)(iph + 1);
540e948693eSPhilip Paeps 		pkt_length = ntohs(iph->ip6_plen) + (char *) th - eh;
541e948693eSPhilip Paeps 	}
542e948693eSPhilip Paeps 
543e948693eSPhilip Paeps 	hdr_length = (char *) th + th->th_off * 4 - eh;
544e948693eSPhilip Paeps 	data_length = (min(pkt_length, rx_buf->size - rxq->sc->rx_prefix_size) -
545e948693eSPhilip Paeps 		       hdr_length);
546e948693eSPhilip Paeps 	th_seq = ntohl(th->th_seq);
547e948693eSPhilip Paeps 	dont_merge = ((data_length <= 0)
548*0fc7bdc9SRichard Scheffenegger 		      | (tcp_get_flags(th) & (TH_URG | TH_SYN | TH_RST | TH_FIN)));
549e948693eSPhilip Paeps 
550e948693eSPhilip Paeps 	/* Check for options other than aligned timestamp. */
551e948693eSPhilip Paeps 	if (th->th_off != 5) {
552e948693eSPhilip Paeps 		const uint32_t *opt_ptr = (const uint32_t *) (th + 1);
553e948693eSPhilip Paeps 		if (th->th_off == 8 &&
554e948693eSPhilip Paeps 		    opt_ptr[0] == ntohl((TCPOPT_NOP << 24) |
555e948693eSPhilip Paeps 					(TCPOPT_NOP << 16) |
556e948693eSPhilip Paeps 					(TCPOPT_TIMESTAMP << 8) |
557e948693eSPhilip Paeps 					TCPOLEN_TIMESTAMP)) {
558e948693eSPhilip Paeps 			/* timestamp option -- okay */
559e948693eSPhilip Paeps 		} else {
560e948693eSPhilip Paeps 			dont_merge = 1;
561e948693eSPhilip Paeps 		}
562e948693eSPhilip Paeps 	}
563e948693eSPhilip Paeps 
564e948693eSPhilip Paeps 	if (__predict_false(th_seq != c->next_seq)) {
565e948693eSPhilip Paeps 		/* Out-of-order, so start counting again. */
566b7b0edd1SGeorge V. Neville-Neil 		if (c->mbuf != NULL)
567e948693eSPhilip Paeps 			sfxge_lro_deliver(&rxq->lro, c);
568e948693eSPhilip Paeps 		c->n_in_order_pkts -= lro_loss_packets;
569e948693eSPhilip Paeps 		c->next_seq = th_seq + data_length;
570e948693eSPhilip Paeps 		++rxq->lro.n_misorder;
571e948693eSPhilip Paeps 		goto deliver_buf_out;
572e948693eSPhilip Paeps 	}
573e948693eSPhilip Paeps 	c->next_seq = th_seq + data_length;
574e948693eSPhilip Paeps 
575e948693eSPhilip Paeps 	now = ticks;
576e948693eSPhilip Paeps 	if (now - c->last_pkt_ticks > lro_idle_ticks) {
577e948693eSPhilip Paeps 		++rxq->lro.n_drop_idle;
578b7b0edd1SGeorge V. Neville-Neil 		if (c->mbuf != NULL)
579e948693eSPhilip Paeps 			sfxge_lro_deliver(&rxq->lro, c);
580e948693eSPhilip Paeps 		sfxge_lro_drop(rxq, c);
581b7b0edd1SGeorge V. Neville-Neil 		return (0);
582e948693eSPhilip Paeps 	}
583e948693eSPhilip Paeps 	c->last_pkt_ticks = ticks;
584e948693eSPhilip Paeps 
585e948693eSPhilip Paeps 	if (c->n_in_order_pkts < lro_slow_start_packets) {
586e948693eSPhilip Paeps 		/* May be in slow-start, so don't merge. */
587e948693eSPhilip Paeps 		++rxq->lro.n_slow_start;
588e948693eSPhilip Paeps 		++c->n_in_order_pkts;
589e948693eSPhilip Paeps 		goto deliver_buf_out;
590e948693eSPhilip Paeps 	}
591e948693eSPhilip Paeps 
592e948693eSPhilip Paeps 	if (__predict_false(dont_merge)) {
593b7b0edd1SGeorge V. Neville-Neil 		if (c->mbuf != NULL)
594e948693eSPhilip Paeps 			sfxge_lro_deliver(&rxq->lro, c);
595*0fc7bdc9SRichard Scheffenegger 		if (tcp_get_flags(th) & (TH_FIN | TH_RST)) {
596e948693eSPhilip Paeps 			++rxq->lro.n_drop_closed;
597e948693eSPhilip Paeps 			sfxge_lro_drop(rxq, c);
598b7b0edd1SGeorge V. Neville-Neil 			return (0);
599e948693eSPhilip Paeps 		}
600e948693eSPhilip Paeps 		goto deliver_buf_out;
601e948693eSPhilip Paeps 	}
602e948693eSPhilip Paeps 
603e948693eSPhilip Paeps 	rx_buf->mbuf->m_data += rxq->sc->rx_prefix_size;
604e948693eSPhilip Paeps 
605e948693eSPhilip Paeps 	if (__predict_true(c->mbuf != NULL)) {
606e948693eSPhilip Paeps 		/* Remove headers and any padding */
607e948693eSPhilip Paeps 		rx_buf->mbuf->m_data += hdr_length;
608e948693eSPhilip Paeps 		rx_buf->mbuf->m_len = data_length;
609e948693eSPhilip Paeps 
610e948693eSPhilip Paeps 		sfxge_lro_merge(&rxq->lro, c, rx_buf->mbuf, th);
611e948693eSPhilip Paeps 	} else {
612e948693eSPhilip Paeps 		/* Remove any padding */
613e948693eSPhilip Paeps 		rx_buf->mbuf->m_len = pkt_length;
614e948693eSPhilip Paeps 
615e948693eSPhilip Paeps 		sfxge_lro_start(&rxq->lro, c, rx_buf->mbuf, c->next_nh, th);
616e948693eSPhilip Paeps 	}
617e948693eSPhilip Paeps 
618e948693eSPhilip Paeps 	rx_buf->mbuf = NULL;
619b7b0edd1SGeorge V. Neville-Neil 	return (1);
620e948693eSPhilip Paeps 
621e948693eSPhilip Paeps  deliver_buf_out:
622dae57086SAndrew Rybchenko 	sfxge_rx_deliver(rxq, rx_buf);
623b7b0edd1SGeorge V. Neville-Neil 	return (1);
624e948693eSPhilip Paeps }
625e948693eSPhilip Paeps 
sfxge_lro_new_conn(struct sfxge_lro_state * st,uint32_t conn_hash,uint16_t l2_id,void * nh,struct tcphdr * th)626e948693eSPhilip Paeps static void sfxge_lro_new_conn(struct sfxge_lro_state *st, uint32_t conn_hash,
627e948693eSPhilip Paeps 			       uint16_t l2_id, void *nh, struct tcphdr *th)
628e948693eSPhilip Paeps {
629e948693eSPhilip Paeps 	unsigned bucket = conn_hash & st->conns_mask;
630e948693eSPhilip Paeps 	struct sfxge_lro_conn *c;
631e948693eSPhilip Paeps 
632e948693eSPhilip Paeps 	if (st->conns_n[bucket] >= lro_chain_max) {
633e948693eSPhilip Paeps 		++st->n_too_many;
634e948693eSPhilip Paeps 		return;
635e948693eSPhilip Paeps 	}
636e948693eSPhilip Paeps 
637e948693eSPhilip Paeps 	if (!TAILQ_EMPTY(&st->free_conns)) {
638e948693eSPhilip Paeps 		c = TAILQ_FIRST(&st->free_conns);
639e948693eSPhilip Paeps 		TAILQ_REMOVE(&st->free_conns, c, link);
640e948693eSPhilip Paeps 	} else {
641e275c0d3SGleb Smirnoff 		c = malloc(sizeof(*c), M_SFXGE, M_NOWAIT);
642e948693eSPhilip Paeps 		if (c == NULL)
643e948693eSPhilip Paeps 			return;
644e948693eSPhilip Paeps 		c->mbuf = NULL;
645e948693eSPhilip Paeps 		c->next_buf.mbuf = NULL;
646e948693eSPhilip Paeps 	}
647e948693eSPhilip Paeps 
648e948693eSPhilip Paeps 	/* Create the connection tracking data */
649e948693eSPhilip Paeps 	++st->conns_n[bucket];
650e948693eSPhilip Paeps 	TAILQ_INSERT_HEAD(&st->conns[bucket], c, link);
651e948693eSPhilip Paeps 	c->l2_id = l2_id;
652e948693eSPhilip Paeps 	c->conn_hash = conn_hash;
653e948693eSPhilip Paeps 	c->source = th->th_sport;
654e948693eSPhilip Paeps 	c->dest = th->th_dport;
655e948693eSPhilip Paeps 	c->n_in_order_pkts = 0;
656e948693eSPhilip Paeps 	c->last_pkt_ticks = *(volatile int *)&ticks;
657e948693eSPhilip Paeps 	c->delivered = 0;
658e948693eSPhilip Paeps 	++st->n_new_stream;
659e948693eSPhilip Paeps 	/* NB. We don't initialise c->next_seq, and it doesn't matter what
660e948693eSPhilip Paeps 	 * value it has.  Most likely the next packet received for this
661e948693eSPhilip Paeps 	 * connection will not match -- no harm done.
662e948693eSPhilip Paeps 	 */
663e948693eSPhilip Paeps }
664e948693eSPhilip Paeps 
665e948693eSPhilip Paeps /* Process mbuf and decide whether to dispatch it to the stack now or
666e948693eSPhilip Paeps  * later.
667e948693eSPhilip Paeps  */
668e948693eSPhilip Paeps static void
sfxge_lro(struct sfxge_rxq * rxq,struct sfxge_rx_sw_desc * rx_buf)669e948693eSPhilip Paeps sfxge_lro(struct sfxge_rxq *rxq, struct sfxge_rx_sw_desc *rx_buf)
670e948693eSPhilip Paeps {
671e948693eSPhilip Paeps 	struct sfxge_softc *sc = rxq->sc;
672e948693eSPhilip Paeps 	struct mbuf *m = rx_buf->mbuf;
673e948693eSPhilip Paeps 	struct ether_header *eh;
674e948693eSPhilip Paeps 	struct sfxge_lro_conn *c;
675e948693eSPhilip Paeps 	uint16_t l2_id;
676e948693eSPhilip Paeps 	uint16_t l3_proto;
677e948693eSPhilip Paeps 	void *nh;
678e948693eSPhilip Paeps 	struct tcphdr *th;
679e948693eSPhilip Paeps 	uint32_t conn_hash;
680e948693eSPhilip Paeps 	unsigned bucket;
681e948693eSPhilip Paeps 
682e948693eSPhilip Paeps 	/* Get the hardware hash */
68354c1459cSAndrew Rybchenko 	conn_hash = efx_pseudo_hdr_hash_get(rxq->common,
6843c838a9fSAndrew Rybchenko 					    EFX_RX_HASHALG_TOEPLITZ,
685e948693eSPhilip Paeps 					    mtod(m, uint8_t *));
686e948693eSPhilip Paeps 
687e948693eSPhilip Paeps 	eh = (struct ether_header *)(m->m_data + sc->rx_prefix_size);
688e948693eSPhilip Paeps 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
689e948693eSPhilip Paeps 		struct ether_vlan_header *veh = (struct ether_vlan_header *)eh;
690e948693eSPhilip Paeps 		l2_id = EVL_VLANOFTAG(ntohs(veh->evl_tag)) |
691e948693eSPhilip Paeps 			SFXGE_LRO_L2_ID_VLAN;
692e948693eSPhilip Paeps 		l3_proto = veh->evl_proto;
693e948693eSPhilip Paeps 		nh = veh + 1;
694e948693eSPhilip Paeps 	} else {
695e948693eSPhilip Paeps 		l2_id = 0;
696e948693eSPhilip Paeps 		l3_proto = eh->ether_type;
697e948693eSPhilip Paeps 		nh = eh + 1;
698e948693eSPhilip Paeps 	}
699e948693eSPhilip Paeps 
700e948693eSPhilip Paeps 	/* Check whether this is a suitable packet (unfragmented
701e948693eSPhilip Paeps 	 * TCP/IPv4 or TCP/IPv6).  If so, find the TCP header and
702e948693eSPhilip Paeps 	 * length, and compute a hash if necessary.  If not, return.
703e948693eSPhilip Paeps 	 */
704e948693eSPhilip Paeps 	if (l3_proto == htons(ETHERTYPE_IP)) {
705e948693eSPhilip Paeps 		struct ip *iph = nh;
7063b3390c1SAndrew Rybchenko 
7073b3390c1SAndrew Rybchenko 		KASSERT(iph->ip_p == IPPROTO_TCP,
7083b3390c1SAndrew Rybchenko 		    ("IPv4 protocol is not TCP, but packet marker is set"));
7093b3390c1SAndrew Rybchenko 		if ((iph->ip_hl - (sizeof(*iph) >> 2u)) |
710e948693eSPhilip Paeps 		    (iph->ip_off & htons(IP_MF | IP_OFFMASK)))
711e948693eSPhilip Paeps 			goto deliver_now;
712e948693eSPhilip Paeps 		th = (struct tcphdr *)(iph + 1);
713e948693eSPhilip Paeps 	} else if (l3_proto == htons(ETHERTYPE_IPV6)) {
714e948693eSPhilip Paeps 		struct ip6_hdr *iph = nh;
7153b3390c1SAndrew Rybchenko 
7163b3390c1SAndrew Rybchenko 		KASSERT(iph->ip6_nxt == IPPROTO_TCP,
7173b3390c1SAndrew Rybchenko 		    ("IPv6 next header is not TCP, but packet marker is set"));
718e948693eSPhilip Paeps 		l2_id |= SFXGE_LRO_L2_ID_IPV6;
719e948693eSPhilip Paeps 		th = (struct tcphdr *)(iph + 1);
720e948693eSPhilip Paeps 	} else {
721e948693eSPhilip Paeps 		goto deliver_now;
722e948693eSPhilip Paeps 	}
723e948693eSPhilip Paeps 
724e948693eSPhilip Paeps 	bucket = conn_hash & rxq->lro.conns_mask;
725e948693eSPhilip Paeps 
726e948693eSPhilip Paeps 	TAILQ_FOREACH(c, &rxq->lro.conns[bucket], link) {
727e948693eSPhilip Paeps 		if ((c->l2_id - l2_id) | (c->conn_hash - conn_hash))
728e948693eSPhilip Paeps 			continue;
729e948693eSPhilip Paeps 		if ((c->source - th->th_sport) | (c->dest - th->th_dport))
730e948693eSPhilip Paeps 			continue;
731b7b0edd1SGeorge V. Neville-Neil 		if (c->mbuf != NULL) {
732e948693eSPhilip Paeps 			if (SFXGE_LRO_CONN_IS_TCPIPV4(c)) {
733e948693eSPhilip Paeps 				struct ip *c_iph, *iph = nh;
734e948693eSPhilip Paeps 				c_iph = c->nh;
735e948693eSPhilip Paeps 				if ((c_iph->ip_src.s_addr - iph->ip_src.s_addr) |
736e948693eSPhilip Paeps 				    (c_iph->ip_dst.s_addr - iph->ip_dst.s_addr))
737e948693eSPhilip Paeps 					continue;
738e948693eSPhilip Paeps 			} else {
739e948693eSPhilip Paeps 				struct ip6_hdr *c_iph, *iph = nh;
740e948693eSPhilip Paeps 				c_iph = c->nh;
741e948693eSPhilip Paeps 				if (ipv6_addr_cmp(&c_iph->ip6_src, &iph->ip6_src) |
742e948693eSPhilip Paeps 				    ipv6_addr_cmp(&c_iph->ip6_dst, &iph->ip6_dst))
743e948693eSPhilip Paeps 					continue;
744e948693eSPhilip Paeps 			}
745e948693eSPhilip Paeps 		}
746e948693eSPhilip Paeps 
747e948693eSPhilip Paeps 		/* Re-insert at head of list to reduce lookup time. */
748e948693eSPhilip Paeps 		TAILQ_REMOVE(&rxq->lro.conns[bucket], c, link);
749e948693eSPhilip Paeps 		TAILQ_INSERT_HEAD(&rxq->lro.conns[bucket], c, link);
750e948693eSPhilip Paeps 
751b7b0edd1SGeorge V. Neville-Neil 		if (c->next_buf.mbuf != NULL) {
752e948693eSPhilip Paeps 			if (!sfxge_lro_try_merge(rxq, c))
753e948693eSPhilip Paeps 				goto deliver_now;
754e948693eSPhilip Paeps 		} else {
755e948693eSPhilip Paeps 			LIST_INSERT_HEAD(&rxq->lro.active_conns, c,
756e948693eSPhilip Paeps 			    active_link);
757e948693eSPhilip Paeps 		}
758e948693eSPhilip Paeps 		c->next_buf = *rx_buf;
759e948693eSPhilip Paeps 		c->next_eh = eh;
760e948693eSPhilip Paeps 		c->next_nh = nh;
761e948693eSPhilip Paeps 
762e948693eSPhilip Paeps 		rx_buf->mbuf = NULL;
763e948693eSPhilip Paeps 		rx_buf->flags = EFX_DISCARD;
764e948693eSPhilip Paeps 		return;
765e948693eSPhilip Paeps 	}
766e948693eSPhilip Paeps 
767e948693eSPhilip Paeps 	sfxge_lro_new_conn(&rxq->lro, conn_hash, l2_id, nh, th);
768e948693eSPhilip Paeps  deliver_now:
769dae57086SAndrew Rybchenko 	sfxge_rx_deliver(rxq, rx_buf);
770e948693eSPhilip Paeps }
771e948693eSPhilip Paeps 
sfxge_lro_end_of_burst(struct sfxge_rxq * rxq)772e948693eSPhilip Paeps static void sfxge_lro_end_of_burst(struct sfxge_rxq *rxq)
773e948693eSPhilip Paeps {
774e948693eSPhilip Paeps 	struct sfxge_lro_state *st = &rxq->lro;
775e948693eSPhilip Paeps 	struct sfxge_lro_conn *c;
776e948693eSPhilip Paeps 	unsigned t;
777e948693eSPhilip Paeps 
778e948693eSPhilip Paeps 	while (!LIST_EMPTY(&st->active_conns)) {
779e948693eSPhilip Paeps 		c = LIST_FIRST(&st->active_conns);
780b7b0edd1SGeorge V. Neville-Neil 		if (!c->delivered && c->mbuf != NULL)
781e948693eSPhilip Paeps 			sfxge_lro_deliver(st, c);
782e948693eSPhilip Paeps 		if (sfxge_lro_try_merge(rxq, c)) {
783b7b0edd1SGeorge V. Neville-Neil 			if (c->mbuf != NULL)
784e948693eSPhilip Paeps 				sfxge_lro_deliver(st, c);
785e948693eSPhilip Paeps 			LIST_REMOVE(c, active_link);
786e948693eSPhilip Paeps 		}
787e948693eSPhilip Paeps 		c->delivered = 0;
788e948693eSPhilip Paeps 	}
789e948693eSPhilip Paeps 
790e948693eSPhilip Paeps 	t = *(volatile int *)&ticks;
791e948693eSPhilip Paeps 	if (__predict_false(t != st->last_purge_ticks))
792e948693eSPhilip Paeps 		sfxge_lro_purge_idle(rxq, t);
793e948693eSPhilip Paeps }
794e948693eSPhilip Paeps 
79518daa0eeSAndrew Rybchenko #else	/* !SFXGE_LRO */
79618daa0eeSAndrew Rybchenko 
79718daa0eeSAndrew Rybchenko static void
sfxge_lro(struct sfxge_rxq * rxq,struct sfxge_rx_sw_desc * rx_buf)79818daa0eeSAndrew Rybchenko sfxge_lro(struct sfxge_rxq *rxq, struct sfxge_rx_sw_desc *rx_buf)
79918daa0eeSAndrew Rybchenko {
80018daa0eeSAndrew Rybchenko }
80118daa0eeSAndrew Rybchenko 
80218daa0eeSAndrew Rybchenko static void
sfxge_lro_end_of_burst(struct sfxge_rxq * rxq)80318daa0eeSAndrew Rybchenko sfxge_lro_end_of_burst(struct sfxge_rxq *rxq)
80418daa0eeSAndrew Rybchenko {
80518daa0eeSAndrew Rybchenko }
80618daa0eeSAndrew Rybchenko 
80718daa0eeSAndrew Rybchenko #endif	/* SFXGE_LRO */
80818daa0eeSAndrew Rybchenko 
809e948693eSPhilip Paeps void
sfxge_rx_qcomplete(struct sfxge_rxq * rxq,boolean_t eop)810e948693eSPhilip Paeps sfxge_rx_qcomplete(struct sfxge_rxq *rxq, boolean_t eop)
811e948693eSPhilip Paeps {
812e948693eSPhilip Paeps 	struct sfxge_softc *sc = rxq->sc;
81304abf87bSJustin Hibbits 	int if_capenable = if_getcapenable(sc->ifnet);
814b5bae9f4SAndrew Rybchenko 	int lro_enabled = if_capenable & IFCAP_LRO;
815e948693eSPhilip Paeps 	unsigned int index;
816e565fa55SJohn Baldwin 	struct sfxge_evq *evq __diagused;
817e948693eSPhilip Paeps 	unsigned int completed;
818e948693eSPhilip Paeps 	unsigned int level;
819e948693eSPhilip Paeps 	struct mbuf *m;
820e948693eSPhilip Paeps 	struct sfxge_rx_sw_desc *prev = NULL;
821e948693eSPhilip Paeps 
822e948693eSPhilip Paeps 	index = rxq->index;
823e948693eSPhilip Paeps 	evq = sc->evq[index];
824e948693eSPhilip Paeps 
825763cab71SAndrew Rybchenko 	SFXGE_EVQ_LOCK_ASSERT_OWNED(evq);
826e948693eSPhilip Paeps 
827e948693eSPhilip Paeps 	completed = rxq->completed;
828e948693eSPhilip Paeps 	while (completed != rxq->pending) {
829e948693eSPhilip Paeps 		unsigned int id;
830e948693eSPhilip Paeps 		struct sfxge_rx_sw_desc *rx_desc;
831e948693eSPhilip Paeps 
832385b1d8eSGeorge V. Neville-Neil 		id = completed++ & rxq->ptr_mask;
833e948693eSPhilip Paeps 		rx_desc = &rxq->queue[id];
834e948693eSPhilip Paeps 		m = rx_desc->mbuf;
835e948693eSPhilip Paeps 
836851128b8SAndrew Rybchenko 		if (__predict_false(rxq->init_state != SFXGE_RXQ_STARTED))
837e948693eSPhilip Paeps 			goto discard;
838e948693eSPhilip Paeps 
839e948693eSPhilip Paeps 		if (rx_desc->flags & (EFX_ADDR_MISMATCH | EFX_DISCARD))
840e948693eSPhilip Paeps 			goto discard;
841e948693eSPhilip Paeps 
842453130d9SPedro F. Giffuni 		/* Read the length from the pseudo header if required */
8433c838a9fSAndrew Rybchenko 		if (rx_desc->flags & EFX_PKT_PREFIX_LEN) {
8443c838a9fSAndrew Rybchenko 			uint16_t tmp_size;
845e565fa55SJohn Baldwin 			int rc __diagused;
846e565fa55SJohn Baldwin 
84754c1459cSAndrew Rybchenko 			rc = efx_pseudo_hdr_pkt_length_get(rxq->common,
8483c838a9fSAndrew Rybchenko 							   mtod(m, uint8_t *),
8493c838a9fSAndrew Rybchenko 							   &tmp_size);
8503c838a9fSAndrew Rybchenko 			KASSERT(rc == 0, ("cannot get packet length: %d", rc));
8513c838a9fSAndrew Rybchenko 			rx_desc->size = (int)tmp_size + sc->rx_prefix_size;
8523c838a9fSAndrew Rybchenko 		}
8533c838a9fSAndrew Rybchenko 
854e948693eSPhilip Paeps 		prefetch_read_many(mtod(m, caddr_t));
855e948693eSPhilip Paeps 
856b5bae9f4SAndrew Rybchenko 		switch (rx_desc->flags & (EFX_PKT_IPV4 | EFX_PKT_IPV6)) {
857b5bae9f4SAndrew Rybchenko 		case EFX_PKT_IPV4:
858b5bae9f4SAndrew Rybchenko 			if (~if_capenable & IFCAP_RXCSUM)
859b5bae9f4SAndrew Rybchenko 				rx_desc->flags &=
860b5bae9f4SAndrew Rybchenko 				    ~(EFX_CKSUM_IPV4 | EFX_CKSUM_TCPUDP);
861b5bae9f4SAndrew Rybchenko 			break;
862b5bae9f4SAndrew Rybchenko 		case EFX_PKT_IPV6:
863b5bae9f4SAndrew Rybchenko 			if (~if_capenable & IFCAP_RXCSUM_IPV6)
864b5bae9f4SAndrew Rybchenko 				rx_desc->flags &= ~EFX_CKSUM_TCPUDP;
865b5bae9f4SAndrew Rybchenko 			break;
866b5bae9f4SAndrew Rybchenko 		case 0:
867e948693eSPhilip Paeps 			/* Check for loopback packets */
868b5bae9f4SAndrew Rybchenko 			{
869e948693eSPhilip Paeps 				struct ether_header *etherhp;
870e948693eSPhilip Paeps 
871e948693eSPhilip Paeps 				/*LINTED*/
872e948693eSPhilip Paeps 				etherhp = mtod(m, struct ether_header *);
873e948693eSPhilip Paeps 
874e948693eSPhilip Paeps 				if (etherhp->ether_type ==
875e948693eSPhilip Paeps 				    htons(SFXGE_ETHERTYPE_LOOPBACK)) {
876e948693eSPhilip Paeps 					EFSYS_PROBE(loopback);
877e948693eSPhilip Paeps 
878e948693eSPhilip Paeps 					rxq->loopback++;
879e948693eSPhilip Paeps 					goto discard;
880e948693eSPhilip Paeps 				}
881e948693eSPhilip Paeps 			}
882b5bae9f4SAndrew Rybchenko 			break;
883b5bae9f4SAndrew Rybchenko 		default:
884b5bae9f4SAndrew Rybchenko 			KASSERT(B_FALSE,
885b5bae9f4SAndrew Rybchenko 			    ("Rx descriptor with both IPv4 and IPv6 flags"));
886b5bae9f4SAndrew Rybchenko 			goto discard;
887b5bae9f4SAndrew Rybchenko 		}
888e948693eSPhilip Paeps 
889e948693eSPhilip Paeps 		/* Pass packet up the stack or into LRO (pipelined) */
890e948693eSPhilip Paeps 		if (prev != NULL) {
8913b3390c1SAndrew Rybchenko 			if (lro_enabled &&
8923b3390c1SAndrew Rybchenko 			    ((prev->flags & (EFX_PKT_TCP | EFX_CKSUM_TCPUDP)) ==
8933b3390c1SAndrew Rybchenko 			     (EFX_PKT_TCP | EFX_CKSUM_TCPUDP)))
894e948693eSPhilip Paeps 				sfxge_lro(rxq, prev);
895e948693eSPhilip Paeps 			else
896dae57086SAndrew Rybchenko 				sfxge_rx_deliver(rxq, prev);
897e948693eSPhilip Paeps 		}
898e948693eSPhilip Paeps 		prev = rx_desc;
899e948693eSPhilip Paeps 		continue;
900e948693eSPhilip Paeps 
901e948693eSPhilip Paeps discard:
902e948693eSPhilip Paeps 		/* Return the packet to the pool */
903e948693eSPhilip Paeps 		m_free(m);
904e948693eSPhilip Paeps 		rx_desc->mbuf = NULL;
905e948693eSPhilip Paeps 	}
906e948693eSPhilip Paeps 	rxq->completed = completed;
907e948693eSPhilip Paeps 
908e948693eSPhilip Paeps 	level = rxq->added - rxq->completed;
909e948693eSPhilip Paeps 
910e948693eSPhilip Paeps 	/* Pass last packet up the stack or into LRO */
911e948693eSPhilip Paeps 	if (prev != NULL) {
9123b3390c1SAndrew Rybchenko 		if (lro_enabled &&
9133b3390c1SAndrew Rybchenko 		    ((prev->flags & (EFX_PKT_TCP | EFX_CKSUM_TCPUDP)) ==
9143b3390c1SAndrew Rybchenko 		     (EFX_PKT_TCP | EFX_CKSUM_TCPUDP)))
915e948693eSPhilip Paeps 			sfxge_lro(rxq, prev);
916e948693eSPhilip Paeps 		else
917dae57086SAndrew Rybchenko 			sfxge_rx_deliver(rxq, prev);
918e948693eSPhilip Paeps 	}
919e948693eSPhilip Paeps 
920e948693eSPhilip Paeps 	/*
921e948693eSPhilip Paeps 	 * If there are any pending flows and this is the end of the
922e948693eSPhilip Paeps 	 * poll then they must be completed.
923e948693eSPhilip Paeps 	 */
924e948693eSPhilip Paeps 	if (eop)
925e948693eSPhilip Paeps 		sfxge_lro_end_of_burst(rxq);
926e948693eSPhilip Paeps 
927e948693eSPhilip Paeps 	/* Top up the queue if necessary */
928385b1d8eSGeorge V. Neville-Neil 	if (level < rxq->refill_threshold)
929385b1d8eSGeorge V. Neville-Neil 		sfxge_rx_qfill(rxq, EFX_RXQ_LIMIT(rxq->entries), B_FALSE);
930e948693eSPhilip Paeps }
931e948693eSPhilip Paeps 
932e948693eSPhilip Paeps static void
sfxge_rx_qstop(struct sfxge_softc * sc,unsigned int index)933e948693eSPhilip Paeps sfxge_rx_qstop(struct sfxge_softc *sc, unsigned int index)
934e948693eSPhilip Paeps {
935e948693eSPhilip Paeps 	struct sfxge_rxq *rxq;
936e948693eSPhilip Paeps 	struct sfxge_evq *evq;
937e948693eSPhilip Paeps 	unsigned int count;
9383c838a9fSAndrew Rybchenko 	unsigned int retry = 3;
9393c838a9fSAndrew Rybchenko 
9403c838a9fSAndrew Rybchenko 	SFXGE_ADAPTER_LOCK_ASSERT_OWNED(sc);
941e948693eSPhilip Paeps 
942e948693eSPhilip Paeps 	rxq = sc->rxq[index];
943e948693eSPhilip Paeps 	evq = sc->evq[index];
944e948693eSPhilip Paeps 
945763cab71SAndrew Rybchenko 	SFXGE_EVQ_LOCK(evq);
946e948693eSPhilip Paeps 
947e948693eSPhilip Paeps 	KASSERT(rxq->init_state == SFXGE_RXQ_STARTED,
948e948693eSPhilip Paeps 	    ("rxq not started"));
949e948693eSPhilip Paeps 
950e948693eSPhilip Paeps 	rxq->init_state = SFXGE_RXQ_INITIALIZED;
951e948693eSPhilip Paeps 
952e948693eSPhilip Paeps 	callout_stop(&rxq->refill_callout);
953e948693eSPhilip Paeps 
9543c838a9fSAndrew Rybchenko 	while (rxq->flush_state != SFXGE_FLUSH_DONE && retry != 0) {
955e948693eSPhilip Paeps 		rxq->flush_state = SFXGE_FLUSH_PENDING;
956e948693eSPhilip Paeps 
957763cab71SAndrew Rybchenko 		SFXGE_EVQ_UNLOCK(evq);
958e948693eSPhilip Paeps 
9593c838a9fSAndrew Rybchenko 		/* Flush the receive queue */
9603c838a9fSAndrew Rybchenko 		if (efx_rx_qflush(rxq->common) != 0) {
9613c838a9fSAndrew Rybchenko 			SFXGE_EVQ_LOCK(evq);
9623c838a9fSAndrew Rybchenko 			rxq->flush_state = SFXGE_FLUSH_FAILED;
9633c838a9fSAndrew Rybchenko 			break;
9643c838a9fSAndrew Rybchenko 		}
9653c838a9fSAndrew Rybchenko 
966e948693eSPhilip Paeps 		count = 0;
967e948693eSPhilip Paeps 		do {
968e948693eSPhilip Paeps 			/* Spin for 100 ms */
969e948693eSPhilip Paeps 			DELAY(100000);
970e948693eSPhilip Paeps 
971e948693eSPhilip Paeps 			if (rxq->flush_state != SFXGE_FLUSH_PENDING)
972e948693eSPhilip Paeps 				break;
973e948693eSPhilip Paeps 
974e948693eSPhilip Paeps 		} while (++count < 20);
975e948693eSPhilip Paeps 
976763cab71SAndrew Rybchenko 		SFXGE_EVQ_LOCK(evq);
977e948693eSPhilip Paeps 
9783c838a9fSAndrew Rybchenko 		if (rxq->flush_state == SFXGE_FLUSH_PENDING) {
9793c838a9fSAndrew Rybchenko 			/* Flush timeout - neither done nor failed */
9803c838a9fSAndrew Rybchenko 			log(LOG_ERR, "%s: Cannot flush Rx queue %u\n",
9813c838a9fSAndrew Rybchenko 			    device_get_nameunit(sc->dev), index);
982e948693eSPhilip Paeps 			rxq->flush_state = SFXGE_FLUSH_DONE;
9833c838a9fSAndrew Rybchenko 		}
9843c838a9fSAndrew Rybchenko 		retry--;
9853c838a9fSAndrew Rybchenko 	}
9863c838a9fSAndrew Rybchenko 	if (rxq->flush_state == SFXGE_FLUSH_FAILED) {
9873c838a9fSAndrew Rybchenko 		log(LOG_ERR, "%s: Flushing Rx queue %u failed\n",
9883c838a9fSAndrew Rybchenko 		    device_get_nameunit(sc->dev), index);
9893c838a9fSAndrew Rybchenko 		rxq->flush_state = SFXGE_FLUSH_DONE;
9903c838a9fSAndrew Rybchenko 	}
991e948693eSPhilip Paeps 
992e948693eSPhilip Paeps 	rxq->pending = rxq->added;
993e948693eSPhilip Paeps 	sfxge_rx_qcomplete(rxq, B_TRUE);
994e948693eSPhilip Paeps 
995e948693eSPhilip Paeps 	KASSERT(rxq->completed == rxq->pending,
996e948693eSPhilip Paeps 	    ("rxq->completed != rxq->pending"));
997e948693eSPhilip Paeps 
998e948693eSPhilip Paeps 	rxq->added = 0;
9993c838a9fSAndrew Rybchenko 	rxq->pushed = 0;
1000e948693eSPhilip Paeps 	rxq->pending = 0;
1001e948693eSPhilip Paeps 	rxq->completed = 0;
1002e948693eSPhilip Paeps 	rxq->loopback = 0;
1003e948693eSPhilip Paeps 
1004e948693eSPhilip Paeps 	/* Destroy the common code receive queue. */
1005e948693eSPhilip Paeps 	efx_rx_qdestroy(rxq->common);
1006e948693eSPhilip Paeps 
1007e948693eSPhilip Paeps 	efx_sram_buf_tbl_clear(sc->enp, rxq->buf_base_id,
1008385b1d8eSGeorge V. Neville-Neil 	    EFX_RXQ_NBUFS(sc->rxq_entries));
1009e948693eSPhilip Paeps 
1010763cab71SAndrew Rybchenko 	SFXGE_EVQ_UNLOCK(evq);
1011e948693eSPhilip Paeps }
1012e948693eSPhilip Paeps 
1013e948693eSPhilip Paeps static int
sfxge_rx_qstart(struct sfxge_softc * sc,unsigned int index)1014e948693eSPhilip Paeps sfxge_rx_qstart(struct sfxge_softc *sc, unsigned int index)
1015e948693eSPhilip Paeps {
1016e948693eSPhilip Paeps 	struct sfxge_rxq *rxq;
1017e948693eSPhilip Paeps 	efsys_mem_t *esmp;
1018e948693eSPhilip Paeps 	struct sfxge_evq *evq;
1019e948693eSPhilip Paeps 	int rc;
1020e948693eSPhilip Paeps 
10213c838a9fSAndrew Rybchenko 	SFXGE_ADAPTER_LOCK_ASSERT_OWNED(sc);
10223c838a9fSAndrew Rybchenko 
1023e948693eSPhilip Paeps 	rxq = sc->rxq[index];
1024e948693eSPhilip Paeps 	esmp = &rxq->mem;
1025e948693eSPhilip Paeps 	evq = sc->evq[index];
1026e948693eSPhilip Paeps 
1027e948693eSPhilip Paeps 	KASSERT(rxq->init_state == SFXGE_RXQ_INITIALIZED,
1028e948693eSPhilip Paeps 	    ("rxq->init_state != SFXGE_RXQ_INITIALIZED"));
1029e948693eSPhilip Paeps 	KASSERT(evq->init_state == SFXGE_EVQ_STARTED,
1030e948693eSPhilip Paeps 	    ("evq->init_state != SFXGE_EVQ_STARTED"));
1031e948693eSPhilip Paeps 
1032e948693eSPhilip Paeps 	/* Program the buffer table. */
1033e948693eSPhilip Paeps 	if ((rc = efx_sram_buf_tbl_set(sc->enp, rxq->buf_base_id, esmp,
1034385b1d8eSGeorge V. Neville-Neil 	    EFX_RXQ_NBUFS(sc->rxq_entries))) != 0)
1035385b1d8eSGeorge V. Neville-Neil 		return (rc);
1036e948693eSPhilip Paeps 
1037e948693eSPhilip Paeps 	/* Create the common code receive queue. */
103834113442SAndrew Rybchenko 	if ((rc = efx_rx_qcreate(sc->enp, index, 0, EFX_RXQ_TYPE_DEFAULT,
10399445d1c5SAndrew Rybchenko 	    esmp, sc->rxq_entries, rxq->buf_base_id, EFX_RXQ_FLAG_NONE,
10409445d1c5SAndrew Rybchenko 	    evq->common, &rxq->common)) != 0)
1041e948693eSPhilip Paeps 		goto fail;
1042e948693eSPhilip Paeps 
1043763cab71SAndrew Rybchenko 	SFXGE_EVQ_LOCK(evq);
1044e948693eSPhilip Paeps 
1045e948693eSPhilip Paeps 	/* Enable the receive queue. */
1046e948693eSPhilip Paeps 	efx_rx_qenable(rxq->common);
1047e948693eSPhilip Paeps 
1048e948693eSPhilip Paeps 	rxq->init_state = SFXGE_RXQ_STARTED;
10493c838a9fSAndrew Rybchenko 	rxq->flush_state = SFXGE_FLUSH_REQUIRED;
1050e948693eSPhilip Paeps 
1051e948693eSPhilip Paeps 	/* Try to fill the queue from the pool. */
1052385b1d8eSGeorge V. Neville-Neil 	sfxge_rx_qfill(rxq, EFX_RXQ_LIMIT(sc->rxq_entries), B_FALSE);
1053e948693eSPhilip Paeps 
1054763cab71SAndrew Rybchenko 	SFXGE_EVQ_UNLOCK(evq);
1055e948693eSPhilip Paeps 
1056e948693eSPhilip Paeps 	return (0);
1057e948693eSPhilip Paeps 
1058e948693eSPhilip Paeps fail:
1059e948693eSPhilip Paeps 	efx_sram_buf_tbl_clear(sc->enp, rxq->buf_base_id,
1060385b1d8eSGeorge V. Neville-Neil 	    EFX_RXQ_NBUFS(sc->rxq_entries));
1061385b1d8eSGeorge V. Neville-Neil 	return (rc);
1062e948693eSPhilip Paeps }
1063e948693eSPhilip Paeps 
1064e948693eSPhilip Paeps void
sfxge_rx_stop(struct sfxge_softc * sc)1065e948693eSPhilip Paeps sfxge_rx_stop(struct sfxge_softc *sc)
1066e948693eSPhilip Paeps {
1067e948693eSPhilip Paeps 	int index;
1068e948693eSPhilip Paeps 
10693c838a9fSAndrew Rybchenko 	efx_mac_filter_default_rxq_clear(sc->enp);
10703c838a9fSAndrew Rybchenko 
1071e948693eSPhilip Paeps 	/* Stop the receive queue(s) */
1072133366a6SAndrew Rybchenko 	index = sc->rxq_count;
1073e948693eSPhilip Paeps 	while (--index >= 0)
1074e948693eSPhilip Paeps 		sfxge_rx_qstop(sc, index);
1075e948693eSPhilip Paeps 
1076e948693eSPhilip Paeps 	sc->rx_prefix_size = 0;
1077e948693eSPhilip Paeps 	sc->rx_buffer_size = 0;
1078e948693eSPhilip Paeps 
1079e948693eSPhilip Paeps 	efx_rx_fini(sc->enp);
1080e948693eSPhilip Paeps }
1081e948693eSPhilip Paeps 
1082e948693eSPhilip Paeps int
sfxge_rx_start(struct sfxge_softc * sc)1083e948693eSPhilip Paeps sfxge_rx_start(struct sfxge_softc *sc)
1084e948693eSPhilip Paeps {
10853c838a9fSAndrew Rybchenko 	const efx_nic_cfg_t *encp;
10863c838a9fSAndrew Rybchenko 	size_t hdrlen, align, reserved;
1087e948693eSPhilip Paeps 	int index;
1088e948693eSPhilip Paeps 	int rc;
1089e948693eSPhilip Paeps 
1090e948693eSPhilip Paeps 	/* Initialize the common code receive module. */
1091e948693eSPhilip Paeps 	if ((rc = efx_rx_init(sc->enp)) != 0)
1092e948693eSPhilip Paeps 		return (rc);
1093e948693eSPhilip Paeps 
10943c838a9fSAndrew Rybchenko 	encp = efx_nic_cfg_get(sc->enp);
109504abf87bSJustin Hibbits 	sc->rx_buffer_size = EFX_MAC_PDU(if_getmtu(sc->ifnet));
10963c838a9fSAndrew Rybchenko 
1097e948693eSPhilip Paeps 	/* Calculate the receive packet buffer size. */
10983c838a9fSAndrew Rybchenko 	sc->rx_prefix_size = encp->enc_rx_prefix_size;
10993c838a9fSAndrew Rybchenko 
11003c838a9fSAndrew Rybchenko 	/* Ensure IP headers are 32bit aligned */
11013c838a9fSAndrew Rybchenko 	hdrlen = sc->rx_prefix_size + sizeof (struct ether_header);
1102ec30f0beSAndrew Rybchenko 	sc->rx_buffer_align = EFX_P2ROUNDUP(size_t, hdrlen, 4) - hdrlen;
11033c838a9fSAndrew Rybchenko 
11043c838a9fSAndrew Rybchenko 	sc->rx_buffer_size += sc->rx_buffer_align;
11053c838a9fSAndrew Rybchenko 
11063c838a9fSAndrew Rybchenko 	/* Align end of packet buffer for RX DMA end padding */
11073c838a9fSAndrew Rybchenko 	align = MAX(1, encp->enc_rx_buf_align_end);
11083c838a9fSAndrew Rybchenko 	EFSYS_ASSERT(ISP2(align));
1109ec30f0beSAndrew Rybchenko 	sc->rx_buffer_size = EFX_P2ROUNDUP(size_t, sc->rx_buffer_size, align);
11103c838a9fSAndrew Rybchenko 
11113c838a9fSAndrew Rybchenko 	/*
11123c838a9fSAndrew Rybchenko 	 * Standard mbuf zones only guarantee pointer-size alignment;
11133c838a9fSAndrew Rybchenko 	 * we need extra space to align to the cache line
11143c838a9fSAndrew Rybchenko 	 */
11153c838a9fSAndrew Rybchenko 	reserved = sc->rx_buffer_size + CACHE_LINE_SIZE;
1116e948693eSPhilip Paeps 
1117e948693eSPhilip Paeps 	/* Select zone for packet buffers */
11183c838a9fSAndrew Rybchenko 	if (reserved <= MCLBYTES)
1119009d75e7SGleb Smirnoff 		sc->rx_cluster_size = MCLBYTES;
11203c838a9fSAndrew Rybchenko 	else if (reserved <= MJUMPAGESIZE)
1121009d75e7SGleb Smirnoff 		sc->rx_cluster_size = MJUMPAGESIZE;
11223c838a9fSAndrew Rybchenko 	else if (reserved <= MJUM9BYTES)
1123009d75e7SGleb Smirnoff 		sc->rx_cluster_size = MJUM9BYTES;
1124e948693eSPhilip Paeps 	else
1125009d75e7SGleb Smirnoff 		sc->rx_cluster_size = MJUM16BYTES;
1126e948693eSPhilip Paeps 
1127e948693eSPhilip Paeps 	/*
1128e948693eSPhilip Paeps 	 * Set up the scale table.  Enable all hash types and hash insertion.
1129e948693eSPhilip Paeps 	 */
113044fcad03SAndrew Rybchenko 	for (index = 0; index < nitems(sc->rx_indir_table); index++)
1131f949e9f8SAndrew Rybchenko #ifdef RSS
1132f949e9f8SAndrew Rybchenko 		sc->rx_indir_table[index] =
1133f949e9f8SAndrew Rybchenko 			rss_get_indirection_to_bucket(index) % sc->rxq_count;
1134f949e9f8SAndrew Rybchenko #else
1135133366a6SAndrew Rybchenko 		sc->rx_indir_table[index] = index % sc->rxq_count;
1136f949e9f8SAndrew Rybchenko #endif
113782af879cSAndrew Rybchenko 	if ((rc = efx_rx_scale_tbl_set(sc->enp, EFX_RSS_CONTEXT_DEFAULT,
113882af879cSAndrew Rybchenko 				       sc->rx_indir_table,
113944fcad03SAndrew Rybchenko 				       nitems(sc->rx_indir_table))) != 0)
1140e948693eSPhilip Paeps 		goto fail;
114182af879cSAndrew Rybchenko 	(void)efx_rx_scale_mode_set(sc->enp, EFX_RSS_CONTEXT_DEFAULT,
114282af879cSAndrew Rybchenko 	    EFX_RX_HASHALG_TOEPLITZ,
114319734dbbSAndrew Rybchenko 	    EFX_RX_HASH_IPV4 | EFX_RX_HASH_TCPIPV4 |
114419734dbbSAndrew Rybchenko 	    EFX_RX_HASH_IPV6 | EFX_RX_HASH_TCPIPV6, B_TRUE);
1145e948693eSPhilip Paeps 
11462da88194SAndrew Rybchenko #ifdef RSS
11472da88194SAndrew Rybchenko 	rss_getkey(toep_key);
11482da88194SAndrew Rybchenko #endif
114982af879cSAndrew Rybchenko 	if ((rc = efx_rx_scale_key_set(sc->enp, EFX_RSS_CONTEXT_DEFAULT,
115082af879cSAndrew Rybchenko 				       toep_key,
1151e948693eSPhilip Paeps 				       sizeof(toep_key))) != 0)
1152e948693eSPhilip Paeps 		goto fail;
1153e948693eSPhilip Paeps 
1154e948693eSPhilip Paeps 	/* Start the receive queue(s). */
1155133366a6SAndrew Rybchenko 	for (index = 0; index < sc->rxq_count; index++) {
1156e948693eSPhilip Paeps 		if ((rc = sfxge_rx_qstart(sc, index)) != 0)
1157e948693eSPhilip Paeps 			goto fail2;
1158e948693eSPhilip Paeps 	}
1159e948693eSPhilip Paeps 
11603c838a9fSAndrew Rybchenko 	rc = efx_mac_filter_default_rxq_set(sc->enp, sc->rxq[0]->common,
11613c838a9fSAndrew Rybchenko 					    sc->intr.n_alloc > 1);
11623c838a9fSAndrew Rybchenko 	if (rc != 0)
11633c838a9fSAndrew Rybchenko 		goto fail3;
11643c838a9fSAndrew Rybchenko 
1165e948693eSPhilip Paeps 	return (0);
1166e948693eSPhilip Paeps 
11673c838a9fSAndrew Rybchenko fail3:
1168e948693eSPhilip Paeps fail2:
1169e948693eSPhilip Paeps 	while (--index >= 0)
1170e948693eSPhilip Paeps 		sfxge_rx_qstop(sc, index);
1171e948693eSPhilip Paeps 
1172e948693eSPhilip Paeps fail:
1173e948693eSPhilip Paeps 	efx_rx_fini(sc->enp);
1174e948693eSPhilip Paeps 
1175e948693eSPhilip Paeps 	return (rc);
1176e948693eSPhilip Paeps }
1177e948693eSPhilip Paeps 
117818daa0eeSAndrew Rybchenko #ifdef SFXGE_LRO
117918daa0eeSAndrew Rybchenko 
sfxge_lro_init(struct sfxge_rxq * rxq)1180e948693eSPhilip Paeps static void sfxge_lro_init(struct sfxge_rxq *rxq)
1181e948693eSPhilip Paeps {
1182e948693eSPhilip Paeps 	struct sfxge_lro_state *st = &rxq->lro;
1183e948693eSPhilip Paeps 	unsigned i;
1184e948693eSPhilip Paeps 
1185e948693eSPhilip Paeps 	st->conns_mask = lro_table_size - 1;
1186e948693eSPhilip Paeps 	KASSERT(!((st->conns_mask + 1) & st->conns_mask),
1187e948693eSPhilip Paeps 		("lro_table_size must be a power of 2"));
1188e948693eSPhilip Paeps 	st->sc = rxq->sc;
1189e948693eSPhilip Paeps 	st->conns = malloc((st->conns_mask + 1) * sizeof(st->conns[0]),
1190e948693eSPhilip Paeps 			   M_SFXGE, M_WAITOK);
1191e948693eSPhilip Paeps 	st->conns_n = malloc((st->conns_mask + 1) * sizeof(st->conns_n[0]),
1192e948693eSPhilip Paeps 			     M_SFXGE, M_WAITOK);
1193e948693eSPhilip Paeps 	for (i = 0; i <= st->conns_mask; ++i) {
1194e948693eSPhilip Paeps 		TAILQ_INIT(&st->conns[i]);
1195e948693eSPhilip Paeps 		st->conns_n[i] = 0;
1196e948693eSPhilip Paeps 	}
1197e948693eSPhilip Paeps 	LIST_INIT(&st->active_conns);
1198e948693eSPhilip Paeps 	TAILQ_INIT(&st->free_conns);
1199e948693eSPhilip Paeps }
1200e948693eSPhilip Paeps 
sfxge_lro_fini(struct sfxge_rxq * rxq)1201e948693eSPhilip Paeps static void sfxge_lro_fini(struct sfxge_rxq *rxq)
1202e948693eSPhilip Paeps {
1203e948693eSPhilip Paeps 	struct sfxge_lro_state *st = &rxq->lro;
1204e948693eSPhilip Paeps 	struct sfxge_lro_conn *c;
1205e948693eSPhilip Paeps 	unsigned i;
1206e948693eSPhilip Paeps 
1207e948693eSPhilip Paeps 	/* Return cleanly if sfxge_lro_init() has not been called. */
1208e948693eSPhilip Paeps 	if (st->conns == NULL)
1209e948693eSPhilip Paeps 		return;
1210e948693eSPhilip Paeps 
1211e948693eSPhilip Paeps 	KASSERT(LIST_EMPTY(&st->active_conns), ("found active connections"));
1212e948693eSPhilip Paeps 
1213e948693eSPhilip Paeps 	for (i = 0; i <= st->conns_mask; ++i) {
1214e948693eSPhilip Paeps 		while (!TAILQ_EMPTY(&st->conns[i])) {
1215e948693eSPhilip Paeps 			c = TAILQ_LAST(&st->conns[i], sfxge_lro_tailq);
1216e948693eSPhilip Paeps 			sfxge_lro_drop(rxq, c);
1217e948693eSPhilip Paeps 		}
1218e948693eSPhilip Paeps 	}
1219e948693eSPhilip Paeps 
1220e948693eSPhilip Paeps 	while (!TAILQ_EMPTY(&st->free_conns)) {
1221e948693eSPhilip Paeps 		c = TAILQ_FIRST(&st->free_conns);
1222e948693eSPhilip Paeps 		TAILQ_REMOVE(&st->free_conns, c, link);
1223e948693eSPhilip Paeps 		KASSERT(!c->mbuf, ("found orphaned mbuf"));
1224e948693eSPhilip Paeps 		free(c, M_SFXGE);
1225e948693eSPhilip Paeps 	}
1226e948693eSPhilip Paeps 
1227e948693eSPhilip Paeps 	free(st->conns_n, M_SFXGE);
1228e948693eSPhilip Paeps 	free(st->conns, M_SFXGE);
1229e948693eSPhilip Paeps 	st->conns = NULL;
1230e948693eSPhilip Paeps }
1231e948693eSPhilip Paeps 
123218daa0eeSAndrew Rybchenko #else
123318daa0eeSAndrew Rybchenko 
123418daa0eeSAndrew Rybchenko static void
sfxge_lro_init(struct sfxge_rxq * rxq)123518daa0eeSAndrew Rybchenko sfxge_lro_init(struct sfxge_rxq *rxq)
123618daa0eeSAndrew Rybchenko {
123718daa0eeSAndrew Rybchenko }
123818daa0eeSAndrew Rybchenko 
123918daa0eeSAndrew Rybchenko static void
sfxge_lro_fini(struct sfxge_rxq * rxq)124018daa0eeSAndrew Rybchenko sfxge_lro_fini(struct sfxge_rxq *rxq)
124118daa0eeSAndrew Rybchenko {
124218daa0eeSAndrew Rybchenko }
124318daa0eeSAndrew Rybchenko 
124418daa0eeSAndrew Rybchenko #endif	/* SFXGE_LRO */
124518daa0eeSAndrew Rybchenko 
1246e948693eSPhilip Paeps static void
sfxge_rx_qfini(struct sfxge_softc * sc,unsigned int index)1247e948693eSPhilip Paeps sfxge_rx_qfini(struct sfxge_softc *sc, unsigned int index)
1248e948693eSPhilip Paeps {
1249e948693eSPhilip Paeps 	struct sfxge_rxq *rxq;
1250e948693eSPhilip Paeps 
1251e948693eSPhilip Paeps 	rxq = sc->rxq[index];
1252e948693eSPhilip Paeps 
1253e948693eSPhilip Paeps 	KASSERT(rxq->init_state == SFXGE_RXQ_INITIALIZED,
1254e948693eSPhilip Paeps 	    ("rxq->init_state != SFXGE_RXQ_INITIALIZED"));
1255e948693eSPhilip Paeps 
1256e948693eSPhilip Paeps 	/* Free the context array and the flow table. */
1257e948693eSPhilip Paeps 	free(rxq->queue, M_SFXGE);
1258e948693eSPhilip Paeps 	sfxge_lro_fini(rxq);
1259e948693eSPhilip Paeps 
1260e948693eSPhilip Paeps 	/* Release DMA memory. */
1261e948693eSPhilip Paeps 	sfxge_dma_free(&rxq->mem);
1262e948693eSPhilip Paeps 
1263e948693eSPhilip Paeps 	sc->rxq[index] = NULL;
1264e948693eSPhilip Paeps 
1265e948693eSPhilip Paeps 	free(rxq, M_SFXGE);
1266e948693eSPhilip Paeps }
1267e948693eSPhilip Paeps 
1268e948693eSPhilip Paeps static int
sfxge_rx_qinit(struct sfxge_softc * sc,unsigned int index)1269e948693eSPhilip Paeps sfxge_rx_qinit(struct sfxge_softc *sc, unsigned int index)
1270e948693eSPhilip Paeps {
1271e948693eSPhilip Paeps 	struct sfxge_rxq *rxq;
1272e948693eSPhilip Paeps 	efsys_mem_t *esmp;
1273e948693eSPhilip Paeps 	int rc;
1274e948693eSPhilip Paeps 
1275133366a6SAndrew Rybchenko 	KASSERT(index < sc->rxq_count, ("index >= %d", sc->rxq_count));
1276e948693eSPhilip Paeps 
1277e948693eSPhilip Paeps 	rxq = malloc(sizeof(struct sfxge_rxq), M_SFXGE, M_ZERO | M_WAITOK);
1278e948693eSPhilip Paeps 	rxq->sc = sc;
1279e948693eSPhilip Paeps 	rxq->index = index;
1280385b1d8eSGeorge V. Neville-Neil 	rxq->entries = sc->rxq_entries;
1281385b1d8eSGeorge V. Neville-Neil 	rxq->ptr_mask = rxq->entries - 1;
1282385b1d8eSGeorge V. Neville-Neil 	rxq->refill_threshold = RX_REFILL_THRESHOLD(rxq->entries);
1283e948693eSPhilip Paeps 
1284e948693eSPhilip Paeps 	sc->rxq[index] = rxq;
1285e948693eSPhilip Paeps 	esmp = &rxq->mem;
1286e948693eSPhilip Paeps 
1287e948693eSPhilip Paeps 	/* Allocate and zero DMA space. */
1288385b1d8eSGeorge V. Neville-Neil 	if ((rc = sfxge_dma_alloc(sc, EFX_RXQ_SIZE(sc->rxq_entries), esmp)) != 0)
1289e948693eSPhilip Paeps 		return (rc);
1290e948693eSPhilip Paeps 
1291e948693eSPhilip Paeps 	/* Allocate buffer table entries. */
1292385b1d8eSGeorge V. Neville-Neil 	sfxge_sram_buf_tbl_alloc(sc, EFX_RXQ_NBUFS(sc->rxq_entries),
1293e948693eSPhilip Paeps 				 &rxq->buf_base_id);
1294e948693eSPhilip Paeps 
1295e948693eSPhilip Paeps 	/* Allocate the context array and the flow table. */
1296385b1d8eSGeorge V. Neville-Neil 	rxq->queue = malloc(sizeof(struct sfxge_rx_sw_desc) * sc->rxq_entries,
1297e948693eSPhilip Paeps 	    M_SFXGE, M_WAITOK | M_ZERO);
1298e948693eSPhilip Paeps 	sfxge_lro_init(rxq);
1299e948693eSPhilip Paeps 
1300fd90e2edSJung-uk Kim 	callout_init(&rxq->refill_callout, 1);
1301e948693eSPhilip Paeps 
1302e948693eSPhilip Paeps 	rxq->init_state = SFXGE_RXQ_INITIALIZED;
1303e948693eSPhilip Paeps 
1304e948693eSPhilip Paeps 	return (0);
1305e948693eSPhilip Paeps }
1306e948693eSPhilip Paeps 
1307e948693eSPhilip Paeps static const struct {
1308e948693eSPhilip Paeps 	const char *name;
1309e948693eSPhilip Paeps 	size_t offset;
1310e948693eSPhilip Paeps } sfxge_rx_stats[] = {
1311e948693eSPhilip Paeps #define	SFXGE_RX_STAT(name, member) \
1312e948693eSPhilip Paeps 	{ #name, offsetof(struct sfxge_rxq, member) }
131318daa0eeSAndrew Rybchenko #ifdef SFXGE_LRO
1314e948693eSPhilip Paeps 	SFXGE_RX_STAT(lro_merges, lro.n_merges),
1315e948693eSPhilip Paeps 	SFXGE_RX_STAT(lro_bursts, lro.n_bursts),
1316e948693eSPhilip Paeps 	SFXGE_RX_STAT(lro_slow_start, lro.n_slow_start),
1317e948693eSPhilip Paeps 	SFXGE_RX_STAT(lro_misorder, lro.n_misorder),
1318e948693eSPhilip Paeps 	SFXGE_RX_STAT(lro_too_many, lro.n_too_many),
1319e948693eSPhilip Paeps 	SFXGE_RX_STAT(lro_new_stream, lro.n_new_stream),
1320e948693eSPhilip Paeps 	SFXGE_RX_STAT(lro_drop_idle, lro.n_drop_idle),
1321e948693eSPhilip Paeps 	SFXGE_RX_STAT(lro_drop_closed, lro.n_drop_closed)
132218daa0eeSAndrew Rybchenko #endif
1323e948693eSPhilip Paeps };
1324e948693eSPhilip Paeps 
1325e948693eSPhilip Paeps static int
sfxge_rx_stat_handler(SYSCTL_HANDLER_ARGS)1326e948693eSPhilip Paeps sfxge_rx_stat_handler(SYSCTL_HANDLER_ARGS)
1327e948693eSPhilip Paeps {
1328e948693eSPhilip Paeps 	struct sfxge_softc *sc = arg1;
1329e948693eSPhilip Paeps 	unsigned int id = arg2;
1330e948693eSPhilip Paeps 	unsigned int sum, index;
1331e948693eSPhilip Paeps 
1332e948693eSPhilip Paeps 	/* Sum across all RX queues */
1333e948693eSPhilip Paeps 	sum = 0;
1334133366a6SAndrew Rybchenko 	for (index = 0; index < sc->rxq_count; index++)
1335e948693eSPhilip Paeps 		sum += *(unsigned int *)((caddr_t)sc->rxq[index] +
1336e948693eSPhilip Paeps 					 sfxge_rx_stats[id].offset);
1337e948693eSPhilip Paeps 
1338b7b0edd1SGeorge V. Neville-Neil 	return (SYSCTL_OUT(req, &sum, sizeof(sum)));
1339e948693eSPhilip Paeps }
1340e948693eSPhilip Paeps 
1341e948693eSPhilip Paeps static void
sfxge_rx_stat_init(struct sfxge_softc * sc)1342e948693eSPhilip Paeps sfxge_rx_stat_init(struct sfxge_softc *sc)
1343e948693eSPhilip Paeps {
1344e948693eSPhilip Paeps 	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev);
1345e948693eSPhilip Paeps 	struct sysctl_oid_list *stat_list;
1346e948693eSPhilip Paeps 	unsigned int id;
1347e948693eSPhilip Paeps 
1348e948693eSPhilip Paeps 	stat_list = SYSCTL_CHILDREN(sc->stats_node);
1349e948693eSPhilip Paeps 
1350612d8e28SAndrew Rybchenko 	for (id = 0; id < nitems(sfxge_rx_stats); id++) {
13517029da5cSPawel Biernacki 		SYSCTL_ADD_PROC(ctx, stat_list, OID_AUTO,
13527029da5cSPawel Biernacki 		    sfxge_rx_stats[id].name,
13537029da5cSPawel Biernacki 		    CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
13547029da5cSPawel Biernacki 		    sc, id, sfxge_rx_stat_handler, "IU", "");
1355e948693eSPhilip Paeps 	}
1356e948693eSPhilip Paeps }
1357e948693eSPhilip Paeps 
1358e948693eSPhilip Paeps void
sfxge_rx_fini(struct sfxge_softc * sc)1359e948693eSPhilip Paeps sfxge_rx_fini(struct sfxge_softc *sc)
1360e948693eSPhilip Paeps {
1361e948693eSPhilip Paeps 	int index;
1362e948693eSPhilip Paeps 
1363133366a6SAndrew Rybchenko 	index = sc->rxq_count;
1364e948693eSPhilip Paeps 	while (--index >= 0)
1365e948693eSPhilip Paeps 		sfxge_rx_qfini(sc, index);
1366133366a6SAndrew Rybchenko 
1367133366a6SAndrew Rybchenko 	sc->rxq_count = 0;
1368e948693eSPhilip Paeps }
1369e948693eSPhilip Paeps 
1370e948693eSPhilip Paeps int
sfxge_rx_init(struct sfxge_softc * sc)1371e948693eSPhilip Paeps sfxge_rx_init(struct sfxge_softc *sc)
1372e948693eSPhilip Paeps {
1373e948693eSPhilip Paeps 	struct sfxge_intr *intr;
1374e948693eSPhilip Paeps 	int index;
1375e948693eSPhilip Paeps 	int rc;
1376e948693eSPhilip Paeps 
137718daa0eeSAndrew Rybchenko #ifdef SFXGE_LRO
1378245d1576SAndrew Rybchenko 	if (!ISP2(lro_table_size)) {
1379245d1576SAndrew Rybchenko 		log(LOG_ERR, "%s=%u must be power of 2",
1380245d1576SAndrew Rybchenko 		    SFXGE_LRO_PARAM(table_size), lro_table_size);
1381245d1576SAndrew Rybchenko 		rc = EINVAL;
1382245d1576SAndrew Rybchenko 		goto fail_lro_table_size;
1383245d1576SAndrew Rybchenko 	}
1384245d1576SAndrew Rybchenko 
1385e948693eSPhilip Paeps 	if (lro_idle_ticks == 0)
1386e948693eSPhilip Paeps 		lro_idle_ticks = hz / 10 + 1; /* 100 ms */
138718daa0eeSAndrew Rybchenko #endif
1388e948693eSPhilip Paeps 
1389e948693eSPhilip Paeps 	intr = &sc->intr;
1390e948693eSPhilip Paeps 
1391133366a6SAndrew Rybchenko 	sc->rxq_count = intr->n_alloc;
1392133366a6SAndrew Rybchenko 
1393e948693eSPhilip Paeps 	KASSERT(intr->state == SFXGE_INTR_INITIALIZED,
1394e948693eSPhilip Paeps 	    ("intr->state != SFXGE_INTR_INITIALIZED"));
1395e948693eSPhilip Paeps 
1396e948693eSPhilip Paeps 	/* Initialize the receive queue(s) - one per interrupt. */
1397133366a6SAndrew Rybchenko 	for (index = 0; index < sc->rxq_count; index++) {
1398e948693eSPhilip Paeps 		if ((rc = sfxge_rx_qinit(sc, index)) != 0)
1399e948693eSPhilip Paeps 			goto fail;
1400e948693eSPhilip Paeps 	}
1401e948693eSPhilip Paeps 
1402e948693eSPhilip Paeps 	sfxge_rx_stat_init(sc);
1403e948693eSPhilip Paeps 
1404e948693eSPhilip Paeps 	return (0);
1405e948693eSPhilip Paeps 
1406e948693eSPhilip Paeps fail:
1407e948693eSPhilip Paeps 	/* Tear down the receive queue(s). */
1408e948693eSPhilip Paeps 	while (--index >= 0)
1409e948693eSPhilip Paeps 		sfxge_rx_qfini(sc, index);
1410e948693eSPhilip Paeps 
1411133366a6SAndrew Rybchenko 	sc->rxq_count = 0;
1412245d1576SAndrew Rybchenko 
1413245d1576SAndrew Rybchenko #ifdef SFXGE_LRO
1414245d1576SAndrew Rybchenko fail_lro_table_size:
1415245d1576SAndrew Rybchenko #endif
1416e948693eSPhilip Paeps 	return (rc);
1417e948693eSPhilip Paeps }
1418