summaryrefslogtreecommitdiffstats
path: root/src/kernel/msghandler.C
blob: 71a9d84d13042712a49a9df2f6259e436d0b99b7 (plain)
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/*  IBM_PROLOG_BEGIN_TAG
 *  This is an automatically generated prolog.
 *
 *  $Source: src/kernel/msghandler.C $
 *
 *  IBM CONFIDENTIAL
 *
 *  COPYRIGHT International Business Machines Corp. 2011-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_TAG
 */
#include <assert.h>
#include <errno.h>
#include <util/locked/queue.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>

void MessageHandler::sendMessage(msg_sys_types_t i_type, void* i_key,
                                 void* i_data, task_t* i_task)
{
    // Task to switch to due to waiter being ready to handle message.
    task_t* ready_task = NULL;

    // 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);
            ready_task = waiter;
        }
        iv_msgq->lock.unlock();
    }

    // Defer task while waiting for message response.
    if (NULL != i_task)
    {
        // Set block status.
        i_task->state = TASK_STATE_BLOCK_USRSPACE;
        i_task->state_info = i_key;

        if (i_task == TaskManager::getCurrentTask())
        {
            // Switch to ready waiter, or pick a new task off the scheduler.
            if (ready_task)
            {
                TaskManager::setCurrentTask(ready_task);
                ready_task = NULL;
            }
            else
            {
                // Select next task off scheduler.
                i_task->cpu->scheduler->setNextRunnable();
            }
        }
    }

    // Switch to ready waiter.
    if (NULL != ready_task)
    {
        task_t* current = TaskManager::getCurrentTask();
        current->cpu->scheduler->addTask(current);
        TaskManager::setCurrentTask(ready_task);
        ready_task = NULL;
    }

    // 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)
    {
        printkd("MessageHandler::recvMessage> type=%d\n", i_msg->type);
        return -EINVAL;
    }

    // Lock subsystem spinlock.
    if (iv_lock) iv_lock->lock();

    // List of tasks to end due to errors.
    //     Ending the task must happen outside of the spinlock due to
    //     requirements of TaskManager::endTask.
    Util::Locked::Queue<task_t> endTaskList;

    // 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);
            endTaskList.insert(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();

    while(task_t* end_task = endTaskList.remove())
    {
        TaskManager::endTask(end_task, NULL, TASK_STATUS_CRASHED);
    }

    // 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;
}
OpenPOWER on IntegriCloud