// -*- c-basic-offset: 4; related-file-name: "../include/click/router.hh" -*- /* * router.{cc,hh} -- a Click router configuration * Eddie Kohler * * Copyright (c) 1999-2000 Massachusetts Institute of Technology * Copyright (c) 2000 Mazu Networks, Inc. * Copyright (c) 2004-2005 Regents of the University of California * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, subject to the conditions * listed in the Click LICENSE file. These conditions include: you must * preserve this copyright notice, and you cannot mention the copyright * holders in advertising related to the Software without their permission. * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This * notice is a summary of the Click LICENSE file; the license in that file is * legally binding. */ #define WANT_MOD_USE_COUNT 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CLICK_USERLEVEL # include #endif #ifdef CLICK_NS #include "../elements/ns/fromsimdevice.hh" #endif CLICK_DECLS /** @class Router * @brief A router configuration. */ const Handler* Handler::the_blank_handler; static Handler* globalh; static int nglobalh; static int globalh_cap; Router::Router(const String &configuration, Master *m) : _master(0), _state(ROUTER_NEW), _have_connections(false), _running(RUNNING_INACTIVE), _handler_bufs(0), _nhandlers_bufs(0), _free_handler(-1), _root_element(0), _configuration(configuration), _notifier_signals(0), _n_notifier_signals(0), _arena_factory(new HashMap_ArenaFactory), _hotswap_router(0), _thread_sched(0), _name_info(0), _next_router(0) { _refcount = 0; _runcount = 0; _root_element = new ErrorElement; _root_element->attach_router(this, -1); m->register_router(this); } Router::~Router() { if (_refcount != 0) click_chatter("deleting router while ref count = %d", _refcount.value()); // unuse the hotswap router if (_hotswap_router) _hotswap_router->unuse(); // Delete the ArenaFactory, which detaches the Arenas delete _arena_factory; // Clean up elements in reverse configuration order if (_state == ROUTER_LIVE) { // Unschedule tasks and timers _master->kill_router(this); for (int ord = _elements.size() - 1; ord >= 0; ord--) _elements[ _element_configure_order[ord] ]->cleanup(Element::CLEANUP_ROUTER_INITIALIZED); } else if (_state != ROUTER_DEAD) { assert(_element_configure_order.size() == 0 && _state <= ROUTER_PRECONFIGURE); for (int i = _elements.size() - 1; i >= 0; i--) _elements[i]->cleanup(Element::CLEANUP_NO_ROUTER); } // Delete elements in reverse configuration order if (_element_configure_order.size()) for (int ord = _elements.size() - 1; ord >= 0; ord--) delete _elements[ _element_configure_order[ord] ]; else for (int i = 0; i < _elements.size(); i++) delete _elements[i]; delete _root_element; #if CLICK_LINUXMODULE // decrement module use counts for (struct module **m = _modules.begin(); m < _modules.end(); m++) { # if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) module_put(*m); # else __MOD_DEC_USE_COUNT(*m); # endif } #endif for (int i = 0; i < _nhandlers_bufs; i += HANDLER_BUFSIZ) delete[] _handler_bufs[i / HANDLER_BUFSIZ]; delete[] _handler_bufs; delete[] _notifier_signals; delete _name_info; if (_master) _master->unregister_router(this); } void Router::unuse() { if (_refcount.dec_and_test()) delete this; } // ACCESS Element * Router::find(const String &name, String prefix, ErrorHandler *errh) const { while (1) { int got = -1; String n = prefix + name; for (int i = 0; i < _elements.size(); i++) if (_element_names[i] == n) { if (got >= 0) { if (errh) errh->error("more than one element named '%s'", n.c_str()); return 0; } else got = i; } if (got >= 0) return _elements[got]; if (!prefix) break; int slash = prefix.find_right('/', prefix.length() - 2); prefix = (slash >= 0 ? prefix.substring(0, slash + 1) : String()); } if (errh) errh->error("no element named '%s'", name.c_str()); return 0; } Element * Router::find(const String &name, Element *context, ErrorHandler *errh) const { String prefix = ename(context->eindex()); int slash = prefix.find_right('/'); return find(name, (slash >= 0 ? prefix.substring(0, slash + 1) : String()), errh); } Element * Router::element(const Router *r, int ei) { if (r && ei >= 0 && ei < r->nelements()) return r->_elements[ei]; else if (r && ei == -1) return r->root_element(); else return 0; } const String & Router::ename(int ei) const { if (ei < 0 || ei >= nelements()) return String::empty_string(); else return _element_names[ei]; } const String & Router::default_configuration_string(int ei) const { if (ei < 0 || ei >= nelements()) return String::empty_string(); else return _element_configurations[ei]; } void Router::set_default_configuration_string(int ei, const String &s) { if (ei >= 0 && ei < nelements()) _element_configurations[ei] = s; } const String & Router::elandmark(int ei) const { if (ei < 0 || ei >= nelements()) return String::empty_string(); else return _element_landmarks[ei]; } // CREATION #if CLICK_LINUXMODULE int Router::add_module_ref(struct module *module) { if (!module) return 0; for (struct module **m = _modules.begin(); m < _modules.end(); m++) if (*m == module) return 0; # if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 9) if (try_module_get(module) == 0) return -1; # else __MOD_INC_USE_COUNT(module); # endif _modules.push_back(module); return 0; } #endif int Router::add_element(Element *e, const String &ename, const String &conf, const String &landmark) { if (_state != ROUTER_NEW || !e || e->router()) return -1; _elements.push_back(e); _element_names.push_back(ename); _element_landmarks.push_back(landmark); _element_configurations.push_back(conf); int i = _elements.size() - 1; e->attach_router(this, i); // router now owns the element return i; } int Router::add_connection(int from_idx, int from_port, int to_idx, int to_port) { assert(from_idx >= 0 && from_port >= 0 && to_idx >= 0 && to_port >= 0); if (_state != ROUTER_NEW) return -1; Hookup hfrom(from_idx, from_port); Hookup hto(to_idx, to_port); // only add new connections for (int i = 0; i < _hookup_from.size(); i++) if (_hookup_from[i] == hfrom && _hookup_to[i] == hto) return 0; _hookup_from.push_back(hfrom); _hookup_to.push_back(hto); return 0; } void Router::add_requirement(const String &r) { assert(cp_is_word(r)); _requirements.push_back(r); } // CHECKING HOOKUP void Router::remove_hookup(int c) { _hookup_from[c] = _hookup_from.back(); _hookup_from.pop_back(); _hookup_to[c] = _hookup_to.back(); _hookup_to.pop_back(); } void Router::hookup_error(const Hookup &h, bool is_from, const char *message, ErrorHandler *errh) { bool is_output = is_from; const char *kind = (is_output ? "output" : "input"); element_lerror(errh, _elements[h.idx], message, _elements[h.idx], kind, h.port); } int Router::check_hookup_elements(ErrorHandler *errh) { if (!errh) errh = ErrorHandler::default_handler(); int before = errh->nerrors(); // Check each hookup to ensure it connects valid elements for (int c = 0; c < _hookup_from.size(); c++) { Hookup &hfrom = _hookup_from[c]; Hookup &hto = _hookup_to[c]; int before = errh->nerrors(); if (hfrom.idx < 0 || hfrom.idx >= nelements() || !_elements[hfrom.idx]) errh->error("bad element number '%d'", hfrom.idx); if (hto.idx < 0 || hto.idx >= nelements() || !_elements[hto.idx]) errh->error("bad element number '%d'", hto.idx); if (hfrom.port < 0) errh->error("bad port number '%d'", hfrom.port); if (hto.port < 0) errh->error("bad port number '%d'", hto.port); // remove the connection if there were errors if (errh->nerrors() != before) { remove_hookup(c); c--; } } return (errh->nerrors() == before ? 0 : -1); } void Router::notify_hookup_range(ErrorHandler *errh) { // Count inputs and outputs, and notify elements how many they have Vector nin(nelements(), -1); Vector nout(nelements(), -1); for (int c = 0; c < _hookup_from.size(); c++) { if (_hookup_from[c].port > nout[_hookup_from[c].idx]) nout[_hookup_from[c].idx] = _hookup_from[c].port; if (_hookup_to[c].port > nin[_hookup_to[c].idx]) nin[_hookup_to[c].idx] = _hookup_to[c].port; } for (int f = 0; f < nelements(); f++) _elements[f]->notify_nports(nin[f] + 1, nout[f] + 1, errh); } int Router::check_hookup_range(ErrorHandler *errh, bool check_only) { int before_all = errh->nerrors(); // Check each hookup to ensure its port numbers are within range for (int c = 0; c < _hookup_from.size(); c++) { Hookup &hfrom = _hookup_from[c]; Hookup &hto = _hookup_to[c]; int before = errh->nerrors(); if (hfrom.port >= _elements[hfrom.idx]->noutputs()) hookup_error(hfrom, true, "'%{element}' has no %s %d", errh); if (hto.port >= _elements[hto.idx]->ninputs()) hookup_error(hto, false, "'%{element}' has no %s %d", errh); // remove the connection if there were errors if (errh->nerrors() == before) /* do nothing */; else if (!check_only) { remove_hookup(c); c--; } } return errh->nerrors() == before_all ? 0 : -1; } int Router::check_hookup_completeness(ErrorHandler *errh, bool check_only) { Bitvector used_outputs(ngports(true), false); Bitvector used_inputs(ngports(false), false); int before_all = errh->nerrors(); // Check each hookup to ensure it doesn't reuse a port. // Completely duplicate connections never got into the Router for (int c = 0; c < _hookup_from.size(); c++) { Hookup &hfrom = _hookup_from[c]; Hookup &hto = _hookup_to[c]; int before = errh->nerrors(); int fromg = gport(true, hfrom); int tog = gport(false, hto); if (used_outputs[fromg] && _elements[hfrom.idx]->output_is_push(hfrom.port)) hookup_error(hfrom, true, "can't reuse '%{element}' push %s %d", errh); else if (used_inputs[tog] && _elements[hto.idx]->input_is_pull(hto.port)) hookup_error(hto, false, "can't reuse '%{element}' pull %s %d", errh); // remove the connection if there were errors if (errh->nerrors() == before) { used_outputs[fromg] = true; used_inputs[tog] = true; } else if (!check_only) { remove_hookup(c); c--; } } // Check for unused inputs and outputs. for (int g = 0; g < ngports(false); g++) if (!used_inputs[g]) hookup_error(gport_hookup(false, g), false, "'%{element}' %s %d unused", errh); for (int g = 0; g < ngports(true); g++) if (!used_outputs[g]) hookup_error(gport_hookup(true, g), true, "'%{element}' %s %d unused", errh); return errh->nerrors() == before_all ? 0 : -1; } // PORT INDEXES void Router::make_gports() { _gports[0].e2g.assign(1, 0); _gports[1].e2g.assign(1, 0); _gports[0].g2e.clear(); _gports[1].g2e.clear(); for (int i = 0; i < _elements.size(); i++) { Element *e = _elements[i]; _gports[0].e2g.push_back(_gports[0].e2g.back() + e->ninputs()); _gports[1].e2g.push_back(_gports[1].e2g.back() + e->noutputs()); for (int j = 0; j < e->ninputs(); j++) _gports[0].g2e.push_back(i); for (int j = 0; j < e->noutputs(); j++) _gports[1].g2e.push_back(i); } } inline int Router::gport(bool isoutput, const Hookup &h) const { return _gports[isoutput].e2g[h.idx] + h.port; } inline Router::Hookup Router::gport_hookup(bool isoutput, int g) const { int e = _gports[isoutput].g2e[g]; return Hookup(e, g - _gports[isoutput].e2g[e]); } void Router::gport_list_elements(bool isoutput, const Bitvector& bv, Vector& results) const { const Gport& gport = _gports[isoutput]; int ng = gport.size(); assert(bv.size() == ng); const int* gp = gport.e2g.begin(); for (int g = 0; g < ng; ) { while (g >= gp[1]) gp++; if (bv[g]) { results.push_back(_elements[gp - gport.e2g.begin()]); g = gp[1]; } else g++; } } void Router::make_hookup_gports() { if (_hookup_gports[0].size() != _hookup_to.size()) { for (int c = 0; c < _hookup_to.size(); c++) { _hookup_gports[0].push_back(gport(false, _hookup_to[c])); _hookup_gports[1].push_back(gport(true, _hookup_from[c])); } assert(_hookup_gports[0].size() == _hookup_to.size()); } } // PROCESSING int Router::processing_error(const Hookup &hfrom, const Hookup &hto, bool aggie, int processing_from, ErrorHandler *errh) { const char *type1 = (processing_from == Element::VPUSH ? "push" : "pull"); const char *type2 = (processing_from == Element::VPUSH ? "pull" : "push"); if (!aggie) errh->error("'%{element}' %s output %d connected to '%{element}' %s input %d", _elements[hfrom.idx], type1, hfrom.port, _elements[hto.idx], type2, hto.port); else errh->error("agnostic '%{element}' in mixed context: %s input %d, %s output %d", _elements[hfrom.idx], type2, hto.port, type1, hfrom.port); return -1; } int Router::check_push_and_pull(ErrorHandler *errh) { if (!errh) errh = ErrorHandler::default_handler(); // set up processing vectors Vector input_pers(ngports(false), 0); Vector output_pers(ngports(true), 0); for (int e = 0; e < nelements(); e++) _elements[e]->processing_vector(input_pers.begin() + _gports[0].e2g[e], output_pers.begin() + _gports[1].e2g[e], errh); // add fake connections for agnostics Vector hookup_from = _hookup_from; Vector hookup_to = _hookup_to; Bitvector bv; for (int* inputp = input_pers.begin(); inputp < input_pers.end(); inputp++) if (*inputp == Element::VAGNOSTIC) { Hookup h = gport_hookup(false, inputp - input_pers.begin()); _elements[h.idx]->port_flow(false, h.port, &bv); int og = _gports[1].e2g[h.idx]; for (int j = 0; j < bv.size(); j++) if (bv[j] && output_pers[og+j] == Element::VAGNOSTIC) { hookup_from.push_back(Hookup(h.idx, j)); hookup_to.push_back(Hookup(h.idx, h.port)); } } int before = errh->nerrors(); int first_agnostic = _hookup_from.size(); // spread personalities while (true) { bool changed = false; for (int c = 0; c < hookup_from.size(); c++) { if (hookup_from[c].idx < 0) continue; int gf = gport(true, hookup_from[c]); int gt = gport(false, hookup_to[c]); int pf = output_pers[gf]; int pt = input_pers[gt]; switch (pt) { case Element::VAGNOSTIC: if (pf != Element::VAGNOSTIC) { input_pers[gt] = pf; changed = true; } break; case Element::VPUSH: case Element::VPULL: if (pf == Element::VAGNOSTIC) { output_pers[gf] = pt; changed = true; } else if (pf != pt) { processing_error(hookup_from[c], hookup_to[c], c >= first_agnostic, pf, errh); hookup_from[c].idx = -1; } break; } } if (!changed) break; } if (errh->nerrors() != before) return -1; for (int e = 0; e < nelements(); e++) _elements[e]->initialize_ports(input_pers.begin() + _gports[0].e2g[e], output_pers.begin() + _gports[1].e2g[e]); return 0; } // SET CONNECTIONS int Router::element_lerror(ErrorHandler *errh, Element *e, const char *format, ...) const { va_list val; va_start(val, format); errh->verror(ErrorHandler::ERR_ERROR, e->landmark(), format, val); va_end(val); return -1; } void Router::set_connections() { // actually assign ports for (int c = 0; c < _hookup_from.size(); c++) { Hookup& hfrom = _hookup_from[c]; Element* frome = _elements[hfrom.idx]; Hookup& hto = _hookup_to[c]; Element* toe = _elements[hto.idx]; frome->connect_port(true, hfrom.port, toe, hto.port); toe->connect_port(false, hto.port, frome, hfrom.port); } _have_connections = true; } // RUNCOUNT void Router::set_runcount(int32_t x) { _runcount = x; if (x <= 0) { _master->set_stopper(1); // ensure that at least one thread is awake to handle the stop event _master->_threads[2]->wake(); } } void Router::adjust_runcount(int32_t delta) { // beware of overflow int32_t old_value, new_value; do { old_value = _runcount; if (delta > 0 && old_value > 0x7FFFFFFF - delta) new_value = 0x7FFFFFFF; else if (delta < 0 && old_value < STOP_RUNCOUNT - delta) new_value = STOP_RUNCOUNT; else new_value = old_value + delta; } while (!_runcount.compare_and_swap(old_value, new_value)); if (new_value <= 0) { _master->set_stopper(1); // ensure that at least one thread is awake to handle the stop event _master->_threads[2]->wake(); } } // FLOWS int Router::global_port_flow(bool forward, Element* first_element, int first_port, ElementFilter* stop_filter, Bitvector& results) { if (!_have_connections || first_element->router() != this) return -1; make_hookup_gports(); int nresult = ngports(!forward); int nsource = ngports(forward); Bitvector old_results(nresult, false); results.assign(nresult, false); Bitvector diff, scratch; Bitvector source(nsource, false); int first_gport = _gports[forward].e2g[first_element->eindex()]; if (first_port < 0) for (int i = 0; i < first_element->nports(forward); i++) source[first_gport + i] = true; else if (first_port < first_element->nports(forward)) source[first_gport + first_port] = true; while (true) { old_results = results; for (const int* gfrom = _hookup_gports[forward].begin(); gfrom < _hookup_gports[forward].end(); gfrom++) if (source[*gfrom]) { int i = gfrom - _hookup_gports[forward].begin(); results[_hookup_gports[!forward][i]] = true; } diff = results - old_results; if (diff.zero()) break; source.assign(nsource, false); for (int g = 0; g < nresult; g++) if (diff[g]) { Hookup h = gport_hookup(!forward, g); if (!stop_filter || !stop_filter->match_port(_elements[h.idx], !forward, h.port)) { _elements[h.idx]->port_flow(!forward, h.port, &scratch); source.or_at(scratch, _gports[forward].e2g[h.idx]); } } } return 0; } int Router::downstream_elements(Element* first_element, int first_output, ElementFilter* stop_filter, Vector& results) { Bitvector bv; if (global_port_flow(true, first_element, first_output, stop_filter, bv) < 0) return -1; gport_list_elements(false, bv, results); return 0; } int Router::downstream_elements(Element* first_element, int first_output, Vector& results) { return downstream_elements(first_element, first_output, 0, results); } int Router::downstream_elements(Element* first_element, Vector& results) { return downstream_elements(first_element, -1, 0, results); } int Router::upstream_elements(Element* first_element, int first_input, ElementFilter* stop_filter, Vector& results) { Bitvector bv; if (global_port_flow(false, first_element, first_input, stop_filter, bv) < 0) return -1; gport_list_elements(true, bv, results); return 0; } int Router::upstream_elements(Element* first_element, int first_input, Vector& results) { return upstream_elements(first_element, first_input, 0, results); } int Router::upstream_elements(Element* first_element, Vector& results) { return upstream_elements(first_element, -1, 0, results); } // INITIALIZATION String Router::context_message(int element_no, const char* message) const { Element* e = _elements[element_no]; StringAccum sa; if (e->landmark()) sa << e->landmark() << ": "; sa << message << " '" << e->declaration() << "':"; return sa.take_string(); } static int configure_order_compar(const void *athunk, const void *bthunk, void *copthunk) { const int* a = (const int*) athunk, *b = (const int*) bthunk; const int* configure_order_phase = (const int*) copthunk; return configure_order_phase[*a] - configure_order_phase[*b]; } inline Handler* Router::xhandler(int hi) const { return &_handler_bufs[hi / HANDLER_BUFSIZ][hi % HANDLER_BUFSIZ]; } void Router::initialize_handlers(bool defaults, bool specifics) { _ehandler_first_by_element.assign(nelements(), -1); _ehandler_to_handler.clear(); _ehandler_next.clear(); _handler_first_by_name.clear(); for (int i = 0; i < _nhandlers_bufs; i += HANDLER_BUFSIZ) delete[] _handler_bufs[i / HANDLER_BUFSIZ]; _nhandlers_bufs = 0; _free_handler = -1; if (defaults) for (int i = 0; i < _elements.size(); i++) _elements[i]->add_default_handlers(specifics); if (specifics) for (int i = 0; i < _elements.size(); i++) _elements[i]->add_handlers(); } int Router::initialize(ErrorHandler *errh) { if (_state != ROUTER_NEW) return errh->error("second attempt to initialize router"); _state = ROUTER_PRECONFIGURE; // initialize handlers to empty initialize_handlers(false, false); // clear attachments _attachment_names.clear(); _attachments.clear(); if (check_hookup_elements(errh) < 0) return -1; _runcount = 1; _master->prepare_router(this); // set up configuration order _element_configure_order.assign(nelements(), 0); if (_element_configure_order.size()) { Vector configure_phase(nelements(), 0); for (int i = 0; i < _elements.size(); i++) { configure_phase[i] = _elements[i]->configure_phase(); _element_configure_order[i] = i; } click_qsort(&_element_configure_order[0], _element_configure_order.size(), sizeof(int), configure_order_compar, configure_phase.begin()); } // notify elements of hookup range // XXX Note: When set_ninputs/set_noutputs is fully removed, this should // change to report errors and bail out if necessary. notify_hookup_range(errh); if (check_hookup_range(ErrorHandler::silent_handler(), true) >= 0) { make_gports(); if (check_push_and_pull(ErrorHandler::silent_handler()) >= 0 && check_hookup_completeness(ErrorHandler::silent_handler(), true) >= 0) set_connections(); } // Configure all elements in configure order. Remember the ones that failed Vector element_stage(nelements(), Element::CLEANUP_CONFIGURED); bool all_ok = true; Vector conf; #if CLICK_DMALLOC char dmalloc_buf[12]; #endif for (int ord = 0; ord < _elements.size(); ord++) { int i = _element_configure_order[ord]; #if CLICK_DMALLOC sprintf(dmalloc_buf, "c%d ", i); CLICK_DMALLOC_REG(dmalloc_buf); #endif ContextErrorHandler cerrh (errh, context_message(i, "While configuring")); int before = cerrh.nerrors(); conf.clear(); cp_argvec(_element_configurations[i], conf); if (_elements[i]->configure(conf, &cerrh) < 0) { element_stage[i] = Element::CLEANUP_CONFIGURE_FAILED; all_ok = false; if (cerrh.nerrors() == before) cerrh.error("unspecified error"); } } #if CLICK_DMALLOC CLICK_DMALLOC_REG("iHoo"); #endif int before = errh->nerrors(); if (!_have_connections) { check_hookup_range(errh, false); make_gports(); check_push_and_pull(errh); check_hookup_completeness(errh, false); set_connections(); } _state = ROUTER_PREINITIALIZE; if (before != errh->nerrors()) all_ok = false; // Initialize elements if OK so far. if (all_ok) { initialize_handlers(true, true); for (int ord = 0; all_ok && ord < _elements.size(); ord++) { int i = _element_configure_order[ord]; assert(element_stage[i] == Element::CLEANUP_CONFIGURED); #if CLICK_DMALLOC sprintf(dmalloc_buf, "i%d ", i); CLICK_DMALLOC_REG(dmalloc_buf); #endif ContextErrorHandler cerrh (errh, context_message(i, "While initializing")); int before = cerrh.nerrors(); if (_elements[i]->initialize(&cerrh) >= 0) element_stage[i] = Element::CLEANUP_INITIALIZED; else { // don't report 'unspecified error' for ErrorElements: // keep error messages clean if (cerrh.nerrors() == before && !_elements[i]->cast("Error")) cerrh.error("unspecified error"); element_stage[i] = Element::CLEANUP_INITIALIZE_FAILED; all_ok = false; } } } #if CLICK_DMALLOC CLICK_DMALLOC_REG("iXXX"); #endif // If there were errors, uninitialize any elements that we initialized // successfully and return -1 (error). Otherwise, we're all set! if (all_ok) { _state = ROUTER_LIVE; #ifdef CLICK_NAMEDB_CHECK NameInfo::check(_root_element, errh); #endif return 0; } else { _state = ROUTER_DEAD; errh->verror_text(ErrorHandler::ERR_CONTEXT_ERROR, "", "Router could not be initialized!"); // Unschedule tasks and timers master()->kill_router(this); // Clean up elements for (int ord = _elements.size() - 1; ord >= 0; ord--) { int i = _element_configure_order[ord]; _elements[i]->cleanup((Element::CleanupStage) element_stage[i]); } // Remove element-specific handlers initialize_handlers(true, false); _runcount = 0; return -1; } } void Router::activate(bool foreground, ErrorHandler *errh) { if (_state != ROUTER_LIVE || _running != RUNNING_PREPARING) return; // Take state if appropriate if (_hotswap_router && _hotswap_router->_state == ROUTER_LIVE) { // Unschedule tasks and timers master()->kill_router(_hotswap_router); for (int i = 0; i < _elements.size(); i++) { Element *e = _elements[_element_configure_order[i]]; if (Element *other = e->hotswap_element()) { ContextErrorHandler cerrh (errh, context_message(i, "While hot-swapping state into")); e->take_state(other, &cerrh); } } } if (_hotswap_router) { _hotswap_router->unuse(); _hotswap_router = 0; } // Activate router master()->run_router(this, foreground); // sets _running to RUNNING_BACKGROUND or RUNNING_ACTIVE } // steal state void Router::set_hotswap_router(Router *r) { assert(_state == ROUTER_NEW && !_hotswap_router && (!r || r->initialized())); _hotswap_router = r; if (_hotswap_router) _hotswap_router->use(); } // HANDLERS /** @class Handler @brief Represents a router's handlers. Each handler is represented by a Handler object. Handlers are not attached to specific elements, allowing a single handler object to be shared among multiple elements with the same basic handler definition. Handlers cannot be created directly; to create one, call methods such as Router::add_read_handler(), Router::add_write_handler(), Router::set_handler(), Element::add_read_handler(), Element::add_write_handler(), and Element::set_handler(). */ /** @brief Call a read handler, possibly with parameters. @param e element on which to call the handler @param param parameters, or an empty string if no parameters @param raw true iff value is raw text (see raw()) @param errh error handler The element must be nonnull; to call a global handler, pass the relevant router's Router::root_element(). @a errh may be null, in which case errors are reported to ErrorHandler::silent_handler(). */ String Handler::call_read(Element* e, const String& param, bool raw, ErrorHandler* errh) const { if (!errh) errh = ErrorHandler::silent_handler(); if (param && !(_flags & READ_PARAM)) errh->error("read handler '%s' does not take parameters", unparse_name(e).c_str()); else if ((_flags & (ONE_HOOK | OP_READ)) == OP_READ) return _hook.rw.r(e, _thunk1); else if (_flags & OP_READ) { String s(param); if ((_flags & RAW) && !raw) s = cp_unquote(s); if (_hook.h(OP_READ, s, e, this, errh) >= 0) return s; } else errh->error("'%s' not a read handler", unparse_name(e).c_str()); // error cases get here return String(); } /** @brief Call a write handler. @param s value to write to the handler @param e element on which to call the handler @param raw true iff value is raw text (see raw()) @param errh error handler The element must be nonnull; to call a global handler, pass the relevant router's Router::root_element(). @a errh may be null, in which case errors are reported to ErrorHandler::silent_handler(). */ int Handler::call_write(const String& s, Element* e, bool raw, ErrorHandler* errh) const { if (!errh) errh = ErrorHandler::silent_handler(); String s_copy(s); if ((_flags & RAW) && !raw) s_copy = cp_unquote(s_copy); if ((_flags & (ONE_HOOK | OP_WRITE)) == OP_WRITE) return _hook.rw.w(s_copy, e, _thunk2, errh); else if (_flags & OP_WRITE) return _hook.h(OP_WRITE, s_copy, e, this, errh); else { errh->error("'%s' not a write handler", unparse_name(e).c_str()); return -EACCES; } } /** @brief Unparses this handler's name, including */ String Handler::unparse_name(Element *e, const String &hname) { if (e && e != e->router()->root_element()) return e->name() + "." + hname; else return hname; } /** @brief Unparses this handler's name. @param e the relevant element If @a e is an actual element, then returns "ENAME.NAME", where I@a e->name() + "." + */ String Handler::unparse_name(Element *e) const { if (this == the_blank_handler) return _name; else return unparse_name(e, _name); } // Private functions for finding and storing handlers // 11.Jul.2000 - We had problems with handlers for medium-sized configurations // (~400 elements): the Linux kernel would crash with a "kmalloc too large". // The solution: Observe that most handlers are shared. For example, all // 'name' handlers can share a Handler structure, since they share the same // read function, read thunk, write function (0), write thunk, and name. This // introduced a bunch of structure to go from elements to handler indices and // from handler names to handler indices, but it was worth it: it reduced the // amount of space required by a normal set of Handlers by about a factor of // 100 -- there used to be 2998 Handlers, now there are 30. (Some of this // space is still not available for system use -- it gets used up by the // indexing structures, particularly _ehandlers. Every element has its own // list of "element handlers", even though most elements with element class C // could share one such list. The space cost is about (48 bytes * # of // elements) more or less. Detecting this sharing would be harder to // implement.) int Router::find_ehandler(int eindex, const String& name, bool allow_star) const { int eh = _ehandler_first_by_element[eindex]; int star_h = -1; while (eh >= 0) { int h = _ehandler_to_handler[eh]; const String &hname = xhandler(h)->name(); if (hname == name) return eh; else if (hname.length() == 1 && hname[0] == '*') star_h = h; eh = _ehandler_next[eh]; } if (allow_star && star_h >= 0 && xhandler(star_h)->writable()) { if (xhandler(star_h)->call_write(name, element(eindex), true, ErrorHandler::default_handler()) >= 0) eh = find_ehandler(eindex, name, false); } return eh; } inline Handler Router::fetch_handler(const Element* e, const String& name) { if (const Handler* h = handler(e, name)) return *h; else return Handler(name); } void Router::store_local_handler(int eindex, const Handler& to_store) { int old_eh = find_ehandler(eindex, to_store.name(), false); if (old_eh >= 0) xhandler(_ehandler_to_handler[old_eh])->_use_count--; // find the offset in _name_handlers int name_index; for (name_index = 0; name_index < _handler_first_by_name.size(); name_index++) { int h = _handler_first_by_name[name_index]; if (xhandler(h)->_name == to_store._name) break; } if (name_index == _handler_first_by_name.size()) _handler_first_by_name.push_back(-1); // find a similar handler, if any exists int* prev_h = &_handler_first_by_name[name_index]; int h = *prev_h; int* blank_prev_h = 0; int blank_h = -1; int stored_h = -1; while (h >= 0) { Handler* han = xhandler(h); if (han->compatible(to_store)) stored_h = h; else if (han->_use_count == 0) blank_h = h, blank_prev_h = prev_h; prev_h = &han->_next_by_name; h = *prev_h; } // if none exists, assign this one to a blank spot if (stored_h < 0 && blank_h >= 0) { stored_h = blank_h; *xhandler(stored_h) = to_store; xhandler(stored_h)->_use_count = 0; } // if no blank spot, add a handler if (stored_h < 0) { if (_free_handler < 0) { int n_handler_bufs = _nhandlers_bufs / HANDLER_BUFSIZ; Handler** new_handler_bufs = new Handler*[n_handler_bufs + 1]; Handler* new_handler_buf = new Handler[HANDLER_BUFSIZ]; if (!new_handler_buf || !new_handler_bufs) { // out of memory delete[] new_handler_bufs; delete[] new_handler_buf; if (old_eh >= 0) // restore use count xhandler(_ehandler_to_handler[old_eh])->_use_count++; return; } for (int i = 0; i < HANDLER_BUFSIZ - 1; i++) new_handler_buf[i]._next_by_name = _nhandlers_bufs + i + 1; _free_handler = _nhandlers_bufs; memcpy(new_handler_bufs, _handler_bufs, sizeof(Handler*) * n_handler_bufs); new_handler_bufs[n_handler_bufs] = new_handler_buf; delete[] _handler_bufs; _handler_bufs = new_handler_bufs; _nhandlers_bufs += HANDLER_BUFSIZ; } stored_h = _free_handler; _free_handler = xhandler(stored_h)->_next_by_name; *xhandler(stored_h) = to_store; xhandler(stored_h)->_use_count = 0; xhandler(stored_h)->_next_by_name = _handler_first_by_name[name_index]; _handler_first_by_name[name_index] = stored_h; } // point ehandler list at new handler if (old_eh >= 0) _ehandler_to_handler[old_eh] = stored_h; else { int new_eh = _ehandler_to_handler.size(); _ehandler_to_handler.push_back(stored_h); _ehandler_next.push_back(_ehandler_first_by_element[eindex]); _ehandler_first_by_element[eindex] = new_eh; } // increment use count xhandler(stored_h)->_use_count++; // perhaps free blank_h if (blank_h >= 0 && xhandler(blank_h)->_use_count == 0) { *blank_prev_h = xhandler(blank_h)->_next_by_name; xhandler(blank_h)->_next_by_name = _free_handler; _free_handler = blank_h; } } void Router::store_global_handler(const Handler &h) { for (int i = 0; i < nglobalh; i++) if (globalh[i]._name == h._name) { globalh[i] = h; globalh[i]._use_count = 1; return; } if (nglobalh >= globalh_cap) { int n = (globalh_cap ? 2 * globalh_cap : 4); Handler *hs = new Handler[n]; if (!hs) // out of memory return; for (int i = 0; i < nglobalh; i++) hs[i] = globalh[i]; delete[] globalh; globalh = hs; globalh_cap = n; } globalh[nglobalh] = h; globalh[nglobalh]._use_count = 1; nglobalh++; } inline void Router::store_handler(const Element* e, const Handler& to_store) { if (e) e->router()->store_local_handler(e->eindex(), to_store); else store_global_handler(to_store); } // Public functions for finding handlers const Handler* Router::handler(const Router* r, int hi) { if (r && hi >= 0 && hi < r->_nhandlers_bufs) return r->xhandler(hi); else if (hi >= FIRST_GLOBAL_HANDLER && hi < FIRST_GLOBAL_HANDLER + nglobalh) return &globalh[hi - FIRST_GLOBAL_HANDLER]; else return 0; } const Handler * Router::handler(const Element* e, const String& hname) { if (e && e != e->router()->_root_element) { const Router *r = e->router(); int eh = r->find_ehandler(e->eindex(), hname, true); if (eh >= 0) return r->xhandler(r->_ehandler_to_handler[eh]); } else { // global handler for (int i = 0; i < nglobalh; i++) if (globalh[i]._name == hname) return &globalh[i]; } return 0; } int Router::hindex(const Element* e, const String& hname) { if (e && e != e->router()->_root_element) { const Router *r = e->router(); int eh = r->find_ehandler(e->eindex(), hname, true); if (eh >= 0) return r->_ehandler_to_handler[eh]; } else { // global handler for (int i = 0; i < nglobalh; i++) if (globalh[i]._name == hname) return FIRST_GLOBAL_HANDLER + i; } return -1; } void Router::element_hindexes(const Element* e, Vector& hindexes) { if (e && e != e->router()->_root_element) { const Router *r = e->router(); for (int eh = r->_ehandler_first_by_element[e->eindex()]; eh >= 0; eh = r->_ehandler_next[eh]) hindexes.push_back(r->_ehandler_to_handler[eh]); } else { for (int i = 0; i < nglobalh; i++) hindexes.push_back(FIRST_GLOBAL_HANDLER + i); } } // Public functions for storing handlers void Router::add_read_handler(const Element* e, const String& name, ReadHandlerHook hook, void* thunk) { Handler to_add = fetch_handler(e, name); if (to_add._flags & Handler::ONE_HOOK) { to_add._hook.rw.w = 0; to_add._thunk2 = 0; to_add._flags &= ~Handler::PRIVATE_MASK; } to_add._hook.rw.r = hook; to_add._thunk1 = thunk; to_add._flags |= Handler::OP_READ; store_handler(e, to_add); } void Router::add_write_handler(const Element* e, const String& name, WriteHandlerHook hook, void* thunk) { Handler to_add = fetch_handler(e, name); if (to_add._flags & Handler::ONE_HOOK) { to_add._hook.rw.r = 0; to_add._thunk1 = 0; to_add._flags &= ~Handler::PRIVATE_MASK; } to_add._hook.rw.w = hook; to_add._thunk2 = thunk; to_add._flags |= Handler::OP_WRITE; store_handler(e, to_add); } void Router::set_handler(const Element* e, const String& name, int flags, HandlerHook hook, void* thunk1, void* thunk2) { Handler to_add(name); to_add._hook.h = hook; to_add._thunk1 = thunk1; to_add._thunk2 = thunk2; to_add._flags = flags | Handler::ONE_HOOK; store_handler(e, to_add); } int Router::change_handler_flags(const Element* e, const String& name, uint32_t clear_flags, uint32_t set_flags) { Handler to_add = fetch_handler(e, name); if (to_add._use_count > 0) { // only modify existing handlers clear_flags &= ~Handler::PRIVATE_MASK; set_flags &= ~Handler::PRIVATE_MASK; to_add._flags = (to_add._flags & ~clear_flags) | set_flags; store_handler(e, to_add); return 0; } else return -1; } // ATTACHMENTS void* Router::attachment(const String &name) const { for (int i = 0; i < _attachments.size(); i++) if (_attachment_names[i] == name) return _attachments[i]; return 0; } void*& Router::force_attachment(const String &name) { for (int i = 0; i < _attachments.size(); i++) if (_attachment_names[i] == name) return _attachments[i]; _attachment_names.push_back(name); _attachments.push_back(0); return _attachments.back(); } void * Router::set_attachment(const String &name, void *value) { for (int i = 0; i < _attachments.size(); i++) if (_attachment_names[i] == name) { void *v = _attachments[i]; _attachments[i] = value; return v; } _attachment_names.push_back(name); _attachments.push_back(value); return 0; } ErrorHandler * Router::chatter_channel(const String &name) const { if (!name || name == "default") return ErrorHandler::default_handler(); else if (void *v = attachment("ChatterChannel." + name)) return (ErrorHandler *)v; else return ErrorHandler::silent_handler(); } /** @brief Create a new basic signal. * @param[out] signal the new signal * * Creates a new basic NotifierSignal and stores it in @a signal. The signal * is initially active. * * @return >= 0 on success, < 0 on failure * @sa NotifierSignal */ int Router::new_notifier_signal(NotifierSignal &signal) { if (!_notifier_signals) _notifier_signals = new atomic_uint32_t[NOTIFIER_SIGNALS_CAPACITY / 32]; if (_n_notifier_signals >= NOTIFIER_SIGNALS_CAPACITY) return -1; else { signal = NotifierSignal(&_notifier_signals[_n_notifier_signals / 32], 1 << (_n_notifier_signals % 32)); signal.set_active(true); _n_notifier_signals++; return 0; } } int ThreadSched::initial_home_thread_id(Task *, bool) { return 0; } NameInfo* Router::force_name_info() { if (!_name_info) _name_info = new NameInfo; return _name_info; } // PRINTING void Router::unparse_requirements(StringAccum &sa, const String &indent) const { // requirements if (_requirements.size()) sa << indent << "require(" << cp_unargvec(_requirements) << ");\n\n"; } void Router::unparse_classes(StringAccum &, const String &) const { // there are never any compound element classes here } void Router::unparse_declarations(StringAccum &sa, const String &indent) const { // element classes Vector conf; for (int i = 0; i < nelements(); i++) { sa << indent << _element_names[i] << " :: " << _elements[i]->class_name(); String conf = (initialized() ? _elements[i]->configuration() : _element_configurations[i]); if (conf.length()) sa << "(" << conf << ")"; sa << ";\n"; } if (nelements() > 0) sa << "\n"; } void Router::unparse_connections(StringAccum &sa, const String &indent) const { int nhookup = _hookup_from.size(); Vector next(nhookup, -1); Bitvector startchain(nhookup, true); for (int c = 0; c < nhookup; c++) { const Hookup &ht = _hookup_to[c]; if (ht.port != 0) continue; int result = -1; for (int d = 0; d < nhookup; d++) if (d != c && _hookup_from[d] == ht) { result = d; if (_hookup_to[d].port == 0) break; } if (result >= 0) { next[c] = result; startchain[result] = false; } } // print hookup Bitvector used(nhookup, false); bool done = false; while (!done) { // print chains for (int c = 0; c < nhookup; c++) { if (used[c] || !startchain[c]) continue; const Hookup &hf = _hookup_from[c]; sa << indent << _element_names[hf.idx]; if (hf.port) sa << " [" << hf.port << "]"; int d = c; while (d >= 0 && !used[d]) { if (d == c) sa << " -> "; else sa << "\n" << indent << " -> "; const Hookup &ht = _hookup_to[d]; if (ht.port) sa << "[" << ht.port << "] "; sa << _element_names[ht.idx]; used[d] = true; d = next[d]; } sa << ";\n"; } // add new chains to include cycles done = true; for (int c = 0; c < nhookup && done; c++) if (!used[c]) startchain[c] = true, done = false; } } void Router::unparse(StringAccum &sa, const String &indent) const { unparse_requirements(sa, indent); unparse_classes(sa, indent); unparse_declarations(sa, indent); unparse_connections(sa, indent); } String Router::element_ports_string(int ei) const { if (ei < 0 || ei >= nelements()) return String(); StringAccum sa; Element *e = _elements[ei]; Vector pers(e->ninputs() + e->noutputs(), 0); int *in_pers = pers.begin(); int *out_pers = pers.begin() + e->ninputs(); e->processing_vector(in_pers, out_pers, 0); sa << e->ninputs() << (e->ninputs() == 1 ? " input\n" : " inputs\n"); for (int i = 0; i < e->ninputs(); i++) { // processing const char *persid = (e->input_is_pull(i) ? "pull" : "push"); if (in_pers[i] == Element::VAGNOSTIC) sa << persid << "~\t"; else sa << persid << "\t"; // counts #if CLICK_STATS >= 1 if (e->input_is_pull(i) || CLICK_STATS >= 2) sa << e->input(i).npackets() << "\t"; else #endif sa << "-\t"; // connections Hookup h(ei, i); const char *sep = ""; for (int c = 0; c < _hookup_from.size(); c++) if (_hookup_to[c] == h) { sa << sep << _element_names[_hookup_from[c].idx] << " [" << _hookup_from[c].port << "]"; sep = ", "; } sa << "\n"; } sa << e->noutputs() << (e->noutputs() == 1 ? " output\n" : " outputs\n"); for (int i = 0; i < e->noutputs(); i++) { // processing const char *persid = (e->output_is_push(i) ? "push" : "pull"); if (out_pers[i] == Element::VAGNOSTIC) sa << persid << "~\t"; else sa << persid << "\t"; // counts #if CLICK_STATS >= 1 if (e->output_is_push(i) || CLICK_STATS >= 2) sa << e->output(i).npackets() << "\t"; else #endif sa << "-\t"; // hookup Hookup h(ei, i); const char *sep = ""; for (int c = 0; c < _hookup_from.size(); c++) if (_hookup_from[c] == h) { sa << sep << "[" << _hookup_to[c].port << "] " << _element_names[_hookup_to[c].idx]; sep = ", "; } sa << "\n"; } return sa.take_string(); } // STATIC INITIALIZATION, DEFAULT GLOBAL HANDLERS enum { GH_VERSION, GH_CONFIG, GH_FLATCONFIG, GH_LIST, GH_REQUIREMENTS }; String Router::router_read_handler(Element *e, void *thunk) { Router *r = (e ? e->router() : 0); switch (reinterpret_cast(thunk)) { case GH_VERSION: return String(CLICK_VERSION "\n"); case GH_CONFIG: if (r) return r->configuration_string(); break; case GH_FLATCONFIG: if (r) { StringAccum sa; r->unparse(sa); return sa.take_string(); } break; case GH_LIST: if (r) { StringAccum sa; sa << r->nelements() << "\n"; for (int i = 0; i < r->nelements(); i++) sa << r->_element_names[i] << "\n"; return sa.take_string(); } break; case GH_REQUIREMENTS: if (r) { StringAccum sa; for (int i = 0; i < r->_requirements.size(); i++) sa << r->_requirements[i] << "\n"; return sa.take_string(); } break; default: return "\n"; } return String(); } static int stop_global_handler(const String &s, Element *e, void *, ErrorHandler *errh) { if (e) { int n = 1; (void) cp_integer(cp_uncomment(s), &n); e->router()->adjust_runcount(-n); } else errh->message("no router to stop"); return 0; } void Router::static_initialize() { if (!nglobalh) { Handler::the_blank_handler = new Handler(""); add_read_handler(0, "version", router_read_handler, (void *)GH_VERSION); add_read_handler(0, "config", router_read_handler, (void *)GH_CONFIG); add_read_handler(0, "flatconfig", router_read_handler, (void *)GH_FLATCONFIG); add_read_handler(0, "list", router_read_handler, (void *)GH_LIST); add_read_handler(0, "requirements", router_read_handler, (void *)GH_REQUIREMENTS); add_write_handler(0, "stop", stop_global_handler, 0); } } void Router::static_cleanup() { delete[] globalh; globalh = 0; nglobalh = globalh_cap = 0; delete Handler::the_blank_handler; } #ifdef CLICK_NS int Router::sim_get_ifid(const char* ifname) { return simclick_sim_ifid_from_name(_master->siminst(), ifname); } Vector * Router::sim_listenvec(int ifid) { for (int i = 0; i < _listenvecs.size(); i++) if (_listenvecs[i]->at(0) == ifid) return _listenvecs[i]; Vector *new_vec = new Vector(1, ifid); if (new_vec) _listenvecs.push_back(new_vec); return new_vec; } int Router::sim_listen(int ifid, int element) { if (Vector *vec = sim_listenvec(ifid)) { for (int i = 1; i < vec->size(); i++) if ((*vec)[i] == element) return 0; vec->push_back(element); return 0; } else return -1; } int Router::sim_write(int ifid,int ptype,const unsigned char* data,int len, simclick_simpacketinfo* pinfo) { return simclick_sim_send_to_if(_master->siminst(),_master->clickinst(),ifid,ptype,data,len,pinfo); } int Router::sim_if_ready(int ifid) { return simclick_sim_if_ready(_master->siminst(),_master->clickinst(),ifid); } int Router::sim_incoming_packet(int ifid, int ptype, const unsigned char* data, int len, simclick_simpacketinfo* pinfo) { if (Vector *vec = sim_listenvec(ifid)) for (int i = 1; i < vec->size(); i++) ((FromSimDevice *)element((*vec)[i]))->incoming_packet(ifid, ptype, data, len, pinfo); return 0; } void Router::sim_trace(const char* event) { simclick_sim_trace(_master->siminst(), _master->clickinst(), event); } int Router::sim_get_node_id() { return simclick_sim_get_node_id(_master->siminst(), _master->clickinst()); } int Router::sim_get_next_pkt_id() { return simclick_sim_get_next_pkt_id(_master->siminst(), _master->clickinst()); } #endif // CLICK_NS #if CLICK_USERLEVEL // Vector template instance # include #endif CLICK_ENDDECLS