if_re_netmap.h revision 250184
1227614Sluigi/* 2227614Sluigi * Copyright (C) 2011 Luigi Rizzo. All rights reserved. 3227614Sluigi * 4227614Sluigi * Redistribution and use in source and binary forms, with or without 5227614Sluigi * modification, are permitted provided that the following conditions 6227614Sluigi * are met: 7227614Sluigi * 1. Redistributions of source code must retain the above copyright 8227614Sluigi * notice, this list of conditions and the following disclaimer. 9227614Sluigi * 2. Redistributions in binary form must reproduce the above copyright 10227614Sluigi * notice, this list of conditions and the following disclaimer in the 11227614Sluigi * documentation and/or other materials provided with the distribution. 12227614Sluigi * 13227614Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14227614Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15227614Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16227614Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17227614Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18227614Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19227614Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20227614Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21227614Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22227614Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23227614Sluigi * SUCH DAMAGE. 24227614Sluigi */ 25227614Sluigi 26227614Sluigi/* 27227614Sluigi * $FreeBSD: head/sys/dev/netmap/if_re_netmap.h 250184 2013-05-02 16:01:04Z luigi $ 28227614Sluigi * 29232238Sluigi * netmap support for "re" 30232238Sluigi * For details on netmap support please see ixgbe_netmap.h 31227614Sluigi */ 32227614Sluigi 33232238Sluigi 34227614Sluigi#include <net/netmap.h> 35227614Sluigi#include <sys/selinfo.h> 36227614Sluigi#include <vm/vm.h> 37227614Sluigi#include <vm/pmap.h> /* vtophys ? */ 38227614Sluigi#include <dev/netmap/netmap_kern.h> 39227614Sluigi 40227614Sluigi 41227614Sluigi/* 42227614Sluigi * wrapper to export locks to the generic code 43227614Sluigi * We should not use the tx/rx locks 44227614Sluigi */ 45227614Sluigistatic void 46231594Sluigire_netmap_lock_wrapper(struct ifnet *ifp, int what, u_int queueid) 47227614Sluigi{ 48231594Sluigi struct rl_softc *adapter = ifp->if_softc; 49227614Sluigi 50227614Sluigi switch (what) { 51227614Sluigi case NETMAP_CORE_LOCK: 52227614Sluigi RL_LOCK(adapter); 53227614Sluigi break; 54227614Sluigi case NETMAP_CORE_UNLOCK: 55227614Sluigi RL_UNLOCK(adapter); 56227614Sluigi break; 57227614Sluigi 58227614Sluigi case NETMAP_TX_LOCK: 59227614Sluigi case NETMAP_RX_LOCK: 60227614Sluigi case NETMAP_TX_UNLOCK: 61227614Sluigi case NETMAP_RX_UNLOCK: 62227614Sluigi D("invalid lock call %d, no tx/rx locks here", what); 63227614Sluigi break; 64227614Sluigi } 65227614Sluigi} 66227614Sluigi 67227614Sluigi 68227614Sluigi/* 69227614Sluigi * support for netmap register/unregisted. We are already under core lock. 70227614Sluigi * only called on the first register or the last unregister. 71227614Sluigi */ 72227614Sluigistatic int 73227614Sluigire_netmap_reg(struct ifnet *ifp, int onoff) 74227614Sluigi{ 75227614Sluigi struct rl_softc *adapter = ifp->if_softc; 76227614Sluigi struct netmap_adapter *na = NA(ifp); 77227614Sluigi int error = 0; 78227614Sluigi 79228276Sluigi if (na == NULL) 80227614Sluigi return EINVAL; 81227614Sluigi /* Tell the stack that the interface is no longer active */ 82227614Sluigi ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 83227614Sluigi 84227614Sluigi re_stop(adapter); 85227614Sluigi 86227614Sluigi if (onoff) { 87227614Sluigi ifp->if_capenable |= IFCAP_NETMAP; 88227614Sluigi 89228276Sluigi /* save if_transmit to restore it later */ 90227614Sluigi na->if_transmit = ifp->if_transmit; 91227614Sluigi ifp->if_transmit = netmap_start; 92227614Sluigi 93227614Sluigi re_init_locked(adapter); 94227614Sluigi 95227614Sluigi if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) == 0) { 96227614Sluigi error = ENOMEM; 97227614Sluigi goto fail; 98227614Sluigi } 99227614Sluigi } else { 100227614Sluigifail: 101227614Sluigi /* restore if_transmit */ 102227614Sluigi ifp->if_transmit = na->if_transmit; 103227614Sluigi ifp->if_capenable &= ~IFCAP_NETMAP; 104227614Sluigi re_init_locked(adapter); /* also enables intr */ 105227614Sluigi } 106228276Sluigi return (error); 107227614Sluigi} 108227614Sluigi 109227614Sluigi 110227614Sluigi/* 111227614Sluigi * Reconcile kernel and user view of the transmit ring. 112227614Sluigi */ 113227614Sluigistatic int 114231594Sluigire_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) 115227614Sluigi{ 116231594Sluigi struct rl_softc *sc = ifp->if_softc; 117227614Sluigi struct rl_txdesc *txd = sc->rl_ldata.rl_tx_desc; 118227614Sluigi struct netmap_adapter *na = NA(sc->rl_ifp); 119227614Sluigi struct netmap_kring *kring = &na->tx_rings[ring_nr]; 120227614Sluigi struct netmap_ring *ring = kring->ring; 121228276Sluigi int j, k, l, n, lim = kring->nkr_num_slots - 1; 122227614Sluigi 123227614Sluigi k = ring->cur; 124228276Sluigi if (k > lim) 125227614Sluigi return netmap_ring_reinit(kring); 126227614Sluigi 127227614Sluigi if (do_lock) 128227614Sluigi RL_LOCK(sc); 129227614Sluigi 130227614Sluigi /* Sync the TX descriptor list */ 131227614Sluigi bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag, 132227614Sluigi sc->rl_ldata.rl_tx_list_map, 133227614Sluigi BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 134227614Sluigi 135228276Sluigi /* XXX move after the transmissions */ 136227614Sluigi /* record completed transmissions */ 137228276Sluigi for (n = 0, l = sc->rl_ldata.rl_tx_considx; 138228276Sluigi l != sc->rl_ldata.rl_tx_prodidx; 139228276Sluigi n++, l = RL_TX_DESC_NXT(sc, l)) { 140227614Sluigi uint32_t cmdstat = 141228276Sluigi le32toh(sc->rl_ldata.rl_tx_list[l].rl_cmdstat); 142227614Sluigi if (cmdstat & RL_TDESC_STAT_OWN) 143227614Sluigi break; 144227614Sluigi } 145227614Sluigi if (n > 0) { 146228276Sluigi sc->rl_ldata.rl_tx_considx = l; 147227614Sluigi sc->rl_ldata.rl_tx_free += n; 148227614Sluigi kring->nr_hwavail += n; 149227614Sluigi } 150227614Sluigi 151232238Sluigi /* update avail to what the kernel knows */ 152227614Sluigi ring->avail = kring->nr_hwavail; 153250184Sluigi 154228276Sluigi j = kring->nr_hwcur; 155227614Sluigi if (j != k) { /* we have new packets to send */ 156228276Sluigi l = sc->rl_ldata.rl_tx_prodidx; 157231881Sluigi for (n = 0; j != k; n++) { 158227614Sluigi struct netmap_slot *slot = &ring->slot[j]; 159228276Sluigi struct rl_desc *desc = &sc->rl_ldata.rl_tx_list[l]; 160227614Sluigi int cmd = slot->len | RL_TDESC_CMD_EOF | 161227614Sluigi RL_TDESC_CMD_OWN | RL_TDESC_CMD_SOF ; 162229939Sluigi uint64_t paddr; 163229939Sluigi void *addr = PNMB(slot, &paddr); 164227614Sluigi int len = slot->len; 165227614Sluigi 166227614Sluigi if (addr == netmap_buffer_base || len > NETMAP_BUF_SIZE) { 167227614Sluigi if (do_lock) 168227614Sluigi RL_UNLOCK(sc); 169228276Sluigi // XXX what about prodidx ? 170227614Sluigi return netmap_ring_reinit(kring); 171227614Sluigi } 172250184Sluigi 173228276Sluigi if (l == lim) /* mark end of ring */ 174227614Sluigi cmd |= RL_TDESC_CMD_EOR; 175227614Sluigi 176227614Sluigi if (slot->flags & NS_BUF_CHANGED) { 177227614Sluigi desc->rl_bufaddr_lo = htole32(RL_ADDR_LO(paddr)); 178227614Sluigi desc->rl_bufaddr_hi = htole32(RL_ADDR_HI(paddr)); 179227614Sluigi /* buffer has changed, unload and reload map */ 180227614Sluigi netmap_reload_map(sc->rl_ldata.rl_tx_mtag, 181229939Sluigi txd[l].tx_dmamap, addr); 182227614Sluigi slot->flags &= ~NS_BUF_CHANGED; 183227614Sluigi } 184227614Sluigi slot->flags &= ~NS_REPORT; 185227614Sluigi desc->rl_cmdstat = htole32(cmd); 186227614Sluigi bus_dmamap_sync(sc->rl_ldata.rl_tx_mtag, 187228276Sluigi txd[l].tx_dmamap, BUS_DMASYNC_PREWRITE); 188227614Sluigi j = (j == lim) ? 0 : j + 1; 189228276Sluigi l = (l == lim) ? 0 : l + 1; 190227614Sluigi } 191228276Sluigi sc->rl_ldata.rl_tx_prodidx = l; 192232238Sluigi kring->nr_hwcur = k; /* the saved ring->cur */ 193232238Sluigi ring->avail -= n; // XXX see others 194227614Sluigi kring->nr_hwavail = ring->avail; 195227614Sluigi 196227614Sluigi bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag, 197227614Sluigi sc->rl_ldata.rl_tx_list_map, 198227614Sluigi BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); 199227614Sluigi 200227614Sluigi /* start ? */ 201227614Sluigi CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START); 202227614Sluigi } 203227614Sluigi if (do_lock) 204227614Sluigi RL_UNLOCK(sc); 205227614Sluigi return 0; 206227614Sluigi} 207227614Sluigi 208227614Sluigi 209227614Sluigi/* 210227614Sluigi * Reconcile kernel and user view of the receive ring. 211227614Sluigi */ 212227614Sluigistatic int 213231594Sluigire_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) 214227614Sluigi{ 215231594Sluigi struct rl_softc *sc = ifp->if_softc; 216227614Sluigi struct rl_rxdesc *rxd = sc->rl_ldata.rl_rx_desc; 217227614Sluigi struct netmap_adapter *na = NA(sc->rl_ifp); 218227614Sluigi struct netmap_kring *kring = &na->rx_rings[ring_nr]; 219227614Sluigi struct netmap_ring *ring = kring->ring; 220232238Sluigi int j, l, n, lim = kring->nkr_num_slots - 1; 221232238Sluigi int force_update = do_lock || kring->nr_kflags & NKR_PENDINTR; 222232238Sluigi u_int k = ring->cur, resvd = ring->reserved; 223227614Sluigi 224227614Sluigi k = ring->cur; 225228276Sluigi if (k > lim) 226227614Sluigi return netmap_ring_reinit(kring); 227227614Sluigi 228227614Sluigi if (do_lock) 229227614Sluigi RL_LOCK(sc); 230227614Sluigi /* XXX check sync modes */ 231227614Sluigi bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag, 232227614Sluigi sc->rl_ldata.rl_rx_list_map, 233227614Sluigi BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 234227614Sluigi 235227614Sluigi /* 236232238Sluigi * Import newly received packets into the netmap ring. 237232238Sluigi * j is an index in the netmap ring, l in the NIC ring. 238232238Sluigi * 239227614Sluigi * The device uses all the buffers in the ring, so we need 240227614Sluigi * another termination condition in addition to RL_RDESC_STAT_OWN 241227614Sluigi * cleared (all buffers could have it cleared. The easiest one 242227614Sluigi * is to limit the amount of data reported up to 'lim' 243227614Sluigi */ 244228276Sluigi l = sc->rl_ldata.rl_rx_prodidx; /* next pkt to check */ 245232238Sluigi j = netmap_idx_n2k(kring, l); /* the kring index */ 246232238Sluigi if (netmap_no_pendintr || force_update) { 247245579Sluigi uint16_t slot_flags = kring->nkr_slot_flags; 248245579Sluigi 249232238Sluigi for (n = kring->nr_hwavail; n < lim ; n++) { 250232238Sluigi struct rl_desc *cur_rx = &sc->rl_ldata.rl_rx_list[l]; 251232238Sluigi uint32_t rxstat = le32toh(cur_rx->rl_cmdstat); 252232238Sluigi uint32_t total_len; 253227614Sluigi 254232238Sluigi if ((rxstat & RL_RDESC_STAT_OWN) != 0) 255232238Sluigi break; 256232238Sluigi total_len = rxstat & sc->rl_rxlenmask; 257232238Sluigi /* XXX subtract crc */ 258232238Sluigi total_len = (total_len < 4) ? 0 : total_len - 4; 259232238Sluigi kring->ring->slot[j].len = total_len; 260245579Sluigi kring->ring->slot[j].flags = slot_flags; 261232238Sluigi /* sync was in re_newbuf() */ 262232238Sluigi bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag, 263232238Sluigi rxd[l].rx_dmamap, BUS_DMASYNC_POSTREAD); 264232238Sluigi j = (j == lim) ? 0 : j + 1; 265232238Sluigi l = (l == lim) ? 0 : l + 1; 266232238Sluigi } 267232238Sluigi if (n != kring->nr_hwavail) { 268232238Sluigi sc->rl_ldata.rl_rx_prodidx = l; 269232238Sluigi sc->rl_ifp->if_ipackets += n - kring->nr_hwavail; 270232238Sluigi kring->nr_hwavail = n; 271232238Sluigi } 272232238Sluigi kring->nr_kflags &= ~NKR_PENDINTR; 273227614Sluigi } 274227614Sluigi 275232238Sluigi /* skip past packets that userspace has released */ 276227614Sluigi j = kring->nr_hwcur; 277232238Sluigi if (resvd > 0) { 278232238Sluigi if (resvd + ring->avail >= lim + 1) { 279232238Sluigi D("XXX invalid reserve/avail %d %d", resvd, ring->avail); 280232238Sluigi ring->reserved = resvd = 0; // XXX panic... 281232238Sluigi } 282232238Sluigi k = (k >= resvd) ? k - resvd : k + lim + 1 - resvd; 283232238Sluigi } 284232238Sluigi if (j != k) { /* userspace has released some packets. */ 285232238Sluigi l = netmap_idx_k2n(kring, j); /* the NIC index */ 286231881Sluigi for (n = 0; j != k; n++) { 287227614Sluigi struct netmap_slot *slot = ring->slot + j; 288228276Sluigi struct rl_desc *desc = &sc->rl_ldata.rl_rx_list[l]; 289234225Sluigi int cmd = NETMAP_BUF_SIZE | RL_RDESC_CMD_OWN; 290229939Sluigi uint64_t paddr; 291229939Sluigi void *addr = PNMB(slot, &paddr); 292227614Sluigi 293227614Sluigi if (addr == netmap_buffer_base) { /* bad buf */ 294227614Sluigi if (do_lock) 295227614Sluigi RL_UNLOCK(sc); 296227614Sluigi return netmap_ring_reinit(kring); 297227614Sluigi } 298227614Sluigi 299228276Sluigi if (l == lim) /* mark end of ring */ 300227614Sluigi cmd |= RL_RDESC_CMD_EOR; 301227614Sluigi 302227614Sluigi slot->flags &= ~NS_REPORT; 303227614Sluigi if (slot->flags & NS_BUF_CHANGED) { 304232238Sluigi netmap_reload_map(sc->rl_ldata.rl_rx_mtag, 305232238Sluigi rxd[l].rx_dmamap, addr); 306227614Sluigi desc->rl_bufaddr_lo = htole32(RL_ADDR_LO(paddr)); 307227614Sluigi desc->rl_bufaddr_hi = htole32(RL_ADDR_HI(paddr)); 308227614Sluigi slot->flags &= ~NS_BUF_CHANGED; 309227614Sluigi } 310232238Sluigi desc->rl_cmdstat = htole32(cmd); 311227614Sluigi bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag, 312228276Sluigi rxd[l].rx_dmamap, BUS_DMASYNC_PREREAD); 313227614Sluigi j = (j == lim) ? 0 : j + 1; 314228276Sluigi l = (l == lim) ? 0 : l + 1; 315227614Sluigi } 316227614Sluigi kring->nr_hwavail -= n; 317227614Sluigi kring->nr_hwcur = k; 318227614Sluigi /* Flush the RX DMA ring */ 319227614Sluigi 320227614Sluigi bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag, 321227614Sluigi sc->rl_ldata.rl_rx_list_map, 322227614Sluigi BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); 323227614Sluigi } 324227614Sluigi /* tell userspace that there are new packets */ 325232238Sluigi ring->avail = kring->nr_hwavail - resvd; 326227614Sluigi if (do_lock) 327227614Sluigi RL_UNLOCK(sc); 328227614Sluigi return 0; 329227614Sluigi} 330227614Sluigi 331228276Sluigi/* 332228276Sluigi * Additional routines to init the tx and rx rings. 333228276Sluigi * In other drivers we do that inline in the main code. 334228276Sluigi */ 335227614Sluigistatic void 336227614Sluigire_netmap_tx_init(struct rl_softc *sc) 337250184Sluigi{ 338227614Sluigi struct rl_txdesc *txd; 339227614Sluigi struct rl_desc *desc; 340228276Sluigi int i, n; 341227614Sluigi struct netmap_adapter *na = NA(sc->rl_ifp); 342227614Sluigi struct netmap_slot *slot = netmap_reset(na, NR_TX, 0, 0); 343227614Sluigi 344227614Sluigi /* slot is NULL if we are not in netmap mode */ 345227614Sluigi if (!slot) 346227614Sluigi return; 347227614Sluigi /* in netmap mode, overwrite addresses and maps */ 348227614Sluigi txd = sc->rl_ldata.rl_tx_desc; 349227614Sluigi desc = sc->rl_ldata.rl_tx_list; 350228276Sluigi n = sc->rl_ldata.rl_tx_desc_cnt; 351227614Sluigi 352228276Sluigi /* l points in the netmap ring, i points in the NIC ring */ 353228276Sluigi for (i = 0; i < n; i++) { 354228276Sluigi uint64_t paddr; 355232238Sluigi int l = netmap_idx_n2k(&na->tx_rings[0], i); 356231881Sluigi void *addr = PNMB(slot + l, &paddr); 357227614Sluigi 358227614Sluigi desc[i].rl_bufaddr_lo = htole32(RL_ADDR_LO(paddr)); 359227614Sluigi desc[i].rl_bufaddr_hi = htole32(RL_ADDR_HI(paddr)); 360227614Sluigi netmap_load_map(sc->rl_ldata.rl_tx_mtag, 361229939Sluigi txd[i].tx_dmamap, addr); 362227614Sluigi } 363227614Sluigi} 364227614Sluigi 365227614Sluigistatic void 366227614Sluigire_netmap_rx_init(struct rl_softc *sc) 367227614Sluigi{ 368227614Sluigi struct netmap_adapter *na = NA(sc->rl_ifp); 369227614Sluigi struct netmap_slot *slot = netmap_reset(na, NR_RX, 0, 0); 370227614Sluigi struct rl_desc *desc = sc->rl_ldata.rl_rx_list; 371227614Sluigi uint32_t cmdstat; 372231796Sluigi int i, n, max_avail; 373227614Sluigi 374227614Sluigi if (!slot) 375227614Sluigi return; 376228276Sluigi n = sc->rl_ldata.rl_rx_desc_cnt; 377231796Sluigi /* 378231796Sluigi * Userspace owned hwavail packets before the reset, 379231796Sluigi * so the NIC that last hwavail descriptors of the ring 380231796Sluigi * are still owned by the driver (and keep one empty). 381231796Sluigi */ 382231796Sluigi max_avail = n - 1 - na->rx_rings[0].nr_hwavail; 383228276Sluigi for (i = 0; i < n; i++) { 384228276Sluigi void *addr; 385228276Sluigi uint64_t paddr; 386232238Sluigi int l = netmap_idx_n2k(&na->rx_rings[0], i); 387227614Sluigi 388229939Sluigi addr = PNMB(slot + l, &paddr); 389229939Sluigi 390229939Sluigi netmap_reload_map(sc->rl_ldata.rl_rx_mtag, 391230055Sluigi sc->rl_ldata.rl_rx_desc[i].rx_dmamap, addr); 392229939Sluigi bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag, 393229939Sluigi sc->rl_ldata.rl_rx_desc[i].rx_dmamap, BUS_DMASYNC_PREREAD); 394227614Sluigi desc[i].rl_bufaddr_lo = htole32(RL_ADDR_LO(paddr)); 395227614Sluigi desc[i].rl_bufaddr_hi = htole32(RL_ADDR_HI(paddr)); 396234225Sluigi cmdstat = NETMAP_BUF_SIZE; 397231796Sluigi if (i == n - 1) /* mark the end of ring */ 398227614Sluigi cmdstat |= RL_RDESC_CMD_EOR; 399231796Sluigi if (i < max_avail) 400228276Sluigi cmdstat |= RL_RDESC_CMD_OWN; 401228276Sluigi desc[i].rl_cmdstat = htole32(cmdstat); 402227614Sluigi } 403227614Sluigi} 404232238Sluigi 405232238Sluigi 406232238Sluigistatic void 407232238Sluigire_netmap_attach(struct rl_softc *sc) 408232238Sluigi{ 409232238Sluigi struct netmap_adapter na; 410232238Sluigi 411232238Sluigi bzero(&na, sizeof(na)); 412232238Sluigi 413232238Sluigi na.ifp = sc->rl_ifp; 414232238Sluigi na.separate_locks = 0; 415232238Sluigi na.num_tx_desc = sc->rl_ldata.rl_tx_desc_cnt; 416232238Sluigi na.num_rx_desc = sc->rl_ldata.rl_rx_desc_cnt; 417232238Sluigi na.nm_txsync = re_netmap_txsync; 418232238Sluigi na.nm_rxsync = re_netmap_rxsync; 419232238Sluigi na.nm_lock = re_netmap_lock_wrapper; 420232238Sluigi na.nm_register = re_netmap_reg; 421232238Sluigi netmap_attach(&na, 1); 422232238Sluigi} 423232238Sluigi/* end of file */ 424