aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/netmap/netmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/netmap/netmap.c')
-rw-r--r--sys/dev/netmap/netmap.c1023
1 files changed, 543 insertions, 480 deletions
diff --git a/sys/dev/netmap/netmap.c b/sys/dev/netmap/netmap.c
index 11229ccf6b87..0a728bbf94e7 100644
--- a/sys/dev/netmap/netmap.c
+++ b/sys/dev/netmap/netmap.c
@@ -293,7 +293,7 @@ ports attached to the switch)
* kring->nm_sync() == DEVICE_netmap_rxsync()
* 2) device interrupt handler
* na->nm_notify() == netmap_notify()
- * - tx from host stack
+ * - rx from host stack
* concurrently:
* 1) host stack
* netmap_transmit()
@@ -313,31 +313,113 @@ ports attached to the switch)
*
* -= SYSTEM DEVICE WITH GENERIC SUPPORT =-
*
+ * na == NA(ifp) == generic_netmap_adapter created in generic_netmap_attach()
*
- *
- * -= VALE PORT =-
- *
- *
- *
- * -= NETMAP PIPE =-
- *
- *
- *
- * -= SYSTEM DEVICE WITH NATIVE SUPPORT, CONNECTED TO VALE, NO HOST RINGS =-
+ * - tx from netmap userspace:
+ * concurrently:
+ * 1) ioctl(NIOCTXSYNC)/netmap_poll() in process context
+ * kring->nm_sync() == generic_netmap_txsync()
+ * linux: dev_queue_xmit() with NM_MAGIC_PRIORITY_TX
+ * generic_ndo_start_xmit()
+ * orig. dev. start_xmit
+ * FreeBSD: na->if_transmit() == orig. dev if_transmit
+ * 2) generic_mbuf_destructor()
+ * na->nm_notify() == netmap_notify()
+ * - rx from netmap userspace:
+ * 1) ioctl(NIOCRXSYNC)/netmap_poll() in process context
+ * kring->nm_sync() == generic_netmap_rxsync()
+ * mbq_safe_dequeue()
+ * 2) device driver
+ * generic_rx_handler()
+ * mbq_safe_enqueue()
+ * na->nm_notify() == netmap_notify()
+ * - rx from host stack:
+ * concurrently:
+ * 1) host stack
+ * linux: generic_ndo_start_xmit()
+ * netmap_transmit()
+ * FreeBSD: ifp->if_input() == netmap_transmit
+ * both:
+ * na->nm_notify() == netmap_notify()
+ * 2) ioctl(NIOCRXSYNC)/netmap_poll() in process context
+ * kring->nm_sync() == netmap_rxsync_from_host_compat
+ * netmap_rxsync_from_host(na, NULL, NULL)
+ * - tx to host stack:
+ * ioctl(NIOCTXSYNC)/netmap_poll() in process context
+ * kring->nm_sync() == netmap_txsync_to_host_compat
+ * netmap_txsync_to_host(na)
+ * NM_SEND_UP()
+ * FreeBSD: na->if_input() == ??? XXX
+ * linux: netif_rx() with NM_MAGIC_PRIORITY_RX
*
*
+ * -= VALE =-
*
- * -= SYSTEM DEVICE WITH NATIVE SUPPORT, CONNECTED TO VALE, WITH HOST RINGS =-
+ * INCOMING:
*
+ * - VALE ports:
+ * ioctl(NIOCTXSYNC)/netmap_poll() in process context
+ * kring->nm_sync() == netmap_vp_txsync()
*
+ * - system device with native support:
+ * from cable:
+ * interrupt
+ * na->nm_notify() == netmap_bwrap_intr_notify(ring_nr != host ring)
+ * kring->nm_sync() == DEVICE_netmap_rxsync()
+ * netmap_vp_txsync()
+ * kring->nm_sync() == DEVICE_netmap_rxsync()
+ * from host stack:
+ * netmap_transmit()
+ * na->nm_notify() == netmap_bwrap_intr_notify(ring_nr == host ring)
+ * kring->nm_sync() == netmap_rxsync_from_host_compat()
+ * netmap_vp_txsync()
*
- * -= SYSTEM DEVICE WITH GENERIC SUPPORT, CONNECTED TO VALE, NO HOST RINGS =-
+ * - system device with generic support:
+ * from device driver:
+ * generic_rx_handler()
+ * na->nm_notify() == netmap_bwrap_intr_notify(ring_nr != host ring)
+ * kring->nm_sync() == generic_netmap_rxsync()
+ * netmap_vp_txsync()
+ * kring->nm_sync() == generic_netmap_rxsync()
+ * from host stack:
+ * netmap_transmit()
+ * na->nm_notify() == netmap_bwrap_intr_notify(ring_nr == host ring)
+ * kring->nm_sync() == netmap_rxsync_from_host_compat()
+ * netmap_vp_txsync()
*
+ * (all cases) --> nm_bdg_flush()
+ * dest_na->nm_notify() == (see below)
*
+ * OUTGOING:
*
- * -= SYSTEM DEVICE WITH GENERIC SUPPORT, CONNECTED TO VALE, WITH HOST RINGS =-
+ * - VALE ports:
+ * concurrently:
+ * 1) ioctlNIOCRXSYNC)/netmap_poll() in process context
+ * kring->nm_sync() == netmap_vp_rxsync()
+ * 2) from nm_bdg_flush()
+ * na->nm_notify() == netmap_notify()
*
+ * - system device with native support:
+ * to cable:
+ * na->nm_notify() == netmap_bwrap_notify()
+ * netmap_vp_rxsync()
+ * kring->nm_sync() == DEVICE_netmap_txsync()
+ * netmap_vp_rxsync()
+ * to host stack:
+ * netmap_vp_rxsync()
+ * kring->nm_sync() == netmap_txsync_to_host_compat
+ * netmap_vp_rxsync_locked()
*
+ * - system device with generic adapter:
+ * to device driver:
+ * na->nm_notify() == netmap_bwrap_notify()
+ * netmap_vp_rxsync()
+ * kring->nm_sync() == generic_netmap_txsync()
+ * netmap_vp_rxsync()
+ * to host stack:
+ * netmap_vp_rxsync()
+ * kring->nm_sync() == netmap_txsync_to_host_compat
+ * netmap_vp_rxsync()
*
*/
@@ -412,15 +494,6 @@ ports attached to the switch)
MALLOC_DEFINE(M_NETMAP, "netmap", "Network memory map");
-/*
- * The following variables are used by the drivers and replicate
- * fields in the global memory pool. They only refer to buffers
- * used by physical interfaces.
- */
-u_int netmap_total_buffers;
-u_int netmap_buf_size;
-char *netmap_buffer_base; /* also address of an invalid buffer */
-
/* user-controlled variables */
int netmap_verbose;
@@ -446,7 +519,6 @@ SYSCTL_INT(_dev_netmap, OID_AUTO, adaptive_io, CTLFLAG_RW,
int netmap_flags = 0; /* debug flags */
int netmap_fwd = 0; /* force transparent mode */
-int netmap_mmap_unreg = 0; /* allow mmap of unregistered fds */
/*
* netmap_admode selects the netmap mode to use.
@@ -464,7 +536,6 @@ int netmap_generic_rings = 1; /* number of queues in generic. */
SYSCTL_INT(_dev_netmap, OID_AUTO, flags, CTLFLAG_RW, &netmap_flags, 0 , "");
SYSCTL_INT(_dev_netmap, OID_AUTO, fwd, CTLFLAG_RW, &netmap_fwd, 0 , "");
-SYSCTL_INT(_dev_netmap, OID_AUTO, mmap_unreg, CTLFLAG_RW, &netmap_mmap_unreg, 0, "");
SYSCTL_INT(_dev_netmap, OID_AUTO, admode, CTLFLAG_RW, &netmap_admode, 0 , "");
SYSCTL_INT(_dev_netmap, OID_AUTO, generic_mit, CTLFLAG_RW, &netmap_generic_mit, 0 , "");
SYSCTL_INT(_dev_netmap, OID_AUTO, generic_ringsize, CTLFLAG_RW, &netmap_generic_ringsize, 0 , "");
@@ -472,15 +543,6 @@ SYSCTL_INT(_dev_netmap, OID_AUTO, generic_rings, CTLFLAG_RW, &netmap_generic_rin
NMG_LOCK_T netmap_global_lock;
-
-static void
-nm_kr_get(struct netmap_kring *kr)
-{
- while (NM_ATOMIC_TEST_AND_SET(&kr->nr_busy))
- tsleep(kr, 0, "NM_KR_GET", 4);
-}
-
-
/*
* mark the ring as stopped, and run through the locks
* to make sure other users get to see it.
@@ -495,34 +557,14 @@ netmap_disable_ring(struct netmap_kring *kr)
nm_kr_put(kr);
}
-/* stop or enable a single tx ring */
-void
-netmap_set_txring(struct netmap_adapter *na, u_int ring_id, int stopped)
-{
- if (stopped)
- netmap_disable_ring(na->tx_rings + ring_id);
- else
- na->tx_rings[ring_id].nkr_stopped = 0;
- /* nofify that the stopped state has changed. This is currently
- *only used by bwrap to propagate the state to its own krings.
- * (see netmap_bwrap_intr_notify).
- */
- na->nm_notify(na, ring_id, NR_TX, NAF_DISABLE_NOTIFY);
-}
-
-/* stop or enable a single rx ring */
+/* stop or enable a single ring */
void
-netmap_set_rxring(struct netmap_adapter *na, u_int ring_id, int stopped)
+netmap_set_ring(struct netmap_adapter *na, u_int ring_id, enum txrx t, int stopped)
{
if (stopped)
- netmap_disable_ring(na->rx_rings + ring_id);
+ netmap_disable_ring(NMR(na, t) + ring_id);
else
- na->rx_rings[ring_id].nkr_stopped = 0;
- /* nofify that the stopped state has changed. This is currently
- *only used by bwrap to propagate the state to its own krings.
- * (see netmap_bwrap_intr_notify).
- */
- na->nm_notify(na, ring_id, NR_RX, NAF_DISABLE_NOTIFY);
+ NMR(na, t)[ring_id].nkr_stopped = 0;
}
@@ -531,20 +573,15 @@ void
netmap_set_all_rings(struct netmap_adapter *na, int stopped)
{
int i;
- u_int ntx, nrx;
+ enum txrx t;
if (!nm_netmap_on(na))
return;
- ntx = netmap_real_tx_rings(na);
- nrx = netmap_real_rx_rings(na);
-
- for (i = 0; i < ntx; i++) {
- netmap_set_txring(na, i, stopped);
- }
-
- for (i = 0; i < nrx; i++) {
- netmap_set_rxring(na, i, stopped);
+ for_rx_tx(t) {
+ for (i = 0; i < netmap_real_rings(na, t); i++) {
+ netmap_set_ring(na, i, t, stopped);
+ }
}
}
@@ -657,7 +694,8 @@ netmap_update_config(struct netmap_adapter *na)
txr = txd = rxr = rxd = 0;
if (na->nm_config == NULL ||
- na->nm_config(na, &txr, &txd, &rxr, &rxd)) {
+ na->nm_config(na, &txr, &txd, &rxr, &rxd))
+ {
/* take whatever we had at init time */
txr = na->num_tx_rings;
txd = na->num_tx_desc;
@@ -738,73 +776,59 @@ netmap_krings_create(struct netmap_adapter *na, u_int tailroom)
{
u_int i, len, ndesc;
struct netmap_kring *kring;
- u_int ntx, nrx;
+ u_int n[NR_TXRX];
+ enum txrx t;
/* account for the (possibly fake) host rings */
- ntx = na->num_tx_rings + 1;
- nrx = na->num_rx_rings + 1;
+ n[NR_TX] = na->num_tx_rings + 1;
+ n[NR_RX] = na->num_rx_rings + 1;
- len = (ntx + nrx) * sizeof(struct netmap_kring) + tailroom;
+ len = (n[NR_TX] + n[NR_RX]) * sizeof(struct netmap_kring) + tailroom;
na->tx_rings = malloc((size_t)len, M_DEVBUF, M_NOWAIT | M_ZERO);
if (na->tx_rings == NULL) {
D("Cannot allocate krings");
return ENOMEM;
}
- na->rx_rings = na->tx_rings + ntx;
+ na->rx_rings = na->tx_rings + n[NR_TX];
/*
* All fields in krings are 0 except the one initialized below.
* but better be explicit on important kring fields.
*/
- ndesc = na->num_tx_desc;
- for (i = 0; i < ntx; i++) { /* Transmit rings */
- kring = &na->tx_rings[i];
- bzero(kring, sizeof(*kring));
- kring->na = na;
- kring->ring_id = i;
- kring->nkr_num_slots = ndesc;
- if (i < na->num_tx_rings) {
- kring->nm_sync = na->nm_txsync;
- } else if (i == na->num_tx_rings) {
- kring->nm_sync = netmap_txsync_to_host_compat;
- }
- /*
- * IMPORTANT: Always keep one slot empty.
- */
- kring->rhead = kring->rcur = kring->nr_hwcur = 0;
- kring->rtail = kring->nr_hwtail = ndesc - 1;
- snprintf(kring->name, sizeof(kring->name) - 1, "%s TX%d", na->name, i);
- ND("ktx %s h %d c %d t %d",
- kring->name, kring->rhead, kring->rcur, kring->rtail);
- mtx_init(&kring->q_lock, "nm_txq_lock", NULL, MTX_DEF);
- init_waitqueue_head(&kring->si);
- }
-
- ndesc = na->num_rx_desc;
- for (i = 0; i < nrx; i++) { /* Receive rings */
- kring = &na->rx_rings[i];
- bzero(kring, sizeof(*kring));
- kring->na = na;
- kring->ring_id = i;
- kring->nkr_num_slots = ndesc;
- if (i < na->num_rx_rings) {
- kring->nm_sync = na->nm_rxsync;
- } else if (i == na->num_rx_rings) {
- kring->nm_sync = netmap_rxsync_from_host_compat;
+ for_rx_tx(t) {
+ ndesc = nma_get_ndesc(na, t);
+ for (i = 0; i < n[t]; i++) {
+ kring = &NMR(na, t)[i];
+ bzero(kring, sizeof(*kring));
+ kring->na = na;
+ kring->ring_id = i;
+ kring->tx = t;
+ kring->nkr_num_slots = ndesc;
+ if (i < nma_get_nrings(na, t)) {
+ kring->nm_sync = (t == NR_TX ? na->nm_txsync : na->nm_rxsync);
+ } else if (i == na->num_tx_rings) {
+ kring->nm_sync = (t == NR_TX ?
+ netmap_txsync_to_host_compat :
+ netmap_rxsync_from_host_compat);
+ }
+ kring->nm_notify = na->nm_notify;
+ kring->rhead = kring->rcur = kring->nr_hwcur = 0;
+ /*
+ * IMPORTANT: Always keep one slot empty.
+ */
+ kring->rtail = kring->nr_hwtail = (t == NR_TX ? ndesc - 1 : 0);
+ snprintf(kring->name, sizeof(kring->name) - 1, "%s %s%d", na->name,
+ nm_txrx2str(t), i);
+ ND("ktx %s h %d c %d t %d",
+ kring->name, kring->rhead, kring->rcur, kring->rtail);
+ mtx_init(&kring->q_lock, (t == NR_TX ? "nm_txq_lock" : "nm_rxq_lock"), NULL, MTX_DEF);
+ init_waitqueue_head(&kring->si);
}
- kring->rhead = kring->rcur = kring->nr_hwcur = 0;
- kring->rtail = kring->nr_hwtail = 0;
- snprintf(kring->name, sizeof(kring->name) - 1, "%s RX%d", na->name, i);
- ND("krx %s h %d c %d t %d",
- kring->name, kring->rhead, kring->rcur, kring->rtail);
- mtx_init(&kring->q_lock, "nm_rxq_lock", NULL, MTX_DEF);
- init_waitqueue_head(&kring->si);
+ init_waitqueue_head(&na->si[t]);
}
- init_waitqueue_head(&na->tx_si);
- init_waitqueue_head(&na->rx_si);
- na->tailroom = na->rx_rings + nrx;
+ na->tailroom = na->rx_rings + n[NR_RX];
return 0;
}
@@ -829,6 +853,10 @@ void
netmap_krings_delete(struct netmap_adapter *na)
{
struct netmap_kring *kring = na->tx_rings;
+ enum txrx t;
+
+ for_rx_tx(t)
+ netmap_knlist_destroy(&na->si[t]);
/* we rely on the krings layout described above */
for ( ; kring != na->tailroom; kring++) {
@@ -858,142 +886,35 @@ netmap_hw_krings_delete(struct netmap_adapter *na)
}
-/* create a new netmap_if for a newly registered fd.
- * If this is the first registration of the adapter,
- * also create the netmap rings and their in-kernel view,
- * the netmap krings.
- */
-/* call with NMG_LOCK held */
-static struct netmap_if*
-netmap_if_new(struct netmap_adapter *na)
-{
- struct netmap_if *nifp;
-
- if (netmap_update_config(na)) {
- /* configuration mismatch, report and fail */
- return NULL;
- }
-
- if (na->active_fds) /* already registered */
- goto final;
-
- /* create and init the krings arrays.
- * Depending on the adapter, this may also create
- * the netmap rings themselves
- */
- if (na->nm_krings_create(na))
- return NULL;
-
- /* create all missing netmap rings */
- if (netmap_mem_rings_create(na))
- goto cleanup;
-
-final:
-
- /* in all cases, create a new netmap if */
- nifp = netmap_mem_if_new(na);
- if (nifp == NULL)
- goto cleanup;
-
- return (nifp);
-
-cleanup:
-
- if (na->active_fds == 0) {
- netmap_mem_rings_delete(na);
- na->nm_krings_delete(na);
- }
-
- return NULL;
-}
-
-
-/* grab a reference to the memory allocator, if we don't have one already. The
- * reference is taken from the netmap_adapter registered with the priv.
- */
-/* call with NMG_LOCK held */
-static int
-netmap_get_memory_locked(struct netmap_priv_d* p)
-{
- struct netmap_mem_d *nmd;
- int error = 0;
-
- if (p->np_na == NULL) {
- if (!netmap_mmap_unreg)
- return ENODEV;
- /* for compatibility with older versions of the API
- * we use the global allocator when no interface has been
- * registered
- */
- nmd = &nm_mem;
- } else {
- nmd = p->np_na->nm_mem;
- }
- if (p->np_mref == NULL) {
- error = netmap_mem_finalize(nmd, p->np_na);
- if (!error)
- p->np_mref = nmd;
- } else if (p->np_mref != nmd) {
- /* a virtual port has been registered, but previous
- * syscalls already used the global allocator.
- * We cannot continue
- */
- error = ENODEV;
- }
- return error;
-}
-
-
-/* call with NMG_LOCK *not* held */
-int
-netmap_get_memory(struct netmap_priv_d* p)
-{
- int error;
- NMG_LOCK();
- error = netmap_get_memory_locked(p);
- NMG_UNLOCK();
- return error;
-}
-
-
-/* call with NMG_LOCK held */
-static int
-netmap_have_memory_locked(struct netmap_priv_d* p)
-{
- return p->np_mref != NULL;
-}
-
-
-/* call with NMG_LOCK held */
-static void
-netmap_drop_memory_locked(struct netmap_priv_d* p)
-{
- if (p->np_mref) {
- netmap_mem_deref(p->np_mref, p->np_na);
- p->np_mref = NULL;
- }
-}
-
/*
- * Call nm_register(ifp,0) to stop netmap mode on the interface and
+ * Undo everything that was done in netmap_do_regif(). In particular,
+ * call nm_register(ifp,0) to stop netmap mode on the interface and
* revert to normal operation.
- * The second argument is the nifp to work on. In some cases it is
- * not attached yet to the netmap_priv_d so we need to pass it as
- * a separate argument.
*/
/* call with NMG_LOCK held */
+static void netmap_unset_ringid(struct netmap_priv_d *);
+static void netmap_rel_exclusive(struct netmap_priv_d *);
static void
-netmap_do_unregif(struct netmap_priv_d *priv, struct netmap_if *nifp)
+netmap_do_unregif(struct netmap_priv_d *priv)
{
struct netmap_adapter *na = priv->np_na;
NMG_LOCK_ASSERT();
na->active_fds--;
+ /* release exclusive use if it was requested on regif */
+ netmap_rel_exclusive(priv);
if (na->active_fds <= 0) { /* last instance */
if (netmap_verbose)
D("deleting last instance for %s", na->name);
+
+#ifdef WITH_MONITOR
+ /* walk through all the rings and tell any monitor
+ * that the port is going to exit netmap mode
+ */
+ netmap_monitor_stop(na);
+#endif
/*
* (TO CHECK) This function is only called
* when the last reference to this file descriptor goes
@@ -1014,37 +935,33 @@ netmap_do_unregif(struct netmap_priv_d *priv, struct netmap_if *nifp)
* XXX The wake up now must happen during *_down(), when
* we order all activities to stop. -gl
*/
- netmap_knlist_destroy(&na->tx_si);
- netmap_knlist_destroy(&na->rx_si);
-
/* delete rings and buffers */
netmap_mem_rings_delete(na);
na->nm_krings_delete(na);
}
+ /* possibily decrement counter of tx_si/rx_si users */
+ netmap_unset_ringid(priv);
/* delete the nifp */
- netmap_mem_if_delete(na, nifp);
-}
-
-/* call with NMG_LOCK held */
-static __inline int
-nm_tx_si_user(struct netmap_priv_d *priv)
-{
- return (priv->np_na != NULL &&
- (priv->np_txqlast - priv->np_txqfirst > 1));
+ netmap_mem_if_delete(na, priv->np_nifp);
+ /* drop the allocator */
+ netmap_mem_deref(na->nm_mem, na);
+ /* mark the priv as unregistered */
+ priv->np_na = NULL;
+ priv->np_nifp = NULL;
}
/* call with NMG_LOCK held */
static __inline int
-nm_rx_si_user(struct netmap_priv_d *priv)
+nm_si_user(struct netmap_priv_d *priv, enum txrx t)
{
return (priv->np_na != NULL &&
- (priv->np_rxqlast - priv->np_rxqfirst > 1));
+ (priv->np_qlast[t] - priv->np_qfirst[t] > 1));
}
-
/*
* Destructor of the netmap_priv_d, called when the fd has
- * no active open() and mmap(). Also called in error paths.
+ * no active open() and mmap().
+ * Undo all the things done by NIOCREGIF.
*
* returns 1 if this is the last instance and we can free priv
*/
@@ -1066,17 +983,8 @@ netmap_dtor_locked(struct netmap_priv_d *priv)
if (!na) {
return 1; //XXX is it correct?
}
- netmap_do_unregif(priv, priv->np_nifp);
- priv->np_nifp = NULL;
- netmap_drop_memory_locked(priv);
- if (priv->np_na) {
- if (nm_tx_si_user(priv))
- na->tx_si_users--;
- if (nm_rx_si_user(priv))
- na->rx_si_users--;
- netmap_adapter_put(na);
- priv->np_na = NULL;
- }
+ netmap_do_unregif(priv);
+ netmap_adapter_put(na);
return 1;
}
@@ -1148,7 +1056,7 @@ static void
netmap_grab_packets(struct netmap_kring *kring, struct mbq *q, int force)
{
u_int const lim = kring->nkr_num_slots - 1;
- u_int const head = kring->ring->head;
+ u_int const head = kring->rhead;
u_int n;
struct netmap_adapter *na = kring->na;
@@ -1235,7 +1143,6 @@ void
netmap_txsync_to_host(struct netmap_adapter *na)
{
struct netmap_kring *kring = &na->tx_rings[na->num_tx_rings];
- struct netmap_ring *ring = kring->ring;
u_int const lim = kring->nkr_num_slots - 1;
u_int const head = kring->rhead;
struct mbq q;
@@ -1246,14 +1153,12 @@ netmap_txsync_to_host(struct netmap_adapter *na)
* the queue is drained in all cases.
*/
mbq_init(&q);
- ring->cur = head;
netmap_grab_packets(kring, &q, 1 /* force */);
ND("have %d pkts in queue", mbq_len(&q));
kring->nr_hwcur = head;
kring->nr_hwtail = head + lim;
if (kring->nr_hwtail > lim)
kring->nr_hwtail -= lim + 1;
- nm_txsync_finalize(kring);
netmap_send_up(na->ifp, &q);
}
@@ -1281,11 +1186,13 @@ netmap_rxsync_from_host(struct netmap_adapter *na, struct thread *td, void *pwai
u_int const lim = kring->nkr_num_slots - 1;
u_int const head = kring->rhead;
int ret = 0;
- struct mbq *q = &kring->rx_queue;
+ struct mbq *q = &kring->rx_queue, fq;
(void)pwait; /* disable unused warnings */
(void)td;
+ mbq_init(&fq); /* fq holds packets to be freed */
+
mbq_lock(q);
/* First part: import newly received packets */
@@ -1308,7 +1215,7 @@ netmap_rxsync_from_host(struct netmap_adapter *na, struct thread *td, void *pwai
slot->len = len;
slot->flags = kring->nkr_slot_flags;
nm_i = nm_next(nm_i, lim);
- m_freem(m);
+ mbq_enqueue(&fq, m);
}
kring->nr_hwtail = nm_i;
}
@@ -1323,13 +1230,15 @@ netmap_rxsync_from_host(struct netmap_adapter *na, struct thread *td, void *pwai
kring->nr_hwcur = head;
}
- nm_rxsync_finalize(kring);
-
/* access copies of cur,tail in the kring */
if (kring->rcur == kring->rtail && td) /* no bufs available */
OS_selrecord(td, &kring->si);
mbq_unlock(q);
+
+ mbq_purge(&fq);
+ mbq_destroy(&fq);
+
return ret;
}
@@ -1363,9 +1272,11 @@ netmap_get_hw_na(struct ifnet *ifp, struct netmap_adapter **na)
{
/* generic support */
int i = netmap_admode; /* Take a snapshot. */
- int error = 0;
struct netmap_adapter *prev_na;
+#ifdef WITH_GENERIC
struct netmap_generic_adapter *gna;
+ int error = 0;
+#endif
*na = NULL; /* default */
@@ -1401,6 +1312,7 @@ netmap_get_hw_na(struct ifnet *ifp, struct netmap_adapter **na)
if (!NETMAP_CAPABLE(ifp) && i == NETMAP_ADMODE_NATIVE)
return EOPNOTSUPP;
+#ifdef WITH_GENERIC
/* Otherwise, create a generic adapter and return it,
* saving the previously used netmap adapter, if any.
*
@@ -1431,6 +1343,9 @@ netmap_get_hw_na(struct ifnet *ifp, struct netmap_adapter **na)
ND("Created generic NA %p (prev %p)", gna, gna->prev);
return 0;
+#else /* !WITH_GENERIC */
+ return EOPNOTSUPP;
+#endif
}
@@ -1489,7 +1404,7 @@ netmap_get_na(struct nmreq *nmr, struct netmap_adapter **na, int create)
return error;
if (*na != NULL) /* valid match in netmap_get_bdg_na() */
- goto pipes;
+ goto out;
/*
* This must be a hardware na, lookup the name in the system.
@@ -1509,14 +1424,6 @@ netmap_get_na(struct nmreq *nmr, struct netmap_adapter **na, int create)
*na = ret;
netmap_adapter_get(ret);
-pipes:
- /*
- * If we are opening a pipe whose parent was not in netmap mode,
- * we have to allocate the pipe array now.
- * XXX get rid of this clumsiness (2014-03-15)
- */
- error = netmap_pipe_alloc(*na, nmr);
-
out:
if (error && ret != NULL)
netmap_adapter_put(ret);
@@ -1541,9 +1448,10 @@ out:
*
* hwcur, rhead, rtail and hwtail are reliable
*/
-u_int
+static u_int
nm_txsync_prologue(struct netmap_kring *kring)
{
+#define NM_ASSERT(t) if (t) { D("fail " #t); goto error; }
struct netmap_ring *ring = kring->ring;
u_int head = ring->head; /* read only once */
u_int cur = ring->cur; /* read only once */
@@ -1569,25 +1477,20 @@ nm_txsync_prologue(struct netmap_kring *kring)
*/
if (kring->rtail >= kring->rhead) {
/* want rhead <= head <= rtail */
- if (head < kring->rhead || head > kring->rtail)
- goto error;
+ NM_ASSERT(head < kring->rhead || head > kring->rtail);
/* and also head <= cur <= rtail */
- if (cur < head || cur > kring->rtail)
- goto error;
+ NM_ASSERT(cur < head || cur > kring->rtail);
} else { /* here rtail < rhead */
/* we need head outside rtail .. rhead */
- if (head > kring->rtail && head < kring->rhead)
- goto error;
+ NM_ASSERT(head > kring->rtail && head < kring->rhead);
/* two cases now: head <= rtail or head >= rhead */
if (head <= kring->rtail) {
/* want head <= cur <= rtail */
- if (cur < head || cur > kring->rtail)
- goto error;
+ NM_ASSERT(cur < head || cur > kring->rtail);
} else { /* head >= rhead */
/* cur must be outside rtail..head */
- if (cur > kring->rtail && cur < head)
- goto error;
+ NM_ASSERT(cur > kring->rtail && cur < head);
}
}
if (ring->tail != kring->rtail) {
@@ -1600,12 +1503,13 @@ nm_txsync_prologue(struct netmap_kring *kring)
return head;
error:
- RD(5, "%s kring error: hwcur %d rcur %d hwtail %d cur %d tail %d",
+ RD(5, "%s kring error: head %d cur %d tail %d rhead %d rcur %d rtail %d hwcur %d hwtail %d",
kring->name,
- kring->nr_hwcur,
- kring->rcur, kring->nr_hwtail,
- cur, ring->tail);
+ head, cur, ring->tail,
+ kring->rhead, kring->rcur, kring->rtail,
+ kring->nr_hwcur, kring->nr_hwtail);
return n;
+#undef NM_ASSERT
}
@@ -1620,14 +1524,14 @@ error:
* hwcur and hwtail are reliable.
*
*/
-u_int
+static u_int
nm_rxsync_prologue(struct netmap_kring *kring)
{
struct netmap_ring *ring = kring->ring;
uint32_t const n = kring->nkr_num_slots;
uint32_t head, cur;
- ND("%s kc %d kt %d h %d c %d t %d",
+ ND(5,"%s kc %d kt %d h %d c %d t %d",
kring->name,
kring->nr_hwcur, kring->nr_hwtail,
ring->head, ring->cur, ring->tail);
@@ -1719,7 +1623,7 @@ netmap_ring_reinit(struct netmap_kring *kring)
for (i = 0; i <= lim; i++) {
u_int idx = ring->slot[i].buf_idx;
u_int len = ring->slot[i].len;
- if (idx < 2 || idx >= netmap_total_buffers) {
+ if (idx < 2 || idx >= kring->na->na_lut.objtotal) {
RD(5, "bad index at slot %d idx %d len %d ", i, idx, len);
ring->slot[i].buf_idx = 0;
ring->slot[i].len = 0;
@@ -1754,6 +1658,7 @@ netmap_interp_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags
struct netmap_adapter *na = priv->np_na;
u_int j, i = ringid & NETMAP_RING_MASK;
u_int reg = flags & NR_REG_MASK;
+ enum txrx t;
if (reg == NR_REG_DEFAULT) {
/* convert from old ringid to flags */
@@ -1770,12 +1675,12 @@ netmap_interp_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags
case NR_REG_ALL_NIC:
case NR_REG_PIPE_MASTER:
case NR_REG_PIPE_SLAVE:
- priv->np_txqfirst = 0;
- priv->np_txqlast = na->num_tx_rings;
- priv->np_rxqfirst = 0;
- priv->np_rxqlast = na->num_rx_rings;
+ for_rx_tx(t) {
+ priv->np_qfirst[t] = 0;
+ priv->np_qlast[t] = nma_get_nrings(na, t);
+ }
ND("%s %d %d", "ALL/PIPE",
- priv->np_rxqfirst, priv->np_rxqlast);
+ priv->np_qfirst[NR_RX], priv->np_qlast[NR_RX]);
break;
case NR_REG_SW:
case NR_REG_NIC_SW:
@@ -1783,31 +1688,27 @@ netmap_interp_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags
D("host rings not supported");
return EINVAL;
}
- priv->np_txqfirst = (reg == NR_REG_SW ?
- na->num_tx_rings : 0);
- priv->np_txqlast = na->num_tx_rings + 1;
- priv->np_rxqfirst = (reg == NR_REG_SW ?
- na->num_rx_rings : 0);
- priv->np_rxqlast = na->num_rx_rings + 1;
+ for_rx_tx(t) {
+ priv->np_qfirst[t] = (reg == NR_REG_SW ?
+ nma_get_nrings(na, t) : 0);
+ priv->np_qlast[t] = nma_get_nrings(na, t) + 1;
+ }
ND("%s %d %d", reg == NR_REG_SW ? "SW" : "NIC+SW",
- priv->np_rxqfirst, priv->np_rxqlast);
+ priv->np_qfirst[NR_RX], priv->np_qlast[NR_RX]);
break;
case NR_REG_ONE_NIC:
if (i >= na->num_tx_rings && i >= na->num_rx_rings) {
D("invalid ring id %d", i);
return EINVAL;
}
- /* if not enough rings, use the first one */
- j = i;
- if (j >= na->num_tx_rings)
- j = 0;
- priv->np_txqfirst = j;
- priv->np_txqlast = j + 1;
- j = i;
- if (j >= na->num_rx_rings)
- j = 0;
- priv->np_rxqfirst = j;
- priv->np_rxqlast = j + 1;
+ for_rx_tx(t) {
+ /* if not enough rings, use the first one */
+ j = i;
+ if (j >= nma_get_nrings(na, t))
+ j = 0;
+ priv->np_qfirst[t] = j;
+ priv->np_qlast[t] = j + 1;
+ }
break;
default:
D("invalid regif type %d", reg);
@@ -1818,10 +1719,10 @@ netmap_interp_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags
if (netmap_verbose) {
D("%s: tx [%d,%d) rx [%d,%d) id %d",
na->name,
- priv->np_txqfirst,
- priv->np_txqlast,
- priv->np_rxqfirst,
- priv->np_rxqlast,
+ priv->np_qfirst[NR_TX],
+ priv->np_qlast[NR_TX],
+ priv->np_qfirst[NR_RX],
+ priv->np_qlast[NR_RX],
i);
}
return 0;
@@ -1837,6 +1738,7 @@ netmap_set_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags)
{
struct netmap_adapter *na = priv->np_na;
int error;
+ enum txrx t;
error = netmap_interp_ringid(priv, ringid, flags);
if (error) {
@@ -1850,11 +1752,107 @@ netmap_set_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags)
* The default netmap_notify() callback will then
* avoid signaling the global queue if nobody is using it
*/
- if (nm_tx_si_user(priv))
- na->tx_si_users++;
- if (nm_rx_si_user(priv))
- na->rx_si_users++;
+ for_rx_tx(t) {
+ if (nm_si_user(priv, t))
+ na->si_users[t]++;
+ }
+ return 0;
+}
+
+static void
+netmap_unset_ringid(struct netmap_priv_d *priv)
+{
+ struct netmap_adapter *na = priv->np_na;
+ enum txrx t;
+
+ for_rx_tx(t) {
+ if (nm_si_user(priv, t))
+ na->si_users[t]--;
+ priv->np_qfirst[t] = priv->np_qlast[t] = 0;
+ }
+ priv->np_flags = 0;
+ priv->np_txpoll = 0;
+}
+
+
+/* check that the rings we want to bind are not exclusively owned by a previous
+ * bind. If exclusive ownership has been requested, we also mark the rings.
+ */
+static int
+netmap_get_exclusive(struct netmap_priv_d *priv)
+{
+ struct netmap_adapter *na = priv->np_na;
+ u_int i;
+ struct netmap_kring *kring;
+ int excl = (priv->np_flags & NR_EXCLUSIVE);
+ enum txrx t;
+
+ ND("%s: grabbing tx [%d, %d) rx [%d, %d)",
+ na->name,
+ priv->np_qfirst[NR_TX],
+ priv->np_qlast[NR_TX],
+ priv->np_qfirst[NR_RX],
+ priv->np_qlast[NR_RX]);
+
+ /* first round: check that all the requested rings
+ * are neither alread exclusively owned, nor we
+ * want exclusive ownership when they are already in use
+ */
+ for_rx_tx(t) {
+ for (i = priv->np_qfirst[t]; i < priv->np_qlast[t]; i++) {
+ kring = &NMR(na, t)[i];
+ if ((kring->nr_kflags & NKR_EXCLUSIVE) ||
+ (kring->users && excl))
+ {
+ ND("ring %s busy", kring->name);
+ return EBUSY;
+ }
+ }
+ }
+
+ /* second round: increment usage cound and possibly
+ * mark as exclusive
+ */
+
+ for_rx_tx(t) {
+ for (i = priv->np_qfirst[t]; i < priv->np_qlast[t]; i++) {
+ kring = &NMR(na, t)[i];
+ kring->users++;
+ if (excl)
+ kring->nr_kflags |= NKR_EXCLUSIVE;
+ }
+ }
+
return 0;
+
+}
+
+/* undo netmap_get_ownership() */
+static void
+netmap_rel_exclusive(struct netmap_priv_d *priv)
+{
+ struct netmap_adapter *na = priv->np_na;
+ u_int i;
+ struct netmap_kring *kring;
+ int excl = (priv->np_flags & NR_EXCLUSIVE);
+ enum txrx t;
+
+ ND("%s: releasing tx [%d, %d) rx [%d, %d)",
+ na->name,
+ priv->np_qfirst[NR_TX],
+ priv->np_qlast[NR_TX],
+ priv->np_qfirst[NR_RX],
+ priv->np_qlast[MR_RX]);
+
+
+ for_rx_tx(t) {
+ for (i = priv->np_qfirst[t]; i < priv->np_qlast[t]; i++) {
+ kring = &NMR(na, t)[i];
+ if (excl)
+ kring->nr_kflags &= ~NKR_EXCLUSIVE;
+ kring->users--;
+ }
+ }
}
/*
@@ -1871,9 +1869,8 @@ netmap_set_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags)
* The bwrap has to override this, since it has to forward
* the request to the wrapped adapter (netmap_bwrap_config).
*
- * XXX netmap_if_new calls this again (2014-03-15)
*
- * na->nm_krings_create() [by netmap_if_new]
+ * na->nm_krings_create()
* (create and init the krings array)
*
* One of the following:
@@ -1927,15 +1924,14 @@ netmap_set_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags)
* the hwna notify callback (to get the frames
* coming from outside go through the bridge).
*
- * XXX maybe netmap_if_new() should be merged with this (2014-03-15).
*
*/
-struct netmap_if *
+int
netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na,
- uint16_t ringid, uint32_t flags, int *err)
+ uint16_t ringid, uint32_t flags)
{
struct netmap_if *nifp = NULL;
- int error, need_mem = 0;
+ int error;
NMG_LOCK_ASSERT();
/* ring configuration may have changed, fetch from the card */
@@ -1943,57 +1939,121 @@ netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na,
priv->np_na = na; /* store the reference */
error = netmap_set_ringid(priv, ringid, flags);
if (error)
- goto out;
- /* ensure allocators are ready */
- need_mem = !netmap_have_memory_locked(priv);
- if (need_mem) {
- error = netmap_get_memory_locked(priv);
- ND("get_memory returned %d", error);
+ goto err;
+ error = netmap_mem_finalize(na->nm_mem, na);
+ if (error)
+ goto err;
+
+ if (na->active_fds == 0) {
+ /*
+ * If this is the first registration of the adapter,
+ * also create the netmap rings and their in-kernel view,
+ * the netmap krings.
+ */
+
+ /*
+ * Depending on the adapter, this may also create
+ * the netmap rings themselves
+ */
+ error = na->nm_krings_create(na);
+ if (error)
+ goto err_drop_mem;
+
+ /* create all missing netmap rings */
+ error = netmap_mem_rings_create(na);
if (error)
- goto out;
+ goto err_del_krings;
}
- /* Allocate a netmap_if and, if necessary, all the netmap_ring's */
- nifp = netmap_if_new(na);
- if (nifp == NULL) { /* allocation failed */
+
+ /* now the kring must exist and we can check whether some
+ * previous bind has exclusive ownership on them
+ */
+ error = netmap_get_exclusive(priv);
+ if (error)
+ goto err_del_rings;
+
+ /* in all cases, create a new netmap if */
+ nifp = netmap_mem_if_new(na);
+ if (nifp == NULL) {
error = ENOMEM;
- goto out;
+ goto err_rel_excl;
}
+
na->active_fds++;
if (!nm_netmap_on(na)) {
/* Netmap not active, set the card in netmap mode
* and make it use the shared buffers.
*/
/* cache the allocator info in the na */
- na->na_lut = netmap_mem_get_lut(na->nm_mem);
- ND("%p->na_lut == %p", na, na->na_lut);
- na->na_lut_objtotal = netmap_mem_get_buftotal(na->nm_mem);
- na->na_lut_objsize = netmap_mem_get_bufsize(na->nm_mem);
+ netmap_mem_get_lut(na->nm_mem, &na->na_lut);
+ ND("%p->na_lut == %p", na, na->na_lut.lut);
error = na->nm_register(na, 1); /* mode on */
- if (error) {
- netmap_do_unregif(priv, nifp);
- nifp = NULL;
- }
- }
-out:
- *err = error;
- if (error) {
- /* we should drop the allocator, but only
- * if we were the ones who grabbed it
- */
- if (need_mem)
- netmap_drop_memory_locked(priv);
- priv->np_na = NULL;
- }
- if (nifp != NULL) {
- /*
- * advertise that the interface is ready bt setting ni_nifp.
- * The barrier is needed because readers (poll and *SYNC)
- * check for priv->np_nifp != NULL without locking
- */
- wmb(); /* make sure previous writes are visible to all CPUs */
- priv->np_nifp = nifp;
+ if (error)
+ goto err_del_if;
}
- return nifp;
+
+ /*
+ * advertise that the interface is ready by setting np_nifp.
+ * The barrier is needed because readers (poll, *SYNC and mmap)
+ * check for priv->np_nifp != NULL without locking
+ */
+ mb(); /* make sure previous writes are visible to all CPUs */
+ priv->np_nifp = nifp;
+
+ return 0;
+
+err_del_if:
+ memset(&na->na_lut, 0, sizeof(na->na_lut));
+ na->active_fds--;
+ netmap_mem_if_delete(na, nifp);
+err_rel_excl:
+ netmap_rel_exclusive(priv);
+err_del_rings:
+ if (na->active_fds == 0)
+ netmap_mem_rings_delete(na);
+err_del_krings:
+ if (na->active_fds == 0)
+ na->nm_krings_delete(na);
+err_drop_mem:
+ netmap_mem_deref(na->nm_mem, na);
+err:
+ priv->np_na = NULL;
+ return error;
+}
+
+
+/*
+ * update kring and ring at the end of txsync.
+ */
+static inline void
+nm_txsync_finalize(struct netmap_kring *kring)
+{
+ /* update ring tail to what the kernel knows */
+ kring->ring->tail = kring->rtail = kring->nr_hwtail;
+
+ /* note, head/rhead/hwcur might be behind cur/rcur
+ * if no carrier
+ */
+ ND(5, "%s now hwcur %d hwtail %d head %d cur %d tail %d",
+ kring->name, kring->nr_hwcur, kring->nr_hwtail,
+ kring->rhead, kring->rcur, kring->rtail);
+}
+
+
+/*
+ * update kring and ring at the end of rxsync
+ */
+static inline void
+nm_rxsync_finalize(struct netmap_kring *kring)
+{
+ /* tell userspace that there might be new packets */
+ //struct netmap_ring *ring = kring->ring;
+ ND("head %d cur %d tail %d -> %d", ring->head, ring->cur, ring->tail,
+ kring->nr_hwtail);
+ kring->ring->tail = kring->rtail = kring->nr_hwtail;
+ /* make a copy of the state for next round */
+ kring->rhead = kring->ring->head;
+ kring->rcur = kring->ring->cur;
}
@@ -2021,6 +2081,7 @@ netmap_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
u_int i, qfirst, qlast;
struct netmap_if *nifp;
struct netmap_kring *krings;
+ enum txrx t;
(void)dev; /* UNUSED */
(void)fflag; /* UNUSED */
@@ -2108,7 +2169,7 @@ netmap_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
do {
u_int memflags;
- if (priv->np_na != NULL) { /* thread already registered */
+ if (priv->np_nifp != NULL) { /* thread already registered */
error = EBUSY;
break;
}
@@ -2121,12 +2182,12 @@ netmap_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
error = EBUSY;
break;
}
- nifp = netmap_do_regif(priv, na, nmr->nr_ringid, nmr->nr_flags, &error);
- if (!nifp) { /* reg. failed, release priv and ref */
+ error = netmap_do_regif(priv, na, nmr->nr_ringid, nmr->nr_flags);
+ if (error) { /* reg. failed, release priv and ref */
netmap_adapter_put(na);
- priv->np_nifp = NULL;
break;
}
+ nifp = priv->np_nifp;
priv->np_td = td; // XXX kqueue, debugging only
/* return the offset of the netmap_if object */
@@ -2137,16 +2198,17 @@ netmap_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
error = netmap_mem_get_info(na->nm_mem, &nmr->nr_memsize, &memflags,
&nmr->nr_arg2);
if (error) {
+ netmap_do_unregif(priv);
netmap_adapter_put(na);
break;
}
if (memflags & NETMAP_MEM_PRIVATE) {
*(uint32_t *)(uintptr_t)&nifp->ni_flags |= NI_PRIV_MEM;
}
- priv->np_txsi = (priv->np_txqlast - priv->np_txqfirst > 1) ?
- &na->tx_si : &na->tx_rings[priv->np_txqfirst].si;
- priv->np_rxsi = (priv->np_rxqlast - priv->np_rxqfirst > 1) ?
- &na->rx_si : &na->rx_rings[priv->np_rxqfirst].si;
+ for_rx_tx(t) {
+ priv->np_si[t] = nm_si_user(priv, t) ?
+ &na->si[t] : &NMR(na, t)[priv->np_qfirst[t]].si;
+ }
if (nmr->nr_arg3) {
D("requested %d extra buffers", nmr->nr_arg3);
@@ -2182,15 +2244,10 @@ netmap_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
break;
}
- if (cmd == NIOCTXSYNC) {
- krings = na->tx_rings;
- qfirst = priv->np_txqfirst;
- qlast = priv->np_txqlast;
- } else {
- krings = na->rx_rings;
- qfirst = priv->np_rxqfirst;
- qlast = priv->np_rxqlast;
- }
+ t = (cmd == NIOCTXSYNC ? NR_TX : NR_RX);
+ krings = NMR(na, t);
+ qfirst = priv->np_qfirst[t];
+ qlast = priv->np_qlast[t];
for (i = qfirst; i < qlast; i++) {
struct netmap_kring *kring = krings + i;
@@ -2205,15 +2262,19 @@ netmap_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
kring->nr_hwcur);
if (nm_txsync_prologue(kring) >= kring->nkr_num_slots) {
netmap_ring_reinit(kring);
- } else {
- kring->nm_sync(kring, NAF_FORCE_RECLAIM);
+ } else if (kring->nm_sync(kring, NAF_FORCE_RECLAIM) == 0) {
+ nm_txsync_finalize(kring);
}
if (netmap_verbose & NM_VERB_TXSYNC)
D("post txsync ring %d cur %d hwcur %d",
i, kring->ring->cur,
kring->nr_hwcur);
} else {
- kring->nm_sync(kring, NAF_FORCE_READ);
+ if (nm_rxsync_prologue(kring) >= kring->nkr_num_slots) {
+ netmap_ring_reinit(kring);
+ } else if (kring->nm_sync(kring, NAF_FORCE_READ) == 0) {
+ nm_rxsync_finalize(kring);
+ }
microtime(&na->rx_rings[i].ring->ts);
}
nm_kr_put(kring);
@@ -2221,9 +2282,11 @@ netmap_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
break;
+#ifdef WITH_VALE
case NIOCCONFIG:
error = netmap_bdg_config(nmr);
break;
+#endif
#ifdef __FreeBSD__
case FIONBIO:
case FIOASYNC:
@@ -2286,10 +2349,13 @@ netmap_poll(struct cdev *dev, int events, struct thread *td)
struct netmap_priv_d *priv = NULL;
struct netmap_adapter *na;
struct netmap_kring *kring;
- u_int i, check_all_tx, check_all_rx, want_tx, want_rx, revents = 0;
+ u_int i, check_all_tx, check_all_rx, want[NR_TXRX], revents = 0;
+#define want_tx want[NR_TX]
+#define want_rx want[NR_RX]
struct mbq q; /* packets from hw queues to host stack */
void *pwait = dev; /* linux compatibility */
int is_kevent = 0;
+ enum txrx t;
/*
* In order to avoid nested locks, we need to "double check"
@@ -2320,7 +2386,7 @@ netmap_poll(struct cdev *dev, int events, struct thread *td)
D("No if registered");
return POLLERR;
}
- rmb(); /* make sure following reads are not from cache */
+ mb(); /* make sure following reads are not from cache */
na = priv->np_na;
@@ -2346,28 +2412,22 @@ netmap_poll(struct cdev *dev, int events, struct thread *td)
* there are pending packets to send. The latter can be disabled
* passing NETMAP_NO_TX_POLL in the NIOCREG call.
*/
- check_all_tx = nm_tx_si_user(priv);
- check_all_rx = nm_rx_si_user(priv);
+ check_all_tx = nm_si_user(priv, NR_TX);
+ check_all_rx = nm_si_user(priv, NR_RX);
/*
* We start with a lock free round which is cheap if we have
* slots available. If this fails, then lock and call the sync
* routines.
*/
- for (i = priv->np_rxqfirst; want_rx && i < priv->np_rxqlast; i++) {
- kring = &na->rx_rings[i];
- /* XXX compare ring->cur and kring->tail */
- if (!nm_ring_empty(kring->ring)) {
- revents |= want_rx;
- want_rx = 0; /* also breaks the loop */
- }
- }
- for (i = priv->np_txqfirst; want_tx && i < priv->np_txqlast; i++) {
- kring = &na->tx_rings[i];
- /* XXX compare ring->cur and kring->tail */
- if (!nm_ring_empty(kring->ring)) {
- revents |= want_tx;
- want_tx = 0; /* also breaks the loop */
+ for_rx_tx(t) {
+ for (i = priv->np_qfirst[t]; want[t] && i < priv->np_qlast[t]; i++) {
+ kring = &NMR(na, t)[i];
+ /* XXX compare ring->cur and kring->tail */
+ if (!nm_ring_empty(kring->ring)) {
+ revents |= want[t];
+ want[t] = 0; /* also breaks the loop */
+ }
}
}
@@ -2386,7 +2446,7 @@ netmap_poll(struct cdev *dev, int events, struct thread *td)
* used to skip rings with no pending transmissions.
*/
flush_tx:
- for (i = priv->np_txqfirst; i < priv->np_txqlast; i++) {
+ for (i = priv->np_qfirst[NR_TX]; i < priv->np_qlast[NR_RX]; i++) {
int found = 0;
kring = &na->tx_rings[i];
@@ -2410,6 +2470,8 @@ flush_tx:
} else {
if (kring->nm_sync(kring, 0))
revents |= POLLERR;
+ else
+ nm_txsync_finalize(kring);
}
/*
@@ -2423,12 +2485,12 @@ flush_tx:
if (found) { /* notify other listeners */
revents |= want_tx;
want_tx = 0;
- na->nm_notify(na, i, NR_TX, 0);
+ kring->nm_notify(kring, 0);
}
}
if (want_tx && retry_tx && !is_kevent) {
OS_selrecord(td, check_all_tx ?
- &na->tx_si : &na->tx_rings[priv->np_txqfirst].si);
+ &na->si[NR_TX] : &na->tx_rings[priv->np_qfirst[NR_TX]].si);
retry_tx = 0;
goto flush_tx;
}
@@ -2442,7 +2504,7 @@ flush_tx:
int send_down = 0; /* transparent mode */
/* two rounds here for race avoidance */
do_retry_rx:
- for (i = priv->np_rxqfirst; i < priv->np_rxqlast; i++) {
+ for (i = priv->np_qfirst[NR_RX]; i < priv->np_qlast[NR_RX]; i++) {
int found = 0;
kring = &na->rx_rings[i];
@@ -2454,6 +2516,12 @@ do_retry_rx:
continue;
}
+ if (nm_rxsync_prologue(kring) >= kring->nkr_num_slots) {
+ netmap_ring_reinit(kring);
+ revents |= POLLERR;
+ }
+ /* now we can use kring->rcur, rtail */
+
/*
* transparent mode support: collect packets
* from the rxring(s).
@@ -2468,17 +2536,18 @@ do_retry_rx:
if (kring->nm_sync(kring, 0))
revents |= POLLERR;
+ else
+ nm_rxsync_finalize(kring);
if (netmap_no_timestamp == 0 ||
kring->ring->flags & NR_TIMESTAMP) {
microtime(&kring->ring->ts);
}
- /* after an rxsync we can use kring->rcur, rtail */
found = kring->rcur != kring->rtail;
nm_kr_put(kring);
if (found) {
revents |= want_rx;
retry_rx = 0;
- na->nm_notify(na, i, NR_RX, 0);
+ kring->nm_notify(kring, 0);
}
}
@@ -2497,7 +2566,7 @@ do_retry_rx:
if (retry_rx && !is_kevent)
OS_selrecord(td, check_all_rx ?
- &na->rx_si : &na->rx_rings[priv->np_rxqfirst].si);
+ &na->si[NR_RX] : &na->rx_rings[priv->np_qfirst[NR_RX]].si);
if (send_down > 0 || retry_rx) {
retry_rx = 0;
if (send_down)
@@ -2523,6 +2592,8 @@ do_retry_rx:
netmap_send_up(na->ifp, &q);
return (revents);
+#undef want_tx
+#undef want_rx
}
@@ -2532,27 +2603,19 @@ static int netmap_hw_krings_create(struct netmap_adapter *);
/* default notify callback */
static int
-netmap_notify(struct netmap_adapter *na, u_int n_ring,
- enum txrx tx, int flags)
+netmap_notify(struct netmap_kring *kring, int flags)
{
- struct netmap_kring *kring;
+ struct netmap_adapter *na = kring->na;
+ enum txrx t = kring->tx;
+
+ OS_selwakeup(&kring->si, PI_NET);
+ /* optimization: avoid a wake up on the global
+ * queue if nobody has registered for more
+ * than one ring
+ */
+ if (na->si_users[t] > 0)
+ OS_selwakeup(&na->si[t], PI_NET);
- if (tx == NR_TX) {
- kring = na->tx_rings + n_ring;
- OS_selwakeup(&kring->si, PI_NET);
- /* optimization: avoid a wake up on the global
- * queue if nobody has registered for more
- * than one ring
- */
- if (na->tx_si_users > 0)
- OS_selwakeup(&na->tx_si, PI_NET);
- } else {
- kring = na->rx_rings + n_ring;
- OS_selwakeup(&kring->si, PI_NET);
- /* optimization: same as above */
- if (na->rx_si_users > 0)
- OS_selwakeup(&na->rx_si, PI_NET);
- }
return 0;
}
@@ -2605,11 +2668,14 @@ netmap_attach_common(struct netmap_adapter *na)
if (na->nm_mem == NULL)
/* use the global allocator */
na->nm_mem = &nm_mem;
+ netmap_mem_get(na->nm_mem);
+#ifdef WITH_VALE
if (na->nm_bdg_attach == NULL)
/* no special nm_bdg_attach callback. On VALE
* attach, we need to interpose a bwrap
*/
na->nm_bdg_attach = netmap_bwrap_attach;
+#endif
return 0;
}
@@ -2626,8 +2692,8 @@ netmap_detach_common(struct netmap_adapter *na)
na->nm_krings_delete(na);
}
netmap_pipe_dealloc(na);
- if (na->na_flags & NAF_MEM_OWNER)
- netmap_mem_private_delete(na->nm_mem);
+ if (na->nm_mem)
+ netmap_mem_put(na->nm_mem);
bzero(na, sizeof(*na));
free(na, M_DEVBUF);
}
@@ -2678,7 +2744,7 @@ netmap_attach(struct netmap_adapter *arg)
if (hwna == NULL)
goto fail;
hwna->up = *arg;
- hwna->up.na_flags |= NAF_HOST_RINGS;
+ hwna->up.na_flags |= NAF_HOST_RINGS | NAF_NATIVE;
strncpy(hwna->up.name, ifp->if_xname, sizeof(hwna->up.name));
hwna->nm_hw_register = hwna->up.nm_register;
hwna->up.nm_register = netmap_hw_register;
@@ -2691,7 +2757,7 @@ netmap_attach(struct netmap_adapter *arg)
#ifdef linux
if (ifp->netdev_ops) {
/* prepare a clone of the netdev ops */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
+#ifndef NETMAP_LINUX_HAVE_NETDEV_OPS
hwna->nm_ndo.ndo_start_xmit = ifp->netdev_ops;
#else
hwna->nm_ndo = *ifp->netdev_ops;
@@ -2702,7 +2768,7 @@ netmap_attach(struct netmap_adapter *arg)
hwna->nm_eto = *ifp->ethtool_ops;
}
hwna->nm_eto.set_ringparam = linux_netmap_set_ringparam;
-#ifdef ETHTOOL_SCHANNELS
+#ifdef NETMAP_LINUX_HAVE_SET_CHANNELS
hwna->nm_eto.set_channels = linux_netmap_set_channels;
#endif
if (arg->nm_config == NULL) {
@@ -2710,17 +2776,9 @@ netmap_attach(struct netmap_adapter *arg)
}
#endif /* linux */
-#ifdef __FreeBSD__
if_printf(ifp, "netmap queues/slots: TX %d/%d, RX %d/%d\n",
hwna->up.num_tx_rings, hwna->up.num_tx_desc,
hwna->up.num_rx_rings, hwna->up.num_rx_desc);
-#else
- D("success for %s tx %d/%d rx %d/%d queues/slots",
- hwna->up.name,
- hwna->up.num_tx_rings, hwna->up.num_tx_desc,
- hwna->up.num_rx_rings, hwna->up.num_rx_desc
- );
-#endif
return 0;
fail:
@@ -2788,16 +2846,19 @@ netmap_detach(struct ifnet *ifp)
NMG_LOCK();
netmap_disable_all_rings(ifp);
- if (!netmap_adapter_put(na)) {
- /* someone is still using the adapter,
- * tell them that the interface is gone
- */
- na->ifp = NULL;
- // XXX also clear NAF_NATIVE_ON ?
- na->na_flags &= ~NAF_NETMAP_ON;
- /* give them a chance to notice */
- netmap_enable_all_rings(ifp);
+ na->ifp = NULL;
+ na->na_flags &= ~NAF_NETMAP_ON;
+ /*
+ * if the netmap adapter is not native, somebody
+ * changed it, so we can not release it here.
+ * The NULL na->ifp will notify the new owner that
+ * the driver is gone.
+ */
+ if (na->na_flags & NAF_NATIVE) {
+ netmap_adapter_put(na);
}
+ /* give them a chance to notice */
+ netmap_enable_all_rings(ifp);
NMG_UNLOCK();
}
@@ -2824,6 +2885,7 @@ netmap_transmit(struct ifnet *ifp, struct mbuf *m)
struct mbq *q;
int space;
+ kring = &na->rx_rings[na->num_rx_rings];
// XXX [Linux] we do not need this lock
// if we follow the down/configure/up protocol -gl
// mtx_lock(&na->core_lock);
@@ -2834,7 +2896,6 @@ netmap_transmit(struct ifnet *ifp, struct mbuf *m)
goto done;
}
- kring = &na->rx_rings[na->num_rx_rings];
q = &kring->rx_queue;
// XXX reconsider long packets if we handle fragments
@@ -2872,7 +2933,7 @@ done:
if (m)
m_freem(m);
/* unconditionally wake up listeners */
- na->nm_notify(na, na->num_rx_rings, NR_RX, 0);
+ kring->nm_notify(kring, 0);
/* this is normally netmap_notify(), but for nics
* connected to a bridge it is netmap_bwrap_intr_notify(),
* that possibly forwards the frames through the switch
@@ -2953,7 +3014,7 @@ netmap_reset(struct netmap_adapter *na, enum txrx tx, u_int n,
* We do the wakeup here, but the ring is not yet reconfigured.
* However, we are under lock so there are no races.
*/
- na->nm_notify(na, n, tx, 0);
+ kring->nm_notify(kring, 0);
return kring->ring->slot;
}
@@ -2977,6 +3038,7 @@ netmap_common_irq(struct ifnet *ifp, u_int q, u_int *work_done)
{
struct netmap_adapter *na = NA(ifp);
struct netmap_kring *kring;
+ enum txrx t = (work_done ? NR_RX : NR_TX);
q &= NETMAP_RING_MASK;
@@ -2984,19 +3046,16 @@ netmap_common_irq(struct ifnet *ifp, u_int q, u_int *work_done)
RD(5, "received %s queue %d", work_done ? "RX" : "TX" , q);
}
- if (work_done) { /* RX path */
- if (q >= na->num_rx_rings)
- return; // not a physical queue
- kring = na->rx_rings + q;
+ if (q >= nma_get_nrings(na, t))
+ return; // not a physical queue
+
+ kring = NMR(na, t) + q;
+
+ if (t == NR_RX) {
kring->nr_kflags |= NKR_PENDINTR; // XXX atomic ?
- na->nm_notify(na, q, NR_RX, 0);
*work_done = 1; /* do not fire napi again */
- } else { /* TX path */
- if (q >= na->num_tx_rings)
- return; // not a physical queue
- kring = na->tx_rings + q;
- na->nm_notify(na, q, NR_TX, 0);
}
+ kring->nm_notify(kring, 0);
}
@@ -3057,7 +3116,7 @@ extern struct cdevsw netmap_cdevsw;
void
netmap_fini(void)
{
- // XXX destroy_bridges() ?
+ netmap_uninit_bridges();
if (netmap_dev)
destroy_dev(netmap_dev);
netmap_mem_fini();
@@ -3087,10 +3146,14 @@ netmap_init(void)
if (!netmap_dev)
goto fail;
- netmap_init_bridges();
+ error = netmap_init_bridges();
+ if (error)
+ goto fail;
+
#ifdef __FreeBSD__
nm_vi_init_index();
#endif
+
printf("netmap: loaded module\n");
return (0);
fail: