Annotation of prex-old/sys/ipc/msg.c, Revision 1.1.1.1.2.1
1.1 nbrk 1: /*-
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: /*
1.1.1.1.2.1! nbrk 35: * Design:
! 36: *
1.1 nbrk 37: * Messages are sent to the specific object by using msg_send().
38: * The transmission of a message is completely synchronous with
1.1.1.1.2.1! nbrk 39: * this kernel. This means the thread which sent a message is
! 40: * blocked until it receives a response from another thread.
! 41: * msg_receive() performs reception of a message. msg_receive() is
! 42: * also blocked when no message is reached to the target object.
! 43: * The receiver thread must answer the message using msg_reply()
! 44: * after it finishes its message processing.
1.1 nbrk 45: *
46: * The receiver thread can not receive another message until it
47: * replies to the sender. In short, a thread can receive only one
48: * message at once. Once the thread receives message, it can send
1.1.1.1.2.1! nbrk 49: * another message to different object. This mechanism allows
! 50: * threads to redirect the sender's request to another thread.
1.1 nbrk 51: *
52: * The message is copied from thread to thread directly without any
1.1.1.1.2.1! nbrk 53: * kernel buffering. If sent message contains a buffer, sender's
! 54: * memory region is automatically mapped to the receiver's memory
! 55: * in kernel. Since there is no page out of memory in this system,
! 56: * we can copy the message data via physical memory at anytime.
1.1 nbrk 57: */
58:
59: #include <kernel.h>
60: #include <queue.h>
61: #include <event.h>
62: #include <kmem.h>
63: #include <sched.h>
64: #include <thread.h>
65: #include <task.h>
66: #include <vm.h>
67: #include <ipc.h>
68:
69: #define min(a,b) (((a) < (b)) ? (a) : (b))
70:
71: /* forward declarations */
1.1.1.1.2.1! nbrk 72: static thread_t msg_dequeue(queue_t);
! 73: static void msg_enqueue(queue_t, thread_t);
1.1 nbrk 74:
75: /* event for IPC operation */
76: static struct event ipc_event;
77:
78: /*
1.1.1.1.2.1! nbrk 79: * Send a message.
! 80: *
! 81: * The current thread will be blocked until any other thread
! 82: * receives the message and calls msg_reply() for the target
! 83: * object. When new message has been reached to the object, it
! 84: * will be received by highest priority thread waiting for
! 85: * that message. A thread can send a message to any object if
! 86: * it knows the object id.
1.1 nbrk 87: */
88: int
89: msg_send(object_t obj, void *msg, size_t size)
90: {
1.1.1.1.2.1! nbrk 91: struct msg_header *hdr;
1.1 nbrk 92: thread_t th;
93: void *kmsg;
94: int rc;
95:
96: if (!user_area(msg))
97: return EFAULT;
98:
99: if (size < sizeof(struct msg_header))
100: return EINVAL;
101:
102: sched_lock();
103:
104: if (!object_valid(obj)) {
105: sched_unlock();
106: return EINVAL;
107: }
108: if (obj->owner != cur_task() && !task_capable(CAP_IPC)) {
109: sched_unlock();
110: return EPERM;
111: }
112: /*
1.1.1.1.2.1! nbrk 113: * A thread can not send a message when the
! 114: * thread is already receiving from the target
! 115: * object. This will obviously cause a deadlock.
1.1 nbrk 116: */
1.1.1.1.2.1! nbrk 117: if (obj == cur_thread->recvobj) {
1.1 nbrk 118: sched_unlock();
119: return EDEADLK;
120: }
121: /*
1.1.1.1.2.1! nbrk 122: * Translate message address to the kernel linear
! 123: * address. So that a receiver thread can access
! 124: * the message via kernel pointer. We can catch
! 125: * the page fault here.
1.1 nbrk 126: */
127: if ((kmsg = kmem_map(msg, size)) == NULL) {
128: /* Error - no physical address for the message */
129: sched_unlock();
130: return EFAULT;
131: }
132: /*
1.1.1.1.2.1! nbrk 133: * The sender ID in the message header is filled
! 134: * by the kernel. So, the receiver can trust it.
1.1 nbrk 135: */
1.1.1.1.2.1! nbrk 136: hdr = (struct msg_header *)kmsg;
! 137: hdr->task = cur_task();
1.1 nbrk 138:
1.1.1.1.2.1! nbrk 139: /* Save information about the message block. */
! 140: cur_thread->msgaddr = kmsg;
! 141: cur_thread->msgsize = size;
1.1 nbrk 142:
143: /*
1.1.1.1.2.1! nbrk 144: * If receiver already exists, wake it up.
! 145: * Highest priority thread will get this message.
1.1 nbrk 146: */
147: if (!queue_empty(&obj->recvq)) {
148: th = msg_dequeue(&obj->recvq);
149: sched_unsleep(th, 0);
150: }
151: /*
152: * Sleep until we get a reply message.
1.1.1.1.2.1! nbrk 153: * Note: Do not touch any data in the object
! 154: * structure after we wakeup. This is because the
! 155: * target object may be deleted during we were
! 156: * sleeping.
1.1 nbrk 157: */
1.1.1.1.2.1! nbrk 158: cur_thread->sendobj = obj;
1.1 nbrk 159: msg_enqueue(&obj->sendq, cur_thread);
160: rc = sched_sleep(&ipc_event);
161: if (rc == SLP_INTR)
162: queue_remove(&cur_thread->ipc_link);
1.1.1.1.2.1! nbrk 163: cur_thread->sendobj = NULL;
1.1 nbrk 164:
165: sched_unlock();
166:
167: /*
168: * Check sleep result.
169: */
170: switch (rc) {
171: case SLP_BREAK:
172: return EAGAIN; /* Receiver has been terminated */
173: case SLP_INVAL:
174: return EINVAL; /* Object has been deleted */
175: case SLP_INTR:
176: return EINTR; /* Exception */
1.1.1.1.2.1! nbrk 177: default:
! 178: /* DO NOTHING */
! 179: break;
1.1 nbrk 180: }
181: return 0;
182: }
183:
184: /*
185: * Receive a message.
186: *
1.1.1.1.2.1! nbrk 187: * A thread can receive a message from the object which was
! 188: * created by any thread belongs to same task. If the message
! 189: * has not arrived yet, it blocks until any message comes in.
1.1 nbrk 190: *
191: * The size argument specifies the "maximum" size of the message
1.1.1.1.2.1! nbrk 192: * buffer to receive. If the sent message is larger than this
! 193: * size, the kernel will automatically clip the message to the
! 194: * receive buffer size.
1.1 nbrk 195: *
196: * When message is received, the sender thread is removed from
1.1.1.1.2.1! nbrk 197: * object's send queue. So, another thread can receive the
! 198: * subsequent message from that object. This is important for
! 199: * the multi-thread server which receives some messages
! 200: * simultaneously.
1.1 nbrk 201: */
202: int
203: msg_receive(object_t obj, void *msg, size_t size)
204: {
205: thread_t th;
206: size_t len;
1.1.1.1.2.1! nbrk 207: int rc, err = 0;
1.1 nbrk 208:
209: if (!user_area(msg))
210: return EFAULT;
211:
212: sched_lock();
213:
214: if (!object_valid(obj)) {
215: err = EINVAL;
216: goto out;
217: }
218: if (obj->owner != cur_task()) {
219: err = EACCES;
220: goto out;
221: }
222: /*
1.1.1.1.2.1! nbrk 223: * Check if this thread finished previous receive
! 224: * operation. A thread can not receive different
! 225: * messages at once.
1.1 nbrk 226: */
1.1.1.1.2.1! nbrk 227: if (cur_thread->recvobj) {
1.1 nbrk 228: err = EBUSY;
229: goto out;
230: }
1.1.1.1.2.1! nbrk 231: cur_thread->recvobj = obj;
1.1 nbrk 232:
233: /*
234: * If no message exists, wait until message arrives.
235: */
236: while (queue_empty(&obj->sendq)) {
237: /*
1.1.1.1.2.1! nbrk 238: * Block until someone sends the message.
1.1 nbrk 239: */
240: msg_enqueue(&obj->recvq, cur_thread);
241: rc = sched_sleep(&ipc_event);
1.1.1.1.2.1! nbrk 242: if (rc != 0) {
1.1 nbrk 243: /*
1.1.1.1.2.1! nbrk 244: * Receive is failed due to some reasons.
1.1 nbrk 245: */
1.1.1.1.2.1! nbrk 246: switch (rc) {
! 247: case SLP_INVAL:
! 248: err = EINVAL; /* Object has been deleted */
! 249: break;
! 250: case SLP_INTR:
! 251: queue_remove(&cur_thread->ipc_link);
! 252: err = EINTR; /* Got exception */
! 253: break;
! 254: default:
! 255: panic("msg_receive: invalid wake reason");
! 256: break;
! 257: }
! 258: cur_thread->recvobj = NULL;
! 259: goto out;
1.1 nbrk 260: }
1.1.1.1.2.1! nbrk 261:
1.1 nbrk 262: /*
1.1.1.1.2.1! nbrk 263: * Even if this thread is woken by the sender thread,
! 264: * the message may be received by another thread
! 265: * before this thread runs. This can occur when
! 266: * higher priority thread becomes runnable at that
! 267: * time. So, it is necessary to check the existence
! 268: * of the sender, again.
1.1 nbrk 269: */
270: }
271:
272: th = msg_dequeue(&obj->sendq);
273:
274: /*
1.1.1.1.2.1! nbrk 275: * Copy out the message to the user-space.
1.1 nbrk 276: * The smaller buffer size is used as copy length
277: * between sender and receiver thread.
278: */
1.1.1.1.2.1! nbrk 279: len = min(size, th->msgsize);
1.1 nbrk 280: if (len > 0) {
1.1.1.1.2.1! nbrk 281: if (umem_copyout(th->msgaddr, msg, len)) {
1.1 nbrk 282: msg_enqueue(&obj->sendq, th);
1.1.1.1.2.1! nbrk 283: cur_thread->recvobj = NULL;
1.1 nbrk 284: err = EFAULT;
285: goto out;
286: }
287: }
288: /*
289: * Detach the message from the target object.
290: */
291: cur_thread->sender = th;
292: th->receiver = cur_thread;
293: out:
294: sched_unlock();
295: return err;
296: }
297:
298: /*
299: * Send a reply message.
300: *
1.1.1.1.2.1! nbrk 301: * The target object must be an appropriate object that current
! 302: * thread has been received from. Otherwise, this function will
! 303: * be failed.
1.1 nbrk 304: *
1.1.1.1.2.1! nbrk 305: * Since the target object may already be deleted, we can not
! 306: * access the data of the object within this routine.
1.1 nbrk 307: */
308: int
309: msg_reply(object_t obj, void *msg, size_t size)
310: {
311: thread_t th;
312: size_t len;
313: int err = 0;
314:
315: if (!user_area(msg))
316: return EFAULT;
317:
318: sched_lock();
319:
1.1.1.1.2.1! nbrk 320: if (!object_valid(obj) || obj != cur_thread->recvobj) {
1.1 nbrk 321: sched_unlock();
322: return EINVAL;
323: }
324: /*
325: * Check if sender still exists
326: */
327: if (cur_thread->sender == NULL) {
328: err = EINVAL;
329: goto out;
330: }
331: /*
332: * Copy message to the sender's buffer.
333: */
334: th = cur_thread->sender;
1.1.1.1.2.1! nbrk 335: len = min(size, th->msgsize);
1.1 nbrk 336: if (len > 0) {
1.1.1.1.2.1! nbrk 337: if (umem_copyin(msg, th->msgaddr, len)) {
1.1 nbrk 338: sched_unlock();
339: return EFAULT;
340: }
341: }
342: /*
343: * Wakeup sender with no error.
344: */
345: sched_unsleep(th, 0);
346: th->receiver = NULL;
347: out:
348: /* Clear transmit state */
349: cur_thread->sender = NULL;
1.1.1.1.2.1! nbrk 350: cur_thread->recvobj = NULL;
1.1 nbrk 351:
352: sched_unlock();
353: return err;
354: }
355:
356: /*
357: * Clean up pending message operation of specified thread in order
1.1.1.1.2.1! nbrk 358: * to prevent deadlock. This is called when the thread is killed.
1.1 nbrk 359: * It is necessary to deal with the following conditions.
360: *
361: * If killed thread is sender:
362: * 1. Killed after message is received
363: * -> The received thread will reply to the invalid thread.
364: *
365: * 2. Killed before message is received
366: * -> The thread remains in send queue of the object.
367: *
368: * When thread is receiver:
369: * 3. Killed after message is sent
370: * -> The sender thread continues waiting for reply forever.
371: *
372: * 4. Killed before message is sent
373: * -> The thread remains in receive queue of the object.
374: */
375: void
376: msg_cleanup(thread_t th)
377: {
378:
379: sched_lock();
380:
1.1.1.1.2.1! nbrk 381: if (th->sendobj) {
1.1 nbrk 382: if (th->receiver)
383: th->receiver->sender = NULL;
384: else
385: queue_remove(&th->ipc_link);
386: }
1.1.1.1.2.1! nbrk 387: if (th->recvobj) {
1.1 nbrk 388: if (th->sender) {
389: sched_unsleep(th->sender, SLP_BREAK);
390: th->sender->receiver = NULL;
391: } else
392: queue_remove(&th->ipc_link);
393: }
394: sched_unlock();
395: }
396:
397: /*
1.1.1.1.2.1! nbrk 398: * Cancel all message operation relevant to the specified
! 399: * object.
! 400: *
! 401: * This is called when target object is deleted. All threads
! 402: * in message queue are woken to avoid deadlock. If the
! 403: * message has already been received, send/reply operation
1.1 nbrk 404: * continue processing normally.
405: */
406: void
407: msg_cancel(object_t obj)
408: {
1.1.1.1.2.1! nbrk 409: queue_t q;
1.1 nbrk 410: thread_t th;
411:
412: sched_lock();
413:
414: /*
1.1.1.1.2.1! nbrk 415: * Force wakeup all threads in the send queue.
1.1 nbrk 416: */
1.1.1.1.2.1! nbrk 417: while (!queue_empty(&obj->sendq)) {
! 418: q = dequeue(&obj->sendq);
1.1 nbrk 419: th = queue_entry(q, struct thread, ipc_link);
420: sched_unsleep(th, SLP_INVAL);
421: }
422: /*
1.1.1.1.2.1! nbrk 423: * Force wakeup all threads waiting for receive.
1.1 nbrk 424: */
1.1.1.1.2.1! nbrk 425: while (!queue_empty(&obj->recvq)) {
! 426: q = dequeue(&obj->sendq);
1.1 nbrk 427: th = queue_entry(q, struct thread, ipc_link);
428: sched_unsleep(th, SLP_INVAL);
429: }
430: sched_unlock();
431: }
432:
433: /*
434: * Dequeue thread from specified queue.
435: * The most highest priority thread will be chosen.
436: */
437: static thread_t
438: msg_dequeue(queue_t head)
439: {
440: queue_t q;
441: thread_t th, top;
442:
443: q = queue_first(head);
444: top = queue_entry(q, struct thread, ipc_link);
445: while (!queue_end(head, q)) {
446: th = queue_entry(q, struct thread, ipc_link);
447: if (th->prio < top->prio)
448: top = th;
449: q = queue_next(q);
450: }
451: queue_remove(&top->ipc_link);
452: return top;
453: }
454:
455: static void
456: msg_enqueue(queue_t head, thread_t th)
457: {
458:
459: enqueue(head, &th->ipc_link);
460: }
461:
462: void
463: msg_init(void)
464: {
465:
466: event_init(&ipc_event, "ipc");
467: }
CVSweb