summaryrefslogtreecommitdiff
path: root/nest/proto.c
diff options
context:
space:
mode:
authorMartin Mares <mj@ucw.cz>2000-01-16 16:44:50 +0000
committerMartin Mares <mj@ucw.cz>2000-01-16 16:44:50 +0000
commit50fe90edf3deab409ea7887c131bfe6ce89fa556 (patch)
treed9aad4ed34285a59cfcc0286a22ca5ccdb63a82f /nest/proto.c
parent394aec8fdd112a81da1e2f6f0e09ee74256dc24e (diff)
First attempt on dynamic reconfiguration. There are still lots of bugs
and problems to solve, but the hardest part works.
Diffstat (limited to 'nest/proto.c')
-rw-r--r--nest/proto.c155
1 files changed, 109 insertions, 46 deletions
diff --git a/nest/proto.c b/nest/proto.c
index a58c3f71..ecf0d906 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -1,7 +1,7 @@
/*
* BIRD -- Protocols
*
- * (c) 1998--1999 Martin Mares <mj@ucw.cz>
+ * (c) 1998--2000 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@@ -38,6 +38,7 @@ static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
static char *c_states[] = { "HUNGRY", "FEEDING", "HAPPY", "FLUSHING" };
static int proto_flush_all(void *);
+static void proto_rethink_goal(struct proto *p);
static void
proto_enqueue(list *l, struct proto *p)
@@ -103,6 +104,7 @@ proto_init_instance(struct proto *p)
p->pool = rp_new(proto_pool, p->proto->name);
p->attn = ev_new(p->pool);
p->attn->data = p;
+ rt_lock_table(p->table);
}
struct announce_hook *
@@ -185,42 +187,118 @@ protos_postconfig(struct config *c)
debug("\n");
}
+static struct proto *
+proto_init(struct proto_config *c)
+{
+ struct protocol *p = c->protocol;
+ struct proto *q = p->init(c);
+
+ q->proto_state = PS_DOWN;
+ q->core_state = FS_HUNGRY;
+ proto_enqueue(&initial_proto_list, q);
+ /*
+ * HACK ALERT! In case of multiple kernel routing tables,
+ * the kernel syncer acts as multiple protocols which cooperate
+ * with each other. In order to speed up their initialization,
+ * we need to know when we're initializing the last one, hence
+ * the startup counter.
+ */
+ if (!q->disabled)
+ p->startup_counter++;
+ return q;
+}
+
void
-protos_commit(struct config *c)
+protos_commit(struct config *new, struct config *old, int force_reconfig)
{
- struct proto_config *x;
- struct protocol *p;
- struct proto *q;
+ struct proto_config *oc, *nc;
+ struct proto *p, *n;
- debug("Protocol commit:");
- WALK_LIST(x, c->protos)
+ DBG("protos_commit:\n");
+ if (old)
{
- debug(" %s", x->name);
- p = x->protocol;
- q = p->init(x);
- q->proto_state = PS_DOWN;
- q->core_state = FS_HUNGRY;
- proto_enqueue(&initial_proto_list, q);
- /*
- * HACK ALERT! In case of multiple kernel routing tables,
- * the kernel syncer acts as multiple protocols which cooperate
- * with each other. In order to speed up their initialization,
- * we need to know when we're initializing the last one, hence
- * the startup counter.
- */
- if (!q->disabled)
- p->startup_counter++;
+ WALK_LIST(oc, old->protos)
+ {
+ struct proto *p = oc->proto;
+ struct symbol *sym = cf_find_symbol(oc->name);
+ if (sym && sym->class == SYM_PROTO)
+ {
+ /* Found match, let's check if we can smoothly switch to new configuration */
+ nc = sym->def;
+ if (!force_reconfig
+ && nc->protocol == oc->protocol
+ && nc->preference == oc->preference
+ && nc->disabled == oc->disabled
+ && nc->table->table == oc->table->table
+ && nc->in_filter == oc->in_filter
+ && nc->out_filter == oc->out_filter
+ && p->proto_state != PS_DOWN)
+ {
+ /* Generic attributes match, try converting them and then ask the protocol */
+ p->debug = nc->debug;
+ if (p->proto->reconfigure(p, nc))
+ {
+ DBG("\t%s: same\n", oc->name);
+ p->cf = nc;
+ nc->proto = p;
+ continue;
+ }
+ }
+ /* Unsuccessful, force reconfig */
+ DBG("\t%s: power cycling\n", oc->name);
+ p->cf_new = nc;
+ }
+ else
+ {
+ DBG("\t%s: deleting\n", oc->name);
+ p->cf_new = NULL;
+ }
+ p->reconfiguring = 1;
+ config_add_obstacle(old);
+ proto_rethink_goal(p);
+ }
}
- debug("\n");
+
+ WALK_LIST(nc, new->protos)
+ if (!nc->proto)
+ {
+ DBG("\t%s: adding\n", nc->name);
+ proto_init(nc);
+ }
+ DBG("\tdone\n");
+
+ DBG("Protocol start\n");
+ WALK_LIST_DELSAFE(p, n, initial_proto_list)
+ proto_rethink_goal(p);
}
static void
proto_rethink_goal(struct proto *p)
{
- struct protocol *q = p->proto;
+ struct protocol *q;
+
+ if (p->reconfiguring && p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN)
+ {
+ struct proto_config *nc = p->cf_new;
+ DBG("%s has shut down for reconfiguration\n", p->name);
+ config_del_obstacle(p->cf->global);
+ rem_node(&p->n);
+ mb_free(p);
+ if (!nc)
+ return;
+ p = proto_init(nc); /* FIXME: What about protocol priorities??? */
+ }
+
+ /* Determine what state we want to reach */
+ if (p->disabled || shutting_down || p->reconfiguring)
+ p->core_goal = FS_HUNGRY;
+ else
+ p->core_goal = FS_HAPPY;
if (p->core_state == p->core_goal)
return;
+
+ q = p->proto;
if (p->core_goal == FS_HAPPY) /* Going up */
{
if (p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN)
@@ -242,25 +320,6 @@ proto_rethink_goal(struct proto *p)
}
}
-static void
-proto_set_goal(struct proto *p, unsigned goal)
-{
- if (p->disabled || shutting_down)
- goal = FS_HUNGRY;
- p->core_goal = goal;
- proto_rethink_goal(p);
-}
-
-void
-protos_start(void)
-{
- struct proto *p, *n;
-
- debug("Protocol start\n");
- WALK_LIST_DELSAFE(p, n, initial_proto_list)
- proto_set_goal(p, FS_HAPPY);
-}
-
void
protos_shutdown(void)
{
@@ -271,12 +330,12 @@ protos_shutdown(void)
if (p->core_state != FS_HUNGRY || p->proto_state != PS_DOWN)
{
proto_shutdown_counter++;
- proto_set_goal(p, FS_HUNGRY);
+ proto_rethink_goal(p);
}
WALK_LIST_BACKWARDS_DELSAFE(p, n, proto_list)
{
proto_shutdown_counter++;
- proto_set_goal(p, FS_HUNGRY);
+ proto_rethink_goal(p);
}
}
@@ -329,6 +388,7 @@ static void
proto_fell_down(struct proto *p)
{
DBG("Protocol %s down\n", p->name);
+ rt_unlock_table(p->table);
if (!--proto_shutdown_counter)
protos_shutdown_notify();
proto_rethink_goal(p);
@@ -363,7 +423,10 @@ proto_notify_state(struct proto *p, unsigned ps)
{
case PS_DOWN:
if (cs == FS_HUNGRY) /* Shutdown finished */
- proto_fell_down(p);
+ {
+ proto_fell_down(p);
+ return; /* The protocol might have ceased to exist */
+ }
else if (cs == FS_FLUSHING) /* Still flushing... */
;
else /* Need to start flushing */