Skip to content

Commit

Permalink
Support Zero-Copy for Netmap
Browse files Browse the repository at this point in the history
This commit adds zero-copy through buffer swapping with extra buffer allocated on startup.

Netmap Buffers are now organized in pool much like click pool through a completed implementation of Luigi Rizzo's NetmapBufQ. A shared linked list allows multiple thread-local NetmapBufQ to exchange batches of buffers.

The amount of extra buffers allocated on startup is set using NetmapInfo, which is now a real element. NetmapBufQ and other old NetmapInfo methods (renamed more accurately NetmapDevice) are moved to lib/netmapdevice.cc

Also there was confusion between HAVE_NET_NETMAP_H and HAVE_NETMAP, and the ELEMENT_REQUIRES(netmap). It's not really needed to have part of the netmap subsystem built without HAVE_NETMAP, so HAVE_NET_NETMAP_H is changed by HAVE_NETMAP.

This is a first, minimal change just to introduce Zero Copy, it provides improvement but the send and receive method are nearly untouched and not re-worked in this commit.
  • Loading branch information
Tom Barbette committed Dec 8, 2015
1 parent 1490de3 commit cc3eb42
Show file tree
Hide file tree
Showing 15 changed files with 640 additions and 283 deletions.
6 changes: 3 additions & 3 deletions config-userlevel.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,6 @@
/* Define if you have the <netdb.h> header file. */
#undef HAVE_NETDB_H

/* Define if you have the <net/netmap.h> header file. */
#undef HAVE_NET_NETMAP_H

/* Define if you have the <netpacket/packet.h> header file. */
#undef HAVE_NETPACKET_PACKET_H

Expand Down Expand Up @@ -214,6 +211,9 @@
/* Define if a Click user-level driver might run multiple threads. */
#undef HAVE_USER_MULTITHREAD

/* Define if Netmap support is enabled. */
#undef HAVE_NETMAP

/* Define if a Click user-level driver uses Intel DPDK. */
#undef HAVE_DPDK

Expand Down
6 changes: 5 additions & 1 deletion configure
Original file line number Diff line number Diff line change
Expand Up @@ -10705,11 +10705,15 @@ $as_echo "$ac_cv_working_net_netmap_h" >&6; }
CPPFLAGS="$saveflags"
if test "$HAVE_NETMAP" = yes -a "$use_netmap" != no; then

$as_echo "#define HAVE_NET_NETMAP_H 1" >>confdefs.h
$as_echo "#define HAVE_NETMAP 1" >>confdefs.h

EXTRA_DRIVER_OBJS="netmapdevice.o $EXTRA_DRIVER_OBJS"
else
HAVE_NETMAP=no
fi



if test "$HAVE_PCAP" != yes -a "$HAVE_NETMAP" != yes -a "$ac_cv_under_linux" != yes; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
=========================================
Expand Down
17 changes: 10 additions & 7 deletions elements/userlevel/fromdevice.cc
Original file line number Diff line number Diff line change
Expand Up @@ -472,9 +472,15 @@ FromDevice::emit_packet(WritablePacket *p, int extra_len, const Timestamp &ts)
else
checked_output_push(1, p);
}

void
FromDevice::emit_packet_arg(WritablePacket *p, int extra_len, const Timestamp &ts, void* arg) {
FromDevice* fd = static_cast<FromDevice*>(arg);
fd->emit_packet(p, extra_len, ts);
}
#endif

#if FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_NETMAP
#if FROMDEVICE_ALLOW_PCAP
CLICK_ENDDECLS
extern "C" {
void
Expand All @@ -497,7 +503,6 @@ FromDevice_get_packet(u_char* clientdata,
CLICK_DECLS
#endif


void
FromDevice::selected(int, int)
{
Expand All @@ -507,8 +512,7 @@ FromDevice::selected(int, int)
#if FROMDEVICE_ALLOW_NETMAP
if (_method == method_netmap) {
// Read and push() at most one burst of packets.
int r = _netmap.dispatch(_burst,
reinterpret_cast<nm_cb_t>(FromDevice_get_packet), (u_char *) this);
int r = _netmap.receive(_burst, _headroom, emit_packet_arg, this);
if (r > 0) {
_count += r;
_task.reschedule();
Expand Down Expand Up @@ -570,8 +574,7 @@ FromDevice::run_task(Task *)
# if FROMDEVICE_ALLOW_NETMAP
if (_method == method_netmap) {
// Read and push() at most one burst of packets.
r = _netmap.dispatch(_burst,
reinterpret_cast<nm_cb_t>(FromDevice_get_packet), (u_char *) this);
r = _netmap.receive(_burst,_headroom,emit_packet_arg, this);
if (r < 0 && ++_pcap_complaints < 5)
ErrorHandler::default_handler()->error("%p{element}: %s",
this, "nm_dispatch failed");
Expand Down Expand Up @@ -652,5 +655,5 @@ FromDevice::add_handlers()
}

CLICK_ENDDECLS
ELEMENT_REQUIRES(userlevel FakePcap KernelFilter NetmapInfo)
ELEMENT_REQUIRES(userlevel FakePcap KernelFilter)
EXPORT_ELEMENT(FromDevice)
10 changes: 5 additions & 5 deletions elements/userlevel/fromdevice.hh
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ int pcap_setnonblock(pcap_t *p, int nonblock, char *errbuf);
}
#endif

#if HAVE_NET_NETMAP_H
#if HAVE_NETMAP
# define FROMDEVICE_ALLOW_NETMAP 1
# include "elements/userlevel/netmapinfo.hh"
# include <click/netmapdevice.hh>
#endif

#if FROMDEVICE_ALLOW_NETMAP || FROMDEVICE_ALLOW_PCAP
Expand Down Expand Up @@ -187,7 +187,6 @@ class FromDevice : public Element { public:
#else
inline int fd() const { return -1; }
#endif

void selected(int fd, int mask);

#if FROMDEVICE_ALLOW_PCAP
Expand All @@ -203,7 +202,7 @@ class FromDevice : public Element { public:
#endif

#if FROMDEVICE_ALLOW_NETMAP
const NetmapInfo *netmap() const { return _method == method_netmap ? &_netmap : 0; }
const NetmapDevice *netmap() const { return _method == method_netmap ? &_netmap : 0; }
#endif

#if FROMDEVICE_ALLOW_NETMAP || FROMDEVICE_ALLOW_PCAP
Expand All @@ -222,13 +221,14 @@ class FromDevice : public Element { public:
#endif
#if FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_NETMAP
void emit_packet(WritablePacket *p, int extra_len, const Timestamp &ts);
static void emit_packet_arg(WritablePacket *p, int extra_len, const Timestamp &ts, void* arg);
#endif
#if FROMDEVICE_ALLOW_PCAP
pcap_t *_pcap;
int _pcap_complaints;
#endif
#if FROMDEVICE_ALLOW_NETMAP
NetmapInfo _netmap;
NetmapDevice _netmap;
int netmap_dispatch();
#endif
#if FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_NETMAP
Expand Down
138 changes: 18 additions & 120 deletions elements/userlevel/netmapinfo.cc
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// -*- mode: c++; c-basic-offset: 4 -*-
/*
* netmapinfo.{cc,hh} -- library for interfacing with netmap
* Eddie Kohler, Luigi Rizzo
*
* Copyright (c) 2012 Eddie Kohler
* Copyright (c) 2015 Tom Barbette
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
Expand All @@ -17,130 +16,29 @@
*/

#include <click/config.h>
#include <click/glue.hh>
#if HAVE_NET_NETMAP_H
#define NETMAP_WITH_LIBS
#include <click/args.hh>
#include <click/error.hh>
#include "netmapinfo.hh"
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <click/sync.hh>
#include <unistd.h>
#include <fcntl.h>
CLICK_DECLS

/*
* keep a list of netmap ports so matching the name we
* can recycle the regions
*/
static Spinlock netmap_memory_lock;
//static struct NetmapInfo *netmap_ports;

int
NetmapInfo::open(const String &ifname,
bool always_error, ErrorHandler *errh)
{
click_chatter("%s ifname %s\n", __FUNCTION__, ifname.c_str());
ErrorHandler *initial_errh = always_error ? errh : ErrorHandler::silent_handler();
CLICK_DECLS

netmap_memory_lock.acquire();
// for the time being, just a new block
do {
desc = nm_open(ifname.c_str(), NULL, 0, NULL);
if (desc == NULL) {
initial_errh->error("nm_open(%s): %s", ifname.c_str(), strerror(errno));
break;
int NetmapInfo::configure(Vector<String> &conf, ErrorHandler *errh) {
if (instance) {
return errh->error("You cannot place multiple instances of NetmapInfo !");
}
click_chatter("%s %s memsize %d mem %p buf_start %p buf_end %p",
__FUNCTION__, desc->req.nr_name,
desc->memsize, desc->mem, desc->buf_start, desc->buf_end);
bufq.init(desc->buf_start, desc->buf_end,
desc->some_ring->nr_buf_size);
/* eventually try to match the region */
destructor_arg = this;
click_chatter("private mapping for %s\n", ifname.c_str());
} while (0);
netmap_memory_lock.release();
return desc ? desc->fd : -1;
}
instance = this;
if (Args(conf, this, errh)
.read_p("EXTRA_BUFFER", NetmapDevice::global_alloc)
.complete() < 0)
return -1;

void
NetmapInfo::initialize_rings_rx(int timestamp)
{
click_chatter("%s timestamp %d\n", __FUNCTION__, timestamp);
if (timestamp >= 0) {
int flags = (timestamp > 0 ? NR_TIMESTAMP : 0);
for (unsigned i = desc->first_rx_ring; i <= desc->last_rx_ring; ++i)
NETMAP_RXRING(desc->nifp, i)->flags = flags;
}
return 0;
}

void
NetmapInfo::initialize_rings_tx()
{
click_chatter("%s\n", __FUNCTION__);
}

int
NetmapInfo::dispatch(int count, nm_cb_t cb, u_char *arg)
{
return nm_dispatch(desc, count, cb, arg);
}

bool
NetmapInfo::send_packet(Packet *p, int noutputs)
{
int ret = nm_inject(desc, p->data(), p->length());
if (0) click_chatter("%s buf %p size %d returns %d\n",
__FUNCTION__, p->data(), p->length(), ret);
return ret > 0 ? 0 : -1;
#if 0
// we can do a smart nm_inject
for (unsigned ri = desc->first_tx_ring; ri <= desc->last_tx_ring; ++ri) {
struct netmap_ring *ring = NETMAP_TXRING(desc->nifp, ri);
if (nm_ring_empty(ring))
continue;
unsigned cur = ring->cur;
unsigned buf_idx = ring->slot[cur].buf_idx;
if (buf_idx < 2)
continue;
unsigned char *buf = (unsigned char *) NETMAP_BUF(ring, buf_idx);
uint32_t p_length = p->length();
if (NetmapInfo::is_netmap_buffer(p)
&& !p->shared()
&& p->buffer() == p->data()
&& (char *)p->buffer() >= desc->buf_start
&& (char *)p->buffer() < desc->buf_end
&& noutputs == 0) {
// put the original buffer in the freelist
NetmapInfo::buffer_destructor(buf, 0, (void *)this);
// now enqueue
ring->slot[cur].buf_idx = NETMAP_BUF_IDX(ring, (char *) p->buffer());
ring->slot[cur].flags |= NS_BUF_CHANGED;
// and make sure nobody uses this packet
p->reset_buffer();
} else
memcpy(buf, p->data(), p_length);
ring->slot[cur].len = p_length;
__asm__ volatile("" : : : "memory");
ring->head = ring->cur = nm_ring_next(ring, cur);
return 0;
}
errno = ENOBUFS;
return -1;
#endif
}
void
NetmapInfo::close(int fd)
{
click_chatter("fd %d interface %s\n",
fd, desc->req.nr_name);
netmap_memory_lock.acquire();
// unlink from the list ?
nm_close(desc);
desc = 0;
netmap_memory_lock.release();
}
NetmapInfo* NetmapInfo::instance = 0;

CLICK_ENDDECLS
#endif
ELEMENT_PROVIDES(NetmapInfo)

ELEMENT_REQUIRES(userlevel netmap)
EXPORT_ELEMENT(NetmapInfo)
ELEMENT_MT_SAFE(NetmapInfo)
Loading

0 comments on commit cc3eb42

Please sign in to comment.