1use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
77use std::sync::{Arc, Mutex, OnceLock};
78
79use cudarc::driver::{CudaContext, CudaEvent, CudaSlice, CudaStream};
80
81use crate::Engine;
82
83pub fn pp_cuts(n_layers: usize) -> Option<Vec<usize>> {
88 let n_st: usize = match std::env::var("MEMRA_PP_STAGES") {
89 Ok(v) if v.is_empty() || v == "0" || v == "1" => return None,
90 Ok(v) => match v.parse::<usize>() {
91 Ok(n) => n,
92 Err(_) => {
93 warn_bad_once(&format!("MEMRA_PP_STAGES={v} unparseable; door stays OFF"));
94 return None;
95 }
96 },
97 Err(_) => return None,
98 };
99 if n_st < 2 || n_st > n_layers {
100 warn_bad_once(&format!(
101 "MEMRA_PP_STAGES={n_st} outside [2, n_layers={n_layers}]; door stays OFF"
102 ));
103 return None;
104 }
105 let mut fence = Vec::with_capacity(n_st + 1);
106 fence.push(0usize);
107 if let Ok(s) = std::env::var("MEMRA_PP_SPLITS") {
108 let parts: Result<Vec<usize>, _> =
109 s.split(',').map(|p| p.trim().parse::<usize>()).collect();
110 match parts {
111 Ok(cuts) if cuts.len() == n_st - 1 => fence.extend(cuts),
112 _ => {
113 warn_bad_once(&format!(
114 "MEMRA_PP_SPLITS={s} invalid (want {} comma-separated cuts); door stays OFF",
115 n_st - 1
116 ));
117 return None;
118 }
119 }
120 } else if let Ok(v) = std::env::var("MEMRA_PP_SPLIT") {
121 if n_st != 2 {
124 warn_bad_once(&format!(
125 "MEMRA_PP_SPLIT={v} set with MEMRA_PP_STAGES={n_st}; use MEMRA_PP_SPLITS \
126 for N>2 — door stays OFF"
127 ));
128 return None;
129 }
130 match v.parse::<usize>() {
131 Ok(c) => fence.push(c),
132 Err(_) => {
133 warn_bad_once(&format!("MEMRA_PP_SPLIT={v} unparseable; door stays OFF"));
134 return None;
135 }
136 }
137 } else {
138 for s in 1..n_st {
139 fence.push(s * n_layers / n_st);
140 }
141 }
142 fence.push(n_layers);
143 for w in fence.windows(2) {
144 if w[0] >= w[1] {
145 warn_bad_once(&format!(
146 "pp stage fence {fence:?} not strictly increasing over [0, {n_layers}]; \
147 door stays OFF"
148 ));
149 return None;
150 }
151 }
152 Some(fence)
153}
154
155pub fn pp2_split(n_layers: usize) -> Option<usize> {
158 pp_cuts(n_layers).filter(|f| f.len() == 3).map(|f| f[1])
159}
160
161pub fn stage_of(fence: &[usize], il: usize) -> usize {
163 debug_assert!(fence.len() >= 2);
164 match fence[1..fence.len() - 1].binary_search(&il) {
165 Ok(k) => k + 1,
167 Err(k) => k,
168 }
169}
170
171pub fn pp2_streams_off() -> bool {
174 matches!(std::env::var("MEMRA_PP_STREAMS").as_deref(), Ok("0"))
175}
176
177pub fn pp_multi_stream_same_device() -> bool {
188 let stages_open = std::env::var("MEMRA_PP_STAGES")
189 .map(|v| v.parse::<usize>().map(|n| n >= 2).unwrap_or(false))
190 .unwrap_or(false);
191 let devices = std::env::var("MEMRA_PP_DEVICES").ok().filter(|v| !v.is_empty());
192 if (!stages_open && devices.is_none()) || pp2_streams_off() {
193 return false;
194 }
195 match devices {
196 None => true, Some(s) => {
198 let mut v: Vec<&str> = s.split(',').map(|p| p.trim()).collect();
199 let n = v.len();
200 v.sort_unstable();
201 v.dedup();
202 v.len() < n }
204 }
205}
206
207pub fn pp_sharded_cross_device() -> bool {
221 let stages_open = std::env::var("MEMRA_PP_STAGES")
222 .map(|v| v.parse::<usize>().map(|n| n >= 2).unwrap_or(false))
223 .unwrap_or(false);
224 if !stages_open || pp_shard_off() || pp2_streams_off() {
231 return false;
232 }
233 match pp2_devices_env() {
234 None => false, Some(s) => {
236 let mut v: Vec<&str> = s.split(',').map(|p| p.trim()).collect();
237 v.sort_unstable();
238 v.dedup();
239 v.len() >= 2
240 }
241 }
242}
243
244pub fn refuse_unsplit_if_remote(path: &str, alt: &str) -> Result<(), Box<dyn std::error::Error>> {
256 if pp_host_bounce_active() {
257 return Err(format!(
258 "{path}: refused with MEMRA_PP_HOST_BOUNCE=1 on sharded cross-device PP — \
259 this unsplit path peer-reads remote weights, while host bounce covers only \
260 explicit stage-boundary transfers. Use {alt}; the \
261 MEMRA_PP_ALLOW_UNSPLIT_BATCH override is unavailable on a broken-peer host."
262 )
263 .into());
264 }
265 if pp_sharded_cross_device()
266 && std::env::var("MEMRA_PP_ALLOW_UNSPLIT_BATCH").as_deref() != Ok("1")
267 {
268 return Err(format!(
269 "{path}: refused with the ppN door open across 2+ devices — this path has no pp \
270 stage split, so it would walk ALL layers on one stream and peer-read every \
271 remote stage's weights each step (measured 28x slower at B=1, 13.9x at B=8 on \
272 a PRO 6000 pair over PCIe Gen5 x16 P2P; research/pp2-hardening-20260806). \
273 Exactness is unaffected — peer reads return identical bytes and the exactness \
274 gates PASS on this config — which is exactly why it must refuse instead of \
275 being caught by a gate. Fixes, in order: {alt}; or MEMRA_PP_SHARD=0 (all \
276 weights home on the primary — full speed, forfeits the capacity PP-2 exists \
277 for); or close the pp door. MEMRA_PP_ALLOW_UNSPLIT_BATCH=1 overrides for \
278 measurement."
279 )
280 .into());
281 }
282 Ok(())
283}
284
285pub fn batch_pp_on() -> bool {
293 std::env::var("MEMRA_BATCH_PP").as_deref() != Ok("0")
294}
295
296#[derive(Clone, Copy, PartialEq, Eq, Debug)]
314pub enum DualPpMode {
315 Off,
316 Forced,
317 Auto,
318}
319
320pub fn dual_pp_mode_resolve(v: Option<&str>) -> DualPpMode {
323 match v {
324 Some("0") => DualPpMode::Off,
325 Some("1") => DualPpMode::Forced,
326 _ => DualPpMode::Auto,
327 }
328}
329
330pub fn dual_pp_mode() -> DualPpMode {
331 dual_pp_mode_resolve(std::env::var("MEMRA_DUAL_PP").ok().as_deref())
332}
333
334pub fn dual_pp_on() -> bool {
337 dual_pp_mode() != DualPpMode::Off
338}
339
340pub fn dual_pp_route(
346 mode: DualPpMode,
347 batch: usize,
348 stages: usize,
349 double_slot: bool,
350 host_bounce: bool,
351) -> bool {
352 if batch < 2 {
353 return false;
354 }
355 match mode {
356 DualPpMode::Off => false,
357 DualPpMode::Forced => true,
358 DualPpMode::Auto => stages == 2 && double_slot && !host_bounce,
359 }
360}
361
362pub const DUAL_PP_SINGLE_SLOT_REFUSAL: &str =
365 "decode_step_batch_dual: refused: PP boundary is single-slot; set MEMRA_PP_OVERLAP=1 so both alternating boundary slots are prepared before dual-active decode";
366pub const DUAL_PP_HOST_BOUNCE_REFUSAL: &str =
367 "decode_step_batch_dual: refused: MEMRA_PP_HOST_BOUNCE=1 is unvalidated for dual-active decode; disable MEMRA_DUAL_PP or use peer transport";
368
369pub fn dual_pp_wave_mid(batch: usize) -> Option<usize> {
372 (batch >= 2).then_some((batch + 1) / 2)
373}
374
375pub fn dual_pp_eligibility(
378 stages: usize,
379 double_slot: bool,
380 host_bounce: bool,
381) -> Result<(), &'static str> {
382 if stages != 2 {
383 return Err("decode_step_batch_dual: refused: dual-active decode requires exactly two PP stages");
384 }
385 if !double_slot {
386 return Err(DUAL_PP_SINGLE_SLOT_REFUSAL);
387 }
388 if host_bounce {
389 return Err(DUAL_PP_HOST_BOUNCE_REFUSAL);
390 }
391 Ok(())
392}
393
394static DUAL_PP_OVERLAPS: AtomicUsize = AtomicUsize::new(0);
397static DUAL_PP_ACTIVE_STAGES: AtomicUsize = AtomicUsize::new(0);
398static DUAL_PP_STAGE_NS: [AtomicU64; 4] = [
399 AtomicU64::new(0), AtomicU64::new(0), AtomicU64::new(0), AtomicU64::new(0),
400];
401static DUAL_PP_STAGE_SAMPLES: [AtomicUsize; 4] = [
402 AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),
403];
404static DUAL_PP_TIMING_DROPPED: AtomicUsize = AtomicUsize::new(0);
405static DUAL_PP_SLOT_PAIRS: AtomicUsize = AtomicUsize::new(0);
406static DUAL_PP_SLOT_USES: [AtomicUsize; 2] = [AtomicUsize::new(0), AtomicUsize::new(0)];
407static DUAL_PP_SLOT_COLLISIONS: AtomicUsize = AtomicUsize::new(0);
408
409pub const DUAL_PP_STAGE_NAMES: [&str; 4] = [
410 "wave_a_stage0", "wave_a_stage1", "wave_b_stage0", "wave_b_stage1",
411];
412
413pub fn dual_pp_overlaps() -> usize {
414 DUAL_PP_OVERLAPS.load(Ordering::Relaxed)
415}
416
417pub(crate) fn record_dual_pp_slot_pair(slot_a: usize, slot_b: usize) -> bool {
421 debug_assert!(slot_a < DUAL_PP_SLOT_USES.len());
422 debug_assert!(slot_b < DUAL_PP_SLOT_USES.len());
423 if slot_a == slot_b {
424 DUAL_PP_SLOT_COLLISIONS.fetch_add(1, Ordering::Relaxed);
425 return false;
426 }
427 DUAL_PP_SLOT_USES[slot_a].fetch_add(1, Ordering::Relaxed);
428 DUAL_PP_SLOT_USES[slot_b].fetch_add(1, Ordering::Relaxed);
429 DUAL_PP_SLOT_PAIRS.fetch_add(1, Ordering::Relaxed);
430 true
431}
432
433pub fn dual_pp_slot_snapshot() -> (usize, [usize; 2], usize) {
435 (
436 DUAL_PP_SLOT_PAIRS.load(Ordering::Relaxed),
437 std::array::from_fn(|i| DUAL_PP_SLOT_USES[i].load(Ordering::Relaxed)),
438 DUAL_PP_SLOT_COLLISIONS.load(Ordering::Relaxed),
439 )
440}
441
442pub fn dual_pp_timing_on() -> bool {
446 static ON: OnceLock<bool> = OnceLock::new();
447 *ON.get_or_init(|| std::env::var("MEMRA_DUAL_PP_TIMING").as_deref() == Ok("1"))
448}
449
450pub(crate) fn record_dual_pp_stage_ms(stage: usize, ms: f32) {
451 assert!(stage < DUAL_PP_STAGE_NS.len(), "dual PP timing stage out of range");
452 let ns = (f64::from(ms) * 1_000_000.0).round() as u64;
453 DUAL_PP_STAGE_NS[stage].fetch_add(ns, Ordering::Relaxed);
454 DUAL_PP_STAGE_SAMPLES[stage].fetch_add(1, Ordering::Relaxed);
455}
456
457pub(crate) fn record_dual_pp_timing_drop(
460 context: &str,
461 err: &dyn std::fmt::Display,
462) {
463 let previous = DUAL_PP_TIMING_DROPPED.fetch_add(1, Ordering::Relaxed);
464 if previous == 0 {
465 eprintln!(
466 "[dual-pp] WARN: skipped diagnostic timing sample at {context}: {err}; decode continues"
467 );
468 }
469}
470
471pub(crate) fn record_dual_pp_stage_result<E: std::fmt::Display>(
472 stage: usize,
473 elapsed: Result<f32, E>,
474) {
475 match elapsed {
476 Ok(ms) => record_dual_pp_stage_ms(stage, ms),
477 Err(err) => record_dual_pp_timing_drop(DUAL_PP_STAGE_NAMES[stage], &err),
478 }
479}
480
481pub fn dual_pp_timing_dropped() -> usize {
482 DUAL_PP_TIMING_DROPPED.load(Ordering::Relaxed)
483}
484
485pub fn dual_pp_timing_snapshot() -> ([u64; 4], [usize; 4]) {
487 (
488 std::array::from_fn(|i| DUAL_PP_STAGE_NS[i].load(Ordering::Relaxed)),
489 std::array::from_fn(|i| DUAL_PP_STAGE_SAMPLES[i].load(Ordering::Relaxed)),
490 )
491}
492
493pub(crate) struct DualPpStageGuard;
494
495pub(crate) fn enter_dual_pp_stage() -> DualPpStageGuard {
496 let active = DUAL_PP_ACTIVE_STAGES.fetch_add(1, Ordering::AcqRel);
497 if active > 0 {
498 DUAL_PP_OVERLAPS.fetch_add(1, Ordering::Relaxed);
499 }
500 DualPpStageGuard
501}
502
503impl Drop for DualPpStageGuard {
504 fn drop(&mut self) {
505 let active = DUAL_PP_ACTIVE_STAGES.fetch_sub(1, Ordering::AcqRel);
506 debug_assert!(active > 0, "dual PP active-stage counter underflow");
507 }
508}
509
510pub fn prime_pp_on() -> bool {
520 std::env::var("MEMRA_PRIME_PP").as_deref() != Ok("0")
521}
522
523pub fn prime_pipe_on() -> bool {
528 std::env::var("MEMRA_PRIME_PIPE").as_deref() != Ok("0")
529}
530
531pub static PRIME_SPLIT_CHUNKS: AtomicUsize = AtomicUsize::new(0);
538
539pub fn prime_split_chunks() -> usize {
541 PRIME_SPLIT_CHUNKS.load(Ordering::Relaxed)
542}
543
544pub static PRIME_PIPE_OVERLAPS: AtomicUsize = AtomicUsize::new(0);
549
550pub fn prime_pipe_overlaps() -> usize {
552 PRIME_PIPE_OVERLAPS.load(Ordering::Relaxed)
553}
554
555static PRIME_PIPE_ACTIVE_STAGES: AtomicUsize = AtomicUsize::new(0);
556
557pub(crate) struct PrimePipeStageGuard;
558
559pub(crate) fn enter_prime_pipe_stage() -> PrimePipeStageGuard {
562 let active = PRIME_PIPE_ACTIVE_STAGES.fetch_add(1, Ordering::AcqRel);
563 if active > 0 {
564 PRIME_PIPE_OVERLAPS.fetch_add(1, Ordering::Relaxed);
565 }
566 PrimePipeStageGuard
567}
568
569impl Drop for PrimePipeStageGuard {
570 fn drop(&mut self) {
571 let active = PRIME_PIPE_ACTIVE_STAGES.fetch_sub(1, Ordering::AcqRel);
572 debug_assert!(active > 0, "prime pipeline active-stage counter underflow");
573 }
574}
575
576pub static STEP35_PRIME_BATCHES: AtomicUsize = AtomicUsize::new(0);
580pub static STEP35_PRIME_BATCH_SPLITS: AtomicUsize = AtomicUsize::new(0);
581
582pub fn step35_prime_batches() -> usize {
583 STEP35_PRIME_BATCHES.load(Ordering::Relaxed)
584}
585
586pub fn step35_prime_batch_splits() -> usize {
587 STEP35_PRIME_BATCH_SPLITS.load(Ordering::Relaxed)
588}
589
590pub fn spec_pp_on() -> bool {
598 std::env::var("MEMRA_SPEC_PP").as_deref() != Ok("0")
599}
600
601pub fn pp2_overlap() -> bool {
612 pp2_overlap_resolve(std::env::var("MEMRA_PP_OVERLAP").ok().as_deref(), dual_pp_mode())
613}
614
615pub fn pp2_overlap_resolve(v: Option<&str>, mode: DualPpMode) -> bool {
618 match v {
619 Some("1") => true,
620 Some(_) => false,
621 None => mode == DualPpMode::Auto,
622 }
623}
624
625pub fn pp_host_bounce_on() -> bool {
628 matches!(std::env::var("MEMRA_PP_HOST_BOUNCE").as_deref(), Ok("1"))
629}
630
631pub fn pp_host_bounce_active() -> bool {
634 (pp_host_bounce_on() || PEER_RUNTIME_HOST_BOUNCE.load(Ordering::Acquire))
635 && pp_sharded_cross_device()
636}
637
638pub fn pp_shard_off() -> bool {
642 matches!(std::env::var("MEMRA_PP_SHARD").as_deref(), Ok("0"))
643}
644
645fn pp2_devices_env() -> Option<String> {
648 std::env::var("MEMRA_PP_DEVICES").ok().filter(|v| !v.is_empty())
649}
650
651static WARNED_BAD: AtomicBool = AtomicBool::new(false);
652fn warn_bad_once(msg: &str) {
653 if !WARNED_BAD.swap(true, Ordering::Relaxed) {
654 eprintln!("[pp] {msg}");
655 }
656}
657
658static WARNED_UNWIRED: AtomicBool = AtomicBool::new(false);
659pub fn warn_unwired_once(path: &str) {
662 let open = std::env::var("MEMRA_PP_STAGES")
663 .map(|v| !v.is_empty() && v != "0" && v != "1")
664 .unwrap_or(false);
665 if open && !WARNED_UNWIRED.swap(true, Ordering::Relaxed) {
666 eprintln!(
667 "[pp] MEMRA_PP_STAGES set but `{path}` has no pp arm at this N; running unsplit"
668 );
669 }
670}
671
672pub struct StageRt {
680 pub dev: usize,
681 pub ctx: Arc<CudaContext>,
682 pub stream: Arc<CudaStream>,
683 engine: Option<Engine>,
685}
686
687struct BoundarySlot {
692 buf: Mutex<Option<CudaSlice<f32>>>,
693 ev_tx: CudaEvent,
696 ev_rx: CudaEvent,
700}
701
702struct BoundaryRt {
706 slots: [BoundarySlot; 2],
707 step: AtomicUsize,
708 cross: bool,
710}
711
712#[derive(Clone, Copy, Debug, PartialEq, Eq)]
713enum BoundaryTransport {
714 Local,
715 Peer,
716 HostBounce,
717}
718
719#[derive(Clone, Copy)]
720struct BoundaryPath {
721 boundary: usize,
722 src_stage: usize,
723 dst_stage: usize,
724 transport: BoundaryTransport,
725}
726
727fn boundary_transport(cross: bool, host_bounce: bool) -> BoundaryTransport {
728 match (cross, host_bounce) {
729 (false, _) => BoundaryTransport::Local,
730 (true, false) => BoundaryTransport::Peer,
731 (true, true) => BoundaryTransport::HostBounce,
732 }
733}
734
735const PEER_PROBE_FIXED_BYTES: usize = 16 * 1024;
736const PEER_PROBE_TOKEN_WIDTHS: [usize; 4] = [
737 1,
738 8,
739 16,
740 crate::cache::PRIME_CHUNK_MAX_TOKENS,
741];
742
743pub const PEER_RUNTIME_PROBE_INTERVAL_COPIES: u64 = 8 * 1024;
746pub const PEER_RUNTIME_PROBE_CYCLE_COPIES: u64 =
748 PEER_RUNTIME_PROBE_INTERVAL_COPIES * PEER_PROBE_TOKEN_WIDTHS.len() as u64;
749pub const PEER_RUNTIME_PROBE_DEFERRAL_BOUND_INTERVALS: u64 =
752 PEER_PROBE_TOKEN_WIDTHS.len() as u64;
753const PEER_RUNTIME_PROBE_BUDGET_NS: u64 = 5_000_000;
755
756pub const PEER_PROBE_REQUIRED_REFUSAL: &str =
757 "PP bring-up refused: MEMRA_PEER_PROBE=0 cannot authorize native peer transport for a \
758 sharded cross-device placement while MEMRA_PP_HOST_BOUNCE!=1; leave MEMRA_PEER_PROBE \
759 enabled or set MEMRA_PP_HOST_BOUNCE=1";
760
761#[derive(Clone, Copy, Debug, PartialEq, Eq)]
762pub enum PeerProbeStartupPolicy {
763 Allowed,
764 BypassedWithHostBounce,
765}
766
767pub fn peer_probe_startup_policy(
770 probe_on: bool,
771 sharded_cross_device: bool,
772 host_bounce: bool,
773) -> Result<PeerProbeStartupPolicy, &'static str> {
774 match (probe_on, sharded_cross_device, host_bounce) {
775 (false, true, false) => Err(PEER_PROBE_REQUIRED_REFUSAL),
776 (false, true, true) => Ok(PeerProbeStartupPolicy::BypassedWithHostBounce),
777 _ => Ok(PeerProbeStartupPolicy::Allowed),
778 }
779}
780
781static PEER_PROBE_BYPASSED: AtomicU64 = AtomicU64::new(0);
782static PEER_BOUNDARY_COPIES: AtomicU64 = AtomicU64::new(0);
783static PEER_RUNTIME_PROBES: AtomicU64 = AtomicU64::new(0);
784static PEER_RUNTIME_PROBE_FAILURES: AtomicU64 = AtomicU64::new(0);
785static PEER_RUNTIME_PROBE_DEFERRED: AtomicU64 = AtomicU64::new(0);
786static PEER_RUNTIME_PROBE_INTEGRITY_DEGRADED: AtomicBool = AtomicBool::new(false);
787static PEER_RUNTIME_PROBE_FAILED: AtomicBool = AtomicBool::new(false);
788static PEER_RUNTIME_HOST_BOUNCE: AtomicBool = AtomicBool::new(false);
789static PEER_RUNTIME_NEXT_PROBE_COPY: [AtomicU64; PEER_PROBE_TOKEN_WIDTHS.len()] = [
790 AtomicU64::new(PEER_RUNTIME_PROBE_INTERVAL_COPIES),
791 AtomicU64::new(2 * PEER_RUNTIME_PROBE_INTERVAL_COPIES),
792 AtomicU64::new(3 * PEER_RUNTIME_PROBE_INTERVAL_COPIES),
793 AtomicU64::new(4 * PEER_RUNTIME_PROBE_INTERVAL_COPIES),
794];
795static PEER_RUNTIME_PROBE_MAX_COST_NS: [AtomicU64; PEER_PROBE_TOKEN_WIDTHS.len()] = [
796 AtomicU64::new(0),
797 AtomicU64::new(0),
798 AtomicU64::new(0),
799 AtomicU64::new(0),
800];
801
802#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
803pub struct PeerProbeMetrics {
804 pub bypassed: u64,
805 pub boundary_copies: u64,
806 pub runtime_probes: u64,
807 pub runtime_failures: u64,
808 pub deferred_total: u64,
809 pub integrity_degraded: bool,
810 pub degraded_to_host_bounce: bool,
811}
812
813pub fn peer_probe_metrics() -> PeerProbeMetrics {
814 PeerProbeMetrics {
815 bypassed: PEER_PROBE_BYPASSED.load(Ordering::Relaxed),
816 boundary_copies: PEER_BOUNDARY_COPIES.load(Ordering::Relaxed),
817 runtime_probes: PEER_RUNTIME_PROBES.load(Ordering::Relaxed),
818 runtime_failures: PEER_RUNTIME_PROBE_FAILURES.load(Ordering::Relaxed),
819 deferred_total: PEER_RUNTIME_PROBE_DEFERRED.load(Ordering::Relaxed),
820 integrity_degraded: PEER_RUNTIME_PROBE_INTEGRITY_DEGRADED.load(Ordering::Acquire),
821 degraded_to_host_bounce: PEER_RUNTIME_HOST_BOUNCE.load(Ordering::Acquire),
822 }
823}
824
825#[derive(Clone, Copy, Debug, PartialEq, Eq)]
826pub enum RuntimePeerProbeStatus {
827 NotRun,
828 Deferred,
829 Passed,
830 DegradedToHostBounce,
831}
832
833impl RuntimePeerProbeStatus {
834 pub fn ran(self) -> bool {
835 matches!(self, Self::Passed | Self::DegradedToHostBounce)
836 }
837}
838
839fn publish_runtime_peer_probe_deferral(
840 deferred_total: &AtomicU64,
841 integrity_degraded: &AtomicBool,
842 intervals: u64,
843 bound_reached: bool,
844) {
845 deferred_total.fetch_add(intervals, Ordering::Relaxed);
846 if bound_reached {
847 integrity_degraded.store(true, Ordering::Release);
848 }
849}
850
851pub fn record_runtime_peer_probe_deferral(intervals: u64, bound_reached: bool) {
854 publish_runtime_peer_probe_deferral(
855 &PEER_RUNTIME_PROBE_DEFERRED,
856 &PEER_RUNTIME_PROBE_INTEGRITY_DEGRADED,
857 intervals,
858 bound_reached,
859 );
860}
861
862pub fn clear_runtime_peer_probe_integrity_degraded() {
864 PEER_RUNTIME_PROBE_INTEGRITY_DEGRADED.store(false, Ordering::Release);
865}
866
867fn runtime_peer_probe_idle_only(width_index: usize, measured_cost_ns: u64) -> bool {
868 width_index + 1 == PEER_PROBE_TOKEN_WIDTHS.len()
869 || measured_cost_ns > PEER_RUNTIME_PROBE_BUDGET_NS
870}
871
872fn runtime_peer_probe_candidate(
875 copies: u64,
876 next_probe_copy: [u64; PEER_PROBE_TOKEN_WIDTHS.len()],
877 measured_cost_ns: [u64; PEER_PROBE_TOKEN_WIDTHS.len()],
878 scheduler_idle: bool,
879) -> Option<(usize, usize)> {
880 let mut selected: Option<(usize, u64)> = None;
881 for width_index in 0..PEER_PROBE_TOKEN_WIDTHS.len() {
882 let due = next_probe_copy[width_index];
883 if copies < due
884 || (!scheduler_idle
885 && runtime_peer_probe_idle_only(width_index, measured_cost_ns[width_index]))
886 {
887 continue;
888 }
889 if selected.is_none_or(|(_, selected_due)| due < selected_due) {
890 selected = Some((width_index, due));
891 }
892 }
893 selected.map(|(width_index, _)| (width_index, PEER_PROBE_TOKEN_WIDTHS[width_index]))
894}
895
896fn runtime_peer_probe_next_copy(due: u64, copies: u64) -> u64 {
899 let cycles = copies.saturating_sub(due) / PEER_RUNTIME_PROBE_CYCLE_COPIES + 1;
900 due.saturating_add(PEER_RUNTIME_PROBE_CYCLE_COPIES.saturating_mul(cycles))
901}
902
903fn latch_runtime_host_bounce<E>(
906 native_failed: &AtomicBool,
907 degraded_to_host_bounce: &AtomicBool,
908 arm_and_validate: impl FnOnce() -> Result<(), E>,
909) -> Result<(), E> {
910 native_failed.store(true, Ordering::Release);
911 arm_and_validate()?;
912 degraded_to_host_bounce.store(true, Ordering::Release);
913 Ok(())
914}
915
916fn peer_probe_on() -> bool {
917 std::env::var("MEMRA_PEER_PROBE").as_deref() != Ok("0")
918}
919
920#[derive(Clone, Copy, Debug, PartialEq, Eq)]
921enum PeerProbeDecision {
922 Clean,
923 ProceedWithHostBounce { mismatches: usize },
924}
925
926fn peer_probe_mismatch_count(expected: &[u8], readback: &[u8]) -> usize {
927 expected
928 .iter()
929 .zip(readback)
930 .filter(|(a, b)| a != b)
931 .count()
932 + expected.len().abs_diff(readback.len())
933}
934
935fn peer_probe_decision(
936 expected: &[u8],
937 readback: &[u8],
938 host_bounce: bool,
939) -> Result<PeerProbeDecision, String> {
940 let mismatches = peer_probe_mismatch_count(expected, readback);
941 if mismatches == 0 {
942 Ok(PeerProbeDecision::Clean)
943 } else if host_bounce {
944 Ok(PeerProbeDecision::ProceedWithHostBounce { mismatches })
945 } else {
946 Err(format!("{mismatches} mismatched byte(s)"))
947 }
948}
949
950fn peer_probe_pattern(bytes: usize, boundary: usize, src_dev: usize, dst_dev: usize) -> Vec<u8> {
951 let mut state = 0xD1B5_4A32_D192_ED03u64
952 ^ (bytes as u64).rotate_left(7)
953 ^ (boundary as u64).rotate_left(19)
954 ^ (src_dev as u64).rotate_left(31)
955 ^ (dst_dev as u64).rotate_left(43);
956 (0..bytes)
957 .map(|_| {
958 state ^= state << 13;
959 state ^= state >> 7;
960 state ^= state << 17;
961 state as u8
962 })
963 .collect()
964}
965
966fn peer_probe_bytes_to_f32(bytes: &[u8]) -> Vec<f32> {
967 assert_eq!(bytes.len() % std::mem::size_of::<f32>(), 0);
968 bytes
969 .chunks_exact(std::mem::size_of::<f32>())
970 .map(|chunk| f32::from_bits(u32::from_ne_bytes(chunk.try_into().unwrap())))
971 .collect()
972}
973
974fn peer_probe_f32_to_bytes(values: &[f32]) -> Vec<u8> {
975 values
976 .iter()
977 .flat_map(|value| value.to_bits().to_ne_bytes())
978 .collect()
979}
980
981struct PeerProbeBuffer {
985 ctx: Arc<CudaContext>,
986 ptr: cudarc::driver::sys::CUdeviceptr,
987}
988
989impl PeerProbeBuffer {
990 fn new(ctx: &Arc<CudaContext>, bytes: usize) -> Result<Self, Box<dyn std::error::Error>> {
991 ctx.bind_to_thread()?;
992 let ptr = unsafe { cudarc::driver::result::malloc_sync(bytes)? };
993 Ok(Self { ctx: ctx.clone(), ptr })
994 }
995}
996
997impl Drop for PeerProbeBuffer {
998 fn drop(&mut self) {
999 if self.ctx.bind_to_thread().is_ok() {
1000 let _ = unsafe { cudarc::driver::result::free_sync(self.ptr) };
1001 }
1002 }
1003}
1004
1005fn peer_probe_copy(
1006 src: &StageRt,
1007 dst: &StageRt,
1008 expected: &[u8],
1009) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
1010 let bytes = expected.len();
1011 let src_buf = PeerProbeBuffer::new(&src.ctx, bytes)?;
1012 unsafe {
1013 cudarc::driver::result::memcpy_htod_sync(src_buf.ptr, expected)?;
1014 }
1015
1016 let dst_buf = PeerProbeBuffer::new(&dst.ctx, bytes)?;
1017 let poison: Vec<u8> = expected.iter().map(|b| !b).collect();
1018 unsafe {
1019 cudarc::driver::result::memcpy_htod_sync(dst_buf.ptr, &poison)?;
1020 }
1021
1022 src.ctx.bind_to_thread()?;
1023 unsafe {
1024 cudarc::driver::result::memcpy_peer_async(
1025 dst.ctx.cu_ctx(),
1026 dst_buf.ptr,
1027 src.ctx.cu_ctx(),
1028 src_buf.ptr,
1029 bytes,
1030 src.stream.cu_stream(),
1031 )?;
1032 }
1033 src.stream.synchronize()?;
1034
1035 dst.ctx.bind_to_thread()?;
1036 let mut readback = vec![0u8; bytes];
1037 unsafe {
1038 cudarc::driver::result::memcpy_dtoh_sync(&mut readback, dst_buf.ptr)?;
1039 }
1040 Ok(readback)
1041}
1042
1043fn run_peer_probe_pass(
1044 stages: &[StageRt],
1045 peer_capable: &[(usize, usize)],
1046 host_bounce: bool,
1047 label: &str,
1048 bytes: usize,
1049) -> Result<(), Box<dyn std::error::Error>> {
1050 if bytes == 0 {
1051 return Err(format!("PP peer byte-integrity probe {label} size is zero").into());
1052 }
1053 let started = std::time::Instant::now();
1054 let mut copies = 0usize;
1055 let mut skipped = 0usize;
1056 let mut total_mismatches = 0usize;
1057
1058 for boundary in 0..stages.len() - 1 {
1059 if stages[boundary].dev == stages[boundary + 1].dev {
1060 continue;
1061 }
1062 for (src_idx, dst_idx) in [(boundary, boundary + 1), (boundary + 1, boundary)] {
1063 let src = &stages[src_idx];
1064 let dst = &stages[dst_idx];
1065 if !peer_capable.contains(&(src.dev, dst.dev)) {
1066 if host_bounce {
1067 skipped += 1;
1068 eprintln!(
1069 "[pp] peer byte-integrity probe SKIP: boundary={boundary} \
1070 dev{}->dev{} label={label} bytes={bytes} (peer capability unavailable; \
1071 MEMRA_PP_HOST_BOUNCE=1 remains fail-safe)",
1072 src.dev, dst.dev,
1073 );
1074 continue;
1075 }
1076 return Err(format!(
1077 "PP peer byte-integrity probe cannot run boundary={boundary} \
1078 dev{}->dev{}: peer access was not enabled",
1079 src.dev, dst.dev,
1080 )
1081 .into());
1082 }
1083
1084 let expected = peer_probe_pattern(bytes, boundary, src.dev, dst.dev);
1085 let readback = match peer_probe_copy(src, dst, &expected) {
1086 Ok(readback) => readback,
1087 Err(err) if host_bounce => {
1088 skipped += 1;
1089 eprintln!(
1090 "[pp] peer byte-integrity probe ERROR: boundary={boundary} \
1091 dev{}->dev{} label={label} bytes={bytes}: {err}; \
1092 MEMRA_PP_HOST_BOUNCE=1, proceeding on the host-staged path",
1093 src.dev, dst.dev,
1094 );
1095 continue;
1096 }
1097 Err(err) => {
1098 return Err(format!(
1099 "PP peer byte-integrity probe FAILED: boundary={boundary} \
1100 dev{}->dev{} label={label} bytes={bytes}: {err}; refusing native P2P \
1101 (set MEMRA_PP_HOST_BOUNCE=1 to use the host-staged path; \
1102 MEMRA_PEER_PROBE=0 cannot authorize sharded native peer transport)",
1103 src.dev, dst.dev,
1104 )
1105 .into());
1106 }
1107 };
1108 copies += 1;
1109 match peer_probe_decision(&expected, &readback, host_bounce) {
1110 Ok(PeerProbeDecision::Clean) => {}
1111 Ok(PeerProbeDecision::ProceedWithHostBounce { mismatches }) => {
1112 total_mismatches += mismatches;
1113 eprintln!(
1114 "[pp] peer byte-integrity probe CORRUPTION: boundary={boundary} \
1115 dev{}->dev{} label={label} bytes={bytes} mismatches={mismatches}; \
1116 MEMRA_PP_HOST_BOUNCE=1, proceeding on the host-staged path",
1117 src.dev, dst.dev,
1118 );
1119 }
1120 Err(mismatch) => {
1121 return Err(format!(
1122 "PP peer byte-integrity probe FAILED: boundary={boundary} \
1123 dev{}->dev{} label={label} bytes={bytes}: {mismatch}; refusing native \
1124 P2P (set MEMRA_PP_HOST_BOUNCE=1 to use the host-staged path; \
1125 MEMRA_PEER_PROBE=0 cannot authorize sharded native peer transport)",
1126 src.dev, dst.dev,
1127 )
1128 .into());
1129 }
1130 }
1131 }
1132 }
1133
1134 let status = if total_mismatches > 0 {
1135 "BOUNCE"
1136 } else if skipped > 0 && copies > 0 {
1137 "PARTIAL"
1138 } else if skipped > 0 {
1139 "SKIP"
1140 } else {
1141 "PASS"
1142 };
1143 eprintln!(
1144 "[pp] peer byte-integrity probe {}: label={label} bytes={bytes} copies={copies} \
1145 skipped={skipped} mismatches={total_mismatches} elapsed_ms={:.3}",
1146 status,
1147 started.elapsed().as_secs_f64() * 1e3,
1148 );
1149 Ok(())
1150}
1151
1152fn host_bounce_capacity(n_embd: usize) -> Result<(usize, usize), String> {
1153 if n_embd == 0 {
1154 return Err("MEMRA_PP_HOST_BOUNCE needs non-zero model n_embd".into());
1155 }
1156 let elems = n_embd
1157 .checked_mul(crate::cache::PRIME_CHUNK_MAX_TOKENS)
1158 .ok_or_else(|| format!("host-bounce element count overflows for n_embd={n_embd}"))?;
1159 let bytes = elems
1160 .checked_mul(std::mem::size_of::<f32>())
1161 .ok_or_else(|| format!("host-bounce byte count overflows for n_embd={n_embd}"))?;
1162 Ok((elems, bytes))
1163}
1164
1165struct PinnedHostBounce {
1170 ptr: *mut f32,
1171 len: usize,
1172}
1173
1174unsafe impl Send for PinnedHostBounce {}
1175unsafe impl Sync for PinnedHostBounce {}
1176
1177impl PinnedHostBounce {
1178 fn new(len: usize) -> Result<Self, Box<dyn std::error::Error>> {
1179 let bytes = len
1180 .checked_mul(std::mem::size_of::<f32>())
1181 .ok_or("host-bounce pinned allocation size overflow")?;
1182 let ptr = unsafe {
1183 cudarc::driver::result::malloc_host(
1184 bytes,
1185 cudarc::driver::sys::CU_MEMHOSTALLOC_PORTABLE,
1186 )?
1187 } as *mut f32;
1188 if ptr.is_null() {
1189 return Err("cuMemHostAlloc returned a null host-bounce pointer".into());
1190 }
1191 Ok(Self { ptr, len })
1192 }
1193
1194 fn prefix(&self, n: usize) -> &[f32] {
1195 assert!(n <= self.len, "host-bounce source {n} > capacity {}", self.len);
1196 unsafe { std::slice::from_raw_parts(self.ptr, n) }
1197 }
1198
1199 fn prefix_mut(&mut self, n: usize) -> &mut [f32] {
1200 assert!(n <= self.len, "host-bounce destination {n} > capacity {}", self.len);
1201 unsafe { std::slice::from_raw_parts_mut(self.ptr, n) }
1202 }
1203}
1204
1205impl Drop for PinnedHostBounce {
1206 fn drop(&mut self) {
1207 let _ = unsafe { cudarc::driver::result::free_host(self.ptr.cast()) };
1208 }
1209}
1210
1211struct HostBounceRt {
1212 n_embd: usize,
1213 capacity: usize,
1214 slots: Vec<Option<[Mutex<PinnedHostBounce>; 2]>>,
1215}
1216
1217impl HostBounceRt {
1218 fn new(n_embd: usize, boundaries: &[BoundaryRt]) -> Result<Self, Box<dyn std::error::Error>> {
1219 let (capacity, _) = host_bounce_capacity(n_embd)?;
1220 let mut slots = Vec::with_capacity(boundaries.len());
1221 for boundary in boundaries {
1222 slots.push(if boundary.cross {
1223 Some([
1224 Mutex::new(PinnedHostBounce::new(capacity)?),
1225 Mutex::new(PinnedHostBounce::new(capacity)?),
1226 ])
1227 } else {
1228 None
1229 });
1230 }
1231 Ok(Self { n_embd, capacity, slots })
1232 }
1233
1234 fn slot(
1235 &self,
1236 boundary: usize,
1237 slot: usize,
1238 ) -> Result<&Mutex<PinnedHostBounce>, Box<dyn std::error::Error>> {
1239 self.slots
1240 .get(boundary)
1241 .and_then(Option::as_ref)
1242 .and_then(|slots| slots.get(slot))
1243 .ok_or_else(|| format!("host-bounce slot {boundary}:{slot} is not initialized").into())
1244 }
1245}
1246
1247pub struct PpNRt {
1248 stages: Vec<StageRt>,
1249 boundaries: Vec<BoundaryRt>,
1250 cross_any: bool,
1252 host_bounce: bool,
1255 peer_probe: bool,
1257 peer_capable: Vec<(usize, usize)>,
1259 peer_probe_geometry: OnceLock<Result<usize, String>>,
1261 bounce: OnceLock<Result<HostBounceRt, String>>,
1263 readback: Arc<CudaStream>,
1266}
1267
1268pub type Pp2Rt = PpNRt;
1270
1271static RTN: OnceLock<Result<PpNRt, String>> = OnceLock::new();
1272
1273impl PpNRt {
1274 pub fn get(e: &Engine) -> Result<&'static PpNRt, Box<dyn std::error::Error>> {
1278 RTN.get_or_init(|| Self::build(e).map_err(|err| err.to_string()))
1279 .as_ref()
1280 .map_err(|s| -> Box<dyn std::error::Error> { s.clone().into() })
1281 }
1282
1283 fn build(e: &Engine) -> Result<PpNRt, Box<dyn std::error::Error>> {
1284 let primary_dev = e.ctx().ordinal();
1285 let devices: Vec<usize> = match pp2_devices_env() {
1288 Some(s) => {
1289 let parts: Result<Vec<usize>, _> =
1290 s.split(',').map(|p| p.trim().parse::<usize>()).collect();
1291 match parts {
1292 Ok(v) if v.len() >= 2 => v,
1293 _ => {
1294 return Err(format!(
1295 "MEMRA_PP_DEVICES={s} unparseable (want <d0>,..,<dN-1> e.g. 0,1,2,3)"
1296 )
1297 .into())
1298 }
1299 }
1300 }
1301 None => {
1302 let n_st = std::env::var("MEMRA_PP_STAGES")
1303 .ok()
1304 .and_then(|v| v.parse::<usize>().ok())
1305 .filter(|&n| n >= 2)
1306 .unwrap_or(2);
1307 vec![primary_dev; n_st]
1308 }
1309 };
1310 if let Ok(v) = std::env::var("MEMRA_PP_STAGES") {
1311 if let Ok(n) = v.parse::<usize>() {
1312 if n >= 2 && n != devices.len() {
1313 return Err(format!(
1314 "MEMRA_PP_DEVICES lists {} devices but MEMRA_PP_STAGES={n} — \
1315 refusing an ambiguous placement",
1316 devices.len()
1317 )
1318 .into());
1319 }
1320 }
1321 }
1322 let n_st = devices.len();
1323 let cross_any = devices.iter().any(|&d| d != devices[0]);
1324 let host_bounce = pp_host_bounce_on();
1325 let peer_probe = peer_probe_on();
1326 let sharded_cross_device = cross_any && !pp_shard_off();
1327 if host_bounce && cross_any {
1328 if pp_shard_off() {
1329 return Err(
1330 "MEMRA_PP_HOST_BOUNCE=1 refuses MEMRA_PP_SHARD=0: the boundary can bounce, \
1331 but remote stages would still peer-read primary-device weights"
1332 .into(),
1333 );
1334 }
1335 if devices.last().copied() != Some(primary_dev) {
1336 return Err(format!(
1337 "MEMRA_PP_HOST_BOUNCE=1 requires the primary engine on the last/head stage \
1338 (primary dev{primary_dev}, placement {devices:?}); otherwise returned \
1339 logits/hidden state remain peer reads"
1340 )
1341 .into());
1342 }
1343 }
1344 let peer_probe_policy =
1345 peer_probe_startup_policy(peer_probe, sharded_cross_device, host_bounce)?;
1346 if peer_probe_policy == PeerProbeStartupPolicy::BypassedWithHostBounce {
1347 PEER_PROBE_BYPASSED.fetch_add(1, Ordering::Relaxed);
1348 eprintln!(
1349 "[pp] SECURITY RED: peer_probe_bypassed: MEMRA_PEER_PROBE=0 on a sharded \
1350 cross-device placement; MEMRA_PP_HOST_BOUNCE=1 is the only enabled transport"
1351 );
1352 }
1353
1354 let mut used: Vec<usize> = devices.clone();
1358 used.push(primary_dev);
1359 used.sort_unstable();
1360 used.dedup();
1361 let mut peer_capable = Vec::new();
1362 if used.len() > 1 {
1363 let n = cudarc::driver::result::device::get_count()? as usize;
1364 for &d in &used {
1365 if d >= n {
1366 return Err(format!(
1367 "MEMRA_PP_DEVICES={devices:?} but only {n} CUDA device(s) present"
1368 )
1369 .into());
1370 }
1371 }
1372 if !host_bounce || peer_probe {
1373 for &a in &used {
1374 for &b in &used {
1375 if a == b {
1376 continue;
1377 }
1378 let da = cudarc::driver::result::device::get(a as i32)?;
1379 let db = cudarc::driver::result::device::get(b as i32)?;
1380 let mut can: i32 = 0;
1381 let capability = unsafe {
1382 cudarc::driver::sys::cuDeviceCanAccessPeer(&mut can, da, db).result()
1383 };
1384 if let Err(err) = capability {
1385 if host_bounce {
1386 eprintln!(
1387 "[pp] peer byte-integrity probe capability query failed for \
1388 dev{a}->dev{b}: {err}; MEMRA_PP_HOST_BOUNCE=1 remains active"
1389 );
1390 continue;
1391 }
1392 return Err(err.into());
1393 }
1394 if can == 0 {
1395 if !host_bounce {
1396 return Err(format!(
1397 "device {a} cannot peer-access device {b} \
1398 (cuDeviceCanAccessPeer=0); ppN cross-device needs P2P — \
1399 refusing a silently-staged path"
1400 )
1401 .into());
1402 }
1403 } else {
1404 peer_capable.push((a, b));
1405 }
1406 }
1407 }
1408 }
1409 }
1410
1411 let mk_stage = |dev: usize, s: usize| -> Result<StageRt, Box<dyn std::error::Error>> {
1424 if dev == primary_dev && s == 0 {
1425 let ctx = e.ctx().clone();
1426 let stream = ctx.new_stream()?;
1427 Ok(StageRt { dev, ctx, stream, engine: None })
1428 } else {
1429 let eng = Engine::new(dev)?;
1430 let ctx = eng.ctx().clone();
1431 let stream = ctx.new_stream()?;
1432 Ok(StageRt { dev, ctx, stream, engine: Some(eng) })
1433 }
1434 };
1435 let mut stages = Vec::with_capacity(n_st);
1436 for (s, &d) in devices.iter().enumerate() {
1437 stages.push(mk_stage(d, s)?);
1438 }
1439
1440 if cross_any
1441 && !peer_probe
1442 && peer_probe_policy != PeerProbeStartupPolicy::BypassedWithHostBounce
1443 {
1444 eprintln!(
1445 "[pp] WARNING: MEMRA_PEER_PROBE=0 skips the boot-time peer byte-integrity \
1446 gate; diagnostics escape hatch active"
1447 );
1448 }
1449
1450 if used.len() > 1 {
1451 if !host_bounce {
1452 let ctx_of = |d: usize| -> &Arc<CudaContext> {
1455 if d == primary_dev {
1456 e.ctx()
1457 } else {
1458 &stages.iter().find(|s| s.dev == d).unwrap().ctx
1459 }
1460 };
1461 for &a in &used {
1464 for &b in &used {
1465 if a == b {
1466 continue;
1467 }
1468 ctx_of(a).bind_to_thread()?;
1469 let rc = unsafe {
1470 cudarc::driver::sys::cuCtxEnablePeerAccess(ctx_of(b).cu_ctx(), 0)
1471 };
1472 use cudarc::driver::sys::cudaError_enum as E;
1473 if rc != E::CUDA_SUCCESS && rc != E::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED {
1474 return Err(format!(
1475 "cuCtxEnablePeerAccess(dev{a} -> dev{b}) failed: {rc:?}"
1476 )
1477 .into());
1478 }
1479 }
1480 }
1481 if peer_probe && cross_any {
1485 let probe = run_peer_probe_pass(
1486 &stages,
1487 &peer_capable,
1488 host_bounce,
1489 "fixed-16KiB",
1490 PEER_PROBE_FIXED_BYTES,
1491 );
1492 e.ctx().bind_to_thread()?;
1493 probe?;
1494 }
1495 for &owner in &used {
1504 for &accessor in &used {
1505 if owner == accessor {
1506 continue;
1507 }
1508 let dev = cudarc::driver::result::device::get(owner as i32)?;
1509 let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
1510 unsafe {
1511 cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, dev).result()?;
1512 }
1513 let desc = cudarc::driver::sys::CUmemAccessDesc {
1514 location: cudarc::driver::sys::CUmemLocation {
1515 type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
1516 id: accessor as i32,
1517 },
1518 flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
1519 };
1520 let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
1521 if rc != cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
1522 return Err(format!(
1523 "cuMemPoolSetAccess(dev{owner} pool -> dev{accessor}) failed: {rc:?}"
1524 )
1525 .into());
1526 }
1527 }
1528 }
1529 for (owner, accessor) in [(stages[0].dev, stages[1].dev), (stages[1].dev, stages[0].dev)] {
1538 let dev = cudarc::driver::result::device::get(owner as i32)?;
1539 let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
1540 unsafe {
1541 cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, dev).result()?;
1542 }
1543 let desc = cudarc::driver::sys::CUmemAccessDesc {
1544 location: cudarc::driver::sys::CUmemLocation {
1545 type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
1546 id: accessor as i32,
1547 },
1548 flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
1549 };
1550 let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
1551 if rc != cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
1552 return Err(format!(
1553 "cuMemPoolSetAccess(dev{owner} pool -> dev{accessor}) failed: {rc:?}"
1554 )
1555 .into());
1556 }
1557 }
1558 e.ctx().bind_to_thread()?;
1560 eprintln!(
1561 "[pp] cross-device transport: {} (cudaMemcpyPeerAsync per cross boundary; \
1562 peer + default-pool access granted all pairs over {used:?}; weight home: {})",
1563 devices
1564 .iter()
1565 .enumerate()
1566 .map(|(s, d)| format!("stage{s}=dev{d}"))
1567 .collect::<Vec<_>>()
1568 .join(" "),
1569 if pp_shard_off() {
1570 format!("dev{primary_dev} (MEMRA_PP_SHARD=0 bring-up placement)")
1571 } else {
1572 "per-stage (sharded loader)".to_string()
1573 }
1574 );
1575 } else {
1576 e.ctx().bind_to_thread()?;
1577 eprintln!(
1578 "[pp] cross-device transport: {} (HOST-STAGED pinned D2H -> H2D per cross \
1579 boundary; MEMRA_PP_HOST_BOUNCE=1; peer-pool grants bypassed; \
1580 diagnostic peer access is removed before host-staged serving; \
1581 weight home: per-stage (sharded loader))",
1582 devices
1583 .iter()
1584 .enumerate()
1585 .map(|(s, d)| format!("stage{s}=dev{d}"))
1586 .collect::<Vec<_>>()
1587 .join(" "),
1588 );
1589 }
1590 }
1591
1592 let mk_slot = |tx: &StageRt, rx: &StageRt| -> Result<BoundarySlot, Box<dyn std::error::Error>> {
1593 Ok(BoundarySlot {
1594 buf: Mutex::new(None),
1595 ev_tx: tx.ctx.new_event(None)?,
1596 ev_rx: rx.ctx.new_event(None)?,
1597 })
1598 };
1599 let mut boundaries = Vec::with_capacity(n_st - 1);
1600 for b in 0..n_st - 1 {
1601 let (tx, rx) = (&stages[b], &stages[b + 1]);
1602 boundaries.push(BoundaryRt {
1603 slots: [mk_slot(tx, rx)?, mk_slot(tx, rx)?],
1604 step: AtomicUsize::new(0),
1605 cross: tx.dev != rx.dev,
1606 });
1607 }
1608 let readback = stages[n_st - 1].ctx.new_stream()?;
1609 let rt = PpNRt {
1610 stages,
1611 boundaries,
1612 cross_any,
1613 host_bounce,
1614 peer_probe,
1615 peer_capable,
1616 peer_probe_geometry: OnceLock::new(),
1617 bounce: OnceLock::new(),
1618 readback,
1619 };
1620 if rt.peer_probe && rt.cross_any && rt.host_bounce {
1621 rt.run_host_bounce_legacy_probe(e)?;
1622 }
1623 Ok(rt)
1624 }
1625
1626 pub fn n_stages(&self) -> usize {
1627 self.stages.len()
1628 }
1629
1630 pub fn cross_device(&self) -> bool {
1632 self.cross_any
1633 }
1634
1635 fn host_bounce_active(&self) -> bool {
1636 self.host_bounce || PEER_RUNTIME_HOST_BOUNCE.load(Ordering::Acquire)
1637 }
1638
1639 fn context_for_dev<'a>(
1640 &'a self,
1641 e: &'a Engine,
1642 dev: usize,
1643 ) -> Result<&'a Arc<CudaContext>, Box<dyn std::error::Error>> {
1644 if dev == e.ctx().ordinal() {
1645 return Ok(e.ctx());
1646 }
1647 self.stages
1648 .iter()
1649 .find(|stage| stage.dev == dev)
1650 .map(|stage| &stage.ctx)
1651 .ok_or_else(|| format!("PP peer probe has no CUDA context for dev{dev}").into())
1652 }
1653
1654 fn enable_probe_peer_access(
1655 &self,
1656 e: &Engine,
1657 pairs: &[(usize, usize)],
1658 ) -> Result<Vec<(usize, usize)>, Box<dyn std::error::Error>> {
1659 let mut enabled = Vec::new();
1660 for &(src_dev, dst_dev) in pairs {
1661 let enable = (|| -> Result<(), Box<dyn std::error::Error>> {
1662 let src_ctx = self.context_for_dev(e, src_dev)?;
1663 let dst_ctx = self.context_for_dev(e, dst_dev)?;
1664 src_ctx.bind_to_thread()?;
1665 let rc = unsafe { cudarc::driver::sys::cuCtxEnablePeerAccess(dst_ctx.cu_ctx(), 0) };
1666 use cudarc::driver::sys::cudaError_enum as E;
1667 if rc == E::CUDA_SUCCESS || rc == E::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED {
1668 Ok(())
1669 } else {
1670 Err(format!("{rc:?}").into())
1671 }
1672 })();
1673 if let Err(err) = enable {
1674 eprintln!(
1675 "[pp] peer byte-integrity probe could not enable \
1676 dev{src_dev}->dev{dst_dev}: {err}; MEMRA_PP_HOST_BOUNCE=1 remains active"
1677 );
1678 } else {
1679 enabled.push((src_dev, dst_dev));
1680 }
1681 }
1682 Ok(enabled)
1683 }
1684
1685 fn disable_probe_peer_access(
1686 &self,
1687 e: &Engine,
1688 pairs: &[(usize, usize)],
1689 ) -> Result<(), Box<dyn std::error::Error>> {
1690 let mut failures = Vec::new();
1691 for &(src_dev, dst_dev) in pairs {
1692 let disable = (|| -> Result<(), Box<dyn std::error::Error>> {
1693 let src_ctx = self.context_for_dev(e, src_dev)?;
1694 let dst_ctx = self.context_for_dev(e, dst_dev)?;
1695 src_ctx.bind_to_thread()?;
1696 let rc = unsafe { cudarc::driver::sys::cuCtxDisablePeerAccess(dst_ctx.cu_ctx()) };
1697 use cudarc::driver::sys::cudaError_enum as E;
1698 if rc == E::CUDA_SUCCESS || rc == E::CUDA_ERROR_PEER_ACCESS_NOT_ENABLED {
1699 Ok(())
1700 } else {
1701 Err(format!("{rc:?}").into())
1702 }
1703 })();
1704 if let Err(err) = disable {
1705 failures.push(format!("dev{src_dev}->dev{dst_dev}: {err}"));
1706 }
1707 }
1708 e.ctx().bind_to_thread()?;
1709 if failures.is_empty() {
1710 eprintln!(
1711 "[pp] peer byte-integrity probe teardown: disabled {} diagnostic pair(s); \
1712 host-bounce serving has no probe-enabled peer access",
1713 pairs.len(),
1714 );
1715 Ok(())
1716 } else {
1717 Err(format!(
1718 "PP peer probe could not disable diagnostic peer access ({}); \
1719 refusing host-bounce serving",
1720 failures.join(", "),
1721 )
1722 .into())
1723 }
1724 }
1725
1726 fn grant_probe_pool_access(
1727 &self,
1728 e: &Engine,
1729 pairs: &[(usize, usize)],
1730 ) -> Result<Vec<(usize, usize)>, Box<dyn std::error::Error>> {
1731 let mut granted = Vec::new();
1732 for &(src_dev, dst_dev) in pairs {
1733 let grant = (|| -> Result<(), Box<dyn std::error::Error>> {
1734 self.context_for_dev(e, dst_dev)?.bind_to_thread()?;
1735 let dev = cudarc::driver::result::device::get(dst_dev as i32)?;
1736 let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
1737 unsafe {
1738 cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, dev).result()?;
1739 }
1740 let desc = cudarc::driver::sys::CUmemAccessDesc {
1741 location: cudarc::driver::sys::CUmemLocation {
1742 type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
1743 id: src_dev as i32,
1744 },
1745 flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
1746 };
1747 let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
1748 if rc == cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
1749 Ok(())
1750 } else {
1751 Err(format!("{rc:?}").into())
1752 }
1753 })();
1754 if let Err(err) = grant {
1755 eprintln!(
1756 "[pp] production-slot probe could not grant dev{src_dev} access to \
1757 dev{dst_dev}'s default pool: {err}; MEMRA_PP_HOST_BOUNCE=1 remains active"
1758 );
1759 } else {
1760 granted.push((src_dev, dst_dev));
1761 }
1762 }
1763 Ok(granted)
1764 }
1765
1766 fn revoke_probe_pool_access(
1767 &self,
1768 e: &Engine,
1769 pairs: &[(usize, usize)],
1770 ) -> Result<(), Box<dyn std::error::Error>> {
1771 let mut failures = Vec::new();
1772 for &(src_dev, dst_dev) in pairs {
1773 let revoke = (|| -> Result<(), Box<dyn std::error::Error>> {
1774 self.context_for_dev(e, dst_dev)?.bind_to_thread()?;
1775 let dev = cudarc::driver::result::device::get(dst_dev as i32)?;
1776 let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
1777 unsafe {
1778 cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, dev).result()?;
1779 }
1780 let desc = cudarc::driver::sys::CUmemAccessDesc {
1781 location: cudarc::driver::sys::CUmemLocation {
1782 type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
1783 id: src_dev as i32,
1784 },
1785 flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_NONE,
1786 };
1787 let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
1788 if rc == cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
1789 Ok(())
1790 } else {
1791 Err(format!("{rc:?}").into())
1792 }
1793 })();
1794 if let Err(err) = revoke {
1795 failures.push(format!("dev{src_dev}->dev{dst_dev}: {err}"));
1796 }
1797 }
1798 e.ctx().bind_to_thread()?;
1799 if failures.is_empty() {
1800 Ok(())
1801 } else {
1802 Err(format!(
1803 "PP peer probe could not revoke diagnostic pool access ({}); \
1804 refusing host-bounce serving",
1805 failures.join(", "),
1806 )
1807 .into())
1808 }
1809 }
1810
1811 fn run_host_bounce_legacy_probe(
1812 &self,
1813 e: &Engine,
1814 ) -> Result<(), Box<dyn std::error::Error>> {
1815 let enabled = self.enable_probe_peer_access(e, &self.peer_capable)?;
1816 let probe = run_peer_probe_pass(
1817 &self.stages,
1818 &enabled,
1819 true,
1820 "fixed-16KiB-legacy-preflight",
1821 PEER_PROBE_FIXED_BYTES,
1822 );
1823 let disable = self.disable_probe_peer_access(e, &enabled);
1824 disable?;
1825 probe
1826 }
1827
1828 fn new_peer_probe_boundary(
1829 &self,
1830 src_stage: usize,
1831 dst_stage: usize,
1832 ) -> Result<BoundaryRt, Box<dyn std::error::Error>> {
1833 let tx = &self.stages[src_stage];
1834 let rx = &self.stages[dst_stage];
1835 let mk_slot = || -> Result<BoundarySlot, Box<dyn std::error::Error>> {
1836 Ok(BoundarySlot {
1837 buf: Mutex::new(None),
1838 ev_tx: tx.ctx.new_event(None)?,
1839 ev_rx: rx.ctx.new_event(None)?,
1840 })
1841 };
1842 Ok(BoundaryRt {
1843 slots: [mk_slot()?, mk_slot()?],
1844 step: AtomicUsize::new(0),
1845 cross: tx.dev != rx.dev,
1846 })
1847 }
1848
1849 fn production_probe_readback(
1850 &self,
1851 path: BoundaryPath,
1852 boundary: &BoundaryRt,
1853 expected: &[u8],
1854 n: usize,
1855 slot_idx: usize,
1856 ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
1857 debug_assert_eq!(expected.len(), n * std::mem::size_of::<f32>());
1858 let host = peer_probe_bytes_to_f32(expected);
1859 let poison_bytes: Vec<u8> = expected.iter().map(|byte| !byte).collect();
1860 let poison = peer_probe_bytes_to_f32(&poison_bytes);
1861 let src = &self.stages[path.src_stage];
1862 let dst = &self.stages[path.dst_stage];
1863
1864 dst.ctx.bind_to_thread()?;
1867 let poison_buf = dst.stream.clone_htod(&poison)?;
1868 dst.stream.synchronize()?;
1869 let replaced = boundary.slots[slot_idx].buf.lock().unwrap().replace(poison_buf);
1870 drop(replaced);
1871 dst.stream.synchronize()?;
1872
1873 src.ctx.bind_to_thread()?;
1874 let x = src.stream.clone_htod(&host)?;
1875 self.tx_slot_path(path, boundary, &x, n, slot_idx)?;
1876
1877 dst.ctx.bind_to_thread()?;
1878 let work = self.rx_slot_path(path, boundary, slot_idx, n)?;
1879 let back = dst.stream.clone_dtoh(&work)?;
1880 dst.stream.synchronize()?;
1881 Ok(peer_probe_f32_to_bytes(&back))
1882 }
1883
1884 fn clear_peer_probe_boundary(
1885 &self,
1886 boundary: &BoundaryRt,
1887 src_stage: usize,
1888 dst_stage: usize,
1889 ) -> Result<(), Box<dyn std::error::Error>> {
1890 self.stages[dst_stage].ctx.bind_to_thread()?;
1891 for slot in &boundary.slots {
1892 let buffer = slot.buf.lock().unwrap().take();
1893 drop(buffer);
1894 }
1895 self.stages[src_stage].stream.synchronize()?;
1896 self.stages[dst_stage].stream.synchronize()?;
1897 Ok(())
1898 }
1899
1900 fn run_production_peer_probe(
1901 &self,
1902 enabled_pairs: &[(usize, usize)],
1903 host_bounce: bool,
1904 n_embd: usize,
1905 ) -> Result<(), Box<dyn std::error::Error>> {
1906 let started = std::time::Instant::now();
1907 let mut copies = 0usize;
1908 let mut skipped = 0usize;
1909 let mut total_mismatches = 0usize;
1910 let mut largest_clean_payload = 0usize;
1911
1912 for boundary_idx in 0..self.stages.len() - 1 {
1913 if self.stages[boundary_idx].dev == self.stages[boundary_idx + 1].dev {
1914 continue;
1915 }
1916 for (src_stage, dst_stage) in [
1917 (boundary_idx, boundary_idx + 1),
1918 (boundary_idx + 1, boundary_idx),
1919 ] {
1920 let src_dev = self.stages[src_stage].dev;
1921 let dst_dev = self.stages[dst_stage].dev;
1922 if !enabled_pairs.contains(&(src_dev, dst_dev)) {
1923 if host_bounce {
1924 skipped += PEER_PROBE_TOKEN_WIDTHS.len();
1925 eprintln!(
1926 "[pp] production-slot peer probe SKIP: boundary={boundary_idx} \
1927 dev{src_dev}->dev{dst_dev} widths_tokens={:?} \
1928 (peer or pool access unavailable; MEMRA_PP_HOST_BOUNCE=1 remains \
1929 fail-safe)",
1930 PEER_PROBE_TOKEN_WIDTHS,
1931 );
1932 continue;
1933 }
1934 return Err(format!(
1935 "PP production-slot peer probe cannot run boundary={boundary_idx} \
1936 dev{src_dev}->dev{dst_dev}: peer/pool access is not enabled"
1937 )
1938 .into());
1939 }
1940
1941 let probe_boundary = self.new_peer_probe_boundary(src_stage, dst_stage)?;
1942 let path = BoundaryPath {
1943 boundary: boundary_idx,
1944 src_stage,
1945 dst_stage,
1946 transport: BoundaryTransport::Peer,
1947 };
1948 let mut direction_copies = 0usize;
1949 let mut direction_skipped = 0usize;
1950 let mut direction_mismatches = 0usize;
1951 let mut direction_largest_clean = 0usize;
1952 let mut failure = None;
1953
1954 for (width_idx, tokens) in PEER_PROBE_TOKEN_WIDTHS.into_iter().enumerate() {
1955 let n = n_embd.checked_mul(tokens).ok_or_else(|| {
1956 format!(
1957 "PP production-slot probe element count overflows for \
1958 n_embd={n_embd} tokens={tokens}"
1959 )
1960 })?;
1961 let bytes = n.checked_mul(std::mem::size_of::<f32>()).ok_or_else(|| {
1962 format!(
1963 "PP production-slot probe byte count overflows for \
1964 n_embd={n_embd} tokens={tokens}"
1965 )
1966 })?;
1967 let expected = peer_probe_pattern(
1968 bytes,
1969 boundary_idx,
1970 src_dev,
1971 dst_dev,
1972 );
1973 let readback = match self.production_probe_readback(
1974 path,
1975 &probe_boundary,
1976 &expected,
1977 n,
1978 width_idx % 2,
1979 ) {
1980 Ok(readback) => readback,
1981 Err(err) if host_bounce => {
1982 skipped += 1;
1983 direction_skipped += 1;
1984 eprintln!(
1985 "[pp] production-slot peer probe ERROR: \
1986 boundary={boundary_idx} dev{src_dev}->dev{dst_dev} \
1987 tokens={tokens} bytes={bytes}: {err}; \
1988 MEMRA_PP_HOST_BOUNCE=1, proceeding on the host-staged path"
1989 );
1990 continue;
1991 }
1992 Err(err) => {
1993 failure = Some(format!(
1994 "PP production-slot peer probe FAILED: \
1995 boundary={boundary_idx} dev{src_dev}->dev{dst_dev} \
1996 tokens={tokens} bytes={bytes}: {err}; refusing native P2P \
1997 (set MEMRA_PP_HOST_BOUNCE=1 to use the host-staged path; \
1998 MEMRA_PEER_PROBE=0 cannot authorize sharded native peer \
1999 transport)"
2000 ));
2001 break;
2002 }
2003 };
2004 copies += 1;
2005 direction_copies += 1;
2006 let mismatches = peer_probe_mismatch_count(&expected, &readback);
2007 if mismatches == 0 {
2008 largest_clean_payload = largest_clean_payload.max(bytes);
2009 direction_largest_clean = direction_largest_clean.max(bytes);
2010 } else if host_bounce {
2011 total_mismatches += mismatches;
2012 direction_mismatches += mismatches;
2013 eprintln!(
2014 "[pp] production-slot peer probe CORRUPTION: \
2015 boundary={boundary_idx} dev{src_dev}->dev{dst_dev} tokens={tokens} \
2016 bytes={bytes} mismatches={mismatches}; MEMRA_PP_HOST_BOUNCE=1, \
2017 proceeding on the host-staged path"
2018 );
2019 } else {
2020 failure = Some(format!(
2021 "PP production-slot peer probe FAILED: boundary={boundary_idx} \
2022 dev{src_dev}->dev{dst_dev} tokens={tokens} bytes={bytes}: \
2023 {mismatches} mismatched byte(s); refusing native P2P \
2024 (set MEMRA_PP_HOST_BOUNCE=1 to use the host-staged path; \
2025 MEMRA_PEER_PROBE=0 cannot authorize sharded native peer transport)"
2026 ));
2027 break;
2028 }
2029 }
2030
2031 self.clear_peer_probe_boundary(&probe_boundary, src_stage, dst_stage)?;
2032 if let Some(err) = failure {
2033 return Err(err.into());
2034 }
2035 eprintln!(
2036 "[pp] production-slot peer probe direction: boundary={boundary_idx} \
2037 dev{src_dev}->dev{dst_dev} copies={direction_copies} \
2038 skipped={direction_skipped} mismatches={direction_mismatches} \
2039 largest_clean_payload_bytes={direction_largest_clean}"
2040 );
2041 }
2042 }
2043
2044 let status = if total_mismatches > 0 {
2045 "BOUNCE"
2046 } else if skipped > 0 && copies > 0 {
2047 "PARTIAL"
2048 } else if skipped > 0 {
2049 "SKIP"
2050 } else {
2051 "PASS"
2052 };
2053 eprintln!(
2054 "[pp] production-slot peer probe {status}: widths_tokens={:?} copies={copies} \
2055 skipped={skipped} mismatches={total_mismatches} \
2056 largest_clean_payload_bytes={largest_clean_payload} elapsed_ms={:.3}",
2057 PEER_PROBE_TOKEN_WIDTHS,
2058 started.elapsed().as_secs_f64() * 1e3,
2059 );
2060 Ok(())
2061 }
2062
2063 fn run_host_bounce_production_probe(
2064 &self,
2065 e: &Engine,
2066 n_embd: usize,
2067 ) -> Result<(), Box<dyn std::error::Error>> {
2068 let enabled = self.enable_probe_peer_access(e, &self.peer_capable)?;
2069 let granted = self.grant_probe_pool_access(e, &enabled)?;
2070 let probe = self.run_production_peer_probe(&granted, true, n_embd);
2071 let revoke = self.revoke_probe_pool_access(e, &granted);
2077 let disable = self.disable_probe_peer_access(e, &enabled);
2078 probe?;
2079 revoke?;
2080 disable?;
2081 Ok(())
2082 }
2083
2084 fn init_peer_probe_geometry(
2085 &self,
2086 e: &Engine,
2087 n_embd: usize,
2088 ) -> Result<(), Box<dyn std::error::Error>> {
2089 if !self.peer_probe || !self.cross_any {
2090 return Ok(());
2091 }
2092 let bytes = n_embd
2093 .checked_mul(std::mem::size_of::<f32>())
2094 .ok_or_else(|| format!("PP boundary-slot byte count overflows for n_embd={n_embd}"))?;
2095 let result = self.peer_probe_geometry.get_or_init(|| {
2096 let probe = if self.host_bounce_active() {
2097 self.run_host_bounce_production_probe(e, n_embd)
2098 } else {
2099 self.run_production_peer_probe(&self.peer_capable, false, n_embd)
2100 };
2101 let restore = e.ctx().bind_to_thread();
2102 match (probe, restore) {
2103 (Ok(()), Ok(())) => Ok(bytes),
2104 (Err(err), _) => Err(err.to_string()),
2105 (_, Err(err)) => Err(err.to_string()),
2106 }
2107 });
2108 let probed = result
2109 .as_ref()
2110 .map_err(|err| -> Box<dyn std::error::Error> { err.clone().into() })?;
2111 if *probed != bytes {
2112 return Err(format!(
2113 "peer probe initialized for boundary-slot bytes={probed} but model requests \
2114 bytes={bytes}; one PP runtime supports one model geometry per process"
2115 )
2116 .into());
2117 }
2118 Ok(())
2119 }
2120
2121 fn init_host_bounce_staging(
2122 &self,
2123 e: &Engine,
2124 n_embd: usize,
2125 ) -> Result<(), Box<dyn std::error::Error>> {
2126 if !self.cross_any {
2127 return Ok(());
2128 }
2129 e.ctx().bind_to_thread()?;
2130 let result = self.bounce.get_or_init(|| {
2131 HostBounceRt::new(n_embd, &self.boundaries)
2132 .map(|rt| {
2133 let bytes = rt.capacity * std::mem::size_of::<f32>();
2134 eprintln!(
2135 "[pp] host-bounce staging ready: n_embd={n_embd} max_tokens={} \
2136 slot_bytes={bytes} slots_per_cross_boundary=2",
2137 crate::cache::PRIME_CHUNK_MAX_TOKENS,
2138 );
2139 rt
2140 })
2141 .map_err(|err| err.to_string())
2142 });
2143 let bounce = result
2144 .as_ref()
2145 .map_err(|err| -> Box<dyn std::error::Error> { err.clone().into() })?;
2146 if bounce.n_embd != n_embd {
2147 return Err(format!(
2148 "host-bounce runtime initialized for n_embd={} but model requests n_embd={n_embd}; \
2149 one PP runtime supports one model geometry per process",
2150 bounce.n_embd,
2151 )
2152 .into());
2153 }
2154 Ok(())
2155 }
2156
2157 fn validate_host_bounce_staging(
2161 &self,
2162 e: &Engine,
2163 n_embd: usize,
2164 ) -> Result<(), Box<dyn std::error::Error>> {
2165 let bytes = n_embd
2166 .checked_mul(std::mem::size_of::<f32>())
2167 .ok_or_else(|| {
2168 format!("host-bounce validation byte count overflows for n_embd={n_embd}")
2169 })?;
2170 for boundary_idx in 0..self.stages.len() - 1 {
2171 if !self.boundaries[boundary_idx].cross {
2172 continue;
2173 }
2174 let src_stage = boundary_idx;
2175 let dst_stage = boundary_idx + 1;
2176 let probe_boundary = self.new_peer_probe_boundary(src_stage, dst_stage)?;
2177 let path = BoundaryPath {
2178 boundary: boundary_idx,
2179 src_stage,
2180 dst_stage,
2181 transport: BoundaryTransport::HostBounce,
2182 };
2183 let expected = peer_probe_pattern(
2184 bytes,
2185 boundary_idx,
2186 self.stages[src_stage].dev,
2187 self.stages[dst_stage].dev,
2188 );
2189 let readback = self.production_probe_readback(
2190 path,
2191 &probe_boundary,
2192 &expected,
2193 n_embd,
2194 0,
2195 );
2196 let clear = self.clear_peer_probe_boundary(
2197 &probe_boundary,
2198 src_stage,
2199 dst_stage,
2200 );
2201 let readback = readback?;
2202 clear?;
2203 let mismatches = peer_probe_mismatch_count(&expected, &readback);
2204 if mismatches > 0 {
2205 return Err(format!(
2206 "runtime host-bounce staging validation FAILED: boundary={boundary_idx} \
2207 bytes={bytes} mismatches={mismatches}"
2208 )
2209 .into());
2210 }
2211 }
2212 e.ctx().bind_to_thread()?;
2213 eprintln!(
2214 "[pp] runtime host-bounce staging validation PASS: row_bytes={bytes} \
2215 cross_boundaries={}",
2216 self.boundaries.iter().filter(|boundary| boundary.cross).count(),
2217 );
2218 Ok(())
2219 }
2220
2221 fn arm_runtime_host_bounce(
2222 &self,
2223 e: &Engine,
2224 row_bytes: usize,
2225 ) -> Result<(), Box<dyn std::error::Error>> {
2226 if row_bytes == 0 || row_bytes % std::mem::size_of::<f32>() != 0 {
2227 return Err(format!(
2228 "runtime host-bounce cannot recover n_embd from row_bytes={row_bytes}"
2229 )
2230 .into());
2231 }
2232 let n_embd = row_bytes / std::mem::size_of::<f32>();
2233 self.init_host_bounce_staging(e, n_embd)?;
2234 self.validate_host_bounce_staging(e, n_embd)
2235 }
2236
2237 pub fn init_boundary_transport(
2243 &self,
2244 e: &Engine,
2245 n_embd: usize,
2246 ) -> Result<(), Box<dyn std::error::Error>> {
2247 if PEER_RUNTIME_PROBE_FAILED.load(Ordering::Acquire)
2248 && !PEER_RUNTIME_HOST_BOUNCE.load(Ordering::Acquire)
2249 {
2250 return Err(
2251 "PP runtime peer byte-integrity probe previously failed; refusing native P2P \
2252 reuse because runtime host-bounce staging could not be armed"
2253 .into(),
2254 );
2255 }
2256 self.init_peer_probe_geometry(e, n_embd)?;
2257 if !self.host_bounce_active() || !self.cross_any {
2258 return Ok(());
2259 }
2260 self.init_host_bounce_staging(e, n_embd)
2261 }
2262
2263 fn service_runtime_peer_probe(
2268 &self,
2269 e: &Engine,
2270 scheduler_idle: bool,
2271 probe_allowed: bool,
2272 ) -> Result<RuntimePeerProbeStatus, Box<dyn std::error::Error>> {
2273 if !self.peer_probe || !self.cross_any || self.host_bounce_active() {
2274 return Ok(RuntimePeerProbeStatus::NotRun);
2275 }
2276 if PEER_RUNTIME_PROBE_FAILED.load(Ordering::Acquire) {
2277 return Err(
2278 "PP runtime peer byte-integrity probe previously failed; native P2P is latched off"
2279 .into(),
2280 );
2281 }
2282 let row_bytes = match self.peer_probe_geometry.get() {
2283 Some(Ok(bytes)) => *bytes,
2284 _ => return Ok(RuntimePeerProbeStatus::NotRun),
2285 };
2286
2287 let copies = PEER_BOUNDARY_COPIES.load(Ordering::Relaxed);
2288 let (width_index, tokens) = loop {
2289 let next_probe_copy = std::array::from_fn(|width_index| {
2290 PEER_RUNTIME_NEXT_PROBE_COPY[width_index].load(Ordering::Relaxed)
2291 });
2292 let measured_cost_ns = std::array::from_fn(|width_index| {
2293 PEER_RUNTIME_PROBE_MAX_COST_NS[width_index].load(Ordering::Relaxed)
2294 });
2295 let Some(candidate) = runtime_peer_probe_candidate(
2296 copies,
2297 next_probe_copy,
2298 measured_cost_ns,
2299 scheduler_idle,
2300 ) else {
2301 return Ok(RuntimePeerProbeStatus::NotRun);
2302 };
2303 if !probe_allowed {
2308 return Ok(RuntimePeerProbeStatus::Deferred);
2309 }
2310 let due = next_probe_copy[candidate.0];
2311 let next = runtime_peer_probe_next_copy(due, copies);
2312 if PEER_RUNTIME_NEXT_PROBE_COPY[candidate.0]
2313 .compare_exchange(due, next, Ordering::AcqRel, Ordering::Relaxed)
2314 .is_ok()
2315 {
2316 break candidate;
2317 }
2318 };
2319 let probe_index = PEER_RUNTIME_PROBES.fetch_add(1, Ordering::Relaxed);
2320 let probe_bytes = row_bytes.checked_mul(tokens);
2321 let scheduler_class = if scheduler_idle { "idle" } else { "busy" };
2322 let label = format!("runtime-{scheduler_class}-{tokens}tok");
2323 let started = std::time::Instant::now();
2324 let probe = match probe_bytes {
2325 Some(bytes) => run_peer_probe_pass(
2326 &self.stages,
2327 &self.peer_capable,
2328 false,
2329 &label,
2330 bytes,
2331 ),
2332 None => Err(format!(
2333 "PP runtime peer probe byte count overflows for row_bytes={row_bytes} \
2334 tokens={tokens}"
2335 )
2336 .into()),
2337 };
2338 let restore = e.ctx().bind_to_thread();
2339 let elapsed_ns = started.elapsed().as_nanos().min(u64::MAX as u128) as u64;
2340 let previous_max = PEER_RUNTIME_PROBE_MAX_COST_NS[width_index]
2341 .fetch_max(elapsed_ns, Ordering::Relaxed);
2342 let verdict = match (probe, restore) {
2343 (Ok(()), Ok(())) => Ok(()),
2344 (Err(err), _) => Err(err.to_string()),
2345 (_, Err(err)) => Err(err.to_string()),
2346 };
2347 if let Err(err) = verdict {
2348 PEER_RUNTIME_PROBE_FAILURES.fetch_add(1, Ordering::Relaxed);
2349 let arm = latch_runtime_host_bounce(
2350 &PEER_RUNTIME_PROBE_FAILED,
2351 &PEER_RUNTIME_HOST_BOUNCE,
2352 || {
2353 self.arm_runtime_host_bounce(e, row_bytes)
2354 .map_err(|arm_err| arm_err.to_string())
2355 },
2356 );
2357 if let Err(arm_err) = arm {
2358 let message = format!(
2359 "PP runtime peer byte-integrity re-probe FAILED after \
2360 boundary_copies={copies} rung={}/{} tokens={tokens}: {err}; native P2P is \
2361 latched off and host-bounce staging could not be armed: {arm_err}",
2362 width_index + 1,
2363 PEER_PROBE_TOKEN_WIDTHS.len(),
2364 );
2365 eprintln!("[pp] SECURITY RED: {message}; worker must stop");
2366 return Err(message.into());
2367 }
2368 eprintln!(
2369 "[pp] SECURITY RED: PP runtime peer byte-integrity re-probe FAILED after \
2370 boundary_copies={copies} rung={}/{} tokens={tokens}: {err}; native P2P is \
2371 latched off and the live transport DEGRADED to validated host bounce for the \
2372 remainder of this process",
2373 width_index + 1,
2374 PEER_PROBE_TOKEN_WIDTHS.len(),
2375 );
2376 return Ok(RuntimePeerProbeStatus::DegradedToHostBounce);
2377 }
2378 if width_index + 1 != PEER_PROBE_TOKEN_WIDTHS.len()
2379 && previous_max <= PEER_RUNTIME_PROBE_BUDGET_NS
2380 && elapsed_ns > PEER_RUNTIME_PROBE_BUDGET_NS
2381 {
2382 eprintln!(
2383 "[pp] runtime peer re-probe rung exceeded the {:.3}ms owner-thread budget: \
2384 tokens={tokens} measured_ms={:.3}; future runs are idle-only",
2385 PEER_RUNTIME_PROBE_BUDGET_NS as f64 / 1e6,
2386 elapsed_ns as f64 / 1e6,
2387 );
2388 }
2389 eprintln!(
2390 "[pp] runtime peer byte-integrity re-probe PASS: \
2391 boundary_copies={copies} interval_copies={PEER_RUNTIME_PROBE_INTERVAL_COPIES} \
2392 rung={}/{} tokens={tokens} bytes={} probe_index={probe_index} elapsed_ms={:.3} \
2393 scheduler_idle={scheduler_idle}",
2394 width_index + 1,
2395 PEER_PROBE_TOKEN_WIDTHS.len(),
2396 probe_bytes.unwrap(),
2397 elapsed_ns as f64 / 1e6,
2398 );
2399 Ok(RuntimePeerProbeStatus::Passed)
2400 }
2401
2402 fn bounce_rt(&self) -> Result<&HostBounceRt, Box<dyn std::error::Error>> {
2403 self.bounce
2404 .get()
2405 .ok_or_else(|| -> Box<dyn std::error::Error> {
2406 "MEMRA_PP_HOST_BOUNCE=1 staging was not initialized from model geometry".into()
2407 })?
2408 .as_ref()
2409 .map_err(|err| -> Box<dyn std::error::Error> { err.clone().into() })
2410 }
2411
2412 pub fn engine<'a>(&'a self, s: usize, primary: &'a Engine) -> &'a Engine {
2415 self.stages[s].engine.as_ref().unwrap_or(primary)
2416 }
2417
2418 pub fn bind_stage(&self, s: usize) -> Result<(), Box<dyn std::error::Error>> {
2420 self.stages[s].ctx.bind_to_thread()?;
2421 Ok(())
2422 }
2423
2424 pub fn enter(&self, s: usize) -> memra_runtime::StreamOverride {
2427 memra_runtime::push_stream_override(self.stages[s].stream.clone())
2428 }
2429
2430 pub fn prepare_overlap_slots(&self, b: usize, n: usize)
2436 -> Result<(), Box<dyn std::error::Error>> {
2437 let bd = &self.boundaries[b];
2438 let s_rx = &self.stages[b + 1].stream;
2439 let mut grew = false;
2440 for sl in &bd.slots {
2441 let mut guard = sl.buf.lock().unwrap();
2442 if guard.as_ref().map(|bf| bf.len() < n).unwrap_or(true) {
2443 *guard = Some(s_rx.alloc_zeros::<f32>(n)?);
2444 grew = true;
2445 }
2446 }
2447 if grew {
2448 s_rx.synchronize()?;
2449 }
2450 Ok(())
2451 }
2452
2453 pub fn tx(&self, b: usize, x: &CudaSlice<f32>, n: usize)
2467 -> Result<usize, Box<dyn std::error::Error>> {
2468 assert_eq!(x.len(), n, "pp tx: residual length mismatch");
2469 let bd = &self.boundaries[b];
2470 let slot_idx = if pp2_overlap() {
2471 bd.step.fetch_add(1, Ordering::Relaxed) % 2
2472 } else {
2473 0
2474 };
2475 self.tx_slot(b, x, n, slot_idx)
2476 }
2477
2478 pub fn tx_pipelined(&self, b: usize, x: &CudaSlice<f32>, n: usize)
2482 -> Result<usize, Box<dyn std::error::Error>> {
2483 assert_eq!(x.len(), n, "pp tx: residual length mismatch");
2484 let slot_idx = self.boundaries[b].step.fetch_add(1, Ordering::Relaxed) % 2;
2485 self.tx_slot(b, x, n, slot_idx)
2486 }
2487
2488 fn tx_slot(&self, b: usize, x: &CudaSlice<f32>, n: usize, slot_idx: usize)
2489 -> Result<usize, Box<dyn std::error::Error>> {
2490 let bd = &self.boundaries[b];
2491 let path = BoundaryPath {
2492 boundary: b,
2493 src_stage: b,
2494 dst_stage: b + 1,
2495 transport: boundary_transport(bd.cross, self.host_bounce_active()),
2496 };
2497 let copied_slot = self.tx_slot_path(path, bd, x, n, slot_idx)?;
2498 if path.transport == BoundaryTransport::Peer {
2499 PEER_BOUNDARY_COPIES.fetch_add(1, Ordering::Relaxed);
2500 }
2501 Ok(copied_slot)
2502 }
2503
2504 fn tx_slot_path(
2505 &self,
2506 path: BoundaryPath,
2507 bd: &BoundaryRt,
2508 x: &CudaSlice<f32>,
2509 n: usize,
2510 slot_idx: usize,
2511 ) -> Result<usize, Box<dyn std::error::Error>> {
2512 debug_assert!(slot_idx < 2);
2513 let sl = &bd.slots[slot_idx];
2514 let s_tx = &self.stages[path.src_stage].stream;
2515 s_tx.wait(&sl.ev_rx)?;
2516 let mut guard = sl.buf.lock().unwrap();
2517 if guard.as_ref().map(|bf| bf.len() < n).unwrap_or(true) {
2518 let s_rx = &self.stages[path.dst_stage].stream;
2520 *guard = Some(s_rx.alloc_zeros::<f32>(n)?);
2521 s_rx.synchronize()?;
2531 }
2532 let buf = guard.as_mut().unwrap();
2533 match path.transport {
2534 BoundaryTransport::Local => s_tx.memcpy_dtod(x, buf)?,
2535 BoundaryTransport::HostBounce => {
2536 debug_assert_eq!(path.src_stage, path.boundary);
2537 debug_assert_eq!(path.dst_stage, path.boundary + 1);
2538 let bounce = self.bounce_rt()?;
2539 if n > bounce.capacity {
2540 return Err(format!(
2541 "pp host-bounce payload {n} exceeds geometry-sized capacity {} \
2542 (n_embd={}, max prime tokens={})",
2543 bounce.capacity,
2544 bounce.n_embd,
2545 crate::cache::PRIME_CHUNK_MAX_TOKENS,
2546 )
2547 .into());
2548 }
2549 let mut host = bounce.slot(path.boundary, slot_idx)?.lock().unwrap();
2550 s_tx.memcpy_dtoh(x, host.prefix_mut(n))?;
2554 }
2555 BoundaryTransport::Peer => {
2556 use cudarc::driver::{DevicePtr, DevicePtrMut};
2559 let (sp, _g0) = x.device_ptr(s_tx);
2560 let (dp, _g1) = buf.device_ptr_mut(s_tx);
2561 self.stages[path.src_stage].ctx.bind_to_thread()?;
2562 unsafe {
2563 cudarc::driver::result::memcpy_peer_async(
2564 self.stages[path.dst_stage].ctx.cu_ctx(),
2565 dp,
2566 self.stages[path.src_stage].ctx.cu_ctx(),
2567 sp,
2568 n * std::mem::size_of::<f32>(),
2569 s_tx.cu_stream(),
2570 )?;
2571 }
2572 }
2573 }
2574 sl.ev_tx.record(s_tx)?;
2575 Ok(slot_idx)
2576 }
2577
2578 pub fn rx(&self, b: usize, slot_idx: usize, n: usize)
2583 -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2584 let bd = &self.boundaries[b];
2585 let path = BoundaryPath {
2586 boundary: b,
2587 src_stage: b,
2588 dst_stage: b + 1,
2589 transport: boundary_transport(bd.cross, self.host_bounce_active()),
2590 };
2591 self.rx_slot_path(path, bd, slot_idx, n)
2592 }
2593
2594 fn rx_slot_path(
2595 &self,
2596 path: BoundaryPath,
2597 bd: &BoundaryRt,
2598 slot_idx: usize,
2599 n: usize,
2600 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2601 let sl = &bd.slots[slot_idx];
2602 let s_rx = &self.stages[path.dst_stage].stream;
2603 s_rx.wait(&sl.ev_tx)?;
2604 let mut guard = sl.buf.lock().unwrap();
2605 let buf = guard.as_mut().expect("pp rx before tx");
2606 assert!(buf.len() >= n, "pp rx: slot holds {} < requested {n}", buf.len());
2607 if path.transport == BoundaryTransport::HostBounce {
2608 debug_assert_eq!(path.src_stage, path.boundary);
2609 debug_assert_eq!(path.dst_stage, path.boundary + 1);
2610 let bounce = self.bounce_rt()?;
2611 let host = bounce.slot(path.boundary, slot_idx)?.lock().unwrap();
2612 let mut dst = buf.slice_mut(0..n);
2613 s_rx.memcpy_htod(host.prefix(n), &mut dst)?;
2616 }
2617 let mut work = unsafe { s_rx.alloc::<f32>(n)? };
2620 s_rx.memcpy_dtod(&buf.slice(0..n), &mut work)?;
2624 sl.ev_rx.record(s_rx)?;
2625 Ok(work)
2626 }
2627
2628 pub fn publish_to(&self, s: usize, dst: &Arc<CudaStream>)
2655 -> Result<(), Box<dyn std::error::Error>> {
2656 let st = &self.stages[s];
2657 if Arc::ptr_eq(&st.stream, dst) {
2660 return Ok(());
2661 }
2662 let ev = st.ctx.new_event(None)?;
2663 ev.record(&st.stream)?;
2664 dst.wait(&ev)?;
2665 Ok(())
2666 }
2667
2668 pub fn fence_stages_behind(&self, src: &Arc<CudaStream>)
2690 -> Result<(), Box<dyn std::error::Error>> {
2691 let ev = src.context().new_event(None)?;
2692 ev.record(src)?;
2693 for st in &self.stages {
2694 if Arc::ptr_eq(&st.stream, src) {
2695 continue;
2696 }
2697 st.stream.wait(&ev)?;
2698 }
2699 Ok(())
2700 }
2701
2702 pub fn record_done(&self) -> Result<CudaEvent, Box<dyn std::error::Error>> {
2705 let last = &self.stages[self.stages.len() - 1];
2706 let ev = last.ctx.new_event(None)?;
2707 ev.record(&last.stream)?;
2708 Ok(ev)
2709 }
2710
2711 pub fn readback_stream(&self) -> &Arc<CudaStream> {
2713 &self.readback
2714 }
2715}
2716
2717pub fn service_runtime_peer_probe(
2720 e: &Engine,
2721 scheduler_idle: bool,
2722 probe_allowed: bool,
2723) -> Result<RuntimePeerProbeStatus, Box<dyn std::error::Error>> {
2724 let Some(rt) = RTN.get() else { return Ok(RuntimePeerProbeStatus::NotRun) };
2725 let rt = rt
2726 .as_ref()
2727 .map_err(|err| -> Box<dyn std::error::Error> { err.clone().into() })?;
2728 rt.service_runtime_peer_probe(e, scheduler_idle, probe_allowed)
2729}
2730
2731pub struct PendingLogits {
2736 logits: CudaSlice<f32>,
2737 ev: CudaEvent,
2738 rb: Arc<CudaStream>,
2739}
2740
2741impl PendingLogits {
2742 pub fn new(logits: CudaSlice<f32>, ev: CudaEvent, rb: Arc<CudaStream>) -> Self {
2743 PendingLogits { logits, ev, rb }
2744 }
2745
2746 pub fn wait(self) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2750 self.rb.wait(&self.ev)?;
2751 let host = self.rb.clone_dtoh(&self.logits)?;
2752 self.rb.synchronize()?;
2753 Ok(host)
2756 }
2757}
2758
2759pub fn init_model_transport(
2762 e: &Engine,
2763 cfg: &memra_gguf::config::ModelConfig,
2764 n_trunk: usize,
2765) -> Result<(), Box<dyn std::error::Error>> {
2766 if pp2_streams_off() || pp2_devices_env().is_none() || pp_cuts(n_trunk).is_none() {
2767 return Ok(());
2768 }
2769 PpNRt::get(e)?.init_boundary_transport(e, cfg.n_embd as usize)
2770}
2771
2772pub fn new_cache(e: &Engine, cfg: &memra_gguf::config::ModelConfig, max_ctx: usize)
2779 -> Result<crate::cache::Cache, Box<dyn std::error::Error>> {
2780 let n_trunk = (cfg.n_layer - cfg.nextn_predict_layers) as usize;
2781 if let Some(fence) = pp_cuts(n_trunk) {
2782 if pp2_devices_env().is_some() && !pp2_streams_off() {
2783 let rt = PpNRt::get(e)?;
2784 rt.init_boundary_transport(e, cfg.n_embd as usize)?;
2785 let n_st = fence.len() - 1;
2786 assert_eq!(
2787 rt.n_stages(), n_st,
2788 "PpNRt stage count {} != fence stages {n_st}", rt.n_stages()
2789 );
2790 rt.fence_stages_behind(&e.stream())?;
2799 let devs: Vec<&dyn memra_kv::KvDev> =
2800 (0..n_st).map(|s| rt.engine(s, e) as &dyn memra_kv::KvDev).collect();
2801 let cache = crate::cache::Cache::new_ppn(&devs, &fence, cfg, max_ctx)?;
2802 sync_stages_after_load(e, n_trunk)?;
2803 return Ok(cache);
2804 }
2805 if !pp2_streams_off() {
2806 let cache = crate::cache::Cache::new(e, cfg, max_ctx)?;
2814 sync_stages_after_load(e, n_trunk)?;
2815 return Ok(cache);
2816 }
2817 }
2818 crate::cache::Cache::new(e, cfg, max_ctx)
2819}
2820
2821pub fn sync_stages_after_load(e: &Engine, n_trunk: usize)
2830 -> Result<(), Box<dyn std::error::Error>> {
2831 if pp2_streams_off() || pp_cuts(n_trunk).is_none() {
2832 return Ok(());
2833 }
2834 let rt = PpNRt::get(e)?;
2835 for s in 0..rt.n_stages() {
2836 rt.stages[s].ctx.bind_to_thread()?;
2837 unsafe {
2838 cudarc::driver::sys::cuCtxSynchronize().result()?;
2839 }
2840 }
2841 e.ctx().bind_to_thread()?;
2842 unsafe {
2843 cudarc::driver::sys::cuCtxSynchronize().result()?;
2844 }
2845 Ok(())
2846}
2847
2848pub fn layer_engine<'a>(e: &'a Engine, n_trunk: usize, il: usize)
2854 -> Result<&'a Engine, Box<dyn std::error::Error>> {
2855 if pp_shard_off() || pp2_devices_env().is_none() || pp2_streams_off() {
2856 return Ok(e);
2857 }
2858 let Some(fence) = pp_cuts(n_trunk) else { return Ok(e) };
2859 let rt = PpNRt::get(e)?;
2860 let s = stage_of(&fence, il.min(n_trunk - 1));
2861 Ok(rt.engine(s, e))
2862}
2863
2864pub fn restore_cache_checkpoint(
2875 e: &Engine,
2876 cfg: &memra_gguf::config::ModelConfig,
2877 source: Option<&crate::cache::Cache>,
2878 target: &mut crate::cache::Cache,
2879 snap: &crate::cache::CacheSnapshot,
2880) -> Result<(), Box<dyn std::error::Error>> {
2881 let n = target.kv.len();
2882 if target.recur.len() != n
2883 || snap.kv_len.len() != n
2884 || snap.conv.len() != n
2885 || snap.ssm.len() != n
2886 || source.is_some_and(|s| s.kv.len() != n || s.recur.len() != n)
2887 {
2888 return Err("checkpoint cache layer-count mismatch".into());
2889 }
2890 if snap.pos > target.max_ctx {
2891 return Err(format!(
2892 "checkpoint pos {} exceeds target capacity {}",
2893 snap.pos, target.max_ctx,
2894 )
2895 .into());
2896 }
2897
2898 let n_trunk = (cfg.n_layer - cfg.nextn_predict_layers) as usize;
2899 for il in 0..n {
2900 let owner = layer_engine(e, n_trunk, il)?;
2901 let src_kv = source.map(|s| &s.kv[il]);
2902 match (src_kv, target.kv[il].as_mut(), snap.kv_len[il]) {
2903 (Some(Some(src)), Some(dst), Some(len)) => {
2904 if len > src.len || len > target.max_ctx {
2905 return Err(format!(
2906 "checkpoint layer {il} len {len} exceeds source {} or target {}",
2907 src.len, target.max_ctx,
2908 )
2909 .into());
2910 }
2911 if src.kv_dim_k != dst.kv_dim_k
2912 || src.kv_dim_v != dst.kv_dim_v
2913 || src.k_tok_bytes != dst.k_tok_bytes
2914 || src.v_tok_bytes != dst.v_tok_bytes
2915 {
2916 return Err(format!("checkpoint KV layout mismatch at layer {il}").into());
2917 }
2918 let kb = len * src.k_tok_bytes;
2919 let vb = len * src.v_tok_bytes;
2920 if kb > 0 {
2921 owner.copy_u8_into(&mut dst.k, 0, &src.k, kb)?;
2922 }
2923 if vb > 0 {
2924 owner.copy_u8_into(&mut dst.v, 0, &src.v, vb)?;
2925 }
2926 dst.len = len;
2927 owner.set_i32_one(&mut dst.len_d, len as i32)?;
2928 }
2929 (None, Some(dst), Some(len)) => {
2930 if len > dst.len || len > target.max_ctx {
2931 return Err(format!(
2932 "checkpoint layer {il} len {len} exceeds live {} or target {}",
2933 dst.len, target.max_ctx,
2934 )
2935 .into());
2936 }
2937 dst.len = len;
2938 owner.set_i32_one(&mut dst.len_d, len as i32)?;
2939 }
2940 (Some(None), None, None) | (None, None, None) => {}
2941 _ => return Err(format!("checkpoint KV kind mismatch at layer {il}").into()),
2942 }
2943
2944 match (
2945 target.recur[il].as_mut(),
2946 &snap.conv[il],
2947 &snap.ssm[il],
2948 ) {
2949 (Some(dst), Some(conv), Some(ssm)) => {
2950 if conv.len() != dst.conv_state.len() || ssm.len() != dst.ssm_state.len() {
2951 return Err(
2952 format!("checkpoint recurrent layout mismatch at layer {il}").into(),
2953 );
2954 }
2955 owner.copy_into(&mut dst.conv_state, 0, conv, conv.len())?;
2956 owner.copy_into(&mut dst.ssm_state, 0, ssm, ssm.len())?;
2957 }
2958 (None, None, None) => {}
2959 _ => {
2960 return Err(
2961 format!("checkpoint recurrent kind mismatch at layer {il}").into(),
2962 );
2963 }
2964 }
2965 }
2966 target.pos = snap.pos;
2967
2968 sync_stages_after_load(e, n_trunk)?;
2971 if source.is_some() {
2972 e.stream().synchronize()?;
2975 }
2976 Ok(())
2977}
2978
2979#[cfg(test)]
2980mod host_bounce_tests {
2981 use super::{
2982 boundary_transport, dual_pp_eligibility, dual_pp_timing_dropped,
2983 dual_pp_timing_snapshot, dual_pp_wave_mid, host_bounce_capacity,
2984 latch_runtime_host_bounce,
2985 peer_probe_bytes_to_f32, peer_probe_decision, peer_probe_f32_to_bytes,
2986 peer_probe_mismatch_count, peer_probe_pattern, peer_probe_startup_policy,
2987 publish_runtime_peer_probe_deferral, record_dual_pp_stage_result,
2988 runtime_peer_probe_candidate,
2989 runtime_peer_probe_next_copy,
2990 BoundaryTransport, PeerProbeDecision, PeerProbeStartupPolicy,
2991 DUAL_PP_HOST_BOUNCE_REFUSAL, DUAL_PP_SINGLE_SLOT_REFUSAL, PEER_PROBE_FIXED_BYTES,
2992 PEER_PROBE_REQUIRED_REFUSAL, PEER_PROBE_TOKEN_WIDTHS,
2993 PEER_RUNTIME_PROBE_BUDGET_NS, PEER_RUNTIME_PROBE_CYCLE_COPIES,
2994 PEER_RUNTIME_PROBE_DEFERRAL_BOUND_INTERVALS, PEER_RUNTIME_PROBE_INTERVAL_COPIES,
2995 };
2996
2997 #[test]
3001 fn flip_default_is_dual_auto_with_explicit_off_and_forced_seams() {
3002 use super::{dual_pp_mode_resolve, DualPpMode};
3003 assert_eq!(dual_pp_mode_resolve(None), DualPpMode::Auto);
3004 assert_eq!(dual_pp_mode_resolve(Some("0")), DualPpMode::Off);
3005 assert_eq!(dual_pp_mode_resolve(Some("1")), DualPpMode::Forced);
3006 assert_eq!(dual_pp_mode_resolve(Some("2")), DualPpMode::Auto);
3008 assert_eq!(dual_pp_mode_resolve(Some("")), DualPpMode::Auto);
3009 }
3010
3011 #[test]
3012 fn flip_overlap_follows_mode_and_one_flag_restores_preflip_serial() {
3013 use super::{pp2_overlap_resolve, DualPpMode};
3014 assert!(pp2_overlap_resolve(None, DualPpMode::Auto));
3016 assert!(!pp2_overlap_resolve(None, DualPpMode::Off));
3018 assert!(!pp2_overlap_resolve(None, DualPpMode::Forced));
3020 for mode in [DualPpMode::Off, DualPpMode::Forced, DualPpMode::Auto] {
3022 assert!(pp2_overlap_resolve(Some("1"), mode));
3023 assert!(!pp2_overlap_resolve(Some("0"), mode));
3024 }
3025 }
3026
3027 #[test]
3028 fn flip_auto_routes_only_the_regated_regime_and_degrades_serially_elsewhere() {
3029 use super::{dual_pp_route, DualPpMode};
3030 assert!(dual_pp_route(DualPpMode::Auto, 2, 2, true, false));
3032 assert!(dual_pp_route(DualPpMode::Auto, 17, 2, true, false));
3033 assert!(!dual_pp_route(DualPpMode::Auto, 1, 2, true, false)); assert!(!dual_pp_route(DualPpMode::Auto, 2, 3, true, false)); assert!(!dual_pp_route(DualPpMode::Auto, 2, 2, false, false)); assert!(!dual_pp_route(DualPpMode::Auto, 2, 2, true, true)); assert!(dual_pp_route(DualPpMode::Forced, 2, 3, false, true));
3040 assert!(!dual_pp_route(DualPpMode::Forced, 1, 2, true, false));
3041 assert!(!dual_pp_route(DualPpMode::Off, 8, 2, true, false));
3043 }
3044
3045 #[test]
3046 fn dual_pp_split_is_honest_at_one_and_ceil_first_afterward() {
3047 assert_eq!(dual_pp_wave_mid(1), None);
3048 assert_eq!(dual_pp_wave_mid(2), Some(1));
3049 assert_eq!(dual_pp_wave_mid(3), Some(2));
3050 assert_eq!(dual_pp_wave_mid(8), Some(4));
3051 assert_eq!(dual_pp_wave_mid(16), Some(8));
3052 assert_eq!(dual_pp_wave_mid(31), Some(16));
3053 assert_eq!(dual_pp_wave_mid(32), Some(16));
3054 }
3055
3056 #[test]
3057 fn dual_pp_refuses_single_slot_and_non_pp2_shapes() {
3058 assert_eq!(dual_pp_eligibility(2, false, false), Err(DUAL_PP_SINGLE_SLOT_REFUSAL));
3059 assert!(dual_pp_eligibility(2, true, false).is_ok());
3060 assert!(dual_pp_eligibility(3, true, false).is_err());
3061 }
3062
3063 #[test]
3064 fn dual_pp_refuses_unvalidated_host_bounce_transport() {
3065 assert_eq!(
3066 dual_pp_eligibility(2, true, true),
3067 Err(DUAL_PP_HOST_BOUNCE_REFUSAL),
3068 );
3069 }
3070
3071 #[test]
3072 fn dual_pp_timing_error_is_counted_without_recording_a_sample() {
3073 let dropped_before = dual_pp_timing_dropped();
3074 let (_, samples_before) = dual_pp_timing_snapshot();
3075 record_dual_pp_stage_result(0, Err::<f32, _>("CUDA_ERROR_NOT_READY"));
3076 let (_, samples_after) = dual_pp_timing_snapshot();
3077 assert_eq!(samples_after[0], samples_before[0]);
3078 assert!(dual_pp_timing_dropped() >= dropped_before + 1);
3079 }
3080
3081 #[test]
3082 fn corrupted_peer_readback_fails_closed_unless_host_bounce_is_selected() {
3083 assert_eq!(
3084 PEER_PROBE_TOKEN_WIDTHS,
3085 [1, 8, 16, crate::cache::PRIME_CHUNK_MAX_TOKENS],
3086 );
3087 let largest_payload_bytes = PEER_PROBE_TOKEN_WIDTHS[3]
3088 * 4096
3089 * std::mem::size_of::<f32>();
3090 assert_eq!(largest_payload_bytes, 64 * 1024 * 1024);
3091 assert!(largest_payload_bytes >= 1024 * 1024);
3092 let expected = peer_probe_pattern(PEER_PROBE_FIXED_BYTES, 2, 0, 1);
3093 assert_eq!(
3094 peer_probe_f32_to_bytes(&peer_probe_bytes_to_f32(&expected)),
3095 expected,
3096 );
3097 let mut corrupted = expected.clone();
3098 for offset in [0, 8_191, PEER_PROBE_FIXED_BYTES - 1] {
3099 corrupted[offset] ^= 0x5a;
3100 }
3101
3102 assert_eq!(peer_probe_mismatch_count(&expected, &corrupted), 3);
3103 assert_eq!(
3104 peer_probe_decision(&expected, &corrupted, false),
3105 Err("3 mismatched byte(s)".to_string()),
3106 );
3107 assert_eq!(
3108 peer_probe_decision(&expected, &corrupted, true),
3109 Ok(PeerProbeDecision::ProceedWithHostBounce { mismatches: 3 }),
3110 );
3111 }
3112
3113 #[test]
3114 fn probe_off_refusal_matrix_is_fail_closed_only_for_sharded_native_peer() {
3115 for probe_on in [false, true] {
3116 for sharded in [false, true] {
3117 for host_bounce in [false, true] {
3118 let got = peer_probe_startup_policy(probe_on, sharded, host_bounce);
3119 let expected = match (probe_on, sharded, host_bounce) {
3120 (false, true, false) => Err(PEER_PROBE_REQUIRED_REFUSAL),
3121 (false, true, true) => {
3122 Ok(PeerProbeStartupPolicy::BypassedWithHostBounce)
3123 }
3124 _ => Ok(PeerProbeStartupPolicy::Allowed),
3125 };
3126 assert_eq!(
3127 got, expected,
3128 "probe_on={probe_on} sharded={sharded} host_bounce={host_bounce}",
3129 );
3130 }
3131 }
3132 }
3133 assert!(PEER_PROBE_REQUIRED_REFUSAL.contains("MEMRA_PEER_PROBE=0"));
3134 assert!(PEER_PROBE_REQUIRED_REFUSAL.contains("MEMRA_PP_HOST_BOUNCE!=1"));
3135 }
3136
3137 #[test]
3138 fn runtime_reprobe_keeps_cheap_deadlines_live_while_expensive_work_waits_for_idle() {
3139 let every = PEER_RUNTIME_PROBE_INTERVAL_COPIES;
3140 assert_eq!(PEER_RUNTIME_PROBE_CYCLE_COPIES, 4 * every);
3141 let mut next = [every, 2 * every, 3 * every, 4 * every];
3142 let measured_ns = [1_000_000, 2_000_000, 3_000_000, 0];
3143
3144 assert_eq!(
3145 runtime_peer_probe_candidate(every - 1, next, measured_ns, false),
3146 None,
3147 );
3148 assert_eq!(
3149 runtime_peer_probe_candidate(every, next, measured_ns, false),
3150 Some((0, 1)),
3151 );
3152
3153 next[..3].copy_from_slice(&[5 * every, 6 * every, 7 * every]);
3156 assert_eq!(
3157 runtime_peer_probe_candidate(4 * every, next, measured_ns, false),
3158 None,
3159 );
3160 assert_eq!(
3163 runtime_peer_probe_candidate(5 * every, next, measured_ns, false),
3164 Some((0, 1)),
3165 );
3166 assert_eq!(
3168 runtime_peer_probe_candidate(5 * every, next, measured_ns, true),
3169 Some((3, crate::cache::PRIME_CHUNK_MAX_TOKENS)),
3170 );
3171 }
3172
3173 #[test]
3174 fn runtime_reprobe_moves_any_measured_over_budget_rung_to_idle_only() {
3175 let every = PEER_RUNTIME_PROBE_INTERVAL_COPIES;
3176 let next = [u64::MAX, every, u64::MAX, u64::MAX];
3177 let mut measured_ns = [0; PEER_PROBE_TOKEN_WIDTHS.len()];
3178 measured_ns[1] = PEER_RUNTIME_PROBE_BUDGET_NS + 1;
3179 assert_eq!(runtime_peer_probe_candidate(every, next, measured_ns, false), None);
3180 assert_eq!(
3181 runtime_peer_probe_candidate(every, next, measured_ns, true),
3182 Some((1, 8)),
3183 );
3184 }
3185
3186 #[test]
3187 fn late_runtime_reprobe_advances_once_instead_of_bursting_catchup() {
3188 let every = PEER_RUNTIME_PROBE_INTERVAL_COPIES;
3189 let due = every;
3190 assert_eq!(runtime_peer_probe_next_copy(due, due), due + 4 * every);
3191 assert_eq!(runtime_peer_probe_next_copy(due, 20 * every), 21 * every);
3192 }
3193
3194 #[test]
3195 fn runtime_reprobe_deferral_metric_counts_intervals_and_publishes_bound_state() {
3196 use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
3197
3198 assert_eq!(
3199 PEER_RUNTIME_PROBE_DEFERRAL_BOUND_INTERVALS * PEER_RUNTIME_PROBE_INTERVAL_COPIES,
3200 PEER_RUNTIME_PROBE_CYCLE_COPIES,
3201 );
3202 let deferred = AtomicU64::new(0);
3203 let degraded = AtomicBool::new(false);
3204 publish_runtime_peer_probe_deferral(&deferred, °raded, 1, false);
3205 assert_eq!(deferred.load(Ordering::Relaxed), 1);
3206 assert!(!degraded.load(Ordering::Acquire));
3207
3208 publish_runtime_peer_probe_deferral(
3209 &deferred,
3210 °raded,
3211 PEER_RUNTIME_PROBE_DEFERRAL_BOUND_INTERVALS - 1,
3212 true,
3213 );
3214 assert_eq!(
3215 deferred.load(Ordering::Relaxed),
3216 PEER_RUNTIME_PROBE_DEFERRAL_BOUND_INTERVALS,
3217 );
3218 assert!(degraded.load(Ordering::Acquire));
3219 }
3220
3221 #[test]
3222 fn runtime_probe_failure_latches_native_before_publishing_validated_bounce() {
3223 use std::sync::atomic::{AtomicBool, Ordering};
3224
3225 let failed = AtomicBool::new(false);
3226 let degraded = AtomicBool::new(false);
3227 let armed = latch_runtime_host_bounce(&failed, °raded, || Ok::<_, String>(()));
3228 assert!(armed.is_ok());
3229 assert!(failed.load(Ordering::Acquire));
3230 assert!(degraded.load(Ordering::Acquire));
3231
3232 let failed = AtomicBool::new(false);
3233 let degraded = AtomicBool::new(false);
3234 let refused = latch_runtime_host_bounce(&failed, °raded, || {
3235 Err::<(), _>("injected staging mismatch".to_string())
3236 });
3237 assert_eq!(refused, Err("injected staging mismatch".to_string()));
3238 assert!(failed.load(Ordering::Acquire));
3239 assert!(!degraded.load(Ordering::Acquire));
3240 }
3241
3242 #[test]
3243 fn transport_selection_keeps_peer_default_and_bounces_only_cross_device() {
3244 assert_eq!(boundary_transport(false, false), BoundaryTransport::Local);
3245 assert_eq!(boundary_transport(false, true), BoundaryTransport::Local);
3246 assert_eq!(boundary_transport(true, false), BoundaryTransport::Peer);
3247 assert_eq!(
3248 boundary_transport(true, true),
3249 BoundaryTransport::HostBounce
3250 );
3251 }
3252
3253 #[test]
3254 fn step37_geometry_sizes_each_slot_from_the_prime_cap() {
3255 let (elems, bytes) = host_bounce_capacity(4096).expect("valid Step-3.7 geometry");
3256 assert_eq!(elems, 4096 * crate::cache::PRIME_CHUNK_MAX_TOKENS);
3257 assert_eq!(bytes, 64 * 1024 * 1024);
3258 }
3259
3260 #[test]
3261 fn host_bounce_capacity_rejects_invalid_or_overflowing_geometry() {
3262 assert!(host_bounce_capacity(0).is_err());
3263 assert!(host_bounce_capacity(usize::MAX).is_err());
3264 }
3265}