我在使用kfree_skb方面遇到了一些麻烦。以下3行代码以奇怪的方式表现,
printk(KERN_ALERT"1 - SKB user: %d", atomic_read(&skb->users));
kfree_skb(skb);
printk(KERN_ALERT"2 - SKB user: %d", atomic_read(&skb->users));
我希望第二个printk导致内核恐慌,因为我释放了skb,但事实并非如此。这些行的输出如下;
1 - SKB user: 1
2 - SKB user: 2
我错过了什么意思?
编辑:很抱歉我错误输入了第二个输出。它如下:2 - SKB user: 1
答案 0 :(得分:0)
当你释放skb时,很有可能在另一个线程中分配它。 试想一下这个
Thread 1:
printk(KERN_ALERT"1 - SKB user: %d", atomic_read(&skb->users));
kfree_skb
-------> scheduled out
Thread 2:
alloc_skb()
//inc user count (whatever the kernel call)
-------> Scheduled out
然后现在返回线程1
printk(KERN_ALERT"2 - SKB user: %d", atomic_read(&skb->users));
现在非常有可能获得用户数2。
由于skb是从slab缓存分配的,所以很有可能刚刚释放的skb首先被分配
答案 1 :(得分:0)
这个问题有点老了,但是我相信它仍然令人困惑(我不久前就在想这个问题)。因此,首先让我们快速了解一下kfree_skb()
函数。该代码已在v4.13上更改,但其行为仍相同:
在v4.13之前(发布问题后):
void kfree_skb(struct sk_buff *skb)
{
if (unlikely(!skb))
return;
if (likely(atomic_read(&skb->users) == 1))
smp_rmb();
else if (likely(!atomic_dec_and_test(&skb->users)))
return;
trace_kfree_skb(skb, __builtin_return_address(0));
__kfree_skb(skb);
}
v4.13之后:
void kfree_skb(struct sk_buff *skb)
{
if (!skb_unref(skb))
return;
trace_kfree_skb(skb, __builtin_return_address(0));
__kfree_skb(skb);
}
static inline bool skb_unref(struct sk_buff *skb)
{
if (unlikely(!skb))
return false;
if (likely(refcount_read(&skb->users) == 1))
smp_rmb();
else if (likely(!refcount_dec_and_test(&skb->users)))
return false;
return true;
}
在两种情况下,我都注意到,当您调用kfree_skb()
时,skb确实被释放了(已调用__kfree_skb()
)。但是,我还注意到,在两种情况下,如果skb->users
在调用kfree_skb()
之前为1,即使调用kfree_skb()
之后它也将保持为1。
所以事情是,当应该释放skb时,kfree_skb()确实释放了skb,但它不会保护您免于双重释放skb的麻烦。看来它是有意为避免使用不必要的原子操作而创建的(您可以在skbuff.h代码的某处找到此注释):
/*
* If users == 1, we are the only owner and can avoid redundant atomic changes.
*/
尽管如此,您仍然可以看到,在__dev_kfree_skb_irq()
中的行为只是您所期望的(如果在调用skb->users
之前__dev_kfree_skb_irq()
为1,则skb->users
设置为预期为0):
void __dev_kfree_skb_irq(struct sk_buff *skb, enum skb_free_reason reason)
{
unsigned long flags;
if (unlikely(!skb))
return;
if (likely(refcount_read(&skb->users) == 1)) {
smp_rmb();
refcount_set(&skb->users, 0);
} else if (likely(!refcount_dec_and_test(&skb->users))) {
return;
}
get_kfree_skb_cb(skb)->reason = reason;
local_irq_save(flags);
skb->next = __this_cpu_read(softnet_data.completion_queue);
__this_cpu_write(softnet_data.completion_queue, skb);
raise_softirq_irqoff(NET_TX_SOFTIRQ);
local_irq_restore(flags);
}