ixgbe网卡驱动Ⅰ----网卡初始化及收发数据概览

目录

1 网卡中断逻辑

2 操作系统网络初始化

3 网卡初始化

4 网卡硬中断处理流程

5 软中断处理数据

6 关于接收数据时 RPS


1 网卡中断逻辑

网卡接到数据后,触发中断,内核回调中断处理程序 ISR. 一般中断都会分成上半部和下半部 (bh), 上半部执行时间短,不允许程序休眠,并且此时中断处于禁止状态。下半部有多种实现,网卡使用软中断,由 ksoftirqd 处理,耗时较长。

在石器时代,网卡中断只由一个 cpu 处理,但是在大数据高吐吞时,就会把某个核(一般是 cpu0) 拖跨,一直频繁的响应中断,严重影响网卡吞吐。所以就有了硬件层面的 RSS 概念,receive side scaling,网卡实现多个队列,每个队列一个中断并且绑定到不同 cpu, 将中断分摊到多个 cpu. 如果硬件不支持硬件队列呢?就有了 RPS 概念,在软件层面模拟一个队列,如果有 RSS,就没必要用 RPS

读取网卡数据有两种方式:中断和轮循。只有中断会使 cpu 负载变得很高,一直忙于响应中断。只用轮循时延得不到保证,会使其它进程恶心。为了高效的使用 cpu,现代操作系统都是采用 中断 + 轮循 的方式,这就是下面会提到的 NAPI

2 操作系统网络初始化

在网卡正式工作前,先由内核为网络做准备工作。内核调用 net/dev/core.c net_dev_init 初始化网络,主要做如下工作:

  1. dev_proc_init 在操作系统 /proc/net 目录下生成相关文件,/proc/net/dev, /proc/net/ptype, /proc/net/dev_mcast 存放一些统计及状态信息
  2. netdev_kobject_init 创建操作系统目录 /sys/class/net, 当网卡启动后都会在这里进行注册,方便查看
  3. 初始化全局 ptype_all 结构,这里存放不同协义族如何处理接收数据包的回调。打印 /proc/net/ptype 文件可以看到全部,比如 ip_rcv, 这一块很重要
  4. for_each_possible_cpu 初始化每个 cpu 的局部变量,per_cpu 是一个宏,表示他所引用的变量,每个 cpu 都有一个私有的。
    for_each_possible_cpu(i) {
        struct work_struct *flush = per_cpu_ptr(&flush_works, i);
        struct softnet_data *sd = &per_cpu(softnet_data, i);

        INIT_WORK(flush, flush_backlog);
        skb_queue_head_init(&sd->input_pkt_queue);
        skb_queue_head_init(&sd->process_queue);
        INIT_LIST_HEAD(&sd->poll_list);
        sd->output_queue_tailp = &sd->output_queue;
#ifdef CONFIG_RPS
        sd->csd.func = rps_trigger_softirq;
        sd->csd.info = sd;
        sd->cpu = i;
#endif
        sd->backlog.poll = process_backlog;
        sd->backlog.weight = weight_p;
    }

work_struct 每个 cpu 都会有一个,如果网卡消失了,那么回调 flush_backlog 释放属于这个网卡的 skb. 接下来最重要的就是 softnet_data 结构体,非常重要,所有网卡的数据包都是挂载到某个 cpu, 就是放在这个结构体里。如果开启了 RPS,设置软中断回调。最后设置 backlog.poll 回调 process_backlog,这个就是 NAPI Poll

  1. open_softirq 设置软中断传输发送回调分别是 net_tx_action, net_rx_action
  2. cpuhp_setup_state_nocalls 注册通知事件,如果 cpu 坏掉,那么 dev_cpu_dead 将坏掉 cpu 的本地网络包,转移到其它 cpu

3 网卡初始化

分为两个阶段,在驱动加载时调用 ixgbe_probe 初始化硬件一次,当网卡激活,也就是 if up 时调用 ixgbe_open 初始化与操作系统相关工作。这里以 intel 网卡举例,老的是 igb,新的驱动是 igbxe, 代码路径 drivers/net/ethernet/intel/ixgbe. 有的商用服务器会用 broadcom 网卡,价格便宜一些,驱动原理相近。

代码有些涉及硬件知识,不影响分析的情况下忽略。这里涉及几个重要结构体,网卡本身是一个 pci 设备,所以自然是 pci_dev, 并且有 pci_dev_id. 所有网卡在内核都用 net_device 来抽象表示。

ixgbe_probe

函数声明如下,传入 pci_dev 设备

int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
  1. 激活网卡内存,配置设备 DMA, 在内核虚拟地址空间申请地址,保留网卡 io, memory 地址
  2. 最重要的是调用 alloc_etherdev_mq 在内核分配空间,生成 net_device 结构并初始化。这里重点关注 indices 用来初始化 RSS 队列,默认 64 个队列。
  3. ioremap 将网卡 pci 地址重新映射,赋值给 hw_addr, io_addr
  4. ixgbe_set_ethtool_ops 设置 ethtool 操作接口,这样将网卡操作抽象出来。
  5. ixgbe_sw_init 初始化网卡私有数据,这里看到设置 RSS 队列个数逻辑 min_t(int, ixgbe_max_rss_indices(adapter), num_online_cpus()),取硬件队列和 cpu 的最小值。
  6. ixgbe_enable_sriov 打开 sriov 硬件虚拟化
  7. eth_platform_get_mac_address 设置网卡硬件 mac 地址和 mac filter
  8. ixgbe_init_interrupt_scheme 申请中断,优先判断硬件是否支持 MSI-X, 检查中断向量 num_q_vector 个数,取 cpu 核数和硬件队列最小值,分配中断向量空间 msix_entries. 查看测试机,由于 cpu 总核数 40,所以 num_q_vector 也是 40. 如果不支持 MSI-X 那么设置 num_q_vectors 为 1.
  9. ixgbe_alloc_q_vectors 在内核申请空间,可以看到队列结构体 ixgbe_ring 是和 ixgbe_q_vector 一起分配的,将 vector 中断和队列绑定,rxtx 两个队列同时绑定到一个 vector 中断,理想情况下一对队列拥有一个中断,如果中断不够用,那么会有多个共用。这里还涉及到 cpu 亲缘性和 numa. 最后 netif_napi_add 给每个中断设置 NAPI_POLL 回调 ixgbe_poll 。
  10. ixgbe_cache_ring_rss 将队列 ixgbe_ring 与硬件 RSS 关联对应
  11. register_netdev 该网卡设备注册到名字空间

ixgbe_probe 主要功能就是生成 net_device 结构, 读取硬件 mac 地址,将网卡注册到系统中。由于支持 msi-x 根据 cpu 个数配置硬件中断和队列,设置 ixgbe_poll 回调。

ixgbe_open

第二个初始化的函数,在网卡激活时调用。其本功能很简单,申请所有 rx, tx 队列资源,申请中断,配置 msi-x.

  1. ixgbe_setup_all_tx_resources 分配发送队列,每个队列申请自己的 DMA 地址,总长度 sizeof(struct ixgbe_tx_buffer) * tx_ring->count, 其中 count 就是大家常说的网卡队列 ring buffer 个数,默认 512,一般都要调大,防止丢包。
  2. ixgbe_setup_all_rx_resources 同理,分配接收队列,我的测试机是 40 个列队。
  3. ixgbe_configure 设置网卡虚拟化,接收模式,flow director 等等,最后调用 ixgbe_configure_tx, ixgbe_configure_rx 网卡硬件配置接收发送队列。
  4. ixgbe_request_irq 向操作系统申请 irq 硬中断,当前网卡支持 msi-x,最终调用 ixgbe_request_msix_irqs 申请中断向量,并配置中断回调 ixgbe_msix_clean_rings

4 网卡硬中断处理流程

这个图非常简单,大致流程如下:

               ┌────────────────┐   4.Call      ┌───────────────┐   5.Raise     ┌─────────────────┐
               │                │    Back       │               │   softirq     │                 │
               │      CPU       │─────────────▶ │   NIC DRIVER  │──────────────▶│    KSOFTSWAPD   │
               │                │               │               │               │                 │
               └────────────────┘               └───────────────┘               └─────────────────┘
                        ▲                                                                │         
             3.Raise    │                                                                │         
               IRQ      │                                                                │         
                        │                                                                │         
                        │                                                                │         
               ┌────────────────┐                                                        ▼         
               │                │                                                                  
               │                │                                                                  
               │                │                                                                  
               │      NIC       │                                                                  
  1. packet    │                │                                                                  
   ──────────▶ │                │                                                                  
               │                │                                                                  
               │                │           ┌────┬────┬────┬────┬────┬────┬────┐                   
               │   ┌────────┐   │ 2. DMA    │    │    │    │    │    │    │    │                   
               │   │  DMA   │ ──┼─────────▶ │    │    │    │    │    │    │    │                   
               │   └────────┘   │           │    │    │    │    │    │    │    │                   
               └────────────────┘           └────┴────┴────┴────┴────┴────┴────┘                   
                                                                                                   
                                                       Ring buffer                                 
                                                                                                                                                                                                  
  1. 网卡接收数据包,如果没有开启混杂模式,那么 filter 过滤掉不属于自己 mac 的包。
  2. DMA 控制器将数据包写到内存。
  3. 网卡通知 cpu 产生硬中断,触发 callback ixgbe_msix_clean_rings, 此时 q_vector 网卡硬中断处于关闭状态。
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);
}

将等待轮循列表添加到 cpu 私有变量 softnet_data 里,然后触发软中断 NET_RX_SOFTIRQ

  1. 每个 cpu 都有一个软中断守护进程 KSOFTIRQD, 由他负责回调处理。

其实这里有个疑问,ring buffer 每个队列有一个,那数据包到来后如何选择写到哪个呢?另外,直到此时网卡硬中断还处于关闭状态。

5 软中断处理数据

在操作系统初始化得知,软中断回调 net_rx_action 来完成数据包的轮循,但不是死循环,有两个退出条件:处理数据超过 budget 配额或是超时一定时间,代码写写 300个和 2000us. 如果有未处理数据,再次触发软中断,等待调度。最后打开硬中断。

这个逻辑在函数 ixgbe_poll 里,当前 cpu 不止一个队列,所以会将配额尽可能得分配。对于发送,接收队列分别调用 ixgbe_clean_tx_irq, ixgbe_clean_rx_irq 处理。

ixgbe_clean_rx_irq: 每个 ixgbe_ring 保存一个环形数组,ring buffer,读出 skb 数据后调用 ixgbe_rx_skb 开始处理,如果开启了 GRO 那么合并小包,然后再查看是否有 RPS 逻辑,如果有就走,没有就调用 __netif_receive_skb 根据上文提到的 ptype_all,来选择是 ip_rcv, 还是 ipv6_rcv 再继续上层逻辑。调用 ip_rcv 之后会回调 NF_INET_PRE_ROUTING hook, 也就是大家都熟悉的 iptables pre_routing 链。

ixgbe_clean_tx_irq: 每次限制发送 q_vector->tx.work_limit 个报文,底层网卡逻辑暂时不看了。

6 关于接收数据时 RPS

ixgbe_rx_skb 处理数据包时,先判断是否开启 RPS, 如果 get_rps_cpu 返回 cpu, 那么将 skb 数据包入队列 softnet_data.input_pkt_queue, 最后触发软中断 net_rx_action,那么这个软中断回调是哪个函数呢?是 process_backlog,在 net_dev_init 里指定。最终还是走到 __netif_receive_skb 逻辑。

其间队列可能满,会丢弃包,通过 netdev_max_backlog 来调节 backlog 队列大小。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值