[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

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