[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.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