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

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);
	}
    ...
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

初识C++ · 类和对象(中)(2)

前言&#xff1a;上篇文章已经介绍了6个默认成员函数中的3个函数&#xff0c;分别是构造函数&#xff0c;析构函数&#xff0c;拷贝构造函数&#xff0c;本文介绍的是后三个&#xff0c;赋值运算符重载&#xff0c;const成员函数&#xff0c;取地址操纵符重载。 目录​​​​​…

全世界IT人苦竞业久矣!美国FTC宣布全面废除员工竞业协议

2023 年 1 月&#xff0c;美国联邦贸易委员会&#xff08;FTC&#xff09;发布声明称&#xff0c;拟在全国范围禁止用人单位与雇员签订竞业禁止性条款。当地时间 4 月 23 日&#xff0c;FTC 宣布全面禁止所有员工&#xff08;包括高级管理人员&#xff09;签署新的竞业禁止协议…

Vue3+Echarts: 浏览器缩小后,图表内容发生重叠

一、问题 Vue3Echarts项目&#xff1a;浏览器缩小后&#xff0c;图表内容发生重叠。本文将提供几个解决上述问题的思路&#xff0c;后续有新的解决思路将在此处进行补充。 二、解决思路 1、动态调整ECharts配置 如果图表容器的尺寸没有随着浏览器窗口的缩小而进行相应地调整…

[Linux_IMX6ULL驱动开发]-设备树简述

目录 设备树的引入 设备树具体框架 设备树的属性 label address-cells和size-cells compatible model status reg 设备树的编译 内核对设备树的处理 plateform_device如何对应plateform_driver 设备树的引入 之前已经学习了解过了总线驱动模型的概念&#xff0c;也…

分类预测 | Matlab实现CNN-BiLSTM-SAM-Attention卷积双向长短期记忆神经网络融合空间注意力机制的数据分类预测

分类预测 | Matlab实现CNN-BiLSTM-SAM-Attention卷积双向长短期记忆神经网络融合空间注意力机制的数据分类预测 目录 分类预测 | Matlab实现CNN-BiLSTM-SAM-Attention卷积双向长短期记忆神经网络融合空间注意力机制的数据分类预测分类效果基本描述程序设计参考资料 分类效果 基…

excel相同行不同列查询

EXCEL中e列和f列是每一行对应的&#xff0c;我想在d列中找和e列一样的元素&#xff0c;然后获取同一行中f列的值 IFERROR(VLOOKUP(D1, E:F, 2, FALSE), "")

STC8H8K64U I2C主机模式相关寄存器

STC8H8K64U I2C主机模式相关寄存器 STC8H8K64U-TSSOP20 I2CCFG I2C配置寄存器 I2CMSCR I2C主机控制寄存器 I2CMSST I2C主机状态寄存器 I2CMSAUX I2C主机辅助控制寄存器 I2CTXD I2C数据发送寄存器 I2CRXD I2C数据接收寄存器 I2CCFG I2C配置寄存器 B7ENI2C ENI2C&#xff1a…

【电子元件】常用的二极管、极管规格参数一览表

目录 1. 常用的二极管规格参数1.1 贴片二极管1.2 直插二极管 2. 常用的三极管规格参数2.1 贴片三极管2.2 直插三极管 参考资料 1. 常用的二极管规格参数 1.1 贴片二极管 型号/封装丝印正向压降(Vf) 反向击穿电压(Vr)平均整流电流(Io)/正向工作电流(If)反向电流(Ir)反向恢复时间…

实验:使用apache + yum实现自制yum仓库

实验准备 Web服务器端&#xff1a;cenos-1&#xff08;IP&#xff1a;10.9.25.33&#xff09; 客户端&#xff1a;centos-2 保证两台机器网络畅通&#xff0c;原yum仓库可用&#xff0c;关闭防火墙和selinux Web服务器端 ①安装httpd并运行&#xff0c;设置开机自启动 安装…

2024五一萌趣嘉年华主题展活动策划案

2024五一国宝大作战 萌趣嘉年华熊猫滚滚来野主题展活动策划案-53P 活动策划信息&#xff1a; 方案页码&#xff1a;53页 文件格式&#xff1a;PPT 方案简介&#xff1a; 活动思路&#xff1a; 五一马上就要到了~再加上全民关注的对象--大熊猫&#xff01;&#xff01; 这…

Echarts异步数据与动画加载

目录 简介 头部代码 这段代码是使用 Echarts 绘制图表的关键部分。首先&#xff0c;初始化了一个 Echarts 实例。然后&#xff0c;通过 Ajax 请求获取数据&#xff0c;并基于此设置图表选项。其中包括颜色、背景色、标题、提示框、图例以及饼图的具体配置。 具体解释如下&a…

面试二十一、红黑树

性质&#xff1a; 插入&#xff1a; 旋转&#xff1a;

【论文阅读】互连网络的负载平衡路由算法 (RLB RLBth)

前言Oblivious Load Balancing 不经意路由负载平衡 1. oblivious routing 不经意/无关路由的背景知识 1. oblivious routing, adaptive routing & minimal/non-minimal routing algorithms 2. Balancing a 1-Dimensional ring: RLB and RLBth 一维 ring 的 RLB and RLBth 1…

强力的应用容器引擎---------Docker的资源控制

目录 一、CPU 资源控制 1.1cgroups有四大功能 1.2设置CPU使用率上限 1.2.1查看CPU使用率 1.2.2进行CPU压力测试 1.2.3设置50%的比例分配CPU使用时间上限 1.3设置CPU资源占用比&#xff08;设置多个容器时才有效&#xff09; 1.3.1创建两个容器为hua1 和hua2&#xff0c…

The_Maya_Society

突然发现自己做了一些逆向题都没有写笔记 今天&#xff0c;发现这道题有意思 1.解压文件 三个文件The Maya Society.html&#xff0c;maim.cc,maya.png 当时我看到这个题的时候&#xff0c;我以为是不是会是js逆向 看来是我蠢了 这三个文件&#xff0c;main.css和maya.png这两…

【算法分析与设计】重复的DNA

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;算法分析与设计 ⛺️稳中求进&#xff0c;晒太阳 题目 DNA序列 由一系列核苷酸组成&#xff0c;缩写为 A, C, G 和 T.。 例如&#xff0c;"ACGAATTCCG" 是一个 DNA序列 。 在研究…

libVLC 制作一款精美的播放器

1.简介 本文将简单介绍使用libVLC制作一款精美的播放器。 开发环境:Visual Studio + Qt插件。 Qt版本:Qt5.9。 libVLC版本:3.0.20。 以下是运行界面效果图:截取其中几张。 右键菜单,功能还是比较齐全。 2.ui界面构成 接下来简单介绍一下ui界面构成。 主界面由播放树…

二维码图片的链接怎么提取?在线获取解码链接的方法

随着现在二维码成为内容展示的主要用途&#xff0c;很多场景下都会需要通过扫码的方式在手机上获取内容。那么在遇到无法扫码的情况时&#xff0c;可以通过提取二维码短链接来访问内容&#xff0c;点击链接跳转到对应的内容页面。 二维码链接想要快速的提取出来&#xff0c;最…

在 vue3 中使用高德地图

前言&#xff1a;定位地图位置所需要的经纬度&#xff0c;可以通过 拾取坐标 获取。 一&#xff1a;快速上手 1. 安装依赖 npm install amap/amap-jsapi-loader # or pnpm add amap/amap-jsapi-loader # or yarn add amap/amap-jsapi-loader 2. 创建组件 src/components/Ma…

面向对象三大特征(python)

目录 1. 封装 为什么使用封装&#xff1f; 如何实现封装&#xff1f; 一个简单的封装示例 二.继承 为什么使用继承&#xff1f; 如何实现继承&#xff1f; 一个简单的继承示例 使用继承的好处 三.多态 为什么使用多态&#xff1f; 如何实现多态&#xff1f; 一个简…