NAPI(New API)的一些浅见

2023-11-15

NAPI真的是kernel开发者词穷想的名字吧,你看看kernel里面各种名字,不知道为啥就不能起个好听点的。

言归正传,wiki:https://en.wikipedia.org/wiki/New_API 给出的解释是NAPI是一种用于网络设备的中断缓解的技术。

听起来比较抽象,想象下双十一当天的淘宝服务器,有撑破天际的网络数据从全世界各个角落汇聚到该服务器的每个网卡,也就意味着无时无刻该网卡都有大量数据进来,IRQ不断的产生, CPU正常调度不断被IRQ打断,网卡多累,CPU的效率多低。我这里把他称为纯中断方式。所以NAPI的核心想法就是减轻网卡的负担,不让网卡每次都响应中断,响应一次中断缓一缓,让子弹飞一会,数据攒一会,再响应中断, 收割积攒的数据,这样就使得效率高很多。

谈到NAPI,很多人的理解就仅仅是poll方式,好像我用了NAPI就是等于用了poll方式接收数据,其实不然。

NAPI的精髓是在poll方式 / 纯中断方式之间自由灵活的游走切换

As a compromise, the Linux kernel uses the interrupt-driven mode by

default and only switches to polling mode when the flow of incoming

packets exceeds a certain threshold, known as the "weight" of the

network interface. --wiki

进一步说是,数据量不大的时候用纯中断,数据量很大的时候用poll的方式。那么什么时候switch,如何switch呢,且听我慢慢道来。

要搞懂NAPI,就要从它背后的逻辑和机制看起。要看懂此文,需要了解linux的中断处理的大致机制

I.

首先从三个重要的API开始:

netif_napi_add   --driver告诉内核要使用napi的机制,初始化响应参数,注册poll的回调函数

napi_schedule    --driver告诉内核开始调度napi的机制,稍后poll回调函数会被调用

napi_complete    --driver告诉内核其工作不饱满即中断不多,数据量不大,改变napi的状态机,后续将采用纯中断方式响应数据

net_rx_action      --内核初始化注册的软中断,注册poll的回调函数会被其调用

使用起来好像不是特别复杂。

II.

再来,我们看下几个重要的数据结构和原理。

softnet_data --这是一个PER_CPU的queue,更准确地说是一个和每个CPU绑定,属于该CPU的data queue,incoming packets are placed on per-CPU queues. 注意它的成员poll_list

struct softnet_data {

struct Qdisc  *output_queue;

struct Qdisc  **output_queue_tailp;

struct list_head poll_list; napi->poll_list结构挂入这个list,包括NAPI接口的driver以及非NAPI接口的driver都可以统一加入到这个poll_list

struct sk_buff  *completion_queue;

...省略

};

napi_struct -- napi的关键结构,它也有一个poll_list,用于挂在softnet_data的poll_list上是softnet_data poll list上的最小调度单位。还有一个weight需要着重说一下,这是每次调用poll回调函数时分配的最大skb数量,或者说从DMA buffer里面可以收割的最大的skb的数量。也就是前面wiki说的threshold。默认64,千兆网卡。netif_napi_add的时候注册。
struct napi_struct {

    struct list_head poll_list; /* 用于加入处于轮询状态的设备队列 */

    unsigned long state; /* 设备的状态 */

    int weight; /* 每次处理的该设备的最大skb数量 */

    int (*poll) (struct napi_struct *, int); /* 此设备的轮询方法 */

#ifdef CONFIG_NETPOLL

    ...省略

#endif

    unsigned int gro_count;

    struct net_device *dev;

    struct list_head dev_list;

    struct sk_buff *gro_list;

    struct sk_buff *skb;

};

画个图简单说

在这里插入图片描述

所以一个napi_structsoftnet_data的poll list会发生的故事只可能有:

a. enqueue, 入队列

b. dequeue, 出队列

c. reorder, 重新调整队列顺序

III.

那么napi_struct什么时候enqueue挂在softnet_data的poll list,以及什么时候reorder移动它,或者dequeue从list里面去掉,不妨先了解下整个NAPI工作的流程



请注意上面的图不是软件流程图,只是大概的因果关系,具体怎么回事等我细细道来,

1. HW ISR,具体来说算是DMA中断,告诉CPU搬砖完成,这样会通过触发中断最终触发driver注册到内核的中断函数,例如注册的PCI的中断.

2. 在这个IRQ函数中,通常driver会disable和clear IRQ,比如关PCI中断,然后调用napi_schedule,它所作的事情就是enqueue,然后mask一个NET_RX_SOFTIRQ event

3.接下来不久,当中断上半部结束,开始中断下半部的时候,会在__do_softirq中发现先前mask的NET_RX_SOFTIRQ有效,net_rx_action作为其callback会被调用。在net_rx_action会调用driver注册的poll函数。net_rx_action这个最重要的函数还是要看下的。

static void net_rx_action(struct softirq_action *h) //from kernel 3.13

{

struct softnet_data *sd = &__get_cpu_var(softnet_data);

unsigned long time_limit = jiffies + 2;

int budget = netdev_budget;//300

void *have;

local_irq_disable();

while (!list_empty(&sd->poll_list)) {

struct napi_struct *n;

int work, weight;

/* If softirq window is exhuasted then punt.

* Allow this to run for 2 jiffies since which will allow

* an average latency of 1.5/HZ.

*/

if (unlikely(budget <= 0 || time_after_eq(jiffies, time_limit)))

goto softnet_break;

local_irq_enable();

/* Even though interrupts have been re-enabled, this

* access is safe because interrupts can only add new

* entries to the tail of this list, and only ->poll()

* calls can remove this head entry from the list.

*/

n = list_first_entry(&sd->poll_list, struct napi_struct, poll_list);

have = netpoll_poll_lock(n);

weight = n->weight;

/* This NAPI_STATE_SCHED test is for avoiding a race

* with netpoll's poll_napi().  Only the entity which

* obtains the lock and sees NAPI_STATE_SCHED set will

* actually make the ->poll() call.  Therefore we avoid

* accidentally calling ->poll() when NAPI is not scheduled.

*/

work = 0;

if (test_bit(NAPI_STATE_SCHED, &n->state)) {

work = n->poll(n, weight);

trace_napi_poll(n);

}

WARN_ON_ONCE(work > weight);

budget -= work;

local_irq_disable();

/* Drivers must not modify the NAPI state if they

* consume the entire weight.  In such cases this code

* still "owns" the NAPI instance and therefore can

* move the instance around on the list at-will.

*/

if (unlikely(work == weight)) {

if (unlikely(napi_disable_pending(n))) {

local_irq_enable();

napi_complete(n);

local_irq_disable();

} else {

if (n->gro_list) {

/* flush too old packets

* If HZ < 1000, flush all packets.

*/

local_irq_enable();

napi_gro_flush(n, HZ >= 1000);

local_irq_disable();

}

list_move_tail(&n->poll_list, &sd->poll_list);

}

}

netpoll_poll_unlock(have);

}

out:

net_rps_action_and_irq_enable(sd);

#ifdef CONFIG_NET_DMA

/*

* There may not be any more sk_buffs coming right now, so push

* any pending DMA copies to hardware

*/

dma_issue_pending_all();

#endif

return;

softnet_break:

sd->time_squeeze++;

__raise_softirq_irqoff(NET_RX_SOFTIRQ);

goto out;

}

一条条说:

1) 每次NET_RX这个软中断也会分配一个budget,默认300,含义见下条。


int budget = netdev_budget;//300


2)每次都从list头取个napi_struct,然后调poll回调函数。结合之前的napi的budget,我们大概明白,poll回调函数会消耗软中断的budget,如果这个poll list的poll消耗完了软中断的所有budget(300),则循环退出,按照每次poll消耗都是64来算也就是5次。这个时候poll list还有待poll的napi struct。另一个退出条件是超时2 jiffies。这个两种条件一种是poll list上的poll收割的数据都是大包,所以时间没超时但是总的budget用完。另一种情况就是都是数量庞大的小包,但是都没超budget,最后超时。然后不管哪种情况,都会mask一个NET_RX_SOFTIRQ event。留待下次net_rx_action处理。


if (unlikely(budget <= 0 || time_after_eq(jiffies, time_limit)))

goto softnet_break; ...省略

n = list_first_entry(&sd->poll_list, struct napi_struct, poll_list); ... 省略

work = n->poll(n, weight); ... 省略

budget -= work;... 省略

__raise_softirq_irqoff(NET_RX_SOFTIRQ);


3)Driver的poll函数通常每家实现不一样,大概逻辑就是通常会从dma ring里面把数据拿出来,最多拿napi的注册weight(默认64)的skb。

如果ring里面的数据不够weight,注意了,这里很关键,就会认为,数据已经拿完,执行napi_complete, 将该napi_struct从list里面拿掉,并且开启中断,比如开启PCI,进入纯中断模式;这之后有可能有新的中断触发,于是新的napi_struct在HW ISR中(抢占软中断)被加入到poll list,而且那个时候for循环还没有退出。

如果ring里面数据太多,那就只能拿到weight这么多,注意了,这里不会将napi_struct从list上拿掉,也不会关中断,poll函数返回时会在net_rx_action中返回消耗的budget,如果消耗到最大值weight(64),说明还有数据要取,这样会放到poll list的尾部(假设不考虑GRO的情况)。留待下次到队头再次poll。这时候是switch到了poll模式


work = n->poll(n, weight); ... 省略

if (unlikely(work == weight)) {

if (unlikely(napi_disable_pending(n))) {

... 省略

} else {

if (n->gro_list) {

... 省略

}

list_move_tail(&n->poll_list, &sd->poll_list);

}


只有在poll回调函数里面认为已收割数据不超过weight,中断才打开,才会有新的中断进来,才会有新的napi_struct加进list(queue)。

只要poll回调函数里面的中断没有打开,比如PCI,都属于poll模式,在net_rx_action的budget(300)和2 jiffies时间用完时,退出for循环, 不一定是poll模式,比如一直都是小包的且很频繁。也有可能会退出。


4. 非正常退出时,list上一定还是有napi_struct的,那么非正常退出之后又发生了什么?无论是哪种非正常退出(netdev_budget 用完 or 超时2 jiffies),新的NET_RX_SOFTIRQ mask会置起来,那么然后什么时候处理呢?

这里要对内核软中断机制有一定的了解。简单说就是net_rx_action退出之后返回到__do_softirq, 这里面还会check是否有pending的中断。如果有并且没有超时(2ms),也没有超过最大restart次数(10次),也不需要resched,那么会重新调用net_rx_action重复之前的过程;

但是,如果不满足上述条件,将会退出软中断过程,

1)将net_rx_action的执行放在ksoftirqd

2)由于其他设备中断触发软中断后执行__do_softirq,间接执行net_rx_action

3)内核进程调用local_bh_enable中也会调用__do_softirq


pending=local_softirq_pending();

if(pending){

    if(time_before(jiffies,end)&&!need_resched()&&--max_restart)    

            goto restart;

    wakeup_softirqd();

}

所以补全之前的图:


IV.

关于NAPI的Tunning,可以看到主要有的参数有netdev_budget, napi_struct的budget,以及超时的2 jiffies。时间是hard-coded,除非你想改内核代码,前两个都可以在不同阶段tunning。最灵活的netdev_budget,可以run time改动,比如

 echo 600 > /proc/sys/net/core/netdev_budget

或者

sudo sysctl -w net.core.netdev_budget=600(更改 /etc/sysctl.conf当然也可以)

关于NAPI的debug或者说网络性能的debug,还有一个有意思统计值,就是softnet_data中的time_squeeze, 它记录了非正常退出的次数,对理解网络瓶颈有很大意义。


sd->time_squeeze++;


具体来说time_squeeze可以通过以下路径找到
cat /proc/net/softnet_stat 

打印出来的结果是一系列的值,对于kernel 3.13,来自net-procfs.c,softnet_seq_showtime_squeeze就是第三个值,请记住结果中的每一行代表一个CPU


seq_printf(seq,

"%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x\n",

sd->processed, sd->dropped, sd->time_squeeze,0,

0,0,0,0,/*was fastroute*/

sd->cpu_collision, sd->received_rps, flow_limit_count);


V.

最后我们不妨来考虑下面几种情况,到底是poll模式还是中断模式:

a. 中断频繁,数据量大

b. 中断不频繁,数据量大

c. 中断频繁,数据量小

d. 中断不频繁,数据量小

d. 中断不频繁,大包小包都有,就是数据量波动

欢迎讨论:bmebob_zhao@163.com

以上.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

NAPI(New API)的一些浅见 的相关文章

  • 无法加载 JavaHL 库。- linux/eclipse

    在尝试安装 Subversion 插件时 当 Eclipse 启动时出现此错误 Failed to load JavaHL Library These are the errors that were encountered no libs
  • 通过特定分隔符删除字符串

    我的文件中有几列 其中第二列有 分隔符 我想删除第二列中的第一个 第三个和第四个字符串 并将第二个字符串留在该列中 但我有正常的分隔符空间 所以我不知道 input 22 16050075 A G 16050075 A G 22 16050
  • 如何使用 bash 锁定文件

    我有一个任务从远程服务器同步目录 rsync av email protected cdn cgi l email protection srv data srv data 为了使其定期运行并避免脚本 reEnter 问题 我使用 rsyn
  • 仅打印“docker-container ls -la”输出中的“Names”列

    发出时docker container ls la命令 输出如下所示 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a67f0c2b1769 busybox tail f dev
  • Linux TUN/TAP:无法从 TAP 设备读回数据

    问题是关于如何正确配置想要使用 Tun Tap 模块的 Linux 主机 My Goal 利用现有的路由软件 以下为APP1和APP2 但拦截并修改其发送和接收的所有消息 由Mediator完成 我的场景 Ubuntu 10 04 Mach
  • 我可以从命令行打印 html 文件(带有图像、css)吗?

    我想从脚本中打印带有图像的样式化 html 页面 谁能建议一个开源解决方案 我使用的是 Linux Ubuntu 8 04 但也对其他操作系统的解决方案感兴趣 你可以给html2ps http user it uu se jan html2
  • Linux中的定时器类

    我需要一个计时器来以相对较低的分辨率执行回调 在 Linux 中实现此类 C 计时器类的最佳方法是什么 有我可以使用的库吗 如果您在框架 Glib Qt Wx 内编写 那么您已经拥有一个具有定时回调功能的事件循环 我认为情况并非如此 如果您
  • chown:不允许操作

    我有问题 我需要通过 php 脚本为系统中的不同用户设置文件所有者权限 所以我通过以下命令执行此操作 其中 1002 是系统的用户 ID file put contents filename content system chown 100
  • 如何有效截断文件头?

    大家都知道truncate file size 函数 通过截断文件尾部将文件大小更改为给定大小 但是如何做同样的事情 只截断文件的尾部和头部呢 通常 您必须重写整个文件 最简单的方法是跳过前几个字节 将其他所有内容复制到临时文件中 并在完成
  • 如何使用GDB修改内存内容?

    我知道我们可以使用几个命令来访问和读取内存 例如 print p x 但是如何更改任何特定位置的内存内容 在 GDB 中调试时 最简单的是设置程序变量 参见GDB 分配 http sourceware org gdb current onl
  • Discord.net 无法在 Linux 上运行

    我正在尝试让在 Linux VPS 上运行的 Discord net 中编码的不和谐机器人 我通过单声道运行 但我不断收到此错误 Unhandled Exception System Exception Connection lost at
  • 如何在 Linux shell 中将十六进制转换为 ASCII 字符?

    假设我有一个字符串5a 这是 ASCII 字母的十六进制表示Z 我需要找到一个 Linux shell 命令 它将接受一个十六进制字符串并输出该十六进制字符串代表的 ASCII 字符 所以如果我这样做 echo 5a command im
  • 安装J语言的JQt IDE,出现错误

    我一直按照这里的说明进行操作 http code jsoftware com wiki System Installation Linux http code jsoftware com wiki System Installation L
  • 有谁知道在哪里定义硬件、版本和序列号。 /proc/cpuinfo 的字段?

    我想确保我的 proc cpuinfo 是准确的 目前它输出 Hardware am335xevm Revision 0000 Serial 0000000000000000 我可以在代码中的哪里更改它以给出实际值 这取决于 Linux 的
  • 如何授予 apache 使用 NTFS 分区上的目录的权限?

    我在一台带有 20GB 硬盘的旧机器上运行 Linux Lubutu 12 10 我有一个 1 TB 外部硬盘 上面有一个 NTFS 分区 在该分区上 有一个 www 目录 用于保存我的网页内容 它在启动时自动安装为 media t515
  • docker容器大小远大于实际大小

    我正在尝试从中构建图像debian latest 构建后 报告的图像虚拟大小来自docker images命令为 1 917 GB 我登录查看尺寸 du sh 大小为 573 MB 我很确定这么大的尺寸通常是不可能的 这里发生了什么 如何获
  • 查找哪些页面不再与写入时复制共享

    假设我在 Linux 中有一个进程 我从中fork 另一个相同的过程 后forking 因为原始进程将开始写入内存 Linux写时复制机制将为进程提供与分叉进程使用的不同的唯一物理内存页 在执行的某个时刻 我如何知道原始进程的哪些页面已被写
  • 我的线程图像生成应用程序如何将其数据传输到 GUI?

    Mandelbrot 生成器的缓慢多精度实现 线程化 使用 POSIX 线程 Gtk 图形用户界面 我有点失落了 这是我第一次尝试编写线程程序 我实际上并没有尝试转换它的单线程版本 只是尝试实现基本框架 到目前为止它是如何工作的简要描述 M
  • 如何通过ssh检查ubuntu服务器上是否存在php和apache

    如何通过ssh检查Ubuntu服务器上apache是 否安装了php和mysql 另外如果安装的话在哪个目录 如果安装了其他软件包 例如 lighttpd 那么它在哪里 确定程序是否已安装的另一种方法是使用which命令 它将显示您正在搜索
  • Linux 上有关 getBounds() 和 setBounds() 的 bug_id=4806603 的解决方法?

    在 Linux 平台上 Frame getBounds 和 Frame setBounds 的工作方式不一致 这在 2003 年就已经有报道了 请参见此处 http bugs java com bugdatabase view bug do

随机推荐

  • python使用Axes3D画三维图加入legend图例时报错AttributeError: ‘Poly3DCollection‘ object has no attribute ‘_edgecolo

    Q python使用Axes3D画三维图加入legend图例时报错AttributeError Poly3DCollection object has no attribute edgecolors2d 报错源代码 fig plt figu
  • 计算机网络和因特网的基本认识

    计算机网络和因特网 笔者阅读 计算机网络 自顶向下方法 第七版第一章所作笔记如下 主要为计算机网络和因特网的基本认识 第一章概述笔记入口 第二章应用层笔记入口 第三章运输层笔记入口 第四章网络层数据平面笔记入口 1 什么是因特网 1 1 因
  • Git教程:标签

    实际上看到这边 对于Git分支仓库这些概念以及commit push pull等操作 但是有些东西我们可能常见到 但是不怎么经常用的 tag 标签其实就算一个 我们下载一些开源项目的时候 在GitHub上经常看到版本 有的项目是通过tag来
  • systemui

    一 SystemUI 概述 二 模块基本布局 三 模块内部框架 四 模块流程 五 重要文件的介绍 一 SystemUI 概述 1 Statusbar 的功能作用 1 1 状态栏的通知功能 包括时间 通知 系统状态等 1 2 状态栏的日期显示
  • VBA &金融

    汇总数据 算提成 G py 210523finance VBA专题12 课程需用材料 Sub Macro1 Dim ws As Worksheet Dim i As Integer For i 1 To 20 Set ws Sheets A
  • kotlin 中intent的传值应用

    main Activity fun intent position Int content String var intent Intent this MainActivity TestActivity class java var bun
  • org.apache.poi.ss.usermodel.Cell.setCellValue(Ljava/time/LocalDateTime;)V

    java 导出功能 出现错误的原因是 实体中存在日期 而导出的时候Excel无法转换日期类型 解决方法 在实体中将日期类型转换为String类型 ColumnWidth 25 ExcelProperty value 计划开始日期 index
  • 100道Python练习题集合,拿去刷

    看书 看视频都可以帮助你学习代码 但都只是辅助作用 学好Python 最重要的还是多敲代码 多刷题 不知道怎么找题刷题的小伙伴 可以看看小编今天带来的 Python100练习题 覆盖了基本语法 数据结构 算法等多个方面 100道Python
  • moviepy音视频开发:使用credits1给视频加片头片尾字幕

    前往老猿Python博文目录 一 概述 在 moviepy音视频剪辑 视频基类VideoClip子类DataVideoClip UpdatedVideoClip ImageClip ColorClip TextClip类详解 介绍了Text
  • 通过NodeJS对接微信客服实现第三方API管理消息

    最近项目有个需求 要对接微信客服 之前也没做过 一脸懵逼 我属于那种不知其然 就心里很没底的人 感觉不知道怎么去开发了 所以就趁着后端还没开发完接口 先自己对接一下试试 接下来就是整个流程 首先我们先注册一个企业微信 这一步是必须的 因为我
  • map根据属性排序、取出map前n个后n个

    map 按 key 升序排序 map 按 key 升序排序 param map return private Map
  • linux top命令详解

    一 top简介 top命令经常用来监控linux的系统状况 是常用的性能分析工具 能够实时显示系统中各个进程的资源占用情况 在Linux操作系统中 top是使用最频繁 也是比较全的一个命令 Top命令类似于Windows系统的任务管理器工具
  • python找不到reshape_python – AttributeError:’Tensor’对象没有属性’reshape’

    我想写一个去噪自动编码器 为了可视化的目的 我想打印出损坏的图像 这是我想要显示损坏图像的测试部分 def corrupt x noise tf random normal shape tf shape x mean 0 0 stddev
  • 调速阀------电磁阀

    目录 调速阀 节流阀简图的理解 安装方式 注意 安装注意事项 电磁阀 1 直动式电磁阀 2 分步直动电磁阀 3 先导式电磁阀 二位二通电磁阀 二位三通电磁阀 二位四通电磁阀 三位三通电磁阀 三位四通电磁阀 管道联系式电磁阀 直接控制式电磁阀
  • 芷菏随身wifi14.5 相关教程

    芷菏随身wifi 芷菏都知道 sim一般都是没有密码 直接可以切换的 是可以直接用ARDC连接投屏到电脑上的 按住随身wifi上的按钮 进入9008模式 可以将随身wifi刷boot模块 再从投屏中修补面具 起到root的效果 这款sim1
  • python发送邮件(带附件)

    usr bin python coding UTF 8 import logging import smtplib from email mime multipart import MIMEMultipart from email mime
  • jquery mobile和ajax,使用jquery mobile不可忽视的细节

    jQuery Mobile 是创建移动WEB应用程序的框架 在学习和使用该框架的过程中 有一些心得想要和大家分享一下 一 框架 因为是移动端开发 所以不要忘了下面这个重要的meta标签哦 使用jquery mobile要引入相应的css文件
  • 数据的探索性分析

    探索一下 数据分析的起点 数据分类 一 描述性分析 整理数据 定义 主要作用 可视化技术 定义 主要作用 常用方法 二 相关性分析 分析数据 定义 主要作用 相关性分类 相关性测定 三 假设检验 分析数据 定义 作用 步骤 相对理论 常见的
  • 对某底层硬件模块编写底层程序的主要步骤及经验

    一 禁止硬件模块运行 配置好相关寄存器 这里的硬件模块是指那些CPU发一些指令后就能独立工作的模块 二 置位硬件模块控制寄存器的使能位 使能硬件模块 三 明确功能 搞清楚哪些工作是由硬件来做的 哪些该由软件来执行 四 当由硬件模块来做的工作
  • NAPI(New API)的一些浅见

    NAPI真的是kernel开发者词穷想的名字吧 你看看kernel里面各种名字 不知道为啥就不能起个好听点的 言归正传 wiki https en wikipedia org wiki New API 给出的解释是NAPI是一种用于网络设备