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