1*93abd332SWang Yaxin.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2*93abd332SWang Yaxin.. include:: ../disclaimer-zh_CN.rst 3*93abd332SWang Yaxin 4*93abd332SWang Yaxin:Original: Documentation/networking/napi.rst 5*93abd332SWang Yaxin 6*93abd332SWang Yaxin:翻译: 7*93abd332SWang Yaxin 8*93abd332SWang Yaxin 王亚鑫 Yaxin Wang <wang.yaxin@zte.com.cn> 9*93abd332SWang Yaxin 10*93abd332SWang Yaxin==== 11*93abd332SWang YaxinNAPI 12*93abd332SWang Yaxin==== 13*93abd332SWang Yaxin 14*93abd332SWang YaxinNAPI 是 Linux 网络堆栈中使用的事件处理机制。NAPI 的名称现在不再代表任何特定含义 [#]_。 15*93abd332SWang Yaxin 16*93abd332SWang Yaxin在基本操作中,设备通过中断通知主机有新事件发生。主机随后调度 NAPI 实例来处理这些事件。 17*93abd332SWang Yaxin该设备也可以通过 NAPI 进行事件轮询,而无需先接收中断信号(:ref:`忙轮询<poll_zh_CN>`)。 18*93abd332SWang Yaxin 19*93abd332SWang YaxinNAPI 处理通常发生在软中断上下文中,但有一个选项,可以使用 :ref:`单独的内核线程<threaded_zh_CN>` 20*93abd332SWang Yaxin来进行 NAPI 处理。 21*93abd332SWang Yaxin 22*93abd332SWang Yaxin总的来说,NAPI 为驱动程序抽象了事件(数据包接收和发送)处理的上下文环境和配置情况。 23*93abd332SWang Yaxin 24*93abd332SWang Yaxin驱动程序API 25*93abd332SWang Yaxin=========== 26*93abd332SWang Yaxin 27*93abd332SWang YaxinNAPI 最重要的两个元素是 struct napi_struct 和关联的 poll 方法。struct napi_struct 28*93abd332SWang Yaxin持有 NAPI 实例的状态,而方法则是与驱动程序相关的事件处理器。该方法通常会释放已传输的发送 29*93abd332SWang Yaxin(Tx)数据包并处理新接收的数据包。 30*93abd332SWang Yaxin 31*93abd332SWang Yaxin.. _drv_ctrl_zh_CN: 32*93abd332SWang Yaxin 33*93abd332SWang Yaxin控制API 34*93abd332SWang Yaxin------- 35*93abd332SWang Yaxin 36*93abd332SWang Yaxinnetif_napi_add() 和 netif_napi_del() 用于向系统中添加/删除一个 NAPI 实例。实例会被 37*93abd332SWang Yaxin附加到作为参数传递的 netdevice上(并在 netdevice 注销时自动删除)。实例在添加时处于禁 38*93abd332SWang Yaxin用状态。 39*93abd332SWang Yaxin 40*93abd332SWang Yaxinnapi_enable() 和 napi_disable() 管理禁用状态。禁用的 NAPI 不会被调度,并且保证其 41*93abd332SWang Yaxinpoll 方法不会被调用。napi_disable() 会等待 NAPI 实例的所有权被释放。 42*93abd332SWang Yaxin 43*93abd332SWang Yaxin这些控制 API 并非幂等的。控制 API 调用在面对数据路径 API 的并发使用时是安全的,但控制 44*93abd332SWang YaxinAPI 调用顺序错误可能会导致系统崩溃、死锁或竞态条件。例如,连续多次调用 napi_disable() 45*93abd332SWang Yaxin会造成死锁。 46*93abd332SWang Yaxin 47*93abd332SWang Yaxin数据路径API 48*93abd332SWang Yaxin----------- 49*93abd332SWang Yaxin 50*93abd332SWang Yaxinnapi_schedule() 是调度 NAPI 轮询的基本方法。驱动程序应在其中断处理程序中调用此函数 51*93abd332SWang Yaxin(更多信息请参见 :ref:`drv_sched_zh_CN`)。成功的 napi_schedule() 调用将获得 NAPI 实例 52*93abd332SWang Yaxin的所有权。 53*93abd332SWang Yaxin 54*93abd332SWang Yaxin之后,在 NAPI 被调度后,驱动程序的 poll 方法将被调用以处理事件/数据包。该方法接受一个 55*93abd332SWang Yaxin``budget`` 参数 - 驱动程序可以处理任意数量的发送 (Tx) 数据包完成,但处理最多处理 56*93abd332SWang Yaxin``budget`` 个接收 (Rx) 数据包。处理接收数据包通常开销更大。 57*93abd332SWang Yaxin 58*93abd332SWang Yaxin换句话说,对于接收数据包的处理,``budget`` 参数限制了驱动程序在单次轮询中能够处理的数 59*93abd332SWang Yaxin据包数量。当 ``budget`` 为 0 时,像页面池或 XDP 这类专门用于接收的 API 根本无法使用。 60*93abd332SWang Yaxin无论 ``budget`` 的值是多少,skb 的发送处理都应该进行,但是如果 ``budget`` 参数为 0, 61*93abd332SWang Yaxin驱动程序就不能调用任何 XDP(或页面池)API。 62*93abd332SWang Yaxin 63*93abd332SWang Yaxin.. warning:: 64*93abd332SWang Yaxin 65*93abd332SWang Yaxin 如果内核仅尝试处理skb的发送完成情况,而不处理接收 (Rx) 或 XDP 数据包,那么 ``budget`` 66*93abd332SWang Yaxin 参数可能为 0。 67*93abd332SWang Yaxin 68*93abd332SWang Yaxin轮询方法会返回已完成的工作量。如果驱动程序仍有未完成的工作(例如,``budget`` 已用完), 69*93abd332SWang Yaxin轮询方法应精确返回 ``budget`` 的值。在这种情况下,NAPI 实例将再次被处理 / 轮询(无需 70*93abd332SWang Yaxin重新调度)。 71*93abd332SWang Yaxin 72*93abd332SWang Yaxin如果事件处理已完成(所有未处理的数据包都已处理完毕),轮询方法在返回之前应调用 napi_complete_done()。 73*93abd332SWang Yaxinnapi_complete_done() 会释放实例的所有权。 74*93abd332SWang Yaxin 75*93abd332SWang Yaxin.. warning:: 76*93abd332SWang Yaxin 77*93abd332SWang Yaxin 当出现既完成了所有事件处理,又恰好达到了 ``budget`` 数量的情况时,必须谨慎处理。因为没 78*93abd332SWang Yaxin 有办法将这种(很少出现的)情况报告给协议栈,所以驱动程序要么不调用 napi_complete_done() 79*93abd332SWang Yaxin 并等待再次被调用,要么返回 ``budget - 1``。 80*93abd332SWang Yaxin 81*93abd332SWang Yaxin 当 ``budget`` 为 0 时,napi_complete_done() 绝对不能被调用。 82*93abd332SWang Yaxin 83*93abd332SWang Yaxin调用序列 84*93abd332SWang Yaxin-------- 85*93abd332SWang Yaxin 86*93abd332SWang Yaxin驱动程序不应假定调用的顺序是固定不变的。即使驱动程序没有调度该实例,轮询方法也可能会被调用 87*93abd332SWang Yaxin(除非该实例处于禁用状态)。同样,即便 napi_schedule() 调用成功,也不能保证轮询方法一定 88*93abd332SWang Yaxin会被调用(例如,如果该实例被禁用)。 89*93abd332SWang Yaxin 90*93abd332SWang Yaxin正如在 :ref:`drv_ctrl_zh_CN` 部分所提到的,napi_disable() 以及后续对轮询方法的调用, 91*93abd332SWang Yaxin仅会等待该实例的所有权被释放,而不会等待轮询方法退出。这意味着,驱动程序在调用 napi_complete_done() 92*93abd332SWang Yaxin之后,应避免访问任何数据结构。 93*93abd332SWang Yaxin 94*93abd332SWang Yaxin.. _drv_sched_zh_CN: 95*93abd332SWang Yaxin 96*93abd332SWang Yaxin调度与IRQ屏蔽 97*93abd332SWang Yaxin------------- 98*93abd332SWang Yaxin 99*93abd332SWang Yaxin驱动程序应在调度 NAPI 实例后保持中断屏蔽 - 直到 NAPI 轮询完成,任何进一步的中断都是不必要的。 100*93abd332SWang Yaxin 101*93abd332SWang Yaxin显式屏蔽中断的驱动程序(而非设备自动屏蔽 IRQ)应使用 napi_schedule_prep() 和 102*93abd332SWang Yaxin__napi_schedule() 调用: 103*93abd332SWang Yaxin 104*93abd332SWang Yaxin.. code-block:: c 105*93abd332SWang Yaxin 106*93abd332SWang Yaxin if (napi_schedule_prep(&v->napi)) { 107*93abd332SWang Yaxin mydrv_mask_rxtx_irq(v->idx); 108*93abd332SWang Yaxin /* 在屏蔽后调度以避免竞争 */ 109*93abd332SWang Yaxin __napi_schedule(&v->napi); 110*93abd332SWang Yaxin } 111*93abd332SWang Yaxin 112*93abd332SWang YaxinIRQ 仅应在成功调用 napi_complete_done() 后取消屏蔽: 113*93abd332SWang Yaxin 114*93abd332SWang Yaxin.. code-block:: c 115*93abd332SWang Yaxin 116*93abd332SWang Yaxin if (budget && napi_complete_done(&v->napi, work_done)) { 117*93abd332SWang Yaxin mydrv_unmask_rxtx_irq(v->idx); 118*93abd332SWang Yaxin return min(work_done, budget - 1); 119*93abd332SWang Yaxin } 120*93abd332SWang Yaxin 121*93abd332SWang Yaxinnapi_schedule_irqoff() 是 napi_schedule() 的一个变体,它利用了在中断请求(IRQ)上下文 122*93abd332SWang Yaxin环境中调用所带来的特性(无需屏蔽中断)。如果中断请求(IRQ)是通过线程处理的(例如启用了 123*93abd332SWang Yaxin``PREEMPT_RT`` 时的情况),napi_schedule_irqoff() 会回退为使用 napi_schedule() 。 124*93abd332SWang Yaxin 125*93abd332SWang Yaxin实例到队列的映射 126*93abd332SWang Yaxin---------------- 127*93abd332SWang Yaxin 128*93abd332SWang Yaxin现代设备每个接口有多个 NAPI 实例(struct napi_struct)。关于实例如何映射到队列和中断没有 129*93abd332SWang Yaxin严格要求。NAPI 主要是事件处理/轮询抽象,没有用户可见的语义。也就是说,大多数网络设备最终以 130*93abd332SWang Yaxin非常相似的方式使用 NAPI。 131*93abd332SWang Yaxin 132*93abd332SWang YaxinNAPI 实例最常以 1:1:1 映射到中断和队列对(队列对是由一个接收队列和一个发送队列组成的一组 133*93abd332SWang Yaxin队列)。 134*93abd332SWang Yaxin 135*93abd332SWang Yaxin在不太常见的情况下,一个 NAPI 实例可能会用于处理多个队列,或者在单个内核上,接收(Rx)队列 136*93abd332SWang Yaxin和发送(Tx)队列可以由不同的 NAPI 实例来处理。不过,无论队列如何分配,通常 NAPI 实例和中断 137*93abd332SWang Yaxin之间仍然保持一一对应的关系。 138*93abd332SWang Yaxin 139*93abd332SWang Yaxin值得注意的是,ethtool API 使用了 “通道” 这一术语,每个通道可以是 ``rx`` (接收)、``tx`` 140*93abd332SWang Yaxin(发送)或 ``combined`` (组合)类型。目前尚不清楚一个通道具体由什么构成,建议的理解方式是 141*93abd332SWang Yaxin将一个通道视为一个为特定类型队列提供服务的 IRQ(中断请求)/ NAPI 实例。例如,配置为 1 个 142*93abd332SWang Yaxin``rx`` 通道、1 个 ``tx`` 通道和 1 个 ``combined`` 通道的情况下,预计会使用 3 个中断、 143*93abd332SWang Yaxin2 个接收队列和 2 个发送队列。 144*93abd332SWang Yaxin 145*93abd332SWang Yaxin持久化NAPI配置 146*93abd332SWang Yaxin-------------- 147*93abd332SWang Yaxin 148*93abd332SWang Yaxin驱动程序常常会动态地分配和释放 NAPI 实例。这就导致每当 NAPI 实例被重新分配时,与 NAPI 相关 149*93abd332SWang Yaxin的用户配置就会丢失。netif_napi_add_config() API接口通过将每个 NAPI 实例与基于驱动程序定义 150*93abd332SWang Yaxin的索引值(如队列编号)的持久化 NAPI 配置相关联,从而避免了这种配置丢失的情况。 151*93abd332SWang Yaxin 152*93abd332SWang Yaxin使用此 API 可实现持久化的 NAPI 标识符(以及其他设置),这对于使用 ``SO_INCOMING_NAPI_ID`` 153*93abd332SWang Yaxin的用户空间程序来说是有益的。有关其他 NAPI 配置的设置,请参阅以下章节。 154*93abd332SWang Yaxin 155*93abd332SWang Yaxin驱动程序应尽可能尝试使用 netif_napi_add_config()。 156*93abd332SWang Yaxin 157*93abd332SWang Yaxin用户API 158*93abd332SWang Yaxin======= 159*93abd332SWang Yaxin 160*93abd332SWang Yaxin用户与 NAPI 的交互依赖于 NAPI 实例 ID。这些实例 ID 仅通过 ``SO_INCOMING_NAPI_ID`` 套接字 161*93abd332SWang Yaxin选项对用户可见。 162*93abd332SWang Yaxin 163*93abd332SWang Yaxin用户可以使用 Netlink 来查询某个设备或设备队列的 NAPI 标识符。这既可以在用户应用程序中通过编程 164*93abd332SWang Yaxin方式实现,也可以使用内核源代码树中包含的一个脚本:tools/net/ynl/pyynl/cli.py 来完成。 165*93abd332SWang Yaxin 166*93abd332SWang Yaxin例如,使用该脚本转储某个设备的所有队列(这将显示每个队列的 NAPI 标识符): 167*93abd332SWang Yaxin 168*93abd332SWang Yaxin 169*93abd332SWang Yaxin.. code-block:: bash 170*93abd332SWang Yaxin 171*93abd332SWang Yaxin $ kernel-source/tools/net/ynl/pyynl/cli.py \ 172*93abd332SWang Yaxin --spec Documentation/netlink/specs/netdev.yaml \ 173*93abd332SWang Yaxin --dump queue-get \ 174*93abd332SWang Yaxin --json='{"ifindex": 2}' 175*93abd332SWang Yaxin 176*93abd332SWang Yaxin有关可用操作和属性的更多详细信息,请参阅 ``Documentation/netlink/specs/netdev.yaml``。 177*93abd332SWang Yaxin 178*93abd332SWang Yaxin软件IRQ合并 179*93abd332SWang Yaxin----------- 180*93abd332SWang Yaxin 181*93abd332SWang Yaxin默认情况下,NAPI 不执行任何显式的事件合并。在大多数场景中,数据包的批量处理得益于设备进行 182*93abd332SWang Yaxin的中断请求(IRQ)合并。不过,在某些情况下,软件层面的合并操作也很有帮助。 183*93abd332SWang Yaxin 184*93abd332SWang Yaxin可以将 NAPI 配置为设置一个重新轮询定时器,而不是在处理完所有数据包后立即取消屏蔽硬件中断。 185*93abd332SWang Yaxin网络设备的 ``gro_flush_timeout`` sysfs 配置项可用于控制该定时器的延迟时间,而 ``napi_defer_hard_irqs`` 186*93abd332SWang Yaxin则用于控制在 NAPI 放弃并重新启用硬件中断之前,连续进行空轮询的次数。 187*93abd332SWang Yaxin 188*93abd332SWang Yaxin上述参数也可以通过 Netlink 的 netdev-genl 接口,基于每个 NAPI 实例进行设置。当通过 189*93abd332SWang YaxinNetlink 进行配置且是基于每个 NAPI 实例设置时,上述参数使用连字符(-)而非下划线(_) 190*93abd332SWang Yaxin来命名,即 ``gro-flush-timeout`` 和 ``napi-defer-hard-irqs``。 191*93abd332SWang Yaxin 192*93abd332SWang Yaxin基于每个 NAPI 实例的配置既可以在用户应用程序中通过编程方式完成,也可以使用内核源代码树中的 193*93abd332SWang Yaxin一个脚本实现,该脚本为 ``tools/net/ynl/pyynl/cli.py``。 194*93abd332SWang Yaxin 195*93abd332SWang Yaxin例如,通过如下方式使用该脚本: 196*93abd332SWang Yaxin 197*93abd332SWang Yaxin.. code-block:: bash 198*93abd332SWang Yaxin 199*93abd332SWang Yaxin $ kernel-source/tools/net/ynl/pyynl/cli.py \ 200*93abd332SWang Yaxin --spec Documentation/netlink/specs/netdev.yaml \ 201*93abd332SWang Yaxin --do napi-set \ 202*93abd332SWang Yaxin --json='{"id": 345, 203*93abd332SWang Yaxin "defer-hard-irqs": 111, 204*93abd332SWang Yaxin "gro-flush-timeout": 11111}' 205*93abd332SWang Yaxin 206*93abd332SWang Yaxin类似地,参数 ``irq-suspend-timeout`` 也可以通过 netlink 的 netdev-genl 设置。没有全局 207*93abd332SWang Yaxin的 sysfs 参数可用于设置这个值。 208*93abd332SWang Yaxin 209*93abd332SWang Yaxin``irq-suspend-timeout`` 用于确定应用程序可以完全挂起 IRQ 的时长。与 SO_PREFER_BUSY_POLL 210*93abd332SWang Yaxin结合使用,后者可以通过 ``EPIOCSPARAMS`` ioctl 在每个 epoll 上下文中设置。 211*93abd332SWang Yaxin 212*93abd332SWang Yaxin.. _poll_zh_CN: 213*93abd332SWang Yaxin 214*93abd332SWang Yaxin忙轮询 215*93abd332SWang Yaxin------ 216*93abd332SWang Yaxin 217*93abd332SWang Yaxin忙轮询允许用户进程在设备中断触发前检查传入的数据包。与其他忙轮询一样,它以 CPU 周期换取更低 218*93abd332SWang Yaxin的延迟(生产环境中 NAPI 忙轮询的使用尚不明确)。 219*93abd332SWang Yaxin 220*93abd332SWang Yaxin通过在选定套接字上设置 ``SO_BUSY_POLL`` 或使用全局 ``net.core.busy_poll`` 和 ``net.core.busy_read`` 221*93abd332SWang Yaxin等 sysctls 启用忙轮询。还存在基于 io_uring 的 NAPI 忙轮询 API 可使用。 222*93abd332SWang Yaxin 223*93abd332SWang Yaxin基于epoll的忙轮询 224*93abd332SWang Yaxin----------------- 225*93abd332SWang Yaxin 226*93abd332SWang Yaxin可以从 ``epoll_wait`` 调用直接触发数据包处理。为了使用此功能,用户应用程序必须确保添加到 227*93abd332SWang Yaxinepoll 上下文的所有文件描述符具有相同的 NAPI ID。 228*93abd332SWang Yaxin 229*93abd332SWang Yaxin如果应用程序使用专用的 acceptor 线程,那么该应用程序可以获取传入连接的 NAPI ID(使用 230*93abd332SWang YaxinSO_INCOMING_NAPI_ID)然后将该文件描述符分发给工作线程。工作线程将该文件描述符添加到其 231*93abd332SWang Yaxinepoll 上下文。这确保了每个工作线程的 epoll 上下文中所包含的文件描述符具有相同的 NAPI ID。 232*93abd332SWang Yaxin 233*93abd332SWang Yaxin或者,如果应用程序使用 SO_REUSEPORT,可以插入 bpf 或 ebpf 程序来分发传入连接,使得每个 234*93abd332SWang Yaxin线程只接收具有相同 NAPI ID 的连接。但是必须谨慎处理系统中可能存在多个网卡的情况。 235*93abd332SWang Yaxin 236*93abd332SWang Yaxin为了启用忙轮询,有两种选择: 237*93abd332SWang Yaxin 238*93abd332SWang Yaxin1. ``/proc/sys/net/core/busy_poll`` 可以设置为微秒数以在忙循环中等待事件。这是一个系统 239*93abd332SWang Yaxin 范围的设置,将导致所有基于 epoll 的应用程序在调用 epoll_wait 时忙轮询。这可能不是理想 240*93abd332SWang Yaxin 的情况,因为许多应用程序可能不需要忙轮询。 241*93abd332SWang Yaxin 242*93abd332SWang Yaxin2. 使用最新内核的应用程序可以在 epoll 上下文的文件描述符上发出 ioctl 来设置(``EPIOCSPARAMS``) 243*93abd332SWang Yaxin 或获取(``EPIOCGPARAMS``) ``struct epoll_params``,用户程序定义如下: 244*93abd332SWang Yaxin 245*93abd332SWang Yaxin.. code-block:: c 246*93abd332SWang Yaxin 247*93abd332SWang Yaxin struct epoll_params { 248*93abd332SWang Yaxin uint32_t busy_poll_usecs; 249*93abd332SWang Yaxin uint16_t busy_poll_budget; 250*93abd332SWang Yaxin uint8_t prefer_busy_poll; 251*93abd332SWang Yaxin 252*93abd332SWang Yaxin /* 将结构填充到 64 位的倍数 */ 253*93abd332SWang Yaxin uint8_t __pad; 254*93abd332SWang Yaxin }; 255*93abd332SWang Yaxin 256*93abd332SWang YaxinIRQ缓解 257*93abd332SWang Yaxin------- 258*93abd332SWang Yaxin 259*93abd332SWang Yaxin虽然忙轮询旨在用于低延迟应用,但类似的机制可用于减少中断请求。 260*93abd332SWang Yaxin 261*93abd332SWang Yaxin每秒高请求的应用程序(尤其是路由/转发应用程序和特别使用 AF_XDP 套接字的应用程序) 262*93abd332SWang Yaxin可能希望在处理完一个请求或一批数据包之前不被中断。 263*93abd332SWang Yaxin 264*93abd332SWang Yaxin此类应用程序可以向内核承诺会定期执行忙轮询操作,而驱动程序应将设备的中断请求永久屏蔽。 265*93abd332SWang Yaxin通过使用 ``SO_PREFER_BUSY_POLL`` 套接字选项可启用此模式。为避免系统出现异常,如果 266*93abd332SWang Yaxin在 ``gro_flush_timeout`` 时间内没有进行任何忙轮询调用,该承诺将被撤销。对于基于 267*93abd332SWang Yaxinepoll 的忙轮询应用程序,可以将 ``struct epoll_params`` 结构体中的 ``prefer_busy_poll`` 268*93abd332SWang Yaxin字段设置为 1,并使用 ``EPIOCSPARAMS`` 输入 / 输出控制(ioctl)操作来启用此模式。 269*93abd332SWang Yaxin更多详情请参阅上述章节。 270*93abd332SWang Yaxin 271*93abd332SWang YaxinNAPI 忙轮询的 budget 低于默认值(这符合正常忙轮询的低延迟意图)。减少中断请求的场景中 272*93abd332SWang Yaxin并非如此,因此 budget 可以通过 ``SO_BUSY_POLL_BUDGET`` 套接字选项进行调整。对于基于 273*93abd332SWang Yaxinepoll 的忙轮询应用程序,可以通过调整 ``struct epoll_params`` 中的 ``busy_poll_budget`` 274*93abd332SWang Yaxin字段为特定值,并使用 ``EPIOCSPARAMS`` ioctl 在特定 epoll 上下文中设置。更多详细信 275*93abd332SWang Yaxin息请参见上述部分。 276*93abd332SWang Yaxin 277*93abd332SWang Yaxin需要注意的是,为 ``gro_flush_timeout`` 选择较大的值会延迟中断请求,以实现更好的批 278*93abd332SWang Yaxin量处理,但在系统未满载时会增加延迟。为 ``gro_flush_timeout`` 选择较小的值可能会因 279*93abd332SWang Yaxin设备中断请求和软中断处理而干扰尝试进行忙轮询的用户应用程序。应权衡这些因素后谨慎选择 280*93abd332SWang Yaxin该值。基于 epoll 的忙轮询应用程序可以通过为 ``maxevents`` 选择合适的值来减少用户 281*93abd332SWang Yaxin处理的干扰。 282*93abd332SWang Yaxin 283*93abd332SWang Yaxin用户可能需要考虑使用另一种方法,IRQ 挂起,以帮助应对这些权衡问题。 284*93abd332SWang Yaxin 285*93abd332SWang YaxinIRQ挂起 286*93abd332SWang Yaxin------- 287*93abd332SWang Yaxin 288*93abd332SWang YaxinIRQ 挂起是一种机制,其中设备 IRQ 在 epoll 触发 NAPI 数据包处理期间被屏蔽。 289*93abd332SWang Yaxin 290*93abd332SWang Yaxin只要应用程序对 epoll_wait 的调用成功获取事件,内核就会推迟 IRQ 挂起定时器。如果 291*93abd332SWang Yaxin在忙轮询期间没有获取任何事件(例如,因为网络流量减少),则会禁用IRQ挂起功能,并启 292*93abd332SWang Yaxin用上述减少中断请求的策略。 293*93abd332SWang Yaxin 294*93abd332SWang Yaxin这允许用户在 CPU 消耗和网络处理效率之间取得平衡。 295*93abd332SWang Yaxin 296*93abd332SWang Yaxin要使用此机制: 297*93abd332SWang Yaxin 298*93abd332SWang Yaxin 1. 每个 NAPI 的配置参数 ``irq-suspend-timeout`` 应设置为应用程序可以挂起 299*93abd332SWang Yaxin IRQ 的最大时间(纳秒)。这通过 netlink 完成,如上所述。此超时时间作为一 300*93abd332SWang Yaxin 种安全机制,如果应用程序停滞,将重新启动中断驱动程序的中断处理。此值应选择 301*93abd332SWang Yaxin 为覆盖用户应用程序调用 epoll_wait 处理数据所需的时间,需注意的是,应用程 302*93abd332SWang Yaxin 序可通过在调用 epoll_wait 时设置 ``max_events`` 来控制获取的数据量。 303*93abd332SWang Yaxin 304*93abd332SWang Yaxin 2. sysfs 参数或每个 NAPI 的配置参数 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs`` 305*93abd332SWang Yaxin 可以设置为较低值。它们将用于在忙轮询未找到数据时延迟 IRQs。 306*93abd332SWang Yaxin 307*93abd332SWang Yaxin 3. 必须将 ``prefer_busy_poll`` 标志设置为 true。如前文所述,可使用 ``EPIOCSPARAMS`` 308*93abd332SWang Yaxin ioctl操作来完成此设置。 309*93abd332SWang Yaxin 310*93abd332SWang Yaxin 4. 应用程序按照上述方式使用 epoll 触发 NAPI 数据包处理。 311*93abd332SWang Yaxin 312*93abd332SWang Yaxin如上所述,只要后续对 epoll_wait 的调用向用户空间返回事件,``irq-suspend-timeout`` 313*93abd332SWang Yaxin就会被推迟并且 IRQ 会被禁用。这允许应用程序在无干扰的情况下处理数据。 314*93abd332SWang Yaxin 315*93abd332SWang Yaxin一旦 epoll_wait 的调用没有找到任何事件,IRQ 挂起会被自动禁用,并且 ``gro_flush_timeout`` 316*93abd332SWang Yaxin和 ``napi_defer_hard_irqs`` 缓解机制将开始起作用。 317*93abd332SWang Yaxin 318*93abd332SWang Yaxin预期是 ``irq-suspend-timeout`` 的设置值会远大于 ``gro_flush_timeout``,因为 ``irq-suspend-timeout`` 319*93abd332SWang Yaxin应在一个用户空间处理周期内暂停中断请求。 320*93abd332SWang Yaxin 321*93abd332SWang Yaxin虽然严格来说不必通过 ``napi_defer_hard_irqs`` 和 ``gro_flush_timeout`` 来执行 IRQ 挂起, 322*93abd332SWang Yaxin但强烈建议这样做。 323*93abd332SWang Yaxin 324*93abd332SWang Yaxin中断请求挂起会使系统在轮询模式和由中断驱动的数据包传输模式之间切换。在网络繁忙期间,``irq-suspend-timeout`` 325*93abd332SWang Yaxin会覆盖 ``gro_flush_timeout``,使系统保持忙轮询状态,但是当 epoll 未发现任何事件时,``gro_flush_timeout`` 326*93abd332SWang Yaxin和 ``napi_defer_hard_irqs`` 的设置将决定下一步的操作。 327*93abd332SWang Yaxin 328*93abd332SWang Yaxin有三种可能的网络处理和数据包交付循环: 329*93abd332SWang Yaxin 330*93abd332SWang Yaxin1) 硬中断 -> 软中断 -> NAPI 轮询;基本中断交付 331*93abd332SWang Yaxin2) 定时器 -> 软中断 -> NAPI 轮询;延迟的 IRQ 处理 332*93abd332SWang Yaxin3) epoll -> 忙轮询 -> NAPI 轮询;忙循环 333*93abd332SWang Yaxin 334*93abd332SWang Yaxin循环 2 可以接管循环 1,如果设置了 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs``。 335*93abd332SWang Yaxin 336*93abd332SWang Yaxin如果设置了 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs``,循环 2 和 3 将互相“争夺”控制权。 337*93abd332SWang Yaxin 338*93abd332SWang Yaxin在繁忙时期,``irq-suspend-timeout`` 用作循环 2 的定时器,这基本上使网络处理倾向于循环 3。 339*93abd332SWang Yaxin 340*93abd332SWang Yaxin如果不设置 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs``,循环 3 无法从循环 1 接管。 341*93abd332SWang Yaxin 342*93abd332SWang Yaxin因此,建议设置 ``gro_flush_timeout`` 和 ``napi_defer_hard_irqs``,因为若不这样做,设置 343*93abd332SWang Yaxin``irq-suspend-timeout`` 可能不会有明显效果。 344*93abd332SWang Yaxin 345*93abd332SWang Yaxin.. _threaded_zh_CN: 346*93abd332SWang Yaxin 347*93abd332SWang Yaxin线程化NAPI 348*93abd332SWang Yaxin---------- 349*93abd332SWang Yaxin 350*93abd332SWang Yaxin线程化 NAPI 是一种操作模式,它使用专用的内核线程而非软件中断上下文来进行 NAPI 处理。这种配置 351*93abd332SWang Yaxin是针对每个网络设备的,并且会影响该设备的所有 NAPI 实例。每个 NAPI 实例将生成一个单独的线程 352*93abd332SWang Yaxin(称为 ``napi/${ifc-name}-${napi-id}`` )。 353*93abd332SWang Yaxin 354*93abd332SWang Yaxin建议将每个内核线程固定到单个 CPU 上,这个 CPU 与处理中断的 CPU 相同。请注意,中断请求(IRQ) 355*93abd332SWang Yaxin和 NAPI 实例之间的映射关系可能并不简单(并且取决于驱动程序)。NAPI 实例 ID 的分配顺序将与内 356*93abd332SWang Yaxin核线程的进程 ID 顺序相反。 357*93abd332SWang Yaxin 358*93abd332SWang Yaxin线程化 NAPI 是通过向网络设备的 sysfs 目录中的 ``threaded`` 文件写入 0 或 1 来控制的。 359*93abd332SWang Yaxin 360*93abd332SWang Yaxin.. rubric:: 脚注 361*93abd332SWang Yaxin 362*93abd332SWang Yaxin.. [#] NAPI 最初在 2.4 Linux 中被称为 New API。 363