napi —— linux 网卡驱动收包机制

news2025/1/12 13:30:02

linux 操作系统一般指  linux 内核。在 linux 上开发应用的时候,可以使用 linux 提供的系统调用。linux 内核管理着机器上的硬件资源:内存,磁盘,网卡等。开发应用的时候不能直接操作这些硬件,而只能通过系统调用来使用这些资源。linux 系统调用可以说是 linux 内核提供给上层的一些接口。这种内核和用户隔离的机制保证了安全性的同时,也简化了应用的开发。

linux 内核除了给上层应用提供了接口之外,给底层的硬件驱动也提供了框架和机制。在 linux 中开发驱动的时候,硬件驱动也可以看做是 linux 内核的应用层,不过和用户态应用相比,硬件驱动工作在内核态。

linux 内核的用户有两个部分,一个是用户态的程序,一个是底层的硬件驱动。

1 基础收包方式

在讨论 io 模型时,经常讨论阻塞,非阻塞,同步,异步的 io 模型。在网卡收包方面,有两种基础的收包方式,分别是中断和轮询。中断和轮询的工作机制与 io 模型是有些类似的,中断是异步的,轮询是同步非阻塞的的。

1.1 中断

中断方式是软件和硬件交互的基本方式。在 linux 中,中断的优先级是最高的,高于线程也高于软中断。

中断的优点是实时性好(谁都可以打断),适用于网络流量不是很大的场景。如果网络流量很大的话,那么 cpu 就会一直陷入网络报文的处理中,其它中断和其它任务得不到机会运行。

当内核线程被中断打断时,调用中断服务例程之前还要保存当前线程的寄存器和栈信息。如果中断太多的话会造成频繁的线程上下文和中断之间的切换,上下文切换也会造成 cpu 的浪费。

1.2 轮询

中断方式是硬件主动通知软件,轮询方式是软件主动查询硬件。轮询收包的方式,一般会有一个线程,这个线程中是一个 while(1) 死循环,在死循环中会通过读网卡的寄存器来判断当前接收队列中有没有包,如果有包的话便会从接收队列中接收报文。

使用轮询方式收包,不会有线程和中断之间的上下文切换。轮询方式适用于网络流量比较大的场景,因为轮询方式会占满一个 cpu,如果网络流量很小的话,那么线程会有很多空转的情况,造成 cpu 资源的浪费。一些网络专用设备,比如路由器,核心工作就是转发报文,不像服务器上会跑很多业务,路由器的功能比较单一, 所以在路由器上使用轮询的方式,即使一直占着  cpu,也不会影响其它应用,因为路由器上其它的应用很少。DPDK 中使用了轮询的方式来收包。

2 napi

napi 的名字全称是 new api,一个新的 api,这样的名字并不是很直观。在软件开发中起名字是很困难的事情,从 napi 也可以看出来,即使在 linux 中,也有这样不是很直观的名字存在。

从上边的分析中也可以看出来,中断方式收包和轮询方式收包,各有优缺点。中断方式收包的优点是响应快,缺点是只能适用于流量较小的场景,如果在流量较大的场景下使用中断方式收包,网络中断会影响系统其它任务的执行。轮询方式收包的优点是不会有线程和中断之间的上下文切换,缺点是当流量较小的时候,会造成 cpu 资源的浪费。

所以中断收包适用于流量较小的场景,轮询适用于流量较大的场景。

 napi 收包方式中既有中断,也有轮询,集成了中断和轮询的优点。

本文中以 ixgbe 网卡为例进行记录。

2.1 napi 框架

软中断收包:

使用 napi 来接收报文是在软中断中处理的。硬中断服务例程中只做很少量的工作,做完之后硬中断立即返回,报文后续的处理在软中断重处理。

(1)硬中断会调用函数 napi_schedule_irqoff(),最终会调用到函数 ____napi_schedule(),在该函数中做两件事

/* Called with irq disabled */
static inline void ____napi_schedule(struct softnet_data *sd,
				     struct napi_struct *napi)
{
	list_add_tail(&napi->poll_list, &sd->poll_list);
	__raise_softirq_irqoff(NET_RX_SOFTIRQ);
}

① 将 napi 加到数据结构 struct softnet_data 中,struct softnet_data 是一个全局的数据结构,是 per cpu 类型的。在软中断中处理的时候便会从 struct softnet_data 中找到挂接的 napi,从 napi 找到处理函数,然后进行收包。

② 触发 NET_RX_SOFTIRQ 软中断

struct napi_struct:

/*
 * Structure for NAPI scheduling similar to tasklet but with weighting
 */
struct napi_struct {
	/* The poll_list must only be managed by the entity which
	 * changes the state of the NAPI_STATE_SCHED bit.  This means
	 * whoever atomically sets that bit can add this napi_struct
	 * to the per-CPU poll_list, and whoever clears that bit
	 * can remove from the list right before clearing the bit.
	 */
	struct list_head	poll_list;

	unsigned long		state;
    ...
    int			(*poll)(struct napi_struct *, int); NAPI_STATE_SCHED
	int			weight;
	...
};

struct napi_struct 中重要的成员有 4 个。

weight

napi 一次可以处理的报文个数,在软中断中收包是轮询的方式。如果网络流量很大,那么会一直轮询收包吗 ? 不会的,为了网络不影响系统中其它任务的执行,napi 每次接收报文的数量是有上限的,当接收的的报文数量达到上限时,即使网卡接收队列中有报文,也不再继续处理了,而是让出 cpu,等下次软中断处理时再进程接收。

保证了收包任务和系统其它任务的公平性。

poll函数指针,使用 napi 的网卡将网卡的收包函数挂到这个指针上。
statenapi 的状态,

NAPI_STATE_SCHED 状态说明 napi 是可以工作的。

poll_list上边也说了,napi 通过 poll_list 与 struct softnet_data 进行关联。

netif_napi_add():
网卡驱动中会调用这个函数对网卡使用 napi 进行初始化。包括挂接 poll 函数,初始化 weight。

void netif_napi_add(struct net_device *dev, struct napi_struct *napi,

            int (*poll)(struct napi_struct *, int), int weight)

2.2 网卡驱动使用 napi

struct ixgbe_q_vector 可以看作网卡的一个接收队列。在 struct ixgbe_q_vector 中有一个成员 struct napi_struct napi。

struct ixgbe_q_vector {
    ...
	struct napi_struct napi;
    ...
};

在初始化中断时,中断的参数是 struct ixgbe_q_vector * 指针类型,所以在中断处理函数中能通过 q_vector 找到 napi,从而可以将 napi 挂接到 struct softnet_data 中,进而被软中断处理。

static int ixgbe_request_msix_irqs(struct ixgbe_adapter *adapter)
{
    ...
    err = request_irq(entry->vector, &ixgbe_msix_clean_rings, 0,
                q_vector->name, q_vector);
    ...
    return err;
}


从下边的代码中可以看出来,ixgbe 网卡的 napi 收包函数是 ixgbe_poll(),weight 是 64。

static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter,
				int v_count, int v_idx,
				int txr_count, int txr_idx,
				int xdp_count, int xdp_idx,
				int rxr_count, int rxr_idx)
{
    ...
	/* initialize NAPI */
	netif_napi_add(adapter->netdev, &q_vector->napi,
		       ixgbe_poll, 64);
    ...
}

2.3 网卡收包过程分析

 网卡收包过程分为两个阶段,第一阶段是硬中断处理,第二阶段是软中断处理。

2.3.1 硬中断

ixgbe_msix_clean_rings() 是 ixgbe 网卡的中断服务例程。从注释来看,调用这个函数的时候,中断是关闭的,所以调用函数 napi_schedule_irqoff() 来调度 napi。

static irqreturn_t ixgbe_msix_clean_rings(int irq, void *data)
{
    struct ixgbe_q_vector *q_vector = data;

    /* EIAM disabled interrupts (on this vector) for us */
    if (q_vector->rx.ring || q_vector->tx.ring)
        napi_schedule_irqoff(&q_vector->napi);

    return IRQ_HANDLED;
}

对于一个 napi 来说,将这个 napi 调度,就是将这个 napi 设置状态 NAPI_STATE_SCHED,只有设置了这个状态,软中断处理流程中才会处理这个 napi,否则不处理。

对于一个 napi,在同一时刻只允许调度一次,不允许多次调度。函数 napi_schedule_prep() 就是判断 napi 是不是已经被调度,如果是的话返回 false,否则返回 true。

/**
 *	napi_schedule_irqoff - schedule NAPI poll
 *	@n: NAPI context
 *
 * Variant of napi_schedule(), assuming hard irqs are masked.
 */
static inline void napi_schedule_irqoff(struct napi_struct *n)
{
	if (napi_schedule_prep(n))
		__napi_schedule_irqoff(n);
}

在硬中断服务例程中,最终调用到函数 ____napi_schedule(),在该函数中做两件事:将 napi 挂接到 struct softnet_data 中,触发软中断。

static inline void ____napi_schedule(struct softnet_data *sd,
				     struct napi_struct *napi)
{
	list_add_tail(&napi->poll_list, &sd->poll_list);
	__raise_softirq_irqoff(NET_RX_SOFTIRQ);
}

2.3.2 软中断

函数 net_rx_action() 是收包软中断的入口函数。

static __latent_entropy void net_rx_action(struct softirq_action *h)
{
    // softnet_data 是全局的数据结构,是 per cpu 类型的
    // 在硬中断中将 napi 挂在了 softnet_data 中
    // 在这里将 napi 取出来进行处理
	struct softnet_data *sd = this_cpu_ptr(&softnet_data);

    // 软中断处理的最长时间
	unsigned long time_limit = jiffies +
		usecs_to_jiffies(READ_ONCE(netdev_budget_usecs));

    // 软中断处理的最大报文个数
	int budget = READ_ONCE(netdev_budget);
	LIST_HEAD(list);
	LIST_HEAD(repoll);

	local_irq_disable();
	list_splice_init(&sd->poll_list, &list);
	local_irq_enable();

	for (;;) {
		struct napi_struct *n;

		if (list_empty(&list)) {
			if (!sd_has_rps_ipi_waiting(sd) && list_empty(&repoll))
				goto out;
			break;
		}

		n = list_first_entry(&list, struct napi_struct, poll_list);
        // 调用网卡的 poll 函数进行收包
		budget -= napi_poll(n, &repoll);

		/* If softirq window is exhausted then punt.
		 * Allow this to run for 2 jiffies since which will allow
		 * an average latency of 1.5/HZ.
		 */
        // 当处理的报文数量超过 budget 或者处理的时间超过限制的时候
        // 停止处理,保证内核任务的公平性
		if (unlikely(budget <= 0 ||
			     time_after_eq(jiffies, time_limit))) {
			sd->time_squeeze++;
			break;
		}
	}

	local_irq_disable();

	list_splice_tail_init(&sd->poll_list, &list);
	list_splice_tail(&repoll, &list);
	list_splice(&list, &sd->poll_list);
    // 如果 softnet_data 还没处理完,那么再次触发软中断
	if (!list_empty(&sd->poll_list))
		__raise_softirq_irqoff(NET_RX_SOFTIRQ);

	net_rps_action_and_irq_enable(sd);
out:
	__kfree_skb_flush();
}

函数 napi_poll() 中调用 napi 中的 poll 函数指针来收包。对于 ixgbe 网卡来说,对应的函数是 ixgbe_poll()。

static int napi_poll(struct napi_struct *n, struct list_head *repoll)
{
    ...
	if (test_bit(NAPI_STATE_SCHED, &n->state)) {
		work = n->poll(n, weight);
		trace_napi_poll(n, work, weight);
	}
    ...
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1618581.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣HOT100 - 2. 两数相加

解题思路&#xff1a; 缺位的节点进行补零处理&#xff0c;如97323补充为973023 注意相加的进位问题 class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {ListNode head null, tail null;int carry 0;while (l1 ! null || l2 ! null) {int n1 l…

Go语言并发赋值的安全性

struct并发赋值 type Test struct {X intY int }func main() {var g Testfor i : 0; i < 1000000; i {var wg sync.WaitGroup// 协程 1wg.Add(1)go func() {defer wg.Done()g Test{1, 2}}()// 协程 2wg.Add(1)go func() {defer wg.Done()g Test{3, 4}}()wg.Wait()// 赋值…

Photoshop 2024 25.4蓝猫版_支持参数滤波器和Ai神经滤镜

网盘下载 Photoshop 2024 (Beta) 蓝猫版v25.4.0(2426)全新功能&#xff1a;支持参数滤波器和AI神经滤镜。 最新的PS 25.4 Beta版新增了参数滤波器&#xff08;Parametric Filters&#xff09;功能&#xff0c;而正式版的PS 2024还没有这个功能&#xff0c;只有Beta版才有&…

基础SQL DQL语句

基础查询 select * from 表名; 查询所有字段 create table emp(id int comment 编号,workno varchar(10) comment 工号,name varchar(10) comment 姓名,gender char(1) comment 性别,age tinyint unsigned comment 年龄,idcard char(18) comment 身份证号,worka…

JRT1.5发布演示

JRT1.5演示视频 这是一次思想的解放&#xff0c;这是一次自我的挑战&#xff0c;这是一次涅槃重生。信创、安可、Linux、麒麟、UOS、King、PGSQL、ARM、Java围绕在我周围。JRT在DotNetCore的基础上完成了重生。对我而言&#xff0c;它不仅仅是一套框架那么简单&#xff1b;它更…

【MySQL】InnoDB存储引擎实现事务的原理及MVCC-实现原理

redo log 实现了事务的持久性 如果没有redo log&#xff0c;可能出现脏页现象&#xff0c;导致从缓冲池中更改后加载到硬盘的过程中出现脏页&#xff0c;无法保证持久性。 redo log会记录内存结构中缓冲区中的增删改变化&#xff0c;即时出现脏页&#xff0c;redo log把变化加…

使用keil uv5打开工程显示Device not include in Legacy Device Database怎么解决?

使用keil uv5打开工程显示Device not include in Legacy Device Database怎么解决&#xff1f; 案例&#xff1a;我从gigadevice下载了GD32F303的开发资料&#xff0c;解压后想打开里面的案例。 然后提示 开始我想到的是支持库没有装&#xff0c;就下载了&#xff1a;GigaDe…

Kotlin语法入门-访问和属性修饰符(5)

Kotlin语法入门-访问和属性修饰符(5) 文章目录 Kotlin语法入门-访问和属性修饰符(5)五、访问和属性修饰符1、kotlin修饰符2、internal3、默认修饰符4、open关键字开启继承并实现 五、访问和属性修饰符 1、kotlin修饰符 kotlin在常见的访问修饰符private&#xff0c;protected…

为什么代码签名证书都是“硬证书”?如何选择代码签名证书?

代码签名证书是一种给软件应用程序数字签名的数字证书&#xff0c;它可以确保软件代码的完整性和来源的可信性。代码签名证书可以分为OV代码签名证书和EV代码签名证书&#xff0c;自OV代码签名证书也升级为“硬证书”之后&#xff0c;代码签名证书全部采用“硬证书”。那么&…

Linux--内核移植(二)移植流程及驱动修改

本文来总结一下如何将 NXP 官方提供的 Linux 内核移植到正点原子的 I.MX6U-ALPHA 开发板上。 一、官方开发板内核测试 NXP 提供的 Linux 源码肯定是可以在自己的 I.MX6ULL EVK 开发板上运行下去的&#xff0c;所以我们肯定是以 I.MX6ULL EVK 开发板为参考&#xff0…

信息打点--公众号服务

微信公众号 获取微信公众号的途径https://weixin.sogou.com/ 微信公众号没有第三方服务 Github监控 人员&域名&邮箱 eg&#xff1a;xxx.cn password in:file https://gitee.com/ https://github.com/ https://www.huzhan.com/ 资源搜索 in:name test 仓库标题搜索含有…

sklearn【AUC-ROC】原理,以及绘制ROC曲线!

一、AUC-ROC 介绍 在分类任务中&#xff0c;特别是当数据集中的类别分布不平衡时&#xff0c;评估模型的性能变得尤为重要。AUC-ROC&#xff08;Area Under the Receiver Operating Characteristic Curve&#xff0c;受试者工作特征曲线下的面积&#xff09;是一种有效的评估指…

fawawf

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

Flume的安装及使用

Flume的安装及使用 文章目录 Flume的安装及使用Flume的安装1、上传至虚拟机&#xff0c;并解压2、重命名目录&#xff0c;并配置环境变量3、查看flume版本4、测试flume5、flume的使用 Flume的安装 1、上传至虚拟机&#xff0c;并解压 tar -zxvf apache-flume-1.9.0-bin.tar.g…

Datawhale ChatGPT基础科普

根据课程GitHub - datawhalechina/hugging-llm: HuggingLLM, Hugging Future. 摘写自己不懂得一些地方&#xff0c;具体可以再到以上项目地址 LM&#xff1a;这是ChatGPT的基石的基石。 Transformer&#xff1a;这是ChatGPT的基石&#xff0c;准确来说它的一部分是基石。 G…

UDS诊断故障码DTC

在汽车的各种零部件ECU设计中&#xff0c;开发人员会罗列出他能想到的这些零部件可能发生的所有故障。并且会为每一个故障分配一个代码、代号或数字。这个分配的数据就是DTC&#xff0c;DTC就是每个故障码的身份证号。 五位故障码属于OBD诊断协议。 五位故障码实际上只占用2个…

如何启用启用WordPress调试模式

最近我们的WordPress网站在访问时&#xff0c;经常出现打不开的现象&#xff0c;我们向主机提供商Hostease咨询后&#xff0c;他们提到这是由于WordPress的某个插件导致的问题&#xff0c;我们在将插件版本升级至最新后&#xff0c;这个问题就消失了。为了方便后续的检查&#…

【SpringBoot】-MyBatis详解+单表操作

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀冬瓜的主页&#x1f319; 专栏&#xff1a;【Framework】 主要内容&#xff1a;什么是MyBatis框架&#xff1f;MyBatis框架有什么用&#xff1f;MyBatis实现查询步骤详解。MyBatis实现单表的增删查改。MyBatis模糊查询&…

【智能算法】蜉蝣算法(MA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2020年&#xff0c;K Zervoudakis等人受到自然界蜉蝣交配繁殖行为启发&#xff0c;提出了蜉蝣算法&#xff08;Mayfly Algorithm, MA&#xff09;。 2.算法原理 2.1算法思想 MA灵感来自蜉蝣交配…

简化图卷积 笔记

1 Title Simplifying Graph Convolutional Networks&#xff08;Felix Wu、Tianyi Zhang、Amauri Holanda de、 Souza Jr、Christopher Fifty、Tao Yu、Kilian Q. Weinberger&#xff09;【ICML 2019】 2 Conclusion This paper proposes a simplified graph convolutional m…