1 概述
当一个数据包到达的时候,网卡驱动会完成接收并且触发中断。产生中断的每个设备都有一个相应的中断处理程序,每个网卡都有一个中断处理程序,是设备驱动程序的一部分。用于通知网卡该中断已经被接收了,以及把网卡缓冲区的数据包拷贝到内存中。当网卡接收来自网络的数据包时,需要通知内核数据包到了。内核通过执行网卡已注册的中断处理函数来做出应答。中断处理程序开始执行,通知硬件,拷贝最新的网络数据包到内存,然后读取网卡更多的数据包。
这些都是重要、紧迫而又与硬件相关的工作。内核通常需要快速的拷贝网络数据包到系统内存,因为网卡上接收网络数据包的缓存大小固定,而且相比系统内存也要小得多。所以上述拷贝动作一旦被延迟,必然造成网卡缓存溢出 - 进入的数据包占满了网卡的缓存,后续的包只能被丢弃。
一个中断处理函数主要分两个部分,上半部和下半部。中断产生并发送给CPU的时候,对于NAPI和不支持NAPI的设备来说处理结果是不一样的,NAPI调用的函数是napi_schedule,非NAPI调用的函数是netif_rx,这两个函数都是在网卡驱动的中断处理函数上半部分被调用的。
当网络数据包被拷贝到系统内存后,中断的上半部任务算完成了,这时它把控制权交还给被系统中断前运行的程序,处理和操作数据包的其他工作在随后的下半部中进行。
不管是否支持NAPI,对于驱动来说无非是调用napi_schedule或者netif_rx来通知内核,将数据包交给内核。所以如果不知道驱动使用的中断处理程序是哪个,那么只要搜索一下这两个函数就能定位出来了。因为NAPI是基于前者发展出来的,所以先从netif_rx开始分析。
2 函数netif_rx
非NAPI的接收处理,定义位于net/core/dev.c
1 int netif_rx(struct sk_buff *skb) 2 { 3 trace_netif_rx_entry(skb); 4 5 return netif_rx_internal(skb); 6 } 7 8 static int netif_rx_internal(struct sk_buff *skb) 9 { 10 int ret; 11 12 net_timestamp_check(netdev_tstamp_prequeue, skb);//记录接收时间到skb->tstamp 13 14 trace_netif_rx(skb); 15 #ifdef CONFIG_RPS 16 if (static_key_false(&rps_needed)) { 17 struct rps_dev_flow voidflow, *rflow = &voidflow; 18 int cpu; 19 20 preempt_disable(); 21 rcu_read_lock(); 22 23 cpu = get_rps_cpu(skb->dev, skb, &rflow);////如果有支持rps,则获取这个包交给了哪个cpu处理 24 if (cpu < 0) 25 cpu = smp_processor_id();//如果上面获取失败,则用另外一种方式获取当前cpu的id 26 27 ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail);//调用该函数将包添加到queue->input_pkt_queue里面 28 29 rcu_read_unlock(); 30 preempt_enable(); 31 } else 32 #endif 33 { 34 unsigned int qtail; 35 ret = enqueue_to_backlog(skb, get_cpu(), &qtail); 36 put_cpu(); 37 } 38 return ret; 39 }
2.1 函数enqueue_to_backlog
这个函数最后调用enqueue_to_backlog将包添加到queue->input_pkt_queue的尾部,这个input_pkt_queue是每个cpu都有的一个队列,这个队列的初始化在net_dev_init()中完成。
1 static int enqueue_to_backlog(struct sk_buff *skb, int cpu, 2 unsigned int *qtail) 3 { 4 struct softnet_data *sd; 5 unsigned long flags; 6 unsigned int qlen; 7 8 sd = &per_cpu(softnet_data, cpu);//获取当前cpu的softnet_data对象 9 10 local_irq_save(flags);//保存中断状态 11 12 rps_lock(sd); 13 if (!netif_running(skb->dev))//确认net_device的dev->state是__LINK_STATE_START状态,如果该网络设备没有运行,直接退出,不进行包的处理 14 goto drop; 15 qlen = skb_queue_len(&sd->input_pkt_queue);//获取input_pkt_queue的当前长度 16 if (qlen <= netdev_max_backlog && !skb_flow_limit(skb, qlen)) {//如果当前长度小于最大长度,且符合流量限制的要求 17 if (qlen) { 18 enqueue: 19 __skb_queue_tail(&sd->input_pkt_queue, skb);//将SKB添加到input_pkt_queue队列的后面 20 input_queue_tail_incr_save(sd, qtail);//队列尾部指针加1 21 rps_unlock(sd); 22 local_irq_restore(flags);//恢复中断状态 23 return NET_RX_SUCCESS;//返回接收成功 24 } 25 26 /* Schedule NAPI for backlog device 27 * We can use non atomic operation since we own the queue lock 28 */ 29 if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state)) { 30 if (!rps_ipi_queued(sd)) 31 ____napi_schedule(sd, &sd->backlog);//把虚拟设备backlog添加到sd->poll_list中以便进行轮询,最后设置NET_RX_SOFTIRQ标志触发软中断 32 } 33 goto enqueue; 34 } 35 36 drop: 37 sd->dropped++;//若接收队列满了就直接丢弃 38 rps_unlock(sd); 39 40 local_irq_restore(flags);//恢复本地中断 41 42 atomic_long_inc(&skb->dev->rx_dropped); 43 kfree_skb(skb); 44 return NET_RX_DROP; 45 }
2.2 函数net_dev_init
初始化input_pkt_queue队列,每个cpu都有的一个这样的队列。
1 static int __init net_dev_init(void) 2 { 3 ... 4 5 for_each_possible_cpu(i) { 6 struct work_struct *flush = per_cpu_ptr(&flush_works, i); 7 struct softnet_data *sd = &per_cpu(softnet_data, i); 8 9 INIT_WORK(flush, flush_backlog); 10 11 skb_queue_head_init(&sd->input_pkt_queue);//初始化每个cpu的input_pkt_queue队列 12 skb_queue_head_init(&sd->process_queue); 13 INIT_LIST_HEAD(&sd->poll_list); 14 sd->output_queue_tailp = &sd->output_queue; 15 #ifdef CONFIG_RPS 16 sd->csd.func = rps_trigger_softirq; 17 sd->csd.info = sd; 18 sd->cpu = i; 19 #endif 20 21 sd->backlog.poll = process_backlog;//初始化非NAPI的poll callback函数 ----------------B 22 sd->backlog.weight = weight_p; 23 } 24 25 ... 26 open_softirq(NET_TX_SOFTIRQ, net_tx_action);//初始化TX中断 27 open_softirq(NET_RX_SOFTIRQ, net_rx_action);//初始化RX中断 ---------------------- A 28 29 hotcpu_notifier(dev_cpu_callback, 0); 30 dst_subsys_init(); 31 rc = 0; 32 out: 33 return rc; 34 }
2.3 函数调用__napi_schedule
作用:触发软中断
1 /* Called with irq disabled */ 2 static inline void ____napi_schedule(struct softnet_data *sd, 3 struct napi_struct *napi) 4 { 5 list_add_tail(&napi->poll_list, &sd->poll_list); 6 __raise_softirq_irqoff(NET_RX_SOFTIRQ); 7 }
此软中断的初始化在2.2节函数net_dev_init中A处。最后调用函数net_rx_action
3 函数napi_schedule
NAPI接收处理,定义位于: include\linux\netdevice.h 和net/core/dev.c
1 static inline void napi_schedule(struct napi_struct *n) 2 { 3 if (napi_schedule_prep(n)) 4 __napi_schedule(n); 5 } 6 7 void __napi_schedule(struct napi_struct *n) 8 { 9 unsigned long flags; 10 11 local_irq_save(flags);//保存中断 12 ____napi_schedule(this_cpu_ptr(&softnet_data), n);//详见2.3节,触发RX软中断,最后同2.3节调用函数net_rx_action 13 local_irq_restore(flags);//恢复中断 14 }
4 函数net_rx_action
此函数为RX中断的下半部分处理,第2-3节是RX中断的上半部处理。
1 static __latent_entropy void net_rx_action(struct softirq_action *h) 2 { 3 struct softnet_data *sd = this_cpu_ptr(&softnet_data); 4 unsigned long time_limit = jiffies + 2; 5 int budget = netdev_budget;//指定一次软中断处理的skb的数目,这里是300 6 LIST_HEAD(list); 7 LIST_HEAD(repoll); 8 9 local_irq_disable(); 10 list_splice_init(&sd->poll_list, &list); 11 local_irq_enable(); 12 13 for (;;) { 14 struct napi_struct *n; 15 16 if (list_empty(&list)) {//检查POLL队列(poll_list)上是否有设备在准备等待轮询 17 if (!sd_has_rps_ipi_waiting(sd) && list_empty(&repoll)) 18 return; 19 break; 20 } 21 22 n = list_first_entry(&list, struct napi_struct, poll_list); 23 budget -= napi_poll(n, &repoll);//调用poll函数从网卡驱动中读取一定数量的skb 24 25 /* If softirq window is exhausted then punt. 26 * Allow this to run for 2 jiffies since which will allow 27 * an average latency of 1.5/HZ. 28 */ 29 if (unlikely(budget <= 0 || //如果读取的数量超过300,则终止中断处理 30 time_after_eq(jiffies, time_limit))) { 31 sd->time_squeeze++; 32 break; 33 } 34 } 35 36 __kfree_skb_flush(); 37 local_irq_disable(); 38 39 list_splice_tail_init(&sd->poll_list, &list); 40 list_splice_tail(&repoll, &list); 41 list_splice(&list, &sd->poll_list); 42 if (!list_empty(&sd->poll_list))//如果poll list中不为空,表示还有skb没有读取完成,则继续读取,触发下一次软中断 43 __raise_softirq_irqoff(NET_RX_SOFTIRQ); 44 45 net_rps_action_and_irq_enable(sd); 46 }
4.1 函数napi_poll
1 static int napi_poll(struct napi_struct *n, struct list_head *repoll) 2 { 3 void *have; 4 int work, weight; 5 6 list_del_init(&n->poll_list); 7 8 have = netpoll_poll_lock(n); 9 10 weight = n->weight; 11 12 /* This NAPI_STATE_SCHED test is for avoiding a race 13 * with netpoll's poll_napi(). Only the entity which 14 * obtains the lock and sees NAPI_STATE_SCHED set will 15 * actually make the ->poll() call. Therefore we avoid 16 * accidentally calling ->poll() when NAPI is not scheduled. 17 */ 18 work = 0; 19 if (test_bit(NAPI_STATE_SCHED, &n->state)) { 20 work = n->poll(n, weight);//在这里调用驱动的poll函数,如果驱动有支持NAPI,会定义并初始化这个poll函数,默认的poll函数是process_backlog 21 trace_napi_poll(n, work, weight); 22 } 23 24 WARN_ON_ONCE(work > weight); 25 26 if (likely(work < weight)) 27 goto out_unlock; 28 29 /* Drivers must not modify the NAPI state if they 30 * consume the entire weight. In such cases this code 31 * still "owns" the NAPI instance and therefore can 32 * move the instance around on the list at-will. 33 */ 34 if (unlikely(napi_disable_pending(n))) { 35 napi_complete(n); 36 goto out_unlock; 37 } 38 39 if (n->gro_list) { 40 /* flush too old packets 41 * If HZ < 1000, flush all packets. 42 */ 43 napi_gro_flush(n, HZ >= 1000); 44 } 45 46 /* Some drivers may have called napi_schedule 47 * prior to exhausting their budget. 48 */ 49 if (unlikely(!list_empty(&n->poll_list))) { 50 pr_warn_once("%s: Budget exhausted after napi rescheduled\n", 51 n->dev ? n->dev->name : "backlog"); 52 goto out_unlock; 53 } 54 55 list_add_tail(&n->poll_list, repoll); 56 57 out_unlock: 58 netpoll_poll_unlock(have); 59 60 return work; 61 }
4.2 非NAPI的poll函数
非NAPI的poll函数在上述2.2节B处赋值,即为函数process_backlog
1 static int process_backlog(struct napi_struct *napi, int quota) 2 { 3 struct softnet_data *sd = container_of(napi, struct softnet_data, backlog); 4 bool again = true; 5 int work = 0; 6 7 /* Check if we have pending ipi, its better to send them now, 8 * not waiting net_rx_action() end. 9 */ 10 if (sd_has_rps_ipi_waiting(sd)) { 11 local_irq_disable(); 12 net_rps_action_and_irq_enable(sd); 13 } 14 15 napi->weight = weight_p; 16 while (again) { 17 struct sk_buff *skb; 18 19 while ((skb = __skb_dequeue(&sd->process_queue)))//从队列头部读取一个skb { 20 rcu_read_lock(); 21 __netif_receive_skb(skb);//调用此函数将skb传给网路层 ------------------ A 22 rcu_read_unlock(); 23 input_queue_head_incr(sd);//将队列头部往后偏移一个单位 24 if (++work >= quota) 25 return work; 26 27 } 28 29 local_irq_disable(); 30 rps_lock(sd); 31 if (skb_queue_empty(&sd->input_pkt_queue)) {//如果队列为空,表示skb读取完了 32 33 napi->state = 0;//状态置0并退出读取循环 34 again = false; 35 } else { 36 skb_queue_splice_tail_init(&sd->input_pkt_queue, 37 &sd->process_queue); 38 } 39 rps_unlock(sd); 40 local_irq_enable(); 41 } 42 43 return work; 44 }
4.3 NAPI类的poll函数
对于NAPI来说,它的poll函数是在驱动加载初始化的时候指定的。以linux-4.9.73\drivers\net\wireless\ath\wil6210\pcie_bus.c
为例来分析NAPI类的poll函数赋值过程。
从wil6210 驱动的probe函数开始:
1 static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) 2 { 3 ... 4 rc = wil_if_add(wil); 5 if (rc) { 6 wil_err(wil, "wil_if_add failed: %d\n", rc); 7 goto bus_disable; 8 ... 9 } 10 11 int wil_if_add(struct wil6210_priv *wil) 12 { 13 struct wireless_dev *wdev = wil_to_wdev(wil); 14 struct wiphy *wiphy = wdev->wiphy; 15 struct net_device *ndev = wil_to_ndev(wil); 16 int rc; 17 18 wil_dbg_misc(wil, "entered"); 19 20 strlcpy(wiphy->fw_version, wil->fw_version, sizeof(wiphy->fw_version)); 21 22 rc = wiphy_register(wiphy);//注册wiphy 23 if (rc < 0) { 24 wil_err(wil, "failed to register wiphy, err %d\n", rc); 25 return rc; 26 } 27 28 netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx,//注册poll函数wil6210_netdev_poll_rx 29 WIL6210_NAPI_BUDGET); 30 ... 31 } 32 void netif_napi_add(struct net_device *dev, struct napi_struct *napi, 33 int (*poll)(struct napi_struct *, int), int weight) 34 { 35 ... 36 napi->timer.function = napi_watchdog; 37 napi->gro_count = 0; 38 napi->gro_list = NULL; 39 napi->skb = NULL; 40 napi->poll = poll;//赋值poll callback函数 41 ... 42 }
4.3.1 wil6210的poll函数
1 static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget) 2 { 3 struct wil6210_priv *wil = container_of(napi, struct wil6210_priv, 4 napi_rx); 5 int quota = budget; 6 int done; 7 8 wil_rx_handle(wil, "a); 9 ... 10 } 11 void wil_rx_handle(struct wil6210_priv *wil, int *quota) 12 { 13 ... 14 15 wil_netif_rx_any(skb, ndev); 16 ... 17 } 18 void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) 19 { 20 ... 21 if (skb) { /* deliver to local stack */ 22 23 skb->protocol = eth_type_trans(skb, ndev); 24 rc = napi_gro_receive(&wil->napi_rx, skb);//将分散的skb进行组装,形成一个skb 25 wil_dbg_txrx(wil, "Rx complete %d bytes => %s\n", 26 len, gro_res_str[rc]); 27 } 28 ... 29 }
4.3.2 函数napi_gro_receive
1 gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) 2 { 3 skb_mark_napi_id(skb, napi); 4 trace_napi_gro_receive_entry(skb); 5 6 skb_gro_reset_offset(skb); 7 8 return napi_skb_finish(dev_gro_receive(napi, skb), skb); 9 } 10 static gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb) 11 { 12 switch (ret) { 13 case GRO_NORMAL: 14 if (netif_receive_skb_internal(skb)) 15 ret = GRO_DROP; 16 break; 17 ... 18 } 19 static int netif_receive_skb_internal(struct sk_buff *skb) 20 { 21 ... 22 ret = __netif_receive_skb(skb);//将skb传送给网路层 ------------------ A 23 rcu_read_unlock(); 24 return ret; 25 }
4.4 NAPI和非NAPI
经上分析4.2节非NAPI的poll函数A处和NAPI类poll函数的4.3.2节的A处,最终都调用函数__netif_receive_skb,将skb传送给网路层。下面来看此函数:
1 static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc) 2 { 3 struct packet_type *ptype, *pt_prev;//用于操作包类型 4 rx_handler_func_t *rx_handler; 5 struct net_device *orig_dev;//存放报文的原始设备 6 bool deliver_exact = false; 7 int ret = NET_RX_DROP; 8 __be16 type; 9 10 net_timestamp_check(!netdev_tstamp_prequeue, skb);//check时间戳,并且会更新skb的时间戳,skb->tstamp 11 12 trace_netif_receive_skb(skb); 13 14 orig_dev = skb->dev;//将原始的dve做一个备份 15 16 skb_reset_network_header(skb);//重置network header,此时skb已经指向IP头(没有vlan的情况下) 17 if (!skb_transport_header_was_set(skb)) 18 skb_reset_transport_header(skb); 19 skb_reset_mac_len(skb);//重置mac len 20 21 pt_prev = NULL; 22 23 another_round: 24 skb->skb_iif = skb->dev->ifindex; 25 26 __this_cpu_inc(softnet_data.processed); 27 28 if (skb->protocol == cpu_to_be16(ETH_P_8021Q) || 29 skb->protocol == cpu_to_be16(ETH_P_8021AD)) { 30 skb = skb_vlan_untag(skb);//去除vlan tag 31 if (unlikely(!skb)) 32 goto out; 33 } 34 35 #ifdef CONFIG_NET_CLS_ACT 36 if (skb->tc_verd & TC_NCLS) { 37 skb->tc_verd = CLR_TC_NCLS(skb->tc_verd); 38 goto ncls; 39 } 40 #endif 41 42 if (pfmemalloc) 43 goto skip_taps; 44 /*把包交给特定协议相关的处理函数前,先调用ptype_all中注册的函数。最常见的为tcpdump,该工具就是从这里拿到所有收到的包的,例如raw socket和tcpdump实现*/ 45 list_for_each_entry_rcu(ptype, &ptype_all, list) { 46 if (pt_prev) 47 ret = deliver_skb(skb, pt_prev, orig_dev);//将包直接传给应用层 48 pt_prev = ptype;//pt_prev的加入是为了优化,只有当找到下一个匹配的时候,才执行这一次的回调函数 49 } 50 /*设备上注册ptype_all,做相应的处理,更加精细的控制,ptype_all里面包括IP和arp等 */ 51 list_for_each_entry_rcu(ptype, &skb->dev->ptype_all, list) { 52 if (pt_prev) 53 ret = deliver_skb(skb, pt_prev, orig_dev); 54 pt_prev = ptype; 55 } 56 57 skip_taps: 58 #ifdef CONFIG_NET_INGRESS 59 if (static_key_false(&ingress_needed)) { 60 skb = sch_handle_ingress(skb, &pt_prev, &ret, orig_dev); 61 if (!skb) 62 goto out; 63 64 if (nf_ingress(skb, &pt_prev, &ret, orig_dev) < 0) 65 goto out; 66 } 67 #endif 68 #ifdef CONFIG_NET_CLS_ACT 69 skb->tc_verd = 0; 70 ncls: 71 #endif 72 if (pfmemalloc && !skb_pfmemalloc_protocol(skb)) 73 goto drop; 74 75 if (skb_vlan_tag_present(skb)) {//如果需要将vlan的信息提供给上层,则执行下面的代码 76 if (pt_prev) { 77 ret = deliver_skb(skb, pt_prev, orig_dev); 78 pt_prev = NULL; 79 } 80 if (vlan_do_receive(&skb)) 81 goto another_round; 82 else if (unlikely(!skb)) 83 goto out; 84 } 85 86 rx_handler = rcu_dereference(skb->dev->rx_handler);//设备rx_handler,加入OVS时会注册为OVS的入口函数 87 if (rx_handler) { 88 if (pt_prev) { 89 ret = deliver_skb(skb, pt_prev, orig_dev); 90 pt_prev = NULL; 91 } 92 switch (rx_handler(&skb)) {//执行rx_handler处理,例如进入OVS,OVS不支持报头中携带vlan的报文 93 case RX_HANDLER_CONSUMED: 94 ret = NET_RX_SUCCESS; 95 goto out; 96 case RX_HANDLER_ANOTHER: 97 goto another_round; 98 case RX_HANDLER_EXACT: 99 deliver_exact = true; 100 case RX_HANDLER_PASS: 101 break; 102 default: 103 BUG(); 104 } 105 } 106 107 if (unlikely(skb_vlan_tag_present(skb))) { 108 if (skb_vlan_tag_get_id(skb)) 109 skb->pkt_type = PACKET_OTHERHOST; 110 /* Note: we might in the future use prio bits 111 * and set skb->priority like in vlan_do_receive() 112 * For the time being, just ignore Priority Code Point 113 */ 114 skb->vlan_tci = 0; 115 } 116 117 type = skb->protocol; 118 119 /* deliver only exact match when indicated */ 120 if (likely(!deliver_exact)) { 121 deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type,//根据全局定义的协议处理报文 122 &ptype_base[ntohs(type) & 123 PTYPE_HASH_MASK]); 124 } 125 126 deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type,//根据设备上注册的协议进行处理 127 &orig_dev->ptype_specific); 128 129 if (unlikely(skb->dev != orig_dev)) {//如果设备发生变化,那么还需要针对新设备的注册协议进行处理 130 deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type, 131 &skb->dev->ptype_specific); 132 } 133 134 if (pt_prev) { 135 if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC))) 136 goto drop; 137 else 138 ret = pt_prev->func(skb, skb->dev, pt_prev, //调用协议处理orig_dev); 139 } else { 140 drop: 141 if (!deliver_exact) 142 atomic_long_inc(&skb->dev->rx_dropped); 143 else 144 atomic_long_inc(&skb->dev->rx_nohandler); 145 kfree_skb(skb); 146 /* Jamal, now you will not able to escape explaining 147 * me how you were going to use this. :-) 148 */ 149 ret = NET_RX_DROP; 150 } 151 152 out: 153 return ret; 154 }
到此即为数据包从驱动接收到接口层的过程,接下来会通过接口层传送给网路层。
4.4.1 补充下函数__netif_receive_skb
补充说明下这两个链表ptype_base和ptype_all,在内核中存储情况如下图:
从图中可以看到,ptype_all是一个链表,这个链表里面最大的区别是func=packet_rcv,即这个链表一般是提供给一些抓包程序使用的,比如tcp_dump。它可以不区分包的类型而将所有的包的抓取过来,它的统一处理函数都是packet_rcv,在这里面可以对一些过滤选项进行处理。对象中的type一般使用的是以太网类型,而dev表示在哪个接口上抓包。
但是ptype_base则是一个哈希表,注意这个表是以type来进行分类的,比如ip协议可以指定不同的dev接口,但是他们都在同一张表上。不同的协议类型对应了不同的接收函数,比如IP报文的接收函数是ip_rcv, 802.2对应的是llc_rcv等。总的来说,报文从网卡驱动里面上来以后,第一次在这里进行分流,不同的报文类型交给不同的协议处理函数进行处理。
5 总结整个flow:
参考博文:https://blog.csdn.net/lee244868149/article/details/77625367