Line data Source code
1 : #include "../../disco/topo/fd_topo.h"
2 : #include "../../discof/fd_startup.h"
3 : #include "../../util/log/fd_log.h"
4 : #include "../../tango/dcache/fd_dcache.h"
5 : #include "../../tango/fd_tango_base.h"
6 :
7 : #include <errno.h>
8 : #include <fcntl.h>
9 : #include <stdio.h>
10 : #include <unistd.h>
11 : #include <sys/stat.h>
12 :
13 : #include "../../flamenco/capture/fd_capture_ctx.h"
14 : #include "../../flamenco/capture/fd_solcap_writer.h"
15 : #include <time.h>
16 : #include "generated/fd_solcap_tile_seccomp.h"
17 :
18 :
19 : /* The solcap tile is responsible for managing capture context
20 : for debugging runtime execution.
21 :
22 : The tile is enabled when capture context is enabled in the config.
23 :
24 : ```
25 : [capture]
26 : solcap_capture = "/path/to/filename.solcap.pcapng"
27 : ```
28 :
29 : When enabled, each tile that writes to solcap will initialize
30 : a mcache/dcache pair to use as a shared buffer to communicate with
31 : the solcap tile. Each tile that requires solcap writes will declare
32 : their own capture context to pass into runtime execution or post
33 : execution to write to the buffer via API's provided in the capture
34 : context and notify the solcap tile. The solcap tile will then
35 : process the messages from the link and write out to the file.
36 :
37 : More information about capture context in fd_capture_ctx.h
38 :
39 : Solcap Tile:
40 :
41 : The solcap tile is initialized with the incoming links from the
42 : topology, for each tile that requires solcap writes. The handling for
43 : messages is slightly altered from that of the normal stem run loop.
44 :
45 : The messages sent to the solcap tile are bounded by the size of the
46 : 10mb (size of account data) + (much smaller) header information. In
47 : order to handle the larger messages in an efficient manner,
48 : the tile providing the data will send the data in chunks of at most
49 : 128kb, on a link ~4mb. This is in order to avoid cache trashing. This
50 : is done by using a custom input selection control to shuffle the
51 : incoming frags and origin in-link only if the current message has
52 : been read completely using the SOM/EOM flags.
53 :
54 : The recent-only mode is a mode that allows for the capture of the
55 : last N slots of the execution. This is useful for debugging runtime
56 : on a live network. The mode is enabled by setting the recent_only
57 : configuration to 1. The number of slots per file is set by the
58 : recent_slots_per_file configuration. The default is 128 slots per
59 : file. The files are named recent_0.solcap, recent_1.solcap,. The
60 : files are rotated when the current file reaches the number of slots
61 : per file.
62 : */
63 :
64 : struct fd_solcap_tile_ctx {
65 : ulong tile_idx;
66 :
67 : fd_startup_gate_t startup_gate[1];
68 :
69 : ulong msg_idx;
70 : ushort msg_set_sig;
71 : ulong msg_set_slot;
72 : uint block_len;
73 :
74 : /* Capture context management */
75 : fd_capture_ctx_t * capture_ctx;
76 : fd_capture_link_t * capctx_type;
77 :
78 : int fd; /* Current file descriptor */
79 :
80 : /* Recent-only rotating capture state */
81 : int recent_only; /* 1 if using 2-file flip-flop, 0 for single file */
82 : int recent_fds[2]; /* File descriptors for flip-flop and system calls */
83 : ulong recent_current_idx; /* Current file index (0 or 1) */
84 : ulong recent_file_start_slot; /* Slot number when current file was started (ULONG_MAX = uninitialized) */
85 : ulong recent_slots_per_file; /* Number of slots per file */
86 :
87 : /* Incoming links for mcache/dcache processing */
88 : struct {
89 : fd_wksp_t * mem;
90 : ulong chunk0;
91 : ulong wmark;
92 : ulong mtu;
93 : } in[32];
94 :
95 : ulong in_cnt;
96 :
97 : /* Track which input link we're currently processing */
98 : ulong current_in_idx; /* ULONG_MAX means no active message */
99 :
100 : };
101 :
102 : typedef struct fd_solcap_tile_ctx fd_solcap_tile_ctx_t;
103 :
104 : FD_FN_CONST static inline ulong
105 0 : scratch_align( void ) {
106 0 : return 128UL;
107 0 : }
108 :
109 : FD_FN_PURE static inline ulong
110 0 : scratch_footprint( fd_topo_tile_t const * tile ) {
111 0 : (void)tile;
112 0 : ulong l = FD_LAYOUT_INIT;
113 0 : l = FD_LAYOUT_APPEND ( l, alignof(fd_solcap_tile_ctx_t), sizeof(fd_solcap_tile_ctx_t) );
114 0 : l = FD_LAYOUT_APPEND ( l, fd_capture_ctx_align(), fd_capture_ctx_footprint() );
115 0 : return FD_LAYOUT_FINI( l, scratch_align() );
116 0 : }
117 :
118 : static ulong
119 : populate_allowed_seccomp( fd_topo_t const * topo,
120 : fd_topo_tile_t const * tile,
121 : ulong out_cnt,
122 0 : struct sock_filter * out ) {
123 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
124 0 : fd_solcap_tile_ctx_t const * ctx = (fd_solcap_tile_ctx_t const *)scratch;
125 :
126 0 : uint solcap_fd_0 = ctx->recent_only ? (uint)ctx->recent_fds[0] : (uint)ctx->fd;
127 0 : uint solcap_fd_1 = ctx->recent_only ? (uint)ctx->recent_fds[1] : (uint)ctx->fd;
128 :
129 0 : populate_sock_filter_policy_fd_solcap_tile( out_cnt,
130 0 : out,
131 0 : (uint)fd_log_private_logfile_fd(),
132 0 : solcap_fd_0,
133 0 : solcap_fd_1 );
134 0 : return sock_filter_policy_fd_solcap_tile_instr_cnt;
135 0 : }
136 :
137 : static ulong
138 : populate_allowed_fds( fd_topo_t const * topo,
139 : fd_topo_tile_t const * tile,
140 : ulong out_fds_cnt,
141 0 : int * out_fds ) {
142 0 : if( FD_UNLIKELY( out_fds_cnt<4UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
143 :
144 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
145 0 : fd_solcap_tile_ctx_t const * ctx = (fd_solcap_tile_ctx_t const *)scratch;
146 :
147 0 : ulong out_cnt = 0UL;
148 :
149 0 : out_fds[ out_cnt++ ] = 2; /* stderr */
150 0 : if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
151 0 : out_fds[ out_cnt++ ] = fd_log_private_logfile_fd();
152 :
153 0 : if( ctx->recent_only ) {
154 : /* In recent_only mode, allow both flip-flop file descriptors */
155 0 : if( FD_LIKELY( -1!=ctx->recent_fds[0] ) )
156 0 : out_fds[ out_cnt++ ] = ctx->recent_fds[0];
157 0 : if( FD_LIKELY( -1!=ctx->recent_fds[1] ) )
158 0 : out_fds[ out_cnt++ ] = ctx->recent_fds[1];
159 0 : } else {
160 : /* Traditional single file mode */
161 0 : if( FD_LIKELY( -1!=ctx->fd ) )
162 0 : out_fds[ out_cnt++ ] = ctx->fd;
163 0 : }
164 :
165 0 : return out_cnt;
166 0 : }
167 :
168 : /* fd_capctx_buf_process_msg processes a message fragment from the
169 : shared buffer and writes it to the solcap file using the solcap
170 : writer API.
171 :
172 : Returns block_len, the total PCAPNG Enhanced Packet Block (EPB)
173 : length in bytes. This value represents the complete size of the
174 : PCAPNG block including:
175 : - EPB header (28 bytes)
176 : - Internal chunk header
177 : - Message payload data
178 : - Padding (to align to 4-byte boundary)
179 : - Block footer (4 bytes)
180 :
181 : For messages that span multiple fragments:
182 : - Only the first fragment (SOM) returns a non-zero block_len,
183 : representing the total calculated block size
184 : - Continuation fragments return 0 as they don't write EPB headers
185 : - The block_len from SOM is saved and used when writing the EPB
186 : footer on the final fragment (EOM) */
187 : /* fd_capctx_buf_process_som processes the first fragment (SOM) of a
188 : message and writes the appropriate header structures.
189 :
190 : Returns block_len, the total PCAPNG Enhanced Packet Block (EPB)
191 : length in bytes for this message. */
192 : uint
193 : fd_capctx_buf_process_som( fd_solcap_tile_ctx_t * ctx,
194 : fd_solcap_buf_msg_t * msg_hdr,
195 0 : char * actual_data ) {
196 0 : uint block_len = 0U;
197 0 : FD_TEST( ctx->capture_ctx->capture != NULL );
198 :
199 0 : switch( msg_hdr->sig ) {
200 0 : case SOLCAP_WRITE_ACCOUNT: {
201 0 : fd_solcap_account_update_hdr_t * account_update = fd_type_pun( actual_data );
202 0 : block_len = fd_solcap_write_account_hdr( ctx->capture_ctx->capture, msg_hdr, account_update );
203 0 : break;
204 0 : }
205 0 : case SOLCAP_WRITE_BANK_PREIMAGE: {
206 0 : fd_solcap_bank_preimage_t * bank_preimage = fd_type_pun( actual_data );
207 0 : block_len = fd_solcap_write_bank_preimage( ctx->capture_ctx->capture, msg_hdr, bank_preimage );
208 0 : break;
209 0 : }
210 0 : case SOLCAP_STAKE_REWARDS_BEGIN: {
211 0 : fd_solcap_stake_rewards_begin_t * stake_rewards_begin = fd_type_pun( actual_data );
212 0 : block_len = fd_solcap_write_stake_rewards_begin( ctx->capture_ctx->capture, msg_hdr, stake_rewards_begin );
213 0 : break;
214 0 : }
215 0 : case SOLCAP_STAKE_REWARD_EVENT: {
216 0 : fd_solcap_stake_reward_event_t * stake_reward_event = fd_type_pun( actual_data );
217 0 : block_len = fd_solcap_write_stake_reward_event( ctx->capture_ctx->capture, msg_hdr, stake_reward_event );
218 0 : break;
219 0 : }
220 0 : case SOLCAP_STAKE_ACCOUNT_PAYOUT: {
221 0 : fd_solcap_stake_account_payout_t * stake_account_payout = fd_type_pun( actual_data );
222 0 : block_len = fd_solcap_write_stake_account_payout( ctx->capture_ctx->capture, msg_hdr, stake_account_payout );
223 0 : break;
224 0 : }
225 0 : default:
226 : /* Unknown signal received in message processing */
227 0 : FD_LOG_ERR(( "Unknown signal received in message processing: sig=%lu", (ulong)msg_hdr->sig ));
228 0 : break;
229 0 : }
230 0 : return block_len;
231 0 : }
232 :
233 : /* fd_capctx_buf_process_continuation processes continuation fragments
234 : (SOM=0) which contain raw data bytes to append to the current message.
235 : This is generic and works for any fragmented message type (account
236 : updates, or any future large message types). */
237 : void
238 : fd_capctx_buf_process_continuation( fd_solcap_tile_ctx_t * ctx,
239 : char * data,
240 0 : ulong data_sz ) {
241 0 : FD_TEST( ctx->capture_ctx->capture != NULL );
242 0 : fd_solcap_write_data( ctx->capture_ctx->capture, data, data_sz );
243 0 : }
244 :
245 : /* returnable_frag processes incoming message fragments and handles
246 : fragmented solcap messages using SOM (Start of Message) and EOM (End
247 : of Message) control flags.
248 :
249 : Message Fragmentation:
250 : ----------------------
251 : Solcap messages can be very large (up to 10MB for account data). To
252 : avoid cache thrashing and fit within the ~4MB link size, large
253 : messages are split into smaller fragments of at most 128KB
254 : (SOLCAP_WRITE_ACCOUNT_DATA_MTU).
255 :
256 : SOM/EOM Control Flags:
257 : ----------------------
258 : Each fragment has two control flags set in the frag metadata:
259 : - SOM (Start of Message): Set on the first fragment of a message
260 : - EOM (End of Message): Set on the last fragment of a message
261 :
262 : For a single-fragment message:
263 : Single Fragment: SOM=1, EOM=1
264 : For a multi-fragment message:
265 : First fragment: SOM=1, EOM=0
266 : Middle fragments: SOM=0, EOM=0
267 : Last fragment: SOM=0, EOM=1
268 :
269 : Fragment Processing:
270 : --------------------
271 : When SOM is set:
272 : - The fragment begins with a fd_solcap_buf_msg_t header containing
273 : the message type (sig), slot, and transaction index
274 : - This header is parsed and saved in the context (msg_set_sig,
275 : msg_set_slot)
276 : - The input link is locked (current_in_idx) to ensure all fragments
277 : of this message are processed sequentially from the same link
278 : - The actual data follows the header in the fragment
279 : - The PCAPNG block_len is calculated and saved (ctx->block_len) for
280 : use when writing the footer on EOM
281 :
282 : When SOM is not set (continuation fragment):
283 : - The entire fragment is data (no header)
284 : - The previously saved message state is used
285 : - No block_len is calculated (continuation data is appended)
286 :
287 : When EOM is set:
288 : - The PCAPNG block footer is written using the block_len from SOM
289 : - For bank preimage messages, the file is synced to disk
290 : - All message state is reset (msg_idx, block_len, msg_set_sig, etc.)
291 : - The input link is unlocked (current_in_idx = ULONG_MAX) to allow
292 : processing the next message
293 :
294 : This design allows the solcap tile to handle arbitrarily large
295 : messages while maintaining efficient memory usage and cache locality.
296 : */
297 :
298 : static inline void
299 : after_credit( fd_solcap_tile_ctx_t * ctx,
300 : fd_stem_context_t * stem,
301 : int * opt_poll_in,
302 0 : int * charge_busy ) {
303 0 : (void)stem; (void)opt_poll_in; (void)charge_busy;
304 0 : fd_startup_gate_idle( ctx->startup_gate );
305 0 : }
306 :
307 : static inline int
308 : returnable_frag( fd_solcap_tile_ctx_t * ctx,
309 : ulong in_idx,
310 : ulong seq FD_PARAM_UNUSED,
311 : ulong sig FD_PARAM_UNUSED,
312 : ulong chunk,
313 : ulong sz,
314 : ulong ctl,
315 : ulong tsorig FD_PARAM_UNUSED,
316 : ulong tspub FD_PARAM_UNUSED,
317 0 : fd_stem_context_t * stem FD_PARAM_UNUSED ) {
318 0 : fd_startup_gate_busy( ctx->startup_gate );
319 :
320 0 : if( FD_UNLIKELY( sz!=0UL && (chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) ) )
321 0 : FD_LOG_ERR(( "chunk %lu %lu from in %lu corrupt, not in range [%lu,%lu]", chunk, sz, in_idx, ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark ));
322 :
323 :
324 : /* If we're processing a message from a different input, skip this
325 : fragment for now without incrementing input seq number */
326 0 : if( FD_UNLIKELY( ctx->current_in_idx != ULONG_MAX && ctx->current_in_idx != in_idx ) ) return 1;
327 :
328 0 : uchar const * data = fd_chunk_to_laddr_const( ctx->in[in_idx].mem, chunk );
329 :
330 0 : int som = fd_frag_meta_ctl_som( ctl );
331 0 : int eom = fd_frag_meta_ctl_eom( ctl );
332 :
333 0 : if( som ) {
334 0 : fd_solcap_buf_msg_t * msg_hdr = fd_type_pun( (void *)data );
335 0 : char * actual_data = (char *)(data + sizeof(fd_solcap_buf_msg_t));
336 0 : FD_TEST( sz >= sizeof(fd_solcap_buf_msg_t) );
337 0 : ctx->msg_set_slot = msg_hdr->slot;
338 0 : ctx->msg_set_sig = (ushort)msg_hdr->sig;
339 0 : ctx->current_in_idx = in_idx; /* Start tracking this input */
340 :
341 : /* Handle file rotation for recent_only mode */
342 0 : if( ctx->recent_only ) {
343 0 : if( ctx->recent_file_start_slot == ULONG_MAX ) {
344 0 : ctx->recent_file_start_slot = msg_hdr->slot;
345 0 : }
346 0 : else if( msg_hdr->slot >= ctx->recent_file_start_slot + ctx->recent_slots_per_file ) {
347 : /* Check if we need to rotate (>= slots_per_file slots from start) */
348 : /* Flip-flop to the other file */
349 0 : ulong next_idx = 1UL - ctx->recent_current_idx;
350 0 : int next_fd = ctx->recent_fds[next_idx];
351 :
352 : /* The following is a series of checks to ensure the file is
353 : synced and truncated correctly. This occurs via:
354 : 1. Syncing the current file
355 : 2. Syncing the next file
356 : 3. Truncating the next file
357 : 4. Resetting the file descriptor position to 0
358 : 5. Reinitializing the solcap writer with the new file descriptor
359 : */
360 0 : FD_TEST( fsync( ctx->fd ) == 0 );
361 0 : FD_TEST( ftruncate( next_fd, 0L ) == 0 );
362 0 : FD_TEST( lseek( next_fd, 0L, SEEK_SET ) == 0L );
363 :
364 0 : fd_solcap_writer_init( ctx->capture_ctx->capture, next_fd );
365 0 : ctx->recent_current_idx = next_idx;
366 0 : ctx->recent_file_start_slot = msg_hdr->slot;
367 0 : ctx->fd = next_fd;
368 0 : }
369 0 : }
370 :
371 0 : uint block_len = fd_capctx_buf_process_som( ctx, msg_hdr, actual_data );
372 0 : FD_TEST( block_len > 0 ); /* SOM must return valid block length */
373 0 : ctx->block_len = block_len;
374 0 : } else {
375 : /* Continuation fragment: just raw data bytes */
376 0 : fd_capctx_buf_process_continuation( ctx, (char *)data, sz );
377 0 : }
378 :
379 : /* If message you receive has the eom flag, write footer */
380 0 : if( eom ) {
381 0 : FD_TEST( ctx->block_len > 0 ); /* Must have valid block length before writing footer */
382 0 : fd_solcap_write_ftr( ctx->capture_ctx->capture, ctx->block_len );
383 :
384 0 : ctx->msg_idx = 0;
385 0 : ctx->block_len = 0;
386 0 : ctx->msg_set_sig = 0;
387 0 : ctx->msg_set_slot = 0;
388 0 : ctx->current_in_idx = ULONG_MAX; /* Reset to sentinel - ready for next message */
389 0 : } else {
390 0 : ctx->msg_idx++;
391 0 : }
392 :
393 0 : return 0;
394 0 : }
395 :
396 : static void
397 : privileged_init( fd_topo_t const * topo,
398 0 : fd_topo_tile_t const * tile ) {
399 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
400 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
401 0 : fd_solcap_tile_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_solcap_tile_ctx_t), sizeof(fd_solcap_tile_ctx_t) );
402 0 : void * _capture_ctx = FD_SCRATCH_ALLOC_APPEND( l, fd_capture_ctx_align(), fd_capture_ctx_footprint() );
403 :
404 0 : ctx->tile_idx = tile->kind_id;
405 :
406 0 : ctx->capture_ctx = fd_capture_ctx_join( fd_capture_ctx_new( _capture_ctx ) );
407 0 : FD_TEST( ctx->capture_ctx );
408 :
409 0 : ctx->recent_only = tile->solcap.recent_only;
410 0 : ctx->recent_slots_per_file = tile->solcap.recent_slots_per_file ? tile->solcap.recent_slots_per_file : 128UL;
411 :
412 0 : struct stat path_stat;
413 0 : int stat_result = stat( tile->solcap.solcap_capture, &path_stat );
414 :
415 0 : if( ctx->recent_only ) {
416 : /* recent_only=1: Ensure path is a directory, create if not exists */
417 0 : if( stat_result != 0 ) {
418 0 : if( FD_UNLIKELY( mkdir(tile->solcap.solcap_capture, 0755) != 0 ) ) {
419 0 : FD_LOG_ERR(( "solcap_recent_only=1 but could not create directory: %s (%i-%s)",
420 0 : tile->solcap.solcap_capture, errno, strerror(errno) ));
421 0 : }
422 0 : } else if( FD_UNLIKELY( !S_ISDIR(path_stat.st_mode) ) ) {
423 0 : FD_LOG_ERR(( "solcap_recent_only=1 but path is not a directory: %s", tile->solcap.solcap_capture ));
424 0 : }
425 :
426 0 : ctx->recent_current_idx = 0;
427 0 : ctx->recent_file_start_slot = ULONG_MAX; /* Will be set on first fragment */
428 :
429 0 : for( ulong i = 0; i < 2; i++ ) {
430 0 : char filepath[PATH_MAX];
431 0 : int ret = snprintf( filepath, PATH_MAX, "%s/recent_%lu.solcap", tile->solcap.solcap_capture, i );
432 0 : if( FD_UNLIKELY( ret<0 || ret>=PATH_MAX ) ) {
433 0 : FD_LOG_ERR(( "snprintf failed or path too long for recent file %lu", i ));
434 0 : }
435 :
436 0 : ctx->recent_fds[i] = open( filepath, O_RDWR | O_CREAT | O_TRUNC, 0644 );
437 0 : if( FD_UNLIKELY( ctx->recent_fds[i] == -1 ) ) {
438 0 : FD_LOG_ERR(( "failed to open or create solcap recent file %s (%i-%s)",
439 0 : filepath, errno, strerror(errno) ));
440 0 : }
441 0 : }
442 :
443 0 : ctx->fd = ctx->recent_fds[0];
444 :
445 0 : } else {
446 : /* recent_only=0: Validate that path is a file*/
447 0 : if( FD_UNLIKELY( stat_result == 0 && S_ISDIR(path_stat.st_mode) ) ) {
448 0 : FD_LOG_ERR(( "solcap_recent_only=0 but path is a directory: %s (should be a file path)", tile->solcap.solcap_capture ));
449 0 : }
450 :
451 0 : ctx->fd = open( tile->solcap.solcap_capture, O_RDWR | O_CREAT | O_TRUNC, 0644 );
452 0 : if( FD_UNLIKELY( ctx->fd == -1 ) ) {
453 0 : FD_LOG_ERR(( "failed to open or create solcap capture file %s (%i-%s)",
454 0 : tile->solcap.solcap_capture, errno, strerror(errno) ));
455 0 : }
456 0 : }
457 :
458 0 : FD_TEST( ctx->capture_ctx->capture );
459 :
460 0 : ctx->capture_ctx->capture_solcap = 1;
461 0 : ctx->capture_ctx->solcap_start_slot = tile->solcap.capture_start_slot;
462 0 : fd_solcap_writer_init( ctx->capture_ctx->capture, ctx->fd );
463 :
464 0 : ctx->current_in_idx = ULONG_MAX; /* No active message initially */
465 0 : ctx->msg_idx = 0UL;
466 0 : ctx->msg_set_sig = 0U;
467 0 : ctx->msg_set_slot = 0UL;
468 0 : ctx->block_len = 0U;
469 :
470 0 : ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, scratch_align() );
471 :
472 0 : if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
473 0 : FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
474 :
475 0 : }
476 :
477 : static void
478 : unprivileged_init( fd_topo_t const * topo,
479 0 : fd_topo_tile_t const * tile ) {
480 :
481 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
482 0 : fd_solcap_tile_ctx_t * ctx = (fd_solcap_tile_ctx_t *)scratch;
483 :
484 0 : fd_startup_gate_init( ctx->startup_gate, topo, tile->in_cnt );
485 :
486 0 : ctx->in_cnt = 0UL;
487 0 : FD_TEST( tile->in_cnt <= 32UL );
488 0 : for( ulong i = 0UL; i < tile->in_cnt; i++ ) {
489 0 : fd_topo_link_t const * link = &topo->links[ tile->in_link_id[ i ] ];
490 0 : fd_topo_wksp_t const * wksp = &topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ];
491 :
492 0 : ctx->in[ctx->in_cnt].mem = wksp->wksp;
493 0 : ctx->in[ctx->in_cnt].chunk0 = fd_dcache_compact_chunk0( wksp->wksp, link->dcache );
494 0 : ctx->in[ctx->in_cnt].wmark = fd_dcache_compact_wmark( wksp->wksp, link->dcache, link->mtu );
495 0 : ctx->in[ctx->in_cnt].mtu = link->mtu;
496 0 : ctx->in_cnt++;
497 0 : }
498 0 : }
499 :
500 0 : #define STEM_BURST (1UL)
501 :
502 : /* See explanation in fd_pack */
503 0 : #define STEM_LAZY (128L*3000L)
504 :
505 0 : #define STEM_CALLBACK_CONTEXT_TYPE fd_solcap_tile_ctx_t
506 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_solcap_tile_ctx_t)
507 :
508 0 : #define STEM_CALLBACK_AFTER_CREDIT after_credit
509 0 : #define STEM_CALLBACK_RETURNABLE_FRAG returnable_frag
510 :
511 : #include "../../disco/stem/fd_stem.c"
512 :
513 : fd_topo_run_tile_t fd_tile_solcap = {
514 : .name = "solcap",
515 : .populate_allowed_seccomp = populate_allowed_seccomp,
516 : .populate_allowed_fds = populate_allowed_fds,
517 : .scratch_align = scratch_align,
518 : .scratch_footprint = scratch_footprint,
519 : .privileged_init = privileged_init,
520 : .unprivileged_init = unprivileged_init,
521 : .run = stem_run
522 : };
|