Line data Source code
1 : #include "fd_txncache_shmem.h"
2 : #include "fd_txncache_private.h"
3 :
4 : #define POOL_NAME blockcache_pool
5 162 : #define POOL_T fd_txncache_blockcache_shmem_t
6 : #define POOL_IDX_T ulong
7 614370 : #define POOL_NEXT pool.next
8 : #define POOL_IMPL_STYLE 2
9 : #include "../../util/tmpl/fd_pool.c"
10 :
11 : #define MAP_NAME blockhash_map
12 3717 : #define MAP_KEY blockhash
13 0 : #define MAP_ELE_T fd_txncache_blockcache_shmem_t
14 : #define MAP_KEY_T fd_hash_t
15 3717 : #define MAP_PREV blockhash_map.prev
16 3717 : #define MAP_NEXT blockhash_map.next
17 : #define MAP_KEY_EQ(k0,k1) fd_hash_eq( k0, k1 )
18 : #define MAP_KEY_HASH(key,seed) (__extension__({ (void)(seed); fd_ulong_load_8_fast( (key)->uc ); }))
19 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
20 : #define MAP_MULTI 1
21 : #define MAP_IMPL_STYLE 2
22 : #include "../../util/tmpl/fd_map_chain.c"
23 :
24 : #define SLIST_NAME root_slist
25 : #define SLIST_ELE_T fd_txncache_blockcache_shmem_t
26 : #define SLIST_IDX_T ulong
27 0 : #define SLIST_NEXT slist.next
28 : #define SLIST_IMPL_STYLE 2
29 : #include "../../util/tmpl/fd_slist.c"
30 :
31 : FD_FN_CONST ushort
32 : fd_txncache_max_txnpages_per_blockhash( ulong max_active_slots,
33 216 : ulong max_txn_per_slot ) {
34 : /* The maximum number of transaction pages we might need to store all
35 : the transactions that could be seen in a blockhash.
36 :
37 : In the worst case, every transaction in every live bank refers to
38 : the same blockhash. */
39 :
40 216 : ulong result = 1UL+(max_txn_per_slot*max_active_slots)/FD_TXNCACHE_TXNS_PER_PAGE;
41 216 : if( FD_UNLIKELY( result>USHORT_MAX-2UL ) ) return 0; /* MAX is the invalid flag, MAX-1 is the xbusy flag. */
42 216 : return (ushort)result;
43 216 : }
44 :
45 : FD_FN_CONST ushort
46 : fd_txncache_max_txnpages( ulong max_active_slots,
47 216 : ulong max_txn_per_slot ) {
48 : /* We need to be able to store potentially every slot that is live
49 : being completely full of transactions. This would be
50 :
51 : max_active_slots*max_txn_per_slot
52 :
53 : transactions, except that we are counting pages here, not
54 : transactions. It's not enough to divide by the page size, because
55 : pages might be wasted. The maximum page wastage occurs when all
56 : the blockhashes except one have one transaction in them, and the
57 : remaining blockhash has all other transactions. In that case, the
58 : full blockhash needs
59 :
60 : (max_active_slots*max_txn_per_slot)/FD_TXNCACHE_TXNS_PER_PAGE
61 :
62 : pages, and the other blockhashes need 1 page each. */
63 :
64 216 : ulong result = max_active_slots-1UL+max_active_slots*(1UL+(max_txn_per_slot-1UL)/FD_TXNCACHE_TXNS_PER_PAGE);
65 216 : if( FD_UNLIKELY( result>USHORT_MAX-2UL ) ) return 0; /* MAX is the invalid flag, MAX-1 is the xbusy flag. */
66 216 : return (ushort)result;
67 216 : }
68 :
69 : FD_FN_CONST ulong
70 216 : fd_txncache_shmem_align( void ) {
71 216 : return FD_TXNCACHE_SHMEM_ALIGN;
72 216 : }
73 :
74 : FD_FN_CONST ulong
75 : fd_txncache_shmem_footprint( ulong max_live_slots,
76 108 : ulong max_txn_per_slot ) {
77 108 : if( FD_UNLIKELY( max_live_slots<1UL ) ) return 0UL;
78 108 : if( FD_UNLIKELY( max_txn_per_slot<1UL ) ) return 0UL;
79 :
80 108 : ulong max_active_slots = FD_TXNCACHE_MAX_BLOCKHASH_DISTANCE+max_live_slots;
81 108 : ulong blockhash_map_chains = fd_ulong_pow2_up( 2UL*max_active_slots );
82 :
83 : /* To save memory, txnpages are referenced as ushort which is enough
84 : to support mainnet parameters without overflow. */
85 108 : ushort _max_txnpages = fd_txncache_max_txnpages( max_active_slots, max_txn_per_slot );
86 108 : if( FD_UNLIKELY( !_max_txnpages ) ) return 0UL;
87 :
88 108 : ulong _max_txnpages_per_blockhash = fd_txncache_max_txnpages_per_blockhash( max_active_slots, max_txn_per_slot );
89 108 : if( FD_UNLIKELY( !_max_txnpages_per_blockhash ) ) return 0UL;
90 :
91 108 : ulong _descends_footprint = descends_set_footprint( max_active_slots );
92 108 : if( FD_UNLIKELY( !_descends_footprint ) ) return 0UL;
93 :
94 108 : ulong l;
95 108 : l = FD_LAYOUT_INIT;
96 108 : l = FD_LAYOUT_APPEND( l, FD_TXNCACHE_SHMEM_ALIGN, sizeof(fd_txncache_shmem_t) );
97 108 : l = FD_LAYOUT_APPEND( l, blockhash_map_align(), blockhash_map_footprint( blockhash_map_chains ) );
98 108 : l = FD_LAYOUT_APPEND( l, blockcache_pool_align(), blockcache_pool_footprint( max_active_slots ) );
99 108 : l = FD_LAYOUT_APPEND( l, alignof(ushort), max_active_slots*_max_txnpages_per_blockhash*sizeof(ushort) ); /* blockcache->pages */
100 108 : l = FD_LAYOUT_APPEND( l, alignof(uint), max_active_slots*max_txn_per_slot*sizeof(uint) ); /* blockcache->heads */
101 108 : l = FD_LAYOUT_APPEND( l, descends_set_align(), max_active_slots*_descends_footprint ); /* blockcache->descends */
102 108 : l = FD_LAYOUT_APPEND( l, alignof(ushort), _max_txnpages*sizeof(ushort) ); /* txnpages_free */
103 108 : l = FD_LAYOUT_APPEND( l, alignof(fd_txncache_txnpage_t), _max_txnpages*sizeof(fd_txncache_txnpage_t) ); /* txnpages */
104 108 : l = FD_LAYOUT_APPEND( l, alignof(ushort), _max_txnpages_per_blockhash*sizeof(ushort) ); /* scratchpad txnpage pointer array for purge stale */
105 108 : l = FD_LAYOUT_APPEND( l, alignof(uint), max_txn_per_slot*sizeof(uint) ); /* scratchpad heads for purge stale */
106 108 : l = FD_LAYOUT_APPEND( l, alignof(fd_txncache_txnpage_t), sizeof(fd_txncache_txnpage_t) ); /* scratchpad txnpage for purge stale */
107 108 : return FD_LAYOUT_FINI( l, FD_TXNCACHE_SHMEM_ALIGN );
108 108 : }
109 :
110 : void *
111 : fd_txncache_shmem_new( void * shmem,
112 : ulong max_live_slots,
113 : ulong max_txn_per_slot,
114 54 : ulong seed ) {
115 54 : if( FD_UNLIKELY( !shmem ) ) {
116 0 : FD_LOG_WARNING(( "NULL shmem" ));
117 0 : return NULL;
118 0 : }
119 :
120 54 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_txncache_shmem_align() ) ) ) {
121 0 : FD_LOG_WARNING(( "misaligned shmem" ));
122 0 : return NULL;
123 0 : }
124 :
125 54 : if( FD_UNLIKELY( !max_live_slots ) ) return NULL;
126 54 : if( FD_UNLIKELY( !max_txn_per_slot ) ) return NULL;
127 :
128 54 : ulong max_active_slots = FD_TXNCACHE_MAX_BLOCKHASH_DISTANCE+max_live_slots;
129 54 : ulong blockhash_map_chains = fd_ulong_pow2_up( 2UL*max_active_slots );
130 :
131 54 : ushort _max_txnpages = fd_txncache_max_txnpages( max_active_slots, max_txn_per_slot );
132 54 : ushort _max_txnpages_per_blockhash = fd_txncache_max_txnpages_per_blockhash( max_active_slots, max_txn_per_slot );
133 :
134 54 : if( FD_UNLIKELY( !_max_txnpages ) ) return NULL;
135 54 : if( FD_UNLIKELY( !_max_txnpages_per_blockhash ) ) return NULL;
136 :
137 54 : ulong _descends_footprint = descends_set_footprint( max_active_slots );
138 54 : if( FD_UNLIKELY( !_descends_footprint ) ) return NULL;
139 :
140 54 : FD_SCRATCH_ALLOC_INIT( l, shmem );
141 54 : fd_txncache_shmem_t * tc = FD_SCRATCH_ALLOC_APPEND( l, FD_TXNCACHE_SHMEM_ALIGN, sizeof(fd_txncache_shmem_t) );
142 54 : void * _blockhash_map = FD_SCRATCH_ALLOC_APPEND( l, blockhash_map_align(), blockhash_map_footprint( blockhash_map_chains ) );
143 54 : void * _blockcache_pool = FD_SCRATCH_ALLOC_APPEND( l, blockcache_pool_align(), blockcache_pool_footprint( max_active_slots ) );
144 54 : FD_SCRATCH_ALLOC_APPEND( l, alignof(ushort), max_active_slots*_max_txnpages_per_blockhash*sizeof(ushort) );
145 54 : FD_SCRATCH_ALLOC_APPEND( l, alignof(uint), max_active_slots*max_txn_per_slot*sizeof(uint) );
146 54 : void * _blockcache_descends = FD_SCRATCH_ALLOC_APPEND( l, descends_set_align(), max_active_slots*_descends_footprint );
147 54 : void * _txnpages_free = FD_SCRATCH_ALLOC_APPEND( l, alignof(ushort), _max_txnpages*sizeof(ushort) );
148 54 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_txncache_txnpage_t), _max_txnpages*sizeof(fd_txncache_txnpage_t) );
149 54 : FD_SCRATCH_ALLOC_APPEND( l, alignof(ushort), _max_txnpages_per_blockhash*sizeof(ushort) );
150 54 : FD_SCRATCH_ALLOC_APPEND( l, alignof(uint), max_txn_per_slot*sizeof(uint) );
151 54 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_txncache_txnpage_t), sizeof(fd_txncache_txnpage_t) );
152 :
153 54 : fd_txncache_blockcache_shmem_t * blockcache_pool = blockcache_pool_join( blockcache_pool_new( _blockcache_pool, max_active_slots ) );
154 54 : FD_TEST( blockcache_pool );
155 9216 : for( ulong i=0UL; i<max_active_slots; i++ ) blockcache_pool[ i ].frozen = -1;
156 :
157 54 : blockhash_map_t * blockhash_map = blockhash_map_join( blockhash_map_new( _blockhash_map, blockhash_map_chains, 0UL /* seed not used */ ) );
158 54 : FD_TEST( blockhash_map );
159 :
160 9216 : for( ulong i=0UL; i<max_active_slots; i++ ) {
161 9162 : descends_set_t * descends_set = descends_set_join( descends_set_new( (uchar *)_blockcache_descends + i*_descends_footprint, max_active_slots ) );
162 9162 : FD_TEST( descends_set );
163 9162 : }
164 :
165 54 : tc->root_cnt = 0UL;
166 54 : FD_TEST( root_slist_join( root_slist_new( tc->root_ll ) ) );
167 :
168 54 : tc->lock->value = 0;
169 :
170 54 : tc->txn_per_slot_max = max_txn_per_slot;
171 54 : tc->active_slots_max = max_active_slots;
172 54 : tc->txnpages_per_blockhash_max = _max_txnpages_per_blockhash;
173 54 : tc->max_txnpages = _max_txnpages;
174 :
175 54 : tc->blockcache_generation = 0U;
176 54 : tc->txnpages_free_cnt = _max_txnpages;
177 54 : ushort * txnpages_free = (ushort *)_txnpages_free;
178 18324 : for( ushort i=0; i<_max_txnpages; i++ ) txnpages_free[ i ] = i;
179 :
180 54 : tc->seed = seed;
181 :
182 54 : FD_COMPILER_MFENCE();
183 54 : FD_VOLATILE( tc->magic ) = FD_TXNCACHE_SHMEM_MAGIC;
184 54 : FD_COMPILER_MFENCE();
185 :
186 54 : return (void *)tc;
187 54 : }
188 :
189 : fd_txncache_shmem_t *
190 54 : fd_txncache_shmem_join( void * shtc ) {
191 54 : if( FD_UNLIKELY( !shtc ) ) {
192 0 : FD_LOG_WARNING(( "NULL shtc" ));
193 0 : return NULL;
194 0 : }
195 :
196 54 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shtc, fd_txncache_shmem_align() ) ) ) {
197 0 : FD_LOG_WARNING(( "misaligned shtc" ));
198 0 : return NULL;
199 0 : }
200 :
201 54 : fd_txncache_shmem_t * tc = (fd_txncache_shmem_t *)shtc;
202 :
203 54 : if( FD_UNLIKELY( tc->magic!=FD_TXNCACHE_SHMEM_MAGIC ) ) {
204 0 : FD_LOG_WARNING(( "bad magic" ));
205 0 : return NULL;
206 0 : }
207 :
208 54 : return tc;
209 54 : }
|