我编写了内核模块,它执行nf_register_hook并使用字符设备机制将抓取的数据包通过设备读取挂钩获取到用户空间。我使用全局缓冲区和缓冲区vars这就是为什么我需要在新数据包到来或用户阅读我的设备时锁定它。 我使用了splinlock_irqsave和spin_unlock_irqrestore(& locker,flags),但我的模块陷入死锁并且系统冻结。
unsigned int main_hook(unsigned int hooknum, struct sk_buff *skb,
const struct net_device *in, const struct net_device *out,
int(*okfn)(struct sk_buff*)) {
unsigned long flags;
spin_lock_irqsave(&locker,flags);
...
spin_unlock_irqrestore(&locker,flags);
}
ssize_t sniffer_dev_read(struct file *filep, char *buff, size_t count, loff_t *offp) {
spin_lock_irqsave(&locker,flags);
...
spin_unlock_irqrestore(&locker,flags);
}
main_hook is registered in nf_register_hook()
sniffer_dev_read is registered in register_chrdev
当用户从设备读取时,系统进入死锁状态。 想法? 或者可能是irq保存/恢复与netfiler hook / char设备读取不兼容,我必须在这里使用特殊锁定?
答案 0 :(得分:5)
首先,您应该在启用锁定调试选项的情况下重新编译内核并再次尝试。他们可以帮助指出原因。
spin_lock_irqsave中存在死锁的几种可能原因。它可能是递归锁定(也就是说,您试图在锁定旋转锁定的代码部分内再次调用spin_lock_ *)。可能是你在旋转锁被锁定的情况下睡觉(不要这样做 - 对于你所持有的锁定所调用的每个功能,你必须知道它是否可以睡眠)。它可能是一个AB / BA死锁(代码的一部分首先锁定A然后是B;另一部分首先锁定B然后锁定A;如果第一部分锁定A但不锁定B而第二部分锁定B但不锁定A你有一个僵局)。等等。锁定调试选项可以检测并警告您其中的许多内容。
由于您锁定的是“全局缓冲区和缓冲区变量”,因此请尝试将锁定区域减小到最小。不是锁定在函数顶部并在最后解锁,而是在锁定之外尽可能多地操作并且仅在操作缓冲区时锁定。理想情况下,锁定部分只是一些没有函数调用的指令。在这种情况下陷入僵局要困难得多。
现在我说了所有这些,我尝试进行通灵调试(即猜测问题出在哪里):你正在调用copy_to_user(可以睡觉)并保持旋转锁定。
答案 1 :(得分:1)
您不应该使用自旋锁来锁定可以在不同上下文级别使用的资源。它忙于阻塞锁定它的处理器。
main_hook是从中断/下半部分上下文调用的吗?如果是这样,您可以使用work_queues以较低优先级完成“job”(memcpy ...)。作为一般规则,你应该尽可能少地在螺旋锁内。
答案 2 :(得分:1)
我的猜测是你有一些可能很简单的编程故障(例如尝试deference NULL等),但是由于spin_lock_irqsave禁用了中断,当你点击异常模式时你会禁用中断,所以整个机器都被锁定了。 / p>
请注意,由于NF钩子在下半部分上下文中运行,因此您不需要禁用中断 - 只需要下半部分。这将使调试变得更容易。