Commit bbc1fe55 authored by Tom Barbette's avatar Tom Barbette

Take back changes for the PR for the mainline click for new Netmap integration

It supports zero-copy through buffer swapping with extra buffer allocated on startup. This more or less double the throughput.

The output device also supports a "Full Push" mode, allowing you to build a Click pipeline only with push paths, as the output rings of modern NICs are most of the time enough to absorb transient traffic, allowing to avoid using very slow Queue() elments.

Netmap Buffers are now organized in pool much like click pool through a completed implementation of Luigi Rizzo's NetmapBufQ. A shared Ring 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 methods are moved to a library in lib/netmapdevice.cc

For all those new features, the Netmap integration becomes more different than the other FromDevice/ToDevice methods and this commits moves netmap integration to independent FromNetmapDevice and ToNetmapDevice. This matches the DPDK Integrations. Also, ToNetmapDevice is agnostic, and thereforce could not be kept with ToDevice which is pull-only, or ToDpdkDevice which is push-only.

DPDK fake buffer destructor is removed to use the same empty_destructor than Netmap, which won't be called as it is known to be empty.
parent 33790cbc
Pipeline #47 passed with stage
......@@ -41,12 +41,6 @@
/* Define if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define if a Click user-level driver uses Intel DPDK. */
#undef HAVE_DPDK
/* Define if a Click user-level driver uses Netmap. */
#undef HAVE_NETMAP
/* Define if you have the <nuda.h> header file. */
#undef HAVE_NUMA
......@@ -122,18 +116,9 @@
/* 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 use only netmap buffer as data buffer. */
#undef HAVE_NETMAP_PACKET_POOL
/* Define if you have the <netpacket/packet.h> header file. */
#undef HAVE_NETPACKET_PACKET_H
/* Define if Zero-copy is enabled. */
#undef HAVE_ZEROCOPY
/* Define if <new.h> exists and works. */
#undef HAVE_NEW_H
......@@ -229,6 +214,15 @@
/* Define if a Click user-level driver might run multiple threads. */
#undef HAVE_USER_MULTITHREAD
/* Define if Zero-copy is enabled. */
#undef HAVE_ZEROCOPY
/* Define if Netmap support is enabled. */
#undef HAVE_NETMAP
/* Define if you use only netmap buffer as data buffer. */
#undef HAVE_NETMAP_PACKET_POOL
/* Define if a Click user-level driver uses Intel DPDK. */
#undef HAVE_DPDK
......
......@@ -1501,7 +1501,7 @@ Optional Features:
--disable-kqueue do not use kqueue()
--enable-dpdk use Intel DPDK
--enable-dpdk-pools Click manages DPDK packet pools
--enable-zerocopy use Zero Copy
--disable-zerocopy disable Zero Copy
--disable-linuxmodule disable Linux kernel driver
--disable-fixincludes do not patch Linux kernel headers for C++
--enable-multithread support kernel multithreading
......@@ -6564,15 +6564,6 @@ if test "x$enable_dpdk" = "xyes"; then
Cannot find \$RTE_SDK/\$RTE_TARGET/include/rte_eal.h for Intel DPDK.
Define \$RTE_SDK and \$RTE_TARGET as per Intel DPDK documentation.
=========================================" "$LINENO" 5
fi
if test ! -f "$RTE_SDK/$RTE_TARGET/lib/librte_eal.a"; then
as_fn_error $? "
=========================================
Cannot find \$RTE_SDK/\$RTE_TARGET/lib/librte_eal.a for Intel DPDK.
Define \$RTE_SDK and \$RTE_TARGET as per Intel DPDK documentation.
=========================================" "$LINENO" 5
fi
rte_ver_major=`grep "#define RTE_VER_MAJOR" "$RTE_SDK/$RTE_TARGET/include/rte_version.h" | head -n 1 | awk '{print $3}'`
......@@ -6606,7 +6597,7 @@ fi
if test "${enable_zerocopy+set}" = set; then :
enableval=$enable_zerocopy; :
else
enable_zerocopy=no
enable_zerocopy=yes
fi
if test "x$enable_zerocopy" = xyes; then
......@@ -10894,12 +10885,16 @@ $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:
=========================================
......
......@@ -39,12 +39,12 @@ EnsureNetmapBuffer::smaction(Packet* p) {
#if HAVE_NETMAP_PACKET_POOL
WritablePacket* q = WritablePacket::make(p->length());
#else
unsigned char* buffer = NetmapBufQ::get_local_pool()->extract_p();
unsigned char* buffer = NetmapBufQ::local_pool()->extract_p();
if (!buffer) {
p->kill();
return 0;
}
WritablePacket* q = WritablePacket::make(buffer,NetmapBufQ::buffer_size(),NetmapBufQ::buffer_destructor,NetmapBufQ::get_local_pool());
WritablePacket* q = WritablePacket::make(buffer,NetmapBufQ::buffer_size(),NetmapBufQ::buffer_destructor,NetmapBufQ::local_pool());
#endif
q->copy(p);
p->kill();
......
......@@ -57,7 +57,7 @@ CLICK_DECLS
FromDevice::FromDevice()
:
#if FROMDEVICE_ALLOW_NETMAP || FROMDEVICE_ALLOW_PCAP
#if FROMDEVICE_ALLOW_PCAP
_task(this),
#endif
#if FROMDEVICE_ALLOW_PCAP
......@@ -65,7 +65,7 @@ FromDevice::FromDevice()
#endif
_datalink(-1), _count(0), _promisc(0), _snaplen(0)
{
#if FROMDEVICE_ALLOW_LINUX || FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_NETMAP
#if FROMDEVICE_ALLOW_LINUX || FROMDEVICE_ALLOW_PCAP
_fd = -1;
#endif
}
......@@ -122,7 +122,7 @@ FromDevice::configure(Vector<String> &conf, ErrorHandler *errh)
// set _method
if (capture == "") {
#if FROMDEVICE_ALLOW_NETMAP || FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_LINUX
#if FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_LINUX
# if FROMDEVICE_ALLOW_PCAP
_method = _bpf_filter ? method_pcap : method_default;
# else
......@@ -139,10 +139,6 @@ FromDevice::configure(Vector<String> &conf, ErrorHandler *errh)
#if FROMDEVICE_ALLOW_PCAP
else if (capture == "PCAP")
_method = method_pcap;
#endif
#if FROMDEVICE_ALLOW_NETMAP
else if (capture == "NETMAP")
_method = method_netmap;
#endif
else
return errh->error("bad METHOD");
......@@ -308,17 +304,6 @@ FromDevice::initialize(ErrorHandler *errh)
if (!_ifname)
return errh->error("interface not set");
#if FROMDEVICE_ALLOW_NETMAP
if (_method == method_default || _method == method_netmap) {
_fd = _netmap.open(_ifname, _method == method_netmap, errh);
if (_fd >= 0) {
_datalink = FAKE_DLT_EN10MB;
_method = method_netmap;
_netmap.initialize_rings_rx(_timestamp);
}
}
#endif
#if FROMDEVICE_ALLOW_PCAP
if (_method == method_default || _method == method_pcap) {
assert(!_pcap);
......@@ -408,11 +393,11 @@ FromDevice::initialize(ErrorHandler *errh)
}
#endif
#if FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_NETMAP
if (_method == method_pcap || _method == method_netmap)
#if FROMDEVICE_ALLOW_PCAP
if (_method == method_pcap)
ScheduleInfo::initialize_task(this, &_task, false, errh);
#endif
#if FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_LINUX || FROMDEVICE_ALLOW_NETMAP
#if FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_LINUX
if (_fd >= 0)
add_select(_fd, SELECT_READ);
#endif
......@@ -429,10 +414,6 @@ FromDevice::cleanup(CleanupStage stage)
{
if (stage >= CLEANUP_INITIALIZED && !_sniffer)
KernelFilter::device_filter(_ifname, false, ErrorHandler::default_handler());
#if FROMDEVICE_ALLOW_NETMAP
if (_fd >= 0 && _method == method_netmap)
_netmap.close(_fd);
#endif
#if FROMDEVICE_ALLOW_LINUX
if (_fd >= 0 && _method == method_linux) {
if (_was_promisc >= 0)
......@@ -445,12 +426,12 @@ FromDevice::cleanup(CleanupStage stage)
pcap_close(_pcap);
_pcap = 0;
#endif
#if FROMDEVICE_ALLOW_NETMAP || FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_LINUX
#if FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_LINUX
_fd = -1;
#endif
}
#if FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_NETMAP
#if FROMDEVICE_ALLOW_PCAP
void
FromDevice::emit_packet(WritablePacket *p, int extra_len, const Timestamp &ts)
{
......@@ -474,7 +455,7 @@ FromDevice::emit_packet(WritablePacket *p, int extra_len, const Timestamp &ts)
}
#endif
#if FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_NETMAP
#if FROMDEVICE_ALLOW_PCAP
CLICK_ENDDECLS
extern "C" {
void
......@@ -501,22 +482,6 @@ CLICK_DECLS
void
FromDevice::selected(int, int)
{
// netmap and pcap are essentially the same code, different
// dispatch function. This code is also in run_task()
// with fast_reschedule()
#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);
if (r > 0) {
_count += r;
_task.reschedule();
} else if (r < 0 && ++_pcap_complaints < 5)
ErrorHandler::default_handler()->error("%p{element}: %s",
this, "nm_dispatch failed");
}
#endif
#if FROMDEVICE_ALLOW_PCAP
if (_method == method_pcap) {
// Read and push() at most one burst of packets.
......@@ -561,29 +526,17 @@ FromDevice::selected(int, int)
#endif
}
#if FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_NETMAP
#if FROMDEVICE_ALLOW_PCAP
bool
FromDevice::run_task(Task *)
{
// Read and push() at most one burst of packets.
int r = 0;
# 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);
if (r < 0 && ++_pcap_complaints < 5)
ErrorHandler::default_handler()->error("%p{element}: %s",
this, "nm_dispatch failed");
}
# endif
# if FROMDEVICE_ALLOW_PCAP
if (_method == method_pcap) {
r = pcap_dispatch(_pcap, _burst, FromDevice_get_packet, (u_char *) this);
if (r < 0 && ++_pcap_complaints < 5)
ErrorHandler::default_handler()->error("%p{element}: %s", this, pcap_geterr(_pcap));
}
# endif
if (r > 0) {
_count += r;
_task.fast_reschedule();
......@@ -652,5 +605,5 @@ FromDevice::add_handlers()
}
CLICK_ENDDECLS
ELEMENT_REQUIRES(userlevel FakePcap KernelFilter NetmapInfo)
ELEMENT_REQUIRES(userlevel FakePcap KernelFilter)
EXPORT_ELEMENT(FromDevice)
......@@ -18,12 +18,7 @@ int pcap_setnonblock(pcap_t *p, int nonblock, char *errbuf);
}
#endif
#if HAVE_NET_NETMAP_H
# define FROMDEVICE_ALLOW_NETMAP 1
# include "elements/userlevel/netmapinfo.hh"
#endif
#if FROMDEVICE_ALLOW_NETMAP || FROMDEVICE_ALLOW_PCAP
#if FROMDEVICE_ALLOW_PCAP
# include <click/task.hh>
extern "C" {
void FromDevice_get_packet(u_char*, const struct pcap_pkthdr*, const u_char*);
......@@ -182,7 +177,7 @@ class FromDevice : public Element { public:
void add_handlers() CLICK_COLD;
inline String ifname() const { return _ifname; }
#if FROMDEVICE_ALLOW_LINUX || FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_NETMAP
#if FROMDEVICE_ALLOW_LINUX || FROMDEVICE_ALLOW_PCAP
inline int fd() const { return _fd; }
#else
inline int fd() const { return -1; }
......@@ -202,11 +197,7 @@ class FromDevice : public Element { public:
static int set_promiscuous(int, String, bool);
#endif
#if FROMDEVICE_ALLOW_NETMAP
const NetmapInfo *netmap() const { return _method == method_netmap ? &_netmap : 0; }
#endif
#if FROMDEVICE_ALLOW_NETMAP || FROMDEVICE_ALLOW_PCAP
#if FROMDEVICE_ALLOW_PCAP
bool run_task(Task *task);
#endif
......@@ -214,24 +205,16 @@ class FromDevice : public Element { public:
private:
#if FROMDEVICE_ALLOW_LINUX || FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_NETMAP
#if FROMDEVICE_ALLOW_LINUX || FROMDEVICE_ALLOW_PCAP
int _fd;
#endif
#if FROMDEVICE_ALLOW_NETMAP || FROMDEVICE_ALLOW_PCAP
#if FROMDEVICE_ALLOW_PCAP
Task _task;
#endif
#if FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_NETMAP
void emit_packet(WritablePacket *p, int extra_len, const Timestamp &ts);
#endif
#if FROMDEVICE_ALLOW_PCAP
pcap_t *_pcap;
int _pcap_complaints;
#endif
#if FROMDEVICE_ALLOW_NETMAP
NetmapInfo _netmap;
int netmap_dispatch();
#endif
#if FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_NETMAP
#if FROMDEVICE_ALLOW_PCAP
friend void FromDevice_get_packet(u_char*, const struct pcap_pkthdr*,
const u_char*);
#endif
......@@ -259,7 +242,7 @@ class FromDevice : public Element { public:
int _snaplen;
uint16_t _protocol;
unsigned _headroom;
enum { method_default, method_netmap, method_pcap, method_linux };
enum { method_default, method_pcap, method_linux };
int _method;
#if FROMDEVICE_ALLOW_PCAP
String _bpf_filter;
......
......@@ -35,6 +35,7 @@ FromNetmapDevice::FromNetmapDevice() : _device(NULL), _promisc(1),_blockant(fals
#if HAVE_BATCH
in_batch_mode = BATCH_MODE_YES;
#endif
NetmapDevice::global_alloc += 2048;
}
void *
......@@ -210,9 +211,9 @@ FromNetmapDevice::receive_packets(Task* task, int begin, int end, bool fromtask)
#if HAVE_ZEROCOPY
if (slot->len > 64 && !(slot->flags & NS_MOREFRAG)) {
__builtin_prefetch(data);
p = Packet::make( data, slot->len, NetmapBufQ::buffer_destructor,NetmapBufQ::get_local_pool());
p = Packet::make( data, slot->len, NetmapBufQ::buffer_destructor,NetmapBufQ::local_pool());
if (!p) goto error;
slot->buf_idx = NetmapBufQ::get_local_pool()->extract();
slot->buf_idx = NetmapBufQ::local_pool()->extract();
slot->flags = NS_BUF_CHANGED;
} else
#endif
......
......@@ -18,12 +18,14 @@ CLICK_DECLS
/*
* =c
*
* FromNetmapDevice
* FromNetmapDevice(DEVNAME [, QUEUE, NR_QUEUE, [, I<keywords> PROMISC, BURST])
*
* * =item DEVNAME
* =s netdevices
*
* =item DEVNAME
*
* String. Device number
* *
*
* =item PROMISC
*
* Boolean. FromNetmapDevice puts the device in promiscuous mode if PROMISC is
......@@ -79,7 +81,6 @@ public:
int initialize(ErrorHandler*) CLICK_COLD;
void cleanup(CleanupStage) CLICK_COLD;
inline bool receive_packets(Task* task, int begin, int end, bool fromtask);
bool run_task(Task *);
......
// -*- 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"),
......@@ -17,135 +16,28 @@
*/
#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();
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;
}
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;
}
CLICK_DECLS
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;
int NetmapInfo::configure(Vector<String> &conf, ErrorHandler *errh) {
if (instance) {
return errh->error("You cannot place multiple instances of NetmapInfo !");
}
}
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);
}
instance = this;
if (Args(conf, this, errh)
.read_p("EXTRA_BUFFER", NetmapDevice::global_alloc)
.complete() < 0)
return -1;
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);
if (ret == 0)
ioctl(desc->fd, NIOCTXSYNC, 0);
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;
#else
(void)noutputs;
#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_REQUIRES(netmap)
ELEMENT_PROVIDES(NetmapInfo)
ELEMENT_REQUIRES(userlevel netmap)
EXPORT_ELEMENT(NetmapInfo)
#ifndef CLICK_NETMAPINFO_HH
#define CLICK_NETMAPINFO_HH 1
#define CLICK_NETMAPINFO_HH
#include <click/element.hh>
#include <click/netmapdevice.hh>
#if HAVE_NET_NETMAP_H
#include <net/if.h>
#ifndef NETMAP_WITH_LIBS
#define NETMAP_WITH_LIBS 1
#endif
#include <net/netmap.h>
#include <net/netmap_user.h>
// XXX bug in netmap_user.h , the prototype should be available
#ifndef NETMAP_WITH_LIBS
typedef void (*nm_cb_t)(u_char *, const struct nm_pkthdr *, const u_char *d);
#endif
#include <click/packet.hh>
#include <click/error.hh>
CLICK_DECLS
/* a netmap port as returned by nm_open */
class NetmapInfo { public:
struct nm_desc *desc;
class NetmapInfo *parent; /* same pool */
class NetmapBufQ bufq; /* free buffer queue */
// to recycle buffers,
// nmr.arg3 is the number of extra buffers
// nifp->ni_bufs_head is the index of the first buffer.
unsigned int active_users; // we do not close until done.
NetmapInfo *destructor_arg; // either this or parent's main_mem
class NetmapInfo : public Element { public:
const char *class_name() const { return "NetmapInfo"; }
int open(const String &ifname,
bool always_error, ErrorHandler *errh);
void initialize_rings_rx(int timestamp);
void initialize_rings_tx();
void close(int fd);
// send a packet, possibly using zerocopy if noutputs == 0
// and other conditions apply
bool send_packet(Packet *p, int noutputs);
int dispatch(int burst, nm_cb_t cb, u_char *arg);
#if 0
// XXX return a buffer to the ring
bool refill(struct netmap_ring *ring) {
if (buffers) {
unsigned char *buf = buffers;
buffers = *reinterpret_cast<unsigned char **>(buffers);
unsigned res1idx = ring->head;
ring->slot[res1idx].buf_idx = NETMAP_BUF_IDX(ring, (char *) buf);
ring->slot[res1idx].flags |= NS_BUF_CHANGED;
ring->head = nm_ring_next(ring, res1idx);
return true;
} else
return false;
}
#endif
int configure_phase() const { return CONFIGURE_PHASE_FIRST; }
static bool is_netmap_buffer(Packet *p) {
return p->buffer_destructor() == buffer_destructor;
}
int configure(Vector<String> &conf, ErrorHandler *errh);
/*
* the destructor appends the buffer to the freelist in the ring,
* using the first field as pointer.
*/
static void buffer_destructor(unsigned char *buf, size_t, void *arg) {
NetmapInfo *x = reinterpret_cast<NetmapInfo *>(arg);
click_chatter("%s ni %p buf %p\n", __FUNCTION__,
x, buf);
if (x->bufq.insert_p(buf)) {
// XXX error
}
}
static NetmapInfo* instance;
};