
016、影像虚拟化与多虚拟机共享ISP资源高通与联发科平台的Hypervisor方案去年年底做的一个项目车规级座舱域控制器一颗高通SA8295P要同时跑QNX仪表和Android中控娱乐两边都要用摄像头。仪表侧要AVM全景拼接Android侧要DMS驾驶员监控两个系统各自为政谁都不肯让出ISP。硬件上只有两颗ISP但需求是四路摄像头同时出图而且QNX侧要求硬实时Android侧要跑AI算法。一开始我们想得简单把四路摄像头分成两组一组给QNX一组给Android各自独占一个ISP。结果方案评审时就被系统架构师怼了——万一QNX侧某路摄像头故障Android侧想接管那路摄像头做冗余怎么办ISP资源被虚拟机隔离死了没法动态调度。这就是影像虚拟化的核心痛点不是简单地把ISP分给谁而是要让多个虚拟机安全地、高效地共享同一份ISP硬件资源同时保证隔离性和实时性。高通平台的方案是基于Hypervisor的VirtIO-GPU扩展但影像这块高通有自己的私有协议。SA8295P的ISP是Spectra 380内部有多个ISP流水线pipeline每个pipeline可以独立配置。高通的思路是把ISP抽象成多个虚拟ISP实例每个虚拟机看到的是一个虚拟的ISP设备但实际上底层是共享物理ISP的。关键点在于——高通的Hypervisor基于Xen或KVM在设备树层面做了手脚每个虚拟机拿到的设备树节点里ISP的寄存器地址空间是经过映射的物理地址被重映射到虚拟地址而且中断号也做了隔离。这里踩过坑如果你直接修改设备树把整个ISP的寄存器空间都映射给某个虚拟机另一个虚拟机就完全看不到ISP了而且一旦某个虚拟机崩溃可能把整个ISP寄存器空间搞乱导致另一个虚拟机也挂掉。正确做法是只映射该虚拟机需要的那个pipeline对应的寄存器子块并且要启用IOMMU做DMA隔离。高通平台具体实现时我们用了他们的Camera Virtualization Framework这套框架在固件层就做了资源划分。每个虚拟机通过Hypervisor调用QHEEQualcomm Hypervisor Execution Environment的接口来申请ISP资源。QHEE里跑了一个Camera Resource Manager它维护着一张资源分配表记录哪个pipeline被哪个虚拟机占用以及当前的工作模式单摄、双摄、三摄。这里有个坑QHEE的资源管理器默认只支持固定分配不支持动态迁移。也就是说如果你想让Android侧在QNX侧某路摄像头故障时接管那路摄像头光靠QHEE是做不到的需要在Hypervisor层自己写一个代理驱动把QNX侧释放的pipeline重新分配给Android侧。我们当时是写了一个共享内存通道QNX侧释放资源时往共享内存写一个释放消息Android侧的代理驱动轮询这个共享内存发现有释放消息就去QHEE申请资源。别这样写——不要用轮询用中断通知否则实时性没法保证我们后来改成中断机制后资源切换时间从50ms降到了5ms。联发科平台的思路完全不同。MTK的Imagiq ISP是模块化设计但他们的Hypervisor方案更依赖TrustZone和虚拟化扩展。MTK的ISP虚拟化是通过一个叫做ISP Trusted Agent的组件实现的这个组件跑在TrustZone的安全世界里所有虚拟机对ISP的访问都要经过它。MTK的做法是把ISP的配置寄存器全部放到安全世界普通世界的虚拟机只能通过RPC远程过程调用来请求ISP服务。这种方案的优点是隔离性极好虚拟机根本碰不到ISP硬件所有操作都要经过安全世界的校验。但缺点也很明显——性能开销大每次配置ISP都要陷入安全世界来回切换上下文我们实测在MTK天玑9000上一次ISP配置的RPC调用要花掉80微秒而高通平台直接写寄存器只要10微秒。如果你做的是对延迟敏感的应用比如AR导航的摄像头预览这个开销可能不可接受。MTK平台还有个特殊问题——他们的ISP虚拟化不支持多路并发流的动态带宽分配。Imagiq的带宽管理器是静态配置的你在系统启动时就要告诉它每个虚拟机最多占用多少ISP带宽。我们当时在MTK平台上做双虚拟机共享ISP时QNX侧要跑4K60的AVM拼接Android侧要跑1080P30的DMS结果发现Imagiq的带宽管理器在某个特定配置下会死锁——两个虚拟机同时申请带宽时带宽管理器进入了等待状态导致两边都拿不到数据。这个问题的根源是MTK的带宽管理器没有做优先级抢占低优先级的请求会阻塞高优先级的请求。我们最后是通过在安全世界里加了一个带宽仲裁逻辑强制高优先级虚拟机QNX的带宽请求先被处理才解决了这个问题。这个坑在MTK的文档里完全没有提到是我们自己调试了三天才发现的。瑞芯微平台相对简单他们的ISP虚拟化方案还在早期阶段目前主要是通过V4L2的media controller框架做资源隔离。瑞芯微的RK3588有独立的ISP单元但他们的Hypervisor支持还不完善目前只能做到静态分配——启动时通过设备树把ISP的某个channel固定分配给某个虚拟机。这种方案的灵活性很差但胜在简单可靠适合对成本敏感的项目。如果你做的是工业视觉项目对实时性要求不高瑞芯微的方案够用。但如果你做的是车规级项目建议还是用高通或MTK的方案因为瑞芯微的ISP虚拟化目前不支持安全隔离虚拟机之间可以通过DMA直接访问对方的buffer这在车规级场景下是致命缺陷。回到高通和MTK的对比我个人的经验是高通平台适合做复杂的多虚拟机影像系统因为他们的Spectra ISP本身就有很强的虚拟化基因而且QHEE的资源管理框架比较成熟。但高通的方案有个隐含成本——你需要购买他们的Camera Virtualization Framework授权而且这套框架的调试工具非常难用我们当时为了调试一个pipeline分配问题不得不去解析QHEE的日志那个日志格式简直反人类。MTK平台的优势是隔离性更好适合对安全性要求极高的场景但性能开销和带宽管理的问题需要你自己去解决。如果你做的是量产项目我建议你在方案选型时就把虚拟化方案考虑进去不要先做裸机方案再回头加虚拟化——我们在这个项目上就吃了这个亏前期裸机调好的ISP参数在虚拟化环境下全部要重新调因为虚拟化层的调度延迟会影响ISP的时序。最后给个经验性建议做影像虚拟化项目一定要在项目启动时就确定好虚拟化方案并且让ISP调优工程师和Hypervisor工程师坐在一起办公。我们项目后期ISP调优工程师和Hypervisor工程师互相甩锅ISP调优说虚拟化层把时序搞乱了Hypervisor说ISP调优的参数不合理最后是架构师出面拉通才发现问题出在共享内存的缓存一致性上——两个虚拟机访问同一块共享内存时没有做缓存同步导致ISP读到的配置参数是过期的。这个问题在裸机环境下根本不会出现但在虚拟化环境下是必踩的坑。如果你不想踩这个坑记得在共享内存的访问路径上加上缓存屏障指令或者直接用uncached的内存属性。