]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/net/bsd_tcpip/v2_0/src/ecos/timeout.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / net / bsd_tcpip / v2_0 / src / ecos / timeout.c
1 //==========================================================================
2 //
3 //      src/ecos/timeout.c
4 //
5 //==========================================================================
6 //####BSDCOPYRIGHTBEGIN####
7 //
8 // -------------------------------------------
9 //
10 // Portions of this software may have been derived from OpenBSD, 
11 // FreeBSD or other sources, and are covered by the appropriate
12 // copyright disclaimers included herein.
13 //
14 // Portions created by Red Hat are
15 // Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
16 //
17 // -------------------------------------------
18 //
19 //####BSDCOPYRIGHTEND####
20 //==========================================================================
21
22 //==========================================================================
23 //
24 //        lib/timeout.c
25 //
26 //        timeout support
27 //
28 //==========================================================================
29 //####BSDCOPYRIGHTBEGIN####
30 //
31 // -------------------------------------------
32 //
33 // Portions of this software may have been derived from OpenBSD or other sources,
34 // and are covered by the appropriate copyright disclaimers included herein.
35 //
36 // -------------------------------------------
37 //
38 //####BSDCOPYRIGHTEND####
39 //==========================================================================
40 //#####DESCRIPTIONBEGIN####
41 //
42 // Author(s):     gthomas, hmt
43 // Contributors:  gthomas, hmt
44 // Date:          1999-02-05
45 // Description:   Simple timeout functions
46 //####DESCRIPTIONEND####
47
48 #include <sys/param.h>
49 #include <pkgconf/net.h>
50 #include <cyg/kernel/kapi.h>
51 #include <cyg/infra/cyg_ass.h>
52
53 // Timeout support
54
55 void alarm_timeout_init(void);
56
57 #ifndef NTIMEOUTS
58 #define NTIMEOUTS 8
59 #endif
60 static timeout_entry _timeouts[NTIMEOUTS];
61 static timeout_entry *timeouts = (timeout_entry *)NULL;
62 static cyg_handle_t timeout_alarm_handle;
63 static cyg_alarm timeout_alarm;
64 static cyg_int32 last_delta;
65 static cyg_tick_count_t last_set_time;
66
67 #define STACK_SIZE CYGNUM_NET_FAST_THREAD_STACKSIZE
68
69 static char alarm_stack[STACK_SIZE];
70 static cyg_thread alarm_thread_data;
71 static cyg_handle_t alarm_thread_handle;
72
73 static cyg_flag_t alarm_flag;  
74
75 // ------------------------------------------------------------------------
76 // This routine exists so that this module can synchronize:
77 extern cyg_uint32 cyg_splinternal(void);
78
79 #ifdef TIMEOUT_DEBUG
80 static void
81 _show_timeouts(void)
82 {
83     timeout_entry *f;
84     for (f = timeouts;  f;  f = f->next) {
85         diag_printf("%p: delta: %d, fun: %p, param: %p\n", f, f->delta, f->fun, f->arg);
86     }
87 }
88 #endif // TIMEOUT_DEBUG
89
90 // ------------------------------------------------------------------------
91 // CALLBACK FUNCTION
92 // Called from the thread, this runs the alarm callbacks.
93 // Locking is already in place when this is called.
94 static void
95 do_timeout(void)
96 {
97     cyg_int32 min_delta;
98     timeout_entry *e, *e_next;
99
100     CYG_ASSERT( 0 < last_delta, "last_delta underflow" );
101
102     min_delta = last_delta; // local copy
103     last_delta = -1; // flag recursive call underway
104
105     e = timeouts;
106     while (e) {
107         e_next = e->next;  // Because this can change during processing
108         if (e->delta) {
109 #ifdef TIMEOUT_DEBUG
110                 if ( !(e->delta >= min_delta)) {
111                     diag_printf("Bad delta in timeout: %p, delta: %d, min: %d, last: %ld\n", e, e->delta, min_delta, last_set_time);
112                     _show_timeouts();
113                 }                
114 #endif
115 // Note: this _can_ happen if timeouts are scheduled before the clock starts!
116 //            CYG_ASSERT( e->delta >= min_delta, "e->delta underflow" );
117             e->delta -= min_delta;
118             if (e->delta <= 0) { // Defensive
119                 // Time for this item to 'fire'
120                 timeout_fun *fun = e->fun;
121                 void *arg = e->arg;
122                 // Call it *after* cleansing the record
123 //                diag_printf("%s(%p, %p, %p)\n", __FUNCTION__, e, e->fun, e->arg);
124                 e->flags &= ~CALLOUT_PENDING;
125                 e->delta = 0;
126                 if (e->next) {
127                     e->next->prev = e->prev;
128                 }
129                 if (e->prev) {
130                     e->prev->next = e->next;
131                 } else {
132                     timeouts = e->next;
133                 }
134                 (*fun)(arg);
135             }
136         }
137         e = e_next;
138     }
139
140     // Now scan for a new timeout *after* running all the callbacks
141     // (because they can add timeouts themselves)
142     min_delta = 0x7FFFFFFF;  // Maxint
143     for (e = timeouts;  e;  e = e->next )
144         if (e->delta)
145             if (e->delta < min_delta)
146                 min_delta = e->delta;
147
148     CYG_ASSERT( 0 < min_delta, "min_delta underflow" );
149
150     if (min_delta != 0x7FFFFFFF) {
151         // Still something to do, schedule it
152         last_set_time = cyg_current_time();
153         cyg_alarm_initialize(timeout_alarm_handle, last_set_time+min_delta, 0);
154         last_delta = min_delta;
155     } else {
156         last_delta = 0; // flag no activity
157     }
158 #ifdef TIMEOUT_DEBUG
159     diag_printf("Timeout list after %s\n", __FUNCTION__);
160     _show_timeouts();
161 #endif
162 }
163
164 // ------------------------------------------------------------------------
165 // ALARM EVENT FUNCTION
166 // This is the DSR for the alarm firing:
167 static void
168 do_alarm(cyg_handle_t alarm, cyg_addrword_t data)
169 {
170     cyg_flag_setbits( &alarm_flag, 1 ); 
171 }
172
173 void ecos_synch_eth_drv_dsr(void)
174 {
175     cyg_flag_setbits( &alarm_flag, 2 ); 
176 }
177
178 // ------------------------------------------------------------------------
179 // HANDLER THREAD ENTRY ROUTINE
180 // This waits on the DSR to tell it to run:
181 static void
182 alarm_thread(cyg_addrword_t param)
183 {
184     // This is from the logical ethernet dev; it calls those delivery
185     // functions who need attention.
186     extern void eth_drv_run_deliveries( void );
187
188     // This is from the logical ethernet dev; it tickles somehow
189     // all ethernet devices in case one is wedged.
190     extern void eth_drv_tickle_devices( void );
191
192     while ( 1 ) {
193         int spl;
194         int x;
195 #ifdef CYGPKG_NET_FAST_THREAD_TICKLE_DEVS
196         cyg_tick_count_t later = cyg_current_time();
197         later += CYGNUM_NET_FAST_THREAD_TICKLE_DEVS_DELAY;
198         x = cyg_flag_timed_wait(
199             &alarm_flag,
200             -1,
201             CYG_FLAG_WAITMODE_OR | CYG_FLAG_WAITMODE_CLR,
202             later );
203 #else
204         x = cyg_flag_wait(
205             &alarm_flag,
206             -1,
207             CYG_FLAG_WAITMODE_OR | CYG_FLAG_WAITMODE_CLR );
208
209         CYG_ASSERT( 3 & x, "Lost my bits" );
210 #endif // CYGPKG_NET_FAST_THREAD_TICKLE_DEVS
211         CYG_ASSERT( !((~3) & x), "Extra bits" );
212
213         spl = cyg_splinternal();
214
215         CYG_ASSERT( 0 == spl, "spl nonzero" );
216
217         if ( 2 & x )
218             eth_drv_run_deliveries();
219 #ifdef CYGPKG_NET_FAST_THREAD_TICKLE_DEVS
220         // This is in the else clause for "do we deliver" because the
221         // network stack might have continuous timing events anyway - so
222         // the timeout would not occur, x would be 1 every time.
223         else // Tickle the devices...
224             eth_drv_tickle_devices();
225 #endif // CYGPKG_NET_FAST_THREAD_TICKLE_DEVS
226
227         if ( 1 & x )
228             do_timeout();
229
230         cyg_splx(spl);
231     }
232 }
233
234 // ------------------------------------------------------------------------
235 // INITIALIZATION FUNCTION
236 void
237 cyg_alarm_timeout_init( void )
238 {
239     // Init the alarm object, attached to the real time clock
240     cyg_handle_t h;
241     cyg_clock_to_counter(cyg_real_time_clock(), &h);
242     cyg_alarm_create(h, do_alarm, 0, &timeout_alarm_handle, &timeout_alarm);
243     // Init the flag of waking up
244     cyg_flag_init( &alarm_flag );
245     // Create alarm background thread to run the callbacks
246     cyg_thread_create(
247         CYGPKG_NET_FAST_THREAD_PRIORITY, // Priority
248         alarm_thread,                   // entry
249         0,                              // entry parameter
250         "Network alarm support",        // Name
251         &alarm_stack[0],                // Stack
252         STACK_SIZE,                     // Size
253         &alarm_thread_handle,           // Handle
254         &alarm_thread_data              // Thread data structure
255         );
256     cyg_thread_resume(alarm_thread_handle);    // Start it
257 }
258
259 // ------------------------------------------------------------------------
260 // EXPORTED API: SET A TIMEOUT
261 // This can be called from anywhere, including recursively from the timeout
262 // functions themselves.
263 cyg_uint32
264 timeout(timeout_fun *fun, void *arg, cyg_int32 delta)
265 {
266     int i;
267     timeout_entry *e;
268     cyg_uint32 stamp;
269
270     // this needs to be atomic - recursive calls from the alarm
271     // handler thread itself are allowed:
272     int spl = cyg_splinternal();
273
274     stamp = 0;  // Assume no slots available
275     for (e = _timeouts, i = 0;  i < NTIMEOUTS;  i++, e++) {
276         if ((e->flags & CALLOUT_PENDING) == 0) {
277             // Free entry
278             callout_init(e);
279             e->flags = CALLOUT_LOCAL;
280             callout_reset(e, delta, fun, arg);
281             stamp = (cyg_uint32)e;
282             break;
283         }
284     }
285     cyg_splx(spl);
286     return stamp;
287 }
288
289 // ------------------------------------------------------------------------
290 // EXPORTED API: CANCEL A TIMEOUT
291 // This can be called from anywhere, including recursively from the timeout
292 // functions themselves.
293 void
294 untimeout(timeout_fun *fun, void * arg)
295 {
296     int i;
297     timeout_entry *e;
298     int spl = cyg_splinternal();
299
300     for (e = _timeouts, i = 0; i < NTIMEOUTS; i++, e++) {
301         if (e->delta && (e->fun == fun) && (e->arg == arg)) {
302             callout_stop(e);
303             break;
304         }
305     }
306     cyg_splx(spl);
307 }
308
309 void 
310 callout_init(struct callout *c) 
311 {
312     bzero(c, sizeof(*c));
313 }
314
315 void 
316 callout_reset(struct callout *c, int delta, timeout_fun *f, void *p) 
317 {
318     int spl = cyg_splinternal();
319
320     CYG_ASSERT( 0 < delta, "delta is right now, or even sooner!" );
321
322     // Renormalize delta wrt the existing set alarm, if there is one
323     if (last_delta > 0) {
324 #ifdef TIMEOUT_DEBUG
325         int _delta = delta;    
326         int _time = cyg_current_time();
327 #endif // TIMEOUT_DEBUG
328         // There is an active alarm
329         if (last_set_time != 0) {
330             // Adjust the delta to be absolute, relative to the alarm
331             delta += (cyg_int32)(cyg_current_time() - last_set_time);
332         } else {
333             // We don't know exactly when the alarm will fire, so just
334             // schedule this event for the first time, or sometime after
335             ;  // Leaving the value alone won't be "too wrong"
336         }
337 #ifdef TIMEOUT_DEBUG
338         diag_printf("delta changed from %d to %d, now: %d, then: %d, last_delta: %d\n", 
339                     _delta, delta, _time, (int)last_set_time, last_delta);
340         _show_timeouts();
341 #endif
342     }
343     // So recorded_delta is set to either:
344     // alarm is active:   delta + NOW - THEN
345     // alarm is inactive: delta
346
347     // Add this callout/timeout to the list of things to do
348     if (c->flags & CALLOUT_PENDING) {
349         callout_stop(c);
350     }
351     c->prev = (timeout_entry *)NULL;    
352     c->next = timeouts;
353     if (c->next != (timeout_entry *)NULL) {
354         c->next->prev = c;
355     }
356     timeouts = c;
357     c->flags |= CALLOUT_PENDING | CALLOUT_ACTIVE;
358     c->fun = f;
359     c->arg = p;
360     c->delta = delta;
361
362 #ifdef TIMEOUT_DEBUG
363     diag_printf("%s(%p, %d, %p, %p)\n", __FUNCTION__, c, delta, f, p);
364     _show_timeouts();
365 #endif
366
367     if ((0 == last_delta ||      // alarm was inactive  OR
368          delta < last_delta) ) { // alarm was active but later than we need
369
370         // (if last_delta is -1, this call is recursive from the handler so
371         //  also do nothing in that case)
372
373         // Here, we know the new item added is sooner than that which was
374         // most recently set, if any, so we can just go and set it up.
375         if ( 0 == last_delta )
376             last_set_time = cyg_current_time();
377         
378         // So we use, to set the alarm either:
379         // alarm is active:   (delta + NOW - THEN) + THEN
380         // alarm is inactive:  delta + NOW
381         // and in either case it is true that
382         //  (recorded_delta + last_set_time) == (delta + NOW)
383         cyg_alarm_initialize(timeout_alarm_handle, last_set_time+delta, 0);
384 #ifdef TIMEOUT_DEBUG
385         if ((int)last_set_time == 0) {
386             diag_printf("delta: %d, time: %ld, last_delta: %d\n", delta, last_set_time, last_delta);
387         }
388 #endif
389         last_delta = delta;
390     }
391     // Otherwise, the alarm is active, AND it is set to fire sooner than we
392     // require, so when it does, that will sort out calling the item we
393     // just added.
394
395 #ifdef CYGPKG_INFRA_DEBUG
396     // Do some more checking akin to that in the alarm handler:
397     if ( last_delta != -1 ) { // not a recursive call
398         cyg_tick_count_t now = cyg_current_time();
399         timeout_entry *e;
400
401         CYG_ASSERT( last_delta >= 0, "Bad last delta" );
402         delta = 0x7fffffff;
403         for (e = timeouts;  e;  e = e->next) {
404             if (e->delta) {
405                 CYG_ASSERT( e->delta >= last_delta, "e->delta underflow" );
406                 // the following triggers if the "next" timeout has not just
407                 // passed, but passed by 1000 ticks - which with the normal
408                 // 1 tick = 10ms means 10 seconds - a long time.
409                 CYG_ASSERT( last_set_time + e->delta + 1000 > now,
410                             "Recorded alarm not in the future! Starved network thread?" );
411                 if ( e->delta < delta )
412                     delta = e->delta;
413             } else {
414                 CYG_ASSERT( 0 == e->fun, "Function recorded for 0 delta" );
415             }
416         }
417         if (delta < last_delta) {
418             diag_printf("Failed to pick smallest delta - picked: %d, last: %d\n", delta, last_delta);
419             for (e = timeouts;  e;  e = e->next) {
420                 diag_printf("  timeout: %p at %d\n", e->fun, e->delta);
421             }
422         }
423         CYG_ASSERT( delta >= last_delta, "We didn't pick the smallest delta!" );
424     }
425 #endif
426     cyg_splx(spl);
427 }
428
429 void 
430 callout_stop(struct callout *c) 
431 {
432     int spl = cyg_splinternal();
433
434 #ifdef TIMEOUT_DEBUG
435     diag_printf("%s(%p) = %x\n", __FUNCTION__, c, c->flags);
436 #endif
437     if ((c->flags & CALLOUT_PENDING) == 0) {
438         c->flags &= ~CALLOUT_ACTIVE;
439         cyg_splx(spl);
440         return;
441     }
442     c->flags &= ~(CALLOUT_PENDING | CALLOUT_ACTIVE);
443     if (c->next) {
444         c->next->prev = c->prev;
445     }
446     if (c->prev) {
447         c->prev->next = c->next;
448     } else {
449         timeouts = c->next;
450     }
451     cyg_splx(spl);
452 }
453
454 int  
455 callout_active(struct callout *c) 
456 {
457     return ((c->flags & CALLOUT_ACTIVE) != 0);
458 }
459
460 void 
461 callout_deactivate(struct callout *c) 
462 {
463     c->flags &= ~CALLOUT_ACTIVE;
464 }
465
466 int  
467 callout_pending(struct callout *c) 
468 {
469     return ((c->flags & CALLOUT_PENDING) != 0);
470 }
471
472
473 // ------------------------------------------------------------------------
474
475 // EOF timeout.c