==> 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