閱讀434 返回首頁    go 阿裏雲 go 技術社區[雲棲]


網絡子係統20_傳輸接收軟中斷

//	數據幀接收軟中斷處理函數
//	調用路徑:do_softirq->net_rx_action
//	函數主要任務:
//		1.在關中斷情況下檢查調度隊列poll_list是否有設備接收到入口幀,然後開中斷
//		2.單次運行處理的入口幀不能超過netdev_max_backlog=300,運行時間不能超過1個jiffies
//		3.依次遍曆調度隊列poll_list
//		4.如果設備配額以用完,或者設備驅動的poll函數表明仍然有入口數據幀
//			4.1 將設備重新添加poll_list尾部,重新分配配額,準備接受下一次調度
//		5.如果單次運行沒有調度完所有接收到入口幀的設備,則再次設置接受軟中斷調度標誌,退出

//	注:接收軟中斷中,poll_list上掛載的npai設備,或者非napi設備使用的積壓設備。
3.2 static void net_rx_action(struct softirq_action *h)
{
	struct softnet_data *queue = &__get_cpu_var(softnet_data);
	unsigned long start_time = jiffies;
	int budget = netdev_max_backlog;

	//關本地中斷
	local_irq_disable();

	//有設備有輸入流量
	while (!list_empty(&queue->poll_list)) {
		struct net_device *dev;

		//執行時間超過了一個1 jiffies
		//或者本次軟中斷處理的輸入流量已經超過了netdev_max_backlog,退出軟中
		if (budget <= 0 || jiffies - start_time > 1)
			goto softnet_break;

		local_irq_enable();
		//獲得設備指針
		dev = list_entry(queue->poll_list.next,
				 struct net_device, poll_list);

		//如果設備的配額已經用完,或者執行一次poll之後,此設備依然有待處理的流量
		if (dev->quota <= 0 || dev->poll(dev, &budget)) {
			local_irq_disable();
			//將設備從list上刪除,添加到list尾部
			list_del(&dev->poll_list);
			list_add_tail(&dev->poll_list, &queue->poll_list);
			//給設備重新分配配額,已weight作為一次分配的量,範圍從1024~64
			if (dev->quota < 0)
				dev->quota += dev->weight;
			else
				dev->quota = dev->weight;
		} else {
			dev_put(dev);
			local_irq_disable();
		}
	}
out:
	local_irq_enable();
	return;

softnet_break:
	__get_cpu_var(netdev_rx_stat).time_squeeze++;
	//重新調度接收軟中斷
	__raise_softirq_irqoff(NET_RX_SOFTIRQ);
	goto out;
}


//	發送軟中斷處理函數
//		一次處理output_queue中所有等待傳輸的設備
//	調用路徑:do_softirq->net_tx_action

//	函數主要功能:
//		1.釋放本cpu所有已經完成傳輸的skb
//		2.關中斷的情況獲取本cpu的傳輸設備隊列output_queue, 其中的設備有數據需要進行傳輸
//		3.清除設備傳輸調度標誌,表明設備可以接收下一次傳輸調度
//		4.獲取設備隊列鎖queue_lock,通過隊列規則接口,進行設備數據傳輸
//		5.如果獲取queue_lock失敗,說明設備當前正在進行傳輸,重調度設備

//	注:發送軟中斷中,output_queue掛載的設備均為有隊列規則設備,沒有隊列規則的設備不會在軟中斷進行傳輸,因為其沒有傳輸調度的概念
3.3 static void net_tx_action(struct softirq_action *h)
{
	struct softnet_data *sd = &__get_cpu_var(softnet_data);
	//如果完成隊列非空
	if (sd->completion_queue) {
		struct sk_buff *clist;

		//關本cpu中斷
		local_irq_disable();
		//取下本cpu的完成隊列
		clist = sd->completion_queue;
		sd->completion_queue = NULL;
		local_irq_enable();

		while (clist) {
			struct sk_buff *skb = clist;
			clist = clist->next;
			//釋放對應的skb
			__kfree_skb(skb);
		}
	}
	//如果輸出隊列非空
	if (sd->output_queue) {
		struct net_device *head;
		//將輸出隊列取下
		local_irq_disable();
		head = sd->output_queue;
		sd->output_queue = NULL;
		local_irq_enable();
		while (head) {
			struct net_device *dev = head;
			//net_device 通過next_sched鏈接到output_queue上
			head = head->next_sched;
			//一個內存屏障
			smp_mb__before_clear_bit();
			//設備準備好進行傳輸,清除設備調度標誌,說明,當設備執行傳輸時,屬於非調度狀態,可以被重新調度。
			clear_bit(__LINK_STATE_SCHED, &dev->state);

			//獲取設備的輸出隊列鎖
			if (spin_trylock(&dev->queue_lock)) {
				//通過出口隊列規則,調度設備
				qdisc_run(dev);
				spin_unlock(&dev->queue_lock);
			} else {
				//說明隊列正在進行傳輸,重新調度設備
				netif_schedule(dev);
			}
		}
	}
}

最後更新:2017-04-03 15:22:11

  上一篇:go 更改 SAR 時間輸出格式 AM/PM
  下一篇:go 網絡子係統18_arp對代理的處理