分类: LINUX
2018-08-16 23:14:26
假设两个模块A、B(A、B既可以是进程/内核线程,也可以是软中断)引用该skb,并分别调用kfree_skb1/kfree_skb2。下面以数字来表示执行的先后顺序。
void kfree_skb1(struct sk_buff *skb)
{
if (unlikely(!skb))
return;
1)A、B先后到达此处,计数为2;
if (likely(atomic_read(&skb->users) == 1))
smp_rmb();
else {
2)A、B先后到达此处,并依次减1(减1是原子操作),并均会返回,但此时计数已经为0,skb并未被释放,这显然是有问题的(问题出在:atomic_read和atomic_dec分别是原子操作,但合在一起就不是原子操作);
atomic_dec(&skb->users);
return;
}
trace_kfree_skb(skb, __builtin_return_address(0));
__kfree_skb(skb);
}
void kfree_skb2(struct sk_buff *skb)
{
if (unlikely(!skb))
return;
1)A到达此处,此时计数为2,减1后返回(注意:减1和判断是原子操作,不可分割)
2)B到达此处,此时计数为1,减1后变成0,则继续执行,后面释放skb
此时skb也可以正常被释放,也即功能没问题。
但A和B共调用atomic_dec_and_test两次,而对于kfree_skb函数而言,调用一次atomic_dec_and_test和两次atomic_read,很难说哪个效率高。但是考虑到skb引用计数为1的情况较多,因此kfree_skb相对效率较高。
if (likely(atomic_dec_and_test(&skb->users)))
smp_rmb();
else
return;
trace_kfree_skb(skb, __builtin_return_address(0));
__kfree_skb(skb);
}
1)kfree_skb1函数存在bug,在特殊情况下,skb无法被正确释放。
2)总体来说,kfree_skb要比kfree_skb2效率要高。
3)atomic_read和atomic_dec分别是原子操作,但合在一起就不是原子操作,因此需要atomic_dec_and_test这样的函数,包含了读写功能,又是原子操作。
misteryoung2018-08-20 22:45:13
dev_kfree_skb_irq对skb的操作包括以下几步:
1)skb引用计数(skb->users)减1;
2)若未减到0,则返回什么也不做;
3)若是减到0,则将该skb插入到当前CPU对应的completion_queue队列的首部,并触发NET_TX_SOFTIRQ软中断;
4)在软中断处理函数net_tx_action中,会将completion_queue队列中所有的skb释放;
简单来说,硬中断中只负责引用计数减1,并将skb插入到当前CPU队列,然后触发软中断,后者负责skb的真正释放。
misteryoung2018-08-20 22:36:08
需要注意是,若在硬中断上下文中释放skb,需要使用dev_kfree_skb_irq。
若是不清楚是否在硬中断上下文,为保险起见可以使用dev_kfree_skb_any,该函数一般被驱动程序调用。