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")
192 .ok()
193 .filter(|v| !v.is_empty());
194 if (!stages_open && devices.is_none()) || pp2_streams_off() {
195 return false;
196 }
197 match devices {
198 None => true, Some(s) => {
200 let mut v: Vec<&str> = s.split(',').map(|p| p.trim()).collect();
201 let n = v.len();
202 v.sort_unstable();
203 v.dedup();
204 v.len() < n }
206 }
207}
208
209pub fn pp_sharded_cross_device() -> bool {
223 let stages_open = std::env::var("MEMRA_PP_STAGES")
224 .map(|v| v.parse::<usize>().map(|n| n >= 2).unwrap_or(false))
225 .unwrap_or(false);
226 if !stages_open || pp_shard_off() || pp2_streams_off() {
233 return false;
234 }
235 match pp2_devices_env() {
236 None => false, Some(s) => {
238 let mut v: Vec<&str> = s.split(',').map(|p| p.trim()).collect();
239 v.sort_unstable();
240 v.dedup();
241 v.len() >= 2
242 }
243 }
244}
245
246pub fn refuse_unsplit_if_remote(path: &str, alt: &str) -> Result<(), Box<dyn std::error::Error>> {
258 if pp_host_bounce_active() {
259 return Err(format!(
260 "{path}: refused with MEMRA_PP_HOST_BOUNCE=1 on sharded cross-device PP — \
261 this unsplit path peer-reads remote weights, while host bounce covers only \
262 explicit stage-boundary transfers. Use {alt}; the \
263 MEMRA_PP_ALLOW_UNSPLIT_BATCH override is unavailable on a broken-peer host."
264 )
265 .into());
266 }
267 if pp_sharded_cross_device()
268 && std::env::var("MEMRA_PP_ALLOW_UNSPLIT_BATCH").as_deref() != Ok("1")
269 {
270 return Err(format!(
271 "{path}: refused with the ppN door open across 2+ devices — this path has no pp \
272 stage split, so it would walk ALL layers on one stream and peer-read every \
273 remote stage's weights each step (measured 28x slower at B=1, 13.9x at B=8 on \
274 a PRO 6000 pair over PCIe Gen5 x16 P2P; research/pp2-hardening-20260806). \
275 Exactness is unaffected — peer reads return identical bytes and the exactness \
276 gates PASS on this config — which is exactly why it must refuse instead of \
277 being caught by a gate. Fixes, in order: {alt}; or MEMRA_PP_SHARD=0 (all \
278 weights home on the primary — full speed, forfeits the capacity PP-2 exists \
279 for); or close the pp door. MEMRA_PP_ALLOW_UNSPLIT_BATCH=1 overrides for \
280 measurement."
281 )
282 .into());
283 }
284 Ok(())
285}
286
287pub fn batch_pp_on() -> bool {
295 std::env::var("MEMRA_BATCH_PP").as_deref() != Ok("0")
296}
297
298#[derive(Clone, Copy, PartialEq, Eq, Debug)]
316pub enum DualPpMode {
317 Off,
318 Forced,
319 Auto,
320}
321
322pub fn dual_pp_mode_resolve(v: Option<&str>) -> DualPpMode {
325 match v {
326 Some("0") => DualPpMode::Off,
327 Some("1") => DualPpMode::Forced,
328 _ => DualPpMode::Auto,
329 }
330}
331
332pub fn dual_pp_mode() -> DualPpMode {
333 dual_pp_mode_resolve(std::env::var("MEMRA_DUAL_PP").ok().as_deref())
334}
335
336pub fn dual_pp_on() -> bool {
339 dual_pp_mode() != DualPpMode::Off
340}
341
342pub fn dual_pp_route(
348 mode: DualPpMode,
349 batch: usize,
350 stages: usize,
351 double_slot: bool,
352 host_bounce: bool,
353) -> bool {
354 if batch < 2 {
355 return false;
356 }
357 match mode {
358 DualPpMode::Off => false,
359 DualPpMode::Forced => true,
360 DualPpMode::Auto => stages == 2 && double_slot && !host_bounce,
361 }
362}
363
364pub const DUAL_PP_SINGLE_SLOT_REFUSAL: &str = "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";
367pub const DUAL_PP_HOST_BOUNCE_REFUSAL: &str = "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(
384 "decode_step_batch_dual: refused: dual-active decode requires exactly two PP stages",
385 );
386 }
387 if !double_slot {
388 return Err(DUAL_PP_SINGLE_SLOT_REFUSAL);
389 }
390 if host_bounce {
391 return Err(DUAL_PP_HOST_BOUNCE_REFUSAL);
392 }
393 Ok(())
394}
395
396static DUAL_PP_OVERLAPS: AtomicUsize = AtomicUsize::new(0);
399static DUAL_PP_ACTIVE_STAGES: AtomicUsize = AtomicUsize::new(0);
400static DUAL_PP_STAGE_NS: [AtomicU64; 4] = [
401 AtomicU64::new(0),
402 AtomicU64::new(0),
403 AtomicU64::new(0),
404 AtomicU64::new(0),
405];
406static DUAL_PP_STAGE_SAMPLES: [AtomicUsize; 4] = [
407 AtomicUsize::new(0),
408 AtomicUsize::new(0),
409 AtomicUsize::new(0),
410 AtomicUsize::new(0),
411];
412static DUAL_PP_TIMING_DROPPED: AtomicUsize = AtomicUsize::new(0);
413static DUAL_PP_SLOT_PAIRS: AtomicUsize = AtomicUsize::new(0);
414static DUAL_PP_SLOT_USES: [AtomicUsize; 2] = [AtomicUsize::new(0), AtomicUsize::new(0)];
415static DUAL_PP_SLOT_COLLISIONS: AtomicUsize = AtomicUsize::new(0);
416
417pub const DUAL_PP_STAGE_NAMES: [&str; 4] = [
418 "wave_a_stage0",
419 "wave_a_stage1",
420 "wave_b_stage0",
421 "wave_b_stage1",
422];
423
424pub fn dual_pp_overlaps() -> usize {
425 DUAL_PP_OVERLAPS.load(Ordering::Relaxed)
426}
427
428pub(crate) fn record_dual_pp_slot_pair(slot_a: usize, slot_b: usize) -> bool {
432 debug_assert!(slot_a < DUAL_PP_SLOT_USES.len());
433 debug_assert!(slot_b < DUAL_PP_SLOT_USES.len());
434 if slot_a == slot_b {
435 DUAL_PP_SLOT_COLLISIONS.fetch_add(1, Ordering::Relaxed);
436 return false;
437 }
438 DUAL_PP_SLOT_USES[slot_a].fetch_add(1, Ordering::Relaxed);
439 DUAL_PP_SLOT_USES[slot_b].fetch_add(1, Ordering::Relaxed);
440 DUAL_PP_SLOT_PAIRS.fetch_add(1, Ordering::Relaxed);
441 true
442}
443
444pub fn dual_pp_slot_snapshot() -> (usize, [usize; 2], usize) {
446 (
447 DUAL_PP_SLOT_PAIRS.load(Ordering::Relaxed),
448 std::array::from_fn(|i| DUAL_PP_SLOT_USES[i].load(Ordering::Relaxed)),
449 DUAL_PP_SLOT_COLLISIONS.load(Ordering::Relaxed),
450 )
451}
452
453pub fn dual_pp_timing_on() -> bool {
457 static ON: OnceLock<bool> = OnceLock::new();
458 *ON.get_or_init(|| std::env::var("MEMRA_DUAL_PP_TIMING").as_deref() == Ok("1"))
459}
460
461pub(crate) fn record_dual_pp_stage_ms(stage: usize, ms: f32) {
462 assert!(
463 stage < DUAL_PP_STAGE_NS.len(),
464 "dual PP timing stage out of range"
465 );
466 let ns = (f64::from(ms) * 1_000_000.0).round() as u64;
467 DUAL_PP_STAGE_NS[stage].fetch_add(ns, Ordering::Relaxed);
468 DUAL_PP_STAGE_SAMPLES[stage].fetch_add(1, Ordering::Relaxed);
469}
470
471pub(crate) fn record_dual_pp_timing_drop(context: &str, err: &dyn std::fmt::Display) {
474 let previous = DUAL_PP_TIMING_DROPPED.fetch_add(1, Ordering::Relaxed);
475 if previous == 0 {
476 eprintln!(
477 "[dual-pp] WARN: skipped diagnostic timing sample at {context}: {err}; decode continues"
478 );
479 }
480}
481
482pub(crate) fn record_dual_pp_stage_result<E: std::fmt::Display>(
483 stage: usize,
484 elapsed: Result<f32, E>,
485) {
486 match elapsed {
487 Ok(ms) => record_dual_pp_stage_ms(stage, ms),
488 Err(err) => record_dual_pp_timing_drop(DUAL_PP_STAGE_NAMES[stage], &err),
489 }
490}
491
492pub fn dual_pp_timing_dropped() -> usize {
493 DUAL_PP_TIMING_DROPPED.load(Ordering::Relaxed)
494}
495
496pub fn dual_pp_timing_snapshot() -> ([u64; 4], [usize; 4]) {
498 (
499 std::array::from_fn(|i| DUAL_PP_STAGE_NS[i].load(Ordering::Relaxed)),
500 std::array::from_fn(|i| DUAL_PP_STAGE_SAMPLES[i].load(Ordering::Relaxed)),
501 )
502}
503
504pub(crate) struct DualPpStageGuard;
505
506pub(crate) fn enter_dual_pp_stage() -> DualPpStageGuard {
507 let active = DUAL_PP_ACTIVE_STAGES.fetch_add(1, Ordering::AcqRel);
508 if active > 0 {
509 DUAL_PP_OVERLAPS.fetch_add(1, Ordering::Relaxed);
510 }
511 DualPpStageGuard
512}
513
514impl Drop for DualPpStageGuard {
515 fn drop(&mut self) {
516 let active = DUAL_PP_ACTIVE_STAGES.fetch_sub(1, Ordering::AcqRel);
517 debug_assert!(active > 0, "dual PP active-stage counter underflow");
518 }
519}
520
521pub fn prime_pp_on() -> bool {
531 std::env::var("MEMRA_PRIME_PP").as_deref() != Ok("0")
532}
533
534pub fn prime_pipe_on() -> bool {
539 std::env::var("MEMRA_PRIME_PIPE").as_deref() != Ok("0")
540}
541
542pub static PRIME_SPLIT_CHUNKS: AtomicUsize = AtomicUsize::new(0);
549
550pub fn prime_split_chunks() -> usize {
552 PRIME_SPLIT_CHUNKS.load(Ordering::Relaxed)
553}
554
555pub static PRIME_PIPE_OVERLAPS: AtomicUsize = AtomicUsize::new(0);
560
561pub fn prime_pipe_overlaps() -> usize {
563 PRIME_PIPE_OVERLAPS.load(Ordering::Relaxed)
564}
565
566static PRIME_PIPE_ACTIVE_STAGES: AtomicUsize = AtomicUsize::new(0);
567
568pub(crate) struct PrimePipeStageGuard;
569
570pub(crate) fn enter_prime_pipe_stage() -> PrimePipeStageGuard {
573 let active = PRIME_PIPE_ACTIVE_STAGES.fetch_add(1, Ordering::AcqRel);
574 if active > 0 {
575 PRIME_PIPE_OVERLAPS.fetch_add(1, Ordering::Relaxed);
576 }
577 PrimePipeStageGuard
578}
579
580impl Drop for PrimePipeStageGuard {
581 fn drop(&mut self) {
582 let active = PRIME_PIPE_ACTIVE_STAGES.fetch_sub(1, Ordering::AcqRel);
583 debug_assert!(active > 0, "prime pipeline active-stage counter underflow");
584 }
585}
586
587pub static STEP35_PRIME_BATCHES: AtomicUsize = AtomicUsize::new(0);
591pub static STEP35_PRIME_BATCH_SPLITS: AtomicUsize = AtomicUsize::new(0);
592
593pub fn step35_prime_batches() -> usize {
594 STEP35_PRIME_BATCHES.load(Ordering::Relaxed)
595}
596
597pub fn step35_prime_batch_splits() -> usize {
598 STEP35_PRIME_BATCH_SPLITS.load(Ordering::Relaxed)
599}
600
601pub fn spec_pp_on() -> bool {
609 std::env::var("MEMRA_SPEC_PP").as_deref() != Ok("0")
610}
611
612pub fn pp2_overlap() -> bool {
623 pp2_overlap_resolve(
624 std::env::var("MEMRA_PP_OVERLAP").ok().as_deref(),
625 dual_pp_mode(),
626 )
627}
628
629pub fn pp2_overlap_resolve(v: Option<&str>, mode: DualPpMode) -> bool {
632 match v {
633 Some("1") => true,
634 Some(_) => false,
635 None => mode == DualPpMode::Auto,
636 }
637}
638
639pub fn pp_host_bounce_on() -> bool {
642 matches!(std::env::var("MEMRA_PP_HOST_BOUNCE").as_deref(), Ok("1"))
643}
644
645pub fn pp_host_bounce_active() -> bool {
648 (pp_host_bounce_on() || PEER_RUNTIME_HOST_BOUNCE.load(Ordering::Acquire))
649 && pp_sharded_cross_device()
650}
651
652pub fn pp_shard_off() -> bool {
656 matches!(std::env::var("MEMRA_PP_SHARD").as_deref(), Ok("0"))
657}
658
659fn pp2_devices_env() -> Option<String> {
662 std::env::var("MEMRA_PP_DEVICES")
663 .ok()
664 .filter(|v| !v.is_empty())
665}
666
667static WARNED_BAD: AtomicBool = AtomicBool::new(false);
668fn warn_bad_once(msg: &str) {
669 if !WARNED_BAD.swap(true, Ordering::Relaxed) {
670 eprintln!("[pp] {msg}");
671 }
672}
673
674static WARNED_UNWIRED: AtomicBool = AtomicBool::new(false);
675pub fn warn_unwired_once(path: &str) {
678 let open = std::env::var("MEMRA_PP_STAGES")
679 .map(|v| !v.is_empty() && v != "0" && v != "1")
680 .unwrap_or(false);
681 if open && !WARNED_UNWIRED.swap(true, Ordering::Relaxed) {
682 eprintln!("[pp] MEMRA_PP_STAGES set but `{path}` has no pp arm at this N; running unsplit");
683 }
684}
685
686pub struct StageRt {
694 pub dev: usize,
695 pub ctx: Arc<CudaContext>,
696 pub stream: Arc<CudaStream>,
697 pub blas: Arc<cudarc::cublaslt::CudaBlasLT>,
698 engine: Option<Engine>,
700}
701
702struct BoundarySlot {
707 buf: Mutex<Option<CudaSlice<f32>>>,
708 ev_tx: CudaEvent,
711 ev_rx: CudaEvent,
715}
716
717struct BoundaryRt {
721 slots: [BoundarySlot; 2],
722 step: AtomicUsize,
723 cross: bool,
725}
726
727#[derive(Clone, Copy, Debug, PartialEq, Eq)]
728enum BoundaryTransport {
729 Local,
730 Peer,
731 HostBounce,
732}
733
734#[derive(Clone, Copy)]
735struct BoundaryPath {
736 boundary: usize,
737 src_stage: usize,
738 dst_stage: usize,
739 transport: BoundaryTransport,
740}
741
742fn boundary_transport(cross: bool, host_bounce: bool) -> BoundaryTransport {
743 match (cross, host_bounce) {
744 (false, _) => BoundaryTransport::Local,
745 (true, false) => BoundaryTransport::Peer,
746 (true, true) => BoundaryTransport::HostBounce,
747 }
748}
749
750const PEER_PROBE_FIXED_BYTES: usize = 16 * 1024;
751const PEER_PROBE_TOKEN_WIDTHS: [usize; 4] = [1, 8, 16, crate::cache::PRIME_CHUNK_MAX_TOKENS];
752
753pub const PEER_RUNTIME_PROBE_INTERVAL_COPIES: u64 = 8 * 1024;
756pub const PEER_RUNTIME_PROBE_CYCLE_COPIES: u64 =
758 PEER_RUNTIME_PROBE_INTERVAL_COPIES * PEER_PROBE_TOKEN_WIDTHS.len() as u64;
759pub const PEER_RUNTIME_PROBE_DEFERRAL_BOUND_INTERVALS: u64 = PEER_PROBE_TOKEN_WIDTHS.len() as u64;
762const PEER_RUNTIME_PROBE_BUDGET_NS: u64 = 5_000_000;
764
765pub const PEER_PROBE_REQUIRED_REFUSAL: &str = "PP bring-up refused: MEMRA_PEER_PROBE=0 cannot authorize native peer transport for a \
766 sharded cross-device placement while MEMRA_PP_HOST_BOUNCE!=1; leave MEMRA_PEER_PROBE \
767 enabled or set MEMRA_PP_HOST_BOUNCE=1";
768
769#[derive(Clone, Copy, Debug, PartialEq, Eq)]
770pub enum PeerProbeStartupPolicy {
771 Allowed,
772 BypassedWithHostBounce,
773}
774
775pub fn peer_probe_startup_policy(
778 probe_on: bool,
779 sharded_cross_device: bool,
780 host_bounce: bool,
781) -> Result<PeerProbeStartupPolicy, &'static str> {
782 match (probe_on, sharded_cross_device, host_bounce) {
783 (false, true, false) => Err(PEER_PROBE_REQUIRED_REFUSAL),
784 (false, true, true) => Ok(PeerProbeStartupPolicy::BypassedWithHostBounce),
785 _ => Ok(PeerProbeStartupPolicy::Allowed),
786 }
787}
788
789static PEER_PROBE_BYPASSED: AtomicU64 = AtomicU64::new(0);
790static PEER_BOUNDARY_COPIES: AtomicU64 = AtomicU64::new(0);
791static PEER_RUNTIME_PROBES: AtomicU64 = AtomicU64::new(0);
792static PEER_RUNTIME_PROBE_FAILURES: AtomicU64 = AtomicU64::new(0);
793static PEER_RUNTIME_PROBE_DEFERRED: AtomicU64 = AtomicU64::new(0);
794static PEER_RUNTIME_PROBE_INTEGRITY_DEGRADED: AtomicBool = AtomicBool::new(false);
795static PEER_RUNTIME_PROBE_FAILED: AtomicBool = AtomicBool::new(false);
796static PEER_RUNTIME_HOST_BOUNCE: AtomicBool = AtomicBool::new(false);
797static PEER_RUNTIME_NEXT_PROBE_COPY: [AtomicU64; PEER_PROBE_TOKEN_WIDTHS.len()] = [
798 AtomicU64::new(PEER_RUNTIME_PROBE_INTERVAL_COPIES),
799 AtomicU64::new(2 * PEER_RUNTIME_PROBE_INTERVAL_COPIES),
800 AtomicU64::new(3 * PEER_RUNTIME_PROBE_INTERVAL_COPIES),
801 AtomicU64::new(4 * PEER_RUNTIME_PROBE_INTERVAL_COPIES),
802];
803static PEER_RUNTIME_PROBE_MAX_COST_NS: [AtomicU64; PEER_PROBE_TOKEN_WIDTHS.len()] = [
804 AtomicU64::new(0),
805 AtomicU64::new(0),
806 AtomicU64::new(0),
807 AtomicU64::new(0),
808];
809
810#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
811pub struct PeerProbeMetrics {
812 pub bypassed: u64,
813 pub boundary_copies: u64,
814 pub runtime_probes: u64,
815 pub runtime_failures: u64,
816 pub deferred_total: u64,
817 pub integrity_degraded: bool,
818 pub degraded_to_host_bounce: bool,
819}
820
821pub fn peer_probe_metrics() -> PeerProbeMetrics {
822 PeerProbeMetrics {
823 bypassed: PEER_PROBE_BYPASSED.load(Ordering::Relaxed),
824 boundary_copies: PEER_BOUNDARY_COPIES.load(Ordering::Relaxed),
825 runtime_probes: PEER_RUNTIME_PROBES.load(Ordering::Relaxed),
826 runtime_failures: PEER_RUNTIME_PROBE_FAILURES.load(Ordering::Relaxed),
827 deferred_total: PEER_RUNTIME_PROBE_DEFERRED.load(Ordering::Relaxed),
828 integrity_degraded: PEER_RUNTIME_PROBE_INTEGRITY_DEGRADED.load(Ordering::Acquire),
829 degraded_to_host_bounce: PEER_RUNTIME_HOST_BOUNCE.load(Ordering::Acquire),
830 }
831}
832
833#[derive(Clone, Copy, Debug, PartialEq, Eq)]
834pub enum RuntimePeerProbeStatus {
835 NotRun,
836 Deferred,
837 Passed,
838 DegradedToHostBounce,
839}
840
841impl RuntimePeerProbeStatus {
842 pub fn ran(self) -> bool {
843 matches!(self, Self::Passed | Self::DegradedToHostBounce)
844 }
845}
846
847fn publish_runtime_peer_probe_deferral(
848 deferred_total: &AtomicU64,
849 integrity_degraded: &AtomicBool,
850 intervals: u64,
851 bound_reached: bool,
852) {
853 deferred_total.fetch_add(intervals, Ordering::Relaxed);
854 if bound_reached {
855 integrity_degraded.store(true, Ordering::Release);
856 }
857}
858
859pub fn record_runtime_peer_probe_deferral(intervals: u64, bound_reached: bool) {
862 publish_runtime_peer_probe_deferral(
863 &PEER_RUNTIME_PROBE_DEFERRED,
864 &PEER_RUNTIME_PROBE_INTEGRITY_DEGRADED,
865 intervals,
866 bound_reached,
867 );
868}
869
870pub fn clear_runtime_peer_probe_integrity_degraded() {
872 PEER_RUNTIME_PROBE_INTEGRITY_DEGRADED.store(false, Ordering::Release);
873}
874
875fn runtime_peer_probe_idle_only(width_index: usize, measured_cost_ns: u64) -> bool {
876 width_index + 1 == PEER_PROBE_TOKEN_WIDTHS.len()
877 || measured_cost_ns > PEER_RUNTIME_PROBE_BUDGET_NS
878}
879
880fn runtime_peer_probe_candidate(
883 copies: u64,
884 next_probe_copy: [u64; PEER_PROBE_TOKEN_WIDTHS.len()],
885 measured_cost_ns: [u64; PEER_PROBE_TOKEN_WIDTHS.len()],
886 scheduler_idle: bool,
887) -> Option<(usize, usize)> {
888 let mut selected: Option<(usize, u64)> = None;
889 for width_index in 0..PEER_PROBE_TOKEN_WIDTHS.len() {
890 let due = next_probe_copy[width_index];
891 if copies < due
892 || (!scheduler_idle
893 && runtime_peer_probe_idle_only(width_index, measured_cost_ns[width_index]))
894 {
895 continue;
896 }
897 if selected.is_none_or(|(_, selected_due)| due < selected_due) {
898 selected = Some((width_index, due));
899 }
900 }
901 selected.map(|(width_index, _)| (width_index, PEER_PROBE_TOKEN_WIDTHS[width_index]))
902}
903
904fn runtime_peer_probe_next_copy(due: u64, copies: u64) -> u64 {
907 let cycles = copies.saturating_sub(due) / PEER_RUNTIME_PROBE_CYCLE_COPIES + 1;
908 due.saturating_add(PEER_RUNTIME_PROBE_CYCLE_COPIES.saturating_mul(cycles))
909}
910
911fn latch_runtime_host_bounce<E>(
914 native_failed: &AtomicBool,
915 degraded_to_host_bounce: &AtomicBool,
916 arm_and_validate: impl FnOnce() -> Result<(), E>,
917) -> Result<(), E> {
918 native_failed.store(true, Ordering::Release);
919 arm_and_validate()?;
920 degraded_to_host_bounce.store(true, Ordering::Release);
921 Ok(())
922}
923
924fn peer_probe_on() -> bool {
925 std::env::var("MEMRA_PEER_PROBE").as_deref() != Ok("0")
926}
927
928#[derive(Clone, Copy, Debug, PartialEq, Eq)]
929enum PeerProbeDecision {
930 Clean,
931 ProceedWithHostBounce { mismatches: usize },
932}
933
934fn peer_probe_mismatch_count(expected: &[u8], readback: &[u8]) -> usize {
935 expected
936 .iter()
937 .zip(readback)
938 .filter(|(a, b)| a != b)
939 .count()
940 + expected.len().abs_diff(readback.len())
941}
942
943fn peer_probe_decision(
944 expected: &[u8],
945 readback: &[u8],
946 host_bounce: bool,
947) -> Result<PeerProbeDecision, String> {
948 let mismatches = peer_probe_mismatch_count(expected, readback);
949 if mismatches == 0 {
950 Ok(PeerProbeDecision::Clean)
951 } else if host_bounce {
952 Ok(PeerProbeDecision::ProceedWithHostBounce { mismatches })
953 } else {
954 Err(format!("{mismatches} mismatched byte(s)"))
955 }
956}
957
958fn peer_probe_pattern(bytes: usize, boundary: usize, src_dev: usize, dst_dev: usize) -> Vec<u8> {
959 let mut state = 0xD1B5_4A32_D192_ED03u64
960 ^ (bytes as u64).rotate_left(7)
961 ^ (boundary as u64).rotate_left(19)
962 ^ (src_dev as u64).rotate_left(31)
963 ^ (dst_dev as u64).rotate_left(43);
964 (0..bytes)
965 .map(|_| {
966 state ^= state << 13;
967 state ^= state >> 7;
968 state ^= state << 17;
969 state as u8
970 })
971 .collect()
972}
973
974fn peer_probe_bytes_to_f32(bytes: &[u8]) -> Vec<f32> {
975 assert_eq!(bytes.len() % std::mem::size_of::<f32>(), 0);
976 bytes
977 .chunks_exact(std::mem::size_of::<f32>())
978 .map(|chunk| f32::from_bits(u32::from_ne_bytes(chunk.try_into().unwrap())))
979 .collect()
980}
981
982fn peer_probe_f32_to_bytes(values: &[f32]) -> Vec<u8> {
983 values
984 .iter()
985 .flat_map(|value| value.to_bits().to_ne_bytes())
986 .collect()
987}
988
989struct PeerProbeBuffer {
993 ctx: Arc<CudaContext>,
994 ptr: cudarc::driver::sys::CUdeviceptr,
995}
996
997impl PeerProbeBuffer {
998 fn new(ctx: &Arc<CudaContext>, bytes: usize) -> Result<Self, Box<dyn std::error::Error>> {
999 ctx.bind_to_thread()?;
1000 let ptr = unsafe { cudarc::driver::result::malloc_sync(bytes)? };
1001 Ok(Self {
1002 ctx: ctx.clone(),
1003 ptr,
1004 })
1005 }
1006}
1007
1008impl Drop for PeerProbeBuffer {
1009 fn drop(&mut self) {
1010 if self.ctx.bind_to_thread().is_ok() {
1011 let _ = unsafe { cudarc::driver::result::free_sync(self.ptr) };
1012 }
1013 }
1014}
1015
1016fn peer_probe_copy(
1017 src: &StageRt,
1018 dst: &StageRt,
1019 expected: &[u8],
1020) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
1021 let bytes = expected.len();
1022 let src_buf = PeerProbeBuffer::new(&src.ctx, bytes)?;
1023 unsafe {
1024 cudarc::driver::result::memcpy_htod_sync(src_buf.ptr, expected)?;
1025 }
1026
1027 let dst_buf = PeerProbeBuffer::new(&dst.ctx, bytes)?;
1028 let poison: Vec<u8> = expected.iter().map(|b| !b).collect();
1029 unsafe {
1030 cudarc::driver::result::memcpy_htod_sync(dst_buf.ptr, &poison)?;
1031 }
1032
1033 src.ctx.bind_to_thread()?;
1034 unsafe {
1035 cudarc::driver::result::memcpy_peer_async(
1036 dst.ctx.cu_ctx(),
1037 dst_buf.ptr,
1038 src.ctx.cu_ctx(),
1039 src_buf.ptr,
1040 bytes,
1041 src.stream.cu_stream(),
1042 )?;
1043 }
1044 src.stream.synchronize()?;
1045
1046 dst.ctx.bind_to_thread()?;
1047 let mut readback = vec![0u8; bytes];
1048 unsafe {
1049 cudarc::driver::result::memcpy_dtoh_sync(&mut readback, dst_buf.ptr)?;
1050 }
1051 Ok(readback)
1052}
1053
1054fn run_peer_probe_pass(
1055 stages: &[StageRt],
1056 peer_capable: &[(usize, usize)],
1057 host_bounce: bool,
1058 label: &str,
1059 bytes: usize,
1060) -> Result<(), Box<dyn std::error::Error>> {
1061 if bytes == 0 {
1062 return Err(format!("PP peer byte-integrity probe {label} size is zero").into());
1063 }
1064 let started = std::time::Instant::now();
1065 let mut copies = 0usize;
1066 let mut skipped = 0usize;
1067 let mut total_mismatches = 0usize;
1068
1069 for boundary in 0..stages.len() - 1 {
1070 if stages[boundary].dev == stages[boundary + 1].dev {
1071 continue;
1072 }
1073 for (src_idx, dst_idx) in [(boundary, boundary + 1), (boundary + 1, boundary)] {
1074 let src = &stages[src_idx];
1075 let dst = &stages[dst_idx];
1076 if !peer_capable.contains(&(src.dev, dst.dev)) {
1077 if host_bounce {
1078 skipped += 1;
1079 eprintln!(
1080 "[pp] peer byte-integrity probe SKIP: boundary={boundary} \
1081 dev{}->dev{} label={label} bytes={bytes} (peer capability unavailable; \
1082 MEMRA_PP_HOST_BOUNCE=1 remains fail-safe)",
1083 src.dev, dst.dev,
1084 );
1085 continue;
1086 }
1087 return Err(format!(
1088 "PP peer byte-integrity probe cannot run boundary={boundary} \
1089 dev{}->dev{}: peer access was not enabled",
1090 src.dev, dst.dev,
1091 )
1092 .into());
1093 }
1094
1095 let expected = peer_probe_pattern(bytes, boundary, src.dev, dst.dev);
1096 let readback = match peer_probe_copy(src, dst, &expected) {
1097 Ok(readback) => readback,
1098 Err(err) if host_bounce => {
1099 skipped += 1;
1100 eprintln!(
1101 "[pp] peer byte-integrity probe ERROR: boundary={boundary} \
1102 dev{}->dev{} label={label} bytes={bytes}: {err}; \
1103 MEMRA_PP_HOST_BOUNCE=1, proceeding on the host-staged path",
1104 src.dev, dst.dev,
1105 );
1106 continue;
1107 }
1108 Err(err) => {
1109 return Err(format!(
1110 "PP peer byte-integrity probe FAILED: boundary={boundary} \
1111 dev{}->dev{} label={label} bytes={bytes}: {err}; refusing native P2P \
1112 (set MEMRA_PP_HOST_BOUNCE=1 to use the host-staged path; \
1113 MEMRA_PEER_PROBE=0 cannot authorize sharded native peer transport)",
1114 src.dev, dst.dev,
1115 )
1116 .into());
1117 }
1118 };
1119 copies += 1;
1120 match peer_probe_decision(&expected, &readback, host_bounce) {
1121 Ok(PeerProbeDecision::Clean) => {}
1122 Ok(PeerProbeDecision::ProceedWithHostBounce { mismatches }) => {
1123 total_mismatches += mismatches;
1124 eprintln!(
1125 "[pp] peer byte-integrity probe CORRUPTION: boundary={boundary} \
1126 dev{}->dev{} label={label} bytes={bytes} mismatches={mismatches}; \
1127 MEMRA_PP_HOST_BOUNCE=1, proceeding on the host-staged path",
1128 src.dev, dst.dev,
1129 );
1130 }
1131 Err(mismatch) => {
1132 return Err(format!(
1133 "PP peer byte-integrity probe FAILED: boundary={boundary} \
1134 dev{}->dev{} label={label} bytes={bytes}: {mismatch}; refusing native \
1135 P2P (set MEMRA_PP_HOST_BOUNCE=1 to use the host-staged path; \
1136 MEMRA_PEER_PROBE=0 cannot authorize sharded native peer transport)",
1137 src.dev, dst.dev,
1138 )
1139 .into());
1140 }
1141 }
1142 }
1143 }
1144
1145 let status = if total_mismatches > 0 {
1146 "BOUNCE"
1147 } else if skipped > 0 && copies > 0 {
1148 "PARTIAL"
1149 } else if skipped > 0 {
1150 "SKIP"
1151 } else {
1152 "PASS"
1153 };
1154 eprintln!(
1155 "[pp] peer byte-integrity probe {}: label={label} bytes={bytes} copies={copies} \
1156 skipped={skipped} mismatches={total_mismatches} elapsed_ms={:.3}",
1157 status,
1158 started.elapsed().as_secs_f64() * 1e3,
1159 );
1160 Ok(())
1161}
1162
1163fn host_bounce_capacity(n_embd: usize) -> Result<(usize, usize), String> {
1164 if n_embd == 0 {
1165 return Err("MEMRA_PP_HOST_BOUNCE needs non-zero model n_embd".into());
1166 }
1167 let elems = n_embd
1168 .checked_mul(crate::cache::PRIME_CHUNK_MAX_TOKENS)
1169 .ok_or_else(|| format!("host-bounce element count overflows for n_embd={n_embd}"))?;
1170 let bytes = elems
1171 .checked_mul(std::mem::size_of::<f32>())
1172 .ok_or_else(|| format!("host-bounce byte count overflows for n_embd={n_embd}"))?;
1173 Ok((elems, bytes))
1174}
1175
1176struct PinnedHostBounce {
1181 ptr: *mut f32,
1182 len: usize,
1183}
1184
1185unsafe impl Send for PinnedHostBounce {}
1186unsafe impl Sync for PinnedHostBounce {}
1187
1188impl PinnedHostBounce {
1189 fn new(len: usize) -> Result<Self, Box<dyn std::error::Error>> {
1190 let bytes = len
1191 .checked_mul(std::mem::size_of::<f32>())
1192 .ok_or("host-bounce pinned allocation size overflow")?;
1193 let ptr = unsafe {
1194 cudarc::driver::result::malloc_host(
1195 bytes,
1196 cudarc::driver::sys::CU_MEMHOSTALLOC_PORTABLE,
1197 )?
1198 } as *mut f32;
1199 if ptr.is_null() {
1200 return Err("cuMemHostAlloc returned a null host-bounce pointer".into());
1201 }
1202 Ok(Self { ptr, len })
1203 }
1204
1205 fn prefix(&self, n: usize) -> &[f32] {
1206 assert!(
1207 n <= self.len,
1208 "host-bounce source {n} > capacity {}",
1209 self.len
1210 );
1211 unsafe { std::slice::from_raw_parts(self.ptr, n) }
1212 }
1213
1214 fn prefix_mut(&mut self, n: usize) -> &mut [f32] {
1215 assert!(
1216 n <= self.len,
1217 "host-bounce destination {n} > capacity {}",
1218 self.len
1219 );
1220 unsafe { std::slice::from_raw_parts_mut(self.ptr, n) }
1221 }
1222}
1223
1224impl Drop for PinnedHostBounce {
1225 fn drop(&mut self) {
1226 let _ = unsafe { cudarc::driver::result::free_host(self.ptr.cast()) };
1227 }
1228}
1229
1230struct HostBounceRt {
1231 n_embd: usize,
1232 capacity: usize,
1233 slots: Vec<Option<[Mutex<PinnedHostBounce>; 2]>>,
1234}
1235
1236impl HostBounceRt {
1237 fn new(n_embd: usize, boundaries: &[BoundaryRt]) -> Result<Self, Box<dyn std::error::Error>> {
1238 let (capacity, _) = host_bounce_capacity(n_embd)?;
1239 let mut slots = Vec::with_capacity(boundaries.len());
1240 for boundary in boundaries {
1241 slots.push(if boundary.cross {
1242 Some([
1243 Mutex::new(PinnedHostBounce::new(capacity)?),
1244 Mutex::new(PinnedHostBounce::new(capacity)?),
1245 ])
1246 } else {
1247 None
1248 });
1249 }
1250 Ok(Self {
1251 n_embd,
1252 capacity,
1253 slots,
1254 })
1255 }
1256
1257 fn slot(
1258 &self,
1259 boundary: usize,
1260 slot: usize,
1261 ) -> Result<&Mutex<PinnedHostBounce>, Box<dyn std::error::Error>> {
1262 self.slots
1263 .get(boundary)
1264 .and_then(Option::as_ref)
1265 .and_then(|slots| slots.get(slot))
1266 .ok_or_else(|| format!("host-bounce slot {boundary}:{slot} is not initialized").into())
1267 }
1268}
1269
1270pub struct PpNRt {
1271 stages: Vec<StageRt>,
1272 boundaries: Vec<BoundaryRt>,
1273 cross_any: bool,
1275 host_bounce: bool,
1278 peer_probe: bool,
1280 peer_capable: Vec<(usize, usize)>,
1282 peer_probe_geometry: OnceLock<Result<usize, String>>,
1284 bounce: OnceLock<Result<HostBounceRt, String>>,
1286 readback: Arc<CudaStream>,
1289}
1290
1291pub type Pp2Rt = PpNRt;
1293
1294static RTN: OnceLock<Result<PpNRt, String>> = OnceLock::new();
1295
1296impl PpNRt {
1297 pub fn get(e: &Engine) -> Result<&'static PpNRt, Box<dyn std::error::Error>> {
1301 RTN.get_or_init(|| Self::build(e).map_err(|err| err.to_string()))
1302 .as_ref()
1303 .map_err(|s| -> Box<dyn std::error::Error> { s.clone().into() })
1304 }
1305
1306 fn build(e: &Engine) -> Result<PpNRt, Box<dyn std::error::Error>> {
1307 let primary_dev = e.ctx().ordinal();
1308 let devices: Vec<usize> =
1311 match pp2_devices_env() {
1312 Some(s) => {
1313 let parts: Result<Vec<usize>, _> =
1314 s.split(',').map(|p| p.trim().parse::<usize>()).collect();
1315 match parts {
1316 Ok(v) if v.len() >= 2 => v,
1317 _ => return Err(format!(
1318 "MEMRA_PP_DEVICES={s} unparseable (want <d0>,..,<dN-1> e.g. 0,1,2,3)"
1319 )
1320 .into()),
1321 }
1322 }
1323 None => {
1324 let n_st = std::env::var("MEMRA_PP_STAGES")
1325 .ok()
1326 .and_then(|v| v.parse::<usize>().ok())
1327 .filter(|&n| n >= 2)
1328 .unwrap_or(2);
1329 vec![primary_dev; n_st]
1330 }
1331 };
1332 if let Ok(v) = std::env::var("MEMRA_PP_STAGES") {
1333 if let Ok(n) = v.parse::<usize>() {
1334 if n >= 2 && n != devices.len() {
1335 return Err(format!(
1336 "MEMRA_PP_DEVICES lists {} devices but MEMRA_PP_STAGES={n} — \
1337 refusing an ambiguous placement",
1338 devices.len()
1339 )
1340 .into());
1341 }
1342 }
1343 }
1344 let n_st = devices.len();
1345 let cross_any = devices.iter().any(|&d| d != devices[0]);
1346 let host_bounce = pp_host_bounce_on();
1347 let peer_probe = peer_probe_on();
1348 let sharded_cross_device = cross_any && !pp_shard_off();
1349 if host_bounce && cross_any {
1350 if pp_shard_off() {
1351 return Err(
1352 "MEMRA_PP_HOST_BOUNCE=1 refuses MEMRA_PP_SHARD=0: the boundary can bounce, \
1353 but remote stages would still peer-read primary-device weights"
1354 .into(),
1355 );
1356 }
1357 if devices.last().copied() != Some(primary_dev) {
1358 return Err(format!(
1359 "MEMRA_PP_HOST_BOUNCE=1 requires the primary engine on the last/head stage \
1360 (primary dev{primary_dev}, placement {devices:?}); otherwise returned \
1361 logits/hidden state remain peer reads"
1362 )
1363 .into());
1364 }
1365 }
1366 let peer_probe_policy =
1367 peer_probe_startup_policy(peer_probe, sharded_cross_device, host_bounce)?;
1368 if peer_probe_policy == PeerProbeStartupPolicy::BypassedWithHostBounce {
1369 PEER_PROBE_BYPASSED.fetch_add(1, Ordering::Relaxed);
1370 eprintln!(
1371 "[pp] SECURITY RED: peer_probe_bypassed: MEMRA_PEER_PROBE=0 on a sharded \
1372 cross-device placement; MEMRA_PP_HOST_BOUNCE=1 is the only enabled transport"
1373 );
1374 }
1375
1376 let mut used: Vec<usize> = devices.clone();
1380 used.push(primary_dev);
1381 used.sort_unstable();
1382 used.dedup();
1383 let mut peer_capable = Vec::new();
1384 if used.len() > 1 {
1385 let n = cudarc::driver::result::device::get_count()? as usize;
1386 for &d in &used {
1387 if d >= n {
1388 return Err(format!(
1389 "MEMRA_PP_DEVICES={devices:?} but only {n} CUDA device(s) present"
1390 )
1391 .into());
1392 }
1393 }
1394 if !host_bounce || peer_probe {
1395 for &a in &used {
1396 for &b in &used {
1397 if a == b {
1398 continue;
1399 }
1400 let da = cudarc::driver::result::device::get(a as i32)?;
1401 let db = cudarc::driver::result::device::get(b as i32)?;
1402 let mut can: i32 = 0;
1403 let capability = unsafe {
1404 cudarc::driver::sys::cuDeviceCanAccessPeer(&mut can, da, db).result()
1405 };
1406 if let Err(err) = capability {
1407 if host_bounce {
1408 eprintln!(
1409 "[pp] peer byte-integrity probe capability query failed for \
1410 dev{a}->dev{b}: {err}; MEMRA_PP_HOST_BOUNCE=1 remains active"
1411 );
1412 continue;
1413 }
1414 return Err(err.into());
1415 }
1416 if can == 0 {
1417 if !host_bounce {
1418 return Err(format!(
1419 "device {a} cannot peer-access device {b} \
1420 (cuDeviceCanAccessPeer=0); ppN cross-device needs P2P — \
1421 refusing a silently-staged path"
1422 )
1423 .into());
1424 }
1425 } else {
1426 peer_capable.push((a, b));
1427 }
1428 }
1429 }
1430 }
1431 }
1432
1433 let mk_stage = |dev: usize, s: usize| -> Result<StageRt, Box<dyn std::error::Error>> {
1446 if dev == primary_dev && s == 0 {
1447 let ctx = e.ctx().clone();
1448 let stream = ctx.new_stream()?;
1449 let blas = Arc::new(cudarc::cublaslt::CudaBlasLT::new(stream.clone())?);
1450 Ok(StageRt {
1451 dev,
1452 ctx,
1453 stream,
1454 blas,
1455 engine: None,
1456 })
1457 } else {
1458 let eng = Engine::new(dev)?;
1459 let ctx = eng.ctx().clone();
1460 let stream = ctx.new_stream()?;
1461 let blas = Arc::new(cudarc::cublaslt::CudaBlasLT::new(stream.clone())?);
1462 Ok(StageRt {
1463 dev,
1464 ctx,
1465 stream,
1466 blas,
1467 engine: Some(eng),
1468 })
1469 }
1470 };
1471 let mut stages = Vec::with_capacity(n_st);
1472 for (s, &d) in devices.iter().enumerate() {
1473 stages.push(mk_stage(d, s)?);
1474 }
1475
1476 if cross_any
1477 && !peer_probe
1478 && peer_probe_policy != PeerProbeStartupPolicy::BypassedWithHostBounce
1479 {
1480 eprintln!(
1481 "[pp] WARNING: MEMRA_PEER_PROBE=0 skips the boot-time peer byte-integrity \
1482 gate; diagnostics escape hatch active"
1483 );
1484 }
1485
1486 if used.len() > 1 {
1487 if !host_bounce {
1488 let ctx_of = |d: usize| -> &Arc<CudaContext> {
1491 if d == primary_dev {
1492 e.ctx()
1493 } else {
1494 &stages.iter().find(|s| s.dev == d).unwrap().ctx
1495 }
1496 };
1497 for &a in &used {
1500 for &b in &used {
1501 if a == b {
1502 continue;
1503 }
1504 ctx_of(a).bind_to_thread()?;
1505 let rc = unsafe {
1506 cudarc::driver::sys::cuCtxEnablePeerAccess(ctx_of(b).cu_ctx(), 0)
1507 };
1508 use cudarc::driver::sys::cudaError_enum as E;
1509 if rc != E::CUDA_SUCCESS && rc != E::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED
1510 {
1511 return Err(format!(
1512 "cuCtxEnablePeerAccess(dev{a} -> dev{b}) failed: {rc:?}"
1513 )
1514 .into());
1515 }
1516 }
1517 }
1518 if peer_probe && cross_any {
1522 let probe = run_peer_probe_pass(
1523 &stages,
1524 &peer_capable,
1525 host_bounce,
1526 "fixed-16KiB",
1527 PEER_PROBE_FIXED_BYTES,
1528 );
1529 e.ctx().bind_to_thread()?;
1530 probe?;
1531 }
1532 for &owner in &used {
1541 for &accessor in &used {
1542 if owner == accessor {
1543 continue;
1544 }
1545 let dev = cudarc::driver::result::device::get(owner as i32)?;
1546 let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
1547 unsafe {
1548 cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, dev)
1549 .result()?;
1550 }
1551 let desc = cudarc::driver::sys::CUmemAccessDesc {
1552 location: cudarc::driver::sys::CUmemLocation {
1553 type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
1554 id: accessor as i32,
1555 },
1556 flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
1557 };
1558 let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
1559 if rc != cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
1560 return Err(format!(
1561 "cuMemPoolSetAccess(dev{owner} pool -> dev{accessor}) failed: {rc:?}"
1562 )
1563 .into());
1564 }
1565 }
1566 }
1567 for (owner, accessor) in [
1576 (stages[0].dev, stages[1].dev),
1577 (stages[1].dev, stages[0].dev),
1578 ] {
1579 let dev = cudarc::driver::result::device::get(owner as i32)?;
1580 let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
1581 unsafe {
1582 cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, dev).result()?;
1583 }
1584 let desc = cudarc::driver::sys::CUmemAccessDesc {
1585 location: cudarc::driver::sys::CUmemLocation {
1586 type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
1587 id: accessor as i32,
1588 },
1589 flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
1590 };
1591 let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
1592 if rc != cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
1593 return Err(format!(
1594 "cuMemPoolSetAccess(dev{owner} pool -> dev{accessor}) failed: {rc:?}"
1595 )
1596 .into());
1597 }
1598 }
1599 e.ctx().bind_to_thread()?;
1601 eprintln!(
1602 "[pp] cross-device transport: {} (cudaMemcpyPeerAsync per cross boundary; \
1603 peer + default-pool access granted all pairs over {used:?}; weight home: {})",
1604 devices
1605 .iter()
1606 .enumerate()
1607 .map(|(s, d)| format!("stage{s}=dev{d}"))
1608 .collect::<Vec<_>>()
1609 .join(" "),
1610 if pp_shard_off() {
1611 format!("dev{primary_dev} (MEMRA_PP_SHARD=0 bring-up placement)")
1612 } else {
1613 "per-stage (sharded loader)".to_string()
1614 }
1615 );
1616 } else {
1617 e.ctx().bind_to_thread()?;
1618 eprintln!(
1619 "[pp] cross-device transport: {} (HOST-STAGED pinned D2H -> H2D per cross \
1620 boundary; MEMRA_PP_HOST_BOUNCE=1; peer-pool grants bypassed; \
1621 diagnostic peer access is removed before host-staged serving; \
1622 weight home: per-stage (sharded loader))",
1623 devices
1624 .iter()
1625 .enumerate()
1626 .map(|(s, d)| format!("stage{s}=dev{d}"))
1627 .collect::<Vec<_>>()
1628 .join(" "),
1629 );
1630 }
1631 }
1632
1633 let mk_slot =
1634 |tx: &StageRt, rx: &StageRt| -> Result<BoundarySlot, Box<dyn std::error::Error>> {
1635 Ok(BoundarySlot {
1636 buf: Mutex::new(None),
1637 ev_tx: tx.ctx.new_event(None)?,
1638 ev_rx: rx.ctx.new_event(None)?,
1639 })
1640 };
1641 let mut boundaries = Vec::with_capacity(n_st - 1);
1642 for b in 0..n_st - 1 {
1643 let (tx, rx) = (&stages[b], &stages[b + 1]);
1644 boundaries.push(BoundaryRt {
1645 slots: [mk_slot(tx, rx)?, mk_slot(tx, rx)?],
1646 step: AtomicUsize::new(0),
1647 cross: tx.dev != rx.dev,
1648 });
1649 }
1650 let readback = stages[n_st - 1].ctx.new_stream()?;
1651 let rt = PpNRt {
1652 stages,
1653 boundaries,
1654 cross_any,
1655 host_bounce,
1656 peer_probe,
1657 peer_capable,
1658 peer_probe_geometry: OnceLock::new(),
1659 bounce: OnceLock::new(),
1660 readback,
1661 };
1662 if rt.peer_probe && rt.cross_any && rt.host_bounce {
1663 rt.run_host_bounce_legacy_probe(e)?;
1664 }
1665 Ok(rt)
1666 }
1667
1668 pub fn n_stages(&self) -> usize {
1669 self.stages.len()
1670 }
1671
1672 pub fn cross_device(&self) -> bool {
1674 self.cross_any
1675 }
1676
1677 fn host_bounce_active(&self) -> bool {
1678 self.host_bounce || PEER_RUNTIME_HOST_BOUNCE.load(Ordering::Acquire)
1679 }
1680
1681 fn context_for_dev<'a>(
1682 &'a self,
1683 e: &'a Engine,
1684 dev: usize,
1685 ) -> Result<&'a Arc<CudaContext>, Box<dyn std::error::Error>> {
1686 if dev == e.ctx().ordinal() {
1687 return Ok(e.ctx());
1688 }
1689 self.stages
1690 .iter()
1691 .find(|stage| stage.dev == dev)
1692 .map(|stage| &stage.ctx)
1693 .ok_or_else(|| format!("PP peer probe has no CUDA context for dev{dev}").into())
1694 }
1695
1696 fn enable_probe_peer_access(
1697 &self,
1698 e: &Engine,
1699 pairs: &[(usize, usize)],
1700 ) -> Result<Vec<(usize, usize)>, Box<dyn std::error::Error>> {
1701 let mut enabled = Vec::new();
1702 for &(src_dev, dst_dev) in pairs {
1703 let enable = (|| -> Result<(), Box<dyn std::error::Error>> {
1704 let src_ctx = self.context_for_dev(e, src_dev)?;
1705 let dst_ctx = self.context_for_dev(e, dst_dev)?;
1706 src_ctx.bind_to_thread()?;
1707 let rc = unsafe { cudarc::driver::sys::cuCtxEnablePeerAccess(dst_ctx.cu_ctx(), 0) };
1708 use cudarc::driver::sys::cudaError_enum as E;
1709 if rc == E::CUDA_SUCCESS || rc == E::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED {
1710 Ok(())
1711 } else {
1712 Err(format!("{rc:?}").into())
1713 }
1714 })();
1715 if let Err(err) = enable {
1716 eprintln!(
1717 "[pp] peer byte-integrity probe could not enable \
1718 dev{src_dev}->dev{dst_dev}: {err}; MEMRA_PP_HOST_BOUNCE=1 remains active"
1719 );
1720 } else {
1721 enabled.push((src_dev, dst_dev));
1722 }
1723 }
1724 Ok(enabled)
1725 }
1726
1727 fn disable_probe_peer_access(
1728 &self,
1729 e: &Engine,
1730 pairs: &[(usize, usize)],
1731 ) -> Result<(), Box<dyn std::error::Error>> {
1732 let mut failures = Vec::new();
1733 for &(src_dev, dst_dev) in pairs {
1734 let disable = (|| -> Result<(), Box<dyn std::error::Error>> {
1735 let src_ctx = self.context_for_dev(e, src_dev)?;
1736 let dst_ctx = self.context_for_dev(e, dst_dev)?;
1737 src_ctx.bind_to_thread()?;
1738 let rc = unsafe { cudarc::driver::sys::cuCtxDisablePeerAccess(dst_ctx.cu_ctx()) };
1739 use cudarc::driver::sys::cudaError_enum as E;
1740 if rc == E::CUDA_SUCCESS || rc == E::CUDA_ERROR_PEER_ACCESS_NOT_ENABLED {
1741 Ok(())
1742 } else {
1743 Err(format!("{rc:?}").into())
1744 }
1745 })();
1746 if let Err(err) = disable {
1747 failures.push(format!("dev{src_dev}->dev{dst_dev}: {err}"));
1748 }
1749 }
1750 e.ctx().bind_to_thread()?;
1751 if failures.is_empty() {
1752 eprintln!(
1753 "[pp] peer byte-integrity probe teardown: disabled {} diagnostic pair(s); \
1754 host-bounce serving has no probe-enabled peer access",
1755 pairs.len(),
1756 );
1757 Ok(())
1758 } else {
1759 Err(format!(
1760 "PP peer probe could not disable diagnostic peer access ({}); \
1761 refusing host-bounce serving",
1762 failures.join(", "),
1763 )
1764 .into())
1765 }
1766 }
1767
1768 fn grant_probe_pool_access(
1769 &self,
1770 e: &Engine,
1771 pairs: &[(usize, usize)],
1772 ) -> Result<Vec<(usize, usize)>, Box<dyn std::error::Error>> {
1773 let mut granted = Vec::new();
1774 for &(src_dev, dst_dev) in pairs {
1775 let grant = (|| -> Result<(), Box<dyn std::error::Error>> {
1776 self.context_for_dev(e, dst_dev)?.bind_to_thread()?;
1777 let dev = cudarc::driver::result::device::get(dst_dev as i32)?;
1778 let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
1779 unsafe {
1780 cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, dev).result()?;
1781 }
1782 let desc = cudarc::driver::sys::CUmemAccessDesc {
1783 location: cudarc::driver::sys::CUmemLocation {
1784 type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
1785 id: src_dev as i32,
1786 },
1787 flags:
1788 cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
1789 };
1790 let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
1791 if rc == cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
1792 Ok(())
1793 } else {
1794 Err(format!("{rc:?}").into())
1795 }
1796 })();
1797 if let Err(err) = grant {
1798 eprintln!(
1799 "[pp] production-slot probe could not grant dev{src_dev} access to \
1800 dev{dst_dev}'s default pool: {err}; MEMRA_PP_HOST_BOUNCE=1 remains active"
1801 );
1802 } else {
1803 granted.push((src_dev, dst_dev));
1804 }
1805 }
1806 Ok(granted)
1807 }
1808
1809 fn revoke_probe_pool_access(
1810 &self,
1811 e: &Engine,
1812 pairs: &[(usize, usize)],
1813 ) -> Result<(), Box<dyn std::error::Error>> {
1814 let mut failures = Vec::new();
1815 for &(src_dev, dst_dev) in pairs {
1816 let revoke = (|| -> Result<(), Box<dyn std::error::Error>> {
1817 self.context_for_dev(e, dst_dev)?.bind_to_thread()?;
1818 let dev = cudarc::driver::result::device::get(dst_dev as i32)?;
1819 let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
1820 unsafe {
1821 cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, dev).result()?;
1822 }
1823 let desc = cudarc::driver::sys::CUmemAccessDesc {
1824 location: cudarc::driver::sys::CUmemLocation {
1825 type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
1826 id: src_dev as i32,
1827 },
1828 flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_NONE,
1829 };
1830 let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
1831 if rc == cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
1832 Ok(())
1833 } else {
1834 Err(format!("{rc:?}").into())
1835 }
1836 })();
1837 if let Err(err) = revoke {
1838 failures.push(format!("dev{src_dev}->dev{dst_dev}: {err}"));
1839 }
1840 }
1841 e.ctx().bind_to_thread()?;
1842 if failures.is_empty() {
1843 Ok(())
1844 } else {
1845 Err(format!(
1846 "PP peer probe could not revoke diagnostic pool access ({}); \
1847 refusing host-bounce serving",
1848 failures.join(", "),
1849 )
1850 .into())
1851 }
1852 }
1853
1854 fn run_host_bounce_legacy_probe(&self, e: &Engine) -> Result<(), Box<dyn std::error::Error>> {
1855 let enabled = self.enable_probe_peer_access(e, &self.peer_capable)?;
1856 let probe = run_peer_probe_pass(
1857 &self.stages,
1858 &enabled,
1859 true,
1860 "fixed-16KiB-legacy-preflight",
1861 PEER_PROBE_FIXED_BYTES,
1862 );
1863 let disable = self.disable_probe_peer_access(e, &enabled);
1864 disable?;
1865 probe
1866 }
1867
1868 fn new_peer_probe_boundary(
1869 &self,
1870 src_stage: usize,
1871 dst_stage: usize,
1872 ) -> Result<BoundaryRt, Box<dyn std::error::Error>> {
1873 let tx = &self.stages[src_stage];
1874 let rx = &self.stages[dst_stage];
1875 let mk_slot = || -> Result<BoundarySlot, Box<dyn std::error::Error>> {
1876 Ok(BoundarySlot {
1877 buf: Mutex::new(None),
1878 ev_tx: tx.ctx.new_event(None)?,
1879 ev_rx: rx.ctx.new_event(None)?,
1880 })
1881 };
1882 Ok(BoundaryRt {
1883 slots: [mk_slot()?, mk_slot()?],
1884 step: AtomicUsize::new(0),
1885 cross: tx.dev != rx.dev,
1886 })
1887 }
1888
1889 fn production_probe_readback(
1890 &self,
1891 path: BoundaryPath,
1892 boundary: &BoundaryRt,
1893 expected: &[u8],
1894 n: usize,
1895 slot_idx: usize,
1896 ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
1897 debug_assert_eq!(expected.len(), n * std::mem::size_of::<f32>());
1898 let host = peer_probe_bytes_to_f32(expected);
1899 let poison_bytes: Vec<u8> = expected.iter().map(|byte| !byte).collect();
1900 let poison = peer_probe_bytes_to_f32(&poison_bytes);
1901 let src = &self.stages[path.src_stage];
1902 let dst = &self.stages[path.dst_stage];
1903
1904 dst.ctx.bind_to_thread()?;
1907 let poison_buf = dst.stream.clone_htod(&poison)?;
1908 dst.stream.synchronize()?;
1909 let replaced = boundary.slots[slot_idx]
1910 .buf
1911 .lock()
1912 .unwrap()
1913 .replace(poison_buf);
1914 drop(replaced);
1915 dst.stream.synchronize()?;
1916
1917 src.ctx.bind_to_thread()?;
1918 let x = src.stream.clone_htod(&host)?;
1919 self.tx_slot_path(path, boundary, &x, n, slot_idx)?;
1920
1921 dst.ctx.bind_to_thread()?;
1922 let work = self.rx_slot_path(path, boundary, slot_idx, n)?;
1923 let back = dst.stream.clone_dtoh(&work)?;
1924 dst.stream.synchronize()?;
1925 Ok(peer_probe_f32_to_bytes(&back))
1926 }
1927
1928 fn clear_peer_probe_boundary(
1929 &self,
1930 boundary: &BoundaryRt,
1931 src_stage: usize,
1932 dst_stage: usize,
1933 ) -> Result<(), Box<dyn std::error::Error>> {
1934 self.stages[dst_stage].ctx.bind_to_thread()?;
1935 for slot in &boundary.slots {
1936 let buffer = slot.buf.lock().unwrap().take();
1937 drop(buffer);
1938 }
1939 self.stages[src_stage].stream.synchronize()?;
1940 self.stages[dst_stage].stream.synchronize()?;
1941 Ok(())
1942 }
1943
1944 fn run_production_peer_probe(
1945 &self,
1946 enabled_pairs: &[(usize, usize)],
1947 host_bounce: bool,
1948 n_embd: usize,
1949 ) -> Result<(), Box<dyn std::error::Error>> {
1950 let started = std::time::Instant::now();
1951 let mut copies = 0usize;
1952 let mut skipped = 0usize;
1953 let mut total_mismatches = 0usize;
1954 let mut largest_clean_payload = 0usize;
1955
1956 for boundary_idx in 0..self.stages.len() - 1 {
1957 if self.stages[boundary_idx].dev == self.stages[boundary_idx + 1].dev {
1958 continue;
1959 }
1960 for (src_stage, dst_stage) in [
1961 (boundary_idx, boundary_idx + 1),
1962 (boundary_idx + 1, boundary_idx),
1963 ] {
1964 let src_dev = self.stages[src_stage].dev;
1965 let dst_dev = self.stages[dst_stage].dev;
1966 if !enabled_pairs.contains(&(src_dev, dst_dev)) {
1967 if host_bounce {
1968 skipped += PEER_PROBE_TOKEN_WIDTHS.len();
1969 eprintln!(
1970 "[pp] production-slot peer probe SKIP: boundary={boundary_idx} \
1971 dev{src_dev}->dev{dst_dev} widths_tokens={:?} \
1972 (peer or pool access unavailable; MEMRA_PP_HOST_BOUNCE=1 remains \
1973 fail-safe)",
1974 PEER_PROBE_TOKEN_WIDTHS,
1975 );
1976 continue;
1977 }
1978 return Err(format!(
1979 "PP production-slot peer probe cannot run boundary={boundary_idx} \
1980 dev{src_dev}->dev{dst_dev}: peer/pool access is not enabled"
1981 )
1982 .into());
1983 }
1984
1985 let probe_boundary = self.new_peer_probe_boundary(src_stage, dst_stage)?;
1986 let path = BoundaryPath {
1987 boundary: boundary_idx,
1988 src_stage,
1989 dst_stage,
1990 transport: BoundaryTransport::Peer,
1991 };
1992 let mut direction_copies = 0usize;
1993 let mut direction_skipped = 0usize;
1994 let mut direction_mismatches = 0usize;
1995 let mut direction_largest_clean = 0usize;
1996 let mut failure = None;
1997
1998 for (width_idx, tokens) in PEER_PROBE_TOKEN_WIDTHS.into_iter().enumerate() {
1999 let n = n_embd.checked_mul(tokens).ok_or_else(|| {
2000 format!(
2001 "PP production-slot probe element count overflows for \
2002 n_embd={n_embd} tokens={tokens}"
2003 )
2004 })?;
2005 let bytes = n.checked_mul(std::mem::size_of::<f32>()).ok_or_else(|| {
2006 format!(
2007 "PP production-slot probe byte count overflows for \
2008 n_embd={n_embd} tokens={tokens}"
2009 )
2010 })?;
2011 let expected = peer_probe_pattern(bytes, boundary_idx, src_dev, dst_dev);
2012 let readback = match self.production_probe_readback(
2013 path,
2014 &probe_boundary,
2015 &expected,
2016 n,
2017 width_idx % 2,
2018 ) {
2019 Ok(readback) => readback,
2020 Err(err) if host_bounce => {
2021 skipped += 1;
2022 direction_skipped += 1;
2023 eprintln!(
2024 "[pp] production-slot peer probe ERROR: \
2025 boundary={boundary_idx} dev{src_dev}->dev{dst_dev} \
2026 tokens={tokens} bytes={bytes}: {err}; \
2027 MEMRA_PP_HOST_BOUNCE=1, proceeding on the host-staged path"
2028 );
2029 continue;
2030 }
2031 Err(err) => {
2032 failure = Some(format!(
2033 "PP production-slot peer probe FAILED: \
2034 boundary={boundary_idx} dev{src_dev}->dev{dst_dev} \
2035 tokens={tokens} bytes={bytes}: {err}; refusing native P2P \
2036 (set MEMRA_PP_HOST_BOUNCE=1 to use the host-staged path; \
2037 MEMRA_PEER_PROBE=0 cannot authorize sharded native peer \
2038 transport)"
2039 ));
2040 break;
2041 }
2042 };
2043 copies += 1;
2044 direction_copies += 1;
2045 let mismatches = peer_probe_mismatch_count(&expected, &readback);
2046 if mismatches == 0 {
2047 largest_clean_payload = largest_clean_payload.max(bytes);
2048 direction_largest_clean = direction_largest_clean.max(bytes);
2049 } else if host_bounce {
2050 total_mismatches += mismatches;
2051 direction_mismatches += mismatches;
2052 eprintln!(
2053 "[pp] production-slot peer probe CORRUPTION: \
2054 boundary={boundary_idx} dev{src_dev}->dev{dst_dev} tokens={tokens} \
2055 bytes={bytes} mismatches={mismatches}; MEMRA_PP_HOST_BOUNCE=1, \
2056 proceeding on the host-staged path"
2057 );
2058 } else {
2059 failure = Some(format!(
2060 "PP production-slot peer probe FAILED: boundary={boundary_idx} \
2061 dev{src_dev}->dev{dst_dev} tokens={tokens} bytes={bytes}: \
2062 {mismatches} mismatched byte(s); refusing native P2P \
2063 (set MEMRA_PP_HOST_BOUNCE=1 to use the host-staged path; \
2064 MEMRA_PEER_PROBE=0 cannot authorize sharded native peer transport)"
2065 ));
2066 break;
2067 }
2068 }
2069
2070 self.clear_peer_probe_boundary(&probe_boundary, src_stage, dst_stage)?;
2071 if let Some(err) = failure {
2072 return Err(err.into());
2073 }
2074 eprintln!(
2075 "[pp] production-slot peer probe direction: boundary={boundary_idx} \
2076 dev{src_dev}->dev{dst_dev} copies={direction_copies} \
2077 skipped={direction_skipped} mismatches={direction_mismatches} \
2078 largest_clean_payload_bytes={direction_largest_clean}"
2079 );
2080 }
2081 }
2082
2083 let status = if total_mismatches > 0 {
2084 "BOUNCE"
2085 } else if skipped > 0 && copies > 0 {
2086 "PARTIAL"
2087 } else if skipped > 0 {
2088 "SKIP"
2089 } else {
2090 "PASS"
2091 };
2092 eprintln!(
2093 "[pp] production-slot peer probe {status}: widths_tokens={:?} copies={copies} \
2094 skipped={skipped} mismatches={total_mismatches} \
2095 largest_clean_payload_bytes={largest_clean_payload} elapsed_ms={:.3}",
2096 PEER_PROBE_TOKEN_WIDTHS,
2097 started.elapsed().as_secs_f64() * 1e3,
2098 );
2099 Ok(())
2100 }
2101
2102 fn run_host_bounce_production_probe(
2103 &self,
2104 e: &Engine,
2105 n_embd: usize,
2106 ) -> Result<(), Box<dyn std::error::Error>> {
2107 let enabled = self.enable_probe_peer_access(e, &self.peer_capable)?;
2108 let granted = self.grant_probe_pool_access(e, &enabled)?;
2109 let probe = self.run_production_peer_probe(&granted, true, n_embd);
2110 let revoke = self.revoke_probe_pool_access(e, &granted);
2116 let disable = self.disable_probe_peer_access(e, &enabled);
2117 probe?;
2118 revoke?;
2119 disable?;
2120 Ok(())
2121 }
2122
2123 fn init_peer_probe_geometry(
2124 &self,
2125 e: &Engine,
2126 n_embd: usize,
2127 ) -> Result<(), Box<dyn std::error::Error>> {
2128 if !self.peer_probe || !self.cross_any {
2129 return Ok(());
2130 }
2131 let bytes = n_embd
2132 .checked_mul(std::mem::size_of::<f32>())
2133 .ok_or_else(|| format!("PP boundary-slot byte count overflows for n_embd={n_embd}"))?;
2134 let result = self.peer_probe_geometry.get_or_init(|| {
2135 let probe = if self.host_bounce_active() {
2136 self.run_host_bounce_production_probe(e, n_embd)
2137 } else {
2138 self.run_production_peer_probe(&self.peer_capable, false, n_embd)
2139 };
2140 let restore = e.ctx().bind_to_thread();
2141 match (probe, restore) {
2142 (Ok(()), Ok(())) => Ok(bytes),
2143 (Err(err), _) => Err(err.to_string()),
2144 (_, Err(err)) => Err(err.to_string()),
2145 }
2146 });
2147 let probed = result
2148 .as_ref()
2149 .map_err(|err| -> Box<dyn std::error::Error> { err.clone().into() })?;
2150 if *probed != bytes {
2151 return Err(format!(
2152 "peer probe initialized for boundary-slot bytes={probed} but model requests \
2153 bytes={bytes}; one PP runtime supports one model geometry per process"
2154 )
2155 .into());
2156 }
2157 Ok(())
2158 }
2159
2160 fn init_host_bounce_staging(
2161 &self,
2162 e: &Engine,
2163 n_embd: usize,
2164 ) -> Result<(), Box<dyn std::error::Error>> {
2165 if !self.cross_any {
2166 return Ok(());
2167 }
2168 e.ctx().bind_to_thread()?;
2169 let result = self.bounce.get_or_init(|| {
2170 HostBounceRt::new(n_embd, &self.boundaries)
2171 .map(|rt| {
2172 let bytes = rt.capacity * std::mem::size_of::<f32>();
2173 eprintln!(
2174 "[pp] host-bounce staging ready: n_embd={n_embd} max_tokens={} \
2175 slot_bytes={bytes} slots_per_cross_boundary=2",
2176 crate::cache::PRIME_CHUNK_MAX_TOKENS,
2177 );
2178 rt
2179 })
2180 .map_err(|err| err.to_string())
2181 });
2182 let bounce = result
2183 .as_ref()
2184 .map_err(|err| -> Box<dyn std::error::Error> { err.clone().into() })?;
2185 if bounce.n_embd != n_embd {
2186 return Err(format!(
2187 "host-bounce runtime initialized for n_embd={} but model requests n_embd={n_embd}; \
2188 one PP runtime supports one model geometry per process",
2189 bounce.n_embd,
2190 )
2191 .into());
2192 }
2193 Ok(())
2194 }
2195
2196 fn validate_host_bounce_staging(
2200 &self,
2201 e: &Engine,
2202 n_embd: usize,
2203 ) -> Result<(), Box<dyn std::error::Error>> {
2204 let bytes = n_embd
2205 .checked_mul(std::mem::size_of::<f32>())
2206 .ok_or_else(|| {
2207 format!("host-bounce validation byte count overflows for n_embd={n_embd}")
2208 })?;
2209 for boundary_idx in 0..self.stages.len() - 1 {
2210 if !self.boundaries[boundary_idx].cross {
2211 continue;
2212 }
2213 let src_stage = boundary_idx;
2214 let dst_stage = boundary_idx + 1;
2215 let probe_boundary = self.new_peer_probe_boundary(src_stage, dst_stage)?;
2216 let path = BoundaryPath {
2217 boundary: boundary_idx,
2218 src_stage,
2219 dst_stage,
2220 transport: BoundaryTransport::HostBounce,
2221 };
2222 let expected = peer_probe_pattern(
2223 bytes,
2224 boundary_idx,
2225 self.stages[src_stage].dev,
2226 self.stages[dst_stage].dev,
2227 );
2228 let readback =
2229 self.production_probe_readback(path, &probe_boundary, &expected, n_embd, 0);
2230 let clear = self.clear_peer_probe_boundary(&probe_boundary, src_stage, dst_stage);
2231 let readback = readback?;
2232 clear?;
2233 let mismatches = peer_probe_mismatch_count(&expected, &readback);
2234 if mismatches > 0 {
2235 return Err(format!(
2236 "runtime host-bounce staging validation FAILED: boundary={boundary_idx} \
2237 bytes={bytes} mismatches={mismatches}"
2238 )
2239 .into());
2240 }
2241 }
2242 e.ctx().bind_to_thread()?;
2243 eprintln!(
2244 "[pp] runtime host-bounce staging validation PASS: row_bytes={bytes} \
2245 cross_boundaries={}",
2246 self.boundaries
2247 .iter()
2248 .filter(|boundary| boundary.cross)
2249 .count(),
2250 );
2251 Ok(())
2252 }
2253
2254 fn arm_runtime_host_bounce(
2255 &self,
2256 e: &Engine,
2257 row_bytes: usize,
2258 ) -> Result<(), Box<dyn std::error::Error>> {
2259 if row_bytes == 0 || row_bytes % std::mem::size_of::<f32>() != 0 {
2260 return Err(format!(
2261 "runtime host-bounce cannot recover n_embd from row_bytes={row_bytes}"
2262 )
2263 .into());
2264 }
2265 let n_embd = row_bytes / std::mem::size_of::<f32>();
2266 self.init_host_bounce_staging(e, n_embd)?;
2267 self.validate_host_bounce_staging(e, n_embd)
2268 }
2269
2270 pub fn init_boundary_transport(
2276 &self,
2277 e: &Engine,
2278 n_embd: usize,
2279 ) -> Result<(), Box<dyn std::error::Error>> {
2280 if PEER_RUNTIME_PROBE_FAILED.load(Ordering::Acquire)
2281 && !PEER_RUNTIME_HOST_BOUNCE.load(Ordering::Acquire)
2282 {
2283 return Err(
2284 "PP runtime peer byte-integrity probe previously failed; refusing native P2P \
2285 reuse because runtime host-bounce staging could not be armed"
2286 .into(),
2287 );
2288 }
2289 self.init_peer_probe_geometry(e, n_embd)?;
2290 if !self.host_bounce_active() || !self.cross_any {
2291 return Ok(());
2292 }
2293 self.init_host_bounce_staging(e, n_embd)
2294 }
2295
2296 fn service_runtime_peer_probe(
2301 &self,
2302 e: &Engine,
2303 scheduler_idle: bool,
2304 probe_allowed: bool,
2305 ) -> Result<RuntimePeerProbeStatus, Box<dyn std::error::Error>> {
2306 if !self.peer_probe || !self.cross_any || self.host_bounce_active() {
2307 return Ok(RuntimePeerProbeStatus::NotRun);
2308 }
2309 if PEER_RUNTIME_PROBE_FAILED.load(Ordering::Acquire) {
2310 return Err(
2311 "PP runtime peer byte-integrity probe previously failed; native P2P is latched off"
2312 .into(),
2313 );
2314 }
2315 let row_bytes = match self.peer_probe_geometry.get() {
2316 Some(Ok(bytes)) => *bytes,
2317 _ => return Ok(RuntimePeerProbeStatus::NotRun),
2318 };
2319
2320 let copies = PEER_BOUNDARY_COPIES.load(Ordering::Relaxed);
2321 let (width_index, tokens) = loop {
2322 let next_probe_copy = std::array::from_fn(|width_index| {
2323 PEER_RUNTIME_NEXT_PROBE_COPY[width_index].load(Ordering::Relaxed)
2324 });
2325 let measured_cost_ns = std::array::from_fn(|width_index| {
2326 PEER_RUNTIME_PROBE_MAX_COST_NS[width_index].load(Ordering::Relaxed)
2327 });
2328 let Some(candidate) = runtime_peer_probe_candidate(
2329 copies,
2330 next_probe_copy,
2331 measured_cost_ns,
2332 scheduler_idle,
2333 ) else {
2334 return Ok(RuntimePeerProbeStatus::NotRun);
2335 };
2336 if !probe_allowed {
2341 return Ok(RuntimePeerProbeStatus::Deferred);
2342 }
2343 let due = next_probe_copy[candidate.0];
2344 let next = runtime_peer_probe_next_copy(due, copies);
2345 if PEER_RUNTIME_NEXT_PROBE_COPY[candidate.0]
2346 .compare_exchange(due, next, Ordering::AcqRel, Ordering::Relaxed)
2347 .is_ok()
2348 {
2349 break candidate;
2350 }
2351 };
2352 let probe_index = PEER_RUNTIME_PROBES.fetch_add(1, Ordering::Relaxed);
2353 let probe_bytes = row_bytes.checked_mul(tokens);
2354 let scheduler_class = if scheduler_idle { "idle" } else { "busy" };
2355 let label = format!("runtime-{scheduler_class}-{tokens}tok");
2356 let started = std::time::Instant::now();
2357 let probe = match probe_bytes {
2358 Some(bytes) => {
2359 run_peer_probe_pass(&self.stages, &self.peer_capable, false, &label, bytes)
2360 }
2361 None => Err(format!(
2362 "PP runtime peer probe byte count overflows for row_bytes={row_bytes} \
2363 tokens={tokens}"
2364 )
2365 .into()),
2366 };
2367 let restore = e.ctx().bind_to_thread();
2368 let elapsed_ns = started.elapsed().as_nanos().min(u64::MAX as u128) as u64;
2369 let previous_max =
2370 PEER_RUNTIME_PROBE_MAX_COST_NS[width_index].fetch_max(elapsed_ns, Ordering::Relaxed);
2371 let verdict = match (probe, restore) {
2372 (Ok(()), Ok(())) => Ok(()),
2373 (Err(err), _) => Err(err.to_string()),
2374 (_, Err(err)) => Err(err.to_string()),
2375 };
2376 if let Err(err) = verdict {
2377 PEER_RUNTIME_PROBE_FAILURES.fetch_add(1, Ordering::Relaxed);
2378 let arm = latch_runtime_host_bounce(
2379 &PEER_RUNTIME_PROBE_FAILED,
2380 &PEER_RUNTIME_HOST_BOUNCE,
2381 || {
2382 self.arm_runtime_host_bounce(e, row_bytes)
2383 .map_err(|arm_err| arm_err.to_string())
2384 },
2385 );
2386 if let Err(arm_err) = arm {
2387 let message = format!(
2388 "PP runtime peer byte-integrity re-probe FAILED after \
2389 boundary_copies={copies} rung={}/{} tokens={tokens}: {err}; native P2P is \
2390 latched off and host-bounce staging could not be armed: {arm_err}",
2391 width_index + 1,
2392 PEER_PROBE_TOKEN_WIDTHS.len(),
2393 );
2394 eprintln!("[pp] SECURITY RED: {message}; worker must stop");
2395 return Err(message.into());
2396 }
2397 eprintln!(
2398 "[pp] SECURITY RED: PP runtime peer byte-integrity re-probe FAILED after \
2399 boundary_copies={copies} rung={}/{} tokens={tokens}: {err}; native P2P is \
2400 latched off and the live transport DEGRADED to validated host bounce for the \
2401 remainder of this process",
2402 width_index + 1,
2403 PEER_PROBE_TOKEN_WIDTHS.len(),
2404 );
2405 return Ok(RuntimePeerProbeStatus::DegradedToHostBounce);
2406 }
2407 if width_index + 1 != PEER_PROBE_TOKEN_WIDTHS.len()
2408 && previous_max <= PEER_RUNTIME_PROBE_BUDGET_NS
2409 && elapsed_ns > PEER_RUNTIME_PROBE_BUDGET_NS
2410 {
2411 eprintln!(
2412 "[pp] runtime peer re-probe rung exceeded the {:.3}ms owner-thread budget: \
2413 tokens={tokens} measured_ms={:.3}; future runs are idle-only",
2414 PEER_RUNTIME_PROBE_BUDGET_NS as f64 / 1e6,
2415 elapsed_ns as f64 / 1e6,
2416 );
2417 }
2418 eprintln!(
2419 "[pp] runtime peer byte-integrity re-probe PASS: \
2420 boundary_copies={copies} interval_copies={PEER_RUNTIME_PROBE_INTERVAL_COPIES} \
2421 rung={}/{} tokens={tokens} bytes={} probe_index={probe_index} elapsed_ms={:.3} \
2422 scheduler_idle={scheduler_idle}",
2423 width_index + 1,
2424 PEER_PROBE_TOKEN_WIDTHS.len(),
2425 probe_bytes.unwrap(),
2426 elapsed_ns as f64 / 1e6,
2427 );
2428 Ok(RuntimePeerProbeStatus::Passed)
2429 }
2430
2431 fn bounce_rt(&self) -> Result<&HostBounceRt, Box<dyn std::error::Error>> {
2432 self.bounce
2433 .get()
2434 .ok_or_else(|| -> Box<dyn std::error::Error> {
2435 "MEMRA_PP_HOST_BOUNCE=1 staging was not initialized from model geometry".into()
2436 })?
2437 .as_ref()
2438 .map_err(|err| -> Box<dyn std::error::Error> { err.clone().into() })
2439 }
2440
2441 pub fn engine<'a>(&'a self, s: usize, primary: &'a Engine) -> &'a Engine {
2444 self.stages[s].engine.as_ref().unwrap_or(primary)
2445 }
2446
2447 pub fn bind_stage(&self, s: usize) -> Result<(), Box<dyn std::error::Error>> {
2449 self.stages[s].ctx.bind_to_thread()?;
2450 Ok(())
2451 }
2452
2453 pub fn enter(&self, s: usize) -> memra_runtime::StreamOverride {
2456 memra_runtime::push_stream_override(
2457 self.stages[s].stream.clone(),
2458 self.stages[s].blas.clone(),
2459 )
2460 }
2461
2462 pub fn prepare_overlap_slots(
2468 &self,
2469 b: usize,
2470 n: usize,
2471 ) -> Result<(), Box<dyn std::error::Error>> {
2472 let bd = &self.boundaries[b];
2473 let s_rx = &self.stages[b + 1].stream;
2474 let mut grew = false;
2475 for sl in &bd.slots {
2476 let mut guard = sl.buf.lock().unwrap();
2477 if guard.as_ref().map(|bf| bf.len() < n).unwrap_or(true) {
2478 *guard = Some(s_rx.alloc_zeros::<f32>(n)?);
2479 grew = true;
2480 }
2481 }
2482 if grew {
2483 s_rx.synchronize()?;
2484 }
2485 Ok(())
2486 }
2487
2488 pub fn tx(
2502 &self,
2503 b: usize,
2504 x: &CudaSlice<f32>,
2505 n: usize,
2506 ) -> Result<usize, Box<dyn std::error::Error>> {
2507 assert_eq!(x.len(), n, "pp tx: residual length mismatch");
2508 let bd = &self.boundaries[b];
2509 let slot_idx = if pp2_overlap() {
2510 bd.step.fetch_add(1, Ordering::Relaxed) % 2
2511 } else {
2512 0
2513 };
2514 self.tx_slot(b, x, n, slot_idx)
2515 }
2516
2517 pub fn tx_pipelined(
2521 &self,
2522 b: usize,
2523 x: &CudaSlice<f32>,
2524 n: usize,
2525 ) -> Result<usize, Box<dyn std::error::Error>> {
2526 assert_eq!(x.len(), n, "pp tx: residual length mismatch");
2527 let slot_idx = self.boundaries[b].step.fetch_add(1, Ordering::Relaxed) % 2;
2528 self.tx_slot(b, x, n, slot_idx)
2529 }
2530
2531 fn tx_slot(
2532 &self,
2533 b: usize,
2534 x: &CudaSlice<f32>,
2535 n: usize,
2536 slot_idx: usize,
2537 ) -> Result<usize, Box<dyn std::error::Error>> {
2538 let bd = &self.boundaries[b];
2539 let path = BoundaryPath {
2540 boundary: b,
2541 src_stage: b,
2542 dst_stage: b + 1,
2543 transport: boundary_transport(bd.cross, self.host_bounce_active()),
2544 };
2545 let copied_slot = self.tx_slot_path(path, bd, x, n, slot_idx)?;
2546 if path.transport == BoundaryTransport::Peer {
2547 PEER_BOUNDARY_COPIES.fetch_add(1, Ordering::Relaxed);
2548 }
2549 Ok(copied_slot)
2550 }
2551
2552 fn tx_slot_path(
2553 &self,
2554 path: BoundaryPath,
2555 bd: &BoundaryRt,
2556 x: &CudaSlice<f32>,
2557 n: usize,
2558 slot_idx: usize,
2559 ) -> Result<usize, Box<dyn std::error::Error>> {
2560 debug_assert!(slot_idx < 2);
2561 let sl = &bd.slots[slot_idx];
2562 let s_tx = &self.stages[path.src_stage].stream;
2563 s_tx.wait(&sl.ev_rx)?;
2564 let mut guard = sl.buf.lock().unwrap();
2565 if guard.as_ref().map(|bf| bf.len() < n).unwrap_or(true) {
2566 let s_rx = &self.stages[path.dst_stage].stream;
2568 *guard = Some(s_rx.alloc_zeros::<f32>(n)?);
2569 s_rx.synchronize()?;
2579 }
2580 let buf = guard.as_mut().unwrap();
2581 match path.transport {
2582 BoundaryTransport::Local => s_tx.memcpy_dtod(x, buf)?,
2583 BoundaryTransport::HostBounce => {
2584 debug_assert_eq!(path.src_stage, path.boundary);
2585 debug_assert_eq!(path.dst_stage, path.boundary + 1);
2586 let bounce = self.bounce_rt()?;
2587 if n > bounce.capacity {
2588 return Err(format!(
2589 "pp host-bounce payload {n} exceeds geometry-sized capacity {} \
2590 (n_embd={}, max prime tokens={})",
2591 bounce.capacity,
2592 bounce.n_embd,
2593 crate::cache::PRIME_CHUNK_MAX_TOKENS,
2594 )
2595 .into());
2596 }
2597 let mut host = bounce.slot(path.boundary, slot_idx)?.lock().unwrap();
2598 s_tx.memcpy_dtoh(x, host.prefix_mut(n))?;
2602 }
2603 BoundaryTransport::Peer => {
2604 use cudarc::driver::{DevicePtr, DevicePtrMut};
2607 let (sp, _g0) = x.device_ptr(s_tx);
2608 let (dp, _g1) = buf.device_ptr_mut(s_tx);
2609 self.stages[path.src_stage].ctx.bind_to_thread()?;
2610 unsafe {
2611 cudarc::driver::result::memcpy_peer_async(
2612 self.stages[path.dst_stage].ctx.cu_ctx(),
2613 dp,
2614 self.stages[path.src_stage].ctx.cu_ctx(),
2615 sp,
2616 n * std::mem::size_of::<f32>(),
2617 s_tx.cu_stream(),
2618 )?;
2619 }
2620 }
2621 }
2622 sl.ev_tx.record(s_tx)?;
2623 Ok(slot_idx)
2624 }
2625
2626 pub fn rx(
2631 &self,
2632 b: usize,
2633 slot_idx: usize,
2634 n: usize,
2635 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2636 let bd = &self.boundaries[b];
2637 let path = BoundaryPath {
2638 boundary: b,
2639 src_stage: b,
2640 dst_stage: b + 1,
2641 transport: boundary_transport(bd.cross, self.host_bounce_active()),
2642 };
2643 self.rx_slot_path(path, bd, slot_idx, n)
2644 }
2645
2646 fn rx_slot_path(
2647 &self,
2648 path: BoundaryPath,
2649 bd: &BoundaryRt,
2650 slot_idx: usize,
2651 n: usize,
2652 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2653 let sl = &bd.slots[slot_idx];
2654 let s_rx = &self.stages[path.dst_stage].stream;
2655 s_rx.wait(&sl.ev_tx)?;
2656 let mut guard = sl.buf.lock().unwrap();
2657 let buf = guard.as_mut().expect("pp rx before tx");
2658 assert!(
2659 buf.len() >= n,
2660 "pp rx: slot holds {} < requested {n}",
2661 buf.len()
2662 );
2663 if path.transport == BoundaryTransport::HostBounce {
2664 debug_assert_eq!(path.src_stage, path.boundary);
2665 debug_assert_eq!(path.dst_stage, path.boundary + 1);
2666 let bounce = self.bounce_rt()?;
2667 let host = bounce.slot(path.boundary, slot_idx)?.lock().unwrap();
2668 let mut dst = buf.slice_mut(0..n);
2669 s_rx.memcpy_htod(host.prefix(n), &mut dst)?;
2672 }
2673 let mut work = unsafe { s_rx.alloc::<f32>(n)? };
2676 s_rx.memcpy_dtod(&buf.slice(0..n), &mut work)?;
2680 sl.ev_rx.record(s_rx)?;
2681 Ok(work)
2682 }
2683
2684 pub fn publish_to(
2711 &self,
2712 s: usize,
2713 dst: &Arc<CudaStream>,
2714 ) -> Result<(), Box<dyn std::error::Error>> {
2715 let st = &self.stages[s];
2716 if Arc::ptr_eq(&st.stream, dst) {
2719 return Ok(());
2720 }
2721 let ev = st.ctx.new_event(None)?;
2722 ev.record(&st.stream)?;
2723 dst.wait(&ev)?;
2724 Ok(())
2725 }
2726
2727 pub fn fence_stages_behind(
2749 &self,
2750 src: &Arc<CudaStream>,
2751 ) -> Result<(), Box<dyn std::error::Error>> {
2752 let ev = src.context().new_event(None)?;
2753 ev.record(src)?;
2754 for st in &self.stages {
2755 if Arc::ptr_eq(&st.stream, src) {
2756 continue;
2757 }
2758 st.stream.wait(&ev)?;
2759 }
2760 Ok(())
2761 }
2762
2763 pub fn record_done(&self) -> Result<CudaEvent, Box<dyn std::error::Error>> {
2766 let last = &self.stages[self.stages.len() - 1];
2767 let ev = last.ctx.new_event(None)?;
2768 ev.record(&last.stream)?;
2769 Ok(ev)
2770 }
2771
2772 pub fn readback_stream(&self) -> &Arc<CudaStream> {
2774 &self.readback
2775 }
2776}
2777
2778pub fn service_runtime_peer_probe(
2781 e: &Engine,
2782 scheduler_idle: bool,
2783 probe_allowed: bool,
2784) -> Result<RuntimePeerProbeStatus, Box<dyn std::error::Error>> {
2785 let Some(rt) = RTN.get() else {
2786 return Ok(RuntimePeerProbeStatus::NotRun);
2787 };
2788 let rt = rt
2789 .as_ref()
2790 .map_err(|err| -> Box<dyn std::error::Error> { err.clone().into() })?;
2791 rt.service_runtime_peer_probe(e, scheduler_idle, probe_allowed)
2792}
2793
2794pub struct PendingLogits {
2799 logits: CudaSlice<f32>,
2800 ev: CudaEvent,
2801 rb: Arc<CudaStream>,
2802}
2803
2804impl PendingLogits {
2805 pub fn new(logits: CudaSlice<f32>, ev: CudaEvent, rb: Arc<CudaStream>) -> Self {
2806 PendingLogits { logits, ev, rb }
2807 }
2808
2809 pub fn wait(self) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2813 self.rb.wait(&self.ev)?;
2814 let host = self.rb.clone_dtoh(&self.logits)?;
2815 self.rb.synchronize()?;
2816 Ok(host)
2819 }
2820}
2821
2822pub fn init_model_transport(
2825 e: &Engine,
2826 cfg: &memra_gguf::config::ModelConfig,
2827 n_trunk: usize,
2828) -> Result<(), Box<dyn std::error::Error>> {
2829 if pp2_streams_off() || pp2_devices_env().is_none() || pp_cuts(n_trunk).is_none() {
2830 return Ok(());
2831 }
2832 PpNRt::get(e)?.init_boundary_transport(e, cfg.n_embd as usize)
2833}
2834
2835pub fn new_cache(
2842 e: &Engine,
2843 cfg: &memra_gguf::config::ModelConfig,
2844 max_ctx: usize,
2845) -> Result<crate::cache::Cache, Box<dyn std::error::Error>> {
2846 new_cache_inner(e, cfg, None, max_ctx)
2847}
2848
2849pub fn new_cache_planned(
2850 e: &Engine,
2851 cfg: &memra_gguf::config::ModelConfig,
2852 plan: &memra_gguf::model_plan::ModelPlan,
2853 max_ctx: usize,
2854) -> Result<crate::cache::Cache, Box<dyn std::error::Error>> {
2855 new_cache_inner(e, cfg, Some(plan), max_ctx)
2856}
2857
2858fn new_cache_inner(
2859 e: &Engine,
2860 cfg: &memra_gguf::config::ModelConfig,
2861 plan: Option<&memra_gguf::model_plan::ModelPlan>,
2862 max_ctx: usize,
2863) -> Result<crate::cache::Cache, Box<dyn std::error::Error>> {
2864 let n_trunk = (cfg.n_layer - cfg.nextn_predict_layers) as usize;
2865 if let Some(fence) = pp_cuts(n_trunk) {
2866 if pp2_devices_env().is_some() && !pp2_streams_off() {
2867 let rt = PpNRt::get(e)?;
2868 rt.init_boundary_transport(e, cfg.n_embd as usize)?;
2869 let n_st = fence.len() - 1;
2870 assert_eq!(
2871 rt.n_stages(),
2872 n_st,
2873 "PpNRt stage count {} != fence stages {n_st}",
2874 rt.n_stages()
2875 );
2876 rt.fence_stages_behind(&e.stream())?;
2885 let devs: Vec<&dyn memra_kv::KvDev> = (0..n_st)
2886 .map(|s| rt.engine(s, e) as &dyn memra_kv::KvDev)
2887 .collect();
2888 let cache = match plan {
2889 Some(plan) => {
2890 crate::cache::Cache::new_ppn_planned(&devs, &fence, cfg, plan, max_ctx)?
2891 }
2892 None => crate::cache::Cache::new_ppn(&devs, &fence, cfg, max_ctx)?,
2893 };
2894 sync_stages_after_load(e, n_trunk)?;
2895 return Ok(cache);
2896 }
2897 if !pp2_streams_off() {
2898 let cache = match plan {
2906 Some(plan) => crate::cache::Cache::new_planned(e, cfg, plan, max_ctx)?,
2907 None => crate::cache::Cache::new(e, cfg, max_ctx)?,
2908 };
2909 sync_stages_after_load(e, n_trunk)?;
2910 return Ok(cache);
2911 }
2912 }
2913 match plan {
2914 Some(plan) => crate::cache::Cache::new_planned(e, cfg, plan, max_ctx),
2915 None => crate::cache::Cache::new(e, cfg, max_ctx),
2916 }
2917}
2918
2919pub fn sync_stages_after_load(
2928 e: &Engine,
2929 n_trunk: usize,
2930) -> Result<(), Box<dyn std::error::Error>> {
2931 if pp2_streams_off() || pp_cuts(n_trunk).is_none() {
2932 return Ok(());
2933 }
2934 let rt = PpNRt::get(e)?;
2935 for s in 0..rt.n_stages() {
2936 rt.stages[s].ctx.bind_to_thread()?;
2937 unsafe {
2938 cudarc::driver::sys::cuCtxSynchronize().result()?;
2939 }
2940 }
2941 e.ctx().bind_to_thread()?;
2942 unsafe {
2943 cudarc::driver::sys::cuCtxSynchronize().result()?;
2944 }
2945 Ok(())
2946}
2947
2948pub fn layer_engine<'a>(
2954 e: &'a Engine,
2955 n_trunk: usize,
2956 il: usize,
2957) -> Result<&'a Engine, Box<dyn std::error::Error>> {
2958 if pp_shard_off() || pp2_devices_env().is_none() || pp2_streams_off() {
2959 return Ok(e);
2960 }
2961 let Some(fence) = pp_cuts(n_trunk) else {
2962 return Ok(e);
2963 };
2964 let rt = PpNRt::get(e)?;
2965 let s = stage_of(&fence, il.min(n_trunk - 1));
2966 Ok(rt.engine(s, e))
2967}
2968
2969pub fn restore_cache_checkpoint(
2981 e: &Engine,
2982 model: &crate::hybrid::HybridModel,
2983 source: Option<&crate::cache::Cache>,
2984 target: &mut crate::cache::Cache,
2985 snap: &crate::cache::CacheSnapshot,
2986) -> Result<(), Box<dyn std::error::Error>> {
2987 let cfg = &model.cfg;
2988 let n = target.kv.len();
2989 if target.recur.len() != n
2990 || target.tp_kv.len() != n
2991 || snap.kv_len.len() != n
2992 || snap.tp_kv_len.len() != n
2993 || snap.conv.len() != n
2994 || snap.ssm.len() != n
2995 || source.is_some_and(|s| s.kv.len() != n || s.recur.len() != n || s.tp_kv.len() != n)
2996 {
2997 return Err("checkpoint cache layer-count mismatch".into());
2998 }
2999 if snap.pos > target.max_ctx {
3000 return Err(format!(
3001 "checkpoint pos {} exceeds target capacity {}",
3002 snap.pos, target.max_ctx,
3003 )
3004 .into());
3005 }
3006
3007 let n_trunk = (cfg.n_layer - cfg.nextn_predict_layers) as usize;
3008 for il in 0..n {
3009 let owner = layer_engine(e, n_trunk, il)?;
3010 let src_kv = source.map(|s| &s.kv[il]);
3011 match (src_kv, target.kv[il].as_mut(), snap.kv_len[il]) {
3012 (Some(Some(src)), Some(dst), Some(len)) => {
3013 if len > src.len || len > target.max_ctx {
3014 return Err(format!(
3015 "checkpoint layer {il} len {len} exceeds source {} or target {}",
3016 src.len, target.max_ctx,
3017 )
3018 .into());
3019 }
3020 if src.kv_dim_k != dst.kv_dim_k
3021 || src.kv_dim_v != dst.kv_dim_v
3022 || src.k_tok_bytes != dst.k_tok_bytes
3023 || src.v_tok_bytes != dst.v_tok_bytes
3024 {
3025 return Err(format!("checkpoint KV layout mismatch at layer {il}").into());
3026 }
3027 let kb = len * src.k_tok_bytes;
3028 let vb = len * src.v_tok_bytes;
3029 if kb > 0 {
3030 owner.copy_u8_into(&mut dst.k, 0, &src.k, kb)?;
3031 }
3032 if vb > 0 {
3033 owner.copy_u8_into(&mut dst.v, 0, &src.v, vb)?;
3034 }
3035 dst.len = len;
3036 owner.set_i32_one(&mut dst.len_d, len as i32)?;
3037 }
3038 (None, Some(dst), Some(len)) => {
3039 if len > dst.len || len > target.max_ctx {
3040 return Err(format!(
3041 "checkpoint layer {il} len {len} exceeds live {} or target {}",
3042 dst.len, target.max_ctx,
3043 )
3044 .into());
3045 }
3046 dst.len = len;
3047 owner.set_i32_one(&mut dst.len_d, len as i32)?;
3048 }
3049 (Some(None), None, None) | (None, None, None) => {}
3050 _ => return Err(format!("checkpoint KV kind mismatch at layer {il}").into()),
3051 }
3052
3053 match (source, snap.tp_kv_len[il]) {
3054 (None, Some(len)) => target.tp_kv[il]
3055 .as_mut()
3056 .ok_or_else(|| format!("checkpoint TP KV target is absent at layer {il}"))?
3057 .rewind_to(len)?,
3058 (None, None) => {
3059 if target.tp_kv[il].is_some() {
3060 return Err(format!("checkpoint TP KV kind mismatch at layer {il}").into());
3061 }
3062 }
3063 (Some(src_cache), Some(len)) => {
3064 let src = src_cache.tp_kv[il]
3065 .as_ref()
3066 .ok_or_else(|| format!("checkpoint TP KV source is absent at layer {il}"))?;
3067 if target.tp_kv[il].is_some() {
3068 return Err(
3069 format!("checkpoint TP KV grow target is not fresh at layer {il}").into(),
3070 );
3071 }
3072 let runtime = model.step_tp_runtime_for_layer(il).ok_or_else(|| {
3073 format!("checkpoint TP KV layer {il} has no distributed runtime")
3074 })?;
3075 let grown = runtime.grow_tp_kv_cache(src, target.max_ctx, len)?;
3076 target.tp_kv[il] = Some(grown);
3077 }
3078 (Some(src_cache), None) => {
3079 if src_cache.tp_kv[il].is_some() || target.tp_kv[il].is_some() {
3080 return Err(format!("checkpoint TP KV kind mismatch at layer {il}").into());
3081 }
3082 }
3083 }
3084
3085 match (target.recur[il].as_mut(), &snap.conv[il], &snap.ssm[il]) {
3086 (Some(dst), Some(conv), Some(ssm)) => {
3087 if conv.len() != dst.conv_state.len() || ssm.len() != dst.ssm_state.len() {
3088 return Err(
3089 format!("checkpoint recurrent layout mismatch at layer {il}").into(),
3090 );
3091 }
3092 owner.copy_into(&mut dst.conv_state, 0, conv, conv.len())?;
3093 owner.copy_into(&mut dst.ssm_state, 0, ssm, ssm.len())?;
3094 }
3095 (None, None, None) => {}
3096 _ => {
3097 return Err(format!("checkpoint recurrent kind mismatch at layer {il}").into());
3098 }
3099 }
3100 }
3101 target.pos = snap.pos;
3102
3103 sync_stages_after_load(e, n_trunk)?;
3106 if source.is_some() {
3107 e.stream().synchronize()?;
3110 }
3111 Ok(())
3112}
3113
3114#[cfg(test)]
3115mod host_bounce_tests {
3116 use super::{
3117 BoundaryTransport, DUAL_PP_HOST_BOUNCE_REFUSAL, DUAL_PP_SINGLE_SLOT_REFUSAL,
3118 PEER_PROBE_FIXED_BYTES, PEER_PROBE_REQUIRED_REFUSAL, PEER_PROBE_TOKEN_WIDTHS,
3119 PEER_RUNTIME_PROBE_BUDGET_NS, PEER_RUNTIME_PROBE_CYCLE_COPIES,
3120 PEER_RUNTIME_PROBE_DEFERRAL_BOUND_INTERVALS, PEER_RUNTIME_PROBE_INTERVAL_COPIES,
3121 PeerProbeDecision, PeerProbeStartupPolicy, boundary_transport, dual_pp_eligibility,
3122 dual_pp_timing_dropped, dual_pp_timing_snapshot, dual_pp_wave_mid, host_bounce_capacity,
3123 latch_runtime_host_bounce, peer_probe_bytes_to_f32, peer_probe_decision,
3124 peer_probe_f32_to_bytes, peer_probe_mismatch_count, peer_probe_pattern,
3125 peer_probe_startup_policy, publish_runtime_peer_probe_deferral,
3126 record_dual_pp_stage_result, runtime_peer_probe_candidate, runtime_peer_probe_next_copy,
3127 };
3128
3129 #[test]
3133 fn flip_default_is_dual_auto_with_explicit_off_and_forced_seams() {
3134 use super::{DualPpMode, dual_pp_mode_resolve};
3135 assert_eq!(dual_pp_mode_resolve(None), DualPpMode::Auto);
3136 assert_eq!(dual_pp_mode_resolve(Some("0")), DualPpMode::Off);
3137 assert_eq!(dual_pp_mode_resolve(Some("1")), DualPpMode::Forced);
3138 assert_eq!(dual_pp_mode_resolve(Some("2")), DualPpMode::Auto);
3140 assert_eq!(dual_pp_mode_resolve(Some("")), DualPpMode::Auto);
3141 }
3142
3143 #[test]
3144 fn flip_overlap_follows_mode_and_one_flag_restores_preflip_serial() {
3145 use super::{DualPpMode, pp2_overlap_resolve};
3146 assert!(pp2_overlap_resolve(None, DualPpMode::Auto));
3148 assert!(!pp2_overlap_resolve(None, DualPpMode::Off));
3150 assert!(!pp2_overlap_resolve(None, DualPpMode::Forced));
3152 for mode in [DualPpMode::Off, DualPpMode::Forced, DualPpMode::Auto] {
3154 assert!(pp2_overlap_resolve(Some("1"), mode));
3155 assert!(!pp2_overlap_resolve(Some("0"), mode));
3156 }
3157 }
3158
3159 #[test]
3160 fn flip_auto_routes_only_the_regated_regime_and_degrades_serially_elsewhere() {
3161 use super::{DualPpMode, dual_pp_route};
3162 assert!(dual_pp_route(DualPpMode::Auto, 2, 2, true, false));
3164 assert!(dual_pp_route(DualPpMode::Auto, 17, 2, true, false));
3165 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));
3172 assert!(!dual_pp_route(DualPpMode::Forced, 1, 2, true, false));
3173 assert!(!dual_pp_route(DualPpMode::Off, 8, 2, true, false));
3175 }
3176
3177 #[test]
3178 fn dual_pp_split_is_honest_at_one_and_ceil_first_afterward() {
3179 assert_eq!(dual_pp_wave_mid(1), None);
3180 assert_eq!(dual_pp_wave_mid(2), Some(1));
3181 assert_eq!(dual_pp_wave_mid(3), Some(2));
3182 assert_eq!(dual_pp_wave_mid(8), Some(4));
3183 assert_eq!(dual_pp_wave_mid(16), Some(8));
3184 assert_eq!(dual_pp_wave_mid(31), Some(16));
3185 assert_eq!(dual_pp_wave_mid(32), Some(16));
3186 }
3187
3188 #[test]
3189 fn dual_pp_refuses_single_slot_and_non_pp2_shapes() {
3190 assert_eq!(
3191 dual_pp_eligibility(2, false, false),
3192 Err(DUAL_PP_SINGLE_SLOT_REFUSAL)
3193 );
3194 assert!(dual_pp_eligibility(2, true, false).is_ok());
3195 assert!(dual_pp_eligibility(3, true, false).is_err());
3196 }
3197
3198 #[test]
3199 fn dual_pp_refuses_unvalidated_host_bounce_transport() {
3200 assert_eq!(
3201 dual_pp_eligibility(2, true, true),
3202 Err(DUAL_PP_HOST_BOUNCE_REFUSAL),
3203 );
3204 }
3205
3206 #[test]
3207 fn dual_pp_timing_error_is_counted_without_recording_a_sample() {
3208 let dropped_before = dual_pp_timing_dropped();
3209 let (_, samples_before) = dual_pp_timing_snapshot();
3210 record_dual_pp_stage_result(0, Err::<f32, _>("CUDA_ERROR_NOT_READY"));
3211 let (_, samples_after) = dual_pp_timing_snapshot();
3212 assert_eq!(samples_after[0], samples_before[0]);
3213 assert!(dual_pp_timing_dropped() >= dropped_before + 1);
3214 }
3215
3216 #[test]
3217 fn corrupted_peer_readback_fails_closed_unless_host_bounce_is_selected() {
3218 assert_eq!(
3219 PEER_PROBE_TOKEN_WIDTHS,
3220 [1, 8, 16, crate::cache::PRIME_CHUNK_MAX_TOKENS],
3221 );
3222 let largest_payload_bytes = PEER_PROBE_TOKEN_WIDTHS[3] * 4096 * std::mem::size_of::<f32>();
3223 assert_eq!(largest_payload_bytes, 64 * 1024 * 1024);
3224 assert!(largest_payload_bytes >= 1024 * 1024);
3225 let expected = peer_probe_pattern(PEER_PROBE_FIXED_BYTES, 2, 0, 1);
3226 assert_eq!(
3227 peer_probe_f32_to_bytes(&peer_probe_bytes_to_f32(&expected)),
3228 expected,
3229 );
3230 let mut corrupted = expected.clone();
3231 for offset in [0, 8_191, PEER_PROBE_FIXED_BYTES - 1] {
3232 corrupted[offset] ^= 0x5a;
3233 }
3234
3235 assert_eq!(peer_probe_mismatch_count(&expected, &corrupted), 3);
3236 assert_eq!(
3237 peer_probe_decision(&expected, &corrupted, false),
3238 Err("3 mismatched byte(s)".to_string()),
3239 );
3240 assert_eq!(
3241 peer_probe_decision(&expected, &corrupted, true),
3242 Ok(PeerProbeDecision::ProceedWithHostBounce { mismatches: 3 }),
3243 );
3244 }
3245
3246 #[test]
3247 fn probe_off_refusal_matrix_is_fail_closed_only_for_sharded_native_peer() {
3248 for probe_on in [false, true] {
3249 for sharded in [false, true] {
3250 for host_bounce in [false, true] {
3251 let got = peer_probe_startup_policy(probe_on, sharded, host_bounce);
3252 let expected = match (probe_on, sharded, host_bounce) {
3253 (false, true, false) => Err(PEER_PROBE_REQUIRED_REFUSAL),
3254 (false, true, true) => Ok(PeerProbeStartupPolicy::BypassedWithHostBounce),
3255 _ => Ok(PeerProbeStartupPolicy::Allowed),
3256 };
3257 assert_eq!(
3258 got, expected,
3259 "probe_on={probe_on} sharded={sharded} host_bounce={host_bounce}",
3260 );
3261 }
3262 }
3263 }
3264 assert!(PEER_PROBE_REQUIRED_REFUSAL.contains("MEMRA_PEER_PROBE=0"));
3265 assert!(PEER_PROBE_REQUIRED_REFUSAL.contains("MEMRA_PP_HOST_BOUNCE!=1"));
3266 }
3267
3268 #[test]
3269 fn runtime_reprobe_keeps_cheap_deadlines_live_while_expensive_work_waits_for_idle() {
3270 let every = PEER_RUNTIME_PROBE_INTERVAL_COPIES;
3271 assert_eq!(PEER_RUNTIME_PROBE_CYCLE_COPIES, 4 * every);
3272 let mut next = [every, 2 * every, 3 * every, 4 * every];
3273 let measured_ns = [1_000_000, 2_000_000, 3_000_000, 0];
3274
3275 assert_eq!(
3276 runtime_peer_probe_candidate(every - 1, next, measured_ns, false),
3277 None,
3278 );
3279 assert_eq!(
3280 runtime_peer_probe_candidate(every, next, measured_ns, false),
3281 Some((0, 1)),
3282 );
3283
3284 next[..3].copy_from_slice(&[5 * every, 6 * every, 7 * every]);
3287 assert_eq!(
3288 runtime_peer_probe_candidate(4 * every, next, measured_ns, false),
3289 None,
3290 );
3291 assert_eq!(
3294 runtime_peer_probe_candidate(5 * every, next, measured_ns, false),
3295 Some((0, 1)),
3296 );
3297 assert_eq!(
3299 runtime_peer_probe_candidate(5 * every, next, measured_ns, true),
3300 Some((3, crate::cache::PRIME_CHUNK_MAX_TOKENS)),
3301 );
3302 }
3303
3304 #[test]
3305 fn runtime_reprobe_moves_any_measured_over_budget_rung_to_idle_only() {
3306 let every = PEER_RUNTIME_PROBE_INTERVAL_COPIES;
3307 let next = [u64::MAX, every, u64::MAX, u64::MAX];
3308 let mut measured_ns = [0; PEER_PROBE_TOKEN_WIDTHS.len()];
3309 measured_ns[1] = PEER_RUNTIME_PROBE_BUDGET_NS + 1;
3310 assert_eq!(
3311 runtime_peer_probe_candidate(every, next, measured_ns, false),
3312 None
3313 );
3314 assert_eq!(
3315 runtime_peer_probe_candidate(every, next, measured_ns, true),
3316 Some((1, 8)),
3317 );
3318 }
3319
3320 #[test]
3321 fn late_runtime_reprobe_advances_once_instead_of_bursting_catchup() {
3322 let every = PEER_RUNTIME_PROBE_INTERVAL_COPIES;
3323 let due = every;
3324 assert_eq!(runtime_peer_probe_next_copy(due, due), due + 4 * every);
3325 assert_eq!(runtime_peer_probe_next_copy(due, 20 * every), 21 * every);
3326 }
3327
3328 #[test]
3329 fn runtime_reprobe_deferral_metric_counts_intervals_and_publishes_bound_state() {
3330 use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
3331
3332 assert_eq!(
3333 PEER_RUNTIME_PROBE_DEFERRAL_BOUND_INTERVALS * PEER_RUNTIME_PROBE_INTERVAL_COPIES,
3334 PEER_RUNTIME_PROBE_CYCLE_COPIES,
3335 );
3336 let deferred = AtomicU64::new(0);
3337 let degraded = AtomicBool::new(false);
3338 publish_runtime_peer_probe_deferral(&deferred, °raded, 1, false);
3339 assert_eq!(deferred.load(Ordering::Relaxed), 1);
3340 assert!(!degraded.load(Ordering::Acquire));
3341
3342 publish_runtime_peer_probe_deferral(
3343 &deferred,
3344 °raded,
3345 PEER_RUNTIME_PROBE_DEFERRAL_BOUND_INTERVALS - 1,
3346 true,
3347 );
3348 assert_eq!(
3349 deferred.load(Ordering::Relaxed),
3350 PEER_RUNTIME_PROBE_DEFERRAL_BOUND_INTERVALS,
3351 );
3352 assert!(degraded.load(Ordering::Acquire));
3353 }
3354
3355 #[test]
3356 fn runtime_probe_failure_latches_native_before_publishing_validated_bounce() {
3357 use std::sync::atomic::{AtomicBool, Ordering};
3358
3359 let failed = AtomicBool::new(false);
3360 let degraded = AtomicBool::new(false);
3361 let armed = latch_runtime_host_bounce(&failed, °raded, || Ok::<_, String>(()));
3362 assert!(armed.is_ok());
3363 assert!(failed.load(Ordering::Acquire));
3364 assert!(degraded.load(Ordering::Acquire));
3365
3366 let failed = AtomicBool::new(false);
3367 let degraded = AtomicBool::new(false);
3368 let refused = latch_runtime_host_bounce(&failed, °raded, || {
3369 Err::<(), _>("injected staging mismatch".to_string())
3370 });
3371 assert_eq!(refused, Err("injected staging mismatch".to_string()));
3372 assert!(failed.load(Ordering::Acquire));
3373 assert!(!degraded.load(Ordering::Acquire));
3374 }
3375
3376 #[test]
3377 fn transport_selection_keeps_peer_default_and_bounces_only_cross_device() {
3378 assert_eq!(boundary_transport(false, false), BoundaryTransport::Local);
3379 assert_eq!(boundary_transport(false, true), BoundaryTransport::Local);
3380 assert_eq!(boundary_transport(true, false), BoundaryTransport::Peer);
3381 assert_eq!(
3382 boundary_transport(true, true),
3383 BoundaryTransport::HostBounce
3384 );
3385 }
3386
3387 #[test]
3388 fn step37_geometry_sizes_each_slot_from_the_prime_cap() {
3389 let (elems, bytes) = host_bounce_capacity(4096).expect("valid Step-3.7 geometry");
3390 assert_eq!(elems, 4096 * crate::cache::PRIME_CHUNK_MAX_TOKENS);
3391 assert_eq!(bytes, 64 * 1024 * 1024);
3392 }
3393
3394 #[test]
3395 fn host_bounce_capacity_rejects_invalid_or_overflowing_geometry() {
3396 assert!(host_bounce_capacity(0).is_err());
3397 assert!(host_bounce_capacity(usize::MAX).is_err());
3398 }
3399}