Commit 585e4dfd authored by Charlie Jacobsen's avatar Charlie Jacobsen
Browse files

Clarify thc ipc recv/send, add return statuses to some functions.

    -- thc_ipc_send ==> thc_ipc_send_request
    -- thc_ipc_recv ==> thc_ipc_recv_response
    -- thc_ipc_recv_response ignores requests and messages that
       are neither requests or responses (i.e., it only handles
       responses). See updated header documentation.
    -- awe_mapper_create_id and awe_mapper_get_id no longer
       BUG on error conditions; they return a non-zero exit status
       instead.
    -- THCYieldToId and THCYieldToIdAndSave return non-zero if
       the id does not match an awe.

Not built or tested yet. Updated examples. (In the context switch
example, I just BUG on in there, so you have the same semantics
as before.)
parent b3789f7a
Pipeline #640 skipped
......@@ -84,13 +84,17 @@ static bool is_slot_allocated(uint32_t id)
/*
* Returns new available id.
*/
uint32_t
int
LIBASYNC_FUNC_ATTR
awe_mapper_create_id(void)
awe_mapper_create_id(uint32_t *new_id)
{
awe_table_t *awe_map = get_awe_map();
BUG_ON(unlikely((awe_map->used_slots >= AWE_TABLE_COUNT) &&
"Too many slots have been requested."));
if (unlikely(awe_map->used_slots >= AWE_TABLE_COUNT))
{
printk(KERN_ERR "awe_mapper_create_id: too many slots requested\n");
return -ENOMEM;
}
do
{
......@@ -98,11 +102,13 @@ awe_mapper_create_id(void)
}
while( is_slot_allocated(awe_map->next_id) );
(awe_map->awe_list)[awe_map->next_id] = (void*)initialized_marker;
awe_map->awe_list[awe_map->next_id] = (void*)initialized_marker;
awe_map->used_slots++;
return awe_map->next_id;
*new_id = awe_map->next_id;
return 0;
}
EXPORT_SYMBOL(awe_mapper_create_id);
......@@ -150,8 +156,8 @@ void*
LIBASYNC_FUNC_ATTR
awe_mapper_get_awe_ptr(uint32_t id)
{
awe_table_t *awe_map = get_awe_map();
BUG_ON(unlikely(id >= AWE_TABLE_COUNT));
return (awe_map->awe_list)[id];
awe_table_t *awe_map = get_awe_map();
if (id >= AWE_TABLE_COUNT)
return NULL;
return awe_map->awe_list[id];
}
......@@ -1069,13 +1069,19 @@ static void thc_yieldto_with_cont(void *a, void *arg) {
thc_awe_execute_0(awe);
}
void
int
LIBASYNC_FUNC_ATTR
THCYieldToIdAndSave(uint32_t id_to, uint32_t id_from) {
awe_t *awe_ptr = (awe_t *)awe_mapper_get_awe_ptr(id_to);
if (!awe_ptr)
return -EINVAL; // id_to not valid
if ( likely(PTS() == awe_ptr->pts) ) {
// Switch to awe_ptr
CALL_CONT_LAZY_AND_SAVE((void*)&thc_yieldto_with_cont, id_from, (void*)awe_ptr);
// We were woken up
return 0;
}
//NOTE: for multiple threads, the code in the 'else'
//probably needs to be changed so that
......@@ -1083,20 +1089,30 @@ THCYieldToIdAndSave(uint32_t id_to, uint32_t id_from) {
//as long as we are assuming our single threaded model.
else {
THCSchedule(awe_ptr);
return 0;
}
}
EXPORT_SYMBOL(THCYieldToIdAndSave);
void
int
LIBASYNC_FUNC_ATTR
THCYieldToId(uint32_t id_to)
{
awe_t *awe_ptr = (awe_t *)awe_mapper_get_awe_ptr(id_to);
if (!awe_ptr) {
return -EINVAL;
}
if ( likely(PTS() == awe_ptr->pts) ) {
// Switch to target awe
CALL_CONT_LAZY((void*)&thc_yieldto_with_cont, (void*)awe_ptr);
// We were woken up
return 0;
}
else {
THCSchedule(awe_ptr);
return 0;
}
}
EXPORT_SYMBOL(THCYieldToId);
......
......@@ -32,26 +32,40 @@ thc_channel_init(struct thc_channel *chnl,
}
EXPORT_SYMBOL(thc_channel_init);
//assumes msg is a valid received message
static int thc_recv_predicate(struct fipc_message* msg, void* data)
{
struct predicate_payload* payload_ptr = (struct predicate_payload*)data;
if( thc_get_msg_type(msg) == (uint32_t)msg_type_request )
{
payload_ptr->msg_type = msg_type_request;
return 1;
}
else if( thc_get_msg_id(msg) == payload_ptr->expected_msg_id )
{
payload_ptr->msg_type = msg_type_response;
return 1; //message for this awe
}
else
{
payload_ptr->actual_msg_id = thc_get_msg_id(msg);
return 0; //message not for this awe
}
struct predicate_payload* payload_ptr = (struct predicate_payload*)data;
payload_ptr->msg_type = thc_get_msg_type(msg);
if (payload_ptr->msg_type == msg_type_request) {
/*
* Ignore requests
*/
return 0;
} else if (payload_ptr->msg_type == msg_type_response) {
payload_ptr->actual_msg_id = thc_get_msg_id(msg);
if (payload_ptr->actual_msg_id ==
payload_ptr->expected_msg_id) {
/*
* Response is for us; tell libfipc we want it
*/
return 1;
} else {
/*
* Response for another awe; we will switch to
* them. Meanwhile, the message should stay in
* rx. They (the awe we switch to) will pick it up.
*/
return 0;
}
} else {
/*
* Ignore any other message types
*/
return 0;
}
}
//assumes msg is a valid received message
......@@ -71,49 +85,121 @@ static int poll_recv_predicate(struct fipc_message* msg, void* data)
}
}
static void drop_one_rx_msg(struct thc_channel *chnl)
{
int ret;
struct fipc_message *msg;
ret = fipc_recv_msg_start(chnl, &msg);
if (ret)
printk(KERN_ERR "thc_ipc_recv_response: failed to drop bad message\n");
ret = fipc_recv_msg_end(chnl, &msg);
if (ret)
printk(KERN_ERR "thc_ipc_recv_response: failed to drop bad message (mark as received)\n");
return ret;
}
static void try_yield(struct thc_channel *chnl, uint32_t our_request_cookie,
uint32_t received_request_cookie)
{
int ret;
/*
* Switch to the pending awe the response belongs to
*/
ret = THCYieldToIdAndSave(received_request_cookie,
our_request_cookie);
if (ret) {
/*
* Oops, the switch failed
*/
printk(KERN_ERR "thc_ipc_recv_response: Invalid request cookie 0x%x received; dropping the message\n",
received_request_cookie);
drop_one_rx_msg(chnl);
return;
}
/*
* We were woken back up
*/
return;
}
int
LIBASYNC_FUNC_ATTR
thc_ipc_recv(struct thc_channel *chnl,
unsigned long msg_id,
struct fipc_message** out_msg)
thc_ipc_recv_response(struct thc_channel *chnl,
uint32_t request_cookie,
struct fipc_message **response)
{
struct predicate_payload payload = {
.expected_msg_id = msg_id
};
int ret;
while( true )
{
struct predicate_payload payload = {
.expected_msg_id = request_cookie
};
int ret;
retry:
ret = fipc_recv_msg_if(thc_channel_to_fipc(chnl), thc_recv_predicate,
&payload, out_msg);
if( !ret ) //message for us
{
if( payload.msg_type == msg_type_response )
{
awe_mapper_remove_id(msg_id);
}
return 0;
}
else if( ret == -ENOMSG ) //message not for us
{
THCYieldToIdAndSave((uint32_t)payload.actual_msg_id,
(uint32_t) msg_id);
if (unlikely(thc_channel_is_dead(chnl)))
return -EPIPE; // someone killed the channel
}
else if( ret == -EWOULDBLOCK ) //no message, Yield
{
THCYieldAndSave((uint32_t) msg_id);
if (unlikely(thc_channel_is_dead(chnl)))
return -EPIPE; // someone killed the channel
}
else
{
printk(KERN_ERR "error in thc_ipc_recv: %d\n", ret);
return ret;
}
}
&payload, response);
if (ret == 0) {
/*
* Message for us; remove request_cookie from awe mapper
*/
awe_mapper_remove_id(request_cookie);
return 0;
} else if (ret == -ENOMSG && payload.msg_type == msg_type_request) {
/*
* Ignore requests; yield so someone else can receive it (msgs
* are received in fifo order).
*/
goto yield;
} else if (ret == -ENOMSG && payload.msg_type == msg_type_response) {
/*
* Response for someone else. Try to yield to them.
*/
try_yield(chnl, request_cookie, payload.actual_msg_id);
/*
* We either yielded to the pending awe the response
* belonged to, or the switch failed.
*
* Make sure the channel didn't die in case we did go to
* sleep.
*/
if (unlikely(thc_channel_is_dead(chnl)))
return -EPIPE; /* someone killed the channel */
goto retry;
} else if (ret == -ENOMSG) {
/*
* Unknown or unspecified message type; yield and let someone
* else handle it.
*/
goto yield;
} else if (ret == -EWOULDBLOCK) {
/*
* No messages in rx buffer; go to sleep.
*/
goto yield;
} else {
/*
* Error
*/
printk(KERN_ERR "thc_ipc_recv_response: fipc returned %d\n",
ret);
return ret;
}
yield:
/*
* Go to sleep, we will be woken up at some later time
* by the dispatch loop or some other awe.
*/
THCYieldAndSave(request_cookie);
/*
* We were woken up; make sure the channel didn't die while
* we were asleep.
*/
if (unlikely(thc_channel_is_dead(chnl)))
return -EPIPE; /* someone killed the channel */
else
goto retry;
}
EXPORT_SYMBOL(thc_ipc_recv);
EXPORT_SYMBOL(thc_ipc_recv_response);
int
LIBASYNC_FUNC_ATTR
......@@ -183,35 +269,30 @@ thc_ipc_call(struct thc_channel *chnl,
struct fipc_message *request,
struct fipc_message **response)
{
uint32_t msg_id;
uint32_t request_cookie;
int ret;
/*
* Get an id for our current awe, and store in request.
*/
msg_id = awe_mapper_create_id();
thc_set_msg_type(request, msg_type_request);
thc_set_msg_id(request, msg_id);
/*
* Send request
*/
ret = fipc_send_msg_end(thc_channel_to_fipc(chnl), request);
ret = thc_ipc_send(chnl, request, &request_cookie);
if (ret) {
printk(KERN_ERR "thc: error sending request");
goto fail1;
printk(KERN_ERR "thc_ipc_call: error sending request\n");
goto fail1;
}
/*
* Receive response
*/
ret = thc_ipc_recv(chnl, msg_id, response);
ret = thc_ipc_recv_response(chnl, request_cookie, response);
if (ret) {
printk(KERN_ERR "thc: error receiving response");
goto fail1;
printk(KERN_ERR "thc_ipc_call: error receiving response\n");
goto fail2;
}
return 0;
fail2:
awe_mapper_remove_id(request_cookie);
fail1:
awe_mapper_remove_id(msg_id);
return ret;
}
EXPORT_SYMBOL(thc_ipc_call);
......@@ -227,7 +308,11 @@ thc_ipc_send(struct thc_channel *chnl,
/*
* Get an id for our current awe, and store in request.
*/
msg_id = awe_mapper_create_id();
ret = awe_mapper_create_id(&msg_id);
if (ret) {
printk(KERN_ERR "thc_ipc_send: error getting request cookie\n");
goto fail0;
}
thc_set_msg_type(request, msg_type_request);
thc_set_msg_id(request, msg_id);
/*
......@@ -245,6 +330,7 @@ thc_ipc_send(struct thc_channel *chnl,
fail1:
awe_mapper_remove_id(msg_id);
fail0:
return ret;
}
EXPORT_SYMBOL(thc_ipc_send);
......
......@@ -39,9 +39,11 @@ void awe_mapper_init(void);
void awe_mapper_uninit(void);
/*
* Returns new available id.
* Returns new available id as out param.
*
* Returns non-zero on failure.
*/
uint32_t awe_mapper_create_id(void);
int awe_mapper_create_id(uint32_t *new_id);
/*
* Marks provided id as available
......@@ -55,6 +57,9 @@ void awe_mapper_set_id(uint32_t id, void* awe_ptr);
/*
* Returns awe_ptr that corresponds to id.
*
* If there is no awe associated with id, returns
* NULL.
*/
void* awe_mapper_get_awe_ptr(uint32_t id);
......
......@@ -306,15 +306,21 @@ void THCYield(void);
// to THCYieldTo on the run-queue.)
void THCYieldTo(awe_t *awe_ptr);
//Same as THCYieldTo except that an id number is provided to locate a particular
//awe that was set to correspond to this id_num.
//id_to is the id that corresponds to the awe that should be yielded to.
//id_from is the id that corresponds to the awe that should save the
//current context.
void THCYieldToIdAndSave(uint32_t id_to, uint32_t id_from);
//Yields to without saving curent awe id.
void THCYieldToId(uint32_t id_to);
// Same as THCYieldTo except that an id number is provided to locate a
// particular awe that was set to correspond to this id_num.
//
// id_to is the id that corresponds to the awe that should be yielded to.
//
// id_from is the id that corresponds to the awe that should save the
// current context.
//
// If id_to is invalid, returns -EINVAL, zero otherwise.
int THCYieldToIdAndSave(uint32_t id_to, uint32_t id_from);
// Yields to without saving curent awe id.
//
// If there is no awe associated with id_to, returns non-zero.
int THCYieldToId(uint32_t id_to);
// Cancellation actions. These are executed in LIFO order when cancellation
// occurs. Once cancellation has been requested, it is assumed that no
......
......@@ -116,12 +116,13 @@ thc_channel_to_fipc(struct thc_channel *chnl)
* NOTE: The caller of these functions should ensure they hold a reference
* to the thc_channel (i.e., the caller is the creator of the channel, or
* has explicitly taken a reference via thc_channel_inc_ref). This is
* especially important in thc_ipc_recv/thc_ipc_poll_recv, where the "state
* of the world" can change when we yield and then get scheduled again.
* especially important in thc_ipc_recv_response/thc_ipc_poll_recv, where
* the "state of the world" can change when we yield and then get scheduled
* again later on.
*/
/*
* thc_ipc_send
* thc_ipc_send_request
*
* Use case: You are expecting a response after
* this send, and you want the calling awe to handle
......@@ -150,61 +151,95 @@ thc_channel_to_fipc(struct thc_channel *chnl)
*
* Returns 0 on success, non-zero otherwise.
*/
int thc_ipc_send(struct thc_channel *chnl,
struct fipc_message *request,
uint32_t *request_cookie);
int thc_ipc_send_request(struct thc_channel *chnl,
struct fipc_message *request,
uint32_t *request_cookie);
/*
* thc_ipc_recv
* thc_ipc_recv_response
*
* Receives the fipc message that matches request_cookie.
*
* Use case: You did an earlier thc_ipc_send, and you want
* to receive the response.
*
* The request_cookie (message id) is used to receive the response
* with a particular id and must always be valid. If a message is
* received but its id does not match the id provided to this function,
* then this function will check if the message corresponds to another
* pending AWE, and if so, it will yield to that AWE. If there is
* not a message that has been received yet, it will yield to dispatch any
* pending work. If a message is received that corresponds to the provided
* request_cookie, this function will put the message in the *out_msg
* parameter and return.
*
* This last case is the only time the function will return, assuming
* there are no error conditions. In the other two cases involving yields,
* execution will eventually come back to this function until it receives
* the message corresponding to request_cookie.
* Receive the response from a prior thc_ipc_send_request.
*
* thc_ipc_recv_response will peek at the fipc channel to see
* if the response has arrived. It uses the request_cookie to
* identify the response.
*
* There are a few things that can happen:
*
* 1 - No message is in the channel (i.e., the rx buffer).
* thc_ipc_recv_response will yield back to the dispatch
* loop, to be scheduled at a later time. (If there are
* no other awe's, the dispatch loop will just keep
* switching back to thc_ipc_recv_response, and thc_ipc_recv_response
* keep yielding.)
* 2 - A response arrives from the other side, and its
* request cookie matches request_cookie. thc_ipc_recv_response
* will return the response as an out parameter. This
* is the target scenario.
* 3 - A response arrives with a request cookie that does not match
* request_cookie. There are two subcases here:
*
* 3a - The response has a request_cookie that
* belongs to a pending awe. thc_ipc_recv_response
* will switch to that pending awe X. If X
* had not called thc_ipc_recv_response when
* it yielded, or does not end up calling
* thc_ipc_recv_response when it is woken up,
* you could run into trouble.
*
* 3b - The response has a request_cookie that
* *does not* belong to a pending awe.
* thc_ipc_recv_response will print an error notice
* to the console, receive and drop the message,
* and start over.
*
* XXX: This case may pose a security issue if the sender of the
* response passes a request cookie for an awe that didn't
* even send a request ...
*
* 4 - A *request* arrives from the other side, rather than
* a response. (This can happen if you are using the
* same fipc channel for requests and responses, i.e.,
* you receive requests and responses in the same rx
* buffer.) thc_ipc_recv_response will ignore the message, and
* yield as if no message was received. (Some other
* awe will need to pick up the request.)
*
* It is the caller's responsibility to mark the message as having completed
* the receive. (ex: fipc_recv_msg_end(chnl, msg))
*
* Returns 0 on success, non-zero otherwise.
*/
int thc_ipc_recv(struct thc_channel *chnl,
unsigned long msg_id,
struct fipc_message** out_msg);
int thc_ipc_recv_response(struct thc_channel *chnl,
uint32_t request_cookie,
struct fipc_message **response);
/* thc_poll_recv
*
* Same as thc_ipc_recv except that if no message is present, it
* returns -EWOULDBLOCK instead of yielding.
* Looks at the fipc channel, and does the following:
*
* -- If a request is waiting in the rx buffer, returns
* it.
* -- If a response is waiting, attempts to dispatch to
* the matching pending awe (like thc_ipc_recv_response does).
* If no awe matches, prints error to console, drops
* message, and returns -ENOMSG.
* -- If no message is waiting in the rx buffer, returns
* -EWOULDBLOCK (rather than yieldling).
*
* There is also no request_cookie passed in because poll_recv should
* There is no request_cookie passed in because thc_ipc_poll_recv should
* not expect a specific message.
*
* Returns 0 on success, non-zero otherwise.
*/
int thc_ipc_poll_recv(struct thc_channel* chnl,
struct fipc_message** out_msg);
int thc_ipc_poll_recv(struct thc_channel *chnl,
struct fipc_message **out_msg);
/*
* thc_ipc_call
*
* Performs both a send and an async receive.
* request is the message to send.
* (*response) will have the received message.
* Performs a thc_ipc_send_request followed by an immediate
* thc_ipc_recv_response.
*
* It is the caller's responsibility to mark the message as having completed
* the receive. (ex: fipc_recv_msg_end(chnl, msg))
......@@ -218,8 +253,12 @@ int thc_ipc_call(struct thc_channel *chnl,
/*
* thc_ipc_reply
*
* This is for the other side of the channel that receives
* message with a request cookie, and wants to send a response.
* Send a response with request cookie request_cookie
* on the channel's tx buffer.
*
* This is for the receiver of a request. The receiver does
* some work, and then wants to send a response.
*
* You can get the request_cookie in a request message via
* thc_get_request_cookie.
*
......
......@@ -14,8 +14,8 @@ typedef enum {
struct predicate_payload
{
unsigned long expected_msg_id;
unsigned long actual_msg_id;
uint32_t expected_msg_id;
uint32_t actual_msg_id;
msg_type_t msg_type;
};
......
......@@ -30,6 +30,7 @@ static int test_ctx_switch_and_thd_creation(void)
unsigned long t1, t2, t3;
uint32_t msg_id;
int i;
int ret;
thc_init();
for( i = 0; i < NUM_SWITCH_MEASUREMENTS; i++ )
......@@ -44,11 +45,12 @@ static int test_ctx_switch_and_thd_creation(void)
THCYieldAndSave(msg_id);
t3 = test_fipc_stop_stopwatch();
awe_mapper_remove_id(msg_id);
ret = awe_mapper_remove_id(&msg_id);
BUG(ret);
});
ASYNC({
t2 = test_fipc_start_stopwatch();
THCYieldToId(msg_id);