=================================================================== RCS file: /cvs/prex-old/sys/ipc/msg.c,v retrieving revision 1.1.1.1 retrieving revision 1.1.1.1.2.1 diff -u -r1.1.1.1 -r1.1.1.1.2.1 --- prex-old/sys/ipc/msg.c 2008/06/03 10:38:46 1.1.1.1 +++ prex-old/sys/ipc/msg.c 2008/08/13 17:12:31 1.1.1.1.2.1 @@ -32,26 +32,28 @@ */ /* + * Design: + * * Messages are sent to the specific object by using msg_send(). * The transmission of a message is completely synchronous with - * this kernel. This means the thread which sent a message is blocked - * until it receives a response from another thread. msg_receive() - * performs reception of a message. msg_receive() is also blocked - * when no message is reached to the target object. The receiver - * thread must answer the message using msg_reply() after it finishes - * its message processing. + * this kernel. This means the thread which sent a message is + * blocked until it receives a response from another thread. + * msg_receive() performs reception of a message. msg_receive() is + * also blocked when no message is reached to the target object. + * The receiver thread must answer the message using msg_reply() + * after it finishes its message processing. * * The receiver thread can not receive another message until it * replies to the sender. In short, a thread can receive only one * message at once. Once the thread receives message, it can send - * another message to different object. This mechanism allows threads - * to redirect the sender's request to another thread. + * another message to different object. This mechanism allows + * threads to redirect the sender's request to another thread. * * The message is copied from thread to thread directly without any - * kernel buffering. If sent message contains a buffer, sender's memory - * region is automatically mapped to the receiver's memory in kernel. - * Since there is no page out of memory in this system, we can copy the - * message data via physical memory at anytime. + * kernel buffering. If sent message contains a buffer, sender's + * memory region is automatically mapped to the receiver's memory + * in kernel. Since there is no page out of memory in this system, + * we can copy the message data via physical memory at anytime. */ #include @@ -67,27 +69,26 @@ #define min(a,b) (((a) < (b)) ? (a) : (b)) /* forward declarations */ -static thread_t msg_dequeue(queue_t); -static void msg_enqueue(queue_t, thread_t); +static thread_t msg_dequeue(queue_t); +static void msg_enqueue(queue_t, thread_t); /* event for IPC operation */ static struct event ipc_event; /* - * msg_send - send a message. - * @obj: object ID to send a message. - * @msg: pointer to the message buffer - * @size: size of the message buffer. + * Send a message. * - * The current thread will be blocked until any other thread receives - * the message and calls msg_reply() for the target object. - * When new message has been reached to the object, it will be received - * by highest priority thread waiting for that message. - * A thread can send a message to any object if it knows the object id. + * The current thread will be blocked until any other thread + * receives the message and calls msg_reply() for the target + * object. When new message has been reached to the object, it + * will be received by highest priority thread waiting for + * that message. A thread can send a message to any object if + * it knows the object id. */ int msg_send(object_t obj, void *msg, size_t size) { + struct msg_header *hdr; thread_t th; void *kmsg; int rc; @@ -109,18 +110,19 @@ return EPERM; } /* - * A thread can not send a message when the thread is - * already receiving from the target object. This will - * obviously cause a deadlock. + * A thread can not send a message when the + * thread is already receiving from the target + * object. This will obviously cause a deadlock. */ - if (obj == cur_thread->recv_obj) { + if (obj == cur_thread->recvobj) { sched_unlock(); return EDEADLK; } /* - * Translate message address to the kernel linear address. - * So that a receiver thread can access the message via - * kernel pointer. We can catch the page fault here. + * Translate message address to the kernel linear + * address. So that a receiver thread can access + * the message via kernel pointer. We can catch + * the page fault here. */ if ((kmsg = kmem_map(msg, size)) == NULL) { /* Error - no physical address for the message */ @@ -128,18 +130,19 @@ return EFAULT; } /* - * Fill sender task ID in the message header. - * So, the receiver can trust this ID. + * The sender ID in the message header is filled + * by the kernel. So, the receiver can trust it. */ - ((struct msg_header *)kmsg)->task = cur_task(); + hdr = (struct msg_header *)kmsg; + hdr->task = cur_task(); - /* Save the data for message block. */ - cur_thread->msg_addr = kmsg; - cur_thread->msg_size = size; + /* Save information about the message block. */ + cur_thread->msgaddr = kmsg; + cur_thread->msgsize = size; /* - * If receiver already exists, wake it up. Highest priority - * thread will get this message. + * If receiver already exists, wake it up. + * Highest priority thread will get this message. */ if (!queue_empty(&obj->recvq)) { th = msg_dequeue(&obj->recvq); @@ -147,15 +150,17 @@ } /* * Sleep until we get a reply message. - * Note: we can not touch the data in obj after we wakeup - * because it may be deleted during we were sleeping. + * Note: Do not touch any data in the object + * structure after we wakeup. This is because the + * target object may be deleted during we were + * sleeping. */ - cur_thread->send_obj = obj; + cur_thread->sendobj = obj; msg_enqueue(&obj->sendq, cur_thread); rc = sched_sleep(&ipc_event); if (rc == SLP_INTR) queue_remove(&cur_thread->ipc_link); - cur_thread->send_obj = NULL; + cur_thread->sendobj = NULL; sched_unlock(); @@ -169,6 +174,9 @@ return EINVAL; /* Object has been deleted */ case SLP_INTR: return EINTR; /* Exception */ + default: + /* DO NOTHING */ + break; } return 0; } @@ -176,28 +184,28 @@ /* * Receive a message. * - * A thread can receive a message from the object which was created - * by any thread belongs to same task. If the message has not arrived - * yet, it blocks until any message comes in. + * A thread can receive a message from the object which was + * created by any thread belongs to same task. If the message + * has not arrived yet, it blocks until any message comes in. * * The size argument specifies the "maximum" size of the message - * buffer to receive. If the sent message is larger than this size, - * the kernel will automatically clip the message to the receive buffer - * size. + * buffer to receive. If the sent message is larger than this + * size, the kernel will automatically clip the message to the + * receive buffer size. * * When message is received, the sender thread is removed from - * object's send queue. So, another thread can receive the subsequent - * message from that object. This is important for the multi-thread - * server which receives some messages simultaneously. + * object's send queue. So, another thread can receive the + * subsequent message from that object. This is important for + * the multi-thread server which receives some messages + * simultaneously. */ int msg_receive(object_t obj, void *msg, size_t size) { thread_t th; - int err, rc; size_t len; + int rc, err = 0; - err = 0; if (!user_area(msg)) return EFAULT; @@ -212,67 +220,67 @@ goto out; } /* - * Check if this thread finished previous receive operation. - * A thread can not receive different messages at once. + * Check if this thread finished previous receive + * operation. A thread can not receive different + * messages at once. */ - if (cur_thread->recv_obj) { + if (cur_thread->recvobj) { err = EBUSY; goto out; } - cur_thread->recv_obj = obj; + cur_thread->recvobj = obj; /* * If no message exists, wait until message arrives. */ while (queue_empty(&obj->sendq)) { /* - * Sleep until message comes in. + * Block until someone sends the message. */ msg_enqueue(&obj->recvq, cur_thread); rc = sched_sleep(&ipc_event); - if (rc == 0) { + if (rc != 0) { /* - * Even if this thread is woken by the sender thread, - * the message may be received by another thread - * before this thread runs. This can occur when - * higher priority thread becomes runnable at that - * time. So, it is necessary to check the existence - * of the sender here again. The following line must - * be "continue" instead of "break" to check the - * queue again. + * Receive is failed due to some reasons. */ - continue; + switch (rc) { + case SLP_INVAL: + err = EINVAL; /* Object has been deleted */ + break; + case SLP_INTR: + queue_remove(&cur_thread->ipc_link); + err = EINTR; /* Got exception */ + break; + default: + panic("msg_receive: invalid wake reason"); + break; + } + cur_thread->recvobj = NULL; + goto out; } + /* - * Receive is failed by some reason. + * Even if this thread is woken by the sender thread, + * the message may be received by another thread + * before this thread runs. This can occur when + * higher priority thread becomes runnable at that + * time. So, it is necessary to check the existence + * of the sender, again. */ - switch (rc) { - case SLP_INVAL: - err = EINVAL; /* Object has been deleted */ - break; - case SLP_INTR: - queue_remove(&cur_thread->ipc_link); - err = EINTR; /* Got exception */ - break; - default: - panic("msg_receive"); - } - cur_thread->recv_obj = NULL; - goto out; } th = msg_dequeue(&obj->sendq); /* - * Copy message to user space. + * Copy out the message to the user-space. * The smaller buffer size is used as copy length * between sender and receiver thread. */ - len = min(size, th->msg_size); + len = min(size, th->msgsize); if (len > 0) { - if (umem_copyout(th->msg_addr, msg, len)) { + if (umem_copyout(th->msgaddr, msg, len)) { msg_enqueue(&obj->sendq, th); - cur_thread->recv_obj = NULL; + cur_thread->recvobj = NULL; err = EFAULT; goto out; } @@ -290,11 +298,12 @@ /* * Send a reply message. * - * The target object must be an appropriate object that current thread - * has been received from. Otherwise, this function will be failed. + * The target object must be an appropriate object that current + * thread has been received from. Otherwise, this function will + * be failed. * - * Since the target object may already be deleted, we can not access - * the data of the object within this routine. + * Since the target object may already be deleted, we can not + * access the data of the object within this routine. */ int msg_reply(object_t obj, void *msg, size_t size) @@ -308,7 +317,7 @@ sched_lock(); - if (!object_valid(obj) || obj != cur_thread->recv_obj) { + if (!object_valid(obj) || obj != cur_thread->recvobj) { sched_unlock(); return EINVAL; } @@ -323,9 +332,9 @@ * Copy message to the sender's buffer. */ th = cur_thread->sender; - len = min(size, th->msg_size); + len = min(size, th->msgsize); if (len > 0) { - if (umem_copyin(msg, th->msg_addr, len)) { + if (umem_copyin(msg, th->msgaddr, len)) { sched_unlock(); return EFAULT; } @@ -338,7 +347,7 @@ out: /* Clear transmit state */ cur_thread->sender = NULL; - cur_thread->recv_obj = NULL; + cur_thread->recvobj = NULL; sched_unlock(); return err; @@ -346,8 +355,7 @@ /* * Clean up pending message operation of specified thread in order - * to prevent deadlock. - * This is called when the thread is killed. + * to prevent deadlock. This is called when the thread is killed. * It is necessary to deal with the following conditions. * * If killed thread is sender: @@ -370,13 +378,13 @@ sched_lock(); - if (th->send_obj) { + if (th->sendobj) { if (th->receiver) th->receiver->sender = NULL; else queue_remove(&th->ipc_link); } - if (th->recv_obj) { + if (th->recvobj) { if (th->sender) { sched_unsleep(th->sender, SLP_BREAK); th->sender->receiver = NULL; @@ -387,33 +395,35 @@ } /* - * Cancel all message operation relevant to the specified object. - * This is called when target object is deleted. - * All threads in message queue are woken to avoid deadlock. - * If the message has already been received, send/reply operation + * Cancel all message operation relevant to the specified + * object. + * + * This is called when target object is deleted. All threads + * in message queue are woken to avoid deadlock. If the + * message has already been received, send/reply operation * continue processing normally. */ void msg_cancel(object_t obj) { - queue_t head, q; + queue_t q; thread_t th; sched_lock(); /* - * Force wakeup all thread in the send queue. + * Force wakeup all threads in the send queue. */ - head = &obj->sendq; - for (q = queue_first(head); !queue_end(head, q); q = queue_next(q)) { + while (!queue_empty(&obj->sendq)) { + q = dequeue(&obj->sendq); th = queue_entry(q, struct thread, ipc_link); sched_unsleep(th, SLP_INVAL); } /* - * Force wakeup all thread waiting for receive. + * Force wakeup all threads waiting for receive. */ - head = &obj->recvq; - for (q = queue_first(head); !queue_end(head, q); q = queue_next(q)) { + while (!queue_empty(&obj->recvq)) { + q = dequeue(&obj->sendq); th = queue_entry(q, struct thread, ipc_link); sched_unsleep(th, SLP_INVAL); }