diff options
| author | Luigi Rizzo <luigi@FreeBSD.org> | 2012-02-27 19:05:01 +0000 |
|---|---|---|
| committer | Luigi Rizzo <luigi@FreeBSD.org> | 2012-02-27 19:05:01 +0000 |
| commit | 64ae02c36579bad7d5e682589a0bc1023e359f9d (patch) | |
| tree | a547096f4399bc66370c43d717a40e4b79eb8401 /sys/dev/netmap/netmap.c | |
| parent | d7ccbd70099774d72fd45fa7a0b942c360dd9878 (diff) | |
Notes
Diffstat (limited to 'sys/dev/netmap/netmap.c')
| -rw-r--r-- | sys/dev/netmap/netmap.c | 397 |
1 files changed, 228 insertions, 169 deletions
diff --git a/sys/dev/netmap/netmap.c b/sys/dev/netmap/netmap.c index 8dc62d8fd4ef..ae9a599ee916 100644 --- a/sys/dev/netmap/netmap.c +++ b/sys/dev/netmap/netmap.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 Matteo Landi, Luigi Rizzo. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -9,7 +9,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -87,10 +87,10 @@ MALLOC_DEFINE(M_NETMAP, "netmap", "Network memory map"); /* * lock and unlock for the netmap memory allocator */ -#define NMA_LOCK() mtx_lock(&netmap_mem_d->nm_mtx); -#define NMA_UNLOCK() mtx_unlock(&netmap_mem_d->nm_mtx); +#define NMA_LOCK() mtx_lock(&nm_mem->nm_mtx); +#define NMA_UNLOCK() mtx_unlock(&nm_mem->nm_mtx); struct netmap_mem_d; -static struct netmap_mem_d *netmap_mem_d; /* Our memory allocator. */ +static struct netmap_mem_d *nm_mem; /* Our memory allocator. */ u_int netmap_total_buffers; char *netmap_buffer_base; /* address of an invalid buffer */ @@ -254,10 +254,10 @@ struct netmap_mem_d { /* Shorthand to compute a netmap interface offset. */ #define netmap_if_offset(v) \ - ((char *) (v) - (char *) netmap_mem_d->nm_buffer) + ((char *) (v) - (char *) nm_mem->nm_buffer) /* .. and get a physical address given a memory offset */ #define netmap_ofstophys(o) \ - (vtophys(netmap_mem_d->nm_buffer) + (o)) + (vtophys(nm_mem->nm_buffer) + (o)) /*------ netmap memory allocator -------*/ @@ -265,7 +265,7 @@ struct netmap_mem_d { * Request for a chunk of memory. * * Memory objects are arranged into a list, hence we need to walk this - * list until we find an object with the needed amount of data free. + * list until we find an object with the needed amount of data free. * This sounds like a completely inefficient implementation, but given * the fact that data allocation is done once, we can handle it * flawlessly. @@ -279,7 +279,7 @@ netmap_malloc(size_t size, __unused const char *msg) void *ret = NULL; NMA_LOCK(); - TAILQ_FOREACH(mem_obj, &netmap_mem_d->nm_molist, nmo_next) { + TAILQ_FOREACH(mem_obj, &nm_mem->nm_molist, nmo_next) { if (mem_obj->nmo_used != 0 || mem_obj->nmo_size < size) continue; @@ -295,7 +295,7 @@ netmap_malloc(size_t size, __unused const char *msg) mem_obj->nmo_size -= size; mem_obj->nmo_data = (char *) mem_obj->nmo_data + size; if (mem_obj->nmo_size == 0) { - TAILQ_REMOVE(&netmap_mem_d->nm_molist, mem_obj, + TAILQ_REMOVE(&nm_mem->nm_molist, mem_obj, nmo_next); free(mem_obj, M_NETMAP); } @@ -328,7 +328,7 @@ netmap_free(void *addr, const char *msg) } NMA_LOCK(); - TAILQ_FOREACH(cur, &netmap_mem_d->nm_molist, nmo_next) { + TAILQ_FOREACH(cur, &nm_mem->nm_molist, nmo_next) { if (cur->nmo_data == addr && cur->nmo_used) break; } @@ -345,7 +345,7 @@ netmap_free(void *addr, const char *msg) if present. */ prev = TAILQ_PREV(cur, netmap_mem_obj_h, nmo_next); if (prev && prev->nmo_used == 0) { - TAILQ_REMOVE(&netmap_mem_d->nm_molist, cur, nmo_next); + TAILQ_REMOVE(&nm_mem->nm_molist, cur, nmo_next); prev->nmo_size += cur->nmo_size; free(cur, M_NETMAP); cur = prev; @@ -354,7 +354,7 @@ netmap_free(void *addr, const char *msg) /* merge with the next one */ next = TAILQ_NEXT(cur, nmo_next); if (next && next->nmo_used == 0) { - TAILQ_REMOVE(&netmap_mem_d->nm_molist, next, nmo_next); + TAILQ_REMOVE(&nm_mem->nm_molist, next, nmo_next); cur->nmo_size += next->nmo_size; free(next, M_NETMAP); } @@ -374,21 +374,24 @@ netmap_if_new(const char *ifname, struct netmap_adapter *na) { struct netmap_if *nifp; struct netmap_ring *ring; + struct netmap_kring *kring; char *buff; - u_int i, len, ofs; - u_int n = na->num_queues + 1; /* shorthand, include stack queue */ + u_int i, len, ofs, numdesc; + u_int nrx = na->num_rx_queues + 1; /* shorthand, include stack queue */ + u_int ntx = na->num_tx_queues + 1; /* shorthand, include stack queue */ /* * the descriptor is followed inline by an array of offsets * to the tx and rx rings in the shared memory region. */ - len = sizeof(struct netmap_if) + 2 * n * sizeof(ssize_t); + len = sizeof(struct netmap_if) + (nrx + ntx) * sizeof(ssize_t); nifp = netmap_if_malloc(len); if (nifp == NULL) return (NULL); /* initialize base fields */ - *(int *)(uintptr_t)&nifp->ni_num_queues = na->num_queues; + *(int *)(uintptr_t)&nifp->ni_rx_queues = na->num_rx_queues; + *(int *)(uintptr_t)&nifp->ni_tx_queues = na->num_tx_queues; strncpy(nifp->ni_name, ifname, IFNAMSIZ); (na->refcount)++; /* XXX atomic ? we are under lock */ @@ -396,16 +399,15 @@ netmap_if_new(const char *ifname, struct netmap_adapter *na) goto final; /* - * If this is the first instance, allocate the shadow rings and - * buffers for this card (one for each hw queue, one for the host). + * First instance. Allocate the netmap rings + * (one for each hw queue, one pair for the host). * The rings are contiguous, but have variable size. * The entire block is reachable at - * na->tx_rings[0].ring + * na->tx_rings[0] */ - - len = n * (2 * sizeof(struct netmap_ring) + - (na->num_tx_desc + na->num_rx_desc) * - sizeof(struct netmap_slot) ); + len = (ntx + nrx) * sizeof(struct netmap_ring) + + (ntx * na->num_tx_desc + nrx * na->num_rx_desc) * + sizeof(struct netmap_slot); buff = netmap_ring_malloc(len); if (buff == NULL) { D("failed to allocate %d bytes for %s shadow ring", @@ -415,9 +417,8 @@ error: netmap_if_free(nifp); return (NULL); } - /* do we have the bufers ? we are in need of num_tx_desc buffers for - * each tx ring and num_tx_desc buffers for each rx ring. */ - len = n * (na->num_tx_desc + na->num_rx_desc); + /* Check whether we have enough buffers */ + len = ntx * na->num_tx_desc + nrx * na->num_rx_desc; NMA_LOCK(); if (nm_buf_pool.free < len) { NMA_UNLOCK(); @@ -429,11 +430,7 @@ error: * and initialize the rings. We are under NMA_LOCK(). */ ofs = 0; - for (i = 0; i < n; i++) { - struct netmap_kring *kring; - int numdesc; - - /* Transmit rings */ + for (i = 0; i < ntx; i++) { /* Transmit rings */ kring = &na->tx_rings[i]; numdesc = na->num_tx_desc; bzero(kring, sizeof(*kring)); @@ -459,8 +456,9 @@ error: ofs += sizeof(struct netmap_ring) + numdesc * sizeof(struct netmap_slot); + } - /* Receive rings */ + for (i = 0; i < nrx; i++) { /* Receive rings */ kring = &na->rx_rings[i]; numdesc = na->num_rx_desc; bzero(kring, sizeof(*kring)); @@ -480,21 +478,21 @@ error: numdesc * sizeof(struct netmap_slot); } NMA_UNLOCK(); - for (i = 0; i < n+1; i++) { - // XXX initialize the selrecord structs. - } + // XXX initialize the selrecord structs. + final: /* * fill the slots for the rx and tx queues. They contain the offset * between the ring and nifp, so the information is usable in * userspace to reach the ring from the nifp. */ - for (i = 0; i < n; i++) { - char *base = (char *)nifp; + for (i = 0; i < ntx; i++) { *(ssize_t *)(uintptr_t)&nifp->ring_ofs[i] = - (char *)na->tx_rings[i].ring - base; - *(ssize_t *)(uintptr_t)&nifp->ring_ofs[i+n] = - (char *)na->rx_rings[i].ring - base; + (char *)na->tx_rings[i].ring - (char *)nifp; + } + for (i = 0; i < nrx; i++) { + *(ssize_t *)(uintptr_t)&nifp->ring_ofs[i+ntx] = + (char *)na->rx_rings[i].ring - (char *)nifp; } return (nifp); } @@ -532,17 +530,17 @@ netmap_memory_init(void) ); if (buf) break; - } + } if (buf == NULL) return (ENOMEM); sz += extra_sz; - netmap_mem_d = malloc(sizeof(struct netmap_mem_d), M_NETMAP, + nm_mem = malloc(sizeof(struct netmap_mem_d), M_NETMAP, M_WAITOK | M_ZERO); - mtx_init(&netmap_mem_d->nm_mtx, "netmap memory allocator lock", NULL, + mtx_init(&nm_mem->nm_mtx, "netmap memory allocator lock", NULL, MTX_DEF); - TAILQ_INIT(&netmap_mem_d->nm_molist); - netmap_mem_d->nm_buffer = buf; - netmap_mem_d->nm_totalsize = sz; + TAILQ_INIT(&nm_mem->nm_molist); + nm_mem->nm_buffer = buf; + nm_mem->nm_totalsize = sz; /* * A buffer takes 2k, a slot takes 8 bytes + ring overhead, @@ -550,24 +548,24 @@ netmap_memory_init(void) * the memory for the rings, and the rest for the buffers, * and be sure we never run out. */ - netmap_mem_d->nm_size = sz/200; - netmap_mem_d->nm_buf_start = - (netmap_mem_d->nm_size + PAGE_SIZE - 1) & ~(PAGE_SIZE-1); - netmap_mem_d->nm_buf_len = sz - netmap_mem_d->nm_buf_start; + nm_mem->nm_size = sz/200; + nm_mem->nm_buf_start = + (nm_mem->nm_size + PAGE_SIZE - 1) & ~(PAGE_SIZE-1); + nm_mem->nm_buf_len = sz - nm_mem->nm_buf_start; - nm_buf_pool.base = netmap_mem_d->nm_buffer; - nm_buf_pool.base += netmap_mem_d->nm_buf_start; + nm_buf_pool.base = nm_mem->nm_buffer; + nm_buf_pool.base += nm_mem->nm_buf_start; netmap_buffer_base = nm_buf_pool.base; D("netmap_buffer_base %p (offset %d)", - netmap_buffer_base, (int)netmap_mem_d->nm_buf_start); + netmap_buffer_base, (int)nm_mem->nm_buf_start); /* number of buffers, they all start as free */ netmap_total_buffers = nm_buf_pool.total_buffers = - netmap_mem_d->nm_buf_len / NETMAP_BUF_SIZE; + nm_mem->nm_buf_len / NETMAP_BUF_SIZE; nm_buf_pool.bufsize = NETMAP_BUF_SIZE; D("Have %d MB, use %dKB for rings, %d buffers at %p", - (sz >> 20), (int)(netmap_mem_d->nm_size >> 10), + (sz >> 20), (int)(nm_mem->nm_size >> 10), nm_buf_pool.total_buffers, nm_buf_pool.base); /* allocate and initialize the bitmap. Entry 0 is considered @@ -583,10 +581,10 @@ netmap_memory_init(void) mem_obj = malloc(sizeof(struct netmap_mem_obj), M_NETMAP, M_WAITOK | M_ZERO); - TAILQ_INSERT_HEAD(&netmap_mem_d->nm_molist, mem_obj, nmo_next); + TAILQ_INSERT_HEAD(&nm_mem->nm_molist, mem_obj, nmo_next); mem_obj->nmo_used = 0; - mem_obj->nmo_size = netmap_mem_d->nm_size; - mem_obj->nmo_data = netmap_mem_d->nm_buffer; + mem_obj->nmo_size = nm_mem->nm_size; + mem_obj->nmo_data = nm_mem->nm_buffer; return (0); } @@ -603,9 +601,9 @@ netmap_memory_fini(void) { struct netmap_mem_obj *mem_obj; - while (!TAILQ_EMPTY(&netmap_mem_d->nm_molist)) { - mem_obj = TAILQ_FIRST(&netmap_mem_d->nm_molist); - TAILQ_REMOVE(&netmap_mem_d->nm_molist, mem_obj, nmo_next); + while (!TAILQ_EMPTY(&nm_mem->nm_molist)) { + mem_obj = TAILQ_FIRST(&nm_mem->nm_molist); + TAILQ_REMOVE(&nm_mem->nm_molist, mem_obj, nmo_next); if (mem_obj->nmo_used == 1) { printf("netmap: leaked %d bytes at %p\n", (int)mem_obj->nmo_size, @@ -613,9 +611,9 @@ netmap_memory_fini(void) } free(mem_obj, M_NETMAP); } - contigfree(netmap_mem_d->nm_buffer, netmap_mem_d->nm_totalsize, M_NETMAP); + contigfree(nm_mem->nm_buffer, nm_mem->nm_totalsize, M_NETMAP); // XXX mutex_destroy(nm_mtx); - free(netmap_mem_d, M_NETMAP); + free(nm_mem, M_NETMAP); } /*------------- end of memory allocator -----------------*/ @@ -647,7 +645,7 @@ netmap_dtor_locked(void *data) na->refcount--; if (na->refcount <= 0) { /* last instance */ - u_int i; + u_int i, j, lim; D("deleting last netmap instance for %s", ifp->if_xname); /* @@ -669,24 +667,22 @@ netmap_dtor_locked(void *data) /* Wake up any sleeping threads. netmap_poll will * then return POLLERR */ - for (i = 0; i < na->num_queues + 2; i++) { + for (i = 0; i < na->num_tx_queues + 1; i++) selwakeuppri(&na->tx_rings[i].si, PI_NET); + for (i = 0; i < na->num_rx_queues + 1; i++) selwakeuppri(&na->rx_rings[i].si, PI_NET); - } + selwakeuppri(&na->tx_si, PI_NET); + selwakeuppri(&na->rx_si, PI_NET); /* release all buffers */ NMA_LOCK(); - for (i = 0; i < na->num_queues + 1; i++) { - int j, lim; - struct netmap_ring *ring; - - ND("tx queue %d", i); - ring = na->tx_rings[i].ring; + for (i = 0; i < na->num_tx_queues + 1; i++) { + struct netmap_ring *ring = na->tx_rings[i].ring; lim = na->tx_rings[i].nkr_num_slots; for (j = 0; j < lim; j++) netmap_free_buf(nifp, ring->slot[j].buf_idx); - - ND("rx queue %d", i); - ring = na->rx_rings[i].ring; + } + for (i = 0; i < na->num_rx_queues + 1; i++) { + struct netmap_ring *ring = na->rx_rings[i].ring; lim = na->rx_rings[i].nkr_num_slots; for (j = 0; j < lim; j++) netmap_free_buf(nifp, ring->slot[j].buf_idx); @@ -708,7 +704,7 @@ netmap_dtor(void *data) na->nm_lock(ifp, NETMAP_REG_LOCK, 0); netmap_dtor_locked(data); - na->nm_lock(ifp, NETMAP_REG_UNLOCK, 0); + na->nm_lock(ifp, NETMAP_REG_UNLOCK, 0); if_rele(ifp); bzero(priv, sizeof(*priv)); /* XXX for safety */ @@ -758,7 +754,7 @@ netmap_mmap(__unused struct cdev *dev, static void netmap_sync_to_host(struct netmap_adapter *na) { - struct netmap_kring *kring = &na->tx_rings[na->num_queues]; + struct netmap_kring *kring = &na->tx_rings[na->num_tx_queues]; struct netmap_ring *ring = kring->ring; struct mbuf *head = NULL, *tail = NULL, *m; u_int k, n, lim = kring->nkr_num_slots - 1; @@ -818,31 +814,37 @@ netmap_sync_to_host(struct netmap_adapter *na) static void netmap_sync_from_host(struct netmap_adapter *na, struct thread *td) { - struct netmap_kring *kring = &na->rx_rings[na->num_queues]; + struct netmap_kring *kring = &na->rx_rings[na->num_rx_queues]; struct netmap_ring *ring = kring->ring; - int error = 1, delta; - u_int k = ring->cur, lim = kring->nkr_num_slots; + u_int j, n, lim = kring->nkr_num_slots; + u_int k = ring->cur, resvd = ring->reserved; na->nm_lock(na->ifp, NETMAP_CORE_LOCK, 0); - if (k >= lim) /* bad value */ - goto done; - delta = k - kring->nr_hwcur; - if (delta < 0) - delta += lim; - kring->nr_hwavail -= delta; - if (kring->nr_hwavail < 0) /* error */ - goto done; + if (k >= lim) { + netmap_ring_reinit(kring); + return; + } + /* new packets are already set in nr_hwavail */ + /* skip past packets that userspace has released */ + j = kring->nr_hwcur; + if (resvd > 0) { + if (resvd + ring->avail >= lim + 1) { + D("XXX invalid reserve/avail %d %d", resvd, ring->avail); + ring->reserved = resvd = 0; // XXX panic... + } + k = (k >= resvd) ? k - resvd : k + lim - resvd; + } + if (j != k) { + n = k >= j ? k - j : k + lim - j; + kring->nr_hwavail -= n; kring->nr_hwcur = k; - error = 0; - k = ring->avail = kring->nr_hwavail; + } + k = ring->avail = kring->nr_hwavail - resvd; if (k == 0 && td) selrecord(td, &kring->si); if (k && (netmap_verbose & NM_VERB_HOST)) D("%d pkts from stack", k); -done: na->nm_lock(na->ifp, NETMAP_CORE_UNLOCK, 0); - if (error) - netmap_ring_reinit(kring); } @@ -907,13 +909,13 @@ netmap_ring_reinit(struct netmap_kring *kring) } if (errors) { int pos = kring - kring->na->tx_rings; - int n = kring->na->num_queues + 2; + int n = kring->na->num_tx_queues + 1; D("total %d errors", errors); errors++; D("%s %s[%d] reinit, cur %d -> %d avail %d -> %d", kring->na->ifp->if_xname, - pos < n ? "TX" : "RX", pos < n ? pos : pos - n, + pos < n ? "TX" : "RX", pos < n ? pos : pos - n, ring->cur, kring->nr_hwcur, ring->avail, kring->nr_hwavail); ring->cur = kring->nr_hwcur; @@ -933,10 +935,13 @@ netmap_set_ringid(struct netmap_priv_d *priv, u_int ringid) struct ifnet *ifp = priv->np_ifp; struct netmap_adapter *na = NA(ifp); u_int i = ringid & NETMAP_RING_MASK; - /* first time we don't lock */ + /* initially (np_qfirst == np_qlast) we don't want to lock */ int need_lock = (priv->np_qfirst != priv->np_qlast); + int lim = na->num_rx_queues; - if ( (ringid & NETMAP_HW_RING) && i >= na->num_queues) { + if (na->num_tx_queues > lim) + lim = na->num_tx_queues; + if ( (ringid & NETMAP_HW_RING) && i >= lim) { D("invalid ring id %d", i); return (EINVAL); } @@ -944,14 +949,14 @@ netmap_set_ringid(struct netmap_priv_d *priv, u_int ringid) na->nm_lock(ifp, NETMAP_CORE_LOCK, 0); priv->np_ringid = ringid; if (ringid & NETMAP_SW_RING) { - priv->np_qfirst = na->num_queues; - priv->np_qlast = na->num_queues + 1; + priv->np_qfirst = NETMAP_SW_RING; + priv->np_qlast = 0; } else if (ringid & NETMAP_HW_RING) { priv->np_qfirst = i; priv->np_qlast = i + 1; } else { priv->np_qfirst = 0; - priv->np_qlast = na->num_queues; + priv->np_qlast = NETMAP_HW_RING ; } priv->np_txpoll = (ringid & NETMAP_NO_TX_POLL) ? 0 : 1; if (need_lock) @@ -962,8 +967,7 @@ netmap_set_ringid(struct netmap_priv_d *priv, u_int ringid) D("ringid %s set to HW RING %d", ifp->if_xname, priv->np_qfirst); else - D("ringid %s set to all %d HW RINGS", ifp->if_xname, - priv->np_qlast); + D("ringid %s set to all %d HW RINGS", ifp->if_xname, lim); return 0; } @@ -989,7 +993,7 @@ netmap_ioctl(__unused struct cdev *dev, u_long cmd, caddr_t data, struct nmreq *nmr = (struct nmreq *) data; struct netmap_adapter *na; int error; - u_int i; + u_int i, lim; struct netmap_if *nifp; CURVNET_SET(TD_TO_VNET(td)); @@ -1004,22 +1008,36 @@ netmap_ioctl(__unused struct cdev *dev, u_long cmd, caddr_t data, switch (cmd) { case NIOCGINFO: /* return capabilities etc */ /* memsize is always valid */ - nmr->nr_memsize = netmap_mem_d->nm_totalsize; + nmr->nr_memsize = nm_mem->nm_totalsize; nmr->nr_offset = 0; - nmr->nr_numrings = 0; - nmr->nr_numslots = 0; + nmr->nr_rx_rings = nmr->nr_tx_rings = 0; + nmr->nr_rx_slots = nmr->nr_tx_slots = 0; + if (nmr->nr_version != NETMAP_API) { + D("API mismatch got %d have %d", + nmr->nr_version, NETMAP_API); + nmr->nr_version = NETMAP_API; + error = EINVAL; + break; + } if (nmr->nr_name[0] == '\0') /* just get memory info */ break; error = get_ifp(nmr->nr_name, &ifp); /* get a refcount */ if (error) break; na = NA(ifp); /* retrieve netmap_adapter */ - nmr->nr_numrings = na->num_queues; - nmr->nr_numslots = na->num_tx_desc; + nmr->nr_rx_rings = na->num_rx_queues; + nmr->nr_tx_rings = na->num_tx_queues; + nmr->nr_rx_slots = na->num_rx_desc; + nmr->nr_tx_slots = na->num_tx_desc; if_rele(ifp); /* return the refcount */ break; case NIOCREGIF: + if (nmr->nr_version != NETMAP_API) { + nmr->nr_version = NETMAP_API; + error = EINVAL; + break; + } if (priv != NULL) { /* thread already registered */ error = netmap_set_ringid(priv, nmr->nr_ringid); break; @@ -1095,9 +1113,11 @@ error: } /* return the offset of the netmap_if object */ - nmr->nr_numrings = na->num_queues; - nmr->nr_numslots = na->num_tx_desc; - nmr->nr_memsize = netmap_mem_d->nm_totalsize; + nmr->nr_rx_rings = na->num_rx_queues; + nmr->nr_tx_rings = na->num_tx_queues; + nmr->nr_rx_slots = na->num_rx_desc; + nmr->nr_tx_slots = na->num_tx_desc; + nmr->nr_memsize = nm_mem->nm_totalsize; nmr->nr_offset = netmap_if_offset(nifp); break; @@ -1120,17 +1140,19 @@ error: } ifp = priv->np_ifp; /* we have a reference */ na = NA(ifp); /* retrieve netmap adapter */ - - if (priv->np_qfirst == na->num_queues) { - /* queues to/from host */ + if (priv->np_qfirst == NETMAP_SW_RING) { /* host rings */ if (cmd == NIOCTXSYNC) netmap_sync_to_host(na); else netmap_sync_from_host(na, NULL); break; } + /* find the last ring to scan */ + lim = priv->np_qlast; + if (lim == NETMAP_HW_RING) + lim = (cmd == NIOCTXSYNC) ? na->num_tx_queues : na->num_rx_queues; - for (i = priv->np_qfirst; i < priv->np_qlast; i++) { + for (i = priv->np_qfirst; i < lim; i++) { if (cmd == NIOCTXSYNC) { struct netmap_kring *kring = &na->tx_rings[i]; if (netmap_verbose & NM_VERB_TXSYNC) @@ -1195,6 +1217,7 @@ netmap_poll(__unused struct cdev *dev, int events, struct thread *td) struct ifnet *ifp; struct netmap_kring *kring; u_int core_lock, i, check_all, want_tx, want_rx, revents = 0; + u_int lim_tx, lim_rx; enum {NO_CL, NEED_CL, LOCKED_CL }; /* see below */ if (devfs_get_cdevpriv((void **)&priv) != 0 || priv == NULL) @@ -1212,17 +1235,18 @@ netmap_poll(__unused struct cdev *dev, int events, struct thread *td) na = NA(ifp); /* retrieve netmap adapter */ + lim_tx = na->num_tx_queues; + lim_rx = na->num_rx_queues; /* how many queues we are scanning */ - i = priv->np_qfirst; - if (i == na->num_queues) { /* from/to host */ + if (priv->np_qfirst == NETMAP_SW_RING) { if (priv->np_txpoll || want_tx) { /* push any packets up, then we are always ready */ - kring = &na->tx_rings[i]; + kring = &na->tx_rings[lim_tx]; netmap_sync_to_host(na); revents |= want_tx; } if (want_rx) { - kring = &na->rx_rings[i]; + kring = &na->rx_rings[lim_rx]; if (kring->ring->avail == 0) netmap_sync_from_host(na, td); if (kring->ring->avail > 0) { @@ -1253,7 +1277,7 @@ netmap_poll(__unused 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 = (i + 1 != priv->np_qlast); + check_all = (priv->np_qlast == NETMAP_HW_RING) && (lim_tx > 1 || lim_rx > 1); /* * core_lock indicates what to do with the core lock. @@ -1270,25 +1294,29 @@ netmap_poll(__unused struct cdev *dev, int events, struct thread *td) * LOCKED_CL core lock is set, so we need to release it. */ core_lock = (check_all || !na->separate_locks) ? NEED_CL : NO_CL; + if (priv->np_qlast != NETMAP_HW_RING) { + lim_tx = lim_rx = priv->np_qlast; + } + /* * We start with a lock free round which is good if we have * data available. If this fails, then lock and call the sync * routines. */ - for (i = priv->np_qfirst; want_rx && i < priv->np_qlast; i++) { - kring = &na->rx_rings[i]; - if (kring->ring->avail > 0) { - revents |= want_rx; - want_rx = 0; /* also breaks the loop */ - } + for (i = priv->np_qfirst; want_rx && i < lim_rx; i++) { + kring = &na->rx_rings[i]; + if (kring->ring->avail > 0) { + revents |= want_rx; + want_rx = 0; /* also breaks the loop */ } - for (i = priv->np_qfirst; want_tx && i < priv->np_qlast; i++) { - kring = &na->tx_rings[i]; - if (kring->ring->avail > 0) { - revents |= want_tx; - want_tx = 0; /* also breaks the loop */ - } + } + for (i = priv->np_qfirst; want_tx && i < lim_tx; i++) { + kring = &na->tx_rings[i]; + if (kring->ring->avail > 0) { + revents |= want_tx; + want_tx = 0; /* also breaks the loop */ } + } /* * If we to push packets out (priv->np_txpoll) or want_tx is @@ -1296,7 +1324,7 @@ netmap_poll(__unused struct cdev *dev, int events, struct thread *td) * to avoid that the tx rings stall). */ if (priv->np_txpoll || want_tx) { - for (i = priv->np_qfirst; i < priv->np_qlast; i++) { + for (i = priv->np_qfirst; i < lim_tx; i++) { kring = &na->tx_rings[i]; /* * Skip the current ring if want_tx == 0 @@ -1340,7 +1368,7 @@ netmap_poll(__unused struct cdev *dev, int events, struct thread *td) * Do it on all rings because otherwise we starve. */ if (want_rx) { - for (i = priv->np_qfirst; i < priv->np_qlast; i++) { + for (i = priv->np_qfirst; i < lim_rx; i++) { kring = &na->rx_rings[i]; if (core_lock == NEED_CL) { na->nm_lock(ifp, NETMAP_CORE_LOCK, 0); @@ -1364,12 +1392,11 @@ netmap_poll(__unused struct cdev *dev, int events, struct thread *td) na->nm_lock(ifp, NETMAP_RX_UNLOCK, i); } } - if (check_all && revents == 0) { - i = na->num_queues + 1; /* the global queue */ + if (check_all && revents == 0) { /* signal on the global queue */ if (want_tx) - selrecord(td, &na->tx_rings[i].si); + selrecord(td, &na->tx_si); if (want_rx) - selrecord(td, &na->rx_rings[i].si); + selrecord(td, &na->rx_si); } if (core_lock == LOCKED_CL) na->nm_lock(ifp, NETMAP_CORE_UNLOCK, 0); @@ -1430,28 +1457,37 @@ netmap_lock_wrapper(struct ifnet *dev, int what, u_int queueid) * kring N is for the host stack queue * kring N+1 is only used for the selinfo for all queues. * Return 0 on success, ENOMEM otherwise. + * + * na->num_tx_queues can be set for cards with different tx/rx setups */ int netmap_attach(struct netmap_adapter *na, int num_queues) { - int n = num_queues + 2; - int size = sizeof(*na) + 2 * n * sizeof(struct netmap_kring); + int i, n, size; void *buf; struct ifnet *ifp = na->ifp; - int i; if (ifp == NULL) { D("ifp not set, giving up"); return EINVAL; } + /* clear other fields ? */ na->refcount = 0; - na->num_queues = num_queues; + if (na->num_tx_queues == 0) + na->num_tx_queues = num_queues; + na->num_rx_queues = num_queues; + /* on each direction we have N+1 resources + * 0..n-1 are the hardware rings + * n is the ring attached to the stack. + */ + n = na->num_rx_queues + na->num_tx_queues + 2; + size = sizeof(*na) + n * sizeof(struct netmap_kring); buf = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO); if (buf) { WNA(ifp) = buf; na->tx_rings = (void *)((char *)buf + sizeof(*na)); - na->rx_rings = na->tx_rings + n; + na->rx_rings = na->tx_rings + na->num_tx_queues + 1; na->buff_size = NETMAP_BUF_SIZE; bcopy(na, buf, sizeof(*na)); ifp->if_capabilities |= IFCAP_NETMAP; @@ -1460,11 +1496,17 @@ netmap_attach(struct netmap_adapter *na, int num_queues) if (na->nm_lock == NULL) na->nm_lock = netmap_lock_wrapper; mtx_init(&na->core_lock, "netmap core lock", NULL, MTX_DEF); - for (i = 0 ; i < num_queues; i++) + for (i = 0 ; i < na->num_tx_queues + 1; i++) mtx_init(&na->tx_rings[i].q_lock, "netmap txq lock", NULL, MTX_DEF); - for (i = 0 ; i < num_queues; i++) + for (i = 0 ; i < na->num_rx_queues + 1; i++) mtx_init(&na->rx_rings[i].q_lock, "netmap rxq lock", NULL, MTX_DEF); } +#ifdef linux + D("netdev_ops %p", ifp->netdev_ops); + /* prepare a clone of the netdev ops */ + na->nm_ndo = *ifp->netdev_ops; + na->nm_ndo.ndo_start_xmit = netmap_start_linux; +#endif D("%s for %s", buf ? "ok" : "failed", ifp->if_xname); return (buf ? 0 : ENOMEM); @@ -1484,10 +1526,16 @@ netmap_detach(struct ifnet *ifp) if (!na) return; - for (i = 0; i < na->num_queues + 2; i++) { + for (i = 0; i < na->num_tx_queues + 1; i++) { knlist_destroy(&na->tx_rings[i].si.si_note); + mtx_destroy(&na->tx_rings[i].q_lock); + } + for (i = 0; i < na->num_rx_queues + 1; i++) { knlist_destroy(&na->rx_rings[i].si.si_note); + mtx_destroy(&na->rx_rings[i].q_lock); } + knlist_destroy(&na->tx_si.si_note); + knlist_destroy(&na->rx_si.si_note); bzero(na, sizeof(*na)); WNA(ifp) = NULL; free(na, M_DEVBUF); @@ -1503,7 +1551,7 @@ int netmap_start(struct ifnet *ifp, struct mbuf *m) { struct netmap_adapter *na = NA(ifp); - struct netmap_kring *kring = &na->rx_rings[na->num_queues]; + struct netmap_kring *kring = &na->rx_rings[na->num_rx_queues]; u_int i, len = MBUF_LEN(m); int error = EBUSY, lim = kring->nkr_num_slots - 1; struct netmap_slot *slot; @@ -1516,8 +1564,8 @@ netmap_start(struct ifnet *ifp, struct mbuf *m) D("stack ring %s full\n", ifp->if_xname); goto done; /* no space */ } - if (len > na->buff_size) { - D("drop packet size %d > %d", len, na->buff_size); + if (len > NETMAP_BUF_SIZE) { + D("drop packet size %d > %d", len, NETMAP_BUF_SIZE); goto done; /* too long for us */ } @@ -1530,7 +1578,7 @@ netmap_start(struct ifnet *ifp, struct mbuf *m) slot->len = len; kring->nr_hwavail++; if (netmap_verbose & NM_VERB_HOST) - D("wake up host ring %s %d", na->ifp->if_xname, na->num_queues); + D("wake up host ring %s %d", na->ifp->if_xname, na->num_rx_queues); selwakeuppri(&kring->si, PI_NET); error = 0; done: @@ -1556,21 +1604,21 @@ netmap_reset(struct netmap_adapter *na, enum txrx tx, int n, u_int new_cur) { struct netmap_kring *kring; - struct netmap_ring *ring; int new_hwofs, lim; if (na == NULL) return NULL; /* no netmap support here */ if (!(na->ifp->if_capenable & IFCAP_NETMAP)) return NULL; /* nothing to reinitialize */ - kring = tx == NR_TX ? na->tx_rings + n : na->rx_rings + n; - ring = kring->ring; - lim = kring->nkr_num_slots - 1; - if (tx == NR_TX) + if (tx == NR_TX) { + kring = na->tx_rings + n; new_hwofs = kring->nr_hwcur - new_cur; - else + } else { + kring = na->rx_rings + n; new_hwofs = kring->nr_hwcur + kring->nr_hwavail - new_cur; + } + lim = kring->nkr_num_slots - 1; if (new_hwofs > lim) new_hwofs -= lim + 1; @@ -1583,11 +1631,12 @@ netmap_reset(struct netmap_adapter *na, enum txrx tx, int n, tx == NR_TX ? "TX" : "RX", n); /* + * Wakeup on the individual and global lock * We do the wakeup here, but the ring is not yet reconfigured. * However, we are under lock so there are no races. */ selwakeuppri(&kring->si, PI_NET); - selwakeuppri(&kring[na->num_queues + 1 - n].si, PI_NET); + selwakeuppri(tx == NR_TX ? &na->tx_si : &na->rx_si, PI_NET); return kring->ring->slot; } @@ -1603,38 +1652,48 @@ netmap_reset(struct netmap_adapter *na, enum txrx tx, int n, * lock(i); wake(i); unlock(i) * N rings, separate locks: * lock(i); wake(i); unlock(i); lock(core) wake(N+1) unlock(core) + * work_done is non-null on the RX path. */ int netmap_rx_irq(struct ifnet *ifp, int q, int *work_done) { struct netmap_adapter *na; struct netmap_kring *r; + NM_SELINFO_T *main_wq; if (!(ifp->if_capenable & IFCAP_NETMAP)) return 0; na = NA(ifp); - r = work_done ? na->rx_rings : na->tx_rings; + if (work_done) { /* RX path */ + r = na->rx_rings + q; + r->nr_kflags |= NKR_PENDINTR; + main_wq = (na->num_rx_queues > 1) ? &na->tx_si : NULL; + } else { /* tx path */ + r = na->tx_rings + q; + main_wq = (na->num_tx_queues > 1) ? &na->rx_si : NULL; + work_done = &q; /* dummy */ + } if (na->separate_locks) { - mtx_lock(&r[q].q_lock); - selwakeuppri(&r[q].si, PI_NET); - mtx_unlock(&r[q].q_lock); - if (na->num_queues > 1) { + mtx_lock(&r->q_lock); + selwakeuppri(&r->si, PI_NET); + mtx_unlock(&r->q_lock); + if (main_wq) { mtx_lock(&na->core_lock); - selwakeuppri(&r[na->num_queues + 1].si, PI_NET); + selwakeuppri(main_wq, PI_NET); mtx_unlock(&na->core_lock); } } else { mtx_lock(&na->core_lock); - selwakeuppri(&r[q].si, PI_NET); - if (na->num_queues > 1) - selwakeuppri(&r[na->num_queues + 1].si, PI_NET); + selwakeuppri(&r->si, PI_NET); + if (main_wq) + selwakeuppri(main_wq, PI_NET); mtx_unlock(&na->core_lock); } - if (work_done) *work_done = 1; /* do not fire napi again */ return 1; } + static struct cdevsw netmap_cdevsw = { .d_version = D_VERSION, .d_name = "netmap", @@ -1666,7 +1725,7 @@ netmap_init(void) return (error); } printf("netmap: loaded module with %d Mbytes\n", - (int)(netmap_mem_d->nm_totalsize >> 20)); + (int)(nm_mem->nm_totalsize >> 20)); netmap_dev = make_dev(&netmap_cdevsw, 0, UID_ROOT, GID_WHEEL, 0660, "netmap"); return (error); |
