网络数据包收发流程(一):从驱动到协议栈

Linux MAC VLAN实现 概述 1、macvlan是一种网卡虚拟化解决方案,能够将一块物理网卡虚拟成多块虚拟网卡。 2、macvlan 这种技术听起来有点像 VLAN,但它们的实现机制是完全不一样的。macvlan 子接口和原来的主接口是完全独立的,可以单独配置 MAC 地址和 IP 地址,而 VLAN 子接口和主接口共用相同的 MAC 地址。VLAN 用来划分广播域,而 macvlan 共享同一个广播域。 3、macvlan 会根据收到包的目的 MAC 地址判断这个包需要交给哪个虚拟网卡,虚拟网卡再把包交给上层的协议栈处理。 ma 阅读详情
 
网络数据包收发流程(一):从驱动到协议栈

早就想整理网络数据包收发流程了,一直太懒没动笔。今天下决心写了

一、硬件环境

intel82546:PHY与MAC集成在一起的PCI网卡芯片,很强大
bcm5461: PHY芯片,与之对应的MAC是TSEC
TSEC: Three Speed Ethernet Controller,三速以太网控制器,PowerPc 架构CPU里面的MAC模块
注意,TSEC内部有DMA子模块

话说现在的CPU越来越牛叉了,什么功能都往里面加,最常见的如MAC功能。
TSEC只是MAC功能模块的一种,其他架构的cpu也有和TSEC类似的MAC功能模块。
这些集成到CPU芯片上的功能模块有个学名,叫平台设备,即 platform device。

二、网络收包原理


网络驱动收包大致有3种情况:

no NAPI:mac每收到一个以太网包,都会产生一个接收中断给cpu,即完全靠中断方式来收包
缺点是当网络流量很大时,cpu大部分时间都耗在了处理mac的中断。

netpoll:在网络和I/O子系统尚不能完整可用时,模拟了来自指定设备的中断,即轮询收包。
缺点是实时性差

NAPI: 采用 中断 + 轮询 的方式:mac收到一个包来后会产生接收中断,但是马上关闭。
直到收够了netdev_max_backlog个包(默认300),或者收完mac上所有包后,才再打开接收中断
通过sysctl来修改 net.core.netdev_max_backlog
或者通过proc修改 /proc/sys/net/core/netdev_max_backlog


下面只写内核配置成使用NAPI的情况,只写TSEC驱动。(非NAPI的情况和PCI网卡驱动 以后再说)
内核版本 linux 2.6.24

三、NAPI 相关数据结构

每个网络设备(MAC层)都有自己的net_device数据结构,这个结构上有napi_struct。
每当收到数据包时,网络设备驱动会把自己的napi_struct挂到CPU私有变量上。
这样在软中断时,net_rx_action会遍历cpu私有变量的poll_list,
执行上面所挂的napi_struct结构的poll钩子函数,将数据包从驱动传到网络协议栈。

四、内核启动时的准备工作

4.1 初始化网络相关的全局数据结构,并挂载处理网络相关软中断的钩子函数
start_kernel()
--> rest_init()
--> do_basic_setup()
--> do_initcall
-->net_dev_init

__init net_dev_init()
{
//每个CPU都有一个CPU私有变量 _get_cpu_var(softnet_data)
//_get_cpu_var(softnet_data).poll_list很重要,软中断中需要遍历它的

for_each_possible_cpu(i) {
struct softnet_data *queue;
queue = &per_cpu(softnet_data, i);
skb_queue_head_init(&queue->input_pkt_queue);
queue->completion_queue = NULL;
INIT_LIST_HEAD(&queue->poll_list);
queue->backlog.poll = process_backlog;
queue->backlog.weight = weight_p;
}
open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);//在软中断上挂网络发送handler
open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);//在软中断上挂网络接收handler
}

4.2 加载网络设备的驱动
NOTE:这里的网络设备是指MAC层的网络设备,即TSEC和PCI网卡(bcm5461是phy)
在网络设备驱动中创建net_device数据结构,并初始化其钩子函数 open(),close() 等
挂载TSEC的驱动的入口函数是 gfar_probe

// 平台设备 TSEC 的数据结构
static struct platform_driver gfar_driver = {
.probe = gfar_probe,
.remove = gfar_remove,
.driver = {
.name = "fsl-gianfar",
},
};

int gfar_probe(struct platform_device *pdev)
{
dev = alloc_etherdev(sizeof (*priv)); // 创建net_device数据结构

dev->open = gfar_enet_open;
dev->hard_start_xmit = gfar_start_xmit;
dev->tx_timeout = gfar_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
#ifdef CONFIG_GFAR_NAPI
netif_napi_add(dev, &priv->napi,gfar_poll,GFAR_DEV_WEIGHT);//软中断里会调用poll钩子函数
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = gfar_netpoll;
#endif
dev->stop = gfar_close;
dev->change_mtu = gfar_change_mtu;
dev->mtu = 1500;
dev->set_multicast_list = gfar_set_multi;
dev->set_mac_address = gfar_set_mac_address;
dev->ethtool_ops = &gfar_ethtool_ops;
}

五、启用网络设备
5.1 用户调用ifconfig等程序,然后通过ioctl系统调用进入内核
socket的ioctl()系统调用
--> sock_ioctl()
--> dev_ioctl() //判断SIOCSIFFLAGS
--> __dev_get_by_name(net, ifr->ifr_name) //根据名字选net_device
--> dev_change_flags() //判断IFF_UP
--> dev_open(net_device) //调用open钩子函数

对于TSEC来说,挂的钩子函数是 gfar_enet_open(net_device)

5.2 在网络设备的open钩子函数里,分配接收bd,挂中断ISR(包括rx、tx、err),对于TSEC来说

gfar_enet_open
--> 给Rx Tx Bd 分配一致性DMA内存
--> 把Rx Bd的“EA地址”赋给数据结构,物理地址赋给TSEC寄存器
--> 把Tx Bd的“EA地址”赋给数据结构,物理地址赋给TSEC寄存器
--> 给 tx_skbuff 指针数组 分配内存,并初始化为NULL
--> 给 rx_skbuff 指针数组 分配内存,并初始化为NULL

--> 初始化Tx Bd
--> 初始化Rx Bd,提前分配存储以太网包的skb,这里使用的是一次性dma映射
(注意:#define DEFAULT_RX_BUFFER_SIZE 1536保证了skb能存一个以太网包)
rxbdp = priv->rx_bd_base;
for (i = 0; i < priv->rx_ring_size; i++) {
struct sk_buff *skb = NULL;
rxbdp->status = 0;
//这里真正分配skb,并且初始化rxbpd->bufPtr, rxbdpd->length
skb = gfar_new_skb(dev, rxbdp);
priv->rx_skbuff[i] = skb;

rxbdp++;
}
rxbdp--;
rxbdp->status |= RXBD_WRAP; // 给最后一个bd设置标记WRAP标记

--> 注册TSEC相关的中断handler: 错误,接收,发送
request_irq(priv->interruptError, gfar_error, 0, "enet_error", dev)
request_irq(priv->interruptTransmit, gfar_transmit, 0, "enet_tx", dev)//包发送完
request_irq(priv->interruptReceive, gfar_receive, 0, "enet_rx", dev)//包接收完

-->gfar_start(net_device)
// 使能Rx、Tx
// 开启TSEC的 DMA 寄存器
// Mask 掉我们不关心的中断event


最终,TSEC相关的Bd等数据结构应该是下面这个样子的

六、中断里接收以太网包

TSEC的RX已经使能了,网络数据包进入内存的流程为:
网线 --> Rj45网口 --> MDI 差分线
--> bcm5461(PHY芯片进行数模转换) --> MII总线
--> TSEC的DMA Engine 会自动检查下一个可用的Rx bd
--> 把网络数据包 DMA 到 Rx bd 所指向的内存,即skb->data


接收到一个完整的以太网数据包后,TSEC会根据event mask触发一个 Rx 外部中断。
cpu保存现场,根据中断向量,开始执行外部中断处理函数do_IRQ()

do_IRQ 伪代码
{
上半部处理硬中断
查看中断源寄存器,得知是网络外设产生了外部中断
执行网络设备的rx中断handler(设备不同,函数不同,但流程类似,TSEC是gfar_receive)
1. mask 掉 rx event,再来数据包就不会产生rx中断
2. 给napi_struct.state加上 NAPI_STATE_SCHED 状态
3. 挂网络设备自己的napi_struct结构到cpu私有变量_get_cpu_var(softnet_data).poll_list
4. 触发网络接收软中断
下半部处理软中断
依次执行所有软中断handler,包括timer,tasklet等等
执行网络接收的软中断handler net_rx_action
1. 遍历cpu私有变量_get_cpu_var(softnet_data).poll_list
2. 取出poll_list上面挂的napi_struct 结构,执行钩子函数napi_struct.poll()
(设备不同,钩子函数不同,流程类似,TSEC是gfar_poll)
3. 若poll钩子函数处理完所有包,则打开rx event mask,再来数据包的话会产生rx中断
4. 调用napi_complete(napi_struct *n)
把napi_struct 结构从_get_cpu_var(softnet_data).poll_list 上移走
同时去掉 napi_struct.state 的 NAPI_STATE_SCHED 状态
}

6.1 TSEC的接收中断处理函数
gfar_receive
{
#ifdef CONFIG_GFAR_NAPI
// test_and_set当前net_device的napi_struct.state 为 NAPI_STATE_SCHED
// 在软中断里调用 net_rx_action 会检查状态 napi_struct.state

if (netif_rx_schedule_prep(dev, &priv->napi)) {
tempval = gfar_read(&priv->regs->imask);
tempval &= IMASK_RX_DISABLED; //mask掉rx,不再产生rx中断
gfar_write(&priv->regs->imask, tempval);
// 将当前net_device的 napi_struct.poll_list 挂到
// CPU私有变量__get_cpu_var(softnet_data).poll_list 上,并触发软中断
// 所以,在软中断中调用 net_rx_action 的时候,就会执行当前net_device的
// napi_struct.poll()钩子函数,即 gfar_poll()

__netif_rx_schedule(dev, &priv->napi);
}
#else
gfar_clean_rx_ring(dev, priv->rx_ring_size);
#endif
}

6.2 网络接收软中断net_rx_action
net_rx_action()
{
struct list_head *list = &__get_cpu_var(softnet_data).poll_list;
//通过 napi_struct.poll_list, 将N多个 napi_struct 链接到一条链上
//通过 CPU私有变量,我们找到了链头,然后开始遍历这个链


int budget = netdev_budget; //这个值就是 net.core.netdev_max_backlog,通过sysctl来修改

while (!list_empty(list)) {
struct napi_struct *n;
int work, weight;
local_irq_enable();
//从链上取一个 napi_struct 结构(接收中断处理函数里加到链表上的,如gfar_receive)
n = list_entry(list->next, struct napi_struct, poll_list);
weight = n->weight;
work = 0;
if (test_bit(NAPI_STATE_SCHED, &n->state)) //检查状态标记,此标记在接收中断里加上的
work = n->poll(n, weight); //使用NAPI的话,使用的是网络设备自己的napi_struct.poll
//对于TSEC是,是gfar_poll
WARN_ON_ONCE(work > weight);
budget -= work;
local_irq_disable();

if (unlikely(work == weight)) {
if (unlikely(napi_disable_pending(n)))
__napi_complete(n); //操作napi_struct,把去掉NAPI_STATE_SCHED状态,从链表中删去
else
list_move_tail(&n->poll_list, list);
}
netpoll_poll_unlock(have);
}
out:
local_irq_enable();
}

static int gfar_poll(struct napi_struct *napi, int budget)
{
struct gfar_private *priv = container_of(napi, struct gfar_private, napi);
struct net_device *dev = priv->dev; //TSEC对应的网络设备
int howmany;
//根据dev的rx bd,获取skb并送入协议栈,返回处理的skb的个数,即以太网包的个数
howmany = gfar_clean_rx_ring(dev, budget);
// 下面这个判断比较有讲究的
// 收到的包的个数小于budget,代表我们在一个软中断里就全处理完了,所以打开 rx中断
// 要是收到的包的个数大于budget,表示一个软中断里处理不完所有包,那就不打开rx 中断,
// 待到下一个软中断里再接着处理,直到把所有包处理完(即howmany<budget),再打开rx 中断

if (howmany < budget) {
netif_rx_complete(dev, napi);
gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);
//打开 rx 中断,rx 中断是在gfar_receive()中被关闭的
gfar_write(&priv->regs->imask, IMASK_DEFAULT);
}
return howmany;
}

gfar_clean_rx_ring(dev, budget)
{
bdp = priv->cur_rx;
while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) {
rmb();
skb = priv->rx_skbuff[priv->skb_currx];//从rx_skbugg[]中获取skb
howmany++;
dev->stats.rx_packets++;
pkt_len = bdp->length - 4; //从length中去掉以太网包的FCS长度
gfar_process_frame(dev, skb, pkt_len);
dev->stats.rx_bytes += pkt_len;
dev->last_rx = jiffies;
bdp->status &= ~RXBD_STATS; //清rx bd的状态

skb = gfar_new_skb(dev, bdp); // Add another skb for the future
priv->rx_skbuff[priv->skb_currx] = skb;

if (bdp->status & RXBD_WRAP) //更新指向bd的指针
bdp = priv->rx_bd_base; //bd有WARP标记,说明是最后一个bd了,需要“绕回来”
else
bdp++;
priv->skb_currx = (priv->skb_currx + 1) & RX_RING_MOD_MASK(priv->rx_ring_size);
}
priv->cur_rx = bdp; /* Update the current rxbd pointer to be the next one */
return howmany;
}

gfar_process_frame()
-->RECEIVE(skb) //调用netif_receive_skb(skb)进入协议栈


#ifdef CONFIG_GFAR_NAPI
#define RECEIVE(x) netif_receive_skb(x)
#else
#define RECEIVE(x) netif_rx(x)
#endif

------------------------------------ 华丽的分割线 ---------------------------------------


呼,netif_receive_skb(skb)可算到协议栈了,歇会儿....

 

数据包从网卡到应用进程 从空间来看,从网卡到内核空间,再到用户空间,整个过程就是内核将网卡中的数据包拷贝到应用程序。 1.网卡 网卡收到的是数据包的电信号,收到该电信号之后,会将该电信号转化为数字信息,然后进行FCS校验,最后检查数据包的MAC地址是否匹配。 网卡将电信号转化为数字信息之后包含的数据包的信息如下所示: 还原后的数据信息会在网卡上,根据 FCS(帧校验序列,Frame Check Sequence)校验数据,判断数据在传输过程是否因噪音等影响导致信号失真,从而导致数据错误,需要丢弃这种无效的数据包。... 阅读详情

相关推荐

网卡驱动收发包原理深入分析

深入分析82571网卡收发包原理。具有极大代表性,其它所有网卡原理均类似。明白此文档,开发网卡驱动或浏览相关驱动代码,及分析网卡速率瓶颈等工作都会豁然开朗。

linux驱动源码分析(xilinx的千兆网口)

1.涉及的文件: macb_main.c phy_device.c dp83867.c phy_device.c 在内核启动就会运行该驱动,它是匹配PHY和配置phy的重要接口。 2.驱动之间的关系 macb_mii_probe ——> phy_connect_direct ---》 phy_attach_direct phy_init -----》 phy_driver_r...

MENGHUANBEIKE的专栏 3127

PaddleOCR实战:从零开始打造专属OCR模型的完整指南

本文提供了一份从零开始使用PaddleOCR打造专属OCR模型的完整实战指南。内容涵盖环境搭建、数据标注与准备、模型训练与调优,以及最终的模型转换与部署。重点介绍了如何利用PaddleOCR进行模型训练,并整合PPOCRLabel等工具高效准备数据集,帮助开发者快速构建适用于特定场景的高精度OCR识别能力。

rl6adventurer的博客 733

使用GDB+QEMU调试Linux内核环境搭建-进阶篇

  使用GDB+QEMU调试Linux内核环境搭建-进阶篇1 前言2 QEMU命令行参数3 调试的kernel信息设置4 调试virtio_net内核模块的start_xmit函数4.1 启动qemu vm4.2 启动gdb4.2 加载virtio-net内核模块的symbol file4.2.1 获取Guest中virtio-net内核模块的 .text,.data和 .bss4.2.2 在gdb中添加virtio-net内核模块的symbol信息4.2.3 gdb调试start_xmit函数

alexanderwang7的博客 2170

智能变电站协议系列-1、GOOSE、SV、MMS协议简介及GOOSE示例运行问题(IEC61850)

目前有一个新的研究,关于电网使用的GOOSE、SV、MMS协议,并使用相关库来做一下测试。由于goose、sv、mms等都属于IEC61850,因此,需要对IEC61850相关内容有一些了解,都是国际标准,属于电气自动化的一部分,对于自动化或者电气相关领域有接触的专业应该或多或少有一些了解。IEC61850目标是用于变电站自动化、分布式能源、水力和天然气发电厂、风力发电厂、电动汽车作为分布式能源等领域,目前似乎在变电站自动化方面的推广和使用更加广泛。

逍遥游的博客 2万+

别再手动算K值了!ZEMAX中两种锥透镜建模方法详解(标准面 vs. 轴锥面)

本文详细解析了ZEMAX中锥透镜的两种建模方法:标准面与轴锥面。通过对比两者的精度、计算效率和适用场景,帮助工程师在激光整形、光束匀化等应用中做出最优选择,并提供了参数设置和异常排查的实用技巧。

weixin_42548874的博客 321

控制MAC层数据发送过程遇到的问题

做TDMA的一个关键是控制住信号从MAC层向驱动层的数据的发送,从原来的事件触发转换成定时器触发。在原有的80211协议中有两个地方会触发该操作。一个操作是: (1)ieee80211_subif_start_xmit (tx.c): -Addsthe 802.11 header. -Initializestransmission time in devtrans_start (2)ie

九阳神功爱喝茶的专栏 2366

工业视觉检测实战指南:从硬件配置到AI算法部署

本文提供了一套完整的工业视觉检测系统搭建实战指南。从硬件选型(相机、光源、镜头、工控机)到AI算法(深度学习模型)部署,详细解析了如何为生产线配置一双“会思考的眼睛”。重点探讨了深度学习算法在复杂缺陷识别中的应用,并分享了模型训练、优化及云边协同部署的实战心得,旨在帮助工程师实现从实验室Demo到7x24小时稳定运行的工业落地。

weixin_29051193的博客 458

linux内核收发包原理,网络数据包收发流程(一):从驱动到协议栈

早就想整理网络数据包收发流程了,一直太懒没动笔。今天下决心写了一、硬件环境intel82546:PHY与MAC集成在一起的PCI网卡芯片,很强大bcm5461: PHY芯片,与之对应的MAC是TSECTSEC: Three Speed Ethernet Controller,三速以太网控制器,PowerPc 架构CPU里面的MAC模块注意,TSEC内部有DMA子模块话说现在的CPU越...

weixin_36355328的博客 965

深空探测处理软件USGS-ISIS的下载安装

深空探测处理软件USGS-ISIS下载安装教程

qq_31721595的博客 3313

永磁同步电机自抗扰控制ADRC(位置环/速度环控制器)Matlab/Simulink仿真(S-Function)

ESO是ADRC的核心,其作用是跟踪被控对象的输出,同时给出受控对象的各阶状态变量以及系统总扰动的实时估计,将其补偿到控制器中,实现动态补偿线性化。NLSEF采用非线性函数把TD产生的跟踪及微分信号与ESO给出的状态变量估计值之间的误差进行非线性组合得到初级控制量,然后同总扰动的补偿量一起生成最终的控制信号。自抗扰控制(ADRC)技术的核心思想是将简单的积分串联型视为标准模型,把系统模型中不同于标准型的部分和外部干扰之和视为总扰动,并将其扩张为一个新的状态。看作系统内外部扰动之和。令一个高阶状态变量为。

m0_66341377的博客 7615

e1000e网络数据包收发流程(一):从驱动到协议栈

学习资料 转载:http://blog.chinaunix.net/uid-24148050-id-464587.html 一、硬件环境 intel82546:PHY与MAC集成在一起的PCI网卡芯片,很强大bcm5461: PHY芯片,与之对应的MAC是TSECTSEC: Three Speed Ethernet Controller,三速以太网控制器,PowerPc 架...

wangyangzhizunwudi的博客 775

杰理AC696N使用ADC检测电位器调音量教程

本教程详细介绍了在杰理AC696N芯片上使用ADC功能检测电位器电压值的方法。主要内容包括:硬件连接方式(电位器VCC/OUT/GND分别接3.3V、PA3引脚和地线),10位ADC配置(0-1023对应0-3.3V),以及核心代码实现。关键步骤涉及ADC采样通道初始化、GPIO引脚配置(关闭数字输入和上下拉电阻),以及通过定时器实现50Hz采样频率。教程还详解了ADC值到系统音量的线性映射算法,并提供了完整的代码示例和调试方法,帮助开发者实现电位器实时控制音量的功能。

qq_42014563的博客 1246

网络数据包收发流程(一):从驱动到协议栈 .

http://blog.youkuaiyun.com/jw212/article/details/6738457 早就想整理网络数据包收发流程了,一直太懒没动笔。今天下决心写了 一、硬件环境 intel82546:PHY与MAC集成在一起的PCI网卡芯片,很强大 bcm5461:   PHY芯片,与之对应的MAC是TSEC TSEC:      Three Speed Ethernet

happylife1527的专栏 687

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

上一篇: MIPS GCC 嵌入式汇编
下一篇: 网络数据包收发流程(二):不配置NAPI的情况
LonlyGod
博客等级 码龄15年 32粉丝 1原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值