【发布时间】:2017-08-31 17:00:39
【问题描述】:
我正在为我的项目开发一个自定义网络设备驱动程序,与以太网驱动程序不同,我没有接收数据包的中断(设计限制)。因此,我对驱动程序的接收部分使用轮询。 我在 Linux 中使用 tasklet 实现了一种轮询机制(主要从 LDD3 的 jit.c 示例中借用),它将轮询函数重新调度 10000 次(有点随机数),以在每两次轮询之间产生延迟。它工作得很好,但我决定让它成为一个基于计时器的实现以避免额外的开销。我使用了 HRtimers、workqueues 和一个调用 tasklet 的计时器,但它们都面临这个错误
内核恐慌 - 不同步:中断中的致命异常
在 eth_type_trans 函数中。以下是我得到的恐慌错误详细信息:
[ 5031.345599] Unable to handle kernel NULL pointer dereference at virtual address 00000000
[ 5031.346090] pgd = ffffffc07cf7f000
[ 5031.346471] [00000000] *pgd=0000000000000000
[ 5031.346988] Internal error: Oops: 96000005 [#1] PREEMPT SMP
[ 5031.347383] Modules linked in: alex_mcn(O)
[ 5031.348144] CPU: 0 PID: 601 Comm: systemd-journal Tainted: G O 3.16.0-rc6 #1
[ 5031.348744] task: ffffffc07cff5c40 ti: ffffffc07cf64000 task.ti: ffffffc07cf64000
[ 5031.349303] PC is at eth_type_trans+0x5c/0x164
[ 5031.349913] LR is at polling_tasklet_fn+0x84/0x144 [alex_mcn]
然后它给了我堆栈跟踪:
[ 5031.406316] Call trace:
[ 5031.406798] [<ffffffc00045d604>] eth_type_trans+0x5c/0x164
[ 5031.407482] [<ffffffbffc000310>] polling_tasklet_fn+0x80/0x144 [alex_mcn]
[ 5031.408114] [<ffffffc000099198>] tasklet_hi_action+0xc4/0x198
[ 5031.408716] [<ffffffc0000995bc>] __do_softirq+0x10c/0x220
[ 5031.409304] [<ffffffc00009993c>] irq_exit+0x8c/0xc0
[ 5031.409882] [<ffffffc000084514>] handle_IRQ+0x6c/0xe0
[ 5031.410440] [<ffffffc000081290>] gic_handle_irq+0x3c/0x80
我的初始代码是:
static int alex_mcn_single_rx(void){
struct sk_buff *skb;
...
skb = netdev_alloc_skb(net_dev, pktLen+5);
...
skb->protocol = eth_type_trans(skb, net_dev);
skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
if( netif_rx(skb) == NET_RX_SUCCESS){
net_dev->stats.rx_packets++;
}
else{
printk("!!! Failure in receiving the packet\n");
return 1;
}
return 0;
}
static void polling_tasklet_fn(unsigned long arg)
{
polling_data->count++;
if(polling_data->loops){
if((polling_data->count)%10000==0)
{
alex_mcn_single_rx();
}
}
tasklet_schedule(&polling_data->tlet);
}
static void init_polling_tasklet(char * buf){
polling_data->count = 0;
polling_data->loops = 1;
/* register the tasklet */
tasklet_init(&polling_data->tlet, polling_tasklet_fn, 0);
tasklet_hi_schedule(&polling_data->tlet);
}
此代码有效,但是当我删除 if(polling_data->loops) 语句时,它停止工作并给我与上述相同的错误。这对我来说没有任何意义,因为小任务中没有竞争条件。另外,我知道 eth_type_trans 是唯一的罪魁祸首。当我删除它时它不会遇到任何错误(尽管数据包将被丢弃)。如果有人能给我一些线索为什么会发生这种情况,我将不胜感激。
p.s:我正在使用带有 ARMv8 架构的 gem5 模拟器。来测试我的设计。
已解决:我最终将 eth_type_trans() 函数复制到我的设备驱动程序并调试 printks 的问题。以这种方式调试它比重建内核更容易(模拟器需要很多时间)。在我将 eth_trans_type() 函数复制到我的代码中并开始在我的设备驱动程序中调试它后,该函数开始正常工作
【问题讨论】:
-
错误
Unable to handle kernel NULL pointer dereference at virtual address 00000000很能说明问题。也许从字面上阅读会有所帮助?
标签: linux linux-kernel linux-device-driver