xref: /freebsd/sys/dev/ixl/ixl_txrx.c (revision 56c2c47b6476354e78ce7e72767378dca559522a)
161ae650dSJack F Vogel /******************************************************************************
261ae650dSJack F Vogel 
3b6c8f260SJack F Vogel   Copyright (c) 2013-2015, Intel Corporation
461ae650dSJack F Vogel   All rights reserved.
561ae650dSJack F Vogel 
661ae650dSJack F Vogel   Redistribution and use in source and binary forms, with or without
761ae650dSJack F Vogel   modification, are permitted provided that the following conditions are met:
861ae650dSJack F Vogel 
961ae650dSJack F Vogel    1. Redistributions of source code must retain the above copyright notice,
1061ae650dSJack F Vogel       this list of conditions and the following disclaimer.
1161ae650dSJack F Vogel 
1261ae650dSJack F Vogel    2. Redistributions in binary form must reproduce the above copyright
1361ae650dSJack F Vogel       notice, this list of conditions and the following disclaimer in the
1461ae650dSJack F Vogel       documentation and/or other materials provided with the distribution.
1561ae650dSJack F Vogel 
1661ae650dSJack F Vogel    3. Neither the name of the Intel Corporation nor the names of its
1761ae650dSJack F Vogel       contributors may be used to endorse or promote products derived from
1861ae650dSJack F Vogel       this software without specific prior written permission.
1961ae650dSJack F Vogel 
2061ae650dSJack F Vogel   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2161ae650dSJack F Vogel   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2261ae650dSJack F Vogel   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2361ae650dSJack F Vogel   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2461ae650dSJack F Vogel   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2561ae650dSJack F Vogel   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2661ae650dSJack F Vogel   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2761ae650dSJack F Vogel   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2861ae650dSJack F Vogel   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2961ae650dSJack F Vogel   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3061ae650dSJack F Vogel   POSSIBILITY OF SUCH DAMAGE.
3161ae650dSJack F Vogel 
3261ae650dSJack F Vogel ******************************************************************************/
3361ae650dSJack F Vogel /*$FreeBSD$*/
3461ae650dSJack F Vogel 
3561ae650dSJack F Vogel /*
3661ae650dSJack F Vogel **	IXL driver TX/RX Routines:
3761ae650dSJack F Vogel **	    This was seperated to allow usage by
3861ae650dSJack F Vogel ** 	    both the BASE and the VF drivers.
3961ae650dSJack F Vogel */
4061ae650dSJack F Vogel 
41b6c8f260SJack F Vogel #ifndef IXL_STANDALONE_BUILD
4261ae650dSJack F Vogel #include "opt_inet.h"
4361ae650dSJack F Vogel #include "opt_inet6.h"
44df1d7a71SJack F Vogel #include "opt_rss.h"
45b6c8f260SJack F Vogel #endif
46b6c8f260SJack F Vogel 
4761ae650dSJack F Vogel #include "ixl.h"
4861ae650dSJack F Vogel 
49dcd7b3b2SJack F Vogel #ifdef RSS
50dcd7b3b2SJack F Vogel #include <net/rss_config.h>
51dcd7b3b2SJack F Vogel #endif
52dcd7b3b2SJack F Vogel 
5361ae650dSJack F Vogel /* Local Prototypes */
5461ae650dSJack F Vogel static void	ixl_rx_checksum(struct mbuf *, u32, u32, u8);
5561ae650dSJack F Vogel static void	ixl_refresh_mbufs(struct ixl_queue *, int);
5661ae650dSJack F Vogel static int      ixl_xmit(struct ixl_queue *, struct mbuf **);
5761ae650dSJack F Vogel static int	ixl_tx_setup_offload(struct ixl_queue *,
5861ae650dSJack F Vogel 		    struct mbuf *, u32 *, u32 *);
5961ae650dSJack F Vogel static bool	ixl_tso_setup(struct ixl_queue *, struct mbuf *);
6061ae650dSJack F Vogel 
6161ae650dSJack F Vogel static __inline void ixl_rx_discard(struct rx_ring *, int);
6261ae650dSJack F Vogel static __inline void ixl_rx_input(struct rx_ring *, struct ifnet *,
6361ae650dSJack F Vogel 		    struct mbuf *, u8);
6461ae650dSJack F Vogel 
65bc8b78d3SLuigi Rizzo 
6661ae650dSJack F Vogel /*
6761ae650dSJack F Vogel ** Multiqueue Transmit driver
6861ae650dSJack F Vogel */
6961ae650dSJack F Vogel int
7061ae650dSJack F Vogel ixl_mq_start(struct ifnet *ifp, struct mbuf *m)
7161ae650dSJack F Vogel {
7261ae650dSJack F Vogel 	struct ixl_vsi		*vsi = ifp->if_softc;
7361ae650dSJack F Vogel 	struct ixl_queue	*que;
7461ae650dSJack F Vogel 	struct tx_ring		*txr;
7561ae650dSJack F Vogel 	int 			err, i;
76845a028fSJack F Vogel #ifdef RSS
77845a028fSJack F Vogel 	u32			bucket_id;
78845a028fSJack F Vogel #endif
7961ae650dSJack F Vogel 
80845a028fSJack F Vogel 	/*
81845a028fSJack F Vogel 	** Which queue to use:
82845a028fSJack F Vogel 	**
83845a028fSJack F Vogel 	** When doing RSS, map it to the same outbound
84845a028fSJack F Vogel 	** queue as the incoming flow would be mapped to.
85845a028fSJack F Vogel 	** If everything is setup correctly, it should be
86845a028fSJack F Vogel 	** the same bucket that the current CPU we're on is.
87845a028fSJack F Vogel 	*/
88845a028fSJack F Vogel 	if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) {
89845a028fSJack F Vogel #ifdef  RSS
90845a028fSJack F Vogel 		if (rss_hash2bucket(m->m_pkthdr.flowid,
91845a028fSJack F Vogel 		    M_HASHTYPE_GET(m), &bucket_id) == 0) {
92845a028fSJack F Vogel 			i = bucket_id % vsi->num_queues;
93845a028fSJack F Vogel                 } else
94845a028fSJack F Vogel #endif
9561ae650dSJack F Vogel                         i = m->m_pkthdr.flowid % vsi->num_queues;
96845a028fSJack F Vogel         } else
9761ae650dSJack F Vogel 		i = curcpu % vsi->num_queues;
98845a028fSJack F Vogel 	/*
99845a028fSJack F Vogel 	** This may not be perfect, but until something
100845a028fSJack F Vogel 	** better comes along it will keep from scheduling
101845a028fSJack F Vogel 	** on stalled queues.
102845a028fSJack F Vogel 	*/
10361ae650dSJack F Vogel 	if (((1 << i) & vsi->active_queues) == 0)
10461ae650dSJack F Vogel 		i = ffsl(vsi->active_queues);
10561ae650dSJack F Vogel 
10661ae650dSJack F Vogel 	que = &vsi->queues[i];
10761ae650dSJack F Vogel 	txr = &que->txr;
10861ae650dSJack F Vogel 
10961ae650dSJack F Vogel 	err = drbr_enqueue(ifp, txr->br, m);
11061ae650dSJack F Vogel 	if (err)
11161ae650dSJack F Vogel 		return (err);
11261ae650dSJack F Vogel 	if (IXL_TX_TRYLOCK(txr)) {
11361ae650dSJack F Vogel 		ixl_mq_start_locked(ifp, txr);
11461ae650dSJack F Vogel 		IXL_TX_UNLOCK(txr);
11561ae650dSJack F Vogel 	} else
11661ae650dSJack F Vogel 		taskqueue_enqueue(que->tq, &que->tx_task);
11761ae650dSJack F Vogel 
11861ae650dSJack F Vogel 	return (0);
11961ae650dSJack F Vogel }
12061ae650dSJack F Vogel 
12161ae650dSJack F Vogel int
12261ae650dSJack F Vogel ixl_mq_start_locked(struct ifnet *ifp, struct tx_ring *txr)
12361ae650dSJack F Vogel {
12461ae650dSJack F Vogel 	struct ixl_queue	*que = txr->que;
12561ae650dSJack F Vogel 	struct ixl_vsi		*vsi = que->vsi;
12661ae650dSJack F Vogel         struct mbuf		*next;
12761ae650dSJack F Vogel         int			err = 0;
12861ae650dSJack F Vogel 
12961ae650dSJack F Vogel 
13061ae650dSJack F Vogel 	if (((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) ||
13161ae650dSJack F Vogel 	    vsi->link_active == 0)
13261ae650dSJack F Vogel 		return (ENETDOWN);
13361ae650dSJack F Vogel 
13461ae650dSJack F Vogel 	/* Process the transmit queue */
13561ae650dSJack F Vogel 	while ((next = drbr_peek(ifp, txr->br)) != NULL) {
13661ae650dSJack F Vogel 		if ((err = ixl_xmit(que, &next)) != 0) {
13761ae650dSJack F Vogel 			if (next == NULL)
13861ae650dSJack F Vogel 				drbr_advance(ifp, txr->br);
13961ae650dSJack F Vogel 			else
14061ae650dSJack F Vogel 				drbr_putback(ifp, txr->br, next);
14161ae650dSJack F Vogel 			break;
14261ae650dSJack F Vogel 		}
14361ae650dSJack F Vogel 		drbr_advance(ifp, txr->br);
14461ae650dSJack F Vogel 		/* Send a copy of the frame to the BPF listener */
14561ae650dSJack F Vogel 		ETHER_BPF_MTAP(ifp, next);
14661ae650dSJack F Vogel 		if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
14761ae650dSJack F Vogel 			break;
14861ae650dSJack F Vogel 	}
14961ae650dSJack F Vogel 
15061ae650dSJack F Vogel 	if (txr->avail < IXL_TX_CLEANUP_THRESHOLD)
15161ae650dSJack F Vogel 		ixl_txeof(que);
15261ae650dSJack F Vogel 
15361ae650dSJack F Vogel 	return (err);
15461ae650dSJack F Vogel }
15561ae650dSJack F Vogel 
15661ae650dSJack F Vogel /*
15761ae650dSJack F Vogel  * Called from a taskqueue to drain queued transmit packets.
15861ae650dSJack F Vogel  */
15961ae650dSJack F Vogel void
16061ae650dSJack F Vogel ixl_deferred_mq_start(void *arg, int pending)
16161ae650dSJack F Vogel {
16261ae650dSJack F Vogel 	struct ixl_queue	*que = arg;
16361ae650dSJack F Vogel         struct tx_ring		*txr = &que->txr;
16461ae650dSJack F Vogel 	struct ixl_vsi		*vsi = que->vsi;
16561ae650dSJack F Vogel         struct ifnet		*ifp = vsi->ifp;
16661ae650dSJack F Vogel 
16761ae650dSJack F Vogel 	IXL_TX_LOCK(txr);
16861ae650dSJack F Vogel 	if (!drbr_empty(ifp, txr->br))
16961ae650dSJack F Vogel 		ixl_mq_start_locked(ifp, txr);
17061ae650dSJack F Vogel 	IXL_TX_UNLOCK(txr);
17161ae650dSJack F Vogel }
17261ae650dSJack F Vogel 
17361ae650dSJack F Vogel /*
17461ae650dSJack F Vogel ** Flush all queue ring buffers
17561ae650dSJack F Vogel */
17661ae650dSJack F Vogel void
17761ae650dSJack F Vogel ixl_qflush(struct ifnet *ifp)
17861ae650dSJack F Vogel {
17961ae650dSJack F Vogel 	struct ixl_vsi	*vsi = ifp->if_softc;
18061ae650dSJack F Vogel 
18161ae650dSJack F Vogel         for (int i = 0; i < vsi->num_queues; i++) {
18261ae650dSJack F Vogel 		struct ixl_queue *que = &vsi->queues[i];
18361ae650dSJack F Vogel 		struct tx_ring	*txr = &que->txr;
18461ae650dSJack F Vogel 		struct mbuf	*m;
18561ae650dSJack F Vogel 		IXL_TX_LOCK(txr);
18661ae650dSJack F Vogel 		while ((m = buf_ring_dequeue_sc(txr->br)) != NULL)
18761ae650dSJack F Vogel 			m_freem(m);
18861ae650dSJack F Vogel 		IXL_TX_UNLOCK(txr);
18961ae650dSJack F Vogel 	}
19061ae650dSJack F Vogel 	if_qflush(ifp);
19161ae650dSJack F Vogel }
19261ae650dSJack F Vogel 
19361ae650dSJack F Vogel /*
19461ae650dSJack F Vogel ** Find mbuf chains passed to the driver
19561ae650dSJack F Vogel ** that are 'sparse', using more than 8
19661ae650dSJack F Vogel ** mbufs to deliver an mss-size chunk of data
19761ae650dSJack F Vogel */
19861ae650dSJack F Vogel static inline bool
19961ae650dSJack F Vogel ixl_tso_detect_sparse(struct mbuf *mp)
20061ae650dSJack F Vogel {
20161ae650dSJack F Vogel 	struct mbuf	*m;
20261ae650dSJack F Vogel 	int		num = 0, mss;
20361ae650dSJack F Vogel 	bool		ret = FALSE;
20461ae650dSJack F Vogel 
20561ae650dSJack F Vogel 	mss = mp->m_pkthdr.tso_segsz;
20661ae650dSJack F Vogel 	for (m = mp->m_next; m != NULL; m = m->m_next) {
20761ae650dSJack F Vogel 		num++;
20861ae650dSJack F Vogel 		mss -= m->m_len;
20961ae650dSJack F Vogel 		if (mss < 1)
21061ae650dSJack F Vogel 			break;
21161ae650dSJack F Vogel 		if (m->m_next == NULL)
21261ae650dSJack F Vogel 			break;
21361ae650dSJack F Vogel 	}
21461ae650dSJack F Vogel 	if (num > IXL_SPARSE_CHAIN)
21561ae650dSJack F Vogel 		ret = TRUE;
21661ae650dSJack F Vogel 
21761ae650dSJack F Vogel 	return (ret);
21861ae650dSJack F Vogel }
21961ae650dSJack F Vogel 
22061ae650dSJack F Vogel 
22161ae650dSJack F Vogel /*********************************************************************
22261ae650dSJack F Vogel  *
22361ae650dSJack F Vogel  *  This routine maps the mbufs to tx descriptors, allowing the
22461ae650dSJack F Vogel  *  TX engine to transmit the packets.
22561ae650dSJack F Vogel  *  	- return 0 on success, positive on failure
22661ae650dSJack F Vogel  *
22761ae650dSJack F Vogel  **********************************************************************/
22861ae650dSJack F Vogel #define IXL_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS)
22961ae650dSJack F Vogel 
23061ae650dSJack F Vogel static int
23161ae650dSJack F Vogel ixl_xmit(struct ixl_queue *que, struct mbuf **m_headp)
23261ae650dSJack F Vogel {
23361ae650dSJack F Vogel 	struct ixl_vsi		*vsi = que->vsi;
23461ae650dSJack F Vogel 	struct i40e_hw		*hw = vsi->hw;
23561ae650dSJack F Vogel 	struct tx_ring		*txr = &que->txr;
23661ae650dSJack F Vogel 	struct ixl_tx_buf	*buf;
23761ae650dSJack F Vogel 	struct i40e_tx_desc	*txd = NULL;
23861ae650dSJack F Vogel 	struct mbuf		*m_head, *m;
23961ae650dSJack F Vogel 	int             	i, j, error, nsegs, maxsegs;
24061ae650dSJack F Vogel 	int			first, last = 0;
24161ae650dSJack F Vogel 	u16			vtag = 0;
24261ae650dSJack F Vogel 	u32			cmd, off;
24361ae650dSJack F Vogel 	bus_dmamap_t		map;
24461ae650dSJack F Vogel 	bus_dma_tag_t		tag;
24561ae650dSJack F Vogel 	bus_dma_segment_t	segs[IXL_MAX_TSO_SEGS];
24661ae650dSJack F Vogel 
24761ae650dSJack F Vogel 
24861ae650dSJack F Vogel 	cmd = off = 0;
24961ae650dSJack F Vogel 	m_head = *m_headp;
25061ae650dSJack F Vogel 
25161ae650dSJack F Vogel         /*
25261ae650dSJack F Vogel          * Important to capture the first descriptor
25361ae650dSJack F Vogel          * used because it will contain the index of
25461ae650dSJack F Vogel          * the one we tell the hardware to report back
25561ae650dSJack F Vogel          */
25661ae650dSJack F Vogel         first = txr->next_avail;
25761ae650dSJack F Vogel 	buf = &txr->buffers[first];
25861ae650dSJack F Vogel 	map = buf->map;
25961ae650dSJack F Vogel 	tag = txr->tx_tag;
26061ae650dSJack F Vogel 	maxsegs = IXL_MAX_TX_SEGS;
26161ae650dSJack F Vogel 
26261ae650dSJack F Vogel 	if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
26361ae650dSJack F Vogel 		/* Use larger mapping for TSO */
26461ae650dSJack F Vogel 		tag = txr->tso_tag;
26561ae650dSJack F Vogel 		maxsegs = IXL_MAX_TSO_SEGS;
26661ae650dSJack F Vogel 		if (ixl_tso_detect_sparse(m_head)) {
26761ae650dSJack F Vogel 			m = m_defrag(m_head, M_NOWAIT);
268e5100ee2SJack F Vogel 			if (m == NULL) {
269e5100ee2SJack F Vogel 				m_freem(*m_headp);
270e5100ee2SJack F Vogel 				*m_headp = NULL;
271e5100ee2SJack F Vogel 				return (ENOBUFS);
272e5100ee2SJack F Vogel 			}
27361ae650dSJack F Vogel 			*m_headp = m;
27461ae650dSJack F Vogel 		}
27561ae650dSJack F Vogel 	}
27661ae650dSJack F Vogel 
27761ae650dSJack F Vogel 	/*
27861ae650dSJack F Vogel 	 * Map the packet for DMA.
27961ae650dSJack F Vogel 	 */
28061ae650dSJack F Vogel 	error = bus_dmamap_load_mbuf_sg(tag, map,
28161ae650dSJack F Vogel 	    *m_headp, segs, &nsegs, BUS_DMA_NOWAIT);
28261ae650dSJack F Vogel 
28361ae650dSJack F Vogel 	if (error == EFBIG) {
28461ae650dSJack F Vogel 		struct mbuf *m;
28561ae650dSJack F Vogel 
28661ae650dSJack F Vogel 		m = m_collapse(*m_headp, M_NOWAIT, maxsegs);
28761ae650dSJack F Vogel 		if (m == NULL) {
28861ae650dSJack F Vogel 			que->mbuf_defrag_failed++;
28961ae650dSJack F Vogel 			m_freem(*m_headp);
29061ae650dSJack F Vogel 			*m_headp = NULL;
29161ae650dSJack F Vogel 			return (ENOBUFS);
29261ae650dSJack F Vogel 		}
29361ae650dSJack F Vogel 		*m_headp = m;
29461ae650dSJack F Vogel 
29561ae650dSJack F Vogel 		/* Try it again */
29661ae650dSJack F Vogel 		error = bus_dmamap_load_mbuf_sg(tag, map,
29761ae650dSJack F Vogel 		    *m_headp, segs, &nsegs, BUS_DMA_NOWAIT);
29861ae650dSJack F Vogel 
29961ae650dSJack F Vogel 		if (error == ENOMEM) {
30061ae650dSJack F Vogel 			que->tx_dma_setup++;
30161ae650dSJack F Vogel 			return (error);
30261ae650dSJack F Vogel 		} else if (error != 0) {
30361ae650dSJack F Vogel 			que->tx_dma_setup++;
30461ae650dSJack F Vogel 			m_freem(*m_headp);
30561ae650dSJack F Vogel 			*m_headp = NULL;
30661ae650dSJack F Vogel 			return (error);
30761ae650dSJack F Vogel 		}
30861ae650dSJack F Vogel 	} else if (error == ENOMEM) {
30961ae650dSJack F Vogel 		que->tx_dma_setup++;
31061ae650dSJack F Vogel 		return (error);
31161ae650dSJack F Vogel 	} else if (error != 0) {
31261ae650dSJack F Vogel 		que->tx_dma_setup++;
31361ae650dSJack F Vogel 		m_freem(*m_headp);
31461ae650dSJack F Vogel 		*m_headp = NULL;
31561ae650dSJack F Vogel 		return (error);
31661ae650dSJack F Vogel 	}
31761ae650dSJack F Vogel 
31861ae650dSJack F Vogel 	/* Make certain there are enough descriptors */
31961ae650dSJack F Vogel 	if (nsegs > txr->avail - 2) {
32061ae650dSJack F Vogel 		txr->no_desc++;
32161ae650dSJack F Vogel 		error = ENOBUFS;
32261ae650dSJack F Vogel 		goto xmit_fail;
32361ae650dSJack F Vogel 	}
32461ae650dSJack F Vogel 	m_head = *m_headp;
32561ae650dSJack F Vogel 
32661ae650dSJack F Vogel 	/* Set up the TSO/CSUM offload */
32761ae650dSJack F Vogel 	if (m_head->m_pkthdr.csum_flags & CSUM_OFFLOAD) {
32861ae650dSJack F Vogel 		error = ixl_tx_setup_offload(que, m_head, &cmd, &off);
32961ae650dSJack F Vogel 		if (error)
33061ae650dSJack F Vogel 			goto xmit_fail;
33161ae650dSJack F Vogel 	}
33261ae650dSJack F Vogel 
33361ae650dSJack F Vogel 	cmd |= I40E_TX_DESC_CMD_ICRC;
33461ae650dSJack F Vogel 	/* Grab the VLAN tag */
33561ae650dSJack F Vogel 	if (m_head->m_flags & M_VLANTAG) {
33661ae650dSJack F Vogel 		cmd |= I40E_TX_DESC_CMD_IL2TAG1;
33761ae650dSJack F Vogel 		vtag = htole16(m_head->m_pkthdr.ether_vtag);
33861ae650dSJack F Vogel 	}
33961ae650dSJack F Vogel 
34061ae650dSJack F Vogel 	i = txr->next_avail;
34161ae650dSJack F Vogel 	for (j = 0; j < nsegs; j++) {
34261ae650dSJack F Vogel 		bus_size_t seglen;
34361ae650dSJack F Vogel 
34461ae650dSJack F Vogel 		buf = &txr->buffers[i];
34561ae650dSJack F Vogel 		buf->tag = tag; /* Keep track of the type tag */
34661ae650dSJack F Vogel 		txd = &txr->base[i];
34761ae650dSJack F Vogel 		seglen = segs[j].ds_len;
34861ae650dSJack F Vogel 
34961ae650dSJack F Vogel 		txd->buffer_addr = htole64(segs[j].ds_addr);
35061ae650dSJack F Vogel 		txd->cmd_type_offset_bsz =
35161ae650dSJack F Vogel 		    htole64(I40E_TX_DESC_DTYPE_DATA
35261ae650dSJack F Vogel 		    | ((u64)cmd  << I40E_TXD_QW1_CMD_SHIFT)
35361ae650dSJack F Vogel 		    | ((u64)off << I40E_TXD_QW1_OFFSET_SHIFT)
35461ae650dSJack F Vogel 		    | ((u64)seglen  << I40E_TXD_QW1_TX_BUF_SZ_SHIFT)
35561ae650dSJack F Vogel 		    | ((u64)vtag  << I40E_TXD_QW1_L2TAG1_SHIFT));
35661ae650dSJack F Vogel 
35761ae650dSJack F Vogel 		last = i; /* descriptor that will get completion IRQ */
35861ae650dSJack F Vogel 
35961ae650dSJack F Vogel 		if (++i == que->num_desc)
36061ae650dSJack F Vogel 			i = 0;
36161ae650dSJack F Vogel 
36261ae650dSJack F Vogel 		buf->m_head = NULL;
36361ae650dSJack F Vogel 		buf->eop_index = -1;
36461ae650dSJack F Vogel 	}
36561ae650dSJack F Vogel 	/* Set the last descriptor for report */
36661ae650dSJack F Vogel 	txd->cmd_type_offset_bsz |=
36761ae650dSJack F Vogel 	    htole64(((u64)IXL_TXD_CMD << I40E_TXD_QW1_CMD_SHIFT));
36861ae650dSJack F Vogel 	txr->avail -= nsegs;
36961ae650dSJack F Vogel 	txr->next_avail = i;
37061ae650dSJack F Vogel 
37161ae650dSJack F Vogel 	buf->m_head = m_head;
37261ae650dSJack F Vogel 	/* Swap the dma map between the first and last descriptor */
37361ae650dSJack F Vogel 	txr->buffers[first].map = buf->map;
37461ae650dSJack F Vogel 	buf->map = map;
37561ae650dSJack F Vogel 	bus_dmamap_sync(tag, map, BUS_DMASYNC_PREWRITE);
37661ae650dSJack F Vogel 
37761ae650dSJack F Vogel         /* Set the index of the descriptor that will be marked done */
37861ae650dSJack F Vogel         buf = &txr->buffers[first];
37961ae650dSJack F Vogel 	buf->eop_index = last;
38061ae650dSJack F Vogel 
38161ae650dSJack F Vogel         bus_dmamap_sync(txr->dma.tag, txr->dma.map,
38261ae650dSJack F Vogel             BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
38361ae650dSJack F Vogel 	/*
38461ae650dSJack F Vogel 	 * Advance the Transmit Descriptor Tail (Tdt), this tells the
38561ae650dSJack F Vogel 	 * hardware that this frame is available to transmit.
38661ae650dSJack F Vogel 	 */
38761ae650dSJack F Vogel 	++txr->total_packets;
38861ae650dSJack F Vogel 	wr32(hw, txr->tail, i);
38961ae650dSJack F Vogel 
39061ae650dSJack F Vogel 	ixl_flush(hw);
39161ae650dSJack F Vogel 	/* Mark outstanding work */
39261ae650dSJack F Vogel 	if (que->busy == 0)
39361ae650dSJack F Vogel 		que->busy = 1;
39461ae650dSJack F Vogel 	return (0);
39561ae650dSJack F Vogel 
39661ae650dSJack F Vogel xmit_fail:
39761ae650dSJack F Vogel 	bus_dmamap_unload(tag, buf->map);
39861ae650dSJack F Vogel 	return (error);
39961ae650dSJack F Vogel }
40061ae650dSJack F Vogel 
40161ae650dSJack F Vogel 
40261ae650dSJack F Vogel /*********************************************************************
40361ae650dSJack F Vogel  *
40461ae650dSJack F Vogel  *  Allocate memory for tx_buffer structures. The tx_buffer stores all
40561ae650dSJack F Vogel  *  the information needed to transmit a packet on the wire. This is
40661ae650dSJack F Vogel  *  called only once at attach, setup is done every reset.
40761ae650dSJack F Vogel  *
40861ae650dSJack F Vogel  **********************************************************************/
40961ae650dSJack F Vogel int
41061ae650dSJack F Vogel ixl_allocate_tx_data(struct ixl_queue *que)
41161ae650dSJack F Vogel {
41261ae650dSJack F Vogel 	struct tx_ring		*txr = &que->txr;
41361ae650dSJack F Vogel 	struct ixl_vsi		*vsi = que->vsi;
41461ae650dSJack F Vogel 	device_t		dev = vsi->dev;
41561ae650dSJack F Vogel 	struct ixl_tx_buf	*buf;
41661ae650dSJack F Vogel 	int			error = 0;
41761ae650dSJack F Vogel 
41861ae650dSJack F Vogel 	/*
41961ae650dSJack F Vogel 	 * Setup DMA descriptor areas.
42061ae650dSJack F Vogel 	 */
42161ae650dSJack F Vogel 	if ((error = bus_dma_tag_create(NULL,		/* parent */
42261ae650dSJack F Vogel 			       1, 0,			/* alignment, bounds */
42361ae650dSJack F Vogel 			       BUS_SPACE_MAXADDR,	/* lowaddr */
42461ae650dSJack F Vogel 			       BUS_SPACE_MAXADDR,	/* highaddr */
42561ae650dSJack F Vogel 			       NULL, NULL,		/* filter, filterarg */
42661ae650dSJack F Vogel 			       IXL_TSO_SIZE,		/* maxsize */
42761ae650dSJack F Vogel 			       IXL_MAX_TX_SEGS,		/* nsegments */
42861ae650dSJack F Vogel 			       PAGE_SIZE,		/* maxsegsize */
42961ae650dSJack F Vogel 			       0,			/* flags */
43061ae650dSJack F Vogel 			       NULL,			/* lockfunc */
43161ae650dSJack F Vogel 			       NULL,			/* lockfuncarg */
43261ae650dSJack F Vogel 			       &txr->tx_tag))) {
43361ae650dSJack F Vogel 		device_printf(dev,"Unable to allocate TX DMA tag\n");
43461ae650dSJack F Vogel 		goto fail;
43561ae650dSJack F Vogel 	}
43661ae650dSJack F Vogel 
43761ae650dSJack F Vogel 	/* Make a special tag for TSO */
43861ae650dSJack F Vogel 	if ((error = bus_dma_tag_create(NULL,		/* parent */
43961ae650dSJack F Vogel 			       1, 0,			/* alignment, bounds */
44061ae650dSJack F Vogel 			       BUS_SPACE_MAXADDR,	/* lowaddr */
44161ae650dSJack F Vogel 			       BUS_SPACE_MAXADDR,	/* highaddr */
44261ae650dSJack F Vogel 			       NULL, NULL,		/* filter, filterarg */
44361ae650dSJack F Vogel 			       IXL_TSO_SIZE,		/* maxsize */
44461ae650dSJack F Vogel 			       IXL_MAX_TSO_SEGS,	/* nsegments */
44561ae650dSJack F Vogel 			       PAGE_SIZE,		/* maxsegsize */
44661ae650dSJack F Vogel 			       0,			/* flags */
44761ae650dSJack F Vogel 			       NULL,			/* lockfunc */
44861ae650dSJack F Vogel 			       NULL,			/* lockfuncarg */
44961ae650dSJack F Vogel 			       &txr->tso_tag))) {
45061ae650dSJack F Vogel 		device_printf(dev,"Unable to allocate TX TSO DMA tag\n");
45161ae650dSJack F Vogel 		goto fail;
45261ae650dSJack F Vogel 	}
45361ae650dSJack F Vogel 
45461ae650dSJack F Vogel 	if (!(txr->buffers =
45561ae650dSJack F Vogel 	    (struct ixl_tx_buf *) malloc(sizeof(struct ixl_tx_buf) *
45661ae650dSJack F Vogel 	    que->num_desc, M_DEVBUF, M_NOWAIT | M_ZERO))) {
45761ae650dSJack F Vogel 		device_printf(dev, "Unable to allocate tx_buffer memory\n");
45861ae650dSJack F Vogel 		error = ENOMEM;
45961ae650dSJack F Vogel 		goto fail;
46061ae650dSJack F Vogel 	}
46161ae650dSJack F Vogel 
46261ae650dSJack F Vogel         /* Create the descriptor buffer default dma maps */
46361ae650dSJack F Vogel 	buf = txr->buffers;
46461ae650dSJack F Vogel 	for (int i = 0; i < que->num_desc; i++, buf++) {
46561ae650dSJack F Vogel 		buf->tag = txr->tx_tag;
46661ae650dSJack F Vogel 		error = bus_dmamap_create(buf->tag, 0, &buf->map);
46761ae650dSJack F Vogel 		if (error != 0) {
46861ae650dSJack F Vogel 			device_printf(dev, "Unable to create TX DMA map\n");
46961ae650dSJack F Vogel 			goto fail;
47061ae650dSJack F Vogel 		}
47161ae650dSJack F Vogel 	}
47261ae650dSJack F Vogel fail:
47361ae650dSJack F Vogel 	return (error);
47461ae650dSJack F Vogel }
47561ae650dSJack F Vogel 
47661ae650dSJack F Vogel 
47761ae650dSJack F Vogel /*********************************************************************
47861ae650dSJack F Vogel  *
47961ae650dSJack F Vogel  *  (Re)Initialize a queue transmit ring.
48061ae650dSJack F Vogel  *	- called by init, it clears the descriptor ring,
48161ae650dSJack F Vogel  *	  and frees any stale mbufs
48261ae650dSJack F Vogel  *
48361ae650dSJack F Vogel  **********************************************************************/
48461ae650dSJack F Vogel void
48561ae650dSJack F Vogel ixl_init_tx_ring(struct ixl_queue *que)
48661ae650dSJack F Vogel {
48761ae650dSJack F Vogel 	struct tx_ring		*txr = &que->txr;
48861ae650dSJack F Vogel 	struct ixl_tx_buf	*buf;
48961ae650dSJack F Vogel 
49061ae650dSJack F Vogel 	/* Clear the old ring contents */
49161ae650dSJack F Vogel 	IXL_TX_LOCK(txr);
492*56c2c47bSJack F Vogel 
493bc8b78d3SLuigi Rizzo 
49461ae650dSJack F Vogel 	bzero((void *)txr->base,
49561ae650dSJack F Vogel 	      (sizeof(struct i40e_tx_desc)) * que->num_desc);
49661ae650dSJack F Vogel 
49761ae650dSJack F Vogel 	/* Reset indices */
49861ae650dSJack F Vogel 	txr->next_avail = 0;
49961ae650dSJack F Vogel 	txr->next_to_clean = 0;
50061ae650dSJack F Vogel 
50161ae650dSJack F Vogel #ifdef IXL_FDIR
50261ae650dSJack F Vogel 	/* Initialize flow director */
50361ae650dSJack F Vogel 	txr->atr_rate = ixl_atr_rate;
50461ae650dSJack F Vogel 	txr->atr_count = 0;
50561ae650dSJack F Vogel #endif
50661ae650dSJack F Vogel 
50761ae650dSJack F Vogel 	/* Free any existing tx mbufs. */
50861ae650dSJack F Vogel         buf = txr->buffers;
50961ae650dSJack F Vogel 	for (int i = 0; i < que->num_desc; i++, buf++) {
51061ae650dSJack F Vogel 		if (buf->m_head != NULL) {
51161ae650dSJack F Vogel 			bus_dmamap_sync(buf->tag, buf->map,
51261ae650dSJack F Vogel 			    BUS_DMASYNC_POSTWRITE);
51361ae650dSJack F Vogel 			bus_dmamap_unload(buf->tag, buf->map);
51461ae650dSJack F Vogel 			m_freem(buf->m_head);
51561ae650dSJack F Vogel 			buf->m_head = NULL;
51661ae650dSJack F Vogel 		}
51761ae650dSJack F Vogel 		/* Clear the EOP index */
51861ae650dSJack F Vogel 		buf->eop_index = -1;
51961ae650dSJack F Vogel         }
52061ae650dSJack F Vogel 
52161ae650dSJack F Vogel 	/* Set number of descriptors available */
52261ae650dSJack F Vogel 	txr->avail = que->num_desc;
52361ae650dSJack F Vogel 
52461ae650dSJack F Vogel 	bus_dmamap_sync(txr->dma.tag, txr->dma.map,
52561ae650dSJack F Vogel 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
52661ae650dSJack F Vogel 	IXL_TX_UNLOCK(txr);
52761ae650dSJack F Vogel }
52861ae650dSJack F Vogel 
52961ae650dSJack F Vogel 
53061ae650dSJack F Vogel /*********************************************************************
53161ae650dSJack F Vogel  *
53261ae650dSJack F Vogel  *  Free transmit ring related data structures.
53361ae650dSJack F Vogel  *
53461ae650dSJack F Vogel  **********************************************************************/
53561ae650dSJack F Vogel void
53661ae650dSJack F Vogel ixl_free_que_tx(struct ixl_queue *que)
53761ae650dSJack F Vogel {
53861ae650dSJack F Vogel 	struct tx_ring *txr = &que->txr;
53961ae650dSJack F Vogel 	struct ixl_tx_buf *buf;
54061ae650dSJack F Vogel 
54161ae650dSJack F Vogel 	INIT_DBG_IF(que->vsi->ifp, "queue %d: begin", que->me);
54261ae650dSJack F Vogel 
54361ae650dSJack F Vogel 	for (int i = 0; i < que->num_desc; i++) {
54461ae650dSJack F Vogel 		buf = &txr->buffers[i];
54561ae650dSJack F Vogel 		if (buf->m_head != NULL) {
54661ae650dSJack F Vogel 			bus_dmamap_sync(buf->tag, buf->map,
54761ae650dSJack F Vogel 			    BUS_DMASYNC_POSTWRITE);
54861ae650dSJack F Vogel 			bus_dmamap_unload(buf->tag,
54961ae650dSJack F Vogel 			    buf->map);
55061ae650dSJack F Vogel 			m_freem(buf->m_head);
55161ae650dSJack F Vogel 			buf->m_head = NULL;
55261ae650dSJack F Vogel 			if (buf->map != NULL) {
55361ae650dSJack F Vogel 				bus_dmamap_destroy(buf->tag,
55461ae650dSJack F Vogel 				    buf->map);
55561ae650dSJack F Vogel 				buf->map = NULL;
55661ae650dSJack F Vogel 			}
55761ae650dSJack F Vogel 		} else if (buf->map != NULL) {
55861ae650dSJack F Vogel 			bus_dmamap_unload(buf->tag,
55961ae650dSJack F Vogel 			    buf->map);
56061ae650dSJack F Vogel 			bus_dmamap_destroy(buf->tag,
56161ae650dSJack F Vogel 			    buf->map);
56261ae650dSJack F Vogel 			buf->map = NULL;
56361ae650dSJack F Vogel 		}
56461ae650dSJack F Vogel 	}
56561ae650dSJack F Vogel 	if (txr->br != NULL)
56661ae650dSJack F Vogel 		buf_ring_free(txr->br, M_DEVBUF);
56761ae650dSJack F Vogel 	if (txr->buffers != NULL) {
56861ae650dSJack F Vogel 		free(txr->buffers, M_DEVBUF);
56961ae650dSJack F Vogel 		txr->buffers = NULL;
57061ae650dSJack F Vogel 	}
57161ae650dSJack F Vogel 	if (txr->tx_tag != NULL) {
57261ae650dSJack F Vogel 		bus_dma_tag_destroy(txr->tx_tag);
57361ae650dSJack F Vogel 		txr->tx_tag = NULL;
57461ae650dSJack F Vogel 	}
57561ae650dSJack F Vogel 	if (txr->tso_tag != NULL) {
57661ae650dSJack F Vogel 		bus_dma_tag_destroy(txr->tso_tag);
57761ae650dSJack F Vogel 		txr->tso_tag = NULL;
57861ae650dSJack F Vogel 	}
57961ae650dSJack F Vogel 
58061ae650dSJack F Vogel 	INIT_DBG_IF(que->vsi->ifp, "queue %d: end", que->me);
58161ae650dSJack F Vogel 	return;
58261ae650dSJack F Vogel }
58361ae650dSJack F Vogel 
58461ae650dSJack F Vogel /*********************************************************************
58561ae650dSJack F Vogel  *
58661ae650dSJack F Vogel  *  Setup descriptor for hw offloads
58761ae650dSJack F Vogel  *
58861ae650dSJack F Vogel  **********************************************************************/
58961ae650dSJack F Vogel 
59061ae650dSJack F Vogel static int
59161ae650dSJack F Vogel ixl_tx_setup_offload(struct ixl_queue *que,
59261ae650dSJack F Vogel     struct mbuf *mp, u32 *cmd, u32 *off)
59361ae650dSJack F Vogel {
59461ae650dSJack F Vogel 	struct ether_vlan_header	*eh;
5951f873f18SJack F Vogel #ifdef INET
59661ae650dSJack F Vogel 	struct ip			*ip = NULL;
5971f873f18SJack F Vogel #endif
59861ae650dSJack F Vogel 	struct tcphdr			*th = NULL;
5991f873f18SJack F Vogel #ifdef INET6
60061ae650dSJack F Vogel 	struct ip6_hdr			*ip6;
6011f873f18SJack F Vogel #endif
60261ae650dSJack F Vogel 	int				elen, ip_hlen = 0, tcp_hlen;
60361ae650dSJack F Vogel 	u16				etype;
60461ae650dSJack F Vogel 	u8				ipproto = 0;
60561ae650dSJack F Vogel 	bool				tso = FALSE;
60661ae650dSJack F Vogel 
60761ae650dSJack F Vogel 
60861ae650dSJack F Vogel 	/* Set up the TSO context descriptor if required */
60961ae650dSJack F Vogel 	if (mp->m_pkthdr.csum_flags & CSUM_TSO) {
61061ae650dSJack F Vogel 		tso = ixl_tso_setup(que, mp);
61161ae650dSJack F Vogel 		if (tso)
61261ae650dSJack F Vogel 			++que->tso;
61361ae650dSJack F Vogel 		else
61461ae650dSJack F Vogel 			return (ENXIO);
61561ae650dSJack F Vogel 	}
61661ae650dSJack F Vogel 
61761ae650dSJack F Vogel 	/*
61861ae650dSJack F Vogel 	 * Determine where frame payload starts.
61961ae650dSJack F Vogel 	 * Jump over vlan headers if already present,
62061ae650dSJack F Vogel 	 * helpful for QinQ too.
62161ae650dSJack F Vogel 	 */
62261ae650dSJack F Vogel 	eh = mtod(mp, struct ether_vlan_header *);
62361ae650dSJack F Vogel 	if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
62461ae650dSJack F Vogel 		etype = ntohs(eh->evl_proto);
62561ae650dSJack F Vogel 		elen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
62661ae650dSJack F Vogel 	} else {
62761ae650dSJack F Vogel 		etype = ntohs(eh->evl_encap_proto);
62861ae650dSJack F Vogel 		elen = ETHER_HDR_LEN;
62961ae650dSJack F Vogel 	}
63061ae650dSJack F Vogel 
63161ae650dSJack F Vogel 	switch (etype) {
6322f4959abSJack F Vogel #ifdef INET
633b555c614SBjoern A. Zeeb 		case ETHERTYPE_IP:
63461ae650dSJack F Vogel 			ip = (struct ip *)(mp->m_data + elen);
63561ae650dSJack F Vogel 			ip_hlen = ip->ip_hl << 2;
63661ae650dSJack F Vogel 			ipproto = ip->ip_p;
63761ae650dSJack F Vogel 			th = (struct tcphdr *)((caddr_t)ip + ip_hlen);
63861ae650dSJack F Vogel 			/* The IP checksum must be recalculated with TSO */
63961ae650dSJack F Vogel 			if (tso)
64061ae650dSJack F Vogel 				*cmd |= I40E_TX_DESC_CMD_IIPT_IPV4_CSUM;
64161ae650dSJack F Vogel 			else
64261ae650dSJack F Vogel 				*cmd |= I40E_TX_DESC_CMD_IIPT_IPV4;
64361ae650dSJack F Vogel 			break;
644b555c614SBjoern A. Zeeb #endif
6452f4959abSJack F Vogel #ifdef INET6
646b555c614SBjoern A. Zeeb 		case ETHERTYPE_IPV6:
64761ae650dSJack F Vogel 			ip6 = (struct ip6_hdr *)(mp->m_data + elen);
64861ae650dSJack F Vogel 			ip_hlen = sizeof(struct ip6_hdr);
64961ae650dSJack F Vogel 			ipproto = ip6->ip6_nxt;
65061ae650dSJack F Vogel 			th = (struct tcphdr *)((caddr_t)ip6 + ip_hlen);
65161ae650dSJack F Vogel 			*cmd |= I40E_TX_DESC_CMD_IIPT_IPV6;
652b555c614SBjoern A. Zeeb 			break;
6532f4959abSJack F Vogel #endif
65461ae650dSJack F Vogel 		default:
65561ae650dSJack F Vogel 			break;
65661ae650dSJack F Vogel 	}
65761ae650dSJack F Vogel 
65861ae650dSJack F Vogel 	*off |= (elen >> 1) << I40E_TX_DESC_LENGTH_MACLEN_SHIFT;
65961ae650dSJack F Vogel 	*off |= (ip_hlen >> 2) << I40E_TX_DESC_LENGTH_IPLEN_SHIFT;
66061ae650dSJack F Vogel 
66161ae650dSJack F Vogel 	switch (ipproto) {
66261ae650dSJack F Vogel 		case IPPROTO_TCP:
66361ae650dSJack F Vogel 			tcp_hlen = th->th_off << 2;
66461ae650dSJack F Vogel 			if (mp->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_TCP_IPV6)) {
66561ae650dSJack F Vogel 				*cmd |= I40E_TX_DESC_CMD_L4T_EOFT_TCP;
66661ae650dSJack F Vogel 				*off |= (tcp_hlen >> 2) <<
66761ae650dSJack F Vogel 				    I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT;
66861ae650dSJack F Vogel 			}
66961ae650dSJack F Vogel #ifdef IXL_FDIR
67061ae650dSJack F Vogel 			ixl_atr(que, th, etype);
67161ae650dSJack F Vogel #endif
67261ae650dSJack F Vogel 			break;
67361ae650dSJack F Vogel 		case IPPROTO_UDP:
67461ae650dSJack F Vogel 			if (mp->m_pkthdr.csum_flags & (CSUM_UDP|CSUM_UDP_IPV6)) {
67561ae650dSJack F Vogel 				*cmd |= I40E_TX_DESC_CMD_L4T_EOFT_UDP;
67661ae650dSJack F Vogel 				*off |= (sizeof(struct udphdr) >> 2) <<
67761ae650dSJack F Vogel 				    I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT;
67861ae650dSJack F Vogel 			}
67961ae650dSJack F Vogel 			break;
68061ae650dSJack F Vogel 
68161ae650dSJack F Vogel 		case IPPROTO_SCTP:
68261ae650dSJack F Vogel 			if (mp->m_pkthdr.csum_flags & (CSUM_SCTP|CSUM_SCTP_IPV6)) {
68361ae650dSJack F Vogel 				*cmd |= I40E_TX_DESC_CMD_L4T_EOFT_SCTP;
68461ae650dSJack F Vogel 				*off |= (sizeof(struct sctphdr) >> 2) <<
68561ae650dSJack F Vogel 				    I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT;
68661ae650dSJack F Vogel 			}
68761ae650dSJack F Vogel 			/* Fall Thru */
68861ae650dSJack F Vogel 		default:
68961ae650dSJack F Vogel 			break;
69061ae650dSJack F Vogel 	}
69161ae650dSJack F Vogel 
69261ae650dSJack F Vogel         return (0);
69361ae650dSJack F Vogel }
69461ae650dSJack F Vogel 
69561ae650dSJack F Vogel 
69661ae650dSJack F Vogel /**********************************************************************
69761ae650dSJack F Vogel  *
69861ae650dSJack F Vogel  *  Setup context for hardware segmentation offload (TSO)
69961ae650dSJack F Vogel  *
70061ae650dSJack F Vogel  **********************************************************************/
70161ae650dSJack F Vogel static bool
70261ae650dSJack F Vogel ixl_tso_setup(struct ixl_queue *que, struct mbuf *mp)
70361ae650dSJack F Vogel {
70461ae650dSJack F Vogel 	struct tx_ring			*txr = &que->txr;
70561ae650dSJack F Vogel 	struct i40e_tx_context_desc	*TXD;
70661ae650dSJack F Vogel 	struct ixl_tx_buf		*buf;
70761ae650dSJack F Vogel 	u32				cmd, mss, type, tsolen;
70861ae650dSJack F Vogel 	u16				etype;
70961ae650dSJack F Vogel 	int				idx, elen, ip_hlen, tcp_hlen;
71061ae650dSJack F Vogel 	struct ether_vlan_header	*eh;
7111f873f18SJack F Vogel #ifdef INET
71261ae650dSJack F Vogel 	struct ip			*ip;
7131f873f18SJack F Vogel #endif
7141f873f18SJack F Vogel #ifdef INET6
71561ae650dSJack F Vogel 	struct ip6_hdr			*ip6;
7161f873f18SJack F Vogel #endif
717b555c614SBjoern A. Zeeb #if defined(INET6) || defined(INET)
71861ae650dSJack F Vogel 	struct tcphdr			*th;
719b555c614SBjoern A. Zeeb #endif
72061ae650dSJack F Vogel 	u64				type_cmd_tso_mss;
72161ae650dSJack F Vogel 
72261ae650dSJack F Vogel 	/*
72361ae650dSJack F Vogel 	 * Determine where frame payload starts.
72461ae650dSJack F Vogel 	 * Jump over vlan headers if already present
72561ae650dSJack F Vogel 	 */
72661ae650dSJack F Vogel 	eh = mtod(mp, struct ether_vlan_header *);
72761ae650dSJack F Vogel 	if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
72861ae650dSJack F Vogel 		elen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
72961ae650dSJack F Vogel 		etype = eh->evl_proto;
73061ae650dSJack F Vogel 	} else {
73161ae650dSJack F Vogel 		elen = ETHER_HDR_LEN;
73261ae650dSJack F Vogel 		etype = eh->evl_encap_proto;
73361ae650dSJack F Vogel 	}
73461ae650dSJack F Vogel 
73561ae650dSJack F Vogel         switch (ntohs(etype)) {
73661ae650dSJack F Vogel #ifdef INET6
73761ae650dSJack F Vogel 	case ETHERTYPE_IPV6:
73861ae650dSJack F Vogel 		ip6 = (struct ip6_hdr *)(mp->m_data + elen);
73961ae650dSJack F Vogel 		if (ip6->ip6_nxt != IPPROTO_TCP)
74061ae650dSJack F Vogel 			return (ENXIO);
74161ae650dSJack F Vogel 		ip_hlen = sizeof(struct ip6_hdr);
74261ae650dSJack F Vogel 		th = (struct tcphdr *)((caddr_t)ip6 + ip_hlen);
74361ae650dSJack F Vogel 		th->th_sum = in6_cksum_pseudo(ip6, 0, IPPROTO_TCP, 0);
74461ae650dSJack F Vogel 		tcp_hlen = th->th_off << 2;
74561ae650dSJack F Vogel 		break;
74661ae650dSJack F Vogel #endif
74761ae650dSJack F Vogel #ifdef INET
74861ae650dSJack F Vogel 	case ETHERTYPE_IP:
74961ae650dSJack F Vogel 		ip = (struct ip *)(mp->m_data + elen);
75061ae650dSJack F Vogel 		if (ip->ip_p != IPPROTO_TCP)
75161ae650dSJack F Vogel 			return (ENXIO);
75261ae650dSJack F Vogel 		ip->ip_sum = 0;
75361ae650dSJack F Vogel 		ip_hlen = ip->ip_hl << 2;
75461ae650dSJack F Vogel 		th = (struct tcphdr *)((caddr_t)ip + ip_hlen);
75561ae650dSJack F Vogel 		th->th_sum = in_pseudo(ip->ip_src.s_addr,
75661ae650dSJack F Vogel 		    ip->ip_dst.s_addr, htons(IPPROTO_TCP));
75761ae650dSJack F Vogel 		tcp_hlen = th->th_off << 2;
75861ae650dSJack F Vogel 		break;
75961ae650dSJack F Vogel #endif
76061ae650dSJack F Vogel 	default:
761b555c614SBjoern A. Zeeb 		printf("%s: CSUM_TSO but no supported IP version (0x%04x)",
76261ae650dSJack F Vogel 		    __func__, ntohs(etype));
763b555c614SBjoern A. Zeeb 		return FALSE;
76461ae650dSJack F Vogel         }
76561ae650dSJack F Vogel 
76661ae650dSJack F Vogel         /* Ensure we have at least the IP+TCP header in the first mbuf. */
76761ae650dSJack F Vogel         if (mp->m_len < elen + ip_hlen + sizeof(struct tcphdr))
76861ae650dSJack F Vogel 		return FALSE;
76961ae650dSJack F Vogel 
77061ae650dSJack F Vogel 	idx = txr->next_avail;
77161ae650dSJack F Vogel 	buf = &txr->buffers[idx];
77261ae650dSJack F Vogel 	TXD = (struct i40e_tx_context_desc *) &txr->base[idx];
77361ae650dSJack F Vogel 	tsolen = mp->m_pkthdr.len - (elen + ip_hlen + tcp_hlen);
77461ae650dSJack F Vogel 
77561ae650dSJack F Vogel 	type = I40E_TX_DESC_DTYPE_CONTEXT;
77661ae650dSJack F Vogel 	cmd = I40E_TX_CTX_DESC_TSO;
77761ae650dSJack F Vogel 	mss = mp->m_pkthdr.tso_segsz;
77861ae650dSJack F Vogel 
77961ae650dSJack F Vogel 	type_cmd_tso_mss = ((u64)type << I40E_TXD_CTX_QW1_DTYPE_SHIFT) |
78061ae650dSJack F Vogel 	    ((u64)cmd << I40E_TXD_CTX_QW1_CMD_SHIFT) |
78161ae650dSJack F Vogel 	    ((u64)tsolen << I40E_TXD_CTX_QW1_TSO_LEN_SHIFT) |
78261ae650dSJack F Vogel 	    ((u64)mss << I40E_TXD_CTX_QW1_MSS_SHIFT);
78361ae650dSJack F Vogel 	TXD->type_cmd_tso_mss = htole64(type_cmd_tso_mss);
78461ae650dSJack F Vogel 
78561ae650dSJack F Vogel 	TXD->tunneling_params = htole32(0);
78661ae650dSJack F Vogel 	buf->m_head = NULL;
78761ae650dSJack F Vogel 	buf->eop_index = -1;
78861ae650dSJack F Vogel 
78961ae650dSJack F Vogel 	if (++idx == que->num_desc)
79061ae650dSJack F Vogel 		idx = 0;
79161ae650dSJack F Vogel 
79261ae650dSJack F Vogel 	txr->avail--;
79361ae650dSJack F Vogel 	txr->next_avail = idx;
79461ae650dSJack F Vogel 
79561ae650dSJack F Vogel 	return TRUE;
79661ae650dSJack F Vogel }
79761ae650dSJack F Vogel 
79861ae650dSJack F Vogel /*
79961ae650dSJack F Vogel ** ixl_get_tx_head - Retrieve the value from the
80061ae650dSJack F Vogel **    location the HW records its HEAD index
80161ae650dSJack F Vogel */
80261ae650dSJack F Vogel static inline u32
80361ae650dSJack F Vogel ixl_get_tx_head(struct ixl_queue *que)
80461ae650dSJack F Vogel {
80561ae650dSJack F Vogel 	struct tx_ring  *txr = &que->txr;
80661ae650dSJack F Vogel 	void *head = &txr->base[que->num_desc];
80761ae650dSJack F Vogel 	return LE32_TO_CPU(*(volatile __le32 *)head);
80861ae650dSJack F Vogel }
80961ae650dSJack F Vogel 
81061ae650dSJack F Vogel /**********************************************************************
81161ae650dSJack F Vogel  *
81261ae650dSJack F Vogel  *  Examine each tx_buffer in the used queue. If the hardware is done
81361ae650dSJack F Vogel  *  processing the packet then free associated resources. The
81461ae650dSJack F Vogel  *  tx_buffer is put back on the free queue.
81561ae650dSJack F Vogel  *
81661ae650dSJack F Vogel  **********************************************************************/
81761ae650dSJack F Vogel bool
81861ae650dSJack F Vogel ixl_txeof(struct ixl_queue *que)
81961ae650dSJack F Vogel {
82061ae650dSJack F Vogel 	struct tx_ring		*txr = &que->txr;
82161ae650dSJack F Vogel 	u32			first, last, head, done, processed;
82261ae650dSJack F Vogel 	struct ixl_tx_buf	*buf;
82361ae650dSJack F Vogel 	struct i40e_tx_desc	*tx_desc, *eop_desc;
82461ae650dSJack F Vogel 
82561ae650dSJack F Vogel 
82661ae650dSJack F Vogel 	mtx_assert(&txr->mtx, MA_OWNED);
82761ae650dSJack F Vogel 
828e5100ee2SJack F Vogel 
82961ae650dSJack F Vogel 	/* These are not the descriptors you seek, move along :) */
83061ae650dSJack F Vogel 	if (txr->avail == que->num_desc) {
83161ae650dSJack F Vogel 		que->busy = 0;
83261ae650dSJack F Vogel 		return FALSE;
83361ae650dSJack F Vogel 	}
83461ae650dSJack F Vogel 
83561ae650dSJack F Vogel 	processed = 0;
83661ae650dSJack F Vogel 	first = txr->next_to_clean;
83761ae650dSJack F Vogel 	buf = &txr->buffers[first];
83861ae650dSJack F Vogel 	tx_desc = (struct i40e_tx_desc *)&txr->base[first];
83961ae650dSJack F Vogel 	last = buf->eop_index;
84061ae650dSJack F Vogel 	if (last == -1)
84161ae650dSJack F Vogel 		return FALSE;
84261ae650dSJack F Vogel 	eop_desc = (struct i40e_tx_desc *)&txr->base[last];
84361ae650dSJack F Vogel 
84461ae650dSJack F Vogel 	/* Get the Head WB value */
84561ae650dSJack F Vogel 	head = ixl_get_tx_head(que);
84661ae650dSJack F Vogel 
84761ae650dSJack F Vogel 	/*
84861ae650dSJack F Vogel 	** Get the index of the first descriptor
84961ae650dSJack F Vogel 	** BEYOND the EOP and call that 'done'.
85061ae650dSJack F Vogel 	** I do this so the comparison in the
85161ae650dSJack F Vogel 	** inner while loop below can be simple
85261ae650dSJack F Vogel 	*/
85361ae650dSJack F Vogel 	if (++last == que->num_desc) last = 0;
85461ae650dSJack F Vogel 	done = last;
85561ae650dSJack F Vogel 
85661ae650dSJack F Vogel         bus_dmamap_sync(txr->dma.tag, txr->dma.map,
85761ae650dSJack F Vogel             BUS_DMASYNC_POSTREAD);
85861ae650dSJack F Vogel 	/*
85961ae650dSJack F Vogel 	** The HEAD index of the ring is written in a
86061ae650dSJack F Vogel 	** defined location, this rather than a done bit
86161ae650dSJack F Vogel 	** is what is used to keep track of what must be
86261ae650dSJack F Vogel 	** 'cleaned'.
86361ae650dSJack F Vogel 	*/
86461ae650dSJack F Vogel 	while (first != head) {
86561ae650dSJack F Vogel 		/* We clean the range of the packet */
86661ae650dSJack F Vogel 		while (first != done) {
86761ae650dSJack F Vogel 			++txr->avail;
86861ae650dSJack F Vogel 			++processed;
86961ae650dSJack F Vogel 
87061ae650dSJack F Vogel 			if (buf->m_head) {
87161ae650dSJack F Vogel 				txr->bytes += /* for ITR adjustment */
87261ae650dSJack F Vogel 				    buf->m_head->m_pkthdr.len;
87361ae650dSJack F Vogel 				txr->tx_bytes += /* for TX stats */
87461ae650dSJack F Vogel 				    buf->m_head->m_pkthdr.len;
87561ae650dSJack F Vogel 				bus_dmamap_sync(buf->tag,
87661ae650dSJack F Vogel 				    buf->map,
87761ae650dSJack F Vogel 				    BUS_DMASYNC_POSTWRITE);
87861ae650dSJack F Vogel 				bus_dmamap_unload(buf->tag,
87961ae650dSJack F Vogel 				    buf->map);
88061ae650dSJack F Vogel 				m_freem(buf->m_head);
88161ae650dSJack F Vogel 				buf->m_head = NULL;
88261ae650dSJack F Vogel 				buf->map = NULL;
88361ae650dSJack F Vogel 			}
88461ae650dSJack F Vogel 			buf->eop_index = -1;
88561ae650dSJack F Vogel 
88661ae650dSJack F Vogel 			if (++first == que->num_desc)
88761ae650dSJack F Vogel 				first = 0;
88861ae650dSJack F Vogel 
88961ae650dSJack F Vogel 			buf = &txr->buffers[first];
89061ae650dSJack F Vogel 			tx_desc = &txr->base[first];
89161ae650dSJack F Vogel 		}
89261ae650dSJack F Vogel 		++txr->packets;
89361ae650dSJack F Vogel 		/* See if there is more work now */
89461ae650dSJack F Vogel 		last = buf->eop_index;
89561ae650dSJack F Vogel 		if (last != -1) {
89661ae650dSJack F Vogel 			eop_desc = &txr->base[last];
89761ae650dSJack F Vogel 			/* Get next done point */
89861ae650dSJack F Vogel 			if (++last == que->num_desc) last = 0;
89961ae650dSJack F Vogel 			done = last;
90061ae650dSJack F Vogel 		} else
90161ae650dSJack F Vogel 			break;
90261ae650dSJack F Vogel 	}
90361ae650dSJack F Vogel 	bus_dmamap_sync(txr->dma.tag, txr->dma.map,
90461ae650dSJack F Vogel 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
90561ae650dSJack F Vogel 
90661ae650dSJack F Vogel 	txr->next_to_clean = first;
90761ae650dSJack F Vogel 
90861ae650dSJack F Vogel 
90961ae650dSJack F Vogel 	/*
91061ae650dSJack F Vogel 	** Hang detection, we know there's
91161ae650dSJack F Vogel 	** work outstanding or the first return
91261ae650dSJack F Vogel 	** would have been taken, so indicate an
91361ae650dSJack F Vogel 	** unsuccessful pass, in local_timer if
91461ae650dSJack F Vogel 	** the value is too great the queue will
91561ae650dSJack F Vogel 	** be considered hung. If anything has been
91661ae650dSJack F Vogel 	** cleaned then reset the state.
91761ae650dSJack F Vogel 	*/
91861ae650dSJack F Vogel 	if ((processed == 0) && (que->busy != IXL_QUEUE_HUNG))
91961ae650dSJack F Vogel 		++que->busy;
92061ae650dSJack F Vogel 
92161ae650dSJack F Vogel 	if (processed)
92261ae650dSJack F Vogel 		que->busy = 1; /* Note this turns off HUNG */
92361ae650dSJack F Vogel 
92461ae650dSJack F Vogel 	/*
92561ae650dSJack F Vogel 	 * If there are no pending descriptors, clear the timeout.
92661ae650dSJack F Vogel 	 */
92761ae650dSJack F Vogel 	if (txr->avail == que->num_desc) {
92861ae650dSJack F Vogel 		que->busy = 0;
92961ae650dSJack F Vogel 		return FALSE;
93061ae650dSJack F Vogel 	}
93161ae650dSJack F Vogel 
93261ae650dSJack F Vogel 	return TRUE;
93361ae650dSJack F Vogel }
93461ae650dSJack F Vogel 
93561ae650dSJack F Vogel /*********************************************************************
93661ae650dSJack F Vogel  *
93761ae650dSJack F Vogel  *  Refresh mbuf buffers for RX descriptor rings
93861ae650dSJack F Vogel  *   - now keeps its own state so discards due to resource
93961ae650dSJack F Vogel  *     exhaustion are unnecessary, if an mbuf cannot be obtained
94061ae650dSJack F Vogel  *     it just returns, keeping its placeholder, thus it can simply
94161ae650dSJack F Vogel  *     be recalled to try again.
94261ae650dSJack F Vogel  *
94361ae650dSJack F Vogel  **********************************************************************/
94461ae650dSJack F Vogel static void
94561ae650dSJack F Vogel ixl_refresh_mbufs(struct ixl_queue *que, int limit)
94661ae650dSJack F Vogel {
94761ae650dSJack F Vogel 	struct ixl_vsi		*vsi = que->vsi;
94861ae650dSJack F Vogel 	struct rx_ring		*rxr = &que->rxr;
94961ae650dSJack F Vogel 	bus_dma_segment_t	hseg[1];
95061ae650dSJack F Vogel 	bus_dma_segment_t	pseg[1];
95161ae650dSJack F Vogel 	struct ixl_rx_buf	*buf;
95261ae650dSJack F Vogel 	struct mbuf		*mh, *mp;
95361ae650dSJack F Vogel 	int			i, j, nsegs, error;
95461ae650dSJack F Vogel 	bool			refreshed = FALSE;
95561ae650dSJack F Vogel 
95661ae650dSJack F Vogel 	i = j = rxr->next_refresh;
95761ae650dSJack F Vogel 	/* Control the loop with one beyond */
95861ae650dSJack F Vogel 	if (++j == que->num_desc)
95961ae650dSJack F Vogel 		j = 0;
96061ae650dSJack F Vogel 
96161ae650dSJack F Vogel 	while (j != limit) {
96261ae650dSJack F Vogel 		buf = &rxr->buffers[i];
96361ae650dSJack F Vogel 		if (rxr->hdr_split == FALSE)
96461ae650dSJack F Vogel 			goto no_split;
96561ae650dSJack F Vogel 
96661ae650dSJack F Vogel 		if (buf->m_head == NULL) {
96761ae650dSJack F Vogel 			mh = m_gethdr(M_NOWAIT, MT_DATA);
96861ae650dSJack F Vogel 			if (mh == NULL)
96961ae650dSJack F Vogel 				goto update;
97061ae650dSJack F Vogel 		} else
97161ae650dSJack F Vogel 			mh = buf->m_head;
97261ae650dSJack F Vogel 
97361ae650dSJack F Vogel 		mh->m_pkthdr.len = mh->m_len = MHLEN;
97461ae650dSJack F Vogel 		mh->m_len = MHLEN;
97561ae650dSJack F Vogel 		mh->m_flags |= M_PKTHDR;
97661ae650dSJack F Vogel 		/* Get the memory mapping */
97761ae650dSJack F Vogel 		error = bus_dmamap_load_mbuf_sg(rxr->htag,
97861ae650dSJack F Vogel 		    buf->hmap, mh, hseg, &nsegs, BUS_DMA_NOWAIT);
97961ae650dSJack F Vogel 		if (error != 0) {
98061ae650dSJack F Vogel 			printf("Refresh mbufs: hdr dmamap load"
98161ae650dSJack F Vogel 			    " failure - %d\n", error);
98261ae650dSJack F Vogel 			m_free(mh);
98361ae650dSJack F Vogel 			buf->m_head = NULL;
98461ae650dSJack F Vogel 			goto update;
98561ae650dSJack F Vogel 		}
98661ae650dSJack F Vogel 		buf->m_head = mh;
98761ae650dSJack F Vogel 		bus_dmamap_sync(rxr->htag, buf->hmap,
98861ae650dSJack F Vogel 		    BUS_DMASYNC_PREREAD);
98961ae650dSJack F Vogel 		rxr->base[i].read.hdr_addr =
99061ae650dSJack F Vogel 		   htole64(hseg[0].ds_addr);
99161ae650dSJack F Vogel 
99261ae650dSJack F Vogel no_split:
99361ae650dSJack F Vogel 		if (buf->m_pack == NULL) {
99461ae650dSJack F Vogel 			mp = m_getjcl(M_NOWAIT, MT_DATA,
99561ae650dSJack F Vogel 			    M_PKTHDR, rxr->mbuf_sz);
99661ae650dSJack F Vogel 			if (mp == NULL)
99761ae650dSJack F Vogel 				goto update;
99861ae650dSJack F Vogel 		} else
99961ae650dSJack F Vogel 			mp = buf->m_pack;
100061ae650dSJack F Vogel 
100161ae650dSJack F Vogel 		mp->m_pkthdr.len = mp->m_len = rxr->mbuf_sz;
100261ae650dSJack F Vogel 		/* Get the memory mapping */
100361ae650dSJack F Vogel 		error = bus_dmamap_load_mbuf_sg(rxr->ptag,
100461ae650dSJack F Vogel 		    buf->pmap, mp, pseg, &nsegs, BUS_DMA_NOWAIT);
100561ae650dSJack F Vogel 		if (error != 0) {
100661ae650dSJack F Vogel 			printf("Refresh mbufs: payload dmamap load"
100761ae650dSJack F Vogel 			    " failure - %d\n", error);
100861ae650dSJack F Vogel 			m_free(mp);
100961ae650dSJack F Vogel 			buf->m_pack = NULL;
101061ae650dSJack F Vogel 			goto update;
101161ae650dSJack F Vogel 		}
101261ae650dSJack F Vogel 		buf->m_pack = mp;
101361ae650dSJack F Vogel 		bus_dmamap_sync(rxr->ptag, buf->pmap,
101461ae650dSJack F Vogel 		    BUS_DMASYNC_PREREAD);
101561ae650dSJack F Vogel 		rxr->base[i].read.pkt_addr =
101661ae650dSJack F Vogel 		   htole64(pseg[0].ds_addr);
101761ae650dSJack F Vogel 		/* Used only when doing header split */
101861ae650dSJack F Vogel 		rxr->base[i].read.hdr_addr = 0;
101961ae650dSJack F Vogel 
102061ae650dSJack F Vogel 		refreshed = TRUE;
102161ae650dSJack F Vogel 		/* Next is precalculated */
102261ae650dSJack F Vogel 		i = j;
102361ae650dSJack F Vogel 		rxr->next_refresh = i;
102461ae650dSJack F Vogel 		if (++j == que->num_desc)
102561ae650dSJack F Vogel 			j = 0;
102661ae650dSJack F Vogel 	}
102761ae650dSJack F Vogel update:
102861ae650dSJack F Vogel 	if (refreshed) /* Update hardware tail index */
102961ae650dSJack F Vogel 		wr32(vsi->hw, rxr->tail, rxr->next_refresh);
103061ae650dSJack F Vogel 	return;
103161ae650dSJack F Vogel }
103261ae650dSJack F Vogel 
103361ae650dSJack F Vogel 
103461ae650dSJack F Vogel /*********************************************************************
103561ae650dSJack F Vogel  *
103661ae650dSJack F Vogel  *  Allocate memory for rx_buffer structures. Since we use one
103761ae650dSJack F Vogel  *  rx_buffer per descriptor, the maximum number of rx_buffer's
103861ae650dSJack F Vogel  *  that we'll need is equal to the number of receive descriptors
103961ae650dSJack F Vogel  *  that we've defined.
104061ae650dSJack F Vogel  *
104161ae650dSJack F Vogel  **********************************************************************/
104261ae650dSJack F Vogel int
104361ae650dSJack F Vogel ixl_allocate_rx_data(struct ixl_queue *que)
104461ae650dSJack F Vogel {
104561ae650dSJack F Vogel 	struct rx_ring		*rxr = &que->rxr;
104661ae650dSJack F Vogel 	struct ixl_vsi		*vsi = que->vsi;
104761ae650dSJack F Vogel 	device_t 		dev = vsi->dev;
104861ae650dSJack F Vogel 	struct ixl_rx_buf 	*buf;
104961ae650dSJack F Vogel 	int             	i, bsize, error;
105061ae650dSJack F Vogel 
105161ae650dSJack F Vogel 	bsize = sizeof(struct ixl_rx_buf) * que->num_desc;
105261ae650dSJack F Vogel 	if (!(rxr->buffers =
105361ae650dSJack F Vogel 	    (struct ixl_rx_buf *) malloc(bsize,
105461ae650dSJack F Vogel 	    M_DEVBUF, M_NOWAIT | M_ZERO))) {
105561ae650dSJack F Vogel 		device_printf(dev, "Unable to allocate rx_buffer memory\n");
105661ae650dSJack F Vogel 		error = ENOMEM;
105761ae650dSJack F Vogel 		return (error);
105861ae650dSJack F Vogel 	}
105961ae650dSJack F Vogel 
106061ae650dSJack F Vogel 	if ((error = bus_dma_tag_create(NULL,	/* parent */
106161ae650dSJack F Vogel 				   1, 0,	/* alignment, bounds */
106261ae650dSJack F Vogel 				   BUS_SPACE_MAXADDR,	/* lowaddr */
106361ae650dSJack F Vogel 				   BUS_SPACE_MAXADDR,	/* highaddr */
106461ae650dSJack F Vogel 				   NULL, NULL,		/* filter, filterarg */
106561ae650dSJack F Vogel 				   MSIZE,		/* maxsize */
106661ae650dSJack F Vogel 				   1,			/* nsegments */
106761ae650dSJack F Vogel 				   MSIZE,		/* maxsegsize */
106861ae650dSJack F Vogel 				   0,			/* flags */
106961ae650dSJack F Vogel 				   NULL,		/* lockfunc */
107061ae650dSJack F Vogel 				   NULL,		/* lockfuncarg */
107161ae650dSJack F Vogel 				   &rxr->htag))) {
107261ae650dSJack F Vogel 		device_printf(dev, "Unable to create RX DMA htag\n");
107361ae650dSJack F Vogel 		return (error);
107461ae650dSJack F Vogel 	}
107561ae650dSJack F Vogel 
107661ae650dSJack F Vogel 	if ((error = bus_dma_tag_create(NULL,	/* parent */
107761ae650dSJack F Vogel 				   1, 0,	/* alignment, bounds */
107861ae650dSJack F Vogel 				   BUS_SPACE_MAXADDR,	/* lowaddr */
107961ae650dSJack F Vogel 				   BUS_SPACE_MAXADDR,	/* highaddr */
108061ae650dSJack F Vogel 				   NULL, NULL,		/* filter, filterarg */
108161ae650dSJack F Vogel 				   MJUM16BYTES,		/* maxsize */
108261ae650dSJack F Vogel 				   1,			/* nsegments */
108361ae650dSJack F Vogel 				   MJUM16BYTES,		/* maxsegsize */
108461ae650dSJack F Vogel 				   0,			/* flags */
108561ae650dSJack F Vogel 				   NULL,		/* lockfunc */
108661ae650dSJack F Vogel 				   NULL,		/* lockfuncarg */
108761ae650dSJack F Vogel 				   &rxr->ptag))) {
108861ae650dSJack F Vogel 		device_printf(dev, "Unable to create RX DMA ptag\n");
108961ae650dSJack F Vogel 		return (error);
109061ae650dSJack F Vogel 	}
109161ae650dSJack F Vogel 
109261ae650dSJack F Vogel 	for (i = 0; i < que->num_desc; i++) {
109361ae650dSJack F Vogel 		buf = &rxr->buffers[i];
109461ae650dSJack F Vogel 		error = bus_dmamap_create(rxr->htag,
109561ae650dSJack F Vogel 		    BUS_DMA_NOWAIT, &buf->hmap);
109661ae650dSJack F Vogel 		if (error) {
109761ae650dSJack F Vogel 			device_printf(dev, "Unable to create RX head map\n");
109861ae650dSJack F Vogel 			break;
109961ae650dSJack F Vogel 		}
110061ae650dSJack F Vogel 		error = bus_dmamap_create(rxr->ptag,
110161ae650dSJack F Vogel 		    BUS_DMA_NOWAIT, &buf->pmap);
110261ae650dSJack F Vogel 		if (error) {
110361ae650dSJack F Vogel 			device_printf(dev, "Unable to create RX pkt map\n");
110461ae650dSJack F Vogel 			break;
110561ae650dSJack F Vogel 		}
110661ae650dSJack F Vogel 	}
110761ae650dSJack F Vogel 
110861ae650dSJack F Vogel 	return (error);
110961ae650dSJack F Vogel }
111061ae650dSJack F Vogel 
111161ae650dSJack F Vogel 
111261ae650dSJack F Vogel /*********************************************************************
111361ae650dSJack F Vogel  *
111461ae650dSJack F Vogel  *  (Re)Initialize the queue receive ring and its buffers.
111561ae650dSJack F Vogel  *
111661ae650dSJack F Vogel  **********************************************************************/
111761ae650dSJack F Vogel int
111861ae650dSJack F Vogel ixl_init_rx_ring(struct ixl_queue *que)
111961ae650dSJack F Vogel {
1120b3f7259bSBjoern A. Zeeb 	struct	rx_ring 	*rxr = &que->rxr;
1121393c4bb1SJack F Vogel 	struct ixl_vsi		*vsi = que->vsi;
1122a2cf30b9SBjoern A. Zeeb #if defined(INET6) || defined(INET)
112361ae650dSJack F Vogel 	struct ifnet		*ifp = vsi->ifp;
112461ae650dSJack F Vogel 	struct lro_ctrl		*lro = &rxr->lro;
1125b3f7259bSBjoern A. Zeeb #endif
112661ae650dSJack F Vogel 	struct ixl_rx_buf	*buf;
112761ae650dSJack F Vogel 	bus_dma_segment_t	pseg[1], hseg[1];
112861ae650dSJack F Vogel 	int			rsize, nsegs, error = 0;
112961ae650dSJack F Vogel 
113061ae650dSJack F Vogel 	IXL_RX_LOCK(rxr);
113161ae650dSJack F Vogel 	/* Clear the ring contents */
113261ae650dSJack F Vogel 	rsize = roundup2(que->num_desc *
113361ae650dSJack F Vogel 	    sizeof(union i40e_rx_desc), DBA_ALIGN);
113461ae650dSJack F Vogel 	bzero((void *)rxr->base, rsize);
113561ae650dSJack F Vogel 	/* Cleanup any existing buffers */
113661ae650dSJack F Vogel 	for (int i = 0; i < que->num_desc; i++) {
113761ae650dSJack F Vogel 		buf = &rxr->buffers[i];
113861ae650dSJack F Vogel 		if (buf->m_head != NULL) {
113961ae650dSJack F Vogel 			bus_dmamap_sync(rxr->htag, buf->hmap,
114061ae650dSJack F Vogel 			    BUS_DMASYNC_POSTREAD);
114161ae650dSJack F Vogel 			bus_dmamap_unload(rxr->htag, buf->hmap);
114261ae650dSJack F Vogel 			buf->m_head->m_flags |= M_PKTHDR;
114361ae650dSJack F Vogel 			m_freem(buf->m_head);
114461ae650dSJack F Vogel 		}
114561ae650dSJack F Vogel 		if (buf->m_pack != NULL) {
114661ae650dSJack F Vogel 			bus_dmamap_sync(rxr->ptag, buf->pmap,
114761ae650dSJack F Vogel 			    BUS_DMASYNC_POSTREAD);
114861ae650dSJack F Vogel 			bus_dmamap_unload(rxr->ptag, buf->pmap);
114961ae650dSJack F Vogel 			buf->m_pack->m_flags |= M_PKTHDR;
115061ae650dSJack F Vogel 			m_freem(buf->m_pack);
115161ae650dSJack F Vogel 		}
115261ae650dSJack F Vogel 		buf->m_head = NULL;
115361ae650dSJack F Vogel 		buf->m_pack = NULL;
115461ae650dSJack F Vogel 	}
115561ae650dSJack F Vogel 
115661ae650dSJack F Vogel 	/* header split is off */
115761ae650dSJack F Vogel 	rxr->hdr_split = FALSE;
115861ae650dSJack F Vogel 
115961ae650dSJack F Vogel 	/* Now replenish the mbufs */
116061ae650dSJack F Vogel 	for (int j = 0; j != que->num_desc; ++j) {
116161ae650dSJack F Vogel 		struct mbuf	*mh, *mp;
116261ae650dSJack F Vogel 
116361ae650dSJack F Vogel 		buf = &rxr->buffers[j];
1164bc8b78d3SLuigi Rizzo 
116561ae650dSJack F Vogel 		/*
116661ae650dSJack F Vogel 		** Don't allocate mbufs if not
116761ae650dSJack F Vogel 		** doing header split, its wasteful
116861ae650dSJack F Vogel 		*/
116961ae650dSJack F Vogel 		if (rxr->hdr_split == FALSE)
117061ae650dSJack F Vogel 			goto skip_head;
117161ae650dSJack F Vogel 
117261ae650dSJack F Vogel 		/* First the header */
117361ae650dSJack F Vogel 		buf->m_head = m_gethdr(M_NOWAIT, MT_DATA);
117461ae650dSJack F Vogel 		if (buf->m_head == NULL) {
117561ae650dSJack F Vogel 			error = ENOBUFS;
117661ae650dSJack F Vogel 			goto fail;
117761ae650dSJack F Vogel 		}
117861ae650dSJack F Vogel 		m_adj(buf->m_head, ETHER_ALIGN);
117961ae650dSJack F Vogel 		mh = buf->m_head;
118061ae650dSJack F Vogel 		mh->m_len = mh->m_pkthdr.len = MHLEN;
118161ae650dSJack F Vogel 		mh->m_flags |= M_PKTHDR;
118261ae650dSJack F Vogel 		/* Get the memory mapping */
118361ae650dSJack F Vogel 		error = bus_dmamap_load_mbuf_sg(rxr->htag,
118461ae650dSJack F Vogel 		    buf->hmap, buf->m_head, hseg,
118561ae650dSJack F Vogel 		    &nsegs, BUS_DMA_NOWAIT);
118661ae650dSJack F Vogel 		if (error != 0) /* Nothing elegant to do here */
118761ae650dSJack F Vogel 			goto fail;
118861ae650dSJack F Vogel 		bus_dmamap_sync(rxr->htag,
118961ae650dSJack F Vogel 		    buf->hmap, BUS_DMASYNC_PREREAD);
119061ae650dSJack F Vogel 		/* Update descriptor */
119161ae650dSJack F Vogel 		rxr->base[j].read.hdr_addr = htole64(hseg[0].ds_addr);
119261ae650dSJack F Vogel 
119361ae650dSJack F Vogel skip_head:
119461ae650dSJack F Vogel 		/* Now the payload cluster */
119561ae650dSJack F Vogel 		buf->m_pack = m_getjcl(M_NOWAIT, MT_DATA,
119661ae650dSJack F Vogel 		    M_PKTHDR, rxr->mbuf_sz);
119761ae650dSJack F Vogel 		if (buf->m_pack == NULL) {
119861ae650dSJack F Vogel 			error = ENOBUFS;
119961ae650dSJack F Vogel                         goto fail;
120061ae650dSJack F Vogel 		}
120161ae650dSJack F Vogel 		mp = buf->m_pack;
120261ae650dSJack F Vogel 		mp->m_pkthdr.len = mp->m_len = rxr->mbuf_sz;
120361ae650dSJack F Vogel 		/* Get the memory mapping */
120461ae650dSJack F Vogel 		error = bus_dmamap_load_mbuf_sg(rxr->ptag,
120561ae650dSJack F Vogel 		    buf->pmap, mp, pseg,
120661ae650dSJack F Vogel 		    &nsegs, BUS_DMA_NOWAIT);
120761ae650dSJack F Vogel 		if (error != 0)
120861ae650dSJack F Vogel                         goto fail;
120961ae650dSJack F Vogel 		bus_dmamap_sync(rxr->ptag,
121061ae650dSJack F Vogel 		    buf->pmap, BUS_DMASYNC_PREREAD);
121161ae650dSJack F Vogel 		/* Update descriptor */
121261ae650dSJack F Vogel 		rxr->base[j].read.pkt_addr = htole64(pseg[0].ds_addr);
121361ae650dSJack F Vogel 		rxr->base[j].read.hdr_addr = 0;
121461ae650dSJack F Vogel 	}
121561ae650dSJack F Vogel 
121661ae650dSJack F Vogel 
121761ae650dSJack F Vogel 	/* Setup our descriptor indices */
121861ae650dSJack F Vogel 	rxr->next_check = 0;
121961ae650dSJack F Vogel 	rxr->next_refresh = 0;
122061ae650dSJack F Vogel 	rxr->lro_enabled = FALSE;
122161ae650dSJack F Vogel 	rxr->split = 0;
122261ae650dSJack F Vogel 	rxr->bytes = 0;
122361ae650dSJack F Vogel 	rxr->discard = FALSE;
122461ae650dSJack F Vogel 
1225e5100ee2SJack F Vogel 	wr32(vsi->hw, rxr->tail, que->num_desc - 1);
1226e5100ee2SJack F Vogel 	ixl_flush(vsi->hw);
1227e5100ee2SJack F Vogel 
1228b3f7259bSBjoern A. Zeeb #if defined(INET6) || defined(INET)
122961ae650dSJack F Vogel 	/*
123061ae650dSJack F Vogel 	** Now set up the LRO interface:
123161ae650dSJack F Vogel 	*/
123261ae650dSJack F Vogel 	if (ifp->if_capenable & IFCAP_LRO) {
123361ae650dSJack F Vogel 		int err = tcp_lro_init(lro);
123461ae650dSJack F Vogel 		if (err) {
123561ae650dSJack F Vogel 			if_printf(ifp, "queue %d: LRO Initialization failed!\n", que->me);
123661ae650dSJack F Vogel 			goto fail;
123761ae650dSJack F Vogel 		}
123861ae650dSJack F Vogel 		INIT_DBG_IF(ifp, "queue %d: RX Soft LRO Initialized", que->me);
123961ae650dSJack F Vogel 		rxr->lro_enabled = TRUE;
124061ae650dSJack F Vogel 		lro->ifp = vsi->ifp;
124161ae650dSJack F Vogel 	}
1242b3f7259bSBjoern A. Zeeb #endif
124361ae650dSJack F Vogel 
124461ae650dSJack F Vogel 	bus_dmamap_sync(rxr->dma.tag, rxr->dma.map,
124561ae650dSJack F Vogel 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
124661ae650dSJack F Vogel 
124761ae650dSJack F Vogel fail:
124861ae650dSJack F Vogel 	IXL_RX_UNLOCK(rxr);
124961ae650dSJack F Vogel 	return (error);
125061ae650dSJack F Vogel }
125161ae650dSJack F Vogel 
125261ae650dSJack F Vogel 
125361ae650dSJack F Vogel /*********************************************************************
125461ae650dSJack F Vogel  *
125561ae650dSJack F Vogel  *  Free station receive ring data structures
125661ae650dSJack F Vogel  *
125761ae650dSJack F Vogel  **********************************************************************/
125861ae650dSJack F Vogel void
125961ae650dSJack F Vogel ixl_free_que_rx(struct ixl_queue *que)
126061ae650dSJack F Vogel {
126161ae650dSJack F Vogel 	struct rx_ring		*rxr = &que->rxr;
126261ae650dSJack F Vogel 	struct ixl_rx_buf	*buf;
126361ae650dSJack F Vogel 
126461ae650dSJack F Vogel 	INIT_DBG_IF(que->vsi->ifp, "queue %d: begin", que->me);
126561ae650dSJack F Vogel 
126661ae650dSJack F Vogel 	/* Cleanup any existing buffers */
126761ae650dSJack F Vogel 	if (rxr->buffers != NULL) {
126861ae650dSJack F Vogel 		for (int i = 0; i < que->num_desc; i++) {
126961ae650dSJack F Vogel 			buf = &rxr->buffers[i];
127061ae650dSJack F Vogel 			if (buf->m_head != NULL) {
127161ae650dSJack F Vogel 				bus_dmamap_sync(rxr->htag, buf->hmap,
127261ae650dSJack F Vogel 				    BUS_DMASYNC_POSTREAD);
127361ae650dSJack F Vogel 				bus_dmamap_unload(rxr->htag, buf->hmap);
127461ae650dSJack F Vogel 				buf->m_head->m_flags |= M_PKTHDR;
127561ae650dSJack F Vogel 				m_freem(buf->m_head);
127661ae650dSJack F Vogel 			}
127761ae650dSJack F Vogel 			if (buf->m_pack != NULL) {
127861ae650dSJack F Vogel 				bus_dmamap_sync(rxr->ptag, buf->pmap,
127961ae650dSJack F Vogel 				    BUS_DMASYNC_POSTREAD);
128061ae650dSJack F Vogel 				bus_dmamap_unload(rxr->ptag, buf->pmap);
128161ae650dSJack F Vogel 				buf->m_pack->m_flags |= M_PKTHDR;
128261ae650dSJack F Vogel 				m_freem(buf->m_pack);
128361ae650dSJack F Vogel 			}
128461ae650dSJack F Vogel 			buf->m_head = NULL;
128561ae650dSJack F Vogel 			buf->m_pack = NULL;
128661ae650dSJack F Vogel 			if (buf->hmap != NULL) {
128761ae650dSJack F Vogel 				bus_dmamap_destroy(rxr->htag, buf->hmap);
128861ae650dSJack F Vogel 				buf->hmap = NULL;
128961ae650dSJack F Vogel 			}
129061ae650dSJack F Vogel 			if (buf->pmap != NULL) {
129161ae650dSJack F Vogel 				bus_dmamap_destroy(rxr->ptag, buf->pmap);
129261ae650dSJack F Vogel 				buf->pmap = NULL;
129361ae650dSJack F Vogel 			}
129461ae650dSJack F Vogel 		}
129561ae650dSJack F Vogel 		if (rxr->buffers != NULL) {
129661ae650dSJack F Vogel 			free(rxr->buffers, M_DEVBUF);
129761ae650dSJack F Vogel 			rxr->buffers = NULL;
129861ae650dSJack F Vogel 		}
129961ae650dSJack F Vogel 	}
130061ae650dSJack F Vogel 
130161ae650dSJack F Vogel 	if (rxr->htag != NULL) {
130261ae650dSJack F Vogel 		bus_dma_tag_destroy(rxr->htag);
130361ae650dSJack F Vogel 		rxr->htag = NULL;
130461ae650dSJack F Vogel 	}
130561ae650dSJack F Vogel 	if (rxr->ptag != NULL) {
130661ae650dSJack F Vogel 		bus_dma_tag_destroy(rxr->ptag);
130761ae650dSJack F Vogel 		rxr->ptag = NULL;
130861ae650dSJack F Vogel 	}
130961ae650dSJack F Vogel 
131061ae650dSJack F Vogel 	INIT_DBG_IF(que->vsi->ifp, "queue %d: end", que->me);
131161ae650dSJack F Vogel 	return;
131261ae650dSJack F Vogel }
131361ae650dSJack F Vogel 
131461ae650dSJack F Vogel static __inline void
131561ae650dSJack F Vogel ixl_rx_input(struct rx_ring *rxr, struct ifnet *ifp, struct mbuf *m, u8 ptype)
131661ae650dSJack F Vogel {
1317b3f7259bSBjoern A. Zeeb 
1318b3f7259bSBjoern A. Zeeb #if defined(INET6) || defined(INET)
131961ae650dSJack F Vogel         /*
132061ae650dSJack F Vogel          * ATM LRO is only for IPv4/TCP packets and TCP checksum of the packet
132161ae650dSJack F Vogel          * should be computed by hardware. Also it should not have VLAN tag in
132261ae650dSJack F Vogel          * ethernet header.
132361ae650dSJack F Vogel          */
132461ae650dSJack F Vogel         if (rxr->lro_enabled &&
132561ae650dSJack F Vogel             (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0 &&
132661ae650dSJack F Vogel             (m->m_pkthdr.csum_flags & (CSUM_DATA_VALID | CSUM_PSEUDO_HDR)) ==
132761ae650dSJack F Vogel             (CSUM_DATA_VALID | CSUM_PSEUDO_HDR)) {
132861ae650dSJack F Vogel                 /*
132961ae650dSJack F Vogel                  * Send to the stack if:
133061ae650dSJack F Vogel                  **  - LRO not enabled, or
133161ae650dSJack F Vogel                  **  - no LRO resources, or
133261ae650dSJack F Vogel                  **  - lro enqueue fails
133361ae650dSJack F Vogel                  */
133461ae650dSJack F Vogel                 if (rxr->lro.lro_cnt != 0)
133561ae650dSJack F Vogel                         if (tcp_lro_rx(&rxr->lro, m, 0) == 0)
133661ae650dSJack F Vogel                                 return;
133761ae650dSJack F Vogel         }
1338b3f7259bSBjoern A. Zeeb #endif
133961ae650dSJack F Vogel 	IXL_RX_UNLOCK(rxr);
134061ae650dSJack F Vogel         (*ifp->if_input)(ifp, m);
134161ae650dSJack F Vogel 	IXL_RX_LOCK(rxr);
134261ae650dSJack F Vogel }
134361ae650dSJack F Vogel 
134461ae650dSJack F Vogel 
134561ae650dSJack F Vogel static __inline void
134661ae650dSJack F Vogel ixl_rx_discard(struct rx_ring *rxr, int i)
134761ae650dSJack F Vogel {
134861ae650dSJack F Vogel 	struct ixl_rx_buf	*rbuf;
134961ae650dSJack F Vogel 
135061ae650dSJack F Vogel 	rbuf = &rxr->buffers[i];
135161ae650dSJack F Vogel 
135261ae650dSJack F Vogel         if (rbuf->fmp != NULL) {/* Partial chain ? */
135361ae650dSJack F Vogel 		rbuf->fmp->m_flags |= M_PKTHDR;
135461ae650dSJack F Vogel                 m_freem(rbuf->fmp);
135561ae650dSJack F Vogel                 rbuf->fmp = NULL;
135661ae650dSJack F Vogel 	}
135761ae650dSJack F Vogel 
135861ae650dSJack F Vogel 	/*
135961ae650dSJack F Vogel 	** With advanced descriptors the writeback
136061ae650dSJack F Vogel 	** clobbers the buffer addrs, so its easier
136161ae650dSJack F Vogel 	** to just free the existing mbufs and take
136261ae650dSJack F Vogel 	** the normal refresh path to get new buffers
136361ae650dSJack F Vogel 	** and mapping.
136461ae650dSJack F Vogel 	*/
136561ae650dSJack F Vogel 	if (rbuf->m_head) {
136661ae650dSJack F Vogel 		m_free(rbuf->m_head);
136761ae650dSJack F Vogel 		rbuf->m_head = NULL;
136861ae650dSJack F Vogel 	}
136961ae650dSJack F Vogel 
137061ae650dSJack F Vogel 	if (rbuf->m_pack) {
137161ae650dSJack F Vogel 		m_free(rbuf->m_pack);
137261ae650dSJack F Vogel 		rbuf->m_pack = NULL;
137361ae650dSJack F Vogel 	}
137461ae650dSJack F Vogel 
137561ae650dSJack F Vogel 	return;
137661ae650dSJack F Vogel }
137761ae650dSJack F Vogel 
13786b30e6aeSJack F Vogel #ifdef RSS
13796b30e6aeSJack F Vogel /*
1380b6c8f260SJack F Vogel ** i40e_ptype_to_hash: parse the packet type
13816b30e6aeSJack F Vogel ** to determine the appropriate hash.
13826b30e6aeSJack F Vogel */
13836b30e6aeSJack F Vogel static inline int
13846b30e6aeSJack F Vogel ixl_ptype_to_hash(u8 ptype)
13856b30e6aeSJack F Vogel {
13866b30e6aeSJack F Vogel         struct i40e_rx_ptype_decoded	decoded;
1387b2bdc62aSAdrian Chadd 	u8				ex = 0;
13886b30e6aeSJack F Vogel 
1389dcd7b3b2SJack F Vogel 	decoded = decode_rx_desc_ptype(ptype);
13906b30e6aeSJack F Vogel 	ex = decoded.outer_frag;
13916b30e6aeSJack F Vogel 
13926b30e6aeSJack F Vogel 	if (!decoded.known)
13936b30e6aeSJack F Vogel 		return M_HASHTYPE_OPAQUE;
13946b30e6aeSJack F Vogel 
13956b30e6aeSJack F Vogel 	if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_L2)
13966b30e6aeSJack F Vogel 		return M_HASHTYPE_OPAQUE;
13976b30e6aeSJack F Vogel 
13986b30e6aeSJack F Vogel 	/* Note: anything that gets to this point is IP */
13996b30e6aeSJack F Vogel         if (decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV6) {
14006b30e6aeSJack F Vogel 		switch (decoded.inner_prot) {
14016b30e6aeSJack F Vogel 			case I40E_RX_PTYPE_INNER_PROT_TCP:
14026b30e6aeSJack F Vogel 				if (ex)
14036b30e6aeSJack F Vogel 					return M_HASHTYPE_RSS_TCP_IPV6_EX;
14046b30e6aeSJack F Vogel 				else
14056b30e6aeSJack F Vogel 					return M_HASHTYPE_RSS_TCP_IPV6;
14066b30e6aeSJack F Vogel 			case I40E_RX_PTYPE_INNER_PROT_UDP:
14076b30e6aeSJack F Vogel 				if (ex)
14086b30e6aeSJack F Vogel 					return M_HASHTYPE_RSS_UDP_IPV6_EX;
14096b30e6aeSJack F Vogel 				else
14106b30e6aeSJack F Vogel 					return M_HASHTYPE_RSS_UDP_IPV6;
14116b30e6aeSJack F Vogel 			default:
14126b30e6aeSJack F Vogel 				if (ex)
14136b30e6aeSJack F Vogel 					return M_HASHTYPE_RSS_IPV6_EX;
14146b30e6aeSJack F Vogel 				else
14156b30e6aeSJack F Vogel 					return M_HASHTYPE_RSS_IPV6;
14166b30e6aeSJack F Vogel 		}
14176b30e6aeSJack F Vogel 	}
14186b30e6aeSJack F Vogel         if (decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV4) {
14196b30e6aeSJack F Vogel 		switch (decoded.inner_prot) {
14206b30e6aeSJack F Vogel 			case I40E_RX_PTYPE_INNER_PROT_TCP:
14216b30e6aeSJack F Vogel 					return M_HASHTYPE_RSS_TCP_IPV4;
14226b30e6aeSJack F Vogel 			case I40E_RX_PTYPE_INNER_PROT_UDP:
14236b30e6aeSJack F Vogel 				if (ex)
14246b30e6aeSJack F Vogel 					return M_HASHTYPE_RSS_UDP_IPV4_EX;
14256b30e6aeSJack F Vogel 				else
14266b30e6aeSJack F Vogel 					return M_HASHTYPE_RSS_UDP_IPV4;
14276b30e6aeSJack F Vogel 			default:
14286b30e6aeSJack F Vogel 					return M_HASHTYPE_RSS_IPV4;
14296b30e6aeSJack F Vogel 		}
14306b30e6aeSJack F Vogel 	}
14316b30e6aeSJack F Vogel 	/* We should never get here!! */
14326b30e6aeSJack F Vogel 	return M_HASHTYPE_OPAQUE;
14336b30e6aeSJack F Vogel }
14346b30e6aeSJack F Vogel #endif /* RSS */
143561ae650dSJack F Vogel 
143661ae650dSJack F Vogel /*********************************************************************
143761ae650dSJack F Vogel  *
143861ae650dSJack F Vogel  *  This routine executes in interrupt context. It replenishes
143961ae650dSJack F Vogel  *  the mbufs in the descriptor and sends data which has been
144061ae650dSJack F Vogel  *  dma'ed into host memory to upper layer.
144161ae650dSJack F Vogel  *
144261ae650dSJack F Vogel  *  We loop at most count times if count is > 0, or until done if
144361ae650dSJack F Vogel  *  count < 0.
144461ae650dSJack F Vogel  *
144561ae650dSJack F Vogel  *  Return TRUE for more work, FALSE for all clean.
144661ae650dSJack F Vogel  *********************************************************************/
144761ae650dSJack F Vogel bool
144861ae650dSJack F Vogel ixl_rxeof(struct ixl_queue *que, int count)
144961ae650dSJack F Vogel {
145061ae650dSJack F Vogel 	struct ixl_vsi		*vsi = que->vsi;
145161ae650dSJack F Vogel 	struct rx_ring		*rxr = &que->rxr;
145261ae650dSJack F Vogel 	struct ifnet		*ifp = vsi->ifp;
1453b3f7259bSBjoern A. Zeeb #if defined(INET6) || defined(INET)
145461ae650dSJack F Vogel 	struct lro_ctrl		*lro = &rxr->lro;
145561ae650dSJack F Vogel 	struct lro_entry	*queued;
1456b3f7259bSBjoern A. Zeeb #endif
145761ae650dSJack F Vogel 	int			i, nextp, processed = 0;
145861ae650dSJack F Vogel 	union i40e_rx_desc	*cur;
145961ae650dSJack F Vogel 	struct ixl_rx_buf	*rbuf, *nbuf;
146061ae650dSJack F Vogel 
146161ae650dSJack F Vogel 
146261ae650dSJack F Vogel 	IXL_RX_LOCK(rxr);
146361ae650dSJack F Vogel 
1464e5100ee2SJack F Vogel 
146561ae650dSJack F Vogel 	for (i = rxr->next_check; count != 0;) {
146661ae650dSJack F Vogel 		struct mbuf	*sendmp, *mh, *mp;
146761ae650dSJack F Vogel 		u32		rsc, status, error;
146861ae650dSJack F Vogel 		u16		hlen, plen, vtag;
146961ae650dSJack F Vogel 		u64		qword;
147061ae650dSJack F Vogel 		u8		ptype;
147161ae650dSJack F Vogel 		bool		eop;
147261ae650dSJack F Vogel 
147361ae650dSJack F Vogel 		/* Sync the ring. */
147461ae650dSJack F Vogel 		bus_dmamap_sync(rxr->dma.tag, rxr->dma.map,
147561ae650dSJack F Vogel 		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
147661ae650dSJack F Vogel 
147761ae650dSJack F Vogel 		cur = &rxr->base[i];
147861ae650dSJack F Vogel 		qword = le64toh(cur->wb.qword1.status_error_len);
147961ae650dSJack F Vogel 		status = (qword & I40E_RXD_QW1_STATUS_MASK)
148061ae650dSJack F Vogel 		    >> I40E_RXD_QW1_STATUS_SHIFT;
148161ae650dSJack F Vogel 		error = (qword & I40E_RXD_QW1_ERROR_MASK)
148261ae650dSJack F Vogel 		    >> I40E_RXD_QW1_ERROR_SHIFT;
148361ae650dSJack F Vogel 		plen = (qword & I40E_RXD_QW1_LENGTH_PBUF_MASK)
148461ae650dSJack F Vogel 		    >> I40E_RXD_QW1_LENGTH_PBUF_SHIFT;
148561ae650dSJack F Vogel 		hlen = (qword & I40E_RXD_QW1_LENGTH_HBUF_MASK)
148661ae650dSJack F Vogel 		    >> I40E_RXD_QW1_LENGTH_HBUF_SHIFT;
148761ae650dSJack F Vogel 		ptype = (qword & I40E_RXD_QW1_PTYPE_MASK)
148861ae650dSJack F Vogel 		    >> I40E_RXD_QW1_PTYPE_SHIFT;
148961ae650dSJack F Vogel 
149061ae650dSJack F Vogel 		if ((status & (1 << I40E_RX_DESC_STATUS_DD_SHIFT)) == 0) {
149161ae650dSJack F Vogel 			++rxr->not_done;
149261ae650dSJack F Vogel 			break;
149361ae650dSJack F Vogel 		}
149461ae650dSJack F Vogel 		if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
149561ae650dSJack F Vogel 			break;
149661ae650dSJack F Vogel 
149761ae650dSJack F Vogel 		count--;
149861ae650dSJack F Vogel 		sendmp = NULL;
149961ae650dSJack F Vogel 		nbuf = NULL;
150061ae650dSJack F Vogel 		rsc = 0;
150161ae650dSJack F Vogel 		cur->wb.qword1.status_error_len = 0;
150261ae650dSJack F Vogel 		rbuf = &rxr->buffers[i];
150361ae650dSJack F Vogel 		mh = rbuf->m_head;
150461ae650dSJack F Vogel 		mp = rbuf->m_pack;
150561ae650dSJack F Vogel 		eop = (status & (1 << I40E_RX_DESC_STATUS_EOF_SHIFT));
150661ae650dSJack F Vogel 		if (status & (1 << I40E_RX_DESC_STATUS_L2TAG1P_SHIFT))
150761ae650dSJack F Vogel 			vtag = le16toh(cur->wb.qword0.lo_dword.l2tag1);
150861ae650dSJack F Vogel 		else
150961ae650dSJack F Vogel 			vtag = 0;
151061ae650dSJack F Vogel 
151161ae650dSJack F Vogel 		/*
151261ae650dSJack F Vogel 		** Make sure bad packets are discarded,
151361ae650dSJack F Vogel 		** note that only EOP descriptor has valid
151461ae650dSJack F Vogel 		** error results.
151561ae650dSJack F Vogel 		*/
151661ae650dSJack F Vogel                 if (eop && (error & (1 << I40E_RX_DESC_ERROR_RXE_SHIFT))) {
151761ae650dSJack F Vogel 			rxr->discarded++;
151861ae650dSJack F Vogel 			ixl_rx_discard(rxr, i);
151961ae650dSJack F Vogel 			goto next_desc;
152061ae650dSJack F Vogel 		}
152161ae650dSJack F Vogel 
152261ae650dSJack F Vogel 		/* Prefetch the next buffer */
152361ae650dSJack F Vogel 		if (!eop) {
152461ae650dSJack F Vogel 			nextp = i + 1;
152561ae650dSJack F Vogel 			if (nextp == que->num_desc)
152661ae650dSJack F Vogel 				nextp = 0;
152761ae650dSJack F Vogel 			nbuf = &rxr->buffers[nextp];
152861ae650dSJack F Vogel 			prefetch(nbuf);
152961ae650dSJack F Vogel 		}
153061ae650dSJack F Vogel 
153161ae650dSJack F Vogel 		/*
153261ae650dSJack F Vogel 		** The header mbuf is ONLY used when header
153361ae650dSJack F Vogel 		** split is enabled, otherwise we get normal
153461ae650dSJack F Vogel 		** behavior, ie, both header and payload
153561ae650dSJack F Vogel 		** are DMA'd into the payload buffer.
153661ae650dSJack F Vogel 		**
153761ae650dSJack F Vogel 		** Rather than using the fmp/lmp global pointers
153861ae650dSJack F Vogel 		** we now keep the head of a packet chain in the
153961ae650dSJack F Vogel 		** buffer struct and pass this along from one
154061ae650dSJack F Vogel 		** descriptor to the next, until we get EOP.
154161ae650dSJack F Vogel 		*/
154261ae650dSJack F Vogel 		if (rxr->hdr_split && (rbuf->fmp == NULL)) {
154361ae650dSJack F Vogel 			if (hlen > IXL_RX_HDR)
154461ae650dSJack F Vogel 				hlen = IXL_RX_HDR;
154561ae650dSJack F Vogel 			mh->m_len = hlen;
154661ae650dSJack F Vogel 			mh->m_flags |= M_PKTHDR;
154761ae650dSJack F Vogel 			mh->m_next = NULL;
154861ae650dSJack F Vogel 			mh->m_pkthdr.len = mh->m_len;
154961ae650dSJack F Vogel 			/* Null buf pointer so it is refreshed */
155061ae650dSJack F Vogel 			rbuf->m_head = NULL;
155161ae650dSJack F Vogel 			/*
155261ae650dSJack F Vogel 			** Check the payload length, this
155361ae650dSJack F Vogel 			** could be zero if its a small
155461ae650dSJack F Vogel 			** packet.
155561ae650dSJack F Vogel 			*/
155661ae650dSJack F Vogel 			if (plen > 0) {
155761ae650dSJack F Vogel 				mp->m_len = plen;
155861ae650dSJack F Vogel 				mp->m_next = NULL;
155961ae650dSJack F Vogel 				mp->m_flags &= ~M_PKTHDR;
156061ae650dSJack F Vogel 				mh->m_next = mp;
156161ae650dSJack F Vogel 				mh->m_pkthdr.len += mp->m_len;
156261ae650dSJack F Vogel 				/* Null buf pointer so it is refreshed */
156361ae650dSJack F Vogel 				rbuf->m_pack = NULL;
156461ae650dSJack F Vogel 				rxr->split++;
156561ae650dSJack F Vogel 			}
156661ae650dSJack F Vogel 			/*
156761ae650dSJack F Vogel 			** Now create the forward
156861ae650dSJack F Vogel 			** chain so when complete
156961ae650dSJack F Vogel 			** we wont have to.
157061ae650dSJack F Vogel 			*/
157161ae650dSJack F Vogel                         if (eop == 0) {
157261ae650dSJack F Vogel 				/* stash the chain head */
157361ae650dSJack F Vogel                                 nbuf->fmp = mh;
157461ae650dSJack F Vogel 				/* Make forward chain */
157561ae650dSJack F Vogel                                 if (plen)
157661ae650dSJack F Vogel                                         mp->m_next = nbuf->m_pack;
157761ae650dSJack F Vogel                                 else
157861ae650dSJack F Vogel                                         mh->m_next = nbuf->m_pack;
157961ae650dSJack F Vogel                         } else {
158061ae650dSJack F Vogel 				/* Singlet, prepare to send */
158161ae650dSJack F Vogel                                 sendmp = mh;
158261ae650dSJack F Vogel                                 if (vtag) {
158361ae650dSJack F Vogel                                         sendmp->m_pkthdr.ether_vtag = vtag;
158461ae650dSJack F Vogel                                         sendmp->m_flags |= M_VLANTAG;
158561ae650dSJack F Vogel                                 }
158661ae650dSJack F Vogel                         }
158761ae650dSJack F Vogel 		} else {
158861ae650dSJack F Vogel 			/*
158961ae650dSJack F Vogel 			** Either no header split, or a
159061ae650dSJack F Vogel 			** secondary piece of a fragmented
159161ae650dSJack F Vogel 			** split packet.
159261ae650dSJack F Vogel 			*/
159361ae650dSJack F Vogel 			mp->m_len = plen;
159461ae650dSJack F Vogel 			/*
159561ae650dSJack F Vogel 			** See if there is a stored head
159661ae650dSJack F Vogel 			** that determines what we are
159761ae650dSJack F Vogel 			*/
159861ae650dSJack F Vogel 			sendmp = rbuf->fmp;
159961ae650dSJack F Vogel 			rbuf->m_pack = rbuf->fmp = NULL;
160061ae650dSJack F Vogel 
160161ae650dSJack F Vogel 			if (sendmp != NULL) /* secondary frag */
160261ae650dSJack F Vogel 				sendmp->m_pkthdr.len += mp->m_len;
160361ae650dSJack F Vogel 			else {
160461ae650dSJack F Vogel 				/* first desc of a non-ps chain */
160561ae650dSJack F Vogel 				sendmp = mp;
160661ae650dSJack F Vogel 				sendmp->m_flags |= M_PKTHDR;
160761ae650dSJack F Vogel 				sendmp->m_pkthdr.len = mp->m_len;
160861ae650dSJack F Vogel 				if (vtag) {
160961ae650dSJack F Vogel 					sendmp->m_pkthdr.ether_vtag = vtag;
161061ae650dSJack F Vogel 					sendmp->m_flags |= M_VLANTAG;
161161ae650dSJack F Vogel 				}
161261ae650dSJack F Vogel                         }
161361ae650dSJack F Vogel 			/* Pass the head pointer on */
161461ae650dSJack F Vogel 			if (eop == 0) {
161561ae650dSJack F Vogel 				nbuf->fmp = sendmp;
161661ae650dSJack F Vogel 				sendmp = NULL;
161761ae650dSJack F Vogel 				mp->m_next = nbuf->m_pack;
161861ae650dSJack F Vogel 			}
161961ae650dSJack F Vogel 		}
162061ae650dSJack F Vogel 		++processed;
162161ae650dSJack F Vogel 		/* Sending this frame? */
162261ae650dSJack F Vogel 		if (eop) {
162361ae650dSJack F Vogel 			sendmp->m_pkthdr.rcvif = ifp;
162461ae650dSJack F Vogel 			/* gather stats */
162561ae650dSJack F Vogel 			rxr->rx_packets++;
162661ae650dSJack F Vogel 			rxr->rx_bytes += sendmp->m_pkthdr.len;
162761ae650dSJack F Vogel 			/* capture data for dynamic ITR adjustment */
162861ae650dSJack F Vogel 			rxr->packets++;
162961ae650dSJack F Vogel 			rxr->bytes += sendmp->m_pkthdr.len;
163061ae650dSJack F Vogel 			if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
163161ae650dSJack F Vogel 				ixl_rx_checksum(sendmp, status, error, ptype);
1632845a028fSJack F Vogel #ifdef RSS
16336b30e6aeSJack F Vogel 			sendmp->m_pkthdr.flowid =
16346b30e6aeSJack F Vogel 			    le32toh(cur->wb.qword0.hi_dword.rss);
16356b30e6aeSJack F Vogel 			M_HASHTYPE_SET(sendmp, ixl_ptype_to_hash(ptype));
16366b30e6aeSJack F Vogel #else
163761ae650dSJack F Vogel 			sendmp->m_pkthdr.flowid = que->msix;
1638845a028fSJack F Vogel 			M_HASHTYPE_SET(sendmp, M_HASHTYPE_OPAQUE);
16396b30e6aeSJack F Vogel #endif
164061ae650dSJack F Vogel 		}
164161ae650dSJack F Vogel next_desc:
164261ae650dSJack F Vogel 		bus_dmamap_sync(rxr->dma.tag, rxr->dma.map,
164361ae650dSJack F Vogel 		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
164461ae650dSJack F Vogel 
164561ae650dSJack F Vogel 		/* Advance our pointers to the next descriptor. */
164661ae650dSJack F Vogel 		if (++i == que->num_desc)
164761ae650dSJack F Vogel 			i = 0;
164861ae650dSJack F Vogel 
164961ae650dSJack F Vogel 		/* Now send to the stack or do LRO */
165061ae650dSJack F Vogel 		if (sendmp != NULL) {
165161ae650dSJack F Vogel 			rxr->next_check = i;
165261ae650dSJack F Vogel 			ixl_rx_input(rxr, ifp, sendmp, ptype);
165361ae650dSJack F Vogel 			i = rxr->next_check;
165461ae650dSJack F Vogel 		}
165561ae650dSJack F Vogel 
165661ae650dSJack F Vogel                /* Every 8 descriptors we go to refresh mbufs */
165761ae650dSJack F Vogel 		if (processed == 8) {
165861ae650dSJack F Vogel 			ixl_refresh_mbufs(que, i);
165961ae650dSJack F Vogel 			processed = 0;
166061ae650dSJack F Vogel 		}
166161ae650dSJack F Vogel 	}
166261ae650dSJack F Vogel 
166361ae650dSJack F Vogel 	/* Refresh any remaining buf structs */
166461ae650dSJack F Vogel 	if (ixl_rx_unrefreshed(que))
166561ae650dSJack F Vogel 		ixl_refresh_mbufs(que, i);
166661ae650dSJack F Vogel 
166761ae650dSJack F Vogel 	rxr->next_check = i;
166861ae650dSJack F Vogel 
1669b3f7259bSBjoern A. Zeeb #if defined(INET6) || defined(INET)
167061ae650dSJack F Vogel 	/*
167161ae650dSJack F Vogel 	 * Flush any outstanding LRO work
167261ae650dSJack F Vogel 	 */
167361ae650dSJack F Vogel 	while ((queued = SLIST_FIRST(&lro->lro_active)) != NULL) {
167461ae650dSJack F Vogel 		SLIST_REMOVE_HEAD(&lro->lro_active, next);
167561ae650dSJack F Vogel 		tcp_lro_flush(lro, queued);
167661ae650dSJack F Vogel 	}
1677b3f7259bSBjoern A. Zeeb #endif
167861ae650dSJack F Vogel 
167961ae650dSJack F Vogel 	IXL_RX_UNLOCK(rxr);
168061ae650dSJack F Vogel 	return (FALSE);
168161ae650dSJack F Vogel }
168261ae650dSJack F Vogel 
168361ae650dSJack F Vogel 
168461ae650dSJack F Vogel /*********************************************************************
168561ae650dSJack F Vogel  *
168661ae650dSJack F Vogel  *  Verify that the hardware indicated that the checksum is valid.
168761ae650dSJack F Vogel  *  Inform the stack about the status of checksum so that stack
168861ae650dSJack F Vogel  *  doesn't spend time verifying the checksum.
168961ae650dSJack F Vogel  *
169061ae650dSJack F Vogel  *********************************************************************/
169161ae650dSJack F Vogel static void
169261ae650dSJack F Vogel ixl_rx_checksum(struct mbuf * mp, u32 status, u32 error, u8 ptype)
169361ae650dSJack F Vogel {
169461ae650dSJack F Vogel 	struct i40e_rx_ptype_decoded decoded;
169561ae650dSJack F Vogel 
169661ae650dSJack F Vogel 	decoded = decode_rx_desc_ptype(ptype);
169761ae650dSJack F Vogel 
169861ae650dSJack F Vogel 	/* Errors? */
169961ae650dSJack F Vogel  	if (error & ((1 << I40E_RX_DESC_ERROR_IPE_SHIFT) |
170061ae650dSJack F Vogel 	    (1 << I40E_RX_DESC_ERROR_L4E_SHIFT))) {
170161ae650dSJack F Vogel 		mp->m_pkthdr.csum_flags = 0;
170261ae650dSJack F Vogel 		return;
170361ae650dSJack F Vogel 	}
170461ae650dSJack F Vogel 
170561ae650dSJack F Vogel 	/* IPv6 with extension headers likely have bad csum */
170661ae650dSJack F Vogel 	if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
170761ae650dSJack F Vogel 	    decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV6)
170861ae650dSJack F Vogel 		if (status &
170961ae650dSJack F Vogel 		    (1 << I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT)) {
171061ae650dSJack F Vogel 			mp->m_pkthdr.csum_flags = 0;
171161ae650dSJack F Vogel 			return;
171261ae650dSJack F Vogel 		}
171361ae650dSJack F Vogel 
171461ae650dSJack F Vogel 
171561ae650dSJack F Vogel 	/* IP Checksum Good */
171661ae650dSJack F Vogel 	mp->m_pkthdr.csum_flags = CSUM_IP_CHECKED;
171761ae650dSJack F Vogel 	mp->m_pkthdr.csum_flags |= CSUM_IP_VALID;
171861ae650dSJack F Vogel 
171961ae650dSJack F Vogel 	if (status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT)) {
172061ae650dSJack F Vogel 		mp->m_pkthdr.csum_flags |=
172161ae650dSJack F Vogel 		    (CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
172261ae650dSJack F Vogel 		mp->m_pkthdr.csum_data |= htons(0xffff);
172361ae650dSJack F Vogel 	}
172461ae650dSJack F Vogel 	return;
172561ae650dSJack F Vogel }
17264b443922SGleb Smirnoff 
17274b443922SGleb Smirnoff #if __FreeBSD_version >= 1100000
17284b443922SGleb Smirnoff uint64_t
17294b443922SGleb Smirnoff ixl_get_counter(if_t ifp, ift_counter cnt)
17304b443922SGleb Smirnoff {
17314b443922SGleb Smirnoff 	struct ixl_vsi *vsi;
17324b443922SGleb Smirnoff 
17334b443922SGleb Smirnoff 	vsi = if_getsoftc(ifp);
17344b443922SGleb Smirnoff 
17354b443922SGleb Smirnoff 	switch (cnt) {
17364b443922SGleb Smirnoff 	case IFCOUNTER_IPACKETS:
17374b443922SGleb Smirnoff 		return (vsi->ipackets);
17384b443922SGleb Smirnoff 	case IFCOUNTER_IERRORS:
17394b443922SGleb Smirnoff 		return (vsi->ierrors);
17404b443922SGleb Smirnoff 	case IFCOUNTER_OPACKETS:
17414b443922SGleb Smirnoff 		return (vsi->opackets);
17424b443922SGleb Smirnoff 	case IFCOUNTER_OERRORS:
17434b443922SGleb Smirnoff 		return (vsi->oerrors);
17444b443922SGleb Smirnoff 	case IFCOUNTER_COLLISIONS:
17454b443922SGleb Smirnoff 		/* Collisions are by standard impossible in 40G/10G Ethernet */
17464b443922SGleb Smirnoff 		return (0);
17474b443922SGleb Smirnoff 	case IFCOUNTER_IBYTES:
17484b443922SGleb Smirnoff 		return (vsi->ibytes);
17494b443922SGleb Smirnoff 	case IFCOUNTER_OBYTES:
17504b443922SGleb Smirnoff 		return (vsi->obytes);
17514b443922SGleb Smirnoff 	case IFCOUNTER_IMCASTS:
17524b443922SGleb Smirnoff 		return (vsi->imcasts);
17534b443922SGleb Smirnoff 	case IFCOUNTER_OMCASTS:
17544b443922SGleb Smirnoff 		return (vsi->omcasts);
17554b443922SGleb Smirnoff 	case IFCOUNTER_IQDROPS:
17564b443922SGleb Smirnoff 		return (vsi->iqdrops);
17574b443922SGleb Smirnoff 	case IFCOUNTER_OQDROPS:
17584b443922SGleb Smirnoff 		return (vsi->oqdrops);
17594b443922SGleb Smirnoff 	case IFCOUNTER_NOPROTO:
17604b443922SGleb Smirnoff 		return (vsi->noproto);
17614b443922SGleb Smirnoff 	default:
17624b443922SGleb Smirnoff 		return (if_get_counter_default(ifp, cnt));
17634b443922SGleb Smirnoff 	}
17644b443922SGleb Smirnoff }
17654b443922SGleb Smirnoff #endif
1766e5100ee2SJack F Vogel 
1767