结论:tinygrad 的 PCI 接口把「本地发现与独占」「内存分配与映射」和「远程 RPC 调试」三件事放在同一模块里,普通插件很容易只看到最后一句 alloc(),却忽略它前面的宿主状态改动。两条内存路径的所有权并不对称:VRAM 路径走 MemoryManager.valloc(),释放时有 vfree();而 SYS 路径单独持有 VA、系统内存和 GPU 页表,free() 只做本地 munmap(),没有显式归还 VA 或拆页表。远程服务 serve.py 监听 0.0.0.0 且在协议处理前没有认证,属于开发工具。以下为固定归档的静态源码追踪,未解绑驱动、未映射 BAR、未启动远程服务、未访问真实 PCI 设备。
本地调用链:构造对象即改变宿主 PCI 状态
_System.pci_scan_bus() 按 vendor/device 掩码和可选 base class 筛选设备;list_devices() 在设置 REMOTE 时改走 TCP 远程扫描并缓存结果;pci_probe_device() 应用可见设备过滤后构造设备。
真正需要警惕的是 PCIDevice.__init__():它取得文件锁、解绑现有内核驱动、移除同 slot 的 sibling functions、选择 VFIO 或 sysfs 启用,并打开 PCI config。也就是说,构造对象本身就会改变宿主机 PCI 状态,研究时不应把它当成只读操作。PCIIfaceBase.__init__() 随后探测设备、保留 VA 区间、尝试 resize BAR 并构造具体实现。
| 步骤 | 负责函数 | 副作用 / 边界 |
|---|---|---|
| 扫描与筛选 | pci_scan_bus()、pci_probe_device() |
按 vendor/device 掩码与可见设备过滤。 |
| 远程分流 | list_devices() |
设置 REMOTE 时改走 TCP 扫描并缓存。 |
| 独占与启用 | PCIDevice.__init__() |
取文件锁、解绑驱动、移除 sibling function、选 VFIO/sysfs。 |
| VA 与 BAR | PCIIfaceBase.__init__() |
保留 VA 区间、尝试 resize BAR;resize 异常被吞掉并继续。 |
alloc 与 map:所有权不对称
alloc() 依据 host/cpu_access/BAR 选择锁页系统内存或 VRAM。VRAM 路径调用 MemoryManager.valloc();SYS 路径则取得 VA、分配 sysmem 并写 GPU 页表。map() 允许同 host 的 CPU/PYTHON/NPY 缓冲或同 peer group 的 PCI GPU 缓冲进入 GPU VA;host 路径检查页对齐与 VA 范围并锁页,P2P 路径拒绝小 BAR 与跨 peer group 映射,p2p_paddrs() 用目标 BAR 物理基址形成系统地址空间映射。
释放时的不对称是重点。PCIIfaceBase.free() 对 PHYS 路径调用 vfree()、对本地 CPU 映射调用 munmap(),但没有对 SYS 分配调用 unmap_range() 或 va_allocator.free()。同样,map() 对 host 缓冲调用 System.lock_memory(),对应的 unmap() 只调用页表 unmap_range(),未见 munlock()。
| 入口 | 主要职责 | 需要核对的结果 |
|---|---|---|
alloc_sysmem()、system_paddrs() |
MAP_LOCKED 匿名映射并从 pagemap 取物理页 |
依赖权限、页固定和完整物理页列表。 |
map_bar()、bar_info() |
resource 文件长度/地址与 BAR mmap | off/size、BAR 边界与 MADV_DONTFORK 是内核契约。 |
alloc() |
输出 BufferStorage 加 PCIAllocationMeta |
PHYS 有 valloc() 所有权;SYS 单独拥有 VA、映射和页表。 |
free() |
PHYS 走 vfree();本地 CPU 映射 munmap() |
SYS 路径未显式归还 VA 或拆页表。 |
map()、unmap() |
外部缓冲映射与页表拆除 | unmap() 仅拆 GPU PTE,host mlock() 解锁责任不明确。 |
此外,USBPCIDevice 通过 ASM24 桥配置 PCI bus/BAR,并用 512 KiB SRAM Bump 分配器提供 DMA 暂存。这属于特定 USB 桥路径,不应混作通用主板 PCI 内存。另有几个静态可见的失败边界:PCIDevice.reset() 使用 shell 命令但不检查退出状态;BAR resize 异常在 __init__() 被吞掉并继续初始化——因此不能把构造成功等同于 reset/resize 已成功。
远程 RPC:服务端没有认证
RemotePCIDevice 经 TCP 的 REMOTE_REQ/REMOTE_RESP 与服务端通信。RemoteMMIOInterface 的读取使用往返 RPC,写入是 posted 命令,同一 TCP 流上的后续请求用于观察此前的写入。服务端 handle() 暴露设备探测、config/BAR 读写、reset、系统内存映射、直接内存读写,以及 LOAD_PROG/EXEC_PROG。
信任边界很高:server.bind() 监听 0.0.0.0,在协议处理前没有认证或授权;SYSMEM_READ/WRITE 直接以请求提供的地址构造进程内存视图;LOAD_PROG/EXEC_PROG 可装载并执行客户端给出的程序。只要服务可被访问,客户端就获得远超「推理插件数据搬运」的能力。源码本身是开发工具,不应在车载生产网络上裸露运行,应物理或网络隔离,并增加认证、命令白名单与最小权限。这一判断是接口能力分析,不表示当前车辆上已启动该服务。
静态可见的失败边界与验收
除了 alloc/free 的 VA 与锁页不对称,还有两处条件性风险。其一,alloc() 的 SYS 路径先取 VA,再申请系统内存和建立页表,失败中途没有本层回滚,属条件性资源累积风险,需区分底层设备复位或进程退出清理与运行中的热重配。其二,远程 scan()/_connect() 缓存 socket,而 serve.py 的已发现设备、打开设备、BAR 映射和程序列表是模块全局状态,连接关闭不会在同一进程中显式清掉,热重连与多客户端所有权需要另测。
面向自制硬件的验收应先在无设备 mock 中覆盖 VA/SYS 分配失败回滚、host 锁页与释放、BAR 边界和 P2P peer group;再在专用台架验证驱动解绑与恢复、VFIO/IOMMU、DMA 地址、重复启动和断电回收。远程调试服务只能放在隔离开发环境,绝不能成为「万物即插件」的通用车载执行接口;任何 PCI/计算插件都不得直接获得 sendcan 或 panda safety 配置权限。
常见问题
alloc 之后 free 就能把资源全部还回去吗?
不一定对称。PHYS/VRAM 路径在 free() 里走 vfree(),本地 CPU 映射走 munmap();但 SYS 路径没有显式调用 unmap_range() 或 va_allocator.free(),host 锁页的解锁责任也未在这一对函数里明确。
构造 PCIDevice 对象是只读操作吗?
不是。PCIDevice.__init__() 会取得文件锁、解绑现有内核驱动、移除同 slot 的 sibling functions,并选择 VFIO 或 sysfs 启用,因而会改变宿主机 PCI 状态。
远程 serve.py 暴露了什么能力?
它暴露设备探测、config/BAR 读写、reset、系统内存映射、直接内存读写,以及 LOAD_PROG/EXEC_PROG;server.bind() 监听 0.0.0.0 且协议处理前没有认证,SYSMEM_READ/WRITE 直接按请求地址构造进程内存视图。
BAR resize 失败会影响后续初始化吗?
会存在被忽略的情况。BAR resize 的异常在 PCIIfaceBase.__init__() 被吞掉并继续初始化;PCIDevice.reset() 也使用 shell 命令但不检查退出状态,因此不能把构造成功等同于 reset/resize 已成功。
延伸阅读
- tinygrad GPU 内存分配器与页表所有权 —— 承接 VA、页表与分配释放的所有权关系。
- tinygrad AMD 设备 IP 启动与局部复位生命周期 —— 对照设备初始化与复位失败的状态处理。
- 开发、固件与回放工具的信任边界 —— 同类开发工具与生产执行域必须隔离的判断。
- 远程 RPC 与 WebRTC 的边界 —— 对照远端交互中的认证与命令白名单要求。
- 净室插件架构实践 —— 承接计算插件不得获得 CAN 与安全配置权限的设计。
参考资料
- 项目 02 ——
openpilot-research/TINYGRAD_PCI_LOCAL_REMOTE_MEMORY_TRUST_LIFECYCLE_DEEP_DIVE.md(取用了本地调用链、alloc/map/free 所有权不对称、USB 桥路径、远程 RPC 服务端无认证与静态失败边界)。
说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。
更新记录
首次发布 2026-10-11 最近核对:2026-10-11
发表回复