diff --git a/main/gr_module.h b/main/gr_module.h index e39a1a5dc..90f9d29e6 100644 --- a/main/gr_module.h +++ b/main/gr_module.h @@ -32,9 +32,8 @@ void gr_register_api_handler(struct gr_api_handler *); struct gr_module { const char *name; - int init_prio; + const char *depends_on; void (*init)(struct event_base *); - int fini_prio; void (*fini)(struct event_base *); void (*init_dp)(void); void (*fini_dp)(void); diff --git a/main/module.c b/main/module.c index a50ce4517..ad1ef42f6 100644 --- a/main/module.c +++ b/main/module.c @@ -52,15 +52,90 @@ const struct gr_api_handler *lookup_api_handler(const struct gr_api_request *req static STAILQ_HEAD(, gr_module) modules = STAILQ_HEAD_INITIALIZER(modules); void gr_register_module(struct gr_module *mod) { + struct gr_module *m; + if (mod->name == NULL) ABORT("module with no name: %p", mod); + + STAILQ_FOREACH (m, &modules, entries) { + if (strcmp(mod->name, m->name) == 0) + ABORT("duplicate module name: '%s'", mod->name); + } + STAILQ_INSERT_TAIL(&modules, mod, entries); } -static int module_init_prio_order(const void *a, const void *b) { - const struct gr_module *const *mod_a = a; - const struct gr_module *const *mod_b = b; - return (*mod_a)->init_prio - (*mod_b)->init_prio; +static void topo_sort(struct gr_module **mods) { + // Create an adjacency matrix representing all edges + const size_t len = gr_vec_len(mods); + bool *adj_matrix = calloc(len * len, sizeof(bool)); + if (adj_matrix == NULL) + ABORT("cannot allocate memory"); + + for (unsigned i = 0; i < len; i++) { + if (mods[i]->depends_on == NULL) + continue; + + for (unsigned j = 0; j < len; j++) { + if (strcmp(mods[j]->name, mods[i]->depends_on) == 0) { + adj_matrix[j * len + i] = true; + break; + } + } + } + + // Calculate in-degree of each vertex + int *in_degree = calloc(len, sizeof(int)); + if (in_degree == NULL) + ABORT("cannot allocate memory"); + + for (unsigned i = 0; i < len; i++) { + for (unsigned j = 0; j < len; j++) { + if (adj_matrix[i * len + j]) { + in_degree[j]++; + } + } + } + + // Kahn's algorithm for topological sort + unsigned front = 0, rear = 0; + unsigned *queue = calloc(len, sizeof(unsigned)); + if (queue == NULL) + ABORT("cannot allocate memory"); + + for (unsigned i = 0; i < len; i++) { + if (in_degree[i] == 0) { + queue[rear++] = i; + } + } + + struct gr_module **sorted = calloc(len, sizeof(struct gr_module *)); + if (sorted == NULL) + ABORT("cannot allocate memory"); + + unsigned i = 0; + while (front < rear) { + unsigned u = queue[front++]; + sorted[i++] = mods[u]; + + // Reduce in-degree of neighbours + for (unsigned v = 0; v < len; v++) { + if (adj_matrix[u * len + v]) { + in_degree[v]--; + if (in_degree[v] == 0) { + queue[rear++] = v; + } + } + } + } + + // Copy the sorted modules back to the original array + memcpy(mods, sorted, len * sizeof(struct gr_module *)); + + free(sorted); + free(queue); + free(in_degree); + free(adj_matrix); } void modules_init(struct event_base *ev_base) { @@ -72,11 +147,11 @@ void modules_init(struct event_base *ev_base) { if (mods == NULL) ABORT("failed to alloc module array"); - qsort(mods, gr_vec_len(mods), sizeof(struct gr_module *), module_init_prio_order); + topo_sort(mods); gr_vec_foreach (mod, mods) { if (mod->init != NULL) { - LOG(DEBUG, "%s prio %i", mod->name, mod->init_prio); + LOG(DEBUG, "'%s' (depends on '%s')", mod->name, mod->depends_on ?: ""); mod->init(ev_base); } } @@ -84,12 +159,6 @@ void modules_init(struct event_base *ev_base) { gr_vec_free(mods); } -static int module_fini_prio_order(const void *a, const void *b) { - const struct gr_module *const *mod_a = a; - const struct gr_module *const *mod_b = b; - return (*mod_a)->fini_prio - (*mod_b)->fini_prio; -} - void modules_fini(struct event_base *ev_base) { struct gr_module *mod, **mods = NULL; @@ -99,11 +168,13 @@ void modules_fini(struct event_base *ev_base) { if (mods == NULL) ABORT("failed to alloc module array"); - qsort(mods, gr_vec_len(mods), sizeof(struct gr_module *), module_fini_prio_order); + topo_sort(mods); - gr_vec_foreach (mod, mods) { + // call fini() functions in reverse topological order + for (int i = gr_vec_len(mods) - 1; i >= 0; i--) { + mod = mods[i]; if (mod->fini != NULL) { - LOG(DEBUG, "%s prio %i", mod->name, mod->fini_prio); + LOG(DEBUG, "'%s' (depends on '%s')", mod->name, mod->depends_on ?: ""); mod->fini(ev_base); } } diff --git a/modules/infra/control/control_output.c b/modules/infra/control/control_output.c index 7cf1c7915..c7fa56183 100644 --- a/modules/infra/control/control_output.c +++ b/modules/infra/control/control_output.c @@ -107,6 +107,7 @@ static void control_output_fini(struct event_base *) { static struct gr_module control_output_module = { .name = "control_output", + .depends_on = "graph", .init = control_output_init, .fini = control_output_fini, }; diff --git a/modules/infra/control/graph.c b/modules/infra/control/graph.c index 0bd3e193d..505921628 100644 --- a/modules/infra/control/graph.c +++ b/modules/infra/control/graph.c @@ -337,10 +337,9 @@ static void graph_fini(struct event_base *) { static struct gr_module graph_module = { .name = "graph", + .depends_on = "iface", .init = graph_init, - .init_prio = -999, .fini = graph_fini, - .fini_prio = -999, }; RTE_INIT(control_graph_init) { diff --git a/modules/infra/control/iface.c b/modules/infra/control/iface.c index 08f9d9f78..a180ada39 100644 --- a/modules/infra/control/iface.c +++ b/modules/infra/control/iface.c @@ -298,9 +298,9 @@ static void iface_fini(struct event_base *) { static struct gr_module iface_module = { .name = "iface", + .depends_on = "iface port", .init = iface_init, .fini = iface_fini, - .fini_prio = 1000, }; static void iface_event_debug(uint32_t event, const void *obj) { diff --git a/modules/infra/control/loopback.c b/modules/infra/control/loopback.c index 69f8216f6..cd3cb8d27 100644 --- a/modules/infra/control/loopback.c +++ b/modules/infra/control/loopback.c @@ -273,7 +273,6 @@ static struct gr_module loopback_module = { .name = "iface loopback", .init = loopback_module_init, .fini = loopback_module_fini, - .fini_prio = 1000, }; RTE_INIT(loopback_constructor) { diff --git a/modules/infra/control/nexthop.c b/modules/infra/control/nexthop.c index 78ab7c796..83c95d3dc 100644 --- a/modules/infra/control/nexthop.c +++ b/modules/infra/control/nexthop.c @@ -311,7 +311,6 @@ static struct gr_module module = { .name = "nexthop", .init = nh_init, .fini = nh_fini, - .fini_prio = 20000, }; RTE_INIT(init) { diff --git a/modules/infra/control/vlan.c b/modules/infra/control/vlan.c index 83e5deedd..7de8fe559 100644 --- a/modules/infra/control/vlan.c +++ b/modules/infra/control/vlan.c @@ -257,7 +257,6 @@ static struct gr_module vlan_module = { .name = "vlan", .init = vlan_init, .fini = vlan_fini, - .fini_prio = 1000, }; static void port_event(uint32_t event, const void *obj) { diff --git a/modules/infra/control/worker.c b/modules/infra/control/worker.c index 8d6b144ab..085b9b16d 100644 --- a/modules/infra/control/worker.c +++ b/modules/infra/control/worker.c @@ -422,9 +422,9 @@ static void worker_fini(struct event_base *) { static struct gr_module worker_module = { .name = "worker", + .depends_on = "control_output", .init = worker_init, .fini = worker_fini, - .fini_prio = -1000, }; RTE_INIT(control_infra_init) { diff --git a/modules/ip/control/address.c b/modules/ip/control/address.c index 5bd0577f9..14453c10b 100644 --- a/modules/ip/control/address.c +++ b/modules/ip/control/address.c @@ -217,7 +217,6 @@ static struct gr_module addr_module = { .name = "ipv4 address", .init = addr_init, .fini = addr_fini, - .fini_prio = 2000, }; static struct gr_event_subscription iface_pre_rm_subscription = { diff --git a/modules/ip/control/nexthop.c b/modules/ip/control/nexthop.c index 7afd4cd65..4d2e4b9a9 100644 --- a/modules/ip/control/nexthop.c +++ b/modules/ip/control/nexthop.c @@ -294,6 +294,7 @@ static struct gr_api_handler nh4_list_handler = { static struct gr_module nh4_module = { .name = "ipv4 nexthop", + .depends_on = "graph", .init = nh4_init, }; diff --git a/modules/ip/control/route.c b/modules/ip/control/route.c index 78b2715e5..dab17e003 100644 --- a/modules/ip/control/route.c +++ b/modules/ip/control/route.c @@ -476,7 +476,6 @@ static struct gr_module route4_module = { .name = "ipv4 route", .init = route4_init, .fini = route4_fini, - .fini_prio = 10000, }; RTE_INIT(control_ip_init) { diff --git a/modules/ip/datapath/fib4.c b/modules/ip/datapath/fib4.c index e81ffedb9..fcbaa2749 100644 --- a/modules/ip/datapath/fib4.c +++ b/modules/ip/datapath/fib4.c @@ -155,7 +155,6 @@ static struct gr_module module = { .name = "fib4", .init = fib4_init, .fini = fib4_fini, - .fini_prio = 10000, }; RTE_INIT(init) { diff --git a/modules/ip6/control/address.c b/modules/ip6/control/address.c index 497f61e64..2de53247c 100644 --- a/modules/ip6/control/address.c +++ b/modules/ip6/control/address.c @@ -378,7 +378,6 @@ static struct gr_module addr6_module = { .name = "ipv6 address", .init = addr6_init, .fini = addr6_fini, - .fini_prio = 2000, }; static struct gr_event_subscription iface_event_subscription = { diff --git a/modules/ip6/control/nexthop.c b/modules/ip6/control/nexthop.c index 5e4f6dbe1..bdbac2de3 100644 --- a/modules/ip6/control/nexthop.c +++ b/modules/ip6/control/nexthop.c @@ -339,6 +339,7 @@ static struct gr_api_handler nh6_list_handler = { static struct gr_module nh6_module = { .name = "ipv6 nexthop", + .depends_on = "graph", .init = nh6_init, }; diff --git a/modules/ip6/control/route.c b/modules/ip6/control/route.c index 5437027c1..5ac0eec83 100644 --- a/modules/ip6/control/route.c +++ b/modules/ip6/control/route.c @@ -504,7 +504,6 @@ static struct gr_module route6_module = { .name = "ipv6 route", .init = route6_init, .fini = route6_fini, - .fini_prio = 10000, }; RTE_INIT(control_ip_init) { diff --git a/modules/ip6/control/router_advert.c b/modules/ip6/control/router_advert.c index 419f39372..797049fe7 100644 --- a/modules/ip6/control/router_advert.c +++ b/modules/ip6/control/router_advert.c @@ -210,9 +210,9 @@ static void ra_fini(struct event_base * /*ev_base*/) { static struct gr_module ra_module = { .name = "ipv6 router advertisement", + .depends_on = "graph", .init = ra_init, .fini = ra_fini, - .fini_prio = 20000, }; static struct gr_api_handler ra_set_handler = { diff --git a/modules/ip6/datapath/fib6.c b/modules/ip6/datapath/fib6.c index 3d71c6576..74a1b3b2c 100644 --- a/modules/ip6/datapath/fib6.c +++ b/modules/ip6/datapath/fib6.c @@ -175,7 +175,6 @@ static struct gr_module module = { .name = "fib6", .init = fib6_init, .fini = fib6_fini, - .fini_prio = 10000, }; RTE_INIT(init) { diff --git a/modules/ipip/control.c b/modules/ipip/control.c index 29b94aec1..a1bc48e28 100644 --- a/modules/ipip/control.c +++ b/modules/ipip/control.c @@ -156,7 +156,6 @@ static struct gr_module ipip_module = { .name = "ipip", .init = ipip_init, .fini = ipip_fini, - .fini_prio = 1000, }; RTE_INIT(ipip_constructor) { diff --git a/modules/srv6/control_headend.c b/modules/srv6/control_headend.c index 788dec314..5cf8db78a 100644 --- a/modules/srv6/control_headend.c +++ b/modules/srv6/control_headend.c @@ -510,7 +510,6 @@ static struct gr_module srv6_headend_module = { .name = "srv6_headend", .init = srv6_init, .fini = srv6_fini, - .fini_prio = 1000, }; RTE_INIT(srv6_constructor) { diff --git a/modules/srv6/control_local.c b/modules/srv6/control_local.c index 8f334f43b..5f0a6b605 100644 --- a/modules/srv6/control_local.c +++ b/modules/srv6/control_local.c @@ -176,7 +176,6 @@ static struct gr_module srv6_local_module = { .name = "srv6_local", .init = srv6_init, .fini = srv6_fini, - .fini_prio = 1000, }; RTE_INIT(srv6_constructor) {