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 @@
/* Define if IPsec support is enabled. */
#undef HAVE_IPSEC
/* Define if Json support is enabled. */
#undef HAVE_JSON
/* Define to 1 if the system has the type `long long'. */
#undef HAVE_LONG_LONG
......
......@@ -7427,6 +7427,7 @@ fi
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
:
$as_echo "#define HAVE_JSON 1" >>confdefs.h
fi
# Check whether --enable-local was given.
......
......@@ -603,7 +603,7 @@ ELEMENTS_ARG_ENABLE(icmp, [include ICMP 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(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(radio, [include radio elements], NO)
ELEMENTS_ARG_ENABLE(research, [include research elements], NO)
......
......@@ -5,7 +5,7 @@
* Batching support from Georgios Katsikas
*
* 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
* copy of this software and associated documentation files (the "Software"),
......
......@@ -2,7 +2,8 @@
* icmppingrewriter.{cc,hh} -- rewrites ICMP echoes and replies
* 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) 2009-2010 Meraki, Inc.
......@@ -214,15 +215,8 @@ ICMPPingRewriter::process(int port, Packet *p_in)
}
void
ICMPPingRewriter::push(int port, Packet *p)
{
int output_port = process(port, p);
if (output_port < 0) {
p->kill();
return;
}
output(output_port).push(p);
ICMPPingRewriter::push(int port, Packet *p_in) {
checked_output_push(process(port, p_in),p_in);
}
#if HAVE_BATCH
......
......@@ -110,9 +110,9 @@ class ICMPPingRewriter : public IPRewriterBase { public:
const IPFlowID &rewritten_flowid, int input);
void destroy_flow(IPRewriterFlow *flow);
void push(int, Packet *);
void push(int, Packet *) override;
#if HAVE_BATCH
void push_batch(int, PacketBatch *);
void push_batch(int, PacketBatch *) override;
#endif
void add_handlers() CLICK_COLD;
......
......@@ -3,7 +3,10 @@
* specified packet fields
* Eddie Kohler
*
* Computational batching support by Georgios Katsikas
*
* 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
* copy of this software and associated documentation files (the "Software"),
......@@ -22,42 +25,61 @@
#include <click/args.hh>
CLICK_DECLS
HashSwitch::HashSwitch()
: _offset(-1)
HashSwitch::HashSwitch() : _offset(-1)
{
}
int
HashSwitch::configure(Vector<String> &conf, ErrorHandler *errh)
{
_max = noutputs();
if (Args(conf, this, errh)
.read_mp("OFFSET", _offset)
.read_mp("LENGTH", _length).complete() < 0)
.read_mp("LENGTH", _length)
.read("MAX", _max)
.complete() < 0)
return -1;
if (_length == 0)
return errh->error("length must be > 0");
return 0;
}
void
HashSwitch::push(int, Packet *p)
int
HashSwitch::process(Packet *p)
{
const unsigned char *data = p->data();
int o = _offset, l = _length;
if ((int)p->length() < o + l)
output(0).push(p);
return 0;
else {
int d = 0;
for (int i = o; i < o + l; i++)
d += data[i];
int n = noutputs();
int n = _max;
if (n == 2 || n == 4 || n == 8)
output((d ^ (d>>4)) & (n-1)).push(p);
return (d ^ (d>>4)) & (n-1);
else
output(d % n).push(p);
return (d % n);
}
}
void
HashSwitch::push(int port, Packet *p)
{
output(process(p)).push(p);
}
#if HAVE_BATCH
void
HashSwitch::push_batch(int port, PacketBatch *batch)
{
auto fnt = [this, port](Packet *p) { return process(p); };
CLASSIFY_EACH_PACKET(_max + 1, fnt, batch, checked_output_push_batch);
}
#endif
CLICK_ENDDECLS
EXPORT_ELEMENT(HashSwitch)
ELEMENT_MT_SAFE(HashSwitch)
#ifndef CLICK_HASHSWITCH_HH
#define CLICK_HASHSWITCH_HH
#include <click/element.hh>
#include <click/batchelement.hh>
CLICK_DECLS
/*
......@@ -22,10 +22,11 @@ CLICK_DECLS
* Switch, RoundRobinSwitch, StrideSwitch, RandomSwitch
*/
class HashSwitch : public Element {
class HashSwitch : public BatchElement {
int _offset;
int _length;
int _max;
public:
......@@ -37,7 +38,11 @@ class HashSwitch : public Element {
int configure(Vector<String> &, ErrorHandler *) CLICK_COLD;
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)
}
}
#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
EXPORT_ELEMENT(PaintSwitch)
ELEMENT_MT_SAFE(PaintSwitch)
#ifndef CLICK_PAINTSWITCH_HH
#define CLICK_PAINTSWITCH_HH
#include <click/element.hh>
#include <click/batchelement.hh>
CLICK_DECLS
/*
......@@ -28,7 +28,7 @@ specify any one-byte annotation.
=a StaticSwitch, PullSwitch, RoundRobinSwitch, StrideSwitch, HashSwitch,
RandomSwitch, Paint, PaintTee */
class PaintSwitch : public Element { public:
class PaintSwitch : public BatchElement { public:
PaintSwitch() CLICK_COLD;
......@@ -39,6 +39,9 @@ class PaintSwitch : public Element { public:
int configure(Vector<String> &conf, ErrorHandler *errh) CLICK_COLD;
void push(int, Packet *);
#if HAVE_BATCH
void push_batch(int, PacketBatch *);
#endif
private:
......
......@@ -106,25 +106,15 @@ Pipeliner::initialize(ErrorHandler *errh)
stats.compress(passing);
_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++) {
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]) {
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);
}
}
......
......@@ -78,6 +78,11 @@ Used for interprocess communication. Defaults to 1024.
Integer. Number of frames to read/write from/to a DPDK device.
Defaults to 32.
=item RING_FLAGS
Integer. Parameters to pass to a ring-based DPDK process communication.
Defaults to 0.
=item RING_SIZE
Integer. The size of the ring used for DPDK inter-process communication.
......
......@@ -23,6 +23,7 @@
#include <click/error.hh>
#include <click/standard/scheduleinfo.hh>
#include <click/etheraddress.hh>
#include <click/straccum.hh>
#include "fromdpdkdevice.hh"
......@@ -104,7 +105,7 @@ int FromDPDKDevice::initialize(ErrorHandler *errh)
ret = initialize_rx(errh);
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);
if (ret != 0) return ret;
}
......@@ -113,7 +114,12 @@ int FromDPDKDevice::initialize(ErrorHandler *errh)
if (ret != 0) return ret;
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()) {
ret = DPDKDevice::initialize(errh);
......@@ -128,12 +134,13 @@ void FromDPDKDevice::cleanup(CleanupStage)
cleanup_tasks();
}
bool FromDPDKDevice::run_task(Task * t)
bool FromDPDKDevice::run_task(Task *t)
{
struct rte_mbuf *pkts[_burst];
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
PacketBatch* head = 0;
WritablePacket *last;
......@@ -203,19 +210,24 @@ String FromDPDKDevice::read_handler(Element *e, void * thunk)
if (!fd->_dev)
return "false";
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: {
if (!fd->_dev)
return String::make_empty();
struct ether_addr 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:
return fd->_dev->get_device_vendor_name();
case h_driver:
return String(fd->_dev->get_device_driver());
}
}
return 0;
}
......@@ -224,8 +236,9 @@ String FromDPDKDevice::status_handler(Element *e, void * thunk)
{
FromDPDKDevice *fd = static_cast<FromDPDKDevice *>(e);
struct rte_eth_link link;
if (!fd->_dev)
if (!fd->_dev) {
return "0";
}
rte_eth_link_get_nowait(fd->_dev->port_id, &link);
#ifndef ETH_LINK_UP
......@@ -235,7 +248,8 @@ String FromDPDKDevice::status_handler(Element *e, void * thunk)
case h_carrier:
return (link.link_status == ETH_LINK_UP ? "1" : "0");
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)
case h_autoneg:
return String(link.link_autoneg);
......@@ -246,12 +260,13 @@ String FromDPDKDevice::status_handler(Element *e, void * thunk)
return 0;
}
String FromDPDKDevice::statistics_handler(Element *e, void * thunk)
String FromDPDKDevice::statistics_handler(Element *e, void *thunk)
{
FromDPDKDevice *fd = static_cast<FromDPDKDevice *>(e);
struct rte_eth_stats stats;
if (!fd->_dev)
if (!fd->_dev) {
return "0";
}
if (rte_eth_stats_get(fd->_dev->port_id, &stats))
return String::make_empty();
......@@ -261,7 +276,7 @@ String FromDPDKDevice::statistics_handler(Element *e, void * thunk)
return String(stats.ipackets);
case h_ibytes:
return String(stats.ibytes);
case h_idropped:
case h_imissed:
return String(stats.imissed);
case h_ierrors:
return String(stats.ierrors);
......@@ -270,9 +285,102 @@ String FromDPDKDevice::statistics_handler(Element *e, void * thunk)
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,input.c_str()) == 0) {
input = String(xstats[i].value);
return 0;
}
}
return -1;
}
#else
input = "unsupported with DPDK < 16.07";
return -1;
#endif
return 0;
}
void FromDPDKDevice::add_handlers()
{
add_read_handler("device",read_handler, h_device);
add_read_handler("duplex",status_handler, h_duplex);
#if RTE_VERSION >= RTE_VERSION_NUM(16,04,0,0)
......@@ -280,16 +388,24 @@ void FromDPDKDevice::add_handlers()
#endif
add_read_handler("speed",status_handler, h_speed);
add_read_handler("carrier",status_handler, h_carrier);
add_read_handler("type",status_handler, h_type);
set_handler("xstats", Handler::f_read | Handler::f_read_param, xstats_handler);
add_read_handler("active", read_handler, h_active);
add_read_handler("count", count_handler, 0);
add_write_handler("active", write_handler, h_active);
add_read_handler("count", count_handler, h_count);
add_write_handler("reset_counts", reset_count_handler, 0, Handler::BUTTON);
add_read_handler("mac",read_handler, h_mac);
add_read_handler("vendor", read_handler, h_vendor);
add_read_handler("driver", read_handler, h_driver);
add_write_handler("add_mac",write_handler, h_add_mac, 0);
add_write_handler("remove_mac",write_handler, h_remove_mac, 0);
add_read_handler("hw_count",statistics_handler, h_ipackets);
add_read_handler("hw_bytes",statistics_handler, h_ibytes);
add_read_handler("hw_dropped",statistics_handler, h_idropped);
add_read_handler("hw_dropped",statistics_handler, h_imissed);
add_read_handler("hw_errors",statistics_handler, h_ierrors);
add_write_handler("reset_counts", reset_count_handler, 0, Handler::BUTTON);
......
......@@ -50,7 +50,8 @@ Integer. A specific hardware queue to use. Default is 0.
=item N_QUEUES
Integer. Number of hardware queues to use. -1 or default is to use as many queues as threads assigned to this element.
Integer. Number of hardware queues to use. -1 or default is to use as many queues
as threads assigned to this element.
=item PROMISC
......@@ -107,6 +108,8 @@ Resets "count" to zero.
=a DPDKInfo, ToDPDKDevice */
class ToDPDKDevice;
class FromDPDKDevice : public RXQueueDevice {
public:
......@@ -129,14 +132,21 @@ public:
private:
static String read_handler(Element*, void*) CLICK_COLD;
static int write_handler(const String&, Element*, void*, ErrorHandler*)
CLICK_COLD;
static String status_handler(Element *e, void * thunk) CLICK_COLD;
static String statistics_handler(Element *e, void * thunk) CLICK_COLD;
static String read_handler(Element *, void *) CLICK_COLD;
static int write_handler(
const String &, Element *, void *, ErrorHandler *
) CLICK_COLD;
static String status_handler(Element *e, void *thunk) CLICK_COLD;
static String statistics_handler(Element *e, void *thunk) CLICK_COLD;
static int xstats_handler(int operation, String &input, Element *e,
const Handler *handler, ErrorHandler *errh);
enum {
h_vendor, h_driver, h_carrier, h_duplex, h_autoneg, h_speed,
h_ipackets, h_ibytes, h_ierrors, h_idropped, h_active, h_mac
h_vendor, h_driver, h_carrier, h_duplex, h_autoneg, h_speed, h_type,
h_ipackets, h_ibytes, h_imissed, h_ierrors,
h_active,
h_nb_rx_queues, h_nb_tx_queues, h_nb_vf_pools,
h_mac, h_add_mac, h_remove_mac, h_vf_mac,
h_device,
};
DPDKDevice* _dev;
......
......@@ -28,8 +28,8 @@ Vector<int> QueueDevice::inputs_count = Vector<int>();
Vector<int> QueueDevice::shared_offset = Vector<int>();
QueueDevice::QueueDevice() : _minqueues(0),_maxqueues(128), usable_threads(),
queue_per_threads(1), queue_share(1), ndesc(0), allow_nonexistent(false), _maxthreads(-1),firstqueue(-1),n_queues(-1),thread_share(1),
_this_node(0){
queue_per_threads(1), queue_share(1), ndesc(0), allow_nonexistent(false), _maxthreads(-1),firstqueue(-1),lastqueue(-1),n_queues(-1),thread_share(1),
_this_node(0), _active(true) {
_verbose = 1;
}
void QueueDevice::static_initialize() {
......@@ -72,8 +72,10 @@ Args& RXQueueDevice::parse(Args &args) {
#endif
_threadoffset = -1;
_set_rss_aggregate = false;
_set_paint_anno = false;
args.read("RSS_AGGREGATE", _set_rss_aggregate)
.read("PAINT_QUEUE", _set_paint_anno)
.read("NUMA", _use_numa)
.read("THREADOFFSET", _threadoffset);
......@@ -143,7 +145,7 @@ int TXQueueDevice::initialize_tx(ErrorHandler * errh) {
}
if (n_threads == 0) {
return errh->error("No threads end up in this queuedevice...? Aborting.");
errh->warning("No threads end up in this queuedevice...?");
}
if (n_threads > _maxqueues) {
......@@ -155,12 +157,13 @@ int TXQueueDevice::initialize_tx(ErrorHandler * errh) {
else
n_queues = max(_minqueues,n_threads);
if (n_threads > 0) {
queue_per_threads = n_queues / n_threads;
if (queue_per_threads == 0) {
queue_per_threads = 1;
thread_share = n_threads / n_queues;
}
}
n_initialized++;
if (_verbose > 1) {
if (input_is_push(0))
......@@ -181,10 +184,14 @@ int RXQueueDevice::initialize_rx(ErrorHandler *errh) {
usable_threads[router()->thread_sched()
->initial_home_thread_id(this)] = 1;
n_threads = 1;
if (n_queues == -1) {
if (n_threads >= _maxqueues)
n_queues = _maxqueues;
else
n_queues = max(_minqueues,n_threads);
}
queue_per_threads = n_queues / n_threads;
lastqueue = firstqueue + n_queues - 1;
click_chatter(
"%s : remove StaticThreadSched to use FastClick's "
......@@ -220,6 +227,9 @@ int RXQueueDevice::initialize_rx(ErrorHandler *errh) {
click_chatter("Warning : input thread assignment will assign threads already assigned by yourself, as you didn't left any cores for %s",name().c_str());
} else
usable_threads &= (~v);
if (_threadoffset != -1 && !usable_threads[_threadoffset]) {
click_chatter("WARNING : The THREADOFFSET parameter will be ignored because that thread is not usable / assigned to another element.");
}
}
cores_in_node = usable_threads.weight();
......@@ -269,6 +279,7 @@ int RXQueueDevice::initialize_rx(ErrorHandler *errh) {
queue_per_threads = n_queues / n_threads;
if (queue_per_threads * n_threads < n_queues) queue_per_threads ++;
lastqueue = firstqueue + n_queues - 1;
for (int b = 0; b < usable_threads.size(); b++) {
if (count >= n_threads) {
......@@ -356,5 +367,51 @@ int QueueDevice::initialize_tasks(bool schedule, ErrorHandler *errh) {
}
unsigned long long QueueDevice::n_count() {
unsigned long long total = 0;
for (unsigned int i = 0; i < thread_state.weight(); i ++) {
total += thread_state.get_value(i)._count;
}
return total;
}
unsigned long long QueueDevice::n_dropped() {
unsigned long long total = 0;
for (unsigned int i = 0; i < thread_state.weight(); i ++) {
total += thread_state.get_value(i)._dropped;
}
return total;
}
void QueueDevice::reset_count() {
for (unsigned int i = 0; i < thread_state.weight(); i ++) {
thread_state.get_value(i)._count = 0;
thread_state.get_value(i)._dropped = 0;
}
}
String QueueDevice::count_handler(Element *e, void *user_data)
{
QueueDevice *tdd = static_cast<QueueDevice *>(e);
intptr_t what = reinterpret_cast<intptr_t>(user_data);
switch (what) {
case h_count:
return String(tdd->n_count());
}
}
String QueueDevice::dropped_handler(Element *e, void *)
{
QueueDevice *tdd = static_cast<QueueDevice *>(e);