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,28 +9,27 @@
9 9
 
10 10
 #include <gpxe/list.h>
11 11
 
12
-/** Effective maximum retry count for exponential backoff calculation */
13
-#define BACKOFF_LIMIT 5
14
-
15 12
 /** A retry timer */
16 13
 struct retry_timer {
17 14
 	/** List of active timers */
18 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 20
 	/** Timer expired callback
26 21
 	 *
27 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 32
 extern void start_timer ( struct retry_timer *timer );
33
-extern void reset_timer ( struct retry_timer *timer );
34 33
 extern void stop_timer ( struct retry_timer *timer );
35 34
 
36 35
 #endif /* _GPXE_RETRY_H */

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

@@ -115,9 +115,27 @@ static int aoe_send_command ( struct aoe_session *aoe ) {
115 115
 			 aoe->command_offset, data_out_len );
116 116
 
117 117
 	/* Send packet */
118
+	start_timer ( &aoe->timer );
118 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 140
  * Handle AoE response
123 141
  *
@@ -134,10 +152,18 @@ static int aoe_rx_response ( struct aoe_session *aoe, struct aoehdr *aoehdr,
134 152
 	unsigned int data_len;
135 153
 	
136 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 157
 		return -EINVAL;
158
+	}
139 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 167
 	/* Check for fatal errors */
142 168
 	if ( aoehdr->ver_flags & AOE_FL_ERROR ) {
143 169
 		aoe_done ( aoe, -EIO );
@@ -268,6 +294,7 @@ NET_PROTOCOL ( aoe_protocol );
268 294
  */
269 295
 void aoe_open ( struct aoe_session *aoe ) {
270 296
 	memset ( aoe->target, 0xff, sizeof ( aoe->target ) );
297
+	aoe->timer.expired = aoe_timer_expired;
271 298
 	list_add ( &aoe->list, &aoe_sessions );
272 299
 }
273 300
 

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

@@ -27,61 +27,42 @@
27 27
  *
28 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 53
  * Start timer
75 54
  *
76 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 61
 void start_timer ( struct retry_timer *timer ) {
83 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,11 +70,36 @@ void start_timer ( struct retry_timer *timer ) {
89 70
  *
90 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 75
 void stop_timer ( struct retry_timer *timer ) {
76
+	unsigned long old_timeout = timer->timeout;
77
+	unsigned long runtime;
78
+
96 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,12 +111,22 @@ static void retry_step ( struct process *process ) {
105 111
 	struct retry_timer *timer;
106 112
 	struct retry_timer *tmp;
107 113
 	unsigned long now = currticks();
114
+	unsigned long used;
115
+	int fail;
108 116
 
109 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