linux kernel協議棧開發(2)-經過新協議解析後內存泄漏問題

新協議的插入點是ETH_P_ALL

    static struct packet_type  new_packet_type __read_mostly = {
		.type =	cpu_to_be16(ETH_P_ALL),
    	.func =	new_procol_rcv,
    };

ETH_P_ALL這個插入點呢,比較常見的就是tcpdump,這個地方所有的報文都會經過,所以使用的時候特別小心。這不我都遇到兩個問題,驚天大bug,會嚇死人的那種,設備崩掉的那種。
進入正題
接上一篇那個問題解決之後,以爲萬事大吉了,結果是剛開始,設備打流的時候內存泄漏,漏的也不快1個小時也就1個G,同事都給這個bug定義了一個很好的名字:側漏。
當然了天大的bug也不要慌,分析問題先,排除測試,先看看什麼情況下不漏吧。
先後在原代碼的基礎上測試,新協議的從接口,配置成橋模式和不是橋模式來測試,,依然側漏,好吧!!! 一萬個草泥馬飄過。
翻看內核的代碼上下文吧!等等忘了最重要的一句話,內存泄漏嘛,用計算機話說就是內存沒有釋放,報文處理中釋放內存,首先想到的就是 skb,,沒錯就是它,,
原代碼中,new_procol_rcv的返回是這樣的:

return 0;

當初以爲沒毛病的啦!
翻看new_procol_rcv函數執行的上下文:
調用位置在 :netif_receive_skb -> netif_receive_skb_internal ->__netif_receive_skb->__netif_receive_skb_core

static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
省略N行.....
list_for_each_entry_rcu(ptype, &ptype_all, list) {
	if (!ptype->dev || ptype->dev == skb->dev) {
		if (pt_prev)
			ret = deliver_skb(skb, pt_prev, orig_dev);
		pt_prev = ptype;
	}
}
省略N行。。。。

}
這個地方的deliver_skb函數:

static inline int deliver_skb(struct sk_buff *skb,
			      struct packet_type *pt_prev,
			      struct net_device *orig_dev)
{
	if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
		return -ENOMEM;
	atomic_inc(&skb->users);
	return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
}

這裏的 pt_prev->func就是new_procol_rcv,這裏面調用了一次atomic_inc(&skb->users);然而new_procol_rcv中並沒有將skb->users減一啊,這樣skb一直被佔用着,無法釋放,說的很有道理的樣子,試試吧!(skb->users不明白的自個百度,懶得貼了),
so 改代碼,返回的時候skb->users減一下
更改如下:

int new_procol_rcv(struct sk_buff *skb, struct net_device *dev,
	   struct packet_type *pt, struct net_device *orig_dev)
{
省略N行。。。。
	kfree_skb(skb);
	return NET_RX_DROP;/*	linux/netdevice.h 	*/
}

然後測試,新協議的從接口是橋的模式的時候,不會側漏,但是,從接口不是橋模式的時候,還有漏,,好吧!有點進展了,看到希望了,
仔細理一理思路:

netif_receive_skb中調用:
new_procol_rcv
然後執行橋的流程:
br_handler

static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
{
省略N行。。。
	list_for_each_entry_rcu(ptype, &ptype_all, list) {
		if (!ptype->dev || ptype->dev == skb->dev) {
			if (pt_prev)
				ret = deliver_skb(skb, pt_prev, orig_dev);
			pt_prev = ptype;
		}
	}
省略N行。。。
	rx_handler = rcu_dereference(skb->dev->rx_handler);
省略N行。。。
}

然後,new_procol_rcv中會調用 netif_receive_skb(),遞歸了呀
。。。。。
手跟不上腦子的速度了。。。。
。。。。。
。。。。
、。。。
。。
。。

就這樣的吧,說不清楚了,看代碼:

int new_procol_rcv(struct sk_buff *skb, struct net_device *dev,
	   struct packet_type *pt, struct net_device *orig_dev)
{
省略N行。。。。
		/* 數據報文 */
if(IS_DATA_PACKET(plkjhh))
{
	
	if(!(skb->dev->priv_flags & IFF_BRIDGE_PORT))
	{/*	skb->dev 不是橋模式| 不是橋接口*/
		new_recv_data_packet(skb,pVdev);
		netif_receive_skb(skb);
		/*使用ETH_P_ALL 的情況下 	這裏  不能執行 kfree_skb(skb) */
		atomic_read(&skb->users);
		return NET_RX_SUCCESS;
	}else{
	/*	skb->dev 是橋模式| 是橋接口*/
		new_recv_data_packet(skb,pVdev);
		kfree_skb(skb);
		return NET_RX_SUCCESS;
	}
}
    省略N行。。。。
}

總之 橋接口在減skb->users的時候 使用kfree_skb(skb);,非橋接口在減skb->users的時候使用atomic_read(&skb->users);
atomic_read(&skb->users);跟kfree_skb(skb);的區別就是在skb->users爲0時是否kfree(skb).

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章