Browse Source

Updated retry timer mechanism to incorporate smoothed RTT estimation.

AoE now uses the retry timer mechanism.
tags/v0.9.3
Michael Brown 18 years ago
parent
commit
48fb6c6dc2
3 changed files with 101 additions and 59 deletions
  1. 10
    11
      src/include/gpxe/retry.h
  2. 28
    1
      src/net/aoe.c
  3. 63
    47
      src/net/retry.c

+ 10
- 11
src/include/gpxe/retry.h View File

9
 
9
 
10
 #include <gpxe/list.h>
10
 #include <gpxe/list.h>
11
 
11
 
12
-/** Effective maximum retry count for exponential backoff calculation */
13
-#define BACKOFF_LIMIT 5
14
-
15
 /** A retry timer */
12
 /** A retry timer */
16
 struct retry_timer {
13
 struct retry_timer {
17
 	/** List of active timers */
14
 	/** List of active timers */
18
 	struct list_head list;
15
 	struct list_head list;
19
-	/** Base timeout (in ticks) */
20
-	unsigned int base;
21
-	/** Retry count */
22
-	unsigned int retries;
23
-	/** Expiry time (in ticks) */
24
-	unsigned long expiry;
16
+	/** Timeout value (in ticks) */
17
+	unsigned long timeout;
18
+	/** Start time (in ticks) */
19
+	unsigned long start;
25
 	/** Timer expired callback
20
 	/** Timer expired callback
26
 	 *
21
 	 *
27
 	 * @v timer	Retry timer
22
 	 * @v timer	Retry timer
23
+	 * @v fail	Failure indicator
24
+	 *
25
+	 * The timer will already be stopped when this method is
26
+	 * called.  The failure indicator will be True if the retry
27
+	 * timeout has already exceeded @c MAX_TIMEOUT.
28
 	 */
28
 	 */
29
-	void ( * expired ) ( struct retry_timer *timer );
29
+	void ( * expired ) ( struct retry_timer *timer, int over );
30
 };
30
 };
31
 
31
 
32
 extern void start_timer ( struct retry_timer *timer );
32
 extern void start_timer ( struct retry_timer *timer );
33
-extern void reset_timer ( struct retry_timer *timer );
34
 extern void stop_timer ( struct retry_timer *timer );
33
 extern void stop_timer ( struct retry_timer *timer );
35
 
34
 
36
 #endif /* _GPXE_RETRY_H */
35
 #endif /* _GPXE_RETRY_H */

+ 28
- 1
src/net/aoe.c View File

115
 			 aoe->command_offset, data_out_len );
115
 			 aoe->command_offset, data_out_len );
116
 
116
 
117
 	/* Send packet */
117
 	/* Send packet */
118
+	start_timer ( &aoe->timer );
118
 	return net_transmit_via ( pkb, aoe->netdev );
119
 	return net_transmit_via ( pkb, aoe->netdev );
119
 }
120
 }
120
 
121
 
122
+/**
123
+ * Handle AoE retry timer expiry
124
+ *
125
+ * @v timer		AoE retry timer
126
+ * @v fail		Failure indicator
127
+ */
128
+static void aoe_timer_expired ( struct retry_timer *timer, int fail ) {
129
+	struct aoe_session *aoe =
130
+		container_of ( timer, struct aoe_session, timer );
131
+
132
+	if ( fail ) {
133
+		aoe_done ( aoe, -ETIMEDOUT );
134
+	} else {
135
+		aoe_send_command ( aoe );
136
+	}
137
+}
138
+
121
 /**
139
 /**
122
  * Handle AoE response
140
  * Handle AoE response
123
  *
141
  *
134
 	unsigned int data_len;
152
 	unsigned int data_len;
135
 	
153
 	
136
 	/* Sanity check */
154
 	/* Sanity check */
137
-	if ( len < ( sizeof ( *aoehdr ) + sizeof ( *aoecmd ) ) )
155
+	if ( len < ( sizeof ( *aoehdr ) + sizeof ( *aoecmd ) ) ) {
156
+		/* Ignore packet; allow timer to trigger retransmit */
138
 		return -EINVAL;
157
 		return -EINVAL;
158
+	}
139
 	rx_data_len = ( len - sizeof ( *aoehdr ) - sizeof ( *aoecmd ) );
159
 	rx_data_len = ( len - sizeof ( *aoehdr ) - sizeof ( *aoecmd ) );
140
 
160
 
161
+	/* Stop retry timer.  After this point, every code path must
162
+	 * either terminate the AoE operation via aoe_done(), or
163
+	 * transmit a new packet.
164
+	 */
165
+	stop_timer ( &aoe->timer );
166
+
141
 	/* Check for fatal errors */
167
 	/* Check for fatal errors */
142
 	if ( aoehdr->ver_flags & AOE_FL_ERROR ) {
168
 	if ( aoehdr->ver_flags & AOE_FL_ERROR ) {
143
 		aoe_done ( aoe, -EIO );
169
 		aoe_done ( aoe, -EIO );
268
  */
294
  */
269
 void aoe_open ( struct aoe_session *aoe ) {
295
 void aoe_open ( struct aoe_session *aoe ) {
270
 	memset ( aoe->target, 0xff, sizeof ( aoe->target ) );
296
 	memset ( aoe->target, 0xff, sizeof ( aoe->target ) );
297
+	aoe->timer.expired = aoe_timer_expired;
271
 	list_add ( &aoe->list, &aoe_sessions );
298
 	list_add ( &aoe->list, &aoe_sessions );
272
 }
299
 }
273
 
300
 

+ 63
- 47
src/net/retry.c View File

27
  *
27
  *
28
  * Retry timers
28
  * Retry timers
29
  *
29
  *
30
- * A retry timer is a truncated binary exponential backoff timer.  It
31
- * can be used to build automatic retransmission into network
32
- * protocols.
30
+ * A retry timer is a binary exponential backoff timer.  It can be
31
+ * used to build automatic retransmission into network protocols.
33
  */
32
  */
34
 
33
 
35
-/** List of running timers */
36
-static LIST_HEAD ( timers );
34
+/** Default timeout value */
35
+#define MIN_TIMEOUT ( TICKS_PER_SEC / 4 )
37
 
36
 
38
-/**
39
- * Reload timer
40
- *
41
- * @v timer		Retry timer
42
- *
43
- * This reloads the timer with a new expiry time.  The expiry time
44
- * will be the timer's base timeout value, shifted left by the number
45
- * of retries (i.e. the number of timer expiries since the last timer
46
- * reset).
47
- */
48
-static void reload_timer ( struct retry_timer *timer ) {
49
-	unsigned int exp;
50
-
51
-	exp = timer->retries;
52
-	if ( exp > BACKOFF_LIMIT )
53
-		exp = BACKOFF_LIMIT;
54
-	timer->expiry = currticks() + ( timer->base << exp );
55
-}
37
+/** Limit after which the timeout will be deemed permanent */
38
+#define MAX_TIMEOUT ( 10 * TICKS_PER_SEC )
56
 
39
 
57
-/**
58
- * Reset timer
59
- *
60
- * @v timer		Retry timer
61
- *
62
- * This resets the timer, i.e. clears its retry count and starts it
63
- * running with its base timeout value.
64
- *
65
- * Note that it is explicitly permitted to call reset_timer() on an
66
- * inactive timer.
40
+/* The theoretical minimum that the algorithm in stop_timer() can
41
+ * adjust the timeout back down to is seven ticks, so set the minimum
42
+ * timeout to at least that value for the sake of consistency.
67
  */
43
  */
68
-void reset_timer ( struct retry_timer *timer ) {
69
-	timer->retries = 0;
70
-	reload_timer ( timer );
71
-}
44
+#if MIN_TIMEOUT < 7
45
+#undef MIN_TIMEOUT
46
+#define MIN_TIMEOUT 7
47
+#endif
48
+
49
+/** List of running timers */
50
+static LIST_HEAD ( timers );
72
 
51
 
73
 /**
52
 /**
74
  * Start timer
53
  * Start timer
75
  *
54
  *
76
  * @v timer		Retry timer
55
  * @v timer		Retry timer
77
  *
56
  *
78
- * This resets the timer and starts it running (i.e. adds it to the
79
- * list of running timers).  The retry_timer::base and
80
- * retry_timer::callback fields must have been filled in.
57
+ * This starts the timer running with the current timeout value.  If
58
+ * stop_timer() is not called before the timer expires, the timer will
59
+ * be stopped and the timer's callback function will be called.
81
  */
60
  */
82
 void start_timer ( struct retry_timer *timer ) {
61
 void start_timer ( struct retry_timer *timer ) {
83
 	list_add ( &timer->list, &timers );
62
 	list_add ( &timer->list, &timers );
84
-	reset_timer ( timer );
63
+	timer->start = currticks();
64
+	if ( timer->timeout < MIN_TIMEOUT )
65
+		timer->timeout = MIN_TIMEOUT;
85
 }
66
 }
86
 
67
 
87
 /**
68
 /**
89
  *
70
  *
90
  * @v timer		Retry timer
71
  * @v timer		Retry timer
91
  *
72
  *
92
- * This stops the timer (i.e. removes it from the list of running
93
- * timers).
73
+ * This stops the timer and updates the timer's timeout value.
94
  */
74
  */
95
 void stop_timer ( struct retry_timer *timer ) {
75
 void stop_timer ( struct retry_timer *timer ) {
76
+	unsigned long old_timeout = timer->timeout;
77
+	unsigned long runtime;
78
+
96
 	list_del ( &timer->list );
79
 	list_del ( &timer->list );
80
+	runtime = currticks() - timer->start;
81
+
82
+	/* Update timer.  Variables are:
83
+	 *
84
+	 *   r = round-trip time estimate (i.e. runtime)
85
+	 *   t = timeout value (i.e. timer->timeout)
86
+	 *   s = smoothed round-trip time
87
+	 *
88
+	 * By choice, we set t = 4s, i.e. allow for four times the
89
+	 * normal round-trip time to pass before retransmitting.
90
+	 *
91
+	 * We want to smooth according to s := ( 7 s + r ) / 8
92
+	 *
93
+	 * Since we don't actually store s, this reduces to
94
+	 * t := ( 7 t / 8 ) + ( r / 2 )
95
+	 *
96
+	 */
97
+	timer->timeout -= ( timer->timeout >> 3 );
98
+	timer->timeout += ( runtime >> 1 );
99
+	if ( timer->timeout != old_timeout ) {
100
+		DBG ( "Timer updated to %dms\n",
101
+		      ( ( 1000 * timer->timeout ) / TICKS_PER_SEC ) );
102
+	}
97
 }
103
 }
98
 
104
 
99
 /**
105
 /**
105
 	struct retry_timer *timer;
111
 	struct retry_timer *timer;
106
 	struct retry_timer *tmp;
112
 	struct retry_timer *tmp;
107
 	unsigned long now = currticks();
113
 	unsigned long now = currticks();
114
+	unsigned long used;
115
+	int fail;
108
 
116
 
109
 	list_for_each_entry_safe ( timer, tmp, &timers, list ) {
117
 	list_for_each_entry_safe ( timer, tmp, &timers, list ) {
110
-		if ( timer->expiry <= now ) {
111
-			timer->retries++;
112
-			reload_timer ( timer );
113
-			timer->expired ( timer );
118
+		used = ( now - timer->start );
119
+		if ( used >= timer->timeout ) {
120
+			/* Stop timer without performing RTT calculations */
121
+			list_del ( &timer->list );
122
+			/* Back off the timeout value */
123
+			timer->timeout <<= 1;
124
+			if ( ( fail = ( timer->timeout > MAX_TIMEOUT ) ) )
125
+				timer->timeout = MAX_TIMEOUT;
126
+			DBG ( "Timer backed off to %dms\n",
127
+			      ( ( 1000 * timer->timeout ) / TICKS_PER_SEC ) );
128
+			/* Call expiry callback */
129
+			timer->expired ( timer, fail );
114
 		}
130
 		}
115
 	}
131
 	}
116
 
132
 

Loading…
Cancel
Save