scoutos / prex-0.9.0 / sys / ipc / msg.c @ 03e9c04a
History | View | Annotate | Download (10.7 KB)
1 | 03e9c04a | Brad Neuman | /*-
|
---|---|---|---|
2 | * Copyright (c) 2005-2007, Kohsuke Ohtani
|
||
3 | * All rights reserved.
|
||
4 | *
|
||
5 | * Redistribution and use in source and binary forms, with or without
|
||
6 | * modification, are permitted provided that the following conditions
|
||
7 | * are met:
|
||
8 | * 1. Redistributions of source code must retain the above copyright
|
||
9 | * notice, this list of conditions and the following disclaimer.
|
||
10 | * 2. Redistributions in binary form must reproduce the above copyright
|
||
11 | * notice, this list of conditions and the following disclaimer in the
|
||
12 | * documentation and/or other materials provided with the distribution.
|
||
13 | * 3. Neither the name of the author nor the names of any co-contributors
|
||
14 | * may be used to endorse or promote products derived from this software
|
||
15 | * without specific prior written permission.
|
||
16 | *
|
||
17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||
27 | * SUCH DAMAGE.
|
||
28 | */
|
||
29 | |||
30 | /*
|
||
31 | * msg.c - routines to transmit a message.
|
||
32 | */
|
||
33 | |||
34 | /**
|
||
35 | * IPC transmission:
|
||
36 | *
|
||
37 | * Messages are sent to the specific object by using msg_send. The
|
||
38 | * transmission of a message is completely synchronous with this
|
||
39 | * kernel. This means the thread which sent a message is blocked until
|
||
40 | * it receives a response from another thread. msg_receive performs
|
||
41 | * reception of a message. msg_receive is also blocked when no message
|
||
42 | * is reached to the target object. The receiver thread must answer the
|
||
43 | * message using msg_reply after processing the message.
|
||
44 | *
|
||
45 | * The receiver thread can not receive an additional message until it
|
||
46 | * replies to the sender. In short, a thread can receive only one
|
||
47 | * message at once. In other hand, once the thread receives a message,
|
||
48 | * it can send another message to different object. This mechanism
|
||
49 | * allows threads to redirect the sender's request to another thread.
|
||
50 | *
|
||
51 | * A message is copied from thread to thread directly without any kernel
|
||
52 | * buffering. The message buffer in sender's memory space is automatically
|
||
53 | * mapped to the receiver's memory by kernel. Since there is no page
|
||
54 | * out of memory in this system, we can copy the message data via physical
|
||
55 | * memory at anytime.
|
||
56 | */
|
||
57 | |||
58 | #include <kernel.h> |
||
59 | #include <sched.h> |
||
60 | #include <task.h> |
||
61 | #include <kmem.h> |
||
62 | #include <thread.h> |
||
63 | #include <task.h> |
||
64 | #include <event.h> |
||
65 | #include <ipc.h> |
||
66 | |||
67 | /* forward declarations */
|
||
68 | static thread_t msg_dequeue(queue_t);
|
||
69 | static void msg_enqueue(queue_t, thread_t); |
||
70 | |||
71 | static struct event ipc_event; /* event for IPC operation */ |
||
72 | |||
73 | /*
|
||
74 | * Send a message.
|
||
75 | *
|
||
76 | * The current thread will be blocked until any other thread
|
||
77 | * receives and reply the message. A thread can send a
|
||
78 | * message to any object if it knows the object id.
|
||
79 | */
|
||
80 | int
|
||
81 | msg_send(object_t obj, void *msg, size_t size)
|
||
82 | { |
||
83 | struct msg_header *hdr;
|
||
84 | thread_t t; |
||
85 | void *kmsg;
|
||
86 | int rc;
|
||
87 | |||
88 | if (!user_area(msg))
|
||
89 | return EFAULT;
|
||
90 | |||
91 | if (size < sizeof(struct msg_header)) |
||
92 | return EINVAL;
|
||
93 | |||
94 | sched_lock(); |
||
95 | |||
96 | if (!object_valid(obj)) {
|
||
97 | sched_unlock(); |
||
98 | return EINVAL;
|
||
99 | } |
||
100 | /*
|
||
101 | * A thread can not send a message when it is
|
||
102 | * already receiving from the target object.
|
||
103 | * It will obviously cause a deadlock.
|
||
104 | */
|
||
105 | if (obj == curthread->recvobj) {
|
||
106 | sched_unlock(); |
||
107 | return EDEADLK;
|
||
108 | } |
||
109 | /*
|
||
110 | * Translate message address to the kernel linear
|
||
111 | * address. So that a receiver thread can access
|
||
112 | * the message via kernel pointer. We can catch
|
||
113 | * the page fault here.
|
||
114 | */
|
||
115 | if ((kmsg = kmem_map(msg, size)) == NULL) { |
||
116 | sched_unlock(); |
||
117 | return EFAULT;
|
||
118 | } |
||
119 | curthread->msgaddr = kmsg; |
||
120 | curthread->msgsize = size; |
||
121 | |||
122 | /*
|
||
123 | * The sender ID is filled in the message header
|
||
124 | * by the kernel. So, the receiver can trust it.
|
||
125 | */
|
||
126 | hdr = (struct msg_header *)kmsg;
|
||
127 | hdr->task = curtask; |
||
128 | |||
129 | /*
|
||
130 | * If receiver already exists, wake it up.
|
||
131 | * The highest priority thread can get the message.
|
||
132 | */
|
||
133 | if (!queue_empty(&obj->recvq)) {
|
||
134 | t = msg_dequeue(&obj->recvq); |
||
135 | sched_unsleep(t, 0);
|
||
136 | } |
||
137 | /*
|
||
138 | * Sleep until we get a reply message.
|
||
139 | * Note: Do not touch any data in the object
|
||
140 | * structure after we wakeup. This is because the
|
||
141 | * target object may be deleted while we are sleeping.
|
||
142 | */
|
||
143 | curthread->sendobj = obj; |
||
144 | msg_enqueue(&obj->sendq, curthread); |
||
145 | rc = sched_sleep(&ipc_event); |
||
146 | if (rc == SLP_INTR)
|
||
147 | queue_remove(&curthread->ipc_link); |
||
148 | curthread->sendobj = NULL;
|
||
149 | |||
150 | sched_unlock(); |
||
151 | |||
152 | /*
|
||
153 | * Check sleep result.
|
||
154 | */
|
||
155 | switch (rc) {
|
||
156 | case SLP_BREAK:
|
||
157 | return EAGAIN; /* Receiver has been terminated */ |
||
158 | case SLP_INVAL:
|
||
159 | return EINVAL; /* Object has been deleted */ |
||
160 | case SLP_INTR:
|
||
161 | return EINTR; /* Exception */ |
||
162 | default:
|
||
163 | /* DO NOTHING */
|
||
164 | break;
|
||
165 | } |
||
166 | return 0; |
||
167 | } |
||
168 | |||
169 | /*
|
||
170 | * Receive a message.
|
||
171 | *
|
||
172 | * A thread can receive a message from the object which was
|
||
173 | * created by any thread belongs to same task. If the message
|
||
174 | * has not reached yet, it blocks until any message comes in.
|
||
175 | *
|
||
176 | * The size argument specifies the "maximum" size of the message
|
||
177 | * buffer to receive. If the sent message is larger than this
|
||
178 | * size, the kernel will automatically clip the message to this
|
||
179 | * maximum buffer size.
|
||
180 | *
|
||
181 | * When a message is received, the sender thread is removed from
|
||
182 | * object's send queue. So, another thread can receive the
|
||
183 | * subsequent message from that object. This is important for
|
||
184 | * the multi-thread server which must receive multiple messages
|
||
185 | * simultaneously.
|
||
186 | */
|
||
187 | int
|
||
188 | msg_receive(object_t obj, void *msg, size_t size)
|
||
189 | { |
||
190 | thread_t t; |
||
191 | size_t len; |
||
192 | int rc, error = 0; |
||
193 | |||
194 | if (!user_area(msg))
|
||
195 | return EFAULT;
|
||
196 | |||
197 | sched_lock(); |
||
198 | |||
199 | if (!object_valid(obj)) {
|
||
200 | sched_unlock(); |
||
201 | return EINVAL;
|
||
202 | } |
||
203 | if (obj->owner != curtask) {
|
||
204 | sched_unlock(); |
||
205 | return EACCES;
|
||
206 | } |
||
207 | /*
|
||
208 | * Check if this thread finished previous receive
|
||
209 | * operation. A thread can not receive different
|
||
210 | * messages at once.
|
||
211 | */
|
||
212 | if (curthread->recvobj) {
|
||
213 | sched_unlock(); |
||
214 | return EBUSY;
|
||
215 | } |
||
216 | curthread->recvobj = obj; |
||
217 | |||
218 | /*
|
||
219 | * If no message exists, wait until message arrives.
|
||
220 | */
|
||
221 | while (queue_empty(&obj->sendq)) {
|
||
222 | /*
|
||
223 | * Block until someone sends a message.
|
||
224 | */
|
||
225 | msg_enqueue(&obj->recvq, curthread); |
||
226 | rc = sched_sleep(&ipc_event); |
||
227 | if (rc != 0) { |
||
228 | /*
|
||
229 | * Receive is failed due to some reasons.
|
||
230 | */
|
||
231 | switch (rc) {
|
||
232 | case SLP_INVAL:
|
||
233 | error = EINVAL; /* Object has been deleted */
|
||
234 | break;
|
||
235 | case SLP_INTR:
|
||
236 | queue_remove(&curthread->ipc_link); |
||
237 | error = EINTR; /* Got exception */
|
||
238 | break;
|
||
239 | default:
|
||
240 | panic("msg_receive");
|
||
241 | break;
|
||
242 | } |
||
243 | curthread->recvobj = NULL;
|
||
244 | sched_unlock(); |
||
245 | return error;
|
||
246 | } |
||
247 | |||
248 | /*
|
||
249 | * Check the existence of the sender thread again.
|
||
250 | * Even if this thread is woken by the sender thread,
|
||
251 | * the message may be received by another thread.
|
||
252 | * This may happen when another high priority thread
|
||
253 | * becomes runnable before we receive the message.
|
||
254 | */
|
||
255 | } |
||
256 | |||
257 | t = msg_dequeue(&obj->sendq); |
||
258 | |||
259 | /*
|
||
260 | * Copy out the message to the user-space.
|
||
261 | */
|
||
262 | len = MIN(size, t->msgsize); |
||
263 | if (len > 0) { |
||
264 | if (copyout(t->msgaddr, msg, len)) {
|
||
265 | msg_enqueue(&obj->sendq, t); |
||
266 | curthread->recvobj = NULL;
|
||
267 | sched_unlock(); |
||
268 | return EFAULT;
|
||
269 | } |
||
270 | } |
||
271 | /*
|
||
272 | * Detach the message from the target object.
|
||
273 | */
|
||
274 | curthread->sender = t; |
||
275 | t->receiver = curthread; |
||
276 | |||
277 | sched_unlock(); |
||
278 | return error;
|
||
279 | } |
||
280 | |||
281 | /*
|
||
282 | * Send a reply message.
|
||
283 | *
|
||
284 | * The target object must be the object that we are receiving.
|
||
285 | * Otherwise, this function will be failed.
|
||
286 | */
|
||
287 | int
|
||
288 | msg_reply(object_t obj, void *msg, size_t size)
|
||
289 | { |
||
290 | thread_t t; |
||
291 | size_t len; |
||
292 | |||
293 | if (!user_area(msg))
|
||
294 | return EFAULT;
|
||
295 | |||
296 | sched_lock(); |
||
297 | |||
298 | if (!object_valid(obj) || obj != curthread->recvobj) {
|
||
299 | sched_unlock(); |
||
300 | return EINVAL;
|
||
301 | } |
||
302 | /*
|
||
303 | * Check if sender still exists
|
||
304 | */
|
||
305 | if (curthread->sender == NULL) { |
||
306 | /* Clear receive state */
|
||
307 | curthread->recvobj = NULL;
|
||
308 | sched_unlock(); |
||
309 | return EINVAL;
|
||
310 | } |
||
311 | /*
|
||
312 | * Copy a message to the sender's buffer.
|
||
313 | */
|
||
314 | t = curthread->sender; |
||
315 | len = MIN(size, t->msgsize); |
||
316 | if (len > 0) { |
||
317 | if (copyin(msg, t->msgaddr, len)) {
|
||
318 | sched_unlock(); |
||
319 | return EFAULT;
|
||
320 | } |
||
321 | } |
||
322 | /*
|
||
323 | * Wakeup sender with no error.
|
||
324 | */
|
||
325 | sched_unsleep(t, 0);
|
||
326 | t->receiver = NULL;
|
||
327 | |||
328 | /* Clear transmit state */
|
||
329 | curthread->sender = NULL;
|
||
330 | curthread->recvobj = NULL;
|
||
331 | |||
332 | sched_unlock(); |
||
333 | return 0; |
||
334 | } |
||
335 | |||
336 | /*
|
||
337 | * Cancel pending message operation of the specified thread.
|
||
338 | * This is called when the thread is terminated.
|
||
339 | *
|
||
340 | * We have to handle the following conditions to prevent deadlock.
|
||
341 | *
|
||
342 | * If the terminated thread is sending a message:
|
||
343 | * 1. A message is already received.
|
||
344 | * -> The receiver thread will reply to the invalid thread.
|
||
345 | *
|
||
346 | * 2. A message is not received yet.
|
||
347 | * -> The thread remains in send queue of the object.
|
||
348 | *
|
||
349 | * When the terminated thread is receiving a message.
|
||
350 | * 3. A message is already sent.
|
||
351 | * -> The sender thread will wait for reply forever.
|
||
352 | *
|
||
353 | * 4. A message is not sent yet.
|
||
354 | * -> The thread remains in receive queue of the object.
|
||
355 | */
|
||
356 | void
|
||
357 | msg_cancel(thread_t t) |
||
358 | { |
||
359 | |||
360 | sched_lock(); |
||
361 | |||
362 | if (t->sendobj != NULL) { |
||
363 | if (t->receiver != NULL) |
||
364 | t->receiver->sender = NULL;
|
||
365 | else
|
||
366 | queue_remove(&t->ipc_link); |
||
367 | } |
||
368 | if (t->recvobj != NULL) { |
||
369 | if (t->sender != NULL) { |
||
370 | sched_unsleep(t->sender, SLP_BREAK); |
||
371 | t->sender->receiver = NULL;
|
||
372 | } else
|
||
373 | queue_remove(&t->ipc_link); |
||
374 | } |
||
375 | sched_unlock(); |
||
376 | } |
||
377 | |||
378 | /*
|
||
379 | * Abort all message operations relevant to the specified object.
|
||
380 | * This is called when the target object is deleted.
|
||
381 | */
|
||
382 | void
|
||
383 | msg_abort(object_t obj) |
||
384 | { |
||
385 | queue_t q; |
||
386 | thread_t t; |
||
387 | |||
388 | sched_lock(); |
||
389 | |||
390 | /*
|
||
391 | * Force wakeup all threads in the send queue.
|
||
392 | */
|
||
393 | while (!queue_empty(&obj->sendq)) {
|
||
394 | q = dequeue(&obj->sendq); |
||
395 | t = queue_entry(q, struct thread, ipc_link);
|
||
396 | sched_unsleep(t, SLP_INVAL); |
||
397 | } |
||
398 | /*
|
||
399 | * Force wakeup all threads waiting for receive.
|
||
400 | */
|
||
401 | while (!queue_empty(&obj->recvq)) {
|
||
402 | q = dequeue(&obj->recvq); |
||
403 | t = queue_entry(q, struct thread, ipc_link);
|
||
404 | sched_unsleep(t, SLP_INVAL); |
||
405 | } |
||
406 | sched_unlock(); |
||
407 | } |
||
408 | |||
409 | /*
|
||
410 | * Dequeue thread from the IPC queue.
|
||
411 | * The most highest priority thread will be chosen.
|
||
412 | */
|
||
413 | static thread_t
|
||
414 | msg_dequeue(queue_t head) |
||
415 | { |
||
416 | queue_t q; |
||
417 | thread_t t, top; |
||
418 | |||
419 | q = queue_first(head); |
||
420 | top = queue_entry(q, struct thread, ipc_link);
|
||
421 | |||
422 | while (!queue_end(head, q)) {
|
||
423 | t = queue_entry(q, struct thread, ipc_link);
|
||
424 | if (t->priority < top->priority)
|
||
425 | top = t; |
||
426 | q = queue_next(q); |
||
427 | } |
||
428 | queue_remove(&top->ipc_link); |
||
429 | return top;
|
||
430 | } |
||
431 | |||
432 | static void |
||
433 | msg_enqueue(queue_t head, thread_t t) |
||
434 | { |
||
435 | |||
436 | enqueue(head, &t->ipc_link); |
||
437 | } |
||
438 | |||
439 | void
|
||
440 | msg_init(void)
|
||
441 | { |
||
442 | |||
443 | event_init(&ipc_event, "ipc");
|
||
444 | } |