QAIC 驱动程序

QAIC 驱动程序是 AIC100 系列 AI 加速器产品的内核模式驱动程序 (KMD)。

中断

IRQ 风暴缓解

虽然 AIC100 DMA 桥接硬件实现了 IRQ 风暴缓解机制,但仍有可能发生 IRQ 风暴。如果工作负载特别快,并且主机响应迅速,则可能会发生风暴。如果主机能够以设备向其中插入元素的速度一样快地清空响应 FIFO,那么设备将频繁地将响应 FIFO 从空转换为非空,并以相当于工作负载处理输入的能力的速度生成 MSI。已知 lprnet(车牌读取器网络)工作负载会触发此条件,并且每秒可生成超过 10 万个 MSI。据观察,大多数系统无法长期容忍这种情况,并且会因某种形式的看门狗而崩溃,这是因为中断控制器中断主机 CPU 的开销造成的。

为了缓解此问题,QAIC 驱动程序实现了特定的 IRQ 处理。当 QAIC 收到 IRQ 时,它会禁用该线路。这会阻止中断控制器中断 CPU。然后 AIC 会清空 FIFO。一旦 FIFO 被清空,QAIC 会实现“最后机会”轮询算法,其中 QAIC 将休眠一段时间,以查看工作负载是否会生成更多活动。在此期间,IRQ 线路保持禁用状态。如果没有检测到活动,QAIC 将退出轮询模式并重新启用 IRQ 线路。

QAIC 中的这种缓解非常有效。生成每秒 10 万个 IRQ(根据 /proc/interrupts)的相同 lprnet 用例在 5 分钟内减少到大约 64 个 IRQ,同时保持主机系统稳定,并具有相同的工作负载吞吐量性能(在运行到运行的噪声变化范围内)。

单 MSI 模式

并非所有系统都很好地支持 MultiMSI;虚拟化系统更是如此(大约在 2023 年)。在管理程序屏蔽 PCIe MSI 功能结构到 vIOMMU 的大量内存需求(支持 MultiMSI 所需)之间,在需要时能够回退到单个 MSI 是很有用的。

为了支持此回退,我们允许仅分配一个 MSI 的情况,并在 MHI 和 DBC 之间共享该 MSI。当仅配置了一个 MSI 时,设备会检测到,并将 DBC 的中断定向到通常用于 MHI 的中断。不幸的是,这意味着每个 DBC 和 MHI 的中断处理程序都会在每次中断到达时唤醒;但是,仅当检测到要完成的工作时,才会启动 DBC 线程 IRQ 处理程序(MHI 将始终启动其线程处理程序)。

如果将 DBC 配置为强制 MSI 中断,则可能会规避上述软件 IRQ 风暴缓解。由于 MSI 是共享的,因此永远不会禁用它,从而允许 FIFO 中的每个新条目触发新的中断。

神经网络控制 (NNC) 协议

NNC 的实现分为 KMD (QAIC) 和 UMD。一般来说,QAIC 了解如何编码/解码 NNC 线协议,以及需要内核空间知识来处理的协议元素(例如,将主机内存映射到设备 IOVA)。QAIC 了解消息的结构和所有事务。QAIC 不了解命令(直通事务的有效负载)。

QAIC 处理并强制执行所需的小端字节序和 64 位对齐,在其能力范围内。由于 QAIC 不知道直通事务的内容,因此它依赖 UMD 来满足要求。

终止事务对 QAIC 特别有用。QAIC 不知道加载到设备上的资源,因为大部分活动都发生在 NNC 命令中。因此,QAIC 没有办法回滚用户空间活动。为了确保在进程崩溃或出现错误的情况下完全释放用户空间客户端的资源,QAIC 使用终止命令来通知 QSM 用户何时离开,并且可以释放资源。

QSM 可以报告它支持的 NNC 协议的版本号。这是以主编号和次编号的形式表示的。

主编号更新表示对 NNC 协议的更改,这些更改会影响消息格式或事务(会影响 QAIC)。

次编号更新表示对 NNC 协议的更改,这些更改会影响命令(不会影响 QAIC)。

uAPI

QAIC 为每个物理 PCIe 设备创建一个 accel 设备。只要 Linux 知道 PCIe 设备,此 accel 设备就存在。

PCIe 设备可能并不总处于接受用户空间请求的状态。当设备可以接受请求 (ONLINE) 以及由于重置或其他状态转换而不再接受请求 (OFFLINE) 时,QAIC 将触发 KOBJ_ONLINE/OFFLINE uevent 以进行通告。

QAIC 定义了许多驱动程序特定的 IOCTL 作为用户空间 API 的一部分。

DRM_IOCTL_QAIC_MANAGE

此 IOCTL 允许用户空间向 QSM 发送 NNC 请求。该调用将阻塞,直到收到响应或请求超时。

DRM_IOCTL_QAIC_CREATE_BO

此 IOCTL 允许用户空间分配一个缓冲区对象 (BO),该对象可以发送或接收来自工作负载的数据。该调用将返回一个 GEM 句柄,该句柄表示分配的缓冲区。在切片之前,BO 不可用(请参阅 DRM_IOCTL_QAIC_ATTACH_SLICE_BO)。

DRM_IOCTL_QAIC_MMAP_BO

此 IOCTL 允许用户空间准备一个分配的 BO 以 mmap 到用户空间进程中。

DRM_IOCTL_QAIC_ATTACH_SLICE_BO

此 IOCTL 允许用户空间对 BO 进行切片,以准备将 BO 发送到设备。切片是描述 BO 的哪些部分发送到工作负载的哪个位置的操作。这需要一组用于 DMA 桥接的 DMA 传输,因此将 BO 锁定到特定的 DBC。

DRM_IOCTL_QAIC_EXECUTE_BO

此 IOCTL 允许用户空间向设备提交一组切片的 BO。该调用是非阻塞的。成功仅表示 BO 已排队到设备,但不保证它们已被执行。

DRM_IOCTL_QAIC_PARTIAL_EXECUTE_BO

此 IOCTL 的操作方式类似于 DRM_IOCTL_QAIC_EXECUTE_BO,但它允许用户空间缩小发送到设备的 BO 以进行此特定调用。如果一个 BO 通常有 N 个输入,但只有这些输入的子集可用,则此 IOCTL 允许用户空间指示只有 BO 的前 M 个字节应发送到设备,以最大程度地减少数据传输开销。此 IOCTL 会动态地重新计算切片,因此在将 BO 排队到设备之前会产生一些处理开销。

DRM_IOCTL_QAIC_WAIT_BO

此 IOCTL 允许用户空间确定设备何时处理特定的 BO。该调用将阻塞,直到 BO 被处理并且可以重新排队到设备,或者发生超时。

DRM_IOCTL_QAIC_PERF_STATS_BO

此 IOCTL 允许用户空间收集 BO 最近一次执行的性能统计信息。这允许用户空间构建 BO 处理的端到端时间线,以进行性能分析。

DRM_IOCTL_QAIC_DETACH_SLICE_BO

此 IOCTL 允许用户空间从最初由对 DRM_IOCTL_QAIC_ATTACH_SLICE_BO 的调用提供的 BO 中删除切片信息。这是 DRM_IOCTL_QAIC_ATTACH_SLICE_BO 的反向操作。必须先使 BO 处于空闲状态,才能调用 DRM_IOCTL_QAIC_DETACH_SLICE_BO。成功的分离切片操作后,BO 可以通过对 DRM_IOCTL_QAIC_ATTACH_SLICE_BO 的新调用附加新的切片信息。分离切片后,在进行新的附加切片操作之前,无法执行 BO。组合附加切片和分离切片调用允许用户空间将 BO 用于多个工作负载。

用户空间客户端隔离

AIC100 支持多个客户端。单个客户端可以使用多个 DBC,并且多个客户端可以各自使用一个或多个 DBC。工作负载可能包含敏感信息,因此只应允许拥有工作负载的客户端与 DBC 交互。

客户端由与其 open() 关联的实例标识。客户端只能使用他们分配的内存和分配给他们工作负载的 DBC。访问分配给其他客户端的资源的尝试将被拒绝。

模块参数

QAIC 支持以下模块参数

datapath_polling(布尔值)

配置 QAIC 使用轮询线程来处理数据路径事件,而不是依赖于设备中断。对于多 MSI 中断存在问题的平台很有用。必须在 QAIC 驱动程序初始化时设置。默认为 0(关闭)。

mhi_timeout_ms(无符号整数)

以毫秒 (ms) 为单位设置 MHI 操作的超时值。必须在驱动程序检测到设备时设置。默认为 2000(2 秒)。

control_resp_timeout_s(无符号整数)

以秒 (s) 为单位设置 QSM 对 NNC 消息的响应超时值。必须在驱动程序向 QSM 发送请求时设置。默认为 60(一分钟)。

wait_exec_default_timeout_ms(无符号整数)

以毫秒 (ms) 为单位设置 wait_exec ioctl 的默认超时。必须在 waic_exec ioctl 调用之前设置。在 ioctl 调用中指定的值将覆盖该调用的值。默认为 5000(5 秒)。

datapath_poll_interval_us(无符号整数)

当数据路径轮询处于活动状态时,以微秒 (us) 为单位设置轮询间隔。在下一个轮询间隔生效。默认为 100 (100 us)。

timesync_delay_ms(无符号整数)

以毫秒 (ms) 为单位设置两次连续 timesync 操作之间的时间间隔。默认为 1000 (1000 ms)。