第7步中,在igb_probe初始化过程中,还调用到了igb_alloc_q_vector 。他注册了一个NAPI机制所必须的poll函数,对于igb网卡驱动来说,这个函数就是igb_poll,如下代码所示 。
static int igb_alloc_q_vector(struct igb_adapter *adapter, int v_count, int v_idx, int txr_count, int txr_idx, int rxr_count, int rxr_idx){ ...... /* initialize NAPI */ netif_napi_add(adapter->netdev, &q_vector->napi, igb_poll, 64);}
2.5 启动网卡当上面的初始化都完成以后,就可以启动网卡了 。回忆前面网卡驱动初始化时,我们提到了驱动向内核注册了 structure net_device_ops 变量,它包含着网卡启用、发包、设置mac 地址等回调函数(函数指针) 。当启用一个网卡时(例如,通过 ifconfig eth0 up),net_device_ops 中的 igb_open方法会被调用 。它通常会做以下事情:
文章插图
图7 启动网卡
//file: drivers/net/ethernet/intel/igb/igb_main.cstatic int __igb_open(struct net_device *netdev, bool resuming){ /* allocate transmit descriptors */ err = igb_setup_all_tx_resources(adapter); /* allocate receive descriptors */ err = igb_setup_all_rx_resources(adapter); /* 注册中断处理函数 */ err = igb_request_irq(adapter); if (err) goto err_req_irq; /* 启用NAPI */ for (i = 0; i < adapter->num_q_vectors; i++) napi_enable(&(adapter->q_vector[i]->napi)); ......}
在上面__igb_open函数调用了igb_setup_all_tx_resources,和igb_setup_all_rx_resources 。在igb_setup_all_rx_resources这一步操作中,分配了RingBuffer,并建立内存和Rx队列的映射关系 。(Rx Tx 队列的数量和大小可以通过 ethtool 进行配置) 。我们再接着看中断函数注册igb_request_irq:static int igb_request_irq(struct igb_adapter *adapter){ if (adapter->msix_entries) { err = igb_request_msix(adapter); if (!err) goto request_done; ...... }}static int igb_request_msix(struct igb_adapter *adapter){ ...... for (i = 0; i < adapter->num_q_vectors; i++) { ... err = request_irq(adapter->msix_entries[vector].vector, igb_msix_ring, 0, q_vector->name, }
在上面的代码中跟踪函数调用, __igb_open => igb_request_irq => igb_request_msix, 在igb_request_msix中我们看到了,对于多队列的网卡,为每一个队列都注册了中断,其对应的中断处理函数是igb_msix_ring(该函数也在drivers/net/ethernet/intel/igb/igb_main.c下) 。我们也可以看到,msix方式下,每个 RX 队列有独立的MSI-X 中断,从网卡硬件中断的层面就可以设置让收到的包被不同的 CPU处理 。(可以通过 irqbalance,或者修改 /proc/irq/IRQ_NUMBER/smp_affinity能够修改和CPU的绑定行为) 。当做好以上准备工作以后,就可以开门迎客(数据包)了!
三
迎接数据的到来
3.1 硬中断处理首先当数据帧从网线到达网卡上的时候,第一站是网卡的接收队列 。网卡在分配给自己的RingBuffer中寻找可用的内存位置,找到后DMA引擎会把数据DMA到网卡之前关联的内存里,这个时候CPU都是无感的 。当DMA操作完成以后,网卡会向CPU发起一个硬中断,通知CPU有数据到达 。
文章插图
图8 网卡数据硬中断处理过程
注意:当RingBuffer满的时候,新来的数据包将给丢弃 。ifconfig查看网卡的时候,可以里面有个overruns,表示因为环形队列满被丢弃的包 。如果发现有丢包,可能需要通过ethtool命令来加大环形队列的长度 。在启动网卡一节,我们说到了网卡的硬中断注册的处理函数是igb_msix_ring 。
//file: drivers/net/ethernet/intel/igb/igb_main.cstatic irqreturn_t igb_msix_ring(int irq, void *data){ struct igb_q_vector *q_vector = data; /* Write the ITR value calculated from the previous interrupt. */ igb_write_itr(q_vector); napi_schedule(&q_vector->napi); return IRQ_HANDLED;}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 三种使用AI攻击网络安全的方法
- 理解了Linux I/O机制,才能真的明白“什么是多线程”
- Linux网络配置
- 带你重新认识Linux系统的inode
- 古剑奇谭网络版野外pvp 古剑奇谭ol剧情
- 从Linux源码看Socket的listen及连接队列
- linux网络编程常见API详解
- 了解神经网络和模型泛化
- TCP/IP 基础知识总结
- 如何在电脑上安装网络打印机?详细教程全部教给你