// -*- c-basic-offset: 4; related-file-name: "../../lib/element.cc" -*- #ifndef CLICK_ELEMENT_HH #define CLICK_ELEMENT_HH #include #include #include #include #include CLICK_DECLS class Router; class Master; class RouterThread; class Task; class Timer; class NotifierSignal; class Element; class ErrorHandler; class Bitvector; class EtherAddress; /** @file * @brief Click's Element class. */ #ifndef CLICK_ELEMENT_DEPRECATED # define CLICK_ELEMENT_DEPRECATED CLICK_DEPRECATED #endif class Element { public: Element(); virtual ~Element(); static int nelements_allocated; // RUNTIME virtual void push(int port, Packet *p); virtual Packet *pull(int port) CLICK_WARN_UNUSED_RESULT; virtual Packet *simple_action(Packet *p); virtual bool run_task(Task *task); // return true iff did useful work virtual void run_timer(Timer *timer); #if CLICK_USERLEVEL enum { SELECT_READ = 1, SELECT_WRITE = 2 }; virtual void selected(int fd, int mask); virtual void selected(int fd); #endif inline void checked_output_push(int port, Packet *p) const; inline Packet* checked_input_pull(int port) const; // ELEMENT CHARACTERISTICS virtual const char *class_name() const = 0; virtual const char *port_count() const; static const char PORTS_0_0[]; static const char PORTS_0_1[]; static const char PORTS_1_0[]; static const char PORTS_1_1[]; static const char PORTS_1_1X2[]; virtual const char *processing() const; static const char AGNOSTIC[]; static const char PUSH[]; static const char PULL[]; static const char PUSH_TO_PULL[]; static const char PULL_TO_PUSH[]; static const char PROCESSING_A_AH[]; virtual const char *flow_code() const; static const char COMPLETE_FLOW[]; virtual const char *flags() const; int flag_value(int flag) const; virtual void *cast(const char *name); virtual void *port_cast(bool isoutput, int port, const char *name); // CONFIGURATION, INITIALIZATION, AND CLEANUP enum ConfigurePhase { CONFIGURE_PHASE_FIRST = 0, CONFIGURE_PHASE_INFO = 20, CONFIGURE_PHASE_PRIVILEGED = 90, CONFIGURE_PHASE_DEFAULT = 100, CONFIGURE_PHASE_LAST = 2000 }; virtual int configure_phase() const; virtual int configure(Vector &conf, ErrorHandler *errh); virtual void add_handlers(); virtual int initialize(ErrorHandler *errh); virtual void take_state(Element *old_element, ErrorHandler *errh); virtual Element *hotswap_element() const; enum CleanupStage { CLEANUP_NO_ROUTER, CLEANUP_BEFORE_CONFIGURE = CLEANUP_NO_ROUTER, CLEANUP_CONFIGURE_FAILED, CLEANUP_CONFIGURED, CLEANUP_INITIALIZE_FAILED, CLEANUP_INITIALIZED, CLEANUP_ROUTER_INITIALIZED, CLEANUP_MANUAL }; virtual void cleanup(CleanupStage stage); static inline void static_initialize(); static inline void static_cleanup(); // ELEMENT ROUTER CONNECTIONS String name() const; virtual String declaration() const; inline Router *router() const; inline int eindex() const; inline int eindex(Router *r) const; /** @brief Return the element's master. */ inline Master *master() const; inline void attach_router(Router *r, int eindex) { assert(!_router); _router = r; _eindex = eindex; } // INPUTS AND OUTPUTS inline int nports(bool isoutput) const; inline int ninputs() const; inline int noutputs() const; class Port; inline const Port &port(bool isoutput, int port) const; inline const Port &input(int port) const; inline const Port &output(int port) const; inline bool port_active(bool isoutput, int port) const; inline bool input_is_push(int port) const; inline bool input_is_pull(int port) const; inline bool output_is_push(int port) const; inline bool output_is_pull(int port) const; void port_flow(bool isoutput, int port, Bitvector*) const; // LIVE RECONFIGURATION String configuration() const; virtual bool can_live_reconfigure() const; virtual int live_reconfigure(Vector&, ErrorHandler*); RouterThread *home_thread() const; #if CLICK_USERLEVEL // SELECT int add_select(int fd, int mask); int remove_select(int fd, int mask); #endif // HANDLERS void add_read_handler(const String &name, ReadHandlerCallback read_callback, const void *user_data = 0, uint32_t flags = 0); void add_read_handler(const String &name, ReadHandlerCallback read_callback, int user_data, uint32_t flags = 0); void add_read_handler(const char *name, ReadHandlerCallback read_callback, int user_data = 0, uint32_t flags = 0); void add_write_handler(const String &name, WriteHandlerCallback write_callback, const void *user_data = 0, uint32_t flags = 0); void add_write_handler(const String &name, WriteHandlerCallback write_callback, int user_data, uint32_t flags = 0); void add_write_handler(const char *name, WriteHandlerCallback write_callback, int user_data = 0, uint32_t flags = 0); void set_handler(const String &name, int flags, HandlerCallback callback, const void *read_user_data = 0, const void *write_user_data = 0); void set_handler(const String &name, int flags, HandlerCallback callback, int read_user_data, int write_user_data = 0); void set_handler(const char *name, int flags, HandlerCallback callback, int read_user_data = 0, int write_user_data = 0); int set_handler_flags(const String &name, int set_flags, int clear_flags = 0); enum { TASKHANDLER_WRITE_SCHEDULED = 1, TASKHANDLER_WRITE_TICKETS = 2, TASKHANDLER_WRITE_HOME_THREAD = 4, TASKHANDLER_WRITE_ALL = 7, TASKHANDLER_DEFAULT = 6 }; void add_task_handlers(Task *task, NotifierSignal *signal, int flags, const String &prefix = String()); inline void add_task_handlers(Task *task, NotifierSignal *signal, const String &prefix = String()) { add_task_handlers(task, signal, TASKHANDLER_DEFAULT, prefix); } inline void add_task_handlers(Task *task, const String &prefix = String()) { add_task_handlers(task, 0, TASKHANDLER_DEFAULT, prefix); } void add_data_handlers(const char *name, int flags, uint8_t *data); void add_data_handlers(const char *name, int flags, bool *data); void add_data_handlers(const char *name, int flags, uint16_t *data); void add_data_handlers(const char *name, int flags, int *data); void add_data_handlers(const char *name, int flags, unsigned *data); void add_data_handlers(const char *name, int flags, atomic_uint32_t *data); void add_data_handlers(const char *name, int flags, long *data); void add_data_handlers(const char *name, int flags, unsigned long *data); #if HAVE_LONG_LONG void add_data_handlers(const char *name, int flags, long long *data); void add_data_handlers(const char *name, int flags, unsigned long long *data); #endif void add_net_order_data_handlers(const char *name, int flags, uint16_t *data); void add_net_order_data_handlers(const char *name, int flags, uint32_t *data); #if HAVE_FLOAT_TYPES void add_data_handlers(const char *name, int flags, double *data); #endif void add_data_handlers(const char *name, int flags, String *data); void add_data_handlers(const char *name, int flags, IPAddress *data); void add_data_handlers(const char *name, int flags, EtherAddress *data); void add_data_handlers(const char *name, int flags, Timestamp *data, bool is_interval = false); static String read_positional_handler(Element*, void*); static String read_keyword_handler(Element*, void*); static int reconfigure_positional_handler(const String&, Element*, void*, ErrorHandler*); static int reconfigure_keyword_handler(const String&, Element*, void*, ErrorHandler*); virtual int llrpc(unsigned command, void* arg); int local_llrpc(unsigned command, void* arg); class Port { public: inline bool active() const; inline Element* element() const; inline int port() const; inline void push(Packet* p) const; inline Packet* pull() const; #if CLICK_STATS >= 1 unsigned npackets() const { return _packets; } #endif inline void assign(bool isoutput, Element *e, int port); private: Element* _e; int _port; #if HAVE_BOUND_PORT_TRANSFER union { void (*push)(Element *e, int port, Packet *p); Packet *(*pull)(Element *e, int port); } _bound; #endif #if CLICK_STATS >= 1 mutable unsigned _packets; // How many packets have we moved? #endif #if CLICK_STATS >= 2 Element* _owner; // Whose input or output are we? #endif inline Port(); inline void assign(bool isoutput, Element *owner, Element *e, int port); friend class Element; }; // DEPRECATED /** @cond never */ String id() const CLICK_DEPRECATED; String landmark() const CLICK_DEPRECATED; /** @endcond never */ private: enum { INLINE_PORTS = 4 }; Port* _ports[2]; Port _inline_ports[INLINE_PORTS]; int _nports[2]; Router* _router; int _eindex; #if CLICK_STATS >= 2 // STATISTICS unsigned _xfer_calls; // Push and pull calls into this element. click_cycles_t _xfer_own_cycles; // Cycles spent in self from push and pull. click_cycles_t _child_cycles; // Cycles spent in children. unsigned _task_calls; // Calls to tasks owned by this element. click_cycles_t _task_own_cycles; // Cycles spent in self from tasks. unsigned _timer_calls; // Calls to timers owned by this element. click_cycles_t _timer_own_cycles; // Cycles spent in self from timers. inline void reset_cycles() { _xfer_calls = _task_calls = _timer_calls = 0; _xfer_own_cycles = _task_own_cycles = _timer_own_cycles = _child_cycles = 0; } static String read_cycles_handler(Element *, void *); static int write_cycles_handler(const String &, Element *, void *, ErrorHandler *); #endif Element(const Element &); Element &operator=(const Element &); // METHODS USED BY ROUTER int set_nports(int, int); int notify_nports(int, int, ErrorHandler *); enum Processing { VAGNOSTIC, VPUSH, VPULL }; static int next_processing_code(const char*& p, ErrorHandler* errh); void processing_vector(int* input_codes, int* output_codes, ErrorHandler*) const; void initialize_ports(const int* input_codes, const int* output_codes); int connect_port(bool isoutput, int port, Element*, int); static String read_handlers_handler(Element *e, void *user_data); void add_default_handlers(bool writable_config); inline void add_data_handlers(const char *name, int flags, HandlerCallback callback, void *data); friend class Router; #if CLICK_STATS >= 2 friend class Task; friend class Master; friend class TimerSet; # if CLICK_USERLEVEL friend class SelectSet; # endif #endif }; /** @brief Initialize static data for this element class. * * Place initialization code for an element class's shared global state in the * static_initialize() static member function. (For example, the IPFilter * element class uses static_initialize() to set up various parsing tables.) * Click drivers will call this function when the element code is loaded, * before any elements of the class are created. * * static_initialize functions are called in an arbitrary and unpredictable * order (not, for example, the configure_phase() order). Element authors are * responsible for handling static initialization dependencies. * * For Click to find a static_initialize declaration, it must appear inside * the element class's class declaration on its own line and have the * following prototype: * * @code * static void static_initialize(); * @endcode * * It must also have public accessibility. * * @note In most cases you should also define a static_cleanup() function to * clean up state initialized by static_initialize(). * * @sa Element::static_cleanup */ inline void Element::static_initialize() { } /** @brief Clean up static data for this element class. * * Place cleanup code for an element class's shared global state in the * static_cleanup() static member function. Click drivers will call this * function before unloading the element code. * * static_cleanup functions are called in an arbitrary and unpredictable order * (not, for example, the configure_phase() order, and not the reverse of the * static_initialize order). Element authors are responsible for handling * static cleanup dependencies. * * For Click to find a static_cleanup declaration, it must appear inside the * element class's class declaration on its own line and have the following * prototype: * * @code * static void static_cleanup(); * @endcode * * It must also have public accessibility. * * @sa Element::static_initialize */ inline void Element::static_cleanup() { } /** @brief Return the element's router. */ inline Router* Element::router() const { return _router; } /** @brief Return the element's index within its router. * @invariant this == router()->element(eindex()) */ inline int Element::eindex() const { return _eindex; } /** @brief Return the element's index within router @a r. * * Returns -1 if @a r != router(). */ inline int Element::eindex(Router* r) const { return (router() == r ? _eindex : -1); } /** @brief Return the number of input or output ports. * @param isoutput false for input ports, true for output ports */ inline int Element::nports(bool isoutput) const { return _nports[isoutput]; } /** @brief Return the number of input ports. */ inline int Element::ninputs() const { return _nports[0]; } /** @brief Return the number of output ports. */ inline int Element::noutputs() const { return _nports[1]; } /** @brief Return one of the element's ports. * @param isoutput false for input ports, true for output ports * @param port port number * * An assertion fails if @a p is out of range. */ inline const Element::Port& Element::port(bool isoutput, int port) const { assert((unsigned) port < (unsigned) _nports[isoutput]); return _ports[isoutput][port]; } /** @brief Return one of the element's input ports. * @param port port number * * An assertion fails if @a port is out of range. * * @sa Port, port */ inline const Element::Port& Element::input(int port) const { return Element::port(false, port); } /** @brief Return one of the element's output ports. * @param port port number * * An assertion fails if @a port is out of range. * * @sa Port, port */ inline const Element::Port& Element::output(int port) const { return Element::port(true, port); } /** @brief Check whether a port is active. * @param isoutput false for input ports, true for output ports * @param port port number * * Returns true iff @a port is in range and @a port is active. Push outputs * and pull inputs are active; pull outputs and push inputs are not. * * @sa Element::Port::active */ inline bool Element::port_active(bool isoutput, int port) const { return (unsigned) port < (unsigned) nports(isoutput) && _ports[isoutput][port].active(); } /** @brief Check whether output @a port is push. * * Returns true iff output @a port exists and is push. @sa port_active */ inline bool Element::output_is_push(int port) const { return port_active(true, port); } /** @brief Check whether output @a port is pull. * * Returns true iff output @a port exists and is pull. */ inline bool Element::output_is_pull(int port) const { return (unsigned) port < (unsigned) nports(true) && !_ports[1][port].active(); } /** @brief Check whether input @a port is pull. * * Returns true iff input @a port exists and is pull. @sa port_active */ inline bool Element::input_is_pull(int port) const { return port_active(false, port); } /** @brief Check whether input @a port is push. * * Returns true iff input @a port exists and is push. */ inline bool Element::input_is_push(int port) const { return (unsigned) port < (unsigned) nports(false) && !_ports[0][port].active(); } #if CLICK_STATS >= 2 # define PORT_ASSIGN(o) _packets = 0; _owner = (o) #elif CLICK_STATS >= 1 # define PORT_ASSIGN(o) _packets = 0; (void) (o) #else # define PORT_ASSIGN(o) (void) (o) #endif inline Element::Port::Port() : _e(0), _port(-2) { PORT_ASSIGN(0); } inline void Element::Port::assign(bool isoutput, Element *e, int port) { _e = e; _port = port; (void) isoutput; #if HAVE_BOUND_PORT_TRANSFER if (e) { if (isoutput) { void (Element::*pusher)(int, Packet *) = &Element::push; _bound.push = (void (*)(Element *, int, Packet *)) (e->*pusher); } else { Packet *(Element::*puller)(int) = &Element::pull; _bound.pull = (Packet *(*)(Element *, int)) (e->*puller); } } #endif } inline void Element::Port::assign(bool isoutput, Element *owner, Element *e, int port) { PORT_ASSIGN(owner); assign(isoutput, e, port); } /** @brief Returns whether this port is active (a push output or a pull input). * * @sa Element::port_active */ inline bool Element::Port::active() const { return _port >= 0; } /** @brief Returns the element connected to this active port. * * Returns 0 if this port is not active(). */ inline Element* Element::Port::element() const { return _e; } /** @brief Returns the port number of the port connected to this active port. * * Returns < 0 if this port is not active(). */ inline int Element::Port::port() const { return _port; } /** @brief Push packet @a p over this port. * * Pushes packet @a p downstream through the router configuration by passing * it to the next element's @link Element::push() push() @endlink function. * Returns when the rest of the router finishes processing @a p. * * This port must be an active() push output port. Usually called from * element code like @link Element::output output(i) @endlink .push(p). * * When element code calls Element::Port::push(@a p), it relinquishes control * of packet @a p. When push() returns, @a p may have been altered or even * freed by downstream elements. Thus, you must not use @a p after pushing it * downstream. To push a copy and keep a copy, see Packet::clone(). * * output(i).push(p) basically behaves like the following code, although it * maintains additional statistics depending on how CLICK_STATS is defined: * * @code * output(i).element()->push(output(i).port(), p); * @endcode */ inline void Element::Port::push(Packet* p) const { assert(_e && p); #if CLICK_STATS >= 1 ++_packets; #endif #if CLICK_STATS >= 2 ++_e->input(_port)._packets; click_cycles_t start_cycles = click_get_cycles(), start_child_cycles = _e->_child_cycles; # if HAVE_BOUND_PORT_TRANSFER _bound.push(_e, _port, p); # else _e->push(_port, p); # endif click_cycles_t all_delta = click_get_cycles() - start_cycles, own_delta = all_delta - (_e->_child_cycles - start_child_cycles); _e->_xfer_calls += 1; _e->_xfer_own_cycles += own_delta; _owner->_child_cycles += all_delta; #else # if HAVE_BOUND_PORT_TRANSFER _bound.push(_e, _port, p); # else _e->push(_port, p); # endif #endif } /** @brief Pull a packet over this port and return it. * * Pulls a packet from upstream in the router configuration by calling the * previous element's @link Element::pull() pull() @endlink function. When * the router finishes processing, returns the result. * * This port must be an active() pull input port. Usually called from element * code like @link Element::input input(i) @endlink .pull(). * * input(i).pull() basically behaves like the following code, although it * maintains additional statistics depending on how CLICK_STATS is defined: * * @code * input(i).element()->pull(input(i).port()) * @endcode */ inline Packet* Element::Port::pull() const { assert(_e); #if CLICK_STATS >= 2 click_cycles_t start_cycles = click_get_cycles(), old_child_cycles = _e->_child_cycles; # if HAVE_BOUND_PORT_TRANSFER Packet *p = _bound.pull(_e, _port); # else Packet *p = _e->pull(_port); # endif if (p) _e->output(_port)._packets += 1; click_cycles_t all_delta = click_get_cycles() - start_cycles, own_delta = all_delta - (_e->_child_cycles - old_child_cycles); _e->_xfer_calls += 1; _e->_xfer_own_cycles += own_delta; _owner->_child_cycles += all_delta; #else # if HAVE_BOUND_PORT_TRANSFER Packet *p = _bound.pull(_e, _port); # else Packet *p = _e->pull(_port); # endif #endif #if CLICK_STATS >= 1 if (p) ++_packets; #endif return p; } /** @brief Push packet @a p to output @a port, or kill it if @a port is out of * range. * * @param port output port number * @param p packet to push * * If @a port is in range (>= 0 and < noutputs()), then push packet @a p * forward using output(@a port).push(@a p). Otherwise, kill @a p with @a p * ->kill(). * * @note It is invalid to call checked_output_push() on a pull output @a port. */ inline void Element::checked_output_push(int port, Packet* p) const { if ((unsigned) port < (unsigned) noutputs()) _ports[1][port].push(p); else p->kill(); } /** @brief Pull a packet from input @a port, or return 0 if @a port is out of * range. * * @param port input port number * * If @a port is in range (>= 0 and < ninputs()), then return the result * of input(@a port).pull(). Otherwise, return null. * * @note It is invalid to call checked_input_pull() on a push input @a port. */ inline Packet* Element::checked_input_pull(int port) const { if ((unsigned) port < (unsigned) ninputs()) return _ports[0][port].pull(); else return 0; } #undef PORT_ASSIGN CLICK_ENDDECLS #endif