e1000网络驱动分析

简介: e1000网络驱动分析 e1000是intel千兆以太网卡的驱动源码。官方关于驱动的使用可以参考如下链接。 https://www.intel.cn/content/www/cn/zh/support/articles/000006866/network-and-i-o/ethernet-products.html 本文主要结合e1000驱动的源码进行分析. 版本是#define DRV_VERSION "7.3.21-k8-NAPI",这个是Ubuntu4.4.0-63版本上的内核。

e1000是intel千兆以太网卡的驱动源码。官方关于驱动的使用可以参考如下链接。

https://www.intel.cn/content/www/cn/zh/support/articles/000006866/network-and-i-o/ethernet-products.html

本文主要结合e1000驱动的源码进行分析. 版本是#define DRV_VERSION "7.3.21-k8-NAPI",这个是Ubuntu4.4.0-63版本上的内核。代码文件如下几个:

e1000_ethtool.c

e1000.h

e1000_hw.c

e1000_hw.h

e1000_main.c

e1000_osdep.h

e1000_param.c

Makefile

1.2.1 加载和卸载

驱动加载时候先运行e1000_init_module函数,主要实现注册到PCI子系统中,代码如下。

static int __init e1000_init_module(void)

{

        int ret;

        pr_info("%s - version %s\n", e1000_driver_string, e1000_driver_version);

 

        pr_info("%s\n", e1000_copyright);

 

        ret = pci_register_driver(&e1000_driver);

        if (copybreak != COPYBREAK_DEFAULT) {

                if (copybreak == 0)

                        pr_info("copybreak disabled\n");

                else

                        pr_info("copybreak enabled for "

                                   "packets <= %u bytes\n", copybreak);

        }

        return ret;

}

module_init(e1000_init_module);

            通过调用pci_register_driver函数注册e1000_driver(pci_driver为驱动主结构)驱动,将其注册到PCI设备的链表中。

     具体如下:

pci_register_driver会调用

__pci_register_driver(drivers/pci/pci-driver.c)函数,该函数调用driver_register函数. driver_register会检测是否已在总线上已经注册或是否需要更新,没问题就问题就调用bus_add_driver函数,bus_add_driver将驱动加到总线上,然后调用pci_driver中定义的probe函数。

还有driver_add_groups函数创建组,kobject_uevent函数发送一个uevent通知用户层。

     部分原型如下:

int __pci_register_driver(struct pci_driver *drv, struct module *owner,

                          const char *mod_name)

            我们知道在bus_add_driver函数中,会调用pci_driver结构体指向的probe函数。e1000的probe函数是e1000_probe,该函数执行完也就是驱动和设备绑定了, e1000_probe函数完成设备初始化工作,包括pci_dev结构体,操作系统初始化,e1000_adapter结构体配置,和硬件重启工作。设备device也初始化完了。

            卸载是e1000_exit_module函数,直接调用pci_unregister_driver,和pci_register_driver函数是对立的。

static void __exit e1000_exit_module(void)

{

        pci_unregister_driver(&e1000_driver);

}

module_exit(e1000_exit_module);

驱动支持的参数是copybreak,默认是256.

module_param(copybreak, uint, 0644);

MODULE_PARM_DESC(copybreak,

                  "Maximum size of packet that is copied to a new buffer on receive");

1.2.2 网络驱动关键数据结构

设计到的主要结构体有pci_dev,e1000_adapter和net_device。

pci_dev结构是网卡的配置空间和I/O与内存区,net_device结构则向内核提供了操作网卡的抽象接口。

e1000_adapter结构除了体现相应的硬件无关性外,还管理了发送与接收数据包的相应缓冲空间,网卡的物理地址空间映射后的虚拟地址也在此结构中保存。

e1000_adapter结构中的e1000_hw结构主要保存网卡的硬件参数,其值就是通过读取pci_dev的内容获取而来的。

pci_device_id: PCI Device ID Table

e1000_adapter:设备私有数据结构,定义在e1000.h中。

e1000_hw:硬件相关的结构体,定义在e1000_hw.h中。

pci_driver表示pci驱动。

static struct pci_driver e1000_driver = {

        .name     = e1000_driver_name,//驱动的名字,必须唯一

        .id_table = e1000_pci_tbl,//指向pci_device_id表的指针

        .probe    = e1000_probe,//驱动的probe函数指针,被PCI核心调用。

        .remove   = e1000_remove,//当有pci_dev从系统移除时候调用函数的指针,或驱动被卸载时

#ifdef CONFIG_PM                  //如果定义电源管理

        /* Power Management Hooks */

        .suspend  = e1000_suspend,//pci_dev被挂起时调用的指针。

        .resume   = e1000_resume,//pci_dev恢复时调用的函数指针

#endif

        .shutdown = e1000_shutdown,//系统关闭时候调用

        .err_handler = &e1000_err_handler//错误处理句柄

};

static const struct pci_error_handlers e1000_err_handler = {

        .error_detected = e1000_io_error_detected,

        .slot_reset = e1000_io_slot_reset,

        .resume = e1000_io_resume,

};

pci_error_handlers结构体是错误发现,插槽重启的函数句柄。

            其中net_device_ops的定义如下, 包含了网络设备接口操作函数:

static const struct net_device_ops e1000_netdev_ops = {

        .ndo_open               = e1000_open,

        .ndo_stop               = e1000_close,

        .ndo_start_xmit         = e1000_xmit_frame,

        .ndo_get_stats          = e1000_get_stats,

        .ndo_set_rx_mode        = e1000_set_rx_mode,

        .ndo_set_mac_address    = e1000_set_mac,

        .ndo_tx_timeout         = e1000_tx_timeout,

        .ndo_change_mtu         = e1000_change_mtu,

        .ndo_do_ioctl           = e1000_ioctl,

        .ndo_validate_addr      = eth_validate_addr,

        .ndo_vlan_rx_add_vid    = e1000_vlan_rx_add_vid,

        .ndo_vlan_rx_kill_vid   = e1000_vlan_rx_kill_vid,

#ifdef CONFIG_NET_POLL_CONTROLLER

        .ndo_poll_controller    = e1000_netpoll,

#endif

        .ndo_fix_features       = e1000_fix_features,

        .ndo_set_features       = e1000_set_features,

};

            用来填充net_device结构体。

结构体e1000_buffer是buffer_info缓冲块数组的单元,用与接收从硬件传送过来的数据(是skb的包裹函数)

struct e1000_buffer {

        struct sk_buff *skb;

        dma_addr_t dma;

        struct page *page;

        unsigned long time_stamp;

        u16 length;

        u16 next_to_watch;

        unsigned int segs;

        unsigned int bytecount;

        u16 mapped_as_page;

};

            结构体e1000_tx_ring指向ring内存的描述符。

struct e1000_tx_ring {

        /* pointer to the descriptor ring memory */

        void *desc;

        /* physical address of the descriptor ring */

        dma_addr_t dma;

        /* length of descriptor ring in bytes */

        unsigned int size;

        /* number of descriptors in the ring */

        unsigned int count;

        /* next descriptor to associate a buffer with */

        unsigned int next_to_use;

        /* next descriptor to check for DD status bit */

        unsigned int next_to_clean;

        /* array of buffer information structs */

        struct e1000_buffer *buffer_info;

        u16 tdh;

        u16 tdt;

        bool last_tx_tso;

};

借用《Linux-千兆网卡驱动实现机制浅析》一张图如下,比较直观的说明了e1000_adaper,e1000_rx_ring,e1000_rx_desc,e1000_buffer结构体之间的关系:

a1c9f8dd1626838cb699317d2291314260db0876

            其中缓冲块的初始化由函数e1000_tx_map实现。

 

1.2.3 设备打开和关闭

根据e1000_netdev_ops结构体中的定义,打开设备使用的函数为e1000_open。当网络接口激活的时候被调用。

            关闭函数为e1000_close也定义在e1000_netdev_ops结构体中,用于禁用网络接口。

 

1.2.4 数据接收和发送

数据传输是net_device接口的关键组件。驱动程序提供一个hard_start_xmit函数,协议层在发送时调用它来向下传递数据包,现在也是定义在net_dev_ops结构体中如下:

static const struct net_device_ops e1000_netdev_ops = {

        .ndo_open               = e1000_open,

        .ndo_stop               = e1000_close,

        .ndo_start_xmit         = e1000_xmit_frame,

        .ndo_get_stats          = e1000_get_stats,

        .ndo_set_rx_mode        = e1000_set_rx_mode,

        .ndo_set_mac_address    = e1000_set_mac,

        .ndo_tx_timeout         = e1000_tx_timeout,

        .ndo_change_mtu         = e1000_change_mtu,

        .ndo_do_ioctl           = e1000_ioctl,

        .ndo_validate_addr      = eth_validate_addr,

        .ndo_vlan_rx_add_vid    = e1000_vlan_rx_add_vid,

        .ndo_vlan_rx_kill_vid   = e1000_vlan_rx_kill_vid,

#ifdef CONFIG_NET_POLL_CONTROLLER

        .ndo_poll_controller    = e1000_netpoll,

#endif

        .ndo_fix_features       = e1000_fix_features,

        .ndo_set_features       = e1000_set_features,

};

可以看到发送函数为e1000_xmit_frame

 现在的中断驱动程序都采用的是NAPI方式,需要提供poll函数,本驱动中是e1000_netpoll轮询函数。

            当有新数据包要发送时候,首先上层协议调用e1000_xmit_frame函数,然后在该函数中调用e1000_tx_queue来根据相应的参数找到缓冲块存放,缓冲块中有dma成员,表示该数据包所在的总线地址,控制总线会把内容映射到总线地址,然后由网卡传送出去。

            当有新数据包达到时,首先触动中断处理函数e1000_intr,该中断函数会将数据包放在buffer_info的缓冲块中(),就是讲总线地址指向的内容复制到skb中,然后根据skb中的协议将其传给上层协议的接收函数。

 

1.2.5 个别函数分析说明

e1000_get_hw_dev函数通过结构体e1000_hw来得到net_device.

e1000_request_irq函数,通过request_irq函数申请一个终端号,同时关联终端处理函数e1000_intr。

static int e1000_request_irq(struct e1000_adapter *adapter)

e1000_intr函数为终端处理函数,硬中断。

static irqreturn_t e1000_intr(int irq, void *data)

e1000_free_irq函数,调用free_irq释放中断。

e1000_irq_disable函数禁用中断。

e1000_setup_all_tx_resources函数分配发送描述符。

e1000_setup_all_rx_resources函数分配接收描述符

  netif_carrier_on/netif_carrier_off/netif_carrier_ok网卡驱动程序通过来和内核网络子系统传递信息。netif_carrier_on,告诉内核子系统网络链接完整。netif_carrier_off,告诉内核子系统网络断开。netif_carrier_ok,查询网络断开还是链接。以上函数主要是改变net_device dev的state状态来告知内核链路状态的变化。

e1000_power_up_phy函数给物理设备上电。

 

1.2.6 单独编译e1000驱动模块

路径在drivers下,找到对应驱动如net,例如:

drivers/net/ethernet/intel/e1000

可以单独复制出来,

然后在Makefile中增加如下代码:

KERN_DIR = /usr/src/linux-headers-4.4.0-63-generic

all:

    make -C $(KERN_DIR) M=`pwd` modules

clean:

     make -C $(KERN_DIR) M=`pwd` modules clean

-C 参数后面加config文件所在的文件夹,也可以是

KERN_DIR = /lib/modules/`uname -r`/build

M:参数后面是要编译的模块

 

1.2.7 参考

Linux-千兆网卡驱动实现机制浅析 关于PCIE的粗的理解可以查看此文

目录
相关文章
|
18天前
|
数据采集 缓存 定位技术
网络延迟对Python爬虫速度的影响分析
网络延迟对Python爬虫速度的影响分析
|
2月前
|
机器学习/深度学习 数据采集 存储
时间序列预测新突破:深入解析循环神经网络(RNN)在金融数据分析中的应用
【10月更文挑战第7天】时间序列预测是数据科学领域的一个重要课题,特别是在金融行业中。准确的时间序列预测能够帮助投资者做出更明智的决策,比如股票价格预测、汇率变动预测等。近年来,随着深度学习技术的发展,尤其是循环神经网络(Recurrent Neural Networks, RNNs)及其变体如长短期记忆网络(LSTM)和门控循环单元(GRU),在处理时间序列数据方面展现出了巨大的潜力。本文将探讨RNN的基本概念,并通过具体的代码示例展示如何使用这些模型来进行金融数据分析。
270 2
|
2月前
|
机器学习/深度学习 数据采集 人工智能
未来的守护神:AI驱动的网络安全之盾,如何用智慧的光芒驱散网络黑暗势力?揭秘高科技防御系统背后的惊天秘密!
【10月更文挑战第3天】随着网络技术的发展,网络安全问题日益严峻,传统防御手段已显不足。本文探讨了构建AI驱动的自适应网络安全防御系统的必要性及其关键环节:数据采集、行为分析、威胁识别、响应决策和执行。通过Python库(如scapy、scikit-learn和TensorFlow)的应用实例,展示了如何利用AI技术提升网络安全防护水平。这种系统能够实时监控、智能分析并自动化响应,显著提高防护效率与准确性,为数字世界提供更强大的安全保障。
61 2
|
19天前
|
存储 安全 网络安全
网络安全法律框架:全球视角下的合规性分析
网络安全法律框架:全球视角下的合规性分析
32 1
|
28天前
|
网络协议 安全 算法
网络空间安全之一个WH的超前沿全栈技术深入学习之路(9):WireShark 简介和抓包原理及实战过程一条龙全线分析——就怕你学成黑客啦!
实战:WireShark 抓包及快速定位数据包技巧、使用 WireShark 对常用协议抓包并分析原理 、WireShark 抓包解决服务器被黑上不了网等具体操作详解步骤;精典图示举例说明、注意点及常见报错问题所对应的解决方法IKUN和I原们你这要是学不会我直接退出江湖;好吧!!!
网络空间安全之一个WH的超前沿全栈技术深入学习之路(9):WireShark 简介和抓包原理及实战过程一条龙全线分析——就怕你学成黑客啦!
|
2月前
|
存储 安全 网络安全
云端盾牌:云计算时代的网络安全守护在数字化浪潮中,云计算以其高效、灵活的特性成为企业转型的加速器。然而,伴随其迅猛发展,网络安全问题亦如影随形,成为悬在每个组织头顶的达摩克利斯之剑。本文旨在探讨云计算服务中的网络安全挑战,分析信息安全的重要性,并提出相应对策,以期为企业构建一道坚实的云端防护网。
在当今这个数据驱动的时代,云计算已成为推动创新与效率的关键力量。它允许用户随时随地访问强大的计算资源,降低了企业的运营成本,加速了产品上市时间。但随之而来的网络威胁也日益猖獗,尤其是对于依赖云服务的企业而言,数据泄露、身份盗用等安全事件频发,不仅造成经济损失,更严重损害品牌信誉。本文深入剖析云计算环境中的安全风险,强调建立健全的信息安全管理机制的重要性,并分享一系列有效策略,旨在帮助企业和个人用户在享受云服务带来的便利的同时,也能构筑起强有力的网络防线。
|
28天前
|
网络协议 安全 算法
网络空间安全之一个WH的超前沿全栈技术深入学习之路(9-2):WireShark 简介和抓包原理及实战过程一条龙全线分析——就怕你学成黑客啦!
实战:WireShark 抓包及快速定位数据包技巧、使用 WireShark 对常用协议抓包并分析原理 、WireShark 抓包解决服务器被黑上不了网等具体操作详解步骤;精典图示举例说明、注意点及常见报错问题所对应的解决方法IKUN和I原们你这要是学不会我直接退出江湖;好吧!!!
|
2月前
|
安全 网络安全 云计算
云计算与网络安全:技术融合与挑战分析
【9月更文挑战第31天】本文将深入探讨云计算和网络安全之间的关系,包括云服务、网络安全、信息安全等技术领域。我们将从云计算的基本概念和特性出发,分析其在网络安全方面的优势和挑战,并探讨如何通过技术和策略来提高云计算的安全性。同时,我们也将讨论网络安全在云计算环境下的重要性,以及如何通过有效的安全措施来保护云服务的安全。最后,我们将通过代码示例来展示如何在云计算环境中实现网络安全。
65 3
|
2月前
|
安全 网络协议 物联网
物联网僵尸网络和 DDoS 攻击的 CERT 分析
物联网僵尸网络和 DDoS 攻击的 CERT 分析
|
3月前
|
缓存 网络协议 网络架构
网络抓包分析【IP,ICMP,ARP】以及 IP数据报,MAC帧,ICMP报和ARP报的数据报格式
本文详细介绍了如何使用网络抓包工具Wireshark进行网络抓包分析,包括以太网v2 MAC帧、IP数据报、ICMP报文和ARP报文的格式,以及不同网络通信的过程。文章通过抓包分析展示了IP数据报、ICMP数据报和ARP数据报的具体信息,包括MAC地址、IP地址、ICMP类型和代码、以及ARP的硬件类型、协议类型、操作类型等。通过这些分析,可以更好地理解网络协议的工作机制和数据传输过程。
网络抓包分析【IP,ICMP,ARP】以及 IP数据报,MAC帧,ICMP报和ARP报的数据报格式