diff options
-rw-r--r-- | src/include/sys/msg.h | 47 | ||||
-rw-r--r-- | src/kernel/syscall.C | 61 | ||||
-rw-r--r-- | src/lib/syscall_msg.C | 16 | ||||
-rw-r--r-- | src/usr/testcore/kernel/msgtest.H | 124 |
4 files changed, 237 insertions, 11 deletions
diff --git a/src/include/sys/msg.h b/src/include/sys/msg.h index f58e3c8f1..4bbf0b265 100644 --- a/src/include/sys/msg.h +++ b/src/include/sys/msg.h @@ -40,7 +40,8 @@ struct msg_t struct { uint32_t __reserved__async:1; - uint32_t __reserved__unused:31; + uint32_t __reserved__pseudosync:1; + uint32_t __reserved__unused:30; }; uint64_t data[2]; void* extra_data; @@ -162,8 +163,7 @@ msg_q_t msg_q_resolve(const char* name); * @brief Allocate space for message * @return Pointer to message */ -ALWAYS_INLINE - inline msg_t* msg_allocate() { return (msg_t*)malloc(sizeof(msg_t)); } +msg_t* msg_allocate(); /** @fn msg_free @@ -206,6 +206,23 @@ int msg_send(msg_q_t q, msg_t* msg); int msg_sendrecv(msg_q_t q, msg_t* msg); +/** @fn msg_sendrecv_noblk + * @brief Sends a message to a server and get a response without blocking. + * + * From the persepective of the calling [client] task, the message + * transaction is asynchronous, but from the perspective of the recipient + * [server] it is synchronous. When the recipient replies to the message + * the message is relayed onto a secondary message queue that the caller + * provided. + * + * @param[in] q - The message queue to send on. + * @param[in] msg - The message. + * @param[in] q2 - The secondary queue for the response. + * + * @return Zero on success, else negative. + */ +int msg_sendrecv_noblk(msg_q_t q, msg_t* msg, msg_q_t q2); + /** @fn msg_respond * @brief Respond to a synchronous message. * @@ -238,14 +255,34 @@ msg_t* msg_wait(msg_q_t q); /** @fn msg_is_async * @brief Indicates if message is asynchronous. * - * Tests the message field "__reserved__async" which appears to be set to 0 to indicate asynchronous, and 1 to indicate synchronous message. + * Tests the reserved message fields to determine if the message is + * asynchronous or synchronous. These fields are only manipulated by + * system-call or kernel code to maintain the proper state of the fields + * based on the msg interfaces used. * * @return true if asynchronous message */ ALWAYS_INLINE inline uint32_t msg_is_async(msg_t* msg) -{ return 0 == msg->__reserved__async; } +{ + return (0 == msg->__reserved__async); +} +/** @fn msg_is_sync_noblk + * @brief Indicates if the message is a non-blocking synchronous message. + * + * Tests the reserved message fields to determine if the message is + * a blocking synchronous message. These fields are only manipulated by + * system-call or kernel code to maintain the proper state of the fields + * based on the msg interfaces used. + * + * @return true if non-blocking synchronous message. + */ + ALWAYS_INLINE +inline uint32_t msg_is_sync_noblk(msg_t* msg) +{ + return (1 == msg->__reserved__pseudosync); +} #ifdef __cplusplus } diff --git a/src/kernel/syscall.C b/src/kernel/syscall.C index f0f5b81f0..4d723852f 100644 --- a/src/kernel/syscall.C +++ b/src/kernel/syscall.C @@ -336,7 +336,13 @@ namespace Systemcalls { MessageQueue* mq = (MessageQueue*) TASK_GETARG0(t); msg_t* m = (msg_t*) TASK_GETARG1(t); + MessageQueue* mq2 = (MessageQueue*) TASK_GETARG2(t); + m->__reserved__async = 1; // set to sync msg. + if (NULL != mq2) // set as pseudo-sync if secondary queue given. + { + m->__reserved__pseudosync = 1; + } if (m->type >= MSG_FIRST_SYS_TYPE) { @@ -349,9 +355,18 @@ namespace Systemcalls // Create pending response object. MessagePending* mp = new MessagePending(); mp->key = m; - mp->task = t; - t->state = TASK_STATE_BLOCK_MSG; - t->state_info = mq; + if (!m->__reserved__pseudosync) // Normal sync, add task to pending obj. + { + mp->task = t; + t->state = TASK_STATE_BLOCK_MSG; + t->state_info = mq; + } + else // Pseudo-sync, add the secondary queue instead. + { + mp->task = reinterpret_cast<task_t*>(mq2); + TASK_SETRTN(t, 0); // Need to give good RC for the caller, since + // we are returning immediately. + } mq->lock.lock(); @@ -360,14 +375,22 @@ namespace Systemcalls if (NULL == waiter) // None found, add to 'messages' queue. { mq->messages.insert(mp); - // Choose next thread to execute, this one is delayed. - t->cpu->scheduler->setNextRunnable(); + if (!m->__reserved__pseudosync) + { + // Choose next thread to execute, this one is delayed. + t->cpu->scheduler->setNextRunnable(); + } // For pseudo-sync, just keep running the current task. } else // Context switch to waiter. { TASK_SETRTN(waiter, (uint64_t) m); mq->responses.insert(mp); waiter->cpu = t->cpu; + if (m->__reserved__pseudosync) // For pseudo-sync, add this task + // back to scheduler. + { + t->cpu->scheduler->addTask(t); + } TaskManager::setCurrentTask(waiter); } @@ -389,6 +412,7 @@ namespace Systemcalls mq->lock.unlock(); delete mp; + // Kernel message types are handled by MessageHandler objects. if (m->type >= MSG_FIRST_SYS_TYPE) { TASK_SETRTN(t, @@ -399,6 +423,33 @@ namespace Systemcalls t->cpu->scheduler->addTask(t); } } + // Pseudo-sync messages are handled by pushing the response onto + // a message queue. + else if (m->__reserved__pseudosync) + { + MessageQueue* mq2 = (MessageQueue*) waiter; + mq2->lock.lock(); + + // See if there is a waiting task (the original client). + task_t* client = mq2->waiting.remove(); + if (NULL == client) // None found, add to queue. + { + MessagePending* mp2 = new MessagePending(); + mp2->key = m; + mp2->task = t; + mq2->messages.insert(mp2); + } + else // Add waiting task onto its scheduler. + { + TASK_SETRTN(client, (uint64_t) m); + client->cpu->scheduler->addTask(client); + } + + mq2->lock.unlock(); + TASK_SETRTN(t, 0); + + } + // Normal-sync messages are handled by releasing the deferred task. else { waiter->cpu = t->cpu; diff --git a/src/lib/syscall_msg.C b/src/lib/syscall_msg.C index 1b0ea500a..8374d0d34 100644 --- a/src/lib/syscall_msg.C +++ b/src/lib/syscall_msg.C @@ -93,6 +93,15 @@ msg_q_t msg_q_resolve(const char* name) } } +msg_t* msg_allocate() +{ + msg_t* msg = reinterpret_cast<msg_t*>(malloc(sizeof(msg_t))); + + memset(msg, 0, sizeof(msg_t)); + + return msg; +} + int msg_send(msg_q_t q, msg_t* msg) { return (int64_t)_syscall2(MSG_SEND, q, msg); @@ -100,7 +109,12 @@ int msg_send(msg_q_t q, msg_t* msg) int msg_sendrecv(msg_q_t q, msg_t* msg) { - return (int64_t)_syscall2(MSG_SENDRECV, q, msg); + return (int64_t)_syscall3(MSG_SENDRECV, q, msg, NULL); +} + +int msg_sendrecv_noblk(msg_q_t q, msg_t* msg, msg_q_t q2) +{ + return (int64_t)_syscall3(MSG_SENDRECV, q, msg, q2); } int msg_respond(msg_q_t q, msg_t* msg) diff --git a/src/usr/testcore/kernel/msgtest.H b/src/usr/testcore/kernel/msgtest.H new file mode 100644 index 000000000..995baace3 --- /dev/null +++ b/src/usr/testcore/kernel/msgtest.H @@ -0,0 +1,124 @@ +// IBM_PROLOG_BEGIN_TAG +// This is an automatically generated prolog. +// +// $Source: src/usr/testcore/kernel/msgtest.H $ +// +// IBM CONFIDENTIAL +// +// COPYRIGHT International Business Machines Corp. 2012 +// +// p1 +// +// Object Code Only (OCO) source materials +// Licensed Internal Code Source Materials +// IBM HostBoot Licensed Internal Code +// +// The source code for this program is not published or other- +// wise divested of its trade secrets, irrespective of what has +// been deposited with the U.S. Copyright Office. +// +// Origin: 30 +// +// IBM_PROLOG_END + +/** @file msgtest.H + * @brief Testcases for the messaging subsystem of the kernel. + */ + +#include <cxxtest/TestSuite.H> +#include <sys/task.h> +#include <sys/msg.h> + +class MessagingTest : public CxxTest::TestSuite +{ + public: + + /** Test sync-messaging interfaces */ + void testSync() + { + msg_q_t client = msg_q_create(), + server = msg_q_create(); + + msg_t* sync_msg = NULL; + int rc = 0; + + tid_t server_task = task_create(MessagingTest::processMsg, + server); + + // Send a message with the normal sync messaging interface. + sync_msg = msg_allocate(); + sync_msg->type = ECHO; + sync_msg->data[0] = 1; + rc = msg_sendrecv(server, sync_msg); + if (rc) + { + TS_FAIL("Failure to send message (msg_sendrecv)."); + } + else if (sync_msg->data[0] != (2 << 1)) + { + TS_FAIL("Message response doesn't match expected value for " + "sync message. %d", sync_msg->data[0]); + } + msg_free(sync_msg); + + // Send a message with the non-blocking sync message interface. + barrier_t barrier; + barrier_init(&barrier, 2); + sync_msg = msg_allocate(); + sync_msg->type = ECHO; + sync_msg->data[0] = 2; + sync_msg->data[1] = reinterpret_cast<uint64_t>(&barrier); + rc = msg_sendrecv_noblk(server, sync_msg, client); + barrier_wait(&barrier); + sync_msg = msg_wait(client); + if (rc) + { + TS_FAIL("Failure to send message (msg_sendrecv_noblk)"); + } + else if (sync_msg->data[0] != (2 << 2)) + { + TS_FAIL("Message response doesn't match expected value for " + "no-blk sync message. %d", sync_msg->data[0]); + } + msg_free(sync_msg); + barrier_destroy(&barrier); + + // Shutdown the child thread. + msg_t* shutdown_msg = msg_allocate(); + shutdown_msg->type = SHUTDOWN; + msg_send(server, shutdown_msg); + + task_wait_tid(server_task, NULL, NULL); + + }; + + private: + + enum msg_types { SHUTDOWN, ECHO }; + + static void processMsg(void* _msgQ) + { + msg_q_t msgQ = static_cast<msg_q_t>(_msgQ); + + while(msg_t* msg = msg_wait(msgQ)) + { + switch (msg->type) + { + case SHUTDOWN: // Shutdown. + msg_free(msg); + task_end(); + break; + + case ECHO: + if (msg->data[1]) + { + barrier_wait( + reinterpret_cast<barrier_t*>(msg->data[1])); + } + msg->data[0] = (2 << msg->data[0]); + msg_respond(msgQ, msg); + break; + } + } + } +}; |