3、多主机拓扑概述:多Root Complex拓扑、多主机通信模型、NTB原理
好,咱们进入第三章。多主机拓扑,这名字听着挺唬人,对吧?其实说白了,就是让多个CPU(也就是多个Root Complex)能通过同一个PCIe Switch互相通信。我最早接触这个,是在一个存储系统的项目里,两个服务器要共享同一块NVMe盘。那时候我就在想,这玩意儿到底怎么搞?
3.1 多Root Complex拓扑
传统的PCIe系统,一个RC管一片。但多主机场景下,一个Switch要连好几个RC。这就有意思了。
常见的拓扑有两种:
- 对称拓扑:每个RC地位平等,都能访问共享资源。我见过不少高性能计算集群这么干。
- 非对称拓扑:一个RC是主,其他是从。主节点控制全局,从节点干活。嗯,嵌入式系统里挺常见。
你想想看,如果两个RC同时往一个Endpoint写数据,会怎样?冲突!所以,PCIe Switch内部得做地址隔离和路由控制。我个人习惯,在设计初期就把地址映射表画清楚,不然后期调试能让你怀疑人生。
核心要点:多RC拓扑的关键,在于如何划分和管理每个RC的“势力范围”。
3.2 多主机通信模型
多主机之间怎么通信?总不能靠眼神吧。常见的模型有几种:
- 共享内存模型:所有主机都能访问同一块物理内存。简单粗暴,但需要锁机制。我在一个项目中用过,结果死锁了三次才调通。
- 消息传递模型:主机之间通过发送消息来通信。类似网络编程里的Socket,但延迟更低。
- DMA直接传输:一个主机通过DMA直接把数据写到另一个主机的内存里。效率最高,但实现最复杂。
这里有个坑:共享内存模型虽然快,但缓存一致性是个大问题。我曾经因为没处理好Cache Coherence,导致两个主机读到的数据不一样,查了整整两天。
我的建议:如果对延迟要求不高,优先用消息传递模型。调试起来省心很多。
3.3 NTB(Non-Transparent Bridge)原理
NTB,非透明桥。这名字起得挺玄乎,其实原理不复杂。
普通的PCIe桥(Transparent Bridge)是透明的,RC能看到桥后面的所有设备。但NTB不一样,它会在两个RC之间建立一个“秘密通道”。
NTB的核心机制:
- 地址转换:NTB会把一个RC的地址空间,映射到另一个RC的地址空间里。说白了,就是“你的内存,我看得见,但你以为我看不见”。
- 门铃机制(Doorbell):一个RC写一个寄存器,另一个RC就能收到中断。这就像按门铃,通知对方“我有数据给你”。
- Scratchpad寄存器:用来交换一些控制信息,比如数据准备好了没。
我记得第一次调NTB时,门铃中断死活不触发。后来发现是中断路由配置错了。嗯,这里要注意,NTB的中断路由和普通设备不一样,得单独配。
避坑指南:我曾经因为NTB的地址转换窗口没对齐,导致数据写到一半就崩了。记住,窗口的基地址和大小必须是4KB对齐的。
下面这张图,是我画的一个典型NTB通信流程。你看看就明白了。
流程很简单:RC1通过NTB写一个门铃,NTB收到后,通过DMA把数据从RC1的内存搬到共享内存,然后通知RC2来读。RC2看到数据,就知道RC1在叫它了。
最后,给个代码片段,展示NTB的地址映射配置。这是实际项目中用过的,你参考下。
// NTB地址映射配置示例
// 将RC1的0x1000_0000映射到RC2的0x2000_0000
ntb_config_t ntb_cfg;
ntb_cfg.rc1_base_addr = 0x10000000; // RC1侧基地址
ntb_cfg.rc2_base_addr = 0x20000000; // RC2侧基地址
ntb_cfg.window_size = 0x00100000; // 1MB窗口
ntb_cfg.doorbell_en = 1; // 使能门铃中断
// 配置NTB寄存器
ntb_set_window(0, &ntb_cfg);
ntb_enable_doorbell(0, DOORBELL_VEC_0);
嗯,多主机拓扑和NTB,说白了就是让多个CPU能“和平共处”地共享PCIe资源。我做了这么多年,觉得最难的不是技术本身,而是调试时的耐心。你想想看,两个RC同时跑,出问题了你都不知道是哪个RC的锅。
好了,这一章就到这儿。记住,多主机设计,地址映射和中断路由是命门,别搞错了。
公众号:蓝海资料掘金营,微信deep3321