summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOndrej Zajicek <santiago@crfreenet.org>2008-12-08 12:24:55 +0100
committerOndrej Zajicek <santiago@crfreenet.org>2008-12-08 12:24:55 +0100
commitd6a836f8d673a117fd19136d24e98fa9bbc4b27e (patch)
tree5bb42645fe4dffef1b0495cb1fb83edf9022ce68
parentfbde6c39084637c2f3f4d31261a44dbf367958d1 (diff)
downloadbird-d6a836f8d673a117fd19136d24e98fa9bbc4b27e.tar
bird-d6a836f8d673a117fd19136d24e98fa9bbc4b27e.zip
Fixes core state machine.
The core state machine was broken - it didn't free resources in START -> DOWN transition and might freed resources after UP -> STOP transition before protocol turned down. It leads to deadlock on olock acquisition when lock was not freed during previous stop. The current behavior is that resources, allocated during DOWN -> * transition, are freed in * -> DOWN transition, and flushing (scheduled in UP -> *) just counteract feeding (scheduled in * -> UP). Protocol fell down when both flushing is done (if needed) and protocol reports DOWN. BTW, is thera a reason why neighbour cache item acquired by protocol is not tracked by resource mechanism?
-rw-r--r--nest/neighbor.c2
-rw-r--r--nest/proto.c70
2 files changed, 42 insertions, 30 deletions
diff --git a/nest/neighbor.c b/nest/neighbor.c
index c6e2f73..2c5af6a 100644
--- a/nest/neighbor.c
+++ b/nest/neighbor.c
@@ -254,7 +254,7 @@ neigh_if_down(struct iface *i)
static inline void
neigh_prune_one(neighbor *n)
{
- if (n->proto->core_state != FS_FLUSHING)
+ if (n->proto->proto_state != PS_DOWN)
return;
rem_node(&n->n);
if (n->iface)
diff --git a/nest/proto.c b/nest/proto.c
index 6c89f7e..2e1eb8f 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -552,6 +552,30 @@ proto_feed(void *P)
proto_feed_more(P);
}
+static void
+proto_schedule_flush(struct proto *p)
+{
+ /* Need to abort feeding */
+ if (p->core_state == FS_FEEDING)
+ rt_feed_baby_abort(p);
+
+ DBG("%s: Scheduling flush\n", p->name);
+ p->core_state = FS_FLUSHING;
+ proto_relink(p);
+ proto_flush_hooks(p);
+ ev_schedule(proto_flush_event);
+}
+
+static void
+proto_schedule_feed(struct proto *p)
+{
+ DBG("%s: Scheduling meal\n", p->name);
+ p->core_state = FS_FEEDING;
+ proto_relink(p);
+ p->attn->hook = proto_feed;
+ ev_schedule(p->attn);
+}
+
/**
* proto_notify_state - notify core about protocol state change
* @p: protocol the state of which has changed
@@ -562,7 +586,9 @@ proto_feed(void *P)
* it should immediately notify the core about the change by calling
* proto_notify_state() which will write the new state to the &proto
* structure and take all the actions necessary to adapt to the new
- * state.
+ * state. State change to PS_DOWN immediately frees resources of protocol
+ * and might execute start callback of protocol; therefore,
+ * it should be used at tail positions of protocol callbacks.
*/
void
proto_notify_state(struct proto *p, unsigned ps)
@@ -574,23 +600,23 @@ proto_notify_state(struct proto *p, unsigned ps)
if (ops == ps)
return;
+ p->proto_state = ps;
+
switch (ps)
{
case PS_DOWN:
+ neigh_prune(); // FIXME convert neighbors to resource?
+ rfree(p->pool);
+ p->pool = NULL;
+
if (cs == FS_HUNGRY) /* Shutdown finished */
{
- p->proto_state = ps;
proto_fell_down(p);
return; /* The protocol might have ceased to exist */
}
- else if (cs == FS_FLUSHING) /* Still flushing... */
- ;
- else
- {
- if (cs == FS_FEEDING) /* Need to abort feeding */
- rt_feed_baby_abort(p);
- goto schedule_flush; /* Need to start flushing */
- }
+ /* Otherwise, we have something to flush... */
+ else if (cs != FS_FLUSHING)
+ proto_schedule_flush(p);
break;
case PS_START:
ASSERT(ops == PS_DOWN);
@@ -599,27 +625,15 @@ proto_notify_state(struct proto *p, unsigned ps)
case PS_UP:
ASSERT(ops == PS_DOWN || ops == PS_START);
ASSERT(cs == FS_HUNGRY);
- DBG("%s: Scheduling meal\n", p->name);
- cs = FS_FEEDING;
- p->attn->hook = proto_feed;
- ev_schedule(p->attn);
+ proto_schedule_feed(p);
break;
case PS_STOP:
- if (ops != PS_DOWN)
- {
- schedule_flush:
- DBG("%s: Scheduling flush\n", p->name);
- proto_flush_hooks(p);
- cs = FS_FLUSHING;
- ev_schedule(proto_flush_event);
- }
+ if ((cs = FS_FEEDING) || (cs == FS_HAPPY))
+ proto_schedule_flush(p);
break;
default:
bug("Invalid state transition for %s from %s/%s to */%s", p->name, c_states[cs], p_states[ops], p_states[ps]);
}
- p->proto_state = ps;
- p->core_state = cs;
- proto_relink(p);
}
static void
@@ -628,15 +642,13 @@ proto_flush_all(void *unused UNUSED)
struct proto *p;
rt_prune_all();
- neigh_prune();
while ((p = HEAD(flush_proto_list))->n.next)
{
DBG("Flushing protocol %s\n", p->name);
- rfree(p->pool);
- p->pool = NULL;
p->core_state = FS_HUNGRY;
proto_relink(p);
- proto_fell_down(p);
+ if (p->proto_state == PS_DOWN)
+ proto_fell_down(p);
}
}