- add code to run pkt-gen on top of libpcap so we can see the

difference from a native API

- add some testing options, such as conditional prefetching and
  packet copy.
This commit is contained in:
Luigi Rizzo 2012-05-03 15:37:05 +00:00
parent df76012c5b
commit 99fb123f8d
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=234956

View File

@ -25,7 +25,7 @@
/*
* $FreeBSD$
* $Id: pkt-gen.c 10637 2012-02-24 16:36:25Z luigi $
* $Id: pkt-gen.c 10967 2012-05-03 11:29:23Z luigi $
*
* Example program to show how to build a multithreaded packet
* source/sink using the netmap device.
@ -90,6 +90,36 @@ int verbose = 0;
#define SKIP_PAYLOAD 1 /* do not check payload. */
inline void prefetch (const void *x)
{
__asm volatile("prefetcht0 %0" :: "m" (*(const unsigned long *)x));
}
// XXX only for multiples of 32 bytes, non overlapped.
static inline void
pkt_copy(void *_src, void *_dst, int l)
{
uint64_t *src = _src;
uint64_t *dst = _dst;
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
if (unlikely(l >= 1024)) {
bcopy(src, dst, l);
return;
}
for (; l > 0; l-=64) {
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
}
}
#if EXPERIMENTAL
/* Wrapper around `rdtsc' to take reliable timestamps flushing the pipeline */
#define netmap_rdtsc(t) \
@ -140,6 +170,11 @@ struct glob_arg {
int npackets; /* total packets to send */
int nthreads;
int cpus;
int options; /* testing */
#define OPT_PREFETCH 1
#define OPT_ACCESS 2
#define OPT_COPY 4
#define OPT_MEMCPY 8
int use_pcap;
pcap_t *p;
};
@ -394,19 +429,35 @@ check_payload(char *p, int psize)
*/
static int
send_packets(struct netmap_ring *ring, struct pkt *pkt,
int size, u_int count, int fill_all)
int size, u_int count, int options)
{
u_int sent, cur = ring->cur;
if (ring->avail < count)
count = ring->avail;
#if 0
if (options & (OPT_COPY | OPT_PREFETCH) ) {
for (sent = 0; sent < count; sent++) {
struct netmap_slot *slot = &ring->slot[cur];
char *p = NETMAP_BUF(ring, slot->buf_idx);
prefetch(p);
cur = NETMAP_RING_NEXT(ring, cur);
}
cur = ring->cur;
}
#endif
for (sent = 0; sent < count; sent++) {
struct netmap_slot *slot = &ring->slot[cur];
char *p = NETMAP_BUF(ring, slot->buf_idx);
if (fill_all)
if (options & OPT_COPY)
pkt_copy(pkt, p, size);
else if (options & OPT_MEMCPY)
memcpy(p, pkt, size);
else if (options & OPT_PREFETCH)
prefetch(p);
slot->len = size;
if (sent == count - 1)
@ -428,8 +479,8 @@ sender_body(void *data)
struct netmap_if *nifp = targ->nifp;
struct netmap_ring *txring;
int i, n = targ->g->npackets / targ->g->nthreads, sent = 0;
int fill_all = 1;
int options = targ->g->options | OPT_COPY;
D("start");
if (setaffinity(targ->thread, targ->affinity))
goto quit;
/* setup poll(2) mechanism. */
@ -444,9 +495,13 @@ sender_body(void *data)
void *pkt = &targ->pkt;
pcap_t *p = targ->g->p;
for (; sent < n; sent++) {
if (pcap_inject(p, pkt, size) == -1)
break;
for (i = 0; sent < n; i++) {
if (pcap_inject(p, pkt, size) != -1)
sent++;
if (i > 10000) {
targ->count = sent;
i = 0;
}
}
} else {
while (sent < n) {
@ -461,8 +516,8 @@ sender_body(void *data)
/*
* scan our queues and send on those with room
*/
if (sent > 100000)
fill_all = 0;
if (sent > 100000 && !(targ->g->options & OPT_COPY) )
options &= ~OPT_COPY;
for (i = targ->qfirst; i < targ->qlast; i++) {
int m, limit = MIN(n - sent, targ->g->burst);
@ -470,12 +525,12 @@ sender_body(void *data)
if (txring->avail == 0)
continue;
m = send_packets(txring, &targ->pkt, targ->g->pkt_size,
limit, fill_all);
limit, options);
sent += m;
targ->count = sent;
}
}
/* Tell the interface that we have new packets. */
/* flush any remaining packets */
ioctl(fds[0].fd, NIOCTXSYNC, NULL);
/* final part: wait all the TX queues to be empty. */
@ -672,6 +727,7 @@ int
main(int arc, char **argv)
{
int i, fd;
char pcap_errbuf[PCAP_ERRBUF_SIZE];
struct glob_arg g;
@ -696,12 +752,15 @@ main(int arc, char **argv)
g.cpus = 1;
while ( (ch = getopt(arc, argv,
"i:t:r:l:d:s:D:S:b:c:p:T:w:v")) != -1) {
"i:t:r:l:d:s:D:S:b:c:o:p:PT:w:v")) != -1) {
switch(ch) {
default:
D("bad option %c %s", ch, optarg);
usage();
break;
case 'o':
g.options = atoi(optarg);
break;
case 'i': /* interface */
ifname = optarg;
break;
@ -775,6 +834,26 @@ main(int arc, char **argv)
usage();
}
if (td_body == sender_body && g.src_mac == NULL) {
static char mybuf[20] = "ff:ff:ff:ff:ff:ff";
/* retrieve source mac address. */
if (source_hwaddr(ifname, mybuf) == -1) {
D("Unable to retrieve source mac");
// continue, fail later
}
g.src_mac = mybuf;
}
if (g.use_pcap) {
D("using pcap on %s", ifname);
g.p = pcap_open_live(ifname, 0, 1, 100, pcap_errbuf);
if (g.p == NULL) {
D("cannot open pcap on %s", ifname);
usage();
}
mmap_addr = NULL;
fd = -1;
} else {
bzero(&nmr, sizeof(nmr));
nmr.nr_version = NETMAP_API;
/*
@ -811,16 +890,6 @@ main(int arc, char **argv)
// continue, fail later
}
if (td_body == sender_body && g.src_mac == NULL) {
static char mybuf[20] = "ff:ff:ff:ff:ff:ff";
/* retrieve source mac address. */
if (source_hwaddr(ifname, mybuf) == -1) {
D("Unable to retrieve source mac");
// continue, fail later
}
g.src_mac = mybuf;
}
/*
* Map the netmap shared memory: instead of issuing mmap()
* inside the body of the threads, we prefer to keep this
@ -869,8 +938,15 @@ main(int arc, char **argv)
D("aborting");
usage();
}
}
if (g.options) {
D("special options:%s%s%s%s\n",
g.options & OPT_PREFETCH ? " prefetch" : "",
g.options & OPT_ACCESS ? " access" : "",
g.options & OPT_MEMCPY ? " memcpy" : "",
g.options & OPT_COPY ? " copy" : "");
}
/* Wait for PHY reset. */
D("Wait %d secs for phy reset", wait_link);
sleep(wait_link);
@ -881,7 +957,12 @@ main(int arc, char **argv)
signal(SIGINT, sigint_h);
if (g.use_pcap) {
// XXX g.p = pcap_open_live(..);
g.p = pcap_open_live(ifname, 0, 1, 100, NULL);
if (g.p == NULL) {
D("cannot open pcap on %s", ifname);
usage();
} else
D("using pcap %p on %s", g.p, ifname);
}
targs = calloc(g.nthreads, sizeof(*targs));
@ -1018,9 +1099,11 @@ main(int arc, char **argv)
rx_output(count, delta_t);
}
if (g.use_pcap == 0) {
ioctl(fd, NIOCUNREGIF, &nmr);
munmap(mmap_addr, nmr.nr_memsize);
close(fd);
}
return (0);
}