/* Mimics netmap's nm_nextpkt function. This version does not * release buffer in order to avoid extra copies. This has the * disadvantage of potentially holding up the ring if service times * are not equal. * * TODO: explore swapping buffers with a tx and rx ring with a 3rd buffer. * This may prevent packets with exceptionally long service time from * holding up the entire ring... */ static u_char* fp_netmap_nextpkt(struct nm_desc* d, struct nm_pkthdr* hdr) { int ring_i = d->cur_rx_ring; do { /* compute current ring to use */ struct netmap_ring* ring = NETMAP_RXRING(d->nifp, ring_i); if (!nm_ring_empty(ring)) { u_int buf_i = ring->cur; u_int buf_idx = ring->slot[buf_i].buf_idx; u_char* buf_ptr = (u_char*)NETMAP_BUF(ring, buf_idx); // __builtin_prefetch(buf); hdr->ts = ring->ts; hdr->len = hdr->caplen = ring->slot[buf_i].len; ring->cur = nm_ring_next(ring, buf_i); /* we could postpone advancing head if we want * to hold the buffer. This can be supported in * the future. */ // ring->head = ring->cur; d->cur_rx_ring = ring_i; return buf_ptr; } ring_i++; if (ring_i > d->last_rx_ring) ring_i = d->first_rx_ring; } while (ring_i != d->cur_rx_ring); return NULL; /* nothing found */ }
/* move packts from src to destination */ static int move(struct my_ring *src, struct my_ring *dst, u_int limit) { struct netmap_ring *txring, *rxring; u_int m = 0, si = src->begin, di = dst->begin; const char *msg = (src->queueid & NETMAP_SW_RING) ? "host->net" : "net->host"; while (si < src->end && di < dst->end) { rxring = NETMAP_RXRING(src->nifp, si); txring = NETMAP_TXRING(dst->nifp, di); ND("txring %p rxring %p", txring, rxring); if (rxring->avail == 0) { si++; continue; } if (txring->avail == 0) { di++; continue; } m += process_rings(rxring, txring, limit, msg); } return (m); }
/* The exported init function * * ... -net netmap,ifname="..." */ int net_init_netmap(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp) { const NetdevNetmapOptions *netmap_opts = &netdev->u.netmap; struct nm_desc *nmd; NetClientState *nc; Error *err = NULL; NetmapState *s; nmd = netmap_open(netmap_opts, &err); if (err) { error_propagate(errp, err); return -1; } /* Create the object. */ nc = qemu_new_net_client(&net_netmap_info, peer, "netmap", name); s = DO_UPCAST(NetmapState, nc, nc); s->nmd = nmd; s->tx = NETMAP_TXRING(nmd->nifp, 0); s->rx = NETMAP_RXRING(nmd->nifp, 0); s->vnet_hdr_len = 0; pstrcpy(s->ifname, sizeof(s->ifname), netmap_opts->ifname); netmap_read_poll(s, true); /* Initially only poll for reads. */ return 0; }
/* move packts from src to destination */ static int move(struct thr_ctx *th, struct nm_desc *src, struct nm_desc *dst, u_int limit) { struct netmap_ring *txring, *rxring; u_int m = 0, si = src->first_rx_ring, di = dst->first_tx_ring; const char *msg = (src->req.nr_ringid & NETMAP_SW_RING) ? "host->net" : "net->host"; while (si <= src->last_rx_ring && di <= dst->last_tx_ring) { rxring = NETMAP_RXRING(src->nifp, si); txring = NETMAP_TXRING(dst->nifp, di); ND("txring %p rxring %p", txring, rxring); if (nm_ring_empty(rxring)) { si++; continue; } if (nm_ring_empty(txring)) { di++; continue; } m += process_rings(th, rxring, txring, limit, msg); } return (m); }
static void netmap_read(evutil_socket_t fd, short event, void *data) { char *buf; int err, i, pkts, rx_rings; struct netmap_if *ifp; struct netmap_ring *nring; struct nm_if *nmif; nmif = (struct nm_if *)data; ifp = nmif->nm_if_ifp; rx_rings = ifp->ni_rx_rings; if (!nohostring && !nmif->nm_if_vale) rx_rings++; pkts = 0; for (i = 0; i < rx_rings; i++) { nring = NETMAP_RXRING(ifp, i); while (!nm_ring_empty(nring)) { buf = NETMAP_GET_BUF(nring); err = ether_input(nmif, i, buf, NETMAP_SLOT_LEN(nring)); /* Send the packet to hw <-> host bridge. */ if (!nohostring && err == 1) err = ether_bridge(nmif, i, buf, NETMAP_SLOT_LEN(nring)); NETMAP_RING_NEXT(nring); if (err < 0 || ++pkts == burst) goto done; } } done: if_netmap_txsync(); }
struct if_netmap_host_context * if_netmap_register_if(int nmfd, const char *ifname, unsigned int isvale, unsigned int qno) { struct if_netmap_host_context *ctx; ctx = calloc(1, sizeof(struct if_netmap_host_context)); if (NULL == ctx) return (NULL); ctx->fd = nmfd; ctx->isvale = isvale; ctx->ifname = ifname; /* * Disable TCP and checksum offload, which can impact throughput * and also cause packets to be dropped or modified gratuitously. * * Also disable VLAN offload/filtering - we want to talk straight to * the wire. * */ if (!ctx->isvale) { if (0 != if_netmap_set_offload(ctx, 0)) { goto fail; } if (0 != if_netmap_set_promisc(ctx, 1)) { goto fail; } } ctx->req.nr_version = NETMAP_API; ctx->req.nr_ringid = NETMAP_NO_TX_POLL | NETMAP_HW_RING | qno; snprintf(ctx->req.nr_name, sizeof(ctx->req.nr_name), "%s", ifname); if (-1 == ioctl(ctx->fd, NIOCREGIF, &ctx->req)) { goto fail; } ctx->mem = uhi_mmap(NULL, ctx->req.nr_memsize, UHI_PROT_READ | UHI_PROT_WRITE, UHI_MAP_NOCORE | UHI_MAP_SHARED, ctx->fd, 0); if (MAP_FAILED == ctx->mem) { goto fail; } ctx->hw_rx_ring = NETMAP_RXRING(NETMAP_IF(ctx->mem, ctx->req.nr_offset), qno); ctx->hw_tx_ring = NETMAP_TXRING(NETMAP_IF(ctx->mem, ctx->req.nr_offset), qno); /* NIOCREGIF will reset the hardware rings, but the reserved count * might still be non-zero from a previous user's activities */ ctx->hw_rx_ring->reserved = 0; return (ctx); fail: free(ctx); return(NULL); }
static struct peak_netmap * _peak_netmap_claim(void) { struct _peak_netmap *packet; struct netmap_ring *ring; struct my_ring *me; unsigned int j, si; for (j = 0; j < NETMAP_COUNT(); ++j) { me = self->me[j]; for (si = me->begin; si < me->end; ++si) { unsigned int i, idx; ring = NETMAP_RXRING(me->nifp, si); if (!ring->avail) { continue; } packet = NETPKT_GET(); if (!packet) { alert("netmap packet pool empty\n"); return (NULL); } bzero(packet, sizeof(*packet)); i = ring->cur; idx = ring->slot[i].buf_idx; if (idx < 2) { panic("%s bugus RX index %d at offset %d\n", me->nifp->ni_name, idx, i); } /* volatile internals */ packet->ring = ring; packet->i = i; /* external stuff */ packet->data.buf = NETMAP_BUF(ring, idx); packet->data.len = ring->slot[i].len; packet->data.ll = LINKTYPE_ETHERNET; packet->data.ts_ms = (int64_t)ring->ts.tv_sec * 1000 + (int64_t)ring->ts.tv_usec / 1000; packet->data.ts_unix = ring->ts.tv_sec; packet->data.ifname = me->ifname; return (NETPKT_TO_USER(packet)); } } return (NULL); }
struct nm_desc* usnet_init( struct nm_desc *gg_nmd, const char *dev_name, u_int flags) { struct nmreq nmr; struct nm_desc *nmd = NULL; struct netmap_if *nifp = NULL; struct netmap_ring *txr, *rxr; signal(SIGINT, sigint_h); bzero(&nmr, sizeof(nmr)); strcpy(nmr.nr_name, dev_name); // XXX: which netmap flags? //nmr.nr_flags = NR_REG_ALL_NIC; //| flags; printf("nm_open: %s\n", nmr.nr_name); nmd = nm_open(nmr.nr_name, &nmr, 0, NULL); if ( nmd == NULL ) { DEBUG("Cannot open interface %s", nmr.nr_name); exit(1); } nifp = nmd->nifp; txr = NETMAP_TXRING(nifp, 0); rxr = NETMAP_RXRING(nifp, 0); printf("nmreq info, name=%s, version=%d," " flags=%d, memsize=%d," " ni_tx_rings=%d, ni_rx_rings=%d, num_tx_slots=%d, num_rx_slot=%d \n", nifp->ni_name, nifp->ni_version, nifp->ni_flags, nmd->memsize, nifp->ni_tx_rings, nifp->ni_rx_rings, txr->num_slots, rxr->num_slots); memset(&g_config, 0, sizeof(g_config)); g_config.burst = 1000; g_config.tx_rate = 0; memset(&g_ipq, 0, sizeof(g_ipq)); usnet_init_internal(); usnet_route_init(); usnet_network_init(); usnet_udp_init(); usnet_ipv4_init(); usnet_socket_init(); return nmd; }
int nm_ring (char * ifname, int q, struct netmap_ring ** ring, int x, int w) { int fd; char * mem; struct nmreq nmr; struct netmap_if * nifp; /* open netmap for ring */ fd = open ("/dev/netmap", O_RDWR); if (fd < 0) { D ("unable to open /dev/netmap"); return -1; } memset (&nmr, 0, sizeof (nmr)); strcpy (nmr.nr_name, ifname); nmr.nr_version = NETMAP_API; nmr.nr_ringid = q | (NETMAP_NO_TX_POLL | NETMAP_DO_RX_POLL); if (w) nmr.nr_flags |= NR_REG_ONE_NIC; else nmr.nr_flags |= NR_REG_ALL_NIC; if (vale_rings && strncmp (ifname, "vale", 4) == 0) { nmr.nr_rx_rings = vale_rings; nmr.nr_tx_rings = vale_rings; } if (ioctl (fd, NIOCREGIF, &nmr) < 0) { D ("unable to register interface %s", ifname); return -1; } mem = mmap (NULL, nmr.nr_memsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (mem == MAP_FAILED) { D ("unable to mmap"); return -1; } nifp = NETMAP_IF (mem, nmr.nr_offset); if (x > 0) *ring = NETMAP_TXRING (nifp, q); else *ring = NETMAP_RXRING (nifp, q); return fd; }
static void receiver(lua_State *L, int cb_ref, struct nm_desc *d, unsigned int ring_id) { struct pollfd fds; struct netmap_ring *ring; unsigned int i, len; char *buf; time_t now; int pps; now = time(NULL); pps = 0; while (1) { fds.fd = d->fd; fds.events = POLLIN; int r = poll(&fds, 1, 1000); if (r < 0) { if (errno != EINTR) { perror("poll()"); exit(3); } } if (time(NULL) > now) { printf("[+] receiving %d pps\n", pps); pps = 0; now = time(NULL); } ring = NETMAP_RXRING(d->nifp, ring_id); while (!nm_ring_empty(ring)) { i = ring->cur; buf = NETMAP_BUF(ring, ring->slot[i].buf_idx); len = ring->slot[i].len; pps++; if (filter_packet(L, cb_ref, buf, len)) { // forward packet to kernel ring->flags |= NR_FORWARD; ring->slot[i].flags |= NS_FORWARD; printf("+++ PASS\n"); } else { // drop packet printf("--- DROP\n"); } ring->head = ring->cur = nm_ring_next(ring, i); } } }
/* * Note: this thread is the only one pulling packets off of any * given netmap instance */ static void * receiver(void *arg) { struct virtif_user *viu = arg; struct iovec iov; struct netmap_if *nifp = viu->nm_nifp; struct netmap_ring *ring = NETMAP_RXRING(nifp, 0); struct netmap_slot *slot; struct pollfd pfd; int prv; rumpuser_component_kthread(); for (;;) { pfd.fd = viu->viu_fd; pfd.events = POLLIN; if (viu->viu_dying) { break; } prv = 0; while (nm_ring_empty(ring) && prv == 0) { DPRINTF(("receive pkt via netmap\n")); prv = poll(&pfd, 1, 1000); if (prv > 0 || (prv < 0 && errno != EAGAIN)) break; } #if 0 /* XXX: report non-transient errors */ if (ring->avail == 0) { rv = errno; break; } #endif slot = &ring->slot[ring->cur]; DPRINTF(("got pkt of size %d\n", slot->len)); iov.iov_base = NETMAP_BUF(ring, slot->buf_idx); iov.iov_len = slot->len; /* XXX: allow batch processing */ rumpuser_component_schedule(NULL); VIF_DELIVERPKT(viu->viu_virtifsc, &iov, 1); rumpuser_component_unschedule(); ring->head = ring->cur = nm_ring_next(ring, ring->cur); } rumpuser_component_kthread_release(); return NULL; }
static void free_buffers(void) { struct netmap_ring *ring; if (pa == NULL) return; ring = NETMAP_RXRING(pa->nifp, pa->first_rx_ring); dedup_get_fifo_buffers(&dedup, ring, &pa->nifp->ni_bufs_head); nm_close(pa); nm_close(pb); }
static int netmap_port_open(uint32_t idx) { int err; struct netmap_port *port; struct nmreq req; port = ports.p + idx; port->fd = rte_netmap_open("/dev/netmap", O_RDWR); snprintf(req.nr_name, sizeof(req.nr_name), "%s", port->str); req.nr_version = NETMAP_API; req.nr_ringid = 0; err = rte_netmap_ioctl(port->fd, NIOCGINFO, &req); if (err) { printf("[E] NIOCGINFO ioctl failed (error %d)\n", err); return err; } snprintf(req.nr_name, sizeof(req.nr_name), "%s", port->str); req.nr_version = NETMAP_API; req.nr_ringid = 0; err = rte_netmap_ioctl(port->fd, NIOCREGIF, &req); if (err) { printf("[E] NIOCREGIF ioctl failed (error %d)\n", err); return err; } /* mmap only once. */ if (ports.mem == NULL) ports.mem = rte_netmap_mmap(NULL, req.nr_memsize, PROT_WRITE | PROT_READ, MAP_PRIVATE, port->fd, 0); if (ports.mem == MAP_FAILED) { printf("[E] NETMAP mmap failed for fd: %d)\n", port->fd); return -ENOMEM; } port->nmif = NETMAP_IF(ports.mem, req.nr_offset); port->tx_ring = NETMAP_TXRING(port->nmif, 0); port->rx_ring = NETMAP_RXRING(port->nmif, 0); return 0; }
/* * how many packets on this set of queues ? * * Receive: how many frames in the receive path. * Transmit: how many slots are available to transmit. */ int pkt_queued(struct nm_desc *d, int tx) { u_int i, tot = 0; if (tx) { for (i = d->first_tx_ring; i <= d->last_tx_ring; i++) { tot += nm_ring_space(NETMAP_TXRING(d->nifp, i)); } } else { for (i = d->first_rx_ring; i <= d->last_rx_ring; i++) { tot += nm_ring_space(NETMAP_RXRING(d->nifp, i)); } } return tot; }
/** * Open netmap ring. * @param[in,out] ring * @param[in] ringid Ring ID. * @param[in] cached_mmap_mem Pointer to already mmapped shared netmap memory. */ int znm_open(struct znm_ring *ring, const char *ifname, uint16_t ringid, void *cached_mmap_mem) { struct nmreq req; ring->fd = open(ZNM_DEVICE, O_RDWR); if (ring->fd < 0) { ZERO_ELOG(LOG_ERR, "Unable to open %s", ZNM_DEVICE); return -1; } memset(&req, 0, sizeof(req)); req.nr_version = NETMAP_API; strncpy(req.nr_name, ifname, sizeof(req.nr_name)); req.nr_ringid = ringid; req.nr_flags = NR_REG_ONE_NIC; if (0 == ioctl(ring->fd, NIOCGINFO, &req)) { ring->memsize = req.nr_memsize; if (0 == ioctl(ring->fd, NIOCREGIF, &req)) { if (NULL != cached_mmap_mem) { ring->mem = cached_mmap_mem; } else { ring->mem = mmap(0, ring->memsize, PROT_WRITE | PROT_READ, MAP_SHARED, ring->fd, 0); ring->own_mmap = 1; } if (MAP_FAILED != ring->mem) { ZERO_LOG(LOG_DEBUG, "Attached to %s HW ring %u", ifname, ringid); ring->nifp = NETMAP_IF(ring->mem, req.nr_offset); ring->tx = NETMAP_TXRING(ring->nifp, ringid); ring->rx = NETMAP_RXRING(ring->nifp, ringid); // Success. return 0; } else { ring->mem = NULL; ZERO_ELOG(LOG_ERR, "Unable to mmap netmap shared memory"); } } else { ZERO_ELOG(LOG_ERR, "Unable to register %s with netmap", ifname); } } else { ZERO_ELOG(LOG_ERR, "Unable to query netmap for '%s' info", ifname); } close(ring->fd); return -1; }
static int rx(struct thread_args_t *arg, struct nm_desc *src, u_int limit) { struct netmap_ring *rxring; u_int si = src->first_rx_ring; while (si <= src->last_rx_ring) { rxring = NETMAP_RXRING(src->nifp, si); if (nm_ring_empty(rxring)) { si++; continue; } g_rings_counts[si] ++; u_int m = process_rings(rxring, limit); arg->count += m; } return (1); }
int usnet_setup(int argc, char *argv[]) { //struct nm_desc *nmd; char *p; int ret; (void)argc; (void)argv; (void)p; //(void)nmd; setaffinity(0); ret = usnet_get_options(argc, argv); if ( ret < 0 ) { show_help(); exit(0); } g_nmd = usnet_init(g_nmd, (char*)g_interface, 0); if (1) { struct netmap_if *nifp = g_nmd->nifp; struct nmreq *req = &g_nmd->req; int i; D("fisrt_tx_ring=%d, last_tx_ring=%d", g_nmd->first_tx_ring, g_nmd->last_tx_ring); D("nifp at offset %d, %d tx %d rx region %d", req->nr_offset, req->nr_tx_rings, req->nr_rx_rings, req->nr_arg2); for (i = 0; i <= req->nr_tx_rings; i++) { struct netmap_ring *ring = NETMAP_TXRING(nifp, i); D(" TX%d at 0x%p slots %d", i, (void *)((char *)ring - (char *)nifp), ring->num_slots); } for (i = 0; i <= req->nr_rx_rings; i++) { struct netmap_ring *ring = NETMAP_RXRING(nifp, i); D(" RX%d at 0x%p slots %d", i, (void *)((char *)ring - (char *)nifp), ring->num_slots); } } return 0; }
int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user) { struct pcap_ring *pme = p; struct my_ring *me = &pme->me; int got = 0; u_int si; ND("cnt %d", cnt); if (cnt == 0) cnt = -1; /* scan all rings */ for (si = me->begin; si < me->end; si++) { struct netmap_ring *ring = NETMAP_RXRING(me->nifp, si); ND("ring has %d pkts", ring->avail); if (ring->avail == 0) continue; pme->hdr.ts = ring->ts; /* * XXX a proper prefetch should be done as * prefetch(i); callback(i-1); ... */ while ((cnt == -1 || cnt != got) && ring->avail > 0) { u_int i = ring->cur; u_int idx = ring->slot[i].buf_idx; if (idx < 2) { D("%s bogus RX index %d at offset %d", me->nifp->ni_name, idx, i); sleep(2); } u_char *buf = (u_char *)NETMAP_BUF(ring, idx); prefetch(buf); pme->hdr.len = pme->hdr.caplen = ring->slot[i].len; // D("call %p len %d", p, me->hdr.len); callback(user, &pme->hdr, buf); ring->cur = NETMAP_RING_NEXT(ring, i); ring->avail--; got++; } } pme->st.ps_recv += got; return got; }
static __inline int pci_vtnet_netmap_readv(struct nm_desc *nmd, struct iovec *iov, int iovcnt) { int len = 0; int i = 0; int r; for (r = nmd->cur_rx_ring; ; ) { struct netmap_ring *ring = NETMAP_RXRING(nmd->nifp, r); uint32_t cur, idx; char *buf; size_t left; if (nm_ring_empty(ring)) { r++; if (r > nmd->last_rx_ring) r = nmd->first_rx_ring; if (r == nmd->cur_rx_ring) break; continue; } cur = ring->cur; idx = ring->slot[cur].buf_idx; buf = NETMAP_BUF(ring, idx); left = ring->slot[cur].len; for (i = 0; i < iovcnt && left > 0; i++) { if (iov[i].iov_len > left) iov[i].iov_len = left; memcpy(iov[i].iov_base, &buf[len], iov[i].iov_len); len += iov[i].iov_len; left -= iov[i].iov_len; } ring->head = ring->cur = nm_ring_next(ring, cur); nmd->cur_rx_ring = r; ioctl(nmd->fd, NIOCRXSYNC, NULL); break; } for (; i < iovcnt; i++) iov[i].iov_len = 0; return (len); }
/* * how many packets on this set of queues ? */ static int pkt_queued(struct my_ring *me, int tx) { u_int i, tot = 0; ND("me %p begin %d end %d", me, me->begin, me->end); for (i = me->begin; i < me->end; i++) { struct netmap_ring *ring = tx ? NETMAP_TXRING(me->nifp, i) : NETMAP_RXRING(me->nifp, i); tot += ring->avail; } if (0 && verbose && tot && !tx) D("ring %s %s %s has %d avail at %d", me->ifname, tx ? "tx": "rx", me->end >= me->nifp->ni_tx_rings ? // XXX who comes first ? "host":"net", tot, NETMAP_TXRING(me->nifp, me->begin)->cur); return tot; }
void netmap_thread(struct nm_desc* netmap_descriptor, int thread_number) { struct nm_pkthdr h; u_char* buf; struct pollfd fds; fds.fd = netmap_descriptor->fd;//NETMAP_FD(netmap_descriptor); fds.events = POLLIN; struct netmap_ring *rxring = NULL; struct netmap_if *nifp = netmap_descriptor->nifp; printf("Reading from fd %d thread id: %d\n", netmap_descriptor->fd, thread_number); for (;;) { // We will wait 1000 microseconds for retry, for infinite timeout please use -1 int poll_result = poll(&fds, 1, 1000); if (poll_result == 0) { // printf("poll return 0 return code\n"); continue; } if (poll_result == -1) { printf("poll failed with return code -1\n"); } for (int i = netmap_descriptor->first_rx_ring; i <= netmap_descriptor->last_rx_ring; i++) { //printf("Check ring %d from thread %d\n", i, thread_number); rxring = NETMAP_RXRING(nifp, i); if (nm_ring_empty(rxring)) { continue; } int m = receive_packets(rxring); } //while ( (buf = nm_nextpkt(netmap_descriptor, &h)) ) { // consume_pkt(buf, h.len); //} } //nm_close(netmap_descriptor); }
static inline int rx_sync_if(uint32_t port) { uint16_t burst; uint32_t i, rc; struct netmap_if *nifp; struct netmap_ring *r; nifp = ports[port].nmif; burst = ports[port].rx_burst; rc = 0; for (i = 0; i < nifp->ni_rx_rings + 1; i++) { r = NETMAP_RXRING(nifp, i); rx_sync_ring(r, (uint8_t)port, (uint16_t)i, burst); rc += r->avail; } return (rc); }
/*----------------------------------------------------------------------------*/ int32_t netmap_recv_pkts(struct mtcp_thread_context *ctxt, int ifidx) { struct netmap_private_context *npc; struct nm_desc *d; npc = (struct netmap_private_context *)ctxt->io_private_context; d = npc->local_nmd[ifidx]; int p = 0; int c, got = 0, ri = d->cur_rx_ring; int n = d->last_rx_ring - d->first_rx_ring + 1; int cnt = MAX_PKT_BURST; for (c = 0; c < n && cnt != got && npc->dev_poll_flag[ifidx]; c++) { /* compute current ring to use */ struct netmap_ring *ring; ri = d->cur_rx_ring + c; if (ri > d->last_rx_ring) ri = d->first_rx_ring; ring = NETMAP_RXRING(d->nifp, ri); for ( ; !nm_ring_empty(ring) && cnt != got; got++) { u_int i = ring->cur; u_int idx = ring->slot[i].buf_idx; npc->rcv_pktbuf[p] = (u_char *)NETMAP_BUF(ring, idx); npc->rcv_pkt_len[p] = ring->slot[i].len; p++; ring->head = ring->cur = nm_ring_next(ring, i); } } d->cur_rx_ring = ri; npc->dev_poll_flag[ifidx] = 0; return p; }
static void * pinger_body(void *data) { struct targ *targ = (struct targ *) data; struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; struct netmap_if *nifp = targ->nmd->nifp; int i, rx = 0, n = targ->g->npackets; void *frame; int size; uint32_t sent = 0; struct timespec ts, now, last_print; uint32_t count = 0, min = 1000000000, av = 0; frame = &targ->pkt; frame += sizeof(targ->pkt.vh) - targ->g->virt_header; size = targ->g->pkt_size + targ->g->virt_header; if (targ->g->nthreads > 1) { D("can only ping with 1 thread"); return NULL; } clock_gettime(CLOCK_REALTIME_PRECISE, &last_print); now = last_print; while (n == 0 || (int)sent < n) { struct netmap_ring *ring = NETMAP_TXRING(nifp, 0); struct netmap_slot *slot; char *p; for (i = 0; i < 1; i++) { /* XXX why the loop for 1 pkt ? */ slot = &ring->slot[ring->cur]; slot->len = size; p = NETMAP_BUF(ring, slot->buf_idx); if (nm_ring_empty(ring)) { D("-- ouch, cannot send"); } else { struct tstamp *tp; nm_pkt_copy(frame, p, size); clock_gettime(CLOCK_REALTIME_PRECISE, &ts); bcopy(&sent, p+42, sizeof(sent)); tp = (struct tstamp *)(p+46); tp->sec = (uint32_t)ts.tv_sec; tp->nsec = (uint32_t)ts.tv_nsec; sent++; ring->head = ring->cur = nm_ring_next(ring, ring->cur); } } /* should use a parameter to decide how often to send */ if (poll(&pfd, 1, 3000) <= 0) { D("poll error/timeout on queue %d: %s", targ->me, strerror(errno)); continue; } /* see what we got back */ for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) { ring = NETMAP_RXRING(nifp, i); while (!nm_ring_empty(ring)) { uint32_t seq; struct tstamp *tp; slot = &ring->slot[ring->cur]; p = NETMAP_BUF(ring, slot->buf_idx); clock_gettime(CLOCK_REALTIME_PRECISE, &now); bcopy(p+42, &seq, sizeof(seq)); tp = (struct tstamp *)(p+46); ts.tv_sec = (time_t)tp->sec; ts.tv_nsec = (long)tp->nsec; ts.tv_sec = now.tv_sec - ts.tv_sec; ts.tv_nsec = now.tv_nsec - ts.tv_nsec; if (ts.tv_nsec < 0) { ts.tv_nsec += 1000000000; ts.tv_sec--; } if (1) D("seq %d/%d delta %d.%09d", seq, sent, (int)ts.tv_sec, (int)ts.tv_nsec); if (ts.tv_nsec < (int)min) min = ts.tv_nsec; count ++; av += ts.tv_nsec; ring->head = ring->cur = nm_ring_next(ring, ring->cur); rx++; } } //D("tx %d rx %d", sent, rx); //usleep(100000); ts.tv_sec = now.tv_sec - last_print.tv_sec; ts.tv_nsec = now.tv_nsec - last_print.tv_nsec; if (ts.tv_nsec < 0) { ts.tv_nsec += 1000000000; ts.tv_sec--; } if (ts.tv_sec >= 1) { D("count %d min %d av %d", count, min, av/count); count = 0; av = 0; min = 100000000; last_print = now; } } return NULL; } /* * reply to ping requests */ static void * ponger_body(void *data) { struct targ *targ = (struct targ *) data; struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; struct netmap_if *nifp = targ->nmd->nifp; struct netmap_ring *txring, *rxring; int i, rx = 0, sent = 0, n = targ->g->npackets; if (targ->g->nthreads > 1) { D("can only reply ping with 1 thread"); return NULL; } D("understood ponger %d but don't know how to do it", n); while (n == 0 || sent < n) { uint32_t txcur, txavail; //#define BUSYWAIT #ifdef BUSYWAIT ioctl(pfd.fd, NIOCRXSYNC, NULL); #else if (poll(&pfd, 1, 1000) <= 0) { D("poll error/timeout on queue %d: %s", targ->me, strerror(errno)); continue; } #endif txring = NETMAP_TXRING(nifp, 0); txcur = txring->cur; txavail = nm_ring_space(txring); /* see what we got back */ for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) { rxring = NETMAP_RXRING(nifp, i); while (!nm_ring_empty(rxring)) { uint16_t *spkt, *dpkt; uint32_t cur = rxring->cur; struct netmap_slot *slot = &rxring->slot[cur]; char *src, *dst; src = NETMAP_BUF(rxring, slot->buf_idx); //D("got pkt %p of size %d", src, slot->len); rxring->head = rxring->cur = nm_ring_next(rxring, cur); rx++; if (txavail == 0) continue; dst = NETMAP_BUF(txring, txring->slot[txcur].buf_idx); /* copy... */ dpkt = (uint16_t *)dst; spkt = (uint16_t *)src; nm_pkt_copy(src, dst, slot->len); dpkt[0] = spkt[3]; dpkt[1] = spkt[4]; dpkt[2] = spkt[5]; dpkt[3] = spkt[0]; dpkt[4] = spkt[1]; dpkt[5] = spkt[2]; txring->slot[txcur].len = slot->len; /* XXX swap src dst mac */ txcur = nm_ring_next(txring, txcur); txavail--; sent++; } } txring->head = txring->cur = txcur; targ->count = sent; #ifdef BUSYWAIT ioctl(pfd.fd, NIOCTXSYNC, NULL); #endif //D("tx %d rx %d", sent, rx); } return NULL; } static __inline int timespec_ge(const struct timespec *a, const struct timespec *b) { if (a->tv_sec > b->tv_sec) return (1); if (a->tv_sec < b->tv_sec) return (0); if (a->tv_nsec >= b->tv_nsec) return (1); return (0); } static __inline struct timespec timeval2spec(const struct timeval *a) { struct timespec ts = { .tv_sec = a->tv_sec, .tv_nsec = a->tv_usec * 1000 }; return ts; } static __inline struct timeval timespec2val(const struct timespec *a) { struct timeval tv = { .tv_sec = a->tv_sec, .tv_usec = a->tv_nsec / 1000 }; return tv; } static __inline struct timespec timespec_add(struct timespec a, struct timespec b) { struct timespec ret = { a.tv_sec + b.tv_sec, a.tv_nsec + b.tv_nsec }; if (ret.tv_nsec >= 1000000000) { ret.tv_sec++; ret.tv_nsec -= 1000000000; } return ret; } static __inline struct timespec timespec_sub(struct timespec a, struct timespec b) { struct timespec ret = { a.tv_sec - b.tv_sec, a.tv_nsec - b.tv_nsec }; if (ret.tv_nsec < 0) { ret.tv_sec--; ret.tv_nsec += 1000000000; } return ret; } /* * wait until ts, either busy or sleeping if more than 1ms. * Return wakeup time. */ static struct timespec wait_time(struct timespec ts) { for (;;) { struct timespec w, cur; clock_gettime(CLOCK_REALTIME_PRECISE, &cur); w = timespec_sub(ts, cur); if (w.tv_sec < 0) return cur; else if (w.tv_sec > 0 || w.tv_nsec > 1000000) poll(NULL, 0, 1); } } static void * sender_body(void *data) { struct targ *targ = (struct targ *) data; struct pollfd pfd = { .fd = targ->fd, .events = POLLOUT }; struct netmap_if *nifp; struct netmap_ring *txring; int i, n = targ->g->npackets / targ->g->nthreads; int64_t sent = 0; int options = targ->g->options | OPT_COPY; struct timespec nexttime = { 0, 0}; // XXX silence compiler int rate_limit = targ->g->tx_rate; struct pkt *pkt = &targ->pkt; void *frame; int size; if (targ->frame == NULL) { frame = pkt; frame += sizeof(pkt->vh) - targ->g->virt_header; size = targ->g->pkt_size + targ->g->virt_header; } else { frame = targ->frame; size = targ->g->pkt_size; } D("start, fd %d main_fd %d", targ->fd, targ->g->main_fd); if (setaffinity(targ->thread, targ->affinity)) goto quit; /* main loop.*/ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); if (rate_limit) { targ->tic = timespec_add(targ->tic, (struct timespec){2,0}); targ->tic.tv_nsec = 0; wait_time(targ->tic); nexttime = targ->tic; } if (targ->g->dev_type == DEV_TAP) { D("writing to file desc %d", targ->g->main_fd); for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { if (write(targ->g->main_fd, frame, size) != -1) sent++; update_addresses(pkt, targ->g); if (i > 10000) { targ->count = sent; i = 0; } } #ifndef NO_PCAP } else if (targ->g->dev_type == DEV_PCAP) { pcap_t *p = targ->g->p; for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { if (pcap_inject(p, frame, size) != -1) sent++; update_addresses(pkt, targ->g); if (i > 10000) { targ->count = sent; i = 0; } } #endif /* NO_PCAP */ } else { int tosend = 0; int frags = targ->g->frags; nifp = targ->nmd->nifp; while (!targ->cancel && (n == 0 || sent < n)) { if (rate_limit && tosend <= 0) { tosend = targ->g->burst; nexttime = timespec_add(nexttime, targ->g->tx_period); wait_time(nexttime); } /* * wait for available room in the send queue(s) */ if (poll(&pfd, 1, 2000) <= 0) { if (targ->cancel) break; D("poll error/timeout on queue %d: %s", targ->me, strerror(errno)); // goto quit; } if (pfd.revents & POLLERR) { D("poll error"); goto quit; } /* * scan our queues and send on those with room */ if (options & OPT_COPY && sent > 100000 && !(targ->g->options & OPT_COPY) ) { D("drop copy"); options &= ~OPT_COPY; } for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) { int m, limit = rate_limit ? tosend : targ->g->burst; if (n > 0 && n - sent < limit) limit = n - sent; txring = NETMAP_TXRING(nifp, i); if (nm_ring_empty(txring)) continue; if (frags > 1) limit = ((limit + frags - 1) / frags) * frags; m = send_packets(txring, pkt, frame, size, targ->g, limit, options, frags); ND("limit %d tail %d frags %d m %d", limit, txring->tail, frags, m); sent += m; targ->count = sent; if (rate_limit) { tosend -= m; if (tosend <= 0) break; } } } /* flush any remaining packets */ D("flush tail %d head %d on thread %p", txring->tail, txring->head, pthread_self()); ioctl(pfd.fd, NIOCTXSYNC, NULL); /* final part: wait all the TX queues to be empty. */ for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) { txring = NETMAP_TXRING(nifp, i); while (nm_tx_pending(txring)) { RD(5, "pending tx tail %d head %d on ring %d", txring->tail, txring->head, i); ioctl(pfd.fd, NIOCTXSYNC, NULL); usleep(1); /* wait 1 tick */ } } } /* end DEV_NETMAP */ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); targ->completed = 1; targ->count = sent; quit: /* reset the ``used`` flag. */ targ->used = 0; return (NULL); } #ifndef NO_PCAP static void receive_pcap(u_char *user, const struct pcap_pkthdr * h, const u_char * bytes) { int *count = (int *)user; (void)h; /* UNUSED */ (void)bytes; /* UNUSED */ (*count)++; } #endif /* !NO_PCAP */ static int receive_packets(struct netmap_ring *ring, u_int limit, int dump) { u_int cur, rx, n; cur = ring->cur; n = nm_ring_space(ring); if (n < limit) limit = n; for (rx = 0; rx < limit; rx++) { struct netmap_slot *slot = &ring->slot[cur]; char *p = NETMAP_BUF(ring, slot->buf_idx); if (dump) dump_payload(p, slot->len, ring, cur); cur = nm_ring_next(ring, cur); } ring->head = ring->cur = cur; return (rx); } static void * receiver_body(void *data) { struct targ *targ = (struct targ *) data; struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; struct netmap_if *nifp; struct netmap_ring *rxring; int i; uint64_t received = 0; if (setaffinity(targ->thread, targ->affinity)) goto quit; D("reading from %s fd %d main_fd %d", targ->g->ifname, targ->fd, targ->g->main_fd); /* unbounded wait for the first packet. */ for (;!targ->cancel;) { i = poll(&pfd, 1, 1000); if (i > 0 && !(pfd.revents & POLLERR)) break; RD(1, "waiting for initial packets, poll returns %d %d", i, pfd.revents); } /* main loop, exit after 1s silence */ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); if (targ->g->dev_type == DEV_TAP) { while (!targ->cancel) { char buf[MAX_BODYSIZE]; /* XXX should we poll ? */ if (read(targ->g->main_fd, buf, sizeof(buf)) > 0) targ->count++; } #ifndef NO_PCAP } else if (targ->g->dev_type == DEV_PCAP) { while (!targ->cancel) { /* XXX should we poll ? */ pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, (u_char *)&targ->count); } #endif /* !NO_PCAP */ } else { int dump = targ->g->options & OPT_DUMP; nifp = targ->nmd->nifp; while (!targ->cancel) { /* Once we started to receive packets, wait at most 1 seconds before quitting. */ if (poll(&pfd, 1, 1 * 1000) <= 0 && !targ->g->forever) { clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); targ->toc.tv_sec -= 1; /* Subtract timeout time. */ goto out; } if (pfd.revents & POLLERR) { D("poll err"); goto quit; } for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) { int m; rxring = NETMAP_RXRING(nifp, i); if (nm_ring_empty(rxring)) continue; m = receive_packets(rxring, targ->g->burst, dump); received += m; } targ->count = received; } } clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); out: targ->completed = 1; targ->count = received; quit: /* reset the ``used`` flag. */ targ->used = 0; return (NULL); } /* very crude code to print a number in normalized form. * Caller has to make sure that the buffer is large enough. */ static const char * norm(char *buf, double val) { char *units[] = { "", "K", "M", "G", "T" }; u_int i; for (i = 0; val >=1000 && i < sizeof(units)/sizeof(char *) - 1; i++) val /= 1000; sprintf(buf, "%.2f %s", val, units[i]); return buf; } static void tx_output(uint64_t sent, int size, double delta) { double bw, raw_bw, pps; char b1[40], b2[80], b3[80]; printf("Sent %llu packets, %d bytes each, in %.2f seconds.\n", (unsigned long long)sent, size, delta); if (delta == 0) delta = 1e-6; if (size < 60) /* correct for min packet size */ size = 60; pps = sent / delta; bw = (8.0 * size * sent) / delta; /* raw packets have4 bytes crc + 20 bytes framing */ raw_bw = (8.0 * (size + 24) * sent) / delta; printf("Speed: %spps Bandwidth: %sbps (raw %sbps)\n", norm(b1, pps), norm(b2, bw), norm(b3, raw_bw) ); } static void rx_output(uint64_t received, double delta) { double pps; char b1[40]; printf("Received %llu packets, in %.2f seconds.\n", (unsigned long long) received, delta); if (delta == 0) delta = 1e-6; pps = received / delta; printf("Speed: %spps\n", norm(b1, pps)); }
static int thr_run_loop(struct thr_ctx *th) { struct pollfd pollfd[2]; /* setup poll(2) variables. */ memset(pollfd, 0, sizeof(pollfd)); pollfd[0].fd = th->pa->fd; pollfd[1].fd = th->pb->fd; D("Ready to go, %s 0x%x/%d <-> %s 0x%x/%d.", th->pa->req.nr_name, th->pa->first_rx_ring, th->pa->req.nr_rx_rings, th->pb->req.nr_name, th->pb->first_rx_ring, th->pb->req.nr_rx_rings); while (!do_abort) { int n0, n1, ret; pollfd[0].events = pollfd[1].events = 0; pollfd[0].revents = pollfd[1].revents = 0; /* Always poll for read readiness */ pollfd[0].events = POLLIN; pollfd[1].events = POLLIN; /* * If there's no space in the destination * ring, select for POLLOUT. * * Otherwise, an ioctl() will be done to do a * hard txsync, so as to make space for subsequent * read packets. */ if (pkt_queued(th->pa, 1) == 0) pollfd[0].events |= POLLOUT; if (pkt_queued(th->pb, 1) == 0) pollfd[1].events |= POLLOUT; /* * This at least triggers the read check, and * will also schedule TX frames out if needed. */ ret = poll(pollfd, 2, 2500); if (ret <= 0 || verbose) { D("poll %s [0] ev %x %x rx %d@%d tx %d," " [1] ev %x %x rx %d@%d tx %d", ret <= 0 ? "timeout" : "ok", pollfd[0].events, pollfd[0].revents, pkt_queued(th->pa, 0), NETMAP_RXRING(th->pa->nifp, th->pa->cur_rx_ring)->cur, pkt_queued(th->pa, 1), pollfd[1].events, pollfd[1].revents, pkt_queued(th->pb, 0), NETMAP_RXRING(th->pb->nifp, th->pb->cur_rx_ring)->cur, pkt_queued(th->pb, 1) ); } if (ret < 0) continue; if (pollfd[0].revents & POLLERR) { struct netmap_ring *rx = NETMAP_RXRING(th->pa->nifp, th->pa->cur_rx_ring); D("error on fd0, rx [%d,%d,%d)", rx->head, rx->cur, rx->tail); } if (pollfd[1].revents & POLLERR) { struct netmap_ring *rx = NETMAP_RXRING(th->pb->nifp, th->pb->cur_rx_ring); D("error on fd1, rx [%d,%d,%d)", rx->head, rx->cur, rx->tail); } /* * Next: the poll transmit side descriptors are only * reclaimed when it's /just about/ to run out of * descriptors (ie, it's done lazily.) * * So if we're about to run out transmit descriptor slots, * don't wait until we fill the transmit side to have * things reaped; just do a TXSYNC now. * * The poll() has already updated the driver side idea * of transmit/receive availability, so check to see * whether we have space. */ /* * Check to see if there's enough transmit slots in th->pb */ if (pkt_queued(th->pa, 0) > pkt_queued(th->pb, 1)) { /* XXX spinloop? */ while (ioctl(th->pb->fd, NIOCTXSYNC, NULL) != 0) usleep(1); } /* * Check to see if there's enough transmit slots in th->pa */ if (pkt_queued(th->pb, 0) > pkt_queued(th->pa, 1)) { /* XXX spinloop? */ while (ioctl(th->pa->fd, NIOCTXSYNC, NULL) != 0) usleep(1); } /* If we're read-ready, /then/ move */ if (pollfd[1].revents & POLLIN) { move(th, th->pb, th->pa, th->burst); } if (pollfd[0].revents & POLLIN) { move(th, th->pa, th->pb, th->burst); } /* * There's no need to call the ioctl() to flush; * the next trip through poll() with POLLIN set * will update the transmit pointer for us. */ } return (0); }
int main(int arc, char **argv) { int i; struct glob_arg g; int ch; int wait_link = 2; int devqueues = 1; /* how many device queues */ bzero(&g, sizeof(g)); g.main_fd = -1; g.td_body = receiver_body; g.report_interval = 1000; /* report interval */ g.affinity = -1; /* ip addresses can also be a range x.x.x.x-x.x.x.y */ g.src_ip.name = "10.0.0.1"; g.dst_ip.name = "10.1.0.1"; g.dst_mac.name = "ff:ff:ff:ff:ff:ff"; g.src_mac.name = NULL; g.pkt_size = 60; g.burst = 512; // default g.nthreads = 1; g.cpus = 1; g.forever = 1; g.tx_rate = 0; g.frags = 1; g.nmr_config = ""; g.virt_header = 0; while ( (ch = getopt(arc, argv, "a:f:F:n:i:Il:d:s:D:S:b:c:o:p:T:w:WvR:XC:H:e:m:P:zZ")) != -1) { struct sf *fn; switch(ch) { default: D("bad option %c %s", ch, optarg); usage(); break; case 'n': g.npackets = atoi(optarg); break; case 'F': i = atoi(optarg); if (i < 1 || i > 63) { D("invalid frags %d [1..63], ignore", i); break; } g.frags = i; break; case 'f': for (fn = func; fn->key; fn++) { if (!strcmp(fn->key, optarg)) break; } if (fn->key) g.td_body = fn->f; else D("unrecognised function %s", optarg); break; case 'o': /* data generation options */ g.options = atoi(optarg); break; case 'a': /* force affinity */ g.affinity = atoi(optarg); break; case 'i': /* interface */ /* a prefix of tap: netmap: or pcap: forces the mode. * otherwise we guess */ D("interface is %s", optarg); if (strlen(optarg) > MAX_IFNAMELEN - 8) { D("ifname too long %s", optarg); break; } strcpy(g.ifname, optarg); if (!strcmp(optarg, "null")) { g.dev_type = DEV_NETMAP; g.dummy_send = 1; } else if (!strncmp(optarg, "tap:", 4)) { g.dev_type = DEV_TAP; strcpy(g.ifname, optarg + 4); } else if (!strncmp(optarg, "pcap:", 5)) { g.dev_type = DEV_PCAP; strcpy(g.ifname, optarg + 5); } else if (!strncmp(optarg, "netmap:", 7) || !strncmp(optarg, "vale", 4)) { g.dev_type = DEV_NETMAP; } else if (!strncmp(optarg, "tap", 3)) { g.dev_type = DEV_TAP; } else { /* prepend netmap: */ g.dev_type = DEV_NETMAP; sprintf(g.ifname, "netmap:%s", optarg); } break; case 'I': g.options |= OPT_INDIRECT; /* XXX use indirect buffer */ break; case 'l': /* pkt_size */ g.pkt_size = atoi(optarg); break; case 'd': g.dst_ip.name = optarg; break; case 's': g.src_ip.name = optarg; break; case 'T': /* report interval */ g.report_interval = atoi(optarg); break; case 'w': wait_link = atoi(optarg); break; case 'W': /* XXX changed default */ g.forever = 0; /* do not exit rx even with no traffic */ break; case 'b': /* burst */ g.burst = atoi(optarg); break; case 'c': g.cpus = atoi(optarg); break; case 'p': g.nthreads = atoi(optarg); break; case 'D': /* destination mac */ g.dst_mac.name = optarg; break; case 'S': /* source mac */ g.src_mac.name = optarg; break; case 'v': verbose++; break; case 'R': g.tx_rate = atoi(optarg); break; case 'X': g.options |= OPT_DUMP; break; case 'C': g.nmr_config = strdup(optarg); break; case 'H': g.virt_header = atoi(optarg); break; case 'e': /* extra bufs */ g.extra_bufs = atoi(optarg); break; case 'm': if (strcmp(optarg, "tx") == 0) { g.options |= OPT_MONITOR_TX; } else if (strcmp(optarg, "rx") == 0) { g.options |= OPT_MONITOR_RX; } else { D("unrecognized monitor mode %s", optarg); } break; case 'P': g.packet_file = strdup(optarg); break; case 'z': g.options |= OPT_RANDOM_SRC; break; case 'Z': g.options |= OPT_RANDOM_DST; break; } } if (strlen(g.ifname) <=0 ) { D("missing ifname"); usage(); } i = system_ncpus(); if (g.cpus < 0 || g.cpus > i) { D("%d cpus is too high, have only %d cpus", g.cpus, i); usage(); } if (g.cpus == 0) g.cpus = i; if (g.pkt_size < 16 || g.pkt_size > MAX_PKTSIZE) { D("bad pktsize %d [16..%d]\n", g.pkt_size, MAX_PKTSIZE); usage(); } if (g.src_mac.name == NULL) { static char mybuf[20] = "00:00:00:00:00:00"; /* retrieve source mac address. */ if (source_hwaddr(g.ifname, mybuf) == -1) { D("Unable to retrieve source mac"); // continue, fail later } g.src_mac.name = mybuf; } /* extract address ranges */ extract_ip_range(&g.src_ip); extract_ip_range(&g.dst_ip); extract_mac_range(&g.src_mac); extract_mac_range(&g.dst_mac); if (g.src_ip.start != g.src_ip.end || g.src_ip.port0 != g.src_ip.port1 || g.dst_ip.start != g.dst_ip.end || g.dst_ip.port0 != g.dst_ip.port1) g.options |= OPT_COPY; if (g.virt_header != 0 && g.virt_header != VIRT_HDR_1 && g.virt_header != VIRT_HDR_2) { D("bad virtio-net-header length"); usage(); } if (g.dev_type == DEV_TAP) { D("want to use tap %s", g.ifname); g.main_fd = tap_alloc(g.ifname); if (g.main_fd < 0) { D("cannot open tap %s", g.ifname); usage(); } #ifndef NO_PCAP } else if (g.dev_type == DEV_PCAP) { char pcap_errbuf[PCAP_ERRBUF_SIZE]; pcap_errbuf[0] = '\0'; // init the buffer g.p = pcap_open_live(g.ifname, 256 /* XXX */, 1, 100, pcap_errbuf); if (g.p == NULL) { D("cannot open pcap on %s", g.ifname); usage(); } g.main_fd = pcap_fileno(g.p); D("using pcap on %s fileno %d", g.ifname, g.main_fd); #endif /* !NO_PCAP */ } else if (g.dummy_send) { /* but DEV_NETMAP */ D("using a dummy send routine"); } else { struct nmreq base_nmd; bzero(&base_nmd, sizeof(base_nmd)); parse_nmr_config(g.nmr_config, &base_nmd); if (g.extra_bufs) { base_nmd.nr_arg3 = g.extra_bufs; } /* * Open the netmap device using nm_open(). * * protocol stack and may cause a reset of the card, * which in turn may take some time for the PHY to * reconfigure. We do the open here to have time to reset. */ g.nmd = nm_open(g.ifname, &base_nmd, 0, NULL); if (g.nmd == NULL) { D("Unable to open %s: %s", g.ifname, strerror(errno)); goto out; } g.main_fd = g.nmd->fd; D("mapped %dKB at %p", g.nmd->req.nr_memsize>>10, g.nmd->mem); /* get num of queues in tx or rx */ if (g.td_body == sender_body) devqueues = g.nmd->req.nr_tx_rings; else devqueues = g.nmd->req.nr_rx_rings; /* validate provided nthreads. */ if (g.nthreads < 1 || g.nthreads > devqueues) { D("bad nthreads %d, have %d queues", g.nthreads, devqueues); // continue, fail later } if (verbose) { struct netmap_if *nifp = g.nmd->nifp; struct nmreq *req = &g.nmd->req; D("nifp at offset %d, %d tx %d rx region %d", req->nr_offset, req->nr_tx_rings, req->nr_rx_rings, req->nr_arg2); for (i = 0; i <= req->nr_tx_rings; i++) { struct netmap_ring *ring = NETMAP_TXRING(nifp, i); D(" TX%d at 0x%lx slots %d", i, (char *)ring - (char *)nifp, ring->num_slots); } for (i = 0; i <= req->nr_rx_rings; i++) { struct netmap_ring *ring = NETMAP_RXRING(nifp, i); D(" RX%d at 0x%lx slots %d", i, (char *)ring - (char *)nifp, ring->num_slots); } } /* Print some debug information. */ fprintf(stdout, "%s %s: %d queues, %d threads and %d cpus.\n", (g.td_body == sender_body) ? "Sending on" : "Receiving from", g.ifname, devqueues, g.nthreads, g.cpus); if (g.td_body == sender_body) { fprintf(stdout, "%s -> %s (%s -> %s)\n", g.src_ip.name, g.dst_ip.name, g.src_mac.name, g.dst_mac.name); } out: /* Exit if something went wrong. */ if (g.main_fd < 0) { D("aborting"); usage(); } } if (g.options) { D("--- SPECIAL OPTIONS:%s%s%s%s%s\n", g.options & OPT_PREFETCH ? " prefetch" : "", g.options & OPT_ACCESS ? " access" : "", g.options & OPT_MEMCPY ? " memcpy" : "", g.options & OPT_INDIRECT ? " indirect" : "", g.options & OPT_COPY ? " copy" : ""); } g.tx_period.tv_sec = g.tx_period.tv_nsec = 0; if (g.tx_rate > 0) { /* try to have at least something every second, * reducing the burst size to some 0.01s worth of data * (but no less than one full set of fragments) */ uint64_t x; int lim = (g.tx_rate)/300; if (g.burst > lim) g.burst = lim; if (g.burst < g.frags) g.burst = g.frags; x = ((uint64_t)1000000000 * (uint64_t)g.burst) / (uint64_t) g.tx_rate; g.tx_period.tv_nsec = x; g.tx_period.tv_sec = g.tx_period.tv_nsec / 1000000000; g.tx_period.tv_nsec = g.tx_period.tv_nsec % 1000000000; } if (g.td_body == sender_body) D("Sending %d packets every %ld.%09ld s", g.burst, g.tx_period.tv_sec, g.tx_period.tv_nsec); /* Wait for PHY reset. */ D("Wait %d secs for phy reset", wait_link); sleep(wait_link); D("Ready..."); /* Install ^C handler. */ global_nthreads = g.nthreads; signal(SIGINT, sigint_h); start_threads(&g); main_thread(&g); return 0; }
/* * bridge [-v] if1 [if2] * * If only one name, or the two interfaces are the same, * bridges userland and the adapter. Otherwise bridge * two intefaces. */ int main(int argc, char **argv) { struct pollfd pollfd[2]; int ch; u_int burst = 1024, wait_link = 4; struct nm_desc *pa = NULL, *pb = NULL; char *ifa = NULL, *ifb = NULL; char ifabuf[64] = { 0 }; fprintf(stderr, "%s built %s %s\n", argv[0], __DATE__, __TIME__); while ( (ch = getopt(argc, argv, "b:ci:vw:")) != -1) { switch (ch) { default: D("bad option %c %s", ch, optarg); usage(); break; case 'b': /* burst */ burst = atoi(optarg); break; case 'i': /* interface */ if (ifa == NULL) ifa = optarg; else if (ifb == NULL) ifb = optarg; else D("%s ignored, already have 2 interfaces", optarg); break; case 'c': zerocopy = 0; /* do not zerocopy */ break; case 'v': verbose++; break; case 'w': wait_link = atoi(optarg); break; } } argc -= optind; argv += optind; if (argc > 1) ifa = argv[1]; if (argc > 2) ifb = argv[2]; if (argc > 3) burst = atoi(argv[3]); if (!ifb) ifb = ifa; if (!ifa) { D("missing interface"); usage(); } if (burst < 1 || burst > 8192) { D("invalid burst %d, set to 1024", burst); burst = 1024; } if (wait_link > 100) { D("invalid wait_link %d, set to 4", wait_link); wait_link = 4; } if (!strcmp(ifa, ifb)) { D("same interface, endpoint 0 goes to host"); snprintf(ifabuf, sizeof(ifabuf) - 1, "%s^", ifa); ifa = ifabuf; } else { /* two different interfaces. Take all rings on if1 */ } pa = nm_open(ifa, NULL, 0, NULL); if (pa == NULL) { D("cannot open %s", ifa); return (1); } // XXX use a single mmap ? pb = nm_open(ifb, NULL, NM_OPEN_NO_MMAP, pa); if (pb == NULL) { D("cannot open %s", ifb); nm_close(pa); return (1); } zerocopy = zerocopy && (pa->mem == pb->mem); D("------- zerocopy %ssupported", zerocopy ? "" : "NOT "); /* setup poll(2) variables. */ memset(pollfd, 0, sizeof(pollfd)); pollfd[0].fd = pa->fd; pollfd[1].fd = pb->fd; D("Wait %d secs for link to come up...", wait_link); sleep(wait_link); D("Ready to go, %s 0x%x/%d <-> %s 0x%x/%d.", pa->req.nr_name, pa->first_rx_ring, pa->req.nr_rx_rings, pb->req.nr_name, pb->first_rx_ring, pb->req.nr_rx_rings); /* main loop */ signal(SIGINT, sigint_h); while (!do_abort) { int n0, n1, ret; pollfd[0].events = pollfd[1].events = 0; pollfd[0].revents = pollfd[1].revents = 0; n0 = pkt_queued(pa, 0); n1 = pkt_queued(pb, 0); if (n0) pollfd[1].events |= POLLOUT; else pollfd[0].events |= POLLIN; if (n1) pollfd[0].events |= POLLOUT; else pollfd[1].events |= POLLIN; ret = poll(pollfd, 2, 2500); if (ret <= 0 || verbose) D("poll %s [0] ev %x %x rx %d@%d tx %d," " [1] ev %x %x rx %d@%d tx %d", ret <= 0 ? "timeout" : "ok", pollfd[0].events, pollfd[0].revents, pkt_queued(pa, 0), NETMAP_RXRING(pa->nifp, pa->cur_rx_ring)->cur, pkt_queued(pa, 1), pollfd[1].events, pollfd[1].revents, pkt_queued(pb, 0), NETMAP_RXRING(pb->nifp, pb->cur_rx_ring)->cur, pkt_queued(pb, 1) ); if (ret < 0) continue; if (pollfd[0].revents & POLLERR) { struct netmap_ring *rx = NETMAP_RXRING(pa->nifp, pa->cur_rx_ring); D("error on fd0, rx [%d,%d,%d)", rx->head, rx->cur, rx->tail); } if (pollfd[1].revents & POLLERR) { struct netmap_ring *rx = NETMAP_RXRING(pb->nifp, pb->cur_rx_ring); D("error on fd1, rx [%d,%d,%d)", rx->head, rx->cur, rx->tail); } if (pollfd[0].revents & POLLOUT) { move(pb, pa, burst); // XXX we don't need the ioctl */ // ioctl(me[0].fd, NIOCTXSYNC, NULL); } if (pollfd[1].revents & POLLOUT) { move(pa, pb, burst); // XXX we don't need the ioctl */ // ioctl(me[1].fd, NIOCTXSYNC, NULL); } } D("exiting"); nm_close(pb); nm_close(pa); return (0); }
static int netmap_regif(struct nmreq *req, uint32_t idx, uint8_t port) { struct netmap_if *nmif; struct netmap_ring *ring; uint32_t i, slots, start_ring; int32_t rc; if (ports[port].fd < RTE_DIM(fd_port)) { RTE_LOG(ERR, USER1, "port %hhu already in use by fd: %u\n", port, IDX_TO_FD(ports[port].fd)); return (-EBUSY); } if (fd_port[idx].port != FD_PORT_RSRV) { RTE_LOG(ERR, USER1, "fd: %u is misconfigured\n", IDX_TO_FD(idx)); return (-EBUSY); } nmif = ports[port].nmif; /* setup netmap_if fields. */ memset(nmif, 0, netmap.netif_memsz); /* only ALL rings supported right now. */ if (req->nr_ringid != 0) return (-EINVAL); snprintf(nmif->ni_name, sizeof(nmif->ni_name), "%s", req->nr_name); nmif->ni_version = req->nr_version; /* Netmap uses ni_(r|t)x_rings + 1 */ nmif->ni_rx_rings = ports[port].nr_rx_rings - 1; nmif->ni_tx_rings = ports[port].nr_tx_rings - 1; /* * Setup TX rings and slots. * Refer to the comments in netmap.h for details */ slots = 0; for (i = 0; i < nmif->ni_tx_rings + 1; i++) { nmif->ring_ofs[i] = NETMAP_IF_RING_OFS(i, PORT_NUM_RINGS, slots); ring = NETMAP_TXRING(nmif, i); netmap_ring_setup(ring, port, i, ports[port].nr_tx_slots); ring->avail = ring->num_slots; slots += ports[port].nr_tx_slots; } /* * Setup RX rings and slots. * Refer to the comments in netmap.h for details */ start_ring = i; for (; i < nmif->ni_rx_rings + 1 + start_ring; i++) { nmif->ring_ofs[i] = NETMAP_IF_RING_OFS(i, PORT_NUM_RINGS, slots); ring = NETMAP_RXRING(nmif, (i - start_ring)); netmap_ring_setup(ring, port, i, ports[port].nr_rx_slots); ring->avail = 0; slots += ports[port].nr_rx_slots; } if ((rc = rte_eth_dev_start(port)) < 0) { RTE_LOG(ERR, USER1, "Couldn't start ethernet device %s (error %d)\n", req->nr_name, rc); return (rc); } /* setup fdi <--> port relationtip. */ ports[port].fd = idx; fd_port[idx].port = port; req->nr_memsize = netmap.mem_sz; req->nr_offset = (uintptr_t)nmif - (uintptr_t)netmap.mem; return (0); }
void *dispatcher(void *threadarg) { assert(threadarg); struct thread_context *context; struct thread_context *contexts; int rv; struct netmap_ring *rxring; struct ethernet_pkt *etherpkt; struct pollfd pfd; struct dispatcher_data *data; uint32_t *slots_used, *open_transactions; uint32_t i, arpd_idx, num_threads; char *buf; struct msg_hdr *msg; struct ether_addr *mac; context = (struct thread_context *)threadarg; contexts = context->shared->contexts; data = context->data; arpd_idx = context->shared->arpd_idx; mac = &context->shared->if_info->mac; num_threads = context->shared->num_threads; struct transaction *transactions[num_threads]; uint64_t dropped[num_threads]; for (i=0; i < num_threads; i++) { transactions[i] = NULL; dropped[i] = 0; } rv = dispatcher_init(context); if (!rv) { pthread_exit(NULL); } rxring = NETMAP_RXRING(data->nifp, 0); slots_used = bitmap_new(rxring->num_slots); if (!slots_used) pthread_exit(NULL); open_transactions = bitmap_new(num_threads); if (!open_transactions) pthread_exit(NULL); pfd.fd = data->fd; pfd.events = (POLLIN); printf("dispatcher[%d]: initialized\n", context->thread_id); // signal to main() that we are initialized atomic_store_explicit(&context->initialized, 1, memory_order_release); for (;;) { rv = poll(&pfd, 1, POLL_TIMEOUT); // read all packets from the ring if (rv > 0) { for (; rxring->avail > 0; rxring->avail--) { i = rxring->cur; rxring->cur = NETMAP_RING_NEXT(rxring, i); rxring->reserved++; buf = NETMAP_BUF(rxring, rxring->slot[i].buf_idx); etherpkt = (struct ethernet_pkt *)(void *)buf; // TODO: consider pushing this check to the workers if (!ethernet_is_valid(etherpkt, mac)) { if (rxring->reserved == 1) rxring->reserved = 0; continue; } // TODO: dispatch to n workers instead of just 0 switch (etherpkt->h.ether_type) { case IP4_ETHERTYPE: rv = tqueue_insert(contexts[0].pkt_recv_q, &transactions[0], (char *) NULL + i); switch (rv) { case TQUEUE_SUCCESS: bitmap_set(slots_used, i); bitmap_set(open_transactions, 0); break; case TQUEUE_TRANSACTION_FULL: bitmap_set(slots_used, i); bitmap_clear(open_transactions, 0); break; case TQUEUE_FULL: // just drop packet and do accounting dropped[0]++; if (rxring->reserved == 1) rxring->reserved = 0; break; } break; case ARP_ETHERTYPE: rv = tqueue_insert(contexts[arpd_idx].pkt_recv_q, &transactions[arpd_idx], (char *) NULL + i); switch (rv) { case TQUEUE_SUCCESS: tqueue_publish_transaction(contexts[arpd_idx].pkt_recv_q, &transactions[arpd_idx]); bitmap_set(slots_used, i); break; case TQUEUE_TRANSACTION_FULL: bitmap_set(slots_used, i); break; case TQUEUE_FULL: // just drop packet and do accounting dropped[arpd_idx]++; if (rxring->reserved == 1) rxring->reserved = 0; break; } break; default: printf("dispatcher[%d]: unknown/unsupported ethertype %hu\n", context->thread_id, etherpkt->h.ether_type); if (rxring->reserved == 1) rxring->reserved = 0; } // switch (ethertype) } // for (rxring) // publish any open transactions so that the worker can start on it for (i=0; i < num_threads; i++) { if (bitmap_get(open_transactions, i)) tqueue_publish_transaction(contexts[i].pkt_recv_q, &transactions[i]); } bitmap_clearall(open_transactions, num_threads); } // if (packets) // read the message queue rv = squeue_enter(context->msg_q, 1); if (!rv) continue; while ((msg = squeue_get_next_pop_slot(context->msg_q)) != NULL) { switch (msg->msg_type) { case MSG_TRANSACTION_UPDATE: update_slots_used(context->msg_q, slots_used, rxring); break; case MSG_TRANSACTION_UPDATE_SINGLE: update_slots_used_single((void *)msg, slots_used, rxring); break; default: printf("dispatcher: unknown message %hu\n", msg->msg_type); } } squeue_exit(context->msg_q); } // for(;;) pthread_exit(NULL); }
static int netmap_open(odp_pktio_t id ODP_UNUSED, pktio_entry_t *pktio_entry, const char *netdev, odp_pool_t pool) { int i; int err; int sockfd; int mtu; uint32_t buf_size; pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm; struct nm_desc *desc; struct netmap_ring *ring; odp_pktin_hash_proto_t hash_proto; odp_pktio_stats_t cur_stats; if (getenv("ODP_PKTIO_DISABLE_NETMAP")) return -1; if (pool == ODP_POOL_INVALID) return -1; /* Init pktio entry */ memset(pkt_nm, 0, sizeof(*pkt_nm)); pkt_nm->sockfd = -1; pkt_nm->pool = pool; /* max frame len taking into account the l2-offset */ pkt_nm->max_frame_len = ODP_CONFIG_PACKET_BUF_LEN_MAX - odp_buffer_pool_headroom(pool) - odp_buffer_pool_tailroom(pool); snprintf(pktio_entry->s.name, sizeof(pktio_entry->s.name), "%s", netdev); snprintf(pkt_nm->nm_name, sizeof(pkt_nm->nm_name), "netmap:%s", netdev); /* Dummy open here to check if netmap module is available and to read * capability info. */ desc = nm_open(pkt_nm->nm_name, NULL, 0, NULL); if (desc == NULL) { ODP_ERR("nm_open(%s) failed\n", pkt_nm->nm_name); goto error; } if (desc->nifp->ni_rx_rings > NM_MAX_DESC) { ODP_ERR("Unable to store all rx rings\n"); nm_close(desc); goto error; } pkt_nm->num_rx_rings = desc->nifp->ni_rx_rings; pkt_nm->capa.max_input_queues = PKTIO_MAX_QUEUES; if (desc->nifp->ni_rx_rings < PKTIO_MAX_QUEUES) pkt_nm->capa.max_input_queues = desc->nifp->ni_rx_rings; if (desc->nifp->ni_tx_rings > NM_MAX_DESC) { ODP_ERR("Unable to store all tx rings\n"); nm_close(desc); goto error; } pkt_nm->num_tx_rings = desc->nifp->ni_tx_rings; pkt_nm->capa.max_output_queues = PKTIO_MAX_QUEUES; if (desc->nifp->ni_tx_rings < PKTIO_MAX_QUEUES) pkt_nm->capa.max_output_queues = desc->nifp->ni_tx_rings; ring = NETMAP_RXRING(desc->nifp, desc->cur_rx_ring); buf_size = ring->nr_buf_size; nm_close(desc); sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { ODP_ERR("Cannot get device control socket\n"); goto error; } pkt_nm->sockfd = sockfd; /* Use either interface MTU (+ ethernet header length) or netmap buffer * size as MTU, whichever is smaller. */ mtu = mtu_get_fd(pktio_entry->s.pkt_nm.sockfd, pktio_entry->s.name) + ODPH_ETHHDR_LEN; if (mtu < 0) { ODP_ERR("Unable to read interface MTU\n"); goto error; } pkt_nm->mtu = ((uint32_t)mtu < buf_size) ? (uint32_t)mtu : buf_size; /* Check if RSS is supported. If not, set 'max_input_queues' to 1. */ if (rss_conf_get_supported_fd(sockfd, netdev, &hash_proto) == 0) { ODP_DBG("RSS not supported\n"); pkt_nm->capa.max_input_queues = 1; } err = netmap_do_ioctl(pktio_entry, SIOCGIFFLAGS, 0); if (err) goto error; if ((pkt_nm->if_flags & IFF_UP) == 0) ODP_DBG("%s is down\n", pktio_entry->s.name); err = mac_addr_get_fd(sockfd, netdev, pkt_nm->if_mac); if (err) goto error; for (i = 0; i < PKTIO_MAX_QUEUES; i++) { odp_ticketlock_init(&pkt_nm->rx_desc_ring[i].s.lock); odp_ticketlock_init(&pkt_nm->tx_desc_ring[i].s.lock); } /* netmap uses only ethtool to get statistics counters */ err = ethtool_stats_get_fd(pktio_entry->s.pkt_nm.sockfd, pktio_entry->s.name, &cur_stats); if (err) { ODP_ERR( "netmap pktio %s does not support statistics counters\n", pktio_entry->s.name); pktio_entry->s.stats_type = STATS_UNSUPPORTED; } else { pktio_entry->s.stats_type = STATS_ETHTOOL; } (void)netmap_stats_reset(pktio_entry); return 0; error: netmap_close(pktio_entry); return -1; }