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

Annotation of prex/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:  * Design:
        !            36:  *
        !            37:  * Messages are sent to the specific object by using msg_send().
        !            38:  * The transmission of a message is completely synchronous with
        !            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.
        !            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
        !            49:  * another message to different object. This mechanism allows
        !            50:  * threads to redirect the sender's request to another thread.
        !            51:  *
        !            52:  * The message is copied from thread to thread directly without any
        !            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.
        !            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 */
        !            72: static thread_t        msg_dequeue(queue_t);
        !            73: static void    msg_enqueue(queue_t, thread_t);
        !            74:
        !            75: /* event for IPC operation */
        !            76: static struct event ipc_event;
        !            77:
        !            78: /*
        !            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.
        !            87:  */
        !            88: int
        !            89: msg_send(object_t obj, void *msg, size_t size)
        !            90: {
        !            91:        struct msg_header *hdr;
        !            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:        /*
        !           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.
        !           116:         */
        !           117:        if (obj == cur_thread->recvobj) {
        !           118:                sched_unlock();
        !           119:                return EDEADLK;
        !           120:        }
        !           121:        /*
        !           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.
        !           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:        /*
        !           133:         * The sender ID in the message header is filled
        !           134:         * by the kernel. So, the receiver can trust it.
        !           135:         */
        !           136:        hdr = (struct msg_header *)kmsg;
        !           137:        hdr->task = cur_task();
        !           138:
        !           139:        /* Save information about the message block. */
        !           140:        cur_thread->msgaddr = kmsg;
        !           141:        cur_thread->msgsize = size;
        !           142:
        !           143:        /*
        !           144:         * If receiver already exists, wake it up.
        !           145:         * Highest priority thread will get this message.
        !           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.
        !           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.
        !           157:         */
        !           158:        cur_thread->sendobj = obj;
        !           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);
        !           163:        cur_thread->sendobj = NULL;
        !           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 */
        !           177:        default:
        !           178:                /* DO NOTHING */
        !           179:                break;
        !           180:        }
        !           181:        return 0;
        !           182: }
        !           183:
        !           184: /*
        !           185:  * Receive a message.
        !           186:  *
        !           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.
        !           190:  *
        !           191:  * The size argument specifies the "maximum" size of the message
        !           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.
        !           195:  *
        !           196:  * When message is received, the sender thread is removed from
        !           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.
        !           201:  */
        !           202: int
        !           203: msg_receive(object_t obj, void *msg, size_t size)
        !           204: {
        !           205:        thread_t th;
        !           206:        size_t len;
        !           207:        int rc, err = 0;
        !           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:        /*
        !           223:         * Check if this thread finished previous receive
        !           224:         * operation.  A thread can not receive different
        !           225:         * messages at once.
        !           226:         */
        !           227:        if (cur_thread->recvobj) {
        !           228:                err = EBUSY;
        !           229:                goto out;
        !           230:        }
        !           231:        cur_thread->recvobj = obj;
        !           232:
        !           233:        /*
        !           234:         * If no message exists, wait until message arrives.
        !           235:         */
        !           236:        while (queue_empty(&obj->sendq)) {
        !           237:                /*
        !           238:                 * Block until someone sends the message.
        !           239:                 */
        !           240:                msg_enqueue(&obj->recvq, cur_thread);
        !           241:                rc = sched_sleep(&ipc_event);
        !           242:                if (rc != 0) {
        !           243:                        /*
        !           244:                         * Receive is failed due to some reasons.
        !           245:                         */
        !           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;
        !           260:                }
        !           261:
        !           262:                /*
        !           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.
        !           269:                 */
        !           270:        }
        !           271:
        !           272:        th = msg_dequeue(&obj->sendq);
        !           273:
        !           274:        /*
        !           275:         * Copy out the message to the user-space.
        !           276:         * The smaller buffer size is used as copy length
        !           277:         * between sender and receiver thread.
        !           278:         */
        !           279:        len = min(size, th->msgsize);
        !           280:        if (len > 0) {
        !           281:                if (umem_copyout(th->msgaddr, msg, len)) {
        !           282:                        msg_enqueue(&obj->sendq, th);
        !           283:                        cur_thread->recvobj = NULL;
        !           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:  *
        !           301:  * The target object must be an appropriate object that current
        !           302:  * thread has been received from. Otherwise, this function will
        !           303:  * be failed.
        !           304:  *
        !           305:  * Since the target object may already be deleted, we can not
        !           306:  * access the data of the object within this routine.
        !           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:
        !           320:        if (!object_valid(obj) || obj != cur_thread->recvobj) {
        !           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;
        !           335:        len = min(size, th->msgsize);
        !           336:        if (len > 0) {
        !           337:                if (umem_copyin(msg, th->msgaddr, len)) {
        !           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;
        !           350:        cur_thread->recvobj = NULL;
        !           351:
        !           352:        sched_unlock();
        !           353:        return err;
        !           354: }
        !           355:
        !           356: /*
        !           357:  * Clean up pending message operation of specified thread in order
        !           358:  * to prevent deadlock. This is called when the thread is killed.
        !           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:
        !           381:        if (th->sendobj) {
        !           382:                if (th->receiver)
        !           383:                        th->receiver->sender = NULL;
        !           384:                else
        !           385:                        queue_remove(&th->ipc_link);
        !           386:        }
        !           387:        if (th->recvobj) {
        !           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: /*
        !           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
        !           404:  * continue processing normally.
        !           405:  */
        !           406: void
        !           407: msg_cancel(object_t obj)
        !           408: {
        !           409:        queue_t q;
        !           410:        thread_t th;
        !           411:
        !           412:        sched_lock();
        !           413:
        !           414:        /*
        !           415:         * Force wakeup all threads in the send queue.
        !           416:         */
        !           417:        while (!queue_empty(&obj->sendq)) {
        !           418:                q = dequeue(&obj->sendq);
        !           419:                th = queue_entry(q, struct thread, ipc_link);
        !           420:                sched_unsleep(th, SLP_INVAL);
        !           421:        }
        !           422:        /*
        !           423:         * Force wakeup all threads waiting for receive.
        !           424:         */
        !           425:        while (!queue_empty(&obj->recvq)) {
        !           426:                q = dequeue(&obj->sendq);
        !           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