From: Matthias Kruk Date: Fri, 13 Mar 2020 17:08:27 +0000 (+0900) Subject: Add header with documentation for IPC mailboxes, along with a half-baked implementation X-Git-Url: https://git.corax.cc/?a=commitdiff_plain;h=46554a3dfe75ecf4551cd45c3d3ac89352a080a4;p=corax Add header with documentation for IPC mailboxes, along with a half-baked implementation --- diff --git a/kernel/core/mailbox.c b/kernel/core/mailbox.c new file mode 100644 index 0000000..f0a1940 --- /dev/null +++ b/kernel/core/mailbox.c @@ -0,0 +1,83 @@ +#include +#include +#include "mailbox.h" + +int mailbox_init(struct mailbox *mbox) +{ + int ret_val; + + ret_val = -EINVAL; + + if(mbox) { + /* zero the entire mailbox */ + memset(mbox, 0, sizeof(*mbox)); + + /* this should really always return zero */ + ret_val = sem_post(&(mbox->mb_wsem)); + } + + return(ret_val); +} + +int mailbox_put(struct mailbox *mbox, void *msg) +{ + int ret_val; + + ret_val = -EINVAL; + + if(mbox) { + /* + * Wait until the mailbox is available. The sem_wait() + * function always succeeds, so we don't need to bother + * checking its return value + */ + sem_wait(&(mbox->mb_wsem)); + + /* place the message in the mailbox */ + mbox->mb_msg = msg; + + /* + * Notify receivers that a message is ready to be received. + * Since we're only putting one message at a time into the + * mailbox, this really shouldn't overflow. If it does, some + * other part of the kernel must have overwritten the memory + * of we have a use-after-free bug. Either way, the mailbox + * isn't really safe to use anymore. + */ + ret_val = sem_post(&(mbox->mb_rsem)); + + if(!ret_val) { + /* wait for synchronization */ + ret_val = sem_wait(&(mbox->mb_ssem)); + } else { + /* put the semaphore back into its original state */ + sem_post(&(mbox->mb_wsem)); + } + } + + return(ret_val); +} + +int mailbox_get(struct mailbox *mbox, void **dst) +{ + int ret_val; + + ret_val = -EINVAL; + + if(mbox) { + /* + * Wait for a message to be placed in the mailbox. This + * function always succeeds, so we don't have to check its + * return value. + */ + sem_wait(&(mbox->mb_rsem)); + + /* retrieve the message */ + *dst = mbox->mb_msg; + + /* + + } + + return(ret_val); +} diff --git a/kernel/core/mailbox.h b/kernel/core/mailbox.h new file mode 100644 index 0000000..863658a --- /dev/null +++ b/kernel/core/mailbox.h @@ -0,0 +1,161 @@ +#ifndef _MAILBOX_H +#define _MAILBOX_H + +#include +#include + +/* + * DESIGN OF THE IPC-MAILBOXES + * + * Similar to the IPC mechanism in Minix, we try to avoid deadlocks + * by forcing processes to rendevouz during message transmission. That + * means, the sending process waits for the recipient to accept and + * acknowledge the message before continuing execution. This is + * achieved through the use of two semaphores and a mutex as shown in + * the following figure. The '+' and '=' characters signify + * non-blocking and blocking calls, respectively. The --< | <-- thing + * is supposed to be a bridge (it seemed to be the best way to draw a + * non-planar graph in ASCII). + * + * SEND_TASK RECV_TASK + * | | + * +--(mutex_lock)--= | + * | | +------+ | + * | +----(MOV)--->| slot |<---(MOV)--< | <--. + * | | +------+ | | + * | | | | + * | | +------+   | | + * v +-(sem_post)->| wsem |<-(sem_wait)-= | + * +-------+ | +------+ | | + * | mutex | | +----' + * +-------+ | +------+ | + * ^ =-(sem_wait)->| ssem |<-(sem_post)-+ + * | | +------+ | + * '-(mutex_unlock)-+ | + * | | + * X X + * + * The following petri net describes that configuration: + * + * .---. + * .--->|s01|----. + * | '---' | + * | v + * ----- ----- + * ^ ^ | | .---. | + * | | | '-->|x00|---->|---. + * | '----. | '---' | | + * | | | .-->| | + * | | | | | | + * | | v | v + * .---. .---. .---. .---. .---. + * |s00| |x02| |s02| |r00| |r01| + * '---' '---' '---' '---' '---' + * ^ ^ | ^ | + * | | | | | | + * | .----' | '---| | + * | | | .---. |<--' + * | | | .---|x01|<----| + * ----- | | '---' | + * ^ v v + * | ----- + * | .---. | + * '---|s03|<----' + * '---' + * + * The sending task will start in position s00 and the receiving task + * will start in r00. Positions x00 and x01 are the semaphores, and + * position x02 is the mutex. + * + * The state vector has the following columns: + * 0 1 2 3 4 5 6 7 8 + * m = (s00, s01, s02, s03, r00, r01, x00, x01, x02) + * + * From the diagram, we get the following state vector for time n: + * + * m(n) = ( + * m(n-1, 3), + * ( m(n-1, 0) + m(n-1, 6) ) / 2, + * m(n-1, 1), + * ( m(n-1, 2) + m(n-1, 8) ) / 2, + * m(n-1, 5), + * ( m(n-1, 7) + m(n-1, 4) ) / 2 + * m(n-1, 3), + * m(n-1, 1), + * m(n-1, 5) + * ) + * + * TODO: Proof that petri net is deadlock-free. + */ + +struct mailbox { + mutex_t mb_lock; + sem_t mb_rsem; + sem_t mb_ssem; + void *mb_msg; +}; + +/* + * mailbox_init() - Initialize a mailbox + * + * SYNOPSIS + * int mailbox_init(struct mailbox *mbox); + * + * DESCRIPTION + * The mailbox_init() function initializes the mailbox pointed to by + * `mbox'. The function will zero all space occupied by the mailbox + * and initialize the semaphores used for synchronization. + * + * RETURN VALUE + * 0 The mailbox was successfully initialized + * -EINVAL Invalid mailbox specified + */ +int mailbox_init(struct mailbox*); + +/* + * mailbox_put() - Put a message into a mailbox + * + * SYNOPSIS + * int mailbox_put(struct mailbox *mbox, void *msg); + * + * DESCRIPTION + * The mailbox_put() function puts the message pointed to by `msg' + * into the mailbox pointed to by `mbox' and waits for another task + * to receive the message. If the mailbox cannot immediately be + * written to (for example, because another task has put a message + * inside), the caller will be suspended until the mailbox becomes + * available. Once the function returns, the caller can be certain + * that the message has been accepted by the receiving task. + * + * RETURN VALUE + * 0 The message successfully transmitted to another task + * -EINVAL Invalid mailbox specified + * -EOVERFLOW A kernel bug has occured + */ +int mailbox_put(struct mailbox*, void*); + +/* + * mailbox_get() - Get a message from a mailbox + * + * SYNOPSIS + * int mailbox_get(struct mailbox *mbox, void **dst); + * + * DESCRIPTION + * The mailbox_get() function retrieves a message from the mailbox + * pointed to by `mbox' and adjusts the pointer pointed to by `dst' + * to point to the retrieved message. If there is no message waiting + * to be retrieved, the calling task will be suspended until a + * message can be retrieved. The sending and receiving tasks will + * rendevouz during the message transmission, meaning that the sender + * will wait for the message to be retrieved by a receiving task. + * + * RETURN VALUE + * 0 A message was successfully retrieved from the mailbox + * -EINVAL Invalid mailbox specified + * -EOVERFLOW An overflow occured during the rendevouz. This error + * means that the mailbox was overwritten with invalid + * data due to a kernel bug + */ +int mailbox_get(struct mailbox*, void**); + +#endif /* _MAILBOX_H */