if_igb_netmap.h revision 238985
1227614Sluigi/*
2227614Sluigi * Copyright (C) 2011 Universita` di Pisa. All rights reserved.
3227614Sluigi *
4227614Sluigi * Redistribution and use in source and binary forms, with or without
5227614Sluigi * modification, are permitted provided that the following conditions
6227614Sluigi * are met:
7227614Sluigi * 1. Redistributions of source code must retain the above copyright
8227614Sluigi *    notice, this list of conditions and the following disclaimer.
9227614Sluigi * 2. Redistributions in binary form must reproduce the above copyright
10227614Sluigi *    notice, this list of conditions and the following disclaimer in the
11227614Sluigi *    documentation and/or other materials provided with the distribution.
12227614Sluigi *
13227614Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14227614Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15227614Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16227614Sluigi * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17227614Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18227614Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19227614Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20227614Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21227614Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22227614Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23227614Sluigi * SUCH DAMAGE.
24227614Sluigi */
25227614Sluigi
26227614Sluigi/*
27227614Sluigi * $FreeBSD: head/sys/dev/netmap/if_igb_netmap.h 238985 2012-08-02 11:59:43Z luigi $
28232238Sluigi * $Id: if_igb_netmap.h 10627 2012-02-23 19:37:15Z luigi $
29227614Sluigi *
30232238Sluigi * Netmap support for igb, partly contributed by Ahmed Kooli
31232238Sluigi * For details on netmap support please see ixgbe_netmap.h
32227614Sluigi */
33227614Sluigi
34232238Sluigi
35227614Sluigi#include <net/netmap.h>
36227614Sluigi#include <sys/selinfo.h>
37227614Sluigi#include <vm/vm.h>
38227614Sluigi#include <vm/pmap.h>    /* vtophys ? */
39227614Sluigi#include <dev/netmap/netmap_kern.h>
40227614Sluigi
41227614Sluigi
42227614Sluigi/*
43227614Sluigi * wrapper to export locks to the generic code
44227614Sluigi */
45227614Sluigistatic void
46231594Sluigiigb_netmap_lock_wrapper(struct ifnet *ifp, int what, u_int queueid)
47227614Sluigi{
48231594Sluigi	struct adapter *adapter = ifp->if_softc;
49227614Sluigi
50227614Sluigi	ASSERT(queueid < adapter->num_queues);
51227614Sluigi	switch (what) {
52227614Sluigi	case NETMAP_CORE_LOCK:
53227614Sluigi		IGB_CORE_LOCK(adapter);
54227614Sluigi		break;
55227614Sluigi	case NETMAP_CORE_UNLOCK:
56227614Sluigi		IGB_CORE_UNLOCK(adapter);
57227614Sluigi		break;
58227614Sluigi	case NETMAP_TX_LOCK:
59227614Sluigi		IGB_TX_LOCK(&adapter->tx_rings[queueid]);
60227614Sluigi		break;
61227614Sluigi	case NETMAP_TX_UNLOCK:
62227614Sluigi		IGB_TX_UNLOCK(&adapter->tx_rings[queueid]);
63227614Sluigi		break;
64227614Sluigi	case NETMAP_RX_LOCK:
65227614Sluigi		IGB_RX_LOCK(&adapter->rx_rings[queueid]);
66227614Sluigi		break;
67227614Sluigi	case NETMAP_RX_UNLOCK:
68227614Sluigi		IGB_RX_UNLOCK(&adapter->rx_rings[queueid]);
69227614Sluigi		break;
70227614Sluigi	}
71227614Sluigi}
72227614Sluigi
73227614Sluigi
74227614Sluigi/*
75231778Sluigi * register-unregister routine
76227614Sluigi */
77227614Sluigistatic int
78227614Sluigiigb_netmap_reg(struct ifnet *ifp, int onoff)
79227614Sluigi{
80227614Sluigi	struct adapter *adapter = ifp->if_softc;
81227614Sluigi	struct netmap_adapter *na = NA(ifp);
82227614Sluigi	int error = 0;
83227614Sluigi
84228276Sluigi	if (na == NULL)
85231778Sluigi		return EINVAL;	/* no netmap support here */
86227614Sluigi
87227614Sluigi	igb_disable_intr(adapter);
88227614Sluigi
89227614Sluigi	/* Tell the stack that the interface is no longer active */
90227614Sluigi	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
91227614Sluigi
92227614Sluigi	if (onoff) {
93227614Sluigi		ifp->if_capenable |= IFCAP_NETMAP;
94227614Sluigi
95227614Sluigi		na->if_transmit = ifp->if_transmit;
96227614Sluigi		ifp->if_transmit = netmap_start;
97227614Sluigi
98227614Sluigi		igb_init_locked(adapter);
99227614Sluigi		if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) == 0) {
100227614Sluigi			error = ENOMEM;
101227614Sluigi			goto fail;
102227614Sluigi		}
103227614Sluigi	} else {
104227614Sluigifail:
105227614Sluigi		/* restore if_transmit */
106227614Sluigi		ifp->if_transmit = na->if_transmit;
107227614Sluigi		ifp->if_capenable &= ~IFCAP_NETMAP;
108231778Sluigi		igb_init_locked(adapter);	/* also enable intr */
109227614Sluigi	}
110227614Sluigi	return (error);
111227614Sluigi}
112227614Sluigi
113227614Sluigi
114227614Sluigi/*
115232238Sluigi * Reconcile kernel and user view of the transmit ring.
116227614Sluigi */
117227614Sluigistatic int
118231594Sluigiigb_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock)
119227614Sluigi{
120231594Sluigi	struct adapter *adapter = ifp->if_softc;
121227614Sluigi	struct tx_ring *txr = &adapter->tx_rings[ring_nr];
122232238Sluigi	struct netmap_adapter *na = NA(ifp);
123227614Sluigi	struct netmap_kring *kring = &na->tx_rings[ring_nr];
124227614Sluigi	struct netmap_ring *ring = kring->ring;
125232238Sluigi	u_int j, k, l, n = 0, lim = kring->nkr_num_slots - 1;
126227614Sluigi
127227614Sluigi	/* generate an interrupt approximately every half ring */
128238985Sluigi	u_int report_frequency = kring->nkr_num_slots >> 1;
129227614Sluigi
130228276Sluigi	k = ring->cur;
131228276Sluigi	if (k > lim)
132227614Sluigi		return netmap_ring_reinit(kring);
133227614Sluigi
134227614Sluigi	if (do_lock)
135227614Sluigi		IGB_TX_LOCK(txr);
136227614Sluigi	bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map,
137228276Sluigi	    BUS_DMASYNC_POSTREAD);
138227614Sluigi
139231778Sluigi	/* check for new packets to send.
140231778Sluigi	 * j indexes the netmap ring, l indexes the nic ring, and
141231778Sluigi	 *      j = kring->nr_hwcur, l = E1000_TDT (not tracked),
142231778Sluigi	 *      j == (l + kring->nkr_hwofs) % ring_size
143231778Sluigi	 */
144231778Sluigi	j = kring->nr_hwcur;
145232238Sluigi	if (j != k) {	/* we have new packets to send */
146231778Sluigi		/* 82575 needs the queue index added */
147231778Sluigi		u32 olinfo_status =
148231778Sluigi		    (adapter->hw.mac.type == e1000_82575) ? (txr->me << 4) : 0;
149227614Sluigi
150232238Sluigi		l = netmap_idx_k2n(kring, j);
151231881Sluigi		for (n = 0; j != k; n++) {
152232238Sluigi			/* slot is the current slot in the netmap ring */
153227614Sluigi			struct netmap_slot *slot = &ring->slot[j];
154232238Sluigi			/* curr is the current slot in the nic ring */
155227614Sluigi			union e1000_adv_tx_desc *curr =
156228276Sluigi			    (union e1000_adv_tx_desc *)&txr->tx_base[l];
157231778Sluigi			struct igb_tx_buffer *txbuf = &txr->tx_buffers[l];
158227614Sluigi			int flags = ((slot->flags & NS_REPORT) ||
159227614Sluigi				j == 0 || j == report_frequency) ?
160227614Sluigi					E1000_ADVTXD_DCMD_RS : 0;
161231778Sluigi			uint64_t paddr;
162231778Sluigi			void *addr = PNMB(slot, &paddr);
163232238Sluigi			u_int len = slot->len;
164227614Sluigi
165227614Sluigi			if (addr == netmap_buffer_base || len > NETMAP_BUF_SIZE) {
166227614Sluigi				if (do_lock)
167227614Sluigi					IGB_TX_UNLOCK(txr);
168227614Sluigi				return netmap_ring_reinit(kring);
169227614Sluigi			}
170227614Sluigi
171227614Sluigi			slot->flags &= ~NS_REPORT;
172232238Sluigi			if (slot->flags & NS_BUF_CHANGED) {
173232238Sluigi				/* buffer has changed, reload map */
174232238Sluigi				netmap_reload_map(txr->txtag, txbuf->map, addr);
175232238Sluigi				slot->flags &= ~NS_BUF_CHANGED;
176232238Sluigi			}
177229939Sluigi			curr->read.buffer_addr = htole64(paddr);
178232238Sluigi			// XXX check olinfo and cmd_type_len
179227614Sluigi			curr->read.olinfo_status =
180227614Sluigi			    htole32(olinfo_status |
181227614Sluigi				(len<< E1000_ADVTXD_PAYLEN_SHIFT));
182227614Sluigi			curr->read.cmd_type_len =
183227614Sluigi			    htole32(len | E1000_ADVTXD_DTYP_DATA |
184227614Sluigi				    E1000_ADVTXD_DCMD_IFCS |
185227614Sluigi				    E1000_ADVTXD_DCMD_DEXT |
186227614Sluigi				    E1000_ADVTXD_DCMD_EOP | flags);
187227614Sluigi
188227614Sluigi			bus_dmamap_sync(txr->txtag, txbuf->map,
189227614Sluigi				BUS_DMASYNC_PREWRITE);
190227614Sluigi			j = (j == lim) ? 0 : j + 1;
191228276Sluigi			l = (l == lim) ? 0 : l + 1;
192227614Sluigi		}
193232238Sluigi		kring->nr_hwcur = k; /* the saved ring->cur */
194228276Sluigi		kring->nr_hwavail -= n;
195227614Sluigi
196228276Sluigi		/* Set the watchdog XXX ? */
197227614Sluigi		txr->queue_status = IGB_QUEUE_WORKING;
198227614Sluigi		txr->watchdog_time = ticks;
199227614Sluigi
200227614Sluigi		bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map,
201228276Sluigi		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
202227614Sluigi
203228276Sluigi		E1000_WRITE_REG(&adapter->hw, E1000_TDT(txr->me), l);
204227614Sluigi	}
205231778Sluigi
206228276Sluigi	if (n == 0 || kring->nr_hwavail < 1) {
207228276Sluigi		int delta;
208228276Sluigi
209231778Sluigi		/* record completed transmissions using TDH */
210228276Sluigi		l = E1000_READ_REG(&adapter->hw, E1000_TDH(ring_nr));
211231778Sluigi		if (l >= kring->nkr_num_slots) { /* XXX can it happen ? */
212231778Sluigi			D("TDH wrap %d", l);
213228276Sluigi			l -= kring->nkr_num_slots;
214231778Sluigi		}
215228276Sluigi		delta = l - txr->next_to_clean;
216228276Sluigi		if (delta) {
217231778Sluigi			/* some completed, increment hwavail. */
218228276Sluigi			if (delta < 0)
219228276Sluigi				delta += kring->nkr_num_slots;
220228276Sluigi			txr->next_to_clean = l;
221228276Sluigi			kring->nr_hwavail += delta;
222228276Sluigi		}
223228276Sluigi	}
224232238Sluigi	/* update avail to what the kernel knows */
225231778Sluigi	ring->avail = kring->nr_hwavail;
226231778Sluigi
227227614Sluigi	if (do_lock)
228227614Sluigi		IGB_TX_UNLOCK(txr);
229227614Sluigi	return 0;
230227614Sluigi}
231227614Sluigi
232227614Sluigi
233227614Sluigi/*
234227614Sluigi * Reconcile kernel and user view of the receive ring.
235227614Sluigi */
236227614Sluigistatic int
237231594Sluigiigb_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock)
238227614Sluigi{
239231594Sluigi	struct adapter *adapter = ifp->if_softc;
240227614Sluigi	struct rx_ring *rxr = &adapter->rx_rings[ring_nr];
241232238Sluigi	struct netmap_adapter *na = NA(ifp);
242227614Sluigi	struct netmap_kring *kring = &na->rx_rings[ring_nr];
243227614Sluigi	struct netmap_ring *ring = kring->ring;
244232238Sluigi	u_int j, l, n, lim = kring->nkr_num_slots - 1;
245232238Sluigi	int force_update = do_lock || kring->nr_kflags & NKR_PENDINTR;
246232238Sluigi	u_int k = ring->cur, resvd = ring->reserved;
247227614Sluigi
248228276Sluigi	k = ring->cur;
249228276Sluigi	if (k > lim)
250227614Sluigi		return netmap_ring_reinit(kring);
251227614Sluigi
252227614Sluigi	if (do_lock)
253227614Sluigi		IGB_RX_LOCK(rxr);
254227614Sluigi
255231778Sluigi	/* XXX check sync modes */
256227614Sluigi	bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map,
257227614Sluigi	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
258227614Sluigi
259232238Sluigi	/*
260232238Sluigi	 * import newly received packets into the netmap ring.
261232238Sluigi	 * j is an index in the netmap ring, l in the NIC ring.
262231778Sluigi	 */
263228276Sluigi	l = rxr->next_to_check;
264232238Sluigi	j = netmap_idx_n2k(kring, l);
265232238Sluigi	if (netmap_no_pendintr || force_update) {
266232238Sluigi		for (n = 0; ; n++) {
267232238Sluigi			union e1000_adv_rx_desc *curr = &rxr->rx_base[l];
268232238Sluigi			uint32_t staterr = le32toh(curr->wb.upper.status_error);
269227614Sluigi
270232238Sluigi			if ((staterr & E1000_RXD_STAT_DD) == 0)
271232238Sluigi				break;
272232238Sluigi			ring->slot[j].len = le16toh(curr->wb.upper.length);
273232238Sluigi			bus_dmamap_sync(rxr->ptag,
274232238Sluigi				rxr->rx_buffers[l].pmap, BUS_DMASYNC_POSTREAD);
275232238Sluigi			j = (j == lim) ? 0 : j + 1;
276232238Sluigi			l = (l == lim) ? 0 : l + 1;
277232238Sluigi		}
278232238Sluigi		if (n) { /* update the state variables */
279232238Sluigi			rxr->next_to_check = l;
280232238Sluigi			kring->nr_hwavail += n;
281232238Sluigi		}
282232238Sluigi		kring->nr_kflags &= ~NKR_PENDINTR;
283227614Sluigi	}
284232238Sluigi
285232238Sluigi	/* skip past packets that userspace has released */
286232238Sluigi        j = kring->nr_hwcur;    /* netmap ring index */
287232238Sluigi	if (resvd > 0) {
288232238Sluigi		if (resvd + ring->avail >= lim + 1) {
289232238Sluigi			D("XXX invalid reserve/avail %d %d", resvd, ring->avail);
290232238Sluigi			ring->reserved = resvd = 0; // XXX panic...
291232238Sluigi		}
292232238Sluigi		k = (k >= resvd) ? k - resvd : k + lim + 1 - resvd;
293227614Sluigi	}
294232238Sluigi	if (j != k) { /* userspace has released some packets. */
295232238Sluigi		l = netmap_idx_k2n(kring, j);
296231881Sluigi		for (n = 0; j != k; n++) {
297227614Sluigi			struct netmap_slot *slot = ring->slot + j;
298228276Sluigi			union e1000_adv_rx_desc *curr = &rxr->rx_base[l];
299228276Sluigi			struct igb_rx_buf *rxbuf = rxr->rx_buffers + l;
300229939Sluigi			uint64_t paddr;
301229939Sluigi			void *addr = PNMB(slot, &paddr);
302227614Sluigi
303227614Sluigi			if (addr == netmap_buffer_base) { /* bad buf */
304227614Sluigi				if (do_lock)
305227614Sluigi					IGB_RX_UNLOCK(rxr);
306227614Sluigi				return netmap_ring_reinit(kring);
307227614Sluigi			}
308227614Sluigi
309227614Sluigi			if (slot->flags & NS_BUF_CHANGED) {
310229939Sluigi				netmap_reload_map(rxr->ptag, rxbuf->pmap, addr);
311227614Sluigi				slot->flags &= ~NS_BUF_CHANGED;
312227614Sluigi			}
313232238Sluigi			curr->read.pkt_addr = htole64(paddr);
314232238Sluigi			curr->wb.upper.status_error = 0;
315227614Sluigi			bus_dmamap_sync(rxr->ptag, rxbuf->pmap,
316227614Sluigi				BUS_DMASYNC_PREREAD);
317227614Sluigi			j = (j == lim) ? 0 : j + 1;
318228276Sluigi			l = (l == lim) ? 0 : l + 1;
319227614Sluigi		}
320227614Sluigi		kring->nr_hwavail -= n;
321231778Sluigi		kring->nr_hwcur = k;
322227614Sluigi		bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map,
323227614Sluigi			BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
324231778Sluigi		/*
325231778Sluigi		 * IMPORTANT: we must leave one free slot in the ring,
326228276Sluigi		 * so move l back by one unit
327227614Sluigi		 */
328228276Sluigi		l = (l == 0) ? lim : l - 1;
329228276Sluigi		E1000_WRITE_REG(&adapter->hw, E1000_RDT(rxr->me), l);
330227614Sluigi	}
331227614Sluigi	/* tell userspace that there are new packets */
332232238Sluigi	ring->avail = kring->nr_hwavail - resvd;
333227614Sluigi	if (do_lock)
334227614Sluigi		IGB_RX_UNLOCK(rxr);
335227614Sluigi	return 0;
336227614Sluigi}
337232238Sluigi
338232238Sluigi
339232238Sluigistatic void
340232238Sluigiigb_netmap_attach(struct adapter *adapter)
341232238Sluigi{
342232238Sluigi	struct netmap_adapter na;
343232238Sluigi
344232238Sluigi	bzero(&na, sizeof(na));
345232238Sluigi
346232238Sluigi	na.ifp = adapter->ifp;
347232238Sluigi	na.separate_locks = 1;
348232238Sluigi	na.num_tx_desc = adapter->num_tx_desc;
349232238Sluigi	na.num_rx_desc = adapter->num_rx_desc;
350232238Sluigi	na.nm_txsync = igb_netmap_txsync;
351232238Sluigi	na.nm_rxsync = igb_netmap_rxsync;
352232238Sluigi	na.nm_lock = igb_netmap_lock_wrapper;
353232238Sluigi	na.nm_register = igb_netmap_reg;
354232238Sluigi	netmap_attach(&na, adapter->num_queues);
355232238Sluigi}
356231778Sluigi/* end of file */
357