From 6d8c994dde0e61f838272fc9fe07f27bcae992e2 Mon Sep 17 00:00:00 2001 From: newbie <1028933249@qq.com> Date: Sun, 9 Aug 2026 15:42:47 +0000 Subject: [PATCH] =?UTF-8?q?init:=20DPDK=20=E2=86=92=20DPU=20learning=20blo?= =?UTF-8?q?g=20(VitePress)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - VitePress static site for rendering all markdown documents - Auto-discovers .md files from dpdk-roadmap-v3 and dpdk-study-kb - Features: full-text search, dark mode, responsive design - Pages: roadmap, notes (Phase 0-3), product docs, knowledge base Co-Authored-By: Claude --- .gitignore | 4 + .vitepress/config.mts | 82 + .vitepress/theme/custom.css | 97 + .vitepress/theme/index.ts | 4 + index.md | 43 + kb/2026-08.md | 2 + kb/index.md | 43 + notes/index.md | 39 + notes/phase0/day01-pcie-topology.md | 127 ++ notes/phase0/day02-kernel-rx-path.md | 104 + notes/phase0/day03-kernel-memory.md | 147 ++ notes/phase0/day04-rcu-lockfree.md | 135 ++ notes/week1/day001-pcie-topology.md | 251 ++ package-lock.json | 2538 +++++++++++++++++++++ package.json | 17 + products/home-gateway.md | 349 +++ products/index.md | 23 + products/unos-design.md | 1735 ++++++++++++++ products/unos-method.md | 359 +++ roadmap.md | 3163 ++++++++++++++++++++++++++ 20 files changed, 9262 insertions(+) create mode 100644 .gitignore create mode 100644 .vitepress/config.mts create mode 100644 .vitepress/theme/custom.css create mode 100644 .vitepress/theme/index.ts create mode 100644 index.md create mode 100755 kb/2026-08.md create mode 100755 kb/index.md create mode 100644 notes/index.md create mode 100755 notes/phase0/day01-pcie-topology.md create mode 100755 notes/phase0/day02-kernel-rx-path.md create mode 100755 notes/phase0/day03-kernel-memory.md create mode 100755 notes/phase0/day04-rcu-lockfree.md create mode 100755 notes/week1/day001-pcie-topology.md create mode 100644 package-lock.json create mode 100644 package.json create mode 100755 products/home-gateway.md create mode 100644 products/index.md create mode 100755 products/unos-design.md create mode 100755 products/unos-method.md create mode 100755 roadmap.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a8d4bef --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +.vitepress/cache/ +.vitepress/dist/ diff --git a/.vitepress/config.mts b/.vitepress/config.mts new file mode 100644 index 0000000..bcc3c5c --- /dev/null +++ b/.vitepress/config.mts @@ -0,0 +1,82 @@ +import { defineConfig } from 'vitepress' + +export default defineConfig({ + title: 'DPDK → DPU 学习笔记', + description: 'DPDK learning roadmap, uNOS design, home gateway product specs, and knowledge base', + lang: 'zh-CN', + base: '/', + + themeConfig: { + nav: [ + { text: '首页', link: '/' }, + { text: '学习路线', link: '/roadmap' }, + { text: '学习笔记', link: '/notes/' }, + { text: '知识库', link: '/kb' }, + { text: '产品设计', link: '/products' }, + ], + + sidebar: { + '/notes/': [ + { + text: 'Phase 0 — 基础', + items: [ + { text: 'Day01: PCIe 拓扑', link: '/notes/phase0/day01-pcie-topology' }, + { text: 'Day02: 内核收包路径', link: '/notes/phase0/day02-kernel-rx-path' }, + { text: 'Day03: 内核内存管理', link: '/notes/phase0/day03-kernel-memory' }, + { text: 'Day04: RCU & Lock-free', link: '/notes/phase0/day04-rcu-lockfree' }, + ] + }, + { + text: 'Week 1 (Jul 21+)', + items: [ + { text: 'Day001: PCIe 拓扑深度分析', link: '/notes/week1/day001-pcie-topology' }, + ] + } + ], + '/products/': [ + { + text: '产品文档', + items: [ + { text: 'DPDK Home Gateway 产品方案', link: '/products/home-gateway' }, + { text: 'uNOS 设计规格', link: '/products/unos-design' }, + { text: 'uNOS 学习方法论', link: '/products/unos-method' }, + ] + } + ], + '/kb/': [ + { + text: '知识库', + items: [ + { text: '索引', link: '/kb/' }, + { text: '2026-08 更新', link: '/kb/2026-08' }, + ] + } + ], + }, + + socialLinks: [ + { icon: 'github', link: 'http://localhost:3000/wxj/dpdk-roadmap-v3' } + ], + + search: { + provider: 'local', + options: { + translations: { + button: { buttonText: '搜索文档' }, + modal: { displayDetails: '显示详情', noResultsText: '无结果', resetButtonTitle: '重置' } + } + } + }, + + outline: { level: [2, 3], label: '目录' }, + docFooter: { prev: '上一篇', next: '下一篇' }, + darkModeSwitchLabel: '深色模式', + sidebarMenuLabel: '菜单', + returnToTopLabel: '回到顶部', + lastUpdated: { text: '最后更新' }, + }, + + markdown: { + lineNumbers: true, + }, +}) diff --git a/.vitepress/theme/custom.css b/.vitepress/theme/custom.css new file mode 100644 index 0000000..0f8e39f --- /dev/null +++ b/.vitepress/theme/custom.css @@ -0,0 +1,97 @@ +/* Custom styles for DPDK → DPU Blog */ + +:root { + --vp-c-brand-1: #2563eb; + --vp-c-brand-2: #3b82f6; + --vp-c-brand-3: #1d4ed8; + --vp-c-brand-soft: rgba(37, 99, 235, 0.14); + + --vp-font-family-base: 'Inter', 'Noto Sans SC', -apple-system, BlinkMacSystemFont, sans-serif; + --vp-font-family-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace; +} + +/* Hero section tweaks */ +.VPHero .name { + background: linear-gradient(135deg, #2563eb, #06b6d4); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.VPHero .text { + font-size: 2.5rem; +} + +/* Blog-like content */ +.vp-doc h1 { + font-size: 2rem; + font-weight: 700; + letter-spacing: -0.02em; +} + +.vp-doc h2 { + font-size: 1.5rem; + font-weight: 600; + border-bottom: 1px solid var(--vp-c-divider); + padding-bottom: 0.3em; + margin-top: 2em; +} + +/* Code blocks */ +.vp-doc div[class*='language-'] { + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); +} + +.vp-doc code { + font-size: 0.9em; +} + +/* Inline code */ +.vp-doc :not(pre) > code { + background: var(--vp-c-bg-soft); + border-radius: 4px; + padding: 0.15em 0.4em; + color: var(--vp-c-brand-1); +} + +/* Tables */ +.vp-doc table { + width: 100%; + border-collapse: collapse; + margin: 1.5em 0; +} + +.vp-doc th { + background: var(--vp-c-bg-soft); + font-weight: 600; +} + +.vp-doc tr:nth-child(even) { + background: var(--vp-c-bg-soft); +} + +/* Timeline-style for notes */ +.vp-doc ul:has(li input[type="checkbox"]) { + list-style: none; + padding-left: 0; +} + +/* Print-friendly */ +@media print { + .VPNav, .VPSidebar, .VPFooter { + display: none; + } +} + +/* Dark mode tweaks */ +.dark .VPHero .name { + background: linear-gradient(135deg, #60a5fa, #22d3ee); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.dark .vp-doc div[class*='language-'] { + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); +} diff --git a/.vitepress/theme/index.ts b/.vitepress/theme/index.ts new file mode 100644 index 0000000..42fe9a9 --- /dev/null +++ b/.vitepress/theme/index.ts @@ -0,0 +1,4 @@ +import DefaultTheme from 'vitepress/theme' +import './custom.css' + +export default DefaultTheme diff --git a/index.md b/index.md new file mode 100644 index 0000000..3ae25ef --- /dev/null +++ b/index.md @@ -0,0 +1,43 @@ +--- +layout: home + +hero: + name: "DPDK → DPU" + text: "学习笔记与开发日志" + tagline: 从零到 DPU 架构师 — 18 个月系统学习路线 + actions: + - theme: brand + text: 开始学习 + link: /roadmap + - theme: alt + text: 学习笔记 + link: /notes/ + +features: + - title: 📚 系统学习路线 + details: 546 天 DPDK 到 DPU 完整路线图,涵盖网卡驱动、协议栈、SAI 适配、DPU 架构设计 + - title: 🏗️ 产品化实践 + details: uNOS 用户态网络操作系统 + 家庭网关网络加速器,从设计规格到硬件 BOM + - title: ✍️ 每日笔记 + details: Phase 0-3 逐日学习记录,PCIe 拓扑、内核协议栈、RCU、DPDK 源码分析 + - title: 🔧 实战代码 + details: MPSC 无锁队列、dpdk-ws-client、libmtcp-v3、SAI-DPDK 适配器、Ring Benchmark +--- + +## 最近更新 + +### 学习笔记 + +- [Day001: PCIe 拓扑深度分析 (Jul 21, 2026)](/notes/week1/day001-pcie-topology) +- [Day04: RCU 与 Lock-free 编程](/notes/phase0/day04-rcu-lockfree) +- [Day03: 内核内存管理](/notes/phase0/day03-kernel-memory) +- [Day02: 内核收包路径分析](/notes/phase0/day02-kernel-rx-path) +- [Day01: PCIe 拓扑 + 讲座笔记](/notes/phase0/day01-pcie-topology) + +### 最新知识库 + +- [2026-08 知识更新](/kb/2026-08) + +--- + +*本站基于 [VitePress](https://vitepress.dev) 构建,所有 .md 文档均可直接渲染。* diff --git a/kb/2026-08.md b/kb/2026-08.md new file mode 100755 index 0000000..bb5eafa --- /dev/null +++ b/kb/2026-08.md @@ -0,0 +1,2 @@ +# 2026-08 + diff --git a/kb/index.md b/kb/index.md new file mode 100755 index 0000000..7d23416 --- /dev/null +++ b/kb/index.md @@ -0,0 +1,43 @@ +# 知识库 + +DPDK 学习与应用开发的知识积累。 + +> 工作中遇到的问题 → 5 分钟记一行 → 积累成面试素材 + uNOS 设计输入。 + +## 每月记录 + +[2026-08 更新](./2026-08.md) + +## 知识分类 + +### DPDK 核心 + +- 环境抽象层 (EAL) +- 内存管理 (Mbuf, Memzone, Mempool) +- 轮询模式驱动 (PMD) +- 无锁队列 (rte_ring) +- 报文转发框架 + +### Linux 内核对照 + +- 网络协议栈收发包路径 +- PCIe 设备枚举与驱动 +- 内存管理 (Buddy, Slab, CMA) +- 同步机制 (RCU, Spinlock) +- 中断与 NAPI + +### DPU 架构 + +- BlueField / Octeon 对比 +- SAI 交换机抽象接口 +- OVS 数据路径卸载 +- VirtIO / SR-IOV +- DMA 引擎 + +### 性能优化 + +- Cache 友好设计 +- NUMA 亲和性 +- Huge Pages +- 批处理与向量化 +- 零拷贝技术 diff --git a/notes/index.md b/notes/index.md new file mode 100644 index 0000000..a561916 --- /dev/null +++ b/notes/index.md @@ -0,0 +1,39 @@ +# 学习笔记 + +> DPDK → DPU 学习路线每日记录 + +## Phase 0 — 基础夯实 + +| Day | 主题 | 日期 | +|-----|------|------| +| [Day 01](/notes/phase0/day01-pcie-topology) | PCIe 拓扑结构 | Jun 2026 | +| [Day 02](/notes/phase0/day02-kernel-rx-path) | 内核收包路径分析 | Jun 2026 | +| [Day 03](/notes/phase0/day03-kernel-memory) | 内核内存管理 | Jun 2026 | +| [Day 04](/notes/phase0/day04-rcu-lockfree) | RCU & Lock-free 编程 | Jun 2026 | + +## Week 1 (起算: July 21, 2026) + +| Day | 主题 | +|-----|------| +| [Day 001](/notes/week1/day001-pcie-topology) | PCIe 拓扑深度分析 | + +## Phase 1 — DPDK 核心 (待更新) + +- [ ] EAL 初始化流程 +- [ ] Mbuf 内存池管理 +- [ ] PMD 驱动模型 +- [ ] rte_ring 无锁队列 +- [ ] Flow Bifurcation + +## Phase 2 — 协议栈 & 加速 (待更新) + +- [ ] 用户态 TCP/IP (libmtcp-v3) +- [ ] 性能基准测试 +- [ ] 硬件卸载 (RSS, TSO, LRO) + +## Phase 3 — DPU 架构 (待更新) + +- [ ] SAI-DPDK 适配 +- [ ] OVS 卸载 +- [ ] SONiC 集成 +- [ ] DPU 架构设计文档 diff --git a/notes/phase0/day01-pcie-topology.md b/notes/phase0/day01-pcie-topology.md new file mode 100755 index 0000000..ef3cc6c --- /dev/null +++ b/notes/phase0/day01-pcie-topology.md @@ -0,0 +1,127 @@ +# Phase 0 · Day 1: PCIe 总线拓扑与 Config Space + +--- + +## 一、BDF 地址:每个设备的唯一身份 + +``` +0000 : 02 : 01 . 0 + │ │ │ └── Function (0~7) — 一个物理芯片可以有多个功能 + │ │ └────── Device (0~31) — 同一总线上最多 32 个设备 + │ └─────────── Bus (0~255) — 256 条总线 + └───────────────── Domain (0~65535) — 多 CPU socket 系统有多个 domain + +单 CPU 系统 domain=0000。多路服务器每个 socket 一个 domain。 +``` + +DPDK 通过 BDF 地址接管网卡:`dpdk-devbind.py -b vfio-pci 02:01.0` + +### 为什么需要三级? + +PCI 是树形拓扑。每个 Bridge 引入一条新 bus。256 条 bus × 32 设备 × 8 功能 = 65536 个可寻址功能。如果没有 bus 层级,只靠 5-bit device 最多 32 个设备——一张显卡+一张网卡就满了。 + +## 二、拓扑中的四种角色 + +| 角色 | 你的实例 | 职责 | +|------|---------|------| +| **Root Complex** | `00:00.0` | CPU 和 PCIe 世界的桥梁,把 load/store 翻译成 TLP | +| **PCI Bridge** | `00:11.0` → Bus 02 | 传统 PCI 桥,用 subtractive decode 转发未认领的访问 | +| **PCIe Root Port** | `00:15.0` → Bus 03 | PCIe 原生桥,点对点链路,支持更高带宽 | +| **Endpoint** | `02:01.0` `03:00.0` | 叶子节点,实际的网卡设备 | + +关键差异:`02:01.0` (e1000) 是挂在传统 PCI Bridge 后面的老式设备,多条设备共享 Bus 02。`03:00.0` (VMXNET3) 是 PCIe Endpoint,独享 Bus 03 的整条链路(5GT/s × x32)。 + +## 二、PCIe 拓扑 + +``` +[0000:00] ← Root Complex (Host Bridge) + │ CPU 访问 PCIe 设备的唯一入口 + │ + ├── 11.0 → [02] ← PCI Bridge (老式总线) + │ ├── 01.0 e1000 (Endpoint) + │ └── 04.0 e1000 (Endpoint) + │ + └── 15.0 → [03] ← PCIe Root Port (真正的 PCI Express) + └── 00.0 VMXNET3 (Endpoint) +``` + +数据流:CPU → Root Complex → TLP 封装 → PCIe 链路 → Endpoint。 + +## 三、Config Space 完整解码 + +e1000 (02:01.0) 的 256 字节 Config Space,逐字段解码: + +| Offset | 字段 | Hex 值 | 含义 | +|--------|------|--------|------| +| 0x00-01 | Vendor ID | 0x8086 | Intel | +| 0x02-03 | Device ID | 0x100f | 82545EM | +| 0x04-05 | Command | 0x0113 | IO+Mem enable, BusMaster OFF | +| 0x0a-0b | Class Code | 0x020000 | Network Controller | +| 0x0e | Header Type | 0x00 | Endpoint | +| 0x10-13 | BAR0 | 0xfd5c0004 | Memory@0xfd5c0000, 64bit, 128KB, Non-Prefetchable | +| 0x18-1b | BAR2 | 0xfdff0004 | Memory@0xfdff0000, 64KB | +| 0x20-23 | BAR4 | 0x00002001 | IO Port@0x2000 | +| 0x34 | Capability Ptr | 0xdc | 链表起点 | +| 0x3c | Interrupt Line | 0x07 | IRQ 7 | +| 0x3d | Interrupt Pin | 0x01 | INTA# | + +BAR flag bit 解码: bit0=Memory/IO, bit[2:1]=32b/64b, bit3=Prefetchable。`0xfd5c0004` 中的 `4` = 0100₂ → 64-bit Memory, Non-Prefetchable。真实基址 = 0xfd5c0004 & ~0xF = 0xfd5c0000。 + +Capability 链表: 0xdc→PM(ID=1)→0xe4→PCI-X(ID=7)→0x00(End)。 + +## 四、VMXNET3 对比 (真正的 PCIe 设备) + +| | e1000 | VMXNET3 | +|---|---|---| +| 总线 | PCI-X (老) | PCIe v2.0 | +| Capability | PM + PCI-X | PM + PCIe Express + MSI + MSI-X + Serial# | +| MSI-X | 无 | 25 向量, BAR2 offset 0, PBA offset 0x1000 | + +## 五、实战踩坑 + +### 坑 1: Bus Master 没开 +- **现象**: DPDK 启动后一个包都收不到 +- **根因**: Command bit2=0 → 网卡不能 DMA +- **定位**: `lspci -vvv | grep "BusMaster"` +- **修复**: `pci_set_master(pdev)` 或 `setpci -s XX:XX.X COMMAND=0x117` + +### 坑 2: 64-bit BAR 误读 +- **现象**: 物理地址 >4GB 时 MMIO 地址错误 +- **根因**: 只用 uint32_t 读 BAR0 低 32 位 +- **修复**: 用 `resource_size_t pci_resource_start(pdev, 0)` + +### 坑 3: MSI-X 向量不够 +- **现象**: 多队列网卡性能不增长 +- **根因**: pci_alloc_irq_vectors 返回数 < RX 队列数 +- **定位**: `cat /proc/interrupts | grep PCI-MSI | wc -l` +- **修复**: 增大内核 MSI-X 限制或 BIOS 调整 + +### 坑 4: PCIe 链路降速 +- **现象**: 100Gbps 网卡只能跑 25Gbps +- **定位**: `lspci -vvv | grep -E "LnkCap:|LnkSta:"` → Speed 2.5GT/s +- **修复**: 检查物理槽位宽度、PCIe 延长线、BIOS 设置 + +### 坑 5: AER 静默数据损坏 +- **现象**: 偶尔出现 mbuf 数据乱码但不 crash +- **定位**: `dmesg | grep AER` → Uncorrectable Error +- **修复**: 必须换硬件,链路已不可靠 + +## 六、核心认知 + +CPU 操作网卡的全路径: + +``` +读 Config Space → 匹配 Vendor/Device ID → 确定驱动 + → pci_enable_device → ioremap(BAR0) → 拿到虚拟地址 + → ioread32/iowrite32 操作寄存器 → 网卡开始工作 +``` + +BAR 是 MMIO 的入口,Config Space 是身份证,Capability 链表是扩展机制。 + +## 七、检验标准 +- [ ] 能解释 BDF 地址含义 +- [ ] 能手写 Config Space 前 16 字节字段 +- [ ] 能从 hex 值解码 BAR 的基址和大小 +- [ ] 能画 Capability 链表结构 +- [ ] 能对比 e1000(PCI-X) vs VMXNET3(PCIe v2) +- [ ] 能解释为什么 Bus Master bit 不置位就没 DMA diff --git a/notes/phase0/day02-kernel-rx-path.md b/notes/phase0/day02-kernel-rx-path.md new file mode 100755 index 0000000..ae99dd6 --- /dev/null +++ b/notes/phase0/day02-kernel-rx-path.md @@ -0,0 +1,104 @@ +# Phase 0 · Day 2: 内核中断 + 网络收包路径 + +--- + +## 一、你的系统数据 + +``` +IRQ 56 (ens160-rxtx-0): CPU0:1329万 CPU1:191万 CPU2:112万 CPU3:676万 +IRQ 57 (ens160-rxtx-1): CPU0:11万 CPU1:33万 CPU2:36万 CPU3:38万 +IRQ 58 (ens160-rxtx-2): CPU0:19万 CPU1:37万 CPU2:43万 CPU3:42万 +IRQ 59 (ens160-rxtx-3): CPU0:30万 CPU1:39万 CPU2:52万 CPU3:52万 +IRQ 60 (ens160-event): 全 0 (事件队列, 未使用) + +NET_RX 软中断: CPU0:1339万 CPU1:436万 CPU2:381万 CPU3:791万 +ksoftirqd: 每个 CPU 一个 (PID 16/24/30/36) +``` + +- 4 个 MSI-X 向量 → 4 个 RX 队列 → 4 个 CPU 并行收包 +- IRQ 56 在 CPU0 上触发了 1329 万次(主流量),CPU1-3 只触发了少量——IRQ affinity 配置不均 + +## 二、收包 6 道关卡 + +``` +关卡1: 硬件中断 + 网卡 DMA 写 RX ring → MSI-X 中断 → APIC → CPU → IDT → irq_handler + 此时在硬中断上下文,所有中断被屏蔽 + +关卡2: NAPI 调度 + irq_handler → napi_schedule → 关网卡中断 + 触发 NET_RX_SOFTIRQ → 返回 + 源码: net/core/dev.c ____napi_schedule (line 4551) + +关卡3: ksoftirqd + ksoftirqd 被唤醒 → __do_softirq → net_rx_action → 遍历 poll_list + 源码: kernel/softirq.c + net/core/dev.c + +关卡4: 驱动 poll → netif_receive_skb + 从 RX descriptor ring 取包 → alloc_skb → memcpy → netif_receive_skb(skb) + 源码: net/core/dev.c netif_receive_skb (line 5899) + +关卡5: 协议分发 (L2→L3) + __netif_receive_skb_core → skb->protocol → 查 ptype_base 哈希表 → ip_rcv() + 源码: net/core/dev.c __netif_receive_skb_core (line 5434) + net/ipv4/ip_input.c ip_rcv (line 560) + 协议注册: net/ipv4/af_inet.c dev_add_pack (line 2065) + +关卡6: TCP 收包 (L3→L4→socket) + ip_rcv → NF_HOOK → ip_local_deliver → tcp_v4_rcv + → __inet_lookup_established → tcp_rcv_established (fast path!) + → tcp_queue_rcv → sk_data_ready → wake_up → epoll 返回 + 源码: net/ipv4/tcp_ipv4.c (line 1999) + net/ipv4/tcp_input.c tcp_rcv_established +``` + +## 三、DPDK 为什么快 + +| 内核步骤 | 耗时 | DPDK | +|---------|------|------| +| MSI-X → IDT → handler | ~3us | PMD 轮询, 无中断 | +| napi_schedule + ksoftirqd 调度 | ~5us | PMD 线程永不睡眠 | +| alloc_skb (slab 锁) | ~1us | mbuf 从 per-lcore cache 弹出 | +| memcpy DMA→skb | ~2us | DMA 直接写 mbuf, 零拷贝 | +| ptype_base 查找 | ~0.3us | 应用自己解析协议 | +| Netfilter HOOK | ~5us | 不需要 | +| ehash 查 socket | ~1us | rte_hash_lookup | +| copy_to_user | ~3us | 零拷贝 | +| **总计** | **~20us** | **~1us** | + +## 四、实战踩坑 + +### 坑 1: 中断全绑 CPU0 +- **现象**: top 看 CPU0 si=99%, 其他 CPU 空闲 +- **根因**: smp_affinity 默认只开 CPU0 +- **定位**: `cat /proc/irq/56/smp_affinity` +- **修复**: `echo ff > /proc/irq/56/smp_affinity` + +### 坑 2: NAPI budget 耗尽 +- **现象**: 100G 线速达不到,`ethtool -S` 看 rx_missed 增长 +- **根因**: net_rx_action 默认一次最多 300 包,收不够就返回 +- **定位**: `cat /proc/net/softnet_stat`, 第三列 = "times queue full" +- **修复**: 增大 net.core.netdev_budget,或换 DPDK + +### 坑 3: ksoftirqd 被抢占 +- **现象**: 收包延迟 P99 抖动严重 +- **根因**: ksoftirqd 不是实时线程,被磁盘 I/O 抢占 +- **定位**: `perf sched latency` +- **修复**: `chrt -f 1 $(pidof ksoftirqd/0)` 或换 DPDK PMD + +### 坑 4: Netfilter 静默丢包 +- **现象**: tcpdump 能看到包,应用收不到 +- **根因**: Docker/k8s 自动注入 iptables drop 规则 +- **定位**: `iptables -L -v -n`, 看每条规则的计数器 + +### 坑 5: TCP backlog 满 +- **现象**: 客户端 connect 超时 +- **根因**: accept queue=128, 第 129 个 SYN 被内核静默丢弃 +- **定位**: `netstat -s | grep overflow` +- **修复**: `echo 4096 > /proc/sys/net/core/somaxconn` + +## 五、检验标准 +- [ ] 能画中断→NAPI→ksoftirqd→poll→netif_receive_skb→ip_rcv→tcp_v4_rcv 完整调用链 +- [ ] 能解释 NAPI 为什么要关中断、用软中断 +- [ ] 能说 ptype_base 的作用 +- [ ] 能列 DPDK 比内核快 ≥5 个原因 +- [ ] 能在 /proc/interrupts 和 /proc/softirqs 找到你的网卡 diff --git a/notes/phase0/day03-kernel-memory.md b/notes/phase0/day03-kernel-memory.md new file mode 100755 index 0000000..4eb2a1c --- /dev/null +++ b/notes/phase0/day03-kernel-memory.md @@ -0,0 +1,147 @@ +# Phase 0 · Day 3: 内核内存管理 — buddy → slab → slub + +--- + +## 一、你的系统数据 + +``` +Buddy (/proc/buddyinfo, Node 0 DMA32): + order 0(4KB)=2275 1(8KB)=2107 2(16KB)=2287 3(32KB)=2270 + 4(64KB)=1540 5(128KB)=992 6(256KB)=558 7(512KB)=215 + 8(1MB)=35 9(2MB)=7 10(4MB)=0 ← 连续大块已严重碎片化 + +Slab (/proc/slabinfo): + skbuff_head_cache: 4411 active 256B/obj 16 objs/slab + skbuff_small_head: 345 active 704B/obj 23 objs/slab + sock_inode_cache: 896 active 832B/obj + kmalloc-cg-2k: 627 active 2048B/obj + kmalloc-cg-4k: 222 active 4096B/obj +``` + +## 二、三层架构 + +``` +kmalloc(1536) + ↓ +┌─ Slub (对象分配器) ─────────────────────┐ +│ per-CPU cache (kmem_cache_cpu) │ +│ freelist: obj0→obj3→obj7→∅ │ +│ 分配 = 弹 head, O(1), 无锁 (fastpath) │ +│ 释放 = 插 head, O(1), 无锁 │ +│ ↓ freelist 空 │ +│ partial slab → 切对象 → 填 freelist │ +│ ↓ partial 也空 │ +└────────────┬────────────────────────────┘ + ↓ 需要新 slab page +┌─ Buddy System (页分配器) ────────────────┐ +│ order 0(4KB) → order 10(4MB) │ +│ 分配: 找 order n → 没有? 分裂 n+1 │ +│ 释放: 合井相邻 buddy │ +│ 碎片: 长期运行 order 高位全 0 │ +└──────────────────────────────────────────┘ +``` + +## 三、Buddy System + +两个页块互为 buddy: 大小相同 + 物理相邻 + 来自同一父块分裂。 + +``` +16KB 块 (order 2, page 0-3): + [p0][p1][p2][p3] + └buddy┘ └buddy┘ + +释放 p0 时: p1 空闲? → 合并成 8KB (order 1) + [p0,p1] 和 [p2,p3] 都空闲? → 再合并成 16KB (order 2) +``` + +/proc/buddyinfo 就是各级 order 的空闲块计数。你的系统 order 9 只剩 7 个 2MB 块 → 碎片严重。 + +## 四、Slub: per-CPU 无锁分配 + +```c +// include/linux/slub_def.h:50 +struct kmem_cache_cpu { + void **freelist; // 本 CPU 空闲对象链表 + unsigned long tid; // 事务 ID + struct slab *slab; // 当前 slab + struct slab *partial; // 部分使用的 slabs +}; +``` + +### 分配路径 + +``` +slab_alloc(s, gfp, node) + → c = per_cpu_ptr(s->cpu_slab, cpu) ← 本 CPU 的 cache + → object = c->freelist ← 取 freelist head + → if (object) { ← FAST PATH (>99%) + c->freelist = *(void**)object; ← 更新 freelist + return object; ← done, 无锁! + } + → ___slab_alloc(s, gfp, node, c) ← SLOW PATH + → 1. 取 partial slab → 切对象 + → 2. partial 空 → buddy 申请新页 → 切 16 个对象 + → 3. 1 个返回, 15 个填 freelist +``` + +### freelist 嵌入空闲对象内部 + +``` +一个 slab page (4KB): +┌──────┬──────┬──────┬...┬──────┐ +│ obj0 │ obj1 │ obj2 │ │obj15 │ 16个 256B 对象 +└──────┴──────┴──────┴...┴──────┘ + +空闲对象 obj0 的前 8 字节 = &obj3 +空闲对象 obj3 的前 8 字节 = &obj7 +obj7 的前 8 字节 = NULL + +分配: head = freelist; freelist = *(void**)head; return head; +释放: *(void**)obj = freelist; freelist = obj; +``` + +## 五、内核 slub vs DPDK mempool + +| | 内核 slub | DPDK mempool | +|---|---|---| +| **L1 cache** | per-CPU freelist | per-lcore cache | +| **L1 操作** | 弹 freelist head, O(1) | 弹 cache->objs[--len], O(1) | +| **L2 补货** | partial slab → buddy 新页 | rte_ring → driver dequeue | +| **L3 兜底** | buddy alloc → OOM | 驱动层 | +| **锁** | 无锁 (per-CPU) | 无锁 (per-lcore + ring CAS) | +| **预分配** | 按需从 buddy 取 | hugepage 一次性分配 | +| **碎片** | 长期运行 buddy 碎片 | 不会,固定大小,一次分配 | + +## 六、实战踩坑 + +### 坑 1: 跨 NUMA 分配延迟 3x +- **现象**: 双 socket 服务器上性能波动严重 +- **根因**: CPU0 分配了 socket1 的内存 → 远程访问 +- **修复**: `rte_mempool_create(socket_id=rte_socket_id())` + +### 坑 2: buddy 碎片 → 大块分配失败 +- **现象**: dmesg "page allocation failure: order:4" +- **根因**: 频繁 alloc/free skb 拆散所有连续大块 +- **修复**: DPDK 用 hugepage 预分配 + +### 坑 3: skb 泄漏 +- **现象**: /proc/slabinfo skb active_objs 只增不减 +- **根因**: netfilter drop 未调 kfree_skb +- **定位**: `bpftrace -e 'k:alloc_skb { @a=kstack } k:kfree_skb { @f=kstack }'` + +### 坑 4: GFP_ATOMIC 在中断上下文耗光 +- **现象**: 高吞吐下 alloc_skb 返回 NULL +- **根因**: GFP_ATOMIC 不触发页面回收,分配池浅 +- **修复**: 预分配内存池,或在进程上下文用 GFP_KERNEL + +### 坑 5: per-CPU cache false sharing +- **现象**: 多核并发分配性能不线性增长 +- **根因**: s->cpu_slab[0] 和 s->cpu_slab[1] 在同一 cache line +- **修复**: `____cacheline_aligned_in_smp` 或 DPDK `__rte_cache_aligned` + +## 七、检验标准 +- [ ] 能解释 buddy 分裂/合并 + 会读 /proc/buddyinfo +- [ ] 能画 slub 三级分配: freelist → partial slab → buddy +- [ ] 能讲 kmem_cache_cpu 如何无锁 + 和 DPDK mempool 对照 +- [ ] 5 个坑都知道怎么定位 +- [ ] 能在 /proc/slabinfo 找到 skb cache 并解读字段 diff --git a/notes/phase0/day04-rcu-lockfree.md b/notes/phase0/day04-rcu-lockfree.md new file mode 100755 index 0000000..51e4ec9 --- /dev/null +++ b/notes/phase0/day04-rcu-lockfree.md @@ -0,0 +1,135 @@ +# Phase 0 · Day 4: RCU 无锁机制 + 手写 MPSC 队列 + +--- + +## 一、RCU 核心思想 + +### 1.1 rwlock 为什么不行 + +在多核系统中,传统 rwlock 的问题是:即使 100% 都是读操作,每个 CPU 的 `read_lock` 都要原子修改同一个 cache line → 32 核时该 cache line 在 32 个核之间乒乓(cache line bouncing)→ 性能随核数退化。 + +### 1.2 RCU 的做法 + +``` +读者 (数据面): 写者 (控制面): + rcu_read_lock() copy data → modify copy + → preempt_disable() rcu_assign_pointer(ptr, new) + (非 PREEMPT_RCU 内核) synchronize_rcu() ← 等所有老读者退出 + ptr = rcu_dereference(gp) kfree(old) + → READ_ONCE + lockdep + use(ptr) ← 无锁! 纯读! + rcu_read_unlock() + → preempt_enable() +``` + +核心权衡: **用延迟释放换取读者零开销**。 + +### 1.3 核心 API + +| API | 作用 | 场景 | +|-----|------|------| +| `rcu_read_lock/unlock` | 标记读临界区 (非PREEMPT下=preempt_disable) | 读者遍历 | +| `rcu_dereference(p)` | READ_ONCE + lockdep检查 | **必须用!** 裸读=bug | +| `rcu_assign_pointer(p,v)` | smp_store_release 发布新指针 | 写者更新 | +| `synchronize_rcu()` | 阻塞等 grace period 结束 | 模块卸载 (不能中断上下文) | +| `call_rcu(head, func)` | 异步等 GP → 调回调 | 热路径释放 (中断安全) | + +### 1.4 Grace Period 机制 + +每个 CPU 经历一次 quiescent state (上下文切换或退出 rcu_read_lock) → 所有 CPU 都经历 → GP 结束 → 旧数据安全释放。 + +--- + +## 二、RCU 在 DPDK 中的体现 + +| RCU 概念 | DPDK rte_ring 对应 | +|----------|-------------------| +| 读者不阻塞 | consumer 读 `prod.tail` 纯读, 无锁 | +| rcu_dereference (READ_ONCE) | `*old_head = r->prod.head` + smp_rmb() | +| rcu_assign_pointer (release) | CAS release/acquire 语义 | +| Grace Period | head/tail 追赶: tail 更新前不覆盖旧数据 | +| 延迟回收 | per-lcore cache flush → bulk 操作分摊开销 | + +--- + +## 三、实战踩坑 (RCU) + +### 坑 1: Grace Period 过长 → OOM +- **现象**: kmalloc + free 循环中 call_rcu 积压, 内存耗尽 +- **根因**: 长期 rcu_read_lock / NO_HZ idle CPU / CONFIG_RCU_LAZY +- **定位**: `cat /sys/kernel/debug/rcu/rcu_preempt | grep cblist` +- **修复**: `call_rcu_hurry()` 或 `echo 1 > /sys/kernel/rcu_expedited` + +### 坑 2: 漏掉 rcu_dereference → use-after-free +- **现象**: 偶发性 crash, KASAN 报告 use-after-free +- **根因**: 编译器优化导致指针被多次读取, 第二次读时已被释放 +- **定位**: sparse: `make C=2`, KASAN: `dmesg | grep KASAN` +- **修复**: 永远用 `rcu_dereference(p)` 而不是裸读 `p` + +### 坑 3: rcu_read_lock 内睡眠 +- **现象**: 内核 splat: "scheduling while atomic" +- **根因**: 非 PREEMPT_RCU 下 rcu_read_lock 禁止抢占, 不能睡眠 +- **修复**: 用 SRCU (sleepable RCU) 或把睡眠移到临界区外 + +### 坑 4: synchronize_rcu 在中断上下文调用 +- **现象**: 内核 panic +- **根因**: synchronize_rcu 会睡眠, 中断上下文不可 +- **修复**: 用 call_rcu 异步 + +### 坑 5: list_for_each_entry_rcu 遍历时写者并发删除 +- **现象**: 读者遍历到已删除节点 → crash +- **根因**: 写者 list_del_rcu 后立即 kfree (没等 GP) +- **修复**: 写者必须在 synchronize_rcu/call_rcu 之后才能释放 + +--- + +## 四、编码题: MPSC 无锁队列 + +### 需求 + +``` +题目: 用 C11 atomic 实现 MPSC 无锁队列 + +硬性要求: + 1. 固定大小环形 buffer (大小 2 的幂) + 2. enqueue: 多线程安全 (CAS 抢 head) + 3. dequeue: 单线程 (不需要原子操作) + 4. 2 producer + 1 consumer, 各入队 1000000 次, 总数 = 2000000 + +核心算法 (两阶段提交): + enqueue: + ① CAS 抢 head slot + ② 写入数据 + ③ 通知 consumer (方案A: spin等tail→更新; 方案B: consumer自己检查head) + + 推荐方案B (consumer更新tail): producer只CAS head+写数据→结束, 无spin + +参考源码: + - rte_ring CAS: source/dpdk-stable-24.11.6/lib/ring/rte_ring_c11_pvt.h:60-128 + - rte_ring 结构体: source/dpdk-stable-24.11.6/lib/ring/rte_ring_core.h:116-145 +``` + +### 三个陷阱 + +1. **ABA**: head 只增不减 (靠32-bit溢出) → 不会有 ABA, 但如果你让 head 回退就会 +2. **内存屏障位置**: 数据写入必须在 CAS 之前 (CAS release 保证可见) +3. **False sharing**: head 和 tail 放不同 cache line (`alignas(64)`) + +### 代码框架 + +文件: `code/Phase0/mpsc-queue/mpsc_queue.c` + +### 检验 + +```bash +gcc -std=c11 -pthread -O2 -Wall -Wextra -o mpsc_test mpsc_queue.c +./mpsc_test +# PASS: consumed = 2000000 (expected 2000000) +``` + +## 五、检验标准 +- [ ] 能解释 RCU 的核心思想 (读者零开销) +- [ ] 能说出 rcu_dereference 为什么必须用 (READ_ONCE + lockdep) +- [ ] 能解释 grace period 机制 +- [ ] 能对比 RCU 和 DPDK rte_ring 的设计共同点 +- [ ] MPSC 队列: 2 producer + 1 consumer 跑通, 2000000 个数据无丢失 diff --git a/notes/week1/day001-pcie-topology.md b/notes/week1/day001-pcie-topology.md new file mode 100755 index 0000000..ba28197 --- /dev/null +++ b/notes/week1/day001-pcie-topology.md @@ -0,0 +1,251 @@ +# Day 001 (2026-07-21, 周一) · 主题: PCIe总线拓扑 + +## 📖 学习 (30min) + +### PCIe体系结构 +- PCIe是点对点串行总线,替代了旧的PCI/PCI-X并行总线 +- 分层结构:Root Complex → PCI Bridge → Endpoint +- Root Complex集成在CPU中,是CPU和PCIe的桥梁 +- 每个PCIe设备有唯一的BDF地址:Bus:Device.Function + +### 关键概念 +1. **Root Complex (RC)**: 连接CPU和PCIe总线,负责: + - CPU地址 ↔ PCIe地址转换 + - 生成PCIe配置请求 + - 管理中断 + +2. **PCI Bridge**: 扩展PCIe端口,像网络交换机 + - 上游口(Upstream)连RC或其他Bridge + - 下游口(Downstream)连设备或其他Bridge + +3. **Endpoint**: 最终设备 + - 网卡(NIC)、显卡(GPU)、SSD(NVMe) + - 通过BAR(Base Address Register)暴露寄存器 + +4. **BDF地址**: Bus:Device.Function + - Bus: 8位,最多256条总线 + - Device: 5位,每条总线最多32个设备 + - Function: 3位,每个设备最多8个功能 + +## 🔧 动手 (60min) + +### 1. 获取本机PCIe拓扑 + +```bash +root@ubuntu:~# lspci -tv + -[0000:00]-+-00.0 Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge + +-01.0-[01]-- + +-07.0 Intel Corporation 82371AB/EB/MB PIIX4 ISA + +-07.1 Intel Corporation 82371AB/EB/MB PIIX4 IDE + +-07.3 Intel Corporation 82371AB/EB/MB PIIX4 ACPI + +-07.7 VMware Virtual Machine Communication Interface + +-0f.0 VMware SVGA II Adapter + +-10.0 Broadcom / LSI 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI + +-11.0-[02]--+-00.0 VMware USB1.1 UHCI Controller + | +-01.0 Intel Corporation 82545EM Gigabit Ethernet Controller (Copper) + | +-02.0 VMware USB2 EHCI Controller + | +-03.0 VMware SATA AHCI controller + | \-04.0 Intel Corporation 82545EM Gigabit Ethernet Controller (Copper) + +-15.0-[03]----00.0 VMware VMXNET3 Ethernet Controller + +-15.1-[04]-- + ... +``` + +**拓扑分析**: +- 这是VMware虚拟机环境 +- 主桥:Intel 440BX/ZX/DX (很老的芯片组,1998年) +- 总线00: Root Complex + 传统PCI设备 +- 总线01: 空桥(可能是PCIe扩展槽) +- 总线02: PCI Bridge 00:11.0 下游,连接4个设备 +- 总线03: PCI Bridge 00:15.0 下游,连接VMXNET3网卡 + +### 2. 读取网卡详细信息 (02:01.0) + +```bash +root@ubuntu:~# lspci -vvv -s 02:01.0 +02:01.0 Ethernet controller: Intel Corporation 82545EM Gigabit Ethernet Controller (Copper) (rev 01) + DeviceName: Ethernet2 + Subsystem: VMware PRO/1000 MT Single Port Adapter + Physical Slot: 33 + Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV+ VGASnoop- ParErr- Stepping- SERR+ FastB2B- DisINTx- + Status: Cap+ 66MHz+ UDF- FastB2B- ParErr- DEVSEL=medium >TAbort- SERR- = 14.0.0" + } + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.17.7", + "resolved": "https://registry.npmmirror.com/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", + "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", + "@algolia/autocomplete-shared": "1.17.7" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.17.7", + "resolved": "https://registry.npmmirror.com/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", + "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.17.7", + "resolved": "https://registry.npmmirror.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", + "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.17.7", + "resolved": "https://registry.npmmirror.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/client-abtesting": { + "version": "5.56.0", + "resolved": "https://registry.npmmirror.com/@algolia/client-abtesting/-/client-abtesting-5.56.0.tgz", + "integrity": "sha512-7r4Z3NC7yU1oAQVWJNA2HX7tX481F3pJvCGyLIXiTdBcthz4Q/o21jwcMYDFkuI92UWTNBQQmHYgwHo1zS5dzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "5.56.0", + "resolved": "https://registry.npmmirror.com/@algolia/client-analytics/-/client-analytics-5.56.0.tgz", + "integrity": "sha512-avmjXQSq+jadFO8Xl2em05/uQdQnEmHsJyOAdVbZkmVgpMfxL12aJwVVfGNwYr9nulcpuJN1X0lTaQ5wxuNGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.56.0", + "resolved": "https://registry.npmmirror.com/@algolia/client-common/-/client-common-5.56.0.tgz", + "integrity": "sha512-v2TPStUhY//ripPjIVclZ8AWc7DEGooXULZGFlFu37zNatgHjw34oZZ+OSbbc/YHO+xZwPl62I1k8xH1m4S2eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.56.0", + "resolved": "https://registry.npmmirror.com/@algolia/client-insights/-/client-insights-5.56.0.tgz", + "integrity": "sha512-P0ehROpM4Sem3Sqo5x2cKPgj67D3G3jy0rh1Amwkcvsfr6tkvIcdCmerieanqTF7NxUMPNFLkpIFeMO8Rpa50w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "5.56.0", + "resolved": "https://registry.npmmirror.com/@algolia/client-personalization/-/client-personalization-5.56.0.tgz", + "integrity": "sha512-SXK3Vn3WVxyzbm31oePZBJkp1wpOyuWdd4B/Pv7n0aXDxmeSWhC1R1FC1517mMrFAIaPH4Rt0x6RUe7ZNjz8FA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.56.0", + "resolved": "https://registry.npmmirror.com/@algolia/client-query-suggestions/-/client-query-suggestions-5.56.0.tgz", + "integrity": "sha512-5+ZdX8garFnmycnZgKhtXHePEaLj5zqDxI/0lkhhluzCcvTn0/PvvTirTg8hHYetQHvn7GDyeAiqTAieMvMW4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.56.0", + "resolved": "https://registry.npmmirror.com/@algolia/client-search/-/client-search-5.56.0.tgz", + "integrity": "sha512-+mKUdYvqOi0BcvpAEyCEw49vSBptufIcfibtHz2bdr1pI789M46Yt0uQEk/sxtK3teh71OQvVFHaTDzShUWewQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/ingestion": { + "version": "1.56.0", + "resolved": "https://registry.npmmirror.com/@algolia/ingestion/-/ingestion-1.56.0.tgz", + "integrity": "sha512-9g/zj+AZx5moFcdFIrYQoVrueXivjUcc3MQHtCYT8WhIuk1lUh1AyEhvJCS0XBZld09cLvd1AZ3BvDBpVpX2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.56.0", + "resolved": "https://registry.npmmirror.com/@algolia/monitoring/-/monitoring-1.56.0.tgz", + "integrity": "sha512-Qf3Sr6f9A9uxCZUf3MXS0d2b877uYzEB5yxqpVGXAhcJnBCQjrRRon0KvefpGkxy+BshrIJs96OUoMtGqXTFDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.56.0", + "resolved": "https://registry.npmmirror.com/@algolia/recommend/-/recommend-5.56.0.tgz", + "integrity": "sha512-GXWG1rWc5wu8hY4N33Y3b6ernY6sAdAvmKWN/zHAiACOx40WnpG0TVX5YazCAr/9gOYGInSiM2A0y2jy2xbiDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.56.0", + "resolved": "https://registry.npmmirror.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.56.0.tgz", + "integrity": "sha512-7t24cBxaInS3mZb7ddEaZT/tp6q+/aR4YttsQVyP1/i+LmwPR34atO35KjaLFCcRVrlP7sYOAqkCfg6lIRB+ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.56.0", + "resolved": "https://registry.npmmirror.com/@algolia/requester-fetch/-/requester-fetch-5.56.0.tgz", + "integrity": "sha512-R7ePHgVYmDFjZpvrsVAfbDz/d4RxKAYZ5/vgLfIsCVRZRryjWl/3INOxpOICzitehQ5FjNtNjcLQTrmHPTcHBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.56.0", + "resolved": "https://registry.npmmirror.com/@algolia/requester-node-http/-/requester-node-http-5.56.0.tgz", + "integrity": "sha512-PIOUXlSnrqM0S+WOgDRb4RzotydJH7ZoT6tOyL7tAO7qJOfvX5wsEW8Pe+PMKMwvuI4/gIyK9cg2H7lJXqnc4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@docsearch/css": { + "version": "3.8.2", + "resolved": "https://registry.npmmirror.com/@docsearch/css/-/css-3.8.2.tgz", + "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@docsearch/js": { + "version": "3.8.2", + "resolved": "https://registry.npmmirror.com/@docsearch/js/-/js-3.8.2.tgz", + "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/react": "3.8.2", + "preact": "^10.0.0" + } + }, + "node_modules/@docsearch/react": { + "version": "3.8.2", + "resolved": "https://registry.npmmirror.com/@docsearch/react/-/react-3.8.2.tgz", + "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-core": "1.17.7", + "@algolia/autocomplete-preset-algolia": "1.17.7", + "@docsearch/css": "3.8.2", + "algoliasearch": "^5.14.2" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0", + "search-insights": ">= 1 < 3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "optional": true + } + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@iconify-json/simple-icons": { + "version": "1.2.93", + "resolved": "https://registry.npmmirror.com/@iconify-json/simple-icons/-/simple-icons-1.2.93.tgz", + "integrity": "sha512-/XhANjfGYOuqvSR3TmUnkQkINvQ4GVjVuukvymRbxtVFBvIq/yiXJqCDycKcQPT401OYT9H2vIY6ihAlz1QIAw==", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz", + "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz", + "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz", + "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz", + "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz", + "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz", + "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz", + "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz", + "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz", + "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz", + "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz", + "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz", + "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz", + "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz", + "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz", + "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz", + "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz", + "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz", + "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz", + "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz", + "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz", + "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz", + "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz", + "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz", + "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz", + "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/@shikijs/core/-/core-2.5.0.tgz", + "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.4" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz", + "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^3.1.0" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz", + "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/@shikijs/langs/-/langs-2.5.0.tgz", + "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/@shikijs/themes/-/themes-2.5.0.tgz", + "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/transformers": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/@shikijs/transformers/-/transformers-2.5.0.tgz", + "integrity": "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.5.0", + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/types": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/@shikijs/types/-/types-2.5.0.tgz", + "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmmirror.com/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmmirror.com/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.41.tgz", + "integrity": "sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/shared": "3.5.41", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.41.tgz", + "integrity": "sha512-oKacVfNglLvGjnS6BXOlGL7EyG2h8X03pqXCjzotRZUaXGjbrTJUnVAQjrCqUnS+lyu31nwQjZY/d817GmCnfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.41.tgz", + "integrity": "sha512-XJhip7R2wy6vX3knCxdZN4KracFaZUef58s1KYewqluedHIJaPIVfXoYT7MF1F8nCvv6k8bWWxDC8opMkg1VTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/compiler-core": "3.5.41", + "@vue/compiler-dom": "3.5.41", + "@vue/compiler-ssr": "3.5.41", + "@vue/shared": "3.5.41", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.19", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.41.tgz", + "integrity": "sha512-U3v5OejKEGqOI0Wy0+Sz7hGuIFZHA4LSXzrNM3IMIeDyJEBBfTpX26n3SDgToRpP2bLc9FfI2j/kSgcJ8Emq5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.10", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.10.tgz", + "integrity": "sha512-KxtEpUOOpFz/qOGRrAwA36QF7DqIA+FXgCYit9mk9wjbaZt0sXOFz81ElOZtKA4HbWHUdwNjZHBFsFFyp5BZiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.10" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.10", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.10.tgz", + "integrity": "sha512-3WNi2Kq4tbpVbmhml7RiphmAt0279oh3fKNeWMQIrltfX8Q91b4i5PL8DtyNKdwmcsGrV4fg+erwWOmD05CLIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.10", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.10", + "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.10.tgz", + "integrity": "sha512-wOPslzB8vTvpxwdaOcR2qAbwmuSP0L+rhpoC6Cf56V3Jip+HWb7PQQXOUPgBNQARpXsbQX/+mvi8kKucmBGRwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.41.tgz", + "integrity": "sha512-rznsqKM0np0x18EjzF8x88MpEhdNsffbvFbckLL5+oUKz1BxAImEmO7J1ArRYSyo6aQaVoBDp7jEkT91OOxydA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.41.tgz", + "integrity": "sha512-Vcry58hiAKwGen9Z1jUZE0feFsNArPCMOImYI8el48A9Idf6DuQYD0U05zZIF2Iad1hGhPSvcbBbAOhNr55fhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.41.tgz", + "integrity": "sha512-3vVBahVBS9+U6cmXBLyb8nE6/yYo4J/CGI9eVFs3KiMc0YHuudwKyShTD65jtJy/L9PUUxNAFu4cj4LiJ0UFbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.41", + "@vue/runtime-core": "3.5.41", + "@vue/shared": "3.5.41", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.41.tgz", + "integrity": "sha512-n6hx/pNFfbD6SuyeuMVkvqox8bwf/ET9JlA/kAz/imw8sw++wkqKe2mHX5KutjPpbKE4Z56yTHszoOjGMI9igQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.41", + "@vue/runtime-dom": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.41.tgz", + "integrity": "sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "12.8.2", + "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-12.8.2.tgz", + "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/integrations": { + "version": "12.8.2", + "resolved": "https://registry.npmmirror.com/@vueuse/integrations/-/integrations-12.8.2.tgz", + "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vueuse/core": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "^4", + "axios": "^1", + "change-case": "^5", + "drauu": "^0.4", + "focus-trap": "^7", + "fuse.js": "^7", + "idb-keyval": "^6", + "jwt-decode": "^4", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^7" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "12.8.2", + "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-12.8.2.tgz", + "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "12.8.2", + "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-12.8.2.tgz", + "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/algoliasearch": { + "version": "5.56.0", + "resolved": "https://registry.npmmirror.com/algoliasearch/-/algoliasearch-5.56.0.tgz", + "integrity": "sha512-PrqppUmhT4ENdas2pH9caE7efUcxy6EcSFhWzosiVuQBzu2tQ5yLTI6jwomT/1cuBnivzGfxiJCqDNN9FRRh+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.22.0", + "@algolia/client-abtesting": "5.56.0", + "@algolia/client-analytics": "5.56.0", + "@algolia/client-common": "5.56.0", + "@algolia/client-insights": "5.56.0", + "@algolia/client-personalization": "5.56.0", + "@algolia/client-query-suggestions": "5.56.0", + "@algolia/client-search": "5.56.0", + "@algolia/ingestion": "1.56.0", + "@algolia/monitoring": "1.56.0", + "@algolia/recommend": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/copy-anything": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^5.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/emoji-regex-xs": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", + "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/focus-trap": { + "version": "7.8.0", + "resolved": "https://registry.npmmirror.com/focus-trap/-/focus-trap-7.8.0.tgz", + "integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tabbable": "^6.4.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmmirror.com/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-what": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmmirror.com/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmmirror.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/minisearch": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/minisearch/-/minisearch-7.2.0.tgz", + "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", + "dev": true, + "license": "MIT" + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/oniguruma-to-es": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", + "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex-xs": "^1.0.0", + "regex": "^6.0.1", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/preact": { + "version": "10.29.8", + "resolved": "https://registry.npmmirror.com/preact/-/preact-10.29.8.tgz", + "integrity": "sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + }, + "peerDependencies": { + "preact-render-to-string": ">=5" + }, + "peerDependenciesMeta": { + "preact-render-to-string": { + "optional": true + } + } + }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "dev": true, + "license": "MIT" + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.62.4", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.62.4.tgz", + "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.62.4", + "@rollup/rollup-android-arm64": "4.62.4", + "@rollup/rollup-darwin-arm64": "4.62.4", + "@rollup/rollup-darwin-x64": "4.62.4", + "@rollup/rollup-freebsd-arm64": "4.62.4", + "@rollup/rollup-freebsd-x64": "4.62.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.4", + "@rollup/rollup-linux-arm-musleabihf": "4.62.4", + "@rollup/rollup-linux-arm64-gnu": "4.62.4", + "@rollup/rollup-linux-arm64-musl": "4.62.4", + "@rollup/rollup-linux-loong64-gnu": "4.62.4", + "@rollup/rollup-linux-loong64-musl": "4.62.4", + "@rollup/rollup-linux-ppc64-gnu": "4.62.4", + "@rollup/rollup-linux-ppc64-musl": "4.62.4", + "@rollup/rollup-linux-riscv64-gnu": "4.62.4", + "@rollup/rollup-linux-riscv64-musl": "4.62.4", + "@rollup/rollup-linux-s390x-gnu": "4.62.4", + "@rollup/rollup-linux-x64-gnu": "4.62.4", + "@rollup/rollup-linux-x64-musl": "4.62.4", + "@rollup/rollup-openbsd-x64": "4.62.4", + "@rollup/rollup-openharmony-arm64": "4.62.4", + "@rollup/rollup-win32-arm64-msvc": "4.62.4", + "@rollup/rollup-win32-ia32-msvc": "4.62.4", + "@rollup/rollup-win32-x64-gnu": "4.62.4", + "@rollup/rollup-win32-x64-msvc": "4.62.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/search-insights": { + "version": "2.17.3", + "resolved": "https://registry.npmmirror.com/search-insights/-/search-insights-2.17.3.tgz", + "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/shiki": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/shiki/-/shiki-2.5.0.tgz", + "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.5.0", + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/langs": "2.5.0", + "@shikijs/themes": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tabbable": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/tabbable/-/tabbable-6.5.0.tgz", + "integrity": "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmmirror.com/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitepress": { + "version": "1.6.4", + "resolved": "https://registry.npmmirror.com/vitepress/-/vitepress-1.6.4.tgz", + "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/css": "3.8.2", + "@docsearch/js": "3.8.2", + "@iconify-json/simple-icons": "^1.2.21", + "@shikijs/core": "^2.1.0", + "@shikijs/transformers": "^2.1.0", + "@shikijs/types": "^2.1.0", + "@types/markdown-it": "^14.1.2", + "@vitejs/plugin-vue": "^5.2.1", + "@vue/devtools-api": "^7.7.0", + "@vue/shared": "^3.5.13", + "@vueuse/core": "^12.4.0", + "@vueuse/integrations": "^12.4.0", + "focus-trap": "^7.6.4", + "mark.js": "8.11.1", + "minisearch": "^7.1.1", + "shiki": "^2.1.0", + "vite": "^5.4.14", + "vue": "^3.5.13" + }, + "bin": { + "vitepress": "bin/vitepress.js" + }, + "peerDependencies": { + "markdown-it-mathjax3": "^4", + "postcss": "^8" + }, + "peerDependenciesMeta": { + "markdown-it-mathjax3": { + "optional": true + }, + "postcss": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.41", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.41.tgz", + "integrity": "sha512-2laE0p+aK+/AOPG/XL/WepOs/GlK755LJ1XECi9kDUrz1FKNw8rb2Xzlw9JS1rqEV55nb0ttsKxVlTCcd+R5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.41", + "@vue/compiler-sfc": "3.5.41", + "@vue/runtime-dom": "3.5.41", + "@vue/server-renderer": "3.5.41", + "@vue/shared": "3.5.41" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d2827f8 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "blog-site", + "version": "1.0.0", + "description": "DPDK → DPU Learning Blog — powered by VitePress", + "type": "module", + "scripts": { + "dev": "vitepress dev", + "build": "vitepress build", + "preview": "vitepress preview" + }, + "keywords": ["dpdk", "dpu", "network", "learning", "blog"], + "author": "wxj", + "license": "MIT", + "devDependencies": { + "vitepress": "^1.6.4" + } +} diff --git a/products/home-gateway.md b/products/home-gateway.md new file mode 100755 index 0000000..34970d1 --- /dev/null +++ b/products/home-gateway.md @@ -0,0 +1,349 @@ +# 家用 DPDK 网络加速器 — 产品规划文档 + +> **创建日期**: 2026-07-13 +> **关联学习路线**: `2026-06-29-dpdk-roadmap-v3-design.md` +> **目标**: 基于 DPDK 做一个低成本家用网络加速器,可以量产销售 + +--- + +## 一、产品定义 + +| 项目 | 内容 | +|------|------| +| 产品名 | 家用网络加速器(暂定) | +| 定位 | 插在光猫和路由器之间,即插即用 | +| 一句话卖点 | "让千兆宽带真正跑满,全家不卡,不用设置" | +| 目标用户 | 家庭用户,不折腾 | +| 参考售价 | ¥299(100 台试水,单台毛利 ~¥118) | + +### 使用方式 + +``` +原来: 光猫 ─── 路由器 ─── 全家设备 + ↑ 光猫 NAT 性能差,有人下载全家卡 + +加盒子: 光猫 ─── [你的盒子] ─── 路由器 ─── 全家设备 + ↑ ↑ + 桥模式/无感透传 DPDK 线速 + 智能 QoS +``` + +--- + +## 二、三大核心功能 + +| # | 功能 | 痛点 | 实现原理 | +|---|------|------|---------| +| 1 | **提速** | 运营商光猫 NAT 差,千兆宽带只能跑 600M | 光猫改桥接,盒子 DPDK 线速 NAT/转发,跑满 940M | +| 2 | **不卡** | 有人下载/BT/看视频,其他人游戏延迟爆炸 | DPDK rte_flow 按流特征分类:小包优先、大包限速、视频保底 | +| 3 | **零配置** | 用户不想进后台、不想学网络知识 | 开机自动 DHCP 探测 WAN/LAN 方向,默认桥模式透传 | + +### 功能 2 详细:智能流控 + +``` +收包 → rte_flow 五元组分类 + │ + ├── 小包持续流 (<256B, >100pps) + │ → 标记 "游戏/通话/视频会议" + │ → 高优先级队列,绝对优先 + │ + ├── 大包持续流 (>1024B, >50Mbps) + │ → 标记 "下载/BT/视频缓冲" + │ → 低优先级队列,带宽上限 + │ + └── 其他 + → 普通优先级,尽力转发 +``` + +> 关键设计:不识别具体应用(不需要 DPI),只靠流特征判断。简单、可靠、私密。 + +--- + +## 三、硬件 BOM(最低成本,支持 VFIO) + +### 选型理由 + +- **J3355B-ITX**: Apollo Lake 起全系支持 VT-d,这是 Intel 平台最便宜的 VT-d 方案 +- **Intel 82576**: DPDK igb PMD 原生驱动,最老最稳定的网卡之一,二手极便宜 +- **VFIO > UIO**: VFIO 是 DPDK 官方推荐驱动方式,内核主线永久支持,DMA 安全隔离 + +### 逐项 BOM + +| # | 零件 | 规格 | 来源 | 单价 | +|---|------|------|------|------| +| 1 | 主板 | ASRock J3355B-ITX (双核 2.0GHz, 10W, VT-d) | 闲鱼 | ¥100 | +| 2 | 内存 | DDR3L 2GB SODIMM | 闲鱼拆机 | ¥12 | +| 3 | 存储 | 8GB mSATA SSD | 闲鱼拆机 | ¥10 | +| 4 | 网卡 | Intel 82576 双口千兆 (PCIe 2.0 x4) | 闲鱼拆机 | ¥30 | +| 5 | 电源 | 12V 3A 适配器 + DC 5.5×2.5mm 线 | 1688 批发 | ¥8 | +| 6 | 外壳 | 3mm 亚克力激光切割 (17×17cm ITX) | 淘宝定制 | ¥10 | +| 7 | 铜柱+螺丝 | M3×6 一套 4 颗 | 1688 | ¥2 | +| 8 | 导热垫 | 40×40×1mm 硅胶 | — | ¥1 | +| | | | **合计** | **¥173** | + +### 备选主板 + +| 型号 | CPU | 核数 | VT-d | 价格 | 备注 | +|------|-----|------|------|------|------| +| ASRock J3355B-ITX | J3355 | 双核 | ✅ | ¥100 | **首选,最便宜 VT-d** | +| ASRock J3455-ITX | J3455 | 四核 | ✅ | ¥140 | J3355 缺货时备选 | +| ASRock J4205-ITX | J4205 | 四核 | ✅ | ¥160 | 第二备选 | + +### 内存使用分析 + +``` +DPDK hugepage: 64MB +Alpine Linux: ~128MB +DPDK app: ~50MB +空闲缓冲: ~1.7GB +总计 (2GB): ~300MB 使用 / ~1.7GB 空闲 + +结论: 2GB 绰绰有余,不需要 4GB +``` + +### 存储使用分析 + +``` +Alpine Linux: ~130MB ISO → 安装后 ~50MB +DPDK 库: ~20MB +应用二进制: ~5MB +配置/日志: ~10MB +总计: <100MB + +结论: 8GB mSATA 完全够用 +``` + +--- + +## 四、软件技术栈 + +| 层 | 组件 | 说明 | +|----|------|------| +| 操作系统 | Alpine Linux 3.20 | 最小 Linux 发行版,~50MB | +| 内核 | linux-lts 6.6 | VFIO、hugepage、AF_PACKET 主线支持 | +| 数据面框架 | DPDK 24.11 LTS | 用户态高性能包处理 | +| PMD 驱动 | `drivers/net/igb` | Intel 82576 原生 DPDK Poll Mode Driver | +| 设备绑定 | `vfio-pci` | VT-d + IOMMU,DPDK 官方推荐方式 | +| 转发 | 自研 DPDK 应用 | L3 转发 + NAT + rte_flow QoS | +| 控制面 | Unix socket + HTTP API | 配置下发、状态查询 | +| 初始化 | 自定义 init 脚本 | DHCP 探测、自动识别 WAN/LAN | + +### DPDK 技术点与学习路线对应 + +| DPDK 技术 | 用途 | 学习路线阶段 | +|-----------|------|------------| +| `rte_mempool` | 包内存池管理 | P1 | +| `rte_mbuf` | 包数据结构 | P1 | +| `rte_ring` | lcore 间无锁通信 | P1 | +| `rte_eal_init` | EAL 初始化 (hugepage/lcore) | P1 | +| `rte_ethdev` (igb PMD) | 82576 网卡收发 | P1 | +| `rte_eth_tx_burst` / `rte_eth_rx_burst` | 批量收发包 | P1 | +| `rte_flow` | 流分类 (小包/大包分流) | P3 | +| `rte_hash` | NAT 连接跟踪表 | P1 | +| `rte_lcore` | 核绑定 (控制面/数据面分离) | P1 | +| `rte_kni` | DPDK↔内核通道 (可选) | P2 | + +--- + +## 五、数据面架构 + +### 整体架构:快慢路径分离 + +``` + ┌────────────────────┐ + │ 控制面 (lcore 0) │ + │ │ + │ DHCP Client │ ← 自动探测 WAN 口 + │ 配置管理 │ + │ HTTP API (可选) │ ← 手机配置用 + │ 日志/统计 │ + └────────┬───────────┘ + │ + Unix socket + │ + ┌────────────────────────┼────────────────────────┐ + │ │ │ + ▼ ▼ ▼ +WAN 口 (port 0) 转发核 (lcore 1) LAN 口 (port 1) + │ │ │ + ▼ ▼ ▼ +┌─────────┐ ┌───────────────┐ ┌─────────┐ +│ RX 队列 │──mbuf──►│ rte_flow 分流 │──mbuf──►│ TX 队列 │ +│ │ │ │ │ │ +│ 82576 │ │ 小包→高优先 │ │ 82576 │ +│ igb PMD │ │ 大包→低优先 │ │ igb PMD │ +│ │ │ NAT 连接跟踪 │ │ │ +└─────────┘ └───────────────┘ └─────────┘ +``` + +### 收发包路径 + +``` +收包: + rte_eth_rx_burst(port, queue, mbufs, BURST_SIZE) + → 逐包处理: + 1. rte_flow 匹配(五元组 hash) + 2. 如果不是已知流 → 新建流表项 + 3. 按流优先级入队 + 4. NAT 转换 (SNAT/DNAT) + 5. 查路由表决定出端口 + +发包: + 高优先队列 → rte_eth_tx_burst (先发) + 低优先队列 → rte_eth_tx_burst (后发,有剩余带宽才发) +``` + +### Lcore 分配 + +``` +lcore 0 (主核): + - EAL 初始化 + - 控制面逻辑 + - 定时器 (统计收集) + - Unix socket 服务 + +lcore 1 (转发核): + - 数据面死循环 + - 收包 → 分流 → NAT → 发包 + - 独占 100% CPU,无中断干扰 +``` + +--- + +## 六、软件模块划分 + +``` +dpdk-gateway/ +├── src/ +│ ├── main.c # EAL 初始化,启动 lcore +│ ├── forward.c # 转发核主循环 +│ ├── forward.h +│ ├── nat.c # NAT 连接跟踪 (SNAT/DNAT) +│ ├── nat.h +│ ├── classifier.c # rte_flow 流分类 + 优先级队列 +│ ├── classifier.h +│ ├── control.c # 控制面 (DHCP 探测 + 配置) +│ ├── control.h +│ ├── stats.c # 统计收集 +│ └── stats.h +├── scripts/ +│ ├── init.sh # 启动脚本 (配 hugepage/绑定网卡) +│ └── probe.sh # WAN/LAN 自动探测脚本 +├── config/ +│ └── gateway.conf # 默认配置文件 +├── Makefile # 或 meson.build +└── README.md +``` + +--- + +## 七、量产降本路径 + +| 阶段 | 数量 | 主板来源 | 网卡来源 | 单台 BOM | +|------|------|---------|---------|---------| +| 样机验证 | 1 台 | 闲鱼 J3355B ¥100 | 闲鱼 82576 ¥30 | ¥173 | +| 小批试水 | 100 台 | 闲鱼打包 ¥90 | 闲鱼打包 ¥25 | ¥146 | +| 中批 | 500 台 | 批发商 ¥85 | 批发 ¥22 | ¥130 | +| 大批定制 | 1000 台 | 自研板 (I226 焊板) | 芯片直焊 | ~¥120 | + +> 1000 台定制板后,Intel I226-V 网卡芯片直接焊主板上,省去独立网卡和 PCIe 槽。 + +--- + +## 八、成本与利润测算 + +### 100 台试水 + +| 项目 | 金额 | +|------|------| +| 单台 BOM | ¥146 | +| 单台组装(自己干) | ¥0 | +| 包装盒(定制牛皮纸,100 个) | ¥3/台 | +| 快递费(均摊) | ¥5/台 | +| 单台全成本 | ¥154 | +| **售价** | **¥299** | +| **单台毛利** | **¥145** | +| **100 台总毛利** | **¥14,500** | + +### 1000 台(定制板) + +| 项目 | 金额 | +|------|------| +| 单台 BOM | ¥120 | +| 组装(深圳厂 ¥15/台) | ¥15 | +| 包装盒 | ¥3 | +| 快递 | ¥5 | +| 单台全成本 | ¥143 | +| **售价** | **¥299** | +| **单台毛利** | **¥156** | +| **1000 台总毛利** | **¥156,000** | + +--- + +## 九、下一步行动计划 + +``` +Phase 1: 样机验证 (1-2 周) + □ 1. 采购 J3355B-ITX + Intel 82576 + DDR3L 2GB + mSATA 8GB + □ 2. 安装 Alpine Linux,配置 VFIO + hugepage + □ 3. 编译 DPDK,跑通 testpmd L2 转发 + □ 4. 编写最简单的 DPDK L3 转发程序 (main.c + forward.c) + +Phase 2: 核心功能 (2-4 周) + □ 5. 实现 NAT (SNAT/DNAT + 连接跟踪) + □ 6. 实现 rte_flow 流分类 (小包/大包分流) + □ 7. 实现优先级队列调度 + □ 8. 实现 WAN/LAN 自动探测 (DHCP) + +Phase 3: 产品化 (2-4 周) + □ 9. 添加启动脚本 (init.sh + probe.sh) + □ 10. 添加 LED 状态指示 (通过 GPIO 或内核接口) + □ 11. 制作 Alpine 固件镜像 (dd 可刷) + □ 12. 设计外壳图纸 (DXF for 亚克力切割) + +Phase 4: 自用验证 (4 周) + □ 13. 替换家中路由器,自己用 1 个月 + □ 14. 记录所有问题和改进点 + □ 15. 性能压测 (iperf3 小包/大包) + +Phase 5: 开源 + 销售 + □ 16. GitHub 开源基础版 + □ 17. 恩山论坛 / chiphell 发帖 + □ 18. 淘宝/闲鱼上架 +``` + +--- + +## 十、风险评估 + +| 风险 | 严重度 | 概率 | 应对措施 | +|------|--------|------|---------| +| 二手主板货源不稳定 | 🟡 中 | 中 | 初期囤 100 片;J3455-ITX 备选;1000 台后走定制 | +| 用户不会改光猫桥接 | 🔴 高 | 高 | 默认纯桥模式透传,不改任何配置也能工作(仅提速效果打折) | +| 2 口不够替代路由器 | 🟡 中 | 高 | 就定位加速器,不定位路由器;明确告诉用户"插在光猫和路由器之间" | +| GPL 合规问题 | 🟢 低 | 低 | DPDK 用户态程序 BSD 协议,Alpine Linux 用 GPL 但无需修改内核 | +| 售后工作量大 | 🔴 高 | 中 | 初期配详细 LED 故障灯说明、FAQ 文档;逐步建立知识库 | +| 被友商抄袭 | 🟡 中 | 中 | 开源 + 社区粘性 + 持续更新;品牌信任是壁垒 | +| 3C 认证 (做硬件后) | 🔴 高 | 中 | 初期只卖固件/散件/DIY 套件;量大了再走认证 | + +--- + +## 十一、竞品对比 + +| 对比维度 | 运营商光猫 | 小米路由器 | 软路由 (iStoreOS) | **你的盒子** | +|---------|-----------|-----------|-----------------|------------| +| NAT 性能 (千兆) | ~600M | ~940M | ~940M | **~940M (线速)** | +| 满载延迟 (小包) | 200ms+ | 80ms+ | 50ms+ | **<20ms** | +| 智能流控 | ❌ | 基础 QoS | 需手动配置 | **自动,零配置** | +| 配置难度 | 不用 (默认烂) | 中等 | 高 | **不用配** | +| 售价 | ¥0 (送的) | ¥300 | ¥400+ | **¥299** | +| 开源 | ❌ | ❌ | 部分 | **✅ 核心开源** | + +--- + +## 十二、相关资源 + +- **学习路线文档**: `2026-06-29-dpdk-roadmap-v3-design.md` +- **加速路径文档**: `DPDK_ACCELERATED_PATH.md` +- **DPDK 源码**: `~/myshare/dpdk-stable-24.11.6` +- **内核对照**: `~/myshare/linux-stable` (v6.6) +- **性能压测工具**: `~/myshare/dperf-main` diff --git a/products/index.md b/products/index.md new file mode 100644 index 0000000..a05129c --- /dev/null +++ b/products/index.md @@ -0,0 +1,23 @@ +# 产品设计文档 + +## DPDK Home Gateway 网络加速器 + +→ [完整产品方案](/products/home-gateway) + +基于 DPDK 的家庭网关网络加速方案,包括: + +- 硬件选型与 BOM 清单 +- 定价策略与市场分析 +- 分阶段研发计划 +- 竞品对比分析 + +## uNOS — 用户态网络操作系统 + +→ [设计规格书](/products/unos-design) | [学习方法论](/products/unos-method) + +uNOS (user-space Network Operating System) 是一个生产级的用户态网络操作系统设计: + +- **三层剥洋葱法**: 从 Linux 内核到 DPDK,再到 DPU +- **架构设计**: 控制平面 / 数据平面分离 +- **核心模块**: 协议栈、转发引擎、硬件抽象层 +- **对标**: Cisco IOS-XR / Juniper JunOS 架构 diff --git a/products/unos-design.md b/products/unos-design.md new file mode 100755 index 0000000..61e9860 --- /dev/null +++ b/products/unos-design.md @@ -0,0 +1,1735 @@ +# uNOS 详细设计说明书 + +> **版本**: v2.0 — 生产级架构 (RSS 多核 + Graph Pipeline + 内联 TCP) +> **日期**: 2026-07-29 +> **关联文档**: `uNOS_REQUIREMENTS.md`(需求分析)、`lazy-puzzling-rocket.md`(开发执行计划) + +--- + +## 目录 + +1. [设计概述](#一设计概述) +2. [架构设计](#二架构设计) +3. [双产品设计](#三双产品设计) +4. [数据面详细设计](#四数据面详细设计) +5. [控制面详细设计](#五控制面详细设计) +6. [自研 TCP/IP 协议栈详细设计](#六自研-tcpip-协议栈详细设计) +7. [容器网络详细设计](#七容器网络详细设计) +8. [管理面详细设计](#八管理面详细设计) +9. [高可用详细设计](#九高可用详细设计) +10. [构建系统设计](#十构建系统设计) +11. [测试设计](#十一测试设计) +12. [接口契约](#十二接口契约) + +--- + +## 一、设计概述 + +### 1.1 设计目标 + +设计并实现一个**自研用户态网络操作系统(uNOS)**,核心特性: + +- **自研 TCP/IP 协议栈**:从 RFC 793 状态机到 CUBIC 拥塞控制,全部手写 +- **DPDK 高性能数据面**:L2/L3 线速转发 + NAT + ACL + QoS +- **双产品架构**:同一份源码,编译出 Cloud Edge(T140 容器网关)和 Home CPE(J3455 家庭网关) +- **容器网络支持**:memif 零拷贝接入容器 +- **FRR 动态路由集成**:BGP/OSPF 路由自动同步到数据面 +- **完整管理面**:CLI + REST API + Web Dashboard + +### 1.2 产品矩阵 + +| | uNOS Cloud Edge | uNOS Home CPE | +|---|---|---| +| 目标硬件 | Dell T140 (Xeon E-2200, 8GB+) | J3455B-ITX (Celeron, 2GB) | +| 网卡 | X540-T2 10G × 2 + E1G42ET 1G × 4 | E1G42ET 1G × 2 | +| 容器支持 | ✅ memif + CNI | ❌ | +| BGP | ✅ full table | ❌ | +| OSPF | ✅ | ✅ stub | +| HA | ✅ VRRP | ❌ | +| 应用层服务 | ✅ HTTP/DNS/LB | ❌ | +| 编译宏 | `PRODUCT_CLOUD_EDGE` | `PRODUCT_HOME_CPE` | + +### 1.3 设计原则 + +1. **零拷贝优先**:数据面路径上不产生任何 mbuf 拷贝 +2. **编译时剪裁**:产品差异通过 `#ifdef` 在编译时决定,不在运行时判断 +3. **固定 Pipeline**:转发路径不动态改变模块注册顺序,保证分支预测友好 +4. **Cache Line 对齐**:所有热路径数据结构按 64B 对齐,避免 false sharing +5. **错误即崩溃**:数据面关键路径不检查返回值(如 mbuf 分配),由预分配 mempool 保证;控制面优雅处理所有错误 + +--- + +## 二、架构设计 + +### 2.1 系统分层 + +``` +┌─────────────────────────────────────────────────────────┐ +│ Management Plane │ +│ CLI (vtysh) │ REST API │ Web Dashboard │ Prometheus │ +│ │ │ +│ Redis DB (配置存储 + Pub/Sub) │ +└──────────────────────────┬──────────────────────────────┘ + │ Unix socket / TCP +┌──────────────────────────┼──────────────────────────────┐ +│ Control Plane (lcore 0) │ +│ │ +│ Config Manager │ Route Manager │ Interface Manager │ +│ ARP/NDP │ DHCP │ Health Monitor │ +│ CNI Manager ◄── Cloud Edge only │ +│ │ │ +│ IPC (rte_ring) │ +└──────────────────────────┼──────────────────────────────┘ + │ +┌──────────────────────────┼──────────────────────────────┐ +│ Data Plane (lcore 1) │ +│ │ +│ ┌────────────────────────────────────────────────────┐ │ +│ │ Forwarding Pipeline │ │ +│ │ RX → Parser → Classifier → ACL → LPM → NAT → QoS │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ TX (physical / memif) │ │ +│ └────────────────────────────────────────────────────┘ │ +│ │ +│ ┌────────────────────────────────────────────────────┐ │ +│ │ 自研 TCP/IP Stack (lcore 2) │ │ +│ │ TCP State Machine → Congestion → Retransmit → TX │ │ +│ └────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────┘ +``` + +### 2.2 lcore 分配 + +#### Cloud Edge (T140, E-2236 6C/12T) + +``` +Core 0 ─→ lcore 0: Control Plane (main thread, 不参与数据面轮询) + ├── Config Manager (YAML/Redis) + ├── Route Manager (FRR FPM Netlink) + ├── Interface Manager (UP/DOWN/MTU/Stats) + ├── ARP/NDP Manager + ├── CNI Manager (memif create/delete, IPAM) + ├── Health Monitor (rte_timer 5s) + ├── CLI + REST Server (基于自研 TCP 栈) + └── Flow Table GC Worker (全局老化) + +Core 1-2 ─→ lcore 1-2: Data Plane Workers (RSS 对称,每个核独立) + 每个 worker 的数据面: + ┌─────────────────────────────────────────────┐ + │ RX (本 worker 独占的 RSS queues) │ + │ │ │ + │ ┌─────────┴──────────┐ │ + │ │ Graph Pipeline │ ← 独立转发实例 │ + │ │ (per-core graph) │ │ + │ └─────────┬──────────┘ │ + │ │ │ + │ TX (本 worker 独占的 TX queues) │ + │ │ + │ Per-core 状态 (无需锁): │ + │ - Flow Cache (rte_hash) │ + │ - Connection Track Table │ + │ - TCP Stack (集成在 Graph 中) │ + │ - ARP Cache (只读,控制面写入) │ + │ - LPM Route Table (只读,控制面写入) │ + └─────────────────────────────────────────────┘ + + RSS 保证同一五元组永远落在同一个 worker 上, + 因此 per-core 的状态表天然无锁。 + +Core 3 ─→ lcore 3: TCP App Server (从数据面 worker 中接应用层流量) + └── HTTP Server / DNS Server / TCP Proxy + 通过 per-core ring 从数据面 TCP 栈接收应用层请求 + +Core 4 ─→ (非 DPDK) FRR 进程 (bgpd + ospfd) + └── taskset 绑定,通过 Netlink FPM 推送路由到控制面 + +Core 5 ─→ (非 DPDK) 可观测 + Redis + 系统 + └── Prometheus exporter / sFlow / 日志 / Redis Server + +HT 线程 (6 个) ─→ 内核中断 + 辅助任务 + └── isolcpus 隔离数据面核心 (core 1-3) +``` + +#### Home CPE (J3455, 4 cores) + +``` +Core 0 ─→ lcore 0: Control Plane (main thread) + ├── Config Manager / Route Manager (ospfd FPM) + ├── ARP Manager / DHCP Client / Health Monitor + └── CLI (Unix socket) + +Core 1 ─→ lcore 1: Data Plane Worker (单核,1G 线速足够) + └── 物理口 (82576 × 2) RSS queue 0 + Graph Pipeline: L2/L3/NAT/ACL/QoS + Per-core Flow Cache + Connection Track Table + (不包含 TCP 栈 — Home CPE 不跑应用层服务) + +Core 2 ─→ (非 DPDK) FRR ospfd + +Core 3 ─→ 空闲 / Prometheus 轻量 exporter +``` + +### 2.3 模块依赖图 + +``` + ┌─────────────────┐ + │ main.c │ + │ EAL init │ + │ lcore launch │ + └────────┬────────┘ + │ + ┌────────────────┼────────────────┐ + ▼ ▼ ▼ + ┌──────────┐ ┌──────────┐ ┌──────────────┐ + │pipeline.c│ │control.c │ │tcp_main.c │ ← Cloud Edge only + │(lcore 1) │ │(lcore 0) │ │(lcore 2) │ + └────┬─────┘ └────┬─────┘ └──────┬───────┘ + │ │ │ + ┌──────┼──────┐ ┌────┼──────┐ ┌─────┼──────┐ + ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ + parser l3fwd nat arp dhcp iface ipv4 tcp_state http + l2fwd acl qos ndp frr db icmp tcp_cong dns + (Redis) frag tcp_retx proxy + │ tcp_flow + ┌─────┘ tcp_socket + ▼ + Redis Server + (external process) +``` + +--- + +## 三、双产品设计 + +### 3.1 编译时产品选择 + +```meson +# meson_options.txt +option('product', type: 'combo', + choices: ['cloud_edge', 'home_cpe'], + value: 'cloud_edge', + description: 'Target product: cloud_edge (T140) or home_cpe (J3455)') + +# src/meson.build +product = get_option('product') + +product_flags = [] +if product == 'cloud_edge' + product_flags += '-DPRODUCT_CLOUD_EDGE' + sources += files( + 'cni/memif.c', + 'cni/cni_api.c', + 'cni/ipam.c', + 'app/http_server.c', + 'app/dns_server.c', + 'app/tcp_proxy.c', + 'stcp/tcp_socket.c', + 'stcp/tcp_txrx.c', + 'ha/vrrp.c', + ) +elif product == 'home_cpe' + product_flags += '-DPRODUCT_HOME_CPE' + # Home CPE excludes: CNI, HTTP Server, DNS, TCP Proxy, + # VRRP, and full TCP socket layer +endif +``` + +### 3.2 模块编译矩阵 + +``` +模块 Cloud Edge Home CPE 说明 +──── ────────── ──────── ── +src/core/pipeline.c ✅ ✅ 共享 +src/core/ipc.c ✅ ✅ 共享 +src/core/main.c ✅ ✅ 共享 (lcore 分配不同) +src/dp/parser.c ✅ ✅ 共享 +src/dp/l2fwd.c ✅ ✅ 共享 +src/dp/l3fwd.c ✅ ✅ 共享 +src/dp/nat.c ✅ ✅ 共享 +src/dp/acl.c ✅ ✅ 共享 +src/dp/qos.c ✅ ✅ 共享 +src/cp/arp.c ✅ ✅ 共享 +src/cp/iface.c ✅ ✅ 共享 +src/cp/dhcp.c ❌ ✅ Home CPE only +src/cp/frr_sync.c ✅ ✅ 共享 (协议不同) +src/cp/db.c ✅ ✅ 共享 +src/stcp/ipv4.c ✅ ❌ Cloud Edge only +src/stcp/tcp_state.c ✅ ❌ Cloud Edge only +src/stcp/tcp_cong.c ✅ ❌ Cloud Edge only +src/stcp/tcp_retx.c ✅ ❌ Cloud Edge only +src/stcp/tcp_flow.c ✅ ❌ Cloud Edge only +src/stcp/tcp_socket.c ✅ ❌ Cloud Edge only +src/cni/memif.c ✅ ❌ Cloud Edge only +src/cni/cni_api.c ✅ ❌ Cloud Edge only +src/cni/ipam.c ✅ ❌ Cloud Edge only +src/app/http_server.c ✅ ❌ Cloud Edge only +src/app/dns_server.c ✅ ❌ Cloud Edge only +src/app/tcp_proxy.c ✅ ❌ Cloud Edge only +src/mgmt/cli.c ✅ ✅ 共享 (命令集不同) +src/mgmt/rest.c ✅ ❌ Cloud Edge only +src/ha/vrrp.c ✅ ❌ Cloud Edge only +src/telemetry/prometheus.c ✅ ✅ 共享 (指标集不同) +``` + +### 3.3 关键差异点 + +```c +// src/core/main.c — lcore 分配 + +void unos_launch_lcores(void) { +#ifdef PRODUCT_CLOUD_EDGE + // T140: 6+ cores, full allocation + rte_eal_remote_launch(dataplane_forward_loop, NULL, 1); // lcore 1 + rte_eal_remote_launch(tcp_stack_loop, NULL, 2); // lcore 2 + rte_eal_remote_launch(memif_rx_loop, NULL, 3); // lcore 3 (optional) + // lcore 0: control plane (main thread) + // lcore 4-5: FRR + observability (external processes pinned) +#else + // J3455: 4 cores, minimal allocation + rte_eal_remote_launch(dataplane_forward_loop, NULL, 1); // lcore 1 + // lcore 0: control plane (main thread) + // lcore 2: FRR ospfd (external process pinned) + // lcore 3: idle +#endif +} +``` + +--- + +## 四、数据面详细设计(生产级:RSS 多核 + Graph Pipeline) + +### 4.0 架构决策 + +``` +为什么不用固定 Pipeline(v1.0 设计): + ❌ 所有包走完全部阶段,即使不需要 + ❌ 单核转发,无法线性扩展 + ❌ TCP 栈在独立 lcore,跨核 IPC 引入延迟 + cache miss + +为什么用 RSS + Graph Pipeline(v2.0 生产级): + ✅ RSS 把流量按五元组分散到多核,每个核独立运行,无锁、 + 线性扩展,加核就加吞吐 + ✅ Graph DAG 按包类型走不同路径,ARP 包跳过 NAT, + 本地包直接进 TCP 栈,不经过多余阶段 + ✅ TCP 栈作为 Graph 节点内联在数据面,本机包不跨核 + ✅ Per-core Flow Cache 跳过 LPM 查找(首包查路由, + 后续包命中 cache 直接转发) + ✅ 对标 VPP (FD.io) 的 Graph Node 架构,业界验证过 +``` + +### 4.1 Graph Pipeline 核心数据结构 + +```c +// src/dp/graph.h + +#define MAX_NEXT_NODES 8 +#define NODE_NAME_LEN 32 + +// 前向声明 +struct graph_node; + +// Graph Node 处理函数签名: +// 输入: mbuf 数组 + 包计数 +// 输出: 每个包走哪个 next_nodes[idx] 的索引数组 +// 返回: 实际处理的包数 +typedef uint16_t (*graph_node_process_fn_t)( + struct rte_mbuf **mbufs, + uint16_t nb_pkts, + uint8_t *next_indices // [nb_pkts]: 每个包的下一跳 +); + +struct graph_node { + char name[NODE_NAME_LEN]; + graph_node_process_fn_t process; + uint16_t next_count; + struct graph_node *next_nodes[MAX_NEXT_NODES]; + char *next_labels[MAX_NEXT_NODES]; // 分支标签 + + // 统计 (per-lcore, cacheline 对齐) + uint64_t pkts_processed; + uint64_t cycles_spent; + uint64_t pkts_dropped; +} __rte_cache_aligned; + +// Graph 路由辅助宏: 让 process_fn 返回的 next_indices 更有语义 +enum { + NEXT_DROP = 0, // 丢包 + NEXT_TX = 1, // 直接发送 + NEXT_L2 = 2, // L2 转发 + NEXT_L3 = 3, // L3 转发 + NEXT_NAT = 4, // NAT 转换 + NEXT_QOS = 5, // QoS 调度 + NEXT_TCP = 6, // 进 TCP 栈 + NEXT_ARP = 7, // ARP 处理 +}; + +// 每个 worker 拥有独立的 Graph 实例 +struct graph_worker { + struct rte_mbuf *rx_bufs[BURST_SIZE]; // 收包暂存 + struct rte_mbuf *tx_bufs[RTE_MAX_ETHPORTS][BURST_SIZE]; + uint16_t tx_counts[RTE_MAX_ETHPORTS]; + + struct rte_mbuf *tcp_rx_bufs[BURST_SIZE]; // TCP 栈的收包队列 + + // Per-core 状态表(无锁——RSS 保证流的亲和性) + struct flow_cache *flow_cache; // 流缓存 + struct conntrack_table *ct_table; // 连接跟踪 + struct tcp_stack *tcp_stack; // TCP 协议栈实例 + + // 全局只读状态(控制面写入,数据面只读) + struct rte_lpm *lpm_ipv4; // 路由表 (RSS workers 共享) + struct arp_cache *arp_cache; // ARP 表 (RSS workers 共享) + struct acl_engine *acl; // ACL (RSS workers 共享) +} __rte_cache_aligned; +``` + +### 4.2 Graph 拓扑(完整转发图) + +``` + ┌─────────┐ + │ RX │ + │ (NIC RX │ + │ queues)│ + └────┬────┘ + │ + ┌────┴────┐ + │ Parser │ ← 所有包的入口 + │ L2-L4 │ + └────┬────┘ + │ + ┌───────┼───────────┐ + ▼ ▼ ▼ + dst_is_me ARP pkt L2 (MAC查表) + │ │ │ + ▼ ▼ ▼ + ┌────────┐ ┌────────┐ ┌─────────┐ + │Classify│ │ARP │ │L2 Fwd │ + │本机流 │ │Handler │ │MAC学习 │ + └───┬────┘ └───┬────┘ └────┬────┘ + │ │ │ + ┌────┬────┤ ▼ ▼ + ▼ ▼ ▼ ┌────────┐ ┌─────────┐ + TCP DNS HTTP │ TX │ │ TX │ + Stack Server Server └────────┘ └─────────┘ + │ + ▼ (TCP栈处理后) + ┌──────────┐ + │ TX │ + │ (本机发包)│ + └──────────┘ + + ┌─────────────────┐ + │ dst != me │ ← 转发路径 + │ (非本机IP) │ + └────────┬────────┘ + │ + ┌───────┴───────┐ + ▼ ▼ + ┌──────────┐ ┌──────────┐ + │Flow Cache│ │ LPM │ ← 首包走 LPM,后续命中 cache + │(per-core)│ │(路由查找)│ + └────┬─────┘ └────┬─────┘ + │ │ + │ miss │ + └──────┬───────┘ + ▼ + ┌──────────┐ + │ ACL │ ← 安全策略 + └────┬─────┘ + │ + ┌──────────┼──────────┐ + ▼ ▼ ▼ + ┌────────┐ ┌──────────┐ ┌──────┐ + │ NAT │ │ No NAT │ │ DENY │ + │ SNAT/ │ │(纯转发) │ │(丢弃)│ + │ DNAT │ └────┬─────┘ └──┬───┘ + └───┬────┘ │ │ + │ │ rte_pktmbuf_free() + └─────┬─────┘ + ▼ + ┌──────────┐ + │ ARP │ ← 查下一跳 MAC + │ Lookup │ + └────┬─────┘ + │ + ▼ + ┌──────────┐ + │ QOS │ ← 入队 + 调度 + │8队列调度 │ + └────┬─────┘ + │ + ▼ + ┌──────────┐ + │ TX │ + └──────────┘ +``` + +### 4.3 数据面核心循环(每个 RSS worker 独立运行) + +```c +// src/dp/worker.c — 每个 lcore worker 的主循环 + +static __rte_always_inline int +dataplane_worker_loop(void *arg) { + struct graph_worker *gw = (struct graph_worker *)arg; + struct rte_mbuf *pkts[BURST_SIZE]; + uint8_t next_indices[BURST_SIZE]; + uint16_t nb_pkts; + + while (likely(!force_quit)) { + // ==== STEP 1: RX — 轮询本 worker 独占的 RSS queues ==== + uint16_t total_rx = 0; + for (int port = 0; port < gw->port_count; port++) { + nb_pkts = rte_eth_rx_burst(port, gw->queue_id, pkts + total_rx, + BURST_SIZE - total_rx); + if (nb_pkts == 0) continue; + + // 预取第一层 Graph Node(parser_node)的数据 + for (int i = 0; i < nb_pkts; i++) + rte_prefetch0(rte_pktmbuf_mtod(pkts[total_rx + i], void *)); + total_rx += nb_pkts; + } + if (unlikely(total_rx == 0)) continue; + + // ==== STEP 2: Graph Traversal — 每个包沿 DAG 路径前进 ==== + // 用 SIMD 批次追踪每个包当前所在的 node + struct graph_node *current_nodes[BURST_SIZE]; // 每个包当前在哪个 node + uint16_t active_mask = 0; // bitmask: 还有包没走完 + + // 所有包起点: Parser Node + struct graph_node *first = &gw->graph->parser_node; + for (int i = 0; i < total_rx; i++) { + current_nodes[i] = first; + active_mask |= (1 << i); + } + + while (active_mask) { + // 按当前 node 分组 → 同一 node 的包 batch 处理 + for (int batch_start = 0; batch_start < total_rx; ) { + if (!(active_mask & (1 << batch_start))) { + batch_start++; + continue; + } + + // 收集所有在同一个 node 的包 + struct graph_node *batch_node = current_nodes[batch_start]; + uint16_t batch_nb = 0; + struct rte_mbuf *batch_pkts[BURST_SIZE]; + + for (int i = batch_start; i < total_rx; i++) { + if (current_nodes[i] == batch_node) { + batch_pkts[batch_nb++] = pkts[i]; + active_mask &= ~(1 << i); // 从活跃集移除 + } + } + + // 对该 node 的包 batch 处理 + if (batch_node->next_count == 0) { + // 终端节点 (DROP / TX): 直接释放或发送 + if (batch_node == &gw->graph->tx_node) + gw_tx_burst(gw, batch_pkts, batch_nb); + else // drop_node + for (int i = 0; i < batch_nb; i++) + rte_pktmbuf_free(batch_pkts[i]); + } else { + // 内部节点: 调用 process_fn + uint16_t processed = batch_node->process( + batch_pkts, batch_nb, next_indices); + + // 更新每个包的下一跳 + for (int i = 0; i < processed; i++) { + uint8_t nidx = next_indices[i]; + if (nidx < batch_node->next_count) { + current_nodes[batch_start + i] = + batch_node->next_nodes[nidx]; + active_mask |= (1 << (batch_start + i)); + } else { + // 错误: 丢包 + rte_pktmbuf_free(batch_pkts[i]); + } + } + // 处理失败的包 + for (int i = processed; i < batch_nb; i++) + rte_pktmbuf_free(batch_pkts[i]); + } + + // 统计 + batch_node->pkts_processed += batch_nb; + batch_start = batch_start + batch_nb; // 跳过已分组的包 + } + + // 重新调整 active 包的索引 (释放掉的包对应的位置清空) + total_rx = compact_active_packets(pkts, current_nodes, total_rx, + &active_mask); + } + + // ==== STEP 3: TX — 批量发送所有出端口队列 ==== + for (int port = 0; port < gw->port_count; port++) { + if (gw->tx_counts[port] > 0) { + uint16_t sent = rte_eth_tx_burst(port, gw->queue_id, + gw->tx_bufs[port], + gw->tx_counts[port]); + if (unlikely(sent < gw->tx_counts[port])) { + for (int i = sent; i < gw->tx_counts[port]; i++) + rte_pktmbuf_free(gw->tx_bufs[port][i]); + } + gw->tx_counts[port] = 0; + } + } + } + return 0; +} +``` + +### 4.4 Per-Core Flow Cache(关键优化) + +每个 RSS worker 拥有独立的 Flow Cache,避免对 Established 流的重复 LPM 查找: + +```c +// src/dp/flow_cache.h + +// Flow Cache 条目 +struct flow_cache_entry { + uint32_t src_ip; + uint32_t dst_ip; + uint16_t src_port; + uint16_t dst_port; + uint8_t protocol; + uint8_t action; // ACTION_FWD / ACTION_NAT / ACTION_DROP + uint8_t out_port; // 出端口 + struct rte_ether_addr dst_mac; // 下一跳 MAC (不变时缓存) + uint32_t nat_ip; // NAT 后的地址 (不变时缓存) + uint16_t nat_port; // NAT 后的端口 + uint64_t last_seen; +} __rte_cache_aligned; + +// Flow Cache 查找 — 在 Parser 后直接调用 +// 命中 → 跳过 LPM + ACL + NAT,直接填 MAC + 发往 QoS → TX +int flow_cache_lookup(struct flow_cache *fc, struct parser_result *pr, + struct flow_cache_entry *result) { + uint32_t hash_key = pr->src_ip ^ pr->dst_ip ^ + (pr->src_port << 16 | pr->dst_port) ^ pr->protocol; + uint32_t bucket = hash_key & fc->mask; + + // 开放寻址 (cuckoo hash, 最多查 2 个 bucket) + // 命中 → O(1), no locks + // miss → 走完整 LPM + ACL + NAT 路径,然后插入 cache +} +``` + +### 4.5 核心数据结构(不变部分,续用 v1.0 设计) + +以下结构体保持 v1.0 设计,仅从固定 Pipeline 改为 Graph Node 使用: + +#### Parser Result (见 v1.0 的 4.1.3) + +```c +// src/dp/parser.h — 保持不变,但增加 graph_next 字段 +struct parser_result { + // ... (同 v1.0 设计) ... + + uint8_t graph_next; // 新增: Parser 根据包类型设置下一跳 + // PARSER_NEXT_L2 (ether_type == 0x0800 && dst_mac is not me) + // PARSER_NEXT_L3 (ether_type == 0x0800 && dst_mac is me) + // PARSER_NEXT_ARP (ether_type == 0x0806) + // PARSER_NEXT_DROP (unknown protocol) +}; +``` + +#### L3 Forwarder (见 v1.0 的 4.1.4) + +```c +// 不变,但变为所有 RSS worker 共享的只读数据 +// 控制面通过 IPC 更新 (RCU 模式: 控制面写入新副本,数据面原子切换指针) +``` + +#### NAT / Connection Track (见 v1.0 的 4.1.5) + +```c +// Per-core 的 Connection Track Table +// 关键变化: 每个 RSS worker 拥有独立的 ct_table,因为 +// RSS 保证同一五元组永远落在同一个 worker,所以无需跨核锁 +#endif + +--- + +## 五、控制面详细设计 + +### 5.1 IPC 消息协议 + +```c +// src/core/ipc.h + +#define IPC_MSG_MAX_PAYLOAD 1024 +#define IPC_RING_SIZE 2048 + +enum ipc_msg_type { + IPC_MSG_ROUTE_ADD = 1, + IPC_MSG_ROUTE_DEL, + IPC_MSG_INTERFACE_UP, + IPC_MSG_INTERFACE_DOWN, + IPC_MSG_ACL_ADD, + IPC_MSG_ACL_DEL, + IPC_MSG_NAT_CONFIG, + IPC_MSG_QOS_CONFIG, + IPC_MSG_STATS_REPORT, + IPC_MSG_HEALTH_REPORT, +#ifdef PRODUCT_CLOUD_EDGE + IPC_MSG_MEMIF_CREATE, + IPC_MSG_MEMIF_DELETE, + IPC_MSG_CNI_ADD, + IPC_MSG_CNI_DEL, +#endif +}; + +struct ipc_msg { + uint32_t type; // enum ipc_msg_type + uint32_t seq_num; // 序列号 (用于配对 request/response) + uint32_t payload_len; // payload 实际长度 + uint32_t _pad; + uint64_t timestamp; // rdtsc + uint8_t payload[IPC_MSG_MAX_PAYLOAD]; +} __rte_cache_aligned; + +// 控制面 → 数据面: cp_to_dp_ring (MPSC: 控制面单生产者, 数据面单消费者) +// 数据面 → 控制面: dp_to_cp_ring (SPMC: 数据面单生产者, 控制面单消费者) +``` + +### 5.2 控制面主循环 + +```c +// src/cp/control.c + +void control_plane_loop(void) { + while (!force_quit) { + // 1. 定时器处理 (ARP 老化, 统计收集, 健康检查) + rte_timer_manage(); + + // 2. 处理数据面上报 (统计 + 事件) + struct ipc_msg msg; + while (rte_ring_dequeue(dp_to_cp_ring, &msg) == 0) { + switch (msg.type) { + case IPC_MSG_STATS_REPORT: + handle_stats_report(&msg); + break; + case IPC_MSG_HEALTH_REPORT: + handle_health_report(&msg); + break; + } + } + + // 3. 处理 Redis Pub/Sub 消息 (配置变更) + redis_handle_pubsub(); + + // 4. CLI 连接 (Unix socket accept) + cli_accept_connections(); + + // 5. FRR 路由同步 (Netlink socket poll, non-blocking) + frr_poll_netlink(); + } +} +``` + +### 5.3 Redis DB Schema + +``` +CONFIG_DB (持久化): + ROUTE|| → {"nexthop":"10.0.0.1","iface":"wan0","metric":10} + INTERFACE| → {"ip":"192.168.1.1/24","mtu":1500,"state":"up"} + ACL| → {"priority":100,"action":"permit","src":"10.0.0.0/8","dst":"0.0.0.0/0","proto":"tcp","dport":"80"} + NAT| → {"type":"snat","src":"10.0.0.0/8","nat_ip":"203.0.113.1"} + QOS|| → {"scheduler":"sp","weight":0} +#ifdef PRODUCT_CLOUD_EDGE + CNI| → {"ip":"10.244.1.5/24","memif_socket":"/tmp/unos-memif-abc123"} +#endif + +STATE_DB (运行时, 不持久化): + ARP| → {"mac":"00:11:22:33:44:55","iface":"wan0","age":12} + CONNTRACK|summary → {"total":1234,"tcp":1000,"udp":200,"icmp":34} + INTERFACE||status → {"admin":"up","oper":"up","speed":10000} + +STATS_DB (运行时): + PORT||stats → {"rx_bytes":1234567890,"tx_bytes":9876543210,"rx_pps":1234567} +#ifdef PRODUCT_CLOUD_EDGE + CONTAINER||stats → {"rx_bytes":1234,"tx_bytes":5678,"memif_errors":0} +#endif +``` + +--- + +## 六、自研 TCP/IP 协议栈详细设计 + +### 6.0 架构决策: TCP 栈在 Graph 中内联 + +``` +v1.0 (弃用): TCP 栈在独立 lcore → 跨核 IPC → 额外拷贝 + cache miss +v2.0 (生产级): TCP 栈作为 Graph Node → 同一 lcore 直接函数调用 → 零跨核开销 + +Graph 路径: + 物理口 RX → Parser → (dst_is_me?) → TCP Stack Node → App → TX + │ + └── (dst_is_not_me) → L2/L3/ACL/NAT → TX + +本机收包路径 (全程在同一个 worker lcore 内): + rte_eth_rx_burst → Parser → graph_next=NEXT_TCP + → tcp_stack_process_node() ← 这就是 TCP 栈入口 + 内部: + 1. IP 层处理 (分片重组, 校验) + 2. TCP 层处理 (状态机, 序列号, 重传) + 3. 应用层数据放入 recv buffer + 4. 待发送数据构造 mbuf → TX ring + → 应用层 (通过 tcp_recv() 获取数据) + +同一个 mbuf 全程不拷贝,不跨核,不经过 rte_ring IPC。 +``` + +### 6.1 TCP Stack Graph Node + +```c +// src/stcp/tcp_node.c — TCP 栈作为 Graph 处理的实现 + +static uint16_t +tcp_stack_process_node(struct rte_mbuf **mbufs, uint16_t nb_pkts, + uint8_t *next_indices) +{ + for (int i = 0; i < nb_pkts; i++) { + struct rte_mbuf *m = mbufs[i]; + struct parser_result pr; + parser_process(m, &pr); // 快速解析 + + // === IP 层处理 === + if (pr.ip_version == 4) { + if (unlikely(!ipv4_validate_checksum(m))) + goto drop; + // 分片重组 + if (unlikely(ipv4_is_fragment(m))) { + m = ipv4_reassemble(m); + if (m == NULL) continue; // 还没收全 + } + } + + // === TCP 层处理 === + struct tcp_control_block *tcb; + tcb = tcb_lookup(pr.src_ip, pr.dst_ip, pr.src_port, pr.dst_port); + + if (unlikely(tcb == NULL && (pr.tcp_flags & TCP_FLAG_SYN))) { + // 新连接请求 → 查 LISTEN socket → 创建 TCB + tcb = tcp_listen_accept(&pr); + } + + if (unlikely(tcb == NULL)) + goto drop; + + // HTTP Server / DNS 跑在 STCP Socket API 上 + // 当 recv buffer 有数据 → 唤醒应用层 epoll → HTTP handler 调用 tcp_send() 回复 + + // 状态机转换 + tcp_state_process(tcb, &pr, m); + + // 如果 tcp_state_process 内部调用了 tcp_send(),TX mbuf 已经入队 + // 如果是纯 ACK,不需要回复,直接释放 + + // 本机接收路径: mbuf 交给 recv buffer + // 本机发送路径: TCP 栈自己构造的 mbuf 在 TX ring 里, + // 在 graph traversal 的 TX 阶段发出 + continue; + +drop: + next_indices[i] = NEXT_DROP; + } + return nb_pkts; +} + +// 这个函数注册为 Graph Node: +REGISTER_GRAPH_NODE(tcp_stack, tcp_stack_process_node, { + { NEXT_TCP, "tcp_stack" }, // 回环 (重传/ACK) + { NEXT_TX, "tx" }, // 出队发送 + { NEXT_DROP, "drop" }, // 错误丢包 +}); +``` + +### 6.2 TCB (TCP Control Block) — 不变 + +(参见 v1.0 设计,TCB 结构不变。区别仅在于 TCB 挂载在 per-worker 的 tcp_stack 实例中,而非全局哈希表) + +### 6.3 TCP 状态机 — 不变 + +(11 状态转换表不变,参见 v1.0 6.2 节) + +### 6.4 数据面 TCP 的收发包路径 + +``` +收到 SYN (dst_ip == 本机 IP, dst_port == 80): +───────────────────────────────────────────── +物理口 RX: + → Parser: ether_type=0x0800, protocol=TCP, dst_port=80, SYN=1 + → graph_next = NEXT_TCP + +TCP Stack Node: + → tcb_lookup() → NULL (新连接) + → tcp_listen_accept() → 创建 TCB (状态=SYN_RCVD) + → tcp_send_syn_ack(tcb) → 构造 SYN+ACK mbuf + → mbuf 入 TX ring + → 数据面 TX 阶段发出 + +收到 ACK (of SYN+ACK): + → tcb_lookup() → 找到 TCB + → tcp_state_process() → SYN_RCVD → ESTABLISHED + → 无应用数据,不唤醒 + +收到 HTTP GET (ESTABLISHED 状态下): + → tcp_state_process() → 验证 seq, 更新 rcv_nxt, 回复 ACK + → 应用数据放入 tcb->rcv_buf + → 唤醒 HTTP handler (通过 eventfd) + → HTTP handler 调用 tcp_send(tcb->sockfd, response, len) + → TCP 栈构造 DATA mbuf → TX ring → 发出 + +关闭连接: + → 客户端发 FIN → tcp_state: ESTABLISHED → CLOSE_WAIT + → HTTP handler 收到 EOF → close(sockfd) + → tcp_send_fin(tcb) → tcp_state: CLOSE_WAIT → LAST_ACK → 发 FIN + → 收到 ACK → CLOSED → 删除 TCB +``` + +```c +// src/stcp/tcb.h + +#define TCB_HASH_BUCKETS 65536 +#define TCB_MAX_CONNECTIONS 100000 // Cloud Edge +#define TCP_MSS_DEFAULT 1460 +#define TCP_RTO_MIN_MS 200 +#define TCP_RTO_MAX_MS 120000 +#define TCP_2MSL_MS 120000 // TIME_WAIT duration + +enum tcp_state { + TCP_CLOSED = 0, + TCP_LISTEN, + TCP_SYN_SENT, + TCP_SYN_RCVD, + TCP_ESTABLISHED, + TCP_FIN_WAIT_1, + TCP_FIN_WAIT_2, + TCP_CLOSE_WAIT, + TCP_CLOSING, + TCP_LAST_ACK, + TCP_TIME_WAIT, +}; + +struct tcp_control_block { + // === 四元组 === + uint32_t src_ip; + uint32_t dst_ip; + uint16_t src_port; + uint16_t dst_port; + + // === 状态 === + enum tcp_state state; + + // === 发送序列号 === + uint32_t iss; // Initial Send Sequence + uint32_t snd_una; // 最老未确认的序列号 + uint32_t snd_nxt; // 下一个要发送的序列号 + uint32_t snd_wnd; // 发送窗口 + uint32_t snd_wl1; // 上次窗口更新的 seq + uint32_t snd_wl2; // 上次窗口更新的 ack + + // === 接收序列号 === + uint32_t irs; // Initial Receive Sequence + uint32_t rcv_nxt; // 期望收到的下一个序列号 + uint32_t rcv_wnd; // 接收窗口 (通告给对方) + uint32_t rcv_adv; // 上次通告的窗口右边界 + + // === 拥塞控制 === + uint32_t cwnd; // 拥塞窗口 (MSS 为单位) + uint32_t ssthresh; // 慢启动阈值 + uint32_t cwnd_cnt; // cwnd 增长计数器 + + // === RTT / RTO === + uint32_t srtt_us; // Smoothed RTT (微秒) + uint32_t rttvar_us; // RTT 方差 (微秒) + uint32_t rto_us; // 当前 RTO (微秒) + uint64_t rto_deadline; // rte_rdtsc 到期时间 + uint8_t backoff; // 指数退避次数 (0-6) + uint64_t last_rtt_ts; // 上次 RTT 测量时间 + + // === 重传 === + uint32_t dup_ack_count; // 重复 ACK 计数 (快速重传) + uint32_t retrans_count; // 连续重传次数 + uint64_t last_data_ts; // 最后发送数据的时间 + + // === Options === + uint16_t mss; // 协商的 MSS + uint8_t wscale_shift; // Window scale shift + uint8_t sack_permitted; // 对端支持 SACK + + // === 发送缓冲区 (环形) === + uint8_t *snd_buf; + uint32_t snd_buf_size; + uint32_t snd_buf_head; // 写入指针 + uint32_t snd_buf_tail; // 确认指针 + + // === 接收缓冲区 (环形) === + uint8_t *rcv_buf; + uint32_t rcv_buf_size; + uint32_t rcv_buf_head; // 写入指针 + uint32_t rcv_buf_tail; // 读取指针 + + // === Socket 层 === + int sockfd; + uint8_t nonblocking; + void *user_data; // 应用层上下文 + + // === 链表 === + struct tcp_control_block *next; // 哈希冲突链表 + struct tcp_control_block *prev; + uint64_t tcb_id; // 唯一 ID + +} __rte_cache_aligned; +``` + +### 6.2 TCP 状态机实现 + +```c +// src/stcp/tcp_state.c + +// 事件类型 +enum tcp_event { + TCP_EV_SYN, + TCP_EV_SYN_ACK, + TCP_EV_ACK, + TCP_EV_FIN, + TCP_EV_RST, + TCP_EV_TIMEOUT, + TCP_EV_CLOSE, // 应用层调用 close() + TCP_EV_SEND, // 应用层调用 send() +}; + +// 每个状态的事件处理表 +typedef int (*tcp_state_handler_t)(struct tcp_control_block *tcb, + struct tcp_event event, + void *data); + +// 状态转换表: [state][event] → new_state + handler +static const struct { + enum tcp_state new_state; + tcp_state_handler_t handler; +} tcp_transition[11][8] = { + // TCP_CLOSED + [TCP_CLOSED] = { + [TCP_EV_SYN] = { TCP_SYN_RCVD, tcp_handle_listen_syn }, + [TCP_EV_SEND] = { TCP_SYN_SENT, tcp_handle_active_open }, + }, + // TCP_LISTEN + [TCP_LISTEN] = { + [TCP_EV_SYN] = { TCP_SYN_RCVD, tcp_handle_listen_syn }, + [TCP_EV_RST] = { TCP_LISTEN, tcp_handle_listen_rst }, + }, + // TCP_SYN_SENT + [TCP_SYN_SENT] = { + [TCP_EV_SYN_ACK]= { TCP_ESTABLISHED, tcp_handle_syn_sent_syn_ack }, + [TCP_EV_SYN] = { TCP_SYN_RCVD, tcp_handle_simultaneous_open }, + [TCP_EV_RST] = { TCP_CLOSED, tcp_handle_rst }, + [TCP_EV_TIMEOUT]= { TCP_CLOSED, tcp_handle_timeout }, + }, + // TCP_SYN_RCVD + [TCP_SYN_RCVD] = { + [TCP_EV_ACK] = { TCP_ESTABLISHED, tcp_handle_syn_rcvd_ack }, + [TCP_EV_RST] = { TCP_LISTEN, tcp_handle_syn_rcvd_rst }, + [TCP_EV_TIMEOUT]= { TCP_CLOSED, tcp_handle_timeout }, + }, + // TCP_ESTABLISHED + [TCP_ESTABLISHED] = { + [TCP_EV_FIN] = { TCP_CLOSE_WAIT, tcp_handle_established_fin }, + [TCP_EV_CLOSE] = { TCP_FIN_WAIT_1, tcp_handle_close }, + [TCP_EV_RST] = { TCP_CLOSED, tcp_handle_rst }, + }, + // TCP_FIN_WAIT_1 + [TCP_FIN_WAIT_1] = { + [TCP_EV_ACK] = { TCP_FIN_WAIT_2, tcp_handle_fin_wait1_ack }, + [TCP_EV_FIN] = { TCP_TIME_WAIT, tcp_handle_fin_wait1_fin }, + [TCP_EV_FIN|TCP_EV_ACK] = { TCP_TIME_WAIT, tcp_handle_simultaneous_close }, + [TCP_EV_RST] = { TCP_CLOSED, tcp_handle_rst }, + }, + // TCP_FIN_WAIT_2 + [TCP_FIN_WAIT_2] = { + [TCP_EV_FIN] = { TCP_TIME_WAIT, tcp_handle_fin_wait2_fin }, + [TCP_EV_RST] = { TCP_CLOSED, tcp_handle_rst }, + }, + // TCP_CLOSE_WAIT + [TCP_CLOSE_WAIT] = { + [TCP_EV_CLOSE] = { TCP_LAST_ACK, tcp_handle_close_wait_close }, + [TCP_EV_RST] = { TCP_CLOSED, tcp_handle_rst }, + }, + // TCP_CLOSING + [TCP_CLOSING] = { + [TCP_EV_ACK] = { TCP_TIME_WAIT, tcp_handle_closing_ack }, + [TCP_EV_RST] = { TCP_CLOSED, tcp_handle_rst }, + }, + // TCP_LAST_ACK + [TCP_LAST_ACK] = { + [TCP_EV_ACK] = { TCP_CLOSED, tcp_handle_last_ack_ack }, + [TCP_EV_RST] = { TCP_CLOSED, tcp_handle_rst }, + }, + // TCP_TIME_WAIT + [TCP_TIME_WAIT] = { + [TCP_EV_TIMEOUT]= { TCP_CLOSED, tcp_handle_time_wait_timeout }, + [TCP_EV_RST] = { TCP_CLOSED, tcp_handle_rst }, + }, +}; +``` + +--- + +## 七、容器网络详细设计 + +### 7.1 memif 接口架构 + +``` +┌───────────────────────────────────────────────────────┐ +│ Host (uNOS) │ +│ │ +│ ┌──────────────────┐ ┌──────────────────┐ │ +│ │ memif master │ │ memif master │ │ +│ │ socket: /tmp/ │ │ socket: /tmp/ │ │ +│ │ unos-memif-1 │ │ unos-memif-2 │ │ +│ └────────┬─────────┘ └────────┬─────────┘ │ +│ │ shared memory │ shared memory │ +│ │ (hugepage) │ (hugepage) │ +│ ┌────────┴───────────────────────┴─────────┐ │ +│ │ uNOS Data Plane │ │ +│ │ memif1 → Pipeline → memif2 │ │ +│ │ (container-to-container: zero copy) │ │ +│ └───────────────────────────────────────────┘ │ +└───────────────────────────────────────────────────────┘ + │ │ + ┌────┴────┐ ┌────┴────┐ + │Container│ │Container│ + │ A │ │ B │ + │ (nginx) │ │ (redis) │ + │ │ │ │ + │ memif │ │ memif │ + │ slave │ │ slave │ + └─────────┘ └─────────┘ +``` + +### 7.2 memif 接口管理 + +```c +// src/cni/memif.h + +#define MAX_MEMIF_SOCKETS 128 +#define MEMIF_SOCKET_DIR "/var/run/unos/memif/" + +struct memif_port { + int id; // memif interface ID + char socket_path[256]; // /var/run/unos/memif/container-xxx + uint8_t role; // MEMIF_ROLE_MASTER (uNOS) / MEMIF_ROLE_SLAVE (container) + struct rte_mempool *mempool; // per-port mempool + struct rte_ring *rx_queue; // RX ring (shared memory) + struct rte_ring *tx_queue; // TX ring (shared memory) + + // === uNOS data plane reference === + uint8_t unos_port_id; // uNOS internal port ID + struct rte_ether_addr mac; // container side MAC + uint32_t ip; // container IP (from IPAM) + uint8_t prefix_len; + + // === Stats === + uint64_t rx_pkts; + uint64_t tx_pkts; + uint64_t rx_bytes; + uint64_t tx_bytes; +} __rte_cache_aligned; + +struct memif_manager { + struct memif_port ports[MAX_MEMIF_SOCKETS]; + int port_count; + struct rte_hash *port_hash; // socket_path → index +}; +``` + +### 7.3 CNI Plugin 接口 + +```c +// src/cni/cni_api.h + +// CNI 标准 JSON 结构 +struct cni_args { + char container_id[64]; + char netns_path[256]; + char ifname[16]; // e.g. "eth0" + char args[1024]; // extra CNI_ARGS +}; + +struct cni_result { + char ip_addr[64]; // e.g. "10.244.1.5/24" + char gateway[32]; // e.g. "10.244.1.1" + char routes[4096]; // JSON array of routes + char dns[256]; // JSON object of DNS config +}; + +// CNI 操作 +// /opt/cni/bin/unos-cni ADD < CNI_CONFIG_JSON +// /opt/cni/bin/unos-cni DEL < CNI_CONFIG_JSON +// /opt/cni/bin/unos-cni CHECK < CNI_CONFIG_JSON (optional) + +// uNOS CNI 实现: +// ADD: +// 1. 解析 CNI JSON → 提取 container_id, netns_path, ifname +// 2. 连接 uNOS Unix socket (/var/run/unos/cni.sock) +// 3. 发送 IPC_MSG_CNI_ADD → uNOS 控制面: +// a. IPAM 分配 IP (10.244.x.y/24) +// b. 创建 memif master socket +// c. 返回 memif socket path + IP config +// 4. CNI plugin 创建 memif slave 在容器 netns 内 +// 5. 返回 CNI result JSON +// +// DEL: +// 1. 连接 uNOS Unix socket +// 2. 发送 IPC_MSG_CNI_DEL → uNOS 控制面: +// a. 删除 memif socket +// b. 回收 IP 地址 +// 3. CNI plugin 清理容器网络命名空间 +``` + +### 7.4 IPAM 设计 + +```c +// src/cni/ipam.h + +#define POD_CIDR "10.244.0.0/16" // 整体 Pod 网络 +#define NODE_CIDR "10.244.0.0/24" // 本节点子网 +#define GATEWAY_IP "10.244.0.1" // 网关 IP (uNOS 自己) + +struct ipam_pool { + uint32_t subnet; // 10.244.0.0 + uint8_t prefix_len; // 24 + uint64_t allocated_bitmap[4];// 256 IPs bitmap + uint32_t allocated_count; + uint32_t last_allocated; // 上次分配的 IP (减少扫描) +}; + +struct ipam { + struct ipam_pool pool; + uint32_t gateway_ip; // 10.244.0.1 + struct rte_hash *allocations; // container_id → IP +}; + +// IP 分配算法: bitmap + last_allocated 加速 +static inline int +ipam_allocate(struct ipam *ipam, uint32_t *ip_out) { + int host_id = -1; + int start = ipam->pool.last_allocated; + + // 从上次位置开始扫描 (利用局部性) + for (int i = 0; i < 254; i++) { + int idx = (start + i) % 254; + if (!(ipam->pool.allocated_bitmap[idx / 64] & (1ULL << (idx % 64)))) { + host_id = idx + 2; // skip .0 (network) and .1 (gateway) + break; + } + } + if (host_id < 0) + return -1; // pool exhausted + + ipam->pool.allocated_bitmap[host_id / 64] |= (1ULL << (host_id % 64)); + ipam->pool.allocated_count++; + ipam->pool.last_allocated = host_id; + *ip_out = ipam->pool.subnet | host_id; + return 0; +} +``` + +--- + +## 八、管理面详细设计 + +### 8.1 CLI 命令树 + +``` +Command Tree (vtysh style): +───────────────────────────── + +unos> enable +unos# show [running-config|interfaces|route|arp|nat|acl|qos|system|tech-support] +unos# configure terminal +unos(config)# + + [interface ] + [ip address ] + [ipv6 address ] + [mtu <1500|9000>] + [shutdown|no shutdown] + [description ] + [switchport mode ] + [switchport access vlan ] + [switchport trunk allowed vlan ] + + [router ospf] + [router-id ] + [network area ] + [redistribute static] + + [router bgp ] + [neighbor remote-as ] + [network ] + [redistribute connected] + + [ip route [metric ]] + [ip route null0] # 黑洞路由 + + [access-list <100-199> [eq ]] + + [nat source interface overload] # SNAT + [nat destination ] # DNAT + + [qos class <0-7> ] + +#ifdef PRODUCT_CLOUD_EDGE + [container-network] + [cidr ] + [show containers] + + [load-balancer] + [backend add ] + [frontend add backend ] + [algorithm ] + + [vrrp ] + [ip ] + [priority <1-254>] + [preempt] +#endif +``` + +### 8.2 REST API 路由表 + +``` +Method Path Controller +────── ──── ────────── + +# 路由管理 +GET /api/v1/routes list_routes +POST /api/v1/routes add_route +DELETE /api/v1/routes/{prefix} del_route + +# 接口管理 +GET /api/v1/interfaces list_interfaces +GET /api/v1/interfaces/{name} get_interface +PUT /api/v1/interfaces/{name} update_interface +POST /api/v1/interfaces/{name}/reload reload_interface + +# ACL +GET /api/v1/firewall/rules list_acl_rules +POST /api/v1/firewall/rules add_acl_rule +DELETE /api/v1/firewall/rules/{id} del_acl_rule + +# NAT +GET /api/v1/nat/rules list_nat_rules +POST /api/v1/nat/rules add_nat_rule +DELETE /api/v1/nat/rules/{id} del_nat_rule +GET /api/v1/nat/connections list_connections +GET /api/v1/nat/connections/summary connection_summary + +# QoS +GET /api/v1/qos/classes list_qos_classes +PUT /api/v1/qos/classes/{id} update_qos_class + +# 统计 +GET /api/v1/stats/ports port_stats +GET /api/v1/stats/ports/{name} port_stats_detail +GET /api/v1/stats/system system_stats + +# 系统 +GET /api/v1/system/health health_check +POST /api/v1/system/config/export export_config +POST /api/v1/system/config/import import_config + +#ifdef PRODUCT_CLOUD_EDGE +# 容器网络 +GET /api/v1/containers list_containers +GET /api/v1/containers/{id} get_container + +# 负载均衡 +GET /api/v1/loadbalancer/backends list_lb_backends +POST /api/v1/loadbalancer/backends add_lb_backend +DELETE /api/v1/loadbalancer/backends/{name} del_lb_backend +#endif +``` + +--- + +## 九、高可用详细设计 + +### 9.1 VRRP 状态机 + +```c +// src/ha/vrrp.h + +#define VRRP_VERSION 3 // RFC 5798 +#define VRRP_ADVERT_INT 1000 // Advertisement interval (ms) +#define VRRP_MASTER_DOWN 3000 // Master down detection (3 × advert) + +enum vrrp_state { + VRRP_STATE_INITIALIZE = 0, + VRRP_STATE_BACKUP, + VRRP_STATE_MASTER, +}; + +struct vrrp_instance { + uint8_t vrid; // Virtual Router ID + uint8_t priority; // 本节点优先级 (1-254) + uint8_t adv_interval; // Advertisement 间隔 (centiseconds) + uint8_t preempt; // 抢占模式 + uint32_t virtual_ip; + uint8_t virtual_mac[6]; // 00:00:5E:00:01:{VRID} + enum vrrp_state state; + uint64_t master_down_deadline; // 超时时间 (rdtsc) + struct rte_ring *conn_sync_queue; // 连接表同步队列 +}; + +// 状态转换: +// INIT → BACKUP (priority != 255) +// INIT → MASTER (priority == 255, IP owner) +// BACKUP → MASTER (master_down timer expired) +// MASTER → BACKUP (received higher priority advertisement) +``` + +### 9.2 连接表同步协议 + +```c +// src/ha/sync.h + +struct conn_sync_msg { + uint8_t type; // SYNC_ADD / SYNC_DEL / SYNC_FULL + uint8_t version; // 协议版本 + uint16_t count; // 批量条目数 + uint32_t seq_num; // 序列号 (用于去重) + struct conntrack_key keys[32]; // 批量同步 (最多 32 条) +}; +``` + +--- + +## 十、构建系统设计 + +### 10.1 项目结构 + +``` +unos/ +├── meson.build # 顶层构建 +├── meson_options.txt # 产品选项 +├── src/ +│ ├── meson.build # 源码构建 +│ ├── core/ +│ │ ├── meson.build +│ │ ├── main.c +│ │ ├── pipeline.c / pipeline.h +│ │ ├── ipc.c / ipc.h +│ │ └── timer.c / timer.h +│ ├── dp/ +│ │ ├── meson.build +│ │ ├── forward.c +│ │ ├── parser.c / parser.h +│ │ ├── l2fwd.c / l2fwd.h +│ │ ├── l3fwd.c / l3fwd.h +│ │ ├── nat.c / nat.h +│ │ ├── acl.c / acl.h +│ │ └── qos.c / qos.h +│ ├── cp/ +│ │ ├── meson.build +│ │ ├── control.c / control.h +│ │ ├── arp.c / arp.h +│ │ ├── dhcp.c / dhcp.h +│ │ ├── iface.c / iface.h +│ │ ├── frr_sync.c / frr_sync.h +│ │ └── db.c / db.h +│ ├── stcp/ +│ │ ├── meson.build # 仅 Cloud Edge +│ │ ├── stcp.h +│ │ ├── tcb.h +│ │ ├── tcp_state.c +│ │ ├── tcp_cong.c +│ │ ├── tcp_retx.c +│ │ ├── tcp_flow.c +│ │ ├── tcp_socket.c +│ │ ├── tcp_txrx.c +│ │ ├── tcp_option.c +│ │ ├── ipv4.c +│ │ ├── icmp.c +│ │ └── frag.c +│ ├── cni/ +│ │ ├── meson.build # 仅 Cloud Edge +│ │ ├── memif.c / memif.h +│ │ ├── cni_api.c / cni_api.h +│ │ └── ipam.c / ipam.h +│ ├── app/ +│ │ ├── meson.build # 仅 Cloud Edge +│ │ ├── http_server.c +│ │ ├── dns_server.c +│ │ └── tcp_proxy.c +│ ├── mgmt/ +│ │ ├── meson.build +│ │ ├── cli.c / cli.h +│ │ └── rest.c / rest.h +│ ├── ha/ +│ │ ├── meson.build # 仅 Cloud Edge +│ │ ├── vrrp.c / vrrp.h +│ │ └── sync.c / sync.h +│ ├── telemetry/ +│ │ ├── meson.build +│ │ ├── prometheus.c / prometheus.h +│ │ └── stats.c / stats.h +│ └── common/ +│ ├── meson.build +│ ├── types.h +│ ├── utils.c / utils.h +│ └── log.c / log.h +├── web/ +│ ├── index.html +│ └── dashboard.js +├── scripts/ +│ ├── setup.sh +│ ├── bench.sh +│ └── profile.sh +├── config/ +│ ├── cloud_edge.yaml +│ └── home_cpe.yaml +├── test/ +│ ├── meson.build +│ ├── test_parser.c +│ ├── test_lpm.c +│ ├── test_nat.c +│ └── test_tcp_state.c +├── docs/ +│ ├── ARCHITECTURE.md +│ └── REQUIREMENTS.md +├── .gitignore +├── LICENSE +└── README.md +``` + +### 10.2 顶层 meson.build + +```meson +# unos/meson.build +project('unos', 'c', + version: '0.1.0', + default_options: [ + 'c_std=c17', + 'warning_level=3', + 'buildtype=release', + 'default_library=static', + ], +) + +add_project_arguments('-march=native', '-mtune=native', language: 'c') + +# DPDK dependency +dpdk = dependency('libdpdk', required: true, static: true) + +# Other dependencies +threads = dependency('threads') +numa = dependency('numa', required: true) +hiredis = dependency('hiredis', required: true) + +subdir('src') +``` + +### 10.3 src/meson.build (产品条件编译) + +```meson +# unos/src/meson.build + +product = get_option('product') + +# === Common sources (both products) === +common_sources = files( + 'core/main.c', + 'core/pipeline.c', + 'core/ipc.c', + 'core/timer.c', + 'dp/forward.c', + 'dp/parser.c', + 'dp/l2fwd.c', + 'dp/l3fwd.c', + 'dp/nat.c', + 'dp/acl.c', + 'dp/qos.c', + 'cp/control.c', + 'cp/arp.c', + 'cp/iface.c', + 'cp/frr_sync.c', + 'cp/db.c', + 'mgmt/cli.c', + 'telemetry/prometheus.c', + 'telemetry/stats.c', + 'common/utils.c', + 'common/log.c', +) + +# === Product-specific sources === +cloud_edge_sources = files( + 'stcp/ipv4.c', + 'stcp/icmp.c', + 'stcp/frag.c', + 'stcp/tcp_state.c', + 'stcp/tcp_cong.c', + 'stcp/tcp_retx.c', + 'stcp/tcp_flow.c', + 'stcp/tcp_socket.c', + 'stcp/tcp_txrx.c', + 'stcp/tcp_option.c', + 'cni/memif.c', + 'cni/cni_api.c', + 'cni/ipam.c', + 'app/http_server.c', + 'app/dns_server.c', + 'app/tcp_proxy.c', + 'mgmt/rest.c', + 'ha/vrrp.c', + 'ha/sync.c', +) + +home_cpe_sources = files( + 'cp/dhcp.c', +) + +# === Assemble === +sources = common_sources +product_args = [] + +if product == 'cloud_edge' + sources += cloud_edge_sources + product_args += ['-DPRODUCT_CLOUD_EDGE'] + c_args = product_args +elif product == 'home_cpe' + sources += home_cpe_sources + product_args += ['-DPRODUCT_HOME_CPE'] + c_args = product_args +endif + +executable('unos', + sources, + c_args: c_args, + dependencies: [dpdk, threads, numa, hiredis], + install: true, +) + +# === CNI plugin binary (Cloud Edge only) === +if product == 'cloud_edge' + executable('unos-cni', + files('cni/cni_api.c'), + dependencies: [], + install: true, + install_dir: '/opt/cni/bin', + ) +endif +``` + +--- + +## 十一、测试设计 + +### 11.1 单元测试 + +```c +// test/meson.build — 每个模块对应一个测试 + +parser_test = executable('test_parser', + 'test_parser.c', '../src/dp/parser.c', '../src/common/utils.c', + dependencies: [dpdk], +) +test('Parser Test', parser_test) + +lpm_test = executable('test_lpm', + 'test_lpm.c', '../src/dp/l3fwd.c', + dependencies: [dpdk], +) +test('LPM Test', lpm_test) + +nat_test = executable('test_nat', + 'test_nat.c', '../src/dp/nat.c', '../src/dp/parser.c', + dependencies: [dpdk], +) +test('NAT Test', nat_test) + +tcp_state_test = executable('test_tcp_state', + 'test_tcp_state.c', '../src/stcp/tcp_state.c', '../src/stcp/tcb.h', + dependencies: [dpdk], +) +test('TCP State Machine Test', tcp_state_test) +``` + +### 11.2 集成测试脚本 + +```bash +# scripts/bench.sh — 自动化压测 + +#!/bin/bash +echo "=== uNOS Performance Benchmark Suite ===" + +# 1. L2 Forwarding (64B) +echo "[1/6] L2 Forwarding 64B line rate..." +dperf -c 0-1 -n 2 -- --forward-mode mac --pkt-size 64 --duration 30 +# Expected: 1.488 Mpps (1G) / 14.88 Mpps (10G), 0% loss + +# 2. L3 Forwarding (64B) +echo "[2/6] L3 Forwarding 64B..." +dperf -c 0-1 -n 2 -- --forward-mode lpm --pkt-size 64 --duration 30 + +# 3. NAT (64B) +echo "[3/6] NAT Forwarding 64B..." +# Start uNOS with NAT enabled, then: +dperf -c 0-1 -n 2 -- --pkt-size 64 --duration 30 + +# 4. QoS (mixed traffic) +echo "[4/6] QoS mixed traffic test..." +# Background: iperf3 -c target -t 30 -b 900M (big packets) +# Foreground: ping -i 0.001 target -c 10000 (small packets) +# Expected: ping p99 < 2ms even with background load + +# 5. TCP Stack throughput +echo "[5/6] TCP Stack throughput..." +iperf3 -c localhost -p 8080 -t 30 +# Expected: >= 800 Mbps (J3455) / >= 9.4 Gbps (T140) + +# 6. HTTP Server QPS +echo "[6/6] HTTP Server QPS..." +wrk -t 2 -c 100 -d 30s http://localhost:8080/ +# Expected: >= 50K (J3455) / >= 200K (T140) +``` + +--- + +## 十二、接口契约 + +### 12.1 内部模块 API 契约 + +| 生产者 → 消费者 | 接口 | 契约 | +|---------------|------|------| +| Pipeline | Stage | `typedef uint16_t (*process_fn)(mbuf**, uint16_t)` — 输入/输出都是 mbuf 数组,返回处理后的包数 | +| Parser | Rest | `parser_result_t` 写入 mbuf 私有区域,一次解析、多次使用 | +| L3Fwd | ARP | `arp_lookup(ip) → mac` — 非阻塞,miss 返回 NULL | +| NAT | Conntrack | `conntrack_lookup(key) → entry` — O(1) 平均,miss 返回创建的新 entry | +| Control | Dataplane | `ipc_send(msg)` — 非阻塞 ring enqueue | +| Dataplane | Control | `ipc_send(msg)` — 非阻塞 ring enqueue | + +### 12.2 外部接口契约 + +| 接口 | 协议 | 格式 | +|------|------|------| +| CNI Plugin ↔ uNOS | Unix socket (SOCK_STREAM) | JSON | +| CLI ↔ uNOS | Unix socket | Text (vtysh) | +| REST ↔ uNOS | HTTP/1.1 / HTTP/2 | JSON | +| FRR ↔ uNOS | Netlink (AF_NETLINK) | Binary (FPM) | +| Redis ↔ uNOS | TCP (localhost:6379) | Redis Protocol (RESP) | +| Prometheus ↔ uNOS | HTTP | Text (Prometheus exposition format) | + +--- + +> **文档结束** +> +> 版本: v1.0 | 日期: 2026-07-29 +> 随项目推进持续更新设计细节 diff --git a/products/unos-method.md b/products/unos-method.md new file mode 100755 index 0000000..2de618e --- /dev/null +++ b/products/unos-method.md @@ -0,0 +1,359 @@ +# uNOS 学习方法论 — 三层剥皮法 + +> **创建日期**: 2026-08-01 +> **适用**: 系统编程学习(DPDK / 内核 / TCP 协议栈 / PMD 驱动) +> **关联**: `uNOS_REQUIREMENTS.md`、`uNOS_DESIGN_SPEC.md`、`lazy-puzzling-rocket.md` + +--- + +## 一、核心公式 + +``` +掌握 = 内核为什么慢 → DPDK 怎么解决的 → 我亲手写一遍 → 性能数据证明 + ────────── ────────────── ────────── ───────────── + Layer 1 Layer 2 Layer 3 Layer 4 + (理解问题) (理解方案) (内化知识) (验证理解) +``` + +每一层不可跳过: + +| 跳过 | 后果 | +|------|------| +| Layer 1 | 不知道为什么需要 DPDK,面试回答停留在"因为快" | +| Layer 2 | 只会调 API,不理解设计决策 | +| Layer 3 | "看懂了但不会写",面试一问细节就崩 | +| Layer 4 | "写出来了但不知道自己写对了没" | + +--- + +## 二、四层详解 + +### Layer 1: 内核为什么慢(20 分钟,理解问题) + +**做什么**:读内核源码,找到瓶颈的**精确位置**。 + +**方法**: + +1. 先确定要研究的子系统(如:包分配、路由查找、TCP 状态机) +2. 在 Linux 内核源码中找到对应的 C 文件 +3. 跟踪一个完整的数据路径(如:收包 → 分配 skb → 协议栈 → 应用) +4. 标注每一步的延迟来源:中断、锁、内存分配、cache miss、上下文切换 + +**产出标准**:能用一句话解释"内核做 X 要走多少步、每一步慢在哪"。 + +**示例**(mempool): + +``` +问题: 内核是怎么分配网络包的? + +精确到源码: + 收包: net/core/dev.c → netif_rx() → enqueue_to_backlog() + → 每个包触发一次 ksoftirqd 调度 + 分配: include/linux/skbuff.h → alloc_skb() + → kmem_cache_alloc() → slab allocator + → 每次分配都要获取 per-cpu 锁 + +为什么慢: + 1. 每包一次中断 → 每包一次上下文切换 + 2. slab allocator 有锁 → 多核竞争 + 3. kmem_cache 有冷热分离 → 热路径 miss 时有 cache miss + 4. sk_buff 结构体 200+ 字节,跨多个 cacheline +``` + +--- + +### Layer 2: DPDK 怎么解决的(20 分钟,理解方案) + +**做什么**:读 DPDK 源码,找到优化的**精确手段**。 + +**方法**: + +1. 在 DPDK 源码中找到对应的库/驱动 +2. 读头文件的 struct 定义和函数注释 +3. 读实现文件的几个关键函数 +4. 画出"数据路径图":一个包从进入到离开,经过哪些结构体、哪些函数 + +**产出标准**:能用一张图画出关键路径,标注每一级的延迟量级。 + +**示例**(mempool): + +``` +方案: rte_mempool + +精确到源码: + lib/mempool/rte_mempool.h line 230 → struct rte_mempool + lib/mempool/rte_mempool.h line 90 → struct rte_mempool_cache + +三级路径: + L1: mbuf 从 per-lcore cache 拿 → 零锁,L1 cache 常驻 (~5ns) + L2: cache 空了 → 从 ring 批量补 256 个 → CAS 一次 (~50ns 均摊) + L3: ring 也空了 → 从 hugepage 拿 → 很少触发 (~200ns) + +为什么快: + 1. 预分配 → 收发过程零分配,只从池里取 + 2. per-lcore cache → 无锁,无竞争 + 3. ring 批量操作 → 分摊 CAS 开销 + 4. mbuf 固定 128B,2 个 cacheline → prefetch 友好 +``` + +--- + +### Layer 3: 亲手写一遍(60 分钟,内化知识) + +**做什么**:关掉所有参考代码,自己从头写。 + +**方法**: + +1. 读完 Layer 1-2 后,**立即关掉 DPDK 源码** +2. 打开编辑器,自己写 +3. 编译 → crash → gdb backtrace → 读报错 → 修 → 继续 +4. 跑通后用 gdb 单步跟踪关键路径:打印 struct 内容、看指针偏移 +5. 用 `pahole` 看结构体 cache line 布局 + +**产出标准**:能在 gdb 里打印出关键结构体的内部状态,能解释为什么这么快。 + +**示例**(mempool): + +```c +// 自己写: + +// 1. 创建 mempool +struct rte_mempool *pool = rte_pktmbuf_pool_create( + "my_pool", 8191, 256, 0, + RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id() +); + +// 2. 分配/释放循环,测性能 +uint64_t start = rte_rdtsc(); +for (int i = 0; i < 1000000; i++) { + struct rte_mbuf *m = rte_pktmbuf_alloc(pool); + rte_pktmbuf_free(m); +} +uint64_t cycles = (rte_rdtsc() - start) / 1000000; +printf("avg cycles per alloc+free: %lu\n", cycles); + +// 3. gdb 验证 +// (gdb) p *pool +// (gdb) p pool->cache[0] ← per-lcore cache 里有多少个 mbuf? +// (gdb) p pool->cache[0].len ← 当 len > 0 时 alloc 走 L1 +// (gdb) p pool->cache[0].len ← 当 len = 0 时 alloc 走 L2 补货 +``` + +**禁止事项**: + +- ❌ 复制粘贴 DPDK examples 代码 +- ❌ 先看答案再写 +- ❌ crash 后直接问 AI 怎么修(先自己 gdb 至少 5 分钟) + +--- + +### Layer 4: 性能数据证明(20 分钟,验证理解) + +**做什么**:用数据证明你写的东西确实比内核快。 + +**方法**: + +1. `perf stat` 对比内核路径 vs DPDK 路径 +2. `perf record` + FlameGraph 生成火焰图 +3. 填一张对比表 +4. 写入 `docs/` 文档 + +**产出标准**:有一张表格,有数字,有火焰图链接。 + +**示例**(mempool): + +``` +│ 指标 │ 内核 (slab) │ DPDK (mempool) │ +│ alloc+free 延迟 │ ~200ns │ ~15ns │ +│ L1 cache miss │ ~8% │ ~0.5% │ +│ 锁竞争 │ 有 (per-cpu) │ 无 (per-lcore) │ +│ 火焰图 │ [链接] │ [链接] │ +``` + +--- + +## 三、每阶段时间分配 + +``` +总时间: 2 小时/阶段 + +Layer 1: 20 分钟 (读内核源码 + 理解问题) +Layer 2: 20 分钟 (读 DPDK 源码 + 理解方案) +Layer 3: 60 分钟 (手写 + gdb 跟踪) +Layer 4: 20 分钟 (perf 验证 + 写文档) + +输入 (Layer 1-2): 30% +输出 (Layer 3-4): 70% +``` + +--- + +## 四、应用到 uNOS 每个 Phase + +### Phase 1: L2 MAC 转发 + +| Layer | 内容 | 产出 | +|-------|------|------| +| 1 | 读 `net/core/dev.c` NAPI 收包路径 | 理解中断驱动的瓶颈在哪 | +| 2 | 读 DPDK PMD 轮询模式: `rte_eth_rx_burst` | 理解零中断轮询 | +| 3 | 手写 L2 转发循环,gdb 跟踪 mbuf 从口0到口1 | 能跑通,iPerf3 双向通 | +| 4 | `perf record` 火焰图 | CPU 100% 有用功,中断开销 0% | + +### Phase 2: L3 路由转发 + +| Layer | 内容 | 产出 | +|-------|------|------| +| 1 | 读 `net/ipv4/fib_trie.c` 内核路由表 | 理解 FIB trie 的结构 | +| 2 | 读 `lib/librte_lpm/rte_lpm.c` DIR-24-8 算法 | 理解 LPM 的 2 级 Trie | +| 3 | 手写 LPM lookup + TTL 减一 + checksum | 跨网段 ping 通 | +| 4 | rdtsc 测量单次 lookup 延迟 | 内核 ~500ns vs DPDK ~200ns | + +### Phase 3: ARP 协议 + +| Layer | 内容 | 产出 | +|-------|------|------| +| 1 | 读 `net/ipv4/arp.c` 内核 ARP 实现 | 理解 ARP 表的维护逻辑 | +| 2 | 读 `lib/ring/rte_ring.h` 无锁 CAS 原理 | 理解 ring 的 MP/MC 模式 | +| 3 | 手写 ARP 请求/应答 + pending 队列 | arping 能收到回复 | +| 4 | 连续 ARP 风暴,检查 mbuf 泄漏 | 内存无泄漏 | + +### Phase 4: 控制面框架 + +| Layer | 内容 | 产出 | +|-------|------|------| +| 1 | 读内核 workqueue / tasklet 机制 | 理解异步任务调度 | +| 2 | 读 `rte_eal_remote_launch` + `rte_timer` | 理解 DPDK 的多核 + 定时器 | +| 3 | 手写 Pipeline 框架 + Interface Manager | 两机多 lcore 运行 | +| 4 | 控制面定时器无影响数据面延迟 | 数据面延迟不因定时器而抖动 | + +### Phase 5: NAT + 连接跟踪 + +| Layer | 内容 | 产出 | +|-------|------|------| +| 1 | 读 `net/netfilter/nf_conntrack_core.c` | 理解内核 conntrack 的锁竞争 | +| 2 | 读 `lib/hash/rte_cuckoo_hash.c` Cuckoo hash | 理解 2-bucket 开放寻址 | +| 3 | 手写 SNAT/DNAT + rte_hash 连接表 | 内网 curl 外网能通 | +| 4 | 100K 连接查找延迟 + 哈希冲突率 | 查找 O(1),冲突率 < 5% | + +### Phase 6: ACL + QoS + +| Layer | 内容 | 产出 | +|-------|------|------| +| 1 | 读内核 `net/sched/` TC 调度框架 | 理解多队列调度的复杂度 | +| 2 | 读 `lib/acl/rte_acl.h` + `lib/sched/` | 理解硬件加速分类 + 调度 | +| 3 | 手写 5 元组 ACL + 8 队列 SP/WRR | 满载大包时小包延迟 < 2ms | +| 4 | iperf3 大包 + ping 小包 CDF 曲线 | 内核 p99 > 80ms vs uNOS < 2ms | + +### Phase 7: IPv4/IPv6 + ICMP + Fragmentation + +| Layer | 内容 | 产出 | +|-------|------|------| +| 1 | 读 `net/ipv4/ip_input.c` IP 收包路径 | 理解 IP 层在内核中的位置 | +| 2 | 读 RFC 791, RFC 792, RFC 815 | 理解协议规范本身 | +| 3 | 手写 IP 封装/解封 + ICMP Echo Reply | 能 ping 通你的栈 | +| 4 | 分包重组 2000 字节包 | 正确重组 | + +### Phase 8: TCP 状态机 + +| Layer | 内容 | 产出 | +|-------|------|------| +| 1 | 读 `net/ipv4/tcp_input.c` tcp_rcv_state_process | 理解内核 11 状态 | +| 2 | 读 RFC 793(逐字读) | 画出自己的状态转换表 | +| 3 | 手写 11 状态 + scapy 逐状态触发 | gdb 单步跟踪每个转换 | +| 4 | scapy 100 万 TIME_WAIT → 时间轮 O(1) | 内核链表 O(n) vs 时间轮 O(1) | + +### Phase 9: 重传引擎 + +| Layer | 内容 | 产出 | +|-------|------|------| +| 1 | 读 `net/ipv4/tcp_output.c` tcp_retransmit_skb | 理解内核重传逻辑 | +| 2 | 读 RFC 6298 (RTO), RFC 2018 (SACK) | 理解重传算法 | +| 3 | 手写 RTO + 快速重传 + SACK | tc netem 丢包 5% → 正确补偿 | +| 4 | rdtsc 测量 RTO 精度 | 误差 < 10ms | + +### Phase 10: 拥塞控制 + +| Layer | 内容 | 产出 | +|-------|------|------| +| 1 | 读 `net/ipv4/tcp_cubic.c` 内核 CUBIC | 理解三次函数窗口增长 | +| 2 | 读 RFC 8312 + 2008 年 CUBIC 论文 | 理解设计决策 | +| 3 | 手写滑动窗口 + CUBIC | wireshark 验证窗口曲线 | +| 4 | 内核 TCP vs 自研 TCP 吞吐对比 | 1G: 940M vs 940M (持平) | +| | | CPU: 50% vs 8% (6x 优) | + +### Phase 11: Socket API + +| Layer | 内容 | 产出 | +|-------|------|------| +| 1 | 读 POSIX socket 语义 | 理解 API 设计哲学 | +| 2 | 读内核 `net/socket.c` socket 系统调用 | 理解 fd 到 sock 的映射 | +| 3 | 手写 tcp_socket/bind/listen/accept/send/recv | HTTP Server 跑在自研 TCP 上 | +| 4 | wrk 压测 HTTP QPS | 自研栈 ≥ 内核栈 QPS | + +### Phase 12-17 (应用层/管理面/生产级增强) + +每个 Phase 同样应用四层框架。Layer 1-2 以阅读对应 RFC / 工业标准实现为主。 + +--- + +## 五、方法论红黑榜 + +### ✅ 做这些 + +``` +每个概念都按 1→2→3→4 顺序来,不跳层 +Layer 3 时关掉所有参考,完全自己写 +每次 crash 先 gdb 至少 5 分钟再搜答案 +每层产出必须可验证(代码能跑 / 图表能看 / 数据能复现) +每天产出至少 50 行可运行的代码 +``` + +### ❌ 不做这些 + +``` +跳过 Layer 1 直接看 DPDK 文档 → 不知道为什么需要 +跳过 Layer 3 只看不写 → "我看懂了"(其实没有) +Layer 3 复制粘贴 examples → 没有内化 +不写文档 → 一个月后全忘了 +反复讨论设计但不写代码 → 设计永远不会完美 +不跑 perf 不生成数据 → 证明不了自己写的东西好 +``` + +--- + +## 六、每日日志模板 + +``` +日期: 2026-08-XX +阶段: Phase X - XXX + +Layer 1 (20min): + 读了: [内核文件/函数] + 理解: [一句话瓶颈描述] + +Layer 2 (20min): + 读了: [DPDK 文件/函数] + 理解: [一句话优化描述] + 画了: [数据路径图] + +Layer 3 (60min): + 写了: [文件路径,代码行数] + Crash: [次数 + 最关键的 bug] + gdb 发现: [最关键的 insight] + +Layer 4 (20min): + perf 数据: [关键对比数字] + 火焰图: [链接] + 文档: [docs/ 文件路径] + +明天计划: + [下一阶段的 Layer 1 要读什么] +``` + +--- + +> **文档结束** +> +> 这个方法不是学知识的,是逼你写代码的。 +> 系统编程不能靠"看懂",要靠"调通"。 +> 每层 20/20/60/20 分钟,一天 2 小时一个模块。 diff --git a/roadmap.md b/roadmap.md new file mode 100755 index 0000000..6c58d02 --- /dev/null +++ b/roadmap.md @@ -0,0 +1,3163 @@ +# DPDK 全景路线 v3 — 18 个月日级学习与发展规划 + +> **版本**: v3 (升级自 `~/myshare/DPDK_DPU_GROWTH_PLAN.md` v2) +> **起算日**: 2026-06-29 (周一) = Day 1 +> **截止日**: 2027-12-27 (周一) = Day 546 +> **目标定位**: 18 个月内同时具备「大厂网络/DPU 厂商/晋升专家/开源品牌」四个出口能力 +> **投入强度**: 工作日 2-3h + 周末 6-8h,约 25-30h/周,总投入 ~2000h +> **作者**: SONiC / SAI / DPDK 开发者 +> **最后更新**: 2026-06-29 + +--- + +## 📜 目录 + +- [一、规划总览](#一规划总览) +- [二、当前能力盘点](#二当前能力盘点) +- [三、六阶段主线](#三六阶段主线) +- [四、日任务格式约定](#四日任务格式约定) +- [四点五、Linux 内核对照学习策略(横切式)](#四点五linux-内核对照学习策略横切式) +- [五、留白与节假日策略](#五留白与节假日策略) +- [六、出口与简历卖点映射](#六出口与简历卖点映射) +- [七、推荐资料总表](#七推荐资料总表) +- [P1 · M1-M3 · DPDK 内功 + 用户态协议栈](#p1--dpdk-内功--用户态协议栈-m1-m3) +- [P2 · M4-M6 · SONiC×DPDK 融合](#p2--sonicdpdk-融合-m4-m6) +- [P3 · M7-M9 · vSwitch / 云网关数据面](#p3--vswitch--云网关数据面-m7-m9) +- [P4 · M10-M12 · 网络安全数据面](#p4--网络安全数据面-m10-m12) +- [P5 · M13-M15 · DPU 数据面架构](#p5--dpu-数据面架构-m13-m15) +- [P6 · M16-M18 · 作品收口 + 出口](#p6--作品收口--出口-m16-m18) +- [附录 A · 项目仓库结构建议](#附录-a--项目仓库结构建议) +- [附录 B · 性能基线参考](#附录-b--性能基线参考) +- [附录 C · 周/月复盘模板](#附录-c--周月复盘模板) + +--- + +## 一、规划总览 + +### 1.1 一句话目标 + +> **18 个月后**,我能够同时回答这三个问题: +> 1. 给我一张 100Gbps 网卡,我能写出**单核线速**的用户态数据面 +> 2. 给我一颗 DPU/SmartNIC,我能设计它的**数据面架构** +> 3. 给我一套 SONiC,我能让它跑在**纯 DPDK 软件转发**上 + +### 1.2 路径设计逻辑 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ P1 内功 (M1-3) │ +│ └─ mempool/ring/PMD/NUMA + 用户态协议栈 │ +│ ↓ │ +│ P2 SONiC×DPDK (M4-6) │ +│ └─ sonic-vs → SAI-DPDK 最小适配器 │ +│ ↓ │ +│ P3 vSwitch (M7-9) │ +│ └─ OVS-DPDK + VPP + 自研 LB │ +│ ↓ │ +│ P4 安全数据面 (M10-12) │ +│ └─ Suricata-DPDK + DDoS 清洗 │ +│ ↓ │ +│ P5 DPU 架构 (M13-15) │ +│ └─ PCIe + vDPA + rte_flow offload │ +│ ↓ │ +│ P6 收口 (M16-18) │ +│ └─ 开源 + 简历 + 答辩 + 面试 │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 1.3 核心约束 + +- **不超纲**:每天任务不超过 3.5h(工作日)/ 8h(周末),超出即拆分到次日 +- **可观测**:每天必须有一个产出物(代码 / 笔记 / 数据 / 图) +- **可中断**:每周第 7 天为复盘缓冲日,允许欠债结清 +- **可演进**:每月最后一天为月度复盘日,可调整下月节奏 + +--- + +## 二、当前能力盘点 + +| 维度 | 现状 | 备注 | +|---|---|---| +| DPDK API | 中级,能写应用 | `dpdk-ws-client`、`dperf` 改造 | +| 用户态协议栈 | 中级 | `libmtcp` 多版本迭代中 | +| SONiC 架构 | 入门-中级 | 看过 OrchAgent / syncd / APPL_DB | +| SAI | 入门-中级 | 读过头文件,未写过适配器 | +| Linux 内核网络 | 入门 | 有书在读 | +| PCIe | 入门 | 概念知道,未亲手解码 BAR | +| ASIC / SDK | 入门 | T140 / X540 用过 | +| Go / 工程化 | 中级 | go1.21 + 服务端经验 | +| 学历 | 专升本在读 | 不构成阻塞 | + +**核心资产(已有项目)**: +- `~/projects/dpdk-ws-client` — WebSocket over DPDK 客户端 +- `~/myshare/libmtcp-*` — mTCP 改造分支 +- `~/myshare/dperf-main` — 性能压测工具 +- `~/myshare/dpu_dev` — PCIe / DPU 学习目录 +- `~/myshare/dpdk-stable-24.11.6` — 主 DPDK 源码树 + +--- + +## 三、六阶段主线 + +| # | 阶段 | 月份 | Day 区间 | 主题 | 核心交付 | +|---|---|---|---|---|---| +| P1 | DPDK 内功 + 协议栈 | M1-M3 | Day 1-91 | mempool/ring/PMD 源码 + libmtcp 升级 | 单核 100Gbps echo + 性能报告 | +| P2 | SONiC×DPDK | M4-M6 | Day 92-182 | sonic-vs + SAI-DPDK 适配器 | 15 个 SAI API 跑通 L2/L3/ACL | +| P3 | vSwitch / 云网关 | M7-M9 | Day 183-273 | OVS-DPDK + VPP + 自研 LB | VPP plugin + LB 4-tuple hash | +| P4 | 安全数据面 | M10-M12 | Day 274-364 | Suricata-DPDK + DDoS 清洗 | 抗 5Mpps SYN flood | +| P5 | DPU 数据面 | M13-M15 | Day 365-455 | PCIe + vDPA + rte_flow offload | DPU 架构设计文档 | +| P6 | 收口 + 出口 | M16-M18 | Day 456-546 | 开源 + 简历 + 面试 | 简历 + 1 个高质量开源项目 | + +--- + +## 四、日任务格式约定 + +每天的任务条目格式**严格统一**,方便 grep / 勾选 / 复盘: + +```markdown +- [ ] **Day NNN (YYYY-MM-DD, 周X)** · 主题: <主题短句> + - 📖 学习: <读什么> (Xmin) + - 🔧 动手: <做什么> (Xmin) + - 📤 产出: <文件路径或结论> +``` + +**符号约定**: +- 📖 = 读源码 / 读书 / 看视频 +- 🔧 = 写代码 / 跑实验 / 抓数据 +- 📤 = 产出物(代码 commit / 笔记 / 图表 / 数据) +- 🎯 = 周/月里程碑 +- ☕ = 复盘 / 缓冲 / 休息 +- 🐧 = Linux 内核对照学习(与当周 DPDK 主题相互印证) + +--- + +## 四点五、Linux 内核对照学习策略(横切式) + +> **原则**: DPDK 工程师不懂 Linux 内核网络栈 = 只会调 API 的人。 +> v3 把内核学习**横切**进每个阶段,与 DPDK 主题成对照关系——学 DPDK PMD 时对照内核 ixgbe 驱动,学 SAI host_if 时对照内核 netlink,学 PCIe 时对照内核 `drivers/pci/`。 + +### 4.5.1 时间分配 + +| 时段 | 内核学习方式 | 时长/周 | +|---|---|---| +| 工作日(周二/周三 注入子任务)| 🐧 子任务,30min × ~2 天 | ~1h | +| 周日复盘日下半场 | 集中读源码 + 写对照笔记 | 1-2h | +| **合计** | | **~2-3h / 周** | +| 18 个月总投入 | 78 周 × 2.5h | **~195h** | + +### 4.5.2 学习地图(7 大模块,覆盖 P1-P5) + +| 模块 | 子主题 | 主要文件 | 对应阶段 | +|---|---|---|---| +| K1. packet path | skb / 软中断 / napi / rcu | `net/core/dev.c`, `net/ipv4/` | P1 | +| K2. NIC 驱动 | net_device, ndo_*, XDP, AF_XDP | `drivers/net/ethernet/intel/ixgbe/` | P1 | +| K3. TCP/IP 栈 | tcp_v4_rcv, 状态机, cubic/bbr | `net/ipv4/tcp_*.c` | P1 | +| K4. netlink | rtnetlink, genetlink | `net/netlink/`, `net/core/rtnetlink.c` | P2 | +| K5. tc / nftables / flow_offload | qdisc, flower, hw offload | `net/sched/`, `net/netfilter/` | P3-P4 | +| K6. 虚拟网络设备 | tun/tap, veth, bridge, macvlan | `drivers/net/{tun,veth}.c`, `net/bridge/` | P3 | +| K7. PCI / IOMMU / VFIO | pci_driver, msi, vfio | `drivers/pci/`, `drivers/vfio/` | P5 | + +### 4.5.3 推荐资料 + +- 📚 《Linux 内核网络协议栈从入门到精通》(你已有) +- 📚 《深入理解 Linux 网络》—— 张彦飞 +- 📚 《Understanding Linux Network Internals》(O'Reilly) +- 📂 内核源码:`linux-stable` (建议 v6.6 LTS) +- 🎥 LWN.net 网络栈专栏 +- 📝 论文:The Design and Implementation of Open vSwitch (NSDI'15) 对照内核 datapath + +### 4.5.4 阶段对应表 + +| 阶段 | 内核重点 | +|---|---| +| P1 (M1-3) | K1 packet path + K2 NIC 驱动 + K3 TCP/IP 栈(核心,对照 DPDK PMD/libmtcp) | +| P2 (M4-6) | K4 netlink(对照 SONiC host_if / sairedis 通知)| +| P3 (M7-9) | K5 tc/flow_offload + K6 虚拟设备(对照 OVS-DPDK / VPP)| +| P4 (M10-12) | K5 nftables + conntrack(对照 DDoS 清洗)| +| P5 (M13-15) | K7 PCI/VFIO + virtio-net 驱动(对照 vDPA / DPU 数据面)| +| P6 (M16-18) | 综合复盘 + 简历面试中讲对照 | + +### 4.5.5 注入位置 + +P1-P5 详细日计划中,每周选 1-2 天在原任务下注入一行 🐧 子任务(30min),格式: + +```markdown +- 🐧 内核对照: <内核侧主题> 读 `<内核源码路径>` (30min) +``` + +阶段开头也会列出"本阶段内核对照清单"。 + +--- + +## 五、留白与节假日策略 + +### 5.1 每周节奏(7 天循环) + +| 周几 | 类型 | 时长 | +|---|---|---| +| 周一 | 新主题启动 | 2-3h | +| 周二 | 主题深入 | 2-3h | +| 周三 | 主题深入 | 2-3h | +| 周四 | 整合实验 | 2-3h | +| 周五 | 整合实验 | 2-3h | +| 周六 | 大块动手 / 项目推进 | 6-8h | +| 周日 | ☕ 复盘 + 笔记整理 + 欠债补做 | 2-4h | + +### 5.2 月度节奏 + +- 每月最后一天 = ☕ 月度复盘日(不安排新内容) +- 每月第一个周日 = 调整下月节奏 + +### 5.3 法定假期空窗(不安排任务) + +| 假期 | 日期范围 | Day 区间 | +|---|---|---| +| 国庆 2026 | 10.01-10.07 | Day 95-101 | +| 元旦 2027 | 01.01-01.03 | Day 187-189 | +| 春节 2027 | 02.06-02.12 | Day 223-229 | +| 清明 2027 | 04.03-04.05 | Day 279-281 | +| 五一 2027 | 05.01-05.05 | Day 307-311 | +| 端午 2027 | 06.09-06.11 | Day 346-348 | +| 国庆 2027 | 10.01-10.07 | Day 460-466 | + +假期空窗用途:补做欠债 / 长项目冲刺 / 休息。文档中这些 Day 留空 + 标注 ☕ 。 + +--- + +## 六、出口与简历卖点映射 + +| 出口 | 关键阶段 | 简历卖点 | 推荐时机 | +|---|---|---|---| +| 一线大厂网络团队(阿里云/腾讯云/字节/华为 2012) | P1+P3+P5 | "单核 100Gbps 用户态协议栈"、"自研 vSwitch plugin"、"DPU 架构设计" | M15 末投递 | +| DPU 厂商(云豹/中科驭数/Pensando) | P5 主 + P2 副 | "SAI-DPDK 适配器"、"rte_flow offload 验证"、"BlueField vDPA 调研" | M15 末投递 | +| 现公司晋升技术专家 | P2+P4 | "SONiC×DPDK 融合落地"、"DDoS 清洗 5Mpps" | M12 末申报 | +| 开源 / 个人品牌 | 全程 | GitHub libmtcp-v3 + sai-dpdk-mini + 技术博客 | M18 | + +--- + +## 七、推荐资料总表 + +### 7.1 书籍 + +| 书 | 阶段 | 用途 | +|---|---|---| +| 《DPDK 应用开发实战》 | P1 | API 速通 | +| 《深入浅出 DPDK》 | P1-P3 | 原理理解 | +| 《Linux 内核网络协议栈从入门到精通》 | P1 | 内核对照 | +| 《计算机系统结构量化研究方法》 | P5 | DPU 架构 | +| 《PCI Express 体系结构导读》 | P5 | PCIe 必读 | +| 《SONiC 架构与实现》 | P2 | SONiC 入门 | + +### 7.2 源码 + +| 仓库 | 阶段 | +|---|---| +| dpdk-stable-24.11.6 | 全程 | +| sonic-buildimage / sonic-swss / sonic-sairedis | P2 | +| openvswitch-3.x | P3 | +| vpp-master | P3 | +| suricata-master | P4 | +| f-stack / mtcp | P1 | + +### 7.3 论文 + +- mTCP (NSDI'14) +- IX (OSDI'14) +- Snap (SOSP'19) — Google DPDK-like 框架 +- AccelNet (NSDI'18) — Azure SmartNIC +- BlueField DPU Architecture Guide (NVIDIA) + +--- + +# P1 · DPDK 内功 + 用户态协议栈 (M1-M3) + +> **Day 1-91**, 2026-06-29 ~ 2026-09-27 +> **核心目标**: 把 DPDK 的 mempool / ring / mbuf / PMD / NUMA / lcore 调度吃到骨头里,并把你的 `libmtcp` 升级为 v3(单核 100Gbps echo 线速) + +## P1.0 阶段目标 + +1. 读完 DPDK 五大子系统源码并产出笔记:mempool / ring / mbuf / ethdev / eal +2. `libmtcp` 升级到 v3:支持多 RSS 队列 + 零拷贝 + 单核线速 echo +3. 性能 profile 报告:火焰图 + perf top + IPC 数据 +4. 产出 5 篇深度笔记(每篇 3000 字+) + +## P1.1 关键交付物 + +- [ ] `notes/P1/` 下 13 周笔记(每周 1 篇 + 总结 1 篇) +- [ ] `libmtcp-v3` 仓库可运行 + benchmark 数据 +- [ ] `flamegraph/P1-final.svg` 性能火焰图 +- [ ] `perf-data/P1-100G-echo.md` 性能数据表 + +## P1.2 推荐资料 + +- 📚 《深入浅出 DPDK》Ch.3-7 +- 📂 `dpdk-stable-24.11.6/lib/{mempool,ring,mbuf,ethdev,eal}/` +- 🎥 DPDK Summit 历年 Talk(YouTube) +- 📝 mTCP NSDI'14 论文 + +## P1.2.5 🐧 本阶段内核对照清单(K1+K2+K3) + +| 周 | 对照主题 | 内核源码路径 | +|---|---|---| +| W1 | 内核启动 + 模块加载 | `init/main.c`, `kernel/module.c` | +| W2 | slab / kmem_cache (对照 DPDK mempool) | `mm/slab.c`, `mm/slub.c` | +| W3 | 内核无锁 — RCU + atomic (对照 DPDK ring) | `kernel/rcu/`, `include/linux/rculist.h` | +| W4 | skb_buff 结构 (对照 DPDK mbuf) | `include/linux/skbuff.h`, `net/core/skbuff.c` | +| W5 | 内核 ixgbe 驱动 (对照 DPDK ixgbe PMD) | `drivers/net/ethernet/intel/ixgbe/ixgbe_main.c` | +| W6 | NUMA + 进程调度 | `mm/mempolicy.c`, `kernel/sched/` | +| W7 | RPS/RFS + napi (对照 DPDK RSS) | `net/core/dev.c::netif_receive_skb` | +| W8 | 内核 TCP 协议栈总览 (对照 mTCP) | `net/ipv4/tcp.c`, `tcp_input.c` | +| W9 | 内核 socket 层 | `net/socket.c`, `net/ipv4/af_inet.c` | +| W10 | 内核 RX 路径全链路 | `napi_poll → netif_receive_skb → tcp_v4_rcv` | +| W11 | 内核 TX 路径 + qdisc | `__dev_queue_xmit → qdisc → ndo_start_xmit` | +| W12 | XDP / AF_XDP (DPDK 替代品对照) | `net/core/filter.c`, `net/xdp/` | +| W13 | 总结:DPDK vs Kernel 全景对照表 | — | + +## P1.3 周计划总览 (W1-W13) + +| 周 | Day | 主题 | 里程碑 | +|---|---|---|---| +| W1 | 1-7 | DPDK 环境 + EAL 启动流程 | hello_world 改造 | +| W2 | 8-14 | mempool 源码 + cache 机制 | mempool benchmark | +| W3 | 15-21 | ring 源码 + 无锁队列 | ring 压测 | +| W4 | 22-28 | mbuf 源码 + 零拷贝 | mbuf 操作笔记 | +| W5 | 29-35 | PMD 框架 + ixgbe/i40e | PMD 阅读笔记 | +| W6 | 36-42 | NUMA + lcore 调度 | NUMA 性能对比 | +| W7 | 43-49 | RSS + Flow Director | 多队列 echo | +| W8 | 50-56 | mTCP 源码精读 | mTCP 跑通 | +| W9 | 57-63 | libmtcp v3 设计 | 设计文档 | +| W10 | 64-70 | libmtcp v3 实现 (1) | RX/TX 路径 | +| W11 | 71-77 | libmtcp v3 实现 (2) | 协议栈核心 | +| W12 | 78-84 | 性能调优 + 火焰图 | 100G 线速 | +| W13 | 85-91 | P1 总结 + 文档 | 阶段答辩 | + +## P1.4 详细日计划 + +### Week 1 (Day 1-7) · DPDK 环境 + EAL 启动流程 + +- [ ] **Day 001 (2026-06-29, 周一)** · 主题: 环境校验 + 编译 DPDK 24.11 + - 📖 学习: 通读 `dpdk.org/doc/guides/linux_gsg/` (60min) + - 🔧 动手: 用 meson 重新编译 `~/myshare/dpdk-stable-24.11.6` 并跑 testpmd (90min) + - 📤 产出: `notes/P1/W1-D1-env-setup.md` 包含编译命令、hugepage 配置、网卡绑定步骤 + +- [ ] **Day 002 (2026-06-30, 周二)** · 主题: hugepage / IOMMU / VFIO + - 📖 学习: hugepage 原理 + VFIO vs UIO 对比 (60min) + - 🔧 动手: 在本机配置 1G hugepage × 8,验证 `/proc/meminfo` (90min) + - 🐧 内核对照: 读 `arch/x86/mm/hugetlbpage.c` 大页分配路径 (30min) + - 📤 产出: `notes/P1/W1-D2-hugepage-vfio.md` + +- [ ] **Day 003 (2026-07-01, 周三)** · 主题: EAL 启动流程上半段 + - 📖 学习: `lib/eal/common/eal_common_options.c` + `eal_init()` 主线 (90min) + - 🔧 动手: 加 printf 跟踪 EAL 启动过程,画出函数调用图 (90min) + - 📤 产出: `notes/P1/W1-D3-eal-init-1.md` + 调用图 + +- [ ] **Day 004 (2026-07-02, 周四)** · 主题: EAL 启动流程下半段 + - 📖 学习: lcore 创建、master/worker 线程、内存初始化 (90min) + - 🔧 动手: 写一个最小程序,启动 4 个 lcore 打印自己的 ID (90min) + - 📤 产出: `code/P1/eal-lcore-demo/` + +- [ ] **Day 005 (2026-07-03, 周五)** · 主题: hello_world 改造 + - 📖 学习: `examples/helloworld/main.c` (30min) + - 🔧 动手: 改造 helloworld,让每个 lcore 把自己绑定的 CPU、NUMA node、core_id 输出 (90min) + - 📤 产出: `code/P1/helloworld-plus/` + +- [ ] **Day 006 (2026-07-04, 周六)** · 主题: 大块实验 — testpmd 模式全览 + - 📖 学习: testpmd 各种 forward mode (60min) + - 🔧 动手: 用 X540 / X710 跑 io / mac / macswap / rxonly / txonly 五种模式,记录 PPS (240min) + - 📤 产出: `perf-data/P1/W1-testpmd-modes.md` 含 5 组数据 + 图表 + - 📤 产出: `code/P1/testpmd-runner.sh` 自动化脚本 + +- [ ] **Day 007 (2026-07-05, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P1/W1-summary.md`(W1 学到什么、卡在哪、下周计划) + +--- + +### Week 2 (Day 8-14) · mempool 源码 + cache 机制 + +- [ ] **Day 008 (2026-07-06, 周一)** · 主题: mempool 总览 + - 📖 学习: `lib/mempool/rte_mempool.h` + 设计文档 (90min) + - 🔧 动手: 写 demo 创建 mempool、alloc、free 对象 (60min) + - 📤 产出: `notes/P1/W2-D1-mempool-overview.md` + +- [ ] **Day 009 (2026-07-07, 周二)** · 主题: mempool cache 原理 + - 📖 学习: per-lcore cache 数据结构 + flush_threshold (90min) + - 🔧 动手: gdb 进入 `rte_mempool_get` 看 cache 命中路径 (60min) + - 🐧 内核对照: 读 `mm/slub.c` 的 per-cpu cache (kmem_cache_cpu) (30min) + - 📤 产出: `notes/P1/W2-D2-mempool-cache.md` + +- [ ] **Day 010 (2026-07-08, 周三)** · 主题: mempool ops (ring / stack / lf-stack) + - 📖 学习: `rte_mempool_ops` 抽象 + 三种后端实现 (90min) + - 🔧 动手: 用 `--mempool-ops=stack` 跑 testpmd 对比性能 (60min) + - 📤 产出: `notes/P1/W2-D3-mempool-ops.md` + +- [ ] **Day 011 (2026-07-09, 周四)** · 主题: mempool 与 NUMA + - 📖 学习: `rte_mempool_create_empty` 的 socket_id 参数 (60min) + - 🔧 动手: 同 socket vs 跨 socket 测 alloc 延迟 (90min) + - 📤 产出: `perf-data/P1/W2-mempool-numa.md` + +- [ ] **Day 012 (2026-07-10, 周五)** · 主题: mempool benchmark 工具 + - 📖 学习: `app/test/test_mempool_perf.c` (60min) + - 🔧 动手: 跑官方 perf test,记录 cache on/off 的差异 (90min) + - 📤 产出: `perf-data/P1/W2-mempool-perf.md` + +- [ ] **Day 013 (2026-07-11, 周六)** · 主题: 大块实验 — 自研 mempool benchmark + - 🔧 动手: 写一个工具,分别用 cache=0 / cache=128 / cache=512 测 8 核并发 alloc,输出火焰图 (360min) + - 📤 产出: `code/P1/mempool-bench/` + `flamegraph/W2-mempool.svg` + +- [ ] **Day 014 (2026-07-12, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P1/W2-summary.md` + +--- + +### Week 3 (Day 15-21) · ring 源码 + 无锁队列 + +- [ ] **Day 015 (2026-07-13, 周一)** · 主题: ring 基本 API + - 📖 学习: `lib/ring/rte_ring.h` + create/enqueue/dequeue (90min) + - 🔧 动手: 写 SPSC ring 通信 demo (60min) + - 📤 产出: `notes/P1/W3-D1-ring-api.md` + +- [ ] **Day 016 (2026-07-14, 周二)** · 主题: ring 无锁实现 — head/tail 指针 + - 📖 学习: `__rte_ring_move_prod_head` / `__rte_ring_move_cons_head` (120min) + - 🔧 动手: 手画 4 个线程并发 enqueue 的指针变化时序图 (60min) + - 🐧 内核对照: 读 `include/linux/rculist.h` 的 RCU 无锁链表 (30min) + - 📤 产出: `notes/P1/W3-D2-ring-lockfree.md` + +- [ ] **Day 017 (2026-07-15, 周三)** · 主题: ring 的内存屏障 + - 📖 学习: `rte_smp_wmb` / `rte_smp_rmb` + C11 atomic (90min) + - 🔧 动手: 在 ring 源码中标注每个屏障的作用 (60min) + - 📤 产出: `notes/P1/W3-D3-ring-barrier.md` + +- [ ] **Day 018 (2026-07-16, 周四)** · 主题: ring 变体 — RTS / HTS + - 📖 学习: Relaxed Tail Sync / Head Tail Sync 模式 (90min) + - 🔧 动手: 对比 default / RTS / HTS 三种模式的延迟 (60min) + - 📤 产出: `notes/P1/W3-D4-ring-variants.md` + +- [ ] **Day 019 (2026-07-17, 周五)** · 主题: ring 与 mempool 关系 + - 📖 学习: mempool default ops = ring 的原因 (60min) + - 🔧 动手: 看 mempool 如何用 ring 实现 (90min) + - 📤 产出: `notes/P1/W3-D5-ring-mempool.md` + +- [ ] **Day 020 (2026-07-18, 周六)** · 主题: 大块实验 — ring 压测对比 + - 🔧 动手: 写 ring 压测工具,分别测 SPSC/MPSC/MPMC 在 1/2/4/8 producer 下的 throughput + latency (360min) + - 📤 产出: `code/P1/ring-bench/` + `perf-data/P1/W3-ring-perf.md` + +- [ ] **Day 021 (2026-07-19, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P1/W3-summary.md` + +--- + +### Week 4 (Day 22-28) · mbuf 源码 + 零拷贝 + +- [ ] **Day 022 (2026-07-20, 周一)** · 主题: mbuf 结构详解 + - 📖 学习: `struct rte_mbuf` 字段分布 + cache line 布局 (90min) + - 🔧 动手: 写 demo 打印 mbuf 各字段地址,验证 cache line (60min) + - 🐧 内核对照: 读 `include/linux/skbuff.h` 的 `struct sk_buff` (30min) + - 📤 产出: `notes/P1/W4-D1-mbuf-layout.md`(含 mbuf vs skb 对照表) + +- [ ] **Day 023 (2026-07-21, 周二)** · 主题: mbuf 链 + segments + - 📖 学习: chained mbuf / next 指针 / nb_segs (90min) + - 🔧 动手: 构造一个 3 段的 mbuf chain 并发包 (60min) + - 📤 产出: `notes/P1/W4-D2-mbuf-chain.md` + +- [ ] **Day 024 (2026-07-22, 周三)** · 主题: mbuf headroom / tailroom + - 📖 学习: prepend / append / trim / adj (90min) + - 🔧 动手: 写一个 IP-in-IP 封装 demo,用 prepend 加外层 IP (90min) + - 📤 产出: `code/P1/mbuf-encap-demo/` + +- [ ] **Day 025 (2026-07-23, 周四)** · 主题: 外部 mbuf (external buffer) + - 📖 学习: `rte_pktmbuf_attach_extbuf` (60min) + - 🔧 动手: 把用户内存挂到 mbuf 实现零拷贝 (90min) + - 📤 产出: `notes/P1/W4-D4-mbuf-extbuf.md` + +- [ ] **Day 026 (2026-07-24, 周五)** · 主题: mbuf offload flags + - 📖 学习: TX/RX offload 标志位(checksum / TSO / VLAN)(90min) + - 🔧 动手: 用 testpmd 验证 TSO offload (60min) + - 📤 产出: `notes/P1/W4-D5-mbuf-offload.md` + +- [ ] **Day 027 (2026-07-25, 周六)** · 主题: 大块实验 — 零拷贝 echo server + - 🔧 动手: 实现一个不分配新 mbuf 的 echo server(in-place swap MAC/IP)(360min) + - 📤 产出: `code/P1/zerocopy-echo/` + benchmark 数据 + +- [ ] **Day 028 (2026-07-26, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P1/W4-summary.md` + +--- + +### Week 5 (Day 29-35) · PMD 框架 + ixgbe/i40e + +- [ ] **Day 029 (2026-07-27, 周一)** · 主题: ethdev 抽象层 + - 📖 学习: `lib/ethdev/rte_ethdev.h` + dev_ops (90min) + - 🔧 动手: 列出所有 dev_ops 函数指针并理解用途 (60min) + - 📤 产出: `notes/P1/W5-D1-ethdev.md` + +- [ ] **Day 030 (2026-07-28, 周二)** · 主题: ixgbe PMD 总览 + - 📖 学习: `drivers/net/ixgbe/ixgbe_ethdev.c` (120min) + - 🐧 内核对照: 读 `linux/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c` 的 probe + open (45min) + - 📤 产出: `notes/P1/W5-D2-ixgbe-overview.md`(含 DPDK vs Kernel 对照表) + +- [ ] **Day 031 (2026-07-29, 周三)** · 主题: ixgbe RX 路径 + - 📖 学习: `ixgbe_recv_pkts` + 描述符环 (120min) + - 🔧 动手: 在 RX 路径打点统计 burst 大小分布 (60min) + - 📤 产出: `notes/P1/W5-D3-ixgbe-rx.md` + +- [ ] **Day 032 (2026-07-30, 周四)** · 主题: ixgbe TX 路径 + - 📖 学习: `ixgbe_xmit_pkts` + TX descriptor (120min) + - 📤 产出: `notes/P1/W5-D4-ixgbe-tx.md` + +- [ ] **Day 033 (2026-07-31, 周五)** · 主题: vector PMD (AVX) + - 📖 学习: `ixgbe_rxtx_vec_*.c` SIMD 优化 (90min) + - 🔧 动手: 对比 vector / scalar PMD 的 PPS (60min) + - 📤 产出: `notes/P1/W5-D5-ixgbe-vec.md` + +- [ ] **Day 034 (2026-08-01, 周六)** · 主题: 大块实验 — PMD 替换实验 + - 🔧 动手: 写一个跨 PMD 的 forward 程序(ixgbe ↔ virtio),对比性能 (360min) + - 📤 产出: `code/P1/cross-pmd-fwd/` + +- [ ] **Day 035 (2026-08-02, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P1/W5-summary.md` + +--- + +### Week 6 (Day 36-42) · NUMA + lcore 调度 + +- [ ] **Day 036 (2026-08-03, 周一)** · 主题: NUMA 拓扑探查 + - 📖 学习: `lstopo` / `numactl` / `/sys/devices/system/node/` (60min) + - 🔧 动手: 画出本机 NUMA 拓扑图 (90min) + - 📤 产出: `notes/P1/W6-D1-numa-topo.md` + +- [ ] **Day 037 (2026-08-04, 周二)** · 主题: 跨 socket 性能惩罚 + - 🔧 动手: 同 socket / 跨 socket 跑 mempool + ring 测试 (120min) + - 🐧 内核对照: 读 `mm/mempolicy.c` 看内核 NUMA 分配策略 (30min) + - 📤 产出: `perf-data/P1/W6-numa-penalty.md` + +- [ ] **Day 038 (2026-08-05, 周三)** · 主题: lcore 绑核机制 + - 📖 学习: `--lcores` 参数解析 + pthread_setaffinity_np (90min) + - 🔧 动手: 测试不同绑核策略的性能差异 (60min) + - 📤 产出: `notes/P1/W6-D3-lcore-affinity.md` + +- [ ] **Day 039 (2026-08-06, 周四)** · 主题: service core + - 📖 学习: `rte_service` 框架 (60min) + - 🔧 动手: 写一个 service core demo 处理统计任务 (90min) + - 📤 产出: `code/P1/service-core-demo/` + +- [ ] **Day 040 (2026-08-07, 周五)** · 主题: lcore 间通信模式 + - 📖 学习: pipeline / run-to-completion 两种模型 (90min) + - 🔧 动手: 各写一个 demo 对比 (90min) + - 📤 产出: `code/P1/lcore-models/` + +- [ ] **Day 041 (2026-08-08, 周六)** · 主题: 大块实验 — pipeline forward + - 🔧 动手: 实现 RX → classify → fwd 三阶段 pipeline,测 PPS (360min) + - 📤 产出: `code/P1/pipeline-fwd/` + +- [ ] **Day 042 (2026-08-09, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P1/W6-summary.md` + +--- + +### Week 7 (Day 43-49) · RSS + Flow Director + +- [ ] **Day 043 (2026-08-10, 周一)** · 主题: RSS 原理 + - 📖 学习: Toeplitz hash + RETA table (90min) + - 📤 产出: `notes/P1/W7-D1-rss.md` + +- [ ] **Day 044 (2026-08-11, 周二)** · 主题: RSS 配置实战 + - 🔧 动手: testpmd 配置 RSS,观察队列分布 (120min) + - 🐧 内核对照: 读 `net/core/dev.c` 的 RPS/RFS 实现(`get_rps_cpu`)(30min) + - 📤 产出: `notes/P1/W7-D2-rss-cfg.md` + +- [ ] **Day 045 (2026-08-12, 周三)** · 主题: Symmetric RSS + - 📖 学习: 对称 RSS key 设计 (90min) + - 🔧 动手: 用对称 key 让双向流落同队列 (60min) + - 📤 产出: `notes/P1/W7-D3-sym-rss.md` + +- [ ] **Day 046 (2026-08-13, 周四)** · 主题: rte_flow 入门 + - 📖 学习: `lib/ethdev/rte_flow.h` (90min) + - 🔧 动手: 写一条 flow rule 把特定 5-tuple 导到指定队列 (90min) + - 📤 产出: `code/P1/rte-flow-demo/` + +- [ ] **Day 047 (2026-08-14, 周五)** · 主题: Flow Director vs RSS + - 📖 学习: 两者适用场景 (60min) + - 📤 产出: `notes/P1/W7-D5-fdir-vs-rss.md` + +- [ ] **Day 048 (2026-08-15, 周六)** · 主题: 大块实验 — 多队列 echo + - 🔧 动手: 实现 16 队列 RSS echo,测单核 vs 16 核线速 (360min) + - 📤 产出: `code/P1/multiq-echo/` + `perf-data/P1/W7-multiq.md` + +- [ ] **Day 049 (2026-08-16, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P1/W7-summary.md` + +--- + +### Week 8 (Day 50-56) · mTCP 源码精读 + +- [ ] **Day 050 (2026-08-17, 周一)** · 主题: mTCP 论文精读 + - 📖 学习: NSDI'14 mTCP 论文全文 (180min) + - 📤 产出: `notes/P1/W8-D1-mtcp-paper.md` + +- [ ] **Day 051 (2026-08-18, 周二)** · 主题: mTCP 架构 + - 📖 学习: `mtcp/src/core.c` + 主循环 (120min) + - 📤 产出: `notes/P1/W8-D2-mtcp-arch.md` + +- [ ] **Day 052 (2026-08-19, 周三)** · 主题: mTCP TCP 状态机 + - 📖 学习: `mtcp/src/tcp_in.c` / `tcp_out.c` (150min) + - 🐧 内核对照: 读 `net/ipv4/tcp_input.c::tcp_rcv_state_process` (45min) + - 📤 产出: `notes/P1/W8-D3-mtcp-tcp.md`(含 mTCP vs Linux TCP 状态机对照) + +- [ ] **Day 053 (2026-08-20, 周四)** · 主题: mTCP epoll 实现 + - 📖 学习: `mtcp/src/eventpoll.c` (120min) + - 📤 产出: `notes/P1/W8-D4-mtcp-epoll.md` + +- [ ] **Day 054 (2026-08-21, 周五)** · 主题: mTCP IO 抽象 + - 📖 学习: `mtcp/src/io_module.c` 如何对接 DPDK (90min) + - 📤 产出: `notes/P1/W8-D5-mtcp-io.md` + +- [ ] **Day 055 (2026-08-22, 周六)** · 主题: 大块实验 — mTCP 跑通 epwget/epserver + - 🔧 动手: 编译 mTCP + 你已有的 `libmtcp` 跑 epserver/epwget benchmark (360min) + - 📤 产出: `perf-data/P1/W8-mtcp-bench.md` + +- [ ] **Day 056 (2026-08-23, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P1/W8-summary.md` + +--- + +### Week 9 (Day 57-63) · libmtcp v3 设计 + +- [ ] **Day 057 (2026-08-24, 周一)** · 主题: 现有 libmtcp 代码盘点 + - 🔧 动手: 通读你之前的 `libmtcp-20260622` / `20260625` 改动 (120min) + - 📤 产出: `notes/P1/W9-D1-libmtcp-audit.md` + +- [ ] **Day 058 (2026-08-25, 周二)** · 主题: v3 目标定义 + - 🔧 动手: 写 v3 RFC(功能 / 性能目标)(120min) + - 🐧 内核对照: 读 `net/socket.c` 看 socket() / bind() / listen() 内核实现 (30min) + - 📤 产出: `docs/libmtcp-v3-rfc.md` + +- [ ] **Day 059 (2026-08-26, 周三)** · 主题: v3 模块划分 + - 🔧 动手: 画出模块图 + 数据流图 (120min) + - 📤 产出: `docs/libmtcp-v3-arch.md` + +- [ ] **Day 060 (2026-08-27, 周四)** · 主题: v3 API 设计 + - 🔧 动手: 写 `libmtcp_v3.h` 头文件(仅声明)(120min) + - 📤 产出: `code/libmtcp-v3/include/libmtcp_v3.h` + +- [ ] **Day 061 (2026-08-28, 周五)** · 主题: v3 内存模型 + - 🔧 动手: 设计 mempool / mbuf 复用策略 (90min) + - 📤 产出: `docs/libmtcp-v3-memory.md` + +- [ ] **Day 062 (2026-08-29, 周六)** · 主题: 大块实验 — 搭脚手架 + - 🔧 动手: 创建仓库、Makefile、单元测试框架、CI 配置 (360min) + - 📤 产出: `code/libmtcp-v3/` 仓库初始化 + +- [ ] **Day 063 (2026-08-30, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P1/W9-summary.md` + +--- + +### Week 10 (Day 64-70) · libmtcp v3 实现 (1) — RX/TX 路径 + +- [ ] **Day 064 (2026-08-31, 周一)** · 主题: RX 路径骨架 + - 🔧 动手: 实现 `mtcp_rx_burst` + ring 分发 (180min) + - 📤 产出: commit "rx burst skeleton" + +- [ ] **Day 065 (2026-09-01, 周二)** · 主题: RX 路径解析 + - 🔧 动手: 实现 ether/ip/tcp 头部解析 (150min) + - 🐧 内核对照: 读 `net/core/dev.c::__netif_receive_skb_core` 看内核 RX 主入口 (30min) + - 📤 产出: commit "parse headers" + +- [ ] **Day 066 (2026-09-02, 周三)** · 主题: RX 路径连接查表 + - 🔧 动手: 用 rte_hash 实现 4-tuple 查表 (150min) + - 📤 产出: commit "conn lookup" + +- [ ] **Day 067 (2026-09-03, 周四)** · 主题: TX 路径骨架 + - 🔧 动手: 实现 `mtcp_tx_burst` + 批量发送 (150min) + - 📤 产出: commit "tx burst skeleton" + +- [ ] **Day 068 (2026-09-04, 周五)** · 主题: TX 路径优化 + - 🔧 动手: prefetch + batch (120min) + - 📤 产出: commit "tx prefetch" + +- [ ] **Day 069 (2026-09-05, 周六)** · 主题: 大块实验 — 端到端 echo + - 🔧 动手: 让 v3 跑通最简 echo,测初步 PPS (360min) + - 📤 产出: `perf-data/P1/W10-v3-echo-v0.md` + +- [ ] **Day 070 (2026-09-06, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P1/W10-summary.md` + +--- + +### Week 11 (Day 71-77) · libmtcp v3 实现 (2) — 协议栈核心 + +- [ ] **Day 071 (2026-09-07, 周一)** · 主题: TCP 状态机骨架 + - 🔧 动手: 实现 LISTEN/SYN_RECV/ESTABLISHED (180min) + - 📤 产出: commit "tcp fsm 1" + +- [ ] **Day 072 (2026-09-08, 周二)** · 主题: TCP 状态机 (FIN/CLOSE) + - 🔧 动手: 实现 FIN_WAIT/CLOSE_WAIT/TIME_WAIT (180min) + - 🐧 内核对照: 读 `net/ipv4/tcp_timer.c` 看内核 TIME_WAIT 处理 (30min) + - 📤 产出: commit "tcp fsm 2" + +- [ ] **Day 073 (2026-09-09, 周三)** · 主题: 重传 / 拥塞控制 + - 🔧 动手: 实现 RTO + cwnd 简化版 (180min) + - 📤 产出: commit "rto + cwnd" + +- [ ] **Day 074 (2026-09-10, 周四)** · 主题: 收发缓冲区 + - 🔧 动手: 环形 buffer + 零拷贝引用 (150min) + - 📤 产出: commit "sock buffer" + +- [ ] **Day 075 (2026-09-11, 周五)** · 主题: API 封装 + - 🔧 动手: 暴露 mtcp_socket/listen/accept/send/recv (150min) + - 📤 产出: commit "user api" + +- [ ] **Day 076 (2026-09-12, 周六)** · 主题: 大块实验 — echo 完整版 + - 🔧 动手: 写 echo server demo + 客户端,跑 1000 并发连接 (360min) + - 📤 产出: `code/libmtcp-v3/examples/echo/` + +- [ ] **Day 077 (2026-09-13, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P1/W11-summary.md` + +--- + +### Week 12 (Day 78-84) · 性能调优 + 火焰图 + +- [ ] **Day 078 (2026-09-14, 周一)** · 主题: perf top + IPC + - 🔧 动手: 用 `perf top` / `perf stat` 找热点 (150min) + - 📤 产出: `perf-data/P1/W12-perf-top.md` + +- [ ] **Day 079 (2026-09-15, 周二)** · 主题: 火焰图 + - 🔧 动手: 用 FlameGraph 生成 svg (120min) + - 🐧 内核对照: 读 `kernel/events/core.c` 看 perf 子系统入口 (30min) + - 📤 产出: `flamegraph/W12-v3.svg` + +- [ ] **Day 080 (2026-09-16, 周三)** · 主题: cache miss 分析 + - 🔧 动手: `perf c2c` / `perf mem` 找 false sharing (150min) + - 📤 产出: `perf-data/P1/W12-cache-miss.md` + +- [ ] **Day 081 (2026-09-17, 周四)** · 主题: branch / prefetch 优化 + - 🔧 动手: 加 likely/unlikely + rte_prefetch0 (150min) + - 📤 产出: commit "prefetch opt" + +- [ ] **Day 082 (2026-09-18, 周五)** · 主题: 批量化 + - 🔧 动手: 把单包路径改批量路径 (150min) + - 📤 产出: commit "batch path" + +- [ ] **Day 083 (2026-09-19, 周六)** · 主题: 大块实验 — 100G 线速冲刺 + - 🔧 动手: 反复调优 + 测试,目标单核 14.88 Mpps (480min) + - 📤 产出: `perf-data/P1/W12-100G-echo.md`(最终数据) + +- [ ] **Day 084 (2026-09-20, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P1/W12-summary.md` + +--- + +### Week 13 (Day 85-91) · P1 总结 + 文档 + +- [ ] **Day 085 (2026-09-21, 周一)** · 主题: 整理 P1 所有产出 + - 🔧 动手: 把 notes / code / perf-data 重新组织 (180min) + - 📤 产出: `P1-final/` 目录 + +- [ ] **Day 086 (2026-09-22, 周二)** · 主题: 写 P1 技术博客 (1) + - 🔧 动手: "DPDK mempool/ring/mbuf 源码深读" 一文 (180min) + - 🐧 内核对照: 在博客里加 "DPDK vs 内核" 对照章节 (30min) + - 📤 产出: `blog/P1-internals.md` + +- [ ] **Day 087 (2026-09-23, 周三)** · 主题: 写 P1 技术博客 (2) + - 🔧 动手: "从 0 写一个 100G 用户态 TCP 协议栈" 一文 (180min) + - 📤 产出: `blog/P1-libmtcp-v3.md` + +- [ ] **Day 088 (2026-09-24, 周四)** · 主题: libmtcp-v3 README + 文档 + - 🔧 动手: 完善 README、架构图、性能对比表 (180min) + - 📤 产出: `code/libmtcp-v3/README.md` + +- [ ] **Day 089 (2026-09-25, 周五)** · 主题: 简历草稿 v1 + - 🔧 动手: 把 P1 成果写进简历项目栏 (90min) + - 📤 产出: `career/resume-v1.md` + +- [ ] **Day 090 (2026-09-26, 周六)** · 主题: 大块实验 — 公开发布准备 + - 🔧 动手: 项目推 GitHub,写 release note v0.1 (240min) + - 📤 产出: GitHub release + +- [ ] **Day 091 (2026-09-27, 周日)** · 🎯 P1 阶段答辩 + - 📤 产出: `P1-final/answer.md`(自问自答 10 个核心问题) + - 📤 产出: `P1-final/retro.md`(阶段复盘 + P2 调整建议) + +--- + +# P2 · SONiC×DPDK 融合 (M4-M6) + +> **Day 92-182**, 2026-09-28 ~ 2026-12-27 +> **核心目标**: 跑通 sonic-vs,并实现 SAI-DPDK 最小适配器(15 个核心 API),让 SONiC 控制面操作能落到 DPDK 数据面上 + +## P2.0 阶段目标 + +1. 完整跑通 sonic-vs(Virtual Switch)镜像,理解 SONiC 容器栈 +2. 读懂 SAI 头文件 + sairedis 链路 +3. 实现 `sai-dpdk-mini`:15 个 API(switch/port/fdb/vlan/route/neighbor/acl/pkt-io) +4. SONiC 上跑通 L2 forwarding + L3 routing + 基本 ACL + +## P2.1 关键交付物 + +- [ ] `sonic-vs` 完整跑通 + 容器架构笔记 +- [ ] `sai-dpdk-mini` GitHub 仓库 + 15 个 API 实现 +- [ ] SONiC + sai-dpdk-mini 集成 demo 视频/截图 +- [ ] `notes/P2/` 13 周笔记 + +## P2.1.5 🐧 本阶段内核对照清单(K4 + K6) + +| 周 | 对照主题 | 内核源码路径 | +|---|---|---| +| W14 | 内核 namespace (容器隔离基础) | `kernel/nsproxy.c`, `kernel/pid_namespace.c` | +| W15 | cgroup + container | `kernel/cgroup/` | +| W16 | netlink 总览 | `net/netlink/af_netlink.c` | +| W17 | rtnetlink(端口/路由/邻居)| `net/core/rtnetlink.c` | +| W18 | netlink RTM_NEWLINK 流程 | `net/core/rtnetlink.c::rtnl_newlink` | +| W19 | FIB + 路由表 | `net/ipv4/fib_trie.c` | +| W20 | neighbour subsystem (ARP/ND) | `net/core/neighbour.c`, `net/ipv4/arp.c` | +| W21 | net_device + ndo_* | `net/core/dev.c`, `include/linux/netdevice.h` | +| W22 | bridge + FDB | `net/bridge/br_fdb.c` | +| W23 | VLAN (802.1Q)| `net/8021q/vlan_core.c` | +| W24 | nftables / iptables | `net/netfilter/nf_tables_core.c` | +| W25 | tun/tap (host_if 对照) | `drivers/net/tun.c` | +| W26 | 综合:SONiC kernel host_if 路径 | `tun/tap → netif_receive_skb` | + +## P2.2 周计划总览 (W14-W26) + +| 周 | Day | 主题 | 里程碑 | +|---|---|---|---| +| W14 | 92-98 | sonic-vs 跑通 | container up | +| W15 | 99-105 | SONiC 容器架构 | 画出全栈图 | +| W16 | 106-112 | APPL_DB → ASIC_DB 链路 | 抓 redis 流 | +| W17 | 113-119 | SAI 头文件全览 | 分类整理 | +| W18 | 120-126 | sairedis 源码 | RPC 链路 | +| W19 | 127-133 | syncd 源码 | dispatch 机制 | +| W20 | 134-140 | sai-dpdk-mini 设计 | RFC | +| W21 | 141-147 | sai_switch + sai_port | 编译通过 | +| W22 | 148-154 | sai_fdb + sai_vlan | L2 跑通 | +| W23 | 155-161 | sai_route + sai_neighbor | L3 跑通 | +| W24 | 162-168 | sai_acl + rte_flow | ACL 跑通 | +| W25 | 169-175 | sai pkt-io (host_if) | CPU 收发 | +| W26 | 176-182 | 集成 + 答辩 | demo + 文档 | + +## P2.3 详细日计划 + +### Week 14 (Day 92-98) · sonic-vs 跑通 + +- [ ] **Day 092 (2026-09-28, 周一)** · 主题: SONiC 总览 + - 📖 学习: SONiC 官方文档 Architecture 章节 (120min) + - 📤 产出: `notes/P2/W14-D1-sonic-overview.md` + +- [ ] **Day 093 (2026-09-29, 周二)** · 主题: 拉取 sonic-vs 镜像 + - 🔧 动手: 从 sonic-buildimage 拉 vs 镜像或直接 download (120min) + - 🐧 内核对照: 读 `kernel/nsproxy.c` + `Documentation/admin-guide/namespaces/` (30min) + - 📤 产出: `notes/P2/W14-D2-sonic-vs-image.md` + +- [ ] **Day 094 (2026-09-30, 周三)** · 主题: 启动 sonic-vs 容器 + - 🔧 动手: 用 docker 跑起来,登录 cli (120min) + - 📤 产出: `notes/P2/W14-D3-sonic-vs-up.md` + +- [ ] **Day 095 (2026-10-01, 周四)** · ☕ 国庆假期 — 留白 + +- [ ] **Day 096 (2026-10-02, 周五)** · ☕ 国庆假期 — 留白 + +- [ ] **Day 097 (2026-10-03, 周六)** · ☕ 国庆假期 — 留白 + +- [ ] **Day 098 (2026-10-04, 周日)** · ☕ 国庆假期 — 留白 + +--- + +### Week 15 (Day 99-105) · SONiC 容器架构 + +- [ ] **Day 099 (2026-10-05, 周一)** · ☕ 国庆假期 — 留白 + +- [ ] **Day 100 (2026-10-06, 周二)** · ☕ 国庆假期 — 留白 + +- [ ] **Day 101 (2026-10-07, 周三)** · ☕ 国庆假期 — 留白 + +- [ ] **Day 102 (2026-10-08, 周四)** · 主题: SONiC 容器全家福 + - 🔧 动手: `docker ps` 看所有容器,画交互图 (150min) + - 📤 产出: `notes/P2/W15-D4-containers.md` + +- [ ] **Day 103 (2026-10-09, 周五)** · 主题: swss 容器内部 + - 🔧 动手: 进 swss 容器看 orchagent 进程 (150min) + - 🐧 内核对照: 读 `kernel/cgroup/cgroup.c` 看容器资源限制 (30min) + - 📤 产出: `notes/P2/W15-D5-swss.md` + +- [ ] **Day 104 (2026-10-10, 周六)** · 主题: 大块实验 — 容器交互全链路 + - 🔧 动手: 从 CLI 下一条 vlan 命令,跟踪到 ASIC_DB (360min) + - 📤 产出: `notes/P2/W15-D6-fullpath.md` + +- [ ] **Day 105 (2026-10-11, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P2/W15-summary.md` + +--- + +### Week 16 (Day 106-112) · APPL_DB → ASIC_DB 链路 + +- [ ] **Day 106 (2026-10-12, 周一)** · 主题: Redis 数据库结构 + - 📖 学习: 6 个 DB 的用途 (90min) + - 🔧 动手: `redis-cli -n 0/1/2/...` 各看一遍 (60min) + - 📤 产出: `notes/P2/W16-D1-redis-dbs.md` + +- [ ] **Day 107 (2026-10-13, 周二)** · 主题: APPL_DB 表结构 + - 🔧 动手: 抓 PORT/VLAN/ROUTE 表 (120min) + - 🐧 内核对照: 读 `net/core/rtnetlink.c::rtnl_newlink` 看内核如何创建网卡 (30min) + - 📤 产出: `notes/P2/W16-D2-appl-db.md` + +- [ ] **Day 108 (2026-10-14, 周三)** · 主题: ASIC_DB 表结构 + - 🔧 动手: 抓 ASIC_STATE 表 (120min) + - 📤 产出: `notes/P2/W16-D3-asic-db.md` + +- [ ] **Day 109 (2026-10-15, 周四)** · 主题: Producer/Consumer Table + - 📖 学习: swss-common 表抽象 (90min) + - 📤 产出: `notes/P2/W16-D4-tables.md` + +- [ ] **Day 110 (2026-10-16, 周五)** · 主题: KCO/SCO 通知 + - 📖 学习: Key/Sub Change Object 机制 (90min) + - 📤 产出: `notes/P2/W16-D5-notify.md` + +- [ ] **Day 111 (2026-10-17, 周六)** · 主题: 大块实验 — Redis 流量抓取 + - 🔧 动手: `redis-cli monitor` 同步观察一次完整配置 (360min) + - 📤 产出: `perf-data/P2/W16-redis-trace.md` + +- [ ] **Day 112 (2026-10-18, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P2/W16-summary.md` + +--- + +### Week 17 (Day 113-119) · SAI 头文件全览 + +- [ ] **Day 113 (2026-10-19, 周一)** · 主题: SAI 总览 + - 📖 学习: sai.h + sai_object_type (120min) + - 📤 产出: `notes/P2/W17-D1-sai-overview.md` + +- [ ] **Day 114 (2026-10-20, 周二)** · 主题: SAI Switch / Port + - 📖 学习: saiswitch.h / saiport.h (120min) + - 🐧 内核对照: 读 `include/linux/netdevice.h` 看 `struct net_device` 与 ndo_* (30min) + - 📤 产出: `notes/P2/W17-D2-sai-switch-port.md`(含 SAI vs net_device 对照) + +- [ ] **Day 115 (2026-10-21, 周三)** · 主题: SAI FDB / VLAN + - 📖 学习: saifdb.h / saivlan.h (120min) + - 📤 产出: `notes/P2/W17-D3-sai-l2.md` + +- [ ] **Day 116 (2026-10-22, 周四)** · 主题: SAI Route / Neighbor / NextHop + - 📖 学习: sairoute.h / saineighbor.h / sainexthop.h (150min) + - 📤 产出: `notes/P2/W17-D4-sai-l3.md` + +- [ ] **Day 117 (2026-10-23, 周五)** · 主题: SAI ACL + - 📖 学习: saiacl.h (120min) + - 📤 产出: `notes/P2/W17-D5-sai-acl.md` + +- [ ] **Day 118 (2026-10-24, 周六)** · 主题: 大块实验 — SAI API 分类整理 + - 🔧 动手: 把所有 SAI API 列表 + 标红 15 个 MVP API (360min) + - 📤 产出: `notes/P2/W17-D6-sai-api-matrix.md` + +- [ ] **Day 119 (2026-10-25, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P2/W17-summary.md` + +--- + +### Week 18 (Day 120-126) · sairedis 源码 + +- [ ] **Day 120 (2026-10-26, 周一)** · 主题: sairedis 总览 + - 📖 学习: sonic-sairedis 仓库结构 (120min) + - 📤 产出: `notes/P2/W18-D1-sairedis-overview.md` + +- [ ] **Day 121 (2026-10-27, 周二)** · 主题: ClientSai + - 📖 学习: `lib/ClientSai.cpp` (150min) + - 🐧 内核对照: 读 `net/netlink/af_netlink.c` 看 netlink socket 实现 (30min) + - 📤 产出: `notes/P2/W18-D2-clientsai.md` + +- [ ] **Day 122 (2026-10-28, 周三)** · 主题: RedisRemoteSaiInterface + - 📖 学习: 如何把 SAI 调用转 Redis (150min) + - 📤 产出: `notes/P2/W18-D3-redis-interface.md` + +- [ ] **Day 123 (2026-10-29, 周四)** · 主题: 序列化协议 + - 📖 学习: meta + sai_serialize_* (120min) + - 📤 产出: `notes/P2/W18-D4-serialize.md` + +- [ ] **Day 124 (2026-10-30, 周五)** · 主题: 通知反向链路 + - 📖 学习: NotificationProducer/Consumer (120min) + - 📤 产出: `notes/P2/W18-D5-notify-back.md` + +- [ ] **Day 125 (2026-10-31, 周六)** · 主题: 大块实验 — 抓一次 SAI 调用 + - 🔧 动手: 在 orchagent 加日志,抓 sai_create_route 完整链路 (360min) + - 📤 产出: `perf-data/P2/W18-sai-trace.md` + +- [ ] **Day 126 (2026-11-01, 周日)** · ☕ 复盘 + 月度复盘 + - 📤 产出: `notes/P2/W18-summary.md` + `monthly/2026-10.md` + +--- + +### Week 19 (Day 127-133) · syncd 源码 + +- [ ] **Day 127 (2026-11-02, 周一)** · 主题: syncd 启动流程 + - 📖 学习: `syncd/syncd_main.cpp` (120min) + - 📤 产出: `notes/P2/W19-D1-syncd-main.md` + +- [ ] **Day 128 (2026-11-03, 周二)** · 主题: syncd 主循环 + - 📖 学习: `Syncd::run()` (150min) + - 🐧 内核对照: 读 `net/ipv4/fib_trie.c` 看内核 FIB 路由表实现 (30min) + - 📤 产出: `notes/P2/W19-D2-syncd-loop.md` + +- [ ] **Day 129 (2026-11-04, 周三)** · 主题: dispatch 机制 + - 📖 学习: ASIC_STATE 操作如何 dispatch 到 SAI (150min) + - 📤 产出: `notes/P2/W19-D3-dispatch.md` + +- [ ] **Day 130 (2026-11-05, 周四)** · 主题: vendor SAI 加载 + - 📖 学习: dlopen + sai_api_query (120min) + - 📤 产出: `notes/P2/W19-D4-vendor-load.md` + +- [ ] **Day 131 (2026-11-06, 周五)** · 主题: warm/fast boot + - 📖 学习: redis snapshot + restore (120min) + - 📤 产出: `notes/P2/W19-D5-warm-boot.md` + +- [ ] **Day 132 (2026-11-07, 周六)** · 主题: 大块实验 — 替换 SAI 库实验 + - 🔧 动手: 把 vs 的 libsai 换成自己的 stub,看 syncd 怎么调用 (360min) + - 📤 产出: `code/P2/sai-stub/` + +- [ ] **Day 133 (2026-11-08, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P2/W19-summary.md` + +--- + +### Week 20 (Day 134-140) · sai-dpdk-mini 设计 + +- [ ] **Day 134 (2026-11-09, 周一)** · 主题: 项目目标 RFC + - 🔧 动手: 写 RFC 文档 (180min) + - 📤 产出: `docs/sai-dpdk-mini-rfc.md` + +- [ ] **Day 135 (2026-11-10, 周二)** · 主题: 架构设计 + - 🔧 动手: 画架构图:SAI API → DPDK 资源映射 (180min) + - 🐧 内核对照: 读 `net/core/neighbour.c` 看 ARP/ND 表实现 (30min) + - 📤 产出: `docs/sai-dpdk-mini-arch.md` + +- [ ] **Day 136 (2026-11-11, 周三)** · 主题: 内部数据结构 + - 🔧 动手: 设计 port/fdb/route/acl 内部 struct (150min) + - 📤 产出: `docs/sai-dpdk-mini-ds.md` + +- [ ] **Day 137 (2026-11-12, 周四)** · 主题: 线程模型 + - 🔧 动手: 决定 SAI 调用线程 vs DPDK lcore 关系 (120min) + - 📤 产出: `docs/sai-dpdk-mini-thread.md` + +- [ ] **Day 138 (2026-11-13, 周五)** · 主题: 仓库脚手架 + - 🔧 动手: 创建仓库、Makefile、CMakeLists (120min) + - 📤 产出: `code/sai-dpdk-mini/` 初始化 + +- [ ] **Day 139 (2026-11-14, 周六)** · 主题: 大块实验 — 编译框架 + - 🔧 动手: 让 sai_api_query 返回所有空 stub 编译通过 (360min) + - 📤 产出: commit "skeleton compiles" + +- [ ] **Day 140 (2026-11-15, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P2/W20-summary.md` + +--- + +### Week 21 (Day 141-147) · sai_switch + sai_port + +- [ ] **Day 141 (2026-11-16, 周一)** · 主题: sai_create_switch + - 🔧 动手: 实现 EAL 初始化 + 探测端口 (180min) + - 📤 产出: commit "sai_create_switch" + +- [ ] **Day 142 (2026-11-17, 周二)** · 主题: sai_remove_switch + - 🔧 动手: 资源回收 (120min) + - 🐧 内核对照: 读 `net/core/dev.c::register_netdev` / `unregister_netdev` (30min) + - 📤 产出: commit "sai_remove_switch" + +- [ ] **Day 143 (2026-11-18, 周三)** · 主题: sai_create_port + - 🔧 动手: 绑定 DPDK port_id + 启动 (180min) + - 📤 产出: commit "sai_create_port" + +- [ ] **Day 144 (2026-11-19, 周四)** · 主题: port attribute + - 🔧 动手: 实现 admin_state / speed / mtu 等属性 (150min) + - 📤 产出: commit "port attrs" + +- [ ] **Day 145 (2026-11-20, 周五)** · 主题: port 统计 + - 🔧 动手: 实现 get_port_stats 桥接 rte_eth_stats (150min) + - 📤 产出: commit "port stats" + +- [ ] **Day 146 (2026-11-21, 周六)** · 主题: 大块实验 — syncd 加载 sai-dpdk-mini + - 🔧 动手: 把 .so 让 syncd 加载,看到端口出现在 ASIC_DB (360min) + - 📤 产出: `notes/P2/W21-D6-syncd-load.md` + +- [ ] **Day 147 (2026-11-22, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P2/W21-summary.md` + +--- + +### Week 22 (Day 148-154) · sai_fdb + sai_vlan (L2) + +- [ ] **Day 148 (2026-11-23, 周一)** · 主题: sai_create_vlan + - 🔧 动手: 用 bitmap 实现 VLAN 表 (150min) + - 📤 产出: commit "sai_vlan" + +- [ ] **Day 149 (2026-11-24, 周二)** · 主题: VLAN member + - 🔧 动手: 端口 ↔ VLAN 映射 (150min) + - 🐧 内核对照: 读 `net/8021q/vlan_core.c` 看内核 VLAN 处理 (30min) + - 📤 产出: commit "vlan member" + +- [ ] **Day 150 (2026-11-25, 周三)** · 主题: sai_create_fdb_entry + - 🔧 动手: rte_hash MAC 表 (180min) + - 📤 产出: commit "fdb create" + +- [ ] **Day 151 (2026-11-26, 周四)** · 主题: FDB age + flush + - 🔧 动手: 定时器 + 老化机制 (150min) + - 📤 产出: commit "fdb age" + +- [ ] **Day 152 (2026-11-27, 周五)** · 主题: L2 转发路径 + - 🔧 动手: 数据面查 MAC 表转发 (180min) + - 📤 产出: commit "l2 forward" + +- [ ] **Day 153 (2026-11-28, 周六)** · 主题: 大块实验 — L2 跑通 + - 🔧 动手: SONiC 配两个 VLAN,互通验证 (360min) + - 📤 产出: `perf-data/P2/W22-l2-test.md` + +- [ ] **Day 154 (2026-11-29, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P2/W22-summary.md` + +--- + +### Week 23 (Day 155-161) · sai_route + sai_neighbor (L3) + +- [ ] **Day 155 (2026-11-30, 周一)** · 主题: sai_create_router_interface + - 🔧 动手: 实现 RIF 概念 (150min) + - 📤 产出: commit "rif" + +- [ ] **Day 156 (2026-12-01, 周二)** · 主题: sai_create_neighbor + - 🔧 动手: ARP/ND 表 (180min) + - 🐧 内核对照: 读 `net/ipv4/arp.c` 看 ARP 状态机(NUD_* states)(30min) + - 📤 产出: commit "neighbor" + +- [ ] **Day 157 (2026-12-02, 周三)** · 主题: sai_create_next_hop + - 🔧 动手: nexthop 表 (150min) + - 📤 产出: commit "nexthop" + +- [ ] **Day 158 (2026-12-03, 周四)** · 主题: sai_create_route_entry + - 🔧 动手: rte_lpm 路由表 (180min) + - 📤 产出: commit "route lpm" + +- [ ] **Day 159 (2026-12-04, 周五)** · 主题: L3 转发路径 + - 🔧 动手: LPM 查表 → 改 dst MAC → 转发 (180min) + - 📤 产出: commit "l3 forward" + +- [ ] **Day 160 (2026-12-05, 周六)** · 主题: 大块实验 — L3 跑通 + - 🔧 动手: SONiC 配 IP + 静态路由 + ping (360min) + - 📤 产出: `perf-data/P2/W23-l3-test.md` + +- [ ] **Day 161 (2026-12-06, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P2/W23-summary.md` + +--- + +### Week 24 (Day 162-168) · sai_acl + rte_flow + +- [ ] **Day 162 (2026-12-07, 周一)** · 主题: SAI ACL 模型 + - 📖 学习: ACL table / group / entry / counter (120min) + - 📤 产出: `notes/P2/W24-D1-acl-model.md` + +- [ ] **Day 163 (2026-12-08, 周二)** · 主题: rte_flow 映射设计 + - 🔧 动手: 设计 SAI ACL → rte_flow 转换规则 (150min) + - 🐧 内核对照: 读 `net/sched/cls_flower.c` 看内核 flow classifier (30min) + - 📤 产出: `docs/acl-mapping.md`(含 rte_flow vs tc-flower 对照) + +- [ ] **Day 164 (2026-12-09, 周三)** · 主题: sai_create_acl_table + - 🔧 动手: 实现 ACL table 容器 (150min) + - 📤 产出: commit "acl table" + +- [ ] **Day 165 (2026-12-10, 周四)** · 主题: sai_create_acl_entry + - 🔧 动手: 转 rte_flow rule 下发网卡 (180min) + - 📤 产出: commit "acl entry" + +- [ ] **Day 166 (2026-12-11, 周五)** · 主题: ACL counter + - 🔧 动手: rte_flow counter action (120min) + - 📤 产出: commit "acl counter" + +- [ ] **Day 167 (2026-12-12, 周六)** · 主题: 大块实验 — ACL 跑通 + - 🔧 动手: SONiC 配置 ACL drop 某 IP,验证 (360min) + - 📤 产出: `perf-data/P2/W24-acl-test.md` + +- [ ] **Day 168 (2026-12-13, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P2/W24-summary.md` + +--- + +### Week 25 (Day 169-175) · sai pkt-io (host_if) + +- [ ] **Day 169 (2026-12-14, 周一)** · 主题: SAI host_if 模型 + - 📖 学习: trap group / trap / host_if (120min) + - 📤 产出: `notes/P2/W25-D1-hostif-model.md` + +- [ ] **Day 170 (2026-12-15, 周二)** · 主题: TAP 设备桥接 + - 🔧 动手: 创建 TAP,绑定到 SAI port (150min) + - 🐧 内核对照: 读 `drivers/net/tun.c` 看 tun/tap 完整实现 (45min) + - 📤 产出: commit "tap binding" + +- [ ] **Day 171 (2026-12-16, 周三)** · 主题: CPU 收包路径 + - 🔧 动手: 命中 trap 的包送 TAP (180min) + - 📤 产出: commit "cpu rx" + +- [ ] **Day 172 (2026-12-17, 周四)** · 主题: CPU 发包路径 + - 🔧 动手: TAP 写入的包 → DPDK tx (150min) + - 📤 产出: commit "cpu tx" + +- [ ] **Day 173 (2026-12-18, 周五)** · 主题: BGP/LACP 跑通验证 + - 🔧 动手: 起 FRR,看能不能建邻居 (180min) + - 📤 产出: `perf-data/P2/W25-bgp-test.md` + +- [ ] **Day 174 (2026-12-19, 周六)** · 主题: 大块实验 — 端到端打通 + - 🔧 动手: SONiC + sai-dpdk-mini 跑完整 L2/L3/ACL/BGP (360min) + - 📤 产出: 演示视频/截图 + +- [ ] **Day 175 (2026-12-20, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P2/W25-summary.md` + +--- + +### Week 26 (Day 176-182) · 集成 + 答辩 + +- [ ] **Day 176 (2026-12-21, 周一)** · 主题: 集成测试 + - 🔧 动手: 跑 SONiC PTF / VS test (180min) + - 📤 产出: `perf-data/P2/W26-vs-test.md` + +- [ ] **Day 177 (2026-12-22, 周二)** · 主题: README + 架构图 + - 🔧 动手: 完善 sai-dpdk-mini 文档 (180min) + - 🐧 内核对照: 在 README 加一节 "SAI vs Linux kernel netdev API 对照" (30min) + - 📤 产出: `code/sai-dpdk-mini/README.md` + +- [ ] **Day 178 (2026-12-23, 周三)** · 主题: 技术博客 (1) + - 🔧 动手: "SONiC 控制面 → SAI → DPDK 全链路" (180min) + - 📤 产出: `blog/P2-sonic-sai-dpdk.md` + +- [ ] **Day 179 (2026-12-24, 周四)** · 主题: 技术博客 (2) + - 🔧 动手: "用 DPDK 实现 SAI 最小适配器" (180min) + - 📤 产出: `blog/P2-sai-dpdk-mini.md` + +- [ ] **Day 180 (2026-12-25, 周五)** · 主题: 简历更新 v2 + - 🔧 动手: 加入 P2 项目 (90min) + - 📤 产出: `career/resume-v2.md` + +- [ ] **Day 181 (2026-12-26, 周六)** · 主题: 大块实验 — 开源发布 + - 🔧 动手: GitHub release v0.1,发知乎/掘金 (240min) + - 📤 产出: GitHub release + 推广帖 + +- [ ] **Day 182 (2026-12-27, 周日)** · 🎯 P2 阶段答辩 + - 📤 产出: `P2-final/answer.md` + `P2-final/retro.md` + `monthly/2026-12.md` + +--- + +# P3 · vSwitch / 云网关数据面 (M7-M9) + +> **Day 183-273**, 2026-12-28 ~ 2027-03-28 +> **核心目标**: 吃透 OVS-DPDK 数据面、入门 VPP 并写出 plugin、自研一个用户态 LB 原型 + +## P3.0 阶段目标 + +1. OVS-DPDK 源码精读 + 调优实战 +2. VPP 入门 + 写一个自定义 node 插件 +3. 自研 `udp-lb` / `tcp-lb`:4-tuple hash + RSS + 健康检查 +4. 跑通一个云网关原型(NAT/SNAT/DNAT) + +## P3.1 关键交付物 + +- [ ] `notes/P3/` 13 周笔记 +- [ ] `code/vpp-plugin-demo/` VPP 自定义 node +- [ ] `code/dpdk-lb/` 自研用户态 LB +- [ ] `perf-data/P3/` OVS-DPDK 调优数据 + +## P3.1.5 🐧 本阶段内核对照清单(K5 + K6) + +| 周 | 对照主题 | 内核源码路径 | +|---|---|---| +| W27 | 内核 OVS datapath | `net/openvswitch/datapath.c`, `flow_table.c` | +| W28 | 内核 OVS flow_table | `net/openvswitch/flow_table.c` | +| W29 | 内核 conntrack | `net/netfilter/nf_conntrack_core.c` | +| W30 | qdisc + 速率限制 | `net/sched/sch_*.c` | +| W31 | bridge fast path | `net/bridge/br_input.c` | +| W32 | netdev_rx_handler | `net/core/dev.c::netif_receive_skb_core` | +| W33 | XDP 程序加载 | `net/core/dev.c::dev_xdp_install` | +| W34 | XDP redirect / cpumap | `kernel/bpf/cpumap.c`, `devmap.c` | +| W35 | IPVS (LVS) 内核 LB 实现 | `net/netfilter/ipvs/ip_vs_core.c` | +| W36 | maglev / consistent hash | `net/netfilter/ipvs/ip_vs_sh.c` | +| W37 | tc flower offload | `net/sched/cls_flower.c` | +| W38 | NAT (nftables) | `net/netfilter/nf_nat_core.c` | +| W39 | 总结:OVS-kernel vs OVS-DPDK 对照 | — | + +## P3.2 周计划总览 (W27-W39) + +| 周 | Day | 主题 | 里程碑 | +|---|---|---|---| +| W27 | 183-189 | OVS 架构总览 | ovs-vswitchd 起来 | +| W28 | 190-196 | OVS-DPDK datapath | dpcls/dpif | +| W29 | 197-203 | OVS megaflow | EMC + megaflow 抓数据 | +| W30 | 204-210 | OVS-DPDK 调优 | PMD threads tune | +| W31 | 211-217 | VPP 架构 | hello node | +| W32 | 218-224 | VPP graph model | 自定义 node | +| W33 | 225-231 | VPP plugin 框架 | 插件骨架 | +| W34 | 232-238 | VPP plugin 实现 | 编译/加载 | +| W35 | 239-245 | LB 设计 | RFC + 架构 | +| W36 | 246-252 | LB 数据面 | 4-tuple hash | +| W37 | 253-259 | LB 健康检查 | active probe | +| W38 | 260-266 | NAT/SNAT/DNAT | conntrack | +| W39 | 267-273 | P3 总结 | 答辩 | + +## P3.3 详细日计划 + +### Week 27 (Day 183-189) · OVS 架构总览 + +- [ ] **Day 183 (2026-12-28, 周一)** · 主题: OVS 总体架构 + - 📖 学习: ovs-architecture.7 文档 (120min) + - 📤 产出: `notes/P3/W27-D1-ovs-arch.md` + +- [ ] **Day 184 (2026-12-29, 周二)** · 主题: OVS 编译 + DPDK 支持 + - 🔧 动手: 编译 ovs-3.x with-dpdk (120min) + - 🐧 内核对照: 读 `net/openvswitch/datapath.c::ovs_dp_process_packet` (30min) + - 📤 产出: `notes/P3/W27-D2-ovs-build.md` + +- [ ] **Day 185 (2026-12-30, 周三)** · 主题: 启动 ovs-vswitchd + - 🔧 动手: 配 dpdk-init + 创建 br0 (120min) + - 📤 产出: `notes/P3/W27-D3-ovs-up.md` + +- [ ] **Day 186 (2026-12-31, 周四)** · 主题: 添加 dpdk port + - 🔧 动手: add-port 绑定 dpdk0/dpdk1 (120min) + - 📤 产出: `notes/P3/W27-D4-dpdk-port.md` + +- [ ] **Day 187 (2027-01-01, 周五)** · ☕ 元旦假期 — 留白 + +- [ ] **Day 188 (2027-01-02, 周六)** · ☕ 元旦假期 — 留白 + +- [ ] **Day 189 (2027-01-03, 周日)** · ☕ 元旦假期 — 留白 + +--- + +### Week 28 (Day 190-196) · OVS-DPDK datapath + +- [ ] **Day 190 (2027-01-04, 周一)** · 主题: dpif-netdev 总览 + - 📖 学习: `lib/dpif-netdev.c` (120min) + - 📤 产出: `notes/P3/W28-D1-dpif.md` + +- [ ] **Day 191 (2027-01-05, 周二)** · 主题: PMD threads + - 📖 学习: pmd_thread_main 主循环 (150min) + - 🐧 内核对照: 读 `net/openvswitch/flow_table.c` 看内核 OVS megaflow (30min) + - 📤 产出: `notes/P3/W28-D2-pmd.md` + +- [ ] **Day 192 (2027-01-06, 周三)** · 主题: EMC (exact match cache) + - 📖 学习: emc_lookup / emc_insert (120min) + - 📤 产出: `notes/P3/W28-D3-emc.md` + +- [ ] **Day 193 (2027-01-07, 周四)** · 主题: dpcls (megaflow cache) + - 📖 学习: dpcls_lookup (150min) + - 📤 产出: `notes/P3/W28-D4-dpcls.md` + +- [ ] **Day 194 (2027-01-08, 周五)** · 主题: upcall slow path + - 📖 学习: upcall_xlate (90min) + - 📤 产出: `notes/P3/W28-D5-upcall.md` + +- [ ] **Day 195 (2027-01-09, 周六)** · 主题: 大块实验 — 抓 OVS 数据面 + - 🔧 动手: 用 perf 抓 PMD 热点 (360min) + - 📤 产出: `flamegraph/W28-ovs.svg` + +- [ ] **Day 196 (2027-01-10, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P3/W28-summary.md` + +--- + +### Week 29 (Day 197-203) · OVS megaflow + +- [ ] **Day 197 (2027-01-11, 周一)** · 主题: megaflow 概念 + - 📖 学习: 通配符 + bitmask (120min) + - 📤 产出: `notes/P3/W29-D1-megaflow.md` + +- [ ] **Day 198 (2027-01-12, 周二)** · 主题: 抓 megaflow 表 + - 🔧 动手: `ovs-appctl dpctl/dump-flows` (120min) + - 🐧 内核对照: 读 `net/netfilter/nf_conntrack_core.c::nf_conntrack_in` (30min) + - 📤 产出: `notes/P3/W29-D2-dump-flows.md` + +- [ ] **Day 199 (2027-01-13, 周三)** · 主题: revalidator + - 📖 学习: revalidator 线程 (120min) + - 📤 产出: `notes/P3/W29-D3-revalidator.md` + +- [ ] **Day 200 (2027-01-14, 周四)** · 主题: SMC (signature match cache) + - 📖 学习: SMC 优化 (90min) + - 📤 产出: `notes/P3/W29-D4-smc.md` + +- [ ] **Day 201 (2027-01-15, 周五)** · 主题: connection tracking + - 📖 学习: conntrack 模块 (120min) + - 📤 产出: `notes/P3/W29-D5-ct.md` + +- [ ] **Day 202 (2027-01-16, 周六)** · 主题: 大块实验 — 流表压力测试 + - 🔧 动手: 用 dperf 打 100 万流,观察 EMC/SMC/dpcls 命中率 (360min) + - 📤 产出: `perf-data/P3/W29-flow-stress.md` + +- [ ] **Day 203 (2027-01-17, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P3/W29-summary.md` + +--- + +### Week 30 (Day 204-210) · OVS-DPDK 调优 + +- [ ] **Day 204 (2027-01-18, 周一)** · 主题: PMD core 绑定 + - 🔧 动手: pmd-cpu-mask 调试 (120min) + - 📤 产出: `notes/P3/W30-D1-pmd-mask.md` + +- [ ] **Day 205 (2027-01-19, 周二)** · 主题: RXQ assignment + - 🔧 动手: pmd-rxq-affinity (120min) + - 🐧 内核对照: 读 `net/sched/sch_htb.c` 看内核 qdisc 限速 (30min) + - 📤 产出: `notes/P3/W30-D2-rxq.md` + +- [ ] **Day 206 (2027-01-20, 周三)** · 主题: mbuf 池调整 + - 🔧 动手: shared mempool 配置 (120min) + - 📤 产出: `notes/P3/W30-D3-mbuf.md` + +- [ ] **Day 207 (2027-01-21, 周四)** · 主题: TX flush 周期 + - 🔧 动手: tx-flush-interval 调优 (120min) + - 📤 产出: `notes/P3/W30-D4-tx-flush.md` + +- [ ] **Day 208 (2027-01-22, 周五)** · 主题: hardware offload + - 🔧 动手: hw-offload=true 试跑 (120min) + - 📤 产出: `notes/P3/W30-D5-hw-offload.md` + +- [ ] **Day 209 (2027-01-23, 周六)** · 主题: 大块实验 — 调优冲线速 + - 🔧 动手: 把 OVS-DPDK 调到接近线速 (480min) + - 📤 产出: `perf-data/P3/W30-ovs-perf.md` + +- [ ] **Day 210 (2027-01-24, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P3/W30-summary.md` + +--- + +### Week 31 (Day 211-217) · VPP 架构 + +- [ ] **Day 211 (2027-01-25, 周一)** · 主题: VPP 总览 + - 📖 学习: VPP fd.io 文档 (120min) + - 📤 产出: `notes/P3/W31-D1-vpp-overview.md` + +- [ ] **Day 212 (2027-01-26, 周二)** · 主题: VPP 编译安装 + - 🔧 动手: 从源码编译 (120min) + - 🐧 内核对照: 读 `net/bridge/br_input.c` 看内核 bridge fast path (30min) + - 📤 产出: `notes/P3/W31-D2-vpp-build.md` + +- [ ] **Day 213 (2027-01-27, 周三)** · 主题: VPP CLI 基础 + - 🔧 动手: vppctl 配置 L2/L3 (120min) + - 📤 产出: `notes/P3/W31-D3-vpp-cli.md` + +- [ ] **Day 214 (2027-01-28, 周四)** · 主题: vector packet processing + - 📖 学习: VPP 的 vector graph 模型 (150min) + - 📤 产出: `notes/P3/W31-D4-graph.md` + +- [ ] **Day 215 (2027-01-29, 周五)** · 主题: VPP 主要 nodes + - 📖 学习: dpdk-input / ip4-lookup / ip4-rewrite (120min) + - 📤 产出: `notes/P3/W31-D5-nodes.md` + +- [ ] **Day 216 (2027-01-30, 周六)** · 主题: 大块实验 — VPP L3 转发 + - 🔧 动手: 配两口 + 静态路由 + 压测 (360min) + - 📤 产出: `perf-data/P3/W31-vpp-l3.md` + +- [ ] **Day 217 (2027-01-31, 周日)** · ☕ 复盘 + 月度 + - 📤 产出: `notes/P3/W31-summary.md` + `monthly/2027-01.md` + +--- + +### Week 32 (Day 218-224) · VPP graph model + +- [ ] **Day 218 (2027-02-01, 周一)** · 主题: node registration + - 📖 学习: VLIB_REGISTER_NODE 宏 (120min) + - 📤 产出: `notes/P3/W32-D1-node-reg.md` + +- [ ] **Day 219 (2027-02-02, 周二)** · 主题: node function + - 📖 学习: 写一个最小 node (150min) + - 🐧 内核对照: 读 `net/core/dev.c::netdev_rx_handler_register` 看内核 rx_handler 注入机制 (30min) + - 📤 产出: `code/P3/vpp-hello-node/` + +- [ ] **Day 220 (2027-02-03, 周三)** · 主题: next nodes + - 📖 学习: vlib_node_runtime_t::next_index (120min) + - 📤 产出: `notes/P3/W32-D3-next.md` + +- [ ] **Day 221 (2027-02-04, 周四)** · 主题: buffer & metadata + - 📖 学习: vlib_buffer_t (120min) + - 📤 产出: `notes/P3/W32-D4-vlib-buf.md` + +- [ ] **Day 222 (2027-02-05, 周五)** · 主题: trace + counter + - 🔧 动手: 给 node 加 trace (120min) + - 📤 产出: `notes/P3/W32-D5-trace.md` + +- [ ] **Day 223 (2027-02-06, 周六)** · ☕ 春节假期 — 留白 + +- [ ] **Day 224 (2027-02-07, 周日)** · ☕ 春节假期 — 留白 + +--- + +### Week 33 (Day 225-231) · VPP plugin 框架 + +- [ ] **Day 225 (2027-02-08, 周一)** · ☕ 春节假期 — 留白 + +- [ ] **Day 226 (2027-02-09, 周二)** · ☕ 春节假期 — 留白 + +- [ ] **Day 227 (2027-02-10, 周三)** · ☕ 春节假期 — 留白 + +- [ ] **Day 228 (2027-02-11, 周四)** · ☕ 春节假期 — 留白 + +- [ ] **Day 229 (2027-02-12, 周五)** · ☕ 春节假期 — 留白 + +- [ ] **Day 230 (2027-02-13, 周六)** · 主题: plugin skeleton + - 🔧 动手: 用 vpp_plugin_template 生成骨架 (240min) + - 📤 产出: `code/P3/vpp-plugin-demo/` + +- [ ] **Day 231 (2027-02-14, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P3/W33-summary.md` + +--- + +### Week 34 (Day 232-238) · VPP plugin 实现 + +- [ ] **Day 232 (2027-02-15, 周一)** · 主题: API 定义 + - 🔧 动手: 写 .api 文件 (120min) + - 📤 产出: commit "api def" + +- [ ] **Day 233 (2027-02-16, 周二)** · 主题: CLI 命令 + - 🔧 动手: 注册自定义 vppctl 命令 (150min) + - 🐧 内核对照: 读 `kernel/bpf/cpumap.c` 看 XDP cpumap 程序加载 (30min) + - 📤 产出: commit "cli" + +- [ ] **Day 234 (2027-02-17, 周三)** · 主题: node 实现 + - 🔧 动手: 写一个 IP 黑名单 drop node (180min) + - 📤 产出: commit "blacklist node" + +- [ ] **Day 235 (2027-02-18, 周四)** · 主题: 接入 graph + - 🔧 动手: 插到 ip4-input 之后 (120min) + - 📤 产出: commit "graph hook" + +- [ ] **Day 236 (2027-02-19, 周五)** · 主题: 单元测试 + - 🔧 动手: 用 VPP test framework (120min) + - 📤 产出: commit "tests" + +- [ ] **Day 237 (2027-02-20, 周六)** · 主题: 大块实验 — 黑名单压测 + - 🔧 动手: 10 万条黑名单 + 压测 (360min) + - 📤 产出: `perf-data/P3/W34-blacklist.md` + +- [ ] **Day 238 (2027-02-21, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P3/W34-summary.md` + +--- + +### Week 35 (Day 239-245) · LB 设计 + +- [ ] **Day 239 (2027-02-22, 周一)** · 主题: LB 需求分析 + - 🔧 动手: 写需求文档(L4/L7、DR/NAT)(120min) + - 📤 产出: `docs/dpdk-lb-req.md` + +- [ ] **Day 240 (2027-02-23, 周二)** · 主题: 主流 LB 调研 + - 📖 学习: LVS / Maglev / Katran (180min) + - 🐧 内核对照: 读 `net/netfilter/ipvs/ip_vs_core.c::ip_vs_in` 看 LVS 数据面 (30min) + - 📤 产出: `notes/P3/W35-D2-lb-survey.md` + +- [ ] **Day 241 (2027-02-24, 周三)** · 主题: hash 算法选型 + - 📖 学习: 一致性 hash / Maglev hash (120min) + - 📤 产出: `notes/P3/W35-D3-hash.md` + +- [ ] **Day 242 (2027-02-25, 周四)** · 主题: 架构设计 + - 🔧 动手: 画架构图 (150min) + - 📤 产出: `docs/dpdk-lb-arch.md` + +- [ ] **Day 243 (2027-02-26, 周五)** · 主题: 仓库脚手架 + - 🔧 动手: 初始化 (120min) + - 📤 产出: `code/dpdk-lb/` + +- [ ] **Day 244 (2027-02-27, 周六)** · 主题: 大块实验 — 控制面 mock + - 🔧 动手: 写一个 mock config 接口 (360min) + - 📤 产出: commit "config api" + +- [ ] **Day 245 (2027-02-28, 周日)** · ☕ 复盘 + 月度 + - 📤 产出: `notes/P3/W35-summary.md` + `monthly/2027-02.md` + +--- + +### Week 36 (Day 246-252) · LB 数据面 + +- [ ] **Day 246 (2027-03-01, 周一)** · 主题: RX + parse + - 🔧 动手: 实现 RX 路径 (180min) + - 📤 产出: commit "rx parse" + +- [ ] **Day 247 (2027-03-02, 周二)** · 主题: 4-tuple hash + - 🔧 动手: rte_hash + Maglev hash (180min) + - 🐧 内核对照: 读 `net/netfilter/ipvs/ip_vs_sh.c` 看内核源散列调度 (30min) + - 📤 产出: commit "hash" + +- [ ] **Day 248 (2027-03-03, 周三)** · 主题: backend 选择 + - 🔧 动手: real server 列表 + 状态 (150min) + - 📤 产出: commit "backend pick" + +- [ ] **Day 249 (2027-03-04, 周四)** · 主题: NAT 改写 + - 🔧 动手: 改 dst IP/MAC (150min) + - 📤 产出: commit "nat rewrite" + +- [ ] **Day 250 (2027-03-05, 周五)** · 主题: session table + - 🔧 动手: 连接表 + 超时 (180min) + - 📤 产出: commit "session table" + +- [ ] **Day 251 (2027-03-06, 周六)** · 主题: 大块实验 — LB 跑通 + - 🔧 动手: 配 3 个 backend,curl 验证 (360min) + - 📤 产出: `perf-data/P3/W36-lb-test.md` + +- [ ] **Day 252 (2027-03-07, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P3/W36-summary.md` + +--- + +### Week 37 (Day 253-259) · LB 健康检查 + +- [ ] **Day 253 (2027-03-08, 周一)** · 主题: 健康检查设计 + - 🔧 动手: TCP/HTTP probe (150min) + - 📤 产出: commit "hc design" + +- [ ] **Day 254 (2027-03-09, 周二)** · 主题: probe 线程 + - 🔧 动手: 独立 service core 跑 probe (180min) + - 🐧 内核对照: 读 `net/sched/cls_flower.c` 看 tc flower + hw offload 路径 (30min) + - 📤 产出: commit "probe thread" + +- [ ] **Day 255 (2027-03-10, 周三)** · 主题: 故障切换 + - 🔧 动手: 摘除 unhealthy backend (150min) + - 📤 产出: commit "failover" + +- [ ] **Day 256 (2027-03-11, 周四)** · 主题: 统计 + Prometheus + - 🔧 动手: 暴露 metrics (150min) + - 📤 产出: commit "metrics" + +- [ ] **Day 257 (2027-03-12, 周五)** · 主题: 压力测试 + - 🔧 动手: dperf 打 1Mpps (180min) + - 📤 产出: `perf-data/P3/W37-lb-stress.md` + +- [ ] **Day 258 (2027-03-13, 周六)** · 主题: 大块实验 — LB 调优 + - 🔧 动手: 调到 5Mpps (480min) + - 📤 产出: `perf-data/P3/W37-lb-tune.md` + +- [ ] **Day 259 (2027-03-14, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P3/W37-summary.md` + +--- + +### Week 38 (Day 260-266) · NAT/SNAT/DNAT + +- [ ] **Day 260 (2027-03-15, 周一)** · 主题: NAT 概念 + - 📖 学习: SNAT/DNAT/Hairpin (120min) + - 📤 产出: `notes/P3/W38-D1-nat.md` + +- [ ] **Day 261 (2027-03-16, 周二)** · 主题: conntrack 设计 + - 🔧 动手: 设计连接表 (150min) + - 🐧 内核对照: 读 `net/netfilter/nf_nat_core.c::nf_nat_setup_info` 看内核 NAT 实现 (30min) + - 📤 产出: `docs/conntrack.md` + +- [ ] **Day 262 (2027-03-17, 周三)** · 主题: SNAT 实现 + - 🔧 动手: 出方向源地址池 (180min) + - 📤 产出: commit "snat" + +- [ ] **Day 263 (2027-03-18, 周四)** · 主题: DNAT 实现 + - 🔧 动手: 入方向目的地址改写 (180min) + - 📤 产出: commit "dnat" + +- [ ] **Day 264 (2027-03-19, 周五)** · 主题: ALG (FTP) 略 + - 📖 学习: ALG 思路 (90min) + - 📤 产出: `notes/P3/W38-D5-alg.md` + +- [ ] **Day 265 (2027-03-20, 周六)** · 主题: 大块实验 — NAT 跑通 + - 🔧 动手: 配置 SNAT/DNAT 验证 (360min) + - 📤 产出: `perf-data/P3/W38-nat-test.md` + +- [ ] **Day 266 (2027-03-21, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P3/W38-summary.md` + +--- + +### Week 39 (Day 267-273) · P3 总结 + +- [ ] **Day 267 (2027-03-22, 周一)** · 主题: 整理产出 + - 🔧 动手: 组织 P3 文档 (180min) + - 📤 产出: `P3-final/` + +- [ ] **Day 268 (2027-03-23, 周二)** · 主题: 技术博客 (1) + - 🔧 动手: "OVS-DPDK 调优实战" (180min) + - 🐧 内核对照: 博客加一节 "OVS-kernel vs OVS-DPDK datapath 对照" (30min) + - 📤 产出: `blog/P3-ovs-tuning.md` + +- [ ] **Day 269 (2027-03-24, 周三)** · 主题: 技术博客 (2) + - 🔧 动手: "用 DPDK 写一个 5Mpps LB" (180min) + - 📤 产出: `blog/P3-dpdk-lb.md` + +- [ ] **Day 270 (2027-03-25, 周四)** · 主题: 技术博客 (3) + - 🔧 动手: "VPP graph model 与自定义 plugin" (180min) + - 📤 产出: `blog/P3-vpp-plugin.md` + +- [ ] **Day 271 (2027-03-26, 周五)** · 主题: 简历更新 v3 + - 🔧 动手: 加 P3 (90min) + - 📤 产出: `career/resume-v3.md` + +- [ ] **Day 272 (2027-03-27, 周六)** · 主题: 大块实验 — 开源 dpdk-lb + - 🔧 动手: 发 GitHub + 写中英 README (240min) + - 📤 产出: GitHub release + +- [ ] **Day 273 (2027-03-28, 周日)** · 🎯 P3 阶段答辩 + - 📤 产出: `P3-final/answer.md` + `P3-final/retro.md` + `monthly/2027-03.md` + +--- + +# P4 · 网络安全数据面 (M10-M12) + +> **Day 274-364**, 2027-03-29 ~ 2027-06-27 +> **核心目标**: Suricata-DPDK 跑通;自研一个 DDoS 清洗原型(抗 5Mpps SYN flood);理解 IDS/IPS 数据面 + +## P4.0 阶段目标 + +1. Suricata DPDK 模式跑通 + 调优 +2. 自研 DDoS 清洗:SYN cookie / 速率限制 / 黑白名单 +3. 理解 TLS fingerprint (JA3/JA4) +4. 输出 3 篇安全数据面博客 + +## P4.1 关键交付物 + +- [ ] `notes/P4/` 13 周笔记 +- [ ] `code/ddos-clean/` 自研 DDoS 清洗 +- [ ] `perf-data/P4/` 5Mpps SYN flood 抗压数据 + +## P4.1.5 🐧 本阶段内核对照清单(K5 深入 + syn cookie) + +| 周 | 对照主题 | 内核源码路径 | +|---|---|---| +| W40 | 内核 IDS/IPS hook 点 | `net/netfilter/core.c`, `nf_hook_slow` | +| W41 | AF_PACKET / PACKET_MMAP | `net/packet/af_packet.c` | +| W42 | XDP DDoS drop | `samples/bpf/xdp_ddos*.c` | +| W43 | nftables 规则匹配 | `net/netfilter/nf_tables_core.c` | +| W44 | TLS kernel offload (kTLS) | `net/tls/` | +| W45 | 内核限速 (tbf/police) | `net/sched/sch_tbf.c` | +| W46 | 内核 SYN cookie | `net/ipv4/syncookies.c` | +| W47 | 内核 UDP 流量控制 | `net/ipv4/udp.c` | +| W48 | conntrack helper (DNS/FTP) | `net/netfilter/nf_conntrack_helper.c` | +| W49 | ipset (大规模 IP 集合) | `net/netfilter/ipset/` | +| W50 | BPF-based rate limit | `kernel/bpf/`, `samples/bpf/` | +| W51 | 内核 perf + tracepoints | `include/trace/events/net.h` | +| W52 | 总结:内核 vs 用户态清洗对比 | — | + +## P4.2 周计划总览 (W40-W52) + +| 周 | Day | 主题 | 里程碑 | +|---|---|---|---| +| W40 | 274-280 | DDoS 攻击类型 | 攻击谱系 | +| W41 | 281-287 | Suricata 编译 | dpdk runmode | +| W42 | 288-294 | Suricata 数据面 | 抓性能 | +| W43 | 295-301 | Suricata 规则 | Emerging Threats | +| W44 | 302-308 | TLS fingerprint | JA3/JA4 | +| W45 | 309-315 | DDoS 清洗设计 | RFC | +| W46 | 316-322 | SYN flood 防护 | SYN cookie | +| W47 | 323-329 | UDP flood 防护 | rate limit | +| W48 | 330-336 | reflection attack | DNS amp | +| W49 | 337-343 | 黑白名单 + ASN | geo block | +| W50 | 344-350 | 抗压测试 | 5Mpps | +| W51 | 351-357 | 整体调优 | 火焰图 | +| W52 | 358-364 | P4 总结 | 答辩 | + +## P4.3 详细日计划 + +### Week 40 (Day 274-280) · DDoS 攻击类型 + +- [ ] **Day 274 (2027-03-29, 周一)** · 主题: 攻击分类 + - 📖 学习: volumetric / protocol / application (120min) + - 📤 产出: `notes/P4/W40-D1-attack-types.md` + +- [ ] **Day 275 (2027-03-30, 周二)** · 主题: SYN flood 深入 + - 📖 学习: 半连接队列 + cookie (90min) + - 🐧 内核对照: 读 `net/ipv4/syncookies.c` 完整 SYN cookie 实现 (45min) + - 📤 产出: `notes/P4/W40-D2-syn-flood.md` + +- [ ] **Day 276 (2027-03-31, 周三)** · 主题: UDP/ICMP flood + - 📖 学习: 反射放大 (90min) + - 📤 产出: `notes/P4/W40-D3-udp-flood.md` + +- [ ] **Day 277 (2027-04-01, 周四)** · 主题: HTTP/CC 攻击 + - 📖 学习: 慢攻击 + 应用层 (90min) + - 📤 产出: `notes/P4/W40-D4-cc.md` + +- [ ] **Day 278 (2027-04-02, 周五)** · 主题: 业界清洗设备 + - 📖 学习: Arbor / NSFOCUS / 阿里 SCDN (120min) + - 📤 产出: `notes/P4/W40-D5-vendors.md` + +- [ ] **Day 279 (2027-04-03, 周六)** · ☕ 清明假期 — 留白 + +- [ ] **Day 280 (2027-04-04, 周日)** · ☕ 清明假期 — 留白 + +--- + +### Week 41 (Day 281-287) · Suricata 编译 + +- [ ] **Day 281 (2027-04-05, 周一)** · ☕ 清明假期 — 留白 + +- [ ] **Day 282 (2027-04-06, 周二)** · 主题: Suricata 编译 + - 🔧 动手: with-dpdk 编译 (120min) + - 🐧 内核对照: 读 `net/packet/af_packet.c` 看 AF_PACKET / PACKET_MMAP (30min) + - 📤 产出: `notes/P4/W41-D2-build.md` + +- [ ] **Day 283 (2027-04-07, 周三)** · 主题: dpdk runmode 配置 + - 🔧 动手: suricata.yaml dpdk 配置 (120min) + - 📤 产出: `notes/P4/W41-D3-cfg.md` + +- [ ] **Day 284 (2027-04-08, 周四)** · 主题: IDS 模式起跑 + - 🔧 动手: 用 ET rule 跑 IDS (120min) + - 📤 产出: `notes/P4/W41-D4-ids.md` + +- [ ] **Day 285 (2027-04-09, 周五)** · 主题: IPS 模式 + - 🔧 动手: inline + drop 测试 (120min) + - 📤 产出: `notes/P4/W41-D5-ips.md` + +- [ ] **Day 286 (2027-04-10, 周六)** · 主题: 大块实验 — 性能 baseline + - 🔧 动手: 压测 Suricata DPDK 模式吞吐 (360min) + - 📤 产出: `perf-data/P4/W41-suricata-perf.md` + +- [ ] **Day 287 (2027-04-11, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P4/W41-summary.md` + +--- + +### Week 42 (Day 288-294) · Suricata 数据面 + +- [ ] **Day 288 (2027-04-12, 周一)** · 主题: Suricata 流水线 + - 📖 学习: capture → decode → flow → detect (120min) + - 📤 产出: `notes/P4/W42-D1-pipeline.md` + +- [ ] **Day 289 (2027-04-13, 周二)** · 主题: flow tracker + - 📖 学习: flow.c (120min) + - 🐧 内核对照: 读 `samples/bpf/xdp_ddos*.c` 看 XDP DDoS drop 范例 (30min) + - 📤 产出: `notes/P4/W42-D2-flow.md` + +- [ ] **Day 290 (2027-04-14, 周三)** · 主题: 规则匹配引擎 + - 📖 学习: detect.c (150min) + - 📤 产出: `notes/P4/W42-D3-detect.md` + +- [ ] **Day 291 (2027-04-15, 周四)** · 主题: Hyperscan 加速 + - 📖 学习: MPM with hyperscan (120min) + - 📤 产出: `notes/P4/W42-D4-hs.md` + +- [ ] **Day 292 (2027-04-16, 周五)** · 主题: workers 模型 + - 📖 学习: workers vs autofp (90min) + - 📤 产出: `notes/P4/W42-D5-workers.md` + +- [ ] **Day 293 (2027-04-17, 周六)** · 主题: 大块实验 — Suricata 调优 + - 🔧 动手: 调到 10Gbps wire speed (480min) + - 📤 产出: `perf-data/P4/W42-tune.md` + +- [ ] **Day 294 (2027-04-18, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P4/W42-summary.md` + +--- + +### Week 43 (Day 295-301) · Suricata 规则 + +- [ ] **Day 295 (2027-04-19, 周一)** · 主题: 规则语法 + - 📖 学习: rule format (90min) + - 📤 产出: `notes/P4/W43-D1-rule-syntax.md` + +- [ ] **Day 296 (2027-04-20, 周二)** · 主题: ET Open 规则 + - 🔧 动手: 加载 ET Open + 验证 (120min) + - 🐧 内核对照: 读 `net/netfilter/nf_tables_core.c` 看 nft 规则匹配主循环 (30min) + - 📤 产出: `notes/P4/W43-D2-et.md` + +- [ ] **Day 297 (2027-04-21, 周三)** · 主题: 自定义规则 + - 🔧 动手: 写一条 SSH 暴力破解检测 (120min) + - 📤 产出: `code/P4/custom-rules/` + +- [ ] **Day 298 (2027-04-22, 周四)** · 主题: lua scripting + - 📖 学习: Suricata lua 接口 (120min) + - 📤 产出: `notes/P4/W43-D4-lua.md` + +- [ ] **Day 299 (2027-04-23, 周五)** · 主题: EVE JSON output + - 🔧 动手: 接 ELK 看 alert (90min) + - 📤 产出: `notes/P4/W43-D5-eve.md` + +- [ ] **Day 300 (2027-04-24, 周六)** · 主题: 大块实验 — 攻击 replay + - 🔧 动手: replay 真实攻击 pcap,看是否报警 (360min) + - 📤 产出: `perf-data/P4/W43-replay.md` + +- [ ] **Day 301 (2027-04-25, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P4/W43-summary.md` + +--- + +### Week 44 (Day 302-308) · TLS fingerprint + +- [ ] **Day 302 (2027-04-26, 周一)** · 主题: TLS handshake 复习 + - 📖 学习: ClientHello 字段 (90min) + - 📤 产出: `notes/P4/W44-D1-tls.md` + +- [ ] **Day 303 (2027-04-27, 周二)** · 主题: JA3 + - 📖 学习: JA3 hash 算法 (90min) + - 🐧 内核对照: 读 `net/tls/tls_sw.c` 看内核 kTLS 卸载 (30min) + - 📤 产出: `notes/P4/W44-D2-ja3.md` + +- [ ] **Day 304 (2027-04-28, 周三)** · 主题: JA4 + - 📖 学习: JA4 升级版 (90min) + - 📤 产出: `notes/P4/W44-D3-ja4.md` + +- [ ] **Day 305 (2027-04-29, 周四)** · 主题: 用 Suricata 抓 JA3 + - 🔧 动手: 配规则输出 ja3 (120min) + - 📤 产出: `notes/P4/W44-D4-ja3-suricata.md` + +- [ ] **Day 306 (2027-04-30, 周五)** · 主题: 月度复盘 + 整理 + - 📤 产出: `monthly/2027-04.md` + +- [ ] **Day 307 (2027-05-01, 周六)** · ☕ 五一假期 — 留白 + +- [ ] **Day 308 (2027-05-02, 周日)** · ☕ 五一假期 — 留白 + +--- + +### Week 45 (Day 309-315) · DDoS 清洗设计 + +- [ ] **Day 309 (2027-05-03, 周一)** · ☕ 五一假期 — 留白 + +- [ ] **Day 310 (2027-05-04, 周二)** · ☕ 五一假期 — 留白 + +- [ ] **Day 311 (2027-05-05, 周三)** · ☕ 五一假期 — 留白 + +- [ ] **Day 312 (2027-05-06, 周四)** · 主题: 清洗设备需求 + - 🔧 动手: 写 RFC (150min) + - 📤 产出: `docs/ddos-clean-rfc.md` + +- [ ] **Day 313 (2027-05-07, 周五)** · 主题: 架构设计 + - 🔧 动手: 画清洗 pipeline 图 (150min) + - 📤 产出: `docs/ddos-clean-arch.md` + +- [ ] **Day 314 (2027-05-08, 周六)** · 主题: 大块实验 — 仓库脚手架 + - 🔧 动手: 初始化 + Makefile (360min) + - 📤 产出: `code/ddos-clean/` + +- [ ] **Day 315 (2027-05-09, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P4/W45-summary.md` + +--- + +### Week 46 (Day 316-322) · SYN flood 防护 + +- [ ] **Day 316 (2027-05-10, 周一)** · 主题: SYN cookie 原理 + - 📖 学习: cookie 生成算法 (90min) + - 📤 产出: `notes/P4/W46-D1-syn-cookie.md` + +- [ ] **Day 317 (2027-05-11, 周二)** · 主题: SYN proxy 实现 + - 🔧 动手: 实现 SYN proxy 数据面 (180min) + - 🐧 内核对照: 读 `net/netfilter/nf_synproxy_core.c` (30min) + - 📤 产出: commit "syn proxy" + +- [ ] **Day 318 (2027-05-12, 周三)** · 主题: cookie 校验 + - 🔧 动手: ACK 阶段校验 cookie (150min) + - 📤 产出: commit "cookie verify" + +- [ ] **Day 319 (2027-05-13, 周四)** · 主题: 后端连接保持 + - 🔧 动手: 与真实服务器建二段连接 (180min) + - 📤 产出: commit "splice" + +- [ ] **Day 320 (2027-05-14, 周五)** · 主题: SYN 速率限制 + - 🔧 动手: token bucket (120min) + - 📤 产出: commit "syn rate limit" + +- [ ] **Day 321 (2027-05-15, 周六)** · 主题: 大块实验 — SYN flood 抗压 + - 🔧 动手: dperf 打 5Mpps SYN flood (480min) + - 📤 产出: `perf-data/P4/W46-syn-stress.md` + +- [ ] **Day 322 (2027-05-16, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P4/W46-summary.md` + +--- + +### Week 47 (Day 323-329) · UDP flood 防护 + +- [ ] **Day 323 (2027-05-17, 周一)** · 主题: UDP rate limit + - 🔧 动手: 实现 per-src rate limit (150min) + - 📤 产出: commit "udp rate" + +- [ ] **Day 324 (2027-05-18, 周二)** · 主题: payload signature + - 🔧 动手: 检测放大攻击特征 (150min) + - 🐧 内核对照: 读 `net/ipv4/udp.c::__udp4_lib_rcv` 看内核 UDP 主入口 (30min) + - 📤 产出: commit "udp sig" + +- [ ] **Day 325 (2027-05-19, 周三)** · 主题: source validation + - 🔧 动手: BCP38 检查 (120min) + - 📤 产出: commit "bcp38" + +- [ ] **Day 326 (2027-05-20, 周四)** · 主题: ICMP 防护 + - 🔧 动手: ICMP rate + size limit (120min) + - 📤 产出: commit "icmp" + +- [ ] **Day 327 (2027-05-21, 周五)** · 主题: fragmentation 防护 + - 🔧 动手: frag 重组 + 限制 (120min) + - 📤 产出: commit "frag" + +- [ ] **Day 328 (2027-05-22, 周六)** · 主题: 大块实验 — UDP flood 抗压 + - 🔧 动手: 测 10Mpps UDP (480min) + - 📤 产出: `perf-data/P4/W47-udp-stress.md` + +- [ ] **Day 329 (2027-05-23, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P4/W47-summary.md` + +--- + +### Week 48 (Day 330-336) · reflection attack + +- [ ] **Day 330 (2027-05-24, 周一)** · 主题: 反射攻击谱系 + - 📖 学习: DNS/NTP/SSDP/Memcached (120min) + - 📤 产出: `notes/P4/W48-D1-reflection.md` + +- [ ] **Day 331 (2027-05-25, 周二)** · 主题: DNS amp 检测 + - 🔧 动手: 检测异常 DNS 响应 (150min) + - 🐧 内核对照: 读 `net/netfilter/nf_conntrack_helper.c` 看 conntrack helper (30min) + - 📤 产出: commit "dns amp" + +- [ ] **Day 332 (2027-05-26, 周三)** · 主题: NTP monlist 防护 + - 🔧 动手: 拦截 monlist (120min) + - 📤 产出: commit "ntp" + +- [ ] **Day 333 (2027-05-27, 周四)** · 主题: Memcached 防护 + - 🔧 动手: 11211 异常拦截 (120min) + - 📤 产出: commit "memcached" + +- [ ] **Day 334 (2027-05-28, 周五)** · 主题: 反射特征库 + - 🔧 动手: 整理特征库 yaml (120min) + - 📤 产出: `config/reflection.yaml` + +- [ ] **Day 335 (2027-05-29, 周六)** · 主题: 大块实验 — 反射攻击 replay + - 🔧 动手: replay 真实反射 pcap (360min) + - 📤 产出: `perf-data/P4/W48-replay.md` + +- [ ] **Day 336 (2027-05-30, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P4/W48-summary.md` + +--- + +### Week 49 (Day 337-343) · 黑白名单 + ASN + +- [ ] **Day 337 (2027-05-31, 周一)** · 主题: 月度复盘 + - 📤 产出: `monthly/2027-05.md` + +- [ ] **Day 338 (2027-06-01, 周二)** · 主题: IP 黑名单 + - 🔧 动手: rte_lpm 加速黑名单 (180min) + - 🐧 内核对照: 读 `net/netfilter/ipset/ip_set_hash_ip.c` 看内核 ipset (30min) + - 📤 产出: commit "ip blacklist" + +- [ ] **Day 339 (2027-06-02, 周三)** · 主题: GeoIP + - 🔧 动手: 集成 MaxMind (120min) + - 📤 产出: commit "geoip" + +- [ ] **Day 340 (2027-06-03, 周四)** · 主题: ASN 阻断 + - 🔧 动手: ASN 级 drop (120min) + - 📤 产出: commit "asn block" + +- [ ] **Day 341 (2027-06-04, 周五)** · 主题: 白名单 fast path + - 🔧 动手: 白名单直通 (120min) + - 📤 产出: commit "whitelist" + +- [ ] **Day 342 (2027-06-05, 周六)** · 主题: 大块实验 — 1 千万条规则 + - 🔧 动手: 加载 1000 万 IP,看查表性能 (360min) + - 📤 产出: `perf-data/P4/W49-lpm-scale.md` + +- [ ] **Day 343 (2027-06-06, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P4/W49-summary.md` + +--- + +### Week 50 (Day 344-350) · 抗压测试 + +- [ ] **Day 344 (2027-06-07, 周一)** · 主题: 混合攻击测试 + - 🔧 动手: SYN + UDP + HTTP 同时打 (180min) + - 📤 产出: `perf-data/P4/W50-D1-mixed.md` + +- [ ] **Day 345 (2027-06-08, 周二)** · 主题: 长连接保持率 + - 🔧 动手: 攻击时合法用户存活率 (180min) + - 🐧 内核对照: 读 `include/trace/events/net.h` 看内核网络 tracepoints (30min) + - 📤 产出: `perf-data/P4/W50-D2-survival.md` + +- [ ] **Day 346 (2027-06-09, 周三)** · ☕ 端午假期 — 留白 + +- [ ] **Day 347 (2027-06-10, 周四)** · ☕ 端午假期 — 留白 + +- [ ] **Day 348 (2027-06-11, 周五)** · ☕ 端午假期 — 留白 + +- [ ] **Day 349 (2027-06-12, 周六)** · 主题: 大块实验 — 测试报告 + - 🔧 动手: 整理完整测试报告 (480min) + - 📤 产出: `perf-data/P4/W50-report.md` + +- [ ] **Day 350 (2027-06-13, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P4/W50-summary.md` + +--- + +### Week 51 (Day 351-357) · 整体调优 + +- [ ] **Day 351 (2027-06-14, 周一)** · 主题: perf top + - 🔧 动手: 找热点 (150min) + - 📤 产出: `perf-data/P4/W51-D1-perf.md` + +- [ ] **Day 352 (2027-06-15, 周二)** · 主题: 火焰图 + - 🔧 动手: 生成调用栈火焰 (120min) + - 🐧 内核对照: 读 `kernel/bpf/verifier.c` 简述 BPF 校验器(了解 BPF 生态)(30min) + - 📤 产出: `flamegraph/W51-ddos.svg` + +- [ ] **Day 353 (2027-06-16, 周三)** · 主题: cache 优化 + - 🔧 动手: 优化数据结构布局 (180min) + - 📤 产出: commit "cache opt" + +- [ ] **Day 354 (2027-06-17, 周四)** · 主题: 批量化 + - 🔧 动手: 关键路径改批量 (180min) + - 📤 产出: commit "batch" + +- [ ] **Day 355 (2027-06-18, 周五)** · 主题: 多核扩展 + - 🔧 动手: 测 1/2/4/8 核扩展性 (180min) + - 📤 产出: `perf-data/P4/W51-D5-scale.md` + +- [ ] **Day 356 (2027-06-19, 周六)** · 主题: 大块实验 — 极限冲线速 + - 🔧 动手: 单机 100Gbps 入流量清洗 (480min) + - 📤 产出: `perf-data/P4/W51-100G.md` + +- [ ] **Day 357 (2027-06-20, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P4/W51-summary.md` + +--- + +### Week 52 (Day 358-364) · P4 总结 + +- [ ] **Day 358 (2027-06-21, 周一)** · 主题: 整理产出 + - 🔧 动手: 组织 P4 文档 (180min) + - 📤 产出: `P4-final/` + +- [ ] **Day 359 (2027-06-22, 周二)** · 主题: 技术博客 (1) + - 🔧 动手: "Suricata-DPDK 工程实战" (180min) + - 🐧 内核对照: 博客加对照 "AF_PACKET vs Suricata-DPDK" 章节 (30min) + - 📤 产出: `blog/P4-suricata.md` + +- [ ] **Day 360 (2027-06-23, 周三)** · 主题: 技术博客 (2) + - 🔧 动手: "用 DPDK 抗 5Mpps SYN flood" (180min) + - 📤 产出: `blog/P4-syn-defense.md` + +- [ ] **Day 361 (2027-06-24, 周四)** · 主题: 技术博客 (3) + - 🔧 动手: "DDoS 清洗设备数据面架构" (180min) + - 📤 产出: `blog/P4-ddos-arch.md` + +- [ ] **Day 362 (2027-06-25, 周五)** · 主题: 简历 v4 + 晋升材料 + - 🔧 动手: 准备晋升答辩材料 (180min) + - 📤 产出: `career/promotion-package.md` + +- [ ] **Day 363 (2027-06-26, 周六)** · 主题: 大块实验 — 开源 ddos-clean + - 🔧 动手: GitHub release + 推广 (360min) + - 📤 产出: GitHub release + +- [ ] **Day 364 (2027-06-27, 周日)** · 🎯 P4 阶段答辩 + - 📤 产出: `P4-final/answer.md` + `P4-final/retro.md` + `monthly/2027-06.md` + +--- + +# P5 · DPU 数据面架构 (M13-M15) + +> **Day 365-455**, 2027-06-28 ~ 2027-09-26 +> **核心目标**: PCIe + vDPA + rte_flow offload 全打通;输出一份 DPU 数据面架构设计文档(达到 DPU 厂商面试的水平) + +## P5.0 阶段目标 + +1. PCIe 完全吃透:手动解码 BAR / 写最小 PCIe 驱动 +2. vDPA 跑通 + 源码理解 +3. rte_flow hardware offload 实测(CX5/CX6) +4. 调研 BlueField / Pensando / 国产 DPU +5. 写一份"自研 DPU 数据面架构"设计文档 + +## P5.1 关键交付物 + +- [ ] `notes/P5/` 13 周笔记 +- [ ] `code/pcie-driver-demo/` 最小 PCIe 驱动 +- [ ] `code/vdpa-demo/` vDPA 实验 +- [ ] `docs/dpu-arch-design.md` DPU 架构设计文档(30+ 页) + +## P5.1.5 🐧 本阶段内核对照清单(K7 深挖 + virtio 内核驱动) + +> P5 是内核学习最密集的阶段:PCIe、VFIO、virtio-net、vhost 全在内核。 + +| 周 | 对照主题 | 内核源码路径 | +|---|---|---| +| W53 | 内核 PCIe 枚举 | `drivers/pci/probe.c::pci_scan_bus`, `access.c` | +| W54 | 内核 MSI/MSI-X + IOMMU | `drivers/pci/msi.c`, `drivers/iommu/` | +| W55 | 内核 pci_driver 完整生命周期 | `drivers/pci/pci-driver.c` | +| W56 | 内核 VFIO | `drivers/vfio/pci/vfio_pci_core.c` | +| W57 | 内核 virtio-net 驱动 | `drivers/net/virtio_net.c` | +| W58 | 内核 vhost | `drivers/vhost/net.c`, `vhost.c` | +| W59 | 内核 vDPA framework | `drivers/vdpa/vdpa.c`, `drivers/virtio/virtio_vdpa.c` | +| W60 | 内核 mlx5 driver 全览 | `drivers/net/ethernet/mellanox/mlx5/core/` | +| W61 | 内核 SR-IOV | `drivers/pci/iov.c` | +| W62 | 内核 DMA API | `kernel/dma/mapping.c` | +| W63 | 内核热迁移辅助 | `drivers/vfio/vfio_iommu_type1.c` migration | +| W64 | 内核 devlink (卸载配置) | `net/core/devlink.c` | +| W65 | 总结:DPU 数据面内核视角综述 | — | + +## P5.2 周计划总览 (W53-W65) + +| 周 | Day | 主题 | 里程碑 | +|---|---|---|---| +| W53 | 365-371 | PCIe 拓扑 + Config Space | 解码 BAR | +| W54 | 372-378 | PCIe MSI-X + DMA | DMA demo | +| W55 | 379-385 | 写最小 PCIe 驱动 | insmod 成功 | +| W56 | 386-392 | virtio-net 协议 | spec 精读 | +| W57 | 393-399 | vhost-user | qemu 接 OVS | +| W58 | 400-406 | vDPA 框架 | mdev 跑通 | +| W59 | 407-413 | vDPA driver 源码 | mlx5 vdpa | +| W60 | 414-420 | rte_flow offload | CX5 实测 | +| W61 | 421-427 | DPU 业界调研 | 厂商对比 | +| W62 | 428-434 | BlueField 深入 | DOCA 入门 | +| W63 | 435-441 | DPU 架构 RFC | 30 页设计 | +| W64 | 442-448 | 架构评审 + 模拟面试 | 答辩稿 | +| W65 | 449-455 | P5 总结 | 文档定稿 | + +## P5.3 详细日计划 + +### Week 53 (Day 365-371) · PCIe 拓扑 + Config Space + +- [ ] **Day 365 (2027-06-28, 周一)** · 主题: PCIe 总览 + - 📖 学习: 《PCI Express 体系结构导读》Ch.1-2 (120min) + - 📤 产出: `notes/P5/W53-D1-pcie-overview.md` + +- [ ] **Day 366 (2027-06-29, 周二)** · 主题: 拓扑探查 + - 🔧 动手: `lspci -tv` 画本机拓扑图 (120min) + - 🐧 内核对照: 读 `drivers/pci/probe.c::pci_scan_bus` 看 PCIe 枚举 (45min) + - 📤 产出: `notes/P5/W53-D2-topo.md` + +- [ ] **Day 367 (2027-06-30, 周三)** · 主题: Configuration Space + - 🔧 动手: `lspci -xxxx` 解码 X540 头部 (150min) + - 📤 产出: `notes/P5/W53-D3-cfg-space.md` + +- [ ] **Day 368 (2027-07-01, 周四)** · 主题: BAR 解码 + - 🔧 动手: 手动解码 BAR0-5 (120min) + - 📤 产出: `notes/P5/W53-D4-bar.md` + +- [ ] **Day 369 (2027-07-02, 周五)** · 主题: Capability 链 + - 🔧 动手: 遍历 Capability + Ext Capability (120min) + - 📤 产出: `notes/P5/W53-D5-cap.md` + +- [ ] **Day 370 (2027-07-03, 周六)** · 主题: 大块实验 — setpci 实验 + - 🔧 动手: 用 setpci 改寄存器观察现象 (360min) + - 📤 产出: `notes/P5/W53-D6-setpci.md` + +- [ ] **Day 371 (2027-07-04, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P5/W53-summary.md` + +--- + +### Week 54 (Day 372-378) · PCIe MSI-X + DMA + +- [ ] **Day 372 (2027-07-05, 周一)** · 主题: MSI/MSI-X + - 📖 学习: spec + capability (120min) + - 📤 产出: `notes/P5/W54-D1-msix.md` + +- [ ] **Day 373 (2027-07-06, 周二)** · 主题: MSI-X Table 定位 + - 🔧 动手: 在 BAR 中找 MSI-X Table (120min) + - 🐧 内核对照: 读 `drivers/pci/msi.c::pci_alloc_irq_vectors` (45min) + - 📤 产出: `notes/P5/W54-D2-msix-table.md` + +- [ ] **Day 374 (2027-07-07, 周三)** · 主题: DMA 概念 + - 📖 学习: DMA address / IOVA / IOMMU (120min) + - 📤 产出: `notes/P5/W54-D3-dma.md` + +- [ ] **Day 375 (2027-07-08, 周四)** · 主题: dma-buf / iommu + - 📖 学习: VFIO mode (120min) + - 📤 产出: `notes/P5/W54-D4-iommu.md` + +- [ ] **Day 376 (2027-07-09, 周五)** · 主题: AER / ACS + - 📖 学习: Advanced Error Reporting (90min) + - 📤 产出: `notes/P5/W54-D5-aer.md` + +- [ ] **Day 377 (2027-07-10, 周六)** · 主题: 大块实验 — IOMMU 实验 + - 🔧 动手: 开关 IOMMU 跑 DPDK,观察行为 (360min) + - 📤 产出: `perf-data/P5/W54-iommu.md` + +- [ ] **Day 378 (2027-07-11, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P5/W54-summary.md` + +--- + +### Week 55 (Day 379-385) · 写最小 PCIe 驱动 + +- [ ] **Day 379 (2027-07-12, 周一)** · 主题: pci_driver 框架 + - 📖 学习: `include/linux/pci.h` (120min) + - 📤 产出: `notes/P5/W55-D1-driver.md` + +- [ ] **Day 380 (2027-07-13, 周二)** · 主题: probe + remove + - 🔧 动手: 写最小 probe (180min) + - 🐧 内核对照: 读 `drivers/pci/pci-driver.c::__pci_register_driver` (30min) + - 📤 产出: `code/pcie-driver-demo/main.c` + +- [ ] **Day 381 (2027-07-14, 周三)** · 主题: ioremap BAR + - 🔧 动手: 映射 BAR 读寄存器 (180min) + - 📤 产出: commit "ioremap" + +- [ ] **Day 382 (2027-07-15, 周四)** · 主题: MSI-X 申请 + - 🔧 动手: pci_alloc_irq_vectors (180min) + - 📤 产出: commit "msix" + +- [ ] **Day 383 (2027-07-16, 周五)** · 主题: 中断处理 + - 🔧 动手: request_irq + handler (150min) + - 📤 产出: commit "irq handler" + +- [ ] **Day 384 (2027-07-17, 周六)** · 主题: 大块实验 — 加载测试 + - 🔧 动手: insmod 加载 + dmesg 观察 (360min) + - 📤 产出: `perf-data/P5/W55-load.md` + +- [ ] **Day 385 (2027-07-18, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P5/W55-summary.md` + +--- + +### Week 56 (Day 386-392) · virtio-net 协议 + +- [ ] **Day 386 (2027-07-19, 周一)** · 主题: virtio spec 总览 + - 📖 学习: virtio 1.2 spec Ch.1-3 (150min) + - 📤 产出: `notes/P5/W56-D1-virtio.md` + +- [ ] **Day 387 (2027-07-20, 周二)** · 主题: virtqueue + - 📖 学习: split / packed virtqueue (150min) + - 🐧 内核对照: 读 `drivers/net/virtio_net.c` 完整 RX/TX 路径 (45min) + - 📤 产出: `notes/P5/W56-D2-vq.md` + +- [ ] **Day 388 (2027-07-21, 周三)** · 主题: feature bits + - 📖 学习: virtio-net 特性 (120min) + - 📤 产出: `notes/P5/W56-D3-features.md` + +- [ ] **Day 389 (2027-07-22, 周四)** · 主题: virtio-net PMD + - 📖 学习: `drivers/net/virtio/` (150min) + - 📤 产出: `notes/P5/W56-D4-virtio-pmd.md` + +- [ ] **Day 390 (2027-07-23, 周五)** · 主题: virtio mediator + - 📖 学习: vDPA mediator 角色 (90min) + - 📤 产出: `notes/P5/W56-D5-mediator.md` + +- [ ] **Day 391 (2027-07-24, 周六)** · 主题: 大块实验 — 跑 virtio-user + - 🔧 动手: testpmd + virtio-user (360min) + - 📤 产出: `notes/P5/W56-D6-virtio-user.md` + +- [ ] **Day 392 (2027-07-25, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P5/W56-summary.md` + +--- + +### Week 57 (Day 393-399) · vhost-user + +- [ ] **Day 393 (2027-07-26, 周一)** · 主题: vhost-user 协议 + - 📖 学习: spec (120min) + - 📤 产出: `notes/P5/W57-D1-vhost.md` + +- [ ] **Day 394 (2027-07-27, 周二)** · 主题: vhost-user 消息 + - 📖 学习: 28 种 message (150min) + - 🐧 内核对照: 读 `drivers/vhost/net.c::vhost_net_open` (30min) + - 📤 产出: `notes/P5/W57-D2-msg.md` + +- [ ] **Day 395 (2027-07-28, 周三)** · 主题: DPDK vhost lib + - 📖 学习: `lib/vhost/` (150min) + - 📤 产出: `notes/P5/W57-D3-vhost-lib.md` + +- [ ] **Day 396 (2027-07-29, 周四)** · 主题: QEMU + vhost-user + - 🔧 动手: QEMU 接 OVS-DPDK 跑 VM (180min) + - 📤 产出: `notes/P5/W57-D4-qemu.md` + +- [ ] **Day 397 (2027-07-30, 周五)** · 主题: VM 内 PMD + - 🔧 动手: VM 内跑 DPDK testpmd (120min) + - 📤 产出: `notes/P5/W57-D5-vm-pmd.md` + +- [ ] **Day 398 (2027-07-31, 周六)** · 主题: 大块实验 — VM↔VM 性能 + - 🔧 动手: 两 VM 互打,记录 PPS (360min) + - 📤 产出: `perf-data/P5/W57-vm-vm.md` + +- [ ] **Day 399 (2027-08-01, 周日)** · ☕ 复盘 + 月度 + - 📤 产出: `notes/P5/W57-summary.md` + `monthly/2027-07.md` + +--- + +### Week 58 (Day 400-406) · vDPA 框架 + +- [ ] **Day 400 (2027-08-02, 周一)** · 主题: vDPA 概念 + - 📖 学习: vDPA = virtio data path acceleration (120min) + - 📤 产出: `notes/P5/W58-D1-vdpa.md` + +- [ ] **Day 401 (2027-08-03, 周二)** · 主题: vDPA 架构 + - 📖 学习: kernel vDPA framework (150min) + - 🐧 内核对照: 读 `drivers/vdpa/vdpa.c` + `drivers/virtio/virtio_vdpa.c` (45min) + - 📤 产出: `notes/P5/W58-D2-arch.md` + +- [ ] **Day 402 (2027-08-04, 周三)** · 主题: mdev / vhost-vdpa + - 📖 学习: 两种暴露方式 (120min) + - 📤 产出: `notes/P5/W58-D3-mdev.md` + +- [ ] **Day 403 (2027-08-05, 周四)** · 主题: DPDK vdpa lib + - 📖 学习: `lib/vhost/vdpa.c` (120min) + - 📤 产出: `notes/P5/W58-D4-dpdk-vdpa.md` + +- [ ] **Day 404 (2027-08-06, 周五)** · 主题: 业界 vDPA 实现 + - 📖 学习: Mellanox / Intel vDPA (120min) + - 📤 产出: `notes/P5/W58-D5-impl.md` + +- [ ] **Day 405 (2027-08-07, 周六)** · 主题: 大块实验 — 跑 vdpa example + - 🔧 动手: examples/vdpa 跑通 (360min) + - 📤 产出: `notes/P5/W58-D6-example.md` + +- [ ] **Day 406 (2027-08-08, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P5/W58-summary.md` + +--- + +### Week 59 (Day 407-413) · vDPA driver 源码 + +- [ ] **Day 407 (2027-08-09, 周一)** · 主题: mlx5 vdpa 总览 + - 📖 学习: `drivers/vdpa/mlx5/` (120min) + - 📤 产出: `notes/P5/W59-D1-mlx5.md` + +- [ ] **Day 408 (2027-08-10, 周二)** · 主题: 设备初始化 + - 📖 学习: probe + setup (150min) + - 🐧 内核对照: 读 `drivers/net/ethernet/mellanox/mlx5/core/main.c` (45min) + - 📤 产出: `notes/P5/W59-D2-init.md` + +- [ ] **Day 409 (2027-08-11, 周三)** · 主题: virtq 卸载 + - 📖 学习: 如何把 virtqueue 映射到 HW (180min) + - 📤 产出: `notes/P5/W59-D3-virtq-offload.md` + +- [ ] **Day 410 (2027-08-12, 周四)** · 主题: 控制消息 + - 📖 学习: 状态变更处理 (120min) + - 📤 产出: `notes/P5/W59-D4-ctrl.md` + +- [ ] **Day 411 (2027-08-13, 周五)** · 主题: live migration + - 📖 学习: vDPA 热迁移机制 (120min) + - 📤 产出: `notes/P5/W59-D5-migration.md` + +- [ ] **Day 412 (2027-08-14, 周六)** · 主题: 大块实验 — 跑 mlx5 vDPA(若有) + - 🔧 动手: 若有 CX5/CX6,实跑 (360min) + - 📤 产出: `perf-data/P5/W59-mlx5-vdpa.md` + +- [ ] **Day 413 (2027-08-15, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P5/W59-summary.md` + +--- + +### Week 60 (Day 414-420) · rte_flow offload + +- [ ] **Day 414 (2027-08-16, 周一)** · 主题: rte_flow API 复习 + - 📖 学习: pattern / action / attr (120min) + - 📤 产出: `notes/P5/W60-D1-rteflow.md` + +- [ ] **Day 415 (2027-08-17, 周二)** · 主题: flow 探测能力 + - 🔧 动手: rte_flow_validate 探测 (120min) + - 🐧 内核对照: 读 `drivers/pci/iov.c` 看内核 SR-IOV VF 创建 (30min) + - 📤 产出: `notes/P5/W60-D2-cap.md` + +- [ ] **Day 416 (2027-08-18, 周三)** · 主题: 5-tuple 卸载 + - 🔧 动手: 把 5-tuple 卸到 HW (180min) + - 📤 产出: commit "5tuple offload" + +- [ ] **Day 417 (2027-08-19, 周四)** · 主题: RSS action + - 🔧 动手: rss action 配置 (120min) + - 📤 产出: commit "rss action" + +- [ ] **Day 418 (2027-08-20, 周五)** · 主题: counter / meter + - 🔧 动手: 卸载 counter (120min) + - 📤 产出: commit "counter" + +- [ ] **Day 419 (2027-08-21, 周六)** · 主题: 大块实验 — offload 性能对比 + - 🔧 动手: SW vs HW 路径 PPS 对比 (480min) + - 📤 产出: `perf-data/P5/W60-offload.md` + +- [ ] **Day 420 (2027-08-22, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P5/W60-summary.md` + +--- + +### Week 61 (Day 421-427) · DPU 业界调研 + +- [ ] **Day 421 (2027-08-23, 周一)** · 主题: BlueField 1/2/3 + - 📖 学习: NVIDIA 文档 (150min) + - 📤 产出: `notes/P5/W61-D1-bf.md` + +- [ ] **Day 422 (2027-08-24, 周二)** · 主题: Pensando DSC + - 📖 学习: Pensando 架构 (120min) + - 🐧 内核对照: 读 `kernel/dma/mapping.c` DMA API (30min) + - 📤 产出: `notes/P5/W61-D2-pensando.md` + +- [ ] **Day 423 (2027-08-25, 周三)** · 主题: AWS Nitro + - 📖 学习: re:Invent talks (120min) + - 📤 产出: `notes/P5/W61-D3-nitro.md` + +- [ ] **Day 424 (2027-08-26, 周四)** · 主题: Azure SmartNIC (AccelNet) + - 📖 学习: NSDI'18 论文 (120min) + - 📤 产出: `notes/P5/W61-D4-accelnet.md` + +- [ ] **Day 425 (2027-08-27, 周五)** · 主题: 国产 DPU + - 📖 学习: 中科驭数 / 云豹 / 星云 (120min) + - 📤 产出: `notes/P5/W61-D5-cn-dpu.md` + +- [ ] **Day 426 (2027-08-28, 周六)** · 主题: 大块实验 — 厂商对比矩阵 + - 🔧 动手: 写一份对比表(功能/性能/SDK/开放度)(360min) + - 📤 产出: `notes/P5/W61-D6-matrix.md` + +- [ ] **Day 427 (2027-08-29, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P5/W61-summary.md` + +--- + +### Week 62 (Day 428-434) · BlueField 深入 + +- [ ] **Day 428 (2027-08-30, 周一)** · 主题: DOCA 总览 + - 📖 学习: DOCA SDK 文档 (150min) + - 📤 产出: `notes/P5/W62-D1-doca.md` + +- [ ] **Day 429 (2027-08-31, 周二)** · 主题: DOCA Flow + - 📖 学习: DOCA Flow API (150min) + - 🐧 内核对照: 读 `drivers/vfio/vfio_iommu_type1.c` 看 vfio 迁移辅助 (30min) + - 📤 产出: `notes/P5/W62-D2-doca-flow.md` + +- [ ] **Day 430 (2027-09-01, 周三)** · 主题: DOCA App Shield / DPI + - 📖 学习: 安全相关组件 (120min) + - 📤 产出: `notes/P5/W62-D3-app-shield.md` + +- [ ] **Day 431 (2027-09-02, 周四)** · 主题: ARM SoC 调度 + - 📖 学习: BlueField ARM 核分工 (120min) + - 📤 产出: `notes/P5/W62-D4-arm.md` + +- [ ] **Day 432 (2027-09-03, 周五)** · 主题: BlueField 模式 + - 📖 学习: Separated / Embedded / SmartNIC (120min) + - 📤 产出: `notes/P5/W62-D5-modes.md` + +- [ ] **Day 433 (2027-09-04, 周六)** · 主题: 大块实验 — DOCA hello(若无硬件则模拟) + - 🔧 动手: 跑 DOCA 示例(无硬件则读源码)(360min) + - 📤 产出: `notes/P5/W62-D6-doca-hello.md` + +- [ ] **Day 434 (2027-09-05, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P5/W62-summary.md` + +--- + +### Week 63 (Day 435-441) · DPU 架构 RFC + +- [ ] **Day 435 (2027-09-06, 周一)** · 主题: 设计目标 + - 🔧 动手: 写 motivation + 目标 (180min) + - 📤 产出: `docs/dpu-arch-design.md` (1) + +- [ ] **Day 436 (2027-09-07, 周二)** · 主题: 数据面架构 + - 🔧 动手: 画 fast path / slow path 图 (180min) + - 🐧 内核对照: 读 `net/core/devlink.c` 看内核 devlink 卸载配置接口 (30min) + - 📤 产出: `docs/dpu-arch-design.md` (2) + +- [ ] **Day 437 (2027-09-08, 周三)** · 主题: 卸载分层 + - 🔧 动手: 写 HW / FW / SW 三层卸载策略 (180min) + - 📤 产出: `docs/dpu-arch-design.md` (3) + +- [ ] **Day 438 (2027-09-09, 周四)** · 主题: 控制面 + - 🔧 动手: 控制器接口设计 (180min) + - 📤 产出: `docs/dpu-arch-design.md` (4) + +- [ ] **Day 439 (2027-09-10, 周五)** · 主题: 多租户隔离 + - 🔧 动手: SR-IOV / SF / VF 设计 (150min) + - 📤 产出: `docs/dpu-arch-design.md` (5) + +- [ ] **Day 440 (2027-09-11, 周六)** · 主题: 大块实验 — 性能模型 + - 🔧 动手: 算 PPS / 带宽 / 时延预算 (360min) + - 📤 产出: `docs/dpu-arch-design.md` (6) + +- [ ] **Day 441 (2027-09-12, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P5/W63-summary.md` + +--- + +### Week 64 (Day 442-448) · 架构评审 + 模拟面试 + +- [ ] **Day 442 (2027-09-13, 周一)** · 主题: 自评审 + - 🔧 动手: 自己挑漏洞 (180min) + - 📤 产出: `docs/dpu-arch-design-review.md` + +- [ ] **Day 443 (2027-09-14, 周二)** · 主题: 同行评审准备 + - 🔧 动手: 发给同行/导师评审 (120min) + - 🐧 内核对照: 综述"DPU 数据面从内核视角看" — 在设计文档补一节 (60min) + - 📤 产出: review 邮件 + 内核视角综述章节 + +- [ ] **Day 444 (2027-09-15, 周三)** · 主题: 答辩稿 v1 + - 🔧 动手: 写 PPT/Keynote 草稿 (180min) + - 📤 产出: `slides/dpu-arch.pdf` + +- [ ] **Day 445 (2027-09-16, 周四)** · 主题: 模拟面试 (1) + - 🔧 动手: 自我陈述 + Q&A 30 问 (180min) + - 📤 产出: `career/mock-interview-1.md` + +- [ ] **Day 446 (2027-09-17, 周五)** · 主题: 模拟面试 (2) + - 🔧 动手: 找朋友模拟 (150min) + - 📤 产出: `career/mock-interview-2.md` + +- [ ] **Day 447 (2027-09-18, 周六)** · 主题: 大块实验 — 答辩稿定稿 + - 🔧 动手: 反复打磨 PPT + 文档 (480min) + - 📤 产出: 定稿 slides + +- [ ] **Day 448 (2027-09-19, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P5/W64-summary.md` + +--- + +### Week 65 (Day 449-455) · P5 总结 + +- [ ] **Day 449 (2027-09-20, 周一)** · 主题: 整理产出 + - 🔧 动手: 组织 P5 文档 (180min) + - 📤 产出: `P5-final/` + +- [ ] **Day 450 (2027-09-21, 周二)** · 主题: 技术博客 (1) + - 🔧 动手: "从 0 看懂 PCIe + vDPA" (180min) + - 🐧 内核对照: 博客中同时讲内核 vDPA 与 DPDK vDPA 对照 (30min) + - 📤 产出: `blog/P5-pcie-vdpa.md` + +- [ ] **Day 451 (2027-09-22, 周三)** · 主题: 技术博客 (2) + - 🔧 动手: "DPU 数据面架构设计要点" (180min) + - 📤 产出: `blog/P5-dpu-arch.md` + +- [ ] **Day 452 (2027-09-23, 周四)** · 主题: 简历 v5 + - 🔧 动手: 把 P5 加入简历 (120min) + - 📤 产出: `career/resume-v5.md` + +- [ ] **Day 453 (2027-09-24, 周五)** · 主题: 投递准备 + - 🔧 动手: 列目标公司 + JD 分析 (180min) + - 📤 产出: `career/target-companies.md` + +- [ ] **Day 454 (2027-09-25, 周六)** · 主题: 大块实验 — 简历定稿 + - 🔧 动手: 简历定稿 + cover letter 模板 (360min) + - 📤 产出: `career/resume-final.md` + +- [ ] **Day 455 (2027-09-26, 周日)** · 🎯 P5 阶段答辩 + - 📤 产出: `P5-final/answer.md` + `P5-final/retro.md` + `monthly/2027-09.md` + +--- + +# P6 · 作品收口 + 出口 (M16-M18) + +> **Day 456-546**, 2027-09-27 ~ 2027-12-27 +> **核心目标**: 把 18 个月所有产出收口,做出 1 个高质量开源项目、3 篇深度博客、1 套面试材料,正式投递目标公司或申报晋升 + +## P6.0 阶段目标 + +1. 挑选 1 个最有沉淀的项目(`libmtcp-v3` / `sai-dpdk-mini` / `dpdk-lb` / `ddos-clean`)做开源升级 +2. 写出 3 篇引爆型博客(可推阿里云开发者社区/InfoQ/掘金/知乎) +3. 准备完整面试材料:自我介绍 / 项目深度 / 系统设计 / 八股 +4. 投递目标公司 + 跟进 +5. 准备晋升答辩材料(若选晋升路线) + +## P6.1 关键交付物 + +- [ ] 1 个 GitHub Star ≥ 100 的开源项目 +- [ ] 3 篇技术博客(每篇 PV ≥ 5000 目标) +- [ ] 1 份 8-10 页简历 +- [ ] 1 套面试题库(200+ Q&A) +- [ ] 拿到 offer 或晋升通过 + +## P6.1.5 🐧 本阶段内核相关准备 + +P6 阶段不再新增内核学习,而是**整理**: + +- 面试题库中单开一列 "内核 vs DPDK" 对照题 (~20 道) +- 简历技能栏加 "Linux 内核网络栈 (net_device / netlink / conntrack / vfio / vdpa)" +- 收尾博客 "18 个月成长" 中重点讲 "为什么 DPDK 工程师必须懂内核" +- 系统设计题准备时,凡涉及网络的都要有内核视角 +- 面试自我介绍中主动提 "读过 XX 个内核网络子系统源码" + +## P6.2 周计划总览 (W66-W78) + +| 周 | Day | 主题 | 里程碑 | +|---|---|---|---| +| W66 | 456-462 | 选定开源主项目 | 项目选型 | +| W67 | 463-469 | 项目代码 review | issue 列表 | +| W68 | 470-476 | 项目重构 + 文档 | v1.0 release | +| W69 | 477-483 | CI + 测试 + benchmark | 完整 ci | +| W70 | 484-490 | 引爆博客 (1) | 发布 | +| W71 | 491-497 | 引爆博客 (2) | 发布 | +| W72 | 498-504 | 引爆博客 (3) | 发布 | +| W73 | 505-511 | 简历定稿 | 8-10 页 | +| W74 | 512-518 | 面试题库 (1) | 100 题 | +| W75 | 519-525 | 面试题库 (2) | 200 题 | +| W76 | 526-532 | 系统设计 + 模拟面试 | 答辩稿 | +| W77 | 533-539 | 投递 + 一面/二面 | 进展跟踪 | +| W78 | 540-546 | 收口 + 决策 | offer / 续航 | + +## P6.3 详细日计划 + +### Week 66 (Day 456-462) · 选定开源主项目 + +- [ ] **Day 456 (2027-09-27, 周一)** · 主题: 项目盘点 + - 🔧 动手: 把 4 个候选项目列优缺点 (180min) + - 📤 产出: `P6/project-eval.md` + +- [ ] **Day 457 (2027-09-28, 周二)** · 主题: 受众分析 + - 🔧 动手: 判断哪个最有人用 (120min) + - 📤 产出: `P6/audience.md` + +- [ ] **Day 458 (2027-09-29, 周三)** · 主题: 选定 + RFC + - 🔧 动手: 写主项目 v1.0 RFC (180min) + - 📤 产出: `P6/main-project-rfc.md` + +- [ ] **Day 459 (2027-09-30, 周四)** · 主题: 月度复盘 + - 📤 产出: `monthly/2027-09.md` + +- [ ] **Day 460 (2027-10-01, 周五)** · ☕ 国庆假期 — 留白 + +- [ ] **Day 461 (2027-10-02, 周六)** · ☕ 国庆假期 — 留白 + +- [ ] **Day 462 (2027-10-03, 周日)** · ☕ 国庆假期 — 留白 + +--- + +### Week 67 (Day 463-469) · 项目代码 review + +- [ ] **Day 463 (2027-10-04, 周一)** · ☕ 国庆假期 — 留白 + +- [ ] **Day 464 (2027-10-05, 周二)** · ☕ 国庆假期 — 留白 + +- [ ] **Day 465 (2027-10-06, 周三)** · ☕ 国庆假期 — 留白 + +- [ ] **Day 466 (2027-10-07, 周四)** · ☕ 国庆假期 — 留白 + +- [ ] **Day 467 (2027-10-08, 周五)** · 主题: 代码 review (1) + - 🔧 动手: 通读主项目代码挑问题 (180min) + - 📤 产出: `P6/code-review-issues.md` + +- [ ] **Day 468 (2027-10-09, 周六)** · 主题: 大块实验 — 代码 review (2) + - 🔧 动手: 列出 issue 优先级 (360min) + - 📤 产出: GitHub Issues 创建 + +- [ ] **Day 469 (2027-10-10, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P6/W67-summary.md` + +--- + +### Week 68 (Day 470-476) · 项目重构 + 文档 + +- [ ] **Day 470 (2027-10-11, 周一)** · 主题: 接口重构 + - 🔧 动手: 优化 API (180min) + - 📤 产出: commit "api refactor" + +- [ ] **Day 471 (2027-10-12, 周二)** · 主题: 内部代码清理 + - 🔧 动手: 去 warning + 加注释 (180min) + - 📤 产出: commit "cleanup" + +- [ ] **Day 472 (2027-10-13, 周三)** · 主题: README 英文版 + - 🔧 动手: 写英文 README (180min) + - 📤 产出: `README.md` (en) + +- [ ] **Day 473 (2027-10-14, 周四)** · 主题: 架构图 + - 🔧 动手: 画专业的架构图 (180min) + - 📤 产出: `docs/architecture.png` + +- [ ] **Day 474 (2027-10-15, 周五)** · 主题: 使用文档 + - 🔧 动手: 写 quick start + tutorial (180min) + - 📤 产出: `docs/getting-started.md` + +- [ ] **Day 475 (2027-10-16, 周六)** · 主题: 大块实验 — v1.0 release + - 🔧 动手: 打 tag + release note (360min) + - 📤 产出: GitHub Release v1.0 + +- [ ] **Day 476 (2027-10-17, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P6/W68-summary.md` + +--- + +### Week 69 (Day 477-483) · CI + 测试 + benchmark + +- [ ] **Day 477 (2027-10-18, 周一)** · 主题: GitHub Actions + - 🔧 动手: 加 CI 编译 (150min) + - 📤 产出: `.github/workflows/build.yml` + +- [ ] **Day 478 (2027-10-19, 周二)** · 主题: 单元测试 + - 🔧 动手: 补单测覆盖 (180min) + - 📤 产出: commit "unit tests" + +- [ ] **Day 479 (2027-10-20, 周三)** · 主题: 集成测试 + - 🔧 动手: e2e 测试脚本 (180min) + - 📤 产出: commit "e2e tests" + +- [ ] **Day 480 (2027-10-21, 周四)** · 主题: benchmark suite + - 🔧 动手: 标准化压测脚本 (180min) + - 📤 产出: `benchmarks/` + +- [ ] **Day 481 (2027-10-22, 周五)** · 主题: 性能曲线图 + - 🔧 动手: matplotlib 出图 (150min) + - 📤 产出: `benchmarks/results.png` + +- [ ] **Day 482 (2027-10-23, 周六)** · 主题: 大块实验 — Docker 镜像 + - 🔧 动手: Dockerfile + 一键运行 (360min) + - 📤 产出: Docker Hub 镜像 + +- [ ] **Day 483 (2027-10-24, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P6/W69-summary.md` + +--- + +### Week 70 (Day 484-490) · 引爆博客 (1) + +- [ ] **Day 484 (2027-10-25, 周一)** · 主题: 选题 + 大纲 + - 🔧 动手: 写大纲 (120min) + - 📤 产出: `blog/outline-1.md` + +- [ ] **Day 485 (2027-10-26, 周二)** · 主题: 一稿 + - 🔧 动手: 写一稿 (180min) + - 📤 产出: `blog/draft-1.md` + +- [ ] **Day 486 (2027-10-27, 周三)** · 主题: 配图 + - 🔧 动手: 画 5-10 张图 (180min) + - 📤 产出: `blog/images-1/` + +- [ ] **Day 487 (2027-10-28, 周四)** · 主题: 二稿 + 校对 + - 🔧 动手: 打磨 (180min) + - 📤 产出: `blog/final-1.md` + +- [ ] **Day 488 (2027-10-29, 周五)** · 主题: 发布 + - 🔧 动手: 发到知乎 / 掘金 / InfoQ (120min) + - 📤 产出: 博客链接 + +- [ ] **Day 489 (2027-10-30, 周六)** · 主题: 大块实验 — 推广 + 答疑 + - 🔧 动手: 各群分享 + 回复评论 (240min) + - 📤 产出: 数据快照 + +- [ ] **Day 490 (2027-10-31, 周日)** · ☕ 复盘 + 月度 + - 📤 产出: `notes/P6/W70-summary.md` + `monthly/2027-10.md` + +--- + +### Week 71 (Day 491-497) · 引爆博客 (2) + +- [ ] **Day 491 (2027-11-01, 周一)** · 主题: 选题 (2) + - 🔧 动手: 大纲 (120min) + - 📤 产出: `blog/outline-2.md` + +- [ ] **Day 492 (2027-11-02, 周二)** · 主题: 一稿 + - 🔧 动手: 写 (180min) + - 📤 产出: `blog/draft-2.md` + +- [ ] **Day 493 (2027-11-03, 周三)** · 主题: 配图 + - 🔧 动手: 出图 (180min) + - 📤 产出: `blog/images-2/` + +- [ ] **Day 494 (2027-11-04, 周四)** · 主题: 二稿 + - 🔧 动手: 打磨 (180min) + - 📤 产出: `blog/final-2.md` + +- [ ] **Day 495 (2027-11-05, 周五)** · 主题: 发布 + - 🔧 动手: 多平台发布 (120min) + - 📤 产出: 链接 + +- [ ] **Day 496 (2027-11-06, 周六)** · 主题: 大块实验 — 推广 + - 🔧 动手: 推广 + 答疑 (240min) + - 📤 产出: 数据快照 + +- [ ] **Day 497 (2027-11-07, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P6/W71-summary.md` + +--- + +### Week 72 (Day 498-504) · 引爆博客 (3) + +- [ ] **Day 498 (2027-11-08, 周一)** · 主题: 选题 (3) — 终极博客 + - 🔧 动手: 大纲:"18 个月从 DPDK 用户到 DPU 架构师" (120min) + - 📤 产出: `blog/outline-3.md` + +- [ ] **Day 499 (2027-11-09, 周二)** · 主题: 一稿 + - 🔧 动手: 写 (180min) + - 📤 产出: `blog/draft-3.md` + +- [ ] **Day 500 (2027-11-10, 周三)** · 主题: 配图 + 项目集 + - 🔧 动手: 出项目集大图 (180min) + - 📤 产出: `blog/images-3/` + +- [ ] **Day 501 (2027-11-11, 周四)** · 主题: 二稿 + - 🔧 动手: 打磨 (180min) + - 📤 产出: `blog/final-3.md` + +- [ ] **Day 502 (2027-11-12, 周五)** · 主题: 发布 + - 🔧 动手: 全平台 (120min) + - 📤 产出: 链接 + +- [ ] **Day 503 (2027-11-13, 周六)** · 主题: 大块实验 — 全网推广 + - 🔧 动手: 推广 + GitHub Trending (240min) + - 📤 产出: 数据快照 + +- [ ] **Day 504 (2027-11-14, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P6/W72-summary.md` + +--- + +### Week 73 (Day 505-511) · 简历定稿 + +- [ ] **Day 505 (2027-11-15, 周一)** · 主题: 简历重写 (1) + - 🔧 动手: 项目栏 (180min) + - 📤 产出: `career/resume-v6.md` + +- [ ] **Day 506 (2027-11-16, 周二)** · 主题: 简历重写 (2) + - 🔧 动手: 技能栏 + 学历 (150min) + - 📤 产出: `career/resume-v6.md` + +- [ ] **Day 507 (2027-11-17, 周三)** · 主题: 简历定量化 + - 🔧 动手: 把每个项目改成"动词+数字+技术"格式 (180min) + - 📤 产出: `career/resume-v6.md` + +- [ ] **Day 508 (2027-11-18, 周四)** · 主题: PDF 排版 + - 🔧 动手: typora/latex 美化 (180min) + - 📤 产出: `career/resume.pdf` + +- [ ] **Day 509 (2027-11-19, 周五)** · 主题: 多版本简历 + - 🔧 动手: 大厂版 / DPU 厂商版 / 安全版 (180min) + - 📤 产出: `career/resume-{bigtech,dpu,security}.pdf` + +- [ ] **Day 510 (2027-11-20, 周六)** · 主题: 大块实验 — 简历找朋友 review + - 🔧 动手: 发 3-5 个同行 review,收集意见 (240min) + - 📤 产出: review 意见汇总 + +- [ ] **Day 511 (2027-11-21, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P6/W73-summary.md` + +--- + +### Week 74 (Day 512-518) · 面试题库 (1) + +- [ ] **Day 512 (2027-11-22, 周一)** · 主题: DPDK 基础题 + - 🔧 动手: 30 道题 + 答案 (180min) + - 📤 产出: `career/qa-dpdk-basic.md` + +- [ ] **Day 513 (2027-11-23, 周二)** · 主题: DPDK 进阶题 + - 🔧 动手: 30 道题 (180min) + - 📤 产出: `career/qa-dpdk-adv.md` + +- [ ] **Day 514 (2027-11-24, 周三)** · 主题: SONiC/SAI 题 + - 🔧 动手: 20 道题 (150min) + - 📤 产出: `career/qa-sonic.md` + +- [ ] **Day 515 (2027-11-25, 周四)** · 主题: Linux 内核网络 + - 🔧 动手: 20 道题 (150min) + - 🐧 内核对照: 补充 20 道 "内核 vs DPDK 对照题"(skb/mbuf、napi/PMD、tc/rte_flow、netlink/sairedis)(60min) + - 📤 产出: `career/qa-kernel.md`(含 20 内核题 + 20 对照题) + +- [ ] **Day 516 (2027-11-26, 周五)** · 主题: 操作系统/网络八股 + - 🔧 动手: 30 道经典题 (180min) + - 📤 产出: `career/qa-classic.md` + +- [ ] **Day 517 (2027-11-27, 周六)** · 主题: 大块实验 — 集中复习 + - 🔧 动手: 复习上述全部 (480min) + - 📤 产出: `career/review-1.md` + +- [ ] **Day 518 (2027-11-28, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P6/W74-summary.md` + +--- + +### Week 75 (Day 519-525) · 面试题库 (2) + +- [ ] **Day 519 (2027-11-29, 周一)** · 主题: 项目深度题 (libmtcp) + - 🔧 动手: 20 道项目深挖题 + 答案 (180min) + - 📤 产出: `career/qa-project-libmtcp.md` + +- [ ] **Day 520 (2027-11-30, 周二)** · 主题: 项目深度题 (sai-dpdk-mini) + - 🔧 动手: 20 道 (180min) + - 📤 产出: `career/qa-project-sai.md` + +- [ ] **Day 521 (2027-12-01, 周三)** · 主题: 项目深度题 (lb / ddos) + - 🔧 动手: 20 道 (180min) + - 📤 产出: `career/qa-project-lb-ddos.md` + +- [ ] **Day 522 (2027-12-02, 周四)** · 主题: DPU 架构题 + - 🔧 动手: 20 道 (180min) + - 📤 产出: `career/qa-dpu.md` + +- [ ] **Day 523 (2027-12-03, 周五)** · 主题: 行为面试题 + - 🔧 动手: STAR 模板 + 20 个故事 (180min) + - 📤 产出: `career/qa-behavior.md` + +- [ ] **Day 524 (2027-12-04, 周六)** · 主题: 大块实验 — 集中复习 + - 🔧 动手: 200 题全复习 (480min) + - 📤 产出: `career/review-2.md` + +- [ ] **Day 525 (2027-12-05, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P6/W75-summary.md` + +--- + +### Week 76 (Day 526-532) · 系统设计 + 模拟面试 + +- [ ] **Day 526 (2027-12-06, 周一)** · 主题: 系统设计题库 + - 🔧 动手: 收集 10 道经典网络系统设计 (180min) + - 📤 产出: `career/sysdesign-list.md` + +- [ ] **Day 527 (2027-12-07, 周二)** · 主题: 设计 (1) — 全球 CDN + - 🔧 动手: 写设计方案 (180min) + - 📤 产出: `career/sd-cdn.md` + +- [ ] **Day 528 (2027-12-08, 周三)** · 主题: 设计 (2) — DDoS 清洗中心 + - 🔧 动手: 写方案 (180min) + - 📤 产出: `career/sd-ddos.md` + +- [ ] **Day 529 (2027-12-09, 周四)** · 主题: 设计 (3) — 智能网卡卸载 + - 🔧 动手: 写方案 (180min) + - 📤 产出: `career/sd-smartnic.md` + +- [ ] **Day 530 (2027-12-10, 周五)** · 主题: 模拟面试 (技术面) + - 🔧 动手: 找朋友模拟 (180min) + - 📤 产出: `career/mock-tech.md` + +- [ ] **Day 531 (2027-12-11, 周六)** · 主题: 大块实验 — 模拟面试 (综合) + - 🔧 动手: 完整 1.5h 模拟 (240min) + - 📤 产出: `career/mock-full.md` + +- [ ] **Day 532 (2027-12-12, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P6/W76-summary.md` + +--- + +### Week 77 (Day 533-539) · 投递 + 一面/二面 + +- [ ] **Day 533 (2027-12-13, 周一)** · 主题: 投递目标公司 (1) + - 🔧 动手: 内推阿里云/腾讯云/字节 (120min) + - 📤 产出: `career/applications.md` + +- [ ] **Day 534 (2027-12-14, 周二)** · 主题: 投递目标公司 (2) + - 🔧 动手: 投 DPU 厂商 (120min) + - 📤 产出: 更新 applications + +- [ ] **Day 535 (2027-12-15, 周三)** · 主题: 跟进 + 一面 + - 🔧 动手: 处理一面(实际情况调整)(150min) + - 📤 产出: `career/interview-log.md` + +- [ ] **Day 536 (2027-12-16, 周四)** · 主题: 一面 → 二面 + - 🔧 动手: 复盘 + 准备二面 (150min) + - 📤 产出: 更新 log + +- [ ] **Day 537 (2027-12-17, 周五)** · 主题: 二面 / HR 面 + - 🔧 动手: 应对 + 复盘 (150min) + - 📤 产出: 更新 log + +- [ ] **Day 538 (2027-12-18, 周六)** · 主题: 大块实验 — 应对 onsite + - 🔧 动手: 集中处理面试 (480min) + - 📤 产出: 更新 log + +- [ ] **Day 539 (2027-12-19, 周日)** · ☕ 复盘 + - 📤 产出: `notes/P6/W77-summary.md` + +--- + +### Week 78 (Day 540-546) · 收口 + 决策 + +- [ ] **Day 540 (2027-12-20, 周一)** · 主题: offer 比较 / 晋升答辩 + - 🔧 动手: 写决策表 (150min) + - 📤 产出: `career/decision.md` + +- [ ] **Day 541 (2027-12-21, 周二)** · 主题: 谈薪 / 答辩材料 + - 🔧 动手: 准备 (150min) + - 📤 产出: `career/negotiation.md` + +- [ ] **Day 542 (2027-12-22, 周三)** · 主题: 18 个月总复盘 + - 🔧 动手: 写一万字总复盘 (240min) + - 📤 产出: `final/18months-retro.md` + +- [ ] **Day 543 (2027-12-23, 周四)** · 主题: 写下一个 18 个月 v4 草稿 + - 🔧 动手: 启动 v4 规划草稿 (180min) + - 📤 产出: `docs/superpowers/specs/2027-12-23-roadmap-v4-draft.md` + +- [ ] **Day 544 (2027-12-24, 周五)** · 主题: 整理学习仓库 + - 🔧 动手: 把 18 个月所有产出归档 (180min) + - 📤 产出: 归档 README + +- [ ] **Day 545 (2027-12-25, 周六)** · 主题: 大块实验 — 公开总结贴 + - 🔧 动手: 发知乎/B站长文 "18 个月成为 DPDK/DPU 工程师" (360min) + - 📤 产出: 发布链接 + +- [ ] **Day 546 (2027-12-27, 周日)** · 🎯 整体收官答辩 + 庆祝 + - 📤 产出: `final/closing.md`(结束语 + 下一步) + +--- + +## 附录 A · 项目仓库结构建议 + +推荐根目录布局(建议放在 `~/projects/dpdk-roadmap-v3/`): + +``` +dpdk-roadmap-v3/ +├── notes/ # 学习笔记 +│ ├── P1/ ... P6/ +├── code/ # 代码项目 +│ ├── libmtcp-v3/ +│ ├── sai-dpdk-mini/ +│ ├── dpdk-lb/ +│ ├── ddos-clean/ +│ ├── pcie-driver-demo/ +│ └── ... +├── perf-data/ # 性能数据 +├── flamegraph/ # 火焰图 +├── docs/ # 设计文档 +├── blog/ # 博客草稿 +├── career/ # 简历、面试材料 +├── slides/ # 演讲稿 +├── monthly/ # 月度复盘 +├── final/ # 阶段最终产出 +└── docs/superpowers/specs/2026-06-29-dpdk-roadmap-v3-design.md # 本规划文档 +``` + +--- + +## 附录 B · 性能基线参考 + +| 场景 | 硬件 | 目标 PPS | 目标带宽 | +|---|---|---|---| +| 单核 echo (X710 10G) | 1 core | 14.88 Mpps (64B) | 10 Gbps line rate | +| 多核 echo (CX5 100G) | 8 cores | ~100 Mpps (64B) | ~100 Gbps | +| OVS-DPDK L2 fwd | 4 cores | 20 Mpps | 80% line rate | +| dpdk-lb (4-tuple hash) | 4 cores | 5 Mpps | 20 Gbps | +| ddos-clean SYN cookie | 8 cores | 5 Mpps SYN | / | +| Suricata IDS | 8 cores | / | 10 Gbps | + +--- + +## 附录 C · 周/月复盘模板 + +### 周复盘 (`notes/Pn/Wmm-summary.md`) + +```markdown +# Week NN 复盘 (Day xxx-yyy) + +## 完成情况 +- [x] Day xxx: ... +- [ ] Day yyy: ...(未完成原因) + +## 学到的 3 件事 +1. +2. +3. + +## 卡点 / 疑惑 +- + +## 下周计划微调 +- +``` + +### 月复盘 (`monthly/YYYY-MM.md`) + +```markdown +# 月度复盘 YYYY-MM + +## 本月目标 vs 实际 +| 目标 | 完成 | 备注 | + +## 关键产出 +- 代码: +- 笔记: +- 数据: + +## 下月调整 +- 节奏: +- 内容: +- 出口: + +## 自我评分 (1-5) +- 学习深度: +- 动手量: +- 输出量: +- 心态: +``` + +--- + +## 收口语 + +> **18 个月很长,但日子是一天一天过的。** +> 不用每一天都完美执行,但每天都必须有一个**可见的产出物**。 +> 当你回头看 Day 1 到 Day 546 的勾选状态时,你的实力会比任何一份简历都更有说服力。 + +— 起算日 2026-06-29,目标交付日 2027-12-27。 +