Annotation of prex-old/sys/ipc/msg.c, Revision 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