summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/include/sys/msg.h47
-rw-r--r--src/kernel/syscall.C61
-rw-r--r--src/lib/syscall_msg.C16
-rw-r--r--src/usr/testcore/kernel/msgtest.H124
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;
+ }
+ }
+ }
+};
OpenPOWER on IntegriCloud