QAIC 驱动程序¶
QAIC 驱动程序是 AIC100 系列 AI 加速器产品的内核模式驱动程序 (KMD)。
中断¶
中断风暴缓解¶
尽管 AIC100 DMA 桥硬件实现了中断风暴缓解机制,但中断风暴仍然可能发生。如果工作负载特别快,并且主机响应迅速,就可能发生中断风暴。如果主机能够像设备插入元素一样快速地从响应 FIFO 中排出元素,那么设备将频繁地将响应 FIFO 从空转换为非空,并以与工作负载处理输入能力相当的速度生成 MSI。已知 lprnet(车牌识别网络)工作负载会触发此情况,每秒可生成超过 10 万个 MSI。已观察到大多数系统无法长时间容忍这种情况,并且会因中断控制器中断主机 CPU 的开销而导致某种形式的看门狗崩溃。
为缓解此问题,QAIC 驱动程序实现了特定的中断处理。当 QAIC 接收到中断时,它会禁用该线路。这可以防止中断控制器中断 CPU。然后 AIC 排空 FIFO。一旦 FIFO 排空,QAIC 会实现一个“最后机会”轮询算法,其中 QAIC 会休眠一段时间,以查看工作负载是否会产生更多活动。在此期间,中断线路保持禁用状态。如果未检测到活动,QAIC 会退出轮询模式并重新启用中断线路。
QAIC 中的此缓解措施非常有效。同样的 lprnet 用例,每秒生成 10 万个中断(根据 /proc/interrupts),但在 5 分钟内可减少到大约 64 个中断,同时保持主机系统稳定,并具有相同的工作负载吞吐量性能(在运行间的噪声变化范围内)。
单 MSI 模式¶
MultiMSI 在所有系统上都未得到良好支持;虚拟化系统更是如此(约 2023 年)。鉴于虚拟机监控程序会掩盖 PCIe MSI 功能结构,以及 vIOMMU 对大内存的需求(支持 MultiMSI 所必需),因此在需要时能够回退到单个 MSI 是很有用的。
为了支持这种回退,我们允许只分配一个 MSI 的情况,并在 MHI 和 DBC 之间共享该 MSI。当设备检测到只配置了一个 MSI 时,它会将 DBC 的中断重定向到通常用于 MHI 的中断。不幸的是,这意味着每个 DBC 和 MHI 的中断处理程序都会为每个到达的中断唤醒;但是,DBC 线程化的中断处理程序只有在检测到有工作要做时才启动(MHI 总是会启动其线程化处理程序)。
如果 DBC 被配置为强制 MSI 中断,这可以规避上述软件中断风暴缓解措施。由于 MSI 是共享的,它永远不会被禁用,从而允许 FIFO 中的每个新条目触发一个新的中断。
神经网络控制 (NNC) 协议¶
NNC 的实现分为 KMD (QAIC) 和 UMD 两部分。通常,QAIC 了解如何编码/解码 NNC 有线协议,以及需要内核空间知识才能处理的协议元素(例如,将主机内存映射到设备 IOVA)。QAIC 了解消息结构以及所有事务。QAIC 不理解命令(直通事务的有效载荷)。
QAIC 在力所能及的范围内处理并强制执行所需的小端序和 64 位对齐。由于 QAIC 不知道直通事务的内容,因此它依赖 UMD 来满足要求。
终止事务对 QAIC 特别有用。QAIC 不知道加载到设备上的资源,因为大部分活动发生在 NNC 命令内部。因此,QAIC 无法回滚用户空间活动。为确保在进程崩溃或出现 bug 时用户空间客户端的资源得到完全释放,QAIC 使用终止命令通知 QSM 用户已退出,并且资源可以被释放。
QSM 可以报告其支持的 NNC 协议版本号。这以主版本号和次版本号的形式呈现。
主版本号更新表示 NNC 协议发生了更改,这会影响消息格式或事务(影响 QAIC)。
次版本号更新表示 NNC 协议发生了更改,这会影响命令(不影响 QAIC)。
uAPI¶
QAIC 为每个物理 PCIe 设备创建一个加速设备。只要 Linux 识别该 PCIe 设备,该加速设备就存在。
PCIe 设备可能并非总是处于接受用户空间请求的状态。QAIC 将触发 KOBJ_ONLINE/OFFLINE uevents,以通告设备何时可以接受请求 (ONLINE) 以及何时由于复位或其他状态转换而不再接受请求 (OFFLINE)。
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 允许用户空间从 BO 中移除最初由 DRM_IOCTL_QAIC_ATTACH_SLICE_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 使用轮询线程处理数据路径事件,而不是依赖设备中断。适用于 MultiMSI 损坏的平台。必须在 QAIC 驱动程序初始化时设置。默认值为 0(关闭)。
mhi_timeout_ms (无符号整型)
设置 MHI 操作的超时值,单位为毫秒 (ms)。必须在驱动程序检测到设备时设置。默认值为 2000(2 秒)。
control_resp_timeout_s (无符号整型)
设置 QSM 响应 NNC 消息的超时值,单位为秒 (s)。必须在驱动程序向 QSM 发送请求时设置。默认值为 60(一分钟)。
wait_exec_default_timeout_ms (无符号整型)
设置 wait_exec ioctl 的默认超时时间,单位为毫秒 (ms)。必须在调用 wait_exec ioctl 之前设置。ioctl 调用中指定的值将覆盖该次调用的此默认值。默认值为 5000(5 秒)。
datapath_poll_interval_us (无符号整型)
设置数据路径轮询激活时的轮询间隔,单位为微秒 (us)。在下一个轮询间隔生效。默认值为 100(100 微秒)。
timesync_delay_ms (无符号整型)
设置两次连续的时间同步操作之间的时间间隔,单位为毫秒 (ms)。默认值为 1000(1000 毫秒)。