mt-waitset: adding masking of channels so a thread won't handle two messages at the...
[barrelfish] / lib / barrelfish / flounder_support.c
1 /**
2  * \file
3  * \brief Support code for Flounder-generated stubs
4  */
5
6 /*
7  * Copyright (c) 2010, 2011, 2012, ETH Zurich.
8  * All rights reserved.
9  *
10  * This file is distributed under the terms in the attached LICENSE file.
11  * If you do not find this file, copies can be found by writing to:
12  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13  */
14
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <barrelfish/barrelfish.h>
19 #include <barrelfish/monitor_client.h>
20 #include <barrelfish/waitset_chan.h>
21 #include <flounder/flounder_support.h>
22 #include <flounder/flounder_support_caps.h>
23 #include <if/monitor_defs.h>
24
25 /// Special continuation for blocking
26 void blocking_cont(void *v)
27 {
28     debug_printf("%s: should never be called!\n", __func__);
29     assert(0);
30 }
31
32 /*
33  * NB: many of these functions are trivial, but exist so that we don't need to
34  * expose private libbarrelfish headers or generated flounder headers to every
35  * generated stub
36  */
37
38 static void dummy_event_handler(void *arg)
39 {
40 }
41
42 struct event_closure dummy_event_closure = {
43     .handler = dummy_event_handler,
44     .arg = NULL,
45 };
46
47 void flounder_support_trigger_chan(struct waitset_chanstate *wc)
48 {
49     if (waitset_chan_is_registered(wc)) {
50         errval_t err = waitset_chan_trigger(wc);
51         assert(err_is_ok(err)); // shouldn't fail if registered
52     }
53 }
54
55 void flounder_support_deregister_chan(struct waitset_chanstate *wc)
56 {
57     if (waitset_chan_is_registered(wc)) {
58         errval_t err = waitset_chan_deregister(wc);
59         assert(err_is_ok(err)); // shouldn't fail if registered
60     }
61 }
62
63 errval_t flounder_support_register(struct waitset *ws,
64                                    struct waitset_chanstate *wc,
65                                    struct event_closure ec,
66                                    bool trigger_now)
67 {
68     if (trigger_now) {
69         return waitset_chan_trigger_closure(ws, wc, ec);
70     } else {
71         if (ec.handler == blocking_cont) {
72             assert(!wc->wait_for);          // this event should be received
73             wc->wait_for = thread_self();   // only by our thread
74         }
75         return waitset_chan_register(ws, wc, ec);
76     }
77 }
78
79 void flounder_support_waitset_chanstate_init(struct waitset_chanstate *wc)
80 {
81     waitset_chanstate_init(wc, CHANTYPE_FLOUNDER);
82 }
83
84 void flounder_support_waitset_chanstate_init_persistent(struct waitset_chanstate *wc)
85 {
86     waitset_chanstate_init(wc, CHANTYPE_FLOUNDER);
87     wc->persistent = true;
88     wc->masked = true;
89 }
90
91 void flounder_support_waitset_chanstate_destroy(struct waitset_chanstate *wc)
92 {
93     waitset_chanstate_destroy(wc);
94 }
95
96 struct waitset * flounder_support_get_current_monitor_waitset(struct monitor_binding *mb)
97 {
98     return mb->waitset;
99 }
100
101 errval_t flounder_support_change_monitor_waitset(struct monitor_binding *mb,
102                                                  struct waitset *ws)
103 {
104     return mb->change_waitset(mb, ws);
105 }
106
107 void flounder_support_monitor_mutex_enqueue(struct monitor_binding *mb,
108                                             struct event_queue_node *qn,
109                                             struct event_closure cl)
110 {
111     event_mutex_enqueue_lock(&mb->mutex, qn, cl);
112 }
113
114 void flounder_support_monitor_mutex_unlock(struct monitor_binding *mb)
115 {
116     event_mutex_unlock(&mb->mutex);
117 }
118
119 void flounder_support_migrate_notify(struct waitset_chanstate *chan,
120                                      struct waitset *new_ws)
121 {
122     waitset_chan_migrate(chan, new_ws);
123 }
124
125 static void cap_send_cont(void *arg)
126 {
127     struct flounder_cap_state *s = arg;
128     s->cap_send_continuation(s->binding);
129 }
130
131 errval_t flounder_stub_send_cap(struct flounder_cap_state *s,
132                                 struct monitor_binding *mb,
133                                 uintptr_t monitor_id,
134                                 struct capref cap, bool give_away,
135                                 void (*cont)(void *st))
136 {
137     errval_t err;
138
139     s->cap_send_continuation = cont;
140
141     if (give_away) {
142         err = mb->tx_vtbl.cap_move_request(mb, MKCONT(cap_send_cont, s),
143                                            monitor_id, cap, s->tx_capnum);
144     }
145     else {
146         err = mb->tx_vtbl.cap_send_request(mb, MKCONT(cap_send_cont, s),
147                                            monitor_id, cap, s->tx_capnum);
148     }
149     if (err_is_ok(err)) {
150         s->tx_capnum++;
151         return err;
152     } else if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
153         // register to retry
154         return mb->register_send(mb, mb->waitset, MKCONT(cap_send_cont, s));
155     } else {
156         return err_push(err, LIB_ERR_MONITOR_CAP_SEND);
157     }
158 }
159
160 #if defined(CONFIG_INTERCONNECT_DRIVER_UMP)
161 static void flounder_stub_cap_state_init(struct flounder_cap_state *s, void *binding)
162 {
163     s->tx_cap_ack = false;
164     s->rx_cap_ack = false;
165     s->monitor_mutex_held = false;
166     s->tx_capnum = 0;
167     s->rx_capnum = 0;
168     s->binding = binding;
169 }
170 #endif // UMP
171
172 static uintptr_t getword(const uint8_t *buf, size_t *pos, size_t len)
173 {
174     uintptr_t word = 0;
175
176     for (int i = 0; *pos < len && i < sizeof(uintptr_t); i++) {
177         // read and shift in next byte
178         word <<= NBBY;
179         word |= buf[(*pos)++];
180     }
181
182     return word;
183 }
184
185 static void putword(uintptr_t word, uint8_t *buf, size_t *pos, size_t len)
186 {
187     const size_t shift_bits = (sizeof(uintptr_t) - 1) * NBBY;
188
189     // throw away leading zeros if this is the end of the message
190     if (len - *pos < sizeof(uintptr_t)) {
191         word <<= NBBY * (sizeof(uintptr_t) - (len - *pos));
192     }
193
194     for (int i = 0; *pos < len && i < sizeof(uintptr_t); i++) {
195         buf[(*pos)++] = word >> shift_bits;
196         word <<= NBBY;
197     }
198 }
199
200 #ifdef CONFIG_INTERCONNECT_DRIVER_LMP
201
202 #include <flounder/flounder_support_lmp.h>
203
204 errval_t flounder_stub_lmp_send_buf(struct lmp_chan *chan,
205                                     lmp_send_flags_t flags, const void *bufp,
206                                     size_t len, size_t *pos)
207 {
208     errval_t err;
209     const uint8_t *buf = bufp;
210
211     do {
212         // compute number of words for this message
213         size_t msg_words = DIVIDE_ROUND_UP(len - *pos, sizeof(uintptr_t));
214         if (*pos == 0) { // space for header
215             msg_words += 1;
216         }
217         if (msg_words > LMP_MSG_LENGTH)
218             msg_words = LMP_MSG_LENGTH;
219
220         // store initial position for retry
221         size_t restartpos = *pos;
222
223         // is this the start of the string?
224         uintptr_t w1;
225         if (*pos == 0) {
226             // if so, send the length in the first word
227             w1 = len;
228         } else {
229             // otherwise use it for payload
230             w1 = getword(buf, pos, len);
231         }
232
233         // get the rest of the message, painfully
234 #if LMP_MSG_LENGTH > 1
235         uintptr_t w2 = getword(buf, pos, len);
236 #endif
237 #if LMP_MSG_LENGTH > 2
238         uintptr_t w3 = getword(buf, pos, len);
239 #endif
240 #if LMP_MSG_LENGTH > 3
241         uintptr_t w4 = getword(buf, pos, len);
242 #endif
243 #if LMP_MSG_LENGTH > 4
244         uintptr_t w5 = getword(buf, pos, len);
245 #endif
246 #if LMP_MSG_LENGTH > 5
247         uintptr_t w6 = getword(buf, pos, len);
248 #endif
249 #if LMP_MSG_LENGTH > 6
250         uintptr_t w7 = getword(buf, pos, len);
251 #endif
252 #if LMP_MSG_LENGTH > 7
253         uintptr_t w8 = getword(buf, pos, len);
254 #endif
255 #if LMP_MSG_LENGTH > 8
256         uintptr_t w9 = getword(buf, pos, len);
257 #endif
258 #if LMP_MSG_LENGTH > 9
259         uintptr_t w10 = getword(buf, pos, len);
260 #endif
261 #if LMP_MSG_LENGTH > 10
262 #error Need to unroll message send loop further
263 #endif
264
265         // only set the sync flag if this is the last fragment
266         lmp_send_flags_t f = flags;
267         if (*pos < len) {
268             f &= ~LMP_FLAG_SYNC;
269         }
270
271         // try to send
272         err = lmp_chan_send(chan, f, NULL_CAP, msg_words, w1
273 #if LMP_MSG_LENGTH > 1
274                             , w2
275 #endif
276 #if LMP_MSG_LENGTH > 2
277                             , w3
278 #endif
279 #if LMP_MSG_LENGTH > 3
280                             , w4
281 #endif
282 #if LMP_MSG_LENGTH > 4
283                             , w5
284 #endif
285 #if LMP_MSG_LENGTH > 5
286                             , w6
287 #endif
288 #if LMP_MSG_LENGTH > 6
289                             , w7
290 #endif
291 #if LMP_MSG_LENGTH > 7
292                             , w8
293 #endif
294 #if LMP_MSG_LENGTH > 8
295                             , w9
296 #endif
297 #if LMP_MSG_LENGTH > 9
298                             , w10
299 #endif
300                             );
301
302         if (err_is_fail(err)) {
303             *pos = restartpos;
304         }
305     } while (err_is_ok(err) && *pos < len);
306
307     // do we need to send more? if not, zero out our state for the next send
308     if (*pos >= len) {
309         *pos = 0;
310     }
311
312     return err;
313 }
314
315 errval_t flounder_stub_lmp_recv_buf(struct lmp_recv_msg *msg, void *buf,
316                                     size_t *len, size_t *pos, size_t maxsize)
317 {
318     int msgpos;
319
320     assert(buf);
321
322     // is this the first fragment?
323     // if so, unmarshall the length and allocate a buffer
324     if (*pos == 0) {
325         if (msg->buf.msglen == 0) {
326             return FLOUNDER_ERR_RX_INVALID_LENGTH;
327         }
328
329         *len = msg->words[0];
330         assert(*len <= maxsize);
331         msgpos = 1;
332     } else {
333         msgpos = 0;
334     }
335
336     // copy remainder of fragment to buffer
337     for (; msgpos < msg->buf.msglen && *pos < *len; msgpos++) {
338         putword(msg->words[msgpos], buf, pos, *len);
339     }
340
341     // are we done?
342     if (*pos < *len) {
343         return FLOUNDER_ERR_BUF_RECV_MORE;
344     } else {
345         // reset state for next buffer
346         *pos = 0;
347         return SYS_ERR_OK;
348     }
349 }
350
351 errval_t flounder_stub_lmp_send_string(struct lmp_chan *chan,
352                                        lmp_send_flags_t flags,
353                                        const char *str,
354                                        size_t *pos, size_t *len)
355 {
356     // compute length, if this is the first call
357     if (*pos == 0) {
358         if (str == NULL) {
359             *len = 0;
360         } else {
361             // send the '\0', making it easy to reuse the buffer code
362             *len = strlen(str) + 1;
363         }
364     }
365
366     return flounder_stub_lmp_send_buf(chan, flags, str, *len, pos);
367 }
368
369 errval_t flounder_stub_lmp_recv_string(struct lmp_recv_msg *msg, char *str,
370                                        size_t *pos, size_t *len, size_t maxsize)
371 {
372     errval_t err;
373
374     err = flounder_stub_lmp_recv_buf(msg, (void *)str, len, pos, maxsize);
375     if (*len == 0) {
376         str[0] = '\0';
377     }
378     return err;
379 }
380 #endif // CONFIG_INTERCONNECT_DRIVER_LMP
381
382
383 #ifdef CONFIG_INTERCONNECT_DRIVER_UMP
384
385 #include <flounder/flounder_support_ump.h>
386
387 void flounder_stub_ump_state_init(struct flounder_ump_state *s, void *binding)
388 {
389     s->next_id = 1;
390     s->seq_id = 0;
391     s->ack_id = 0;
392     s->last_ack = 0;
393     s->token = 0;
394     flounder_stub_cap_state_init(&s->capst, binding);
395 }
396
397 errval_t flounder_stub_ump_send_buf(struct flounder_ump_state *s,
398                                        int msgnum, const void *bufp,
399                                        size_t len, size_t *pos)
400 {
401     volatile struct ump_message *msg;
402     const uint8_t *buf = bufp;
403     struct ump_control ctrl;
404     int msgpos;
405
406     do {
407         if (!flounder_stub_ump_can_send(s)) {
408             return FLOUNDER_ERR_BUF_SEND_MORE;
409         }
410
411         msg = ump_chan_get_next(&s->chan, &ctrl);
412         flounder_stub_ump_control_fill(s, &ctrl, msgnum);
413
414         // is this the start of the buffer?
415         if (*pos == 0) {
416             // if so, send the length in the first word
417             msg->data[0] = len;
418             // XXX: skip as many words as the largest word size
419             msgpos = (sizeof(uint64_t) / sizeof(uintptr_t));
420         } else {
421             // otherwise use it for payload
422             msgpos = 0;
423         }
424
425         for (; msgpos < UMP_PAYLOAD_WORDS && *pos < len; msgpos++) {
426             msg->data[msgpos] = getword(buf, pos, len);
427         }
428
429         flounder_stub_ump_barrier();
430         msg->header.control = ctrl;
431     } while (*pos < len);
432
433     // we're done. zero out our state for the next buffer
434     assert(*pos >= len);
435     *pos = 0;
436
437     return SYS_ERR_OK;
438 }
439
440 errval_t flounder_stub_ump_recv_buf(volatile struct ump_message *msg,
441                                     void *buf, size_t *len, size_t *pos,
442                                     size_t maxsize)
443 {
444     int msgpos;
445
446     assert(buf);
447
448     // is this the first fragment?
449     // if so, unmarshall the length and allocate a buffer
450     if (*pos == 0) {
451         *len = msg->data[0];
452         assert(*len <= maxsize);
453         // XXX: skip as many words as the largest word size
454         msgpos = (sizeof(uint64_t) / sizeof(uintptr_t));
455     } else {
456         msgpos = 0;
457     }
458
459     // copy remainder of fragment to buffer
460     for (; msgpos < UMP_PAYLOAD_WORDS && *pos < *len; msgpos++) {
461         putword(msg->data[msgpos], buf, pos, *len);
462     }
463
464     // are we done?
465     if (*pos < *len) {
466         return FLOUNDER_ERR_BUF_RECV_MORE;
467     } else {
468         // reset state for next buffer
469         *pos = 0;
470         return SYS_ERR_OK;
471     }
472 }
473
474 errval_t flounder_stub_ump_send_string(struct flounder_ump_state *s,
475                                        int msgnum, const char *str,
476                                        size_t *pos, size_t *len)
477 {
478     // compute length, if this is the first call
479     if (*pos == 0) {
480         if (str == NULL) {
481             *len = 0;
482         } else {
483             // send the '\0', making it easy to reuse the buffer code
484             *len = strlen(str) + 1;
485         }
486     }
487
488     return flounder_stub_ump_send_buf(s, msgnum, str, *len, pos);
489 }
490
491 errval_t flounder_stub_ump_recv_string(volatile struct ump_message *msg,
492                                        char *str, size_t *pos, size_t *len,
493                                        size_t maxsize)
494 {
495     errval_t err;
496
497     err = flounder_stub_ump_recv_buf(msg, (void *)str, len, pos, maxsize);
498     if (*len == 0) {
499         str[0] = '\0';
500     }
501     return err;
502 }
503
504 #endif // CONFIG_INTERCONNECT_DRIVER_UMP