diff --git a/src/include/gpxe/retry.h b/src/include/gpxe/retry.h new file mode 100644 index 00000000..8a9e2cf6 --- /dev/null +++ b/src/include/gpxe/retry.h @@ -0,0 +1,36 @@ +#ifndef _GPXE_RETRY_H +#define _GPXE_RETRY_H + +/** @file + * + * Retry timers + * + */ + +#include + +/** Effective maximum retry count for exponential backoff calculation */ +#define BACKOFF_LIMIT 5 + +/** A retry timer */ +struct retry_timer { + /** List of active timers */ + struct list_head list; + /** Base timeout (in ticks) */ + unsigned int base; + /** Retry count */ + unsigned int retries; + /** Expiry time (in ticks) */ + unsigned long expiry; + /** Timer expired callback + * + * @v timer Retry timer + */ + void ( * expired ) ( struct retry_timer *timer ); +}; + +extern void start_timer ( struct retry_timer *timer ); +extern void reset_timer ( struct retry_timer *timer ); +extern void stop_timer ( struct retry_timer *timer ); + +#endif /* _GPXE_RETRY_H */ diff --git a/src/net/retry.c b/src/net/retry.c new file mode 100644 index 00000000..f00c178e --- /dev/null +++ b/src/net/retry.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2006 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +/** @file + * + * Retry timers + * + * A retry timer is a truncated binary exponential backoff timer. It + * can be used to build automatic retransmission into network + * protocols. + */ + +/** List of running timers */ +static LIST_HEAD ( timers ); + +/** + * Reload timer + * + * @v timer Retry timer + * + * This reloads the timer with a new expiry time. The expiry time + * will be the timer's base timeout value, shifted left by the number + * of retries (i.e. the number of timer expiries since the last timer + * reset). + */ +static void reload_timer ( struct retry_timer *timer ) { + unsigned int exp; + + exp = timer->retries; + if ( exp > BACKOFF_LIMIT ) + exp = BACKOFF_LIMIT; + timer->expiry = currticks() + ( timer->base << exp ); +} + +/** + * Reset timer + * + * @v timer Retry timer + * + * This resets the timer, i.e. clears its retry count and starts it + * running with its base timeout value. + * + * Note that it is explicitly permitted to call reset_timer() on an + * inactive timer. + */ +void reset_timer ( struct retry_timer *timer ) { + timer->retries = 0; + reload_timer ( timer ); +} + +/** + * Start timer + * + * @v timer Retry timer + * + * This resets the timer and starts it running (i.e. adds it to the + * list of running timers). The retry_timer::base and + * retry_timer::callback fields must have been filled in. + */ +void start_timer ( struct retry_timer *timer ) { + list_add ( &timer->list, &timers ); + reset_timer ( timer ); +} + +/** + * Stop timer + * + * @v timer Retry timer + * + * This stops the timer (i.e. removes it from the list of running + * timers). + */ +void stop_timer ( struct retry_timer *timer ) { + list_del ( &timer->list ); +} + +/** + * Single-step the retry timer list + * + * @v process Retry timer process + */ +static void retry_step ( struct process *process ) { + struct retry_timer *timer; + struct retry_timer *tmp; + unsigned long now = currticks(); + + list_for_each_entry_safe ( timer, tmp, &timers, list ) { + if ( timer->expiry >= now ) { + timer->retries++; + reload_timer ( timer ); + timer->expired ( timer ); + } + } + + schedule ( process ); +} + +/** Retry timer process */ +static struct process retry_process = { + .step = retry_step, +}; + +/** Initialise the retry timer module */ +static void init_retry ( void ) { + schedule ( &retry_process ); +} + +INIT_FN ( INIT_PROCESS, init_retry, NULL, NULL );