LCOV - code coverage report
Current view: top level - util/wksp - fd_wksp_user.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 165 328 50.3 %
Date: 2026-10-22 18:10:43 Functions: 9 15 60.0 %

          Line data    Source code
       1             : #include "fd_wksp_private.h"
       2             : 
       3             : FD_TL int fd_wksp_oom_silent = 0;
       4             : 
       5             : /* fd_wksp_private_split_before splits a partition (index i2) into two
       6             :    smaller partitions and returns the partition index (i1) of the
       7             :    partition created by the split.  The created partition will be
       8             :    immediately before the original partition.  It will be in the
       9             :    partitioning with a zero tag.  It will not be in the idle stack, used
      10             :    treap or free treap.
      11             : 
      12             :    sz is size of the original partition post split.  This should be in
      13             :    (0,original_sz) (yes, open on both ends such that the post split
      14             :    partitions both have non-zero size.
      15             : 
      16             :    This will pop the idle stack once to assign the index of the newly
      17             :    created partition.  Assumes the caller knows the idle stack is not
      18             :    empty.
      19             : 
      20             :    Assumes the original partition is in the partitioning with a zero
      21             :    tag.  Further assumes the original partition is not in the idle
      22             :    stack, used treap or free treap.
      23             : 
      24             :    fd_wksp_private_split_after is identical except the partition created
      25             :    by the split is after the original partition. */
      26             : 
      27             : static ulong                                                      /* In [0,part_max) */
      28             : fd_wksp_private_split_before( ulong                     i2,       /* In [0,part_max) */
      29             :                               ulong                     s2,       /* In (0,size of i2) */
      30             :                               fd_wksp_t *               wksp,     /* Current local join */
      31          14 :                               fd_wksp_private_pinfo_t * pinfo ) { /* == fd_wksp_private_pinfo( wksp ) */
      32             : 
      33          14 :   ulong g3 = pinfo[ i2 ].gaddr_hi;                               /* Old end here */
      34          14 :   ulong g2 = g3 - s2;                                            /* New ends here */
      35          14 :   ulong g1 = pinfo[ i2 ].gaddr_lo;                               /* New starts here */
      36             : 
      37          14 :   ulong i0 = fd_wksp_private_pinfo_idx( pinfo[ i2 ].prev_cidx ); /* Old before */
      38          14 :   ulong i1 = fd_wksp_private_idle_stack_pop( wksp, pinfo );      /* New before */
      39             : 
      40          14 :   pinfo[ i1 ].gaddr_lo    = g1;
      41          14 :   pinfo[ i1 ].gaddr_hi    = g2;
      42          14 :   pinfo[ i1 ].tag         = 0UL;
      43          14 :   pinfo[ i1 ].in_same     = 0U;
      44          14 :   pinfo[ i1 ].prev_cidx   = fd_wksp_private_pinfo_cidx( i0 );
      45          14 :   pinfo[ i1 ].next_cidx   = fd_wksp_private_pinfo_cidx( i2 );
      46          14 :   pinfo[ i1 ].left_cidx   = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
      47          14 :   pinfo[ i1 ].right_cidx  = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
      48          14 :   pinfo[ i1 ].same_cidx   = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
      49          14 :   pinfo[ i1 ].parent_cidx = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
      50             : 
      51          14 :   pinfo[ i2 ].gaddr_lo    = g2;
      52          14 :   pinfo[ i2 ].prev_cidx   = fd_wksp_private_pinfo_cidx( i1 );
      53             : 
      54          14 :   if( fd_wksp_private_pinfo_idx_is_null( i0 ) ) wksp->part_head_cidx  = fd_wksp_private_pinfo_cidx( i1 );
      55          14 :   else                                          pinfo[ i0 ].next_cidx = fd_wksp_private_pinfo_cidx( i1 );
      56             : 
      57          14 :   return i1;
      58          14 : }
      59             : 
      60             : static ulong                                                     /* In [0,part_max) */
      61             : fd_wksp_private_split_after( ulong                     i1,       /* In [0,part_max) */
      62             :                              ulong                     s1,       /* In (0,size of i2) */
      63             :                              fd_wksp_t *               wksp,     /* Current local join */
      64         113 :                              fd_wksp_private_pinfo_t * pinfo ) { /* == fd_wksp_private_pinfo( wksp ) */
      65             : 
      66         113 :   ulong g1 = pinfo[ i1 ].gaddr_lo;                               /* Old starts here */
      67         113 :   ulong g2 = g1 + s1;                                            /* New starts here */
      68         113 :   ulong g3 = pinfo[ i1 ].gaddr_hi;                               /* New end here */
      69             : 
      70         113 :   ulong i2 = fd_wksp_private_idle_stack_pop( wksp, pinfo );      /* New before */
      71         113 :   ulong i3 = fd_wksp_private_pinfo_idx( pinfo[ i1 ].next_cidx ); /* Old after */
      72             : 
      73         113 :   pinfo[ i2 ].gaddr_lo    = g2;
      74         113 :   pinfo[ i2 ].gaddr_hi    = g3;
      75         113 :   pinfo[ i2 ].tag         = 0UL;
      76         113 :   pinfo[ i2 ].in_same     = 0U;
      77         113 :   pinfo[ i2 ].prev_cidx   = fd_wksp_private_pinfo_cidx( i1 );
      78         113 :   pinfo[ i2 ].next_cidx   = fd_wksp_private_pinfo_cidx( i3 );
      79         113 :   pinfo[ i2 ].left_cidx   = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
      80         113 :   pinfo[ i2 ].right_cidx  = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
      81         113 :   pinfo[ i2 ].same_cidx   = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
      82         113 :   pinfo[ i2 ].parent_cidx = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
      83             : 
      84         113 :   pinfo[ i1 ].gaddr_hi    = g2;
      85         113 :   pinfo[ i1 ].next_cidx   = fd_wksp_private_pinfo_cidx( i2 );
      86             : 
      87         113 :   if( fd_wksp_private_pinfo_idx_is_null( i3 ) ) wksp->part_tail_cidx  = fd_wksp_private_pinfo_cidx( i2 );
      88           2 :   else                                          pinfo[ i3 ].prev_cidx = fd_wksp_private_pinfo_cidx( i2 );
      89             : 
      90         113 :   return i2;
      91         113 : }
      92             : 
      93             : /* fd_wksp_private_merge_before i1 into i2 where i1 is the partition
      94             :    immediately preceding i2.  Assumes that i2 and the partition before
      95             :    it are not tagged and not in the idle stack, free treap or used
      96             :    treap.
      97             : 
      98             :    This will push the idle stack once to make the index used for the
      99             :    preceding partition available for future use.
     100             : 
     101             :    fd_wksp_private_merge_after is identical except the partition to
     102             :    merge the split is after the original partition. */
     103             : 
     104             : static void
     105             : fd_wksp_private_merge_before( ulong                     i1,       /* In [0,part_max), == prev to i2, on idle stack on return */
     106             :                               ulong                     i2,       /* In [0,part_max) */
     107             :                               fd_wksp_t *               wksp,     /* Current local join */
     108          16 :                               fd_wksp_private_pinfo_t * pinfo ) { /* == fd_wksp_private_pinfo( wksp ) */
     109             : 
     110          16 :   ulong i0 = fd_wksp_private_pinfo_idx( pinfo[ i1 ].prev_cidx ); /* Partition before that (if any) */
     111             : 
     112          16 :   pinfo[ i2 ].gaddr_lo  = pinfo[ i1 ].gaddr_lo;
     113          16 :   pinfo[ i2 ].prev_cidx = fd_wksp_private_pinfo_cidx( i0 );
     114             : 
     115          16 :   if( fd_wksp_private_pinfo_idx_is_null( i0 ) ) wksp->part_head_cidx  = fd_wksp_private_pinfo_cidx( i2 );
     116          13 :   else                                          pinfo[ i0 ].next_cidx = fd_wksp_private_pinfo_cidx( i2 );
     117             : 
     118          16 :   fd_wksp_private_idle_stack_push( i1, wksp, pinfo );
     119          16 : }
     120             : 
     121             : static void
     122             : fd_wksp_private_merge_after( ulong                     i1,       /* In [0,part_max) */
     123             :                              ulong                     i2,       /* In [0,part_max), == next to i1, on idle stack on return */
     124             :                              fd_wksp_t *               wksp,     /* Current local join */
     125          15 :                              fd_wksp_private_pinfo_t * pinfo ) { /* == fd_wksp_private_pinfo( wksp ) */
     126             : 
     127          15 :   ulong i3 = fd_wksp_private_pinfo_idx( pinfo[ i2 ].next_cidx ); /* Partition after that (if any) */
     128             : 
     129          15 :   pinfo[ i1 ].gaddr_hi  = pinfo[ i2 ].gaddr_hi;
     130          15 :   pinfo[ i1 ].next_cidx = fd_wksp_private_pinfo_cidx( i3 );
     131             : 
     132          15 :   if( fd_wksp_private_pinfo_idx_is_null( i3 ) ) wksp->part_tail_cidx  = fd_wksp_private_pinfo_cidx( i1 );
     133          15 :   else                                          pinfo[ i3 ].prev_cidx = fd_wksp_private_pinfo_cidx( i1 );
     134             : 
     135          15 :   fd_wksp_private_idle_stack_push( i2, wksp, pinfo );
     136          15 : }
     137             : 
     138             : static void
     139             : fd_wksp_private_free( ulong                     i,        /* Partition to free, in [0,part_max) */
     140             :                       fd_wksp_t *               wksp,     /* Current local join */
     141          53 :                       fd_wksp_private_pinfo_t * pinfo ) { /* == fd_wksp_private_pinfo( wksp ) */
     142             : 
     143          53 :   ulong part_max = wksp->part_max;
     144             : 
     145             :   /* Officially free i */
     146             : 
     147          53 :   FD_COMPILER_MFENCE();
     148          53 :   FD_VOLATILE( pinfo[ i ].tag ) = 0UL;
     149          53 :   FD_COMPILER_MFENCE();
     150             : 
     151             :   /* Remove it from various structures.  It is okay if we are killed in
     152             :      this as next person to try to lock the wksp will detect this and
     153             :      rebuild the workspace. */
     154             : 
     155          53 :   if( FD_UNLIKELY( fd_wksp_private_used_treap_remove( i, wksp, pinfo ) ) ) {
     156           0 :     FD_LOG_WARNING(( "corrupt wksp detected" ));
     157           0 :     return;
     158           0 :   }
     159             : 
     160          53 :   ulong p = fd_wksp_private_pinfo_idx( pinfo[ i ].prev_cidx );
     161          53 :   if( FD_LIKELY( p<part_max ) && !pinfo[ p ].tag ) {
     162          16 :     if( FD_UNLIKELY( fd_wksp_private_free_treap_remove( p, wksp, pinfo ) ) ) {
     163           0 :       FD_LOG_WARNING(( "corrupt wksp detected" ));
     164           0 :       return;
     165           0 :     }
     166          16 :     fd_wksp_private_merge_before( p, i, wksp, pinfo );
     167          16 :   }
     168             : 
     169          53 :   ulong n = fd_wksp_private_pinfo_idx( pinfo[ i ].next_cidx );
     170          53 :   if( FD_LIKELY( n<part_max ) && !pinfo[ n ].tag ) {
     171          15 :     if( FD_UNLIKELY( fd_wksp_private_free_treap_remove( n, wksp, pinfo ) ) ) {
     172           0 :       FD_LOG_WARNING(( "corrupt wksp detected" ));
     173           0 :       return;
     174           0 :     }
     175          15 :     fd_wksp_private_merge_after( i, n, wksp, pinfo );
     176          15 :   }
     177             : 
     178          53 :   if( FD_UNLIKELY( fd_wksp_private_free_treap_insert( i, wksp, pinfo ) ) ) {
     179           0 :     FD_LOG_WARNING(( "corrupt wksp detected" ));
     180           0 :     return;
     181           0 :   }
     182             : 
     183             : # if FD_HAS_DEEPASAN
     184             :   /* Poison the data region of the now freed allocation. */
     185             :   fd_asan_poison( fd_wksp_laddr_fast( wksp, pinfo[ i ].gaddr_lo ), pinfo[ i ].gaddr_hi - pinfo[ i ].gaddr_lo );
     186             : # endif
     187          53 : }
     188             : 
     189             : /* user APIs **********************************************************/
     190             : 
     191             : void *
     192             : fd_wksp_laddr( fd_wksp_t const * wksp,
     193          60 :                ulong             gaddr ) {
     194          60 :   if( FD_UNLIKELY( !wksp ) ) { FD_LOG_WARNING(( "NULL wksp" )); return NULL; }
     195             : 
     196          60 :   if( !gaddr ) return NULL; /* "NULL" maps to NULL */
     197             : 
     198             :   /* Note: <= used for gaddr_hi below to support mapping ranges of the
     199             :      form [lo,hi) between local and global address spaces with no
     200             :      special handling if allocation put hi at the very end of the
     201             :      workspace. */
     202             : 
     203          60 :   if( FD_UNLIKELY( !((wksp->gaddr_lo<=gaddr) & (gaddr<=wksp->gaddr_hi)) ) ) { FD_LOG_WARNING(( "bad gaddr" )); return NULL; }
     204             : 
     205          60 :   return fd_wksp_laddr_fast( wksp, gaddr );
     206          60 : }
     207             : 
     208             : ulong
     209             : fd_wksp_gaddr( fd_wksp_t const * wksp,
     210           0 :                void const *      laddr ) {
     211           0 :   if( FD_UNLIKELY( !wksp ) ) { FD_LOG_WARNING(( "NULL wksp" )); return 0UL; }
     212             : 
     213           0 :   if( !laddr ) return 0UL; /* NULL maps to "NULL" */
     214             : 
     215           0 :   ulong gaddr = fd_wksp_gaddr_fast( wksp, laddr );
     216             : 
     217             :   /* See note above about why <= for gaddr_hi */
     218             : 
     219           0 :   if( FD_UNLIKELY( !((wksp->gaddr_lo<=gaddr) & (gaddr<=wksp->gaddr_hi)) ) ) { FD_LOG_WARNING(( "bad laddr" )); return 0UL; }
     220             : 
     221           0 :   return gaddr;
     222           0 : }
     223             : 
     224             : ulong
     225             : fd_wksp_alloc_at_least( fd_wksp_t * wksp,
     226             :                         ulong       align,
     227             :                         ulong       sz,
     228             :                         ulong       tag,
     229             :                         ulong *     _lo,
     230         113 :                         ulong *     _hi ) {
     231         113 :   align = fd_ulong_if( !align, FD_WKSP_ALIGN_DEFAULT, align );
     232         113 :   ulong footprint = sz + align - 1UL;
     233             : 
     234         113 :   if( FD_UNLIKELY( !sz                        ) ) goto fail; /* silent */
     235         113 :   if( FD_UNLIKELY( !wksp                      ) ) { FD_LOG_WARNING(( "NULL wksp"   )); goto fail; }
     236         113 :   if( FD_UNLIKELY( !fd_ulong_is_pow2( align ) ) ) { FD_LOG_WARNING(( "bad align"   )); goto fail; }
     237         113 :   if( FD_UNLIKELY( !tag                       ) ) { FD_LOG_WARNING(( "bad tag"     )); goto fail; }
     238             : 
     239             : # if FD_HAS_DEEPASAN
     240             :   /* ASan poisons in 8 byte shadow granules, so bump alignment (and thus
     241             :      footprint) to meet manual poisoning requirements. */
     242             :   align = fd_ulong_if( align < FD_ASAN_ALIGN, FD_ASAN_ALIGN, align );
     243             :   footprint = sz + align - 1UL;
     244             : # endif
     245             : 
     246         113 :   if( FD_UNLIKELY( footprint < sz             ) ) { FD_LOG_WARNING(( "sz overflow" )); goto fail; }
     247             : 
     248         113 :   fd_wksp_private_pinfo_t * pinfo = fd_wksp_private_pinfo( wksp );
     249             : 
     250         113 :   if( FD_UNLIKELY( fd_wksp_private_lock( wksp ) ) ) goto fail; /* logs details */
     251             : 
     252             :   /* Find the smallest free partition size that can handle footprint.
     253             :      Note: it is theoretically possible when there is corruption that a
     254             :      failure to find a suitable partition could be fixed by rebuilding
     255             :      the wksp.  But this should not be common and is expensive and we
     256             :      can't tell if this is a run-of-the-mill allocation failure
     257             :      (insufficient space or too much fragmentation) or an exotic data
     258             :      corruption case.  So we just fail to keep algo cost strict and let
     259             :      the user decide if they want to attempt extreme measures. */
     260             : 
     261         113 :   ulong i = fd_wksp_private_free_treap_query( footprint, wksp, pinfo );
     262         113 :   if( FD_UNLIKELY( fd_wksp_private_pinfo_idx_is_null( i ) ) ) {
     263           0 :     fd_wksp_private_unlock( wksp );
     264           0 :     if( !fd_wksp_oom_silent ) FD_LOG_WARNING(( "no free space available in workspace %s with data size %lu", wksp->name, wksp->data_max ));
     265           0 :     goto fail;
     266           0 :   }
     267             : 
     268             :   /* At this point, i in [0,max), there is at least one suitable
     269             :      partition.  If there is more than one, use one from the same list. */
     270             : 
     271         113 :   if( !fd_wksp_private_free_treap_same_is_empty( i, wksp, pinfo ) ) i = fd_wksp_private_free_treap_same_remove( i, wksp, pinfo );
     272         113 :   else if( FD_UNLIKELY( fd_wksp_private_free_treap_remove( i, wksp, pinfo ) ) ) {
     273           0 :     fd_wksp_private_unlock( wksp );
     274           0 :     FD_LOG_WARNING(( "corrupt wksp detected" ));
     275           0 :     goto fail;
     276           0 :   }
     277             : 
     278             :   /* At this point, partition i has a zero tag and is not in the idle
     279             :      stack, free treap, or used treap.  Further, it is guaranteed to be
     280             :      large enough to hold the request.  Trim it to fit the request as
     281             :      tightly as possible.  We check before we trim that there are enough
     282             :      idle partitions available to complete the request (this improves
     283             :      checkpointing as all allocated partitions will be reasonably
     284             :      tight). */
     285             : 
     286         113 :   ulong lo = pinfo[ i ].gaddr_lo;
     287         113 :   ulong hi = pinfo[ i ].gaddr_hi;
     288             : 
     289         113 :   ulong r0 = fd_ulong_align_up( lo, align );
     290         113 :   ulong r1 = r0 + sz;
     291             : 
     292         113 :   ulong part_max = wksp->part_max;
     293         113 :   ulong idle_idx = fd_wksp_private_pinfo_idx( wksp->idle_top_cidx );
     294         240 :   for( int idle_rem = (r0>lo) + (r1<hi); idle_rem; idle_rem-- ) {
     295         127 :     if( FD_UNLIKELY( idle_idx >= part_max ) ) {
     296           0 :       if( FD_UNLIKELY( fd_wksp_private_free_treap_insert( i, wksp, pinfo ) ) ) { /* Could eliminate this with additional surgery */
     297           0 :         fd_wksp_private_unlock( wksp );
     298           0 :         FD_LOG_WARNING(( "corrupt wksp detected" ));
     299           0 :         goto fail;
     300           0 :       }
     301           0 :       fd_wksp_private_unlock( wksp );
     302           0 :       FD_LOG_WARNING(( "too few partitions available for allocation (part_max %lu)", part_max ));
     303           0 :       goto fail;
     304           0 :     }
     305         127 :     idle_idx = fd_wksp_private_pinfo_idx( pinfo[ idle_idx ].parent_cidx );
     306         127 :   }
     307             : 
     308         113 :   if( FD_UNLIKELY( r0>lo ) ) { /* opt for reasonable alignments */
     309          14 :     ulong j = fd_wksp_private_split_before( i, hi-r0, wksp, pinfo );
     310          14 :     if( FD_UNLIKELY( fd_wksp_private_free_treap_insert( j, wksp, pinfo ) ) ) {
     311           0 :       fd_wksp_private_unlock( wksp );
     312           0 :       FD_LOG_WARNING(( "corrupt wksp detected" ));
     313           0 :       goto fail;
     314           0 :     }
     315          14 :     lo = r0;
     316          14 :   }
     317             : 
     318         113 :   if( FD_LIKELY( r1<hi ) ) { /* opt for splitting a final large partition */
     319         113 :     ulong j = fd_wksp_private_split_after( i, sz, wksp, pinfo );
     320         113 :     if( FD_UNLIKELY( fd_wksp_private_free_treap_insert( j, wksp, pinfo ) ) ) {
     321           0 :       fd_wksp_private_unlock( wksp );
     322           0 :       FD_LOG_WARNING(( "corrupt wksp detected" ));
     323           0 :       goto fail;
     324           0 :     }
     325         113 :     hi = r1;
     326         113 :   }
     327             : 
     328         113 :   if( FD_UNLIKELY( fd_wksp_private_used_treap_insert( i, wksp, pinfo ) ) ) {
     329           0 :     fd_wksp_private_unlock( wksp );
     330           0 :     FD_LOG_WARNING(( "corrupt wksp detected" ));
     331           0 :     goto fail;
     332           0 :   }
     333             : 
     334             :   /* At this point, i is unofficially allocated.  It is okay if we get
     335             :      killed at any point above as the next wksp user to try to lock the
     336             :      wksp will detect that we died in the middle of an operation,
     337             :      potentially leaving the partitioning, idle stack, used treap and/or
     338             :      free treap might be in an inconsistent state and thus proceed to
     339             :      rebuild them.  We now update the tag in the array to make the
     340             :      allocation official. */
     341             : 
     342         113 :   FD_COMPILER_MFENCE();
     343         113 :   FD_VOLATILE( pinfo[ i ].tag ) = tag;
     344         113 :   FD_COMPILER_MFENCE();
     345             : 
     346             : # if FD_HAS_DEEPASAN
     347             :   /* Unpoison the data region of the allocation */
     348             :   fd_asan_unpoison( fd_wksp_laddr_fast( wksp, lo ), hi - lo );
     349             : # endif
     350             : 
     351         113 :   fd_wksp_private_unlock( wksp );
     352         113 :   *_lo = lo;
     353         113 :   *_hi = hi;
     354         113 :   return r0;
     355             : 
     356           0 : fail:
     357           0 :   *_lo = 0UL;
     358           0 :   *_hi = 0UL;
     359           0 :   return 0UL;
     360         113 : }
     361             : 
     362             : void
     363             : fd_wksp_free( fd_wksp_t * wksp,
     364          53 :               ulong       gaddr ) {
     365          53 :   if( FD_UNLIKELY( !gaddr ) ) return;
     366             : 
     367          53 :   if( FD_UNLIKELY( !wksp ) ) { FD_LOG_WARNING(( "NULL wksp" )); return; }
     368             : 
     369          53 :   ulong                     part_max = wksp->part_max;
     370          53 :   fd_wksp_private_pinfo_t * pinfo    = fd_wksp_private_pinfo( wksp );
     371             : 
     372          53 :   if( FD_UNLIKELY( fd_wksp_private_lock( wksp ) ) ) return; /* logs details */
     373             : 
     374          53 :   ulong i = fd_wksp_private_used_treap_query( gaddr, wksp, pinfo );
     375          53 :   if( FD_UNLIKELY( i<part_max ) ) fd_wksp_private_free( i, wksp, pinfo ); /* logs details */
     376             : 
     377          53 :   fd_wksp_private_unlock( wksp );
     378             : 
     379          53 :   if( FD_UNLIKELY( i>=part_max && i!=FD_WKSP_PRIVATE_PINFO_IDX_NULL ) ) {
     380           0 :     FD_LOG_WARNING(( "gaddr does not appear to be a current wksp allocation" ));
     381           0 :   }
     382          53 : }
     383             : 
     384             : ulong
     385             : fd_wksp_tag( fd_wksp_t * wksp,
     386           0 :              ulong       gaddr ) {
     387           0 :   if( FD_UNLIKELY( !wksp ) ) return 0UL;
     388             : 
     389           0 :   ulong                     part_max = wksp->part_max;
     390           0 :   fd_wksp_private_pinfo_t * pinfo    = fd_wksp_private_pinfo( wksp );
     391             : 
     392           0 :   if( FD_UNLIKELY( fd_wksp_private_lock( wksp ) ) ) return 0UL; /* logs details */
     393             : 
     394           0 :   ulong i   = fd_wksp_private_used_treap_query( gaddr, wksp, pinfo );
     395           0 :   ulong tag = FD_LIKELY( i<part_max ) ? pinfo[ i ].tag : 0UL;
     396             : 
     397           0 :   fd_wksp_private_unlock( wksp );
     398             : 
     399           0 :   return tag;
     400           0 : }
     401             : 
     402             : ulong
     403             : fd_wksp_tag_query( fd_wksp_t *                wksp,
     404             :                    ulong const *              tag,
     405             :                    ulong                      tag_cnt,
     406             :                    fd_wksp_tag_query_info_t * info,
     407           0 :                    ulong                      info_max ) {
     408             : 
     409           0 :   if( FD_UNLIKELY( !tag_cnt ) ) return 0UL; /* No tags to query */
     410             : 
     411           0 :   if( FD_UNLIKELY( !wksp ) ) { FD_LOG_WARNING(( "NULL wksp" )); return 0UL; }
     412           0 :   if( FD_UNLIKELY( !tag  ) ) { FD_LOG_WARNING(( "bad tag" ));   return 0UL; }
     413             : 
     414           0 :   if( FD_UNLIKELY( (!!info_max) & (!info) ) ) { FD_LOG_WARNING(( "NULL info" )); return 0UL; }
     415             : 
     416           0 :   ulong                     part_max = wksp->part_max;
     417           0 :   fd_wksp_private_pinfo_t * pinfo    = fd_wksp_private_pinfo( wksp );
     418             : 
     419           0 :   ulong info_cnt = 0UL;
     420             : 
     421           0 :   if( FD_UNLIKELY( fd_wksp_private_lock( wksp ) ) ) return 0UL; /* logs details */
     422             : 
     423           0 :   ulong cycle_tag = wksp->cycle_tag++;
     424             : 
     425           0 :   ulong i = fd_wksp_private_pinfo_idx( wksp->part_head_cidx );
     426           0 :   while( !fd_wksp_private_pinfo_idx_is_null( i ) ) {
     427           0 :     if( FD_UNLIKELY( i>=part_max ) || FD_UNLIKELY( pinfo[ i ].cycle_tag==cycle_tag ) ) {
     428           0 :       fd_wksp_private_unlock( wksp );
     429           0 :       FD_LOG_WARNING(( "corrupt wksp detected" ));
     430           0 :       return 0UL;
     431           0 :     }
     432           0 :     pinfo[ i ].cycle_tag = cycle_tag; /* mark i as visited */
     433             : 
     434           0 :     ulong _tag = pinfo[ i ].tag;
     435           0 :     for( ulong tag_idx=0UL; tag_idx<tag_cnt; tag_idx++ ) { /* TODO: USE BETTER MATCHER */
     436           0 :       if( tag[ tag_idx ]==_tag ) {
     437           0 :         if( FD_LIKELY( info_cnt<info_max ) ) {
     438           0 :           info[ info_cnt ].gaddr_lo = pinfo[ i ].gaddr_lo;
     439           0 :           info[ info_cnt ].gaddr_hi = pinfo[ i ].gaddr_hi;
     440           0 :           info[ info_cnt ].tag      = pinfo[ i ].tag;
     441           0 :         }
     442           0 :         info_cnt++;
     443           0 :         break;
     444           0 :       }
     445           0 :     }
     446             : 
     447           0 :     i = fd_wksp_private_pinfo_idx( pinfo[ i ].next_cidx );
     448           0 :   }
     449             : 
     450           0 :   fd_wksp_private_unlock( wksp );
     451           0 :   return info_cnt;
     452           0 : }
     453             : 
     454             : void
     455             : fd_wksp_tag_free( fd_wksp_t *   wksp,
     456             :                   ulong const * tag,
     457           0 :                   ulong         tag_cnt ) {
     458           0 :   if( FD_UNLIKELY( !tag_cnt ) ) return; /* No tags to free */
     459             : 
     460           0 :   if( FD_UNLIKELY( !wksp ) ) { FD_LOG_WARNING(( "NULL wksp" )); return; }
     461           0 :   if( FD_UNLIKELY( !tag  ) ) { FD_LOG_WARNING(( "bad tag" ));   return; }
     462             : 
     463           0 :   ulong                     part_max = wksp->part_max;
     464           0 :   fd_wksp_private_pinfo_t * pinfo    = fd_wksp_private_pinfo( wksp );
     465             : 
     466           0 :   if( FD_UNLIKELY( fd_wksp_private_lock( wksp ) ) ) return; /* logs details */
     467             : 
     468             :   /* Push matching used partitions onto a stack */
     469             : 
     470           0 :   ulong top = FD_WKSP_PRIVATE_PINFO_IDX_NULL;
     471             : 
     472           0 :   ulong cycle_tag = wksp->cycle_tag++;
     473             : 
     474           0 :   ulong i = fd_wksp_private_pinfo_idx( wksp->part_head_cidx );
     475           0 :   while( !fd_wksp_private_pinfo_idx_is_null( i ) ) {
     476           0 :     if( FD_UNLIKELY( i>=part_max ) || FD_UNLIKELY( pinfo[ i ].cycle_tag==cycle_tag ) ) {
     477           0 :       fd_wksp_private_unlock( wksp );
     478           0 :       FD_LOG_WARNING(( "corrupt wksp detected" ));
     479           0 :       return;
     480           0 :     }
     481           0 :     pinfo[ i ].cycle_tag = cycle_tag; /* mark i as visited */
     482             : 
     483           0 :     ulong _tag = pinfo[ i ].tag;
     484           0 :     if( _tag ) { /* TODO: use a more efficient matcher */
     485           0 :       ulong tag_idx; for( tag_idx=0UL; tag_idx<tag_cnt; tag_idx++ ) if( tag[ tag_idx ]==_tag ) break;
     486           0 :       if( tag_idx<tag_cnt ) {
     487           0 :         pinfo[ i ].stack_cidx = fd_wksp_private_pinfo_cidx( top );
     488           0 :         top = i;
     489           0 :       }
     490           0 :     }
     491           0 :     i = fd_wksp_private_pinfo_idx( pinfo[ i ].next_cidx );
     492           0 :   }
     493             : 
     494             :   /* Free partitions on the stack */
     495             : 
     496           0 :   while( !fd_wksp_private_pinfo_idx_is_null( top ) ) {
     497           0 :     i   = top;
     498           0 :     top = fd_wksp_private_pinfo_idx( pinfo[ i ].stack_cidx );
     499           0 :     fd_wksp_private_free( i, wksp, pinfo );
     500           0 :   }
     501             : 
     502           0 :   fd_wksp_private_unlock( wksp );
     503           0 : }
     504             : 
     505             : void
     506             : fd_wksp_memset( fd_wksp_t * wksp,
     507             :                 ulong       gaddr,
     508           0 :                 int         c ) {
     509           0 :   if( FD_UNLIKELY( !wksp ) ) { FD_LOG_WARNING(( "NULL wksp" )); return; }
     510             : 
     511           0 :   ulong                     part_max = wksp->part_max;
     512           0 :   fd_wksp_private_pinfo_t * pinfo    = fd_wksp_private_pinfo( wksp );
     513             : 
     514           0 :   int err;
     515             : 
     516           0 :   if( FD_UNLIKELY( fd_wksp_private_lock( wksp ) ) ) return; /* logs details */
     517             : 
     518           0 :   ulong i = fd_wksp_private_used_treap_query( gaddr, wksp, pinfo );
     519           0 :   if( FD_UNLIKELY( i>=part_max ) ) err = 1;
     520           0 :   else {
     521           0 :     fd_memset( fd_wksp_laddr_fast( wksp, pinfo[ i ].gaddr_lo ), c, fd_wksp_private_pinfo_sz( pinfo + i ) );
     522           0 :     err = 0;
     523           0 :   }
     524             : 
     525           0 :   fd_wksp_private_unlock( wksp );
     526             : 
     527           0 :   if( FD_UNLIKELY( err ) ) FD_LOG_WARNING(( "gaddr does not seem to point to a current wksp allocation" ));
     528           0 : }
     529             : 
     530             : void
     531             : fd_wksp_reset( fd_wksp_t * wksp,
     532           0 :                uint        seed ) {
     533           0 :   if( FD_UNLIKELY( !wksp ) ) { FD_LOG_WARNING(( "NULL wksp" )); return; }
     534             : 
     535             : # if FD_HAS_DEEPASAN
     536             :   fd_wksp_private_asan_poison_data( wksp );
     537             : # endif
     538             : 
     539           0 :   ulong                     part_max = wksp->part_max;
     540           0 :   fd_wksp_private_pinfo_t * pinfo    = fd_wksp_private_pinfo( wksp );
     541             : 
     542           0 :   if( FD_UNLIKELY( fd_wksp_private_lock( wksp ) ) ) return; /* logs details */
     543             : 
     544           0 :   for( ulong i=0; i<part_max; i++ ) pinfo[ i ].tag = 0UL;
     545           0 :   int err = fd_wksp_rebuild( wksp, seed );
     546             : 
     547           0 :   fd_wksp_private_unlock( wksp );
     548             : 
     549           0 :   if( FD_UNLIKELY( err ) ) FD_LOG_WARNING(( "corrupt wksp detected" ));
     550           0 : }
     551             : 
     552             : fd_wksp_usage_t *
     553             : fd_wksp_usage( fd_wksp_t *       wksp,
     554             :                ulong const *     tag,
     555             :                ulong             tag_cnt,
     556          12 :                fd_wksp_usage_t * usage ) {
     557             : 
     558             :   /* Check input args */
     559             : 
     560          12 :   if( FD_UNLIKELY( !usage ) ) { FD_LOG_WARNING(( "bad usage" )); return usage; }
     561             : 
     562          12 :   fd_memset( usage, 0, sizeof(fd_wksp_usage_t) );
     563             : 
     564          12 :   if( FD_UNLIKELY( !wksp                ) ) { FD_LOG_WARNING(( "bad wksp" )); return usage; }
     565          12 :   if( FD_UNLIKELY( (!tag) & (!!tag_cnt) ) ) { FD_LOG_WARNING(( "bad tag" ));  return usage; }
     566             : 
     567          12 :   ulong                     part_max = wksp->part_max;
     568          12 :   fd_wksp_private_pinfo_t * pinfo    = fd_wksp_private_pinfo( wksp );
     569             : 
     570          12 :   if( FD_UNLIKELY( fd_wksp_private_lock( wksp ) ) ) { FD_LOG_WARNING(( "fd_wksp_private_lock failed" )); return usage; }
     571             : 
     572             :   /* Push matching used partitions onto a stack */
     573             : 
     574          12 :   usage->total_max = part_max;
     575             : 
     576          12 :   ulong cycle_tag = wksp->cycle_tag++;
     577             : 
     578          12 :   ulong i = fd_wksp_private_pinfo_idx( wksp->part_head_cidx );
     579         888 :   while( !fd_wksp_private_pinfo_idx_is_null( i ) ) {
     580         876 :     if( FD_UNLIKELY( i>=part_max ) || FD_UNLIKELY( pinfo[ i ].cycle_tag==cycle_tag ) ) {
     581           0 :       fd_wksp_private_unlock( wksp );
     582           0 :       FD_LOG_WARNING(( "corrupt wksp detected" ));
     583           0 :       fd_memset( usage, 0, sizeof(fd_wksp_usage_t) );
     584           0 :       return usage;
     585           0 :     }
     586         876 :     pinfo[ i ].cycle_tag = cycle_tag; /* mark i as visited */
     587             : 
     588         876 :     ulong part_sz  = fd_wksp_private_pinfo_sz( pinfo + i );
     589         876 :     ulong part_tag = pinfo[ i ].tag;
     590             : 
     591             :     /* TODO: use a more efficient matcher */
     592         990 :     ulong tag_idx; for( tag_idx=0UL; tag_idx<tag_cnt; tag_idx++ ) if( tag[ tag_idx ]==part_tag ) break;
     593             : 
     594         876 :     int is_free = !part_tag;
     595         876 :     int is_used = tag_idx<tag_cnt;
     596             : 
     597         876 :     usage->total_cnt += 1UL;            usage->total_sz +=                       part_sz;
     598         876 :     usage->free_cnt  += (ulong)is_free; usage->free_sz  += fd_ulong_if( is_free, part_sz, 0UL );
     599         876 :     usage->used_cnt  += (ulong)is_used; usage->used_sz  += fd_ulong_if( is_used, part_sz, 0UL );
     600             : 
     601         876 :     i = fd_wksp_private_pinfo_idx( pinfo[ i ].next_cidx );
     602         876 :   }
     603             : 
     604          12 :   fd_wksp_private_unlock( wksp );
     605          12 :   return usage;
     606          12 : }

Generated by: LCOV version 1.14