diff options
| author | Luigi Rizzo <luigi@FreeBSD.org> | 2012-05-17 15:02:51 +0000 |
|---|---|---|
| committer | Luigi Rizzo <luigi@FreeBSD.org> | 2012-05-17 15:02:51 +0000 |
| commit | ff78552d0cd5733e887481436794860af5c87275 (patch) | |
| tree | 75820565dca0a52fecc351a34300ef7b41b3fff9 /sys/dev | |
| parent | 5da0d12406866662f5214acb681caee06bb7b97f (diff) | |
Notes
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/netmap/if_em_netmap.h | 194 | ||||
| -rw-r--r-- | sys/dev/netmap/if_igb_netmap.h | 213 | ||||
| -rw-r--r-- | sys/dev/netmap/if_lem_netmap.h | 212 | ||||
| -rw-r--r-- | sys/dev/netmap/if_re_netmap.h | 181 | ||||
| -rw-r--r-- | sys/dev/netmap/ixgbe_netmap.h | 316 | ||||
| -rw-r--r-- | sys/dev/netmap/netmap.c | 987 | ||||
| -rw-r--r-- | sys/dev/netmap/netmap_kern.h | 101 |
7 files changed, 893 insertions, 1311 deletions
diff --git a/sys/dev/netmap/if_em_netmap.h b/sys/dev/netmap/if_em_netmap.h index 8b0c7e88b79b..8a3893e3b1a4 100644 --- a/sys/dev/netmap/if_em_netmap.h +++ b/sys/dev/netmap/if_em_netmap.h @@ -25,49 +25,25 @@ /* * $FreeBSD$ - * $Id: if_em_netmap.h 9802 2011-12-02 18:42:37Z luigi $ + * $Id: if_em_netmap.h 10627 2012-02-23 19:37:15Z luigi $ * - * netmap changes for if_em. + * netmap support for em. * - * For structure and details on the individual functions please see - * ixgbe_netmap.h + * For more details on netmap support please see ixgbe_netmap.h */ + #include <net/netmap.h> #include <sys/selinfo.h> #include <vm/vm.h> #include <vm/pmap.h> /* vtophys ? */ #include <dev/netmap/netmap_kern.h> + static void em_netmap_block_tasks(struct adapter *); static void em_netmap_unblock_tasks(struct adapter *); -static int em_netmap_reg(struct ifnet *, int onoff); -static int em_netmap_txsync(struct ifnet *, u_int, int); -static int em_netmap_rxsync(struct ifnet *, u_int, int); -static void em_netmap_lock_wrapper(struct ifnet *, int, u_int); -static void -em_netmap_attach(struct adapter *adapter) -{ - struct netmap_adapter na; - - bzero(&na, sizeof(na)); - na.ifp = adapter->ifp; - na.separate_locks = 1; - na.num_tx_desc = adapter->num_tx_desc; - na.num_rx_desc = adapter->num_rx_desc; - na.nm_txsync = em_netmap_txsync; - na.nm_rxsync = em_netmap_rxsync; - na.nm_lock = em_netmap_lock_wrapper; - na.nm_register = em_netmap_reg; - netmap_attach(&na, adapter->num_queues); -} - - -/* - * wrapper to export locks to the generic code - */ static void em_netmap_lock_wrapper(struct ifnet *ifp, int what, u_int queueid) { @@ -137,8 +113,9 @@ em_netmap_unblock_tasks(struct adapter *adapter) } } + /* - * register-unregister routine + * Register/unregister routine */ static int em_netmap_reg(struct ifnet *ifp, int onoff) @@ -170,7 +147,7 @@ em_netmap_reg(struct ifnet *ifp, int onoff) } } else { fail: - /* restore if_transmit */ + /* return to non-netmap mode */ ifp->if_transmit = na->if_transmit; ifp->if_capenable &= ~IFCAP_NETMAP; em_init_locked(adapter); /* also enable intr */ @@ -179,18 +156,19 @@ fail: return (error); } + /* - * Reconcile hardware and user view of the transmit ring. + * Reconcile kernel and user view of the transmit ring. */ static int em_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) { struct adapter *adapter = ifp->if_softc; struct tx_ring *txr = &adapter->tx_rings[ring_nr]; - struct netmap_adapter *na = NA(adapter->ifp); + struct netmap_adapter *na = NA(ifp); struct netmap_kring *kring = &na->tx_rings[ring_nr]; struct netmap_ring *ring = kring->ring; - int j, k, l, n = 0, lim = kring->nkr_num_slots - 1; + u_int j, k, l, n = 0, lim = kring->nkr_num_slots - 1; /* generate an interrupt approximately every half ring */ int report_frequency = kring->nkr_num_slots >> 1; @@ -204,18 +182,17 @@ em_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map, BUS_DMASYNC_POSTREAD); - /* check for new packets to send. - * j indexes the netmap ring, l indexes the nic ring, and - * j = kring->nr_hwcur, l = E1000_TDT (not tracked), - * j == (l + kring->nkr_hwofs) % ring_size + /* + * Process new packets to send. j is the current index in the + * netmap ring, l is the corresponding index in the NIC ring. */ j = kring->nr_hwcur; - if (j != k) { /* we have packets to send */ - l = j - kring->nkr_hwofs; - if (l < 0) - l += lim + 1; - while (j != k) { + if (j != k) { /* we have new packets to send */ + l = netmap_idx_k2n(kring, j); + for (n = 0; j != k; n++) { + /* slot is the current slot in the netmap ring */ struct netmap_slot *slot = &ring->slot[j]; + /* curr is the current slot in the nic ring */ struct e1000_tx_desc *curr = &txr->tx_base[l]; struct em_buffer *txbuf = &txr->tx_buffers[l]; int flags = ((slot->flags & NS_REPORT) || @@ -223,7 +200,8 @@ em_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) E1000_TXD_CMD_RS : 0; uint64_t paddr; void *addr = PNMB(slot, &paddr); - int len = slot->len; + u_int len = slot->len; + if (addr == netmap_buffer_base || len > NETMAP_BUF_SIZE) { if (do_lock) EM_TX_UNLOCK(txr); @@ -231,26 +209,21 @@ em_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) } slot->flags &= ~NS_REPORT; - curr->upper.data = 0; - curr->lower.data = - htole32(adapter->txd_cmd | len | - (E1000_TXD_CMD_EOP | flags) ); if (slot->flags & NS_BUF_CHANGED) { curr->buffer_addr = htole64(paddr); /* buffer has changed, reload map */ netmap_reload_map(txr->txtag, txbuf->map, addr); slot->flags &= ~NS_BUF_CHANGED; } - + curr->upper.data = 0; + curr->lower.data = htole32(adapter->txd_cmd | len | + (E1000_TXD_CMD_EOP | flags) ); bus_dmamap_sync(txr->txtag, txbuf->map, BUS_DMASYNC_PREWRITE); j = (j == lim) ? 0 : j + 1; l = (l == lim) ? 0 : l + 1; - n++; } - kring->nr_hwcur = k; - - /* decrease avail by number of sent packets */ + kring->nr_hwcur = k; /* the saved ring->cur */ kring->nr_hwavail -= n; bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map, @@ -262,9 +235,9 @@ em_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) if (n == 0 || kring->nr_hwavail < 1) { int delta; - /* record completed transmissions using THD. */ + /* record completed transmissions using TDH */ l = E1000_READ_REG(&adapter->hw, E1000_TDH(ring_nr)); - if (l >= kring->nkr_num_slots) { /* XXX can happen */ + if (l >= kring->nkr_num_slots) { /* XXX can it happen ? */ D("TDH wrap %d", l); l -= kring->nkr_num_slots; } @@ -277,7 +250,7 @@ em_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) kring->nr_hwavail += delta; } } - /* update avail to what the hardware knows */ + /* update avail to what the kernel knows */ ring->avail = kring->nr_hwavail; if (do_lock) @@ -285,6 +258,7 @@ em_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) return 0; } + /* * Reconcile kernel and user view of the receive ring. */ @@ -293,10 +267,12 @@ em_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) { struct adapter *adapter = ifp->if_softc; struct rx_ring *rxr = &adapter->rx_rings[ring_nr]; - struct netmap_adapter *na = NA(adapter->ifp); + struct netmap_adapter *na = NA(ifp); struct netmap_kring *kring = &na->rx_rings[ring_nr]; struct netmap_ring *ring = kring->ring; - int j, k, l, n, lim = kring->nkr_num_slots - 1; + u_int j, l, n, lim = kring->nkr_num_slots - 1; + int force_update = do_lock || kring->nr_kflags & NKR_PENDINTR; + u_int k = ring->cur, resvd = ring->reserved; k = ring->cur; if (k > lim) @@ -304,53 +280,51 @@ em_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) if (do_lock) EM_RX_LOCK(rxr); + /* XXX check sync modes */ bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - /* import newly received packets into the netmap ring. - * j is an index in the netmap ring, l in the NIC ring, and - * j = (kring->nr_hwcur + kring->nr_hwavail) % ring_size - * l = rxr->next_to_check; - * and - * j == (l + kring->nkr_hwofs) % ring_size + /* + * Import newly received packets into the netmap ring. + * j is an index in the netmap ring, l in the NIC ring. */ l = rxr->next_to_check; - j = l + kring->nkr_hwofs; - /* here nkr_hwofs can be negative so must check for j < 0 */ - if (j < 0) - j += lim + 1; - else if (j > lim) - j -= lim + 1; - for (n = 0; ; n++) { - struct e1000_rx_desc *curr = &rxr->rx_base[l]; + j = netmap_idx_n2k(kring, l); + if (netmap_no_pendintr || force_update) { + for (n = 0; ; n++) { + struct e1000_rx_desc *curr = &rxr->rx_base[l]; + uint32_t staterr = le32toh(curr->status); - if ((curr->status & E1000_RXD_STAT_DD) == 0) - break; - ring->slot[j].len = le16toh(curr->length); - bus_dmamap_sync(rxr->rxtag, rxr->rx_buffers[l].map, - BUS_DMASYNC_POSTREAD); - j = (j == lim) ? 0 : j + 1; - /* make sure next_to_refresh follows next_to_check */ - rxr->next_to_refresh = l; // XXX - l = (l == lim) ? 0 : l + 1; - } - if (n) { - rxr->next_to_check = l; - kring->nr_hwavail += n; + if ((staterr & E1000_RXD_STAT_DD) == 0) + break; + ring->slot[j].len = le16toh(curr->length); + bus_dmamap_sync(rxr->rxtag, rxr->rx_buffers[l].map, + BUS_DMASYNC_POSTREAD); + j = (j == lim) ? 0 : j + 1; + /* make sure next_to_refresh follows next_to_check */ + rxr->next_to_refresh = l; // XXX + l = (l == lim) ? 0 : l + 1; + } + if (n) { /* update the state variables */ + rxr->next_to_check = l; + kring->nr_hwavail += n; + } + kring->nr_kflags &= ~NKR_PENDINTR; } - /* skip past packets that userspace has already processed */ - j = kring->nr_hwcur; - if (j != k) { /* userspace has read some packets. */ - n = 0; - l = j - kring->nkr_hwofs; /* NIC ring index */ - /* here nkr_hwofs can be negative so check for l > lim */ - if (l < 0) - l += lim + 1; - else if (l > lim) - l -= lim + 1; - while (j != k) { + /* skip past packets that userspace has released */ + j = kring->nr_hwcur; /* netmap ring index */ + 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 + 1 - resvd; + } + if (j != k) { /* userspace has released some packets. */ + l = netmap_idx_k2n(kring, j); /* NIC ring index */ + for (n = 0; j != k; n++) { struct netmap_slot *slot = &ring->slot[j]; struct e1000_rx_desc *curr = &rxr->rx_base[l]; struct em_buffer *rxbuf = &rxr->rx_buffers[l]; @@ -363,20 +337,17 @@ em_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) return netmap_ring_reinit(kring); } - curr->status = 0; if (slot->flags & NS_BUF_CHANGED) { curr->buffer_addr = htole64(paddr); /* buffer has changed, reload map */ netmap_reload_map(rxr->rxtag, rxbuf->map, addr); slot->flags &= ~NS_BUF_CHANGED; } - + curr->status = 0; bus_dmamap_sync(rxr->rxtag, rxbuf->map, BUS_DMASYNC_PREREAD); - j = (j == lim) ? 0 : j + 1; l = (l == lim) ? 0 : l + 1; - n++; } kring->nr_hwavail -= n; kring->nr_hwcur = k; @@ -390,8 +361,29 @@ em_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) E1000_WRITE_REG(&adapter->hw, E1000_RDT(rxr->me), l); } /* tell userspace that there are new packets */ - ring->avail = kring->nr_hwavail ; + ring->avail = kring->nr_hwavail - resvd; if (do_lock) EM_RX_UNLOCK(rxr); return 0; } + + +static void +em_netmap_attach(struct adapter *adapter) +{ + struct netmap_adapter na; + + bzero(&na, sizeof(na)); + + na.ifp = adapter->ifp; + na.separate_locks = 1; + na.num_tx_desc = adapter->num_tx_desc; + na.num_rx_desc = adapter->num_rx_desc; + na.nm_txsync = em_netmap_txsync; + na.nm_rxsync = em_netmap_rxsync; + na.nm_lock = em_netmap_lock_wrapper; + na.nm_register = em_netmap_reg; + netmap_attach(&na, adapter->num_queues); +} + +/* end of file */ diff --git a/sys/dev/netmap/if_igb_netmap.h b/sys/dev/netmap/if_igb_netmap.h index 0ee455f3e9e5..8ba13ee6af61 100644 --- a/sys/dev/netmap/if_igb_netmap.h +++ b/sys/dev/netmap/if_igb_netmap.h @@ -25,42 +25,19 @@ /* * $FreeBSD$ - * $Id: if_igb_netmap.h 9802 2011-12-02 18:42:37Z luigi $ + * $Id: if_igb_netmap.h 10627 2012-02-23 19:37:15Z luigi $ * - * netmap modifications for igb - * contribured by Ahmed Kooli + * Netmap support for igb, partly contributed by Ahmed Kooli + * For details on netmap support please see ixgbe_netmap.h */ + #include <net/netmap.h> #include <sys/selinfo.h> #include <vm/vm.h> #include <vm/pmap.h> /* vtophys ? */ #include <dev/netmap/netmap_kern.h> -static int igb_netmap_reg(struct ifnet *, int onoff); -static int igb_netmap_txsync(struct ifnet *, u_int, int); -static int igb_netmap_rxsync(struct ifnet *, u_int, int); -static void igb_netmap_lock_wrapper(struct ifnet *, int, u_int); - - -static void -igb_netmap_attach(struct adapter *adapter) -{ - struct netmap_adapter na; - - bzero(&na, sizeof(na)); - - na.ifp = adapter->ifp; - na.separate_locks = 1; - na.num_tx_desc = adapter->num_tx_desc; - na.num_rx_desc = adapter->num_rx_desc; - na.nm_txsync = igb_netmap_txsync; - na.nm_rxsync = igb_netmap_rxsync; - na.nm_lock = igb_netmap_lock_wrapper; - na.nm_register = igb_netmap_reg; - netmap_attach(&na, adapter->num_queues); -} - /* * wrapper to export locks to the generic code @@ -95,8 +72,7 @@ igb_netmap_lock_wrapper(struct ifnet *ifp, int what, u_int queueid) /* - * support for netmap register/unregisted. We are already under core lock. - * only called on the first init or the last unregister. + * register-unregister routine */ static int igb_netmap_reg(struct ifnet *ifp, int onoff) @@ -106,7 +82,7 @@ igb_netmap_reg(struct ifnet *ifp, int onoff) int error = 0; if (na == NULL) - return EINVAL; + return EINVAL; /* no netmap support here */ igb_disable_intr(adapter); @@ -116,7 +92,6 @@ igb_netmap_reg(struct ifnet *ifp, int onoff) if (onoff) { ifp->if_capenable |= IFCAP_NETMAP; - /* save if_transmit to restore it later */ na->if_transmit = ifp->if_transmit; ifp->if_transmit = netmap_start; @@ -130,7 +105,7 @@ fail: /* restore if_transmit */ ifp->if_transmit = na->if_transmit; ifp->if_capenable &= ~IFCAP_NETMAP; - igb_init_locked(adapter); /* also enables intr */ + igb_init_locked(adapter); /* also enable intr */ } return (error); } @@ -144,10 +119,10 @@ igb_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) { struct adapter *adapter = ifp->if_softc; struct tx_ring *txr = &adapter->tx_rings[ring_nr]; - struct netmap_adapter *na = NA(adapter->ifp); + struct netmap_adapter *na = NA(ifp); struct netmap_kring *kring = &na->tx_rings[ring_nr]; struct netmap_ring *ring = kring->ring; - int j, k, l, n = 0, lim = kring->nkr_num_slots - 1; + u_int j, k, l, n = 0, lim = kring->nkr_num_slots - 1; /* generate an interrupt approximately every half ring */ int report_frequency = kring->nkr_num_slots >> 1; @@ -161,31 +136,31 @@ igb_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map, BUS_DMASYNC_POSTREAD); - /* update avail to what the hardware knows */ - ring->avail = kring->nr_hwavail; - - j = kring->nr_hwcur; /* netmap ring index */ + /* check for new packets to send. + * j indexes the netmap ring, l indexes the nic ring, and + * j = kring->nr_hwcur, l = E1000_TDT (not tracked), + * j == (l + kring->nkr_hwofs) % ring_size + */ + j = kring->nr_hwcur; if (j != k) { /* we have new packets to send */ - u32 olinfo_status = 0; - - l = j - kring->nkr_hwofs; /* NIC ring index */ - if (l < 0) - l += lim + 1; /* 82575 needs the queue index added */ - if (adapter->hw.mac.type == e1000_82575) - olinfo_status |= txr->me << 4; + u32 olinfo_status = + (adapter->hw.mac.type == e1000_82575) ? (txr->me << 4) : 0; - while (j != k) { + l = netmap_idx_k2n(kring, j); + for (n = 0; j != k; n++) { + /* slot is the current slot in the netmap ring */ struct netmap_slot *slot = &ring->slot[j]; - struct igb_tx_buffer *txbuf = &txr->tx_buffers[l]; + /* curr is the current slot in the nic ring */ union e1000_adv_tx_desc *curr = (union e1000_adv_tx_desc *)&txr->tx_base[l]; - uint64_t paddr; - void *addr = PNMB(slot, &paddr); + struct igb_tx_buffer *txbuf = &txr->tx_buffers[l]; int flags = ((slot->flags & NS_REPORT) || j == 0 || j == report_frequency) ? E1000_ADVTXD_DCMD_RS : 0; - int len = slot->len; + uint64_t paddr; + void *addr = PNMB(slot, &paddr); + u_int len = slot->len; if (addr == netmap_buffer_base || len > NETMAP_BUF_SIZE) { if (do_lock) @@ -194,8 +169,13 @@ igb_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) } slot->flags &= ~NS_REPORT; - // XXX do we need to set the address ? + if (slot->flags & NS_BUF_CHANGED) { + /* buffer has changed, reload map */ + netmap_reload_map(txr->txtag, txbuf->map, addr); + slot->flags &= ~NS_BUF_CHANGED; + } curr->read.buffer_addr = htole64(paddr); + // XXX check olinfo and cmd_type_len curr->read.olinfo_status = htole32(olinfo_status | (len<< E1000_ADVTXD_PAYLEN_SHIFT)); @@ -204,23 +184,14 @@ igb_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) E1000_ADVTXD_DCMD_IFCS | E1000_ADVTXD_DCMD_DEXT | E1000_ADVTXD_DCMD_EOP | flags); - if (slot->flags & NS_BUF_CHANGED) { - /* buffer has changed, reload map */ - netmap_reload_map(txr->txtag, txbuf->map, addr); - slot->flags &= ~NS_BUF_CHANGED; - } bus_dmamap_sync(txr->txtag, txbuf->map, BUS_DMASYNC_PREWRITE); j = (j == lim) ? 0 : j + 1; l = (l == lim) ? 0 : l + 1; - n++; } - kring->nr_hwcur = k; - - /* decrease avail by number of sent packets */ + kring->nr_hwcur = k; /* the saved ring->cur */ kring->nr_hwavail -= n; - ring->avail = kring->nr_hwavail; /* Set the watchdog XXX ? */ txr->queue_status = IGB_QUEUE_WORKING; @@ -231,23 +202,28 @@ igb_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) E1000_WRITE_REG(&adapter->hw, E1000_TDT(txr->me), l); } + if (n == 0 || kring->nr_hwavail < 1) { int delta; - /* record completed transmission using TDH */ + /* record completed transmissions using TDH */ l = E1000_READ_REG(&adapter->hw, E1000_TDH(ring_nr)); - if (l >= kring->nkr_num_slots) /* XXX can it happen ? */ + if (l >= kring->nkr_num_slots) { /* XXX can it happen ? */ + D("TDH wrap %d", l); l -= kring->nkr_num_slots; + } delta = l - txr->next_to_clean; if (delta) { - /* new tx were completed */ + /* some completed, increment hwavail. */ if (delta < 0) delta += kring->nkr_num_slots; txr->next_to_clean = l; kring->nr_hwavail += delta; - ring->avail = kring->nr_hwavail; } } + /* update avail to what the kernel knows */ + ring->avail = kring->nr_hwavail; + if (do_lock) IGB_TX_UNLOCK(txr); return 0; @@ -262,10 +238,12 @@ igb_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) { struct adapter *adapter = ifp->if_softc; struct rx_ring *rxr = &adapter->rx_rings[ring_nr]; - struct netmap_adapter *na = NA(adapter->ifp); + struct netmap_adapter *na = NA(ifp); struct netmap_kring *kring = &na->rx_rings[ring_nr]; struct netmap_ring *ring = kring->ring; - int j, k, l, n, lim = kring->nkr_num_slots - 1; + u_int j, l, n, lim = kring->nkr_num_slots - 1; + int force_update = do_lock || kring->nr_kflags & NKR_PENDINTR; + u_int k = ring->cur, resvd = ring->reserved; k = ring->cur; if (k > lim) @@ -274,45 +252,48 @@ igb_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) if (do_lock) IGB_RX_LOCK(rxr); - /* Sync the ring. */ + /* XXX check sync modes */ bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + /* + * import newly received packets into the netmap ring. + * j is an index in the netmap ring, l in the NIC ring. + */ l = rxr->next_to_check; - j = l + kring->nkr_hwofs; - if (j > lim) - j -= lim + 1; - for (n = 0; ; n++) { - union e1000_adv_rx_desc *curr = &rxr->rx_base[l]; - uint32_t staterr = le32toh(curr->wb.upper.status_error); + j = netmap_idx_n2k(kring, l); + if (netmap_no_pendintr || force_update) { + for (n = 0; ; n++) { + union e1000_adv_rx_desc *curr = &rxr->rx_base[l]; + uint32_t staterr = le32toh(curr->wb.upper.status_error); - if ((staterr & E1000_RXD_STAT_DD) == 0) - break; - ring->slot[j].len = le16toh(curr->wb.upper.length); - - bus_dmamap_sync(rxr->ptag, - rxr->rx_buffers[l].pmap, BUS_DMASYNC_POSTREAD); - j = (j == lim) ? 0 : j + 1; - l = (l == lim) ? 0 : l + 1; - } - if (n) { - rxr->next_to_check = l; - kring->nr_hwavail += n; + if ((staterr & E1000_RXD_STAT_DD) == 0) + break; + ring->slot[j].len = le16toh(curr->wb.upper.length); + bus_dmamap_sync(rxr->ptag, + rxr->rx_buffers[l].pmap, BUS_DMASYNC_POSTREAD); + j = (j == lim) ? 0 : j + 1; + l = (l == lim) ? 0 : l + 1; + } + if (n) { /* update the state variables */ + rxr->next_to_check = l; + kring->nr_hwavail += n; + } + kring->nr_kflags &= ~NKR_PENDINTR; } - /* skip past packets that userspace has already processed, - * making them available for reception. - * advance nr_hwcur and issue a bus_dmamap_sync on the - * buffers so it is safe to write to them. - * Also increase nr_hwavail - */ - j = kring->nr_hwcur; - l = kring->nr_hwcur - kring->nkr_hwofs; - if (l < 0) - l += lim + 1; - if (j != k) { /* userspace has read some packets. */ - n = 0; - while (j != k) { + /* skip past packets that userspace has released */ + j = kring->nr_hwcur; /* netmap ring index */ + 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 + 1 - resvd; + } + if (j != k) { /* userspace has released some packets. */ + l = netmap_idx_k2n(kring, j); + for (n = 0; j != k; n++) { struct netmap_slot *slot = ring->slot + j; union e1000_adv_rx_desc *curr = &rxr->rx_base[l]; struct igb_rx_buf *rxbuf = rxr->rx_buffers + l; @@ -325,33 +306,51 @@ igb_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) return netmap_ring_reinit(kring); } - curr->wb.upper.status_error = 0; - curr->read.pkt_addr = htole64(paddr); if (slot->flags & NS_BUF_CHANGED) { netmap_reload_map(rxr->ptag, rxbuf->pmap, addr); slot->flags &= ~NS_BUF_CHANGED; } - + curr->read.pkt_addr = htole64(paddr); + curr->wb.upper.status_error = 0; bus_dmamap_sync(rxr->ptag, rxbuf->pmap, BUS_DMASYNC_PREREAD); - j = (j == lim) ? 0 : j + 1; l = (l == lim) ? 0 : l + 1; - n++; } kring->nr_hwavail -= n; - kring->nr_hwcur = ring->cur; + kring->nr_hwcur = k; bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); - /* IMPORTANT: we must leave one free slot in the ring, + /* + * IMPORTANT: we must leave one free slot in the ring, * so move l back by one unit */ l = (l == 0) ? lim : l - 1; E1000_WRITE_REG(&adapter->hw, E1000_RDT(rxr->me), l); } /* tell userspace that there are new packets */ - ring->avail = kring->nr_hwavail ; + ring->avail = kring->nr_hwavail - resvd; if (do_lock) IGB_RX_UNLOCK(rxr); return 0; } + + +static void +igb_netmap_attach(struct adapter *adapter) +{ + struct netmap_adapter na; + + bzero(&na, sizeof(na)); + + na.ifp = adapter->ifp; + na.separate_locks = 1; + na.num_tx_desc = adapter->num_tx_desc; + na.num_rx_desc = adapter->num_rx_desc; + na.nm_txsync = igb_netmap_txsync; + na.nm_rxsync = igb_netmap_rxsync; + na.nm_lock = igb_netmap_lock_wrapper; + na.nm_register = igb_netmap_reg; + netmap_attach(&na, adapter->num_queues); +} +/* end of file */ diff --git a/sys/dev/netmap/if_lem_netmap.h b/sys/dev/netmap/if_lem_netmap.h index 5ce93b914847..9f24580a9e8d 100644 --- a/sys/dev/netmap/if_lem_netmap.h +++ b/sys/dev/netmap/if_lem_netmap.h @@ -23,14 +23,14 @@ * SUCH DAMAGE. */ + /* * $FreeBSD$ - * $Id: if_lem_netmap.h 9802 2011-12-02 18:42:37Z luigi $ + * $Id: if_lem_netmap.h 10627 2012-02-23 19:37:15Z luigi $ * - * netmap support for if_lem.c + * netmap support for "lem" * - * For structure and details on the individual functions please see - * ixgbe_netmap.h + * For details on netmap support please see ixgbe_netmap.h */ #include <net/netmap.h> @@ -39,32 +39,6 @@ #include <vm/pmap.h> /* vtophys ? */ #include <dev/netmap/netmap_kern.h> -static int lem_netmap_reg(struct ifnet *, int onoff); -static int lem_netmap_txsync(struct ifnet *, u_int, int); -static int lem_netmap_rxsync(struct ifnet *, u_int, int); -static void lem_netmap_lock_wrapper(struct ifnet *, int, u_int); - - -SYSCTL_NODE(_dev, OID_AUTO, lem, CTLFLAG_RW, 0, "lem card"); - -static void -lem_netmap_attach(struct adapter *adapter) -{ - struct netmap_adapter na; - - bzero(&na, sizeof(na)); - - na.ifp = adapter->ifp; - na.separate_locks = 1; - na.num_tx_desc = adapter->num_tx_desc; - na.num_rx_desc = adapter->num_rx_desc; - na.nm_txsync = lem_netmap_txsync; - na.nm_rxsync = lem_netmap_rxsync; - na.nm_lock = lem_netmap_lock_wrapper; - na.nm_register = lem_netmap_reg; - netmap_attach(&na, 1); -} - static void lem_netmap_lock_wrapper(struct ifnet *ifp, int what, u_int ringid) @@ -96,7 +70,7 @@ lem_netmap_lock_wrapper(struct ifnet *ifp, int what, u_int ringid) /* - * Register/unregister routine + * Register/unregister */ static int lem_netmap_reg(struct ifnet *ifp, int onoff) @@ -113,7 +87,6 @@ lem_netmap_reg(struct ifnet *ifp, int onoff) /* Tell the stack that the interface is no longer active */ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); - /* lem_netmap_block_tasks(adapter); */ #ifndef EM_LEGACY_IRQ // XXX do we need this ? taskqueue_block(adapter->tq); taskqueue_drain(adapter->tq, &adapter->rxtx_task); @@ -122,9 +95,6 @@ lem_netmap_reg(struct ifnet *ifp, int onoff) if (onoff) { ifp->if_capenable |= IFCAP_NETMAP; - /* save if_transmit to restore it when exiting. - * XXX what about if_start and if_qflush ? - */ na->if_transmit = ifp->if_transmit; ifp->if_transmit = netmap_start; @@ -135,10 +105,10 @@ lem_netmap_reg(struct ifnet *ifp, int onoff) } } else { fail: - /* restore non-netmap mode */ + /* return to non-netmap mode */ ifp->if_transmit = na->if_transmit; ifp->if_capenable &= ~IFCAP_NETMAP; - lem_init_locked(adapter); /* also enables intr */ + lem_init_locked(adapter); /* also enable intr */ } #ifndef EM_LEGACY_IRQ @@ -156,14 +126,15 @@ static int lem_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) { struct adapter *adapter = ifp->if_softc; - struct netmap_adapter *na = NA(adapter->ifp); - struct netmap_kring *kring = &na->tx_rings[0]; + struct netmap_adapter *na = NA(ifp); + struct netmap_kring *kring = &na->tx_rings[ring_nr]; struct netmap_ring *ring = kring->ring; - int j, k, l, n = 0, lim = kring->nkr_num_slots - 1; + u_int j, k, l, n = 0, lim = kring->nkr_num_slots - 1; /* generate an interrupt approximately every half ring */ int report_frequency = kring->nkr_num_slots >> 1; + /* take a copy of ring->cur now, and never read it again */ k = ring->cur; if (k > lim) return netmap_ring_reinit(kring); @@ -172,25 +143,25 @@ lem_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) EM_TX_LOCK(adapter); bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map, BUS_DMASYNC_POSTREAD); - - /* update avail to what the hardware knows */ - ring->avail = kring->nr_hwavail; - - j = kring->nr_hwcur; /* points into the netmap ring */ + /* + * Process new packets to send. j is the current index in the + * netmap ring, l is the corresponding index in the NIC ring. + */ + j = kring->nr_hwcur; if (j != k) { /* we have new packets to send */ - l = j - kring->nkr_hwofs; /* points into the NIC ring */ - if (l < 0) - l += lim + 1; - while (j != k) { + l = netmap_idx_k2n(kring, j); + for (n = 0; j != k; n++) { + /* slot is the current slot in the netmap ring */ struct netmap_slot *slot = &ring->slot[j]; + /* curr is the current slot in the nic ring */ struct e1000_tx_desc *curr = &adapter->tx_desc_base[l]; struct em_buffer *txbuf = &adapter->tx_buffer_area[l]; - uint64_t paddr; - void *addr = PNMB(slot, &paddr); int flags = ((slot->flags & NS_REPORT) || j == 0 || j == report_frequency) ? E1000_TXD_CMD_RS : 0; - int len = slot->len; + uint64_t paddr; + void *addr = PNMB(slot, &paddr); + u_int len = slot->len; if (addr == netmap_buffer_base || len > NETMAP_BUF_SIZE) { if (do_lock) @@ -199,28 +170,24 @@ lem_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) } slot->flags &= ~NS_REPORT; - curr->upper.data = 0; - curr->lower.data = - htole32( adapter->txd_cmd | len | - (E1000_TXD_CMD_EOP | flags) ); if (slot->flags & NS_BUF_CHANGED) { - curr->buffer_addr = htole64(paddr); /* buffer has changed, reload map */ netmap_reload_map(adapter->txtag, txbuf->map, addr); + curr->buffer_addr = htole64(paddr); slot->flags &= ~NS_BUF_CHANGED; } + curr->upper.data = 0; + curr->lower.data = + htole32( adapter->txd_cmd | len | + (E1000_TXD_CMD_EOP | flags) ); bus_dmamap_sync(adapter->txtag, txbuf->map, BUS_DMASYNC_PREWRITE); j = (j == lim) ? 0 : j + 1; l = (l == lim) ? 0 : l + 1; - n++; } - kring->nr_hwcur = k; - - /* decrease avail by number of sent packets */ + kring->nr_hwcur = k; /* the saved ring->cur */ kring->nr_hwavail -= n; - ring->avail = kring->nr_hwavail; bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); @@ -233,19 +200,21 @@ lem_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) /* record completed transmissions using TDH */ l = E1000_READ_REG(&adapter->hw, E1000_TDH(0)); - if (l >= kring->nkr_num_slots) { /* can it happen ? */ + if (l >= kring->nkr_num_slots) { /* XXX can it happen ? */ D("bad TDH %d", l); l -= kring->nkr_num_slots; } delta = l - adapter->next_tx_to_clean; if (delta) { + /* some tx completed, increment hwavail. */ if (delta < 0) delta += kring->nkr_num_slots; adapter->next_tx_to_clean = l; kring->nr_hwavail += delta; - ring->avail = kring->nr_hwavail; } } + /* update avail to what the kernel knows */ + ring->avail = kring->nr_hwavail; if (do_lock) EM_TX_UNLOCK(adapter); @@ -260,57 +229,68 @@ static int lem_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) { struct adapter *adapter = ifp->if_softc; - struct netmap_adapter *na = NA(adapter->ifp); - struct netmap_kring *kring = &na->rx_rings[0]; + struct netmap_adapter *na = NA(ifp); + struct netmap_kring *kring = &na->rx_rings[ring_nr]; struct netmap_ring *ring = kring->ring; - int j, k, l, n, lim = kring->nkr_num_slots - 1; + int j, l, n, lim = kring->nkr_num_slots - 1; + int force_update = do_lock || kring->nr_kflags & NKR_PENDINTR; + u_int k = ring->cur, resvd = ring->reserved; - k = ring->cur; if (k > lim) return netmap_ring_reinit(kring); if (do_lock) EM_RX_LOCK(adapter); + /* XXX check sync modes */ bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - /* import newly received packets into the netmap ring */ - l = adapter->next_rx_desc_to_check; /* points into the NIC ring */ - j = l + kring->nkr_hwofs; /* points into the netmap ring */ - if (j > lim) - j -= lim + 1; - for (n = 0; ; n++) { - struct e1000_rx_desc *curr = &adapter->rx_desc_base[l]; - int len; - - if ((curr->status & E1000_RXD_STAT_DD) == 0) - break; - len = le16toh(curr->length) - 4; // CRC + /* + * Import newly received packets into the netmap ring. + * j is an index in the netmap ring, l in the NIC ring. + */ + l = adapter->next_rx_desc_to_check; + j = netmap_idx_n2k(kring, l); + if (netmap_no_pendintr || force_update) { + for (n = 0; ; n++) { + struct e1000_rx_desc *curr = &adapter->rx_desc_base[l]; + uint32_t staterr = le32toh(curr->status); + int len; - if (len < 0) { - D("bogus pkt size at %d", j); - len = 0; + if ((staterr & E1000_RXD_STAT_DD) == 0) + break; + len = le16toh(curr->length) - 4; // CRC + if (len < 0) { + D("bogus pkt size at %d", j); + len = 0; + } + ring->slot[j].len = len; + bus_dmamap_sync(adapter->rxtag, + adapter->rx_buffer_area[l].map, + BUS_DMASYNC_POSTREAD); + j = (j == lim) ? 0 : j + 1; + l = (l == lim) ? 0 : l + 1; } - ring->slot[j].len = len; - bus_dmamap_sync(adapter->rxtag, adapter->rx_buffer_area[l].map, - BUS_DMASYNC_POSTREAD); - j = (j == lim) ? 0 : j + 1; - l = (l == lim) ? 0 : l + 1; - } - if (n) { - adapter->next_rx_desc_to_check = l; - kring->nr_hwavail += n; + if (n) { /* update the state variables */ + adapter->next_rx_desc_to_check = l; + kring->nr_hwavail += n; + } + kring->nr_kflags &= ~NKR_PENDINTR; } - /* skip past packets that userspace has already processed */ - j = kring->nr_hwcur; /* netmap ring index */ - if (j != k) { /* userspace has read some packets. */ - n = 0; - l = j - kring->nkr_hwofs; /* NIC ring index */ - if (l < 0) - l += lim + 1; - while (j != k) { + /* skip past packets that userspace has released */ + j = kring->nr_hwcur; /* netmap ring index */ + 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 + 1 - resvd; + } + if (j != k) { /* userspace has released some packets. */ + l = netmap_idx_k2n(kring, j); /* NIC ring index */ + for (n = 0; j != k; n++) { struct netmap_slot *slot = &ring->slot[j]; struct e1000_rx_desc *curr = &adapter->rx_desc_base[l]; struct em_buffer *rxbuf = &adapter->rx_buffer_area[l]; @@ -322,20 +302,20 @@ lem_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) EM_RX_UNLOCK(adapter); return netmap_ring_reinit(kring); } - curr->status = 0; + if (slot->flags & NS_BUF_CHANGED) { - curr->buffer_addr = htole64(paddr); - /* buffer has changed, and reload map */ + /* buffer has changed, reload map */ netmap_reload_map(adapter->rxtag, rxbuf->map, addr); + curr->buffer_addr = htole64(paddr); slot->flags &= ~NS_BUF_CHANGED; } + curr->status = 0; bus_dmamap_sync(adapter->rxtag, rxbuf->map, BUS_DMASYNC_PREREAD); j = (j == lim) ? 0 : j + 1; l = (l == lim) ? 0 : l + 1; - n++; } kring->nr_hwavail -= n; kring->nr_hwcur = k; @@ -348,12 +328,30 @@ lem_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) l = (l == 0) ? lim : l - 1; E1000_WRITE_REG(&adapter->hw, E1000_RDT(0), l); } - /* tell userspace that there are new packets */ - ring->avail = kring->nr_hwavail ; - + ring->avail = kring->nr_hwavail - resvd; if (do_lock) EM_RX_UNLOCK(adapter); return 0; } + + +static void +lem_netmap_attach(struct adapter *adapter) +{ + struct netmap_adapter na; + + bzero(&na, sizeof(na)); + + na.ifp = adapter->ifp; + na.separate_locks = 1; + na.num_tx_desc = adapter->num_tx_desc; + na.num_rx_desc = adapter->num_rx_desc; + na.nm_txsync = lem_netmap_txsync; + na.nm_rxsync = lem_netmap_rxsync; + na.nm_lock = lem_netmap_lock_wrapper; + na.nm_register = lem_netmap_reg; + netmap_attach(&na, 1); +} + /* end of file */ diff --git a/sys/dev/netmap/if_re_netmap.h b/sys/dev/netmap/if_re_netmap.h index af8e21a486ba..bdd57035b7b5 100644 --- a/sys/dev/netmap/if_re_netmap.h +++ b/sys/dev/netmap/if_re_netmap.h @@ -25,40 +25,19 @@ /* * $FreeBSD$ - * $Id: if_re_netmap.h 10075 2011-12-25 22:55:48Z luigi $ + * $Id: if_re_netmap.h 10609 2012-02-22 19:44:58Z luigi $ * - * netmap support for if_re + * netmap support for "re" + * For details on netmap support please see ixgbe_netmap.h */ + #include <net/netmap.h> #include <sys/selinfo.h> #include <vm/vm.h> #include <vm/pmap.h> /* vtophys ? */ #include <dev/netmap/netmap_kern.h> -static int re_netmap_reg(struct ifnet *, int onoff); -static int re_netmap_txsync(struct ifnet *, u_int, int); -static int re_netmap_rxsync(struct ifnet *, u_int, int); -static void re_netmap_lock_wrapper(struct ifnet *, int, u_int); - -static void -re_netmap_attach(struct rl_softc *sc) -{ - struct netmap_adapter na; - - bzero(&na, sizeof(na)); - - na.ifp = sc->rl_ifp; - na.separate_locks = 0; - na.num_tx_desc = sc->rl_ldata.rl_tx_desc_cnt; - na.num_rx_desc = sc->rl_ldata.rl_rx_desc_cnt; - na.nm_txsync = re_netmap_txsync; - na.nm_rxsync = re_netmap_rxsync; - na.nm_lock = re_netmap_lock_wrapper; - na.nm_register = re_netmap_reg; - netmap_attach(&na, 1); -} - /* * wrapper to export locks to the generic code @@ -170,14 +149,13 @@ re_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) kring->nr_hwavail += n; } - /* update avail to what the hardware knows */ + /* update avail to what the kernel knows */ ring->avail = kring->nr_hwavail; j = kring->nr_hwcur; if (j != k) { /* we have new packets to send */ - n = 0; l = sc->rl_ldata.rl_tx_prodidx; - while (j != k) { + for (n = 0; j != k; n++) { struct netmap_slot *slot = &ring->slot[j]; struct rl_desc *desc = &sc->rl_ldata.rl_tx_list[l]; int cmd = slot->len | RL_TDESC_CMD_EOF | @@ -210,13 +188,10 @@ re_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) txd[l].tx_dmamap, BUS_DMASYNC_PREWRITE); j = (j == lim) ? 0 : j + 1; l = (l == lim) ? 0 : l + 1; - n++; } sc->rl_ldata.rl_tx_prodidx = l; - kring->nr_hwcur = k; - - /* decrease avail by number of sent packets */ - ring->avail -= n; + kring->nr_hwcur = k; /* the saved ring->cur */ + ring->avail -= n; // XXX see others kring->nr_hwavail = ring->avail; bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag, @@ -243,7 +218,9 @@ re_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) struct netmap_adapter *na = NA(sc->rl_ifp); struct netmap_kring *kring = &na->rx_rings[ring_nr]; struct netmap_ring *ring = kring->ring; - int j, k, l, n, lim = kring->nkr_num_slots - 1; + int j, l, n, lim = kring->nkr_num_slots - 1; + int force_update = do_lock || kring->nr_kflags & NKR_PENDINTR; + u_int k = ring->cur, resvd = ring->reserved; k = ring->cur; if (k > lim) @@ -257,52 +234,57 @@ re_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); /* + * Import newly received packets into the netmap ring. + * j is an index in the netmap ring, l in the NIC ring. + * * The device uses all the buffers in the ring, so we need * another termination condition in addition to RL_RDESC_STAT_OWN * cleared (all buffers could have it cleared. The easiest one * is to limit the amount of data reported up to 'lim' */ l = sc->rl_ldata.rl_rx_prodidx; /* next pkt to check */ - j = l + kring->nkr_hwofs; - for (n = kring->nr_hwavail; n < lim ; n++) { - struct rl_desc *cur_rx = &sc->rl_ldata.rl_rx_list[l]; - uint32_t rxstat = le32toh(cur_rx->rl_cmdstat); - uint32_t total_len; + j = netmap_idx_n2k(kring, l); /* the kring index */ + if (netmap_no_pendintr || force_update) { + for (n = kring->nr_hwavail; n < lim ; n++) { + struct rl_desc *cur_rx = &sc->rl_ldata.rl_rx_list[l]; + uint32_t rxstat = le32toh(cur_rx->rl_cmdstat); + uint32_t total_len; - if ((rxstat & RL_RDESC_STAT_OWN) != 0) - break; - total_len = rxstat & sc->rl_rxlenmask; - /* XXX subtract crc */ - total_len = (total_len < 4) ? 0 : total_len - 4; - kring->ring->slot[j].len = total_len; - /* sync was in re_newbuf() */ - bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag, - rxd[l].rx_dmamap, BUS_DMASYNC_POSTREAD); - j = (j == lim) ? 0 : j + 1; - l = (l == lim) ? 0 : l + 1; - } - if (n != kring->nr_hwavail) { - sc->rl_ldata.rl_rx_prodidx = l; - sc->rl_ifp->if_ipackets += n - kring->nr_hwavail; - kring->nr_hwavail = n; + if ((rxstat & RL_RDESC_STAT_OWN) != 0) + break; + total_len = rxstat & sc->rl_rxlenmask; + /* XXX subtract crc */ + total_len = (total_len < 4) ? 0 : total_len - 4; + kring->ring->slot[j].len = total_len; + /* sync was in re_newbuf() */ + bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag, + rxd[l].rx_dmamap, BUS_DMASYNC_POSTREAD); + j = (j == lim) ? 0 : j + 1; + l = (l == lim) ? 0 : l + 1; + } + if (n != kring->nr_hwavail) { + sc->rl_ldata.rl_rx_prodidx = l; + sc->rl_ifp->if_ipackets += n - kring->nr_hwavail; + kring->nr_hwavail = n; + } + kring->nr_kflags &= ~NKR_PENDINTR; } - /* skip past packets that userspace has already processed, - * making them available for reception. - * advance nr_hwcur and issue a bus_dmamap_sync on the - * buffers so it is safe to write to them. - * Also increase nr_hwavail - */ + /* skip past packets that userspace has released */ j = kring->nr_hwcur; - if (j != k) { /* userspace has read some packets. */ - n = 0; - l = kring->nr_hwcur - kring->nkr_hwofs; - if (l < 0) - l += lim + 1; - while (j != k) { + 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 + 1 - resvd; + } + if (j != k) { /* userspace has released some packets. */ + l = netmap_idx_k2n(kring, j); /* the NIC index */ + for (n = 0; j != k; n++) { struct netmap_slot *slot = ring->slot + j; struct rl_desc *desc = &sc->rl_ldata.rl_rx_list[l]; - int cmd = na->buff_size | RL_RDESC_CMD_OWN; + int cmd = NETMAP_BUF_SIZE | RL_RDESC_CMD_OWN; uint64_t paddr; void *addr = PNMB(slot, &paddr); @@ -315,20 +297,19 @@ re_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) if (l == lim) /* mark end of ring */ cmd |= RL_RDESC_CMD_EOR; - desc->rl_cmdstat = htole32(cmd); slot->flags &= ~NS_REPORT; if (slot->flags & NS_BUF_CHANGED) { - desc->rl_bufaddr_lo = htole32(RL_ADDR_LO(paddr)); - desc->rl_bufaddr_hi = htole32(RL_ADDR_HI(paddr)); netmap_reload_map(sc->rl_ldata.rl_rx_mtag, rxd[l].rx_dmamap, addr); + desc->rl_bufaddr_lo = htole32(RL_ADDR_LO(paddr)); + desc->rl_bufaddr_hi = htole32(RL_ADDR_HI(paddr)); slot->flags &= ~NS_BUF_CHANGED; } + desc->rl_cmdstat = htole32(cmd); bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag, rxd[l].rx_dmamap, BUS_DMASYNC_PREREAD); j = (j == lim) ? 0 : j + 1; l = (l == lim) ? 0 : l + 1; - n++; } kring->nr_hwavail -= n; kring->nr_hwcur = k; @@ -339,7 +320,7 @@ re_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); } /* tell userspace that there are new packets */ - ring->avail = kring->nr_hwavail; + ring->avail = kring->nr_hwavail - resvd; if (do_lock) RL_UNLOCK(sc); return 0; @@ -368,15 +349,10 @@ re_netmap_tx_init(struct rl_softc *sc) /* l points in the netmap ring, i points in the NIC ring */ for (i = 0; i < n; i++) { - void *addr; uint64_t paddr; - struct netmap_kring *kring = &na->tx_rings[0]; - int l = i + kring->nkr_hwofs; - - if (l >= n) - l -= n; + int l = netmap_idx_n2k(&na->tx_rings[0], i); + void *addr = PNMB(slot + l, &paddr); - addr = PNMB(slot + l, &paddr); desc[i].rl_bufaddr_lo = htole32(RL_ADDR_LO(paddr)); desc[i].rl_bufaddr_hi = htole32(RL_ADDR_HI(paddr)); netmap_load_map(sc->rl_ldata.rl_tx_mtag, @@ -391,19 +367,21 @@ re_netmap_rx_init(struct rl_softc *sc) struct netmap_slot *slot = netmap_reset(na, NR_RX, 0, 0); struct rl_desc *desc = sc->rl_ldata.rl_rx_list; uint32_t cmdstat; - int i, n; + int i, n, max_avail; if (!slot) return; n = sc->rl_ldata.rl_rx_desc_cnt; + /* + * Userspace owned hwavail packets before the reset, + * so the NIC that last hwavail descriptors of the ring + * are still owned by the driver (and keep one empty). + */ + max_avail = n - 1 - na->rx_rings[0].nr_hwavail; for (i = 0; i < n; i++) { void *addr; uint64_t paddr; - struct netmap_kring *kring = &na->rx_rings[0]; - int l = i + kring->nkr_hwofs; - - if (l >= n) - l -= n; + int l = netmap_idx_n2k(&na->rx_rings[0], i); addr = PNMB(slot + l, &paddr); @@ -413,16 +391,31 @@ re_netmap_rx_init(struct rl_softc *sc) sc->rl_ldata.rl_rx_desc[i].rx_dmamap, BUS_DMASYNC_PREREAD); desc[i].rl_bufaddr_lo = htole32(RL_ADDR_LO(paddr)); desc[i].rl_bufaddr_hi = htole32(RL_ADDR_HI(paddr)); - cmdstat = na->buff_size; - if (i == n - 1) + cmdstat = NETMAP_BUF_SIZE; + if (i == n - 1) /* mark the end of ring */ cmdstat |= RL_RDESC_CMD_EOR; - /* - * userspace knows that hwavail packets were ready before the - * reset, so we need to tell the NIC that last hwavail - * descriptors of the ring are still owned by the driver. - */ - if (i < n - 1 - kring->nr_hwavail) // XXX + 1 ? + if (i < max_avail) cmdstat |= RL_RDESC_CMD_OWN; desc[i].rl_cmdstat = htole32(cmdstat); } } + + +static void +re_netmap_attach(struct rl_softc *sc) +{ + struct netmap_adapter na; + + bzero(&na, sizeof(na)); + + na.ifp = sc->rl_ifp; + na.separate_locks = 0; + na.num_tx_desc = sc->rl_ldata.rl_tx_desc_cnt; + na.num_rx_desc = sc->rl_ldata.rl_rx_desc_cnt; + na.nm_txsync = re_netmap_txsync; + na.nm_rxsync = re_netmap_rxsync; + na.nm_lock = re_netmap_lock_wrapper; + na.nm_register = re_netmap_reg; + netmap_attach(&na, 1); +} +/* end of file */ diff --git a/sys/dev/netmap/ixgbe_netmap.h b/sys/dev/netmap/ixgbe_netmap.h index bdcaf3875e44..fb539636d46f 100644 --- a/sys/dev/netmap/ixgbe_netmap.h +++ b/sys/dev/netmap/ixgbe_netmap.h @@ -25,7 +25,7 @@ /* * $FreeBSD$ - * $Id: ixgbe_netmap.h 9802 2011-12-02 18:42:37Z luigi $ + * $Id: ixgbe_netmap.h 10627 2012-02-23 19:37:15Z luigi $ * * netmap modifications for ixgbe * @@ -47,44 +47,34 @@ #include <vm/pmap.h> */ - #include <dev/netmap/netmap_kern.h> /* - * prototypes for the new API calls that are used by the - * *_netmap_attach() routine. - */ -static int ixgbe_netmap_reg(struct ifnet *, int onoff); -static int ixgbe_netmap_txsync(struct ifnet *, u_int, int); -static int ixgbe_netmap_rxsync(struct ifnet *, u_int, int); -static void ixgbe_netmap_lock_wrapper(struct ifnet *, int, u_int); - - -/* - * The attach routine, called near the end of ixgbe_attach(), - * fills the parameters for netmap_attach() and calls it. - * It cannot fail, in the worst case (such as no memory) - * netmap mode will be disabled and the driver will only - * operate in standard mode. + * ix_crcstrip: 0: keep CRC in rx frames (default), 1: strip it. + * During regular operations the CRC is stripped, but on some + * hardware reception of frames not multiple of 64 is slower, + * so using crcstrip=0 helps in benchmarks. + * + * ix_rx_miss, ix_rx_miss_bufs: + * count packets that might be missed due to lost interrupts. + * + * ix_use_dd + * use the dd bit for completed tx transmissions. + * This is tricky, much better to use TDH for now. */ -static void -ixgbe_netmap_attach(struct adapter *adapter) -{ - struct netmap_adapter na; - - bzero(&na, sizeof(na)); - - na.ifp = adapter->ifp; - na.separate_locks = 1; /* this card has separate rx/tx locks */ - na.num_tx_desc = adapter->num_tx_desc; - na.num_rx_desc = adapter->num_rx_desc; - na.nm_txsync = ixgbe_netmap_txsync; - na.nm_rxsync = ixgbe_netmap_rxsync; - na.nm_lock = ixgbe_netmap_lock_wrapper; - na.nm_register = ixgbe_netmap_reg; - netmap_attach(&na, adapter->num_queues); -} - +SYSCTL_DECL(_dev_netmap); +static int ix_write_len; +SYSCTL_INT(_dev_netmap, OID_AUTO, ix_write_len, + CTLFLAG_RW, &ix_write_len, 0, "write rx len"); +static int ix_rx_miss, ix_rx_miss_bufs, ix_use_dd, ix_crcstrip; +SYSCTL_INT(_dev_netmap, OID_AUTO, ix_crcstrip, + CTLFLAG_RW, &ix_crcstrip, 0, "strip CRC on rx frames"); +SYSCTL_INT(_dev_netmap, OID_AUTO, ix_use_dd, + CTLFLAG_RW, &ix_use_dd, 0, "use dd instead of tdh to detect tx frames"); +SYSCTL_INT(_dev_netmap, OID_AUTO, ix_rx_miss, + CTLFLAG_RW, &ix_rx_miss, 0, "potentially missed rx intr"); +SYSCTL_INT(_dev_netmap, OID_AUTO, ix_rx_miss_bufs, + CTLFLAG_RW, &ix_rx_miss_bufs, 0, "potentially missed rx intr bufs"); /* * wrapper to export locks to the generic netmap code. @@ -118,8 +108,45 @@ ixgbe_netmap_lock_wrapper(struct ifnet *_a, int what, u_int queueid) } +static void +set_crcstrip(struct ixgbe_hw *hw, int onoff) +{ + /* crc stripping is set in two places: + * IXGBE_HLREG0 (modified on init_locked and hw reset) + * IXGBE_RDRXCTL (set by the original driver in + * ixgbe_setup_hw_rsc() called in init_locked. + * We disable the setting when netmap is compiled in). + * We update the values here, but also in ixgbe.c because + * init_locked sometimes is called outside our control. + */ + uint32_t hl, rxc; + + hl = IXGBE_READ_REG(hw, IXGBE_HLREG0); + rxc = IXGBE_READ_REG(hw, IXGBE_RDRXCTL); + if (netmap_verbose) + D("%s read HLREG 0x%x rxc 0x%x", + onoff ? "enter" : "exit", hl, rxc); + /* hw requirements ... */ + rxc &= ~IXGBE_RDRXCTL_RSCFRSTSIZE; + rxc |= IXGBE_RDRXCTL_RSCACKC; + if (onoff && !ix_crcstrip) { + /* keep the crc. Fast rx */ + hl &= ~IXGBE_HLREG0_RXCRCSTRP; + rxc &= ~IXGBE_RDRXCTL_CRCSTRIP; + } else { + /* reset default mode */ + hl |= IXGBE_HLREG0_RXCRCSTRP; + rxc |= IXGBE_RDRXCTL_CRCSTRIP; + } + if (netmap_verbose) + D("%s write HLREG 0x%x rxc 0x%x", + onoff ? "enter" : "exit", hl, rxc); + IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hl); + IXGBE_WRITE_REG(hw, IXGBE_RDRXCTL, rxc); +} + /* - * Netmap register/unregister. We are already under core lock. + * Register/unregister. We are already under core lock. * Only called on the first register or the last unregister. */ static int @@ -129,14 +156,15 @@ ixgbe_netmap_reg(struct ifnet *ifp, int onoff) struct netmap_adapter *na = NA(ifp); int error = 0; - if (!na) /* probably, netmap_attach() failed */ - return EINVAL; + if (na == NULL) + return EINVAL; /* no netmap support here */ ixgbe_disable_intr(adapter); /* Tell the stack that the interface is no longer active */ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + set_crcstrip(&adapter->hw, onoff); if (onoff) { /* enable netmap mode */ ifp->if_capenable |= IFCAP_NETMAP; @@ -161,6 +189,7 @@ fail: /* initialize the card, this time in standard mode */ ixgbe_init_locked(adapter); /* also enables intr */ } + set_crcstrip(&adapter->hw, onoff); return (error); } @@ -197,7 +226,7 @@ ixgbe_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) struct netmap_adapter *na = NA(adapter->ifp); struct netmap_kring *kring = &na->tx_rings[ring_nr]; struct netmap_ring *ring = kring->ring; - int j, k, l, n = 0, lim = kring->nkr_num_slots - 1; + u_int j, k = ring->cur, l, n = 0, lim = kring->nkr_num_slots - 1; /* * ixgbe can generate an interrupt on every tx packet, but it @@ -206,19 +235,10 @@ ixgbe_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) */ int report_frequency = kring->nkr_num_slots >> 1; + if (k > lim) + return netmap_ring_reinit(kring); if (do_lock) IXGBE_TX_LOCK(txr); - /* take a copy of ring->cur now, and never read it again */ - k = ring->cur; - l = k - kring->nr_hwcur; - if (l < 0) - l += lim + 1; - /* if cur is invalid reinitialize the ring. */ - if (k > lim || l > kring->nr_hwavail) { - if (do_lock) - IXGBE_TX_UNLOCK(txr); - return netmap_ring_reinit(kring); - } bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map, BUS_DMASYNC_POSTREAD); @@ -240,11 +260,10 @@ ixgbe_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) */ j = kring->nr_hwcur; if (j != k) { /* we have new packets to send */ - l = j - kring->nkr_hwofs; - if (l < 0) /* wraparound */ - l += lim + 1; - - while (j != k) { + prefetch(&ring->slot[j]); + l = netmap_idx_k2n(kring, j); /* NIC index */ + prefetch(&txr->tx_buffers[l]); + for (n = 0; j != k; n++) { /* * Collect per-slot info. * Note that txbuf and curr are indexed by l. @@ -255,17 +274,25 @@ ixgbe_netmap_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) * Many other drivers preserve the address, so * we only need to access it if NS_BUF_CHANGED * is set. + * XXX note, on this device the dmamap* calls are + * not necessary because tag is 0, however just accessing + * the per-packet tag kills 1Mpps at 900 MHz. */ struct netmap_slot *slot = &ring->slot[j]; - struct ixgbe_tx_buf *txbuf = &txr->tx_buffers[l]; union ixgbe_adv_tx_desc *curr = &txr->tx_base[l]; + struct ixgbe_tx_buf *txbuf = &txr->tx_buffers[l]; uint64_t paddr; - void *addr = PNMB(slot, &paddr); // XXX type for flags and len ? int flags = ((slot->flags & NS_REPORT) || j == 0 || j == report_frequency) ? IXGBE_TXD_CMD_RS : 0; - int len = slot->len; + u_int len = slot->len; + void *addr = PNMB(slot, &paddr); + + j = (j == lim) ? 0 : j + 1; + l = (l == lim) ? 0 : l + 1; + prefetch(&ring->slot[j]); + prefetch(&txr->tx_buffers[l]); /* * Quick check for valid addr and len. @@ -281,41 +308,29 @@ ring_reset: return netmap_ring_reinit(kring); } + if (slot->flags & NS_BUF_CHANGED) { + /* buffer has changed, unload and reload map */ + netmap_reload_map(txr->txtag, txbuf->map, addr); + slot->flags &= ~NS_BUF_CHANGED; + } slot->flags &= ~NS_REPORT; /* * Fill the slot in the NIC ring. * In this driver we need to rewrite the buffer * address in the NIC ring. Other drivers do not * need this. + * Use legacy descriptor, it is faster. */ curr->read.buffer_addr = htole64(paddr); - curr->read.olinfo_status = htole32(len << IXGBE_ADVTXD_PAYLEN_SHIFT); - curr->read.cmd_type_len = - htole32(txr->txd_cmd | len | - (IXGBE_ADVTXD_DTYP_DATA | - IXGBE_ADVTXD_DCMD_DEXT | - IXGBE_ADVTXD_DCMD_IFCS | - IXGBE_TXD_CMD_EOP | flags) ); - /* If the buffer has changed, unload and reload map - * (and possibly the physical address in the NIC - * slot, but we did it already). - */ - if (slot->flags & NS_BUF_CHANGED) { - /* buffer has changed, unload and reload map */ - netmap_reload_map(txr->txtag, txbuf->map, addr); - slot->flags &= ~NS_BUF_CHANGED; - } + curr->read.olinfo_status = 0; + curr->read.cmd_type_len = htole32(len | flags | + IXGBE_ADVTXD_DCMD_IFCS | IXGBE_TXD_CMD_EOP); /* make sure changes to the buffer are synced */ - bus_dmamap_sync(txr->txtag, txbuf->map, - BUS_DMASYNC_PREWRITE); - j = (j == lim) ? 0 : j + 1; - l = (l == lim) ? 0 : l + 1; - n++; + bus_dmamap_sync(txr->txtag, txbuf->map, BUS_DMASYNC_PREWRITE); } kring->nr_hwcur = k; /* the saved ring->cur */ - - /* decrease avail by number of sent packets */ + /* decrease avail by number of packets sent */ kring->nr_hwavail -= n; /* synchronize the NIC ring */ @@ -357,7 +372,7 @@ ring_reset: j= (j < kring->nkr_num_slots / 4 || j >= kring->nkr_num_slots*3/4) ? 0 : report_frequency; kring->nr_kflags = j; /* the slot to check */ - j = txd[j].upper.fields.status & IXGBE_TXD_STAT_DD; + j = txd[j].upper.fields.status & IXGBE_TXD_STAT_DD; // XXX cpu_to_le32 ? } if (j) { int delta; @@ -375,12 +390,26 @@ ring_reset: * For the time being we use TDH, as we do it infrequently * enough not to pose performance problems. */ + if (ix_use_dd) { + struct ixgbe_legacy_tx_desc *txd = + (struct ixgbe_legacy_tx_desc *)txr->tx_base; + + l = txr->next_to_clean; + k = netmap_idx_k2n(kring, kring->nr_hwcur); + delta = 0; + while (l != k && + txd[l].upper.fields.status & IXGBE_TXD_STAT_DD) { + delta++; + l = (l == lim) ? 0 : l + 1; + } + } else { l = IXGBE_READ_REG(&adapter->hw, IXGBE_TDH(ring_nr)); if (l >= kring->nkr_num_slots) { /* XXX can happen */ D("TDH wrap %d", l); l -= kring->nkr_num_slots; } delta = l - txr->next_to_clean; + } if (delta) { /* some tx completed, increment avail */ if (delta < 0) @@ -397,7 +426,6 @@ ring_reset: if (do_lock) IXGBE_TX_UNLOCK(txr); return 0; - } @@ -425,20 +453,15 @@ ixgbe_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) struct netmap_adapter *na = NA(adapter->ifp); struct netmap_kring *kring = &na->rx_rings[ring_nr]; struct netmap_ring *ring = kring->ring; - int j, k, l, n, lim = kring->nkr_num_slots - 1; + u_int j, l, n, lim = kring->nkr_num_slots - 1; int force_update = do_lock || kring->nr_kflags & NKR_PENDINTR; + u_int k = ring->cur, resvd = ring->reserved; - k = ring->cur; /* cache and check value, same as in txsync */ - n = k - kring->nr_hwcur; - if (n < 0) - n += lim + 1; - if (k > lim || n > kring->nr_hwavail) /* userspace is cheating */ + if (k > lim) return netmap_ring_reinit(kring); if (do_lock) IXGBE_RX_LOCK(rxr); - if (n < 0) - n += lim + 1; /* XXX check sync modes */ bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); @@ -458,45 +481,60 @@ ixgbe_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) * * rxr->next_to_check is set to 0 on a ring reinit */ - l = rxr->next_to_check; - j = rxr->next_to_check + kring->nkr_hwofs; - if (j > lim) - j -= lim + 1; + if (netmap_no_pendintr || force_update) { + /* XXX apparently the length field in advanced descriptors + * does not include the CRC irrespective of the setting + * of CRCSTRIP. The data sheets say differently. + * Very strange. + */ + int crclen = ix_crcstrip ? 0 : 4; + l = rxr->next_to_check; + j = netmap_idx_n2k(kring, l); - if (netmap_no_pendintr || force_update) { - for (n = 0; ; n++) { - union ixgbe_adv_rx_desc *curr = &rxr->rx_base[l]; - uint32_t staterr = le32toh(curr->wb.upper.status_error); + for (n = 0; ; n++) { + union ixgbe_adv_rx_desc *curr = &rxr->rx_base[l]; + uint32_t staterr = le32toh(curr->wb.upper.status_error); - if ((staterr & IXGBE_RXD_STAT_DD) == 0) - break; - ring->slot[j].len = le16toh(curr->wb.upper.length); - bus_dmamap_sync(rxr->ptag, - rxr->rx_buffers[l].pmap, BUS_DMASYNC_POSTREAD); - j = (j == lim) ? 0 : j + 1; - l = (l == lim) ? 0 : l + 1; - } - if (n) { /* update the state variables */ - rxr->next_to_check = l; - kring->nr_hwavail += n; + if ((staterr & IXGBE_RXD_STAT_DD) == 0) + break; + ring->slot[j].len = le16toh(curr->wb.upper.length) - crclen; + if (ix_write_len) + D("rx[%d] len %d", j, ring->slot[j].len); + bus_dmamap_sync(rxr->ptag, + rxr->rx_buffers[l].pmap, BUS_DMASYNC_POSTREAD); + j = (j == lim) ? 0 : j + 1; + l = (l == lim) ? 0 : l + 1; + } + if (n) { /* update the state variables */ + if (netmap_no_pendintr && !force_update) { + /* diagnostics */ + ix_rx_miss ++; + ix_rx_miss_bufs += n; + } + rxr->next_to_check = l; + kring->nr_hwavail += n; + } + kring->nr_kflags &= ~NKR_PENDINTR; } - kring->nr_kflags &= ~NKR_PENDINTR; - } /* - * Skip past packets that userspace has already processed - * (from kring->nr_hwcur to ring->cur excluded), and make - * the buffers available for reception. + * Skip past packets that userspace has released + * (from kring->nr_hwcur to ring->cur - ring->reserved excluded), + * and make the buffers available for reception. * As usual j is the index in the netmap ring, l is the index * in the NIC ring, and j == (l + kring->nkr_hwofs) % ring_size */ j = kring->nr_hwcur; - if (j != k) { /* userspace has read some packets. */ - n = 0; - l = kring->nr_hwcur - kring->nkr_hwofs; - if (l < 0) - l += lim + 1; - while (j != k) { + 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 + 1 - resvd; + } + if (j != k) { /* userspace has released some packets. */ + l = netmap_idx_k2n(kring, j); + for (n = 0; j != k; n++) { /* collect per-slot info, with similar validations * and flag handling as in the txsync code. * @@ -514,19 +552,16 @@ ixgbe_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) if (addr == netmap_buffer_base) /* bad buf */ goto ring_reset; - curr->wb.upper.status_error = 0; - curr->read.pkt_addr = htole64(paddr); if (slot->flags & NS_BUF_CHANGED) { netmap_reload_map(rxr->ptag, rxbuf->pmap, addr); slot->flags &= ~NS_BUF_CHANGED; } - + curr->wb.upper.status_error = 0; + curr->read.pkt_addr = htole64(paddr); bus_dmamap_sync(rxr->ptag, rxbuf->pmap, BUS_DMASYNC_PREREAD); - j = (j == lim) ? 0 : j + 1; l = (l == lim) ? 0 : l + 1; - n++; } kring->nr_hwavail -= n; kring->nr_hwcur = k; @@ -539,7 +574,7 @@ ixgbe_netmap_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) IXGBE_WRITE_REG(&adapter->hw, IXGBE_RDT(rxr->me), l); } /* tell userspace that there are new packets */ - ring->avail = kring->nr_hwavail ; + ring->avail = kring->nr_hwavail - resvd; if (do_lock) IXGBE_RX_UNLOCK(rxr); @@ -550,4 +585,31 @@ ring_reset: IXGBE_RX_UNLOCK(rxr); return netmap_ring_reinit(kring); } + + +/* + * The attach routine, called near the end of ixgbe_attach(), + * fills the parameters for netmap_attach() and calls it. + * It cannot fail, in the worst case (such as no memory) + * netmap mode will be disabled and the driver will only + * operate in standard mode. + */ +static void +ixgbe_netmap_attach(struct adapter *adapter) +{ + struct netmap_adapter na; + + bzero(&na, sizeof(na)); + + na.ifp = adapter->ifp; + na.separate_locks = 1; /* this card has separate rx/tx locks */ + na.num_tx_desc = adapter->num_tx_desc; + na.num_rx_desc = adapter->num_rx_desc; + na.nm_txsync = ixgbe_netmap_txsync; + na.nm_rxsync = ixgbe_netmap_rxsync; + na.nm_lock = ixgbe_netmap_lock_wrapper; + na.nm_register = ixgbe_netmap_reg; + netmap_attach(&na, adapter->num_queues); +} + /* end of file */ diff --git a/sys/dev/netmap/netmap.c b/sys/dev/netmap/netmap.c index 198a845311c4..1cd0a92d0a38 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 @@ -24,9 +24,6 @@ */ /* - * $FreeBSD$ - * $Id: netmap.c 9795 2011-12-02 11:39:08Z luigi $ - * * This module supports memory mapped access to network devices, * see netmap(4). * @@ -90,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 */ @@ -114,514 +111,18 @@ SYSCTL_INT(_dev_netmap, OID_AUTO, buf_size, CTLFLAG_RD, &netmap_buf_size, 0, "Size of packet buffers"); int netmap_mitigate = 1; SYSCTL_INT(_dev_netmap, OID_AUTO, mitigate, CTLFLAG_RW, &netmap_mitigate, 0, ""); -int netmap_no_pendintr; +int netmap_no_pendintr = 1; SYSCTL_INT(_dev_netmap, OID_AUTO, no_pendintr, CTLFLAG_RW, &netmap_no_pendintr, 0, "Always look for new received packets."); - -/*----- memory allocator -----------------*/ -/* - * Here we have the low level routines for memory allocator - * and its primary users. - */ - -/* - * Default amount of memory pre-allocated by the module. - * We start with a large size and then shrink our demand - * according to what is avalable when the module is loaded. - * At the moment the block is contiguous, but we can easily - * restrict our demand to smaller units (16..64k) - */ -#define NETMAP_MEMORY_SIZE (64 * 1024 * PAGE_SIZE) -static void * netmap_malloc(size_t size, const char *msg); -static void netmap_free(void *addr, const char *msg); - -#define netmap_if_malloc(len) netmap_malloc(len, "nifp") -#define netmap_if_free(v) netmap_free((v), "nifp") - -#define netmap_ring_malloc(len) netmap_malloc(len, "ring") -#define netmap_free_rings(na) \ - netmap_free((na)->tx_rings[0].ring, "shadow rings"); - -/* - * Allocator for a pool of packet buffers. For each buffer we have - * one entry in the bitmap to signal the state. Allocation scans - * the bitmap, but since this is done only on attach, we are not - * too worried about performance - * XXX if we need to allocate small blocks, a translation - * table is used both for kernel virtual address and physical - * addresses. - */ -struct netmap_buf_pool { - u_int total_buffers; /* total buffers. */ - u_int free; - u_int bufsize; - char *base; /* buffer base address */ - uint32_t *bitmap; /* one bit per buffer, 1 means free */ -}; -struct netmap_buf_pool nm_buf_pool; -SYSCTL_INT(_dev_netmap, OID_AUTO, total_buffers, - CTLFLAG_RD, &nm_buf_pool.total_buffers, 0, "total_buffers"); -SYSCTL_INT(_dev_netmap, OID_AUTO, free_buffers, - CTLFLAG_RD, &nm_buf_pool.free, 0, "free_buffers"); - - - - -/* - * Allocate n buffers from the ring, and fill the slot. - * Buffer 0 is the 'junk' buffer. - */ -static void -netmap_new_bufs(struct netmap_if *nifp __unused, - struct netmap_slot *slot, u_int n) -{ - struct netmap_buf_pool *p = &nm_buf_pool; - uint32_t bi = 0; /* index in the bitmap */ - uint32_t mask, j, i = 0; /* slot counter */ - - if (n > p->free) { - D("only %d out of %d buffers available", i, n); - return; - } - /* termination is guaranteed by p->free */ - while (i < n && p->free > 0) { - uint32_t cur = p->bitmap[bi]; - if (cur == 0) { /* bitmask is fully used */ - bi++; - continue; - } - /* locate a slot */ - for (j = 0, mask = 1; (cur & mask) == 0; j++, mask <<= 1) ; - p->bitmap[bi] &= ~mask; /* slot in use */ - p->free--; - slot[i].buf_idx = bi*32+j; - slot[i].len = p->bufsize; - slot[i].flags = NS_BUF_CHANGED; - i++; - } - ND("allocated %d buffers, %d available", n, p->free); -} - - -static void -netmap_free_buf(struct netmap_if *nifp __unused, uint32_t i) -{ - struct netmap_buf_pool *p = &nm_buf_pool; - - uint32_t pos, mask; - if (i >= p->total_buffers) { - D("invalid free index %d", i); - return; - } - pos = i / 32; - mask = 1 << (i % 32); - if (p->bitmap[pos] & mask) { - D("slot %d already free", i); - return; - } - p->bitmap[pos] |= mask; - p->free++; -} - - -/* Descriptor of the memory objects handled by our memory allocator. */ -struct netmap_mem_obj { - TAILQ_ENTRY(netmap_mem_obj) nmo_next; /* next object in the - chain. */ - int nmo_used; /* flag set on used memory objects. */ - size_t nmo_size; /* size of the memory area reserved for the - object. */ - void *nmo_data; /* pointer to the memory area. */ -}; - -/* Wrap our memory objects to make them ``chainable``. */ -TAILQ_HEAD(netmap_mem_obj_h, netmap_mem_obj); - - -/* Descriptor of our custom memory allocator. */ -struct netmap_mem_d { - struct mtx nm_mtx; /* lock used to handle the chain of memory - objects. */ - struct netmap_mem_obj_h nm_molist; /* list of memory objects */ - size_t nm_size; /* total amount of memory used for rings etc. */ - size_t nm_totalsize; /* total amount of allocated memory - (the difference is used for buffers) */ - size_t nm_buf_start; /* offset of packet buffers. - This is page-aligned. */ - size_t nm_buf_len; /* total memory for buffers */ - void *nm_buffer; /* pointer to the whole pre-allocated memory - area. */ -}; - -/* Shorthand to compute a netmap interface offset. */ -#define netmap_if_offset(v) \ - ((char *) (v) - (char *) netmap_mem_d->nm_buffer) -/* .. and get a physical address given a memory offset */ -#define netmap_ofstophys(o) \ - (vtophys(netmap_mem_d->nm_buffer) + (o)) - - -/*------ netmap memory allocator -------*/ -/* - * 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. - * This sounds like a completely inefficient implementation, but given - * the fact that data allocation is done once, we can handle it - * flawlessly. - * - * Return NULL on failure. - */ -static void * -netmap_malloc(size_t size, __unused const char *msg) -{ - struct netmap_mem_obj *mem_obj, *new_mem_obj; - void *ret = NULL; - - NMA_LOCK(); - TAILQ_FOREACH(mem_obj, &netmap_mem_d->nm_molist, nmo_next) { - if (mem_obj->nmo_used != 0 || mem_obj->nmo_size < size) - continue; - - new_mem_obj = malloc(sizeof(struct netmap_mem_obj), M_NETMAP, - M_WAITOK | M_ZERO); - TAILQ_INSERT_BEFORE(mem_obj, new_mem_obj, nmo_next); - - new_mem_obj->nmo_used = 1; - new_mem_obj->nmo_size = size; - new_mem_obj->nmo_data = mem_obj->nmo_data; - memset(new_mem_obj->nmo_data, 0, new_mem_obj->nmo_size); - - 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, - nmo_next); - free(mem_obj, M_NETMAP); - } - - ret = new_mem_obj->nmo_data; - - break; - } - NMA_UNLOCK(); - ND("%s: %d bytes at %p", msg, size, ret); - - return (ret); -} - -/* - * Return the memory to the allocator. - * - * While freeing a memory object, we try to merge adjacent chunks in - * order to reduce memory fragmentation. - */ -static void -netmap_free(void *addr, const char *msg) -{ - size_t size; - struct netmap_mem_obj *cur, *prev, *next; - - if (addr == NULL) { - D("NULL addr for %s", msg); - return; - } - - NMA_LOCK(); - TAILQ_FOREACH(cur, &netmap_mem_d->nm_molist, nmo_next) { - if (cur->nmo_data == addr && cur->nmo_used) - break; - } - if (cur == NULL) { - NMA_UNLOCK(); - D("invalid addr %s %p", msg, addr); - return; - } - - size = cur->nmo_size; - cur->nmo_used = 0; - - /* merge current chunk of memory with the previous one, - 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); - prev->nmo_size += cur->nmo_size; - free(cur, M_NETMAP); - cur = prev; - } - - /* 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); - cur->nmo_size += next->nmo_size; - free(next, M_NETMAP); - } - NMA_UNLOCK(); - ND("freed %s %d bytes at %p", msg, size, addr); -} - - -/* - * Create and return a new ``netmap_if`` object, and possibly also - * rings and packet buffors. - * - * Return NULL on failure. - */ -static void * -netmap_if_new(const char *ifname, struct netmap_adapter *na) -{ - struct netmap_if *nifp; - struct netmap_ring *ring; - char *buff; - u_int i, len, ofs; - u_int n = na->num_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); - nifp = netmap_if_malloc(len); - if (nifp == NULL) - return (NULL); - - /* initialize base fields */ - *(int *)(uintptr_t)&nifp->ni_num_queues = na->num_queues; - strncpy(nifp->ni_name, ifname, IFNAMSIZ); - - (na->refcount)++; /* XXX atomic ? we are under lock */ - if (na->refcount > 1) - 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). - * The rings are contiguous, but have variable size. - * The entire block is reachable at - * na->tx_rings[0].ring - */ - - len = n * (2 * sizeof(struct netmap_ring) + - (na->num_tx_desc + 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", - len, ifname); -error: - (na->refcount)--; - 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); - NMA_LOCK(); - if (nm_buf_pool.free < len) { - NMA_UNLOCK(); - netmap_free(buff, "not enough bufs"); - goto error; - } - /* - * in the kring, store the pointers to the shared rings - * 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 */ - kring = &na->tx_rings[i]; - numdesc = na->num_tx_desc; - bzero(kring, sizeof(*kring)); - kring->na = na; - - ring = kring->ring = (struct netmap_ring *)(buff + ofs); - *(ssize_t *)(uintptr_t)&ring->buf_ofs = - nm_buf_pool.base - (char *)ring; - ND("txring[%d] at %p ofs %d", i, ring, ring->buf_ofs); - *(uint32_t *)(uintptr_t)&ring->num_slots = - kring->nkr_num_slots = numdesc; - - /* - * IMPORTANT: - * Always keep one slot empty, so we can detect new - * transmissions comparing cur and nr_hwcur (they are - * the same only if there are no new transmissions). - */ - ring->avail = kring->nr_hwavail = numdesc - 1; - ring->cur = kring->nr_hwcur = 0; - *(uint16_t *)(uintptr_t)&ring->nr_buf_size = NETMAP_BUF_SIZE; - netmap_new_bufs(nifp, ring->slot, numdesc); - - ofs += sizeof(struct netmap_ring) + - numdesc * sizeof(struct netmap_slot); - - /* Receive rings */ - kring = &na->rx_rings[i]; - numdesc = na->num_rx_desc; - bzero(kring, sizeof(*kring)); - kring->na = na; - - ring = kring->ring = (struct netmap_ring *)(buff + ofs); - *(ssize_t *)(uintptr_t)&ring->buf_ofs = - nm_buf_pool.base - (char *)ring; - ND("rxring[%d] at %p offset %d", i, ring, ring->buf_ofs); - *(uint32_t *)(uintptr_t)&ring->num_slots = - kring->nkr_num_slots = numdesc; - ring->cur = kring->nr_hwcur = 0; - ring->avail = kring->nr_hwavail = 0; /* empty */ - *(uint16_t *)(uintptr_t)&ring->nr_buf_size = NETMAP_BUF_SIZE; - netmap_new_bufs(nifp, ring->slot, numdesc); - ofs += sizeof(struct netmap_ring) + - numdesc * sizeof(struct netmap_slot); - } - NMA_UNLOCK(); - for (i = 0; i < n+1; i++) { - // 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; - *(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; - } - return (nifp); -} - -/* - * Initialize the memory allocator. - * - * Create the descriptor for the memory , allocate the pool of memory - * and initialize the list of memory objects with a single chunk - * containing the whole pre-allocated memory marked as free. - * - * Start with a large size, then halve as needed if we fail to - * allocate the block. While halving, always add one extra page - * because buffers 0 and 1 are used for special purposes. - * Return 0 on success, errno otherwise. - */ -static int -netmap_memory_init(void) -{ - struct netmap_mem_obj *mem_obj; - void *buf = NULL; - int i, n, sz = NETMAP_MEMORY_SIZE; - int extra_sz = 0; // space for rings and two spare buffers - - for (; sz >= 1<<20; sz >>=1) { - extra_sz = sz/200; - extra_sz = (extra_sz + 2*PAGE_SIZE - 1) & ~(PAGE_SIZE-1); - buf = contigmalloc(sz + extra_sz, - M_NETMAP, - M_WAITOK | M_ZERO, - 0, /* low address */ - -1UL, /* high address */ - PAGE_SIZE, /* alignment */ - 0 /* boundary */ - ); - if (buf) - break; - } - if (buf == NULL) - return (ENOMEM); - sz += extra_sz; - netmap_mem_d = 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_DEF); - TAILQ_INIT(&netmap_mem_d->nm_molist); - netmap_mem_d->nm_buffer = buf; - netmap_mem_d->nm_totalsize = sz; - - /* - * A buffer takes 2k, a slot takes 8 bytes + ring overhead, - * so the ratio is 200:1. In other words, we can use 1/200 of - * 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_buf_pool.base = netmap_mem_d->nm_buffer; - nm_buf_pool.base += netmap_mem_d->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); - /* 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_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), - nm_buf_pool.total_buffers, nm_buf_pool.base); - - /* allocate and initialize the bitmap. Entry 0 is considered - * always busy (used as default when there are no buffers left). - */ - n = (nm_buf_pool.total_buffers + 31) / 32; - nm_buf_pool.bitmap = malloc(sizeof(uint32_t) * n, M_NETMAP, - M_WAITOK | M_ZERO); - nm_buf_pool.bitmap[0] = ~3; /* slot 0 and 1 always busy */ - for (i = 1; i < n; i++) - nm_buf_pool.bitmap[i] = ~0; - nm_buf_pool.free = nm_buf_pool.total_buffers - 2; - - 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); - mem_obj->nmo_used = 0; - mem_obj->nmo_size = netmap_mem_d->nm_size; - mem_obj->nmo_data = netmap_mem_d->nm_buffer; - - return (0); -} - - -/* - * Finalize the memory allocator. - * - * Free all the memory objects contained inside the list, and deallocate - * the pool of memory; finally free the memory allocator descriptor. - */ -static void -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); - if (mem_obj->nmo_used == 1) { - printf("netmap: leaked %d bytes at %p\n", - (int)mem_obj->nmo_size, - mem_obj->nmo_data); - } - free(mem_obj, M_NETMAP); - } - contigfree(netmap_mem_d->nm_buffer, netmap_mem_d->nm_totalsize, M_NETMAP); - // XXX mutex_destroy(nm_mtx); - free(netmap_mem_d, M_NETMAP); -} -/*------------- end of memory allocator -----------------*/ - +/*------------- memory allocator -----------------*/ +#ifdef NETMAP_MEM2 +#include "netmap_mem2.c" +#else /* !NETMAP_MEM2 */ +#include "netmap_mem1.c" +#endif /* !NETMAP_MEM2 */ +/*------------ end of memory allocator ----------*/ /* Structure associated to each thread which registered an interface. */ struct netmap_priv_d { @@ -634,100 +135,6 @@ struct netmap_priv_d { }; -static struct cdev *netmap_dev; /* /dev/netmap character device. */ - - -static d_mmap_t netmap_mmap; -static d_ioctl_t netmap_ioctl; -static d_poll_t netmap_poll; - -#ifdef NETMAP_KEVENT -static d_kqfilter_t netmap_kqfilter; -#endif - -static struct cdevsw netmap_cdevsw = { - .d_version = D_VERSION, - .d_name = "netmap", - .d_mmap = netmap_mmap, - .d_ioctl = netmap_ioctl, - .d_poll = netmap_poll, -#ifdef NETMAP_KEVENT - .d_kqfilter = netmap_kqfilter, -#endif -}; - -#ifdef NETMAP_KEVENT -static int netmap_kqread(struct knote *, long); -static int netmap_kqwrite(struct knote *, long); -static void netmap_kqdetach(struct knote *); - -static struct filterops netmap_read_filterops = { - .f_isfd = 1, - .f_attach = NULL, - .f_detach = netmap_kqdetach, - .f_event = netmap_kqread, -}; - -static struct filterops netmap_write_filterops = { - .f_isfd = 1, - .f_attach = NULL, - .f_detach = netmap_kqdetach, - .f_event = netmap_kqwrite, -}; - -/* - * support for the kevent() system call. - * - * This is the kevent filter, and is executed each time a new event - * is triggered on the device. This function execute some operation - * depending on the received filter. - * - * The implementation should test the filters and should implement - * filter operations we are interested on (a full list in /sys/event.h). - * - * On a match we should: - * - set kn->kn_fop - * - set kn->kn_hook - * - call knlist_add() to deliver the event to the application. - * - * Return 0 if the event should be delivered to the application. - */ -static int -netmap_kqfilter(struct cdev *dev, struct knote *kn) -{ - /* declare variables needed to read/write */ - - switch(kn->kn_filter) { - case EVFILT_READ: - if (netmap_verbose) - D("%s kqfilter: EVFILT_READ" ifp->if_xname); - - /* read operations */ - kn->kn_fop = &netmap_read_filterops; - break; - - case EVFILT_WRITE: - if (netmap_verbose) - D("%s kqfilter: EVFILT_WRITE" ifp->if_xname); - - /* write operations */ - kn->kn_fop = &netmap_write_filterops; - break; - - default: - if (netmap_verbose) - D("%s kqfilter: invalid filter" ifp->if_xname); - return(EINVAL); - } - - kn->kn_hook = 0;// - knlist_add(&netmap_sc->tun_rsel.si_note, kn, 0); - - return (0); -} -#endif /* NETMAP_KEVENT */ - - /* * File descriptor's private data destructor. * @@ -744,7 +151,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); /* @@ -766,24 +173,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_rings + 1; i++) selwakeuppri(&na->tx_rings[i].si, PI_NET); + for (i = 0; i < na->num_rx_rings + 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_rings + 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_rings + 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); @@ -805,7 +210,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 */ @@ -822,14 +227,16 @@ netmap_dtor(void *data) * * Return 0 on success, -1 otherwise. */ + static int +netmap_mmap(__unused struct cdev *dev, #if __FreeBSD_version < 900000 -netmap_mmap(__unused struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, - int nprot) + vm_offset_t offset, vm_paddr_t *paddr, int nprot #else -netmap_mmap(__unused struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, - int nprot, __unused vm_memattr_t *memattr) + vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, + __unused vm_memattr_t *memattr #endif + ) { if (nprot & PROT_EXEC) return (-1); // XXX -1 or EINVAL ? @@ -853,7 +260,7 @@ netmap_mmap(__unused struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, 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_rings]; struct netmap_ring *ring = kring->ring; struct mbuf *head = NULL, *tail = NULL, *m; u_int k, n, lim = kring->nkr_num_slots - 1; @@ -913,31 +320,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_rings]; 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; - kring->nr_hwcur = k; - error = 0; - k = ring->avail = kring->nr_hwavail; + 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; + } + 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); } @@ -1002,13 +415,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_rings + 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; @@ -1028,10 +441,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_rings; - if ( (ringid & NETMAP_HW_RING) && i >= na->num_queues) { + if (na->num_tx_rings > lim) + lim = na->num_tx_rings; + if ( (ringid & NETMAP_HW_RING) && i >= lim) { D("invalid ring id %d", i); return (EINVAL); } @@ -1039,14 +455,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) @@ -1057,8 +473,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; } @@ -1084,7 +499,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)); @@ -1099,22 +514,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_rings; + nmr->nr_tx_rings = na->num_tx_rings; + 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; @@ -1190,9 +619,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_rings; + nmr->nr_tx_rings = na->num_tx_rings; + 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; @@ -1215,35 +646,38 @@ 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_rings : na->num_rx_rings; - for (i = priv->np_qfirst; i < priv->np_qlast; i++) { - if (cmd == NIOCTXSYNC) { - struct netmap_kring *kring = &na->tx_rings[i]; - if (netmap_verbose & NM_VERB_TXSYNC) - D("sync tx ring %d cur %d hwcur %d", - i, kring->ring->cur, - kring->nr_hwcur); - na->nm_txsync(ifp, i, 1 /* do lock */); - if (netmap_verbose & NM_VERB_TXSYNC) - D("after sync tx ring %d cur %d hwcur %d", - i, kring->ring->cur, - kring->nr_hwcur); - } else { - na->nm_rxsync(ifp, i, 1 /* do lock */); - microtime(&na->rx_rings[i].ring->ts); - } + 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) + D("pre txsync ring %d cur %d hwcur %d", + i, kring->ring->cur, + kring->nr_hwcur); + na->nm_txsync(ifp, i, 1 /* do lock */); + if (netmap_verbose & NM_VERB_TXSYNC) + D("post txsync ring %d cur %d hwcur %d", + i, kring->ring->cur, + kring->nr_hwcur); + } else { + na->nm_rxsync(ifp, i, 1 /* do lock */); + microtime(&na->rx_rings[i].ring->ts); + } } - break; + break; case BIOCIMMEDIATE: case BIOCGHDRCMPLT: @@ -1252,11 +686,8 @@ error: D("ignore BIOCIMMEDIATE/BIOCSHDRCMPLT/BIOCSHDRCMPLT/BIOCSSEESENT"); break; - default: + default: /* allow device-specific ioctls */ { - /* - * allow device calls - */ struct socket so; bzero(&so, sizeof(so)); error = get_ifp(nmr->nr_name, &ifp); /* keep reference */ @@ -1266,6 +697,7 @@ error: // so->so_proto not null. error = ifioctl(&so, cmd, data, td); if_rele(ifp); + break; } } @@ -1292,6 +724,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) @@ -1309,17 +742,18 @@ netmap_poll(__unused struct cdev *dev, int events, struct thread *td) na = NA(ifp); /* retrieve netmap adapter */ + lim_tx = na->num_tx_rings; + lim_rx = na->num_rx_rings; /* 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) { @@ -1350,7 +784,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. @@ -1367,25 +801,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 @@ -1393,7 +831,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 @@ -1437,7 +875,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); @@ -1461,12 +899,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); @@ -1477,19 +914,19 @@ netmap_poll(__unused struct cdev *dev, int events, struct thread *td) /*------- driver support routines ------*/ /* - * default lock wrapper. On linux we use mostly netmap-specific locks. + * default lock wrapper. */ static void -netmap_lock_wrapper(struct ifnet *_a, int what, u_int queueid) +netmap_lock_wrapper(struct ifnet *dev, int what, u_int queueid) { - struct netmap_adapter *na = NA(_a); + struct netmap_adapter *na = NA(dev); switch (what) { -#ifndef __FreeBSD__ /* some system do not need lock on register */ +#ifdef linux /* some system do not need lock on register */ case NETMAP_REG_LOCK: case NETMAP_REG_UNLOCK: break; -#endif +#endif /* linux */ case NETMAP_CORE_LOCK: mtx_lock(&na->core_lock); @@ -1527,29 +964,37 @@ netmap_lock_wrapper(struct ifnet *_a, 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_rings 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_rings == 0) + na->num_tx_rings = num_queues; + na->num_rx_rings = 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_rings + na->num_tx_rings + 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->buff_size = NETMAP_BUF_SIZE; + na->rx_rings = na->tx_rings + na->num_tx_rings + 1; bcopy(na, buf, sizeof(*na)); ifp->if_capabilities |= IFCAP_NETMAP; @@ -1557,11 +1002,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_rings + 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_rings + 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); @@ -1581,10 +1032,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_rings + 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_rings + 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); @@ -1600,7 +1057,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_rings]; u_int i, len = MBUF_LEN(m); int error = EBUSY, lim = kring->nkr_num_slots - 1; struct netmap_slot *slot; @@ -1610,11 +1067,12 @@ netmap_start(struct ifnet *ifp, struct mbuf *m) kring->nr_hwcur + kring->nr_hwavail, len); na->nm_lock(ifp, NETMAP_CORE_LOCK, 0); if (kring->nr_hwavail >= lim) { - D("stack ring %s full\n", ifp->if_xname); + if (netmap_verbose) + 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 */ } @@ -1627,7 +1085,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_rings); selwakeuppri(&kring->si, PI_NET); error = 0; done: @@ -1653,21 +1111,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; @@ -1680,11 +1138,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; } @@ -1693,44 +1152,67 @@ netmap_reset(struct netmap_adapter *na, enum txrx tx, int n, * Default functions to handle rx/tx interrupts * we have 4 cases: * 1 ring, single lock: - * lock(core); wake(i=0); unlock(core) + * lock(core); wake(i=0); unlock(core) * N rings, single lock: - * lock(core); wake(i); wake(N+1) unlock(core) + * lock(core); wake(i); wake(N+1) unlock(core) * 1 ring, separate locks: (i=0) - * lock(i); wake(i); unlock(i) + * lock(i); wake(i); unlock(i) * N rings, separate locks: - * lock(i); wake(i); unlock(i); lock(core) wake(N+1) unlock(core) + * 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) +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_rings > 1) ? &na->rx_si : NULL; + } else { /* tx path */ + r = na->tx_rings + q; + main_wq = (na->num_tx_rings > 1) ? &na->tx_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 */ + *work_done = 1; /* do not fire napi again */ return 1; } + +static struct cdevsw netmap_cdevsw = { + .d_version = D_VERSION, + .d_name = "netmap", + .d_mmap = netmap_mmap, + .d_ioctl = netmap_ioctl, + .d_poll = netmap_poll, +}; + + +static struct cdev *netmap_dev; /* /dev/netmap character device. */ + + /* * Module loader. * @@ -1744,19 +1226,16 @@ netmap_init(void) { int error; - error = netmap_memory_init(); if (error != 0) { printf("netmap: unable to initialize the memory allocator."); 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 (0); + return (error); } @@ -1769,9 +1248,7 @@ static void netmap_fini(void) { destroy_dev(netmap_dev); - netmap_memory_fini(); - printf("netmap: unloaded module.\n"); } diff --git a/sys/dev/netmap/netmap_kern.h b/sys/dev/netmap/netmap_kern.h index 4fb59980444f..a1ac925b1c57 100644 --- a/sys/dev/netmap/netmap_kern.h +++ b/sys/dev/netmap/netmap_kern.h @@ -25,7 +25,7 @@ /* * $FreeBSD$ - * $Id: netmap_kern.h 9795 2011-12-02 11:39:08Z luigi $ + * $Id: netmap_kern.h 10602 2012-02-21 16:47:55Z luigi $ * * The header contains the definitions of constants and function * prototypes used only in kernelspace. @@ -34,12 +34,14 @@ #ifndef _NET_NETMAP_KERN_H_ #define _NET_NETMAP_KERN_H_ +#define NETMAP_MEM2 // use the new memory allocator + #if defined(__FreeBSD__) #define NM_LOCK_T struct mtx #define NM_SELINFO_T struct selinfo #define MBUF_LEN(m) ((m)->m_pkthdr.len) #define NM_SEND_UP(ifp, m) ((ifp)->if_input)(ifp, m) -#elif defined (__linux__) +#elif defined (linux) #define NM_LOCK_T spinlock_t #define NM_SELINFO_T wait_queue_head_t #define MBUF_LEN(m) ((m)->len) @@ -65,7 +67,14 @@ MALLOC_DECLARE(M_NETMAP); struct netmap_adapter; /* - * private, kernel view of a ring. + * private, kernel view of a ring. Keeps track of the status of + * a ring across system calls. + * + * nr_hwcur index of the next buffer to refill. + * It corresponds to ring->cur - ring->reserved + * + * nr_hwavail the number of slots "owned" by userspace. + * nr_hwavail =:= ring->avail + ring->reserved * * The indexes in the NIC and netmap rings are offset by nkr_hwofs slots. * This is so that, on a reset, buffers owned by userspace are not @@ -79,7 +88,7 @@ struct netmap_kring { u_int nr_hwcur; int nr_hwavail; u_int nr_kflags; /* private driver flags */ -#define NKR_PENDINTR 0x1 // Pending interrupt. +#define NKR_PENDINTR 0x1 // Pending interrupt. u_int nkr_num_slots; int nkr_hwofs; /* offset between NIC and netmap ring */ @@ -97,19 +106,25 @@ struct netmap_adapter { int refcount; /* number of user-space descriptors using this interface, which is equal to the number of struct netmap_if objs in the mapped region. */ + /* + * The selwakeup in the interrupt thread can use per-ring + * and/or global wait queues. We track how many clients + * of each type we have so we can optimize the drivers, + * and especially avoid huge contention on the locks. + */ + int na_single; /* threads attached to a single hw queue */ + int na_multi; /* threads attached to multiple hw queues */ int separate_locks; /* set if the interface suports different locks for rx, tx and core. */ - u_int num_queues; /* number of tx/rx queue pairs: this is - a duplicate field needed to simplify the - signature of ``netmap_detach``. */ + u_int num_rx_rings; /* number of tx/rx ring pairs */ + u_int num_tx_rings; // if nonzero, overrides num_rx_rings u_int num_tx_desc; /* number of descriptor in each queue */ u_int num_rx_desc; - u_int buff_size; + //u_int buff_size; // XXX deprecate, use NETMAP_BUF_SIZE - //u_int flags; // XXX unused /* tx_rings and rx_rings are private but allocated * as a contiguous chunk of memory. Each array has * N+1 entries, for the adapter queues and for the host queue. @@ -117,11 +132,11 @@ struct netmap_adapter { struct netmap_kring *tx_rings; /* array of TX rings. */ struct netmap_kring *rx_rings; /* array of RX rings. */ + NM_SELINFO_T tx_si, rx_si; /* global wait queues */ + /* copy of if_qflush and if_transmit pointers, to intercept * packets from the network stack when netmap is active. - * XXX probably if_qflush is not necessary. */ - //void (*if_qflush)(struct ifnet *); // XXX unused int (*if_transmit)(struct ifnet *, struct mbuf *); /* references to the ifnet and device routines, used by @@ -135,6 +150,9 @@ struct netmap_adapter { void (*nm_lock)(struct ifnet *, int what, u_int ringid); int (*nm_txsync)(struct ifnet *, u_int ring, int lock); int (*nm_rxsync)(struct ifnet *, u_int ring, int lock); +#ifdef linux + struct net_device_ops nm_ndo; +#endif /* linux */ }; /* @@ -195,7 +213,7 @@ struct netmap_slot *netmap_reset(struct netmap_adapter *na, int netmap_ring_reinit(struct netmap_kring *); extern int netmap_buf_size; -#define NETMAP_BUF_SIZE netmap_buf_size +#define NETMAP_BUF_SIZE netmap_buf_size extern int netmap_mitigate; extern int netmap_no_pendintr; extern u_int netmap_total_buffers; @@ -250,6 +268,51 @@ netmap_reload_map(bus_dma_tag_t tag, bus_dmamap_t map, void *buf) } } +/* + * functions to map NIC to KRING indexes (n2k) and vice versa (k2n) + */ +static inline int +netmap_idx_n2k(struct netmap_kring *kr, int idx) +{ + int n = kr->nkr_num_slots; + idx += kr->nkr_hwofs; + if (idx < 0) + return idx + n; + else if (idx < n) + return idx; + else + return idx - n; +} + + +static inline int +netmap_idx_k2n(struct netmap_kring *kr, int idx) +{ + int n = kr->nkr_num_slots; + idx -= kr->nkr_hwofs; + if (idx < 0) + return idx + n; + else if (idx < n) + return idx; + else + return idx - n; +} + + +#ifdef NETMAP_MEM2 +/* Entries of the look-up table. */ +struct lut_entry { + void *vaddr; /* virtual address. */ + vm_paddr_t paddr; /* phisical address. */ +}; + +struct netmap_obj_pool; +extern struct lut_entry *netmap_buffer_lut; +#define NMB_VA(i) (netmap_buffer_lut[i].vaddr) +#define NMB_PA(i) (netmap_buffer_lut[i].paddr) +#else /* NETMAP_MEM1 */ +#define NMB_VA(i) (netmap_buffer_base + (i * NETMAP_BUF_SIZE) ) +#endif /* NETMAP_MEM2 */ /* * NMB return the virtual address of a buffer (buffer 0 on bad index) @@ -259,25 +322,23 @@ static inline void * NMB(struct netmap_slot *slot) { uint32_t i = slot->buf_idx; - return (i >= netmap_total_buffers) ? netmap_buffer_base : - netmap_buffer_base + (i *NETMAP_BUF_SIZE); + return (i >= netmap_total_buffers) ? NMB_VA(0) : NMB_VA(i); } static inline void * PNMB(struct netmap_slot *slot, uint64_t *pp) { uint32_t i = slot->buf_idx; - void *ret = (i >= netmap_total_buffers) ? netmap_buffer_base : - netmap_buffer_base + (i *NETMAP_BUF_SIZE); + void *ret = (i >= netmap_total_buffers) ? NMB_VA(0) : NMB_VA(i); +#ifdef NETMAP_MEM2 + *pp = (i >= netmap_total_buffers) ? NMB_PA(0) : NMB_PA(i); +#else *pp = vtophys(ret); +#endif return ret; } /* default functions to handle rx/tx interrupts */ int netmap_rx_irq(struct ifnet *, int, int *); #define netmap_tx_irq(_n, _q) netmap_rx_irq(_n, _q, NULL) -#ifdef __linux__ -#define bus_dmamap_sync(_a, _b, _c) // wmb() or rmb() ? -netdev_tx_t netmap_start_linux(struct sk_buff *skb, struct net_device *dev); -#endif #endif /* _NET_NETMAP_KERN_H_ */ |
