Aaron Durbin (adurbin@google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/3191
-gerrit
commit 38e17f27794e25e517a7df798c05b9a8c582b97d Author: Aaron Durbin adurbin@chromium.org Date: Fri May 3 12:53:22 2013 -0500
boot state: add notion of blocking transitions
There are 3 phases of a state: entry, running, and exit. During the entry and exit phases callbacks can be run. In order to prevent states from transitioning the introduction of a completion deadline is made. As the callbacks are currently called synchronously by the main thread the result is a nop in comparison to not having completion deadlines for callbacks.
Change-Id: I188b262873df4469163000c979293c442e21cb31 Signed-off-by: Aaron Durbin adurbin@chromium.org --- src/include/bootstate.h | 74 +++++++++++++++++++++++++++++++++++++++++++------ src/lib/hardwaremain.c | 67 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 123 insertions(+), 18 deletions(-)
diff --git a/src/include/bootstate.h b/src/include/bootstate.h index f732d1e..bfb3481 100644 --- a/src/include/bootstate.h +++ b/src/include/bootstate.h @@ -110,11 +110,27 @@ typedef enum { BS_ON_EXIT } boot_state_sequence_t;
+/* The boot_state_completion sturcture is used inernally to the state machine + * to indicate that a call back needs to complete by the provided sequence + * in the boot state machine. */ +struct boot_state_completion { + boot_state_t state; + boot_state_sequence_t when; +}; + +/* The BS_RAMSTAGE_EXIT is a special purpose encoding of a state to indicate + * when a callback should be completed. The completion state of + * BS_RAMSTAGE_EXIT indicates that a callback needs to be completed prior + * to leaving ramstage. i.e. BS_OS_RESUME and BS_PAYLOAD_BOOT. */ +#define BS_RAMSTAGE_EXIT -1 + struct boot_state_callback { void *arg; void (*callback)(void *arg); - /* For use internal to the boot state machine. */ + /* Everything below this comment should be considered private and + * controlled by the boot state machine. */ struct boot_state_callback *next; + struct boot_state_completion deadline; #if BOOT_STATE_DEBUG const char *location; #endif @@ -130,14 +146,18 @@ struct boot_state_callback { #define INIT_BOOT_STATE_CALLBACK_DEBUG(bscb_) #endif
-#define BOOT_STATE_CALLBACK_INIT(func_, arg_) \ - { \ - .arg = arg_, \ - .callback = func_, \ - .next = NULL, \ - BOOT_STATE_CALLBACK_INIT_DEBUG \ +#define BOOT_STATE_CALLBACK_INIT_DEADLINE(func_, arg_, state_, seq_) \ + { \ + .arg = arg_, \ + .callback = func_, \ + .next = NULL, \ + .deadline = { state_, seq_ }, \ + BOOT_STATE_CALLBACK_INIT_DEBUG \ }
+#define BOOT_STATE_CALLBACK_INIT(func_, arg_) \ + BOOT_STATE_CALLBACK_INIT_DEADLINE(func_, arg_, -2, -2) + #define BOOT_STATE_CALLBACK(name_, func_, arg_) \ struct boot_state_callback name_ = BOOT_STATE_CALLBACK_INIT(func_, arg_)
@@ -147,6 +167,32 @@ struct boot_state_callback { bscb_->callback = func_; \ bscb_->arg = arg_
+/* The boot_state_complete_by_(entry|exit) functions can be called on + * a boot state callback prior to being scheduled. The completion deadline + * is just like the start state and sequence. The boot state machine will + * not proceed past the state until the callback is complete. */ +static inline void +boot_state_complete_by_entry(struct boot_state_callback *bscb, + boot_state_t state) +{ + bscb->deadline.state = state; + bscb->deadline.when = BS_ON_ENTRY; +} + +static inline void +boot_state_complete_by_exit(struct boot_state_callback *bscb, + boot_state_t state) +{ + bscb->deadline.state = state; + bscb->deadline.when = BS_ON_EXIT; +} + +static inline void +boot_state_complete_by_ramstage_exit(struct boot_state_callback *bscb) +{ + bscb->deadline.state = BS_RAMSTAGE_EXIT; +} + /* The following 2 functions schedule a callback to be called on entry/exit * to a given state. Note that thare are no ordering guarantees between the * individual callbacks on a given state. 0 is returned on success < 0 on @@ -173,11 +219,21 @@ struct boot_state_init_entry { #define BOOT_STATE_INIT_ENTRIES(name_) \ static struct boot_state_init_entry name_[] BOOT_STATE_INIT_ATTR
-#define BOOT_STATE_INIT_ENTRY(state_, when_, func_, arg_) \ +#define BOOT_STATE_INIT_ENTRY_DEADLINE(state_, when_, func_, arg_, end_state_, end_when_) \ { \ .state = state_, \ .when = when_, \ - .bscb = BOOT_STATE_CALLBACK_INIT(func_, arg_), \ + .bscb = BOOT_STATE_CALLBACK_INIT_DEADLINE(func_, arg_, end_state_, end_when_), \ }
+/* By default the callback needs to run to completion at the same step the + * callback is called. */ +#define BOOT_STATE_INIT_ENTRY(state_, when_, func_, arg_) \ + BOOT_STATE_INIT_ENTRY_DEADLINE(state_, when_, func_, arg_,\ + state_, when_) + +#define BOOT_STATE_INIT_ENTRY_RAMSTAGE_EXIT(state_, when_, func_, arg_) \ + BOOT_STATE_INIT_ENTRY_DEADLINE(state_, when_, func_, arg_, \ + BS_RAMSTAGE_EXIT, 0) + #endif /* BOOTSTATE_H */ diff --git a/src/lib/hardwaremain.c b/src/lib/hardwaremain.c index 8e5481e..a4d2fc9 100644 --- a/src/lib/hardwaremain.c +++ b/src/lib/hardwaremain.c @@ -72,10 +72,12 @@ struct boot_state_times { struct mono_time samples[MAX_TIME_SAMPLES]; };
+ struct boot_state { const char *name; boot_state_t id; struct boot_state_callback *seq_callbacks[2]; + int seq_blocked[2]; boot_state_t (*run_state)(void *arg); void *arg; int complete : 1; @@ -90,6 +92,7 @@ struct boot_state { .name = #state_, \ .id = state_, \ .seq_callbacks = { NULL, NULL }, \ + .seq_blocked = { 0, 0 }, \ .run_state = run_func_, \ .arg = NULL, \ .complete = 0, \ @@ -296,22 +299,41 @@ static void bs_run_timers(int drain) static void bs_run_timers(int drain) {} #endif
+static void bs_call_callback(struct boot_state_callback *bscb) +{ + bscb->callback(bscb->arg); + + if (bscb->deadline.state == BS_RAMSTAGE_EXIT) { + boot_states[BS_OS_RESUME].seq_blocked[BS_ON_ENTRY]--; + boot_states[BS_PAYLOAD_BOOT].seq_blocked[BS_ON_ENTRY]--; + return; + } + boot_states[bscb->deadline.state].seq_blocked[bscb->deadline.when]--; +} + static void bs_call_callbacks(struct boot_state *state, boot_state_sequence_t seq) { - while (state->seq_callbacks[seq] != NULL) { - struct boot_state_callback *bscb; + while (state->seq_blocked[seq] != 0) { + if (state->seq_callbacks[seq] != NULL) { + struct boot_state_callback *bscb;
- /* Remove the first callback. */ - bscb = state->seq_callbacks[seq]; - state->seq_callbacks[seq] = bscb->next; - bscb->next = NULL; + /* Remove the first callback. */ + bscb = state->seq_callbacks[seq]; + state->seq_callbacks[seq] = bscb->next; + bscb->next = NULL;
#if BOOT_STATE_DEBUG - printk(BS_DEBUG_LVL, "BS: callback (%p) @ %s.\n", - bscb, bscb->location); + printk(BS_DEBUG_LVL, "BS: callback (%p) @ %s.\n", + bscb, bscb->location); #endif - bscb->callback(bscb->arg); + bs_call_callback(bscb); + continue; + } + + /* Run the timers to make forward progress if a timeout callback + * is blocking this state transition. */ + bs_run_timers(0); } }
@@ -355,6 +377,31 @@ static void bs_walk_state_machine(boot_state_t current_state_id) } }
+static void boot_state_sched_completion(struct boot_state *start_state, + struct boot_state_callback *bscb, + boot_state_sequence_t seq) +{ + if (bscb->deadline.state == BS_RAMSTAGE_EXIT) { + boot_states[BS_OS_RESUME].seq_blocked[BS_ON_ENTRY]++; + boot_states[BS_PAYLOAD_BOOT].seq_blocked[BS_ON_ENTRY]++; + return; + } + + /* Handle unitialized completion deadline or a completion deadline + * for a state beforeteh current scheduled one. In both cases the + * deadline is the start state and sequence. */ + if (bscb->deadline.state < start_state->id || + (bscb->deadline.state == start_state->id && + bscb->deadline.when < seq)) { + bscb->deadline.state = start_state->id; + bscb->deadline.when = seq; + start_state->seq_blocked[seq]++; + return; + } + + boot_states[bscb->deadline.state].seq_blocked[bscb->deadline.when]++; +} + static int boot_state_sched_callback(struct boot_state *state, struct boot_state_callback *bscb, boot_state_sequence_t seq) @@ -370,6 +417,8 @@ static int boot_state_sched_callback(struct boot_state *state, bscb->next = state->seq_callbacks[seq]; state->seq_callbacks[seq] = bscb;
+ boot_state_sched_completion(state, bscb, seq); + return 0; }