]> git.corax.cc Git - corax/commitdiff
Add header with documentation for IPC mailboxes, along with a half-baked implementation
authorMatthias Kruk <m@m10k.eu>
Fri, 13 Mar 2020 17:08:27 +0000 (02:08 +0900)
committerMatthias Kruk <m@m10k.eu>
Fri, 13 Mar 2020 17:08:27 +0000 (02:08 +0900)
kernel/core/mailbox.c [new file with mode: 0644]
kernel/core/mailbox.h [new file with mode: 0644]

diff --git a/kernel/core/mailbox.c b/kernel/core/mailbox.c
new file mode 100644 (file)
index 0000000..f0a1940
--- /dev/null
@@ -0,0 +1,83 @@
+#include <spinlock.h>
+#include <sem.h>
+#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 (file)
index 0000000..863658a
--- /dev/null
@@ -0,0 +1,161 @@
+#ifndef _MAILBOX_H
+#define _MAILBOX_H
+
+#include <spinlock.h>
+#include <sem.h>
+
+/*
+ *    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 */