UMP Channel implementation

This commit is contained in:
Sparchatus 2022-05-20 09:51:30 +00:00
parent 937c6fb701
commit 07f8cfe2bc
8 changed files with 812 additions and 18 deletions

View File

@ -21,7 +21,7 @@
// allow access to the toradex board
"runArgs": ["--privileged"],
// "mounts": ["type=bind,src=/dev/bus/usb,dst=/dev/bus/usb"],
"mounts": ["type=bind,src=/dev/bus/usb,dst=/dev/bus/usb"],
"remoteUser": "vscode",
}

251
include/aos/ump_chan.h Normal file
View File

@ -0,0 +1,251 @@
/**
* \file
* \brief bidirectional communication through shared memory
*/
#ifndef _INIT_UMP_CHAN_H_
#define _INIT_UMP_CHAN_H_
#include <aos/aos.h>
/**
* @brief Number of data bytes in a single buffer of the ring buffer
*/
#define UMP_RING_BUF_DATA_SIZE (CACHE_LINE_SIZE - 1)
struct ump_send_queue_entry;
typedef void (*ump_recv_header_callback_fn_t)(void *arg, size_t header_size, void *header, size_t payload_size);
typedef void (*ump_recv_payload_callback_fn_t)(void *arg, size_t payload_size, void *payload);
typedef void (*ump_send_callback_fn_t)(void *arg, void *payload, struct ump_send_queue_entry *entry);
/**
* @brief Specifies which role an endpoint would like to take in the UMP protocol
*
* This is just used to agree on which part of the shared frame should be used
* to send data and which to receive data
*/
enum ump_role {
UMP_ROLE_SERVER,
UMP_ROLE_CLIENT,
UMP_ROLE_COUNT, // How many roles exist
};
/**
* @brief Stores metadata about a ring buffer
*/
struct ump_ring_state {
/**
* @brief first entry of the ring buffer
*/
struct ump_ring_buf_entry *ring_start;
/**
* @brief Number of entries in the ring buffer
*/
size_t ring_entry_count;
/**
* @brief Which entry should be read or written next
*/
size_t ring_entry_next;
};
/**
* @brief State of a send queue entry
*
* Used to keep track of which part of the message we are
* currently sending
*/
enum ump_send_entry_state {
UMP_SEND_ENTRY_STATE_START_MESSAGE,
UMP_SEND_ENTRY_STATE_HEADER,
UMP_SEND_ENTRY_STATE_PAYLOAD,
UMP_SEND_ENTRY_STATE_COUNT, // How many states exist
};
/**
* @brief An entry in the sending queue of a channel.
*
* Contains all metadata information needed to send this message
*/
struct ump_send_queue_entry {
enum ump_send_entry_state state;
size_t header_size;
void *header;
size_t payload_size;
void *payload;
ump_send_callback_fn_t callback;
void *callback_arg;
/**
* @brief first this will refer to the header and when done with that
* it will be updated to refer to the payload
*/
size_t send_bytes_left;
void *send_buf_position;
struct ump_send_queue_entry *next;
};
/**
* @brief object containing metadata required for sending a message on a channel
*/
struct ump_send_chan {
struct ump_ring_state ring_state;
// required to hold this lock for any changes to the send_queue
struct thread_mutex send_queue_lock;
struct ump_send_queue_entry *send_queue_head;
struct ump_send_queue_entry *send_queue_tail;
};
/**
* @brief Used to keep track of which part of a message was received or is being received
*
*/
enum recv_state {
UMP_RECV_STATE_IDLE,
UMP_RECV_STATE_START_MESSAGE,
UMP_RECV_STATE_HEADER,
UMP_RECV_STATE_PAYLOAD_IDLE,
UMP_RECV_STATE_PAYLOAD,
UMP_RECV_STATE_COUNT, // How many states exist
};
/**
* @brief Metadata for the receiving side of the channel
*
*/
struct ump_recv_chan {
struct ump_ring_state ring_state;
/**
* @brief This must be held when editing this object
* from a thread that is not the worker thread
*/
struct thread_mutex recv_register_lock;
enum recv_state state;
/**
* @brief Current read state.
* This will first refer to the header and afterwards to the payload
*/
size_t read_bytes_left;
void *read_buf_position;
size_t header_size;
void *header;
size_t next_payload_size;
void *payload;
ump_recv_header_callback_fn_t recv_header_callback;
ump_recv_payload_callback_fn_t recv_payload_callback;
void *callback_arg;
};
/**
* @brief A single entry in a ring buffer
*/
struct ump_ring_buf_entry {
/**
* @brief data carried by this ring buffer entry
*/
uint8_t data[UMP_RING_BUF_DATA_SIZE];
/**
* @brief specifies who currently owns this buffer entry.
* 0 -> sender
* 1 -> receiver
*/
volatile uint8_t owner;
};
// for now we want each entry to be cache line sized
STATIC_ASSERT_SIZEOF(struct ump_ring_buf_entry, CACHE_LINE_SIZE);
/**
* @brief The first data block for a new message.
* This is only needed for the payload size right now
*/
struct ump_start_message {
size_t payload_size;
// NOTE rueegges: we could remove this field since the reciever also has
// to specify it but for now it is here to ensure there are no missmatches
// in sizes on the two endpoints
size_t header_size;
};
STATIC_ASSERT(sizeof(struct ump_start_message) <= sizeof(struct ump_ring_buf_entry), "struct ump_start_message is too big");
/**
* @brief Initialize the send and receive objects for a channel.
*
* @param role Specifies if this side should be the server or client in the connection
* @param send_chan send channel object to be allocated and initialized
* @param recv_chan receive channel object to be allocated and initialized
* @param recv_header_size size of the headers that will be received
* @param shared_frame the frame capability shared between the client and server
* @param run_on_waitset the waitset on which the async tasks should run. if it is NULL a thread will be started to handle the tasks
* @return errval_t
*/
errval_t ump_chan_init(
enum ump_role role,
struct ump_send_chan **send_chan,
struct ump_recv_chan **recv_chan,
size_t recv_header_size,
struct capref shared_frame,
struct waitset *run_on_waitset
);
/**
* @brief Send a message on the channel
*
* @param chan channel to send on
* @param entry an allocated object to carry the send information
* @param header_size size of the header to be sent
* @param header buffer containing the header to be sent
* @param payload_size size of the payload to be sent
* @param payload buffer containing the payload to be sent
* @param callback function to call after sending
* @param callback_arg argument to pass to the callback function
*/
void ump_send (
struct ump_send_chan *chan,
struct ump_send_queue_entry *entry,
size_t header_size,
void *header,
size_t payload_size,
void *payload,
ump_send_callback_fn_t callback,
void *callback_arg
);
/**
* @brief Receive the header of the next message. Must only be called on a fresh channel or after the receive payload completed
*
* @param chan channel to listen on
* @param callback function to call with the header and the size of the payload
* @param callback_arg argument to pass to the callback function
*/
void ump_recv_header (
struct ump_recv_chan *chan,
ump_recv_header_callback_fn_t callback,
void *callback_arg
);
/**
* @brief Receive the payload of the next message. Must only be called after the receive header completed
*
* @param chan channel to listen on
* @param payload buffer to store the payload in. must be at least as large as the payload_size argument in the corresponding callback from receive header. if this is NULL then the payload will be dropped
* @param callback function to call with payload
* @param callback_arg argument to pass to the callback function
*/
void ump_recv_payload (
struct ump_recv_chan *chan,
void *payload,
ump_recv_payload_callback_fn_t callback,
void *callback_arg
);
#endif /* _INIT_UMP_CHAN_H_ */

View File

@ -26,6 +26,7 @@
"aos_rpc.c",
"aos_urpc.c",
"ump_binding.c",
"ump_chan.c",
"performance.c",
"capabilities.c",
"coreset.c",

View File

@ -528,7 +528,6 @@ errval_t paging_init_params(struct spawn_domain_params *params)
// pt_print_state(&current);
}
// TODO rueegges: not sure this is sufficient?
err = thread_set_exception_handler(pt_exception_handler, NULL, (void*) pt_static_exception_stack, (void*) pt_static_exception_stack + PT_STATIC_EXCEPTION_STACK_SIZE, NULL, NULL);
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_VREGION_PAGEFAULT_HANDLER);

477
lib/aos/ump_chan.c Normal file
View File

@ -0,0 +1,477 @@
#include <aos/aos.h>
#include <aos/ump_chan.h>
#include <aos/paging.h>
#include <aos/waitset_chan.h>
/**
* @brief initializes a ring buffer state
*
* @param ring_state object to initialize
* @param buf start of the ring buffer memory
* @param buf_len size of the ring buffer memory
*/
static void ump_ring_init(struct ump_ring_state *ring_state, void *buf, size_t buf_len) {
// the ring buffers should be cache-line aligned
assert(((uintptr_t) buf) % CACHE_LINE_SIZE == 0);
ring_state->ring_start = buf;
ring_state->ring_entry_next = 0;
ring_state->ring_entry_count = buf_len / sizeof(struct ump_ring_buf_entry);
}
/**
* @brief Tries to write a ring buffer entry without blocking. Advances in the ring if successful
*
* @param ring_state ring to write to
* @param buf start of the buffer containing the data
* @param bytes number of bytes from the buffer to write to the ring
* @return true successfully wrote the entry
* @return false failed to write the entry because the buffer is full
*/
static bool ump_ring_try_write_next(struct ump_ring_state *ring_state, void *buf, size_t bytes) {
assert(bytes <= UMP_RING_BUF_DATA_SIZE);
assert(buf != NULL);
// check if we own the entry
if (ring_state->ring_start[ring_state->ring_entry_next].owner) {
return false;
}
// memory barrier because the other endpoint might be on another core
__asm volatile (
"dmb sy\n"
);
memcpy(&ring_state->ring_start[ring_state->ring_entry_next].data, buf, bytes);
// memory barrier because the other endpoint might be on another core
__asm volatile (
"dmb sy\n"
);
// mark the entry as owned by the receiver
ring_state->ring_start[ring_state->ring_entry_next].owner = 1;
// advance in the ring
ring_state->ring_entry_next = (ring_state->ring_entry_next + 1) % ring_state->ring_entry_count;
return true;
};
/**
* @brief Tries to read an entry of the ring buffer. Advances the ring if successful
*
* @param ring_state ring to read from
* @param buf start of the buffer to write to. if NULL the data will be dropped
* @param bytes number of bytes to read from this buffer (from the start of the buffer. the rest is dropped)
* @return true successfully read an entry
* @return false could not read an entry because no data was available
*/
static bool ump_ring_try_read_next(struct ump_ring_state *ring_state, void *buf, size_t bytes) {
assert(bytes <= UMP_RING_BUF_DATA_SIZE);
// check if we own the entry
if(!ring_state->ring_start[ring_state->ring_entry_next].owner) {
return false;
}
// memory barrier because the other endpoint might be on another core
__asm volatile (
"dmb sy\n"
);
// if we get a NULL buf then we need to drop the data
if (buf != NULL) {
memcpy(buf, &ring_state->ring_start[ring_state->ring_entry_next].data, bytes);
}
// memory barrier because the other endpoint might be on another core
__asm volatile (
"dmb sy\n"
);
// mark the entry as owned by the sender
ring_state->ring_start[ring_state->ring_entry_next].owner = 0;
// advance in the ring
ring_state->ring_entry_next = (ring_state->ring_entry_next + 1) % ring_state->ring_entry_count;
return true;
}
/**
* @brief Try to read the next message. The message might also be read only partially but it will continue in the next call to this function
*
* @param recv_chan channel to read the message on
*/
static void ump_try_read(struct ump_recv_chan *recv_chan) {
// TODO rueegges: is this locking required?!? if writing the state is a single assembly instruction then not, otherwise yes
// make sure we own the read metadata, otherwise we have nothing to do anyways
thread_mutex_lock(&recv_chan->recv_register_lock);
if(
recv_chan->state != UMP_RECV_STATE_START_MESSAGE &&
recv_chan->state != UMP_RECV_STATE_HEADER &&
recv_chan->state != UMP_RECV_STATE_PAYLOAD
) {
thread_mutex_unlock(&recv_chan->recv_register_lock);
return;
}
thread_mutex_unlock(&recv_chan->recv_register_lock);
// first make sure we have read the start message
if (recv_chan->state == UMP_RECV_STATE_START_MESSAGE) {
struct ump_start_message start_message;
if(!ump_ring_try_read_next(&recv_chan->ring_state, &start_message, sizeof(struct ump_start_message))) {
return;
}
// the sender and receiver MUST agree on the header size
assert(recv_chan->header_size == start_message.header_size);
// update the receive state to start reading the header
recv_chan->read_bytes_left = recv_chan->header_size;
recv_chan->read_buf_position = recv_chan->header;
recv_chan->next_payload_size = start_message.payload_size;
recv_chan->state = UMP_RECV_STATE_HEADER;
}
// read as much as popssible of the header or payload
while(recv_chan->read_bytes_left > 0) {
size_t bytes_to_read = MIN(recv_chan->read_bytes_left, UMP_RING_BUF_DATA_SIZE);
if(!ump_ring_try_read_next(&recv_chan->ring_state, recv_chan->read_buf_position, bytes_to_read)) {
// buffer is empty, we need to try again later
return;
}
if(recv_chan->read_buf_position != NULL) {
recv_chan->read_buf_position += bytes_to_read;
}
recv_chan->read_bytes_left -= bytes_to_read;
}
// we managed to read the current stage, advance the state and call the callbacks
if (recv_chan->state == UMP_RECV_STATE_HEADER) {
recv_chan->state = UMP_RECV_STATE_PAYLOAD_IDLE;
recv_chan->recv_header_callback(
recv_chan->callback_arg,
recv_chan->header_size,
recv_chan->header,
recv_chan->next_payload_size
);
} else if (recv_chan->state == UMP_RECV_STATE_PAYLOAD) {
recv_chan->state = UMP_RECV_STATE_IDLE;
recv_chan->recv_payload_callback(
recv_chan->callback_arg,
recv_chan->next_payload_size,
recv_chan->payload
);
} else {
// cannot happen but just to be sure...
assert(false);
}
}
/**
* @brief Try to send the next message. The message might also be sent only partially but it will continue in the next call to this function
*
* @param send_chan channel to send the message on
*/
static void ump_try_send(struct ump_send_chan *send_chan) {
// TODO rueegges: is this locking required?!? if writing the head is a single assembly instruction then not, otherwise yes
// check if there is anything to do.
thread_mutex_lock(&send_chan->send_queue_lock);
if (send_chan->send_queue_head == NULL) {
thread_mutex_unlock(&send_chan->send_queue_lock);
return;
}
thread_mutex_unlock(&send_chan->send_queue_lock);
// get the entry to handle next
struct ump_send_queue_entry *entry = send_chan->send_queue_head;
// first we need to send the start message so the receiver knows how much payload to expect
if (entry->state == UMP_SEND_ENTRY_STATE_START_MESSAGE) {
struct ump_start_message start_message = {
.header_size = entry->header_size,
.payload_size = entry->payload_size,
};
if(!ump_ring_try_write_next(&send_chan->ring_state, &start_message, sizeof(struct ump_start_message))) {
// ring is full, we need to retry later
return;
}
entry->state = UMP_SEND_ENTRY_STATE_HEADER;
entry->send_buf_position = entry->header;
entry->send_bytes_left = entry->header_size;
}
// send as much in the current stage as possible
while(entry->send_bytes_left > 0) {
size_t bytes_to_send = MIN(entry->send_bytes_left, UMP_RING_BUF_DATA_SIZE);
if(!ump_ring_try_write_next(&send_chan->ring_state, entry->send_buf_position, bytes_to_send)) {
return;
}
entry->send_buf_position += bytes_to_send;
entry->send_bytes_left -= bytes_to_send;
}
// we managed to send the current stage, advance the state!
if (entry->state == UMP_SEND_ENTRY_STATE_HEADER) {
entry->send_buf_position = entry->payload;
entry->send_bytes_left = entry->payload_size;
entry->state = UMP_SEND_ENTRY_STATE_PAYLOAD;
} else if (entry->state == UMP_SEND_ENTRY_STATE_PAYLOAD) {
// we are done sending, remove the entry from the queue
thread_mutex_lock(&send_chan->send_queue_lock);
// we take the current send queue state to get "next" because it might have changed since
// we read the entry (NULL -> something)
if (send_chan->send_queue_head->next == NULL) {
send_chan->send_queue_tail = NULL;
}
send_chan->send_queue_head = send_chan->send_queue_head->next;
thread_mutex_unlock(&send_chan->send_queue_lock);
// send the callback
assert(entry->callback != NULL);
entry->callback(entry->callback_arg, entry->payload, entry);
}
}
/**
* @brief Contains references to information required by the ump channel worker
*/
struct ump_worker_context {
struct waitset *run_on_waitset;
struct waitset_chanstate waitset_chan;
struct ump_recv_chan *recv_chan;
struct ump_send_chan *send_chan;
};
/**
* @brief Do the async work on the channel
*
* @param arg
*/
static void ump_work(void *arg) {
struct ump_worker_context *worker_context = arg;
// check the recv queue
ump_try_read(worker_context->recv_chan);
// check the send queue
ump_try_send(worker_context->send_chan);
}
/**
* @brief used to handle the async work on a separate thread
*
* @param arg the worker context
* @return int not used
*/
static int ump_thread_worker(void *arg) {
while (true) {
ump_work(arg);
thread_yield();
}
return 0;
}
static void ump_waitset_worker(void *arg);
/**
* @brief Schedule the worker on its waitset.
*
* @param worker_context
*/
static void ump_worker_schedule(struct ump_worker_context *worker_context) {
// we want to be automatically re-registered upon completion of an event
// worker_context->waitset_chan.persistent = true;
errval_t err = waitset_chan_trigger_closure(
worker_context->run_on_waitset,
&worker_context->waitset_chan,
MKCLOSURE(ump_waitset_worker, worker_context)
);
// this is supposed to be called single threaded and only upon event trigger
// hence this cannot be a reregister and thus not fail
assert(err_is_ok(err));
}
/**
* @brief used to handle the async work on a waitset
*
* @param arg the worker context
*/
static void ump_waitset_worker(void *arg) {
ump_work(arg);
// put back in the queue
ump_worker_schedule(arg);
}
errval_t ump_chan_init(
enum ump_role role,
struct ump_send_chan **send_chan_ret,
struct ump_recv_chan **recv_chan_ret,
size_t recv_header_size,
struct capref shared_frame,
struct waitset *run_on_waitset
) {
errval_t err;
// check that we have a valid role for the channel
if (role >= UMP_ROLE_COUNT) return ERR_INVALID_ARGS;
// identify the shared frame to determine its size
struct frame_identity shared_frame_id;
err = frame_identify(shared_frame, &shared_frame_id);
if (err_is_fail(err)) return LIB_ERR_FRAME_IDENTIFY;
// map the full frame
void *shared_mem;
err = paging_map_frame(get_current_paging_state(), &shared_mem, shared_frame_id.bytes, shared_frame);
if (err_is_fail(err)) return err_push(err, LIB_ERR_PMAP_MAP);
// get the size of the rings for each direction
size_t ring_size = shared_frame_id.bytes / 2;
debug_printf("[ump_chan_init] Each channel is %lu bytes\n", ring_size);
// allocate the channel structures
struct ump_send_chan *send_chan = malloc(sizeof(struct ump_send_chan));
if(send_chan == NULL) {
paging_unmap(get_current_paging_state(), shared_mem);
return LIB_ERR_MALLOC_FAIL;
}
struct ump_recv_chan *recv_chan = malloc(sizeof(struct ump_recv_chan));
if(recv_chan == NULL) {
paging_unmap(get_current_paging_state(), shared_mem);
free(send_chan);
return LIB_ERR_MALLOC_FAIL;
}
// decide on which part of the buffer is used for sending and receiving
// give the server receiving side the first half of the frame
void *receive_buf = role == UMP_ROLE_SERVER ? shared_mem : shared_mem + ring_size;
// give the server sending side the second half of the frame
void *send_buf = role == UMP_ROLE_SERVER ? shared_mem + ring_size : shared_mem;
// initialize recv state
recv_chan->header = malloc(recv_header_size);
if (recv_chan->header == NULL) {
paging_unmap(get_current_paging_state(), shared_mem);
free(send_chan);
free(recv_chan);
return LIB_ERR_MALLOC_FAIL;
}
recv_chan->header_size = recv_header_size;
recv_chan->state = UMP_RECV_STATE_IDLE;
ump_ring_init(&recv_chan->ring_state, receive_buf, ring_size);
thread_mutex_init(&recv_chan->recv_register_lock);
// initialize send state
send_chan->send_queue_head = NULL;
send_chan->send_queue_tail = NULL;
ump_ring_init(&send_chan->ring_state, send_buf, ring_size);
thread_mutex_init(&send_chan->send_queue_lock);
// allocate and initialize the worker state for handling the async work
struct ump_worker_context *worker_context = malloc(sizeof(struct ump_worker_context));
if(worker_context == NULL) {
paging_unmap(get_current_paging_state(), shared_mem);
free(send_chan);
free(recv_chan);
free(recv_chan->header);
return LIB_ERR_MALLOC_FAIL;
}
worker_context->run_on_waitset = run_on_waitset;
worker_context->recv_chan = recv_chan;
worker_context->send_chan = send_chan;
if (run_on_waitset != NULL) {
// we need to run on a waitset so initialize it and schedule the worker on it
waitset_chanstate_init(&worker_context->waitset_chan, CHANTYPE_OTHER);
ump_worker_schedule(worker_context);
} else {
// we need to run on a dedicated thread so create it
struct thread *t = thread_create(ump_thread_worker, worker_context);
if (t == NULL) {
paging_unmap(get_current_paging_state(), shared_mem);
free(send_chan);
free(recv_chan);
free(recv_chan->header);
free(worker_context);
return LIB_ERR_THREAD_CREATE;
}
}
// return the channel metadata
*send_chan_ret = send_chan;
*recv_chan_ret = recv_chan;
return SYS_ERR_OK;
}
void ump_send (
struct ump_send_chan *chan,
struct ump_send_queue_entry *entry,
size_t header_size,
void *header,
size_t payload_size,
void *payload,
ump_send_callback_fn_t callback,
void *callback_arg
) {
// initialize the queue entry
entry->header_size = header_size;
entry->header = header;
entry->payload_size = payload_size;
entry->payload = payload;
entry->callback = callback;
entry->callback_arg = callback_arg;
entry->next = NULL;
// add the new entry to the end of the send queue
thread_mutex_lock(&chan->send_queue_lock);
if(chan->send_queue_tail != NULL) {
chan->send_queue_tail->next = entry;
} else {
chan->send_queue_head = entry;
}
chan->send_queue_tail = entry;
thread_mutex_unlock(&chan->send_queue_lock);
}
void ump_recv_header (
struct ump_recv_chan *chan,
ump_recv_header_callback_fn_t callback,
void *callback_arg
) {
thread_mutex_lock(&chan->recv_register_lock);
// TODO rueegges: caller has to ensure it only registers at most once?! Or should we just make it a noop?
assert(chan->state == UMP_RECV_STATE_IDLE);
chan->recv_header_callback = callback;
chan->callback_arg = callback_arg;
// this must be last so we have written everything when the worker thread sees this state
chan->state = UMP_RECV_STATE_START_MESSAGE;
thread_mutex_unlock(&chan->recv_register_lock);
}
void ump_recv_payload (
struct ump_recv_chan *chan,
void *payload,
ump_recv_payload_callback_fn_t callback,
void *callback_arg
) {
thread_mutex_lock(&chan->recv_register_lock);
// TODO rueegges: caller has to ensure it only registers at most once?! Or should we just make it a noop?
assert(chan->state == UMP_RECV_STATE_PAYLOAD_IDLE);
chan->recv_payload_callback = callback;
chan->callback_arg = callback_arg;
chan->read_bytes_left = chan->next_payload_size;
chan->read_buf_position = payload;
chan->payload = payload;
// this must be last so we have written everything when the worker thread sees this state
chan->state = UMP_RECV_STATE_PAYLOAD;
thread_mutex_unlock(&chan->recv_register_lock);
}

View File

@ -133,6 +133,9 @@ errval_t ump_binding_connect (
);
if (err_is_fail(err)) return err_push(err, LIB_ERR_PMAP_MAP);
// NOTE rueegges: the ring buffer MUST be initialized to all zero so
// all ring buffer entries are initially owned by the
// respective senders
memset(frame_data, 0, UMP_FRAME_SIZE);
err = paging_unmap(get_current_paging_state(), (void *)frame_data);

View File

@ -3,17 +3,47 @@
#include <aos/aos.h>
#include <aos/ump_binding.h>
#include <aos/aos_rpc.h>
#include <aos/ump_chan.h>
#define ECHO_TEST_MESSAGE "Hello Client! Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
__attribute__((__used__))
static void handle_payload_recv(void *arg, size_t payload_size, void *payload) {
printf("Echo server: Received payload(%lu): %.*s\n", payload_size, payload_size, payload);
}
__attribute__((__used__))
static void handle_header_recv(void *arg, size_t header_size, void *header, size_t payload_size) {
printf("Echo server: Received header: %u\n", *(uint8_t *)header);
printf("Echo server: Payload is %lu bytes\n", payload_size);
void *payload = malloc(payload_size);
assert(payload != NULL);
ump_recv_payload(arg, payload, handle_payload_recv, NULL);
}
__attribute__((__used__))
static void handle_send(void *arg, void *payload, struct ump_send_queue_entry *entry) {
printf("Echo server: Message sent!\n");
}
static errval_t connect_server (void *arg, struct capref cap) {
debug_printf("Echo server: incoming connection\n");
errval_t err;
struct paging_state *pstate = get_current_paging_state();
uint8_t *ump_data;
errval_t err = paging_map_frame(pstate, (void **)&ump_data, BASE_PAGE_SIZE, cap);
if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to map ump frame");
printf("Echo server: incoming connection\n");
// TODO: Create an UMP channel
ump_data[0] = 123;
struct ump_send_chan *send_chan;
struct ump_recv_chan *recv_chan;
err = ump_chan_init(UMP_ROLE_SERVER, &send_chan, &recv_chan, 1, cap, get_default_waitset());
if (err_is_fail(err)) return err;
uint8_t *value_to_send = malloc(sizeof(uint8_t));
*value_to_send = 123;
struct ump_send_queue_entry *entry = malloc(sizeof(struct ump_send_queue_entry));
printf("Echo server: Sending Message\n");
ump_send(send_chan, entry, 1, value_to_send, sizeof(ECHO_TEST_MESSAGE), ECHO_TEST_MESSAGE, handle_send, NULL);
printf("Echo server: Receiving Message\n");
ump_recv_header(recv_chan, handle_header_recv, recv_chan);
return SYS_ERR_OK;
}
@ -22,12 +52,12 @@ int main (int argc, char *argv[]) {
errval_t err;
struct ump_binding_server server;
debug_printf("Echo server registering\n");
printf("Echo server: registering\n");
err = ump_binding_register(&server, UMP_SERVER_ECHO, connect_server, NULL);
if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to register UMP server");
debug_printf("Echo server listening\n");
printf("Echo server: listening\n");
struct waitset *default_ws = get_default_waitset();
while (true) {

View File

@ -20,6 +20,7 @@
#include <aos/aos.h>
#include <aos/aos_rpc.h>
#include <aos/deferred.h>
#include <aos/ump_chan.h>
#define HELLO_CMD_CATCH "catch"
#define HELLO_CMD_SPAWN "spawn"
@ -28,6 +29,8 @@
#define HELLO_CMDLINE_READ_LEN 100
#define HELLO_TEST_MESSAGE "Hello Server! Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
__attribute__((__used__))
static int null_dereference(void *ignored) {
debug_printf("[null_dereference] Oh no! :O\n");
@ -36,6 +39,25 @@ static int null_dereference(void *ignored) {
return 1;
}
__attribute__((__used__))
static void handle_payload_recv(void *arg, size_t payload_size, void *payload) {
printf("Echo client: Received payload(%lu): %.*s\n", payload_size, payload_size, payload);
}
__attribute__((__used__))
static void handle_header_recv(void *arg, size_t header_size, void *header, size_t payload_size) {
printf("Echo client: Received header: %u\n", *(uint8_t *)header);
printf("Echo client: Payload is %lu bytes\n", payload_size);
void *payload = malloc(payload_size);
assert(payload != NULL);
ump_recv_payload(arg, payload, handle_payload_recv, NULL);
}
__attribute__((__used__))
static void handle_send(void *arg, void *payload, struct ump_send_queue_entry *entry) {
printf("Echo client: Message sent!\n");
}
int main(int argc, char *argv[])
{
errval_t err;
@ -133,7 +155,7 @@ int main(int argc, char *argv[])
}
if (argc > 1 && !strncmp(argv[1], HELLO_CMD_ECHOCLIENT, sizeof(HELLO_CMD_ECHOCLIENT))) {
debug_printf("Connecting to echo server...\n");
printf("Echo client: Connecting to echo server...\n");
struct capref echo_cap;
for (int attempts = 0; attempts < 50; attempts++) {
err = aos_rpc_ump_connect(rpc, UMP_SERVER_ECHO, &echo_cap);
@ -141,13 +163,24 @@ int main(int argc, char *argv[])
barrelfish_usleep(100000);
}
if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to connect to echo server");
debug_printf("Connected to echo server.\n");
printf("Echo client: Connected to echo server.\n");
struct paging_state *pstate = get_current_paging_state();
uint8_t *ump_data;
err = paging_map_frame(pstate, (void **)&ump_data, BASE_PAGE_SIZE, echo_cap);
if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to map ump frame");
debug_printf("Read byte: %d\n", ump_data[0]);
// Create the two uni-directional channels
struct ump_send_chan *send_chan;
struct ump_recv_chan *recv_chan;
err = ump_chan_init(UMP_ROLE_CLIENT, &send_chan, &recv_chan, 1, echo_cap, NULL);
if (err_is_fail(err)) return err;
// send a message to the server
printf("Echo client: Sending Message\n");
uint8_t *value_to_send = malloc(sizeof(uint8_t));
*value_to_send = 37;
struct ump_send_queue_entry *entry = malloc(sizeof(struct ump_send_queue_entry));
ump_send(send_chan, entry, 1, value_to_send, sizeof(HELLO_TEST_MESSAGE), HELLO_TEST_MESSAGE, handle_send, NULL);
// try to receive and print a message from the server
printf("Echo client: Receiving Message\n");
ump_recv_header(recv_chan, handle_header_recv, recv_chan);
}
return EXIT_SUCCESS;