1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
#include <assert.h>
#include <errno.h>
#include <kernel/msghandler.H>
#include <kernel/task.H>
#include <kernel/cpu.H>
#include <kernel/scheduler.H>
#include <kernel/taskmgr.H>
#include <kernel/console.H>
namespace Systemcalls { void TaskEnd(task_t*); };
void MessageHandler::sendMessage(msg_sys_types_t i_type, void* i_key,
void* i_data, task_t* i_task)
{
// Save pending info for when we get the response.
MessageHandler_Pending* mhp = new MessageHandler_Pending;
mhp->key = i_key;
mhp->task = i_task;
// Send userspace message if one hasn't been sent for this key.
if (!iv_pending.find(i_key))
{
// Create message.
msg_t* m = new msg_t;
m->type = i_type;
m->data[0] = reinterpret_cast<uint64_t>(i_key);
m->data[1] = reinterpret_cast<uint64_t>(i_data);
m->__reserved__async = 1;
// Create pending response object.
MessagePending* mp = new MessagePending();
mp->key = m;
mp->task = reinterpret_cast<task_t*>(this);
// Send to userspace...
iv_msgq->lock.lock();
task_t* waiter = iv_msgq->waiting.remove();
if (NULL == waiter) // No waiting task, queue for msg_wait call.
{
iv_msgq->messages.insert(mp);
}
else // Waiting task, set msg as return and release.
{
TASK_SETRTN(waiter, (uint64_t) m);
iv_msgq->responses.insert(mp);
waiter->cpu = i_task->cpu;
TaskManager::setCurrentTask(waiter);
}
iv_msgq->lock.unlock();
}
// Defer task while waiting for message response.
if ((NULL != i_task) && (TaskManager::getCurrentTask() == i_task))
{
i_task->cpu->scheduler->setNextRunnable();
}
// Insert pending info into our queue until response is recv'd.
iv_pending.insert(mhp);
}
int MessageHandler::recvMessage(msg_t* i_msg)
{
// Verify userspace didn't give a non-kernel message type.
if (i_msg->type < MSG_FIRST_SYS_TYPE)
{
printk("MessageHandler::recvMessage> type=%d\n", i_msg->type);
return -EINVAL;
}
// Lock subsystem spinlock.
if (iv_lock) iv_lock->lock();
// Get <key, rc> from response.
MessageHandler_Pending::key_type key =
reinterpret_cast<MessageHandler_Pending::key_type>(i_msg->data[0]);
int msg_rc = static_cast<int>(i_msg->data[1]);
// Handle all pending responses.
bool restored_task = false;
MessageHandler_Pending* mhp = NULL;
while (NULL != (mhp = iv_pending.find(key)))
{
task_t* deferred_task = mhp->task;
// Call 'handle response'.
HandleResult rc = this->handleResponse(
static_cast<msg_sys_types_t>(i_msg->type),
key, mhp->task, msg_rc);
// Remove pending information from outstanding queue.
iv_pending.erase(mhp);
delete mhp;
// If there is no associated task then there is nothing to do, find
// next pending response.
if (!deferred_task) continue;
// Handle action requested from 'handle response'.
if ((SUCCESS == rc) || (!msg_rc && UNHANDLED_RC == rc))
{
// Successful response, resume task.
if (!restored_task) // Immediately execute first deferred task.
{
restored_task = true;
TaskManager::setCurrentTask(deferred_task);
}
else // Add other deferred tasks to scheduler ready queue.
{
deferred_task->cpu->scheduler->addTask(deferred_task);
}
}
else if (UNHANDLED_RC == rc)
{
// Unsuccessful, unhandled response. Kill task.
printk("Unhandled msg rc %d for key %p on task %d @ %p\n",
msg_rc, key, deferred_task->tid, deferred_task->context.nip);
Systemcalls::TaskEnd(deferred_task);
}
else if (CONTINUE_DEFER == rc)
{
// Requested to continue deferring task. Do nothing.
}
else
{
// Logic bug (new HandleResult?). Shouldn't be here.
kassert(false);
}
}
// Finished handling the response, unlock subsystem.
if (iv_lock) iv_lock->unlock();
// Release memory for message (created from sendMsg).
delete(i_msg);
return 0;
}
MessageHandler::HandleResult MessageHandler::handleResponse(
msg_sys_types_t i_type, void* i_key, task_t* i_task, int i_rc)
{
// Indicate nothing specific has been done for this response. Request
// default behavior of resume/kill task based on rc.
return UNHANDLED_RC;
}
|