source: libbsdport/bsd_eth_drivers/libbsdport/rtems_callout.c @ d8b6f64

Last change on this file since d8b6f64 was d8b6f64, checked in by Till Straumann <strauman@…>, on Aug 6, 2009 at 7:41:57 PM

2009-08-06 Till Straumann <Till.Straumann@…>

  • libbsdport/rtems_callout.c: fixed possible race condition. callout_stop() must check again from critical/protected section of code if callout is still on the list/active. Otherwise, the callout-task could have executed and removed the callout between callout_stop() checking the p_prev pointer and entering the critical section.
  • Property mode set to 100644
File size: 5.6 KB
Line 
1#include <rtems.h>
2#include <rtems/error.h>
3#include <string.h>
4
5#include <rtems/rtems_bsdnet.h>
6#include <rtems/rtems_bsdnet_internal.h>
7
8#include "mutex.h"
9#include "callout.h"
10
11#define STATIC static
12
13/* Implementation modelled after
14 *
15 * "Redesign the BSD Callout and Timer Facilities"
16 * Adam M. Costello, George Varghese
17 * Dpt. of Computer Science, Washington University, 1995.
18 */
19
20#include <assert.h>
21
22/* rely on networking semaphore for now */
23#define LIST_KEY_DECL(k)
24#define LIST_LOCK(k)   do {} while (0)
25#define LIST_UNLOCK(k) do {} while (0)
26
27#define WHEELBITS 5
28
29#define WHEELMASK ((1<<(WHEELBITS))-1)
30
31#define CALLOUT_EVENT   RTEMS_EVENT_1
32#define KILL_EVENT              RTEMS_EVENT_2
33
34
35typedef void (*timeout_t)(void*);
36
37STATIC volatile callout_time_t hard_ticks = 0;
38STATIC volatile callout_time_t soft_ticks = 0;
39
40STATIC struct callout *c_wheel[1<<WHEELBITS] = {0};
41
42static inline void
43c_enq(struct callout **where, struct callout *c)
44{
45        assert( c->c_pprev == 0 && c->c_next == 0 );
46        if ( (c->c_next = *where) )
47                (*where)->c_pprev = &c->c_next;
48        c->c_pprev = where;
49        *where     = c;
50}
51
52static inline void
53c_deq(struct callout *c)
54{
55struct callout *n;
56        assert( c->c_pprev );
57        if ( (n = *c->c_pprev = c->c_next) )
58                n->c_pprev = c->c_pprev;
59        c->c_next = 0;
60        c->c_pprev = 0;
61}
62
63static inline void
64softclock()
65{
66struct callout        *c, *n;
67rtems_interrupt_level  k1;
68callout_time_t         st,ht;
69LIST_KEY_DECL(k);
70
71        /* I believe this is free of a race condition (softclock
72         * and hardclock both update volatile 'soft_ticks' variable):
73         *  a) 'hardclock' runs at IRQ level and is atomic
74         *  b) inside while loop 'soft_ticks' is != 'hard_ticks'
75         *  c) hardclock only modifies soft_ticks if 'soft_ticks'=='hard_ticks'
76         *     hence this could only happen just after the update of 'soft_ticks'
77         *     at the end of the while loop completes.
78         */
79
80        while ( 1 ) {
81        /* Must atomically read 'soft_ticks' and 'hard_ticks' -- otherwise,
82         * hardclock might update both but we get one old and one new value
83         */
84        rtems_interrupt_disable(k1);
85                st = soft_ticks;
86                ht = hard_ticks;
87        rtems_interrupt_enable(k1);
88                if ( st == ht )
89                        break; /* caught up */
90
91                /* at this point, we know that st != ht and therefore,
92                 * hardclock will only increment hard_ticks but leave
93                 * soft_ticks alone.
94                 */
95
96                st++;
97
98                LIST_LOCK(k);
99                for ( c = c_wheel[ st & WHEELMASK ]; c; c=n ) {
100                        n = c->c_next;
101                        if ( c->c_time <= 0 ) {
102                                /* this one expired */
103                                rtems_interrupt_disable(k1);
104                                        c->c_flags &= ~ CALLOUT_PENDING;
105                                rtems_interrupt_enable(k1);
106                                c_deq(c);
107                                if ( c->c_func )
108                                        c->c_func(c->c_arg);
109                        } else {
110                                c->c_time--;
111                        }
112                }
113                LIST_UNLOCK(k);
114                soft_ticks = st;
115                /* here, soft_ticks could have caught up and
116                 * a hardclock occurring here could also
117                 * update soft_ticks.
118                 */
119        }
120}
121
122static inline void
123hardclock(rtems_id tid)
124{
125        if ( hard_ticks++ == soft_ticks && !c_wheel[hard_ticks & WHEELMASK] ) {
126                /* nothing to do */
127                soft_ticks++;
128        } else {
129                rtems_event_send(tid, CALLOUT_EVENT);
130        }
131}
132
133static void
134calloutTick(rtems_id myself, void *arg)
135{
136rtems_id tid = (rtems_id)arg;
137
138        hardclock(tid);
139
140        rtems_timer_fire_after(myself, 1, calloutTick, arg);
141}
142
143static void
144calloutTask(void *arg)
145{
146rtems_event_set   ev;
147rtems_status_code sc;
148rtems_id          ticker = 0;
149rtems_id          me;
150
151        sc = rtems_timer_create(rtems_build_name('b','s','d','c'), &ticker);
152        if ( RTEMS_SUCCESSFUL != sc ) {
153                rtems_error(sc, "Creation of timer failed\n");
154                goto bail;
155        }
156        rtems_task_ident(RTEMS_SELF, RTEMS_LOCAL, &me);
157
158        rtems_timer_fire_after(ticker, 1, calloutTick, (void*)me);
159
160        while ( 1 ) {
161                sc = rtems_bsdnet_event_receive (CALLOUT_EVENT | KILL_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &ev);
162                if ( RTEMS_SUCCESSFUL != sc ) {
163                        rtems_error(sc, "calloutTask: unable to receive event; terminating\n");
164                        break;
165                }
166                if ( ev & KILL_EVENT ) {
167                        break;
168                }
169                softclock();
170        }
171bail:
172        rtems_timer_delete(ticker);
173        rtems_task_delete(RTEMS_SELF);
174}
175
176
177/* We cannot stop a callout that's in progress */
178
179int
180callout_stop(struct callout *c)
181{
182rtems_interrupt_level l;
183LIST_KEY_DECL(k);
184
185        if ( !c->c_pprev )
186                return 0;       /* not currently on a list */
187
188        LIST_LOCK(k);
189                /* have to check again */
190                if ( ! c->c_pprev ) {
191                        LIST_UNLOCK(k);
192                        return 0;
193                }
194                /* remove from list */
195                c_deq(c);
196                rtems_interrupt_disable(l);
197                c->c_flags &= ~(CALLOUT_ACTIVE | CALLOUT_PENDING);
198                rtems_interrupt_enable(l);
199        LIST_UNLOCK(k);
200
201        return 1;
202}
203
204
205int
206callout_reset(struct callout *c, int ticks, timeout_t fn, void *arg)
207{
208rtems_interrupt_level l;
209LIST_KEY_DECL(k);
210int                 i, rval;
211
212        if ( ticks <= 0 )
213                ticks = 1;
214
215        rval = callout_stop(c);
216
217        c->c_func = fn;
218        c->c_arg  = arg;
219
220        LIST_LOCK(k);
221        i         = (hard_ticks + ticks) & WHEELMASK;
222        c->c_time = ticks >> WHEELBITS;
223
224        /* enqueue */
225        c_enq(&c_wheel[i], c);
226
227        rtems_interrupt_disable(l);
228                c->c_flags |= (CALLOUT_ACTIVE | CALLOUT_PENDING);
229        rtems_interrupt_enable(l);
230
231        LIST_UNLOCK(k);
232
233        return rval;
234}
235
236static rtems_id callout_tid = 0;
237
238void
239callout_init(struct callout *c, int mpsafe)
240{
241        /* non thread-safe lazy init in case nobody cared to do it ... */
242        if ( !callout_tid )
243                rtems_callout_initialize();
244        memset(c,0,sizeof(*c)); 
245}
246
247void
248callout_init_mtx(struct callout *c, struct mtx *m, unsigned flags)
249{
250        if ( m->mtx_id )
251                rtems_panic("callout_init_mtx: using mutex not supported\n");
252        callout_init(c,0);
253        c->c_mtx = m;
254}
255
256rtems_id
257rtems_callout_initialize()
258{
259        if ( !callout_tid )
260                callout_tid=rtems_bsdnet_newproc ("cout", 4096, calloutTask, NULL);
261        return callout_tid;
262}
263
264#ifdef DEBUG
265void
266_cexpModuleInitialize(void*u)
267{
268        rtems_bsdnet_initialize_network();
269        rtems_callout_initialize();
270}
271#endif
Note: See TracBrowser for help on using the repository browser.