Skip to content

Commit 8ebeca0

Browse files
committed
Rewrite program cache
- Reduce funk key size to 32 bytes (from 40 bytes) - Move program cache to separate funk instance and wksp - Introduce program cache eviction policy - Restructure program cache logic / general cleanup - Reduce verbosity in API documentation - Clean up API naming - Split up API into a few smaller headers - Reduce program cache fill allocations and memory copies (directly populate calldests map, skip calldests map alloc for newer sBPF versions) - Remove broken/unused concurrency model in previous program cache implementation - Remove strict instruction logging requirements - Simplify cache invalidation logic ("queue program for re- verification"): now just inserts a tombstone
1 parent a75a301 commit 8ebeca0

39 files changed

+1160
-1525
lines changed

src/discof/bank/fd_bank_tile.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ handle_microblock( fd_bank_ctx_t * ctx,
242242
if that happens. We cannot reject the transaction here as there
243243
would be no way to undo the partially applied changes to the bank
244244
in finalize anyway. */
245-
fd_runtime_finalize_txn( ctx->txn_ctx->funk, txn_ctx->status_cache, txn_ctx->xid, txn_ctx, bank, NULL );
245+
fd_runtime_finalize_txn( ctx->txn_ctx->funk, ctx->txn_ctx->progcache, txn_ctx->status_cache, txn_ctx->xid, txn_ctx, bank, NULL );
246246
FD_TEST( txn->flags );
247247
}
248248

src/discof/exec/fd_exec_tile.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ typedef struct fd_exec_tile_ctx {
6262
TODO: These should probably be made read-only handles. */
6363
fd_banks_t * banks;
6464
fd_funk_t funk[ 1 ];
65+
fd_progcache_t progcache[1];
6566

6667
fd_txncache_t * txncache;
6768

@@ -174,6 +175,7 @@ after_frag( fd_exec_tile_ctx_t * ctx,
174175
if( FD_LIKELY( ctx->txn_ctx->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS ) ) {
175176
fd_runtime_finalize_txn(
176177
ctx->funk,
178+
ctx->progcache,
177179
ctx->txncache,
178180
&xid,
179181
ctx->txn_ctx,

src/discof/replay/fd_replay_tile.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ struct fd_replay_tile {
127127
int tx_metadata_storage;
128128

129129
fd_funk_t funk[1];
130+
fd_progcache_t progcache[1];
130131

131132
fd_txncache_t * txncache;
132133
fd_store_t * store;
@@ -1390,7 +1391,7 @@ replay( fd_replay_tile_t * ctx,
13901391

13911392
fd_funk_txn_xid_t xid = { .ul = { ready_txn->slot, ready_txn->slot } };
13921393

1393-
fd_runtime_update_program_cache( bank, ctx->funk, &xid, txn_p, ctx->runtime_spad );
1394+
fd_runtime_update_program_cache( bank, ctx->progcache, ctx->funk, &xid, txn_p, ctx->runtime_spad );
13941395

13951396
/* At this point, we are going to send the txn down the execution
13961397
pipeline. Increment the refcnt so we don't prematurely prune a

src/flamenco/fd_flamenco_base.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99
#define FD_SLOT_NULL ( ULONG_MAX )
1010
#define FD_SHRED_IDX_NULL ( UINT_MAX )
1111

12-
#define FD_FUNK_KEY_TYPE_ACC ((uchar)1)
13-
#define FD_FUNK_KEY_TYPE_ELF_CACHE ((uchar)2)
14-
1512
/* CLUSTER_VERSION is the default value for the cluster version
1613
in the epoch context. This value will foll forward to the
1714
latest version.
@@ -96,6 +93,12 @@ typedef struct fd_borrowed_account fd_borrowed_account_t;
9693
struct fd_txn_account;
9794
typedef struct fd_txn_account fd_txn_account_t;
9895

96+
union fd_features;
97+
typedef union fd_features fd_features_t;
98+
99+
struct fd_progcache;
100+
typedef struct fd_progcache fd_progcache_t;
101+
99102
struct fd_account_meta {
100103
uchar owner[32];
101104
ulong lamports;

src/flamenco/progcache/Local.mk

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
$(call add-hdrs,fd_prog_load.h)
2+
$(call add-objs,fd_prog_load,fd_flamenco)
3+
$(call add-hdrs,fd_progcache.h fd_progcache_rec.h)
4+
$(call add-objs,fd_progcache fd_progcache_rec fd_progcache_evict,fd_flamenco)
5+
$(call make-unit-test,test_progcache,test_progcache,fd_flamenco fd_funk fd_ballet fd_util)
6+
$(call run-unit-test,test_progcache)
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
#include "fd_prog_load.h"
2+
#include "../runtime/program/fd_bpf_loader_program.h"
3+
#include "../runtime/program/fd_loader_v4_program.h"
4+
5+
/* Similar to the below function, but gets the executable program content for the v4 loader.
6+
Unlike the v3 loader, the programdata is stored in a single program account. The program must
7+
NOT be retracted to be added to the cache. Returns a pointer to the programdata on success,
8+
and NULL on failure.
9+
10+
Reasons for failure include:
11+
- The program state cannot be read from the account data or is in the `retracted` state. */
12+
static uchar const *
13+
fd_get_executable_program_content_for_v4_loader( fd_txn_account_t const * program_acc,
14+
ulong * program_data_len ) {
15+
int err;
16+
17+
/* Get the current loader v4 state. This implicitly also checks the dlen. */
18+
fd_loader_v4_state_t const * state = fd_loader_v4_get_state( program_acc, &err );
19+
if( FD_UNLIKELY( err ) ) {
20+
return NULL;
21+
}
22+
23+
/* The program must be deployed or finalized. */
24+
if( FD_UNLIKELY( fd_loader_v4_status_is_retracted( state ) ) ) {
25+
return NULL;
26+
}
27+
28+
/* This subtraction is safe because get_state() implicitly checks the
29+
dlen. */
30+
*program_data_len = fd_txn_account_get_data_len( program_acc )-LOADER_V4_PROGRAM_DATA_OFFSET;
31+
return fd_txn_account_get_data( program_acc )+LOADER_V4_PROGRAM_DATA_OFFSET;
32+
}
33+
34+
/* Gets the programdata for a v3 loader-owned account by decoding the account data
35+
as well as the programdata account. Returns a pointer to the programdata on success,
36+
and NULL on failure.
37+
38+
Reasons for failure include:
39+
- The program account data cannot be decoded or is not in the `program` state.
40+
- The programdata account is not large enough to hold at least `PROGRAMDATA_METADATA_SIZE` bytes. */
41+
static uchar const *
42+
fd_get_executable_program_content_for_upgradeable_loader( fd_funk_t const * funk,
43+
fd_funk_txn_xid_t const * xid,
44+
fd_txn_account_t const * program_acc,
45+
ulong * program_data_len,
46+
fd_funk_txn_xid_t * out_xid ) {
47+
fd_bpf_upgradeable_loader_state_t program_account_state[1];
48+
if( FD_UNLIKELY( !fd_bincode_decode_static(
49+
bpf_upgradeable_loader_state,
50+
program_account_state,
51+
fd_txn_account_get_data( program_acc ),
52+
fd_txn_account_get_data_len( program_acc ),
53+
NULL ) ) ) {
54+
return NULL;
55+
}
56+
if( !fd_bpf_upgradeable_loader_state_is_program( program_account_state ) ) {
57+
return NULL;
58+
}
59+
60+
fd_pubkey_t * programdata_address = &program_account_state->inner.program.programdata_address;
61+
62+
fd_funk_txn_t const * acc_txn = NULL;
63+
fd_account_meta_t const * meta = fd_funk_get_acc_meta_readonly(
64+
funk, xid, programdata_address, NULL, NULL, &acc_txn );
65+
if( FD_UNLIKELY( !meta ) ) return NULL;
66+
*out_xid = acc_txn->xid;
67+
fd_txn_account_t _rec[1];
68+
fd_txn_account_t * programdata_acc = fd_txn_account_join( fd_txn_account_new( _rec, programdata_address, (void *)meta, 0 ), funk->wksp );
69+
if( FD_UNLIKELY( !programdata_acc ) ) FD_LOG_CRIT(( "fd_txn_account_new failed" ));
70+
71+
/* We don't actually need to decode here, just make sure that the account
72+
can be decoded successfully. */
73+
fd_bincode_decode_ctx_t ctx_programdata = {
74+
.data = fd_txn_account_get_data( programdata_acc ),
75+
.dataend = fd_txn_account_get_data( programdata_acc ) + fd_txn_account_get_data_len( programdata_acc ),
76+
};
77+
78+
ulong total_sz = 0UL;
79+
if( FD_UNLIKELY( fd_bpf_upgradeable_loader_state_decode_footprint( &ctx_programdata, &total_sz ) ) ) {
80+
return NULL;
81+
}
82+
83+
if( FD_UNLIKELY( fd_txn_account_get_data_len( programdata_acc )<PROGRAMDATA_METADATA_SIZE ) ) {
84+
return NULL;
85+
}
86+
87+
*program_data_len = fd_txn_account_get_data_len( programdata_acc ) - PROGRAMDATA_METADATA_SIZE;
88+
return fd_txn_account_get_data( programdata_acc ) + PROGRAMDATA_METADATA_SIZE;
89+
}
90+
91+
/* Gets the programdata for a v1/v2 loader-owned account by returning a
92+
pointer to the account data. Returns a pointer to the programdata on
93+
success. Given the txn account API always returns a handle to the
94+
account data, this function should NEVER return NULL (since the
95+
programdata of v1 and v2 loader) accounts start at the beginning of
96+
the data. */
97+
static uchar const *
98+
fd_get_executable_program_content_for_v1_v2_loaders( fd_txn_account_t const * program_acc,
99+
ulong * program_data_len ) {
100+
*program_data_len = fd_txn_account_get_data_len( program_acc );
101+
return fd_txn_account_get_data( program_acc );
102+
}
103+
104+
uchar const *
105+
fd_prog_load_elf( fd_funk_t const * accdb,
106+
fd_funk_txn_xid_t const * xid,
107+
void const * _prog_addr,
108+
ulong * out_sz,
109+
fd_funk_txn_xid_t * out_xid ) {
110+
fd_pubkey_t prog_addr = FD_LOAD( fd_pubkey_t, _prog_addr );
111+
112+
fd_funk_txn_t const * acc_txn = NULL;
113+
fd_account_meta_t const * meta = fd_funk_get_acc_meta_readonly(
114+
accdb, xid, &prog_addr, NULL, NULL, &acc_txn );
115+
if( FD_UNLIKELY( !meta ) ) return NULL;
116+
fd_funk_txn_xid_t rec_xid = acc_txn->xid;
117+
fd_txn_account_t _rec[1];
118+
fd_txn_account_t * rec = fd_txn_account_join( fd_txn_account_new( _rec, &prog_addr, (void *)meta, 0 ), accdb->wksp );
119+
if( FD_UNLIKELY( !rec ) ) FD_LOG_CRIT(( "fd_txn_account_new failed" ));
120+
121+
/* v1/v2 loaders: Programdata is just the account data.
122+
v3 loader: Programdata lives in a separate account. Deserialize the
123+
program account and lookup the programdata account.
124+
Deserialize the programdata account.
125+
v4 loader: Programdata lives in the program account, offset by
126+
LOADER_V4_PROGRAM_DATA_OFFSET. */
127+
fd_pubkey_t const * owner = fd_txn_account_get_owner( rec );
128+
uchar const * elf = NULL;
129+
fd_funk_txn_xid_t sub_xid = { .ul={ 0UL, 0UL } };
130+
if( !memcmp( owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) {
131+
elf = fd_get_executable_program_content_for_upgradeable_loader( accdb, xid, rec, out_sz, &sub_xid );
132+
} else if( !memcmp( owner, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) {
133+
elf = fd_get_executable_program_content_for_v4_loader( rec, out_sz );
134+
} else if( !memcmp( owner, fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) ||
135+
!memcmp( owner, fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) ) {
136+
elf = fd_get_executable_program_content_for_v1_v2_loaders( rec, out_sz );
137+
}
138+
139+
if( FD_LIKELY( elf ) ) {
140+
/* Return the XID with the last modification slow (unclear if they can differ) */
141+
*out_xid = sub_xid.ul[0]>rec_xid.ul[0] ? sub_xid : rec_xid;
142+
} else {
143+
fd_funk_txn_xid_set_root( out_xid );
144+
}
145+
146+
return elf;
147+
}
148+
149+
fd_prog_versions_t
150+
fd_prog_versions( fd_features_t const * features,
151+
ulong slot ) {
152+
int disable_v0 = FD_FEATURE_ACTIVE( slot, features, disable_sbpf_v0_execution );
153+
int reenable_v0 = FD_FEATURE_ACTIVE( slot, features, reenable_sbpf_v0_execution );
154+
int enable_v0 = !disable_v0 || reenable_v0;
155+
int enable_v1 = FD_FEATURE_ACTIVE( slot, features, enable_sbpf_v1_deployment_and_execution );
156+
int enable_v2 = FD_FEATURE_ACTIVE( slot, features, enable_sbpf_v2_deployment_and_execution );
157+
int enable_v3 = FD_FEATURE_ACTIVE( slot, features, enable_sbpf_v3_deployment_and_execution );
158+
159+
fd_prog_versions_t v = {0};
160+
v.min_sbpf_version = enable_v0 ? FD_SBPF_V0 : FD_SBPF_V3;
161+
if( enable_v3 ) {
162+
v.max_sbpf_version = FD_SBPF_V3;
163+
} else if( enable_v2 ) {
164+
v.max_sbpf_version = FD_SBPF_V2;
165+
} else if( enable_v1 ) {
166+
v.max_sbpf_version = FD_SBPF_V1;
167+
} else {
168+
v.max_sbpf_version = FD_SBPF_V0;
169+
}
170+
return v;
171+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#ifndef HEADER_fd_src_flamenco_fd_progcache_fd_prog_load_h
2+
#define HEADER_fd_src_flamenco_fd_progcache_fd_prog_load_h
3+
4+
/* fd_prog_load.h provides high-level APIs for loading Solana programs
5+
from the account database. */
6+
7+
#include "../../funk/fd_funk_rec.h"
8+
#include "../fd_flamenco_base.h"
9+
10+
FD_PROTOTYPES_BEGIN
11+
12+
/* fd_prog_load_elf loads a reference to program data from a funk-backed
13+
account database. *prog_addr gives the account address of the
14+
program account (NOT the program data account). Returns a pointer to
15+
the first byte of the ELF binary on success. *out_sz is set to the
16+
size of the program data account. *out_xid is set to the the txn
17+
XID the program data account was written to. On failure, returns
18+
NULL. Reasons for failure include: program account not found,
19+
program not deployed, program data account not found, etc */
20+
21+
uchar const *
22+
fd_prog_load_elf( fd_funk_t const * accdb,
23+
fd_funk_txn_xid_t const * xid,
24+
void const * prog_addr,
25+
ulong * out_sz,
26+
fd_funk_txn_xid_t * out_xid );
27+
/* FIXME provide an API to detect data race */
28+
/* FIXME clarify edge case where program account and program data
29+
account were modified in different funk txns */
30+
31+
/* fd_prog_versions derives sBPF versions from the current feature set. */
32+
33+
struct fd_prog_versions {
34+
uint min_sbpf_version;
35+
uint max_sbpf_version;
36+
};
37+
typedef struct fd_prog_versions fd_prog_versions_t;
38+
39+
fd_prog_versions_t
40+
fd_prog_versions( fd_features_t const * features,
41+
ulong slot );
42+
43+
FD_PROTOTYPES_END
44+
45+
#endif /* HEADER_fd_src_flamenco_fd_progcache_fd_prog_load_h */

0 commit comments

Comments
 (0)