LCOV - code coverage report
Current view: top level - discof/admin - fd_adminctl.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 210 0.0 %
Date: 2026-08-14 04:54:57 Functions: 0 20 0.0 %

          Line data    Source code
       1             : #include "fd_adminctl.h"
       2             : 
       3             : #include "../../util/log/fd_log.h"
       4             : 
       5             : #include <errno.h>
       6             : #include <signal.h>
       7             : #include <unistd.h>
       8             : 
       9           0 : #define FD_ADMINCTL_MAGIC          (0xF17EDA2C37AD0200UL)
      10           0 : #define FD_ADMINCTL_STATE_MASK     (7UL)
      11           0 : #define FD_ADMINCTL_SEQ_SHIFT      (3UL)
      12           0 : #define FD_ADMINCTL_SEQ_BITS       (8UL)
      13           0 : #define FD_ADMINCTL_SEQ_VALUE_MASK ((1UL<<FD_ADMINCTL_SEQ_BITS)-1UL)
      14           0 : #define FD_ADMINCTL_TS_SHIFT       (FD_ADMINCTL_SEQ_SHIFT+FD_ADMINCTL_SEQ_BITS)
      15           0 : #define FD_ADMINCTL_TS_BITS        (FD_ADMINCTL_PID_SHIFT-FD_ADMINCTL_TS_SHIFT)
      16           0 : #define FD_ADMINCTL_TS_VALUE_MASK  ((1UL<<FD_ADMINCTL_TS_BITS)-1UL)
      17           0 : #define FD_ADMINCTL_PID_SHIFT      (32UL)
      18             : #define FD_ADMINCTL_RESERVE_TTL    (5UL)
      19             : 
      20           0 : #define FD_ADMINCTL_STATE_FREE       (0UL)
      21           0 : #define FD_ADMINCTL_STATE_RESERVED   (1UL)
      22             : #define FD_ADMINCTL_STATE_PUBLISHED  (2UL)
      23           0 : #define FD_ADMINCTL_STATE_PROCESSING (3UL)
      24             : #define FD_ADMINCTL_STATE_DONE       (4UL)
      25             : 
      26             : struct fd_adminctl_slot {
      27             :   ulong state_pid_seq;
      28             :   ulong cmd;
      29             :   ulong result;
      30             :   ulong payload_sz;
      31             :   uchar payload[ FD_ADMINCTL_PAYLOAD_MAX ];
      32             : };
      33             : typedef struct fd_adminctl_slot fd_adminctl_slot_t;
      34             : 
      35             : struct fd_adminctl_private {
      36             :   ulong               magic; /* ==FD_ADMINCTL_MAGIC */
      37             :   ulong               next_request_id;
      38             :   ulong               poll_idx;
      39             :   fd_adminctl_slot_t  slots[ FD_ADMINCTL_SLOT_CNT ];
      40             : };
      41             : typedef struct fd_adminctl_private fd_adminctl_t;
      42             : 
      43             : static inline ulong
      44             : fd_adminctl_state_pid_seq( uint  pid,
      45             :                            ulong seq,
      46             :                            ulong reserve_ts_sec,
      47           0 :                            ulong state ) {
      48           0 :   return (((ulong)pid)<<FD_ADMINCTL_PID_SHIFT) |
      49           0 :          ((reserve_ts_sec & FD_ADMINCTL_TS_VALUE_MASK)<<FD_ADMINCTL_TS_SHIFT) |
      50           0 :          ((seq & FD_ADMINCTL_SEQ_VALUE_MASK)<<FD_ADMINCTL_SEQ_SHIFT) |
      51           0 :          state;
      52           0 : }
      53             : 
      54             : static inline ulong
      55           0 : fd_adminctl_state( ulong state_pid_seq ) {
      56           0 :   return state_pid_seq & FD_ADMINCTL_STATE_MASK;
      57           0 : }
      58             : 
      59             : static inline ulong
      60             : fd_adminctl_state_update( ulong state_pid_seq,
      61           0 :                                   ulong state ) {
      62           0 :   return (state_pid_seq & ~FD_ADMINCTL_STATE_MASK) | state;
      63           0 : }
      64             : 
      65             : static inline ulong
      66           0 : fd_adminctl_reserve_ts_sec( ulong state_pid_seq ) {
      67           0 :   return (state_pid_seq>>FD_ADMINCTL_TS_SHIFT) & FD_ADMINCTL_TS_VALUE_MASK;
      68           0 : }
      69             : 
      70             : static inline uint
      71           0 : fd_adminctl_pid( ulong state_pid_seq ) {
      72           0 :   return (uint)(state_pid_seq>>FD_ADMINCTL_PID_SHIFT);
      73           0 : }
      74             : 
      75             : static inline ulong
      76           0 : fd_adminctl_now_sec( void ) {
      77           0 :   return (ulong)(fd_log_wallclock() / 1000000000L);
      78           0 : }
      79             : 
      80             : static inline ulong
      81             : fd_adminctl_elapsed_sec( ulong now_sec,
      82           0 :                          ulong then_sec ) {
      83           0 :   return (now_sec - then_sec) & FD_ADMINCTL_TS_VALUE_MASK;
      84           0 : }
      85             : 
      86             : static int
      87           0 : fd_adminctl_pid_alive( uint pid ) {
      88           0 :   if( FD_UNLIKELY( !pid ) ) return 0;
      89           0 :   if( FD_UNLIKELY( kill( (pid_t)pid, 0 ) ) ) {
      90           0 :     if( FD_LIKELY( errno==ESRCH ) ) return 0;
      91           0 :     return 1;
      92           0 :   }
      93           0 :   return 1;
      94           0 : }
      95             : 
      96             : static fd_adminctl_slot_t *
      97             : fd_adminctl_slot_laddr( fd_adminctl_t * adminctl,
      98           0 :                         ulong           slot_id ) {
      99           0 :   return &adminctl->slots[ slot_id ];
     100           0 : }
     101             : 
     102             : FD_FN_CONST ulong
     103           0 : fd_adminctl_align( void ) {
     104           0 :   return FD_ADMINCTL_ALIGN;
     105           0 : }
     106             : 
     107             : FD_FN_CONST ulong
     108           0 : fd_adminctl_footprint( void ) {
     109           0 :   return sizeof(fd_adminctl_t);
     110           0 : }
     111             : 
     112             : void *
     113           0 : fd_adminctl_new( void * shmem ) {
     114           0 :   fd_adminctl_t * adminctl = (fd_adminctl_t *)shmem;
     115             : 
     116           0 :   if( FD_UNLIKELY( !shmem ) ) {
     117           0 :     FD_LOG_WARNING(( "NULL shmem" ));
     118           0 :     return NULL;
     119           0 :   }
     120             : 
     121           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_adminctl_align() ) ) ) {
     122           0 :     FD_LOG_WARNING(( "misaligned shmem" ));
     123           0 :     return NULL;
     124           0 :   }
     125             : 
     126           0 :   fd_memset( adminctl, 0, fd_adminctl_footprint() );
     127             : 
     128           0 :   FD_COMPILER_MFENCE();
     129           0 :   FD_VOLATILE( adminctl->magic ) = FD_ADMINCTL_MAGIC;
     130             : 
     131           0 :   return (void *)adminctl;
     132           0 : }
     133             : 
     134             : fd_adminctl_t *
     135           0 : fd_adminctl_join( void * shadminctl ) {
     136             : 
     137           0 :   if( FD_UNLIKELY( !shadminctl ) ) {
     138           0 :     FD_LOG_WARNING(( "NULL shadminctl" ));
     139           0 :     return NULL;
     140           0 :   }
     141             : 
     142           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shadminctl, fd_adminctl_align() ) ) ) {
     143           0 :     FD_LOG_WARNING(( "misaligned shadminctl" ));
     144           0 :     return NULL;
     145           0 :   }
     146             : 
     147           0 :   fd_adminctl_t * adminctl = (fd_adminctl_t *)shadminctl;
     148             : 
     149           0 :   if( FD_UNLIKELY( adminctl->magic!=FD_ADMINCTL_MAGIC ) ) {
     150           0 :     FD_LOG_WARNING(( "bad magic" ));
     151           0 :     return NULL;
     152           0 :   }
     153             : 
     154           0 :   return adminctl;
     155           0 : }
     156             : 
     157             : ulong
     158             : fd_adminctl_reserve( fd_adminctl_t * adminctl,
     159             :                      void **         payload_out,
     160           0 :                      ulong *         payload_max_out ) {
     161             : 
     162             :   /* Identify the command process with a pid and a monotonically
     163             :      increasing sequence number. */
     164           0 :   uint  pid = (uint)getpid();
     165           0 :   ulong seq = FD_ATOMIC_ADD_AND_FETCH( &adminctl->next_request_id, 1UL );
     166             : 
     167             :   /* Track the pid of a process which may be hung */
     168           0 :   uint  hung_pid = UINT_MAX;
     169           0 :   ulong now_sec  = fd_adminctl_now_sec();
     170             : 
     171           0 :   for( ulong slot_id=0UL; slot_id<FD_ADMINCTL_SLOT_CNT; slot_id++ ) {
     172           0 :     fd_adminctl_slot_t * slot          = fd_adminctl_slot_laddr( adminctl, slot_id );
     173           0 :     ulong                state_pid_seq = FD_VOLATILE_CONST( slot->state_pid_seq );
     174           0 :     uint                 owner_pid     = fd_adminctl_pid( state_pid_seq );
     175             : 
     176             :     /* Reclaim a slot if it's a dead command.  The conditions for
     177             :        reclaiming a slot are:
     178             :        - the slot is in a command owned state (RESERVED or DONE)
     179             :        - the process for the slot is dead */
     180           0 :     ulong state = fd_adminctl_state( state_pid_seq );
     181           0 :     if( FD_UNLIKELY( (state==FD_ADMINCTL_STATE_RESERVED || state==FD_ADMINCTL_STATE_DONE) && !fd_adminctl_pid_alive( owner_pid ) ) ) {
     182           0 :       FD_LOG_WARNING(( "a dead command process (pid=%u) has been detected.  This is likely the "
     183           0 :                        "result of a process being killed before it issued the command "
     184           0 :                        "or consumed the result of the command", owner_pid ));
     185           0 :       ulong free_state_pid_seq = fd_adminctl_state_update( state_pid_seq, FD_ADMINCTL_STATE_FREE );
     186           0 :       if( FD_LIKELY( FD_ATOMIC_CAS( &slot->state_pid_seq, state_pid_seq, free_state_pid_seq )==state_pid_seq ) ) {
     187           0 :         state_pid_seq = FD_VOLATILE_CONST( slot->state_pid_seq );
     188           0 :         state         = fd_adminctl_state( state_pid_seq );
     189           0 :       } else {
     190           0 :         continue;
     191           0 :       }
     192           0 :     }
     193             : 
     194             :     /* If a slot is reserved and seems to be hung, track the pid to
     195             :        notify the caller.  Note that kill(pid,0) only checks the pid
     196             :        number; a reused pid can make a dead owner look hung. */
     197           0 :     if( FD_UNLIKELY( state==FD_ADMINCTL_STATE_RESERVED ) ) {
     198           0 :       ulong reserve_ts_sec = fd_adminctl_reserve_ts_sec( state_pid_seq );
     199           0 :       if( FD_UNLIKELY( fd_adminctl_pid_alive( owner_pid ) ) ) {
     200           0 :         if( FD_UNLIKELY( fd_adminctl_elapsed_sec( now_sec, reserve_ts_sec )>FD_ADMINCTL_RESERVE_TTL ) ) {
     201           0 :           hung_pid = owner_pid;
     202           0 :         }
     203           0 :       }
     204           0 :     }
     205             : 
     206           0 :     if( FD_UNLIKELY( state!=FD_ADMINCTL_STATE_FREE ) ) continue;
     207             : 
     208           0 :     ulong reserved_state_pid_seq = fd_adminctl_state_pid_seq( pid, seq, now_sec, FD_ADMINCTL_STATE_RESERVED );
     209           0 :     if( FD_UNLIKELY( FD_ATOMIC_CAS( &slot->state_pid_seq, state_pid_seq, reserved_state_pid_seq )!=state_pid_seq ) ) continue;
     210             : 
     211           0 :     fd_memzero_explicit( slot->payload, sizeof(slot->payload) );
     212           0 :     *payload_out     = slot->payload;
     213           0 :     *payload_max_out = FD_ADMINCTL_PAYLOAD_MAX;
     214           0 :     return slot_id;
     215           0 :   }
     216             : 
     217           0 :   if( FD_UNLIKELY( hung_pid!=UINT_MAX ) ) {
     218           0 :     FD_LOG_WARNING(( "Unable to run command as there is another process (pid=%u) attempting to run a "
     219           0 :                      "command which appears to be hung (or the pid may have been reused) and has not "
     220           0 :                      "executed for over 5 seconds. Consider forcefully killing the process and "
     221           0 :                      "retrying the command.", hung_pid ));
     222             : 
     223           0 :   }
     224             : 
     225           0 :   return ULONG_MAX;
     226           0 : }
     227             : 
     228             : void
     229             : fd_adminctl_publish( fd_adminctl_t * adminctl,
     230             :                      ulong           slot_id,
     231             :                      ulong           cmd_id,
     232           0 :                      ulong           payload_sz ) {
     233             : 
     234             :   /* Now that the caller has written the payload into the adminctl's
     235             :      app region, we can publish the command to the admin tile.  At this
     236             :      point, the admin tile will own the command and return a result. */
     237           0 :   if( FD_UNLIKELY( slot_id>=FD_ADMINCTL_SLOT_CNT ) ) FD_LOG_CRIT(( "bad slot_id %lu", slot_id ));
     238             : 
     239           0 :   fd_adminctl_slot_t * slot          = fd_adminctl_slot_laddr( adminctl, slot_id );
     240           0 :   ulong                state_pid_seq = FD_VOLATILE_CONST( slot->state_pid_seq );
     241           0 :   if( FD_UNLIKELY( fd_adminctl_state( state_pid_seq )!=FD_ADMINCTL_STATE_RESERVED ) ) FD_LOG_CRIT(( "adminctl publish without reservation" ));
     242             : 
     243           0 :   slot->cmd        = cmd_id;
     244           0 :   slot->payload_sz = payload_sz;
     245             : 
     246           0 :   if( FD_UNLIKELY( FD_ATOMIC_CAS( &slot->state_pid_seq,
     247           0 :                                   state_pid_seq,
     248           0 :                                   fd_adminctl_state_update( state_pid_seq, FD_ADMINCTL_STATE_PUBLISHED ) )!=state_pid_seq ) ) {
     249           0 :     FD_LOG_ERR(( "The command process is in an unexpected and invalid state while sending the "
     250           0 :                  "the command to the running firedancer.  This is likely the result of a bug. "
     251           0 :                  "Please report this to the firedancer team." ));
     252           0 :   }
     253           0 : }
     254             : 
     255             : ulong
     256             : fd_adminctl_poll( fd_adminctl_t * adminctl,
     257             :                   ulong *         slot_id_out,
     258             :                   void **         payload_out,
     259           0 :                   ulong *         payload_sz_out ) {
     260             : 
     261           0 :   ulong                slot_id = adminctl->poll_idx++ % FD_ADMINCTL_SLOT_CNT;
     262           0 :   fd_adminctl_slot_t * slot    = fd_adminctl_slot_laddr( adminctl, slot_id );
     263             : 
     264           0 :   ulong state_pid_seq = FD_VOLATILE_CONST( slot->state_pid_seq );
     265           0 :   FD_COMPILER_MFENCE();
     266             : 
     267           0 :   if( FD_UNLIKELY( fd_adminctl_state( state_pid_seq )!=FD_ADMINCTL_STATE_PUBLISHED ) ) return FD_ADMINCTL_CMD_IDLE;
     268             : 
     269           0 :   ulong cmd_id     = slot->cmd;
     270           0 :   ulong payload_sz = slot->payload_sz;
     271             : 
     272           0 :   ulong processing_state_pid_seq = fd_adminctl_state_update( state_pid_seq, FD_ADMINCTL_STATE_PROCESSING );
     273           0 :   if( FD_UNLIKELY( FD_ATOMIC_CAS( &slot->state_pid_seq, state_pid_seq, processing_state_pid_seq )!=state_pid_seq ) ) return FD_ADMINCTL_CMD_IDLE;
     274             : 
     275           0 :   *slot_id_out    = slot_id;
     276           0 :   *payload_out    = slot->payload;
     277           0 :   *payload_sz_out = payload_sz;
     278           0 :   return cmd_id;
     279           0 : }
     280             : 
     281             : void
     282             : fd_adminctl_complete_response( fd_adminctl_t * adminctl,
     283             :                                ulong           slot_id,
     284             :                                ulong           result,
     285             :                                void const *    resp,
     286           0 :                                ulong           resp_sz ) {
     287             : 
     288           0 :   if( FD_UNLIKELY( resp_sz>FD_ADMINCTL_PAYLOAD_MAX ) ) FD_LOG_CRIT(( "bad resp_sz %lu", resp_sz ));
     289             : 
     290           0 :   fd_adminctl_slot_t * slot          = fd_adminctl_slot_laddr( adminctl, slot_id );
     291           0 :   ulong                state_pid_seq = FD_VOLATILE_CONST( slot->state_pid_seq );
     292             : 
     293           0 :   if( FD_UNLIKELY( fd_adminctl_state( state_pid_seq )!=FD_ADMINCTL_STATE_PROCESSING ) ) FD_LOG_ERR(( "adminctl complete without processing command" ));
     294             : 
     295           0 :   fd_memzero_explicit( slot->payload, slot->payload_sz );
     296           0 :   if( FD_UNLIKELY( resp_sz ) ) memcpy( slot->payload, resp, resp_sz );
     297           0 :   slot->payload_sz = resp_sz;
     298           0 :   slot->result     = result;
     299           0 :   if( FD_UNLIKELY( FD_ATOMIC_CAS( &slot->state_pid_seq,
     300           0 :                                   state_pid_seq,
     301           0 :                                   fd_adminctl_state_update( state_pid_seq, FD_ADMINCTL_STATE_DONE ) )!=state_pid_seq ) )
     302           0 :     FD_LOG_ERR(( "invariant violation: adminctl_complete state update failed" ));
     303           0 : }
     304             : 
     305             : void
     306             : fd_adminctl_complete( fd_adminctl_t * adminctl,
     307             :                       ulong           slot_id,
     308           0 :                       ulong           result ) {
     309           0 :   fd_adminctl_complete_response( adminctl, slot_id, result, NULL, 0UL );
     310           0 : }
     311             : 
     312             : ulong
     313             : fd_adminctl_wait_response( fd_adminctl_t * adminctl,
     314             :                            ulong           slot_id,
     315             :                            void *          resp,
     316             :                            ulong           resp_max,
     317           0 :                            ulong *         resp_sz_out ) {
     318             : 
     319           0 :   fd_adminctl_slot_t * slot = fd_adminctl_slot_laddr( adminctl, slot_id );
     320             : 
     321           0 :   uint pid = (uint)getpid();
     322             : 
     323           0 :   for(;;) {
     324           0 :     ulong state_pid_seq = FD_VOLATILE_CONST( slot->state_pid_seq );
     325           0 :     FD_COMPILER_MFENCE();
     326             : 
     327           0 :     ulong state = fd_adminctl_state( state_pid_seq );
     328           0 :     if( FD_UNLIKELY( fd_adminctl_pid( state_pid_seq )!=pid ||
     329           0 :                      (state!=FD_ADMINCTL_STATE_PUBLISHED &&
     330           0 :                       state!=FD_ADMINCTL_STATE_PROCESSING &&
     331           0 :                       state!=FD_ADMINCTL_STATE_DONE) ) ) {
     332           0 :       FD_LOG_ERR(( "The command process is in an unexpected and invalid state while waiting for "
     333           0 :                    "the command to complete.  This is likely the result of a bug in firedancer. "
     334           0 :                    "Please report this to the firedancer team." ));
     335           0 :     }
     336             : 
     337           0 :     if( FD_LIKELY( state==FD_ADMINCTL_STATE_DONE ) ) {
     338           0 :       ulong result  = slot->result;
     339           0 :       ulong resp_sz = fd_ulong_min( slot->payload_sz, FD_ADMINCTL_PAYLOAD_MAX );
     340           0 :       if( FD_UNLIKELY( resp ) ) memcpy( resp, slot->payload, fd_ulong_min( resp_sz, resp_max ) );
     341           0 :       if( FD_UNLIKELY( resp_sz_out ) ) *resp_sz_out = resp_sz;
     342           0 :       fd_memzero_explicit( slot->payload, resp_sz );
     343           0 :       if( FD_LIKELY( FD_ATOMIC_CAS( &slot->state_pid_seq,
     344           0 :                                     state_pid_seq,
     345           0 :                                     fd_adminctl_state_update( state_pid_seq, FD_ADMINCTL_STATE_FREE ) )==state_pid_seq ) ) {
     346           0 :         return result;
     347           0 :       }
     348           0 :     }
     349           0 :     FD_SPIN_PAUSE();
     350           0 :   }
     351           0 : }
     352             : 
     353             : ulong
     354             : fd_adminctl_wait( fd_adminctl_t * adminctl,
     355           0 :                   ulong           slot_id ) {
     356           0 :   return fd_adminctl_wait_response( adminctl, slot_id, NULL, 0UL, NULL );
     357           0 : }

Generated by: LCOV version 1.14