skip to content
==> src/sim_mailbox.c <==
1/* The host side of one program slot, for tests on the simulator. */
2#if !defined(__NVPTX__)
3
4#include "mesh/rt/sim_mailbox.h"
5#include "mesh/abi.h"
6
7#include <stdatomic.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <time.h>
12
13static _Atomic uint32_t *a32(uint8_t *p) { return (_Atomic uint32_t *)(void *)p; }
14static _Atomic uint64_t *a64(uint8_t *p) { return (_Atomic uint64_t *)(void *)p; }
15
16uint32_t mesh_sim_u32(const uint8_t *p) { return atomic_load_explicit(a32((uint8_t *)(uintptr_t)p), memory_order_acquire); }
17uint64_t mesh_sim_u64(const uint8_t *p) { return atomic_load_explicit(a64((uint8_t *)(uintptr_t)p), memory_order_acquire); }
18float mesh_sim_f32(const uint8_t *p) {
19    uint32_t u = mesh_sim_u32(p);
20    float f;
21    memcpy(&f, &u, 4);
22    return f;
23}
24void mesh_sim_set_u32(uint8_t *p, uint32_t v) { atomic_store_explicit(a32(p), v, memory_order_release); }
25void mesh_sim_set_u64(uint8_t *p, uint64_t v) { atomic_store_explicit(a64(p), v, memory_order_release); }
26void mesh_sim_set_f32(uint8_t *p, float v) {
27    uint32_t u;
28    memcpy(&u, &v, 4);
29    mesh_sim_set_u32(p, u);
30}
31
32static uint64_t now_ms(void) {
33    struct timespec ts;
34    clock_gettime(CLOCK_MONOTONIC, &ts);
35    return (uint64_t)ts.tv_sec * 1000u + (uint64_t)ts.tv_nsec / 1000000u;
36}
37static void nap(void) {
38    struct timespec ts = {0, 20000};
39    nanosleep(&ts, NULL);
40}
41
42uint8_t *mesh_sim_mailbox(void) {
43    uint8_t *mb = aligned_alloc(4096, MESH_MAILBOX_BYTES);
44    if (!mb) return NULL;
45    memset(mb, 0, MESH_MAILBOX_BYTES);
46    mesh_sim_set_u32(mb + MESH_H_MAGIC, MESH_MAGIC);
47    mesh_sim_set_u32(mb + MESH_H_VERSION, MESH_VERSION);
48    mesh_sim_set_u32(mb + MESH_H_PEER, MESH_PEER_NONE);
49    mesh_sim_set_u32(mb + MESH_H_SYSTEMS, 1);
50    mesh_sim_set_u32(mb + MESH_H_SMS, 1);
51    return mb;
52}
53
54void mesh_sim_mailbox_free(uint8_t *mb) { free(mb); }
55
56uint8_t *mesh_sim_ctl(uint8_t *mb, uint32_t slot) { return mb + mesh_prog_ctl(slot); }
57uint8_t *mesh_sim_view(uint8_t *mb, uint32_t slot) {
58    return mb + mesh_sim_u32(mb + mesh_prog_entry(slot) + MESH_PT_VIEW_OFF);
59}
60
61/* The entry's writer side of its seqlock: gen odd while it changes. */
62static void gen_begin(uint8_t *e) { mesh_sim_set_u32(e + MESH_PT_GEN, mesh_sim_u32(e + MESH_PT_GEN) | 1u); }
63static void gen_end(uint8_t *e) { mesh_sim_set_u32(e + MESH_PT_GEN, (mesh_sim_u32(e + MESH_PT_GEN) | 1u) + 1u); }
64
65uint8_t *mesh_sim_install(uint8_t *mb, uint32_t slot, const mesh_sim_prog *p) {
66    uint8_t *e = mb + mesh_prog_entry(slot);
67    uint32_t view = p->view_off ? p->view_off : MESH_VIEW_HEAP;
68    if ((uint64_t)view + p->view_bytes > MESH_MAILBOX_BYTES) {
69        fprintf(stderr, "mesh-rt-sim-mailbox: a %u-byte view at 0x%x does not fit the mailbox\n", p->view_bytes, view);
70        abort();
71    }
72    gen_begin(e);
73    memset(e + MESH_PT_NAME, 0, MESH_NAME_BYTES);
74    size_t n = strlen(p->name);
75    memcpy(e + MESH_PT_NAME, p->name, n < MESH_NAME_MAX ? n : MESH_NAME_MAX);
76    mesh_sim_set_u32(e + MESH_PT_VIEW_OFF, p->view_bytes ? view : 0);
77    mesh_sim_set_u32(e + MESH_PT_VIEW_BYTES, p->view_bytes);
78    mesh_sim_set_u32(e + MESH_PT_NODES, p->nodes ? p->nodes : 1);
79    mesh_sim_set_u32(e + MESH_PT_WARPS, p->warps ? p->warps : 1);
80    mesh_sim_set_u32(e + MESH_PT_PEER, p->peer_mode);
81    mesh_sim_set_u32(e + MESH_PT_READS_MASK, p->reads_mask);
82    mesh_sim_set_u32(e + MESH_PT_INPUT_OFF, p->input_off);
83    mesh_sim_set_u32(e + MESH_PT_INPUT_LEN, p->input_len);
84    mesh_sim_set_u32(e + MESH_PT_REQ_OFF, p->req_off);
85    mesh_sim_set_u32(e + MESH_PT_REQ_MAX, p->req_max);
86    mesh_sim_set_u32(e + MESH_PT_RESP_OFF, p->resp_off);
87    mesh_sim_set_u32(e + MESH_PT_RESP_MAX, p->resp_max);
88    mesh_sim_set_u32(e + MESH_PT_RT_ABI, MESH_RT_ABI);
89    mesh_sim_set_u32(e + MESH_PT_STATE, MESH_ST_RUNNING);
90    uint8_t *x = mb + mesh_prog_ext(slot);
91    mesh_sim_set_u32(x + MESH_PX_PARAM, p->param);
92    mesh_sim_set_u64(x + MESH_PX_SCRATCH, (uint64_t)(uintptr_t)p->scratch);
93    mesh_sim_set_u32(x + MESH_PX_SCRATCH_BYTES, p->scratch ? p->scratch_bytes : 0);
94    gen_end(e);
95    return mb + view;
96}
97
98void mesh_sim_set_state(uint8_t *mb, uint32_t slot, uint32_t state) {
99    uint8_t *e = mb + mesh_prog_entry(slot);
100    gen_begin(e);
101    mesh_sim_set_u32(e + MESH_PT_STATE, state);
102    gen_end(e);
103}
104
105void mesh_sim_inbox(uint8_t *mb, uint32_t slot, uint32_t off, uint32_t cap, uint32_t rec, const char *type) {
106    uint8_t *e = mb + mesh_prog_entry(slot), *pp = mb + mesh_pipe_entry(slot);
107    gen_begin(e);
108    mesh_sim_set_u32(pp + MESH_PP_IN_OFF, off);
109    mesh_sim_set_u32(pp + MESH_PP_IN_CAP, cap);
110    mesh_sim_set_u32(pp + MESH_PP_IN_REC, rec);
111    size_t n = strlen(type);
112    mesh_sim_set_u32(pp + MESH_PP_IN_TYPE, mesh_pipe_type_hash((const uint8_t *)type, n));
113    memset(pp + MESH_PP_TYPE_NAME, 0, MESH_NAME_BYTES);
114    memcpy(pp + MESH_PP_TYPE_NAME, type, n < MESH_NAME_MAX ? n : MESH_NAME_MAX);
115    gen_end(e);
116}
117
118void mesh_sim_feed(uint8_t *mb, uint32_t producer, uint32_t consumer) {
119    uint8_t *ep = mb + mesh_prog_entry(producer), *ec = mb + mesh_prog_entry(consumer);
120    gen_begin(ep);
121    mesh_sim_set_u32(mb + mesh_pipe_entry(producer) + MESH_PP_FEEDS_MASK, mesh_sim_u32(mb + mesh_pipe_entry(producer) + MESH_PP_FEEDS_MASK) | 1u << consumer);
122    gen_end(ep);
123    gen_begin(ec);
124    mesh_sim_set_u32(mb + mesh_pipe_entry(consumer) + MESH_PP_FED_MASK, mesh_sim_u32(mb + mesh_pipe_entry(consumer) + MESH_PP_FED_MASK) | 1u << producer);
125    gen_end(ec);
126}
127
128mesh_sim_launch *mesh_sim_launch_prog(uint8_t *mb, uint32_t slot, const mesh_sim_prog *p, mesh_sim_kernel kernel) {
129    mesh_sim_launch *l = mesh_sim_start(kernel, mb, slot, p->nodes ? p->nodes : 1, p->warps ? p->warps : 1, p->shared_bytes);
130    if (!l) {
131        fprintf(stderr, "mesh-rt-sim-mailbox: could not launch %s\n", p->name);
132        abort();
133    }
134    return l;
135}
136
137/* The next ticket to publish lives in P_RING_TAIL (meshd's informational copy). */
138uint32_t mesh_sim_publish(uint8_t *mb, uint32_t slot, uint32_t op, uint32_t arg) {
139    uint8_t *ctl = mesh_sim_ctl(mb, slot);
140    uint32_t t = (uint32_t)mesh_sim_u64(ctl + MESH_P_RING_TAIL);
141    /* Ticket t may be published only once ticket t - 64 completed (its completion slot is reused). */
142    if (t >= MESH_PROG_RING) {
143        uint32_t old = t - MESH_PROG_RING;
144        while (mesh_sim_u32(ctl + MESH_P_MARKS + 4 * (old % MESH_PROG_RESULTS)) != old + 1) nap();
145    }
146    uint8_t *e = ctl + MESH_P_RING + MESH_ENTRY_BYTES * (t % MESH_PROG_RING);
147    mesh_sim_set_u32(e + MESH_E_OP, op);
148    mesh_sim_set_u32(e + MESH_E_ARG, arg);
149    mesh_sim_set_u32(e + MESH_E_SLOT, t % MESH_PROG_RESULTS);
150    mesh_sim_set_u32(e + MESH_E_SEQ, t + 1);
151    mesh_sim_set_u64(ctl + MESH_P_RING_TAIL, (uint64_t)t + 1);
152    return t;
153}
154
155bool mesh_sim_wait(uint8_t *mb, uint32_t slot, uint32_t t, uint32_t *result, uint32_t timeout_ms) {
156    uint8_t *ctl = mesh_sim_ctl(mb, slot);
157    uint32_t c = t % MESH_PROG_RESULTS;
158    uint64_t until = now_ms() + timeout_ms;
159    while (mesh_sim_u32(ctl + MESH_P_MARKS + 4 * c) != t + 1) {
160        if (now_ms() > until) return false;
161        nap();
162    }
163    if (result) *result = mesh_sim_u32(ctl + MESH_P_RESULTS + 4 * c);
164    return true;
165}
166
167uint32_t mesh_sim_call(uint8_t *mb, uint32_t slot, uint32_t op, uint32_t arg) {
168    uint32_t t = mesh_sim_publish(mb, slot, op, arg), r = 0;
169    if (!mesh_sim_wait(mb, slot, t, &r, 60000)) {
170        fprintf(stderr, "mesh-rt-sim-mailbox: ticket %u (op %u, arg %u) of slot %u never completed\n", t, op, arg, slot);
171        abort();
172    }
173    return r;
174}
175
176uint32_t mesh_sim_request(uint8_t *mb, uint32_t slot, const void *req, uint32_t len, void *out, uint32_t cap, uint32_t *out_len) {
177    uint8_t *e = mb + mesh_prog_entry(slot), *ctl = mesh_sim_ctl(mb, slot), *view = mesh_sim_view(mb, slot);
178    uint32_t req_off = mesh_sim_u32(e + MESH_PT_REQ_OFF), req_max = mesh_sim_u32(e + MESH_PT_REQ_MAX);
179    uint32_t resp_off = mesh_sim_u32(e + MESH_PT_RESP_OFF);
180    if (len > req_max) len = req_max;
181    memcpy(view + req_off, req, len);
182    mesh_sim_set_u32(ctl + MESH_P_REQ_LEN, len);
183    uint64_t seq = mesh_sim_u64(ctl + MESH_P_REQ_SEQ) + 1;
184    mesh_sim_set_u64(ctl + MESH_P_REQ_SEQ, seq);
185    uint32_t r = mesh_sim_call(mb, slot, MESH_OP_REQ, 0);
186    if (mesh_sim_u64(ctl + MESH_P_RESP_SEQ) != seq) {
187        *out_len = UINT32_MAX;
188        return r;
189    }
190    uint32_t n = mesh_sim_u32(ctl + MESH_P_RESP_LEN);
191    memcpy(out, view + resp_off, n < cap ? n : cap);
192    *out_len = n;
193    return r;
194}
195
196bool mesh_sim_input(uint8_t *mb, uint32_t slot, const void *bytes, uint32_t n) {
197    uint8_t *e = mb + mesh_prog_entry(slot);
198    uint32_t off = mesh_sim_u32(e + MESH_PT_INPUT_OFF), len = mesh_sim_u32(e + MESH_PT_INPUT_LEN);
199    if (len == 0) return false;
200    uint8_t *q = mesh_sim_view(mb, slot) + off;
201    uint32_t head = mesh_sim_u32(q + MESH_IQ_HEAD), tail = mesh_sim_u32(q + MESH_IQ_TAIL);
202    if (head - tail + n > len) return false;
203    for (uint32_t i = 0; i < n; i++) q[MESH_IQ_DATA + ((head + i) & (len - 1))] = ((const uint8_t *)bytes)[i];
204    mesh_sim_set_u32(q + MESH_IQ_HEAD, head + n);
205    return true;
206}
207
208void mesh_sim_halt(uint8_t *mb, uint32_t slot, mesh_sim_launch *l) {
209    uint8_t *ctl = mesh_sim_ctl(mb, slot);
210    mesh_sim_set_u32(ctl + MESH_P_SHUTDOWN, MESH_SHUTDOWN_HALT);
211    mesh_sim_join(l);
212    mesh_sim_set_u32(ctl + MESH_P_RING_HEAD, (uint32_t)mesh_sim_u64(ctl + MESH_P_RING_TAIL));
213    mesh_sim_set_u32(ctl + MESH_P_ONLINE, 0);
214    mesh_sim_set_u32(ctl + MESH_P_HALTED, 0);
215    mesh_sim_set_u32(ctl + MESH_P_SHUTDOWN, 0);
216}
217
218#else
219typedef int mesh_rt_sim_mailbox_unused;
220#endif