iPXE
retry.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25FILE_SECBOOT ( PERMITTED );
26
27#include <stddef.h>
28#include <ipxe/timer.h>
29#include <ipxe/list.h>
30#include <ipxe/process.h>
31#include <ipxe/init.h>
32#include <ipxe/retry.h>
33
34/** @file
35 *
36 * Retry timers
37 *
38 * A retry timer is a binary exponential backoff timer. It can be
39 * used to build automatic retransmission into network protocols.
40 *
41 * This implementation of the timer is designed to satisfy RFC 2988
42 * and therefore be usable as a TCP retransmission timer.
43 *
44 */
45
46/* The theoretical minimum that the algorithm in stop_timer() can
47 * adjust the timeout back down to is seven ticks, so set the minimum
48 * timeout to at least that value for the sake of consistency.
49 */
50#define MIN_TIMEOUT 7
51
52/** List of running timers */
53static LIST_HEAD ( timers );
54
55/**
56 * Start timer with a specified timeout
57 *
58 * @v timer Retry timer
59 * @v timeout Timeout, in ticks
60 *
61 * This starts the timer running with the specified timeout value. If
62 * stop_timer() is not called before the timer expires, the timer will
63 * be stopped and the timer's callback function will be called.
64 */
65void start_timer_fixed ( struct retry_timer *timer, unsigned long timeout ) {
66
67 /* Add to list of running timers (if applicable) */
68 if ( ! timer->running ) {
69 list_add ( &timer->list, &timers );
70 ref_get ( timer->refcnt );
71 timer->running = 1;
72 }
73
74 /* Record start time */
75 timer->start = currticks();
76
77 /* Record timeout */
78 timer->timeout = timeout;
79
80 DBGC2 ( timer, "Timer %p started at time %ld (expires at %ld)\n",
81 timer, timer->start, ( timer->start + timer->timeout ) );
82}
83
84/**
85 * Start timer
86 *
87 * @v timer Retry timer
88 *
89 * This starts the timer running with the current timeout value
90 * (rounded up to the minimum timeout value). If stop_timer() is not
91 * called before the timer expires, the timer will be stopped and the
92 * timer's callback function will be called.
93 */
94void start_timer ( struct retry_timer *timer ) {
95 unsigned long timeout = timer->timeout;
96 unsigned long min;
97
98 /* Calculate minimum timeout */
99 min = ( timer->min ? timer->min : DEFAULT_MIN_TIMEOUT );
100 if ( min < MIN_TIMEOUT )
102
103 /* Ensure timeout is at least the minimum */
104 if ( timeout < min )
105 timeout = min;
106
107 /* Start timer with this timeout */
109}
110
111/**
112 * Stop timer
113 *
114 * @v timer Retry timer
115 *
116 * This stops the timer and updates the timer's timeout value.
117 */
118void stop_timer ( struct retry_timer *timer ) {
119 unsigned long old_timeout = timer->timeout;
120 unsigned long now = currticks();
121 unsigned long runtime;
122
123 /* If timer was already stopped, do nothing */
124 if ( ! timer->running )
125 return;
126
127 list_del ( &timer->list );
128 runtime = ( now - timer->start );
129 timer->running = 0;
130 DBGC2 ( timer, "Timer %p stopped at time %ld (ran for %ld)\n",
131 timer, now, runtime );
132
133 /* Update timer. Variables are:
134 *
135 * r = round-trip time estimate (i.e. runtime)
136 * t = timeout value (i.e. timer->timeout)
137 * s = smoothed round-trip time
138 *
139 * By choice, we set t = 4s, i.e. allow for four times the
140 * normal round-trip time to pass before retransmitting.
141 *
142 * We want to smooth according to s := ( 7 s + r ) / 8
143 *
144 * Since we don't actually store s, this reduces to
145 * t := ( 7 t / 8 ) + ( r / 2 )
146 *
147 */
148 if ( timer->count ) {
149 timer->count--;
150 } else {
151 timer->timeout -= ( timer->timeout >> 3 );
152 timer->timeout += ( runtime >> 1 );
153 if ( timer->timeout != old_timeout ) {
154 DBGC ( timer, "Timer %p timeout updated to %ld\n",
155 timer, timer->timeout );
156 }
157 }
158
159 ref_put ( timer->refcnt );
160}
161
162/**
163 * Handle expired timer
164 *
165 * @v timer Retry timer
166 */
167static void timer_expired ( struct retry_timer *timer ) {
168 struct refcnt *refcnt = timer->refcnt;
169 unsigned long max = ( timer->max ? timer->max : DEFAULT_MAX_TIMEOUT );
170 int fail;
171
172 /* Stop timer without performing RTT calculations */
173 DBGC2 ( timer, "Timer %p stopped at time %ld on expiry\n",
174 timer, currticks() );
175 assert ( timer->running );
176 list_del ( &timer->list );
177 timer->running = 0;
178 timer->count++;
179
180 /* Back off the timeout value */
181 timer->timeout <<= 1;
182 if ( ( fail = ( timer->timeout > max ) ) )
183 timer->timeout = max;
184 DBGC ( timer, "Timer %p timeout backed off to %ld\n",
185 timer, timer->timeout );
186
187 /* Call expiry callback */
188 timer->expired ( timer, fail );
189 /* If refcnt is NULL, then timer may already have been freed */
190
191 ref_put ( refcnt );
192}
193
194/**
195 * Poll the retry timer list
196 *
197 */
198void retry_poll ( void ) {
199 struct retry_timer *timer;
200 unsigned long now = currticks();
201 unsigned long used;
202
203 /* Process at most one timer expiry. We cannot process
204 * multiple expiries in one pass, because one timer expiring
205 * may end up triggering another timer's deletion from the
206 * list.
207 */
208 list_for_each_entry ( timer, &timers, list ) {
209 used = ( now - timer->start );
210 if ( used >= timer->timeout ) {
212 break;
213 }
214 }
215}
216
217/**
218 * Single-step the retry timer list
219 *
220 * @v process Retry timer process
221 */
222static void retry_step ( struct process *process __unused ) {
223 retry_poll();
224}
225
226/** Retry timer process */
227PERMANENT_PROCESS ( retry_process, retry_step );
#define assert(condition)
Assert a condition at run-time.
Definition assert.h:50
#define min(x, y)
Definition ath.h:36
#define max(x, y)
Definition ath.h:41
void timeout(int)
#define __unused
Declare a variable or data structure as unused.
Definition compiler.h:573
#define DBGC2(...)
Definition compiler.h:522
#define DBGC(...)
Definition compiler.h:505
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define FILE_SECBOOT(_status)
Declare a file's UEFI Secure Boot permission status.
Definition compiler.h:926
iPXE timers
Linked lists.
#define list_for_each_entry(pos, head, member)
Iterate over entries in a list.
Definition list.h:432
#define list_del(list)
Delete an entry from a list.
Definition list.h:120
#define LIST_HEAD(list)
Declare a static list head.
Definition list.h:38
#define list_add(new, head)
Add a new entry to the head of a list.
Definition list.h:70
Processes.
#define PERMANENT_PROCESS(name, step)
Define a permanent process.
Definition process.h:194
#define ref_get(refcnt)
Get additional reference to object.
Definition refcnt.h:93
#define ref_put(refcnt)
Drop reference to object.
Definition refcnt.h:107
#define MIN_TIMEOUT
Definition retry.c:50
static void timer_expired(struct retry_timer *timer)
Handle expired timer.
Definition retry.c:167
void start_timer(struct retry_timer *timer)
Start timer.
Definition retry.c:94
void start_timer_fixed(struct retry_timer *timer, unsigned long timeout)
Start timer with a specified timeout.
Definition retry.c:65
void retry_poll(void)
Poll the retry timer list.
Definition retry.c:198
static void retry_step(struct process *process __unused)
Single-step the retry timer list.
Definition retry.c:222
void stop_timer(struct retry_timer *timer)
Stop timer.
Definition retry.c:118
Retry timers.
#define DEFAULT_MAX_TIMEOUT
Default maximum timeout value (in ticks)
Definition retry.h:19
#define DEFAULT_MIN_TIMEOUT
Default minimum timeout value (in ticks)
Definition retry.h:16
A process.
Definition process.h:18
A reference counter.
Definition refcnt.h:27
A retry timer.
Definition retry.h:22
struct list_head list
List of active timers.
Definition retry.h:24
A timer.
Definition timer.h:29
unsigned long currticks(void)
Get current system time in ticks.
Definition timer.c:43