netmap_pipe.c revision 285349
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 285349 2015-07-10 05:51:36Z 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
75285349Sluigiint netmap_default_pipes = 0; /* ignored, kept for compatibility */
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 */
80285349Sluigistatic int
81285349Sluiginm_pipe_alloc(struct netmap_adapter *na, u_int npipes)
82261909Sluigi{
83261909Sluigi	size_t len;
84285349Sluigi	struct netmap_pipe_adapter **npa;
85261909Sluigi
86285349Sluigi	if (npipes <= na->na_max_pipes)
87285349Sluigi		/* we already have more entries that requested */
88261909Sluigi		return 0;
89285349Sluigi
90285349Sluigi	if (npipes < na->na_next_pipe || npipes > NM_MAXPIPES)
91285349Sluigi		return EINVAL;
92261909Sluigi
93285349Sluigi        len = sizeof(struct netmap_pipe_adapter *) * npipes;
94285349Sluigi	npa = realloc(na->na_pipes, len, M_DEVBUF, M_NOWAIT | M_ZERO);
95285349Sluigi	if (npa == NULL)
96261909Sluigi		return ENOMEM;
97261909Sluigi
98285349Sluigi	na->na_pipes = npa;
99261909Sluigi	na->na_max_pipes = npipes;
100261909Sluigi
101261909Sluigi	return 0;
102261909Sluigi}
103261909Sluigi
104261909Sluigi/* deallocate the parent array in the parent adapter */
105261909Sluigivoid
106261909Sluiginetmap_pipe_dealloc(struct netmap_adapter *na)
107261909Sluigi{
108261909Sluigi	if (na->na_pipes) {
109285349Sluigi		if (na->na_next_pipe > 0) {
110285349Sluigi			D("freeing not empty pipe array for %s (%d dangling pipes)!", na->name,
111285349Sluigi					na->na_next_pipe);
112285349Sluigi		}
113261909Sluigi		free(na->na_pipes, M_DEVBUF);
114261909Sluigi		na->na_pipes = NULL;
115261909Sluigi		na->na_max_pipes = 0;
116261909Sluigi		na->na_next_pipe = 0;
117261909Sluigi	}
118261909Sluigi}
119261909Sluigi
120261909Sluigi/* find a pipe endpoint with the given id among the parent's pipes */
121261909Sluigistatic struct netmap_pipe_adapter *
122261909Sluiginetmap_pipe_find(struct netmap_adapter *parent, u_int pipe_id)
123261909Sluigi{
124261909Sluigi	int i;
125261909Sluigi	struct netmap_pipe_adapter *na;
126261909Sluigi
127261909Sluigi	for (i = 0; i < parent->na_next_pipe; i++) {
128261909Sluigi		na = parent->na_pipes[i];
129261909Sluigi		if (na->id == pipe_id) {
130261909Sluigi			return na;
131261909Sluigi		}
132261909Sluigi	}
133261909Sluigi	return NULL;
134261909Sluigi}
135261909Sluigi
136261909Sluigi/* add a new pipe endpoint to the parent array */
137261909Sluigistatic int
138261909Sluiginetmap_pipe_add(struct netmap_adapter *parent, struct netmap_pipe_adapter *na)
139261909Sluigi{
140261909Sluigi	if (parent->na_next_pipe >= parent->na_max_pipes) {
141285349Sluigi		u_int npipes = parent->na_max_pipes ?  2*parent->na_max_pipes : 2;
142285349Sluigi		int error = nm_pipe_alloc(parent, npipes);
143285349Sluigi		if (error)
144285349Sluigi			return error;
145261909Sluigi	}
146261909Sluigi
147261909Sluigi	parent->na_pipes[parent->na_next_pipe] = na;
148261909Sluigi	na->parent_slot = parent->na_next_pipe;
149261909Sluigi	parent->na_next_pipe++;
150261909Sluigi	return 0;
151261909Sluigi}
152261909Sluigi
153261909Sluigi/* remove the given pipe endpoint from the parent array */
154261909Sluigistatic void
155261909Sluiginetmap_pipe_remove(struct netmap_adapter *parent, struct netmap_pipe_adapter *na)
156261909Sluigi{
157261909Sluigi	u_int n;
158261909Sluigi	n = --parent->na_next_pipe;
159261909Sluigi	if (n != na->parent_slot) {
160285349Sluigi		struct netmap_pipe_adapter **p =
161285349Sluigi			&parent->na_pipes[na->parent_slot];
162285349Sluigi		*p = parent->na_pipes[n];
163285349Sluigi		(*p)->parent_slot = na->parent_slot;
164261909Sluigi	}
165261909Sluigi	parent->na_pipes[n] = NULL;
166261909Sluigi}
167261909Sluigi
168261909Sluigistatic int
169270063Sluiginetmap_pipe_txsync(struct netmap_kring *txkring, int flags)
170261909Sluigi{
171270063Sluigi        struct netmap_kring *rxkring = txkring->pipe;
172261909Sluigi        u_int limit; /* slots to transfer */
173261909Sluigi        u_int j, k, lim_tx = txkring->nkr_num_slots - 1,
174261909Sluigi                lim_rx = rxkring->nkr_num_slots - 1;
175261909Sluigi        int m, busy;
176261909Sluigi
177261909Sluigi        ND("%p: %s %x -> %s", txkring, txkring->name, flags, rxkring->name);
178261909Sluigi        ND(2, "before: hwcur %d hwtail %d cur %d head %d tail %d", txkring->nr_hwcur, txkring->nr_hwtail,
179261909Sluigi                txkring->rcur, txkring->rhead, txkring->rtail);
180261909Sluigi
181261909Sluigi        j = rxkring->nr_hwtail; /* RX */
182261909Sluigi        k = txkring->nr_hwcur;  /* TX */
183261909Sluigi        m = txkring->rhead - txkring->nr_hwcur; /* new slots */
184261909Sluigi        if (m < 0)
185261909Sluigi                m += txkring->nkr_num_slots;
186261909Sluigi        limit = m;
187274361Sluigi        m = lim_rx; /* max avail space on destination */
188261909Sluigi        busy = j - rxkring->nr_hwcur; /* busy slots */
189261909Sluigi	if (busy < 0)
190274361Sluigi		busy += rxkring->nkr_num_slots;
191261909Sluigi	m -= busy; /* subtract busy slots */
192261909Sluigi        ND(2, "m %d limit %d", m, limit);
193261909Sluigi        if (m < limit)
194261909Sluigi                limit = m;
195261909Sluigi
196261909Sluigi	if (limit == 0) {
197261909Sluigi		/* either the rxring is full, or nothing to send */
198261909Sluigi		return 0;
199261909Sluigi	}
200261909Sluigi
201261909Sluigi        while (limit-- > 0) {
202261909Sluigi                struct netmap_slot *rs = &rxkring->save_ring->slot[j];
203261909Sluigi                struct netmap_slot *ts = &txkring->ring->slot[k];
204261909Sluigi                struct netmap_slot tmp;
205261909Sluigi
206261909Sluigi                /* swap the slots */
207261909Sluigi                tmp = *rs;
208261909Sluigi                *rs = *ts;
209261909Sluigi                *ts = tmp;
210261909Sluigi
211285349Sluigi                /* report the buffer change */
212285349Sluigi		ts->flags |= NS_BUF_CHANGED;
213285349Sluigi		rs->flags |= NS_BUF_CHANGED;
214261909Sluigi
215261909Sluigi                j = nm_next(j, lim_rx);
216261909Sluigi                k = nm_next(k, lim_tx);
217261909Sluigi        }
218261909Sluigi
219274457Sluigi        mb(); /* make sure the slots are updated before publishing them */
220261909Sluigi        rxkring->nr_hwtail = j;
221261909Sluigi        txkring->nr_hwcur = k;
222261909Sluigi        txkring->nr_hwtail = nm_prev(k, lim_tx);
223261909Sluigi
224261909Sluigi        ND(2, "after: hwcur %d hwtail %d cur %d head %d tail %d j %d", txkring->nr_hwcur, txkring->nr_hwtail,
225261909Sluigi                txkring->rcur, txkring->rhead, txkring->rtail, j);
226261909Sluigi
227274457Sluigi        mb(); /* make sure rxkring->nr_hwtail is updated before notifying */
228285349Sluigi        rxkring->nm_notify(rxkring, 0);
229261909Sluigi
230261909Sluigi	return 0;
231261909Sluigi}
232261909Sluigi
233261909Sluigistatic int
234270063Sluiginetmap_pipe_rxsync(struct netmap_kring *rxkring, int flags)
235261909Sluigi{
236270063Sluigi        struct netmap_kring *txkring = rxkring->pipe;
237261909Sluigi	uint32_t oldhwcur = rxkring->nr_hwcur;
238261909Sluigi
239261909Sluigi        ND("%s %x <- %s", rxkring->name, flags, txkring->name);
240261909Sluigi        rxkring->nr_hwcur = rxkring->rhead; /* recover user-relased slots */
241261909Sluigi        ND(5, "hwcur %d hwtail %d cur %d head %d tail %d", rxkring->nr_hwcur, rxkring->nr_hwtail,
242261909Sluigi                rxkring->rcur, rxkring->rhead, rxkring->rtail);
243274457Sluigi        mb(); /* paired with the first mb() in txsync */
244261909Sluigi
245261909Sluigi	if (oldhwcur != rxkring->nr_hwcur) {
246261909Sluigi		/* we have released some slots, notify the other end */
247274457Sluigi		mb(); /* make sure nr_hwcur is updated before notifying */
248285349Sluigi		txkring->nm_notify(txkring, 0);
249261909Sluigi	}
250261909Sluigi        return 0;
251261909Sluigi}
252261909Sluigi
253261909Sluigi/* Pipe endpoints are created and destroyed together, so that endopoints do not
254261909Sluigi * have to check for the existence of their peer at each ?xsync.
255261909Sluigi *
256261909Sluigi * To play well with the existing netmap infrastructure (refcounts etc.), we
257261909Sluigi * adopt the following strategy:
258261909Sluigi *
259261909Sluigi * 1) The first endpoint that is created also creates the other endpoint and
260261909Sluigi * grabs a reference to it.
261261909Sluigi *
262261909Sluigi *    state A)  user1 --> endpoint1 --> endpoint2
263261909Sluigi *
264261909Sluigi * 2) If, starting from state A, endpoint2 is then registered, endpoint1 gives
265261909Sluigi * its reference to the user:
266261909Sluigi *
267261909Sluigi *    state B)  user1 --> endpoint1     endpoint2 <--- user2
268261909Sluigi *
269261909Sluigi * 3) Assume that, starting from state B endpoint2 is closed. In the unregister
270261909Sluigi * callback endpoint2 notes that endpoint1 is still active and adds a reference
271261909Sluigi * from endpoint1 to itself. When user2 then releases her own reference,
272261909Sluigi * endpoint2 is not destroyed and we are back to state A. A symmetrical state
273261909Sluigi * would be reached if endpoint1 were released instead.
274261909Sluigi *
275261909Sluigi * 4) If, starting from state A, endpoint1 is closed, the destructor notes that
276261909Sluigi * it owns a reference to endpoint2 and releases it.
277261909Sluigi *
278261909Sluigi * Something similar goes on for the creation and destruction of the krings.
279261909Sluigi */
280261909Sluigi
281261909Sluigi
282261909Sluigi/* netmap_pipe_krings_delete.
283261909Sluigi *
284261909Sluigi * There are two cases:
285261909Sluigi *
286261909Sluigi * 1) state is
287261909Sluigi *
288261909Sluigi *        usr1 --> e1 --> e2
289261909Sluigi *
290261909Sluigi *    and we are e1. We have to create both sets
291261909Sluigi *    of krings.
292261909Sluigi *
293261909Sluigi * 2) state is
294261909Sluigi *
295261909Sluigi *        usr1 --> e1 --> e2
296261909Sluigi *
297261909Sluigi *    and we are e2. e1 is certainly registered and our
298261909Sluigi *    krings already exist, but they may be hidden.
299261909Sluigi */
300261909Sluigistatic int
301261909Sluiginetmap_pipe_krings_create(struct netmap_adapter *na)
302261909Sluigi{
303261909Sluigi	struct netmap_pipe_adapter *pna =
304261909Sluigi		(struct netmap_pipe_adapter *)na;
305261909Sluigi	struct netmap_adapter *ona = &pna->peer->up;
306261909Sluigi	int error = 0;
307285349Sluigi	enum txrx t;
308285349Sluigi
309261909Sluigi	if (pna->peer_ref) {
310261909Sluigi		int i;
311261909Sluigi
312261909Sluigi		/* case 1) above */
313285349Sluigi		ND("%p: case 1, create everything", na);
314261909Sluigi		error = netmap_krings_create(na, 0);
315261909Sluigi		if (error)
316261909Sluigi			goto err;
317261909Sluigi
318261909Sluigi		/* we also create all the rings, since we need to
319261909Sluigi                 * update the save_ring pointers.
320261909Sluigi                 * netmap_mem_rings_create (called by our caller)
321261909Sluigi                 * will not create the rings again
322261909Sluigi                 */
323261909Sluigi
324261909Sluigi		error = netmap_mem_rings_create(na);
325261909Sluigi		if (error)
326261909Sluigi			goto del_krings1;
327261909Sluigi
328261909Sluigi		/* update our hidden ring pointers */
329285349Sluigi		for_rx_tx(t) {
330285349Sluigi			for (i = 0; i < nma_get_nrings(na, t) + 1; i++)
331285349Sluigi				NMR(na, t)[i].save_ring = NMR(na, t)[i].ring;
332285349Sluigi		}
333261909Sluigi
334261909Sluigi		/* now, create krings and rings of the other end */
335261909Sluigi		error = netmap_krings_create(ona, 0);
336261909Sluigi		if (error)
337261909Sluigi			goto del_rings1;
338261909Sluigi
339261909Sluigi		error = netmap_mem_rings_create(ona);
340261909Sluigi		if (error)
341261909Sluigi			goto del_krings2;
342261909Sluigi
343285349Sluigi		for_rx_tx(t) {
344285349Sluigi			for (i = 0; i < nma_get_nrings(ona, t) + 1; i++)
345285349Sluigi				NMR(ona, t)[i].save_ring = NMR(ona, t)[i].ring;
346285349Sluigi		}
347261909Sluigi
348261909Sluigi		/* cross link the krings */
349285349Sluigi		for_rx_tx(t) {
350285349Sluigi			enum txrx r= nm_txrx_swap(t); /* swap NR_TX <-> NR_RX */
351285349Sluigi			for (i = 0; i < nma_get_nrings(na, t); i++) {
352285349Sluigi				NMR(na, t)[i].pipe = NMR(&pna->peer->up, r) + i;
353285349Sluigi				NMR(&pna->peer->up, r)[i].pipe = NMR(na, t) + i;
354285349Sluigi			}
355261909Sluigi		}
356261909Sluigi	} else {
357261909Sluigi		int i;
358261909Sluigi		/* case 2) above */
359261909Sluigi		/* recover the hidden rings */
360261909Sluigi		ND("%p: case 2, hidden rings", na);
361285349Sluigi		for_rx_tx(t) {
362285349Sluigi			for (i = 0; i < nma_get_nrings(na, t) + 1; i++)
363285349Sluigi				NMR(na, t)[i].ring = NMR(na, t)[i].save_ring;
364285349Sluigi		}
365261909Sluigi	}
366261909Sluigi	return 0;
367261909Sluigi
368261909Sluigidel_krings2:
369261909Sluigi	netmap_krings_delete(ona);
370261909Sluigidel_rings1:
371261909Sluigi	netmap_mem_rings_delete(na);
372261909Sluigidel_krings1:
373261909Sluigi	netmap_krings_delete(na);
374261909Sluigierr:
375261909Sluigi	return error;
376261909Sluigi}
377261909Sluigi
378261909Sluigi/* netmap_pipe_reg.
379261909Sluigi *
380261909Sluigi * There are two cases on registration (onoff==1)
381267128Sluigi *
382261909Sluigi * 1.a) state is
383261909Sluigi *
384261909Sluigi *        usr1 --> e1 --> e2
385261909Sluigi *
386261909Sluigi *      and we are e1. Nothing special to do.
387261909Sluigi *
388261909Sluigi * 1.b) state is
389261909Sluigi *
390261909Sluigi *        usr1 --> e1 --> e2 <-- usr2
391261909Sluigi *
392261909Sluigi *      and we are e2. Drop the ref e1 is holding.
393267128Sluigi *
394261909Sluigi *  There are two additional cases on unregister (onoff==0)
395261909Sluigi *
396261909Sluigi *  2.a) state is
397261909Sluigi *
398261909Sluigi *         usr1 --> e1 --> e2
399261909Sluigi *
400261909Sluigi *       and we are e1. Nothing special to do, e2 will
401261909Sluigi *       be cleaned up by the destructor of e1.
402261909Sluigi *
403261909Sluigi *  2.b) state is
404261909Sluigi *
405261909Sluigi *         usr1 --> e1     e2 <-- usr2
406261909Sluigi *
407261909Sluigi *       and we are either e1 or e2. Add a ref from the
408261909Sluigi *       other end and hide our rings.
409261909Sluigi */
410261909Sluigistatic int
411261909Sluiginetmap_pipe_reg(struct netmap_adapter *na, int onoff)
412261909Sluigi{
413261909Sluigi	struct netmap_pipe_adapter *pna =
414261909Sluigi		(struct netmap_pipe_adapter *)na;
415285349Sluigi	enum txrx t;
416285349Sluigi
417261909Sluigi	ND("%p: onoff %d", na, onoff);
418261909Sluigi	if (onoff) {
419270063Sluigi		na->na_flags |= NAF_NETMAP_ON;
420261909Sluigi	} else {
421270063Sluigi		na->na_flags &= ~NAF_NETMAP_ON;
422261909Sluigi	}
423261909Sluigi	if (pna->peer_ref) {
424261909Sluigi		ND("%p: case 1.a or 2.a, nothing to do", na);
425261909Sluigi		return 0;
426261909Sluigi	}
427261909Sluigi	if (onoff) {
428261909Sluigi		ND("%p: case 1.b, drop peer", na);
429261909Sluigi		pna->peer->peer_ref = 0;
430261909Sluigi		netmap_adapter_put(na);
431261909Sluigi	} else {
432261909Sluigi		int i;
433261909Sluigi		ND("%p: case 2.b, grab peer", na);
434261909Sluigi		netmap_adapter_get(na);
435261909Sluigi		pna->peer->peer_ref = 1;
436261909Sluigi		/* hide our rings from netmap_mem_rings_delete */
437285349Sluigi		for_rx_tx(t) {
438285349Sluigi			for (i = 0; i < nma_get_nrings(na, t) + 1; i++) {
439285349Sluigi				NMR(na, t)[i].ring = NULL;
440285349Sluigi			}
441261909Sluigi		}
442261909Sluigi	}
443261909Sluigi	return 0;
444261909Sluigi}
445261909Sluigi
446261909Sluigi/* netmap_pipe_krings_delete.
447261909Sluigi *
448261909Sluigi * There are two cases:
449261909Sluigi *
450261909Sluigi * 1) state is
451261909Sluigi *
452267128Sluigi *                usr1 --> e1 --> e2
453261909Sluigi *
454267128Sluigi *    and we are e1 (e2 is not registered, so krings_delete cannot be
455261909Sluigi *    called on it);
456261909Sluigi *
457261909Sluigi * 2) state is
458261909Sluigi *
459267128Sluigi *                usr1 --> e1     e2 <-- usr2
460261909Sluigi *
461261909Sluigi *    and we are either e1 or e2.
462261909Sluigi *
463261909Sluigi * In the former case we have to also delete the krings of e2;
464261909Sluigi * in the latter case we do nothing (note that our krings
465261909Sluigi * have already been hidden in the unregister callback).
466261909Sluigi */
467261909Sluigistatic void
468261909Sluiginetmap_pipe_krings_delete(struct netmap_adapter *na)
469261909Sluigi{
470261909Sluigi	struct netmap_pipe_adapter *pna =
471261909Sluigi		(struct netmap_pipe_adapter *)na;
472261909Sluigi	struct netmap_adapter *ona; /* na of the other end */
473261909Sluigi	int i;
474285349Sluigi	enum txrx t;
475261909Sluigi
476261909Sluigi	if (!pna->peer_ref) {
477261909Sluigi		ND("%p: case 2, kept alive by peer",  na);
478261909Sluigi		return;
479261909Sluigi	}
480261909Sluigi	/* case 1) above */
481261909Sluigi	ND("%p: case 1, deleting everyhing", na);
482261909Sluigi	netmap_krings_delete(na); /* also zeroes tx_rings etc. */
483261909Sluigi	/* restore the ring to be deleted on the peer */
484261909Sluigi	ona = &pna->peer->up;
485261909Sluigi	if (ona->tx_rings == NULL) {
486261909Sluigi		/* already deleted, we must be on an
487261909Sluigi                 * cleanup-after-error path */
488261909Sluigi		return;
489261909Sluigi	}
490285349Sluigi	for_rx_tx(t) {
491285349Sluigi		for (i = 0; i < nma_get_nrings(ona, t) + 1; i++)
492285349Sluigi			NMR(ona, t)[i].ring = NMR(ona, t)[i].save_ring;
493285349Sluigi	}
494261909Sluigi	netmap_mem_rings_delete(ona);
495261909Sluigi	netmap_krings_delete(ona);
496261909Sluigi}
497261909Sluigi
498261909Sluigi
499261909Sluigistatic void
500261909Sluiginetmap_pipe_dtor(struct netmap_adapter *na)
501261909Sluigi{
502261909Sluigi	struct netmap_pipe_adapter *pna =
503261909Sluigi		(struct netmap_pipe_adapter *)na;
504261909Sluigi	ND("%p", na);
505261909Sluigi	if (pna->peer_ref) {
506261909Sluigi		ND("%p: clean up peer", na);
507261909Sluigi		pna->peer_ref = 0;
508261909Sluigi		netmap_adapter_put(&pna->peer->up);
509261909Sluigi	}
510267128Sluigi	if (pna->role == NR_REG_PIPE_MASTER)
511261909Sluigi		netmap_pipe_remove(pna->parent, pna);
512261909Sluigi	netmap_adapter_put(pna->parent);
513261909Sluigi	pna->parent = NULL;
514261909Sluigi}
515261909Sluigi
516261909Sluigiint
517261909Sluiginetmap_get_pipe_na(struct nmreq *nmr, struct netmap_adapter **na, int create)
518261909Sluigi{
519261909Sluigi	struct nmreq pnmr;
520261909Sluigi	struct netmap_adapter *pna; /* parent adapter */
521261909Sluigi	struct netmap_pipe_adapter *mna, *sna, *req;
522261909Sluigi	u_int pipe_id;
523261909Sluigi	int role = nmr->nr_flags & NR_REG_MASK;
524261909Sluigi	int error;
525261909Sluigi
526261909Sluigi	ND("flags %x", nmr->nr_flags);
527261909Sluigi
528261909Sluigi	if (role != NR_REG_PIPE_MASTER && role != NR_REG_PIPE_SLAVE) {
529261909Sluigi		ND("not a pipe");
530261909Sluigi		return 0;
531261909Sluigi	}
532261909Sluigi	role = nmr->nr_flags & NR_REG_MASK;
533261909Sluigi
534261909Sluigi	/* first, try to find the parent adapter */
535261909Sluigi	bzero(&pnmr, sizeof(pnmr));
536261909Sluigi	memcpy(&pnmr.nr_name, nmr->nr_name, IFNAMSIZ);
537261909Sluigi	/* pass to parent the requested number of pipes */
538261909Sluigi	pnmr.nr_arg1 = nmr->nr_arg1;
539261909Sluigi	error = netmap_get_na(&pnmr, &pna, create);
540261909Sluigi	if (error) {
541261909Sluigi		ND("parent lookup failed: %d", error);
542261909Sluigi		return error;
543261909Sluigi	}
544270063Sluigi	ND("found parent: %s", na->name);
545261909Sluigi
546261909Sluigi	if (NETMAP_OWNED_BY_KERN(pna)) {
547261909Sluigi		ND("parent busy");
548261909Sluigi		error = EBUSY;
549261909Sluigi		goto put_out;
550261909Sluigi	}
551261909Sluigi
552261909Sluigi	/* next, lookup the pipe id in the parent list */
553261909Sluigi	req = NULL;
554261909Sluigi	pipe_id = nmr->nr_ringid & NETMAP_RING_MASK;
555261909Sluigi	mna = netmap_pipe_find(pna, pipe_id);
556261909Sluigi	if (mna) {
557261909Sluigi		if (mna->role == role) {
558261909Sluigi			ND("found %d directly at %d", pipe_id, mna->parent_slot);
559261909Sluigi			req = mna;
560261909Sluigi		} else {
561261909Sluigi			ND("found %d indirectly at %d", pipe_id, mna->parent_slot);
562261909Sluigi			req = mna->peer;
563261909Sluigi		}
564261909Sluigi		/* the pipe we have found already holds a ref to the parent,
565261909Sluigi                 * so we need to drop the one we got from netmap_get_na()
566261909Sluigi                 */
567261909Sluigi		netmap_adapter_put(pna);
568261909Sluigi		goto found;
569261909Sluigi	}
570261909Sluigi	ND("pipe %d not found, create %d", pipe_id, create);
571261909Sluigi	if (!create) {
572261909Sluigi		error = ENODEV;
573261909Sluigi		goto put_out;
574261909Sluigi	}
575267128Sluigi	/* we create both master and slave.
576261909Sluigi         * The endpoint we were asked for holds a reference to
577261909Sluigi         * the other one.
578261909Sluigi         */
579261909Sluigi	mna = malloc(sizeof(*mna), M_DEVBUF, M_NOWAIT | M_ZERO);
580261909Sluigi	if (mna == NULL) {
581261909Sluigi		error = ENOMEM;
582270063Sluigi		goto put_out;
583261909Sluigi	}
584270063Sluigi	snprintf(mna->up.name, sizeof(mna->up.name), "%s{%d", pna->name, pipe_id);
585261909Sluigi
586261909Sluigi	mna->id = pipe_id;
587261909Sluigi	mna->role = NR_REG_PIPE_MASTER;
588261909Sluigi	mna->parent = pna;
589261909Sluigi
590261909Sluigi	mna->up.nm_txsync = netmap_pipe_txsync;
591261909Sluigi	mna->up.nm_rxsync = netmap_pipe_rxsync;
592261909Sluigi	mna->up.nm_register = netmap_pipe_reg;
593261909Sluigi	mna->up.nm_dtor = netmap_pipe_dtor;
594261909Sluigi	mna->up.nm_krings_create = netmap_pipe_krings_create;
595261909Sluigi	mna->up.nm_krings_delete = netmap_pipe_krings_delete;
596261909Sluigi	mna->up.nm_mem = pna->nm_mem;
597261909Sluigi	mna->up.na_lut = pna->na_lut;
598261909Sluigi
599261909Sluigi	mna->up.num_tx_rings = 1;
600261909Sluigi	mna->up.num_rx_rings = 1;
601261909Sluigi	mna->up.num_tx_desc = nmr->nr_tx_slots;
602261909Sluigi	nm_bound_var(&mna->up.num_tx_desc, pna->num_tx_desc,
603261909Sluigi			1, NM_PIPE_MAXSLOTS, NULL);
604261909Sluigi	mna->up.num_rx_desc = nmr->nr_rx_slots;
605261909Sluigi	nm_bound_var(&mna->up.num_rx_desc, pna->num_rx_desc,
606261909Sluigi			1, NM_PIPE_MAXSLOTS, NULL);
607261909Sluigi	error = netmap_attach_common(&mna->up);
608261909Sluigi	if (error)
609270063Sluigi		goto free_mna;
610261909Sluigi	/* register the master with the parent */
611261909Sluigi	error = netmap_pipe_add(pna, mna);
612261909Sluigi	if (error)
613261909Sluigi		goto free_mna;
614261909Sluigi
615261909Sluigi	/* create the slave */
616261909Sluigi	sna = malloc(sizeof(*mna), M_DEVBUF, M_NOWAIT | M_ZERO);
617261909Sluigi	if (sna == NULL) {
618261909Sluigi		error = ENOMEM;
619270063Sluigi		goto free_mna;
620261909Sluigi	}
621261909Sluigi	/* most fields are the same, copy from master and then fix */
622261909Sluigi	*sna = *mna;
623270063Sluigi	snprintf(sna->up.name, sizeof(sna->up.name), "%s}%d", pna->name, pipe_id);
624261909Sluigi	sna->role = NR_REG_PIPE_SLAVE;
625261909Sluigi	error = netmap_attach_common(&sna->up);
626261909Sluigi	if (error)
627261909Sluigi		goto free_sna;
628261909Sluigi
629261909Sluigi	/* join the two endpoints */
630261909Sluigi	mna->peer = sna;
631261909Sluigi	sna->peer = mna;
632261909Sluigi
633261909Sluigi	/* we already have a reference to the parent, but we
634261909Sluigi         * need another one for the other endpoint we created
635261909Sluigi         */
636261909Sluigi	netmap_adapter_get(pna);
637261909Sluigi
638261909Sluigi	if (role == NR_REG_PIPE_MASTER) {
639261909Sluigi		req = mna;
640261909Sluigi		mna->peer_ref = 1;
641261909Sluigi		netmap_adapter_get(&sna->up);
642261909Sluigi	} else {
643261909Sluigi		req = sna;
644261909Sluigi		sna->peer_ref = 1;
645261909Sluigi		netmap_adapter_get(&mna->up);
646261909Sluigi	}
647261909Sluigi	ND("created master %p and slave %p", mna, sna);
648261909Sluigifound:
649261909Sluigi
650261909Sluigi	ND("pipe %d %s at %p", pipe_id,
651261909Sluigi		(req->role == NR_REG_PIPE_MASTER ? "master" : "slave"), req);
652261909Sluigi	*na = &req->up;
653261909Sluigi	netmap_adapter_get(*na);
654261909Sluigi
655261909Sluigi	/* write the configuration back */
656261909Sluigi	nmr->nr_tx_rings = req->up.num_tx_rings;
657261909Sluigi	nmr->nr_rx_rings = req->up.num_rx_rings;
658261909Sluigi	nmr->nr_tx_slots = req->up.num_tx_desc;
659261909Sluigi	nmr->nr_rx_slots = req->up.num_rx_desc;
660261909Sluigi
661261909Sluigi	/* keep the reference to the parent.
662261909Sluigi         * It will be released by the req destructor
663261909Sluigi         */
664261909Sluigi
665261909Sluigi	return 0;
666261909Sluigi
667261909Sluigifree_sna:
668261909Sluigi	free(sna, M_DEVBUF);
669261909Sluigifree_mna:
670261909Sluigi	free(mna, M_DEVBUF);
671261909Sluigiput_out:
672261909Sluigi	netmap_adapter_put(pna);
673261909Sluigi	return error;
674261909Sluigi}
675261909Sluigi
676261909Sluigi
677261909Sluigi#endif /* WITH_PIPES */
678