docs: record v1.1.0 low-RAM TCP backpressure design

This commit is contained in:
2026-05-08 05:53:10 +08:00
parent 2679db4129
commit 47dc46f2a7
2 changed files with 68 additions and 6 deletions
+6 -6
View File
@@ -17,7 +17,7 @@
778 0 0 2 0 0 ip4.o 778 0 0 2 0 0 ip4.o
46 0 4 0 0 0 ip4_addr.o 46 0 4 0 0 0 ip4_addr.o
44 0 0 0 12 0 iwdg.o 44 0 0 0 12 0 iwdg.o
2842 0 185 12 272 0 main.o 3212 0 185 12 272 0 main.o
828 0 0 12 4115 0 mem.o 828 0 0 12 4115 0 mem.o
196 0 244 32 6464 0 memp.o 196 0 244 32 6464 0 memp.o
582 0 0 12 0 0 netif.o 582 0 0 12 0 0 netif.o
@@ -43,13 +43,13 @@
490 0 0 0 0 0 stm32f1xx_it.o 490 0 0 0 0 0 stm32f1xx_it.o
2 0 24 4 0 0 system_stm32f1xx.o 2 0 24 4 0 0 system_stm32f1xx.o
3474 0 193 32 0 0 tcp.o 3474 0 193 32 0 0 tcp.o
1232 0 0 0 1120 0 tcp_client.o 1556 0 0 0 1072 0 tcp_client.o
3684 0 0 36 20 0 tcp_in.o 3684 0 0 36 20 0 tcp_in.o
3862 0 0 0 0 0 tcp_out.o 3862 0 0 0 0 0 tcp_out.o
986 0 0 0 1104 0 tcp_server.o 1346 0 0 0 1048 0 tcp_server.o
164 0 0 0 72 0 tim.o 164 0 0 0 72 0 tim.o
374 0 16 12 0 0 timeouts.o 374 0 16 12 0 0 timeouts.o
1538 0 0 0 2936 0 uart_trans.o 1590 0 0 0 2936 0 uart_trans.o
816 0 0 0 624 0 usart.o 816 0 0 0 624 0 usart.o
Object Totals Object Totals
@@ -57,8 +57,8 @@ Memory Map of the image
Load Region LR_IROM1 Load Region LR_IROM1
Execution Region ER_IROM1 (Exec base: 0x08000000, Size: 0x0000D72C, Max: 0x00010000, END) Execution Region ER_IROM1 (Exec base: 0x08000000, Size: 0x0000DB7C, Max: 0x00010000, END)
Execution Region RW_IRAM1 (Exec base: 0x20000000, Size: 0x00005000, Max: 0x00005000, END) Execution Region RW_IRAM1 (Exec base: 0x20000000, Size: 0x00004F98, Max: 0x00005000, END)
Image component sizes Image component sizes
+62
View File
@@ -184,6 +184,68 @@ EN,LPORT,RIP,RPORT,UART
2. 统一受 `LINK[idx]` 配置驱动 2. 统一受 `LINK[idx]` 配置驱动
3. 由调度层决定实例与 UART 的数据交换路径 3. 由调度层决定实例与 UART 的数据交换路径
### 6.4 `v1.1.0` 低 RAM TCP 背压修复
`v1.1.0` 起,`TCP -> UART` 路径补充如下实现约束,用于解决“TCP 接收过快、UART 发送过慢时本地缓存被冲垮”的问题,同时尽量不新增静态 RAM:
1. 继续复用 `tcp_server` / `tcp_client` 现有 `RX ring`,不为每个连接新增独立的大块 pending payload 缓冲。
2. `tcp_server_on_recv()` / `tcp_client_on_recv()` 不再在回调内立即 `tcp_recved()`
3. lwIP 交来的 `pbuf` 在回调中通过 `pbuf_ref()` 转为应用持有,再释放回调上下文的原始引用;后续由应用在主循环中继续把数据泵入 `RX ring`,最终在消费完成后释放。
4.`RX ring` 暂时装不下时,剩余数据保留在 `hold_pbuf + hold_offset` 中,等待主循环下一轮继续搬运。
5. 只有当数据真正从 `TCP RX ring``drop` 掉,也就是已经被下游 `UART` 接收进入发送路径时,才调用 `tcp_recved()` 释放 TCP 接收窗口。
这样做的效果是:
1. `UART` 慢时,TCP 窗口不会继续无条件放大。
2. 对端发送速度会被 lwIP 接收窗口自然压制。
3. 修复点建立在已有 ring 与主循环调度之上,不引入 `FreeRTOS` 或新的大块静态缓存。
#### RAW 与 MUX 的分流规则
`v1.1.0` 中,`TCP` 侧拿到的都是纯 payload,因此 `TCP` 背压逻辑在 `RAW``MUX` 两种模式下共用到 `UART commit` 之前:
1. `RAW` 模式:
- 主循环先查看 `uart_trans_tx_free()`
- 再按 `min(tcp_available, tx_free, APP_TCP_TO_UART_CHUNK_SIZE)` 从 TCP ring `peek`
- `uart_trans_write()` 实际写入多少,就 `drop + tcp_recved` 多少
2. `MUX` 模式:
- `TCP` payload 本身不带帧头尾
- 只有当 `UART TX free >= payload_len + 6` 时,才在栈上临时编码一帧并一次性写入 `UART TX ring`
- 只有整帧成功入队后,才按原始 payload 长度执行 `drop + tcp_recved`
该设计保证:
1. `RAW` 模式允许流式逐步提交
2. `MUX` 模式保持“单个 UART 输出帧必须完整入队”的语义
3. `TCP` 接收窗口始终以真实下游消费进度为准,而不是以“回调里已经 memcpy 到本地”作为提交点
#### RAM 与 chunk 策略
为给新增的 `hold_pbuf / hold_offset` 状态字段让位,并进一步降低单轮转发压力,`v1.1.0` 同步采用以下策略:
1. 新增 `APP_TCP_TO_UART_CHUNK_SIZE = 128`
2. `TCP_SERVER_RX_BUFFER_SIZE``512` 调整为 `480`
3. `TCP_CLIENT_RX_BUFFER_SIZE``512` 调整为 `480`
设计意图:
1. 利用更小的单次转发块提升主循环调度颗粒度
2.`MUX` 模式下 `payload + 6` 更容易完整进入 `UART TX ring`
3. 在静态 RAM 已接近上限时,为少量新状态字段回收空间
#### 构建基线
`v1.1.0``MDK-ARM/TCP2UART.uvprojx``TCP2UART` Target 为构建验收基线。
当前一次通过的参考结果:
1. `errors = 0`
2. `warnings = 0`
3. `flash_bytes = 56544`
4. `ram_bytes = 20376`
该结果说明修复后工程仍满足 `STM32F103R8T6``20KB RAM` 上限,但余量已经很小;后续若继续增加功能,应优先考虑复用现有缓冲与状态,而不是增加新的静态大数组。
## 七、主循环实现方向 ## 七、主循环实现方向
主循环仍保持裸机轮询风格: 主循环仍保持裸机轮询风格: