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