Skip to main content

atmos/kernel/net/
tx_queue.rs

1//! L2: NIC 送信キューの容量判定(純粋ロジック)。
2//!
3//! ## なぜ必要か
4//! 【2026-07-30 実測】背景写真(3.7MB)の取得中に
5//! ```text
6//! [NET][DIAG] nic tx queue FULL (size=8) dropped_total=2192
7//! ```
8//! と、**送信パケットを 2192 個も捨てていた**。
9//! 送信キューは 8 エントリしかなく、USB の送信が追いつかないと溢れる。
10//!
11//! 捨てられた送信は上位から見ると「送ったのに応答が来ない」に見え、
12//! DNS タイムアウト・TCP 接続タイムアウト・取得の停止として現れる。
13//! **上層では原因が分からない典型**なので、容量判定を純粋関数にして
14//! 単体試験で固定する。
15//!
16//! グローバル状態にもハードウェアにも依存しない。
17
18/// 投入結果。
19#[derive(Debug, PartialEq, Eq, Clone, Copy)]
20pub enum EnqueueOutcome {
21    /// 空きスロットへ格納した。
22    Stored { slot: usize },
23    /// 満杯。**捨てた**(上位は再送で回復する必要がある)。
24    Dropped,
25}
26
27/// 空きスロットを探す。`is_free(i)` が真のスロットのうち最小の添字を返す。
28///
29/// 満杯なら `Dropped`。**満杯を黙って上書きしない**
30/// (送信待ちのパケットを壊すと、相手から見て途中が欠けたストリームになる)。
31pub fn find_slot(len: usize, is_free: impl Fn(usize) -> bool) -> EnqueueOutcome {
32    for i in 0..len {
33        if is_free(i) {
34            return EnqueueOutcome::Stored { slot: i };
35        }
36    }
37    EnqueueOutcome::Dropped
38}
39
40/// キューの使用率(0〜100%)。監視・診断用。
41///
42/// `len` が 0 は不正(ゼロ除算)なので、黙って戻らず
43/// エラーログを出して `None` を返す。
44pub fn utilization_percent(used: usize, len: usize) -> Option<u32> {
45    if len == 0 {
46        crate::error!("[NET][TXQ] キュー長が 0 です");
47        return None;
48    }
49    if used > len {
50        crate::error!("[NET][TXQ] 使用数がキュー長を超えています used={} len={}", used, len);
51        return None;
52    }
53    Some(((used * 100) / len) as u32)
54}
55
56/// この使用率なら送信元へ背圧をかけるべきか。
57///
58/// 満杯になってから捨てるのではなく、**手前で送信を絞る**方が
59/// パケットロスによる再送より安上がり。
60pub const BACKPRESSURE_THRESHOLD_PERCENT: u32 = 75;
61
62pub fn should_apply_backpressure(used: usize, len: usize) -> bool {
63    match utilization_percent(used, len) {
64        Some(p) => p >= BACKPRESSURE_THRESHOLD_PERCENT,
65        // 判定不能なら安全側=背圧をかけない(送信を止めて通信不能にしない)。
66        None => false,
67    }
68}