深入理解linux内核网络收包过程—硬中断与软中断

深入理解linux内核网络收包过程—硬中断与软中断

硬中断处理

 1. 数据帧首先到达网卡的接收队列,分配RingBuffer
 2. DMA把数据搬运到网卡关联的内存
 3. 网卡向CPU发起硬中断,通知CPU有数据
 4. 调用驱动注册的硬中断处理函数
 5. 启动NAPI,触发软中断

上一分析说到网卡硬中断注册的函数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;
}

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);
}

list_add_tail修改了napi的poll_list(双向链表,数据帧等着被处理),
触发一个软中断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;
		}
	}

核⼼逻辑是获取到当前CPU变量 softnet_data,对其poll_list进⾏遍历,然后执⾏到⽹卡驱动注册到的poll函数。

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;
}

从ringbuf中取出数据skb;
收取完数据以后,对其进⾏⼀些校验
设置 sbk 变量的 timestamp, VLAN id, protocol

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将数据发送到网络协议栈。

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值