Commit 1742667d authored by Tom Barbette's avatar Tom Barbette

Import some general-purpose changes from Metron into FastClick

- General documentation fixes
- Avalaibility of JSON library is exported
- HashSwitch is batch-compatible
- PaintSwitch is batch-compatible
- String have trim_space_left and replace
- Add statistics handlers for DPDK elements

Changes are mostly from Georgios Katsikas, RISE SICS and Tom Barbette (ULiege)
parent 315a4e64
Pipeline #1795 passed with stage
in 9 minutes and 57 seconds
...@@ -117,6 +117,9 @@ ...@@ -117,6 +117,9 @@
/* Define if IPsec support is enabled. */ /* Define if IPsec support is enabled. */
#undef HAVE_IPSEC #undef HAVE_IPSEC
/* Define if Json support is enabled. */
#undef HAVE_JSON
/* Define to 1 if the system has the type `long long'. */ /* Define to 1 if the system has the type `long long'. */
#undef HAVE_LONG_LONG #undef HAVE_LONG_LONG
......
...@@ -7427,6 +7427,7 @@ fi ...@@ -7427,6 +7427,7 @@ fi
test "x$enable_all_elements" = xyes -a \( "x$enable_json" = xNO -o "x$enable_json" = x \) && enable_json=yes test "x$enable_all_elements" = xyes -a \( "x$enable_json" = xNO -o "x$enable_json" = x \) && enable_json=yes
if test "x$enable_json" = xyes; then if test "x$enable_json" = xyes; then
: :
$as_echo "#define HAVE_JSON 1" >>confdefs.h
fi fi
# Check whether --enable-local was given. # Check whether --enable-local was given.
......
...@@ -603,7 +603,7 @@ ELEMENTS_ARG_ENABLE(icmp, [include ICMP elements], yes) ...@@ -603,7 +603,7 @@ ELEMENTS_ARG_ENABLE(icmp, [include ICMP elements], yes)
ELEMENTS_ARG_ENABLE(ip, [include IP elements], yes) ELEMENTS_ARG_ENABLE(ip, [include IP elements], yes)
ELEMENTS_ARG_ENABLE(ip6, [include IPv6 elements], NO, AC_DEFINE(HAVE_IP6)) ELEMENTS_ARG_ENABLE(ip6, [include IPv6 elements], NO, AC_DEFINE(HAVE_IP6))
ELEMENTS_ARG_ENABLE(ipsec, [include IP security elements], NO, AC_DEFINE(HAVE_IPSEC)) ELEMENTS_ARG_ENABLE(ipsec, [include IP security elements], NO, AC_DEFINE(HAVE_IPSEC))
ELEMENTS_ARG_ENABLE(json, [include JSON helpers], NO) ELEMENTS_ARG_ENABLE(json, [include JSON helpers], NO, AC_DEFINE(HAVE_JSON))
ELEMENTS_ARG_ENABLE(local, [include local elements], NO) ELEMENTS_ARG_ENABLE(local, [include local elements], NO)
ELEMENTS_ARG_ENABLE(radio, [include radio elements], NO) ELEMENTS_ARG_ENABLE(radio, [include radio elements], NO)
ELEMENTS_ARG_ENABLE(research, [include research elements], NO) ELEMENTS_ARG_ENABLE(research, [include research elements], NO)
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Batching support from Georgios Katsikas * Batching support from Georgios Katsikas
* *
* Copyright (c) 2015 University of Liege * Copyright (c) 2015 University of Liege
* Copyright (c) 2017 Georgios Katsikas, RISE SICS AB * Copyright (c) 2017 Georgios Katsikas, RISE SICS
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
* icmppingrewriter.{cc,hh} -- rewrites ICMP echoes and replies * icmppingrewriter.{cc,hh} -- rewrites ICMP echoes and replies
* Eddie Kohler * Eddie Kohler
* *
* Per-core, thread safe data structures by Georgios Katsikas and batching by Tom Barbette * Per-core, thread safe data structures by Georgios Katsikas and batching by
* Tom Barbette
* *
* Copyright (c) 2000-2001 Mazu Networks, Inc. * Copyright (c) 2000-2001 Mazu Networks, Inc.
* Copyright (c) 2009-2010 Meraki, Inc. * Copyright (c) 2009-2010 Meraki, Inc.
...@@ -214,15 +215,8 @@ ICMPPingRewriter::process(int port, Packet *p_in) ...@@ -214,15 +215,8 @@ ICMPPingRewriter::process(int port, Packet *p_in)
} }
void void
ICMPPingRewriter::push(int port, Packet *p) ICMPPingRewriter::push(int port, Packet *p_in) {
{ checked_output_push(process(port, p_in),p_in);
int output_port = process(port, p);
if (output_port < 0) {
p->kill();
return;
}
output(output_port).push(p);
} }
#if HAVE_BATCH #if HAVE_BATCH
......
...@@ -110,9 +110,9 @@ class ICMPPingRewriter : public IPRewriterBase { public: ...@@ -110,9 +110,9 @@ class ICMPPingRewriter : public IPRewriterBase { public:
const IPFlowID &rewritten_flowid, int input); const IPFlowID &rewritten_flowid, int input);
void destroy_flow(IPRewriterFlow *flow); void destroy_flow(IPRewriterFlow *flow);
void push(int, Packet *); void push(int, Packet *) override;
#if HAVE_BATCH #if HAVE_BATCH
void push_batch(int, PacketBatch *); void push_batch(int, PacketBatch *) override;
#endif #endif
void add_handlers() CLICK_COLD; void add_handlers() CLICK_COLD;
......
...@@ -3,7 +3,10 @@ ...@@ -3,7 +3,10 @@
* specified packet fields * specified packet fields
* Eddie Kohler * Eddie Kohler
* *
* Computational batching support by Georgios Katsikas
*
* Copyright (c) 1999-2000 Massachusetts Institute of Technology * Copyright (c) 1999-2000 Massachusetts Institute of Technology
* Copyright (c) 2018 Georgios Katsikas, RISE SICS
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
...@@ -22,41 +25,60 @@ ...@@ -22,41 +25,60 @@
#include <click/args.hh> #include <click/args.hh>
CLICK_DECLS CLICK_DECLS
HashSwitch::HashSwitch() HashSwitch::HashSwitch() : _offset(-1)
: _offset(-1)
{ {
} }
int int
HashSwitch::configure(Vector<String> &conf, ErrorHandler *errh) HashSwitch::configure(Vector<String> &conf, ErrorHandler *errh)
{ {
_max = noutputs();
if (Args(conf, this, errh) if (Args(conf, this, errh)
.read_mp("OFFSET", _offset) .read_mp("OFFSET", _offset)
.read_mp("LENGTH", _length).complete() < 0) .read_mp("LENGTH", _length)
return -1; .read("MAX", _max)
.complete() < 0)
return -1;
if (_length == 0) if (_length == 0)
return errh->error("length must be > 0"); return errh->error("length must be > 0");
return 0; return 0;
} }
int
HashSwitch::process(Packet *p)
{
const unsigned char *data = p->data();
int o = _offset, l = _length;
if ((int)p->length() < o + l)
return 0;
else {
int d = 0;
for (int i = o; i < o + l; i++)
d += data[i];
int n = _max;
if (n == 2 || n == 4 || n == 8)
return (d ^ (d>>4)) & (n-1);
else
return (d % n);
}
}
void
HashSwitch::push(int port, Packet *p)
{
output(process(p)).push(p);
}
#if HAVE_BATCH
void void
HashSwitch::push(int, Packet *p) HashSwitch::push_batch(int port, PacketBatch *batch)
{ {
const unsigned char *data = p->data(); auto fnt = [this, port](Packet *p) { return process(p); };
int o = _offset, l = _length; CLASSIFY_EACH_PACKET(_max + 1, fnt, batch, checked_output_push_batch);
if ((int)p->length() < o + l)
output(0).push(p);
else {
int d = 0;
for (int i = o; i < o + l; i++)
d += data[i];
int n = noutputs();
if (n == 2 || n == 4 || n == 8)
output((d ^ (d>>4)) & (n-1)).push(p);
else
output(d % n).push(p);
}
} }
#endif
CLICK_ENDDECLS CLICK_ENDDECLS
EXPORT_ELEMENT(HashSwitch) EXPORT_ELEMENT(HashSwitch)
......
#ifndef CLICK_HASHSWITCH_HH #ifndef CLICK_HASHSWITCH_HH
#define CLICK_HASHSWITCH_HH #define CLICK_HASHSWITCH_HH
#include <click/element.hh> #include <click/batchelement.hh>
CLICK_DECLS CLICK_DECLS
/* /*
...@@ -22,22 +22,27 @@ CLICK_DECLS ...@@ -22,22 +22,27 @@ CLICK_DECLS
* Switch, RoundRobinSwitch, StrideSwitch, RandomSwitch * Switch, RoundRobinSwitch, StrideSwitch, RandomSwitch
*/ */
class HashSwitch : public Element { class HashSwitch : public BatchElement {
int _offset; int _offset;
int _length; int _length;
int _max;
public: public:
HashSwitch() CLICK_COLD; HashSwitch() CLICK_COLD;
const char *class_name() const { return "HashSwitch"; } const char *class_name() const { return "HashSwitch"; }
const char *port_count() const { return "1/1-"; } const char *port_count() const { return "1/1-"; }
const char *processing() const { return PUSH; } const char *processing() const { return PUSH; }
int configure(Vector<String> &, ErrorHandler *) CLICK_COLD; int configure(Vector<String> &, ErrorHandler *) CLICK_COLD;
void push(int port, Packet *); int process(Packet *);
void push(int port, Packet *);
#if HAVE_BATCH
void push_batch(int port, PacketBatch *);
#endif
}; };
......
...@@ -52,6 +52,36 @@ PaintSwitch::push(int, Packet *p) ...@@ -52,6 +52,36 @@ PaintSwitch::push(int, Packet *p)
} }
} }
#if HAVE_BATCH
void
PaintSwitch::push_batch(int, PacketBatch *batch) {
auto fnt = [this](Packet* p){
int output_port = static_cast<int>(p->anno_u8(_anno));
if (output_port != 0xFF) {
if (output_port < noutputs())
return output_port;
else
return noutputs();
} else {
for (int i = 0; i < noutputs() - 1; i++) {
if (Packet *q = p->clone()) {
output(i).push_batch(PacketBatch::make_from_packet(q));
}
}
return noutputs() - 1;
}
};
auto on_finish = [this](int port, PacketBatch* batch) {
if (likely(port < noutputs())) {
output(port).push_batch(batch);
} else {
batch->fast_kill();
}
};
CLASSIFY_EACH_PACKET((noutputs() + 1), fnt, batch, on_finish);
}
#endif
CLICK_ENDDECLS CLICK_ENDDECLS
EXPORT_ELEMENT(PaintSwitch) EXPORT_ELEMENT(PaintSwitch)
ELEMENT_MT_SAFE(PaintSwitch) ELEMENT_MT_SAFE(PaintSwitch)
#ifndef CLICK_PAINTSWITCH_HH #ifndef CLICK_PAINTSWITCH_HH
#define CLICK_PAINTSWITCH_HH #define CLICK_PAINTSWITCH_HH
#include <click/element.hh> #include <click/batchelement.hh>
CLICK_DECLS CLICK_DECLS
/* /*
...@@ -28,7 +28,7 @@ specify any one-byte annotation. ...@@ -28,7 +28,7 @@ specify any one-byte annotation.
=a StaticSwitch, PullSwitch, RoundRobinSwitch, StrideSwitch, HashSwitch, =a StaticSwitch, PullSwitch, RoundRobinSwitch, StrideSwitch, HashSwitch,
RandomSwitch, Paint, PaintTee */ RandomSwitch, Paint, PaintTee */
class PaintSwitch : public Element { public: class PaintSwitch : public BatchElement { public:
PaintSwitch() CLICK_COLD; PaintSwitch() CLICK_COLD;
...@@ -39,6 +39,9 @@ class PaintSwitch : public Element { public: ...@@ -39,6 +39,9 @@ class PaintSwitch : public Element { public:
int configure(Vector<String> &conf, ErrorHandler *errh) CLICK_COLD; int configure(Vector<String> &conf, ErrorHandler *errh) CLICK_COLD;
void push(int, Packet *); void push(int, Packet *);
#if HAVE_BATCH
void push_batch(int, PacketBatch *);
#endif
private: private:
......
...@@ -106,36 +106,26 @@ Pipeliner::initialize(ErrorHandler *errh) ...@@ -106,36 +106,26 @@ Pipeliner::initialize(ErrorHandler *errh)
stats.compress(passing); stats.compress(passing);
_home_thread_id = home_thread_id(); _home_thread_id = home_thread_id();
if (_ring_size == -1) {
# if HAVE_BATCH
if (receives_batch) {
_ring_size = 16;
} else
# endif
{
_ring_size = 1024;
}
}
for (unsigned i = 0; i < storage.weight(); i++) { for (unsigned i = 0; i < storage.weight(); i++) {
storage.get_value(i).initialize(_ring_size); if (!storage.get_value(i).initialized())
storage.get_value(i).initialize(_ring_size);
} }
for (int i = 0; i < passing.size(); i++) { for (int i = 0; i < passing.weight(); i++) {
if (passing[i]) { if (passing[i]) {
click_chatter("%p{element} : Pipeline from %d to %d",this, i,_home_thread_id); click_chatter("%p{element} : Pipeline from %d to %d", this, i, _home_thread_id);
WritablePacket::pool_transfer(_home_thread_id,i); WritablePacket::pool_transfer(_home_thread_id,i);
} }
} }
if (_block && !_allow_direct_traversal && passing[_home_thread_id]) { if (_block && !_allow_direct_traversal && passing[_home_thread_id]) {
return errh->error("Possible deadlock ! Pipeliner is served by thread " return errh->error("Possible deadlock ! Pipeliner is served by thread "
"%d, and the same thread can push packets to it. " "%d, and the same thread can push packets to it. "
"As Pipeliner is in blocking mode without direct " "As Pipeliner is in blocking mode without direct "
"traversal, it could block trying to push a " "traversal, it could block trying to push a "
"packet, preventing the very same thread to drain " "packet, preventing the very same thread to drain "
"the queue."); "the queue.");
} }
if (!_nouseless && passing.weight() == 1 && passing[_home_thread_id] == 1) { if (!_nouseless && passing.weight() == 1 && passing[_home_thread_id] == 1) {
......
...@@ -78,6 +78,11 @@ Used for interprocess communication. Defaults to 1024. ...@@ -78,6 +78,11 @@ Used for interprocess communication. Defaults to 1024.
Integer. Number of frames to read/write from/to a DPDK device. Integer. Number of frames to read/write from/to a DPDK device.
Defaults to 32. Defaults to 32.
=item RING_FLAGS
Integer. Parameters to pass to a ring-based DPDK process communication.
Defaults to 0.
=item RING_SIZE =item RING_SIZE
Integer. The size of the ring used for DPDK inter-process communication. Integer. The size of the ring used for DPDK inter-process communication.
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <click/error.hh> #include <click/error.hh>
#include <click/standard/scheduleinfo.hh> #include <click/standard/scheduleinfo.hh>
#include <click/etheraddress.hh> #include <click/etheraddress.hh>
#include <click/straccum.hh>
#include "fromdpdkdevice.hh" #include "fromdpdkdevice.hh"
...@@ -104,7 +105,7 @@ int FromDPDKDevice::initialize(ErrorHandler *errh) ...@@ -104,7 +105,7 @@ int FromDPDKDevice::initialize(ErrorHandler *errh)
ret = initialize_rx(errh); ret = initialize_rx(errh);
if (ret != 0) return ret; if (ret != 0) return ret;
for (int i = firstqueue; i < firstqueue + n_queues; i++) { for (unsigned i = firstqueue; i <= lastqueue; i++) {
ret = _dev->add_rx_queue(i , _promisc, ndesc, errh); ret = _dev->add_rx_queue(i , _promisc, ndesc, errh);
if (ret != 0) return ret; if (ret != 0) return ret;
} }
...@@ -113,7 +114,12 @@ int FromDPDKDevice::initialize(ErrorHandler *errh) ...@@ -113,7 +114,12 @@ int FromDPDKDevice::initialize(ErrorHandler *errh)
if (ret != 0) return ret; if (ret != 0) return ret;
if (queue_share > 1) if (queue_share > 1)
return errh->error("Sharing queue between multiple threads is not yet supported by FromDPDKDevice. Raise the number using N_QUEUES of queues or limit the number of threads using MAXTHREADS"); return errh->error(
"Sharing queue between multiple threads is not "
"yet supported by FromDPDKDevice. "
"Raise the number using N_QUEUES of queues or "
"limit the number of threads using MAXTHREADS"
);
if (all_initialized()) { if (all_initialized()) {
ret = DPDKDevice::initialize(errh); ret = DPDKDevice::initialize(errh);
...@@ -128,15 +134,16 @@ void FromDPDKDevice::cleanup(CleanupStage) ...@@ -128,15 +134,16 @@ void FromDPDKDevice::cleanup(CleanupStage)
cleanup_tasks(); cleanup_tasks();
} }
bool FromDPDKDevice::run_task(Task * t) bool FromDPDKDevice::run_task(Task *t)
{ {
struct rte_mbuf *pkts[_burst]; struct rte_mbuf *pkts[_burst];
int ret = 0; int ret = 0;
for (int iqueue = queue_for_thisthread_begin(); iqueue<=queue_for_thisthread_end();iqueue++) { for (int iqueue = queue_for_thisthread_begin();
iqueue<=queue_for_thisthread_end(); iqueue++) {
#if HAVE_BATCH #if HAVE_BATCH
PacketBatch* head = 0; PacketBatch* head = 0;
WritablePacket *last; WritablePacket *last;
#endif #endif
unsigned n = rte_eth_rx_burst(_dev->port_id, iqueue, pkts, _burst); unsigned n = rte_eth_rx_burst(_dev->port_id, iqueue, pkts, _burst);
for (unsigned i = 0; i < n; ++i) { for (unsigned i = 0; i < n; ++i) {
...@@ -173,7 +180,7 @@ bool FromDPDKDevice::run_task(Task * t) ...@@ -173,7 +180,7 @@ bool FromDPDKDevice::run_task(Task * t)
last->set_next(p); last->set_next(p);
last = p; last = p;
#else #else
output(0).push(p); output(0).push(p);
#endif #endif
} }
#if HAVE_BATCH #if HAVE_BATCH
...@@ -203,18 +210,23 @@ String FromDPDKDevice::read_handler(Element *e, void * thunk) ...@@ -203,18 +210,23 @@ String FromDPDKDevice::read_handler(Element *e, void * thunk)
if (!fd->_dev) if (!fd->_dev)
return "false"; return "false";
else else
return "true"; return String(fd->_active);
case h_device:
if (!fd->_dev)
return "undefined";
else
return String((int) fd->_dev->port_id);
case h_mac: { case h_mac: {
if (!fd->_dev) if (!fd->_dev)
return String::make_empty(); return String::make_empty();
struct ether_addr mac_addr; struct ether_addr mac_addr;
rte_eth_macaddr_get(fd->_dev->port_id, &mac_addr); rte_eth_macaddr_get(fd->_dev->port_id, &mac_addr);
return EtherAddress((unsigned char*)&mac_addr).unparse(); return EtherAddress((unsigned char*)&mac_addr).unparse_colon();
}
case h_vendor: case h_vendor:
return fd->_dev->get_device_vendor_name(); return fd->_dev->get_device_vendor_name();
case h_driver: case h_driver:
return String(fd->_dev->get_device_driver()); return String(fd->_dev->get_device_driver());
}
} }
return 0; return 0;
...@@ -224,8 +236,9 @@ String FromDPDKDevice::status_handler(Element *e, void * thunk) ...@@ -224,8 +236,9 @@ String FromDPDKDevice::status_handler(Element *e, void * thunk)
{ {
FromDPDKDevice *fd = static_cast<FromDPDKDevice *>(e); FromDPDKDevice *fd = static_cast<FromDPDKDevice *>(e);
struct rte_eth_link link; struct rte_eth_link link;
if (!fd->_dev) if (!fd->_dev) {
return "0"; return "0";
}
rte_eth_link_get_nowait(fd->_dev->port_id, &link); rte_eth_link_get_nowait(fd->_dev->port_id, &link);
#ifndef ETH_LINK_UP #ifndef ETH_LINK_UP
...@@ -235,7 +248,8 @@ String FromDPDKDevice::status_handler(Element *e, void * thunk) ...@@ -235,7 +248,8 @@ String FromDPDKDevice::status_handler(Element *e, void * thunk)
case h_carrier: case h_carrier:
return (link.link_status == ETH_LINK_UP ? "1" : "0"); return (link.link_status == ETH_LINK_UP ? "1" : "0");
case h_duplex: case h_duplex:
return (link.link_status == ETH_LINK_UP ? (link.link_duplex == ETH_LINK_FULL_DUPLEX ? "1" : "0") : "-1"); return (link.link_status == ETH_LINK_UP ?
(link.link_duplex == ETH_LINK_FULL_DUPLEX ? "1" : "0") : "-1");
#if RTE_VERSION >= RTE_VERSION_NUM(16,04,0,0) #if RTE_VERSION >= RTE_VERSION_NUM(16,04,0,0)
case h_autoneg: case h_autoneg:
return String(link.link_autoneg); return String(link.link_autoneg);
...@@ -246,12 +260,13 @@ String FromDPDKDevice::status_handler(Element *e, void * thunk) ...@@ -246,12 +260,13 @@ String FromDPDKDevice::status_handler(Element *e, void * thunk)
return 0; return 0;
} }
String FromDPDKDevice::statistics_handler(Element *e, void * thunk) String FromDPDKDevice::statistics_handler(Element *e, void *thunk)
{ {
FromDPDKDevice *fd = static_cast<FromDPDKDevice *>(e); FromDPDKDevice *fd = static_cast<FromDPDKDevice *>(e);
struct rte_eth_stats stats; struct rte_eth_stats stats;
if (!fd->_dev) if (!fd->_dev) {
return "0"; return "0";
}
if (rte_eth_stats_get(fd->_dev->port_id, &stats)) if (rte_eth_stats_get(fd->_dev->port_id, &stats))
return String::make_empty(); return String::make_empty();
...@@ -261,7 +276,7 @@ String FromDPDKDevice::statistics_handler(Element *e, void * thunk) ...@@ -261,7 +276,7 @@ String FromDPDKDevice::statistics_handler(Element *e, void * thunk)
return String(stats.ipackets); return String(stats.ipackets);
case h_ibytes: case h_ibytes:
return String(stats.ibytes); return String(stats.ibytes);
case h_idropped: case h_imissed:
return String(stats.imissed); return String(stats.imissed);
case h_ierrors: case h_ierrors:
return String(stats.ierrors); return String(stats.ierrors);
...@@ -270,9 +285,102 @@ String FromDPDKDevice::statistics_handler(Element *e, void * thunk) ...@@ -270,9 +285,102 @@ String FromDPDKDevice::statistics_handler(Element *e, void * thunk)
return 0; return 0;
} }
int FromDPDKDevice::write_handler(
const String &input, Element *e, void *thunk, ErrorHandler *errh) {
FromDPDKDevice *fd = static_cast<FromDPDKDevice *>(e);
if (!fd->_dev) {
return -1;
}
switch((uintptr_t) thunk) {
case h_add_mac: {
EtherAddress mac;
int pool = 0;
int ret;
if (!EtherAddressArg().parse(input, mac)) {
return errh->error("Invalid MAC address %s",input.c_str());
}
ret = rte_eth_dev_mac_addr_add(
fd->_dev->port_id,
reinterpret_cast<ether_addr*>(mac.data()), pool
);
if (ret != 0) {
return errh->error("Could not add mac address !");
}
return 0;
}
case h_active: {
bool active;
if (!BoolArg::parse(input,active))
return errh->error("Not a valid boolean");
if (fd->_active != active) {
fd->_active = active;
if (fd->_active) {
for (int i = 0; i < fd->usable_threads.weight(); i++) {
fd->_tasks[i]->reschedule();
}
} else {
for (int i = 0; i < fd->usable_threads.weight(); i++) {
fd->_tasks[i]->unschedule();
}
}
}
return 0;
}
}
return -1;
}
int FromDPDKDevice::xstats_handler(
int operation, String& input, Element* e,
const Handler *handler, ErrorHandler* errh) {
FromDPDKDevice *fd = static_cast<FromDPDKDevice *>(e);
if (!fd->_dev)
return -1;
struct rte_eth_xstat_name* names;
#if RTE_VERSION >= RTE_VERSION_NUM(16,07,0,0)
int len = rte_eth_xstats_get_names(fd->_dev->port_id, 0, 0);
names = static_cast<struct rte_eth_xstat_name*>(
malloc(sizeof(struct rte_eth_xstat_name) * len)
);
rte_eth_xstats_get_names(fd->_dev->port_id,names,len);
struct rte_eth_xstat* xstats;
xstats = static_cast<struct rte_eth_xstat*>(malloc(
sizeof(struct rte_eth_xstat) * len)
);
rte_eth_xstats_get(fd->_dev->port_id,xstats,len);
if (input == "") {
StringAccum acc;
for (int i = 0; i < len; i++) {
acc << names[i].name << "["<<
xstats[i].id << "] = " <<
xstats[i].value << "\n";
}
input = acc.take_string();
} else {
for (int i = 0; i < len; i++) {
if (strcmp(names[i].name,