1262153Sluigi/*
2262153Sluigi * Copyright (C) 2014 Giuseppe Lettieri. All rights reserved.
3262153Sluigi *
4262153Sluigi * Redistribution and use in source and binary forms, with or without
5262153Sluigi * modification, are permitted provided that the following conditions
6262153Sluigi * are met:
7262153Sluigi *   1. Redistributions of source code must retain the above copyright
8262153Sluigi *      notice, this list of conditions and the following disclaimer.
9262153Sluigi *   2. Redistributions in binary form must reproduce the above copyright
10262153Sluigi *      notice, this list of conditions and the following disclaimer in the
11262153Sluigi *      documentation and/or other materials provided with the distribution.
12262153Sluigi *
13262153Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14262153Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15262153Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16262153Sluigi * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17262153Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18262153Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19262153Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20262153Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21262153Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22262153Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23262153Sluigi * SUCH DAMAGE.
24262153Sluigi */
25262153Sluigi
26262153Sluigi/* $FreeBSD$ */
27262153Sluigi
28262153Sluigi#if defined(__FreeBSD__)
29262153Sluigi#include <sys/cdefs.h> /* prerequisite */
30262153Sluigi
31262153Sluigi#include <sys/types.h>
32262153Sluigi#include <sys/errno.h>
33262153Sluigi#include <sys/param.h>	/* defines used in kernel.h */
34262153Sluigi#include <sys/kernel.h>	/* types used in module initialization */
35262153Sluigi#include <sys/malloc.h>
36262153Sluigi#include <sys/poll.h>
37262153Sluigi#include <sys/lock.h>
38262153Sluigi#include <sys/rwlock.h>
39262153Sluigi#include <sys/selinfo.h>
40262153Sluigi#include <sys/sysctl.h>
41262153Sluigi#include <sys/socket.h> /* sockaddrs */
42262153Sluigi#include <net/if.h>
43262153Sluigi#include <net/if_var.h>
44262153Sluigi#include <machine/bus.h>	/* bus_dmamap_* */
45262153Sluigi#include <sys/refcount.h>
46262153Sluigi
47262153Sluigi
48262153Sluigi#elif defined(linux)
49262153Sluigi
50262153Sluigi#include "bsd_glue.h"
51262153Sluigi
52262153Sluigi#elif defined(__APPLE__)
53262153Sluigi
54262153Sluigi#warning OSX support is only partial
55262153Sluigi#include "osx_glue.h"
56262153Sluigi
57262153Sluigi#else
58262153Sluigi
59262153Sluigi#error	Unsupported platform
60262153Sluigi
61262153Sluigi#endif /* unsupported */
62262153Sluigi
63262153Sluigi/*
64262153Sluigi * common headers
65262153Sluigi */
66262153Sluigi
67262153Sluigi#include <net/netmap.h>
68262153Sluigi#include <dev/netmap/netmap_kern.h>
69262153Sluigi#include <dev/netmap/netmap_mem2.h>
70262153Sluigi
71262153Sluigi#ifdef WITH_PIPES
72262153Sluigi
73262153Sluigi#define NM_PIPE_MAXSLOTS	4096
74262153Sluigi
75262153Sluigiint netmap_default_pipes = 0; /* default number of pipes for each nic */
76262153SluigiSYSCTL_DECL(_dev_netmap);
77262153SluigiSYSCTL_INT(_dev_netmap, OID_AUTO, default_pipes, CTLFLAG_RW, &netmap_default_pipes, 0 , "");
78262153Sluigi
79262153Sluigi/* allocate the pipe array in the parent adapter */
80262153Sluigiint
81262153Sluiginetmap_pipe_alloc(struct netmap_adapter *na, struct nmreq *nmr)
82262153Sluigi{
83262153Sluigi	size_t len;
84262153Sluigi	int mode = nmr->nr_flags & NR_REG_MASK;
85262153Sluigi	u_int npipes;
86262153Sluigi
87262153Sluigi	if (mode == NR_REG_PIPE_MASTER || mode == NR_REG_PIPE_SLAVE) {
88262153Sluigi		/* this is for our parent, not for us */
89262153Sluigi		return 0;
90262153Sluigi	}
91262153Sluigi
92262153Sluigi	/* TODO: we can resize the array if the new
93262153Sluigi         * request can accomodate the already existing pipes
94262153Sluigi         */
95262153Sluigi	if (na->na_pipes) {
96262153Sluigi		nmr->nr_arg1 = na->na_max_pipes;
97262153Sluigi		return 0;
98262153Sluigi	}
99262153Sluigi
100262153Sluigi	npipes = nmr->nr_arg1;
101262153Sluigi	if (npipes == 0)
102262153Sluigi		npipes = netmap_default_pipes;
103262153Sluigi	nm_bound_var(&npipes, 0, 0, NM_MAXPIPES, NULL);
104262153Sluigi
105262153Sluigi	if (npipes == 0) {
106262153Sluigi		/* really zero, nothing to alloc */
107262153Sluigi		goto out;
108262153Sluigi	}
109262153Sluigi
110262153Sluigi	len = sizeof(struct netmap_pipe_adapter *) * npipes;
111262153Sluigi	na->na_pipes = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
112262153Sluigi	if (na->na_pipes == NULL)
113262153Sluigi		return ENOMEM;
114262153Sluigi
115262153Sluigi	na->na_max_pipes = npipes;
116262153Sluigi	na->na_next_pipe = 0;
117262153Sluigi
118262153Sluigiout:
119262153Sluigi	nmr->nr_arg1 = npipes;
120262153Sluigi
121262153Sluigi	return 0;
122262153Sluigi}
123262153Sluigi
124262153Sluigi/* deallocate the parent array in the parent adapter */
125262153Sluigivoid
126262153Sluiginetmap_pipe_dealloc(struct netmap_adapter *na)
127262153Sluigi{
128262153Sluigi	if (na->na_pipes) {
129262153Sluigi		ND("freeing pipes for %s", NM_IFPNAME(na->ifp));
130262153Sluigi		free(na->na_pipes, M_DEVBUF);
131262153Sluigi		na->na_pipes = NULL;
132262153Sluigi		na->na_max_pipes = 0;
133262153Sluigi		na->na_next_pipe = 0;
134262153Sluigi	}
135262153Sluigi}
136262153Sluigi
137262153Sluigi/* find a pipe endpoint with the given id among the parent's pipes */
138262153Sluigistatic struct netmap_pipe_adapter *
139262153Sluiginetmap_pipe_find(struct netmap_adapter *parent, u_int pipe_id)
140262153Sluigi{
141262153Sluigi	int i;
142262153Sluigi	struct netmap_pipe_adapter *na;
143262153Sluigi
144262153Sluigi	for (i = 0; i < parent->na_next_pipe; i++) {
145262153Sluigi		na = parent->na_pipes[i];
146262153Sluigi		if (na->id == pipe_id) {
147262153Sluigi			return na;
148262153Sluigi		}
149262153Sluigi	}
150262153Sluigi	return NULL;
151262153Sluigi}
152262153Sluigi
153262153Sluigi/* add a new pipe endpoint to the parent array */
154262153Sluigistatic int
155262153Sluiginetmap_pipe_add(struct netmap_adapter *parent, struct netmap_pipe_adapter *na)
156262153Sluigi{
157262153Sluigi	if (parent->na_next_pipe >= parent->na_max_pipes) {
158262153Sluigi		D("%s: no space left for pipes", NM_IFPNAME(parent->ifp));
159262153Sluigi		return ENOMEM;
160262153Sluigi	}
161262153Sluigi
162262153Sluigi	parent->na_pipes[parent->na_next_pipe] = na;
163262153Sluigi	na->parent_slot = parent->na_next_pipe;
164262153Sluigi	parent->na_next_pipe++;
165262153Sluigi	return 0;
166262153Sluigi}
167262153Sluigi
168262153Sluigi/* remove the given pipe endpoint from the parent array */
169262153Sluigistatic void
170262153Sluiginetmap_pipe_remove(struct netmap_adapter *parent, struct netmap_pipe_adapter *na)
171262153Sluigi{
172262153Sluigi	u_int n;
173262153Sluigi	n = --parent->na_next_pipe;
174262153Sluigi	if (n != na->parent_slot) {
175262153Sluigi		parent->na_pipes[na->parent_slot] =
176262153Sluigi			parent->na_pipes[n];
177262153Sluigi	}
178262153Sluigi	parent->na_pipes[n] = NULL;
179262153Sluigi}
180262153Sluigi
181262153Sluigistatic int
182262153Sluiginetmap_pipe_txsync(struct netmap_adapter *na, u_int ring_nr, int flags)
183262153Sluigi{
184262153Sluigi        struct netmap_kring *txkring = na->tx_rings + ring_nr,
185262153Sluigi		*rxkring = txkring->pipe;
186262153Sluigi        u_int limit; /* slots to transfer */
187262153Sluigi        u_int j, k, lim_tx = txkring->nkr_num_slots - 1,
188262153Sluigi                lim_rx = rxkring->nkr_num_slots - 1;
189262153Sluigi        int m, busy;
190262153Sluigi
191262153Sluigi        ND("%p: %s %x -> %s", txkring, txkring->name, flags, rxkring->name);
192262153Sluigi        ND(2, "before: hwcur %d hwtail %d cur %d head %d tail %d", txkring->nr_hwcur, txkring->nr_hwtail,
193262153Sluigi                txkring->rcur, txkring->rhead, txkring->rtail);
194262153Sluigi
195262153Sluigi        j = rxkring->nr_hwtail; /* RX */
196262153Sluigi        k = txkring->nr_hwcur;  /* TX */
197262153Sluigi        m = txkring->rhead - txkring->nr_hwcur; /* new slots */
198262153Sluigi        if (m < 0)
199262153Sluigi                m += txkring->nkr_num_slots;
200262153Sluigi        limit = m;
201262153Sluigi        m = rxkring->nkr_num_slots - 1; /* max avail space on destination */
202262153Sluigi        busy = j - rxkring->nr_hwcur; /* busy slots */
203262153Sluigi	if (busy < 0)
204262153Sluigi		busy += txkring->nkr_num_slots;
205262153Sluigi	m -= busy; /* subtract busy slots */
206262153Sluigi        ND(2, "m %d limit %d", m, limit);
207262153Sluigi        if (m < limit)
208262153Sluigi                limit = m;
209262153Sluigi
210262153Sluigi	if (limit == 0) {
211262153Sluigi		/* either the rxring is full, or nothing to send */
212262153Sluigi		nm_txsync_finalize(txkring); /* actually useless */
213262153Sluigi		return 0;
214262153Sluigi	}
215262153Sluigi
216262153Sluigi        while (limit-- > 0) {
217262153Sluigi                struct netmap_slot *rs = &rxkring->save_ring->slot[j];
218262153Sluigi                struct netmap_slot *ts = &txkring->ring->slot[k];
219262153Sluigi                struct netmap_slot tmp;
220262153Sluigi
221262153Sluigi                /* swap the slots */
222262153Sluigi                tmp = *rs;
223262153Sluigi                *rs = *ts;
224262153Sluigi                *ts = tmp;
225262153Sluigi
226262153Sluigi                /* no need to report the buffer change */
227262153Sluigi
228262153Sluigi                j = nm_next(j, lim_rx);
229262153Sluigi                k = nm_next(k, lim_tx);
230262153Sluigi        }
231262153Sluigi
232262153Sluigi        wmb(); /* make sure the slots are updated before publishing them */
233262153Sluigi        rxkring->nr_hwtail = j;
234262153Sluigi        txkring->nr_hwcur = k;
235262153Sluigi        txkring->nr_hwtail = nm_prev(k, lim_tx);
236262153Sluigi
237262153Sluigi        nm_txsync_finalize(txkring);
238262153Sluigi        ND(2, "after: hwcur %d hwtail %d cur %d head %d tail %d j %d", txkring->nr_hwcur, txkring->nr_hwtail,
239262153Sluigi                txkring->rcur, txkring->rhead, txkring->rtail, j);
240262153Sluigi
241262153Sluigi        wmb(); /* make sure rxkring->nr_hwtail is updated before notifying */
242262153Sluigi        rxkring->na->nm_notify(rxkring->na, rxkring->ring_id, NR_RX, 0);
243262153Sluigi
244262153Sluigi	return 0;
245262153Sluigi}
246262153Sluigi
247262153Sluigistatic int
248262153Sluiginetmap_pipe_rxsync(struct netmap_adapter *na, u_int ring_nr, int flags)
249262153Sluigi{
250262153Sluigi        struct netmap_kring *rxkring = na->rx_rings + ring_nr,
251262153Sluigi		*txkring = rxkring->pipe;
252262153Sluigi	uint32_t oldhwcur = rxkring->nr_hwcur;
253262153Sluigi
254262153Sluigi        ND("%s %x <- %s", rxkring->name, flags, txkring->name);
255262153Sluigi        rxkring->nr_hwcur = rxkring->rhead; /* recover user-relased slots */
256262153Sluigi        ND(5, "hwcur %d hwtail %d cur %d head %d tail %d", rxkring->nr_hwcur, rxkring->nr_hwtail,
257262153Sluigi                rxkring->rcur, rxkring->rhead, rxkring->rtail);
258262153Sluigi        rmb(); /* paired with the first wmb() in txsync */
259262153Sluigi        nm_rxsync_finalize(rxkring);
260262153Sluigi
261262153Sluigi	if (oldhwcur != rxkring->nr_hwcur) {
262262153Sluigi		/* we have released some slots, notify the other end */
263262153Sluigi		wmb(); /* make sure nr_hwcur is updated before notifying */
264262153Sluigi		txkring->na->nm_notify(txkring->na, txkring->ring_id, NR_TX, 0);
265262153Sluigi	}
266262153Sluigi        return 0;
267262153Sluigi}
268262153Sluigi
269262153Sluigi/* Pipe endpoints are created and destroyed together, so that endopoints do not
270262153Sluigi * have to check for the existence of their peer at each ?xsync.
271262153Sluigi *
272262153Sluigi * To play well with the existing netmap infrastructure (refcounts etc.), we
273262153Sluigi * adopt the following strategy:
274262153Sluigi *
275262153Sluigi * 1) The first endpoint that is created also creates the other endpoint and
276262153Sluigi * grabs a reference to it.
277262153Sluigi *
278262153Sluigi *    state A)  user1 --> endpoint1 --> endpoint2
279262153Sluigi *
280262153Sluigi * 2) If, starting from state A, endpoint2 is then registered, endpoint1 gives
281262153Sluigi * its reference to the user:
282262153Sluigi *
283262153Sluigi *    state B)  user1 --> endpoint1     endpoint2 <--- user2
284262153Sluigi *
285262153Sluigi * 3) Assume that, starting from state B endpoint2 is closed. In the unregister
286262153Sluigi * callback endpoint2 notes that endpoint1 is still active and adds a reference
287262153Sluigi * from endpoint1 to itself. When user2 then releases her own reference,
288262153Sluigi * endpoint2 is not destroyed and we are back to state A. A symmetrical state
289262153Sluigi * would be reached if endpoint1 were released instead.
290262153Sluigi *
291262153Sluigi * 4) If, starting from state A, endpoint1 is closed, the destructor notes that
292262153Sluigi * it owns a reference to endpoint2 and releases it.
293262153Sluigi *
294262153Sluigi * Something similar goes on for the creation and destruction of the krings.
295262153Sluigi */
296262153Sluigi
297262153Sluigi
298262153Sluigi/* netmap_pipe_krings_delete.
299262153Sluigi *
300262153Sluigi * There are two cases:
301262153Sluigi *
302262153Sluigi * 1) state is
303262153Sluigi *
304262153Sluigi *        usr1 --> e1 --> e2
305262153Sluigi *
306262153Sluigi *    and we are e1. We have to create both sets
307262153Sluigi *    of krings.
308262153Sluigi *
309262153Sluigi * 2) state is
310262153Sluigi *
311262153Sluigi *        usr1 --> e1 --> e2
312262153Sluigi *
313262153Sluigi *    and we are e2. e1 is certainly registered and our
314262153Sluigi *    krings already exist, but they may be hidden.
315262153Sluigi */
316262153Sluigistatic int
317262153Sluiginetmap_pipe_krings_create(struct netmap_adapter *na)
318262153Sluigi{
319262153Sluigi	struct netmap_pipe_adapter *pna =
320262153Sluigi		(struct netmap_pipe_adapter *)na;
321262153Sluigi	struct netmap_adapter *ona = &pna->peer->up;
322262153Sluigi	int error = 0;
323262153Sluigi	if (pna->peer_ref) {
324262153Sluigi		int i;
325262153Sluigi
326262153Sluigi		/* case 1) above */
327262153Sluigi		D("%p: case 1, create everything", na);
328262153Sluigi		error = netmap_krings_create(na, 0);
329262153Sluigi		if (error)
330262153Sluigi			goto err;
331262153Sluigi
332262153Sluigi		/* we also create all the rings, since we need to
333262153Sluigi                 * update the save_ring pointers.
334262153Sluigi                 * netmap_mem_rings_create (called by our caller)
335262153Sluigi                 * will not create the rings again
336262153Sluigi                 */
337262153Sluigi
338262153Sluigi		error = netmap_mem_rings_create(na);
339262153Sluigi		if (error)
340262153Sluigi			goto del_krings1;
341262153Sluigi
342262153Sluigi		/* update our hidden ring pointers */
343262153Sluigi		for (i = 0; i < na->num_tx_rings + 1; i++)
344262153Sluigi			na->tx_rings[i].save_ring = na->tx_rings[i].ring;
345262153Sluigi		for (i = 0; i < na->num_rx_rings + 1; i++)
346262153Sluigi			na->rx_rings[i].save_ring = na->rx_rings[i].ring;
347262153Sluigi
348262153Sluigi		/* now, create krings and rings of the other end */
349262153Sluigi		error = netmap_krings_create(ona, 0);
350262153Sluigi		if (error)
351262153Sluigi			goto del_rings1;
352262153Sluigi
353262153Sluigi		error = netmap_mem_rings_create(ona);
354262153Sluigi		if (error)
355262153Sluigi			goto del_krings2;
356262153Sluigi
357262153Sluigi		for (i = 0; i < ona->num_tx_rings + 1; i++)
358262153Sluigi			ona->tx_rings[i].save_ring = ona->tx_rings[i].ring;
359262153Sluigi		for (i = 0; i < ona->num_rx_rings + 1; i++)
360262153Sluigi			ona->rx_rings[i].save_ring = ona->rx_rings[i].ring;
361262153Sluigi
362262153Sluigi		/* cross link the krings */
363262153Sluigi		for (i = 0; i < na->num_tx_rings; i++) {
364262153Sluigi			na->tx_rings[i].pipe = pna->peer->up.rx_rings + i;
365262153Sluigi			na->rx_rings[i].pipe = pna->peer->up.tx_rings + i;
366262153Sluigi			pna->peer->up.tx_rings[i].pipe = na->rx_rings + i;
367262153Sluigi			pna->peer->up.rx_rings[i].pipe = na->tx_rings + i;
368262153Sluigi		}
369262153Sluigi	} else {
370262153Sluigi		int i;
371262153Sluigi		/* case 2) above */
372262153Sluigi		/* recover the hidden rings */
373262153Sluigi		ND("%p: case 2, hidden rings", na);
374262153Sluigi		for (i = 0; i < na->num_tx_rings + 1; i++)
375262153Sluigi			na->tx_rings[i].ring = na->tx_rings[i].save_ring;
376262153Sluigi		for (i = 0; i < na->num_rx_rings + 1; i++)
377262153Sluigi			na->rx_rings[i].ring = na->rx_rings[i].save_ring;
378262153Sluigi	}
379262153Sluigi	return 0;
380262153Sluigi
381262153Sluigidel_krings2:
382262153Sluigi	netmap_krings_delete(ona);
383262153Sluigidel_rings1:
384262153Sluigi	netmap_mem_rings_delete(na);
385262153Sluigidel_krings1:
386262153Sluigi	netmap_krings_delete(na);
387262153Sluigierr:
388262153Sluigi	return error;
389262153Sluigi}
390262153Sluigi
391262153Sluigi/* netmap_pipe_reg.
392262153Sluigi *
393262153Sluigi * There are two cases on registration (onoff==1)
394262153Sluigi *
395262153Sluigi * 1.a) state is
396262153Sluigi *
397262153Sluigi *        usr1 --> e1 --> e2
398262153Sluigi *
399262153Sluigi *      and we are e1. Nothing special to do.
400262153Sluigi *
401262153Sluigi * 1.b) state is
402262153Sluigi *
403262153Sluigi *        usr1 --> e1 --> e2 <-- usr2
404262153Sluigi *
405262153Sluigi *      and we are e2. Drop the ref e1 is holding.
406262153Sluigi *
407262153Sluigi *  There are two additional cases on unregister (onoff==0)
408262153Sluigi *
409262153Sluigi *  2.a) state is
410262153Sluigi *
411262153Sluigi *         usr1 --> e1 --> e2
412262153Sluigi *
413262153Sluigi *       and we are e1. Nothing special to do, e2 will
414262153Sluigi *       be cleaned up by the destructor of e1.
415262153Sluigi *
416262153Sluigi *  2.b) state is
417262153Sluigi *
418262153Sluigi *         usr1 --> e1     e2 <-- usr2
419262153Sluigi *
420262153Sluigi *       and we are either e1 or e2. Add a ref from the
421262153Sluigi *       other end and hide our rings.
422262153Sluigi */
423262153Sluigistatic int
424262153Sluiginetmap_pipe_reg(struct netmap_adapter *na, int onoff)
425262153Sluigi{
426262153Sluigi	struct netmap_pipe_adapter *pna =
427262153Sluigi		(struct netmap_pipe_adapter *)na;
428262153Sluigi	struct ifnet *ifp = na->ifp;
429262153Sluigi	ND("%p: onoff %d", na, onoff);
430262153Sluigi	if (onoff) {
431262153Sluigi		ifp->if_capenable |= IFCAP_NETMAP;
432262153Sluigi	} else {
433262153Sluigi		ifp->if_capenable &= ~IFCAP_NETMAP;
434262153Sluigi	}
435262153Sluigi	if (pna->peer_ref) {
436262153Sluigi		ND("%p: case 1.a or 2.a, nothing to do", na);
437262153Sluigi		return 0;
438262153Sluigi	}
439262153Sluigi	if (onoff) {
440262153Sluigi		ND("%p: case 1.b, drop peer", na);
441262153Sluigi		pna->peer->peer_ref = 0;
442262153Sluigi		netmap_adapter_put(na);
443262153Sluigi	} else {
444262153Sluigi		int i;
445262153Sluigi		ND("%p: case 2.b, grab peer", na);
446262153Sluigi		netmap_adapter_get(na);
447262153Sluigi		pna->peer->peer_ref = 1;
448262153Sluigi		/* hide our rings from netmap_mem_rings_delete */
449262153Sluigi		for (i = 0; i < na->num_tx_rings + 1; i++) {
450262153Sluigi			na->tx_rings[i].ring = NULL;
451262153Sluigi		}
452262153Sluigi		for (i = 0; i < na->num_rx_rings + 1; i++) {
453262153Sluigi			na->rx_rings[i].ring = NULL;
454262153Sluigi		}
455262153Sluigi	}
456262153Sluigi	return 0;
457262153Sluigi}
458262153Sluigi
459262153Sluigi/* netmap_pipe_krings_delete.
460262153Sluigi *
461262153Sluigi * There are two cases:
462262153Sluigi *
463262153Sluigi * 1) state is
464262153Sluigi *
465262153Sluigi *                usr1 --> e1 --> e2
466262153Sluigi *
467262153Sluigi *    and we are e1 (e2 is not registered, so krings_delete cannot be
468262153Sluigi *    called on it);
469262153Sluigi *
470262153Sluigi * 2) state is
471262153Sluigi *
472262153Sluigi *                usr1 --> e1     e2 <-- usr2
473262153Sluigi *
474262153Sluigi *    and we are either e1 or e2.
475262153Sluigi *
476262153Sluigi * In the former case we have to also delete the krings of e2;
477262153Sluigi * in the latter case we do nothing (note that our krings
478262153Sluigi * have already been hidden in the unregister callback).
479262153Sluigi */
480262153Sluigistatic void
481262153Sluiginetmap_pipe_krings_delete(struct netmap_adapter *na)
482262153Sluigi{
483262153Sluigi	struct netmap_pipe_adapter *pna =
484262153Sluigi		(struct netmap_pipe_adapter *)na;
485262153Sluigi	struct netmap_adapter *ona; /* na of the other end */
486262153Sluigi	int i;
487262153Sluigi
488262153Sluigi	if (!pna->peer_ref) {
489262153Sluigi		ND("%p: case 2, kept alive by peer",  na);
490262153Sluigi		return;
491262153Sluigi	}
492262153Sluigi	/* case 1) above */
493262153Sluigi	ND("%p: case 1, deleting everyhing", na);
494262153Sluigi	netmap_krings_delete(na); /* also zeroes tx_rings etc. */
495262153Sluigi	/* restore the ring to be deleted on the peer */
496262153Sluigi	ona = &pna->peer->up;
497262153Sluigi	if (ona->tx_rings == NULL) {
498262153Sluigi		/* already deleted, we must be on an
499262153Sluigi                 * cleanup-after-error path */
500262153Sluigi		return;
501262153Sluigi	}
502262153Sluigi	for (i = 0; i < ona->num_tx_rings + 1; i++)
503262153Sluigi		ona->tx_rings[i].ring = ona->tx_rings[i].save_ring;
504262153Sluigi	for (i = 0; i < ona->num_rx_rings + 1; i++)
505262153Sluigi		ona->rx_rings[i].ring = ona->rx_rings[i].save_ring;
506262153Sluigi	netmap_mem_rings_delete(ona);
507262153Sluigi	netmap_krings_delete(ona);
508262153Sluigi}
509262153Sluigi
510262153Sluigi
511262153Sluigistatic void
512262153Sluiginetmap_pipe_dtor(struct netmap_adapter *na)
513262153Sluigi{
514262153Sluigi	struct netmap_pipe_adapter *pna =
515262153Sluigi		(struct netmap_pipe_adapter *)na;
516262153Sluigi	ND("%p", na);
517262153Sluigi	if (pna->peer_ref) {
518262153Sluigi		ND("%p: clean up peer", na);
519262153Sluigi		pna->peer_ref = 0;
520262153Sluigi		netmap_adapter_put(&pna->peer->up);
521262153Sluigi	}
522262153Sluigi	if (pna->role == NR_REG_PIPE_MASTER)
523262153Sluigi		netmap_pipe_remove(pna->parent, pna);
524262153Sluigi	netmap_adapter_put(pna->parent);
525262153Sluigi	free(na->ifp, M_DEVBUF);
526262153Sluigi	na->ifp = NULL;
527262153Sluigi	pna->parent = NULL;
528262153Sluigi}
529262153Sluigi
530262153Sluigiint
531262153Sluiginetmap_get_pipe_na(struct nmreq *nmr, struct netmap_adapter **na, int create)
532262153Sluigi{
533262153Sluigi	struct nmreq pnmr;
534262153Sluigi	struct netmap_adapter *pna; /* parent adapter */
535262153Sluigi	struct netmap_pipe_adapter *mna, *sna, *req;
536262153Sluigi	struct ifnet *ifp, *ifp2;
537262153Sluigi	u_int pipe_id;
538262153Sluigi	int role = nmr->nr_flags & NR_REG_MASK;
539262153Sluigi	int error;
540262153Sluigi
541262153Sluigi	ND("flags %x", nmr->nr_flags);
542262153Sluigi
543262153Sluigi	if (role != NR_REG_PIPE_MASTER && role != NR_REG_PIPE_SLAVE) {
544262153Sluigi		ND("not a pipe");
545262153Sluigi		return 0;
546262153Sluigi	}
547262153Sluigi	role = nmr->nr_flags & NR_REG_MASK;
548262153Sluigi
549262153Sluigi	/* first, try to find the parent adapter */
550262153Sluigi	bzero(&pnmr, sizeof(pnmr));
551262153Sluigi	memcpy(&pnmr.nr_name, nmr->nr_name, IFNAMSIZ);
552262153Sluigi	/* pass to parent the requested number of pipes */
553262153Sluigi	pnmr.nr_arg1 = nmr->nr_arg1;
554262153Sluigi	error = netmap_get_na(&pnmr, &pna, create);
555262153Sluigi	if (error) {
556262153Sluigi		ND("parent lookup failed: %d", error);
557262153Sluigi		return error;
558262153Sluigi	}
559262153Sluigi	ND("found parent: %s", NM_IFPNAME(pna->ifp));
560262153Sluigi
561262153Sluigi	if (NETMAP_OWNED_BY_KERN(pna)) {
562262153Sluigi		ND("parent busy");
563262153Sluigi		error = EBUSY;
564262153Sluigi		goto put_out;
565262153Sluigi	}
566262153Sluigi
567262153Sluigi	/* next, lookup the pipe id in the parent list */
568262153Sluigi	req = NULL;
569262153Sluigi	pipe_id = nmr->nr_ringid & NETMAP_RING_MASK;
570262153Sluigi	mna = netmap_pipe_find(pna, pipe_id);
571262153Sluigi	if (mna) {
572262153Sluigi		if (mna->role == role) {
573262153Sluigi			ND("found %d directly at %d", pipe_id, mna->parent_slot);
574262153Sluigi			req = mna;
575262153Sluigi		} else {
576262153Sluigi			ND("found %d indirectly at %d", pipe_id, mna->parent_slot);
577262153Sluigi			req = mna->peer;
578262153Sluigi		}
579262153Sluigi		/* the pipe we have found already holds a ref to the parent,
580262153Sluigi                 * so we need to drop the one we got from netmap_get_na()
581262153Sluigi                 */
582262153Sluigi		netmap_adapter_put(pna);
583262153Sluigi		goto found;
584262153Sluigi	}
585262153Sluigi	ND("pipe %d not found, create %d", pipe_id, create);
586262153Sluigi	if (!create) {
587262153Sluigi		error = ENODEV;
588262153Sluigi		goto put_out;
589262153Sluigi	}
590262153Sluigi	/* we create both master and slave.
591262153Sluigi         * The endpoint we were asked for holds a reference to
592262153Sluigi         * the other one.
593262153Sluigi         */
594262153Sluigi	ifp = malloc(sizeof(*ifp), M_DEVBUF, M_NOWAIT | M_ZERO);
595262153Sluigi	if (!ifp) {
596262153Sluigi		error = ENOMEM;
597262153Sluigi		goto put_out;
598262153Sluigi	}
599262153Sluigi	strcpy(ifp->if_xname, NM_IFPNAME(pna->ifp));
600262153Sluigi
601262153Sluigi	mna = malloc(sizeof(*mna), M_DEVBUF, M_NOWAIT | M_ZERO);
602262153Sluigi	if (mna == NULL) {
603262153Sluigi		error = ENOMEM;
604262153Sluigi		goto free_ifp;
605262153Sluigi	}
606262153Sluigi	mna->up.ifp = ifp;
607262153Sluigi
608262153Sluigi	mna->id = pipe_id;
609262153Sluigi	mna->role = NR_REG_PIPE_MASTER;
610262153Sluigi	mna->parent = pna;
611262153Sluigi
612262153Sluigi	mna->up.nm_txsync = netmap_pipe_txsync;
613262153Sluigi	mna->up.nm_rxsync = netmap_pipe_rxsync;
614262153Sluigi	mna->up.nm_register = netmap_pipe_reg;
615262153Sluigi	mna->up.nm_dtor = netmap_pipe_dtor;
616262153Sluigi	mna->up.nm_krings_create = netmap_pipe_krings_create;
617262153Sluigi	mna->up.nm_krings_delete = netmap_pipe_krings_delete;
618262153Sluigi	mna->up.nm_mem = pna->nm_mem;
619262153Sluigi	mna->up.na_lut = pna->na_lut;
620262153Sluigi	mna->up.na_lut_objtotal = pna->na_lut_objtotal;
621262153Sluigi
622262153Sluigi	mna->up.num_tx_rings = 1;
623262153Sluigi	mna->up.num_rx_rings = 1;
624262153Sluigi	mna->up.num_tx_desc = nmr->nr_tx_slots;
625262153Sluigi	nm_bound_var(&mna->up.num_tx_desc, pna->num_tx_desc,
626262153Sluigi			1, NM_PIPE_MAXSLOTS, NULL);
627262153Sluigi	mna->up.num_rx_desc = nmr->nr_rx_slots;
628262153Sluigi	nm_bound_var(&mna->up.num_rx_desc, pna->num_rx_desc,
629262153Sluigi			1, NM_PIPE_MAXSLOTS, NULL);
630262153Sluigi	error = netmap_attach_common(&mna->up);
631262153Sluigi	if (error)
632262153Sluigi		goto free_ifp;
633262153Sluigi	/* register the master with the parent */
634262153Sluigi	error = netmap_pipe_add(pna, mna);
635262153Sluigi	if (error)
636262153Sluigi		goto free_mna;
637262153Sluigi
638262153Sluigi	/* create the slave */
639262153Sluigi	ifp2 = malloc(sizeof(*ifp), M_DEVBUF, M_NOWAIT | M_ZERO);
640262153Sluigi	if (!ifp) {
641262153Sluigi		error = ENOMEM;
642262153Sluigi		goto free_mna;
643262153Sluigi	}
644262153Sluigi	strcpy(ifp2->if_xname, NM_IFPNAME(pna->ifp));
645262153Sluigi
646262153Sluigi	sna = malloc(sizeof(*mna), M_DEVBUF, M_NOWAIT | M_ZERO);
647262153Sluigi	if (sna == NULL) {
648262153Sluigi		error = ENOMEM;
649262153Sluigi		goto free_ifp2;
650262153Sluigi	}
651262153Sluigi	/* most fields are the same, copy from master and then fix */
652262153Sluigi	*sna = *mna;
653262153Sluigi	sna->up.ifp = ifp2;
654262153Sluigi	sna->role = NR_REG_PIPE_SLAVE;
655262153Sluigi	error = netmap_attach_common(&sna->up);
656262153Sluigi	if (error)
657262153Sluigi		goto free_sna;
658262153Sluigi
659262153Sluigi	/* join the two endpoints */
660262153Sluigi	mna->peer = sna;
661262153Sluigi	sna->peer = mna;
662262153Sluigi
663262153Sluigi	/* we already have a reference to the parent, but we
664262153Sluigi         * need another one for the other endpoint we created
665262153Sluigi         */
666262153Sluigi	netmap_adapter_get(pna);
667262153Sluigi
668262153Sluigi	if (role == NR_REG_PIPE_MASTER) {
669262153Sluigi		req = mna;
670262153Sluigi		mna->peer_ref = 1;
671262153Sluigi		netmap_adapter_get(&sna->up);
672262153Sluigi	} else {
673262153Sluigi		req = sna;
674262153Sluigi		sna->peer_ref = 1;
675262153Sluigi		netmap_adapter_get(&mna->up);
676262153Sluigi	}
677262153Sluigi	ND("created master %p and slave %p", mna, sna);
678262153Sluigifound:
679262153Sluigi
680262153Sluigi	ND("pipe %d %s at %p", pipe_id,
681262153Sluigi		(req->role == NR_REG_PIPE_MASTER ? "master" : "slave"), req);
682262153Sluigi	*na = &req->up;
683262153Sluigi	netmap_adapter_get(*na);
684262153Sluigi
685262153Sluigi	/* write the configuration back */
686262153Sluigi	nmr->nr_tx_rings = req->up.num_tx_rings;
687262153Sluigi	nmr->nr_rx_rings = req->up.num_rx_rings;
688262153Sluigi	nmr->nr_tx_slots = req->up.num_tx_desc;
689262153Sluigi	nmr->nr_rx_slots = req->up.num_rx_desc;
690262153Sluigi
691262153Sluigi	/* keep the reference to the parent.
692262153Sluigi         * It will be released by the req destructor
693262153Sluigi         */
694262153Sluigi
695262153Sluigi	return 0;
696262153Sluigi
697262153Sluigifree_sna:
698262153Sluigi	free(sna, M_DEVBUF);
699262153Sluigifree_ifp2:
700262153Sluigi	free(ifp2, M_DEVBUF);
701262153Sluigifree_mna:
702262153Sluigi	free(mna, M_DEVBUF);
703262153Sluigifree_ifp:
704262153Sluigi	free(ifp, M_DEVBUF);
705262153Sluigiput_out:
706262153Sluigi	netmap_adapter_put(pna);
707262153Sluigi	return error;
708262153Sluigi}
709262153Sluigi
710262153Sluigi
711262153Sluigi#endif /* WITH_PIPES */
712