From 0c97869505b87f99707c1496776fc0489f29b22d Mon Sep 17 00:00:00 2001 From: Michael McGee Date: Thu, 3 Aug 2023 21:49:20 +0000 Subject: [PATCH] fdctl: bank tile --- config/with-optimization.mk | 6 +- .rustfmt.toml => ffi/rust/.rustfmt.toml | 0 Cargo.toml => ffi/rust/Cargo.toml | 2 +- deny.toml => ffi/rust/deny.toml | 0 ffi/rust/firedancer-sys/src/tango/cnc.rs | 2 + ffi/rust/firedancer-sys/src/tango/fctl.rs | 6 + ffi/rust/firedancer-sys/src/tango/fseq.rs | 1 + ffi/rust/firedancer-sys/src/tango/mod.rs | 3 + ffi/rust/firedancer-sys/src/util/mod.rs | 4 + ffi/rust/firedancer-sys/src/util/pod.rs | 2 + ffi/rust/firedancer-sys/src/util/rng.rs | 19 +++ ffi/rust/firedancer-sys/src/util/tempo.rs | 6 + ffi/rust/firedancer-sys/src/util/wksp.rs | 1 + ffi/rust/firedancer-sys/wrapper_util.h | 1 + ffi/rust/firedancer/Cargo.toml | 11 ++ ffi/rust/firedancer/src/bits.rs | 42 +++++ ffi/rust/firedancer/src/cnc.rs | 78 +++++++++ ffi/rust/firedancer/src/dcache.rs | 39 +++++ ffi/rust/firedancer/src/fctl.rs | 99 +++++++++++ ffi/rust/firedancer/src/fseq.rs | 67 ++++++++ ffi/rust/firedancer/src/gaddr.rs | 41 +++++ ffi/rust/firedancer/src/lib.rs | 21 +++ ffi/rust/firedancer/src/mcache.rs | 118 +++++++++++++ ffi/rust/firedancer/src/pod.rs | 156 +++++++++++++++++ ffi/rust/firedancer/src/rng.rs | 37 ++++ ffi/rust/firedancer/src/workspace.rs | 37 ++++ ffi/rust/firedancer/tests/linker.rs | 7 + solana | 2 +- src/app/fdctl/Local.mk | 4 +- src/app/fdctl/config/default.toml | 7 +- src/app/fdctl/configure/configure.c | 16 +- src/app/fdctl/configure/shmem.c | 7 + src/app/fdctl/configure/workspace.c | 2 + src/app/fdctl/configure/xdp.c | 2 +- src/app/fdctl/fdctl.h | 10 +- src/app/fdctl/main.c | 66 ++------ src/app/fdctl/main1.c | 54 +++++- src/app/fdctl/monitor/monitor.c | 8 + src/app/fdctl/run.c | 8 +- src/app/fddev/Local.mk | 2 +- src/app/fddev/dev.c | 11 +- src/app/fddev/dev1.c | 1 + src/app/fddev/fddev.h | 14 ++ src/app/fddev/main.c | 25 +++ src/app/fddev/txn.c | 198 ++++++++++++++++++++++ src/app/frank/fd_frank_pack.c | 7 +- src/tango/quic/tests/test_quic_txn.c | 4 +- src/util/fd_util.c | 7 - src/util/fd_util_linux.c | 34 ---- 49 files changed, 1159 insertions(+), 136 deletions(-) rename .rustfmt.toml => ffi/rust/.rustfmt.toml (100%) rename Cargo.toml => ffi/rust/Cargo.toml (53%) rename deny.toml => ffi/rust/deny.toml (100%) create mode 100644 ffi/rust/firedancer-sys/src/util/rng.rs create mode 100644 ffi/rust/firedancer-sys/src/util/tempo.rs create mode 100644 ffi/rust/firedancer/Cargo.toml create mode 100644 ffi/rust/firedancer/src/bits.rs create mode 100644 ffi/rust/firedancer/src/cnc.rs create mode 100644 ffi/rust/firedancer/src/dcache.rs create mode 100644 ffi/rust/firedancer/src/fctl.rs create mode 100644 ffi/rust/firedancer/src/fseq.rs create mode 100644 ffi/rust/firedancer/src/gaddr.rs create mode 100644 ffi/rust/firedancer/src/lib.rs create mode 100644 ffi/rust/firedancer/src/mcache.rs create mode 100644 ffi/rust/firedancer/src/pod.rs create mode 100644 ffi/rust/firedancer/src/rng.rs create mode 100644 ffi/rust/firedancer/src/workspace.rs create mode 100644 ffi/rust/firedancer/tests/linker.rs create mode 100644 src/app/fddev/txn.c delete mode 100644 src/util/fd_util_linux.c diff --git a/config/with-optimization.mk b/config/with-optimization.mk index 74cf43babc..bd0dd8fe5d 100644 --- a/config/with-optimization.mk +++ b/config/with-optimization.mk @@ -1,3 +1,3 @@ -CPPFLAGS+=-O3 -ffast-math -fno-associative-math -fno-reciprocal-math -CPPFLAGS+=-DFD_HAS_OPTIMIZATION=1 -FD_HAS_OPTIMIZATION:=1 +# CPPFLAGS+=-O3 -ffast-math -fno-associative-math -fno-reciprocal-math +# CPPFLAGS+=-DFD_HAS_OPTIMIZATION=1 +# FD_HAS_OPTIMIZATION:=1 diff --git a/.rustfmt.toml b/ffi/rust/.rustfmt.toml similarity index 100% rename from .rustfmt.toml rename to ffi/rust/.rustfmt.toml diff --git a/Cargo.toml b/ffi/rust/Cargo.toml similarity index 53% rename from Cargo.toml rename to ffi/rust/Cargo.toml index 6fe709b2a3..9cf1e2e908 100644 --- a/Cargo.toml +++ b/ffi/rust/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["ffi/rust/firedancer-sys", "ffi/rust/firedancer-diff"] +members = ["ffi/rust/firedancer", "ffi/rust/firedancer-sys", "ffi/rust/firedancer-diff"] resolver = "2" [profile.dev] diff --git a/deny.toml b/ffi/rust/deny.toml similarity index 100% rename from deny.toml rename to ffi/rust/deny.toml diff --git a/ffi/rust/firedancer-sys/src/tango/cnc.rs b/ffi/rust/firedancer-sys/src/tango/cnc.rs index fe4bdfa29a..17d19c1033 100644 --- a/ffi/rust/firedancer-sys/src/tango/cnc.rs +++ b/ffi/rust/firedancer-sys/src/tango/cnc.rs @@ -13,7 +13,9 @@ pub use crate::generated::{ fd_cnc_leave, fd_cnc_new, fd_cnc_open, + fd_cnc_signal, fd_cnc_signal_cstr, + fd_cnc_signal_query, fd_cnc_strerror, fd_cnc_t, fd_cnc_type, diff --git a/ffi/rust/firedancer-sys/src/tango/fctl.rs b/ffi/rust/firedancer-sys/src/tango/fctl.rs index eb32ef5094..654a6b4719 100644 --- a/ffi/rust/firedancer-sys/src/tango/fctl.rs +++ b/ffi/rust/firedancer-sys/src/tango/fctl.rs @@ -1,8 +1,14 @@ pub use crate::generated::{ fd_fctl_cfg_done, fd_fctl_cfg_rx_add, + fd_fctl_delete, + fd_fctl_join, + fd_fctl_leave, fd_fctl_new, + fd_fctl_private_rx_t, + fd_fctl_rx_cr_return, fd_fctl_t, + fd_fctl_tx_cr_update, FD_FCTL_ALIGN, FD_FCTL_RX_MAX_MAX, }; diff --git a/ffi/rust/firedancer-sys/src/tango/fseq.rs b/ffi/rust/firedancer-sys/src/tango/fseq.rs index fbdbc34290..605d620b94 100644 --- a/ffi/rust/firedancer-sys/src/tango/fseq.rs +++ b/ffi/rust/firedancer-sys/src/tango/fseq.rs @@ -1,5 +1,6 @@ pub use crate::generated::{ fd_fseq_align, + fd_fseq_app_laddr, fd_fseq_app_laddr_const, fd_fseq_delete, fd_fseq_footprint, diff --git a/ffi/rust/firedancer-sys/src/tango/mod.rs b/ffi/rust/firedancer-sys/src/tango/mod.rs index 77d451c0a7..5a9762ef9f 100644 --- a/ffi/rust/firedancer-sys/src/tango/mod.rs +++ b/ffi/rust/firedancer-sys/src/tango/mod.rs @@ -13,3 +13,6 @@ pub use fseq::*; pub use mcache::*; pub use tcache::*; pub use xdp::*; + +pub use crate::generated::fd_chunk_to_laddr; +pub use crate::generated::fd_chunk_to_laddr_const; diff --git a/ffi/rust/firedancer-sys/src/util/mod.rs b/ffi/rust/firedancer-sys/src/util/mod.rs index 3d99fc0fbb..dbfd9e5ee7 100644 --- a/ffi/rust/firedancer-sys/src/util/mod.rs +++ b/ffi/rust/firedancer-sys/src/util/mod.rs @@ -1,14 +1,18 @@ mod bits; mod log; mod pod; +mod rng; mod shmem; +mod tempo; mod tile; mod wksp; pub use bits::*; pub use log::*; pub use pod::*; +pub use rng::*; pub use shmem::*; +pub use tempo::*; pub use tile::*; pub use wksp::*; diff --git a/ffi/rust/firedancer-sys/src/util/pod.rs b/ffi/rust/firedancer-sys/src/util/pod.rs index 16687f7bf8..44a9150bf2 100644 --- a/ffi/rust/firedancer-sys/src/util/pod.rs +++ b/ffi/rust/firedancer-sys/src/util/pod.rs @@ -1,6 +1,8 @@ pub use crate::generated::{ fd_pod_cnt_subpod, fd_pod_info_t, + fd_pod_join, + fd_pod_leave, fd_pod_query, fd_pod_query_buf, fd_pod_query_char, diff --git a/ffi/rust/firedancer-sys/src/util/rng.rs b/ffi/rust/firedancer-sys/src/util/rng.rs new file mode 100644 index 0000000000..3648ddb24a --- /dev/null +++ b/ffi/rust/firedancer-sys/src/util/rng.rs @@ -0,0 +1,19 @@ +pub use crate::generated::{ + fd_rng_delete, + fd_rng_idx, + fd_rng_idx_set, + fd_rng_int, + fd_rng_join, + fd_rng_leave, + fd_rng_long, + fd_rng_new, + fd_rng_schar, + fd_rng_seq, + fd_rng_seq_set, + fd_rng_short, + fd_rng_t, + fd_rng_uchar, + fd_rng_uint, + fd_rng_ulong, + fd_rng_ushort, +}; diff --git a/ffi/rust/firedancer-sys/src/util/tempo.rs b/ffi/rust/firedancer-sys/src/util/tempo.rs new file mode 100644 index 0000000000..1bc091dd7e --- /dev/null +++ b/ffi/rust/firedancer-sys/src/util/tempo.rs @@ -0,0 +1,6 @@ +pub use crate::generated::{ + fd_tempo_async_min, + fd_tempo_async_reload, + fd_tempo_lazy_default, + fd_tempo_tick_per_ns, +}; diff --git a/ffi/rust/firedancer-sys/src/util/wksp.rs b/ffi/rust/firedancer-sys/src/util/wksp.rs index f43cad4483..83da7cc153 100644 --- a/ffi/rust/firedancer-sys/src/util/wksp.rs +++ b/ffi/rust/firedancer-sys/src/util/wksp.rs @@ -27,5 +27,6 @@ pub use crate::generated::{ fd_wksp_pod_map, fd_wksp_pod_unmap, fd_wksp_t, + fd_wksp_private, fd_wksp_unmap, }; diff --git a/ffi/rust/firedancer-sys/wrapper_util.h b/ffi/rust/firedancer-sys/wrapper_util.h index 01c6b72bc2..948fcfea45 100644 --- a/ffi/rust/firedancer-sys/wrapper_util.h +++ b/ffi/rust/firedancer-sys/wrapper_util.h @@ -1,4 +1,5 @@ #include "src/util/fd_util.h" +#include "src/util/wksp/fd_wksp_private.h" #if FD_MCACHE_LG_INTERLEAVE #error "FD_MCACHE_LG_INTERLEAVE unsupported" diff --git a/ffi/rust/firedancer/Cargo.toml b/ffi/rust/firedancer/Cargo.toml new file mode 100644 index 0000000000..2656000b33 --- /dev/null +++ b/ffi/rust/firedancer/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "firedancer" +version = "0.1.0" +edition = "2021" +authors = ["Firedancer Contributors "] +description = "Safe high level wrappers for Firedancer" +license = "Apache-2.0" + +[dependencies] +paste = "1.0.12" +firedancer-sys = { path = "../firedancer-sys" } diff --git a/ffi/rust/firedancer/src/bits.rs b/ffi/rust/firedancer/src/bits.rs new file mode 100644 index 0000000000..587dae92ec --- /dev/null +++ b/ffi/rust/firedancer/src/bits.rs @@ -0,0 +1,42 @@ +macro_rules! align_up { + ( $x:expr, $a:expr ) => { + ($x + ($a - 1)) & !($a - 1) + }; +} + +macro_rules! layout { + ( value=$value:expr, ) => { $value }; + ( value=$value:expr, ($align:expr, $size:expr), $($tail:tt)*) => { + layout!( value=align_up!($value, $align) + $size, $($tail)*) + }; + ( align=$align:expr, [ $($tail:tt)* ]) => { + align_up!(layout!(value = 0, $($tail)*), $align) + }; +} + +pub(crate) use { + align_up, + layout, +}; + +#[cfg(test)] +mod tests { + #[test] + fn test_align_up() { + let zeros = 0u64; + let ones = u64::MAX; + + for i in 0..64 { + let align = 1u64 << i; + let lo = (1u64 << i) - 1; + let hi = !lo; + + assert_eq!(align_up!(zeros, align), zeros); + assert_eq!(align_up!(ones, align), if i == 0 { ones } else { zeros }); + for j in 0..64 { + let x = 1u64 << j; + assert_eq!(align_up!(x, align), (x + lo) & hi); + } + } + } +} diff --git a/ffi/rust/firedancer/src/cnc.rs b/ffi/rust/firedancer/src/cnc.rs new file mode 100644 index 0000000000..e7586b0fbc --- /dev/null +++ b/ffi/rust/firedancer/src/cnc.rs @@ -0,0 +1,78 @@ +use std::ffi::c_ulong; +use std::ptr::{ + read_volatile, + write_volatile, +}; +use firedancer_sys::*; + +use crate::*; + +pub struct Cnc { + laddr: *mut tango::fd_cnc_t, + diagnostic: *mut c_ulong, + _workspace: Workspace, +} + +impl Drop for Cnc { + fn drop(&mut self) { + unsafe { tango::fd_cnc_leave(self.laddr) }; + } +} + +#[repr(u32)] +#[derive(Copy, Clone, Debug)] +pub enum CncSignal { + Run = tango::FD_CNC_SIGNAL_RUN, + Boot = tango::FD_CNC_SIGNAL_BOOT, + Fail = tango::FD_CNC_SIGNAL_FAIL, + Halt = tango::FD_CNC_SIGNAL_HALT, +} + +#[repr(u32)] +#[derive(Copy, Clone, Debug)] +pub enum CncDiag { + InBackpressure = tango::FD_CNC_DIAG_IN_BACKP, + BackpressureCount = tango::FD_CNC_DIAG_BACKP_CNT, +} + +impl Cnc { + pub unsafe fn join>(gaddr: T) -> Result { + let workspace = Workspace::map(gaddr)?; + let laddr = tango::fd_cnc_join(workspace.laddr.as_ptr()); + if laddr.is_null() { + Err(()) + } else { + let diagnostic = tango::fd_cnc_app_laddr(laddr) as *mut c_ulong; + if diagnostic.is_null() { + Err(()) + } else { + Ok(Self { + laddr, + diagnostic, + _workspace: workspace, + }) + } + } + } + + pub fn query(&self) -> u64 { + unsafe { tango::fd_cnc_signal_query(self.laddr) } + } + + pub fn signal(&self, signal: u64) { + unsafe { tango::fd_cnc_signal(self.laddr, signal) } + } + + pub unsafe fn set(&self, diag: u64, value: u64) { + write_volatile(self.diagnostic.offset(diag as isize), value) + } + + pub unsafe fn increment(&self, diag: u64, value: u64) { + let offset = self.diagnostic.offset(diag as isize); + write_volatile(offset, read_volatile(offset) + value) + } + + pub fn heartbeat(&self, now: i64) { + unsafe { tango::fd_cnc_heartbeat(self.laddr, now) } + } +} diff --git a/ffi/rust/firedancer/src/dcache.rs b/ffi/rust/firedancer/src/dcache.rs new file mode 100644 index 0000000000..8510e34f08 --- /dev/null +++ b/ffi/rust/firedancer/src/dcache.rs @@ -0,0 +1,39 @@ +use firedancer_sys::*; + +use std::ffi::c_void; + +use crate::*; + +pub struct DCache { + laddr: *mut u8, + wksp: *mut util::fd_wksp_t, + _workspace: Workspace, +} + +impl Drop for DCache { + fn drop(&mut self) { + unsafe { tango::fd_dcache_leave(self.laddr) }; + } +} + +impl DCache { + pub unsafe fn join>(gaddr: T) -> Result { + let workspace = Workspace::map(gaddr)?; + let laddr = tango::fd_dcache_join(workspace.laddr.as_ptr()); + if laddr.is_null() { + return Err(()); + } + + let wksp = util::fd_wksp_containing(laddr as *const c_void); + Ok(Self { + laddr, + wksp, + _workspace: workspace, + }) + } + + pub unsafe fn slice<'a>(&self, chunk: u64, offset: u64, len: u64) -> &'a[u8] { + let laddr = tango::fd_chunk_to_laddr_const(self.wksp as *const c_void, chunk); + std::slice::from_raw_parts(laddr.offset(offset as isize) as *const u8, len as usize) + } +} diff --git a/ffi/rust/firedancer/src/fctl.rs b/ffi/rust/firedancer/src/fctl.rs new file mode 100644 index 0000000000..2a8f1f3bfb --- /dev/null +++ b/ffi/rust/firedancer/src/fctl.rs @@ -0,0 +1,99 @@ +use std::ffi::c_void; +use std::marker::PhantomData; +use std::mem::{ + align_of, + size_of, +}; +use std::ptr::null_mut; + +use firedancer_sys::*; + +use crate::*; + +macro_rules! footprint { + ( $rx_max:expr ) => { + layout!( + align = tango::FD_FCTL_ALIGN as usize, + [ + ( + align_of::(), + size_of::() + ), + ( + align_of::(), + $rx_max * size_of::() + ), + ] + ) + }; +} + +pub struct FCtl<'a, 'b> { + _stack: [u8; footprint!(1)], + shmem: *mut c_void, + fctl: *mut tango::fd_fctl_t, + _seq: PhantomData<&'a u64>, + _slow: PhantomData<&'b mut u64>, +} + +impl<'a, 'b> Drop for FCtl<'a, 'b> { + fn drop(&mut self) { + unsafe { tango::fd_fctl_leave(self.fctl) }; + unsafe { tango::fd_fctl_delete(self.shmem) }; + } +} + +impl<'a, 'b> FCtl<'a, 'b> { + pub fn new( + cr_burst: u64, + cr_max: u64, + cr_resume: u64, + cr_refill: u64, + fseq: &FSeq, + ) -> Result { + let mut stack = [0; footprint!(1usize)]; + let shmem = unsafe { tango::fd_fctl_new(&mut stack as *mut _ as *mut c_void, 1) }; + if shmem.is_null() { + return Err(()); + } + + let fctl = unsafe { tango::fd_fctl_join(shmem) }; + if fctl.is_null() { + return Err(()); + } + + let fctl = unsafe { + tango::fd_fctl_cfg_rx_add( + fctl, + cr_max, + fseq.laddr, + fseq.diagnostic.offset(FSeqDiag::SlowCount as isize), + ) + }; + if fctl.is_null() { + return Err(()); + } + + let fctl = unsafe { tango::fd_fctl_cfg_done(fctl, cr_burst, cr_max, cr_resume, cr_refill) }; + + Ok(FCtl { + _stack: stack, + shmem, + fctl, + _seq: PhantomData, + _slow: PhantomData, + }) + } + + pub fn tx_cr_update(&self, cr_avail: u64, mcache: &MCache) -> u64 { + unsafe { tango::fd_fctl_tx_cr_update(self.fctl, cr_avail, mcache.sequence_number) } + } +} + +pub fn housekeeping_default_interval_nanos(cr_max: u64) -> i64 { + unsafe { util::fd_tempo_lazy_default(cr_max) } +} + +pub fn minimum_housekeeping_tick_interval(lazy: i64) -> u64 { + unsafe { util::fd_tempo_async_min(lazy, 1, util::fd_tempo_tick_per_ns(null_mut()) as f32) } +} diff --git a/ffi/rust/firedancer/src/fseq.rs b/ffi/rust/firedancer/src/fseq.rs new file mode 100644 index 0000000000..8792a8b57d --- /dev/null +++ b/ffi/rust/firedancer/src/fseq.rs @@ -0,0 +1,67 @@ +use std::ffi::c_ulong; +use std::ptr::{ + read_volatile, + write_volatile, +}; + +use firedancer_sys::*; + +use crate::*; + +pub struct FSeq { + pub(crate) laddr: *mut c_ulong, + pub(crate) diagnostic: *mut c_ulong, + _workspace: Workspace, +} + +impl Drop for FSeq { + fn drop(&mut self) { + unsafe { tango::fd_fseq_leave(self.laddr) }; + } +} + +#[repr(u32)] +#[derive(Copy, Clone, Debug)] +pub enum FSeqDiag { + PublishedCount = tango::FD_FSEQ_DIAG_PUB_CNT, + PublishedSize = tango::FD_FSEQ_DIAG_PUB_SZ, + FilteredCount = tango::FD_FSEQ_DIAG_FILT_CNT, + FilteredSize = tango::FD_FSEQ_DIAG_FILT_SZ, + OverrunPollingCount = tango::FD_FSEQ_DIAG_OVRNP_CNT, + OverrunReadingCount = tango::FD_FSEQ_DIAG_OVRNR_CNT, + SlowCount = tango::FD_FSEQ_DIAG_SLOW_CNT, +} + +impl FSeq { + pub unsafe fn join>(gaddr: T) -> Result { + let workspace = Workspace::map(gaddr)?; + let laddr = tango::fd_fseq_join(workspace.laddr.as_ptr()); + if laddr.is_null() { + Err(()) + } else { + let diagnostic = tango::fd_fseq_app_laddr(laddr) as *mut c_ulong; + if diagnostic.is_null() { + Err(()) + } else { + Ok(Self { + laddr, + diagnostic, + _workspace: workspace, + }) + } + } + } + + pub unsafe fn set(&self, diag: u64, value: u64) { + write_volatile(self.diagnostic.offset(diag as isize), value) + } + + pub unsafe fn increment(&self, diag: u64, value: u64) { + let offset = self.diagnostic.offset(diag as isize); + write_volatile(offset, read_volatile(offset) + value) + } + + pub fn rx_cr_return(&self, mcache: &MCache) { + unsafe { tango::fd_fctl_rx_cr_return(self.laddr, mcache.sequence_number) } + } +} \ No newline at end of file diff --git a/ffi/rust/firedancer/src/gaddr.rs b/ffi/rust/firedancer/src/gaddr.rs new file mode 100644 index 0000000000..427d90ea35 --- /dev/null +++ b/ffi/rust/firedancer/src/gaddr.rs @@ -0,0 +1,41 @@ +use std::ffi::{ + c_char, + CString, +}; + +pub struct GlobalAddress { + gaddr: CString, + _offset: u64, +} + +impl GlobalAddress { + pub(crate) fn as_ptr(&self) -> *const c_char { + self.gaddr.as_ptr() + } +} + +impl TryFrom for GlobalAddress { + type Error = (); + + fn try_from(value: String) -> Result { + let (_workspace, offset) = match value.split_once(':') { + None => return Err(()), + Some(parts) => parts, + }; + + let offset = match offset.parse() { + Err(_) => return Err(()), + Ok(offset) => offset, + }; + + let gaddr = match CString::new(value) { + Err(_) => return Err(()), + Ok(path) => path, + }; + + Ok(GlobalAddress { + gaddr, + _offset: offset, + }) + } +} diff --git a/ffi/rust/firedancer/src/lib.rs b/ffi/rust/firedancer/src/lib.rs new file mode 100644 index 0000000000..7adcde9139 --- /dev/null +++ b/ffi/rust/firedancer/src/lib.rs @@ -0,0 +1,21 @@ +mod bits; +mod cnc; +mod dcache; +mod fctl; +mod fseq; +mod gaddr; +mod mcache; +mod pod; +mod rng; +mod workspace; + +use bits::*; +pub use cnc::*; +pub use dcache::*; +pub use fctl::*; +pub use fseq::*; +pub use gaddr::*; +pub use mcache::*; +pub use pod::*; +pub use rng::*; +pub use workspace::*; diff --git a/ffi/rust/firedancer/src/mcache.rs b/ffi/rust/firedancer/src/mcache.rs new file mode 100644 index 0000000000..8214976a6a --- /dev/null +++ b/ffi/rust/firedancer/src/mcache.rs @@ -0,0 +1,118 @@ +use std::sync::atomic::{ + compiler_fence, + Ordering, +}; + +use firedancer_sys::*; + +use crate::*; + +pub struct MCache { + laddr: *mut tango::fd_frag_meta_t, + mline: *mut tango::fd_frag_meta_t, + pub(crate) sequence_number: u64, + depth: u64, + + sync: *mut u64, + _workspace: Workspace, +} + +#[derive(Copy, Clone)] +pub enum MCacheCtl { + None +} + +impl MCacheCtl { + fn ctl(&self) -> u64 { + match self { + MCacheCtl::None => 0, + } + } +} + +#[derive(Copy, Clone)] +pub enum Poll { + CaughtUp, + Overrun, + Ready, +} + +#[derive(Copy, Clone)] + pub enum Advance { + Overrun, + Normal, +} + +impl MCache { + pub unsafe fn join>(gaddr: T) -> Result { + let workspace = Workspace::map(gaddr)?; + let laddr = tango::fd_mcache_join(workspace.laddr.as_ptr()); + if laddr.is_null() { + return Err(()); + } + + let depth = tango::fd_mcache_depth(laddr); + let sync = tango::fd_mcache_seq_laddr(laddr); + let sequence_number = tango::fd_mcache_seq_query(sync); + let mline = laddr.offset(tango::fd_mcache_line_idx(sequence_number, depth) as isize); + + Ok(Self { + laddr, + mline, + sequence_number, + depth, + sync, + _workspace: workspace, + }) + } + + pub fn depth(&self) -> u64 { + self.depth + } + + pub fn chunk(&self) -> u32 { + unsafe { (*self.mline).__bindgen_anon_1.chunk } + } + + pub fn housekeep(&self) { + unsafe { tango::fd_mcache_seq_update(self.sync, self.sequence_number) } + } + + pub fn poll(&self) -> Poll { + compiler_fence(Ordering::AcqRel); + let sequence_number_found = unsafe { (*self.mline).__bindgen_anon_1.seq }; + compiler_fence(Ordering::AcqRel); + + match sequence_number_found.cmp(&self.sequence_number) { + std::cmp::Ordering::Less => Poll::Overrun, + std::cmp::Ordering::Equal => Poll::Ready, + std::cmp::Ordering::Greater => Poll::CaughtUp, + } + } + + pub fn size(&self) -> u16 { + unsafe { (*self.mline).__bindgen_anon_1.sz } + } + + pub fn advance(&mut self) -> Advance { + compiler_fence(Ordering::AcqRel); + let sequence_number_found = unsafe { (*self.mline).__bindgen_anon_1.seq }; + compiler_fence(Ordering::AcqRel); + + if sequence_number_found != self.sequence_number { + self.sequence_number = sequence_number_found; + Advance::Overrun + } else { + self.sequence_number += 1; + self.mline = unsafe { + self.laddr + .offset(tango::fd_mcache_line_idx(self.sequence_number, self.depth) as isize) + }; + Advance::Normal + } + } + + pub fn publish(&mut self, sig: u64, chunk: u64, sz: u64, ctl: MCacheCtl, tsorig: u64, tspub: u64) { + unsafe { tango::fd_mcache_publish(self.mline, self.depth, self.sequence_number, sig, chunk, sz, ctl.ctl(), tsorig, tspub) } + } +} diff --git a/ffi/rust/firedancer/src/pod.rs b/ffi/rust/firedancer/src/pod.rs new file mode 100644 index 0000000000..2fb1cd9155 --- /dev/null +++ b/ffi/rust/firedancer/src/pod.rs @@ -0,0 +1,156 @@ +use std::ffi::{ + c_char, + CStr, + CString, c_void, +}; +use std::marker::PhantomData; +use std::ptr::{null, NonNull}; +use std::sync::Arc; + +use firedancer_sys::*; +use paste::paste; + +use crate::*; + +#[derive(Clone)] +pub struct Pod { + laddr: *const u8, + workspace: Arc, +} + +unsafe impl Send for Pod {} +unsafe impl Sync for Pod {} + +impl Drop for Pod { + fn drop(&mut self) { + unsafe { util::fd_pod_leave(self.laddr) }; + } +} + +impl Pod { + pub unsafe fn join>(gaddr: T) -> Result { + let workspace = Workspace::map(gaddr)?; + let laddr = util::fd_pod_join(workspace.laddr.as_ptr()); + if laddr.is_null() { + Err(()) + } else { + Ok(Self { + laddr, + workspace: Arc::new(workspace), + }) + } + } + + pub unsafe fn join_default>(wksp: T) -> Result { + let wksp_str = CString::new(wksp.as_ref()).unwrap(); + let wksp = util::fd_wksp_attach(wksp_str.as_ptr()); + if wksp.is_null() { + return Err(()); + } + + let pod = util::fd_wksp_laddr( wksp, (*wksp).gaddr_lo ); + if pod.is_null() { + return Err(()); + } + + let laddr = util::fd_pod_join(pod); + + if laddr.is_null() { + Err(()) + } else { + Ok(Self { + laddr, + workspace: Arc::new(Workspace { + laddr: NonNull::new(laddr as *mut c_void).unwrap(), + _marker: PhantomData, + }), + }) + } + } + + pub fn try_query>(&self, key: S) -> Option { + let key = match CString::new(key.as_ref()) { + Ok(key) => key, + _ => return None, + }; + + T::try_query(self, key.as_ptr()) + } + + pub fn query>(&self, key: S) -> T { + let key = match CString::new(key.as_ref()) { + Ok(key) => key, + _ => return T::default(), + }; + + T::query(self, key.as_ptr()) + } +} + +pub trait FromPod: Sized { + fn try_query(pod: &Pod, key: *const c_char) -> Option; + + fn query(pod: &Pod, key: *const c_char) -> Self { + FromPod::try_query(pod, key).unwrap() + } +} + +impl FromPod for String { + fn try_query(pod: &Pod, key: *const c_char) -> Option { + let value = unsafe { util::fd_pod_query_cstr(pod.laddr, key, null()) } as *const i8; + + if value.is_null() { + return None; + } + + match unsafe { CStr::from_ptr(value).to_str() } { + Ok(str) => Some(str.to_owned()), + _ => None, + } + } +} + +impl FromPod for Pod { + fn try_query(pod: &Pod, key: *const c_char) -> Option { + let laddr = unsafe { util::fd_pod_query_subpod(pod.laddr, key) }; + + if laddr.is_null() { + return None; + } + + Some(Pod { + laddr, + workspace: pod.workspace.clone(), + }) + } +} + +impl FromPod for GlobalAddress { + fn try_query(pod: &Pod, key: *const c_char) -> Option { + let string: String = FromPod::try_query(pod, key)?; + string.try_into().ok() + } +} + +macro_rules! impl_from_pod { + ($ty:ty, $id:ident) => { + impl FromPod for $ty { + fn try_query(pod: &Pod, key: *const c_char) -> Option { + paste! { + unsafe { + Some(util::[](pod.laddr, key, $ty::default())) + } + } + } + } + }; +} + +impl_from_pod!(i8, char); +impl_from_pod!(i16, short); +impl_from_pod!(i32, int); +impl_from_pod!(i64, long); +impl_from_pod!(u8, uchar); +impl_from_pod!(u16, ushort); +impl_from_pod!(u32, uint); +impl_from_pod!(u64, ulong); diff --git a/ffi/rust/firedancer/src/rng.rs b/ffi/rust/firedancer/src/rng.rs new file mode 100644 index 0000000000..add0a0ab44 --- /dev/null +++ b/ffi/rust/firedancer/src/rng.rs @@ -0,0 +1,37 @@ +use std::ffi::c_void; + +use firedancer_sys::*; + +pub struct Rng { + inner: util::fd_rng_t, + shmem: *mut c_void, +} + +impl Drop for Rng { + fn drop(&mut self) { + unsafe { util::fd_rng_leave(&mut self.inner) }; + unsafe { util::fd_rng_delete(self.shmem) }; + } +} + +impl Rng { + pub fn new(seed: u32, id: u64) -> Result { + let mut inner = util::fd_rng_t { seq: 0, idx: 0 }; + + let shmem = unsafe { util::fd_rng_new(&mut inner as *mut _ as *mut c_void, seed, id) }; + if shmem.is_null() { + return Err(()); + } + + let rng = unsafe { util::fd_rng_join(shmem) }; + if rng.is_null() { + return Err(()); + } + + Ok(Rng { inner, shmem }) + } + + pub fn async_reload(&mut self, min: u64) -> u64 { + unsafe { util::fd_tempo_async_reload(&mut self.inner, min) } + } +} diff --git a/ffi/rust/firedancer/src/workspace.rs b/ffi/rust/firedancer/src/workspace.rs new file mode 100644 index 0000000000..f1cefd18e3 --- /dev/null +++ b/ffi/rust/firedancer/src/workspace.rs @@ -0,0 +1,37 @@ +use std::cell::Cell; +use std::ffi::c_void; +use std::marker::PhantomData; +use std::ptr::NonNull; + +use firedancer_sys::*; + +use crate::*; + +pub struct Workspace { + pub(crate) laddr: NonNull, + pub(crate) _marker: PhantomData>, // Not covariant +} + +impl Drop for Workspace { + fn drop(&mut self) { + unsafe { firedancer_sys::util::fd_wksp_unmap(self.laddr.as_ptr()) } + } +} + +impl Workspace { + pub(crate) unsafe fn map>(gaddr: G) -> Result { + let addr: GlobalAddress = match gaddr.try_into() { + Ok(addr) => addr, + _ => return Err(()), + }; + let laddr = unsafe { util::fd_wksp_map(addr.as_ptr()) }; + if laddr.is_null() { + Err(()) + } else { + Ok(Self { + laddr: NonNull::new(laddr).unwrap(), + _marker: PhantomData, + }) + } + } +} diff --git a/ffi/rust/firedancer/tests/linker.rs b/ffi/rust/firedancer/tests/linker.rs new file mode 100644 index 0000000000..c7a80d077d --- /dev/null +++ b/ffi/rust/firedancer/tests/linker.rs @@ -0,0 +1,7 @@ +use firedancer::*; + +#[test] +fn links_properly() { + let gaddr = GlobalAddress::try_from("".to_string()).unwrap(); + assert!(unsafe { MCache::join(gaddr) }.is_err()); +} diff --git a/solana b/solana index 354d6262d6..3da4992446 160000 --- a/solana +++ b/solana @@ -1 +1 @@ -Subproject commit 354d6262d62e95df27aefa1b8e5d24f1e5f415e2 +Subproject commit 3da4992446a6a1028631bc821e8664cd1d2df49e diff --git a/src/app/fdctl/Local.mk b/src/app/fdctl/Local.mk index a1e6f3feb5..8f74b8a335 100644 --- a/src/app/fdctl/Local.mk +++ b/src/app/fdctl/Local.mk @@ -3,8 +3,8 @@ ifdef FD_HAS_ALLOCA ifdef FD_HAS_X86 ifdef FD_HAS_DOUBLE $(call make-lib,fd_fdctl) -$(call add-objs,main config security utility run keygen monitor/monitor monitor/helper configure/configure configure/large_pages configure/sysctl configure/shmem configure/xdp configure/xdp_leftover configure/ethtool configure/workspace_leftover configure/workspace,fd_fdctl) -$(call make-bin,fdctl,main1,fd_fdctl fd_frank fd_disco fd_ballet fd_tango fd_util fd_quic solana_validator_fd) +$(call add-objs,main1 config security utility run keygen monitor/monitor monitor/helper configure/configure configure/large_pages configure/sysctl configure/shmem configure/xdp configure/xdp_leftover configure/ethtool configure/workspace_leftover configure/workspace,fd_fdctl) +$(call make-bin,fdctl,main,fd_fdctl fd_frank fd_disco fd_ballet fd_tango fd_util fd_quic solana_validator_fd) $(OBJDIR)/bin/fdctl: src/app/fdctl/config/default.toml endif endif diff --git a/src/app/fdctl/config/default.toml b/src/app/fdctl/config/default.toml index e86d4de97d..348615455d 100644 --- a/src/app/fdctl/config/default.toml +++ b/src/app/fdctl/config/default.toml @@ -534,6 +534,9 @@ dynamic_port_range = "8000-10000" # capabilities of the process after initialization to make the attack # surface smaller. This is required in production, but might be too # restrictive during development. + # + # In development, you can disable the sandbox for testing and debugging + # with the `--no-sandbox` argument to `fddev`. sandbox = true # Because of how Firedancer uses UDP and XDP together, we do not receive @@ -549,7 +552,9 @@ dynamic_port_range = "8000-10000" # by `fdctl` within that namespace. # # This is a development only configuration, network namespaces are not - # suitable for production use due to performance overhead. + # suitable for production use due to performance overhead. In developmemnt + # when running with `fddev`, this can also be enabled with the `--netns` + # argument. [development.netns] # If enabled, `fdctl dev` will ensure the network namespaces are # configured properly, can route to each other, and that running diff --git a/src/app/fdctl/configure/configure.c b/src/app/fdctl/configure/configure.c index 65aa9efae2..6ba4937480 100644 --- a/src/app/fdctl/configure/configure.c +++ b/src/app/fdctl/configure/configure.c @@ -4,18 +4,6 @@ #include #include -configure_stage_t * STAGES[ CONFIGURE_STAGE_COUNT ] = { - &large_pages, - &shmem, - &sysctl, - &xdp, - &xdp_leftover, - ðtool, - &workspace_leftover, - &workspace, - NULL, -}; - void configure_cmd_args( int * pargc, char *** pargv, @@ -32,7 +20,7 @@ configure_cmd_args( int * pargc, (*pargv)++; for( int i=0; i<*pargc; i++ ) { - if( FD_UNLIKELY( !strcmp(*pargv[ i ], "all" ) ) ) { + if( FD_UNLIKELY( !strcmp( *pargv[ i ], "all" ) ) ) { (*pargc) -= i + 1; (*pargv) += i + 1; for( int j=0; jconfigure.stages[ j ] = STAGES[ j ]; @@ -43,7 +31,7 @@ configure_cmd_args( int * pargc, if( FD_UNLIKELY( *pargc >= CONFIGURE_STAGE_COUNT ) ) FD_LOG_ERR(( "too many stages specified" )); ulong nstage = 0; - while( pargc ) { + while( *pargc ) { int found = 0; for( configure_stage_t ** stage = STAGES; *stage; stage++ ) { if( FD_UNLIKELY( !strcmp( *pargv[0], (*stage)->name ) ) ) { diff --git a/src/app/fdctl/configure/shmem.c b/src/app/fdctl/configure/shmem.c index d623c40a96..ebf72053d4 100644 --- a/src/app/fdctl/configure/shmem.c +++ b/src/app/fdctl/configure/shmem.c @@ -134,9 +134,12 @@ check( config_t * const config ) { if( FD_UNLIKELY( !fp ) ) FD_LOG_ERR(( "failed to open `/proc/self/mounts`" )); char line[ 4096 ]; + int found = 0; while( FD_LIKELY( fgets( line, 4096, fp ) ) ) { if( FD_UNLIKELY( strlen( line ) == 4095 ) ) FD_LOG_ERR(( "line too long in `/proc/self/mounts`" )); if( FD_UNLIKELY( strstr( line, path[i] ) ) ) { + found = 1; + char * saveptr; char * device = strtok_r( line, " ", &saveptr ); if( FD_UNLIKELY( !device ) ) FD_LOG_ERR(( "error parsing `/proc/self/mounts`, line `%s`", line )); @@ -172,6 +175,7 @@ check( config_t * const config ) { if( FD_UNLIKELY( fclose( fp ) ) ) FD_LOG_ERR(( "error closing `/proc/self/mounts` (%i-%s)", errno, strerror( errno ) )); PARTIALLY_CONFIGURED( "mount `%s` is not mounted read/write, expected `rw`", path[i] ); } + break; } } @@ -179,6 +183,9 @@ check( config_t * const config ) { FD_LOG_ERR(( "error reading `/proc/self/mounts` (%i-%s)", errno, strerror( errno ) )); if( FD_LIKELY( fclose( fp ) ) ) FD_LOG_ERR(( "error closing `/proc/self/mounts` (%i-%s)", errno, strerror( errno ) )); + + if( FD_UNLIKELY( !found ) ) + PARTIALLY_CONFIGURED( "mount `%s` not found in `/proc/self/mounts`", path[i] ); } CONFIGURE_OK(); diff --git a/src/app/fdctl/configure/workspace.c b/src/app/fdctl/configure/workspace.c index 3e174c5491..5f4fe9d68d 100644 --- a/src/app/fdctl/configure/workspace.c +++ b/src/app/fdctl/configure/workspace.c @@ -241,6 +241,8 @@ init( config_t * const config ) { mcache( pod, "mcache%lu", config->tiles.bank.receive_buffer_size, i ); dcache( pod, "dcache%lu", USHORT_MAX, config->layout.bank_tile_count * (ulong)config->tiles.bank.receive_buffer_size, 0, i ); fseq ( pod, "fseq%lu", i ); + mcache( pod, "mcache-back%lu", config->tiles.bank.receive_buffer_size, i ); + fseq ( pod, "fseq-back%lu", i ); } break; case wksp_bank_shred: diff --git a/src/app/fdctl/configure/xdp.c b/src/app/fdctl/configure/xdp.c index 699d901ec4..1e727c93da 100644 --- a/src/app/fdctl/configure/xdp.c +++ b/src/app/fdctl/configure/xdp.c @@ -14,7 +14,7 @@ static void init_perm( security_t * security, config_t * const config ) { if( FD_UNLIKELY( config->development.netns.enabled ) ) - check_cap( security, NAME, CAP_SYS_ADMIN, "enter a network namespace" ); + check_cap( security, NAME, CAP_SYS_ADMIN, "enter a network namespace by calling `setns(2)`" ); else { check_cap( security, NAME, CAP_SYS_ADMIN, "create a BPF map with `bpf_map_create`" ); check_cap( security, NAME, CAP_NET_ADMIN, "create an XSK map with `bpf_map_create`" ); diff --git a/src/app/fdctl/fdctl.h b/src/app/fdctl/fdctl.h index d4fe1f92da..f1feead944 100644 --- a/src/app/fdctl/fdctl.h +++ b/src/app/fdctl/fdctl.h @@ -11,7 +11,7 @@ #include #include -#define CONFIGURE_STAGE_COUNT 9 +#define CONFIGURE_STAGE_COUNT 11 struct configure_stage; typedef union { @@ -24,11 +24,17 @@ typedef union { } monitor; struct { int command; - struct configure_stage * stages[ CONFIGURE_STAGE_COUNT + 2 ]; + struct configure_stage * stages[ CONFIGURE_STAGE_COUNT ]; } configure; struct { int tile; } run1; + struct { + const char * payload_base64; + ulong count; + const char * dst_ip; + ushort dst_port; + } txn; } args_t; typedef struct security security_t; diff --git a/src/app/fdctl/main.c b/src/app/fdctl/main.c index 16aa41059a..749ab6390a 100644 --- a/src/app/fdctl/main.c +++ b/src/app/fdctl/main.c @@ -1,55 +1,23 @@ #include "fdctl.h" -action_t ACTIONS[ 4 ] = { - { .name = "run", .args = NULL, .fn = run_cmd_fn, .perm = run_cmd_perm }, - { .name = "configure", .args = configure_cmd_args, .fn = configure_cmd_fn, .perm = configure_cmd_perm }, - { .name = "monitor", .args = monitor_cmd_args, .fn = monitor_cmd_fn, .perm = monitor_cmd_perm }, - { .name = "keygen", .args = NULL, .fn = keygen_cmd_fn, .perm = NULL }, +#include "configure/configure.h" + +configure_stage_t * STAGES[ CONFIGURE_STAGE_COUNT ] = { + &large_pages, + &shmem, + &sysctl, + &xdp, + &xdp_leftover, + ðtool, + &workspace_leftover, + &workspace, + NULL, + NULL, + NULL, }; int -main1( int argc, - char ** _argv ) { - fd_boot( &argc, &_argv ); - fd_log_thread_set( "main" ); - - char ** argv = _argv; - argc--; argv++; - - /* load configuration and command line parsing */ - config_t config = config_parse( &argc, &argv ); - - if( FD_UNLIKELY( !argc ) ) FD_LOG_ERR(( "no subcommand specified" )); - - action_t * action = NULL; - for( ulong i=0; iargs ) ) action->args( &argc, &argv, &args ); - if( FD_UNLIKELY( argc ) ) FD_LOG_ERR(( "unknown argument `%s`", argv[ 0 ] )); - - /* check if we are appropriate permissioned to run the desired command */ - if( FD_LIKELY( action->perm ) ) { - security_t security = { - .idx = 0, - }; - action->perm( &args, &security, &config ); - if( FD_UNLIKELY( security.idx ) ) { - for( ulong i=0; ifn( &args, &config ); - - return 0; +main( int argc, + char ** argv ) { + main1( argc, argv ); } diff --git a/src/app/fdctl/main1.c b/src/app/fdctl/main1.c index 878deb8352..16aa41059a 100644 --- a/src/app/fdctl/main1.c +++ b/src/app/fdctl/main1.c @@ -1,7 +1,55 @@ #include "fdctl.h" +action_t ACTIONS[ 4 ] = { + { .name = "run", .args = NULL, .fn = run_cmd_fn, .perm = run_cmd_perm }, + { .name = "configure", .args = configure_cmd_args, .fn = configure_cmd_fn, .perm = configure_cmd_perm }, + { .name = "monitor", .args = monitor_cmd_args, .fn = monitor_cmd_fn, .perm = monitor_cmd_perm }, + { .name = "keygen", .args = NULL, .fn = keygen_cmd_fn, .perm = NULL }, +}; + int -main( int argc, - char ** argv ) { - main1( argc, argv ); +main1( int argc, + char ** _argv ) { + fd_boot( &argc, &_argv ); + fd_log_thread_set( "main" ); + + char ** argv = _argv; + argc--; argv++; + + /* load configuration and command line parsing */ + config_t config = config_parse( &argc, &argv ); + + if( FD_UNLIKELY( !argc ) ) FD_LOG_ERR(( "no subcommand specified" )); + + action_t * action = NULL; + for( ulong i=0; iargs ) ) action->args( &argc, &argv, &args ); + if( FD_UNLIKELY( argc ) ) FD_LOG_ERR(( "unknown argument `%s`", argv[ 0 ] )); + + /* check if we are appropriate permissioned to run the desired command */ + if( FD_LIKELY( action->perm ) ) { + security_t security = { + .idx = 0, + }; + action->perm( &args, &security, &config ); + if( FD_UNLIKELY( security.idx ) ) { + for( ulong i=0; ifn( &args, &config ); + + return 0; } diff --git a/src/app/fdctl/monitor/monitor.c b/src/app/fdctl/monitor/monitor.c index c0e7f5ec1d..9e63c1736f 100644 --- a/src/app/fdctl/monitor/monitor.c +++ b/src/app/fdctl/monitor/monitor.c @@ -219,6 +219,14 @@ run_monitor( config_t * const config, links[ link_idx ].fseq = fd_fseq_join( fd_wksp_pod_map( pods[ j ], snprintf1( buf, 64, "fseq%lu", i ) ) ); if( FD_UNLIKELY( !links[ link_idx ].fseq ) ) FD_LOG_ERR(( "fd_fseq_join failed" )); link_idx++; + + links[ link_idx ].src_name = "bank"; + links[ link_idx ].dst_name = "pack"; + links[ link_idx ].mcache = fd_mcache_join( fd_wksp_pod_map( pods[ j ], snprintf1( buf, 64, "mcache-back%lu", i ) ) ); + if( FD_UNLIKELY( !links[ link_idx ].mcache ) ) FD_LOG_ERR(( "fd_mcache_join failed" )); + links[ link_idx ].fseq = fd_fseq_join( fd_wksp_pod_map( pods[ j ], snprintf1( buf, 64, "fseq-back%lu", i ) ) ); + if( FD_UNLIKELY( !links[ link_idx ].fseq ) ) FD_LOG_ERR(( "fd_fseq_join failed" )); + link_idx++; } break; case wksp_bank_shred: diff --git a/src/app/fdctl/run.c b/src/app/fdctl/run.c index e9a3ec49ea..5fd197f0b3 100644 --- a/src/app/fdctl/run.c +++ b/src/app/fdctl/run.c @@ -25,10 +25,12 @@ run_cmd_perm( args_t * args, check_res( security, "run", RLIMIT_NOFILE, 1024000, "increase `RLIMIT_NOFILE` to allow more open files for Solana Labs" ); check_cap( security, "run", CAP_NET_RAW, "call `bind(2)` to bind to a socket with `SOCK_RAW`" ); check_cap( security, "run", CAP_SYS_ADMIN, "initialize XDP by calling `bpf_obj_get`" ); - if( getuid() != config->uid ) + if( FD_LIKELY( getuid() != config->uid ) ) check_cap( security, "run", CAP_SETUID, "switch uid by calling `setuid(2)`" ); - if( getgid() != config->gid ) + if( FD_LIKELY( getgid() != config->gid ) ) check_cap( security, "run", CAP_SETGID, "switch gid by calling `setgid(2)`" ); + if( FD_UNLIKELY( config->development.netns.enabled ) ) + check_cap( security, "run", CAP_SYS_ADMIN, "enter a network namespace by calling `setns(2)`" ); } static void @@ -222,6 +224,8 @@ solana_labs_main( void * args ) { ADD1( "fdctl" ); ADD( "--log", "-" ); + ADD( "--firedancer-app-name", config->name ); + ADD( "--dynamic-port-range", config->dynamic_port_range ); /* consensus */ diff --git a/src/app/fddev/Local.mk b/src/app/fddev/Local.mk index c3be981c5e..69da3e7374 100644 --- a/src/app/fddev/Local.mk +++ b/src/app/fddev/Local.mk @@ -2,7 +2,7 @@ ifdef FD_HAS_HOSTED ifdef FD_HAS_ALLOCA ifdef FD_HAS_X86 ifdef FD_HAS_DOUBLE -$(call make-bin,fddev,main dev dev1 configure/netns configure/cluster,fd_fdctl fd_frank fd_disco fd_ballet fd_tango fd_util fd_quic solana_validator_fd) +$(call make-bin,fddev,main dev dev1 txn configure/netns configure/cluster,fd_fdctl fd_frank fd_disco fd_ballet fd_tango fd_util fd_quic solana_validator_fd) $(OBJDIR)/bin/fddev: src/app/fdctl/config/default.toml endif endif diff --git a/src/app/fddev/dev.c b/src/app/fddev/dev.c index 3098c37706..1843b495bf 100644 --- a/src/app/fddev/dev.c +++ b/src/app/fddev/dev.c @@ -18,10 +18,8 @@ dev_cmd_perm( args_t * args, args_t configure_args = { .configure.command = CONFIGURE_CMD_INIT, }; - configure_args.configure.stages[ 0 ] = &netns; for( ulong i=0; irun1.tile = 3; else if( FD_LIKELY( !strcmp( *pargv[ 0 ], "bank" ) ) ) args->run1.tile = 4; else if( FD_LIKELY( !strcmp( *pargv[ 0 ], "labs" ) ) ) args->run1.tile = 4; + else if( FD_LIKELY( !strcmp( *pargv[ 0 ], "solana" ) ) ) args->run1.tile = 4; else if( FD_LIKELY( !strcmp( *pargv[ 0 ], "solana-labs" ) ) ) args->run1.tile = 4; else FD_LOG_ERR(( "unrecognized tile %s", *pargv[0] )); diff --git a/src/app/fddev/fddev.h b/src/app/fddev/fddev.h index 734219d71c..cb06f933ae 100644 --- a/src/app/fddev/fddev.h +++ b/src/app/fddev/fddev.h @@ -21,4 +21,18 @@ void dev1_cmd_fn( args_t * args, config_t * const config ); +void +txn_cmd_perm( args_t * args, + security_t * security, + config_t * const config ); + +void +txn_cmd_args( int * pargc, + char *** pargv, + args_t * args); + +void +txn_cmd_fn( args_t * args, + config_t * const config ); + #endif /* HEADER_fd_src_app_fddev_fddev_h */ diff --git a/src/app/fddev/main.c b/src/app/fddev/main.c index 673cd818e4..bf349b0020 100644 --- a/src/app/fddev/main.c +++ b/src/app/fddev/main.c @@ -1,15 +1,32 @@ #define _GNU_SOURCE #include "fddev.h" +#include "../fdctl/configure/configure.h" + #include #include #include #include #include +configure_stage_t * STAGES[ CONFIGURE_STAGE_COUNT ] = { + &netns, + &large_pages, + &shmem, + &sysctl, + &xdp, + &xdp_leftover, + ðtool, + &workspace_leftover, + &workspace, + &cluster, + NULL, +}; + static action_t DEV_ACTIONS[] = { { .name = "dev", .args = NULL, .fn = dev_cmd_fn, .perm = dev_cmd_perm }, { .name = "dev1", .args = dev1_cmd_args, .fn = dev1_cmd_fn, .perm = dev_cmd_perm }, + { .name = "txn", .args = txn_cmd_args, .fn = txn_cmd_fn, .perm = txn_cmd_perm }, }; #define MAX_ARGC 32 @@ -77,6 +94,14 @@ main( int argc, int no_sandbox = fd_env_strip_cmdline_contains( &argc, &argv, "--no-sandbox" ); config.development.sandbox = config.development.sandbox && !no_sandbox; + int netns = fd_env_strip_cmdline_contains( &argc, &argv, "--netns" ); + if( FD_UNLIKELY( !config.development.netns.enabled && netns ) ) { + config.development.netns.enabled = 1; + strncpy( config.tiles.quic.interface, + config.development.netns.interface0, + sizeof(config.tiles.quic.interface) ); + } + const char * action_name = "dev"; if( FD_UNLIKELY( argc > 0 && argv[ 0 ][ 0 ] != '-' ) ) { action_name = argv[ 0 ]; diff --git a/src/app/fddev/txn.c b/src/app/fddev/txn.c new file mode 100644 index 0000000000..336c48e899 --- /dev/null +++ b/src/app/fddev/txn.c @@ -0,0 +1,198 @@ +#include "fddev.h" + +#include "../fdctl/configure/configure.h" +#include "../../ballet/base64/fd_base64.h" +#include "../../tango/quic/fd_quic.h" +#include "../../tango/quic/tests/fd_quic_test_helpers.h" + +#include + +FD_IMPORT_BINARY(sample_transaction, "src/tango/quic/tests/quic_txn.bin"); + +static int g_conn_hs_complete = 0; +static int g_conn_final = 0; +static int g_stream_notify = 0; + +#define MAX_TXN_COUNT 128 + +void +txn_cmd_perm( args_t * args, + security_t * security, + config_t * const config ) { + (void)args; + + if( FD_UNLIKELY( config->development.netns.enabled ) ) + check_cap( security, "txn", CAP_SYS_ADMIN, "enter a network namespace by calling `setns(2)`" ); +} + +void +txn_cmd_args( int * pargc, + char *** pargv, + args_t * args ) { + args->txn.payload_base64 = fd_env_strip_cmdline_cstr( pargc, pargv, "--payload-base64-encoded", NULL, NULL ); + args->txn.count = fd_env_strip_cmdline_ulong( pargc, pargv, "--count", NULL, 1 ); + if( FD_UNLIKELY( !args->txn.count || args->txn.count > MAX_TXN_COUNT ) ) + FD_LOG_ERR(( "count must be between 1 and %d", MAX_TXN_COUNT )); + + args->txn.dst_ip = fd_env_strip_cmdline_cstr( pargc, pargv, "--dst-ip", NULL, 0 ); + args->txn.dst_port = fd_env_strip_cmdline_ushort( pargc, pargv, "--dst-port", NULL, 0 ); +} + +static ulong +cb_now( void * context ) { + (void)context; + return (ulong)fd_log_wallclock(); +} + +static void +cb_conn_hs_complete( fd_quic_conn_t * conn, + void * quic_ctx ) { + (void)conn; + (void)quic_ctx; + g_conn_hs_complete = 1; +} + +static void +cb_conn_final( fd_quic_conn_t * conn, + void * quic_ctx ) { + (void)conn; + (void)quic_ctx; + g_conn_final = 1; +} + +static void +cb_stream_notify( fd_quic_stream_t * stream, + void * stream_ctx, + int notify_type ) { + (void)stream; + (void)stream_ctx; + (void)notify_type; + g_stream_notify = 1; +} + +static void +send_quic_transactions( fd_quic_t * quic, + fd_quic_udpsock_t * udpsock, + ulong count, + uint dst_ip, + ushort dst_port, + fd_aio_pkt_info_t * pkt ) { + fd_quic_set_aio_net_tx( quic, udpsock->aio ); + FD_TEST( fd_quic_init( quic ) ); + + quic->cb.now = cb_now; + quic->cb.conn_final = cb_conn_final; + quic->cb.conn_hs_complete = cb_conn_hs_complete; + quic->cb.stream_notify = cb_stream_notify; + + fd_quic_conn_t * conn = fd_quic_connect( quic, dst_ip, dst_port, NULL ); + while ( FD_LIKELY( !( g_conn_hs_complete || g_conn_final ) ) ) { + fd_quic_service( quic ); + fd_quic_udpsock_service( udpsock ); + } + FD_TEST( conn ); + if( FD_UNLIKELY( conn->state != FD_QUIC_CONN_STATE_ACTIVE ) ) + FD_LOG_ERR(( "unable to connect to QUIC endpoint on port %hu, is it running? state is %d", dst_port, conn->state )); + + fd_quic_stream_t * stream = fd_quic_conn_new_stream( conn, FD_QUIC_TYPE_UNIDIR ); + FD_TEST( stream ); + + ulong sent = 0; + while( sent < count ) { + int res = fd_quic_stream_send( stream, pkt + sent, count - sent, 1 ); + if( FD_UNLIKELY( res < 0 ) ) FD_LOG_ERR(( "fd_quic_stream_send failed (%d)", res )); + sent += (ulong)res; + + fd_quic_service( quic ); + fd_quic_udpsock_service( udpsock ); + } + + while ( FD_UNLIKELY( !( g_stream_notify || g_conn_final ) ) ) { + fd_quic_service( quic ); + fd_quic_udpsock_service( udpsock ); + } + + fd_quic_conn_close( conn, 0 ); + fd_quic_fini( quic ); +} + +void +txn_cmd_fn( args_t * args, + config_t * const config ) { + if( FD_UNLIKELY( config->development.netns.enabled ) ) + enter_network_namespace( config->development.netns.interface1 ); + + fd_quic_limits_t quic_limits = { + .conn_cnt = 1UL, + .handshake_cnt = 1UL, + .conn_id_cnt = 4UL, + .conn_id_sparsity = 4.0, + .stream_cnt = { 0UL, // FD_QUIC_STREAM_TYPE_BIDI_CLIENT + 0UL, // FD_QUIC_STREAM_TYPE_BIDI_SERVER + 1UL, // FD_QUIC_STREAM_TYPE_UNI_CLIENT + 0UL }, // FD_QUIC_STREAM_TYPE_UNI_SERVER + .stream_sparsity = 4.0, + .inflight_pkt_cnt = 64UL, + .tx_buf_sz = 1UL<<15UL + }; + ulong quic_footprint = fd_quic_footprint( &quic_limits ); + FD_TEST( quic_footprint ); + + fd_wksp_t * wksp = fd_wksp_new_anonymous( fd_cstr_to_shmem_page_sz("normal"), + 1UL << 10, + fd_shmem_cpu_idx( 0 ), + "wksp", + 0UL ); + FD_TEST( wksp ); + void * mem = fd_wksp_alloc_laddr( wksp, fd_quic_align(), quic_footprint, 1UL ); + fd_quic_t * quic = fd_quic_new( mem, &quic_limits ); + FD_TEST( quic ); + + fd_quic_udpsock_t _udpsock; + fd_quic_udpsock_t * udpsock = fd_quic_client_create_udpsock( &_udpsock, wksp, fd_quic_get_aio_net_rx( quic ), 0 ); + FD_TEST( udpsock == &_udpsock ); + + fd_quic_config_t * client_cfg = &quic->config; + client_cfg->role = FD_QUIC_ROLE_CLIENT; + memcpy( client_cfg->alpns, "\xasolana-tpu", 11UL ); + client_cfg->alpns_sz = 11U; + memcpy(client_cfg->link.dst_mac_addr, "\x52\xF1\x7E\xDA\x2C\xE0", 6UL); + client_cfg->net.ip_addr = udpsock->listen_ip; + client_cfg->net.ephem_udp_port.lo = (ushort)udpsock->listen_port; + client_cfg->net.ephem_udp_port.hi = (ushort)(udpsock->listen_port + 1); + client_cfg->initial_rx_max_stream_data = 1<<15; + client_cfg->idle_timeout = 100; + client_cfg->initial_rx_max_stream_data = FD_QUIC_DEFAULT_INITIAL_RX_MAX_STREAM_DATA; + + fd_aio_pkt_info_t pkt[ MAX_TXN_COUNT ]; + + if( FD_LIKELY( !args->txn.payload_base64 ) ) { + for( ulong i=0; itxn.count; i++ ) { + pkt[ i ].buf = (void * )sample_transaction; + pkt[ i ].buf_sz = (ushort )sample_transaction_sz; + } + } else { + uchar buf[1300]; + int buf_sz = fd_base64_decode( args->txn.payload_base64, buf ); + if( FD_UNLIKELY( buf_sz == -1 ) ) FD_LOG_ERR(( "bad payload input `%s`", args->txn.payload_base64 )); + for( ulong i=0; itxn.count; i++ ) { + pkt[ i ].buf = (void * )buf; + pkt[ i ].buf_sz = (ushort )buf_sz; + } + } + + uint dst_ip; + if( FD_UNLIKELY( !args->txn.dst_ip && config->development.netns.enabled ) ) { + if( FD_UNLIKELY( !fd_cstr_to_ip4_addr( config->development.netns.interface0_addr, &dst_ip ) ) ) FD_LOG_ERR(( "invalid `interface0_addr` for netns in config file" )); + } else if( FD_LIKELY( args->txn.dst_ip ) ) { + if( FD_UNLIKELY( !fd_cstr_to_ip4_addr( args->txn.dst_ip, &dst_ip ) ) ) FD_LOG_ERR(( "invalid --dst-ip" )); + } else { + FD_LOG_ERR(( "--dst-ip must be provided when sending transactions" )); + } + + ushort dst_port = config->tiles.quic.listen_port; + if( FD_UNLIKELY( args->txn.dst_port ) ) dst_port = args->txn.dst_port; + + send_quic_transactions( quic, udpsock, args->txn.count, dst_ip, dst_port, pkt ); + exit_group( 0 ); +} diff --git a/src/app/frank/fd_frank_pack.c b/src/app/frank/fd_frank_pack.c index ea1cba54a5..2c197c99be 100644 --- a/src/app/frank/fd_frank_pack.c +++ b/src/app/frank/fd_frank_pack.c @@ -138,12 +138,11 @@ run( fd_frank_args_t * args ) { out_state out[ FD_FRANK_PACK_MAX_OUT ]; /* FIXME: Plumb this through properly: */ - ulong bank_cnt = fd_pod_cnt( args->out_pod ) / 3UL - 1UL; /* Skip bank 0 */ + ulong bank_cnt = fd_pod_query_ulong( args->out_pod, "num_tiles", 0UL ); + if( FD_UNLIKELY( !bank_cnt ) ) FD_LOG_ERR(( "pack.num_tiles unset or set to zero" )); if( FD_UNLIKELY( bank_cnt>FD_FRANK_PACK_MAX_OUT ) ) FD_LOG_ERR(( "pack tile connects to too many banking tiles" )); - /* Skip bank 0 */ - for( ulong i=0UL; iout_pod, i+1UL ); - + for( ulong i=0UL; iout_pod, i ); ulong max_txn_per_microblock = MAX_MICROBLOCK_SZ/sizeof(fd_txn_p_t); diff --git a/src/tango/quic/tests/test_quic_txn.c b/src/tango/quic/tests/test_quic_txn.c index fcb149759d..222b05b909 100644 --- a/src/tango/quic/tests/test_quic_txn.c +++ b/src/tango/quic/tests/test_quic_txn.c @@ -151,8 +151,8 @@ main( int argc, pkt.buf_sz = ( ushort ) buf_sz; } - fd_wksp_t * wksp = fd_wksp_new_anonymous( fd_cstr_to_shmem_page_sz("gigantic"), - 1UL, + fd_wksp_t * wksp = fd_wksp_new_anonymous( fd_cstr_to_shmem_page_sz("normal"), + 1UL << 15, fd_shmem_cpu_idx( 0 ), "wksp", 0UL ); diff --git a/src/util/fd_util.c b/src/util/fd_util.c index bed42a9c93..cc91da9045 100644 --- a/src/util/fd_util.c +++ b/src/util/fd_util.c @@ -1,7 +1,3 @@ -#if defined(__linux__) -#include "fd_util_linux.c" -#endif - #include "fd_util.h" void @@ -10,9 +6,6 @@ fd_boot( int * pargc, /* At this point, we are immediately after the program start, there is only one thread of execution and fd has not yet been booted. */ fd_log_private_boot ( pargc, pargv ); -# if defined(__linux__) - fd_linux_private_boot( pargc, pargv ); -# endif fd_shmem_private_boot( pargc, pargv ); fd_tile_private_boot ( pargc, pargv ); /* The caller is now tile 0 */ } diff --git a/src/util/fd_util_linux.c b/src/util/fd_util_linux.c deleted file mode 100644 index c3ea27ae9d..0000000000 --- a/src/util/fd_util_linux.c +++ /dev/null @@ -1,34 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include - -#include "fd_util_base.h" -#include "log/fd_log.h" - -/* fd_linux_enter_netns: Replaces the network namespace of the calling - thread with the namespace located at the given nsfs mount path. */ -static void -fd_linux_enter_netns( char const * netns ) { - /* These syscalls mirror `ip netns exec` */ - int ns_fd = open( netns, O_RDONLY|O_CLOEXEC ); - if( FD_UNLIKELY( ns_fd<0 ) ) { - FD_LOG_WARNING(( "Entering netns failed: open(%s) returned (%d-%s)", - netns, errno, strerror( errno ) )); - return; - } - if( FD_UNLIKELY( 0!=setns( ns_fd, CLONE_NEWNET ) ) ) { - FD_LOG_WARNING(( "setns(%s,CLONE_NEWNET) failed (%d-%s)", - netns, errno, strerror( errno ) )); - return; - } - FD_LOG_INFO(( "Using netns %s", netns )); -} - -static void -fd_linux_private_boot( int * pargc, - char *** pargv ) { - char const * netns = fd_env_strip_cmdline_cstr( pargc, pargv, "--netns", "NETNS", NULL ); - if( netns ) fd_linux_enter_netns( netns ); -} -