From d6a836f8d673a117fd19136d24e98fa9bbc4b27e Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 8 Dec 2008 12:24:55 +0100 Subject: 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? --- nest/neighbor.c | 2 +- nest/proto.c | 70 +++++++++++++++++++++++++++++++++------------------------ 2 files changed, 42 insertions(+), 30 deletions(-) (limited to 'nest') 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); } } -- cgit v1.2.3