Line data Source code
1 : #include "../../disco/tiles.h"
2 : #include "generated/fd_exec_tile_seccomp.h"
3 :
4 : #include "../../util/pod/fd_pod_format.h"
5 : #include "../../discof/replay/fd_exec.h"
6 : #include "../../flamenco/runtime/context/fd_capture_ctx.h"
7 : #include "../../flamenco/runtime/fd_bank.h"
8 : #include "../../flamenco/runtime/fd_runtime.h"
9 :
10 : #include "../../funk/fd_funk.h"
11 :
12 : /* The exec tile is responsible for executing single transactions. The
13 : tile recieves a parsed transaction (fd_txn_p_t) and an identifier to
14 : which bank to execute against (index into the bank pool). With this,
15 : the exec tile is able to identify the correct bank and accounts db
16 : handle (funk_txn) to execute the transaction against. The results of
17 : the execution are then published to the writer tile(s). A writer tile
18 : is responsible for committing the results of the transaction to the
19 : accounts db and making any updates to the bank. */
20 :
21 : struct fd_exec_tile_out_ctx {
22 : ulong idx;
23 : fd_wksp_t * mem;
24 : ulong chunk;
25 : ulong chunk0;
26 : ulong wmark;
27 : };
28 : typedef struct fd_exec_tile_out_ctx fd_exec_tile_out_ctx_t;
29 :
30 : struct fd_exec_tile_ctx {
31 :
32 : /* link-related data structures. */
33 : ulong replay_exec_in_idx;
34 : ulong tile_idx;
35 :
36 : fd_wksp_t * replay_in_mem;
37 : ulong replay_in_chunk0;
38 : ulong replay_in_wmark;
39 :
40 : fd_exec_tile_out_ctx_t exec_writer_out[ 1 ];
41 : uchar boot_msg_sent;
42 :
43 : /* Shared bank hash cmp object. */
44 : fd_bank_hash_cmp_t * bank_hash_cmp;
45 :
46 : fd_spad_t * exec_spad;
47 : fd_wksp_t * exec_spad_wksp;
48 :
49 : /* Data structures related to managing and executing the transaction.
50 : The fd_txn_p_t is refreshed with every transaction and is sent
51 : from the dispatch/replay tile. The fd_exec_txn_ctx_t * is a valid
52 : local join that lives in the top-most frame of the spad that is
53 : setup when the exec tile is booted; its members are refreshed on
54 : the slot/epoch boundary. */
55 : fd_exec_txn_ctx_t * txn_ctx;
56 :
57 : /* Capture context for debugging runtime execution. */
58 : fd_capture_ctx_t * capture_ctx;
59 :
60 : /* A transaction can be executed as long as there is a valid handle to
61 : a funk_txn and a bank. These are queried from fd_banks_t and
62 : fd_funk_t.
63 : TODO: These should probably be made read-only handles. */
64 : fd_banks_t * banks;
65 : fd_funk_t funk[1];
66 : };
67 : typedef struct fd_exec_tile_ctx fd_exec_tile_ctx_t;
68 :
69 : FD_FN_CONST static inline ulong
70 0 : scratch_align( void ) {
71 0 : return 128UL;
72 0 : }
73 :
74 : FD_FN_PURE static inline ulong
75 0 : scratch_footprint( fd_topo_tile_t const * tile FD_PARAM_UNUSED ) {
76 : /* clang-format off */
77 0 : ulong l = FD_LAYOUT_INIT;
78 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_exec_tile_ctx_t), sizeof(fd_exec_tile_ctx_t) );
79 0 : l = FD_LAYOUT_APPEND( l, FD_CAPTURE_CTX_ALIGN, FD_CAPTURE_CTX_FOOTPRINT );
80 0 : return FD_LAYOUT_FINI( l, scratch_align() );
81 : /* clang-format on */
82 0 : }
83 :
84 : static void
85 : during_frag( fd_exec_tile_ctx_t * ctx,
86 : ulong in_idx,
87 : ulong seq FD_PARAM_UNUSED,
88 : ulong sig,
89 : ulong chunk,
90 : ulong sz,
91 0 : ulong ctl FD_PARAM_UNUSED ) {
92 :
93 0 : if( FD_LIKELY( in_idx==ctx->replay_exec_in_idx ) ) {
94 0 : if( FD_UNLIKELY( chunk < ctx->replay_in_chunk0 || chunk > ctx->replay_in_wmark ) ) {
95 0 : FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]",
96 0 : chunk,
97 0 : sz,
98 0 : ctx->replay_in_chunk0,
99 0 : ctx->replay_in_wmark ));
100 0 : }
101 :
102 0 : if( FD_LIKELY( sig==EXEC_NEW_TXN_SIG ) ) {
103 0 : fd_exec_txn_msg_t * txn = (fd_exec_txn_msg_t *)fd_chunk_to_laddr( ctx->replay_in_mem, chunk );
104 :
105 0 : ctx->txn_ctx->spad = ctx->exec_spad;
106 0 : ctx->txn_ctx->spad_wksp = ctx->exec_spad_wksp;
107 :
108 0 : ctx->txn_ctx->exec_err = fd_runtime_prepare_and_execute_txn(
109 0 : ctx->banks,
110 0 : txn->bank_idx,
111 0 : ctx->txn_ctx,
112 0 : &txn->txn,
113 0 : ctx->exec_spad,
114 0 : ctx->capture_ctx,
115 0 : 1 );
116 :
117 0 : return;
118 0 : } else {
119 0 : FD_LOG_CRIT(( "Unknown signature" ));
120 0 : }
121 0 : }
122 0 : }
123 :
124 : static void
125 : after_frag( fd_exec_tile_ctx_t * ctx,
126 : ulong in_idx FD_PARAM_UNUSED,
127 : ulong seq FD_PARAM_UNUSED,
128 : ulong sig,
129 : ulong sz FD_PARAM_UNUSED,
130 : ulong tsorig,
131 : ulong tspub,
132 0 : fd_stem_context_t * stem ) {
133 :
134 0 : if( sig==EXEC_NEW_TXN_SIG ) {
135 0 : FD_LOG_DEBUG(( "Sending ack for new txn msg" ));
136 : /* At this point we can assume that the transaction is done
137 : executing. A writer tile will be repsonsible for commiting
138 : the transaction back to funk. */
139 :
140 0 : fd_exec_tile_out_ctx_t * exec_out = ctx->exec_writer_out;
141 :
142 0 : fd_exec_writer_txn_msg_t * msg = fd_type_pun( fd_chunk_to_laddr( exec_out->mem, exec_out->chunk ) );
143 0 : msg->exec_tile_id = (uchar)ctx->tile_idx;
144 :
145 0 : fd_stem_publish(
146 0 : stem,
147 0 : exec_out->idx,
148 0 : FD_WRITER_TXN_SIG,
149 0 : exec_out->chunk,
150 0 : sizeof(*msg),
151 0 : 0UL,
152 0 : tsorig,
153 0 : tspub );
154 0 : exec_out->chunk = fd_dcache_compact_next( exec_out->chunk, sizeof(*msg), exec_out->chunk0, exec_out->wmark );
155 0 : } else {
156 0 : FD_LOG_ERR(( "Unknown message signature" ));
157 0 : }
158 0 : }
159 :
160 : static void
161 : unprivileged_init( fd_topo_t * topo,
162 0 : fd_topo_tile_t * tile ) {
163 :
164 : /********************************************************************/
165 : /* validate allocations */
166 : /********************************************************************/
167 :
168 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
169 :
170 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
171 0 : fd_exec_tile_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_tile_ctx_t), sizeof(fd_exec_tile_ctx_t) );
172 0 : void * capture_ctx_mem = FD_SCRATCH_ALLOC_APPEND( l, FD_CAPTURE_CTX_ALIGN, FD_CAPTURE_CTX_FOOTPRINT );
173 0 : ulong scratch_alloc_mem = FD_SCRATCH_ALLOC_FINI( l, scratch_align() );
174 0 : if( FD_UNLIKELY( scratch_alloc_mem - (ulong)scratch - scratch_footprint( tile ) ) ) {
175 0 : FD_LOG_ERR( ( "Scratch_alloc_mem did not match scratch_footprint diff: %lu alloc: %lu footprint: %lu",
176 0 : scratch_alloc_mem - (ulong)scratch - scratch_footprint( tile ),
177 0 : scratch_alloc_mem,
178 0 : (ulong)scratch + scratch_footprint( tile ) ) );
179 0 : }
180 :
181 : /********************************************************************/
182 : /* validate links */
183 : /********************************************************************/
184 :
185 0 : ctx->tile_idx = tile->kind_id;
186 :
187 : /* First find and setup the in-link from replay to exec. */
188 0 : ctx->replay_exec_in_idx = fd_topo_find_tile_in_link( topo, tile, "replay_exec", ctx->tile_idx );
189 0 : if( FD_UNLIKELY( ctx->replay_exec_in_idx==ULONG_MAX ) ) {
190 0 : FD_LOG_ERR(( "Could not find replay_exec in-link" ));
191 0 : }
192 0 : fd_topo_link_t * replay_exec_in_link = &topo->links[tile->in_link_id[ctx->replay_exec_in_idx]];
193 0 : if( FD_UNLIKELY( !replay_exec_in_link) ) {
194 0 : FD_LOG_ERR(( "Invalid replay_exec in-link" ));
195 0 : }
196 0 : ctx->replay_in_mem = topo->workspaces[topo->objs[replay_exec_in_link->dcache_obj_id].wksp_id].wksp;
197 0 : ctx->replay_in_chunk0 = fd_dcache_compact_chunk0( ctx->replay_in_mem, replay_exec_in_link->dcache );
198 0 : ctx->replay_in_wmark = fd_dcache_compact_wmark( ctx->replay_in_mem,
199 0 : replay_exec_in_link->dcache,
200 0 : replay_exec_in_link->mtu );
201 :
202 : /* Setup out link. */
203 0 : ulong idx = fd_topo_find_tile_out_link( topo, tile, "exec_writer", ctx->tile_idx );
204 0 : fd_topo_link_t * exec_out_link = &topo->links[ tile->out_link_id[ idx ] ];
205 :
206 0 : if( strcmp( exec_out_link->name, "exec_writer" ) ) {
207 0 : FD_LOG_CRIT(("exec_writer link has unexpected name %s", exec_out_link->name ));
208 0 : }
209 :
210 0 : fd_exec_tile_out_ctx_t * exec_out = ctx->exec_writer_out;
211 0 : exec_out->idx = idx;
212 0 : exec_out->mem = topo->workspaces[ topo->objs[ exec_out_link->dcache_obj_id ].wksp_id ].wksp;
213 0 : exec_out->chunk0 = fd_dcache_compact_chunk0( exec_out->mem, exec_out_link->dcache );
214 0 : exec_out->wmark = fd_dcache_compact_wmark( exec_out->mem, exec_out_link->dcache, exec_out_link->mtu );
215 0 : exec_out->chunk = exec_out->chunk0;
216 0 : ctx->boot_msg_sent = 0U;
217 :
218 : /********************************************************************/
219 : /* banks */
220 : /********************************************************************/
221 :
222 0 : ulong banks_obj_id = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "banks" );
223 0 : if( FD_UNLIKELY( banks_obj_id==ULONG_MAX ) ) {
224 0 : FD_LOG_ERR(( "Could not find topology object for banks" ));
225 0 : }
226 :
227 0 : ctx->banks = fd_banks_join( fd_topo_obj_laddr( topo, banks_obj_id ) );
228 0 : if( FD_UNLIKELY( !ctx->banks ) ) {
229 0 : FD_LOG_ERR(( "Failed to join banks" ));
230 0 : }
231 :
232 : /********************************************************************/
233 : /* spad allocator */
234 : /********************************************************************/
235 :
236 : /* First join the correct exec spad and hten the correct runtime spad
237 : which lives inside of the runtime public wksp. */
238 :
239 0 : ulong exec_spad_obj_id = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "exec_spad.%lu", ctx->tile_idx );
240 0 : if( FD_UNLIKELY( exec_spad_obj_id==ULONG_MAX ) ) {
241 0 : FD_LOG_ERR(( "Could not find topology object for exec spad" ));
242 0 : }
243 :
244 0 : ctx->exec_spad = fd_spad_join( fd_topo_obj_laddr( topo, exec_spad_obj_id ) );
245 0 : if( FD_UNLIKELY( !ctx->exec_spad ) ) {
246 0 : FD_LOG_ERR(( "Failed to join exec spad" ));
247 0 : }
248 0 : ctx->exec_spad_wksp = fd_wksp_containing( ctx->exec_spad );
249 :
250 : /********************************************************************/
251 : /* bank hash cmp */
252 : /********************************************************************/
253 :
254 0 : ulong bank_hash_cmp_obj_id = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "bh_cmp" );
255 0 : if( FD_UNLIKELY( bank_hash_cmp_obj_id==ULONG_MAX ) ) {
256 0 : FD_LOG_ERR(( "Could not find topology object for bank hash cmp" ));
257 0 : }
258 0 : ctx->bank_hash_cmp = fd_bank_hash_cmp_join( fd_topo_obj_laddr( topo, bank_hash_cmp_obj_id ) );
259 0 : if( FD_UNLIKELY( !ctx->bank_hash_cmp ) ) {
260 0 : FD_LOG_ERR(( "Failed to join bank hash cmp" ));
261 0 : }
262 :
263 : /********************************************************************/
264 : /* funk-specific setup */
265 : /********************************************************************/
266 :
267 0 : if( FD_UNLIKELY( !fd_funk_join( ctx->funk, fd_topo_obj_laddr( topo, tile->exec.funk_obj_id ) ) ) ) {
268 0 : FD_LOG_ERR(( "Failed to join database cache" ));
269 0 : }
270 :
271 : /********************************************************************/
272 : /* setup txncache */
273 : /********************************************************************/
274 :
275 : /* TODO: Implement this. */
276 :
277 : /********************************************************************/
278 : /* setup txn ctx */
279 : /********************************************************************/
280 :
281 0 : fd_spad_push( ctx->exec_spad );
282 : // FIXME account for this in exec spad footprint
283 0 : uchar * txn_ctx_mem = fd_spad_alloc_check( ctx->exec_spad, FD_EXEC_TXN_CTX_ALIGN, FD_EXEC_TXN_CTX_FOOTPRINT );
284 0 : ctx->txn_ctx = fd_exec_txn_ctx_join( fd_exec_txn_ctx_new( txn_ctx_mem ), ctx->exec_spad, ctx->exec_spad_wksp );
285 0 : *ctx->txn_ctx->funk = *ctx->funk;
286 0 : ctx->txn_ctx->bank_hash_cmp = ctx->bank_hash_cmp;
287 :
288 0 : FD_LOG_INFO(( "Done booting exec tile idx=%lu", ctx->tile_idx ));
289 :
290 0 : if( strlen( tile->exec.dump_proto_dir )>0 ) {
291 0 : ctx->capture_ctx = fd_capture_ctx_new( capture_ctx_mem );
292 0 : ctx->capture_ctx->dump_proto_output_dir = tile->exec.dump_proto_dir;
293 0 : ctx->capture_ctx->dump_proto_start_slot = tile->exec.capture_start_slot;
294 0 : ctx->capture_ctx->dump_instr_to_pb = tile->exec.dump_instr_to_pb;
295 0 : ctx->capture_ctx->dump_txn_to_pb = tile->exec.dump_txn_to_pb;
296 0 : ctx->capture_ctx->dump_syscall_to_pb = tile->exec.dump_syscall_to_pb;
297 0 : ctx->capture_ctx->dump_elf_to_pb = tile->exec.dump_elf_to_pb;
298 0 : } else {
299 0 : ctx->capture_ctx = NULL;
300 0 : }
301 0 : }
302 :
303 : static void
304 : after_credit( fd_exec_tile_ctx_t * ctx,
305 : fd_stem_context_t * stem,
306 : int * opt_poll_in FD_PARAM_UNUSED,
307 0 : int * charge_busy FD_PARAM_UNUSED ) {
308 :
309 0 : if( FD_UNLIKELY( !ctx->boot_msg_sent ) ) {
310 :
311 0 : ctx->boot_msg_sent = 1U;
312 :
313 0 : ulong txn_ctx_gaddr = fd_wksp_gaddr( ctx->exec_spad_wksp, ctx->txn_ctx );
314 0 : if( FD_UNLIKELY( !txn_ctx_gaddr ) ) {
315 0 : FD_LOG_CRIT(( "Could not get gaddr for txn_ctx" ));
316 0 : }
317 :
318 0 : ulong exec_spad_gaddr = fd_wksp_gaddr( ctx->exec_spad_wksp, ctx->exec_spad );
319 0 : if( FD_UNLIKELY( !exec_spad_gaddr ) ) {
320 0 : FD_LOG_CRIT(( "Could not get gaddr for exec_spad" ));
321 0 : }
322 :
323 0 : if( FD_UNLIKELY( txn_ctx_gaddr-exec_spad_gaddr>UINT_MAX ) ) {
324 0 : FD_LOG_CRIT(( "txn_ctx offset from exec spad is too large" ));
325 0 : }
326 :
327 0 : uint txn_ctx_offset = (uint)(txn_ctx_gaddr-exec_spad_gaddr);
328 :
329 : /* Notify writer tiles. */
330 :
331 0 : ulong tsorig = fd_frag_meta_ts_comp( fd_tickcount() );
332 :
333 0 : fd_exec_tile_out_ctx_t * exec_out = ctx->exec_writer_out;
334 :
335 0 : fd_exec_writer_boot_msg_t * msg = fd_type_pun( fd_chunk_to_laddr( exec_out->mem, exec_out->chunk ) );
336 :
337 0 : msg->txn_ctx_offset = txn_ctx_offset;
338 :
339 0 : ulong tspub = fd_frag_meta_ts_comp( fd_tickcount() );
340 0 : fd_stem_publish( stem,
341 0 : exec_out->idx,
342 0 : FD_WRITER_BOOT_SIG,
343 0 : exec_out->chunk,
344 0 : sizeof(*msg),
345 0 : 0UL,
346 0 : tsorig,
347 0 : tspub );
348 0 : exec_out->chunk = fd_dcache_compact_next( exec_out->chunk, sizeof(*msg), exec_out->chunk0, exec_out->wmark );
349 :
350 0 : }
351 0 : }
352 :
353 : static ulong
354 : populate_allowed_seccomp( fd_topo_t const * topo FD_PARAM_UNUSED,
355 : fd_topo_tile_t const * tile FD_PARAM_UNUSED,
356 : ulong out_cnt,
357 0 : struct sock_filter * out ) {
358 0 : populate_sock_filter_policy_fd_exec_tile( out_cnt, out, (uint)fd_log_private_logfile_fd() );
359 0 : return sock_filter_policy_fd_exec_tile_instr_cnt;
360 0 : }
361 :
362 : static ulong
363 : populate_allowed_fds( fd_topo_t const * topo FD_PARAM_UNUSED,
364 : fd_topo_tile_t const * tile FD_PARAM_UNUSED,
365 : ulong out_fds_cnt,
366 0 : int * out_fds ) {
367 :
368 0 : if( FD_UNLIKELY( out_fds_cnt<2UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
369 :
370 0 : ulong out_cnt = 0UL;
371 0 : out_fds[ out_cnt++ ] = 2; /* stderr */
372 0 : if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
373 0 : out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
374 0 : return out_cnt;
375 0 : }
376 :
377 0 : #define STEM_BURST (1UL)
378 :
379 0 : #define STEM_CALLBACK_CONTEXT_TYPE fd_exec_tile_ctx_t
380 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_exec_tile_ctx_t)
381 :
382 0 : #define STEM_CALLBACK_AFTER_CREDIT after_credit
383 0 : #define STEM_CALLBACK_DURING_FRAG during_frag
384 0 : #define STEM_CALLBACK_AFTER_FRAG after_frag
385 :
386 : #include "../../disco/stem/fd_stem.c"
387 :
388 : fd_topo_run_tile_t fd_tile_execor = {
389 : .name = "exec",
390 : .loose_footprint = 0UL,
391 : .populate_allowed_seccomp = populate_allowed_seccomp,
392 : .populate_allowed_fds = populate_allowed_fds,
393 : .scratch_align = scratch_align,
394 : .scratch_footprint = scratch_footprint,
395 : .unprivileged_init = unprivileged_init,
396 : .run = stem_run,
397 : };
|