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