[BACK]Return to msg.c CVS log [TXT][DIR] Up to [local] / prex-old / sys / ipc

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