netmap_pipe.c revision 261909
1261909Sluigi/*
2261909Sluigi * Copyright (C) 2014 Giuseppe Lettieri. All rights reserved.
3261909Sluigi *
4261909Sluigi * Redistribution and use in source and binary forms, with or without
5261909Sluigi * modification, are permitted provided that the following conditions
6261909Sluigi * are met:
7261909Sluigi *   1. Redistributions of source code must retain the above copyright
8261909Sluigi *      notice, this list of conditions and the following disclaimer.
9261909Sluigi *   2. Redistributions in binary form must reproduce the above copyright
10261909Sluigi *      notice, this list of conditions and the following disclaimer in the
11261909Sluigi *      documentation and/or other materials provided with the distribution.
12261909Sluigi *
13261909Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14261909Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15261909Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16261909Sluigi * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17261909Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18261909Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19261909Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20261909Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21261909Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22261909Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23261909Sluigi * SUCH DAMAGE.
24261909Sluigi */
25261909Sluigi
26261909Sluigi/* $FreeBSD: head/sys/dev/netmap/netmap_pipe.c 261909 2014-02-15 04:53:04Z luigi $ */
27261909Sluigi
28261909Sluigi#if defined(__FreeBSD__)
29261909Sluigi#include <sys/cdefs.h> /* prerequisite */
30261909Sluigi
31261909Sluigi#include <sys/types.h>
32261909Sluigi#include <sys/errno.h>
33261909Sluigi#include <sys/param.h>	/* defines used in kernel.h */
34261909Sluigi#include <sys/kernel.h>	/* types used in module initialization */
35261909Sluigi#include <sys/malloc.h>
36261909Sluigi#include <sys/poll.h>
37261909Sluigi#include <sys/lock.h>
38261909Sluigi#include <sys/rwlock.h>
39261909Sluigi#include <sys/selinfo.h>
40261909Sluigi#include <sys/sysctl.h>
41261909Sluigi#include <sys/socket.h> /* sockaddrs */
42261909Sluigi#include <net/if.h>
43261909Sluigi#include <net/if_var.h>
44261909Sluigi#include <machine/bus.h>	/* bus_dmamap_* */
45261909Sluigi#include <sys/refcount.h>
46261909Sluigi
47261909Sluigi
48261909Sluigi#elif defined(linux)
49261909Sluigi
50261909Sluigi#include "bsd_glue.h"
51261909Sluigi
52261909Sluigi#elif defined(__APPLE__)
53261909Sluigi
54261909Sluigi#warning OSX support is only partial
55261909Sluigi#include "osx_glue.h"
56261909Sluigi
57261909Sluigi#else
58261909Sluigi
59261909Sluigi#error	Unsupported platform
60261909Sluigi
61261909Sluigi#endif /* unsupported */
62261909Sluigi
63261909Sluigi/*
64261909Sluigi * common headers
65261909Sluigi */
66261909Sluigi
67261909Sluigi#include <net/netmap.h>
68261909Sluigi#include <dev/netmap/netmap_kern.h>
69261909Sluigi#include <dev/netmap/netmap_mem2.h>
70261909Sluigi
71261909Sluigi#ifdef WITH_PIPES
72261909Sluigi
73261909Sluigi#define NM_PIPE_MAXSLOTS	4096
74261909Sluigi
75261909Sluigiint netmap_default_pipes = 0; /* default number of pipes for each nic */
76261909SluigiSYSCTL_DECL(_dev_netmap);
77261909SluigiSYSCTL_INT(_dev_netmap, OID_AUTO, default_pipes, CTLFLAG_RW, &netmap_default_pipes, 0 , "");
78261909Sluigi
79261909Sluigi/* allocate the pipe array in the parent adapter */
80261909Sluigiint
81261909Sluiginetmap_pipe_alloc(struct netmap_adapter *na, struct nmreq *nmr)
82261909Sluigi{
83261909Sluigi	size_t len;
84261909Sluigi	int mode = nmr->nr_flags & NR_REG_MASK;
85261909Sluigi	u_int npipes;
86261909Sluigi
87261909Sluigi	if (mode == NR_REG_PIPE_MASTER || mode == NR_REG_PIPE_SLAVE) {
88261909Sluigi		/* this is for our parent, not for us */
89261909Sluigi		return 0;
90261909Sluigi	}
91261909Sluigi
92261909Sluigi	/* TODO: we can resize the array if the new
93261909Sluigi         * request can accomodate the already existing pipes
94261909Sluigi         */
95261909Sluigi	if (na->na_pipes) {
96261909Sluigi		nmr->nr_arg1 = na->na_max_pipes;
97261909Sluigi		return 0;
98261909Sluigi	}
99261909Sluigi
100261909Sluigi	npipes = nmr->nr_arg1;
101261909Sluigi	if (npipes == 0)
102261909Sluigi		npipes = netmap_default_pipes;
103261909Sluigi	nm_bound_var(&npipes, 0, 0, NM_MAXPIPES, NULL);
104261909Sluigi
105261909Sluigi	if (npipes == 0) {
106261909Sluigi		/* really zero, nothing to alloc */
107261909Sluigi		goto out;
108261909Sluigi	}
109261909Sluigi
110261909Sluigi	len = sizeof(struct netmap_pipe_adapter *) * npipes;
111261909Sluigi	na->na_pipes = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
112261909Sluigi	if (na->na_pipes == NULL)
113261909Sluigi		return ENOMEM;
114261909Sluigi
115261909Sluigi	na->na_max_pipes = npipes;
116261909Sluigi	na->na_next_pipe = 0;
117261909Sluigi
118261909Sluigiout:
119261909Sluigi	nmr->nr_arg1 = npipes;
120261909Sluigi
121261909Sluigi	return 0;
122261909Sluigi}
123261909Sluigi
124261909Sluigi/* deallocate the parent array in the parent adapter */
125261909Sluigivoid
126261909Sluiginetmap_pipe_dealloc(struct netmap_adapter *na)
127261909Sluigi{
128261909Sluigi	if (na->na_pipes) {
129261909Sluigi		ND("freeing pipes for %s", NM_IFPNAME(na->ifp));
130261909Sluigi		free(na->na_pipes, M_DEVBUF);
131261909Sluigi		na->na_pipes = NULL;
132261909Sluigi		na->na_max_pipes = 0;
133261909Sluigi		na->na_next_pipe = 0;
134261909Sluigi	}
135261909Sluigi}
136261909Sluigi
137261909Sluigi/* find a pipe endpoint with the given id among the parent's pipes */
138261909Sluigistatic struct netmap_pipe_adapter *
139261909Sluiginetmap_pipe_find(struct netmap_adapter *parent, u_int pipe_id)
140261909Sluigi{
141261909Sluigi	int i;
142261909Sluigi	struct netmap_pipe_adapter *na;
143261909Sluigi
144261909Sluigi	for (i = 0; i < parent->na_next_pipe; i++) {
145261909Sluigi		na = parent->na_pipes[i];
146261909Sluigi		if (na->id == pipe_id) {
147261909Sluigi			return na;
148261909Sluigi		}
149261909Sluigi	}
150261909Sluigi	return NULL;
151261909Sluigi}
152261909Sluigi
153261909Sluigi/* add a new pipe endpoint to the parent array */
154261909Sluigistatic int
155261909Sluiginetmap_pipe_add(struct netmap_adapter *parent, struct netmap_pipe_adapter *na)
156261909Sluigi{
157261909Sluigi	if (parent->na_next_pipe >= parent->na_max_pipes) {
158261909Sluigi		D("%s: no space left for pipes", NM_IFPNAME(parent->ifp));
159261909Sluigi		return ENOMEM;
160261909Sluigi	}
161261909Sluigi
162261909Sluigi	parent->na_pipes[parent->na_next_pipe] = na;
163261909Sluigi	na->parent_slot = parent->na_next_pipe;
164261909Sluigi	parent->na_next_pipe++;
165261909Sluigi	return 0;
166261909Sluigi}
167261909Sluigi
168261909Sluigi/* remove the given pipe endpoint from the parent array */
169261909Sluigistatic void
170261909Sluiginetmap_pipe_remove(struct netmap_adapter *parent, struct netmap_pipe_adapter *na)
171261909Sluigi{
172261909Sluigi	u_int n;
173261909Sluigi	n = --parent->na_next_pipe;
174261909Sluigi	if (n != na->parent_slot) {
175261909Sluigi		parent->na_pipes[na->parent_slot] =
176261909Sluigi			parent->na_pipes[n];
177261909Sluigi	}
178261909Sluigi	parent->na_pipes[n] = NULL;
179261909Sluigi}
180261909Sluigi
181261909Sluigistatic int
182261909Sluiginetmap_pipe_txsync(struct netmap_adapter *na, u_int ring_nr, int flags)
183261909Sluigi{
184261909Sluigi        struct netmap_kring *txkring = na->tx_rings + ring_nr,
185261909Sluigi		*rxkring = txkring->pipe;
186261909Sluigi        u_int limit; /* slots to transfer */
187261909Sluigi        u_int j, k, lim_tx = txkring->nkr_num_slots - 1,
188261909Sluigi                lim_rx = rxkring->nkr_num_slots - 1;
189261909Sluigi        int m, busy;
190261909Sluigi
191261909Sluigi        ND("%p: %s %x -> %s", txkring, txkring->name, flags, rxkring->name);
192261909Sluigi        ND(2, "before: hwcur %d hwtail %d cur %d head %d tail %d", txkring->nr_hwcur, txkring->nr_hwtail,
193261909Sluigi                txkring->rcur, txkring->rhead, txkring->rtail);
194261909Sluigi
195261909Sluigi        j = rxkring->nr_hwtail; /* RX */
196261909Sluigi        k = txkring->nr_hwcur;  /* TX */
197261909Sluigi        m = txkring->rhead - txkring->nr_hwcur; /* new slots */
198261909Sluigi        if (m < 0)
199261909Sluigi                m += txkring->nkr_num_slots;
200261909Sluigi        limit = m;
201261909Sluigi        m = rxkring->nkr_num_slots - 1; /* max avail space on destination */
202261909Sluigi        busy = j - rxkring->nr_hwcur; /* busy slots */
203261909Sluigi	if (busy < 0)
204261909Sluigi		busy += txkring->nkr_num_slots;
205261909Sluigi	m -= busy; /* subtract busy slots */
206261909Sluigi        ND(2, "m %d limit %d", m, limit);
207261909Sluigi        if (m < limit)
208261909Sluigi                limit = m;
209261909Sluigi
210261909Sluigi	if (limit == 0) {
211261909Sluigi		/* either the rxring is full, or nothing to send */
212261909Sluigi		nm_txsync_finalize(txkring); /* actually useless */
213261909Sluigi		return 0;
214261909Sluigi	}
215261909Sluigi
216261909Sluigi        while (limit-- > 0) {
217261909Sluigi                struct netmap_slot *rs = &rxkring->save_ring->slot[j];
218261909Sluigi                struct netmap_slot *ts = &txkring->ring->slot[k];
219261909Sluigi                struct netmap_slot tmp;
220261909Sluigi
221261909Sluigi                /* swap the slots */
222261909Sluigi                tmp = *rs;
223261909Sluigi                *rs = *ts;
224261909Sluigi                *ts = tmp;
225261909Sluigi
226261909Sluigi                /* no need to report the buffer change */
227261909Sluigi
228261909Sluigi                j = nm_next(j, lim_rx);
229261909Sluigi                k = nm_next(k, lim_tx);
230261909Sluigi        }
231261909Sluigi
232261909Sluigi        wmb(); /* make sure the slots are updated before publishing them */
233261909Sluigi        rxkring->nr_hwtail = j;
234261909Sluigi        txkring->nr_hwcur = k;
235261909Sluigi        txkring->nr_hwtail = nm_prev(k, lim_tx);
236261909Sluigi
237261909Sluigi        nm_txsync_finalize(txkring);
238261909Sluigi        ND(2, "after: hwcur %d hwtail %d cur %d head %d tail %d j %d", txkring->nr_hwcur, txkring->nr_hwtail,
239261909Sluigi                txkring->rcur, txkring->rhead, txkring->rtail, j);
240261909Sluigi
241261909Sluigi        wmb(); /* make sure rxkring->nr_hwtail is updated before notifying */
242261909Sluigi        rxkring->na->nm_notify(rxkring->na, rxkring->ring_id, NR_RX, 0);
243261909Sluigi
244261909Sluigi	return 0;
245261909Sluigi}
246261909Sluigi
247261909Sluigistatic int
248261909Sluiginetmap_pipe_rxsync(struct netmap_adapter *na, u_int ring_nr, int flags)
249261909Sluigi{
250261909Sluigi        struct netmap_kring *rxkring = na->rx_rings + ring_nr,
251261909Sluigi		*txkring = rxkring->pipe;
252261909Sluigi	uint32_t oldhwcur = rxkring->nr_hwcur;
253261909Sluigi
254261909Sluigi        ND("%s %x <- %s", rxkring->name, flags, txkring->name);
255261909Sluigi        rxkring->nr_hwcur = rxkring->rhead; /* recover user-relased slots */
256261909Sluigi        ND(5, "hwcur %d hwtail %d cur %d head %d tail %d", rxkring->nr_hwcur, rxkring->nr_hwtail,
257261909Sluigi                rxkring->rcur, rxkring->rhead, rxkring->rtail);
258261909Sluigi        rmb(); /* paired with the first wmb() in txsync */
259261909Sluigi        nm_rxsync_finalize(rxkring);
260261909Sluigi
261261909Sluigi	if (oldhwcur != rxkring->nr_hwcur) {
262261909Sluigi		/* we have released some slots, notify the other end */
263261909Sluigi		wmb(); /* make sure nr_hwcur is updated before notifying */
264261909Sluigi		txkring->na->nm_notify(txkring->na, txkring->ring_id, NR_TX, 0);
265261909Sluigi	}
266261909Sluigi        return 0;
267261909Sluigi}
268261909Sluigi
269261909Sluigi/* Pipe endpoints are created and destroyed together, so that endopoints do not
270261909Sluigi * have to check for the existence of their peer at each ?xsync.
271261909Sluigi *
272261909Sluigi * To play well with the existing netmap infrastructure (refcounts etc.), we
273261909Sluigi * adopt the following strategy:
274261909Sluigi *
275261909Sluigi * 1) The first endpoint that is created also creates the other endpoint and
276261909Sluigi * grabs a reference to it.
277261909Sluigi *
278261909Sluigi *    state A)  user1 --> endpoint1 --> endpoint2
279261909Sluigi *
280261909Sluigi * 2) If, starting from state A, endpoint2 is then registered, endpoint1 gives
281261909Sluigi * its reference to the user:
282261909Sluigi *
283261909Sluigi *    state B)  user1 --> endpoint1     endpoint2 <--- user2
284261909Sluigi *
285261909Sluigi * 3) Assume that, starting from state B endpoint2 is closed. In the unregister
286261909Sluigi * callback endpoint2 notes that endpoint1 is still active and adds a reference
287261909Sluigi * from endpoint1 to itself. When user2 then releases her own reference,
288261909Sluigi * endpoint2 is not destroyed and we are back to state A. A symmetrical state
289261909Sluigi * would be reached if endpoint1 were released instead.
290261909Sluigi *
291261909Sluigi * 4) If, starting from state A, endpoint1 is closed, the destructor notes that
292261909Sluigi * it owns a reference to endpoint2 and releases it.
293261909Sluigi *
294261909Sluigi * Something similar goes on for the creation and destruction of the krings.
295261909Sluigi */
296261909Sluigi
297261909Sluigi
298261909Sluigi/* netmap_pipe_krings_delete.
299261909Sluigi *
300261909Sluigi * There are two cases:
301261909Sluigi *
302261909Sluigi * 1) state is
303261909Sluigi *
304261909Sluigi *        usr1 --> e1 --> e2
305261909Sluigi *
306261909Sluigi *    and we are e1. We have to create both sets
307261909Sluigi *    of krings.
308261909Sluigi *
309261909Sluigi * 2) state is
310261909Sluigi *
311261909Sluigi *        usr1 --> e1 --> e2
312261909Sluigi *
313261909Sluigi *    and we are e2. e1 is certainly registered and our
314261909Sluigi *    krings already exist, but they may be hidden.
315261909Sluigi */
316261909Sluigistatic int
317261909Sluiginetmap_pipe_krings_create(struct netmap_adapter *na)
318261909Sluigi{
319261909Sluigi	struct netmap_pipe_adapter *pna =
320261909Sluigi		(struct netmap_pipe_adapter *)na;
321261909Sluigi	struct netmap_adapter *ona = &pna->peer->up;
322261909Sluigi	int error = 0;
323261909Sluigi	if (pna->peer_ref) {
324261909Sluigi		int i;
325261909Sluigi
326261909Sluigi		/* case 1) above */
327261909Sluigi		D("%p: case 1, create everything", na);
328261909Sluigi		error = netmap_krings_create(na, 0);
329261909Sluigi		if (error)
330261909Sluigi			goto err;
331261909Sluigi
332261909Sluigi		/* we also create all the rings, since we need to
333261909Sluigi                 * update the save_ring pointers.
334261909Sluigi                 * netmap_mem_rings_create (called by our caller)
335261909Sluigi                 * will not create the rings again
336261909Sluigi                 */
337261909Sluigi
338261909Sluigi		error = netmap_mem_rings_create(na);
339261909Sluigi		if (error)
340261909Sluigi			goto del_krings1;
341261909Sluigi
342261909Sluigi		/* update our hidden ring pointers */
343261909Sluigi		for (i = 0; i < na->num_tx_rings + 1; i++)
344261909Sluigi			na->tx_rings[i].save_ring = na->tx_rings[i].ring;
345261909Sluigi		for (i = 0; i < na->num_rx_rings + 1; i++)
346261909Sluigi			na->rx_rings[i].save_ring = na->rx_rings[i].ring;
347261909Sluigi
348261909Sluigi		/* now, create krings and rings of the other end */
349261909Sluigi		error = netmap_krings_create(ona, 0);
350261909Sluigi		if (error)
351261909Sluigi			goto del_rings1;
352261909Sluigi
353261909Sluigi		error = netmap_mem_rings_create(ona);
354261909Sluigi		if (error)
355261909Sluigi			goto del_krings2;
356261909Sluigi
357261909Sluigi		for (i = 0; i < ona->num_tx_rings + 1; i++)
358261909Sluigi			ona->tx_rings[i].save_ring = ona->tx_rings[i].ring;
359261909Sluigi		for (i = 0; i < ona->num_rx_rings + 1; i++)
360261909Sluigi			ona->rx_rings[i].save_ring = ona->rx_rings[i].ring;
361261909Sluigi
362261909Sluigi		/* cross link the krings */
363261909Sluigi		for (i = 0; i < na->num_tx_rings; i++) {
364261909Sluigi			na->tx_rings[i].pipe = pna->peer->up.rx_rings + i;
365261909Sluigi			na->rx_rings[i].pipe = pna->peer->up.tx_rings + i;
366261909Sluigi			pna->peer->up.tx_rings[i].pipe = na->rx_rings + i;
367261909Sluigi			pna->peer->up.rx_rings[i].pipe = na->tx_rings + i;
368261909Sluigi		}
369261909Sluigi	} else {
370261909Sluigi		int i;
371261909Sluigi		/* case 2) above */
372261909Sluigi		/* recover the hidden rings */
373261909Sluigi		ND("%p: case 2, hidden rings", na);
374261909Sluigi		for (i = 0; i < na->num_tx_rings + 1; i++)
375261909Sluigi			na->tx_rings[i].ring = na->tx_rings[i].save_ring;
376261909Sluigi		for (i = 0; i < na->num_rx_rings + 1; i++)
377261909Sluigi			na->rx_rings[i].ring = na->rx_rings[i].save_ring;
378261909Sluigi	}
379261909Sluigi	return 0;
380261909Sluigi
381261909Sluigidel_krings2:
382261909Sluigi	netmap_krings_delete(ona);
383261909Sluigidel_rings1:
384261909Sluigi	netmap_mem_rings_delete(na);
385261909Sluigidel_krings1:
386261909Sluigi	netmap_krings_delete(na);
387261909Sluigierr:
388261909Sluigi	return error;
389261909Sluigi}
390261909Sluigi
391261909Sluigi/* netmap_pipe_reg.
392261909Sluigi *
393261909Sluigi * There are two cases on registration (onoff==1)
394261909Sluigi *
395261909Sluigi * 1.a) state is
396261909Sluigi *
397261909Sluigi *        usr1 --> e1 --> e2
398261909Sluigi *
399261909Sluigi *      and we are e1. Nothing special to do.
400261909Sluigi *
401261909Sluigi * 1.b) state is
402261909Sluigi *
403261909Sluigi *        usr1 --> e1 --> e2 <-- usr2
404261909Sluigi *
405261909Sluigi *      and we are e2. Drop the ref e1 is holding.
406261909Sluigi *
407261909Sluigi *  There are two additional cases on unregister (onoff==0)
408261909Sluigi *
409261909Sluigi *  2.a) state is
410261909Sluigi *
411261909Sluigi *         usr1 --> e1 --> e2
412261909Sluigi *
413261909Sluigi *       and we are e1. Nothing special to do, e2 will
414261909Sluigi *       be cleaned up by the destructor of e1.
415261909Sluigi *
416261909Sluigi *  2.b) state is
417261909Sluigi *
418261909Sluigi *         usr1 --> e1     e2 <-- usr2
419261909Sluigi *
420261909Sluigi *       and we are either e1 or e2. Add a ref from the
421261909Sluigi *       other end and hide our rings.
422261909Sluigi */
423261909Sluigistatic int
424261909Sluiginetmap_pipe_reg(struct netmap_adapter *na, int onoff)
425261909Sluigi{
426261909Sluigi	struct netmap_pipe_adapter *pna =
427261909Sluigi		(struct netmap_pipe_adapter *)na;
428261909Sluigi	struct ifnet *ifp = na->ifp;
429261909Sluigi	ND("%p: onoff %d", na, onoff);
430261909Sluigi	if (onoff) {
431261909Sluigi		ifp->if_capenable |= IFCAP_NETMAP;
432261909Sluigi	} else {
433261909Sluigi		ifp->if_capenable &= ~IFCAP_NETMAP;
434261909Sluigi	}
435261909Sluigi	if (pna->peer_ref) {
436261909Sluigi		ND("%p: case 1.a or 2.a, nothing to do", na);
437261909Sluigi		return 0;
438261909Sluigi	}
439261909Sluigi	if (onoff) {
440261909Sluigi		ND("%p: case 1.b, drop peer", na);
441261909Sluigi		pna->peer->peer_ref = 0;
442261909Sluigi		netmap_adapter_put(na);
443261909Sluigi	} else {
444261909Sluigi		int i;
445261909Sluigi		ND("%p: case 2.b, grab peer", na);
446261909Sluigi		netmap_adapter_get(na);
447261909Sluigi		pna->peer->peer_ref = 1;
448261909Sluigi		/* hide our rings from netmap_mem_rings_delete */
449261909Sluigi		for (i = 0; i < na->num_tx_rings + 1; i++) {
450261909Sluigi			na->tx_rings[i].ring = NULL;
451261909Sluigi		}
452261909Sluigi		for (i = 0; i < na->num_rx_rings + 1; i++) {
453261909Sluigi			na->rx_rings[i].ring = NULL;
454261909Sluigi		}
455261909Sluigi	}
456261909Sluigi	return 0;
457261909Sluigi}
458261909Sluigi
459261909Sluigi/* netmap_pipe_krings_delete.
460261909Sluigi *
461261909Sluigi * There are two cases:
462261909Sluigi *
463261909Sluigi * 1) state is
464261909Sluigi *
465261909Sluigi *                usr1 --> e1 --> e2
466261909Sluigi *
467261909Sluigi *    and we are e1 (e2 is not registered, so krings_delete cannot be
468261909Sluigi *    called on it);
469261909Sluigi *
470261909Sluigi * 2) state is
471261909Sluigi *
472261909Sluigi *                usr1 --> e1     e2 <-- usr2
473261909Sluigi *
474261909Sluigi *    and we are either e1 or e2.
475261909Sluigi *
476261909Sluigi * In the former case we have to also delete the krings of e2;
477261909Sluigi * in the latter case we do nothing (note that our krings
478261909Sluigi * have already been hidden in the unregister callback).
479261909Sluigi */
480261909Sluigistatic void
481261909Sluiginetmap_pipe_krings_delete(struct netmap_adapter *na)
482261909Sluigi{
483261909Sluigi	struct netmap_pipe_adapter *pna =
484261909Sluigi		(struct netmap_pipe_adapter *)na;
485261909Sluigi	struct netmap_adapter *ona; /* na of the other end */
486261909Sluigi	int i;
487261909Sluigi
488261909Sluigi	if (!pna->peer_ref) {
489261909Sluigi		ND("%p: case 2, kept alive by peer",  na);
490261909Sluigi		return;
491261909Sluigi	}
492261909Sluigi	/* case 1) above */
493261909Sluigi	ND("%p: case 1, deleting everyhing", na);
494261909Sluigi	netmap_krings_delete(na); /* also zeroes tx_rings etc. */
495261909Sluigi	/* restore the ring to be deleted on the peer */
496261909Sluigi	ona = &pna->peer->up;
497261909Sluigi	if (ona->tx_rings == NULL) {
498261909Sluigi		/* already deleted, we must be on an
499261909Sluigi                 * cleanup-after-error path */
500261909Sluigi		return;
501261909Sluigi	}
502261909Sluigi	for (i = 0; i < ona->num_tx_rings + 1; i++)
503261909Sluigi		ona->tx_rings[i].ring = ona->tx_rings[i].save_ring;
504261909Sluigi	for (i = 0; i < ona->num_rx_rings + 1; i++)
505261909Sluigi		ona->rx_rings[i].ring = ona->rx_rings[i].save_ring;
506261909Sluigi	netmap_mem_rings_delete(ona);
507261909Sluigi	netmap_krings_delete(ona);
508261909Sluigi}
509261909Sluigi
510261909Sluigi
511261909Sluigistatic void
512261909Sluiginetmap_pipe_dtor(struct netmap_adapter *na)
513261909Sluigi{
514261909Sluigi	struct netmap_pipe_adapter *pna =
515261909Sluigi		(struct netmap_pipe_adapter *)na;
516261909Sluigi	ND("%p", na);
517261909Sluigi	if (pna->peer_ref) {
518261909Sluigi		ND("%p: clean up peer", na);
519261909Sluigi		pna->peer_ref = 0;
520261909Sluigi		netmap_adapter_put(&pna->peer->up);
521261909Sluigi	}
522261909Sluigi	if (pna->role == NR_REG_PIPE_MASTER)
523261909Sluigi		netmap_pipe_remove(pna->parent, pna);
524261909Sluigi	netmap_adapter_put(pna->parent);
525261909Sluigi	free(na->ifp, M_DEVBUF);
526261909Sluigi	na->ifp = NULL;
527261909Sluigi	pna->parent = NULL;
528261909Sluigi}
529261909Sluigi
530261909Sluigiint
531261909Sluiginetmap_get_pipe_na(struct nmreq *nmr, struct netmap_adapter **na, int create)
532261909Sluigi{
533261909Sluigi	struct nmreq pnmr;
534261909Sluigi	struct netmap_adapter *pna; /* parent adapter */
535261909Sluigi	struct netmap_pipe_adapter *mna, *sna, *req;
536261909Sluigi	struct ifnet *ifp, *ifp2;
537261909Sluigi	u_int pipe_id;
538261909Sluigi	int role = nmr->nr_flags & NR_REG_MASK;
539261909Sluigi	int error;
540261909Sluigi
541261909Sluigi	ND("flags %x", nmr->nr_flags);
542261909Sluigi
543261909Sluigi	if (role != NR_REG_PIPE_MASTER && role != NR_REG_PIPE_SLAVE) {
544261909Sluigi		ND("not a pipe");
545261909Sluigi		return 0;
546261909Sluigi	}
547261909Sluigi	role = nmr->nr_flags & NR_REG_MASK;
548261909Sluigi
549261909Sluigi	/* first, try to find the parent adapter */
550261909Sluigi	bzero(&pnmr, sizeof(pnmr));
551261909Sluigi	memcpy(&pnmr.nr_name, nmr->nr_name, IFNAMSIZ);
552261909Sluigi	/* pass to parent the requested number of pipes */
553261909Sluigi	pnmr.nr_arg1 = nmr->nr_arg1;
554261909Sluigi	error = netmap_get_na(&pnmr, &pna, create);
555261909Sluigi	if (error) {
556261909Sluigi		ND("parent lookup failed: %d", error);
557261909Sluigi		return error;
558261909Sluigi	}
559261909Sluigi	ND("found parent: %s", NM_IFPNAME(pna->ifp));
560261909Sluigi
561261909Sluigi	if (NETMAP_OWNED_BY_KERN(pna)) {
562261909Sluigi		ND("parent busy");
563261909Sluigi		error = EBUSY;
564261909Sluigi		goto put_out;
565261909Sluigi	}
566261909Sluigi
567261909Sluigi	/* next, lookup the pipe id in the parent list */
568261909Sluigi	req = NULL;
569261909Sluigi	pipe_id = nmr->nr_ringid & NETMAP_RING_MASK;
570261909Sluigi	mna = netmap_pipe_find(pna, pipe_id);
571261909Sluigi	if (mna) {
572261909Sluigi		if (mna->role == role) {
573261909Sluigi			ND("found %d directly at %d", pipe_id, mna->parent_slot);
574261909Sluigi			req = mna;
575261909Sluigi		} else {
576261909Sluigi			ND("found %d indirectly at %d", pipe_id, mna->parent_slot);
577261909Sluigi			req = mna->peer;
578261909Sluigi		}
579261909Sluigi		/* the pipe we have found already holds a ref to the parent,
580261909Sluigi                 * so we need to drop the one we got from netmap_get_na()
581261909Sluigi                 */
582261909Sluigi		netmap_adapter_put(pna);
583261909Sluigi		goto found;
584261909Sluigi	}
585261909Sluigi	ND("pipe %d not found, create %d", pipe_id, create);
586261909Sluigi	if (!create) {
587261909Sluigi		error = ENODEV;
588261909Sluigi		goto put_out;
589261909Sluigi	}
590261909Sluigi	/* we create both master and slave.
591261909Sluigi         * The endpoint we were asked for holds a reference to
592261909Sluigi         * the other one.
593261909Sluigi         */
594261909Sluigi	ifp = malloc(sizeof(*ifp), M_DEVBUF, M_NOWAIT | M_ZERO);
595261909Sluigi	if (!ifp) {
596261909Sluigi		error = ENOMEM;
597261909Sluigi		goto put_out;
598261909Sluigi	}
599261909Sluigi	strcpy(ifp->if_xname, NM_IFPNAME(pna->ifp));
600261909Sluigi
601261909Sluigi	mna = malloc(sizeof(*mna), M_DEVBUF, M_NOWAIT | M_ZERO);
602261909Sluigi	if (mna == NULL) {
603261909Sluigi		error = ENOMEM;
604261909Sluigi		goto free_ifp;
605261909Sluigi	}
606261909Sluigi	mna->up.ifp = ifp;
607261909Sluigi
608261909Sluigi	mna->id = pipe_id;
609261909Sluigi	mna->role = NR_REG_PIPE_MASTER;
610261909Sluigi	mna->parent = pna;
611261909Sluigi
612261909Sluigi	mna->up.nm_txsync = netmap_pipe_txsync;
613261909Sluigi	mna->up.nm_rxsync = netmap_pipe_rxsync;
614261909Sluigi	mna->up.nm_register = netmap_pipe_reg;
615261909Sluigi	mna->up.nm_dtor = netmap_pipe_dtor;
616261909Sluigi	mna->up.nm_krings_create = netmap_pipe_krings_create;
617261909Sluigi	mna->up.nm_krings_delete = netmap_pipe_krings_delete;
618261909Sluigi	mna->up.nm_mem = pna->nm_mem;
619261909Sluigi	mna->up.na_lut = pna->na_lut;
620261909Sluigi	mna->up.na_lut_objtotal = pna->na_lut_objtotal;
621261909Sluigi
622261909Sluigi	mna->up.num_tx_rings = 1;
623261909Sluigi	mna->up.num_rx_rings = 1;
624261909Sluigi	mna->up.num_tx_desc = nmr->nr_tx_slots;
625261909Sluigi	nm_bound_var(&mna->up.num_tx_desc, pna->num_tx_desc,
626261909Sluigi			1, NM_PIPE_MAXSLOTS, NULL);
627261909Sluigi	mna->up.num_rx_desc = nmr->nr_rx_slots;
628261909Sluigi	nm_bound_var(&mna->up.num_rx_desc, pna->num_rx_desc,
629261909Sluigi			1, NM_PIPE_MAXSLOTS, NULL);
630261909Sluigi	error = netmap_attach_common(&mna->up);
631261909Sluigi	if (error)
632261909Sluigi		goto free_ifp;
633261909Sluigi	/* register the master with the parent */
634261909Sluigi	error = netmap_pipe_add(pna, mna);
635261909Sluigi	if (error)
636261909Sluigi		goto free_mna;
637261909Sluigi
638261909Sluigi	/* create the slave */
639261909Sluigi	ifp2 = malloc(sizeof(*ifp), M_DEVBUF, M_NOWAIT | M_ZERO);
640261909Sluigi	if (!ifp) {
641261909Sluigi		error = ENOMEM;
642261909Sluigi		goto free_mna;
643261909Sluigi	}
644261909Sluigi	strcpy(ifp2->if_xname, NM_IFPNAME(pna->ifp));
645261909Sluigi
646261909Sluigi	sna = malloc(sizeof(*mna), M_DEVBUF, M_NOWAIT | M_ZERO);
647261909Sluigi	if (sna == NULL) {
648261909Sluigi		error = ENOMEM;
649261909Sluigi		goto free_ifp2;
650261909Sluigi	}
651261909Sluigi	/* most fields are the same, copy from master and then fix */
652261909Sluigi	*sna = *mna;
653261909Sluigi	sna->up.ifp = ifp2;
654261909Sluigi	sna->role = NR_REG_PIPE_SLAVE;
655261909Sluigi	error = netmap_attach_common(&sna->up);
656261909Sluigi	if (error)
657261909Sluigi		goto free_sna;
658261909Sluigi
659261909Sluigi	/* join the two endpoints */
660261909Sluigi	mna->peer = sna;
661261909Sluigi	sna->peer = mna;
662261909Sluigi
663261909Sluigi	/* we already have a reference to the parent, but we
664261909Sluigi         * need another one for the other endpoint we created
665261909Sluigi         */
666261909Sluigi	netmap_adapter_get(pna);
667261909Sluigi
668261909Sluigi	if (role == NR_REG_PIPE_MASTER) {
669261909Sluigi		req = mna;
670261909Sluigi		mna->peer_ref = 1;
671261909Sluigi		netmap_adapter_get(&sna->up);
672261909Sluigi	} else {
673261909Sluigi		req = sna;
674261909Sluigi		sna->peer_ref = 1;
675261909Sluigi		netmap_adapter_get(&mna->up);
676261909Sluigi	}
677261909Sluigi	ND("created master %p and slave %p", mna, sna);
678261909Sluigifound:
679261909Sluigi
680261909Sluigi	ND("pipe %d %s at %p", pipe_id,
681261909Sluigi		(req->role == NR_REG_PIPE_MASTER ? "master" : "slave"), req);
682261909Sluigi	*na = &req->up;
683261909Sluigi	netmap_adapter_get(*na);
684261909Sluigi
685261909Sluigi	/* write the configuration back */
686261909Sluigi	nmr->nr_tx_rings = req->up.num_tx_rings;
687261909Sluigi	nmr->nr_rx_rings = req->up.num_rx_rings;
688261909Sluigi	nmr->nr_tx_slots = req->up.num_tx_desc;
689261909Sluigi	nmr->nr_rx_slots = req->up.num_rx_desc;
690261909Sluigi
691261909Sluigi	/* keep the reference to the parent.
692261909Sluigi         * It will be released by the req destructor
693261909Sluigi         */
694261909Sluigi
695261909Sluigi	return 0;
696261909Sluigi
697261909Sluigifree_sna:
698261909Sluigi	free(sna, M_DEVBUF);
699261909Sluigifree_ifp2:
700261909Sluigi	free(ifp2, M_DEVBUF);
701261909Sluigifree_mna:
702261909Sluigi	free(mna, M_DEVBUF);
703261909Sluigifree_ifp:
704261909Sluigi	free(ifp, M_DEVBUF);
705261909Sluigiput_out:
706261909Sluigi	netmap_adapter_put(pna);
707261909Sluigi	return error;
708261909Sluigi}
709261909Sluigi
710261909Sluigi
711261909Sluigi#endif /* WITH_PIPES */
712