1 | /** |
---|
2 | * @file |
---|
3 | * |
---|
4 | * @ingroup rtems_bsd_rtems |
---|
5 | * |
---|
6 | * @brief TODO. |
---|
7 | */ |
---|
8 | |
---|
9 | /* |
---|
10 | * Copyright (c) 2009, 2010 embedded brains GmbH. All rights reserved. |
---|
11 | * |
---|
12 | * embedded brains GmbH |
---|
13 | * Obere Lagerstr. 30 |
---|
14 | * 82178 Puchheim |
---|
15 | * Germany |
---|
16 | * <rtems@embedded-brains.de> |
---|
17 | * |
---|
18 | * Redistribution and use in source and binary forms, with or without |
---|
19 | * modification, are permitted provided that the following conditions |
---|
20 | * are met: |
---|
21 | * 1. Redistributions of source code must retain the above copyright |
---|
22 | * notice, this list of conditions and the following disclaimer. |
---|
23 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
24 | * notice, this list of conditions and the following disclaimer in the |
---|
25 | * documentation and/or other materials provided with the distribution. |
---|
26 | * |
---|
27 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
---|
28 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
---|
31 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
32 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
33 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
34 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
35 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
36 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
37 | * SUCH DAMAGE. |
---|
38 | */ |
---|
39 | |
---|
40 | /* |
---|
41 | * FIXME: This seems to be a completely broken implementation. |
---|
42 | */ |
---|
43 | |
---|
44 | #include <machine/rtems-bsd-config.h> |
---|
45 | |
---|
46 | #include <rtems/score/statesimpl.h> |
---|
47 | #include <rtems/score/threaddispatch.h> |
---|
48 | #include <rtems/score/thread.h> |
---|
49 | #include <rtems/score/threadqimpl.h> |
---|
50 | |
---|
51 | #include <rtems/bsd/sys/param.h> |
---|
52 | #include <rtems/bsd/sys/types.h> |
---|
53 | #include <sys/systm.h> |
---|
54 | #include <sys/kernel.h> |
---|
55 | #include <sys/ktr.h> |
---|
56 | #include <rtems/bsd/sys/lock.h> |
---|
57 | #include <sys/mutex.h> |
---|
58 | #include <sys/proc.h> |
---|
59 | #include <machine/pcpu.h> |
---|
60 | |
---|
61 | #define STATES_WAITING_FOR_SLEEP 0x40000 |
---|
62 | |
---|
63 | static int pause_wchan; |
---|
64 | |
---|
65 | typedef struct |
---|
66 | { |
---|
67 | Chain_Node node; |
---|
68 | void *ident; |
---|
69 | Thread_queue_Control queue; |
---|
70 | }sleep_queue_control_t; |
---|
71 | |
---|
72 | sleep_queue_control_t sleep_queue[BSD_MAXIMUM_SLEEP_QUEUES]; //this memory allocation could use _Workspace_Allocate once inside RTEMS tree |
---|
73 | Chain_Control sleep_queue_inactive_nodes; //chain of inactive nodes |
---|
74 | Chain_Control sleep_queue_active_nodes; //chain of active nodes |
---|
75 | |
---|
76 | void |
---|
77 | sleepinit(void) |
---|
78 | { |
---|
79 | int ii; |
---|
80 | |
---|
81 | /* initialize the sleep queue */ |
---|
82 | for( ii = 0; ii < BSD_MAXIMUM_SLEEP_QUEUES; ii++ ) |
---|
83 | { |
---|
84 | sleep_queue[ii].ident = NULL; |
---|
85 | /* |
---|
86 | * Initialize the queue we use to block for signals |
---|
87 | */ |
---|
88 | _Thread_queue_Initialize( |
---|
89 | &sleep_queue[ii].queue, |
---|
90 | THREAD_QUEUE_DISCIPLINE_PRIORITY, |
---|
91 | STATES_WAITING_FOR_SLEEP | STATES_INTERRUPTIBLE_BY_SIGNAL, |
---|
92 | EAGAIN |
---|
93 | ); |
---|
94 | } |
---|
95 | //initialize active chain |
---|
96 | _Chain_Initialize_empty( &sleep_queue_active_nodes ); |
---|
97 | //initialize inactive chain |
---|
98 | _Chain_Initialize( &sleep_queue_inactive_nodes, sleep_queue, BSD_MAXIMUM_SLEEP_QUEUES, sizeof( sleep_queue_control_t )); |
---|
99 | } |
---|
100 | |
---|
101 | sleep_queue_control_t* |
---|
102 | sleep_queue_lookup(void *ident) |
---|
103 | { |
---|
104 | int ii; |
---|
105 | |
---|
106 | /* initialize the sleep queue */ |
---|
107 | for( ii = 0; ii < BSD_MAXIMUM_SLEEP_QUEUES; ii++ ) |
---|
108 | { |
---|
109 | if( sleep_queue[ii].ident == ident ) |
---|
110 | { |
---|
111 | return &sleep_queue[ii]; |
---|
112 | } |
---|
113 | } |
---|
114 | return NULL; |
---|
115 | } |
---|
116 | |
---|
117 | sleep_queue_control_t* |
---|
118 | sleep_queue_get(void *ident) |
---|
119 | { |
---|
120 | sleep_queue_control_t *sq; |
---|
121 | |
---|
122 | sq = sleep_queue_lookup( ident ); |
---|
123 | if (sq == NULL) |
---|
124 | { |
---|
125 | KASSERT(!_Chain_Is_empty( &inactive_nodes ), ("sleep_queue_get")); |
---|
126 | //get a control from the inactive chain |
---|
127 | sq = ( sleep_queue_control_t * )_Chain_Get( &sleep_queue_inactive_nodes ); |
---|
128 | sq->ident = ident; |
---|
129 | _Chain_Append( &sleep_queue_active_nodes, &sq->node ); |
---|
130 | } |
---|
131 | return sq; |
---|
132 | } |
---|
133 | |
---|
134 | /* |
---|
135 | * Block the current thread until it is awakened from its sleep queue |
---|
136 | * or it times out while waiting. |
---|
137 | */ |
---|
138 | int |
---|
139 | sleep_queue_timedwait(void *wchan, int pri, int timeout, int catch) |
---|
140 | { |
---|
141 | sleep_queue_control_t *sq; |
---|
142 | Thread_Control *executing; |
---|
143 | ISR_Level level; |
---|
144 | |
---|
145 | _Thread_Disable_dispatch(); |
---|
146 | |
---|
147 | sq = sleep_queue_get( wchan ); |
---|
148 | |
---|
149 | executing = _Thread_Executing; |
---|
150 | if( timeout ) |
---|
151 | { |
---|
152 | executing->Wait.return_code = EWOULDBLOCK; |
---|
153 | } |
---|
154 | else
|
---|
155 | { |
---|
156 | executing->Wait.return_code = 0; |
---|
157 | } |
---|
158 | _ISR_Disable( level ); |
---|
159 | _Thread_queue_Enter_critical_section( &sq->queue ); |
---|
160 | if( catch ) |
---|
161 | { |
---|
162 | sq->queue.state |= STATES_INTERRUPTIBLE_BY_SIGNAL; |
---|
163 | } |
---|
164 | else |
---|
165 | { |
---|
166 | sq->queue.state &= ~STATES_INTERRUPTIBLE_BY_SIGNAL; |
---|
167 | } |
---|
168 | executing->Wait.queue = &sq->queue; |
---|
169 | _ISR_Enable( level ); |
---|
170 | |
---|
171 | _Thread_queue_Enqueue( &sq->queue, executing, timeout ); |
---|
172 | _Thread_Enable_dispatch(); |
---|
173 | return _Thread_Executing->Wait.return_code; |
---|
174 | } |
---|
175 | |
---|
176 | /* |
---|
177 | * General sleep call. Suspends the current thread until a wakeup is |
---|
178 | * performed on the specified identifier. The thread will then be made |
---|
179 | * runnable with the specified priority. Sleeps at most timo/hz seconds |
---|
180 | * (0 means no timeout). If pri includes PCATCH flag, signals are checked |
---|
181 | * before and after sleeping, else signals are not checked. Returns 0 if |
---|
182 | * awakened, EWOULDBLOCK if the timeout expires. If PCATCH is set and a |
---|
183 | * signal needs to be delivered, ERESTART is returned if the current system |
---|
184 | * call should be restarted if possible, and EINTR is returned if the system |
---|
185 | * call should be interrupted by the signal (return EINTR). |
---|
186 | * |
---|
187 | * The lock argument is unlocked before the caller is suspended, and |
---|
188 | * re-locked before _sleep() returns. If priority includes the PDROP |
---|
189 | * flag the lock is not re-locked before returning. |
---|
190 | */ |
---|
191 | int |
---|
192 | _sleep(void *ident, struct lock_object *lock, int priority, const char *wmesg, int timo) |
---|
193 | { |
---|
194 | struct thread *td; |
---|
195 | struct lock_class *class; |
---|
196 | int catch, flags, lock_state, pri, rval; |
---|
197 | |
---|
198 | td = curthread; |
---|
199 | #ifdef KTRACE |
---|
200 | if (KTRPOINT(td, KTR_CSW)) |
---|
201 | ktrcsw(1, 0); |
---|
202 | #endif |
---|
203 | KASSERT(timo != 0 || mtx_owned(&Giant) || lock != NULL, |
---|
204 | ("sleeping without a lock")); |
---|
205 | KASSERT(ident != NULL && TD_IS_RUNNING(td), ("msleep")); |
---|
206 | if (priority & PDROP) |
---|
207 | KASSERT(lock != NULL && lock != &Giant.lock_object, |
---|
208 | ("PDROP requires a non-Giant lock")); |
---|
209 | if (lock != NULL) |
---|
210 | class = LOCK_CLASS(lock); |
---|
211 | else |
---|
212 | class = NULL; |
---|
213 | |
---|
214 | if (cold) { |
---|
215 | /* |
---|
216 | * During autoconfiguration, just return; |
---|
217 | * don't run any other threads or panic below, |
---|
218 | * in case this is the idle thread and already asleep. |
---|
219 | * XXX: this used to do "s = splhigh(); splx(safepri); |
---|
220 | * splx(s);" to give interrupts a chance, but there is |
---|
221 | * no way to give interrupts a chance now. |
---|
222 | */ |
---|
223 | if (lock != NULL && priority & PDROP) |
---|
224 | class->lc_unlock(lock); |
---|
225 | return (0); |
---|
226 | } |
---|
227 | catch = priority & PCATCH; |
---|
228 | pri = priority & PRIMASK; |
---|
229 | |
---|
230 | if (lock == &Giant.lock_object) |
---|
231 | mtx_assert(&Giant, MA_OWNED); |
---|
232 | DROP_GIANT(); |
---|
233 | if (lock != NULL && lock != &Giant.lock_object && |
---|
234 | !(class->lc_flags & LC_SLEEPABLE)) { |
---|
235 | lock_state = class->lc_unlock(lock); |
---|
236 | } else |
---|
237 | /* GCC needs to follow the Yellow Brick Road */ |
---|
238 | lock_state = -1; |
---|
239 | |
---|
240 | if (lock != NULL && class->lc_flags & LC_SLEEPABLE) { |
---|
241 | lock_state = class->lc_unlock(lock); |
---|
242 | } |
---|
243 | |
---|
244 | rval = sleep_queue_timedwait(ident, pri, timo, catch); |
---|
245 | |
---|
246 | #ifdef KTRACE |
---|
247 | if (KTRPOINT(td, KTR_CSW)) |
---|
248 | ktrcsw(0, 0); |
---|
249 | #endif |
---|
250 | PICKUP_GIANT(); |
---|
251 | if (lock != NULL && lock != &Giant.lock_object && !(priority & PDROP)) { |
---|
252 | class->lc_lock(lock, lock_state); |
---|
253 | } |
---|
254 | return (rval); |
---|
255 | } |
---|
256 | |
---|
257 | /* |
---|
258 | * pause() is like tsleep() except that the intention is to not be |
---|
259 | * explicitly woken up by another thread. Instead, the current thread |
---|
260 | * simply wishes to sleep until the timeout expires. It is |
---|
261 | * implemented using a dummy wait channel. |
---|
262 | */ |
---|
263 | int |
---|
264 | pause(const char *wmesg, int timo) |
---|
265 | { |
---|
266 | |
---|
267 | KASSERT(timo != 0, ("pause: timeout required")); |
---|
268 | return (tsleep(&pause_wchan, 0, wmesg, timo)); |
---|
269 | } |
---|
270 | |
---|
271 | /* |
---|
272 | * Make all threads sleeping on the specified identifier runnable. |
---|
273 | */ |
---|
274 | void |
---|
275 | wakeup(void *ident) |
---|
276 | { |
---|
277 | sleep_queue_control_t *sq; |
---|
278 | Thread_Control *the_thread; |
---|
279 | |
---|
280 | sq = sleep_queue_lookup( ident ); |
---|
281 | if (sq == NULL) |
---|
282 | { |
---|
283 | return (0); |
---|
284 | } |
---|
285 | |
---|
286 | while ( (the_thread = _Thread_queue_Dequeue(&sq->queue)) ) |
---|
287 | { |
---|
288 | } |
---|
289 | return 0; |
---|
290 | } |
---|
291 | |
---|
292 | /* |
---|
293 | * Make a thread sleeping on the specified identifier runnable. |
---|
294 | * May wake more than one thread if a target thread is currently |
---|
295 | * swapped out. |
---|
296 | */ |
---|
297 | void |
---|
298 | wakeup_one(void *ident) |
---|
299 | { |
---|
300 | sleep_queue_control_t *sq; |
---|
301 | Thread_Control *the_thread; |
---|
302 | |
---|
303 | sq = sleep_queue_lookup( ident ); |
---|
304 | if (sq == NULL) |
---|
305 | { |
---|
306 | return (0); |
---|
307 | } |
---|
308 | the_thread = _Thread_queue_Dequeue(&sq->queue); |
---|
309 | return 0; |
---|
310 | |
---|
311 | } |
---|
312 | |
---|