1use anyhow::Result;
9use anyhow::bail;
10
11pub const QUANTUM_NS: u64 = 1_000_000;
13pub const SLICE_MIN_NS: u64 = 10_000;
16pub const SLICE_ULL_NS: u64 = 50_000;
19pub const SLICE_LL_NS: u64 = 100_000;
23pub const SLICE_MLL_NS: u64 = 500_000;
26pub const SLICE_NL_NS: u64 = 1_000_000;
29pub const SLICE_ENL_NS: u64 = 5_000_000;
32pub const SLICE_MAX_NS: u64 = 20_000_000;
36pub const PERIOD_NS: u64 = 80_000_000;
40pub const PRED_MIN_NS: u64 = 1;
42pub const PRED_MAX_NS: u64 = 1_000_000_000;
44pub const CAP_BASE: u32 = 1024;
46pub const WEIGHT_BASE: u32 = 128;
48pub const WEIGHT_MIN: u32 = 1;
50pub const WEIGHT_MAX: u32 = 16_384;
52pub const HINT_MAX: u64 = 8192;
54pub const RED_EMAX_NS: u64 = 256_000;
57pub const RED_TOL_NS: u64 = 64_000;
60pub const ADAPT_GROW_NS: u64 = 64_000;
63pub const ADAPT_SHRINK_NS: u64 = 128_000;
66pub const ADAPT_PROP_MAX_NS: u64 = 1_000_000;
68pub const ADAPT_PROP_SHIFT: u32 = 3;
70pub const ADAPT_DEADBAND_SHIFT: u32 = 5;
73pub const ADAPT_DEADBAND_MIN_NS: u64 = 8_000;
76pub const ADAPT_DAMP_SHIFT: u32 = 1;
79pub const PRED_UP_SHIFT: u32 = 3;
82pub const PRED_DOWN_SHIFT: u32 = 4;
85pub const PRED_DEV_UP_SHIFT: u32 = 2;
88pub const PRED_DEV_DOWN_SHIFT: u32 = 3;
91pub const PRED_DEV_MARGIN_SHIFT: u32 = 1;
94pub const SLICE_LAT_NS: u64 = 100_000;
103pub const LAT_SLEEP_MIN_NS: u64 = 20_000;
108pub const D_LAT_NS: u64 = 4_000_000;
110pub const PREEMPT_MARGIN_NS: u64 = 100_000;
113pub const PREEMPT_TAIL_NS: u64 = 100_000;
116
117const DEF_QUANTUM_NS: u64 = QUANTUM_NS;
119
120#[cfg(test)]
123pub fn adapt_deadband(base: u64) -> u64 {
124 let band = base >> ADAPT_DEADBAND_SHIFT;
125 if band < ADAPT_DEADBAND_MIN_NS {
126 return ADAPT_DEADBAND_MIN_NS;
127 }
128 band
129}
130
131#[cfg(test)]
136pub fn adapt_prop(cur: u32, exceed: u64, slack: u64) -> u32 {
137 let base = if cur == 0 { QUANTUM_NS } else { cur as u64 };
138 let band = adapt_deadband(base);
139 let clamp = |v: u64| -> u32 {
141 if v < SLICE_MIN_NS {
142 return SLICE_MIN_NS as u32;
143 }
144 if v > QUANTUM_NS {
145 return QUANTUM_NS as u32;
146 }
147 v as u32
148 };
149 if exceed != 0 {
150 if exceed <= band {
151 return clamp(base);
152 }
153 let mut step = exceed >> ADAPT_PROP_SHIFT;
154 if step > ADAPT_PROP_MAX_NS {
155 step = ADAPT_PROP_MAX_NS;
156 }
157 if step > (base >> 1) {
158 step = base >> 1;
159 }
160 step >>= ADAPT_DAMP_SHIFT;
161 if step == 0 {
162 return clamp(base);
163 }
164 if base <= step {
165 return SLICE_MIN_NS as u32;
166 }
167 return clamp(base - step);
168 }
169 if slack != 0 {
170 if slack <= band {
171 return clamp(base);
172 }
173 let mut step = slack >> ADAPT_PROP_SHIFT;
174 if step > ADAPT_PROP_MAX_NS {
175 step = ADAPT_PROP_MAX_NS;
176 }
177 if step > (base >> 1) {
178 step = base >> 1;
179 }
180 step >>= ADAPT_DAMP_SHIFT;
181 if step == 0 {
182 return clamp(base);
183 }
184 return clamp(base.saturating_add(step));
185 }
186 clamp(base)
187}
188
189#[cfg(test)]
192pub fn slice_lat_clamp(slice: u32, is_lat: bool) -> u32 {
193 if is_lat && (slice as u64) > SLICE_LAT_NS {
194 return SLICE_LAT_NS as u32;
195 }
196 slice
197}
198
199#[cfg(test)]
202pub fn pred_avg(avg: u64, delta: u64) -> u64 {
203 let d = if delta == 0 { PRED_MIN_NS } else { delta }.clamp(PRED_MIN_NS, PRED_MAX_NS);
204 if avg == 0 {
205 return d;
206 }
207 if d > avg {
208 let diff = (d - avg) >> PRED_UP_SHIFT;
209 return avg.saturating_add(diff).clamp(PRED_MIN_NS, PRED_MAX_NS);
210 }
211 let diff = (avg - d) >> PRED_DOWN_SHIFT;
212 if diff > avg {
213 return PRED_MIN_NS;
214 }
215 (avg - diff).clamp(PRED_MIN_NS, PRED_MAX_NS)
216}
217
218#[cfg(test)]
221pub fn pred_dev(dev: u64, avg: u64, delta: u64) -> u64 {
222 let d = if delta == 0 { PRED_MIN_NS } else { delta }.clamp(PRED_MIN_NS, PRED_MAX_NS);
223 let a = if avg == 0 { d } else { avg };
224 let mut err = d.abs_diff(a);
225 if err == 0 {
226 err = PRED_MIN_NS;
227 }
228 err = err.clamp(PRED_MIN_NS, PRED_MAX_NS);
229 if dev == 0 {
230 let floor = avg >> 2;
231 if floor > err {
232 return floor.clamp(PRED_MIN_NS, PRED_MAX_NS);
233 }
234 return err;
235 }
236 if err > dev {
237 let diff = (err - dev) >> PRED_DEV_UP_SHIFT;
238 return dev.saturating_add(diff).clamp(PRED_MIN_NS, PRED_MAX_NS);
239 }
240 let diff = (dev - err) >> PRED_DEV_DOWN_SHIFT;
241 if diff > dev {
242 return PRED_MIN_NS;
243 }
244 (dev - diff).clamp(PRED_MIN_NS, PRED_MAX_NS)
245}
246
247#[cfg(test)]
250pub fn pred_period(avg: u64, dev: u64) -> u64 {
251 if avg == 0 {
252 return PERIOD_NS;
253 }
254 let margin = dev >> PRED_DEV_MARGIN_SHIFT;
255 let sum = avg.saturating_add(dev).saturating_add(margin);
256 if sum == u64::MAX {
257 return PRED_MAX_NS;
258 }
259 sum.clamp(PRED_MIN_NS, PRED_MAX_NS)
260}
261
262#[cfg(test)]
269pub fn classify_tier(avg: u64, dev: u64, is_crit: bool) -> u32 {
270 let margin = dev >> PRED_DEV_MARGIN_SHIFT;
271 let pred = avg.saturating_add(dev).saturating_add(margin);
272 if is_crit || pred <= SLICE_ULL_NS {
273 return SLICE_ULL_NS as u32;
274 }
275 if pred <= SLICE_LL_NS {
276 return SLICE_LL_NS as u32;
277 }
278 if pred <= SLICE_MLL_NS {
279 return SLICE_MLL_NS as u32;
280 }
281 if pred <= SLICE_NL_NS {
282 return SLICE_NL_NS as u32;
283 }
284 if pred <= SLICE_ENL_NS {
285 return SLICE_ENL_NS as u32;
286 }
287 SLICE_MAX_NS as u32
288}
289
290#[cfg(test)]
295pub fn red_cost(avg: u64, slice: u32) -> u64 {
296 if avg != 0 {
297 if avg < SLICE_MIN_NS {
298 return SLICE_MIN_NS;
299 }
300 if avg > SLICE_MAX_NS {
301 return SLICE_MAX_NS;
302 }
303 return avg;
304 }
305 if slice != 0 {
306 let s = slice as u64;
307 if s < SLICE_MIN_NS {
308 return SLICE_MIN_NS;
309 }
310 if s > SLICE_MAX_NS {
311 return SLICE_MAX_NS;
312 }
313 return s;
314 }
315 QUANTUM_NS
316}
317
318#[cfg(test)]
322pub fn lat_deadline(now: u64, slice: u32) -> u64 {
323 let need = (slice as u64).saturating_add(PREEMPT_MARGIN_NS);
324 let bound = if need > D_LAT_NS { need } else { D_LAT_NS };
325 now.saturating_add(bound)
326}
327
328#[cfg(test)]
335pub fn lat_crit_ext(
336 avg: u64,
337 dev: u64,
338 hint_us: u32,
339 sleep_ns: u64,
340 uclamp_min: u32,
341 eff_w: u32,
342) -> bool {
343 if avg == 0 {
344 return false;
345 }
346 {
347 let margin = dev >> PRED_DEV_MARGIN_SHIFT;
348 let pred = avg.saturating_add(dev).saturating_add(margin);
349 if pred == u64::MAX {
350 return false;
351 }
352 if pred > QUANTUM_NS {
353 return false;
354 }
355 }
356 if hint_us != 0 {
357 let h = hint_us as u64;
358 if h > 18_446_744_073_709_551 {
359 return false;
360 }
361 if h * 1000 > QUANTUM_NS {
362 return false;
363 }
364 }
365 if sleep_ns != 0 && sleep_ns < LAT_SLEEP_MIN_NS {
366 return false;
367 }
368 if uclamp_min != 0 && uclamp_min < 512 {
369 return false;
370 }
371 if eff_w != 0 && eff_w < WEIGHT_BASE {
372 return false;
373 }
374 true
375}
376
377#[cfg(test)]
381pub fn lat_crit(avg: u64, dev: u64) -> bool {
382 if avg == 0 {
383 return false;
384 }
385 let margin = dev >> PRED_DEV_MARGIN_SHIFT;
386 let pred = avg.saturating_add(dev).saturating_add(margin);
387 if pred == u64::MAX {
388 return false;
389 }
390 pred <= QUANTUM_NS
391}
392
393#[cfg(test)]
396pub fn is_lat(
397 avg: u64,
398 dev: u64,
399 hint_us: u32,
400 sleep_ns: u64,
401 uclamp_min: u32,
402 eff_w: u32,
403) -> bool {
404 if !lat_crit(avg, dev) {
405 return false;
406 }
407 lat_crit_ext(avg, dev, hint_us, sleep_ns, uclamp_min, eff_w)
408}
409
410#[cfg(test)]
413fn time_before(a: u64, b: u64) -> bool {
414 (a.wrapping_sub(b) as i64) < 0
415}
416
417#[cfg(test)]
421pub fn sleep_ns(old_wait: u64, now: u64, avg: u64) -> u64 {
422 if old_wait == 0 {
423 return 0;
424 }
425 if !time_before(old_wait, now) {
426 return 0;
427 }
428 let mut gap = now.wrapping_sub(old_wait);
429 if avg != 0 {
430 if gap > avg {
431 gap -= avg;
432 } else {
433 gap = 0;
434 }
435 }
436 gap
437}
438
439#[cfg(test)]
444pub fn preempt_ok(
445 arr_key: u64,
446 occ_key: u64,
447 now: u64,
448 occ_run_at: u64,
449 occ_cur: u32,
450 arr_lat: bool,
451 occ_lat: bool,
452) -> bool {
453 if !arr_lat || occ_lat {
454 return false;
455 }
456 if arr_key == 0 || arr_key == u64::MAX {
457 return false;
458 }
459 if occ_key == 0 || occ_key == u64::MAX {
460 return false;
461 }
462 if !time_before(arr_key, occ_key) {
463 return false;
464 }
465 let margin = arr_key.saturating_add(PREEMPT_MARGIN_NS);
466 if margin == u64::MAX {
467 return false;
468 }
469 if !time_before(margin, occ_key) {
470 return false;
471 }
472 if occ_run_at == 0 || occ_run_at == u64::MAX {
473 return false;
474 }
475 let base = if occ_cur == 0 {
476 QUANTUM_NS
477 } else {
478 occ_cur as u64
479 };
480 let occ_end = occ_run_at.saturating_add(base);
481 if occ_end == u64::MAX {
482 return false;
483 }
484 let tail = now.saturating_add(PREEMPT_TAIL_NS);
485 if tail == u64::MAX {
486 return false;
487 }
488 if !time_before(tail, occ_end) {
489 return false;
490 }
491 true
492}
493
494#[cfg(test)]
500pub fn slice_resume(start: u64, now: u64, is_lat: bool, cur: u32) -> u32 {
501 let base = if cur == 0 { QUANTUM_NS } else { cur as u64 };
502 if start == 0 || start == u64::MAX {
503 return base as u32;
504 }
505 let end = start.saturating_add(base);
506 if end == u64::MAX {
507 return base as u32;
508 }
509 let rem = if !time_before(now, end) && now != end {
510 0
511 } else if time_before(now, end) {
512 end.wrapping_sub(now)
513 } else {
514 0
515 };
516 let cap = if is_lat { SLICE_LAT_NS } else { QUANTUM_NS };
517 if rem < SLICE_MIN_NS {
518 return SLICE_MIN_NS as u32;
519 }
520 if rem > cap {
521 return cap as u32;
522 }
523 rem as u32
524}
525
526#[derive(Debug, Clone, PartialEq, Eq)]
528pub struct Config {
529 pub quantum_ns: u64,
531}
532
533impl Default for Config {
534 fn default() -> Self {
536 Self {
537 quantum_ns: DEF_QUANTUM_NS,
538 }
539 }
540}
541
542impl Config {
543 pub fn validate(&self) -> Result<()> {
578 if self.quantum_ns != QUANTUM_NS {
579 bail!("quantum bad {}", self.quantum_ns);
580 }
581 if SLICE_MIN_NS != 10_000 {
582 bail!("slice floor bad");
583 }
584 if SLICE_MIN_NS >= QUANTUM_NS {
585 bail!("slice floor over ceiling bad");
586 }
587 if WEIGHT_MIN != 1 || WEIGHT_BASE != 128 || WEIGHT_MAX != 16_384 {
588 bail!("weight bounds bad");
589 }
590 if PERIOD_NS != 80_000_000 {
591 bail!("period bounds bad");
592 }
593 if PRED_MIN_NS != 1 || PRED_MAX_NS != 1_000_000_000 {
594 bail!("predictor bounds bad");
595 }
596 if CAP_BASE != 1024 {
597 bail!("capacity base bad");
598 }
599 if HINT_MAX != 8192 {
600 bail!("hint bound bad");
601 }
602 if crate::bpf_intf::flow_consts_FLOW_QUANTUM_NS as u64 != QUANTUM_NS {
603 bail!("quantum header bad");
604 }
605 if crate::bpf_intf::flow_consts_FLOW_SLICE_MIN_NS as u64 != SLICE_MIN_NS {
606 bail!("slice floor header bad");
607 }
608 if crate::bpf_intf::flow_consts_FLOW_PERIOD_NS as u64 != PERIOD_NS {
609 bail!("period header bad");
610 }
611 if crate::bpf_intf::flow_consts_FLOW_PRED_MIN_NS as u64 != PRED_MIN_NS {
612 bail!("pred floor bad");
613 }
614 if crate::bpf_intf::flow_consts_FLOW_PRED_MAX_NS as u64 != PRED_MAX_NS {
615 bail!("pred ceiling bad");
616 }
617 if crate::bpf_intf::flow_consts_FLOW_WEIGHT_MIN as u64 != WEIGHT_MIN as u64 {
618 bail!("weight floor bad");
619 }
620 if crate::bpf_intf::flow_consts_FLOW_WEIGHT_BASE as u64 != WEIGHT_BASE as u64 {
621 bail!("weight base bad");
622 }
623 if crate::bpf_intf::flow_consts_FLOW_WEIGHT_MAX as u64 != WEIGHT_MAX as u64 {
624 bail!("weight top bad");
625 }
626 if crate::bpf_intf::flow_consts_FLOW_CAP_BASE as u64 != CAP_BASE as u64 {
627 bail!("cap base bad");
628 }
629 if crate::bpf_intf::flow_consts_FLOW_HINT_MAX as u64 != HINT_MAX {
630 bail!("hint header bad");
631 }
632 if crate::bpf_intf::flow_consts_FLOW_PREEMPT_MARGIN_NS as u64 != PREEMPT_MARGIN_NS {
633 bail!("margin bad");
634 }
635 if PREEMPT_MARGIN_NS != 100_000 {
636 bail!("margin bounds bad");
637 }
638 if crate::bpf_intf::flow_consts_FLOW_PREEMPT_TAIL_NS as u64 != PREEMPT_TAIL_NS {
639 bail!("tail bad");
640 }
641 if PREEMPT_TAIL_NS != 100_000 {
642 bail!("tail bounds bad");
643 }
644 if crate::bpf_intf::flow_consts_FLOW_VLAG_MAX_NS as u64 != 2_000_000 {
645 bail!("lag bound bad");
646 }
647 if crate::bpf_intf::flow_consts_FLOW_MAX_DSQS as u64 != 1042 {
648 bail!("dsq count bad");
649 }
650 if crate::bpf_intf::flow_consts_FLOW_DISPATCH_MAX_VISIT as u64 != 8 {
651 bail!("visit bound bad");
652 }
653 if crate::bpf_intf::flow_consts_FLOW_STEAL_MIN_PEERS as u64 != 4
654 || crate::bpf_intf::flow_consts_FLOW_STEAL_MAX_PEERS as u64 != 8
655 {
656 bail!("steal window bad");
657 }
658 if crate::bpf_intf::flow_consts_FLOW_BSF_MAX_PEERS as u64 != 4 {
659 bail!("bsf bound bad");
660 }
661 if crate::bpf_intf::flow_consts_FLOW_BSF_MAX_PEERS as u64
662 > crate::bpf_intf::flow_consts_FLOW_DISPATCH_MAX_VISIT as u64
663 {
664 bail!("bsf over visit bad");
665 }
666 if crate::bpf_intf::flow_consts_FLOW_OVERFLOW as u64 != 0x5A01 {
667 bail!("overflow id bad");
668 }
669 if crate::bpf_intf::flow_consts_FLOW_RED_EMAX_NS as u64 != RED_EMAX_NS {
670 bail!("red emax bad");
671 }
672 if crate::bpf_intf::flow_consts_FLOW_RED_TOL_NS as u64 != RED_TOL_NS {
673 bail!("red tol bad");
674 }
675 if crate::bpf_intf::flow_consts_FLOW_ADAPT_GROW_NS as u64 != ADAPT_GROW_NS {
676 bail!("adapt grow bad");
677 }
678 if crate::bpf_intf::flow_consts_FLOW_ADAPT_SHRINK_NS as u64 != ADAPT_SHRINK_NS {
679 bail!("adapt shrink bad");
680 }
681 if crate::bpf_intf::flow_consts_FLOW_ADAPT_PROP_MAX_NS as u64 != ADAPT_PROP_MAX_NS {
682 bail!("adapt prop max bad");
683 }
684 if crate::bpf_intf::flow_consts_FLOW_ADAPT_PROP_SHIFT as u64 != ADAPT_PROP_SHIFT as u64 {
685 bail!("adapt prop shift bad");
686 }
687 if ADAPT_PROP_MAX_NS != 1_000_000 {
688 bail!("prop max bounds bad");
689 }
690 if ADAPT_PROP_SHIFT != 3 {
691 bail!("prop shift bounds bad");
692 }
693 if ADAPT_PROP_MAX_NS > QUANTUM_NS {
694 bail!("prop max over ceiling bad");
695 }
696 if SLICE_LAT_NS != 100_000 {
697 bail!("slice lat bad");
698 }
699 if D_LAT_NS != 4_000_000 {
700 bail!("d lat bad");
701 }
702 if SLICE_LAT_NS <= SLICE_MIN_NS {
703 bail!("slice lat floor bad");
704 }
705 if SLICE_LAT_NS >= QUANTUM_NS {
706 bail!("slice lat over ceiling bad");
707 }
708 if D_LAT_NS >= PERIOD_NS {
709 bail!("d lat over period bad");
710 }
711 if SLICE_ULL_NS != 50_000 {
712 bail!("slice ull bad");
713 }
714 if SLICE_LL_NS != 100_000 {
715 bail!("slice ll bad");
716 }
717 if SLICE_MLL_NS != 500_000 {
718 bail!("slice mll bad");
719 }
720 if SLICE_NL_NS != 1_000_000 {
721 bail!("slice nl bad");
722 }
723 if SLICE_ENL_NS != 5_000_000 {
724 bail!("slice enl bad");
725 }
726 if SLICE_MAX_NS != 20_000_000 {
727 bail!("slice max bad");
728 }
729 if !(SLICE_MIN_NS < SLICE_ULL_NS
730 && SLICE_ULL_NS < SLICE_LL_NS
731 && SLICE_LL_NS < SLICE_MLL_NS
732 && SLICE_MLL_NS < SLICE_NL_NS
733 && SLICE_NL_NS < SLICE_ENL_NS
734 && SLICE_ENL_NS < SLICE_MAX_NS)
735 {
736 bail!("tier order bad");
737 }
738 if SLICE_LL_NS != SLICE_LAT_NS {
739 bail!("tier ll lat bad");
740 }
741 if SLICE_NL_NS != QUANTUM_NS {
742 bail!("tier nl quantum bad");
743 }
744 if PERIOD_NS != 4 * SLICE_MAX_NS {
745 bail!("period slice max bad");
746 }
747 if crate::bpf_intf::flow_consts_FLOW_SLICE_ULL_NS as u64 != SLICE_ULL_NS {
748 bail!("slice ull header bad");
749 }
750 if crate::bpf_intf::flow_consts_FLOW_SLICE_LL_NS as u64 != SLICE_LL_NS {
751 bail!("slice ll header bad");
752 }
753 if crate::bpf_intf::flow_consts_FLOW_SLICE_MLL_NS as u64 != SLICE_MLL_NS {
754 bail!("slice mll header bad");
755 }
756 if crate::bpf_intf::flow_consts_FLOW_SLICE_NL_NS as u64 != SLICE_NL_NS {
757 bail!("slice nl header bad");
758 }
759 if crate::bpf_intf::flow_consts_FLOW_SLICE_ENL_NS as u64 != SLICE_ENL_NS {
760 bail!("slice enl header bad");
761 }
762 if crate::bpf_intf::flow_consts_FLOW_SLICE_MAX_NS as u64 != SLICE_MAX_NS {
763 bail!("slice max header bad");
764 }
765 if PRED_UP_SHIFT != 3 {
766 bail!("pred up bad");
767 }
768 if PRED_DOWN_SHIFT != 4 {
769 bail!("pred down bad");
770 }
771 if PRED_DEV_UP_SHIFT != 2 {
772 bail!("pred dev up bad");
773 }
774 if PRED_DEV_DOWN_SHIFT != 3 {
775 bail!("pred dev down bad");
776 }
777 if PRED_DEV_MARGIN_SHIFT != 1 {
778 bail!("pred margin bad");
779 }
780 if ADAPT_DEADBAND_SHIFT != 5 {
781 bail!("deadband shift bad");
782 }
783 if ADAPT_DEADBAND_MIN_NS != 8_000 {
784 bail!("deadband floor bad");
785 }
786 if ADAPT_DAMP_SHIFT != 1 {
787 bail!("damp bad");
788 }
789 if crate::bpf_intf::flow_consts_FLOW_PRED_UP_SHIFT as u64 != PRED_UP_SHIFT as u64 {
790 bail!("pred up header bad");
791 }
792 if crate::bpf_intf::flow_consts_FLOW_PRED_DOWN_SHIFT as u64 != PRED_DOWN_SHIFT as u64 {
793 bail!("pred down header bad");
794 }
795 if crate::bpf_intf::flow_consts_FLOW_PRED_DEV_UP_SHIFT as u64 != PRED_DEV_UP_SHIFT as u64 {
796 bail!("pred dev up header bad");
797 }
798 if crate::bpf_intf::flow_consts_FLOW_PRED_DEV_DOWN_SHIFT as u64
799 != PRED_DEV_DOWN_SHIFT as u64
800 {
801 bail!("pred dev down header bad");
802 }
803 if crate::bpf_intf::flow_consts_FLOW_PRED_DEV_MARGIN_SHIFT as u64
804 != PRED_DEV_MARGIN_SHIFT as u64
805 {
806 bail!("pred margin header bad");
807 }
808 if crate::bpf_intf::flow_consts_FLOW_ADAPT_DEADBAND_SHIFT as u64
809 != ADAPT_DEADBAND_SHIFT as u64
810 {
811 bail!("deadband shift header bad");
812 }
813 if crate::bpf_intf::flow_consts_FLOW_ADAPT_DEADBAND_MIN_NS as u64 != ADAPT_DEADBAND_MIN_NS {
814 bail!("deadband floor header bad");
815 }
816 if crate::bpf_intf::flow_consts_FLOW_ADAPT_DAMP_SHIFT as u64 != ADAPT_DAMP_SHIFT as u64 {
817 bail!("damp header bad");
818 }
819 if crate::bpf_intf::flow_consts_FLOW_SLICE_LAT_NS as u64 != SLICE_LAT_NS {
820 bail!("slice lat header bad");
821 }
822 if LAT_SLEEP_MIN_NS != 20_000 {
823 bail!("lat sleep bad");
824 }
825 if LAT_SLEEP_MIN_NS < PRED_MIN_NS {
826 bail!("lat sleep floor bad");
827 }
828 if LAT_SLEEP_MIN_NS >= SLICE_LAT_NS {
829 bail!("lat sleep over slice bad");
830 }
831 if crate::bpf_intf::flow_consts_FLOW_LAT_SLEEP_MIN_NS as u64 != LAT_SLEEP_MIN_NS {
832 bail!("lat sleep header bad");
833 }
834 if crate::bpf_intf::flow_consts_FLOW_D_LAT_NS as u64 != D_LAT_NS {
835 bail!("d lat header bad");
836 }
837 if RED_TOL_NS != 64_000 {
838 bail!("red tol bad");
839 }
840 if RED_EMAX_NS != 256_000 {
841 bail!("red emax bad");
842 }
843 if std::mem::size_of::<crate::bpf_intf::flow_sched_stats>() != 136 {
844 bail!("stats size bad");
845 }
846 if std::mem::size_of::<crate::bpf_intf::flow_task_ctx>() != 72 {
847 bail!("task size bad");
848 }
849 Ok(())
850 }
851
852 pub fn describe(&self) -> String {
855 format!("quantum={}us", self.quantum_ns / 1000,)
856 }
857}
858
859#[cfg(test)]
862#[derive(Debug, Clone, Default)]
863pub struct ConfigBuilder {
864 quantum_ns: Option<u64>,
865}
866
867#[cfg(test)]
868impl ConfigBuilder {
869 pub fn quantum_ns(mut self, v: u64) -> Self {
871 self.quantum_ns = Some(v);
872 self
873 }
874 pub fn build(self) -> Result<Config> {
876 let d = Config::default();
877 let cfg = Config {
878 quantum_ns: self.quantum_ns.unwrap_or(d.quantum_ns),
879 };
880 cfg.validate()?;
881 Ok(cfg)
882 }
883}
884
885#[cfg(test)]
886mod tests {
887 use super::*;
888
889 #[test]
890 fn defaults_are_valid() {
891 Config::default().validate().unwrap();
892 }
893
894 #[test]
895 fn builder_defaults_match_config() {
896 let cfg = ConfigBuilder::default().build().unwrap();
897 assert_eq!(cfg, Config::default());
898 }
899
900 #[test]
901 fn rejects_bad_quantum() {
902 let a = ConfigBuilder::default().quantum_ns(1).build();
903 assert!(a.is_err());
904 let b = ConfigBuilder::default().quantum_ns(2_000_000).build();
905 assert!(b.is_err());
906 }
907
908 #[test]
909 fn describe_is_stable() {
911 let s = Config::default().describe();
912 assert!(s.contains("quantum=1000us"));
913 assert!(!s.contains("batch"));
914 }
915
916 #[test]
917 fn slice_bounds_match_intf_h() {
919 assert_eq!(SLICE_MIN_NS, 10_000);
920 assert_eq!(
921 crate::bpf_intf::flow_consts_FLOW_SLICE_MIN_NS as u64,
922 SLICE_MIN_NS
923 );
924 const _: () = assert!(SLICE_MIN_NS < QUANTUM_NS);
925 }
926
927 #[test]
928 fn defaults_match_intf_h() {
930 assert_eq!(
931 Config::default().quantum_ns,
932 crate::bpf_intf::flow_consts_FLOW_QUANTUM_NS as u64
933 );
934 assert_eq!(QUANTUM_NS, 1_000_000);
935 assert_eq!(PERIOD_NS, 80_000_000);
936 assert_eq!(SLICE_ULL_NS, 50_000);
937 assert_eq!(SLICE_LL_NS, 100_000);
938 assert_eq!(SLICE_MLL_NS, 500_000);
939 assert_eq!(SLICE_NL_NS, 1_000_000);
940 assert_eq!(SLICE_ENL_NS, 5_000_000);
941 assert_eq!(SLICE_MAX_NS, 20_000_000);
942 assert_eq!(PERIOD_NS, 4 * SLICE_MAX_NS);
943 assert_eq!(PRED_MIN_NS, 1);
944 assert_eq!(PRED_MAX_NS, 1_000_000_000);
945 assert_eq!(CAP_BASE, 1024);
946 assert_eq!(WEIGHT_MIN, 1);
947 assert_eq!(WEIGHT_BASE, 128);
948 assert_eq!(WEIGHT_MAX, 16_384);
949 assert_eq!(HINT_MAX, 8192);
950 assert_eq!(RED_EMAX_NS, 256_000);
951 assert_eq!(RED_TOL_NS, 64_000);
952 assert_eq!(ADAPT_GROW_NS, 64_000);
953 assert_eq!(ADAPT_SHRINK_NS, 128_000);
954 assert_eq!(crate::bpf_intf::flow_consts_FLOW_MAX_DSQS as u64, 1042);
955 assert_eq!(
956 crate::bpf_intf::flow_consts_FLOW_VLAG_MAX_NS as u64,
957 2_000_000
958 );
959 assert_eq!(crate::bpf_intf::flow_consts_FLOW_MACHINE as u64, 0x5A00);
960 assert_eq!(crate::bpf_intf::flow_consts_FLOW_OVERFLOW as u64, 0x5A01);
961 assert_eq!(
962 crate::bpf_intf::flow_consts_FLOW_DISPATCH_MAX_VISIT as u64,
963 8
964 );
965 assert_eq!(crate::bpf_intf::flow_consts_FLOW_BSF_MAX_PEERS as u64, 4);
966 assert_eq!(crate::bpf_intf::flow_consts_FLOW_LOCAL_BASE as u64, 0x5100);
967 assert_eq!(crate::bpf_intf::flow_consts_FLOW_NODE_BASE as u64, 0x5900);
968 assert_eq!(crate::bpf_intf::flow_consts_FLOW_HINT_MAX as u64, HINT_MAX);
969 assert_eq!(
970 crate::bpf_intf::flow_consts_FLOW_PRED_MIN_NS as u64,
971 PRED_MIN_NS
972 );
973 assert_eq!(
974 crate::bpf_intf::flow_consts_FLOW_PRED_MAX_NS as u64,
975 PRED_MAX_NS
976 );
977 assert_eq!(
978 crate::bpf_intf::flow_consts_FLOW_CAP_BASE as u64,
979 CAP_BASE as u64
980 );
981 assert_eq!(
982 crate::bpf_intf::flow_consts_FLOW_WEIGHT_BASE as u64,
983 WEIGHT_BASE as u64
984 );
985 assert_eq!(
986 crate::bpf_intf::flow_consts_FLOW_WEIGHT_MAX as u64,
987 WEIGHT_MAX as u64
988 );
989 assert_eq!(
990 crate::bpf_intf::flow_consts_FLOW_PREEMPT_MARGIN_NS as u64,
991 PREEMPT_MARGIN_NS
992 );
993 assert_eq!(PREEMPT_MARGIN_NS, 100_000);
994 assert_eq!(
995 crate::bpf_intf::flow_consts_FLOW_PREEMPT_TAIL_NS as u64,
996 PREEMPT_TAIL_NS
997 );
998 assert_eq!(PREEMPT_TAIL_NS, 100_000);
999 assert_eq!(
1000 crate::bpf_intf::flow_consts_FLOW_RED_EMAX_NS as u64,
1001 RED_EMAX_NS
1002 );
1003 assert_eq!(
1004 crate::bpf_intf::flow_consts_FLOW_RED_TOL_NS as u64,
1005 RED_TOL_NS
1006 );
1007 assert_eq!(
1008 crate::bpf_intf::flow_consts_FLOW_ADAPT_GROW_NS as u64,
1009 ADAPT_GROW_NS
1010 );
1011 assert_eq!(
1012 crate::bpf_intf::flow_consts_FLOW_ADAPT_SHRINK_NS as u64,
1013 ADAPT_SHRINK_NS
1014 );
1015 assert_eq!(
1016 crate::bpf_intf::flow_consts_FLOW_ADAPT_PROP_MAX_NS as u64,
1017 ADAPT_PROP_MAX_NS
1018 );
1019 assert_eq!(
1020 crate::bpf_intf::flow_consts_FLOW_ADAPT_PROP_SHIFT as u64,
1021 ADAPT_PROP_SHIFT as u64
1022 );
1023 assert_eq!(ADAPT_PROP_MAX_NS, 1_000_000);
1024 assert_eq!(ADAPT_PROP_SHIFT, 3);
1025 assert_eq!(ADAPT_DEADBAND_SHIFT, 5);
1026 assert_eq!(ADAPT_DEADBAND_MIN_NS, 8_000);
1027 assert_eq!(ADAPT_DAMP_SHIFT, 1);
1028 assert_eq!(PRED_UP_SHIFT, 3);
1029 assert_eq!(PRED_DOWN_SHIFT, 4);
1030 assert_eq!(PRED_DEV_UP_SHIFT, 2);
1031 assert_eq!(PRED_DEV_DOWN_SHIFT, 3);
1032 assert_eq!(PRED_DEV_MARGIN_SHIFT, 1);
1033 assert_eq!(SLICE_LAT_NS, 100_000);
1034 assert_eq!(LAT_SLEEP_MIN_NS, 20_000);
1035 assert_eq!(D_LAT_NS, 4_000_000);
1036 const _: () = assert!(D_LAT_NS < PERIOD_NS);
1037 assert_eq!(
1038 crate::bpf_intf::flow_consts_FLOW_SLICE_ULL_NS as u64,
1039 SLICE_ULL_NS
1040 );
1041 assert_eq!(
1042 crate::bpf_intf::flow_consts_FLOW_SLICE_LL_NS as u64,
1043 SLICE_LL_NS
1044 );
1045 assert_eq!(
1046 crate::bpf_intf::flow_consts_FLOW_SLICE_MLL_NS as u64,
1047 SLICE_MLL_NS
1048 );
1049 assert_eq!(
1050 crate::bpf_intf::flow_consts_FLOW_SLICE_NL_NS as u64,
1051 SLICE_NL_NS
1052 );
1053 assert_eq!(
1054 crate::bpf_intf::flow_consts_FLOW_SLICE_ENL_NS as u64,
1055 SLICE_ENL_NS
1056 );
1057 assert_eq!(
1058 crate::bpf_intf::flow_consts_FLOW_SLICE_MAX_NS as u64,
1059 SLICE_MAX_NS
1060 );
1061 assert_eq!(
1062 crate::bpf_intf::flow_consts_FLOW_PRED_UP_SHIFT as u64,
1063 PRED_UP_SHIFT as u64
1064 );
1065 assert_eq!(
1066 crate::bpf_intf::flow_consts_FLOW_PRED_DOWN_SHIFT as u64,
1067 PRED_DOWN_SHIFT as u64
1068 );
1069 assert_eq!(
1070 crate::bpf_intf::flow_consts_FLOW_PRED_DEV_UP_SHIFT as u64,
1071 PRED_DEV_UP_SHIFT as u64
1072 );
1073 assert_eq!(
1074 crate::bpf_intf::flow_consts_FLOW_PRED_DEV_DOWN_SHIFT as u64,
1075 PRED_DEV_DOWN_SHIFT as u64
1076 );
1077 assert_eq!(
1078 crate::bpf_intf::flow_consts_FLOW_PRED_DEV_MARGIN_SHIFT as u64,
1079 PRED_DEV_MARGIN_SHIFT as u64
1080 );
1081 assert_eq!(
1082 crate::bpf_intf::flow_consts_FLOW_ADAPT_DEADBAND_SHIFT as u64,
1083 ADAPT_DEADBAND_SHIFT as u64
1084 );
1085 assert_eq!(
1086 crate::bpf_intf::flow_consts_FLOW_ADAPT_DEADBAND_MIN_NS as u64,
1087 ADAPT_DEADBAND_MIN_NS
1088 );
1089 assert_eq!(
1090 crate::bpf_intf::flow_consts_FLOW_ADAPT_DAMP_SHIFT as u64,
1091 ADAPT_DAMP_SHIFT as u64
1092 );
1093 assert_eq!(
1094 crate::bpf_intf::flow_consts_FLOW_SLICE_LAT_NS as u64,
1095 SLICE_LAT_NS
1096 );
1097 assert_eq!(
1098 crate::bpf_intf::flow_consts_FLOW_LAT_SLEEP_MIN_NS as u64,
1099 LAT_SLEEP_MIN_NS
1100 );
1101 assert_eq!(crate::bpf_intf::flow_consts_FLOW_D_LAT_NS as u64, D_LAT_NS);
1102 assert_eq!(
1103 std::mem::size_of::<crate::bpf_intf::flow_sched_stats>(),
1104 136
1105 );
1106 assert_eq!(std::mem::size_of::<crate::bpf_intf::flow_task_ctx>(), 72);
1107 }
1108
1109 #[test]
1110 fn prop_shrink_grow_matches_intf_h() {
1112 assert_eq!(adapt_deadband(1_000_000), 31_250);
1114 assert_eq!(adapt_deadband(10_000), 8_000);
1115 assert_eq!(adapt_prop(1_000_000, 8_000, 0), 1_000_000);
1116 assert_eq!(adapt_prop(500_000, 0, 1_600), 500_000);
1117 assert_eq!(adapt_prop(1_000_000, 64_000, 0), 996_000);
1119 assert_eq!(adapt_prop(500_000, 0, 64_000), 504_000);
1121 assert_eq!(adapt_prop(1_000_000, 10_000_000, 0), 750_000);
1123 assert_eq!(adapt_prop(1_000_000, 2_000_000, 0), 875_000);
1125 assert_eq!(adapt_prop(900_000, 0, 10_000_000), 1_000_000);
1127 assert_eq!(adapt_prop(10_000, 80_000, 0), 10_000);
1129 assert_eq!(adapt_prop(500_000, 0, 0), 500_000);
1131 assert_eq!(adapt_prop(0, 8_000, 0), 1_000_000);
1133 assert_eq!(adapt_prop(500_000, 7, 0), 500_000);
1135 assert_eq!(adapt_prop(500_000, 0, 200), 500_000);
1137 assert_eq!(
1139 adapt_prop(500_000, 64_000, 1_600),
1140 adapt_prop(500_000, 64_000, 0)
1141 );
1142 }
1143
1144 #[test]
1145 fn pred_up_down_margin_matches_intf_h() {
1147 assert_eq!(pred_avg(0, 100_000), 100_000);
1149 assert_eq!(pred_avg(100_000, 180_000), 110_000);
1151 assert_eq!(pred_avg(100_000, 20_000), 95_000);
1153 assert_eq!(pred_dev(10_000, 110_000, 180_000), 25_000);
1155 assert_eq!(pred_period(100_000, 10_000), 115_000);
1157 assert_eq!(pred_period(0, 0), PERIOD_NS);
1158 assert_eq!(
1160 crate::bpf_intf::flow_consts_FLOW_PRED_UP_SHIFT as u64,
1161 PRED_UP_SHIFT as u64
1162 );
1163 assert_eq!(
1164 crate::bpf_intf::flow_consts_FLOW_PRED_DOWN_SHIFT as u64,
1165 PRED_DOWN_SHIFT as u64
1166 );
1167 assert_eq!(
1168 crate::bpf_intf::flow_consts_FLOW_ADAPT_DEADBAND_MIN_NS as u64,
1169 ADAPT_DEADBAND_MIN_NS
1170 );
1171 }
1172
1173 #[test]
1174 fn red_uses_pre_clamp_not_post() {
1176 let pre = 1_000_000;
1178 let post = slice_lat_clamp(pre, true);
1179 assert_eq!(post, 100_000);
1180 assert_eq!(red_cost(0, pre), 1_000_000);
1182 assert_eq!(red_cost(0, post), 100_000);
1183 assert!(red_cost(0, pre) > red_cost(0, post));
1184 assert_eq!(red_cost(900_000, 1_000_000), 900_000);
1186 assert_eq!(red_cost(0, 0), QUANTUM_NS);
1187 assert_eq!(red_cost(1, 1_000_000), SLICE_MIN_NS);
1188 assert_eq!(red_cost(5_000_000, 0), 5_000_000);
1190 assert_eq!(red_cost(0, 5_000_000), 5_000_000);
1191 assert_eq!(red_cost(30_000_000, 0), SLICE_MAX_NS);
1192 assert_eq!(red_cost(0, 30_000_000), SLICE_MAX_NS);
1193 }
1194
1195 #[test]
1196 fn lat_bounds_match_intf_h() {
1198 const _: () = assert!(SLICE_LAT_NS > SLICE_MIN_NS);
1199 const _: () = assert!(SLICE_LAT_NS < QUANTUM_NS);
1200 const _: () = assert!(LAT_SLEEP_MIN_NS < SLICE_LAT_NS);
1201 const _: () = assert!(LAT_SLEEP_MIN_NS >= PRED_MIN_NS);
1202 assert_eq!(SLICE_LAT_NS, 100_000);
1203 assert_eq!(LAT_SLEEP_MIN_NS, 20_000);
1204 assert_eq!(D_LAT_NS, 4_000_000);
1205 const _: () = assert!(D_LAT_NS < PERIOD_NS);
1206 assert_eq!(PERIOD_NS, 80_000_000);
1207 assert_eq!(slice_lat_clamp(1_000_000, false), 1_000_000);
1209 assert_eq!(slice_lat_clamp(1_000_000, true), 100_000);
1211 assert_eq!(slice_lat_clamp(100_000, true), 100_000);
1212 assert_eq!(lat_deadline(0, 100_000), 4_000_000);
1214 assert_eq!(lat_deadline(1_000, 1_000_000), 1_000 + 4_000_000);
1215 assert_eq!(lat_deadline(0, 5_000_000), 5_100_000);
1216 assert_eq!(lat_deadline(u64::MAX - 1_000, 100_000), u64::MAX);
1218 }
1219
1220 #[test]
1221 fn lat_ext_veto_matches_intf_h() {
1223 assert!(!lat_crit_ext(0, 0, 0, 0, 0, 0));
1225 assert!(!lat_crit(0, 0));
1226 assert!(!is_lat(0, 0, 0, 0, 0, 0));
1227 assert!(lat_crit_ext(100_000, 10_000, 0, 1_000_000, 0, 128));
1229 assert!(lat_crit(100_000, 10_000));
1230 assert!(is_lat(100_000, 10_000, 0, 1_000_000, 0, 128));
1231 assert!(!is_lat(900_000, 200_000, 0, 1_000_000, 0, 128));
1233 assert!(!is_lat(100_000, 10_000, 2000, 1_000_000, 0, 128));
1234 assert!(!is_lat(100_000, 10_000, 0, 10_000, 0, 128));
1235 assert!(is_lat(100_000, 10_000, 0, 30_000, 0, 128));
1236 assert!(is_lat(100_000, 10_000, 0, 50_000, 0, 128));
1237 assert!(is_lat(100_000, 10_000, 0, 150_000, 0, 128));
1238 assert!(is_lat(100_000, 10_000, 0, 0, 0, 128));
1239 assert!(!lat_crit_ext(900_000, 200_000, 0, 1_000_000, 0, 128));
1241 assert!(!lat_crit_ext(100_000, 10_000, 2000, 1_000_000, 0, 128));
1243 assert!(lat_crit_ext(100_000, 10_000, 500, 1_000_000, 0, 128));
1245 assert!(!lat_crit_ext(100_000, 10_000, 0, 10_000, 0, 128));
1248 assert!(lat_crit_ext(100_000, 10_000, 0, 30_000, 0, 128));
1249 assert!(lat_crit_ext(100_000, 10_000, 0, 50_000, 0, 128));
1250 assert!(lat_crit_ext(100_000, 10_000, 0, 150_000, 0, 128));
1251 assert!(lat_crit_ext(100_000, 10_000, 0, 0, 0, 128));
1252 assert!(!lat_crit_ext(100_000, 10_000, 0, 1_000_000, 100, 128));
1254 assert!(lat_crit_ext(100_000, 10_000, 0, 1_000_000, 1024, 128));
1255 assert!(!lat_crit_ext(100_000, 10_000, 0, 1_000_000, 0, 32));
1257 assert!(lat_crit_ext(100_000, 10_000, 0, 1_000_000, 0, 1024));
1258 }
1259
1260 #[test]
1261 fn sleep_isolates_gap_minus_burst() {
1263 assert_eq!(sleep_ns(1_000, 1_001_000, 200_000), 800_000);
1265 assert_eq!(sleep_ns(1_000, 1_001_000, 2_000_000), 0);
1267 assert_eq!(sleep_ns(1_000, 1_001_000, 0), 1_000_000);
1269 assert_eq!(sleep_ns(0, 1_000, 100_000), 0);
1271 assert_eq!(sleep_ns(1_000, 1_000, 100_000), 0);
1272 assert_eq!(sleep_ns(u64::MAX - 1_000, 500, 0), 1_501);
1274 assert_eq!(sleep_ns(u64::MAX - 1_000, 500, 1_000), 501);
1276 assert_eq!(sleep_ns(u64::MAX - 1_000, 500, 5_000), 0);
1277 }
1278
1279 #[test]
1280 fn resume_uses_cur_not_quantum() {
1282 assert_eq!(
1284 slice_resume(1_000, 1_000 + 100_000, false, 500_000),
1285 400_000
1286 );
1287 assert_eq!(slice_resume(1_000, 1_000 + 100_000, true, 500_000), 100_000);
1289 assert_eq!(slice_resume(1_000, 1_000 + 100_000, true, 250_000), 100_000);
1291 assert_eq!(slice_resume(0, 1_000, false, 500_000), 500_000);
1293 assert_eq!(slice_resume(0, 1_000, true, 500_000), 500_000);
1294 assert_eq!(slice_resume(1_000, 1_000 + 600_000, false, 500_000), 10_000);
1296 assert_eq!(slice_resume(u64::MAX - 10, 100, false, 500_000), 500_000);
1298 }
1299
1300 #[test]
1301 fn preempt_needs_lat_to_batch_only() {
1303 assert!(preempt_ok(
1305 1_000_000, 2_000_000, 500_000, 500_000, 1_000_000, true, false
1306 ));
1307 assert!(!preempt_ok(
1309 1_000_000, 2_000_000, 500_000, 500_000, 1_000_000, true, true
1310 ));
1311 assert!(!preempt_ok(
1312 1_000_000, 2_000_000, 500_000, 500_000, 1_000_000, false, false
1313 ));
1314 assert!(!preempt_ok(
1315 1_000_000, 2_000_000, 500_000, 500_000, 1_000_000, false, true
1316 ));
1317 assert!(!preempt_ok(
1319 1_950_000, 2_000_000, 500_000, 500_000, 1_000_000, true, false
1320 ));
1321 assert!(!preempt_ok(
1323 1_000_000, 2_000_000, 1_450_000, 500_000, 1_000_000, true, false
1324 ));
1325 assert!(preempt_ok(
1327 1_000_000, 2_000_000, 500_000, 500_000, 0, true, false
1328 ));
1329 assert!(!preempt_ok(
1331 1_000_000, 2_000_000, 500_000, 500_000, 50_000, true, false
1332 ));
1333 }
1334
1335 #[test]
1336 fn miss_skip_period_are_intentional() {
1338 assert!(!is_lat(0, 0, 0, 0, 0, 0));
1340 assert!(!preempt_ok(
1342 1_000_000, 2_000_000, 500_000, 500_000, 1_000_000, true, true
1343 ));
1344 assert_eq!(PERIOD_NS, 80_000_000);
1346 assert_eq!(PERIOD_NS, 4 * SLICE_MAX_NS);
1347 const _: () = assert!(D_LAT_NS < PERIOD_NS);
1348 }
1349
1350 #[test]
1351 fn classify_tier_matches_intf_h() {
1353 const _: () = assert!(SLICE_ULL_NS < SLICE_MLL_NS);
1354 const _: () = assert!(SLICE_MLL_NS < SLICE_NL_NS);
1355 const _: () = assert!(SLICE_NL_NS < SLICE_ENL_NS);
1356 const _: () = assert!(SLICE_ENL_NS < SLICE_MAX_NS);
1357 assert_eq!(SLICE_ULL_NS, 50_000);
1358 assert_eq!(SLICE_LL_NS, 100_000);
1359 assert_eq!(SLICE_MLL_NS, 500_000);
1360 assert_eq!(SLICE_NL_NS, 1_000_000);
1361 assert_eq!(SLICE_ENL_NS, 5_000_000);
1362 assert_eq!(SLICE_MAX_NS, 20_000_000);
1363 assert_eq!(classify_tier(20_000_000, 0, true), 50_000);
1365 assert_eq!(classify_tier(30_000, 10_000, false), 50_000);
1367 assert_eq!(classify_tier(50_000, 0, false), 50_000);
1368 assert_eq!(classify_tier(60_000, 10_000, false), 100_000);
1370 assert_eq!(classify_tier(300_000, 100_000, false), 500_000);
1371 assert_eq!(classify_tier(600_000, 100_000, false), 1_000_000);
1372 assert_eq!(classify_tier(3_000_000, 1_000_000, false), 5_000_000);
1373 assert_eq!(classify_tier(6_000_000, 0, false), 20_000_000);
1375 assert_eq!(classify_tier(u64::MAX - 10, 20, false), 20_000_000);
1376 assert_eq!(
1378 crate::bpf_intf::flow_consts_FLOW_SLICE_ULL_NS as u64,
1379 SLICE_ULL_NS
1380 );
1381 assert_eq!(
1382 crate::bpf_intf::flow_consts_FLOW_SLICE_MAX_NS as u64,
1383 SLICE_MAX_NS
1384 );
1385 assert_eq!(
1386 crate::bpf_intf::flow_consts_FLOW_PERIOD_NS as u64,
1387 PERIOD_NS
1388 );
1389 let bulk = classify_tier(6_000_000, 0, false);
1391 assert_eq!(bulk, SLICE_MAX_NS as u32);
1392 assert_eq!(bulk.min(QUANTUM_NS as u32), QUANTUM_NS as u32);
1393 let audio = classify_tier(30_000, 10_000, false);
1394 assert_eq!(audio, SLICE_ULL_NS as u32);
1395 assert!(audio < QUANTUM_NS as u32);
1396 }
1397
1398 #[test]
1399 fn abi_sizes_stay_pinned() {
1401 assert_eq!(std::mem::size_of::<crate::bpf_intf::flow_task_ctx>(), 72);
1402 assert_eq!(
1403 std::mem::size_of::<crate::bpf_intf::flow_sched_stats>(),
1404 136
1405 );
1406 assert_eq!(RED_EMAX_NS, 256_000);
1407 assert_eq!(
1408 crate::bpf_intf::flow_consts_FLOW_RED_EMAX_NS as u64,
1409 RED_EMAX_NS
1410 );
1411 assert_eq!(RED_TOL_NS, 64_000);
1412 assert_eq!(
1413 crate::bpf_intf::flow_consts_FLOW_RED_TOL_NS as u64,
1414 RED_TOL_NS
1415 );
1416 }
1417
1418 #[test]
1419 #[allow(clippy::assertions_on_constants)]
1421 fn fixed_red_matches_intf_h() {
1422 assert_eq!(RED_TOL_NS, 64_000);
1423 assert_eq!(RED_EMAX_NS, 256_000);
1424 }
1425}