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).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值