diff options
Diffstat (limited to 'nest')
-rw-r--r-- | nest/proto.c | 155 | ||||
-rw-r--r-- | nest/protocol.h | 7 | ||||
-rw-r--r-- | nest/route.h | 11 | ||||
-rw-r--r-- | nest/rt-table.c | 67 |
4 files changed, 181 insertions, 59 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 */ diff --git a/nest/protocol.h b/nest/protocol.h index 39d1cf5d..72f88b6b 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -41,7 +41,7 @@ struct protocol { void (*preconfig)(struct protocol *, struct config *); /* Just before configuring */ void (*postconfig)(struct proto_config *); /* After configuring each instance */ struct proto * (*init)(struct proto_config *); /* Create new instance */ - int (*reconfigure)(struct proto *, struct proto_config *); /* Try to reconfigure instance */ + int (*reconfigure)(struct proto *, struct proto_config *); /* Try to reconfigure instance, returns success */ void (*dump)(struct proto *); /* Debugging dump */ void (*dump_attrs)(struct rte *); /* Dump protocol-dependent attributes */ int (*start)(struct proto *); /* Start the instance */ @@ -54,8 +54,7 @@ struct protocol { void protos_build(void); void protos_preconfig(struct config *); void protos_postconfig(struct config *); -void protos_commit(struct config *); -void protos_start(void); +void protos_commit(struct config *new, struct config *old, int force_restart); void protos_dump_all(void); void protos_shutdown(void); @@ -92,6 +91,7 @@ struct proto { node n; struct protocol *proto; /* Protocol */ struct proto_config *cf; /* Configuration data */ + struct proto_config *cf_new; /* Configuration we want to switch to after shutdown (NULL=delete) */ pool *pool; /* Pool containing local objects */ struct event *attn; /* "Pay attention" event */ @@ -103,6 +103,7 @@ struct proto { unsigned proto_state; /* Protocol state machine (see below) */ unsigned core_state; /* Core state machine (see below) */ unsigned core_goal; /* State we want to reach (see below) */ + unsigned reconfiguring; /* We're shutting down due to reconfiguration */ bird_clock_t last_state_change; /* Time of last state transition */ /* diff --git a/nest/route.h b/nest/route.h index 903e9b93..da793d6c 100644 --- a/nest/route.h +++ b/nest/route.h @@ -1,7 +1,7 @@ /* * BIRD Internet Routing Daemon -- Routing Table * - * (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. */ @@ -121,6 +121,11 @@ typedef struct rtable { char *name; /* Name of this table */ list hooks; /* List of announcement hooks */ int pipe_busy; /* Pipe loop detection */ + int use_count; /* Number of protocols using this table */ + struct config *deleted; /* Table doesn't exist in current configuration, + * delete as soon as use_count becomes 0 and remove + * obstacle from this routing table. + */ } rtable; typedef struct network { @@ -171,7 +176,9 @@ struct config; void rt_init(void); void rt_preconfig(struct config *); -void rt_commit(struct config *); +void rt_commit(struct config *new, struct config *old); +void rt_lock_table(rtable *); +void rt_unlock_table(rtable *); void rt_setup(pool *, rtable *, char *); static inline net *net_find(rtable *tab, ip_addr addr, unsigned len) { return (net *) fib_find(&tab->fib, &addr, len); } static inline net *net_get(rtable *tab, ip_addr addr, unsigned len) { return (net *) fib_get(&tab->fib, &addr, len); } diff --git a/nest/rt-table.c b/nest/rt-table.c index 401c5f85..b0d1e291 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -1,7 +1,7 @@ /* * BIRD -- Routing Table * - * (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. */ @@ -480,17 +480,68 @@ rt_preconfig(struct config *c) } void -rt_commit(struct config *c) +rt_lock_table(rtable *r) { - struct rtable_config *r; + r->use_count++; +} + +void +rt_unlock_table(rtable *r) +{ + if (!--r->use_count && r->deleted) + { + struct config *conf = r->deleted; + DBG("Deleting routing table %s\n", r->name); + rem_node(&r->n); + fib_free(&r->fib); + mb_free(r); + config_del_obstacle(conf); + } +} + +void +rt_commit(struct config *new, struct config *old) +{ + struct rtable_config *o, *r; - WALK_LIST(r, c->tables) + DBG("rt_commit:\n"); + if (old) { - rtable *t = mb_alloc(rt_table_pool, sizeof(struct rtable)); - rt_setup(rt_table_pool, t, r->name); - add_tail(&routing_tables, &t->n); - r->table = t; + WALK_LIST(o, old->tables) + { + rtable *ot = o->table; + if (!ot->deleted) + { + struct symbol *sym = cf_find_symbol(o->name); + if (sym && sym->class == SYM_TABLE) + { + DBG("\t%s: same\n", o->name); + r = sym->def; + r->table = ot; + ot->name = r->name; + } + else + { + DBG("\t%s: deleted", o->name); + ot->deleted = old; + config_add_obstacle(old); + rt_lock_table(ot); + rt_unlock_table(ot); + } + } + } } + + WALK_LIST(r, new->tables) + if (!r->table) + { + rtable *t = mb_alloc(rt_table_pool, sizeof(struct rtable)); + DBG("\t%s: created\n", r->name); + rt_setup(rt_table_pool, t, r->name); + add_tail(&routing_tables, &t->n); + r->table = t; + } + DBG("\tdone\n"); } /* |