1use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
77use std::sync::{Arc, Mutex, OnceLock, Weak};
78
79use cudarc::driver::{CudaContext, CudaEvent, CudaSlice, CudaStream};
80
81use crate::Engine;
82
83pub(crate) struct PrimaryContextRestore<'a> {
87 engine: &'a Engine,
88 restored: bool,
89}
90
91impl<'a> PrimaryContextRestore<'a> {
92 pub(crate) fn new(engine: &'a Engine) -> Self {
93 Self {
94 engine,
95 restored: false,
96 }
97 }
98
99 pub(crate) fn restore(mut self) -> Result<(), Box<dyn std::error::Error>> {
100 let result = self.engine.ctx().bind_to_thread();
101 self.restored = result.is_ok();
102 result?;
103 Ok(())
104 }
105}
106
107impl Drop for PrimaryContextRestore<'_> {
108 fn drop(&mut self) {
109 if !self.restored {
110 let _ = self.engine.ctx().bind_to_thread();
111 }
112 }
113}
114
115pub fn pp_cuts(n_layers: usize) -> Option<Vec<usize>> {
120 let n_st: usize = match std::env::var("MEMRA_PP_STAGES") {
121 Ok(v) if v.is_empty() || v == "0" || v == "1" => return None,
122 Ok(v) => match v.parse::<usize>() {
123 Ok(n) => n,
124 Err(_) => {
125 warn_bad_once(&format!("MEMRA_PP_STAGES={v} unparseable; door stays OFF"));
126 return None;
127 }
128 },
129 Err(_) => return None,
130 };
131 if n_st < 2 || n_st > n_layers {
132 warn_bad_once(&format!(
133 "MEMRA_PP_STAGES={n_st} outside [2, n_layers={n_layers}]; door stays OFF"
134 ));
135 return None;
136 }
137 let mut fence = Vec::with_capacity(n_st + 1);
138 fence.push(0usize);
139 if let Ok(s) = std::env::var("MEMRA_PP_SPLITS") {
140 let parts: Result<Vec<usize>, _> =
141 s.split(',').map(|p| p.trim().parse::<usize>()).collect();
142 match parts {
143 Ok(cuts) if cuts.len() == n_st - 1 => fence.extend(cuts),
144 _ => {
145 warn_bad_once(&format!(
146 "MEMRA_PP_SPLITS={s} invalid (want {} comma-separated cuts); door stays OFF",
147 n_st - 1
148 ));
149 return None;
150 }
151 }
152 } else if let Ok(v) = std::env::var("MEMRA_PP_SPLIT") {
153 if n_st != 2 {
156 warn_bad_once(&format!(
157 "MEMRA_PP_SPLIT={v} set with MEMRA_PP_STAGES={n_st}; use MEMRA_PP_SPLITS \
158 for N>2 — door stays OFF"
159 ));
160 return None;
161 }
162 match v.parse::<usize>() {
163 Ok(c) => fence.push(c),
164 Err(_) => {
165 warn_bad_once(&format!("MEMRA_PP_SPLIT={v} unparseable; door stays OFF"));
166 return None;
167 }
168 }
169 } else {
170 for s in 1..n_st {
171 fence.push(s * n_layers / n_st);
172 }
173 }
174 fence.push(n_layers);
175 for w in fence.windows(2) {
176 if w[0] >= w[1] {
177 warn_bad_once(&format!(
178 "pp stage fence {fence:?} not strictly increasing over [0, {n_layers}]; \
179 door stays OFF"
180 ));
181 return None;
182 }
183 }
184 Some(fence)
185}
186
187pub fn pp2_split(n_layers: usize) -> Option<usize> {
190 pp_cuts(n_layers).filter(|f| f.len() == 3).map(|f| f[1])
191}
192
193pub fn stage_of(fence: &[usize], il: usize) -> usize {
195 debug_assert!(fence.len() >= 2);
196 match fence[1..fence.len() - 1].binary_search(&il) {
197 Ok(k) => k + 1,
199 Err(k) => k,
200 }
201}
202
203pub fn pp2_streams_off() -> bool {
206 matches!(std::env::var("MEMRA_PP_STREAMS").as_deref(), Ok("0"))
207}
208
209pub fn pp_exit_publish() -> bool {
236 !matches!(std::env::var("MEMRA_PP_EXIT_PUBLISH").as_deref(), Ok("0"))
237}
238
239pub fn pp_multi_stream_same_device() -> bool {
261 let stages_open = std::env::var("MEMRA_PP_STAGES")
262 .map(|v| v.parse::<usize>().map(|n| n >= 2).unwrap_or(false))
263 .unwrap_or(false);
264 let devices = std::env::var("MEMRA_PP_DEVICES")
265 .ok()
266 .filter(|v| !v.is_empty());
267 if (!stages_open && devices.is_none()) || pp2_streams_off() {
268 return false;
269 }
270 match devices {
271 None => true, Some(s) => pp_devices_repeat(&s),
273 }
274}
275
276fn pp_devices_repeat(raw: &str) -> bool {
277 let Ok(mut devices) = raw
278 .split(',')
279 .map(|part| part.trim().parse::<usize>())
280 .collect::<Result<Vec<_>, _>>()
281 else {
282 return true;
285 };
286 let count = devices.len();
287 devices.sort_unstable();
288 devices.dedup();
289 devices.len() < count
290}
291
292pub fn pp_sharded_cross_device() -> bool {
306 let stages_open = std::env::var("MEMRA_PP_STAGES")
307 .map(|v| v.parse::<usize>().map(|n| n >= 2).unwrap_or(false))
308 .unwrap_or(false);
309 if !stages_open || pp_shard_off() || pp2_streams_off() {
316 return false;
317 }
318 match pp2_devices_env() {
319 None => false, Some(s) => {
321 let mut v: Vec<&str> = s.split(',').map(|p| p.trim()).collect();
322 v.sort_unstable();
323 v.dedup();
324 v.len() >= 2
325 }
326 }
327}
328
329pub fn refuse_unsplit_if_remote(path: &str, alt: &str) -> Result<(), Box<dyn std::error::Error>> {
341 if pp_host_bounce_active() {
342 return Err(format!(
343 "{path}: refused with MEMRA_PP_HOST_BOUNCE=1 on sharded cross-device PP — \
344 this unsplit path peer-reads remote weights, while host bounce covers only \
345 explicit stage-boundary transfers. Use {alt}; the \
346 MEMRA_PP_ALLOW_UNSPLIT_BATCH override is unavailable on a broken-peer host."
347 )
348 .into());
349 }
350 if pp_sharded_cross_device()
351 && std::env::var("MEMRA_PP_ALLOW_UNSPLIT_BATCH").as_deref() != Ok("1")
352 {
353 return Err(format!(
354 "{path}: refused with the ppN door open across 2+ devices — this path has no pp \
355 stage split, so it would walk ALL layers on one stream and peer-read every \
356 remote stage's weights each step (measured 28x slower at B=1, 13.9x at B=8 on \
357 a PRO 6000 pair over PCIe Gen5 x16 P2P; research/pp2-hardening-20260806). \
358 Exactness is unaffected — peer reads return identical bytes and the exactness \
359 gates PASS on this config — which is exactly why it must refuse instead of \
360 being caught by a gate. Fixes, in order: {alt}; or MEMRA_PP_SHARD=0 (all \
361 weights home on the primary — full speed, forfeits the capacity PP-2 exists \
362 for); or close the pp door. MEMRA_PP_ALLOW_UNSPLIT_BATCH=1 overrides for \
363 measurement."
364 )
365 .into());
366 }
367 Ok(())
368}
369
370pub fn batch_pp_on() -> bool {
378 std::env::var("MEMRA_BATCH_PP").as_deref() != Ok("0")
379}
380
381pub const PP_WAVE_MAX_STAGES: usize = 4;
385
386pub fn pp_wave_on_value(value: Option<&str>) -> Result<bool, &'static str> {
390 match value {
391 None | Some("0") => Ok(false),
392 Some("1") => Ok(true),
393 Some(_) => Err("MEMRA_PP_WAVE must be 0 or 1"),
394 }
395}
396
397pub fn pp_wave_on() -> Result<bool, &'static str> {
398 match std::env::var_os("MEMRA_PP_WAVE") {
399 None => pp_wave_on_value(None),
400 Some(value) => value
401 .to_str()
402 .ok_or("MEMRA_PP_WAVE must be valid UTF-8 and exactly 0 or 1")
403 .and_then(|value| pp_wave_on_value(Some(value))),
404 }
405}
406
407pub fn pp_wave_ranges(batch: usize, stages: usize) -> Vec<(usize, usize)> {
411 if batch == 0 || stages == 0 {
412 return Vec::new();
413 }
414 let waves = batch.min(stages);
415 let base = batch / waves;
416 let extra = batch % waves;
417 let mut out = Vec::with_capacity(waves);
418 let mut start = 0usize;
419 for wave in 0..waves {
420 let len = base + usize::from(wave < extra);
421 out.push((start, start + len));
422 start += len;
423 }
424 debug_assert_eq!(start, batch);
425 out
426}
427
428pub fn pp_wave_diagonal(stages: usize, waves: usize, diagonal: usize) -> Vec<(usize, usize)> {
433 if stages == 0 || waves == 0 || diagonal >= stages + waves - 1 {
434 return Vec::new();
435 }
436 let first_stage = diagonal.saturating_sub(waves - 1);
437 let last_stage = diagonal.min(stages - 1);
438 (first_stage..=last_stage)
439 .map(|stage| (diagonal - stage, stage))
440 .collect()
441}
442
443pub fn pp_wave_eligibility(
447 stages: usize,
448 double_slot: bool,
449 host_bounce: bool,
450 repeated_device: bool,
451) -> Result<(), &'static str> {
452 if !(3..=PP_WAVE_MAX_STAGES).contains(&stages) {
453 return Err("PP wavefront requires 3 or 4 stages; PP2 is owned by MEMRA_DUAL_PP");
454 }
455 if !double_slot {
456 return Err("PP wavefront requires MEMRA_PP_OVERLAP=1 double-buffered boundaries");
457 }
458 if host_bounce {
459 return Err(
460 "PP wavefront is unqualified with MEMRA_PP_HOST_BOUNCE=1; use native peer transport",
461 );
462 }
463 if repeated_device {
464 return Err("PP wavefront requires one distinct CUDA device per stage");
465 }
466 Ok(())
467}
468
469pub const PP_WAVE_W4A16_BF16_REFUSAL: &str = "PP wavefront for a W4A16 artifact with preserved BF16 non-expert weights requires \
475 MEMRA_BF16_MMV=1; without the row-wise BF16 program, wave batch-width decomposition changes \
476 logits. Keep MEMRA_PP_WAVE=0 or enable and qualify MEMRA_BF16_MMV=1";
477
478pub fn pp_wave_numeric_eligibility(
479 weight_only_nvfp4: bool,
480 bf16_mmv: bool,
481) -> Result<(), &'static str> {
482 if weight_only_nvfp4 && !bf16_mmv {
483 return Err(PP_WAVE_W4A16_BF16_REFUSAL);
484 }
485 Ok(())
486}
487
488pub fn pp_wave_route_enabled(
491 requested: bool,
492 overlap: bool,
493 stages: usize,
494 work_items: usize,
495) -> bool {
496 requested && overlap && (3..=PP_WAVE_MAX_STAGES).contains(&stages) && work_items >= 2
497}
498
499static PP_WAVE_ACTIVE_CELLS: AtomicUsize = AtomicUsize::new(0);
500static PP_WAVE_OVERLAPS: AtomicUsize = AtomicUsize::new(0);
501static PP_WAVE_TICKS: AtomicUsize = AtomicUsize::new(0);
502static PP_WAVE_CELLS: AtomicUsize = AtomicUsize::new(0);
503
504pub(crate) struct PpWaveCellGuard;
505
506pub(crate) fn enter_pp_wave_cell() -> PpWaveCellGuard {
510 let active = PP_WAVE_ACTIVE_CELLS.fetch_add(1, Ordering::AcqRel);
511 if active > 0 {
512 PP_WAVE_OVERLAPS.fetch_add(1, Ordering::Relaxed);
513 }
514 PP_WAVE_CELLS.fetch_add(1, Ordering::Relaxed);
515 PpWaveCellGuard
516}
517
518impl Drop for PpWaveCellGuard {
519 fn drop(&mut self) {
520 let active = PP_WAVE_ACTIVE_CELLS.fetch_sub(1, Ordering::AcqRel);
521 debug_assert!(active > 0, "PP wave active-cell counter underflow");
522 }
523}
524
525pub(crate) fn record_pp_wave_tick() {
526 PP_WAVE_TICKS.fetch_add(1, Ordering::Relaxed);
527}
528
529pub fn pp_wave_snapshot() -> (usize, usize, usize) {
531 (
532 PP_WAVE_TICKS.load(Ordering::Relaxed),
533 PP_WAVE_CELLS.load(Ordering::Relaxed),
534 PP_WAVE_OVERLAPS.load(Ordering::Relaxed),
535 )
536}
537
538#[derive(Clone, Copy, PartialEq, Eq, Debug)]
556pub enum DualPpMode {
557 Off,
558 Forced,
559 Auto,
560}
561
562pub fn dual_pp_mode_resolve(v: Option<&str>) -> DualPpMode {
565 match v {
566 Some("0") => DualPpMode::Off,
567 Some("1") => DualPpMode::Forced,
568 _ => DualPpMode::Auto,
569 }
570}
571
572pub fn dual_pp_mode() -> DualPpMode {
573 dual_pp_mode_resolve(std::env::var("MEMRA_DUAL_PP").ok().as_deref())
574}
575
576pub fn dual_pp_on() -> bool {
579 dual_pp_mode() != DualPpMode::Off
580}
581
582pub fn dual_pp_route(
588 mode: DualPpMode,
589 batch: usize,
590 stages: usize,
591 double_slot: bool,
592 host_bounce: bool,
593) -> bool {
594 if batch < 2 {
595 return false;
596 }
597 match mode {
598 DualPpMode::Off => false,
599 DualPpMode::Forced => true,
600 DualPpMode::Auto => stages == 2 && double_slot && !host_bounce,
601 }
602}
603
604pub 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";
607pub 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";
608
609#[allow(clippy::manual_div_ceil)] pub fn dual_pp_wave_mid(batch: usize) -> Option<usize> {
613 (batch >= 2).then_some((batch + 1) / 2)
614}
615
616pub fn dual_pp_eligibility(
619 stages: usize,
620 double_slot: bool,
621 host_bounce: bool,
622) -> Result<(), &'static str> {
623 if stages != 2 {
624 return Err(
625 "decode_step_batch_dual: refused: dual-active decode requires exactly two PP stages",
626 );
627 }
628 if !double_slot {
629 return Err(DUAL_PP_SINGLE_SLOT_REFUSAL);
630 }
631 if host_bounce {
632 return Err(DUAL_PP_HOST_BOUNCE_REFUSAL);
633 }
634 Ok(())
635}
636
637static DUAL_PP_OVERLAPS: AtomicUsize = AtomicUsize::new(0);
640static DUAL_PP_ACTIVE_STAGES: AtomicUsize = AtomicUsize::new(0);
641static DUAL_PP_STAGE_NS: [AtomicU64; 4] = [
642 AtomicU64::new(0),
643 AtomicU64::new(0),
644 AtomicU64::new(0),
645 AtomicU64::new(0),
646];
647static DUAL_PP_STAGE_SAMPLES: [AtomicUsize; 4] = [
648 AtomicUsize::new(0),
649 AtomicUsize::new(0),
650 AtomicUsize::new(0),
651 AtomicUsize::new(0),
652];
653static DUAL_PP_TIMING_DROPPED: AtomicUsize = AtomicUsize::new(0);
654static DUAL_PP_SLOT_PAIRS: AtomicUsize = AtomicUsize::new(0);
655static DUAL_PP_SLOT_USES: [AtomicUsize; 2] = [AtomicUsize::new(0), AtomicUsize::new(0)];
656static DUAL_PP_SLOT_COLLISIONS: AtomicUsize = AtomicUsize::new(0);
657
658pub const DUAL_PP_STAGE_NAMES: [&str; 4] = [
659 "wave_a_stage0",
660 "wave_a_stage1",
661 "wave_b_stage0",
662 "wave_b_stage1",
663];
664
665pub fn dual_pp_overlaps() -> usize {
666 DUAL_PP_OVERLAPS.load(Ordering::Relaxed)
667}
668
669pub(crate) fn record_dual_pp_slot_pair(slot_a: usize, slot_b: usize) -> bool {
673 debug_assert!(slot_a < DUAL_PP_SLOT_USES.len());
674 debug_assert!(slot_b < DUAL_PP_SLOT_USES.len());
675 if slot_a == slot_b {
676 DUAL_PP_SLOT_COLLISIONS.fetch_add(1, Ordering::Relaxed);
677 return false;
678 }
679 DUAL_PP_SLOT_USES[slot_a].fetch_add(1, Ordering::Relaxed);
680 DUAL_PP_SLOT_USES[slot_b].fetch_add(1, Ordering::Relaxed);
681 DUAL_PP_SLOT_PAIRS.fetch_add(1, Ordering::Relaxed);
682 true
683}
684
685pub fn dual_pp_slot_snapshot() -> (usize, [usize; 2], usize) {
687 (
688 DUAL_PP_SLOT_PAIRS.load(Ordering::Relaxed),
689 std::array::from_fn(|i| DUAL_PP_SLOT_USES[i].load(Ordering::Relaxed)),
690 DUAL_PP_SLOT_COLLISIONS.load(Ordering::Relaxed),
691 )
692}
693
694pub fn dual_pp_timing_on() -> bool {
698 static ON: OnceLock<bool> = OnceLock::new();
699 *ON.get_or_init(|| std::env::var("MEMRA_DUAL_PP_TIMING").as_deref() == Ok("1"))
700}
701
702pub(crate) fn record_dual_pp_stage_ms(stage: usize, ms: f32) {
703 assert!(
704 stage < DUAL_PP_STAGE_NS.len(),
705 "dual PP timing stage out of range"
706 );
707 let ns = (f64::from(ms) * 1_000_000.0).round() as u64;
708 DUAL_PP_STAGE_NS[stage].fetch_add(ns, Ordering::Relaxed);
709 DUAL_PP_STAGE_SAMPLES[stage].fetch_add(1, Ordering::Relaxed);
710}
711
712pub(crate) fn record_dual_pp_timing_drop(context: &str, err: &dyn std::fmt::Display) {
715 let previous = DUAL_PP_TIMING_DROPPED.fetch_add(1, Ordering::Relaxed);
716 if previous == 0 {
717 eprintln!(
718 "[dual-pp] WARN: skipped diagnostic timing sample at {context}: {err}; decode continues"
719 );
720 }
721}
722
723pub(crate) fn record_dual_pp_stage_result<E: std::fmt::Display>(
724 stage: usize,
725 elapsed: Result<f32, E>,
726) {
727 match elapsed {
728 Ok(ms) => record_dual_pp_stage_ms(stage, ms),
729 Err(err) => record_dual_pp_timing_drop(DUAL_PP_STAGE_NAMES[stage], &err),
730 }
731}
732
733pub fn dual_pp_timing_dropped() -> usize {
734 DUAL_PP_TIMING_DROPPED.load(Ordering::Relaxed)
735}
736
737pub fn dual_pp_timing_snapshot() -> ([u64; 4], [usize; 4]) {
739 (
740 std::array::from_fn(|i| DUAL_PP_STAGE_NS[i].load(Ordering::Relaxed)),
741 std::array::from_fn(|i| DUAL_PP_STAGE_SAMPLES[i].load(Ordering::Relaxed)),
742 )
743}
744
745pub(crate) struct DualPpStageGuard;
746
747pub(crate) fn enter_dual_pp_stage() -> DualPpStageGuard {
748 let active = DUAL_PP_ACTIVE_STAGES.fetch_add(1, Ordering::AcqRel);
749 if active > 0 {
750 DUAL_PP_OVERLAPS.fetch_add(1, Ordering::Relaxed);
751 }
752 DualPpStageGuard
753}
754
755impl Drop for DualPpStageGuard {
756 fn drop(&mut self) {
757 let active = DUAL_PP_ACTIVE_STAGES.fetch_sub(1, Ordering::AcqRel);
758 debug_assert!(active > 0, "dual PP active-stage counter underflow");
759 }
760}
761
762pub fn prime_pp_on() -> bool {
772 std::env::var("MEMRA_PRIME_PP").as_deref() != Ok("0")
773}
774
775pub fn prime_pipe_on() -> bool {
780 std::env::var("MEMRA_PRIME_PIPE").as_deref() != Ok("0")
781}
782
783pub static PRIME_SPLIT_CHUNKS: AtomicUsize = AtomicUsize::new(0);
790
791pub fn prime_split_chunks() -> usize {
793 PRIME_SPLIT_CHUNKS.load(Ordering::Relaxed)
794}
795
796pub static PRIME_PIPE_OVERLAPS: AtomicUsize = AtomicUsize::new(0);
801
802pub fn prime_pipe_overlaps() -> usize {
804 PRIME_PIPE_OVERLAPS.load(Ordering::Relaxed)
805}
806
807static PRIME_PIPE_ACTIVE_STAGES: AtomicUsize = AtomicUsize::new(0);
808
809pub(crate) struct PrimePipeStageGuard;
810
811pub(crate) fn enter_prime_pipe_stage() -> PrimePipeStageGuard {
814 let active = PRIME_PIPE_ACTIVE_STAGES.fetch_add(1, Ordering::AcqRel);
815 if active > 0 {
816 PRIME_PIPE_OVERLAPS.fetch_add(1, Ordering::Relaxed);
817 }
818 PrimePipeStageGuard
819}
820
821impl Drop for PrimePipeStageGuard {
822 fn drop(&mut self) {
823 let active = PRIME_PIPE_ACTIVE_STAGES.fetch_sub(1, Ordering::AcqRel);
824 debug_assert!(active > 0, "prime pipeline active-stage counter underflow");
825 }
826}
827
828pub static STEP35_PRIME_BATCHES: AtomicUsize = AtomicUsize::new(0);
832pub static STEP35_PRIME_BATCH_SPLITS: AtomicUsize = AtomicUsize::new(0);
833
834pub fn step35_prime_batches() -> usize {
835 STEP35_PRIME_BATCHES.load(Ordering::Relaxed)
836}
837
838pub fn step35_prime_batch_splits() -> usize {
839 STEP35_PRIME_BATCH_SPLITS.load(Ordering::Relaxed)
840}
841
842pub fn spec_pp_on() -> bool {
850 std::env::var("MEMRA_SPEC_PP").as_deref() != Ok("0")
851}
852
853pub fn pp2_overlap() -> bool {
864 pp2_overlap_resolve(
865 std::env::var("MEMRA_PP_OVERLAP").ok().as_deref(),
866 dual_pp_mode(),
867 )
868}
869
870pub fn pp2_overlap_resolve(v: Option<&str>, mode: DualPpMode) -> bool {
873 match v {
874 Some("1") => true,
875 Some(_) => false,
876 None => mode == DualPpMode::Auto,
877 }
878}
879
880pub fn pp_host_bounce_on() -> bool {
883 matches!(std::env::var("MEMRA_PP_HOST_BOUNCE").as_deref(), Ok("1"))
884}
885
886pub fn pp_host_bounce_active() -> bool {
889 (pp_host_bounce_on() || PEER_RUNTIME_HOST_BOUNCE.load(Ordering::Acquire))
890 && pp_sharded_cross_device()
891}
892
893pub fn pp_shard_off() -> bool {
897 matches!(std::env::var("MEMRA_PP_SHARD").as_deref(), Ok("0"))
898}
899
900fn pp2_devices_env() -> Option<String> {
903 std::env::var("MEMRA_PP_DEVICES")
904 .ok()
905 .filter(|v| !v.is_empty())
906}
907
908static WARNED_BAD: AtomicBool = AtomicBool::new(false);
909fn warn_bad_once(msg: &str) {
910 if !WARNED_BAD.swap(true, Ordering::Relaxed) {
911 eprintln!("[pp] {msg}");
912 }
913}
914
915static WARNED_UNWIRED: AtomicBool = AtomicBool::new(false);
916pub fn warn_unwired_once(path: &str) {
919 let open = std::env::var("MEMRA_PP_STAGES")
920 .map(|v| !v.is_empty() && v != "0" && v != "1")
921 .unwrap_or(false);
922 if open && !WARNED_UNWIRED.swap(true, Ordering::Relaxed) {
923 eprintln!("[pp] MEMRA_PP_STAGES set but `{path}` has no pp arm at this N; running unsplit");
924 }
925}
926
927pub struct StageRt {
935 pub dev: usize,
936 pub ctx: Arc<CudaContext>,
937 pub stream: Arc<CudaStream>,
938 pub blas: Arc<cudarc::cublaslt::CudaBlasLT>,
939 engine: Option<Engine>,
941}
942
943struct BoundarySlot {
948 buf: Mutex<Option<CudaSlice<f32>>>,
949 ev_tx: CudaEvent,
952 ev_rx: CudaEvent,
956}
957
958struct BoundaryRt {
962 slots: [BoundarySlot; 2],
963 step: AtomicUsize,
964 cross: bool,
966}
967
968#[derive(Clone, Copy, Debug, PartialEq, Eq)]
969enum BoundaryTransport {
970 Local,
971 Peer,
972 HostBounce,
973}
974
975#[derive(Clone, Copy)]
976struct BoundaryPath {
977 boundary: usize,
978 src_stage: usize,
979 dst_stage: usize,
980 transport: BoundaryTransport,
981}
982
983fn boundary_transport(cross: bool, host_bounce: bool) -> BoundaryTransport {
984 match (cross, host_bounce) {
985 (false, _) => BoundaryTransport::Local,
986 (true, false) => BoundaryTransport::Peer,
987 (true, true) => BoundaryTransport::HostBounce,
988 }
989}
990
991const PEER_PROBE_FIXED_BYTES: usize = 16 * 1024;
992const PEER_PROBE_TOKEN_WIDTHS: [usize; 4] = [1, 8, 16, crate::cache::PRIME_CHUNK_MAX_TOKENS];
993
994pub const PEER_RUNTIME_PROBE_INTERVAL_COPIES: u64 = 8 * 1024;
997pub const PEER_RUNTIME_PROBE_CYCLE_COPIES: u64 =
999 PEER_RUNTIME_PROBE_INTERVAL_COPIES * PEER_PROBE_TOKEN_WIDTHS.len() as u64;
1000pub const PEER_RUNTIME_PROBE_DEFERRAL_BOUND_INTERVALS: u64 = PEER_PROBE_TOKEN_WIDTHS.len() as u64;
1003const PEER_RUNTIME_PROBE_BUDGET_NS: u64 = 5_000_000;
1005
1006pub const PEER_PROBE_REQUIRED_REFUSAL: &str = "PP bring-up refused: MEMRA_PEER_PROBE=0 cannot authorize native peer transport for a \
1007 sharded cross-device placement while MEMRA_PP_HOST_BOUNCE!=1; leave MEMRA_PEER_PROBE \
1008 enabled or set MEMRA_PP_HOST_BOUNCE=1";
1009
1010#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1011pub enum PeerProbeStartupPolicy {
1012 Allowed,
1013 BypassedWithHostBounce,
1014}
1015
1016pub fn peer_probe_startup_policy(
1019 probe_on: bool,
1020 sharded_cross_device: bool,
1021 host_bounce: bool,
1022) -> Result<PeerProbeStartupPolicy, &'static str> {
1023 match (probe_on, sharded_cross_device, host_bounce) {
1024 (false, true, false) => Err(PEER_PROBE_REQUIRED_REFUSAL),
1025 (false, true, true) => Ok(PeerProbeStartupPolicy::BypassedWithHostBounce),
1026 _ => Ok(PeerProbeStartupPolicy::Allowed),
1027 }
1028}
1029
1030static PEER_PROBE_BYPASSED: AtomicU64 = AtomicU64::new(0);
1031static PEER_BOUNDARY_COPIES: AtomicU64 = AtomicU64::new(0);
1032static PEER_RUNTIME_PROBES: AtomicU64 = AtomicU64::new(0);
1033static PEER_RUNTIME_PROBE_FAILURES: AtomicU64 = AtomicU64::new(0);
1034static PEER_RUNTIME_PROBE_DEFERRED: AtomicU64 = AtomicU64::new(0);
1035static PEER_RUNTIME_PROBE_INTEGRITY_DEGRADED: AtomicBool = AtomicBool::new(false);
1036static PEER_RUNTIME_PROBE_FAILED: AtomicBool = AtomicBool::new(false);
1037static PEER_RUNTIME_HOST_BOUNCE: AtomicBool = AtomicBool::new(false);
1038static PEER_RUNTIME_NEXT_PROBE_COPY: [AtomicU64; PEER_PROBE_TOKEN_WIDTHS.len()] = [
1039 AtomicU64::new(PEER_RUNTIME_PROBE_INTERVAL_COPIES),
1040 AtomicU64::new(2 * PEER_RUNTIME_PROBE_INTERVAL_COPIES),
1041 AtomicU64::new(3 * PEER_RUNTIME_PROBE_INTERVAL_COPIES),
1042 AtomicU64::new(4 * PEER_RUNTIME_PROBE_INTERVAL_COPIES),
1043];
1044static PEER_RUNTIME_PROBE_MAX_COST_NS: [AtomicU64; PEER_PROBE_TOKEN_WIDTHS.len()] = [
1045 AtomicU64::new(0),
1046 AtomicU64::new(0),
1047 AtomicU64::new(0),
1048 AtomicU64::new(0),
1049];
1050
1051#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1052pub struct PeerProbeMetrics {
1053 pub bypassed: u64,
1054 pub boundary_copies: u64,
1055 pub runtime_probes: u64,
1056 pub runtime_failures: u64,
1057 pub deferred_total: u64,
1058 pub integrity_degraded: bool,
1059 pub degraded_to_host_bounce: bool,
1060}
1061
1062pub fn peer_probe_metrics() -> PeerProbeMetrics {
1063 PeerProbeMetrics {
1064 bypassed: PEER_PROBE_BYPASSED.load(Ordering::Relaxed),
1065 boundary_copies: PEER_BOUNDARY_COPIES.load(Ordering::Relaxed),
1066 runtime_probes: PEER_RUNTIME_PROBES.load(Ordering::Relaxed),
1067 runtime_failures: PEER_RUNTIME_PROBE_FAILURES.load(Ordering::Relaxed),
1068 deferred_total: PEER_RUNTIME_PROBE_DEFERRED.load(Ordering::Relaxed),
1069 integrity_degraded: PEER_RUNTIME_PROBE_INTEGRITY_DEGRADED.load(Ordering::Acquire),
1070 degraded_to_host_bounce: PEER_RUNTIME_HOST_BOUNCE.load(Ordering::Acquire),
1071 }
1072}
1073
1074#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1075pub enum RuntimePeerProbeStatus {
1076 NotRun,
1077 Deferred,
1078 Passed,
1079 DegradedToHostBounce,
1080}
1081
1082impl RuntimePeerProbeStatus {
1083 pub fn ran(self) -> bool {
1084 matches!(self, Self::Passed | Self::DegradedToHostBounce)
1085 }
1086}
1087
1088fn publish_runtime_peer_probe_deferral(
1089 deferred_total: &AtomicU64,
1090 integrity_degraded: &AtomicBool,
1091 intervals: u64,
1092 bound_reached: bool,
1093) {
1094 deferred_total.fetch_add(intervals, Ordering::Relaxed);
1095 if bound_reached {
1096 integrity_degraded.store(true, Ordering::Release);
1097 }
1098}
1099
1100pub fn record_runtime_peer_probe_deferral(intervals: u64, bound_reached: bool) {
1103 publish_runtime_peer_probe_deferral(
1104 &PEER_RUNTIME_PROBE_DEFERRED,
1105 &PEER_RUNTIME_PROBE_INTEGRITY_DEGRADED,
1106 intervals,
1107 bound_reached,
1108 );
1109}
1110
1111pub fn clear_runtime_peer_probe_integrity_degraded() {
1113 PEER_RUNTIME_PROBE_INTEGRITY_DEGRADED.store(false, Ordering::Release);
1114}
1115
1116fn runtime_peer_probe_idle_only(width_index: usize, measured_cost_ns: u64) -> bool {
1117 width_index + 1 == PEER_PROBE_TOKEN_WIDTHS.len()
1118 || measured_cost_ns > PEER_RUNTIME_PROBE_BUDGET_NS
1119}
1120
1121fn runtime_peer_probe_candidate(
1124 copies: u64,
1125 next_probe_copy: [u64; PEER_PROBE_TOKEN_WIDTHS.len()],
1126 measured_cost_ns: [u64; PEER_PROBE_TOKEN_WIDTHS.len()],
1127 scheduler_idle: bool,
1128) -> Option<(usize, usize)> {
1129 let mut selected: Option<(usize, u64)> = None;
1130 for width_index in 0..PEER_PROBE_TOKEN_WIDTHS.len() {
1131 let due = next_probe_copy[width_index];
1132 if copies < due
1133 || (!scheduler_idle
1134 && runtime_peer_probe_idle_only(width_index, measured_cost_ns[width_index]))
1135 {
1136 continue;
1137 }
1138 if selected.is_none_or(|(_, selected_due)| due < selected_due) {
1139 selected = Some((width_index, due));
1140 }
1141 }
1142 selected.map(|(width_index, _)| (width_index, PEER_PROBE_TOKEN_WIDTHS[width_index]))
1143}
1144
1145fn runtime_peer_probe_next_copy(due: u64, copies: u64) -> u64 {
1148 let cycles = copies.saturating_sub(due) / PEER_RUNTIME_PROBE_CYCLE_COPIES + 1;
1149 due.saturating_add(PEER_RUNTIME_PROBE_CYCLE_COPIES.saturating_mul(cycles))
1150}
1151
1152fn latch_runtime_host_bounce<E>(
1155 native_failed: &AtomicBool,
1156 degraded_to_host_bounce: &AtomicBool,
1157 arm_and_validate: impl FnOnce() -> Result<(), E>,
1158) -> Result<(), E> {
1159 native_failed.store(true, Ordering::Release);
1160 arm_and_validate()?;
1161 degraded_to_host_bounce.store(true, Ordering::Release);
1162 Ok(())
1163}
1164
1165fn peer_probe_on() -> bool {
1166 std::env::var("MEMRA_PEER_PROBE").as_deref() != Ok("0")
1167}
1168
1169#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1170enum PeerProbeDecision {
1171 Clean,
1172 ProceedWithHostBounce { mismatches: usize },
1173}
1174
1175fn peer_probe_mismatch_count(expected: &[u8], readback: &[u8]) -> usize {
1176 expected
1177 .iter()
1178 .zip(readback)
1179 .filter(|(a, b)| a != b)
1180 .count()
1181 + expected.len().abs_diff(readback.len())
1182}
1183
1184fn peer_probe_decision(
1185 expected: &[u8],
1186 readback: &[u8],
1187 host_bounce: bool,
1188) -> Result<PeerProbeDecision, String> {
1189 let mismatches = peer_probe_mismatch_count(expected, readback);
1190 if mismatches == 0 {
1191 Ok(PeerProbeDecision::Clean)
1192 } else if host_bounce {
1193 Ok(PeerProbeDecision::ProceedWithHostBounce { mismatches })
1194 } else {
1195 Err(format!("{mismatches} mismatched byte(s)"))
1196 }
1197}
1198
1199fn peer_probe_pattern(bytes: usize, boundary: usize, src_dev: usize, dst_dev: usize) -> Vec<u8> {
1200 let mut state = 0xD1B5_4A32_D192_ED03u64
1201 ^ (bytes as u64).rotate_left(7)
1202 ^ (boundary as u64).rotate_left(19)
1203 ^ (src_dev as u64).rotate_left(31)
1204 ^ (dst_dev as u64).rotate_left(43);
1205 (0..bytes)
1206 .map(|_| {
1207 state ^= state << 13;
1208 state ^= state >> 7;
1209 state ^= state << 17;
1210 state as u8
1211 })
1212 .collect()
1213}
1214
1215fn peer_probe_bytes_to_f32(bytes: &[u8]) -> Vec<f32> {
1216 assert_eq!(bytes.len() % std::mem::size_of::<f32>(), 0);
1217 bytes
1218 .chunks_exact(std::mem::size_of::<f32>())
1219 .map(|chunk| f32::from_bits(u32::from_ne_bytes(chunk.try_into().unwrap())))
1220 .collect()
1221}
1222
1223fn peer_probe_f32_to_bytes(values: &[f32]) -> Vec<u8> {
1224 values
1225 .iter()
1226 .flat_map(|value| value.to_bits().to_ne_bytes())
1227 .collect()
1228}
1229
1230struct PeerProbeBuffer {
1234 ctx: Arc<CudaContext>,
1235 ptr: cudarc::driver::sys::CUdeviceptr,
1236}
1237
1238impl PeerProbeBuffer {
1239 fn new(ctx: &Arc<CudaContext>, bytes: usize) -> Result<Self, Box<dyn std::error::Error>> {
1240 ctx.bind_to_thread()?;
1241 let ptr = unsafe { cudarc::driver::result::malloc_sync(bytes)? };
1242 Ok(Self {
1243 ctx: ctx.clone(),
1244 ptr,
1245 })
1246 }
1247}
1248
1249impl Drop for PeerProbeBuffer {
1250 fn drop(&mut self) {
1251 if self.ctx.bind_to_thread().is_ok() {
1252 let _ = unsafe { cudarc::driver::result::free_sync(self.ptr) };
1253 }
1254 }
1255}
1256
1257fn peer_probe_copy(
1258 src: &StageRt,
1259 dst: &StageRt,
1260 expected: &[u8],
1261) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
1262 let bytes = expected.len();
1263 let src_buf = PeerProbeBuffer::new(&src.ctx, bytes)?;
1264 unsafe {
1265 cudarc::driver::result::memcpy_htod_sync(src_buf.ptr, expected)?;
1266 }
1267
1268 let dst_buf = PeerProbeBuffer::new(&dst.ctx, bytes)?;
1269 let poison: Vec<u8> = expected.iter().map(|b| !b).collect();
1270 unsafe {
1271 cudarc::driver::result::memcpy_htod_sync(dst_buf.ptr, &poison)?;
1272 }
1273
1274 src.ctx.bind_to_thread()?;
1275 unsafe {
1276 cudarc::driver::result::memcpy_peer_async(
1277 dst.ctx.cu_ctx(),
1278 dst_buf.ptr,
1279 src.ctx.cu_ctx(),
1280 src_buf.ptr,
1281 bytes,
1282 src.stream.cu_stream(),
1283 )?;
1284 }
1285
1286 let published = src.ctx.new_event(None)?;
1292 published.record(&src.stream)?;
1293
1294 dst.ctx.bind_to_thread()?;
1295 dst.stream.wait(&published)?;
1296 dst.stream.synchronize()?;
1297 let mut readback = vec![0u8; bytes];
1298 unsafe {
1299 cudarc::driver::result::memcpy_dtoh_sync(&mut readback, dst_buf.ptr)?;
1300 }
1301 Ok(readback)
1302}
1303
1304fn run_peer_probe_pass(
1305 stages: &[StageRt],
1306 peer_capable: &[(usize, usize)],
1307 host_bounce: bool,
1308 label: &str,
1309 bytes: usize,
1310) -> Result<(), Box<dyn std::error::Error>> {
1311 if bytes == 0 {
1312 return Err(format!("PP peer byte-integrity probe {label} size is zero").into());
1313 }
1314 let started = std::time::Instant::now();
1315 let mut copies = 0usize;
1316 let mut skipped = 0usize;
1317 let mut total_mismatches = 0usize;
1318
1319 for boundary in 0..stages.len() - 1 {
1320 if stages[boundary].dev == stages[boundary + 1].dev {
1321 continue;
1322 }
1323 for (src_idx, dst_idx) in [(boundary, boundary + 1), (boundary + 1, boundary)] {
1324 let src = &stages[src_idx];
1325 let dst = &stages[dst_idx];
1326 if !peer_capable.contains(&(src.dev, dst.dev)) {
1327 if host_bounce {
1328 skipped += 1;
1329 eprintln!(
1330 "[pp] peer byte-integrity probe SKIP: boundary={boundary} \
1331 dev{}->dev{} label={label} bytes={bytes} (peer capability unavailable; \
1332 MEMRA_PP_HOST_BOUNCE=1 remains fail-safe)",
1333 src.dev, dst.dev,
1334 );
1335 continue;
1336 }
1337 return Err(format!(
1338 "PP peer byte-integrity probe cannot run boundary={boundary} \
1339 dev{}->dev{}: peer access was not enabled",
1340 src.dev, dst.dev,
1341 )
1342 .into());
1343 }
1344
1345 let expected = peer_probe_pattern(bytes, boundary, src.dev, dst.dev);
1346 let readback = match peer_probe_copy(src, dst, &expected) {
1347 Ok(readback) => readback,
1348 Err(err) if host_bounce => {
1349 skipped += 1;
1350 eprintln!(
1351 "[pp] peer byte-integrity probe ERROR: boundary={boundary} \
1352 dev{}->dev{} label={label} bytes={bytes}: {err}; \
1353 MEMRA_PP_HOST_BOUNCE=1, proceeding on the host-staged path",
1354 src.dev, dst.dev,
1355 );
1356 continue;
1357 }
1358 Err(err) => {
1359 return Err(format!(
1360 "PP peer byte-integrity probe FAILED: boundary={boundary} \
1361 dev{}->dev{} label={label} bytes={bytes}: {err}; refusing native P2P \
1362 (set MEMRA_PP_HOST_BOUNCE=1 to use the host-staged path; \
1363 MEMRA_PEER_PROBE=0 cannot authorize sharded native peer transport)",
1364 src.dev, dst.dev,
1365 )
1366 .into());
1367 }
1368 };
1369 copies += 1;
1370 match peer_probe_decision(&expected, &readback, host_bounce) {
1371 Ok(PeerProbeDecision::Clean) => {}
1372 Ok(PeerProbeDecision::ProceedWithHostBounce { mismatches }) => {
1373 total_mismatches += mismatches;
1374 eprintln!(
1375 "[pp] peer byte-integrity probe CORRUPTION: boundary={boundary} \
1376 dev{}->dev{} label={label} bytes={bytes} mismatches={mismatches}; \
1377 MEMRA_PP_HOST_BOUNCE=1, proceeding on the host-staged path",
1378 src.dev, dst.dev,
1379 );
1380 }
1381 Err(mismatch) => {
1382 return Err(format!(
1383 "PP peer byte-integrity probe FAILED: boundary={boundary} \
1384 dev{}->dev{} label={label} bytes={bytes}: {mismatch}; refusing native \
1385 P2P (set MEMRA_PP_HOST_BOUNCE=1 to use the host-staged path; \
1386 MEMRA_PEER_PROBE=0 cannot authorize sharded native peer transport)",
1387 src.dev, dst.dev,
1388 )
1389 .into());
1390 }
1391 }
1392 }
1393 }
1394
1395 let status = if total_mismatches > 0 {
1396 "BOUNCE"
1397 } else if skipped > 0 && copies > 0 {
1398 "PARTIAL"
1399 } else if skipped > 0 {
1400 "SKIP"
1401 } else {
1402 "PASS"
1403 };
1404 eprintln!(
1405 "[pp] peer byte-integrity probe {}: label={label} bytes={bytes} copies={copies} \
1406 skipped={skipped} mismatches={total_mismatches} elapsed_ms={:.3}",
1407 status,
1408 started.elapsed().as_secs_f64() * 1e3,
1409 );
1410 Ok(())
1411}
1412
1413fn host_bounce_capacity(n_embd: usize) -> Result<(usize, usize), String> {
1414 if n_embd == 0 {
1415 return Err("MEMRA_PP_HOST_BOUNCE needs non-zero model n_embd".into());
1416 }
1417 let elems = n_embd
1418 .checked_mul(crate::cache::PRIME_CHUNK_MAX_TOKENS)
1419 .ok_or_else(|| format!("host-bounce element count overflows for n_embd={n_embd}"))?;
1420 let bytes = elems
1421 .checked_mul(std::mem::size_of::<f32>())
1422 .ok_or_else(|| format!("host-bounce byte count overflows for n_embd={n_embd}"))?;
1423 Ok((elems, bytes))
1424}
1425
1426fn boundary_slot_growth_elements(current: [usize; 2], required: usize) -> usize {
1427 current.into_iter().fold(0usize, |total, len| {
1428 total.saturating_add(required.saturating_sub(len))
1429 })
1430}
1431
1432struct PinnedHostBounce {
1437 ptr: *mut f32,
1438 len: usize,
1439}
1440
1441unsafe impl Send for PinnedHostBounce {}
1442unsafe impl Sync for PinnedHostBounce {}
1443
1444impl PinnedHostBounce {
1445 fn new(len: usize) -> Result<Self, Box<dyn std::error::Error>> {
1446 let bytes = len
1447 .checked_mul(std::mem::size_of::<f32>())
1448 .ok_or("host-bounce pinned allocation size overflow")?;
1449 let ptr = unsafe {
1450 cudarc::driver::result::malloc_host(
1451 bytes,
1452 cudarc::driver::sys::CU_MEMHOSTALLOC_PORTABLE,
1453 )?
1454 } as *mut f32;
1455 if ptr.is_null() {
1456 return Err("cuMemHostAlloc returned a null host-bounce pointer".into());
1457 }
1458 Ok(Self { ptr, len })
1459 }
1460
1461 fn prefix(&self, n: usize) -> &[f32] {
1462 assert!(
1463 n <= self.len,
1464 "host-bounce source {n} > capacity {}",
1465 self.len
1466 );
1467 unsafe { std::slice::from_raw_parts(self.ptr, n) }
1468 }
1469
1470 fn prefix_mut(&mut self, n: usize) -> &mut [f32] {
1471 assert!(
1472 n <= self.len,
1473 "host-bounce destination {n} > capacity {}",
1474 self.len
1475 );
1476 unsafe { std::slice::from_raw_parts_mut(self.ptr, n) }
1477 }
1478}
1479
1480impl Drop for PinnedHostBounce {
1481 fn drop(&mut self) {
1482 let _ = unsafe { cudarc::driver::result::free_host(self.ptr.cast()) };
1483 }
1484}
1485
1486struct HostBounceRt {
1487 n_embd: usize,
1488 capacity: usize,
1489 slots: Vec<Option<[Mutex<PinnedHostBounce>; 2]>>,
1490}
1491
1492impl HostBounceRt {
1493 fn new(n_embd: usize, boundaries: &[BoundaryRt]) -> Result<Self, Box<dyn std::error::Error>> {
1494 let (capacity, _) = host_bounce_capacity(n_embd)?;
1495 let mut slots = Vec::with_capacity(boundaries.len());
1496 for boundary in boundaries {
1497 slots.push(if boundary.cross {
1498 Some([
1499 Mutex::new(PinnedHostBounce::new(capacity)?),
1500 Mutex::new(PinnedHostBounce::new(capacity)?),
1501 ])
1502 } else {
1503 None
1504 });
1505 }
1506 Ok(Self {
1507 n_embd,
1508 capacity,
1509 slots,
1510 })
1511 }
1512
1513 fn slot(
1514 &self,
1515 boundary: usize,
1516 slot: usize,
1517 ) -> Result<&Mutex<PinnedHostBounce>, Box<dyn std::error::Error>> {
1518 self.slots
1519 .get(boundary)
1520 .and_then(Option::as_ref)
1521 .and_then(|slots| slots.get(slot))
1522 .ok_or_else(|| format!("host-bounce slot {boundary}:{slot} is not initialized").into())
1523 }
1524}
1525
1526pub struct PpNRt {
1527 stages: Vec<StageRt>,
1528 boundaries: Vec<BoundaryRt>,
1529 walk_active: Arc<AtomicU64>,
1533 walk_next: AtomicU64,
1534 deferred_walk: Mutex<Weak<PpWalkState>>,
1538 cross_any: bool,
1540 host_bounce: bool,
1543 peer_probe: bool,
1545 peer_capable: Vec<(usize, usize)>,
1547 peer_probe_geometry: OnceLock<Result<usize, String>>,
1549 bounce: OnceLock<Result<HostBounceRt, String>>,
1551 readback: Arc<CudaStream>,
1554}
1555
1556#[derive(Debug)]
1557struct PpWalkState {
1558 active: Arc<AtomicU64>,
1559 generation: u64,
1560 runtime_id: usize,
1561 deferred_owner: Option<std::thread::ThreadId>,
1562}
1563
1564impl PpWalkState {
1565 fn is_active(&self) -> bool {
1566 self.active.load(Ordering::Acquire) == self.generation
1567 }
1568}
1569
1570impl Drop for PpWalkState {
1571 fn drop(&mut self) {
1572 let _ =
1573 self.active
1574 .compare_exchange(self.generation, 0, Ordering::AcqRel, Ordering::Acquire);
1575 }
1576}
1577
1578#[derive(Debug)]
1582pub struct PpWalkLease {
1583 state: Arc<PpWalkState>,
1584}
1585
1586fn next_pp_walk_generation(next: &AtomicU64) -> u64 {
1587 loop {
1588 let generation = next.fetch_add(1, Ordering::Relaxed);
1589 if generation != 0 {
1590 return generation;
1591 }
1592 }
1593}
1594
1595fn acquire_pp_walk(
1596 active: &Arc<AtomicU64>,
1597 next: &AtomicU64,
1598 runtime_id: usize,
1599 deferred_owner: Option<std::thread::ThreadId>,
1600 path: &str,
1601) -> Result<PpWalkLease, String> {
1602 let generation = next_pp_walk_generation(next);
1603 active
1604 .compare_exchange(0, generation, Ordering::AcqRel, Ordering::Acquire)
1605 .map_err(|_| {
1606 format!(
1607 "{path}: refused concurrent PP walk; shared boundary slots already have an owner"
1608 )
1609 })?;
1610 Ok(PpWalkLease {
1611 state: Arc::new(PpWalkState {
1612 active: active.clone(),
1613 generation,
1614 runtime_id,
1615 deferred_owner,
1616 }),
1617 })
1618}
1619
1620fn lock_deferred_walk<'a>(
1621 deferred: &'a Mutex<Weak<PpWalkState>>,
1622 path: &str,
1623) -> Result<std::sync::MutexGuard<'a, Weak<PpWalkState>>, String> {
1624 deferred
1625 .lock()
1626 .map_err(|_| format!("{path}: deferred PP walk owner lock is poisoned"))
1627}
1628
1629fn validate_walk_state(state: &PpWalkState, runtime_id: usize, path: &str) -> Result<(), String> {
1630 if state.runtime_id != runtime_id {
1631 return Err(format!(
1632 "{path}: PP walk permit belongs to a different runtime"
1633 ));
1634 }
1635 if !state.is_active() {
1636 return Err(format!(
1637 "{path}: PP walk permit generation is no longer active"
1638 ));
1639 }
1640 Ok(())
1641}
1642
1643pub type Pp2Rt = PpNRt;
1645
1646static RTN: OnceLock<Result<PpNRt, String>> = OnceLock::new();
1647
1648impl PpNRt {
1649 pub fn get(e: &Engine) -> Result<&'static PpNRt, Box<dyn std::error::Error>> {
1653 RTN.get_or_init(|| Self::build(e).map_err(|err| err.to_string()))
1654 .as_ref()
1655 .map_err(|s| -> Box<dyn std::error::Error> { s.clone().into() })
1656 }
1657
1658 fn build(e: &Engine) -> Result<PpNRt, Box<dyn std::error::Error>> {
1659 pp_wave_on().map_err(|reason| -> Box<dyn std::error::Error> { reason.into() })?;
1662 let primary_dev = e.ctx().ordinal();
1663 let devices: Vec<usize> =
1666 match pp2_devices_env() {
1667 Some(s) => {
1668 let parts: Result<Vec<usize>, _> =
1669 s.split(',').map(|p| p.trim().parse::<usize>()).collect();
1670 match parts {
1671 Ok(v) if v.len() >= 2 => v,
1672 _ => return Err(format!(
1673 "MEMRA_PP_DEVICES={s} unparseable (want <d0>,..,<dN-1> e.g. 0,1,2,3)"
1674 )
1675 .into()),
1676 }
1677 }
1678 None => {
1679 let n_st = std::env::var("MEMRA_PP_STAGES")
1680 .ok()
1681 .and_then(|v| v.parse::<usize>().ok())
1682 .filter(|&n| n >= 2)
1683 .unwrap_or(2);
1684 vec![primary_dev; n_st]
1685 }
1686 };
1687 if let Ok(v) = std::env::var("MEMRA_PP_STAGES")
1688 && let Ok(n) = v.parse::<usize>()
1689 && n >= 2
1690 && n != devices.len()
1691 {
1692 return Err(format!(
1693 "MEMRA_PP_DEVICES lists {} devices but MEMRA_PP_STAGES={n} — \
1694 refusing an ambiguous placement",
1695 devices.len()
1696 )
1697 .into());
1698 }
1699 let n_st = devices.len();
1700 let cross_any = devices.iter().any(|&d| d != devices[0]);
1701 let host_bounce = pp_host_bounce_on();
1702 let peer_probe = peer_probe_on();
1703 let sharded_cross_device = cross_any && !pp_shard_off();
1704 if host_bounce && cross_any {
1705 if pp_shard_off() {
1706 return Err(
1707 "MEMRA_PP_HOST_BOUNCE=1 refuses MEMRA_PP_SHARD=0: the boundary can bounce, \
1708 but remote stages would still peer-read primary-device weights"
1709 .into(),
1710 );
1711 }
1712 if devices.last().copied() != Some(primary_dev) {
1713 return Err(format!(
1714 "MEMRA_PP_HOST_BOUNCE=1 requires the primary engine on the last/head stage \
1715 (primary dev{primary_dev}, placement {devices:?}); otherwise returned \
1716 logits/hidden state remain peer reads"
1717 )
1718 .into());
1719 }
1720 }
1721 let peer_probe_policy =
1722 peer_probe_startup_policy(peer_probe, sharded_cross_device, host_bounce)?;
1723 if peer_probe_policy == PeerProbeStartupPolicy::BypassedWithHostBounce {
1724 PEER_PROBE_BYPASSED.fetch_add(1, Ordering::Relaxed);
1725 eprintln!(
1726 "[pp] SECURITY RED: peer_probe_bypassed: MEMRA_PEER_PROBE=0 on a sharded \
1727 cross-device placement; MEMRA_PP_HOST_BOUNCE=1 is the only enabled transport"
1728 );
1729 }
1730
1731 let mut used: Vec<usize> = devices.clone();
1735 used.push(primary_dev);
1736 used.sort_unstable();
1737 used.dedup();
1738 let mut peer_capable = Vec::new();
1739 if used.len() > 1 {
1740 let n = cudarc::driver::result::device::get_count()? as usize;
1741 for &d in &used {
1742 if d >= n {
1743 return Err(format!(
1744 "MEMRA_PP_DEVICES={devices:?} but only {n} CUDA device(s) present"
1745 )
1746 .into());
1747 }
1748 }
1749 if !host_bounce || peer_probe {
1750 for &a in &used {
1751 for &b in &used {
1752 if a == b {
1753 continue;
1754 }
1755 let da = cudarc::driver::result::device::get(a as i32)?;
1756 let db = cudarc::driver::result::device::get(b as i32)?;
1757 let mut can: i32 = 0;
1758 let capability = unsafe {
1759 cudarc::driver::sys::cuDeviceCanAccessPeer(&mut can, da, db).result()
1760 };
1761 if let Err(err) = capability {
1762 if host_bounce {
1763 eprintln!(
1764 "[pp] peer byte-integrity probe capability query failed for \
1765 dev{a}->dev{b}: {err}; MEMRA_PP_HOST_BOUNCE=1 remains active"
1766 );
1767 continue;
1768 }
1769 return Err(err.into());
1770 }
1771 if can == 0 {
1772 if !host_bounce {
1773 return Err(format!(
1774 "device {a} cannot peer-access device {b} \
1775 (cuDeviceCanAccessPeer=0); ppN cross-device needs P2P — \
1776 refusing a silently-staged path"
1777 )
1778 .into());
1779 }
1780 } else {
1781 peer_capable.push((a, b));
1782 }
1783 }
1784 }
1785 }
1786 }
1787
1788 let mk_stage = |dev: usize, s: usize| -> Result<StageRt, Box<dyn std::error::Error>> {
1801 if dev == primary_dev && s == 0 {
1802 let ctx = e.ctx().clone();
1803 let stream = ctx.new_stream()?;
1804 let blas = Arc::new(cudarc::cublaslt::CudaBlasLT::new(stream.clone())?);
1805 Ok(StageRt {
1806 dev,
1807 ctx,
1808 stream,
1809 blas,
1810 engine: None,
1811 })
1812 } else {
1813 let eng = Engine::new(dev)?;
1814 let ctx = eng.ctx().clone();
1815 let stream = ctx.new_stream()?;
1816 let blas = Arc::new(cudarc::cublaslt::CudaBlasLT::new(stream.clone())?);
1817 Ok(StageRt {
1818 dev,
1819 ctx,
1820 stream,
1821 blas,
1822 engine: Some(eng),
1823 })
1824 }
1825 };
1826 let mut stages = Vec::with_capacity(n_st);
1827 for (s, &d) in devices.iter().enumerate() {
1828 stages.push(mk_stage(d, s)?);
1829 }
1830
1831 if cross_any
1832 && !peer_probe
1833 && peer_probe_policy != PeerProbeStartupPolicy::BypassedWithHostBounce
1834 {
1835 eprintln!(
1836 "[pp] WARNING: MEMRA_PEER_PROBE=0 skips the boot-time peer byte-integrity \
1837 gate; diagnostics escape hatch active"
1838 );
1839 }
1840
1841 if used.len() > 1 {
1842 if !host_bounce {
1843 let ctx_of = |d: usize| -> &Arc<CudaContext> {
1846 if d == primary_dev {
1847 e.ctx()
1848 } else {
1849 &stages.iter().find(|s| s.dev == d).unwrap().ctx
1850 }
1851 };
1852 for &a in &used {
1855 for &b in &used {
1856 if a == b {
1857 continue;
1858 }
1859 ctx_of(a).bind_to_thread()?;
1860 let rc = unsafe {
1861 cudarc::driver::sys::cuCtxEnablePeerAccess(ctx_of(b).cu_ctx(), 0)
1862 };
1863 use cudarc::driver::sys::cudaError_enum as E;
1864 if rc != E::CUDA_SUCCESS && rc != E::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED
1865 {
1866 return Err(format!(
1867 "cuCtxEnablePeerAccess(dev{a} -> dev{b}) failed: {rc:?}"
1868 )
1869 .into());
1870 }
1871 }
1872 }
1873 if peer_probe && cross_any {
1877 let probe = run_peer_probe_pass(
1878 &stages,
1879 &peer_capable,
1880 host_bounce,
1881 "fixed-16KiB",
1882 PEER_PROBE_FIXED_BYTES,
1883 );
1884 e.ctx().bind_to_thread()?;
1885 probe?;
1886 }
1887 for &owner in &used {
1896 for &accessor in &used {
1897 if owner == accessor {
1898 continue;
1899 }
1900 let dev = cudarc::driver::result::device::get(owner as i32)?;
1901 let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
1902 unsafe {
1903 cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, dev)
1904 .result()?;
1905 }
1906 let desc = cudarc::driver::sys::CUmemAccessDesc {
1907 location: cudarc::driver::sys::CUmemLocation {
1908 type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
1909 id: accessor as i32,
1910 },
1911 flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
1912 };
1913 let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
1914 if rc != cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
1915 return Err(format!(
1916 "cuMemPoolSetAccess(dev{owner} pool -> dev{accessor}) failed: {rc:?}"
1917 )
1918 .into());
1919 }
1920 }
1921 }
1922 for (owner, accessor) in [
1931 (stages[0].dev, stages[1].dev),
1932 (stages[1].dev, stages[0].dev),
1933 ] {
1934 let dev = cudarc::driver::result::device::get(owner as i32)?;
1935 let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
1936 unsafe {
1937 cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, dev).result()?;
1938 }
1939 let desc = cudarc::driver::sys::CUmemAccessDesc {
1940 location: cudarc::driver::sys::CUmemLocation {
1941 type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
1942 id: accessor as i32,
1943 },
1944 flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
1945 };
1946 let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
1947 if rc != cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
1948 return Err(format!(
1949 "cuMemPoolSetAccess(dev{owner} pool -> dev{accessor}) failed: {rc:?}"
1950 )
1951 .into());
1952 }
1953 }
1954 e.ctx().bind_to_thread()?;
1956 eprintln!(
1957 "[pp] cross-device transport: {} (cudaMemcpyPeerAsync per cross boundary; \
1958 peer + default-pool access granted all pairs over {used:?}; weight home: {})",
1959 devices
1960 .iter()
1961 .enumerate()
1962 .map(|(s, d)| format!("stage{s}=dev{d}"))
1963 .collect::<Vec<_>>()
1964 .join(" "),
1965 if pp_shard_off() {
1966 format!("dev{primary_dev} (MEMRA_PP_SHARD=0 bring-up placement)")
1967 } else {
1968 "per-stage (sharded loader)".to_string()
1969 }
1970 );
1971 } else {
1972 e.ctx().bind_to_thread()?;
1973 eprintln!(
1974 "[pp] cross-device transport: {} (HOST-STAGED pinned D2H -> H2D per cross \
1975 boundary; MEMRA_PP_HOST_BOUNCE=1; peer-pool grants bypassed; \
1976 diagnostic peer access is removed before host-staged serving; \
1977 weight home: per-stage (sharded loader))",
1978 devices
1979 .iter()
1980 .enumerate()
1981 .map(|(s, d)| format!("stage{s}=dev{d}"))
1982 .collect::<Vec<_>>()
1983 .join(" "),
1984 );
1985 }
1986 }
1987
1988 let mk_slot =
1989 |tx: &StageRt, rx: &StageRt| -> Result<BoundarySlot, Box<dyn std::error::Error>> {
1990 Ok(BoundarySlot {
1991 buf: Mutex::new(None),
1992 ev_tx: tx.ctx.new_event(None)?,
1993 ev_rx: rx.ctx.new_event(None)?,
1994 })
1995 };
1996 let mut boundaries = Vec::with_capacity(n_st - 1);
1997 for b in 0..n_st - 1 {
1998 let (tx, rx) = (&stages[b], &stages[b + 1]);
1999 boundaries.push(BoundaryRt {
2000 slots: [mk_slot(tx, rx)?, mk_slot(tx, rx)?],
2001 step: AtomicUsize::new(0),
2002 cross: tx.dev != rx.dev,
2003 });
2004 }
2005 let readback = stages[n_st - 1].ctx.new_stream()?;
2006 let rt = PpNRt {
2007 stages,
2008 boundaries,
2009 walk_active: Arc::new(AtomicU64::new(0)),
2010 walk_next: AtomicU64::new(1),
2011 deferred_walk: Mutex::new(Weak::new()),
2012 cross_any,
2013 host_bounce,
2014 peer_probe,
2015 peer_capable,
2016 peer_probe_geometry: OnceLock::new(),
2017 bounce: OnceLock::new(),
2018 readback,
2019 };
2020 if rt.peer_probe && rt.cross_any && rt.host_bounce {
2021 rt.run_host_bounce_legacy_probe(e)?;
2022 }
2023 Ok(rt)
2024 }
2025
2026 pub fn n_stages(&self) -> usize {
2027 self.stages.len()
2028 }
2029
2030 pub fn acquire_walk(
2033 &'static self,
2034 path: &str,
2035 ) -> Result<PpWalkLease, Box<dyn std::error::Error>> {
2036 let runtime_id = self as *const Self as usize;
2037 acquire_pp_walk(&self.walk_active, &self.walk_next, runtime_id, None, path)
2038 .map_err(|error| -> Box<dyn std::error::Error> { error.into() })
2039 }
2040
2041 pub(crate) fn acquire_deferred_walk(
2044 &'static self,
2045 path: &str,
2046 ) -> Result<PpWalkLease, Box<dyn std::error::Error>> {
2047 let runtime_id = self as *const Self as usize;
2048 let current_thread = std::thread::current().id();
2049 let mut weak = lock_deferred_walk(&self.deferred_walk, path)
2050 .map_err(|error| -> Box<dyn std::error::Error> { error.into() })?;
2051 if let Some(state) = weak.upgrade() {
2052 validate_walk_state(&state, runtime_id, path)
2053 .map_err(|error| -> Box<dyn std::error::Error> { error.into() })?;
2054 if state.deferred_owner.as_ref() != Some(¤t_thread) {
2055 return Err(format!(
2056 "{path}: refused cross-thread join of the active deferred PP window"
2057 )
2058 .into());
2059 }
2060 return Ok(PpWalkLease { state });
2061 }
2062 let lease = acquire_pp_walk(
2063 &self.walk_active,
2064 &self.walk_next,
2065 runtime_id,
2066 Some(current_thread),
2067 path,
2068 )
2069 .map_err(|error| -> Box<dyn std::error::Error> { error.into() })?;
2070 *weak = Arc::downgrade(&lease.state);
2071 Ok(lease)
2072 }
2073
2074 pub fn cross_device(&self) -> bool {
2076 self.cross_any
2077 }
2078
2079 pub fn host_bounce_active(&self) -> bool {
2080 self.host_bounce || PEER_RUNTIME_HOST_BOUNCE.load(Ordering::Acquire)
2081 }
2082
2083 pub fn repeated_stage_device(&self) -> bool {
2086 let mut devices: Vec<_> = self.stages.iter().map(|stage| stage.dev).collect();
2087 devices.sort_unstable();
2088 devices.dedup();
2089 devices.len() != self.stages.len()
2090 }
2091
2092 fn context_for_dev<'a>(
2093 &'a self,
2094 e: &'a Engine,
2095 dev: usize,
2096 ) -> Result<&'a Arc<CudaContext>, Box<dyn std::error::Error>> {
2097 if dev == e.ctx().ordinal() {
2098 return Ok(e.ctx());
2099 }
2100 self.stages
2101 .iter()
2102 .find(|stage| stage.dev == dev)
2103 .map(|stage| &stage.ctx)
2104 .ok_or_else(|| format!("PP peer probe has no CUDA context for dev{dev}").into())
2105 }
2106
2107 fn enable_probe_peer_access(
2108 &self,
2109 e: &Engine,
2110 pairs: &[(usize, usize)],
2111 ) -> Result<Vec<(usize, usize)>, Box<dyn std::error::Error>> {
2112 let mut enabled = Vec::new();
2113 for &(src_dev, dst_dev) in pairs {
2114 let enable = (|| -> Result<(), Box<dyn std::error::Error>> {
2115 let src_ctx = self.context_for_dev(e, src_dev)?;
2116 let dst_ctx = self.context_for_dev(e, dst_dev)?;
2117 src_ctx.bind_to_thread()?;
2118 let rc = unsafe { cudarc::driver::sys::cuCtxEnablePeerAccess(dst_ctx.cu_ctx(), 0) };
2119 use cudarc::driver::sys::cudaError_enum as E;
2120 if rc == E::CUDA_SUCCESS || rc == E::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED {
2121 Ok(())
2122 } else {
2123 Err(format!("{rc:?}").into())
2124 }
2125 })();
2126 if let Err(err) = enable {
2127 eprintln!(
2128 "[pp] peer byte-integrity probe could not enable \
2129 dev{src_dev}->dev{dst_dev}: {err}; MEMRA_PP_HOST_BOUNCE=1 remains active"
2130 );
2131 } else {
2132 enabled.push((src_dev, dst_dev));
2133 }
2134 }
2135 Ok(enabled)
2136 }
2137
2138 fn disable_probe_peer_access(
2139 &self,
2140 e: &Engine,
2141 pairs: &[(usize, usize)],
2142 ) -> Result<(), Box<dyn std::error::Error>> {
2143 let mut failures = Vec::new();
2144 for &(src_dev, dst_dev) in pairs {
2145 let disable = (|| -> Result<(), Box<dyn std::error::Error>> {
2146 let src_ctx = self.context_for_dev(e, src_dev)?;
2147 let dst_ctx = self.context_for_dev(e, dst_dev)?;
2148 src_ctx.bind_to_thread()?;
2149 let rc = unsafe { cudarc::driver::sys::cuCtxDisablePeerAccess(dst_ctx.cu_ctx()) };
2150 use cudarc::driver::sys::cudaError_enum as E;
2151 if rc == E::CUDA_SUCCESS || rc == E::CUDA_ERROR_PEER_ACCESS_NOT_ENABLED {
2152 Ok(())
2153 } else {
2154 Err(format!("{rc:?}").into())
2155 }
2156 })();
2157 if let Err(err) = disable {
2158 failures.push(format!("dev{src_dev}->dev{dst_dev}: {err}"));
2159 }
2160 }
2161 e.ctx().bind_to_thread()?;
2162 if failures.is_empty() {
2163 eprintln!(
2164 "[pp] peer byte-integrity probe teardown: disabled {} diagnostic pair(s); \
2165 host-bounce serving has no probe-enabled peer access",
2166 pairs.len(),
2167 );
2168 Ok(())
2169 } else {
2170 Err(format!(
2171 "PP peer probe could not disable diagnostic peer access ({}); \
2172 refusing host-bounce serving",
2173 failures.join(", "),
2174 )
2175 .into())
2176 }
2177 }
2178
2179 fn grant_probe_pool_access(
2180 &self,
2181 e: &Engine,
2182 pairs: &[(usize, usize)],
2183 ) -> Result<Vec<(usize, usize)>, Box<dyn std::error::Error>> {
2184 let mut granted = Vec::new();
2185 for &(src_dev, dst_dev) in pairs {
2186 let grant = (|| -> Result<(), Box<dyn std::error::Error>> {
2187 self.context_for_dev(e, dst_dev)?.bind_to_thread()?;
2188 let dev = cudarc::driver::result::device::get(dst_dev as i32)?;
2189 let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
2190 unsafe {
2191 cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, dev).result()?;
2192 }
2193 let desc = cudarc::driver::sys::CUmemAccessDesc {
2194 location: cudarc::driver::sys::CUmemLocation {
2195 type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
2196 id: src_dev as i32,
2197 },
2198 flags:
2199 cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
2200 };
2201 let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
2202 if rc == cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
2203 Ok(())
2204 } else {
2205 Err(format!("{rc:?}").into())
2206 }
2207 })();
2208 if let Err(err) = grant {
2209 eprintln!(
2210 "[pp] production-slot probe could not grant dev{src_dev} access to \
2211 dev{dst_dev}'s default pool: {err}; MEMRA_PP_HOST_BOUNCE=1 remains active"
2212 );
2213 } else {
2214 granted.push((src_dev, dst_dev));
2215 }
2216 }
2217 Ok(granted)
2218 }
2219
2220 fn revoke_probe_pool_access(
2221 &self,
2222 e: &Engine,
2223 pairs: &[(usize, usize)],
2224 ) -> Result<(), Box<dyn std::error::Error>> {
2225 let mut failures = Vec::new();
2226 for &(src_dev, dst_dev) in pairs {
2227 let revoke = (|| -> Result<(), Box<dyn std::error::Error>> {
2228 self.context_for_dev(e, dst_dev)?.bind_to_thread()?;
2229 let dev = cudarc::driver::result::device::get(dst_dev as i32)?;
2230 let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
2231 unsafe {
2232 cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, dev).result()?;
2233 }
2234 let desc = cudarc::driver::sys::CUmemAccessDesc {
2235 location: cudarc::driver::sys::CUmemLocation {
2236 type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
2237 id: src_dev as i32,
2238 },
2239 flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_NONE,
2240 };
2241 let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
2242 if rc == cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
2243 Ok(())
2244 } else {
2245 Err(format!("{rc:?}").into())
2246 }
2247 })();
2248 if let Err(err) = revoke {
2249 failures.push(format!("dev{src_dev}->dev{dst_dev}: {err}"));
2250 }
2251 }
2252 e.ctx().bind_to_thread()?;
2253 if failures.is_empty() {
2254 Ok(())
2255 } else {
2256 Err(format!(
2257 "PP peer probe could not revoke diagnostic pool access ({}); \
2258 refusing host-bounce serving",
2259 failures.join(", "),
2260 )
2261 .into())
2262 }
2263 }
2264
2265 fn run_host_bounce_legacy_probe(&self, e: &Engine) -> Result<(), Box<dyn std::error::Error>> {
2266 let enabled = self.enable_probe_peer_access(e, &self.peer_capable)?;
2267 let probe = run_peer_probe_pass(
2268 &self.stages,
2269 &enabled,
2270 true,
2271 "fixed-16KiB-legacy-preflight",
2272 PEER_PROBE_FIXED_BYTES,
2273 );
2274 let disable = self.disable_probe_peer_access(e, &enabled);
2275 disable?;
2276 probe
2277 }
2278
2279 fn new_peer_probe_boundary(
2280 &self,
2281 src_stage: usize,
2282 dst_stage: usize,
2283 ) -> Result<BoundaryRt, Box<dyn std::error::Error>> {
2284 let tx = &self.stages[src_stage];
2285 let rx = &self.stages[dst_stage];
2286 let mk_slot = || -> Result<BoundarySlot, Box<dyn std::error::Error>> {
2287 Ok(BoundarySlot {
2288 buf: Mutex::new(None),
2289 ev_tx: tx.ctx.new_event(None)?,
2290 ev_rx: rx.ctx.new_event(None)?,
2291 })
2292 };
2293 Ok(BoundaryRt {
2294 slots: [mk_slot()?, mk_slot()?],
2295 step: AtomicUsize::new(0),
2296 cross: tx.dev != rx.dev,
2297 })
2298 }
2299
2300 fn production_probe_readback(
2301 &self,
2302 path: BoundaryPath,
2303 boundary: &BoundaryRt,
2304 expected: &[u8],
2305 n: usize,
2306 slot_idx: usize,
2307 ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
2308 debug_assert_eq!(expected.len(), n * std::mem::size_of::<f32>());
2309 let host = peer_probe_bytes_to_f32(expected);
2310 let poison_bytes: Vec<u8> = expected.iter().map(|byte| !byte).collect();
2311 let poison = peer_probe_bytes_to_f32(&poison_bytes);
2312 let src = &self.stages[path.src_stage];
2313 let dst = &self.stages[path.dst_stage];
2314
2315 dst.ctx.bind_to_thread()?;
2318 let poison_buf = dst.stream.clone_htod(&poison)?;
2319 dst.stream.synchronize()?;
2320 let replaced = boundary.slots[slot_idx]
2321 .buf
2322 .lock()
2323 .unwrap()
2324 .replace(poison_buf);
2325 drop(replaced);
2326 dst.stream.synchronize()?;
2327
2328 src.ctx.bind_to_thread()?;
2329 let x = src.stream.clone_htod(&host)?;
2330 self.tx_slot_path(path, boundary, &x, n, slot_idx)?;
2331
2332 dst.ctx.bind_to_thread()?;
2333 let work = self.rx_slot_path(path, boundary, slot_idx, n)?;
2334 let back = dst.stream.clone_dtoh(&work)?;
2335 dst.stream.synchronize()?;
2336 Ok(peer_probe_f32_to_bytes(&back))
2337 }
2338
2339 fn clear_peer_probe_boundary(
2340 &self,
2341 boundary: &BoundaryRt,
2342 src_stage: usize,
2343 dst_stage: usize,
2344 ) -> Result<(), Box<dyn std::error::Error>> {
2345 self.stages[dst_stage].ctx.bind_to_thread()?;
2346 for slot in &boundary.slots {
2347 let buffer = slot.buf.lock().unwrap().take();
2348 drop(buffer);
2349 }
2350 self.stages[src_stage].stream.synchronize()?;
2351 self.stages[dst_stage].stream.synchronize()?;
2352 Ok(())
2353 }
2354
2355 fn run_production_peer_probe_widths(
2356 &self,
2357 enabled_pairs: &[(usize, usize)],
2358 host_bounce: bool,
2359 n_embd: usize,
2360 widths: &[usize],
2361 ) -> Result<(), Box<dyn std::error::Error>> {
2362 let started = std::time::Instant::now();
2363 let mut copies = 0usize;
2364 let mut skipped = 0usize;
2365 let mut total_mismatches = 0usize;
2366 let mut largest_clean_payload = 0usize;
2367
2368 for boundary_idx in 0..self.stages.len() - 1 {
2369 if self.stages[boundary_idx].dev == self.stages[boundary_idx + 1].dev {
2370 continue;
2371 }
2372 for (src_stage, dst_stage) in [
2373 (boundary_idx, boundary_idx + 1),
2374 (boundary_idx + 1, boundary_idx),
2375 ] {
2376 let src_dev = self.stages[src_stage].dev;
2377 let dst_dev = self.stages[dst_stage].dev;
2378 if !enabled_pairs.contains(&(src_dev, dst_dev)) {
2379 if host_bounce {
2380 skipped += widths.len();
2381 eprintln!(
2382 "[pp] production-slot peer probe SKIP: boundary={boundary_idx} \
2383 dev{src_dev}->dev{dst_dev} widths_tokens={:?} \
2384 (peer or pool access unavailable; MEMRA_PP_HOST_BOUNCE=1 remains \
2385 fail-safe)",
2386 widths,
2387 );
2388 continue;
2389 }
2390 return Err(format!(
2391 "PP production-slot peer probe cannot run boundary={boundary_idx} \
2392 dev{src_dev}->dev{dst_dev}: peer/pool access is not enabled"
2393 )
2394 .into());
2395 }
2396
2397 let probe_boundary = self.new_peer_probe_boundary(src_stage, dst_stage)?;
2398 let path = BoundaryPath {
2399 boundary: boundary_idx,
2400 src_stage,
2401 dst_stage,
2402 transport: BoundaryTransport::Peer,
2403 };
2404 let mut direction_copies = 0usize;
2405 let mut direction_skipped = 0usize;
2406 let mut direction_mismatches = 0usize;
2407 let mut direction_largest_clean = 0usize;
2408 let mut failure = None;
2409
2410 for (width_idx, tokens) in widths.iter().copied().enumerate() {
2411 let n = n_embd.checked_mul(tokens).ok_or_else(|| {
2412 format!(
2413 "PP production-slot probe element count overflows for \
2414 n_embd={n_embd} tokens={tokens}"
2415 )
2416 })?;
2417 let bytes = n.checked_mul(std::mem::size_of::<f32>()).ok_or_else(|| {
2418 format!(
2419 "PP production-slot probe byte count overflows for \
2420 n_embd={n_embd} tokens={tokens}"
2421 )
2422 })?;
2423 let expected = peer_probe_pattern(bytes, boundary_idx, src_dev, dst_dev);
2424 let readback = match self.production_probe_readback(
2425 path,
2426 &probe_boundary,
2427 &expected,
2428 n,
2429 width_idx % 2,
2430 ) {
2431 Ok(readback) => readback,
2432 Err(err) if host_bounce => {
2433 skipped += 1;
2434 direction_skipped += 1;
2435 eprintln!(
2436 "[pp] production-slot peer probe ERROR: \
2437 boundary={boundary_idx} dev{src_dev}->dev{dst_dev} \
2438 tokens={tokens} bytes={bytes}: {err}; \
2439 MEMRA_PP_HOST_BOUNCE=1, proceeding on the host-staged path"
2440 );
2441 continue;
2442 }
2443 Err(err) => {
2444 failure = Some(format!(
2445 "PP production-slot peer probe FAILED: \
2446 boundary={boundary_idx} dev{src_dev}->dev{dst_dev} \
2447 tokens={tokens} bytes={bytes}: {err}; refusing native P2P \
2448 (set MEMRA_PP_HOST_BOUNCE=1 to use the host-staged path; \
2449 MEMRA_PEER_PROBE=0 cannot authorize sharded native peer \
2450 transport)"
2451 ));
2452 break;
2453 }
2454 };
2455 copies += 1;
2456 direction_copies += 1;
2457 let mismatches = peer_probe_mismatch_count(&expected, &readback);
2458 if mismatches == 0 {
2459 largest_clean_payload = largest_clean_payload.max(bytes);
2460 direction_largest_clean = direction_largest_clean.max(bytes);
2461 } else if host_bounce {
2462 total_mismatches += mismatches;
2463 direction_mismatches += mismatches;
2464 eprintln!(
2465 "[pp] production-slot peer probe CORRUPTION: \
2466 boundary={boundary_idx} dev{src_dev}->dev{dst_dev} tokens={tokens} \
2467 bytes={bytes} mismatches={mismatches}; MEMRA_PP_HOST_BOUNCE=1, \
2468 proceeding on the host-staged path"
2469 );
2470 } else {
2471 failure = Some(format!(
2472 "PP production-slot peer probe FAILED: boundary={boundary_idx} \
2473 dev{src_dev}->dev{dst_dev} tokens={tokens} bytes={bytes}: \
2474 {mismatches} mismatched byte(s); refusing native P2P \
2475 (set MEMRA_PP_HOST_BOUNCE=1 to use the host-staged path; \
2476 MEMRA_PEER_PROBE=0 cannot authorize sharded native peer transport)"
2477 ));
2478 break;
2479 }
2480 }
2481
2482 self.clear_peer_probe_boundary(&probe_boundary, src_stage, dst_stage)?;
2483 if let Some(err) = failure {
2484 return Err(err.into());
2485 }
2486 eprintln!(
2487 "[pp] production-slot peer probe direction: boundary={boundary_idx} \
2488 dev{src_dev}->dev{dst_dev} copies={direction_copies} \
2489 skipped={direction_skipped} mismatches={direction_mismatches} \
2490 largest_clean_payload_bytes={direction_largest_clean}"
2491 );
2492 }
2493 }
2494
2495 let status = if total_mismatches > 0 {
2496 "BOUNCE"
2497 } else if skipped > 0 && copies > 0 {
2498 "PARTIAL"
2499 } else if skipped > 0 {
2500 "SKIP"
2501 } else {
2502 "PASS"
2503 };
2504 eprintln!(
2505 "[pp] production-slot peer probe {status}: widths_tokens={:?} copies={copies} \
2506 skipped={skipped} mismatches={total_mismatches} \
2507 largest_clean_payload_bytes={largest_clean_payload} elapsed_ms={:.3}",
2508 widths,
2509 started.elapsed().as_secs_f64() * 1e3,
2510 );
2511 Ok(())
2512 }
2513
2514 fn run_production_peer_probe(
2515 &self,
2516 enabled_pairs: &[(usize, usize)],
2517 host_bounce: bool,
2518 n_embd: usize,
2519 ) -> Result<(), Box<dyn std::error::Error>> {
2520 self.run_production_peer_probe_widths(
2521 enabled_pairs,
2522 host_bounce,
2523 n_embd,
2524 &PEER_PROBE_TOKEN_WIDTHS,
2525 )
2526 }
2527
2528 fn run_host_bounce_production_probe(
2529 &self,
2530 e: &Engine,
2531 n_embd: usize,
2532 ) -> Result<(), Box<dyn std::error::Error>> {
2533 let enabled = self.enable_probe_peer_access(e, &self.peer_capable)?;
2534 let granted = self.grant_probe_pool_access(e, &enabled)?;
2535 let probe = self.run_production_peer_probe(&granted, true, n_embd);
2536 let revoke = self.revoke_probe_pool_access(e, &granted);
2542 let disable = self.disable_probe_peer_access(e, &enabled);
2543 probe?;
2544 revoke?;
2545 disable?;
2546 Ok(())
2547 }
2548
2549 fn init_peer_probe_geometry(
2550 &self,
2551 e: &Engine,
2552 n_embd: usize,
2553 ) -> Result<(), Box<dyn std::error::Error>> {
2554 if !self.peer_probe || !self.cross_any {
2555 return Ok(());
2556 }
2557 let bytes = n_embd
2558 .checked_mul(std::mem::size_of::<f32>())
2559 .ok_or_else(|| format!("PP boundary-slot byte count overflows for n_embd={n_embd}"))?;
2560 let result = self.peer_probe_geometry.get_or_init(|| {
2561 let probe = if self.host_bounce_active() {
2562 self.run_host_bounce_production_probe(e, n_embd)
2563 } else {
2564 self.run_production_peer_probe(&self.peer_capable, false, n_embd)
2565 };
2566 let restore = e.ctx().bind_to_thread();
2567 match (probe, restore) {
2568 (Ok(()), Ok(())) => Ok(bytes),
2569 (Err(err), _) => Err(err.to_string()),
2570 (_, Err(err)) => Err(err.to_string()),
2571 }
2572 });
2573 let probed = result
2574 .as_ref()
2575 .map_err(|err| -> Box<dyn std::error::Error> { err.clone().into() })?;
2576 if *probed != bytes {
2577 return Err(format!(
2578 "peer probe initialized for boundary-slot bytes={probed} but model requests \
2579 bytes={bytes}; one PP runtime supports one model geometry per process"
2580 )
2581 .into());
2582 }
2583 Ok(())
2584 }
2585
2586 fn init_host_bounce_staging(
2587 &self,
2588 e: &Engine,
2589 n_embd: usize,
2590 ) -> Result<(), Box<dyn std::error::Error>> {
2591 if !self.cross_any {
2592 return Ok(());
2593 }
2594 e.ctx().bind_to_thread()?;
2595 let result = self.bounce.get_or_init(|| {
2596 HostBounceRt::new(n_embd, &self.boundaries)
2597 .inspect(|rt| {
2598 let bytes = rt.capacity * std::mem::size_of::<f32>();
2599 eprintln!(
2600 "[pp] host-bounce staging ready: n_embd={n_embd} max_tokens={} \
2601 slot_bytes={bytes} slots_per_cross_boundary=2",
2602 crate::cache::PRIME_CHUNK_MAX_TOKENS,
2603 );
2604 })
2605 .map_err(|err| err.to_string())
2606 });
2607 let bounce = result
2608 .as_ref()
2609 .map_err(|err| -> Box<dyn std::error::Error> { err.clone().into() })?;
2610 if bounce.n_embd != n_embd {
2611 return Err(format!(
2612 "host-bounce runtime initialized for n_embd={} but model requests n_embd={n_embd}; \
2613 one PP runtime supports one model geometry per process",
2614 bounce.n_embd,
2615 )
2616 .into());
2617 }
2618 Ok(())
2619 }
2620
2621 fn validate_host_bounce_staging(
2625 &self,
2626 e: &Engine,
2627 n_embd: usize,
2628 ) -> Result<(), Box<dyn std::error::Error>> {
2629 let bytes = n_embd
2630 .checked_mul(std::mem::size_of::<f32>())
2631 .ok_or_else(|| {
2632 format!("host-bounce validation byte count overflows for n_embd={n_embd}")
2633 })?;
2634 for boundary_idx in 0..self.stages.len() - 1 {
2635 if !self.boundaries[boundary_idx].cross {
2636 continue;
2637 }
2638 let src_stage = boundary_idx;
2639 let dst_stage = boundary_idx + 1;
2640 let probe_boundary = self.new_peer_probe_boundary(src_stage, dst_stage)?;
2641 let path = BoundaryPath {
2642 boundary: boundary_idx,
2643 src_stage,
2644 dst_stage,
2645 transport: BoundaryTransport::HostBounce,
2646 };
2647 let expected = peer_probe_pattern(
2648 bytes,
2649 boundary_idx,
2650 self.stages[src_stage].dev,
2651 self.stages[dst_stage].dev,
2652 );
2653 let readback =
2654 self.production_probe_readback(path, &probe_boundary, &expected, n_embd, 0);
2655 let clear = self.clear_peer_probe_boundary(&probe_boundary, src_stage, dst_stage);
2656 let readback = readback?;
2657 clear?;
2658 let mismatches = peer_probe_mismatch_count(&expected, &readback);
2659 if mismatches > 0 {
2660 return Err(format!(
2661 "runtime host-bounce staging validation FAILED: boundary={boundary_idx} \
2662 bytes={bytes} mismatches={mismatches}"
2663 )
2664 .into());
2665 }
2666 }
2667 e.ctx().bind_to_thread()?;
2668 eprintln!(
2669 "[pp] runtime host-bounce staging validation PASS: row_bytes={bytes} \
2670 cross_boundaries={}",
2671 self.boundaries
2672 .iter()
2673 .filter(|boundary| boundary.cross)
2674 .count(),
2675 );
2676 Ok(())
2677 }
2678
2679 fn arm_runtime_host_bounce(
2680 &self,
2681 e: &Engine,
2682 row_bytes: usize,
2683 ) -> Result<(), Box<dyn std::error::Error>> {
2684 if row_bytes == 0 || !row_bytes.is_multiple_of(std::mem::size_of::<f32>()) {
2685 return Err(format!(
2686 "runtime host-bounce cannot recover n_embd from row_bytes={row_bytes}"
2687 )
2688 .into());
2689 }
2690 let n_embd = row_bytes / std::mem::size_of::<f32>();
2691 self.init_host_bounce_staging(e, n_embd)?;
2692 self.validate_host_bounce_staging(e, n_embd)
2693 }
2694
2695 pub fn init_boundary_transport(
2701 &self,
2702 e: &Engine,
2703 n_embd: usize,
2704 ) -> Result<(), Box<dyn std::error::Error>> {
2705 if PEER_RUNTIME_PROBE_FAILED.load(Ordering::Acquire)
2706 && !PEER_RUNTIME_HOST_BOUNCE.load(Ordering::Acquire)
2707 {
2708 return Err(
2709 "PP runtime peer byte-integrity probe previously failed; refusing native P2P \
2710 reuse because runtime host-bounce staging could not be armed"
2711 .into(),
2712 );
2713 }
2714 self.init_peer_probe_geometry(e, n_embd)?;
2715 if !self.host_bounce_active() || !self.cross_any {
2716 return Ok(());
2717 }
2718 self.init_host_bounce_staging(e, n_embd)
2719 }
2720
2721 fn service_runtime_peer_probe(
2726 &self,
2727 e: &Engine,
2728 scheduler_idle: bool,
2729 probe_allowed: bool,
2730 ) -> Result<RuntimePeerProbeStatus, Box<dyn std::error::Error>> {
2731 if !self.peer_probe || !self.cross_any || self.host_bounce_active() {
2732 return Ok(RuntimePeerProbeStatus::NotRun);
2733 }
2734 if PEER_RUNTIME_PROBE_FAILED.load(Ordering::Acquire) {
2735 return Err(
2736 "PP runtime peer byte-integrity probe previously failed; native P2P is latched off"
2737 .into(),
2738 );
2739 }
2740 let row_bytes = match self.peer_probe_geometry.get() {
2741 Some(Ok(bytes)) => *bytes,
2742 _ => return Ok(RuntimePeerProbeStatus::NotRun),
2743 };
2744
2745 let copies = PEER_BOUNDARY_COPIES.load(Ordering::Relaxed);
2746 let (width_index, tokens) = loop {
2747 let next_probe_copy = std::array::from_fn(|width_index| {
2748 PEER_RUNTIME_NEXT_PROBE_COPY[width_index].load(Ordering::Relaxed)
2749 });
2750 let measured_cost_ns = std::array::from_fn(|width_index| {
2751 PEER_RUNTIME_PROBE_MAX_COST_NS[width_index].load(Ordering::Relaxed)
2752 });
2753 let Some(candidate) = runtime_peer_probe_candidate(
2754 copies,
2755 next_probe_copy,
2756 measured_cost_ns,
2757 scheduler_idle,
2758 ) else {
2759 return Ok(RuntimePeerProbeStatus::NotRun);
2760 };
2761 if !probe_allowed {
2766 return Ok(RuntimePeerProbeStatus::Deferred);
2767 }
2768 let due = next_probe_copy[candidate.0];
2769 let next = runtime_peer_probe_next_copy(due, copies);
2770 if PEER_RUNTIME_NEXT_PROBE_COPY[candidate.0]
2771 .compare_exchange(due, next, Ordering::AcqRel, Ordering::Relaxed)
2772 .is_ok()
2773 {
2774 break candidate;
2775 }
2776 };
2777 let probe_index = PEER_RUNTIME_PROBES.fetch_add(1, Ordering::Relaxed);
2778 let probe_bytes = row_bytes.checked_mul(tokens);
2779 let started = std::time::Instant::now();
2780 let probe = match probe_bytes {
2781 Some(_) => self.run_production_peer_probe_widths(
2782 &self.peer_capable,
2783 false,
2784 row_bytes / std::mem::size_of::<f32>(),
2785 &[tokens],
2786 ),
2787 None => Err(format!(
2788 "PP runtime peer probe byte count overflows for row_bytes={row_bytes} \
2789 tokens={tokens}"
2790 )
2791 .into()),
2792 };
2793 let restore = e.ctx().bind_to_thread();
2794 let elapsed_ns = started.elapsed().as_nanos().min(u64::MAX as u128) as u64;
2795 let previous_max =
2796 PEER_RUNTIME_PROBE_MAX_COST_NS[width_index].fetch_max(elapsed_ns, Ordering::Relaxed);
2797 let verdict = match (probe, restore) {
2798 (Ok(()), Ok(())) => Ok(()),
2799 (Err(err), _) => Err(err.to_string()),
2800 (_, Err(err)) => Err(err.to_string()),
2801 };
2802 if let Err(err) = verdict {
2803 PEER_RUNTIME_PROBE_FAILURES.fetch_add(1, Ordering::Relaxed);
2804 let arm = latch_runtime_host_bounce(
2805 &PEER_RUNTIME_PROBE_FAILED,
2806 &PEER_RUNTIME_HOST_BOUNCE,
2807 || {
2808 self.arm_runtime_host_bounce(e, row_bytes)
2809 .map_err(|arm_err| arm_err.to_string())
2810 },
2811 );
2812 if let Err(arm_err) = arm {
2813 let message = format!(
2814 "PP runtime peer byte-integrity re-probe FAILED after \
2815 boundary_copies={copies} rung={}/{} tokens={tokens}: {err}; native P2P is \
2816 latched off and host-bounce staging could not be armed: {arm_err}",
2817 width_index + 1,
2818 PEER_PROBE_TOKEN_WIDTHS.len(),
2819 );
2820 eprintln!("[pp] SECURITY RED: {message}; worker must stop");
2821 return Err(message.into());
2822 }
2823 eprintln!(
2824 "[pp] SECURITY RED: PP runtime peer byte-integrity re-probe FAILED after \
2825 boundary_copies={copies} rung={}/{} tokens={tokens}: {err}; native P2P is \
2826 latched off and the live transport DEGRADED to validated host bounce for the \
2827 remainder of this process",
2828 width_index + 1,
2829 PEER_PROBE_TOKEN_WIDTHS.len(),
2830 );
2831 return Ok(RuntimePeerProbeStatus::DegradedToHostBounce);
2832 }
2833 if width_index + 1 != PEER_PROBE_TOKEN_WIDTHS.len()
2834 && previous_max <= PEER_RUNTIME_PROBE_BUDGET_NS
2835 && elapsed_ns > PEER_RUNTIME_PROBE_BUDGET_NS
2836 {
2837 eprintln!(
2838 "[pp] runtime peer re-probe rung exceeded the {:.3}ms owner-thread budget: \
2839 tokens={tokens} measured_ms={:.3}; future runs are idle-only",
2840 PEER_RUNTIME_PROBE_BUDGET_NS as f64 / 1e6,
2841 elapsed_ns as f64 / 1e6,
2842 );
2843 }
2844 eprintln!(
2845 "[pp] runtime peer byte-integrity re-probe PASS: \
2846 boundary_copies={copies} interval_copies={PEER_RUNTIME_PROBE_INTERVAL_COPIES} \
2847 rung={}/{} tokens={tokens} bytes={} probe_index={probe_index} elapsed_ms={:.3} \
2848 scheduler_idle={scheduler_idle}",
2849 width_index + 1,
2850 PEER_PROBE_TOKEN_WIDTHS.len(),
2851 probe_bytes.unwrap(),
2852 elapsed_ns as f64 / 1e6,
2853 );
2854 Ok(RuntimePeerProbeStatus::Passed)
2855 }
2856
2857 fn bounce_rt(&self) -> Result<&HostBounceRt, Box<dyn std::error::Error>> {
2858 self.bounce
2859 .get()
2860 .ok_or_else(|| -> Box<dyn std::error::Error> {
2861 "MEMRA_PP_HOST_BOUNCE=1 staging was not initialized from model geometry".into()
2862 })?
2863 .as_ref()
2864 .map_err(|err| -> Box<dyn std::error::Error> { err.clone().into() })
2865 }
2866
2867 pub fn engine<'a>(&'a self, s: usize, primary: &'a Engine) -> &'a Engine {
2870 self.stages[s].engine.as_ref().unwrap_or(primary)
2871 }
2872
2873 pub fn order_engine_behind(
2910 src: &Engine,
2911 dst: &Engine,
2912 ) -> Result<(), Box<dyn std::error::Error>> {
2913 if std::ptr::eq(src, dst) {
2914 return Ok(());
2915 }
2916 let s = src.stream();
2917 let d = dst.gpu.main_stream();
2918 if Arc::ptr_eq(&s, d) {
2919 return Ok(());
2920 }
2921 if src.ctx() == dst.ctx() {
2922 let ev = s.context().new_event(None)?;
2923 ev.record(&s)?;
2924 d.wait(&ev)?;
2925 } else {
2926 s.synchronize()?;
2927 }
2928 Ok(())
2929 }
2930
2931 pub fn bind_stage(&self, s: usize) -> Result<(), Box<dyn std::error::Error>> {
2933 self.stages[s].ctx.bind_to_thread()?;
2934 Ok(())
2935 }
2936
2937 pub fn enter(&self, s: usize) -> memra_runtime::StreamOverride {
2940 memra_runtime::push_stream_override(
2941 self.stages[s].stream.clone(),
2942 self.stages[s].blas.clone(),
2943 )
2944 }
2945
2946 pub fn prepare_overlap_slots(
2952 &self,
2953 b: usize,
2954 n: usize,
2955 ) -> Result<(), Box<dyn std::error::Error>> {
2956 let bd = &self.boundaries[b];
2957 let s_rx = &self.stages[b + 1].stream;
2958 let mut grew = false;
2959 for sl in &bd.slots {
2960 let mut guard = sl.buf.lock().unwrap();
2961 if guard.as_ref().map(|bf| bf.len() < n).unwrap_or(true) {
2962 *guard = Some(s_rx.alloc_zeros::<f32>(n)?);
2963 grew = true;
2964 }
2965 }
2966 if grew {
2967 s_rx.synchronize()?;
2968 }
2969 Ok(())
2970 }
2971
2972 pub fn boundary_slot_growth_bytes(
2976 &self,
2977 b: usize,
2978 n: usize,
2979 ) -> Result<usize, Box<dyn std::error::Error>> {
2980 let boundary = self
2981 .boundaries
2982 .get(b)
2983 .ok_or_else(|| format!("PP boundary {b} is outside the runtime"))?;
2984 let mut current = [0usize; 2];
2985 for (index, slot) in boundary.slots.iter().enumerate() {
2986 let guard = slot
2987 .buf
2988 .lock()
2989 .map_err(|_| format!("PP boundary {b} slot lock is poisoned"))?;
2990 current[index] = guard.as_ref().map_or(0, CudaSlice::len);
2991 }
2992 let elements = boundary_slot_growth_elements(current, n);
2993 Ok(elements.saturating_mul(std::mem::size_of::<f32>()))
2994 }
2995
2996 pub fn tx(
3010 &self,
3011 b: usize,
3012 x: &CudaSlice<f32>,
3013 n: usize,
3014 ) -> Result<usize, Box<dyn std::error::Error>> {
3015 assert_eq!(x.len(), n, "pp tx: residual length mismatch");
3016 let bd = &self.boundaries[b];
3017 let slot_idx = if pp2_overlap() {
3018 bd.step.fetch_add(1, Ordering::Relaxed) % 2
3019 } else {
3020 0
3021 };
3022 self.tx_slot(b, x, n, slot_idx)
3023 }
3024
3025 pub fn tx_pipelined(
3029 &self,
3030 b: usize,
3031 x: &CudaSlice<f32>,
3032 n: usize,
3033 ) -> Result<usize, Box<dyn std::error::Error>> {
3034 assert_eq!(x.len(), n, "pp tx: residual length mismatch");
3035 let slot_idx = self.boundaries[b].step.fetch_add(1, Ordering::Relaxed) % 2;
3036 self.tx_slot(b, x, n, slot_idx)
3037 }
3038
3039 fn tx_slot(
3040 &self,
3041 b: usize,
3042 x: &CudaSlice<f32>,
3043 n: usize,
3044 slot_idx: usize,
3045 ) -> Result<usize, Box<dyn std::error::Error>> {
3046 let bd = &self.boundaries[b];
3047 let path = BoundaryPath {
3048 boundary: b,
3049 src_stage: b,
3050 dst_stage: b + 1,
3051 transport: boundary_transport(bd.cross, self.host_bounce_active()),
3052 };
3053 let copied_slot = self.tx_slot_path(path, bd, x, n, slot_idx)?;
3054 if path.transport == BoundaryTransport::Peer {
3055 PEER_BOUNDARY_COPIES.fetch_add(1, Ordering::Relaxed);
3056 }
3057 Ok(copied_slot)
3058 }
3059
3060 fn tx_slot_path(
3061 &self,
3062 path: BoundaryPath,
3063 bd: &BoundaryRt,
3064 x: &CudaSlice<f32>,
3065 n: usize,
3066 slot_idx: usize,
3067 ) -> Result<usize, Box<dyn std::error::Error>> {
3068 debug_assert!(slot_idx < 2);
3069 let sl = &bd.slots[slot_idx];
3070 let s_tx = &self.stages[path.src_stage].stream;
3071 s_tx.wait(&sl.ev_rx)?;
3072 let mut guard = sl.buf.lock().unwrap();
3073 if guard.as_ref().map(|bf| bf.len() < n).unwrap_or(true) {
3074 let s_rx = &self.stages[path.dst_stage].stream;
3076 *guard = Some(s_rx.alloc_zeros::<f32>(n)?);
3077 s_rx.synchronize()?;
3087 }
3088 let buf = guard.as_mut().unwrap();
3089 match path.transport {
3090 BoundaryTransport::Local => s_tx.memcpy_dtod(x, buf)?,
3091 BoundaryTransport::HostBounce => {
3092 debug_assert_eq!(path.src_stage, path.boundary);
3093 debug_assert_eq!(path.dst_stage, path.boundary + 1);
3094 let bounce = self.bounce_rt()?;
3095 if n > bounce.capacity {
3096 return Err(format!(
3097 "pp host-bounce payload {n} exceeds geometry-sized capacity {} \
3098 (n_embd={}, max prime tokens={})",
3099 bounce.capacity,
3100 bounce.n_embd,
3101 crate::cache::PRIME_CHUNK_MAX_TOKENS,
3102 )
3103 .into());
3104 }
3105 let mut host = bounce.slot(path.boundary, slot_idx)?.lock().unwrap();
3106 s_tx.memcpy_dtoh(x, host.prefix_mut(n))?;
3110 }
3111 BoundaryTransport::Peer => {
3112 use cudarc::driver::{DevicePtr, DevicePtrMut};
3115 let (sp, _g0) = x.device_ptr(s_tx);
3116 let (dp, _g1) = buf.device_ptr_mut(s_tx);
3117 self.stages[path.src_stage].ctx.bind_to_thread()?;
3118 unsafe {
3119 cudarc::driver::result::memcpy_peer_async(
3120 self.stages[path.dst_stage].ctx.cu_ctx(),
3121 dp,
3122 self.stages[path.src_stage].ctx.cu_ctx(),
3123 sp,
3124 n * std::mem::size_of::<f32>(),
3125 s_tx.cu_stream(),
3126 )?;
3127 }
3128 }
3129 }
3130 sl.ev_tx.record(s_tx)?;
3131 Ok(slot_idx)
3132 }
3133
3134 pub fn rx(
3139 &self,
3140 b: usize,
3141 slot_idx: usize,
3142 n: usize,
3143 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3144 let bd = &self.boundaries[b];
3145 let path = BoundaryPath {
3146 boundary: b,
3147 src_stage: b,
3148 dst_stage: b + 1,
3149 transport: boundary_transport(bd.cross, self.host_bounce_active()),
3150 };
3151 self.rx_slot_path(path, bd, slot_idx, n)
3152 }
3153
3154 fn rx_slot_path(
3155 &self,
3156 path: BoundaryPath,
3157 bd: &BoundaryRt,
3158 slot_idx: usize,
3159 n: usize,
3160 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3161 let sl = &bd.slots[slot_idx];
3162 let s_rx = &self.stages[path.dst_stage].stream;
3163 s_rx.wait(&sl.ev_tx)?;
3164 let mut guard = sl.buf.lock().unwrap();
3165 let buf = guard.as_mut().expect("pp rx before tx");
3166 assert!(
3167 buf.len() >= n,
3168 "pp rx: slot holds {} < requested {n}",
3169 buf.len()
3170 );
3171 if path.transport == BoundaryTransport::HostBounce {
3172 debug_assert_eq!(path.src_stage, path.boundary);
3173 debug_assert_eq!(path.dst_stage, path.boundary + 1);
3174 let bounce = self.bounce_rt()?;
3175 let host = bounce.slot(path.boundary, slot_idx)?.lock().unwrap();
3176 let mut dst = buf.slice_mut(0..n);
3177 s_rx.memcpy_htod(host.prefix(n), &mut dst)?;
3180 }
3181 let mut work = unsafe { s_rx.alloc::<f32>(n)? };
3184 s_rx.memcpy_dtod(&buf.slice(0..n), &mut work)?;
3188 sl.ev_rx.record(s_rx)?;
3189 Ok(work)
3190 }
3191
3192 pub fn publish_to(
3219 &self,
3220 s: usize,
3221 dst: &Arc<CudaStream>,
3222 ) -> Result<(), Box<dyn std::error::Error>> {
3223 let st = &self.stages[s];
3224 if Arc::ptr_eq(&st.stream, dst) {
3227 return Ok(());
3228 }
3229 let ev = st.ctx.new_event(None)?;
3230 ev.record(&st.stream)?;
3231 dst.wait(&ev)?;
3232 Ok(())
3233 }
3234
3235 pub fn publish_all_to(&self, dst: &Arc<CudaStream>) -> Result<(), Box<dyn std::error::Error>> {
3266 if !pp_exit_publish() {
3267 return Ok(());
3268 }
3269 for s in 0..self.stages.len() {
3270 self.publish_to(s, dst)?;
3271 }
3272 Ok(())
3273 }
3274
3275 pub fn fence_stages_behind(
3297 &self,
3298 src: &Arc<CudaStream>,
3299 ) -> Result<(), Box<dyn std::error::Error>> {
3300 let ev = src.context().new_event(None)?;
3301 ev.record(src)?;
3302 for st in &self.stages {
3303 if Arc::ptr_eq(&st.stream, src) {
3304 continue;
3305 }
3306 st.stream.wait(&ev)?;
3307 }
3308 Ok(())
3309 }
3310
3311 pub fn record_done(&self) -> Result<CudaEvent, Box<dyn std::error::Error>> {
3314 let last = &self.stages[self.stages.len() - 1];
3315 let ev = last.ctx.new_event(None)?;
3316 ev.record(&last.stream)?;
3317 Ok(ev)
3318 }
3319
3320 pub fn readback_stream(&self) -> &Arc<CudaStream> {
3322 &self.readback
3323 }
3324}
3325
3326pub fn service_runtime_peer_probe(
3329 e: &Engine,
3330 scheduler_idle: bool,
3331 probe_allowed: bool,
3332) -> Result<RuntimePeerProbeStatus, Box<dyn std::error::Error>> {
3333 let Some(rt) = RTN.get() else {
3334 return Ok(RuntimePeerProbeStatus::NotRun);
3335 };
3336 let rt = rt
3337 .as_ref()
3338 .map_err(|err| -> Box<dyn std::error::Error> { err.clone().into() })?;
3339 rt.service_runtime_peer_probe(e, scheduler_idle, probe_allowed)
3340}
3341
3342pub struct PendingLogits {
3347 logits: CudaSlice<f32>,
3348 ev: CudaEvent,
3349 rb: Arc<CudaStream>,
3350 _walk: PpWalkLease,
3351}
3352
3353impl PendingLogits {
3354 pub(crate) fn new(
3355 logits: CudaSlice<f32>,
3356 ev: CudaEvent,
3357 rb: Arc<CudaStream>,
3358 walk: PpWalkLease,
3359 ) -> Self {
3360 PendingLogits {
3361 logits,
3362 ev,
3363 rb,
3364 _walk: walk,
3365 }
3366 }
3367
3368 pub fn wait(self) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3372 self.rb.wait(&self.ev)?;
3373 let host = self.rb.clone_dtoh(&self.logits)?;
3374 self.rb.synchronize()?;
3375 Ok(host)
3378 }
3379}
3380
3381pub fn init_model_transport(
3384 e: &Engine,
3385 cfg: &memra_gguf::config::ModelConfig,
3386 n_trunk: usize,
3387) -> Result<(), Box<dyn std::error::Error>> {
3388 if pp2_streams_off() || pp2_devices_env().is_none() || pp_cuts(n_trunk).is_none() {
3389 return Ok(());
3390 }
3391 PpNRt::get(e)?.init_boundary_transport(e, cfg.n_embd as usize)
3392}
3393
3394pub fn new_cache(
3401 e: &Engine,
3402 cfg: &memra_gguf::config::ModelConfig,
3403 max_ctx: usize,
3404) -> Result<crate::cache::Cache, Box<dyn std::error::Error>> {
3405 new_cache_inner(e, cfg, None, max_ctx)
3406}
3407
3408pub fn new_cache_planned(
3409 e: &Engine,
3410 cfg: &memra_gguf::config::ModelConfig,
3411 plan: &memra_gguf::model_plan::ModelPlan,
3412 max_ctx: usize,
3413) -> Result<crate::cache::Cache, Box<dyn std::error::Error>> {
3414 new_cache_inner(e, cfg, Some(plan), max_ctx)
3415}
3416
3417fn new_cache_inner(
3418 e: &Engine,
3419 cfg: &memra_gguf::config::ModelConfig,
3420 plan: Option<&memra_gguf::model_plan::ModelPlan>,
3421 max_ctx: usize,
3422) -> Result<crate::cache::Cache, Box<dyn std::error::Error>> {
3423 let n_trunk = (cfg.n_layer - cfg.nextn_predict_layers) as usize;
3424 if let Some(fence) = pp_cuts(n_trunk) {
3425 if pp2_devices_env().is_some() && !pp2_streams_off() {
3426 let rt = PpNRt::get(e)?;
3427 rt.init_boundary_transport(e, cfg.n_embd as usize)?;
3428 let n_st = fence.len() - 1;
3429 assert_eq!(
3430 rt.n_stages(),
3431 n_st,
3432 "PpNRt stage count {} != fence stages {n_st}",
3433 rt.n_stages()
3434 );
3435 rt.fence_stages_behind(&e.stream())?;
3444 let devs: Vec<&dyn memra_kv::KvDev> = (0..n_st)
3445 .map(|s| rt.engine(s, e) as &dyn memra_kv::KvDev)
3446 .collect();
3447 let cache = match plan {
3448 Some(plan) => {
3449 crate::cache::Cache::new_ppn_planned(&devs, &fence, cfg, plan, max_ctx)?
3450 }
3451 None => crate::cache::Cache::new_ppn(&devs, &fence, cfg, max_ctx)?,
3452 };
3453 sync_stages_after_load(e, n_trunk)?;
3454 return Ok(cache);
3455 }
3456 if !pp2_streams_off() {
3457 let cache = match plan {
3465 Some(plan) => crate::cache::Cache::new_planned(e, cfg, plan, max_ctx)?,
3466 None => crate::cache::Cache::new(e, cfg, max_ctx)?,
3467 };
3468 sync_stages_after_load(e, n_trunk)?;
3469 return Ok(cache);
3470 }
3471 }
3472 match plan {
3473 Some(plan) => crate::cache::Cache::new_planned(e, cfg, plan, max_ctx),
3474 None => crate::cache::Cache::new(e, cfg, max_ctx),
3475 }
3476}
3477
3478pub fn sync_stages_after_load(
3487 e: &Engine,
3488 n_trunk: usize,
3489) -> Result<(), Box<dyn std::error::Error>> {
3490 if pp2_streams_off() || pp_cuts(n_trunk).is_none() {
3491 return Ok(());
3492 }
3493 let rt = PpNRt::get(e)?;
3494 for s in 0..rt.n_stages() {
3495 rt.stages[s].ctx.bind_to_thread()?;
3496 unsafe {
3497 cudarc::driver::sys::cuCtxSynchronize().result()?;
3498 }
3499 }
3500 e.ctx().bind_to_thread()?;
3501 unsafe {
3502 cudarc::driver::sys::cuCtxSynchronize().result()?;
3503 }
3504 Ok(())
3505}
3506
3507pub fn layer_engine(
3513 e: &Engine,
3514 n_trunk: usize,
3515 il: usize,
3516) -> Result<&Engine, Box<dyn std::error::Error>> {
3517 if pp_shard_off() || pp2_devices_env().is_none() || pp2_streams_off() {
3518 return Ok(e);
3519 }
3520 let Some(fence) = pp_cuts(n_trunk) else {
3521 return Ok(e);
3522 };
3523 let rt = PpNRt::get(e)?;
3524 let s = stage_of(&fence, il.min(n_trunk - 1));
3525 Ok(rt.engine(s, e))
3526}
3527
3528#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3530pub(crate) enum TpRestoreRefusal {
3531 TargetAbsent,
3533 SourceAbsent,
3535 GrowTargetNotFresh,
3537 TokenGraphDoorOpen,
3540}
3541
3542#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3544pub(crate) enum TpRestore {
3545 Nothing,
3547 Rewind(usize),
3549 Grow(usize),
3551 DropMirror,
3555 Refuse(TpRestoreRefusal),
3556}
3557
3558pub(crate) fn tp_restore_plan(
3599 snap_len: Option<usize>,
3600 source_has_tp: Option<bool>,
3601 target_has_tp: bool,
3602 token_graph_door: bool,
3603) -> TpRestore {
3604 match (source_has_tp, snap_len) {
3605 (None, Some(len)) => {
3606 if target_has_tp {
3607 TpRestore::Rewind(len)
3608 } else {
3609 TpRestore::Refuse(TpRestoreRefusal::TargetAbsent)
3610 }
3611 }
3612 (None, None) => {
3613 if !target_has_tp {
3614 TpRestore::Nothing
3615 } else if token_graph_door {
3616 TpRestore::Refuse(TpRestoreRefusal::TokenGraphDoorOpen)
3617 } else {
3618 TpRestore::DropMirror
3619 }
3620 }
3621 (Some(source_has_tp), Some(len)) => {
3622 if !source_has_tp {
3623 TpRestore::Refuse(TpRestoreRefusal::SourceAbsent)
3624 } else if target_has_tp {
3625 TpRestore::Refuse(TpRestoreRefusal::GrowTargetNotFresh)
3626 } else {
3627 TpRestore::Grow(len)
3628 }
3629 }
3630 (Some(_), None) => {
3631 if target_has_tp {
3632 TpRestore::Refuse(TpRestoreRefusal::GrowTargetNotFresh)
3634 } else {
3635 TpRestore::Nothing
3640 }
3641 }
3642 }
3643}
3644
3645pub fn restore_cache_checkpoint(
3657 e: &Engine,
3658 model: &crate::hybrid::HybridModel,
3659 source: Option<&crate::cache::Cache>,
3660 target: &mut crate::cache::Cache,
3661 snap: &crate::cache::CacheSnapshot,
3662) -> Result<(), Box<dyn std::error::Error>> {
3663 target.ensure_usable("restore_cache_checkpoint target")?;
3664 if let Some(source) = source {
3665 source.ensure_usable("restore_cache_checkpoint source")?;
3666 }
3667 let cfg = &model.cfg;
3668 let n = target.kv.len();
3669 if target.recur.len() != n
3670 || target.tp_kv.len() != n
3671 || snap.kv_len.len() != n
3672 || snap.tp_kv_len.len() != n
3673 || snap.conv.len() != n
3674 || snap.ssm.len() != n
3675 || source.is_some_and(|s| s.kv.len() != n || s.recur.len() != n || s.tp_kv.len() != n)
3676 {
3677 return Err("checkpoint cache layer-count mismatch".into());
3678 }
3679 if snap.pos > target.max_ctx {
3680 return Err(format!(
3681 "checkpoint pos {} exceeds target capacity {}",
3682 snap.pos, target.max_ctx,
3683 )
3684 .into());
3685 }
3686
3687 let n_trunk = (cfg.n_layer - cfg.nextn_predict_layers) as usize;
3688 let token_graph_door = crate::tp::step_tp_graph_enabled().unwrap_or(false);
3690 let mut dropped_mirrors = 0usize;
3691 for il in 0..n {
3692 let owner = layer_engine(e, n_trunk, il)?;
3693 let src_kv = source.map(|s| &s.kv[il]);
3694 match (src_kv, target.kv[il].as_mut(), snap.kv_len[il]) {
3695 (Some(Some(src)), Some(dst), Some(len)) => {
3696 if len > src.len || len > target.max_ctx {
3697 return Err(format!(
3698 "checkpoint layer {il} len {len} exceeds source {} or target {}",
3699 src.len, target.max_ctx,
3700 )
3701 .into());
3702 }
3703 if src.kv_dim_k != dst.kv_dim_k
3704 || src.kv_dim_v != dst.kv_dim_v
3705 || src.k_tok_bytes != dst.k_tok_bytes
3706 || src.v_tok_bytes != dst.v_tok_bytes
3707 {
3708 return Err(format!("checkpoint KV layout mismatch at layer {il}").into());
3709 }
3710 match (&src.ring, dst.ring.as_ref()) {
3711 (Some(sring), Some(dring)) => {
3712 if dring.base() != 0 {
3718 return Err(format!(
3719 "checkpoint SWA restore at layer {il} requires a fresh target \
3720 ring (base {}, expected 0)",
3721 dring.base(),
3722 )
3723 .into());
3724 }
3725 let (new_base, phys) = sring.restore_plan(len).map_err(|e| {
3726 format!("checkpoint SWA restore refused at layer {il}: {e}")
3727 })?;
3728 let rows = phys.len();
3729 let kb = rows * src.k_tok_bytes;
3730 let vb = rows * src.v_tok_bytes;
3731 if kb > 0 {
3732 owner.copy_u8_range_into(
3733 &mut dst.k,
3734 0,
3735 &src.k,
3736 phys.start * src.k_tok_bytes,
3737 kb,
3738 )?;
3739 }
3740 if vb > 0 {
3741 owner.copy_u8_range_into(
3742 &mut dst.v,
3743 0,
3744 &src.v,
3745 phys.start * src.v_tok_bytes,
3746 vb,
3747 )?;
3748 }
3749 dst.ring
3750 .as_mut()
3751 .expect("ring presence checked above")
3752 .apply_rebase(new_base);
3753 if let Some(base_d) = dst.base_d.as_mut() {
3754 owner.set_i32_one(base_d, new_base as i32)?;
3755 }
3756 }
3757 (None, None) => {
3758 let kb = len * src.k_tok_bytes;
3759 let vb = len * src.v_tok_bytes;
3760 if kb > 0 {
3761 owner.copy_u8_into(&mut dst.k, 0, &src.k, kb)?;
3762 }
3763 if vb > 0 {
3764 owner.copy_u8_into(&mut dst.v, 0, &src.v, vb)?;
3765 }
3766 }
3767 _ => {
3768 return Err(
3769 format!("checkpoint ring/flat KV mismatch at layer {il}").into()
3770 );
3771 }
3772 }
3773 dst.len = len;
3774 owner.set_i32_one(&mut dst.len_d, len as i32)?;
3775 }
3776 (None, Some(dst), Some(len)) => {
3777 if len > dst.len || len > target.max_ctx {
3778 return Err(format!(
3779 "checkpoint layer {il} len {len} exceeds live {} or target {}",
3780 dst.len, target.max_ctx,
3781 )
3782 .into());
3783 }
3784 if let Some(ring) = &dst.ring
3785 && !ring.can_rewind_to(len)
3786 {
3787 return Err(format!(
3788 "checkpoint SWA rewind at layer {il} has been lapped \
3789 (len {len}, ring base {}); full re-prime required",
3790 ring.base(),
3791 )
3792 .into());
3793 }
3794 dst.len = len;
3795 owner.set_i32_one(&mut dst.len_d, len as i32)?;
3796 }
3797 (Some(None), None, None) | (None, None, None) => {}
3798 _ => return Err(format!("checkpoint KV kind mismatch at layer {il}").into()),
3799 }
3800
3801 match tp_restore_plan(
3802 snap.tp_kv_len[il],
3803 source.map(|s| s.tp_kv[il].is_some()),
3804 target.tp_kv[il].is_some(),
3805 token_graph_door,
3806 ) {
3807 TpRestore::Nothing => {}
3808 TpRestore::Rewind(len) => target.tp_kv[il]
3809 .as_mut()
3810 .expect("tp_restore_plan::Rewind implies a present target mirror")
3811 .rewind_to(len)?,
3812 TpRestore::Grow(len) => {
3813 let src = source
3814 .and_then(|s| s.tp_kv[il].as_ref())
3815 .expect("tp_restore_plan::Grow implies a present source mirror");
3816 let runtime = model.step_tp_runtime_for_layer(il).ok_or_else(|| {
3817 format!("checkpoint TP KV layer {il} has no distributed runtime")
3818 })?;
3819 let grown = runtime.grow_tp_kv_cache(src, target.max_ctx, len)?;
3820 target.tp_kv[il] = Some(grown);
3821 }
3822 TpRestore::DropMirror => {
3823 if target.tp_kv[il].take().is_some() {
3827 dropped_mirrors += 1;
3828 }
3829 }
3830 TpRestore::Refuse(reason) => {
3831 return Err(format!(
3832 "checkpoint TP KV restore refused at layer {il}: {} \
3833 (snap.tp_kv_len={:?}, snap.kv_len={:?}, snap.pos={}, \
3834 source_has_tp={:?}, target_has_tp={}, target_committed={})",
3835 match reason {
3836 TpRestoreRefusal::TargetAbsent =>
3837 "the snapshot recorded a distributed length but the target holds no \
3838 distributed cache to rewind",
3839 TpRestoreRefusal::SourceAbsent =>
3840 "the snapshot recorded a distributed length the parked source cannot \
3841 supply",
3842 TpRestoreRefusal::GrowTargetNotFresh =>
3843 "the freshly allocated grow target already holds a distributed cache",
3844 TpRestoreRefusal::TokenGraphDoorOpen =>
3845 "MEMRA_STEP_TP_GRAPH is open, and its model-level whole-token graph \
3846 bakes the rank-cache pointers, so the stale mirror cannot be freed",
3847 },
3848 snap.tp_kv_len[il],
3849 snap.kv_len[il],
3850 snap.pos,
3851 source.map(|s| s.tp_kv[il].is_some()),
3852 target.tp_kv[il].is_some(),
3853 target.tp_kv[il]
3854 .as_ref()
3855 .map(|c| c.committed_len())
3856 .unwrap_or(0),
3857 )
3858 .into());
3859 }
3860 }
3861
3862 match (target.recur[il].as_mut(), &snap.conv[il], &snap.ssm[il]) {
3863 (Some(dst), Some(conv), Some(ssm)) => {
3864 if conv.len() != dst.conv_state.len() || ssm.len() != dst.ssm_state.len() {
3865 return Err(
3866 format!("checkpoint recurrent layout mismatch at layer {il}").into(),
3867 );
3868 }
3869 owner.copy_into(&mut dst.conv_state, 0, conv, conv.len())?;
3870 owner.copy_into(&mut dst.ssm_state, 0, ssm, ssm.len())?;
3871 }
3872 (None, None, None) => {}
3873 _ => {
3874 return Err(format!("checkpoint recurrent kind mismatch at layer {il}").into());
3875 }
3876 }
3877 }
3878 target.pos = snap.pos;
3879 if dropped_mirrors > 0 {
3880 eprintln!(
3884 "[pp] checkpoint restore: cleared {dropped_mirrors} stale distributed KV mirror(s) \
3885 at pos {} (snapshot predates lazy TP hydration); the next TP use rehydrates them \
3886 from the local plane",
3887 snap.pos,
3888 );
3889 }
3890
3891 sync_stages_after_load(e, n_trunk)?;
3894 if source.is_some() {
3895 e.stream().synchronize()?;
3898 }
3899 Ok(())
3900}
3901
3902#[cfg(test)]
3903mod host_bounce_tests {
3904 use super::{
3905 BoundaryTransport, DUAL_PP_HOST_BOUNCE_REFUSAL, DUAL_PP_SINGLE_SLOT_REFUSAL,
3906 PEER_PROBE_FIXED_BYTES, PEER_PROBE_REQUIRED_REFUSAL, PEER_PROBE_TOKEN_WIDTHS,
3907 PEER_RUNTIME_PROBE_BUDGET_NS, PEER_RUNTIME_PROBE_CYCLE_COPIES,
3908 PEER_RUNTIME_PROBE_DEFERRAL_BOUND_INTERVALS, PEER_RUNTIME_PROBE_INTERVAL_COPIES,
3909 PP_WAVE_MAX_STAGES, PeerProbeDecision, PeerProbeStartupPolicy, acquire_pp_walk,
3910 boundary_slot_growth_elements, boundary_transport, dual_pp_eligibility,
3911 dual_pp_timing_dropped, dual_pp_timing_snapshot, dual_pp_wave_mid, enter_pp_wave_cell,
3912 host_bounce_capacity, latch_runtime_host_bounce, peer_probe_bytes_to_f32,
3913 peer_probe_decision, peer_probe_f32_to_bytes, peer_probe_mismatch_count,
3914 peer_probe_pattern, peer_probe_startup_policy, pp_devices_repeat, pp_wave_diagonal,
3915 pp_wave_eligibility, pp_wave_numeric_eligibility, pp_wave_on_value, pp_wave_ranges,
3916 pp_wave_route_enabled, pp_wave_snapshot, publish_runtime_peer_probe_deferral,
3917 record_dual_pp_stage_result, record_pp_wave_tick, runtime_peer_probe_candidate,
3918 runtime_peer_probe_next_copy,
3919 };
3920
3921 use super::{TpRestore, TpRestoreRefusal, tp_restore_plan};
3931
3932 #[test]
3933 fn mismatch_snapshot_predating_lazy_tp_drops_the_mirror_instead_of_refusing() {
3934 assert_eq!(
3936 tp_restore_plan(None, None, true, false),
3937 TpRestore::DropMirror
3938 );
3939 }
3940
3941 #[test]
3942 fn mismatch_on_the_grow_path_leaves_the_fresh_target_without_a_mirror() {
3943 assert_eq!(
3946 tp_restore_plan(None, Some(true), false, false),
3947 TpRestore::Nothing
3948 );
3949 }
3950
3951 #[test]
3952 fn drop_is_refused_while_the_token_graph_door_bakes_rank_pointers() {
3953 assert_eq!(
3954 tp_restore_plan(None, None, true, true),
3955 TpRestore::Refuse(TpRestoreRefusal::TokenGraphDoorOpen)
3956 );
3957 }
3958
3959 #[test]
3960 fn healthy_arms_are_untouched() {
3961 assert_eq!(
3962 tp_restore_plan(None, None, false, false),
3963 TpRestore::Nothing
3964 );
3965 assert_eq!(tp_restore_plan(None, None, false, true), TpRestore::Nothing);
3966 assert_eq!(
3967 tp_restore_plan(Some(15222), None, true, false),
3968 TpRestore::Rewind(15222)
3969 );
3970 assert_eq!(
3971 tp_restore_plan(Some(15222), Some(true), false, false),
3972 TpRestore::Grow(15222)
3973 );
3974 assert_eq!(
3975 tp_restore_plan(None, Some(false), false, false),
3976 TpRestore::Nothing
3977 );
3978 }
3979
3980 #[test]
3981 fn refuses_a_recorded_distributed_length_with_no_target_mirror() {
3982 assert_eq!(
3983 tp_restore_plan(Some(15222), None, false, false),
3984 TpRestore::Refuse(TpRestoreRefusal::TargetAbsent)
3985 );
3986 }
3987
3988 #[test]
3989 fn refuses_a_recorded_distributed_length_the_source_cannot_supply() {
3990 assert_eq!(
3991 tp_restore_plan(Some(15222), Some(false), false, false),
3992 TpRestore::Refuse(TpRestoreRefusal::SourceAbsent)
3993 );
3994 }
3995
3996 #[test]
3997 fn refuses_a_grow_target_that_is_not_fresh() {
3998 assert_eq!(
3999 tp_restore_plan(Some(15222), Some(true), true, false),
4000 TpRestore::Refuse(TpRestoreRefusal::GrowTargetNotFresh)
4001 );
4002 assert_eq!(
4003 tp_restore_plan(None, Some(true), true, false),
4004 TpRestore::Refuse(TpRestoreRefusal::GrowTargetNotFresh)
4005 );
4006 }
4007
4008 #[test]
4012 fn flip_default_is_dual_auto_with_explicit_off_and_forced_seams() {
4013 use super::{DualPpMode, dual_pp_mode_resolve};
4014 assert_eq!(dual_pp_mode_resolve(None), DualPpMode::Auto);
4015 assert_eq!(dual_pp_mode_resolve(Some("0")), DualPpMode::Off);
4016 assert_eq!(dual_pp_mode_resolve(Some("1")), DualPpMode::Forced);
4017 assert_eq!(dual_pp_mode_resolve(Some("2")), DualPpMode::Auto);
4019 assert_eq!(dual_pp_mode_resolve(Some("")), DualPpMode::Auto);
4020 }
4021
4022 #[test]
4023 fn flip_overlap_follows_mode_and_one_flag_restores_preflip_serial() {
4024 use super::{DualPpMode, pp2_overlap_resolve};
4025 assert!(pp2_overlap_resolve(None, DualPpMode::Auto));
4027 assert!(!pp2_overlap_resolve(None, DualPpMode::Off));
4029 assert!(!pp2_overlap_resolve(None, DualPpMode::Forced));
4031 for mode in [DualPpMode::Off, DualPpMode::Forced, DualPpMode::Auto] {
4033 assert!(pp2_overlap_resolve(Some("1"), mode));
4034 assert!(!pp2_overlap_resolve(Some("0"), mode));
4035 }
4036 }
4037
4038 #[test]
4039 fn flip_auto_routes_only_the_regated_regime_and_degrades_serially_elsewhere() {
4040 use super::{DualPpMode, dual_pp_route};
4041 assert!(dual_pp_route(DualPpMode::Auto, 2, 2, true, false));
4043 assert!(dual_pp_route(DualPpMode::Auto, 17, 2, true, false));
4044 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));
4051 assert!(!dual_pp_route(DualPpMode::Forced, 1, 2, true, false));
4052 assert!(!dual_pp_route(DualPpMode::Off, 8, 2, true, false));
4054 }
4055
4056 #[test]
4057 fn pp_wave_flag_is_strict_and_does_not_inherit_the_pp2_default() {
4058 assert_eq!(pp_wave_on_value(None), Ok(false));
4059 assert!(pp_wave_on_value(Some("")).is_err());
4060 assert_eq!(pp_wave_on_value(Some("0")), Ok(false));
4061 assert_eq!(pp_wave_on_value(Some("1")), Ok(true));
4062 assert!(pp_wave_on_value(Some("auto")).is_err());
4063 assert!(pp_wave_on_value(Some("2")).is_err());
4064 }
4065
4066 #[test]
4067 fn pp_wave_route_treats_overlap_off_and_single_work_item_as_serial_rollback() {
4068 assert!(pp_wave_route_enabled(true, true, 3, 2));
4069 assert!(pp_wave_route_enabled(true, true, 4, 8));
4070 assert!(!pp_wave_route_enabled(true, false, 3, 8));
4071 assert!(!pp_wave_route_enabled(false, true, 3, 8));
4072 assert!(!pp_wave_route_enabled(true, true, 2, 8));
4073 assert!(!pp_wave_route_enabled(true, true, 4, 1));
4074 }
4075
4076 #[test]
4077 fn pp_wave_ranges_are_balanced_contiguous_and_priority_preserving() {
4078 assert!(pp_wave_ranges(0, 4).is_empty());
4079 assert!(pp_wave_ranges(8, 0).is_empty());
4080 assert_eq!(pp_wave_ranges(1, 4), vec![(0, 1)]);
4081 assert_eq!(pp_wave_ranges(2, 4), vec![(0, 1), (1, 2)]);
4082 assert_eq!(pp_wave_ranges(8, 4), vec![(0, 2), (2, 4), (4, 6), (6, 8)]);
4083 assert_eq!(
4084 pp_wave_ranges(17, 4),
4085 vec![(0, 5), (5, 9), (9, 13), (13, 17)]
4086 );
4087 for batch in 1..=64 {
4088 for stages in 2..=PP_WAVE_MAX_STAGES {
4089 let ranges = pp_wave_ranges(batch, stages);
4090 assert_eq!(ranges.len(), batch.min(stages));
4091 assert_eq!(ranges.first().copied().unwrap().0, 0);
4092 assert_eq!(ranges.last().copied().unwrap().1, batch);
4093 assert!(ranges.iter().all(|(lo, hi)| lo < hi));
4094 assert!(ranges.windows(2).all(|pair| pair[0].1 == pair[1].0));
4095 let widths: Vec<_> = ranges.iter().map(|(lo, hi)| hi - lo).collect();
4096 assert!(widths.windows(2).all(|pair| pair[0] >= pair[1]));
4097 assert!(widths.first().unwrap() - widths.last().unwrap() <= 1);
4098 }
4099 }
4100 }
4101
4102 #[test]
4103 fn pp_wave_diagonals_cover_the_grid_without_stage_or_wave_aliasing() {
4104 for stages in 3..=PP_WAVE_MAX_STAGES {
4105 for waves in 1..=stages {
4106 let mut seen = vec![vec![false; stages]; waves];
4107 for diagonal in 0..stages + waves - 1 {
4108 let cells = pp_wave_diagonal(stages, waves, diagonal);
4109 let mut stage_seen = vec![false; stages];
4110 let mut wave_seen = vec![false; waves];
4111 for (wave, stage) in cells {
4112 assert_eq!(wave + stage, diagonal);
4113 assert!(!stage_seen[stage]);
4114 assert!(!wave_seen[wave]);
4115 assert!(!seen[wave][stage]);
4116 stage_seen[stage] = true;
4117 wave_seen[wave] = true;
4118 seen[wave][stage] = true;
4119 }
4120 }
4121 assert!(seen.into_iter().flatten().all(|cell| cell));
4122 }
4123 }
4124 assert!(pp_wave_diagonal(4, 4, 7).is_empty());
4125 }
4126
4127 #[test]
4128 fn pp_wavefront_refuses_every_unqualified_transport_shape() {
4129 assert!(pp_wave_eligibility(3, true, false, false).is_ok());
4130 assert!(pp_wave_eligibility(4, true, false, false).is_ok());
4131 assert!(pp_wave_eligibility(2, true, false, false).is_err());
4132 assert!(pp_wave_eligibility(5, true, false, false).is_err());
4133 assert!(pp_wave_eligibility(3, false, false, false).is_err());
4134 assert!(pp_wave_eligibility(3, true, true, false).is_err());
4135 assert!(pp_wave_eligibility(3, true, false, true).is_err());
4136 }
4137
4138 #[test]
4139 fn pp_wavefront_requires_width_invariant_bf16_for_w4a16() {
4140 assert!(pp_wave_numeric_eligibility(false, false).is_ok());
4141 assert!(pp_wave_numeric_eligibility(false, true).is_ok());
4142 assert!(pp_wave_numeric_eligibility(true, true).is_ok());
4143 assert!(pp_wave_numeric_eligibility(true, false).is_err());
4144 }
4145
4146 #[test]
4147 fn pp_device_aliases_cannot_bypass_the_distinct_stage_gate() {
4148 assert!(!pp_devices_repeat("0,1,2,3"));
4149 assert!(pp_devices_repeat("0,00,1"));
4150 assert!(pp_devices_repeat("2,1,2"));
4151 assert!(pp_devices_repeat("0,nope,1"));
4152 }
4153
4154 #[test]
4155 fn pp_walk_owner_refuses_reentry_and_releases_at_scope_end() {
4156 let active = std::sync::Arc::new(std::sync::atomic::AtomicU64::new(0));
4157 let next = std::sync::atomic::AtomicU64::new(1);
4158 let first = acquire_pp_walk(&active, &next, 7, None, "first").unwrap();
4159 let held_clone = super::PpWalkLease {
4160 state: first.state.clone(),
4161 };
4162 let error = acquire_pp_walk(&active, &next, 7, None, "second").unwrap_err();
4163 assert!(error.contains("refused concurrent PP walk"));
4164 drop(first);
4165 assert!(acquire_pp_walk(&active, &next, 7, None, "third").is_err());
4166 drop(held_clone);
4167 assert!(acquire_pp_walk(&active, &next, 7, None, "fourth").is_ok());
4168 }
4169
4170 #[test]
4171 fn boundary_growth_charges_first_allocation_and_only_missing_high_water_afterward() {
4172 assert_eq!(boundary_slot_growth_elements([0, 0], 4096), 8192);
4173 assert_eq!(boundary_slot_growth_elements([4096, 4096], 4096), 0);
4174 assert_eq!(boundary_slot_growth_elements([4096, 2048], 4096), 2048);
4175 assert_eq!(boundary_slot_growth_elements([8192, 8192], 4096), 0);
4176 }
4177
4178 #[test]
4179 fn pp_wave_liveness_snapshot_counts_ticks_cells_and_real_overlap() {
4180 let before = pp_wave_snapshot();
4181 let first = enter_pp_wave_cell();
4182 let second = enter_pp_wave_cell();
4183 drop(second);
4184 drop(first);
4185 record_pp_wave_tick();
4186 let after = pp_wave_snapshot();
4187 assert!(after.0 > before.0);
4188 assert!(after.1 >= before.1 + 2);
4189 assert!(after.2 > before.2);
4190 }
4191
4192 #[test]
4193 fn dual_pp_split_is_honest_at_one_and_ceil_first_afterward() {
4194 assert_eq!(dual_pp_wave_mid(1), None);
4195 assert_eq!(dual_pp_wave_mid(2), Some(1));
4196 assert_eq!(dual_pp_wave_mid(3), Some(2));
4197 assert_eq!(dual_pp_wave_mid(8), Some(4));
4198 assert_eq!(dual_pp_wave_mid(16), Some(8));
4199 assert_eq!(dual_pp_wave_mid(31), Some(16));
4200 assert_eq!(dual_pp_wave_mid(32), Some(16));
4201 }
4202
4203 #[test]
4204 fn dual_pp_refuses_single_slot_and_non_pp2_shapes() {
4205 assert_eq!(
4206 dual_pp_eligibility(2, false, false),
4207 Err(DUAL_PP_SINGLE_SLOT_REFUSAL)
4208 );
4209 assert!(dual_pp_eligibility(2, true, false).is_ok());
4210 assert!(dual_pp_eligibility(3, true, false).is_err());
4211 }
4212
4213 #[test]
4214 fn dual_pp_refuses_unvalidated_host_bounce_transport() {
4215 assert_eq!(
4216 dual_pp_eligibility(2, true, true),
4217 Err(DUAL_PP_HOST_BOUNCE_REFUSAL),
4218 );
4219 }
4220
4221 #[test]
4222 #[allow(clippy::int_plus_one)] fn dual_pp_timing_error_is_counted_without_recording_a_sample() {
4224 let dropped_before = dual_pp_timing_dropped();
4225 let (_, samples_before) = dual_pp_timing_snapshot();
4226 record_dual_pp_stage_result(0, Err::<f32, _>("CUDA_ERROR_NOT_READY"));
4227 let (_, samples_after) = dual_pp_timing_snapshot();
4228 assert_eq!(samples_after[0], samples_before[0]);
4229 assert!(dual_pp_timing_dropped() >= dropped_before + 1);
4230 }
4231
4232 #[test]
4233 fn corrupted_peer_readback_fails_closed_unless_host_bounce_is_selected() {
4234 assert_eq!(
4235 PEER_PROBE_TOKEN_WIDTHS,
4236 [1, 8, 16, crate::cache::PRIME_CHUNK_MAX_TOKENS],
4237 );
4238 let largest_payload_bytes = PEER_PROBE_TOKEN_WIDTHS[3] * 4096 * std::mem::size_of::<f32>();
4239 assert_eq!(largest_payload_bytes, 64 * 1024 * 1024);
4240 assert!(largest_payload_bytes >= 1024 * 1024);
4241 let expected = peer_probe_pattern(PEER_PROBE_FIXED_BYTES, 2, 0, 1);
4242 assert_eq!(
4243 peer_probe_f32_to_bytes(&peer_probe_bytes_to_f32(&expected)),
4244 expected,
4245 );
4246 let mut corrupted = expected.clone();
4247 for offset in [0, 8_191, PEER_PROBE_FIXED_BYTES - 1] {
4248 corrupted[offset] ^= 0x5a;
4249 }
4250
4251 assert_eq!(peer_probe_mismatch_count(&expected, &corrupted), 3);
4252 assert_eq!(
4253 peer_probe_decision(&expected, &corrupted, false),
4254 Err("3 mismatched byte(s)".to_string()),
4255 );
4256 assert_eq!(
4257 peer_probe_decision(&expected, &corrupted, true),
4258 Ok(PeerProbeDecision::ProceedWithHostBounce { mismatches: 3 }),
4259 );
4260 }
4261
4262 #[test]
4263 fn probe_off_refusal_matrix_is_fail_closed_only_for_sharded_native_peer() {
4264 for probe_on in [false, true] {
4265 for sharded in [false, true] {
4266 for host_bounce in [false, true] {
4267 let got = peer_probe_startup_policy(probe_on, sharded, host_bounce);
4268 let expected = match (probe_on, sharded, host_bounce) {
4269 (false, true, false) => Err(PEER_PROBE_REQUIRED_REFUSAL),
4270 (false, true, true) => Ok(PeerProbeStartupPolicy::BypassedWithHostBounce),
4271 _ => Ok(PeerProbeStartupPolicy::Allowed),
4272 };
4273 assert_eq!(
4274 got, expected,
4275 "probe_on={probe_on} sharded={sharded} host_bounce={host_bounce}",
4276 );
4277 }
4278 }
4279 }
4280 assert!(PEER_PROBE_REQUIRED_REFUSAL.contains("MEMRA_PEER_PROBE=0"));
4281 assert!(PEER_PROBE_REQUIRED_REFUSAL.contains("MEMRA_PP_HOST_BOUNCE!=1"));
4282 }
4283
4284 #[test]
4285 fn runtime_reprobe_keeps_cheap_deadlines_live_while_expensive_work_waits_for_idle() {
4286 let every = PEER_RUNTIME_PROBE_INTERVAL_COPIES;
4287 assert_eq!(PEER_RUNTIME_PROBE_CYCLE_COPIES, 4 * every);
4288 let mut next = [every, 2 * every, 3 * every, 4 * every];
4289 let measured_ns = [1_000_000, 2_000_000, 3_000_000, 0];
4290
4291 assert_eq!(
4292 runtime_peer_probe_candidate(every - 1, next, measured_ns, false),
4293 None,
4294 );
4295 assert_eq!(
4296 runtime_peer_probe_candidate(every, next, measured_ns, false),
4297 Some((0, 1)),
4298 );
4299
4300 next[..3].copy_from_slice(&[5 * every, 6 * every, 7 * every]);
4303 assert_eq!(
4304 runtime_peer_probe_candidate(4 * every, next, measured_ns, false),
4305 None,
4306 );
4307 assert_eq!(
4310 runtime_peer_probe_candidate(5 * every, next, measured_ns, false),
4311 Some((0, 1)),
4312 );
4313 assert_eq!(
4315 runtime_peer_probe_candidate(5 * every, next, measured_ns, true),
4316 Some((3, crate::cache::PRIME_CHUNK_MAX_TOKENS)),
4317 );
4318 }
4319
4320 #[test]
4321 fn runtime_reprobe_moves_any_measured_over_budget_rung_to_idle_only() {
4322 let every = PEER_RUNTIME_PROBE_INTERVAL_COPIES;
4323 let next = [u64::MAX, every, u64::MAX, u64::MAX];
4324 let mut measured_ns = [0; PEER_PROBE_TOKEN_WIDTHS.len()];
4325 measured_ns[1] = PEER_RUNTIME_PROBE_BUDGET_NS + 1;
4326 assert_eq!(
4327 runtime_peer_probe_candidate(every, next, measured_ns, false),
4328 None
4329 );
4330 assert_eq!(
4331 runtime_peer_probe_candidate(every, next, measured_ns, true),
4332 Some((1, 8)),
4333 );
4334 }
4335
4336 #[test]
4337 fn late_runtime_reprobe_advances_once_instead_of_bursting_catchup() {
4338 let every = PEER_RUNTIME_PROBE_INTERVAL_COPIES;
4339 let due = every;
4340 assert_eq!(runtime_peer_probe_next_copy(due, due), due + 4 * every);
4341 assert_eq!(runtime_peer_probe_next_copy(due, 20 * every), 21 * every);
4342 }
4343
4344 #[test]
4345 fn runtime_reprobe_deferral_metric_counts_intervals_and_publishes_bound_state() {
4346 use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
4347
4348 assert_eq!(
4349 PEER_RUNTIME_PROBE_DEFERRAL_BOUND_INTERVALS * PEER_RUNTIME_PROBE_INTERVAL_COPIES,
4350 PEER_RUNTIME_PROBE_CYCLE_COPIES,
4351 );
4352 let deferred = AtomicU64::new(0);
4353 let degraded = AtomicBool::new(false);
4354 publish_runtime_peer_probe_deferral(&deferred, °raded, 1, false);
4355 assert_eq!(deferred.load(Ordering::Relaxed), 1);
4356 assert!(!degraded.load(Ordering::Acquire));
4357
4358 publish_runtime_peer_probe_deferral(
4359 &deferred,
4360 °raded,
4361 PEER_RUNTIME_PROBE_DEFERRAL_BOUND_INTERVALS - 1,
4362 true,
4363 );
4364 assert_eq!(
4365 deferred.load(Ordering::Relaxed),
4366 PEER_RUNTIME_PROBE_DEFERRAL_BOUND_INTERVALS,
4367 );
4368 assert!(degraded.load(Ordering::Acquire));
4369 }
4370
4371 #[test]
4372 fn runtime_probe_failure_latches_native_before_publishing_validated_bounce() {
4373 use std::sync::atomic::{AtomicBool, Ordering};
4374
4375 let failed = AtomicBool::new(false);
4376 let degraded = AtomicBool::new(false);
4377 let armed = latch_runtime_host_bounce(&failed, °raded, || Ok::<_, String>(()));
4378 assert!(armed.is_ok());
4379 assert!(failed.load(Ordering::Acquire));
4380 assert!(degraded.load(Ordering::Acquire));
4381
4382 let failed = AtomicBool::new(false);
4383 let degraded = AtomicBool::new(false);
4384 let refused = latch_runtime_host_bounce(&failed, °raded, || {
4385 Err::<(), _>("injected staging mismatch".to_string())
4386 });
4387 assert_eq!(refused, Err("injected staging mismatch".to_string()));
4388 assert!(failed.load(Ordering::Acquire));
4389 assert!(!degraded.load(Ordering::Acquire));
4390 }
4391
4392 #[test]
4393 fn transport_selection_keeps_peer_default_and_bounces_only_cross_device() {
4394 assert_eq!(boundary_transport(false, false), BoundaryTransport::Local);
4395 assert_eq!(boundary_transport(false, true), BoundaryTransport::Local);
4396 assert_eq!(boundary_transport(true, false), BoundaryTransport::Peer);
4397 assert_eq!(
4398 boundary_transport(true, true),
4399 BoundaryTransport::HostBounce
4400 );
4401 }
4402
4403 #[test]
4404 fn step37_geometry_sizes_each_slot_from_the_prime_cap() {
4405 let (elems, bytes) = host_bounce_capacity(4096).expect("valid Step-3.7 geometry");
4406 assert_eq!(elems, 4096 * crate::cache::PRIME_CHUNK_MAX_TOKENS);
4407 assert_eq!(bytes, 64 * 1024 * 1024);
4408 }
4409
4410 #[test]
4411 fn host_bounce_capacity_rejects_invalid_or_overflowing_geometry() {
4412 assert!(host_bounce_capacity(0).is_err());
4413 assert!(host_bounce_capacity(usize::MAX).is_err());
4414 }
4415}