if_lem_netmap.h revision 232238
1227614Sluigi/*
2227614Sluigi * Copyright (C) 2011 Matteo Landi, Luigi Rizzo. 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
26232238Sluigi
27227614Sluigi/*
28227614Sluigi * $FreeBSD: head/sys/dev/netmap/if_lem_netmap.h 232238 2012-02-27 19:05:01Z luigi $
29232238Sluigi * $Id: if_lem_netmap.h 10627 2012-02-23 19:37:15Z luigi $
30227614Sluigi *
31232238Sluigi * netmap support for "lem"
32228276Sluigi *
33232238Sluigi * For details on netmap support please see ixgbe_netmap.h
34227614Sluigi */
35227614Sluigi
36227614Sluigi#include <net/netmap.h>
37227614Sluigi#include <sys/selinfo.h>
38227614Sluigi#include <vm/vm.h>
39227614Sluigi#include <vm/pmap.h>    /* vtophys ? */
40227614Sluigi#include <dev/netmap/netmap_kern.h>
41227614Sluigi
42227614Sluigi
43227614Sluigistatic void
44231594Sluigilem_netmap_lock_wrapper(struct ifnet *ifp, int what, u_int ringid)
45227614Sluigi{
46231594Sluigi	struct adapter *adapter = ifp->if_softc;
47227614Sluigi
48227614Sluigi	/* only one ring here so ignore the ringid */
49227614Sluigi	switch (what) {
50227614Sluigi	case NETMAP_CORE_LOCK:
51227614Sluigi		EM_CORE_LOCK(adapter);
52227614Sluigi		break;
53227614Sluigi	case NETMAP_CORE_UNLOCK:
54227614Sluigi		EM_CORE_UNLOCK(adapter);
55227614Sluigi		break;
56227614Sluigi	case NETMAP_TX_LOCK:
57227614Sluigi		EM_TX_LOCK(adapter);
58227614Sluigi		break;
59227614Sluigi	case NETMAP_TX_UNLOCK:
60227614Sluigi		EM_TX_UNLOCK(adapter);
61227614Sluigi		break;
62227614Sluigi	case NETMAP_RX_LOCK:
63227614Sluigi		EM_RX_LOCK(adapter);
64227614Sluigi		break;
65227614Sluigi	case NETMAP_RX_UNLOCK:
66227614Sluigi		EM_RX_UNLOCK(adapter);
67227614Sluigi		break;
68227614Sluigi	}
69227614Sluigi}
70227614Sluigi
71227614Sluigi
72227614Sluigi/*
73232238Sluigi * Register/unregister
74227614Sluigi */
75227614Sluigistatic int
76228276Sluigilem_netmap_reg(struct ifnet *ifp, int onoff)
77228276Sluigi{
78228276Sluigi	struct adapter *adapter = ifp->if_softc;
79228276Sluigi	struct netmap_adapter *na = NA(ifp);
80228276Sluigi	int error = 0;
81228276Sluigi
82228276Sluigi	if (na == NULL)
83232238Sluigi		return EINVAL;
84228276Sluigi
85228276Sluigi	lem_disable_intr(adapter);
86228276Sluigi
87228276Sluigi	/* Tell the stack that the interface is no longer active */
88228276Sluigi	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
89228276Sluigi
90228276Sluigi#ifndef EM_LEGACY_IRQ // XXX do we need this ?
91228276Sluigi	taskqueue_block(adapter->tq);
92228276Sluigi	taskqueue_drain(adapter->tq, &adapter->rxtx_task);
93228276Sluigi	taskqueue_drain(adapter->tq, &adapter->link_task);
94228276Sluigi#endif /* !EM_LEGCY_IRQ */
95228276Sluigi	if (onoff) {
96228276Sluigi		ifp->if_capenable |= IFCAP_NETMAP;
97228276Sluigi
98228276Sluigi		na->if_transmit = ifp->if_transmit;
99228276Sluigi		ifp->if_transmit = netmap_start;
100228276Sluigi
101228276Sluigi		lem_init_locked(adapter);
102228276Sluigi		if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) == 0) {
103228276Sluigi			error = ENOMEM;
104228276Sluigi			goto fail;
105228276Sluigi		}
106228276Sluigi	} else {
107228276Sluigifail:
108231778Sluigi		/* return to non-netmap mode */
109228276Sluigi		ifp->if_transmit = na->if_transmit;
110228276Sluigi		ifp->if_capenable &= ~IFCAP_NETMAP;
111231778Sluigi		lem_init_locked(adapter);	/* also enable intr */
112228276Sluigi	}
113228276Sluigi
114228276Sluigi#ifndef EM_LEGACY_IRQ
115228276Sluigi	taskqueue_unblock(adapter->tq); // XXX do we need this ?
116228276Sluigi#endif /* !EM_LEGCY_IRQ */
117228276Sluigi
118228276Sluigi	return (error);
119228276Sluigi}
120228276Sluigi
121228276Sluigi
122228276Sluigi/*
123232238Sluigi * Reconcile kernel and user view of the transmit ring.
124228276Sluigi */
125228276Sluigistatic int
126231594Sluigilem_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock)
127227614Sluigi{
128231594Sluigi	struct adapter *adapter = ifp->if_softc;
129232238Sluigi	struct netmap_adapter *na = NA(ifp);
130231796Sluigi	struct netmap_kring *kring = &na->tx_rings[ring_nr];
131227614Sluigi	struct netmap_ring *ring = kring->ring;
132232238Sluigi	u_int j, k, l, n = 0, lim = kring->nkr_num_slots - 1;
133227614Sluigi
134227614Sluigi	/* generate an interrupt approximately every half ring */
135227614Sluigi	int report_frequency = kring->nkr_num_slots >> 1;
136227614Sluigi
137232238Sluigi	/* take a copy of ring->cur now, and never read it again */
138227614Sluigi	k = ring->cur;
139228276Sluigi	if (k > lim)
140227614Sluigi		return netmap_ring_reinit(kring);
141227614Sluigi
142227614Sluigi	if (do_lock)
143227614Sluigi		EM_TX_LOCK(adapter);
144227614Sluigi	bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map,
145227614Sluigi			BUS_DMASYNC_POSTREAD);
146232238Sluigi	/*
147232238Sluigi	 * Process new packets to send. j is the current index in the
148232238Sluigi	 * netmap ring, l is the corresponding index in the NIC ring.
149231778Sluigi	 */
150231778Sluigi	j = kring->nr_hwcur;
151232238Sluigi	if (j != k) {	/* we have new packets to send */
152232238Sluigi		l = netmap_idx_k2n(kring, j);
153231881Sluigi		for (n = 0; j != k; n++) {
154232238Sluigi			/* slot is the current slot in the netmap ring */
155227614Sluigi			struct netmap_slot *slot = &ring->slot[j];
156232238Sluigi			/* curr is the current slot in the nic ring */
157228276Sluigi			struct e1000_tx_desc *curr = &adapter->tx_desc_base[l];
158228276Sluigi			struct em_buffer *txbuf = &adapter->tx_buffer_area[l];
159227614Sluigi			int flags = ((slot->flags & NS_REPORT) ||
160227614Sluigi				j == 0 || j == report_frequency) ?
161227614Sluigi					E1000_TXD_CMD_RS : 0;
162231778Sluigi			uint64_t paddr;
163231778Sluigi			void *addr = PNMB(slot, &paddr);
164232238Sluigi			u_int len = slot->len;
165227614Sluigi
166227614Sluigi			if (addr == netmap_buffer_base || len > NETMAP_BUF_SIZE) {
167227614Sluigi				if (do_lock)
168227614Sluigi					EM_TX_UNLOCK(adapter);
169227614Sluigi				return netmap_ring_reinit(kring);
170227614Sluigi			}
171227614Sluigi
172228276Sluigi			slot->flags &= ~NS_REPORT;
173227614Sluigi			if (slot->flags & NS_BUF_CHANGED) {
174228276Sluigi				/* buffer has changed, reload map */
175229939Sluigi				netmap_reload_map(adapter->txtag, txbuf->map, addr);
176232238Sluigi				curr->buffer_addr = htole64(paddr);
177227614Sluigi				slot->flags &= ~NS_BUF_CHANGED;
178227614Sluigi			}
179232238Sluigi			curr->upper.data = 0;
180232238Sluigi			curr->lower.data =
181232238Sluigi			    htole32( adapter->txd_cmd | len |
182232238Sluigi				(E1000_TXD_CMD_EOP | flags) );
183227614Sluigi
184227614Sluigi			bus_dmamap_sync(adapter->txtag, txbuf->map,
185228276Sluigi			    BUS_DMASYNC_PREWRITE);
186227614Sluigi			j = (j == lim) ? 0 : j + 1;
187228276Sluigi			l = (l == lim) ? 0 : l + 1;
188227614Sluigi		}
189232238Sluigi		kring->nr_hwcur = k; /* the saved ring->cur */
190228276Sluigi		kring->nr_hwavail -= n;
191227614Sluigi
192227614Sluigi		bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map,
193228276Sluigi		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
194227614Sluigi
195228276Sluigi		E1000_WRITE_REG(&adapter->hw, E1000_TDT(0), l);
196227614Sluigi	}
197228276Sluigi
198228276Sluigi	if (n == 0 || kring->nr_hwavail < 1) {
199228276Sluigi		int delta;
200228276Sluigi
201228276Sluigi		/* record completed transmissions using TDH */
202228276Sluigi		l = E1000_READ_REG(&adapter->hw, E1000_TDH(0));
203231778Sluigi		if (l >= kring->nkr_num_slots) { /* XXX can it happen ? */
204228276Sluigi			D("bad TDH %d", l);
205228276Sluigi			l -= kring->nkr_num_slots;
206228276Sluigi		}
207228276Sluigi		delta = l - adapter->next_tx_to_clean;
208228276Sluigi		if (delta) {
209232238Sluigi			/* some tx completed, increment hwavail. */
210228276Sluigi			if (delta < 0)
211228276Sluigi				delta += kring->nkr_num_slots;
212228276Sluigi			adapter->next_tx_to_clean = l;
213228276Sluigi			kring->nr_hwavail += delta;
214228276Sluigi		}
215228276Sluigi	}
216232238Sluigi	/* update avail to what the kernel knows */
217231778Sluigi	ring->avail = kring->nr_hwavail;
218231198Sluigi
219227614Sluigi	if (do_lock)
220227614Sluigi		EM_TX_UNLOCK(adapter);
221227614Sluigi	return 0;
222227614Sluigi}
223227614Sluigi
224227614Sluigi
225227614Sluigi/*
226228276Sluigi * Reconcile kernel and user view of the receive ring.
227227614Sluigi */
228227614Sluigistatic int
229231594Sluigilem_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock)
230227614Sluigi{
231231594Sluigi	struct adapter *adapter = ifp->if_softc;
232232238Sluigi	struct netmap_adapter *na = NA(ifp);
233231796Sluigi	struct netmap_kring *kring = &na->rx_rings[ring_nr];
234227614Sluigi	struct netmap_ring *ring = kring->ring;
235232238Sluigi	int j, l, n, lim = kring->nkr_num_slots - 1;
236232238Sluigi	int force_update = do_lock || kring->nr_kflags & NKR_PENDINTR;
237232238Sluigi	u_int k = ring->cur, resvd = ring->reserved;
238227614Sluigi
239228276Sluigi	if (k > lim)
240227614Sluigi		return netmap_ring_reinit(kring);
241227614Sluigi
242227614Sluigi	if (do_lock)
243227614Sluigi		EM_RX_LOCK(adapter);
244231778Sluigi
245227614Sluigi	/* XXX check sync modes */
246227614Sluigi	bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map,
247227614Sluigi			BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
248227614Sluigi
249232238Sluigi	/*
250232238Sluigi	 * Import newly received packets into the netmap ring.
251232238Sluigi	 * j is an index in the netmap ring, l in the NIC ring.
252231778Sluigi	 */
253231778Sluigi	l = adapter->next_rx_desc_to_check;
254232238Sluigi	j = netmap_idx_n2k(kring, l);
255232238Sluigi	if (netmap_no_pendintr || force_update) {
256232238Sluigi		for (n = 0; ; n++) {
257232238Sluigi			struct e1000_rx_desc *curr = &adapter->rx_desc_base[l];
258232238Sluigi			uint32_t staterr = le32toh(curr->status);
259232238Sluigi			int len;
260227614Sluigi
261232238Sluigi			if ((staterr & E1000_RXD_STAT_DD) == 0)
262232238Sluigi				break;
263232238Sluigi			len = le16toh(curr->length) - 4; // CRC
264232238Sluigi			if (len < 0) {
265232238Sluigi				D("bogus pkt size at %d", j);
266232238Sluigi				len = 0;
267232238Sluigi			}
268232238Sluigi			ring->slot[j].len = len;
269232238Sluigi			bus_dmamap_sync(adapter->rxtag,
270232238Sluigi				adapter->rx_buffer_area[l].map,
271232238Sluigi				    BUS_DMASYNC_POSTREAD);
272232238Sluigi			j = (j == lim) ? 0 : j + 1;
273232238Sluigi			l = (l == lim) ? 0 : l + 1;
274227614Sluigi		}
275232238Sluigi		if (n) { /* update the state variables */
276232238Sluigi			adapter->next_rx_desc_to_check = l;
277232238Sluigi			kring->nr_hwavail += n;
278232238Sluigi		}
279232238Sluigi		kring->nr_kflags &= ~NKR_PENDINTR;
280227614Sluigi	}
281227614Sluigi
282232238Sluigi	/* skip past packets that userspace has released */
283231796Sluigi	j = kring->nr_hwcur;	/* netmap ring index */
284232238Sluigi	if (resvd > 0) {
285232238Sluigi		if (resvd + ring->avail >= lim + 1) {
286232238Sluigi			D("XXX invalid reserve/avail %d %d", resvd, ring->avail);
287232238Sluigi			ring->reserved = resvd = 0; // XXX panic...
288232238Sluigi		}
289232238Sluigi		k = (k >= resvd) ? k - resvd : k + lim + 1 - resvd;
290232238Sluigi	}
291232238Sluigi	if (j != k) { /* userspace has released some packets. */
292232238Sluigi		l = netmap_idx_k2n(kring, j); /* NIC ring index */
293231881Sluigi		for (n = 0; j != k; n++) {
294227614Sluigi			struct netmap_slot *slot = &ring->slot[j];
295228276Sluigi			struct e1000_rx_desc *curr = &adapter->rx_desc_base[l];
296228276Sluigi			struct em_buffer *rxbuf = &adapter->rx_buffer_area[l];
297229939Sluigi			uint64_t paddr;
298229939Sluigi			void *addr = PNMB(slot, &paddr);
299227614Sluigi
300227614Sluigi			if (addr == netmap_buffer_base) { /* bad buf */
301227614Sluigi				if (do_lock)
302227614Sluigi					EM_RX_UNLOCK(adapter);
303227614Sluigi				return netmap_ring_reinit(kring);
304227614Sluigi			}
305231778Sluigi
306227614Sluigi			if (slot->flags & NS_BUF_CHANGED) {
307231796Sluigi				/* buffer has changed, reload map */
308229939Sluigi				netmap_reload_map(adapter->rxtag, rxbuf->map, addr);
309232238Sluigi				curr->buffer_addr = htole64(paddr);
310227614Sluigi				slot->flags &= ~NS_BUF_CHANGED;
311227614Sluigi			}
312232238Sluigi			curr->status = 0;
313227614Sluigi
314227614Sluigi			bus_dmamap_sync(adapter->rxtag, rxbuf->map,
315228276Sluigi			    BUS_DMASYNC_PREREAD);
316227614Sluigi
317227614Sluigi			j = (j == lim) ? 0 : j + 1;
318228276Sluigi			l = (l == lim) ? 0 : l + 1;
319227614Sluigi		}
320227614Sluigi		kring->nr_hwavail -= n;
321228276Sluigi		kring->nr_hwcur = k;
322227614Sluigi		bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map,
323228276Sluigi		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
324227614Sluigi		/*
325227614Sluigi		 * 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(0), l);
330227614Sluigi	}
331227614Sluigi	/* tell userspace that there are new packets */
332232238Sluigi	ring->avail = kring->nr_hwavail - resvd;
333227614Sluigi	if (do_lock)
334227614Sluigi		EM_RX_UNLOCK(adapter);
335227614Sluigi	return 0;
336227614Sluigi}
337232238Sluigi
338232238Sluigi
339232238Sluigistatic void
340232238Sluigilem_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 = lem_netmap_txsync;
351232238Sluigi	na.nm_rxsync = lem_netmap_rxsync;
352232238Sluigi	na.nm_lock = lem_netmap_lock_wrapper;
353232238Sluigi	na.nm_register = lem_netmap_reg;
354232238Sluigi	netmap_attach(&na, 1);
355232238Sluigi}
356232238Sluigi
357231198Sluigi/* end of file */
358