目录
数据通过网络发送过来
上一分析说到网卡硬中断注册的函数igb_msix_ring
- static 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;
- }
igb_write_itr仅记录硬件中断频率
- static inline void ____napi_schedule(struct softnet_data *sd,
- struct napi_struct *napi)
- {
- list_add_tail(&napi->poll_list, &sd->poll_list);
- //触发一个软中断NET_RX_SOFTIRQ
- __raise_softirq_irqoff(NET_RX_SOFTIRQ);
- }
判断softirq_pending标志
- static int ksoftirqd_should_run(unsigned int cpu)
- {
- return local_softirq_pending();
- }
执行run_ksoftirqd->__do_softirq
- asmlinkage __visible void __softirq_entry __do_softirq(void)
- {
-
- while ((softirq_bit = ffs(pending))) {
-
- trace_softirq_entry(vec_nr);
- h->action(h);
- trace_softirq_exit(vec_nr);
-
- wakeup_softirqd();
- }
- ...
- }
调用action中断函数
- static __latent_entropy void net_rx_action(struct softirq_action *h)
- {
- struct softnet_data *sd = this_cpu_ptr(&softnet_data);
- unsigned long time_limit = jiffies +
- usecs_to_jiffies(netdev_budget_usecs);
- int budget = netdev_budget;
-
- for (;;) {
- struct napi_struct *n;
-
- n = list_first_entry(&list, struct napi_struct, poll_list);
- //变量sd,调用poll函数
- budget -= napi_poll(n, &repoll);
-
- //budget 与 time_limit控制退出
- if (unlikely(budget <= 0 ||
- time_after_eq(jiffies, time_limit))) {
- sd->time_squeeze++;
- break;
- }
- }
- static int igb_poll(struct napi_struct *napi, int budget)
- {
-
- if (q_vector->tx.ring)
- clean_complete = igb_clean_tx_irq(q_vector, budget);
-
- if (q_vector->rx.ring) {
- int cleaned = igb_clean_rx_irq(q_vector, budget);
- }
-
- }
-
-
- static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget)
- {
- while (likely(total_packets < budget)) {
- union e1000_adv_rx_desc *rx_desc;
- struct igb_rx_buffer *rx_buffer;
- unsigned int size;
-
- rx_buffer = igb_get_rx_buffer(rx_ring, size);
-
-
- igb_put_rx_buffer(rx_ring, rx_buffer);
- cleaned_count++;
-
- /* fetch next buffer in frame if non-eop */
- if (igb_is_non_eop(rx_ring, rx_desc))
- continue;
-
- /* verify the packet layout is correct */
- if (igb_cleanup_headers(rx_ring, rx_desc, skb)) {
- skb = NULL;
- continue;
- }
-
- /* probably a little skewed due to removing CRC */
- total_bytes += skb->len;
-
- /* populate checksum, timestamp, VLAN, and protocol */
- igb_process_skb_fields(rx_ring, rx_desc, skb);
-
- napi_gro_receive(&q_vector->napi, skb);
-
- /* update budget accounting */
- total_packets++;
- }
-
- return total_packets;
- }
- gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
- {
- skb_gro_reset_offset(skb);
-
- ret = napi_skb_finish(napi, skb, dev_gro_receive(napi, skb));
- trace_napi_gro_receive_exit(ret);
-
- }
napi_gro_receive函数代表的是⽹卡 GRO 特性,可以简单理解成把相关的⼩包合并成⼀个⼤包。
- /* Pass the currently batched GRO_NORMAL SKBs up to the stack. */
- static void gro_normal_list(struct napi_struct *napi)
- {
- if (!napi->rx_count)
- return;
- netif_receive_skb_list_internal(&napi->rx_list);
- INIT_LIST_HEAD(&napi->rx_list);
- napi->rx_count = 0;
- }
-
- /* Queue one GRO_NORMAL SKB up for list processing. If batch size exceeded,
- * pass the whole batch up to the stack.
- */
- static void gro_normal_one(struct napi_struct *napi, struct sk_buff *skb)
- {
- list_add_tail(&skb->list, &napi->rx_list);
- if (++napi->rx_count >= gro_normal_batch)
- gro_normal_list(napi);
- }
-
- static gro_result_t napi_skb_finish(struct napi_struct *napi,
- struct sk_buff *skb,
- gro_result_t ret)
- {
- switch (ret) {
- case GRO_NORMAL:
- gro_normal_one(napi, skb);
- break;
- ...
- }
最终调用 gro_normal_list将数据发送到网络协议栈。
参考