xref: /netbsd-src/sys/dev/pci/ixgbe/ixgbe_netmap.c (revision 55485da1d7c3dd560cd83aca0370533ca1207584)
1*55485da1Smsaitoh /* $NetBSD: ixgbe_netmap.c,v 1.6 2023/10/06 14:37:04 msaitoh Exp $ */
2dc7f84c8Smsaitoh /******************************************************************************
3dc7f84c8Smsaitoh 
4dc7f84c8Smsaitoh   Copyright (c) 2001-2017, Intel Corporation
5dc7f84c8Smsaitoh   All rights reserved.
6dc7f84c8Smsaitoh 
7dc7f84c8Smsaitoh   Redistribution and use in source and binary forms, with or without
8dc7f84c8Smsaitoh   modification, are permitted provided that the following conditions are met:
9dc7f84c8Smsaitoh 
10dc7f84c8Smsaitoh    1. Redistributions of source code must retain the above copyright notice,
11dc7f84c8Smsaitoh       this list of conditions and the following disclaimer.
12dc7f84c8Smsaitoh 
13dc7f84c8Smsaitoh    2. Redistributions in binary form must reproduce the above copyright
14dc7f84c8Smsaitoh       notice, this list of conditions and the following disclaimer in the
15dc7f84c8Smsaitoh       documentation and/or other materials provided with the distribution.
16dc7f84c8Smsaitoh 
17dc7f84c8Smsaitoh    3. Neither the name of the Intel Corporation nor the names of its
18dc7f84c8Smsaitoh       contributors may be used to endorse or promote products derived from
19dc7f84c8Smsaitoh       this software without specific prior written permission.
20dc7f84c8Smsaitoh 
21dc7f84c8Smsaitoh   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22dc7f84c8Smsaitoh   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23dc7f84c8Smsaitoh   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24dc7f84c8Smsaitoh   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25dc7f84c8Smsaitoh   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26dc7f84c8Smsaitoh   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27dc7f84c8Smsaitoh   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28dc7f84c8Smsaitoh   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29dc7f84c8Smsaitoh   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30dc7f84c8Smsaitoh   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31dc7f84c8Smsaitoh   POSSIBILITY OF SUCH DAMAGE.
32dc7f84c8Smsaitoh 
33dc7f84c8Smsaitoh ******************************************************************************/
34dc7f84c8Smsaitoh /*$FreeBSD: head/sys/dev/ixgbe/ixgbe_netmap.c 320688 2017-07-05 17:27:03Z erj $*/
35dc7f84c8Smsaitoh 
36dc7f84c8Smsaitoh /*
37dc7f84c8Smsaitoh  * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved.
38dc7f84c8Smsaitoh  *
39dc7f84c8Smsaitoh  * Redistribution and use in source and binary forms, with or without
40dc7f84c8Smsaitoh  * modification, are permitted provided that the following conditions
41dc7f84c8Smsaitoh  * are met:
42dc7f84c8Smsaitoh  * 1. Redistributions of source code must retain the above copyright
43dc7f84c8Smsaitoh  *    notice, this list of conditions and the following disclaimer.
44dc7f84c8Smsaitoh  * 2. Redistributions in binary form must reproduce the above copyright
45dc7f84c8Smsaitoh  *    notice, this list of conditions and the following disclaimer in the
46dc7f84c8Smsaitoh  *    documentation and/or other materials provided with the distribution.
47dc7f84c8Smsaitoh  *
48dc7f84c8Smsaitoh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
49dc7f84c8Smsaitoh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50dc7f84c8Smsaitoh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51dc7f84c8Smsaitoh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
52dc7f84c8Smsaitoh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53dc7f84c8Smsaitoh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54dc7f84c8Smsaitoh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55dc7f84c8Smsaitoh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56dc7f84c8Smsaitoh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57dc7f84c8Smsaitoh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58dc7f84c8Smsaitoh  * SUCH DAMAGE.
59dc7f84c8Smsaitoh  */
60dc7f84c8Smsaitoh 
61dc7f84c8Smsaitoh /*
62dc7f84c8Smsaitoh  * $FreeBSD: head/sys/dev/ixgbe/ixgbe_netmap.c 320688 2017-07-05 17:27:03Z erj $
63dc7f84c8Smsaitoh  *
64dc7f84c8Smsaitoh  * netmap support for: ixgbe
65dc7f84c8Smsaitoh  *
66dc7f84c8Smsaitoh  * This file is meant to be a reference on how to implement
67dc7f84c8Smsaitoh  * netmap support for a network driver.
68dc7f84c8Smsaitoh  * This file contains code but only static or inline functions used
69dc7f84c8Smsaitoh  * by a single driver. To avoid replication of code we just #include
70dc7f84c8Smsaitoh  * it near the beginning of the standard driver.
71dc7f84c8Smsaitoh  */
72dc7f84c8Smsaitoh 
73ab119b16Smsaitoh #include <sys/cdefs.h>
74*55485da1Smsaitoh __KERNEL_RCSID(0, "$NetBSD: ixgbe_netmap.c,v 1.6 2023/10/06 14:37:04 msaitoh Exp $");
75ab119b16Smsaitoh 
76dc7f84c8Smsaitoh #ifdef DEV_NETMAP
77dc7f84c8Smsaitoh /*
78dc7f84c8Smsaitoh  * Some drivers may need the following headers. Others
79dc7f84c8Smsaitoh  * already include them by default
80dc7f84c8Smsaitoh 
81dc7f84c8Smsaitoh #include <vm/vm.h>
82dc7f84c8Smsaitoh #include <vm/pmap.h>
83dc7f84c8Smsaitoh 
84dc7f84c8Smsaitoh  */
85dc7f84c8Smsaitoh #include "ixgbe.h"
86dc7f84c8Smsaitoh 
87dc7f84c8Smsaitoh /*
88dc7f84c8Smsaitoh  * device-specific sysctl variables:
89dc7f84c8Smsaitoh  *
90dc7f84c8Smsaitoh  * ix_crcstrip: 0: keep CRC in rx frames (default), 1: strip it.
91dc7f84c8Smsaitoh  *	During regular operations the CRC is stripped, but on some
92dc7f84c8Smsaitoh  *	hardware reception of frames not multiple of 64 is slower,
93dc7f84c8Smsaitoh  *	so using crcstrip=0 helps in benchmarks.
94dc7f84c8Smsaitoh  *
95dc7f84c8Smsaitoh  * ix_rx_miss, ix_rx_miss_bufs:
96dc7f84c8Smsaitoh  *	count packets that might be missed due to lost interrupts.
97dc7f84c8Smsaitoh  */
98dc7f84c8Smsaitoh SYSCTL_DECL(_dev_netmap);
99dc7f84c8Smsaitoh static int ix_rx_miss, ix_rx_miss_bufs;
100dc7f84c8Smsaitoh int ix_crcstrip;
101dc7f84c8Smsaitoh SYSCTL_INT(_dev_netmap, OID_AUTO, ix_crcstrip,
102dc7f84c8Smsaitoh     CTLFLAG_RW, &ix_crcstrip, 0, "strip CRC on rx frames");
103dc7f84c8Smsaitoh SYSCTL_INT(_dev_netmap, OID_AUTO, ix_rx_miss,
104dc7f84c8Smsaitoh     CTLFLAG_RW, &ix_rx_miss, 0, "potentially missed rx intr");
105dc7f84c8Smsaitoh SYSCTL_INT(_dev_netmap, OID_AUTO, ix_rx_miss_bufs,
106dc7f84c8Smsaitoh     CTLFLAG_RW, &ix_rx_miss_bufs, 0, "potentially missed rx intr bufs");
107dc7f84c8Smsaitoh 
108dc7f84c8Smsaitoh 
109dc7f84c8Smsaitoh static void
set_crcstrip(struct ixgbe_hw * hw,int onoff)110dc7f84c8Smsaitoh set_crcstrip(struct ixgbe_hw *hw, int onoff)
111dc7f84c8Smsaitoh {
112dc7f84c8Smsaitoh 	/* crc stripping is set in two places:
113dc7f84c8Smsaitoh 	 * IXGBE_HLREG0 (modified on init_locked and hw reset)
114dc7f84c8Smsaitoh 	 * IXGBE_RDRXCTL (set by the original driver in
115dc7f84c8Smsaitoh 	 *	ixgbe_setup_hw_rsc() called in init_locked.
116dc7f84c8Smsaitoh 	 *	We disable the setting when netmap is compiled in).
117dc7f84c8Smsaitoh 	 * We update the values here, but also in ixgbe.c because
118dc7f84c8Smsaitoh 	 * init_locked sometimes is called outside our control.
119dc7f84c8Smsaitoh 	 */
120dc7f84c8Smsaitoh 	uint32_t hl, rxc;
121dc7f84c8Smsaitoh 
122dc7f84c8Smsaitoh 	hl = IXGBE_READ_REG(hw, IXGBE_HLREG0);
123dc7f84c8Smsaitoh 	rxc = IXGBE_READ_REG(hw, IXGBE_RDRXCTL);
124e4f827b8Smsaitoh #ifdef D
125dc7f84c8Smsaitoh 	if (netmap_verbose)
126dc7f84c8Smsaitoh 		D("%s read  HLREG 0x%x rxc 0x%x",
127dc7f84c8Smsaitoh 			onoff ? "enter" : "exit", hl, rxc);
128e4f827b8Smsaitoh #endif
129dc7f84c8Smsaitoh 	/* hw requirements ... */
130dc7f84c8Smsaitoh 	rxc &= ~IXGBE_RDRXCTL_RSCFRSTSIZE;
131dc7f84c8Smsaitoh 	rxc |= IXGBE_RDRXCTL_RSCACKC;
132dc7f84c8Smsaitoh 	if (onoff && !ix_crcstrip) {
133dc7f84c8Smsaitoh 		/* keep the crc. Fast rx */
134dc7f84c8Smsaitoh 		hl &= ~IXGBE_HLREG0_RXCRCSTRP;
135dc7f84c8Smsaitoh 		rxc &= ~IXGBE_RDRXCTL_CRCSTRIP;
136dc7f84c8Smsaitoh 	} else {
137dc7f84c8Smsaitoh 		/* reset default mode */
138dc7f84c8Smsaitoh 		hl |= IXGBE_HLREG0_RXCRCSTRP;
139dc7f84c8Smsaitoh 		rxc |= IXGBE_RDRXCTL_CRCSTRIP;
140dc7f84c8Smsaitoh 	}
141e4f827b8Smsaitoh #ifdef D
142dc7f84c8Smsaitoh 	if (netmap_verbose)
143dc7f84c8Smsaitoh 		D("%s write HLREG 0x%x rxc 0x%x",
144dc7f84c8Smsaitoh 			onoff ? "enter" : "exit", hl, rxc);
145e4f827b8Smsaitoh #endif
146dc7f84c8Smsaitoh 	IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hl);
147dc7f84c8Smsaitoh 	IXGBE_WRITE_REG(hw, IXGBE_RDRXCTL, rxc);
148dc7f84c8Smsaitoh }
149dc7f84c8Smsaitoh 
150dc7f84c8Smsaitoh 
151dc7f84c8Smsaitoh /*
152dc7f84c8Smsaitoh  * Register/unregister. We are already under netmap lock.
153dc7f84c8Smsaitoh  * Only called on the first register or the last unregister.
154dc7f84c8Smsaitoh  */
155dc7f84c8Smsaitoh static int
ixgbe_netmap_reg(struct netmap_adapter * na,int onoff)156dc7f84c8Smsaitoh ixgbe_netmap_reg(struct netmap_adapter *na, int onoff)
157dc7f84c8Smsaitoh {
158dc7f84c8Smsaitoh 	struct ifnet *ifp = na->ifp;
159*55485da1Smsaitoh 	struct ixgbe_softc *sc = ifp->if_softc;
160dc7f84c8Smsaitoh 
161*55485da1Smsaitoh 	IXGBE_CORE_LOCK(sc);
162*55485da1Smsaitoh 	sc->stop_locked(sc);
163dc7f84c8Smsaitoh 
164*55485da1Smsaitoh 	set_crcstrip(&sc->hw, onoff);
165dc7f84c8Smsaitoh 	/* enable or disable flags and callbacks in na and ifp */
166dc7f84c8Smsaitoh 	if (onoff) {
167dc7f84c8Smsaitoh 		nm_set_native_flags(na);
168dc7f84c8Smsaitoh 	} else {
169dc7f84c8Smsaitoh 		nm_clear_native_flags(na);
170dc7f84c8Smsaitoh 	}
171*55485da1Smsaitoh 	sc->init_locked(sc);	/* also enables intr */
172*55485da1Smsaitoh 	set_crcstrip(&sc->hw, onoff); // XXX why twice ?
173*55485da1Smsaitoh 	IXGBE_CORE_UNLOCK(sc);
174dc7f84c8Smsaitoh 	return (ifp->if_drv_flags & IFF_DRV_RUNNING ? 0 : 1);
175dc7f84c8Smsaitoh }
176dc7f84c8Smsaitoh 
177dc7f84c8Smsaitoh 
178dc7f84c8Smsaitoh /*
179dc7f84c8Smsaitoh  * Reconcile kernel and user view of the transmit ring.
180dc7f84c8Smsaitoh  *
181dc7f84c8Smsaitoh  * All information is in the kring.
182dc7f84c8Smsaitoh  * Userspace wants to send packets up to the one before kring->rhead,
183dc7f84c8Smsaitoh  * kernel knows kring->nr_hwcur is the first unsent packet.
184dc7f84c8Smsaitoh  *
185dc7f84c8Smsaitoh  * Here we push packets out (as many as possible), and possibly
186dc7f84c8Smsaitoh  * reclaim buffers from previously completed transmission.
187dc7f84c8Smsaitoh  *
188dc7f84c8Smsaitoh  * The caller (netmap) guarantees that there is only one instance
189dc7f84c8Smsaitoh  * running at any time. Any interference with other driver
190dc7f84c8Smsaitoh  * methods should be handled by the individual drivers.
191dc7f84c8Smsaitoh  */
192dc7f84c8Smsaitoh static int
ixgbe_netmap_txsync(struct netmap_kring * kring,int flags)193dc7f84c8Smsaitoh ixgbe_netmap_txsync(struct netmap_kring *kring, int flags)
194dc7f84c8Smsaitoh {
195dc7f84c8Smsaitoh 	struct netmap_adapter *na = kring->na;
196dc7f84c8Smsaitoh 	struct ifnet *ifp = na->ifp;
197dc7f84c8Smsaitoh 	struct netmap_ring *ring = kring->ring;
198dc7f84c8Smsaitoh 	u_int nm_i;	/* index into the netmap ring */
199dc7f84c8Smsaitoh 	u_int nic_i;	/* index into the NIC ring */
200dc7f84c8Smsaitoh 	u_int n;
201dc7f84c8Smsaitoh 	u_int const lim = kring->nkr_num_slots - 1;
202dc7f84c8Smsaitoh 	u_int const head = kring->rhead;
203dc7f84c8Smsaitoh 	/*
204dc7f84c8Smsaitoh 	 * interrupts on every tx packet are expensive so request
205dc7f84c8Smsaitoh 	 * them every half ring, or where NS_REPORT is set
206dc7f84c8Smsaitoh 	 */
207dc7f84c8Smsaitoh 	u_int report_frequency = kring->nkr_num_slots >> 1;
208dc7f84c8Smsaitoh 
209dc7f84c8Smsaitoh 	/* device-specific */
210*55485da1Smsaitoh 	struct ixgbe_softc *sc = ifp->if_softc;
211*55485da1Smsaitoh 	struct tx_ring *txr = &sc->tx_rings[kring->ring_id];
212dc7f84c8Smsaitoh 	int reclaim_tx;
213dc7f84c8Smsaitoh 
214dc7f84c8Smsaitoh 	bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map,
215dc7f84c8Smsaitoh 			BUS_DMASYNC_POSTREAD);
216dc7f84c8Smsaitoh 
217dc7f84c8Smsaitoh 	/*
218dc7f84c8Smsaitoh 	 * First part: process new packets to send.
219dc7f84c8Smsaitoh 	 * nm_i is the current index in the netmap ring,
220dc7f84c8Smsaitoh 	 * nic_i is the corresponding index in the NIC ring.
221dc7f84c8Smsaitoh 	 * The two numbers differ because upon a *_init() we reset
222dc7f84c8Smsaitoh 	 * the NIC ring but leave the netmap ring unchanged.
223dc7f84c8Smsaitoh 	 * For the transmit ring, we have
224dc7f84c8Smsaitoh 	 *
225dc7f84c8Smsaitoh 	 *		nm_i = kring->nr_hwcur
226dc7f84c8Smsaitoh 	 *		nic_i = IXGBE_TDT (not tracked in the driver)
227dc7f84c8Smsaitoh 	 * and
228dc7f84c8Smsaitoh 	 * 		nm_i == (nic_i + kring->nkr_hwofs) % ring_size
229dc7f84c8Smsaitoh 	 *
230dc7f84c8Smsaitoh 	 * In this driver kring->nkr_hwofs >= 0, but for other
231dc7f84c8Smsaitoh 	 * drivers it might be negative as well.
232dc7f84c8Smsaitoh 	 */
233dc7f84c8Smsaitoh 
234dc7f84c8Smsaitoh 	/*
235dc7f84c8Smsaitoh 	 * If we have packets to send (kring->nr_hwcur != kring->rhead)
236dc7f84c8Smsaitoh 	 * iterate over the netmap ring, fetch length and update
237dc7f84c8Smsaitoh 	 * the corresponding slot in the NIC ring. Some drivers also
238dc7f84c8Smsaitoh 	 * need to update the buffer's physical address in the NIC slot
239dc7f84c8Smsaitoh 	 * even NS_BUF_CHANGED is not set (PNMB computes the addresses).
240dc7f84c8Smsaitoh 	 *
241dc7f84c8Smsaitoh 	 * The netmap_reload_map() calls is especially expensive,
242dc7f84c8Smsaitoh 	 * even when (as in this case) the tag is 0, so do only
243dc7f84c8Smsaitoh 	 * when the buffer has actually changed.
244dc7f84c8Smsaitoh 	 *
245dc7f84c8Smsaitoh 	 * If possible do not set the report/intr bit on all slots,
246dc7f84c8Smsaitoh 	 * but only a few times per ring or when NS_REPORT is set.
247dc7f84c8Smsaitoh 	 *
248dc7f84c8Smsaitoh 	 * Finally, on 10G and faster drivers, it might be useful
249dc7f84c8Smsaitoh 	 * to prefetch the next slot and txr entry.
250dc7f84c8Smsaitoh 	 */
251dc7f84c8Smsaitoh 
252dc7f84c8Smsaitoh 	nm_i = kring->nr_hwcur;
253dc7f84c8Smsaitoh 	if (nm_i != head) {	/* we have new packets to send */
254dc7f84c8Smsaitoh 		nic_i = netmap_idx_k2n(kring, nm_i);
255dc7f84c8Smsaitoh 
256dc7f84c8Smsaitoh 		__builtin_prefetch(&ring->slot[nm_i]);
257dc7f84c8Smsaitoh 		__builtin_prefetch(&txr->tx_buffers[nic_i]);
258dc7f84c8Smsaitoh 
259dc7f84c8Smsaitoh 		for (n = 0; nm_i != head; n++) {
260dc7f84c8Smsaitoh 			struct netmap_slot *slot = &ring->slot[nm_i];
261dc7f84c8Smsaitoh 			u_int len = slot->len;
262dc7f84c8Smsaitoh 			uint64_t paddr;
263dc7f84c8Smsaitoh 			void *addr = PNMB(na, slot, &paddr);
264dc7f84c8Smsaitoh 
265dc7f84c8Smsaitoh 			/* device-specific */
266dc7f84c8Smsaitoh 			union ixgbe_adv_tx_desc *curr = &txr->tx_base[nic_i];
267dc7f84c8Smsaitoh 			struct ixgbe_tx_buf *txbuf = &txr->tx_buffers[nic_i];
268dc7f84c8Smsaitoh 			int flags = (slot->flags & NS_REPORT ||
269dc7f84c8Smsaitoh 				nic_i == 0 || nic_i == report_frequency) ?
270dc7f84c8Smsaitoh 				IXGBE_TXD_CMD_RS : 0;
271dc7f84c8Smsaitoh 
272dc7f84c8Smsaitoh 			/* prefetch for next round */
273dc7f84c8Smsaitoh 			__builtin_prefetch(&ring->slot[nm_i + 1]);
274dc7f84c8Smsaitoh 			__builtin_prefetch(&txr->tx_buffers[nic_i + 1]);
275dc7f84c8Smsaitoh 
276dc7f84c8Smsaitoh 			NM_CHECK_ADDR_LEN(na, addr, len);
277dc7f84c8Smsaitoh 
278dc7f84c8Smsaitoh 			if (slot->flags & NS_BUF_CHANGED) {
279dc7f84c8Smsaitoh 				/* buffer has changed, reload map */
280dc7f84c8Smsaitoh 				netmap_reload_map(na, txr->txtag, txbuf->map, addr);
281dc7f84c8Smsaitoh 			}
282dc7f84c8Smsaitoh 			slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED);
283dc7f84c8Smsaitoh 
284dc7f84c8Smsaitoh 			/* Fill the slot in the NIC ring. */
285dc7f84c8Smsaitoh 			/* Use legacy descriptor, they are faster? */
286dc7f84c8Smsaitoh 			curr->read.buffer_addr = htole64(paddr);
287dc7f84c8Smsaitoh 			curr->read.olinfo_status = 0;
288dc7f84c8Smsaitoh 			curr->read.cmd_type_len = htole32(len | flags |
289dc7f84c8Smsaitoh 				IXGBE_ADVTXD_DCMD_IFCS | IXGBE_TXD_CMD_EOP);
290dc7f84c8Smsaitoh 
291dc7f84c8Smsaitoh 			/* make sure changes to the buffer are synced */
292dc7f84c8Smsaitoh 			bus_dmamap_sync(txr->txtag, txbuf->map,
293dc7f84c8Smsaitoh 				BUS_DMASYNC_PREWRITE);
294dc7f84c8Smsaitoh 
295dc7f84c8Smsaitoh 			nm_i = nm_next(nm_i, lim);
296dc7f84c8Smsaitoh 			nic_i = nm_next(nic_i, lim);
297dc7f84c8Smsaitoh 		}
298dc7f84c8Smsaitoh 		kring->nr_hwcur = head;
299dc7f84c8Smsaitoh 
300dc7f84c8Smsaitoh 		/* synchronize the NIC ring */
301dc7f84c8Smsaitoh 		bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map,
302dc7f84c8Smsaitoh 			BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
303dc7f84c8Smsaitoh 
304dc7f84c8Smsaitoh 		/* (re)start the tx unit up to slot nic_i (excluded) */
305*55485da1Smsaitoh 		IXGBE_WRITE_REG(&sc->hw, txr->tail, nic_i);
306dc7f84c8Smsaitoh 	}
307dc7f84c8Smsaitoh 
308dc7f84c8Smsaitoh 	/*
309dc7f84c8Smsaitoh 	 * Second part: reclaim buffers for completed transmissions.
310dc7f84c8Smsaitoh 	 * Because this is expensive (we read a NIC register etc.)
311dc7f84c8Smsaitoh 	 * we only do it in specific cases (see below).
312dc7f84c8Smsaitoh 	 */
313dc7f84c8Smsaitoh 	if (flags & NAF_FORCE_RECLAIM) {
314dc7f84c8Smsaitoh 		reclaim_tx = 1; /* forced reclaim */
315dc7f84c8Smsaitoh 	} else if (!nm_kr_txempty(kring)) {
316dc7f84c8Smsaitoh 		reclaim_tx = 0; /* have buffers, no reclaim */
317dc7f84c8Smsaitoh 	} else {
318dc7f84c8Smsaitoh 		/*
319dc7f84c8Smsaitoh 		 * No buffers available. Locate previous slot with
320dc7f84c8Smsaitoh 		 * REPORT_STATUS set.
321dc7f84c8Smsaitoh 		 * If the slot has DD set, we can reclaim space,
322dc7f84c8Smsaitoh 		 * otherwise wait for the next interrupt.
323dc7f84c8Smsaitoh 		 * This enables interrupt moderation on the tx
324dc7f84c8Smsaitoh 		 * side though it might reduce throughput.
325dc7f84c8Smsaitoh 		 */
326dc7f84c8Smsaitoh 		struct ixgbe_legacy_tx_desc *txd =
327dc7f84c8Smsaitoh 		    (struct ixgbe_legacy_tx_desc *)txr->tx_base;
328dc7f84c8Smsaitoh 
329dc7f84c8Smsaitoh 		nic_i = txr->next_to_clean + report_frequency;
330dc7f84c8Smsaitoh 		if (nic_i > lim)
331dc7f84c8Smsaitoh 			nic_i -= lim + 1;
332dc7f84c8Smsaitoh 		// round to the closest with dd set
333dc7f84c8Smsaitoh 		nic_i = (nic_i < kring->nkr_num_slots / 4 ||
334dc7f84c8Smsaitoh 			 nic_i >= kring->nkr_num_slots*3/4) ?
335dc7f84c8Smsaitoh 			0 : report_frequency;
336dc7f84c8Smsaitoh 		reclaim_tx = txd[nic_i].upper.fields.status & IXGBE_TXD_STAT_DD;	// XXX cpu_to_le32 ?
337dc7f84c8Smsaitoh 	}
338dc7f84c8Smsaitoh 	if (reclaim_tx) {
339dc7f84c8Smsaitoh 		/*
340dc7f84c8Smsaitoh 		 * Record completed transmissions.
341dc7f84c8Smsaitoh 		 * We (re)use the driver's txr->next_to_clean to keep
342dc7f84c8Smsaitoh 		 * track of the most recently completed transmission.
343dc7f84c8Smsaitoh 		 *
344dc7f84c8Smsaitoh 		 * The datasheet discourages the use of TDH to find
345dc7f84c8Smsaitoh 		 * out the number of sent packets, but we only set
346dc7f84c8Smsaitoh 		 * REPORT_STATUS in a few slots so TDH is the only
347dc7f84c8Smsaitoh 		 * good way.
348dc7f84c8Smsaitoh 		 */
349*55485da1Smsaitoh 		nic_i = IXGBE_READ_REG(&sc->hw, IXGBE_TDH(kring->ring_id));
350dc7f84c8Smsaitoh 		if (nic_i >= kring->nkr_num_slots) { /* XXX can it happen ? */
351e4f827b8Smsaitoh #ifdef D
352dc7f84c8Smsaitoh 			D("TDH wrap %d", nic_i);
353e4f827b8Smsaitoh #endif
354dc7f84c8Smsaitoh 			nic_i -= kring->nkr_num_slots;
355dc7f84c8Smsaitoh 		}
356dc7f84c8Smsaitoh 		if (nic_i != txr->next_to_clean) {
357dc7f84c8Smsaitoh 			/* some tx completed, increment avail */
358dc7f84c8Smsaitoh 			txr->next_to_clean = nic_i;
359dc7f84c8Smsaitoh 			kring->nr_hwtail = nm_prev(netmap_idx_n2k(kring, nic_i), lim);
360dc7f84c8Smsaitoh 		}
361dc7f84c8Smsaitoh 	}
362dc7f84c8Smsaitoh 
363dc7f84c8Smsaitoh 	return 0;
364dc7f84c8Smsaitoh }
365dc7f84c8Smsaitoh 
366dc7f84c8Smsaitoh 
367dc7f84c8Smsaitoh /*
368dc7f84c8Smsaitoh  * Reconcile kernel and user view of the receive ring.
369dc7f84c8Smsaitoh  * Same as for the txsync, this routine must be efficient.
370dc7f84c8Smsaitoh  * The caller guarantees a single invocations, but races against
371dc7f84c8Smsaitoh  * the rest of the driver should be handled here.
372dc7f84c8Smsaitoh  *
373dc7f84c8Smsaitoh  * On call, kring->rhead is the first packet that userspace wants
374dc7f84c8Smsaitoh  * to keep, and kring->rcur is the wakeup point.
375dc7f84c8Smsaitoh  * The kernel has previously reported packets up to kring->rtail.
376dc7f84c8Smsaitoh  *
377dc7f84c8Smsaitoh  * If (flags & NAF_FORCE_READ) also check for incoming packets irrespective
378dc7f84c8Smsaitoh  * of whether or not we received an interrupt.
379dc7f84c8Smsaitoh  */
380dc7f84c8Smsaitoh static int
ixgbe_netmap_rxsync(struct netmap_kring * kring,int flags)381dc7f84c8Smsaitoh ixgbe_netmap_rxsync(struct netmap_kring *kring, int flags)
382dc7f84c8Smsaitoh {
383dc7f84c8Smsaitoh 	struct netmap_adapter *na = kring->na;
384dc7f84c8Smsaitoh 	struct ifnet *ifp = na->ifp;
385dc7f84c8Smsaitoh 	struct netmap_ring *ring = kring->ring;
386dc7f84c8Smsaitoh 	u_int nm_i;	/* index into the netmap ring */
387dc7f84c8Smsaitoh 	u_int nic_i;	/* index into the NIC ring */
388dc7f84c8Smsaitoh 	u_int n;
389dc7f84c8Smsaitoh 	u_int const lim = kring->nkr_num_slots - 1;
390dc7f84c8Smsaitoh 	u_int const head = kring->rhead;
391dc7f84c8Smsaitoh 	int force_update = (flags & NAF_FORCE_READ) || kring->nr_kflags & NKR_PENDINTR;
392dc7f84c8Smsaitoh 
393dc7f84c8Smsaitoh 	/* device-specific */
394*55485da1Smsaitoh 	struct ixgbe_softc *sc = ifp->if_softc;
395*55485da1Smsaitoh 	struct rx_ring *rxr = &sc->rx_rings[kring->ring_id];
396dc7f84c8Smsaitoh 
397dc7f84c8Smsaitoh 	if (head > lim)
398dc7f84c8Smsaitoh 		return netmap_ring_reinit(kring);
399dc7f84c8Smsaitoh 
400dc7f84c8Smsaitoh 	/* XXX check sync modes */
401dc7f84c8Smsaitoh 	bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map,
402dc7f84c8Smsaitoh 			BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
403dc7f84c8Smsaitoh 
404dc7f84c8Smsaitoh 	/*
405dc7f84c8Smsaitoh 	 * First part: import newly received packets.
406dc7f84c8Smsaitoh 	 *
407dc7f84c8Smsaitoh 	 * nm_i is the index of the next free slot in the netmap ring,
408dc7f84c8Smsaitoh 	 * nic_i is the index of the next received packet in the NIC ring,
409dc7f84c8Smsaitoh 	 * and they may differ in case if_init() has been called while
410dc7f84c8Smsaitoh 	 * in netmap mode. For the receive ring we have
411dc7f84c8Smsaitoh 	 *
412dc7f84c8Smsaitoh 	 *	nic_i = rxr->next_to_check;
413dc7f84c8Smsaitoh 	 *	nm_i = kring->nr_hwtail (previous)
414dc7f84c8Smsaitoh 	 * and
415dc7f84c8Smsaitoh 	 *	nm_i == (nic_i + kring->nkr_hwofs) % ring_size
416dc7f84c8Smsaitoh 	 *
417dc7f84c8Smsaitoh 	 * rxr->next_to_check is set to 0 on a ring reinit
418dc7f84c8Smsaitoh 	 */
419dc7f84c8Smsaitoh 	if (netmap_no_pendintr || force_update) {
420dc7f84c8Smsaitoh 		int crclen = (ix_crcstrip) ? 0 : 4;
421dc7f84c8Smsaitoh 
422dc7f84c8Smsaitoh 		nic_i = rxr->next_to_check; // or also k2n(kring->nr_hwtail)
423dc7f84c8Smsaitoh 		nm_i = netmap_idx_n2k(kring, nic_i);
424dc7f84c8Smsaitoh 
425dc7f84c8Smsaitoh 		for (n = 0; ; n++) {
426dc7f84c8Smsaitoh 			union ixgbe_adv_rx_desc *curr = &rxr->rx_base[nic_i];
427dc7f84c8Smsaitoh 			uint32_t staterr = le32toh(curr->wb.upper.status_error);
428dc7f84c8Smsaitoh 
429dc7f84c8Smsaitoh 			if ((staterr & IXGBE_RXD_STAT_DD) == 0)
430dc7f84c8Smsaitoh 				break;
431dc7f84c8Smsaitoh 			ring->slot[nm_i].len = le16toh(curr->wb.upper.length) - crclen;
432bec7d0d6Smsaitoh 			ring->slot[nm_i].flags = 0;
433dc7f84c8Smsaitoh 			bus_dmamap_sync(rxr->ptag,
434dc7f84c8Smsaitoh 			    rxr->rx_buffers[nic_i].pmap, BUS_DMASYNC_POSTREAD);
435dc7f84c8Smsaitoh 			nm_i = nm_next(nm_i, lim);
436dc7f84c8Smsaitoh 			nic_i = nm_next(nic_i, lim);
437dc7f84c8Smsaitoh 		}
438dc7f84c8Smsaitoh 		if (n) { /* update the state variables */
439dc7f84c8Smsaitoh 			if (netmap_no_pendintr && !force_update) {
440dc7f84c8Smsaitoh 				/* diagnostics */
441dc7f84c8Smsaitoh 				ix_rx_miss ++;
442dc7f84c8Smsaitoh 				ix_rx_miss_bufs += n;
443dc7f84c8Smsaitoh 			}
444dc7f84c8Smsaitoh 			rxr->next_to_check = nic_i;
445dc7f84c8Smsaitoh 			kring->nr_hwtail = nm_i;
446dc7f84c8Smsaitoh 		}
447dc7f84c8Smsaitoh 		kring->nr_kflags &= ~NKR_PENDINTR;
448dc7f84c8Smsaitoh 	}
449dc7f84c8Smsaitoh 
450dc7f84c8Smsaitoh 	/*
451dc7f84c8Smsaitoh 	 * Second part: skip past packets that userspace has released.
452dc7f84c8Smsaitoh 	 * (kring->nr_hwcur to kring->rhead excluded),
453dc7f84c8Smsaitoh 	 * and make the buffers available for reception.
454dc7f84c8Smsaitoh 	 * As usual nm_i is the index in the netmap ring,
455dc7f84c8Smsaitoh 	 * nic_i is the index in the NIC ring, and
456dc7f84c8Smsaitoh 	 * nm_i == (nic_i + kring->nkr_hwofs) % ring_size
457dc7f84c8Smsaitoh 	 */
458dc7f84c8Smsaitoh 	nm_i = kring->nr_hwcur;
459dc7f84c8Smsaitoh 	if (nm_i != head) {
460dc7f84c8Smsaitoh 		nic_i = netmap_idx_k2n(kring, nm_i);
461dc7f84c8Smsaitoh 		for (n = 0; nm_i != head; n++) {
462dc7f84c8Smsaitoh 			struct netmap_slot *slot = &ring->slot[nm_i];
463dc7f84c8Smsaitoh 			uint64_t paddr;
464dc7f84c8Smsaitoh 			void *addr = PNMB(na, slot, &paddr);
465dc7f84c8Smsaitoh 
466dc7f84c8Smsaitoh 			union ixgbe_adv_rx_desc *curr = &rxr->rx_base[nic_i];
467dc7f84c8Smsaitoh 			struct ixgbe_rx_buf *rxbuf = &rxr->rx_buffers[nic_i];
468dc7f84c8Smsaitoh 
469dc7f84c8Smsaitoh 			if (addr == NETMAP_BUF_BASE(na)) /* bad buf */
470dc7f84c8Smsaitoh 				goto ring_reset;
471dc7f84c8Smsaitoh 
472dc7f84c8Smsaitoh 			if (slot->flags & NS_BUF_CHANGED) {
473dc7f84c8Smsaitoh 				/* buffer has changed, reload map */
474dc7f84c8Smsaitoh 				netmap_reload_map(na, rxr->ptag, rxbuf->pmap, addr);
475dc7f84c8Smsaitoh 				slot->flags &= ~NS_BUF_CHANGED;
476dc7f84c8Smsaitoh 			}
477dc7f84c8Smsaitoh 			curr->wb.upper.status_error = 0;
478dc7f84c8Smsaitoh 			curr->read.pkt_addr = htole64(paddr);
479dc7f84c8Smsaitoh 			bus_dmamap_sync(rxr->ptag, rxbuf->pmap,
480dc7f84c8Smsaitoh 			    BUS_DMASYNC_PREREAD);
481dc7f84c8Smsaitoh 			nm_i = nm_next(nm_i, lim);
482dc7f84c8Smsaitoh 			nic_i = nm_next(nic_i, lim);
483dc7f84c8Smsaitoh 		}
484dc7f84c8Smsaitoh 		kring->nr_hwcur = head;
485dc7f84c8Smsaitoh 
486dc7f84c8Smsaitoh 		bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map,
487dc7f84c8Smsaitoh 		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
488dc7f84c8Smsaitoh 		/*
489dc7f84c8Smsaitoh 		 * IMPORTANT: we must leave one free slot in the ring,
490dc7f84c8Smsaitoh 		 * so move nic_i back by one unit
491dc7f84c8Smsaitoh 		 */
492dc7f84c8Smsaitoh 		nic_i = nm_prev(nic_i, lim);
493*55485da1Smsaitoh 		IXGBE_WRITE_REG(&sc->hw, rxr->tail, nic_i);
494dc7f84c8Smsaitoh 	}
495dc7f84c8Smsaitoh 
496dc7f84c8Smsaitoh 	return 0;
497dc7f84c8Smsaitoh 
498dc7f84c8Smsaitoh ring_reset:
499dc7f84c8Smsaitoh 	return netmap_ring_reinit(kring);
500dc7f84c8Smsaitoh }
501dc7f84c8Smsaitoh 
502dc7f84c8Smsaitoh 
503dc7f84c8Smsaitoh /*
504dc7f84c8Smsaitoh  * The attach routine, called near the end of ixgbe_attach(),
505dc7f84c8Smsaitoh  * fills the parameters for netmap_attach() and calls it.
506dc7f84c8Smsaitoh  * It cannot fail, in the worst case (such as no memory)
507dc7f84c8Smsaitoh  * netmap mode will be disabled and the driver will only
508dc7f84c8Smsaitoh  * operate in standard mode.
509dc7f84c8Smsaitoh  */
510dc7f84c8Smsaitoh void
ixgbe_netmap_attach(struct ixgbe_softc * sc)511*55485da1Smsaitoh ixgbe_netmap_attach(struct ixgbe_softc *sc)
512dc7f84c8Smsaitoh {
513dc7f84c8Smsaitoh 	struct netmap_adapter na;
514dc7f84c8Smsaitoh 
515dc7f84c8Smsaitoh 	bzero(&na, sizeof(na));
516dc7f84c8Smsaitoh 
517*55485da1Smsaitoh 	na.ifp = sc->ifp;
518dc7f84c8Smsaitoh 	na.na_flags = NAF_BDG_MAYSLEEP;
519*55485da1Smsaitoh 	na.num_tx_desc = sc->num_tx_desc;
520*55485da1Smsaitoh 	na.num_rx_desc = sc->num_rx_desc;
521dc7f84c8Smsaitoh 	na.nm_txsync = ixgbe_netmap_txsync;
522dc7f84c8Smsaitoh 	na.nm_rxsync = ixgbe_netmap_rxsync;
523dc7f84c8Smsaitoh 	na.nm_register = ixgbe_netmap_reg;
524*55485da1Smsaitoh 	na.num_tx_rings = na.num_rx_rings = sc->num_queues;
525dc7f84c8Smsaitoh 	netmap_attach(&na);
526dc7f84c8Smsaitoh }
527dc7f84c8Smsaitoh 
528dc7f84c8Smsaitoh #endif /* DEV_NETMAP */
529dc7f84c8Smsaitoh 
530dc7f84c8Smsaitoh /* end of file */
531