郁闷的串口中断 nobody cared

 

 

这几天碰到一个很奇怪的问题, kernel 启动的时候,会碰到 抱怨 irq 19: nobody cared. 这个中断是串口中断. 而且我确认串口是可以work的. 因为在 early console->console 已经成功了.

经过一番搜索, 发现把kernel_init 函数中的

/* Open the /dev/console on the rootfs, this should never fail */
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
printk(KERN_WARNING "Warning: unable to open an initial console.\n");
注释掉就可以ok. 看来问题是 在 sys_open((const char __user *) "/dev/console" 中.

我的问题是: 到这里的时候, rootfs 还没有被 mount上(我的rootfs是放在USB上面的),我加了一些log,发现在 打开/dev/console后才会 去mount USB的rootfs. 那么这个时候去open /dev/console, 一定会失败了. 但是 注释说 , this should never fail. 是不是在mount rootfs之前,kernel 会创建这个设备? 在那里创建的?


NET: Registered protocol family 17
NET: Registered protocol family 15
Registering platform devices
irq 19: nobody cared (try booting with the "irqpoll" option)
Call Trace:
[] dump_stack+0x8/0x34
[] __report_bad_irq+0x44/0xe8
[] note_interrupt+0x188/0x250
[] handle_percpu_irq+0xb4/0xe0
[] do_IRQ+0x2c/0x40
[] ret_from_irq+0x0/0x4
[] __do_softirq+0x98/0x270
[] do_softirq+0xa0/0xc8
[] irq_exit+0xb4/0xd8
[] ret_from_irq+0x0/0x4
[] __setup_irq+0x1f8/0x440
[] request_threaded_irq+0x124/0x188
[] serial8250_startup+0xb6c/0xca8
[] uart_startup+0x74/0x290
[] uart_open+0x190/0x648
[] tty_open+0x288/0x850
[] chrdev_open+0x1e4/0x370
[] __dentry_open+0xf0/0x348
[] do_last+0x720/0xa10
[] do_filp_open+0x1f0/0x670
[] do_sys_open+0x9c/0x1d8
[] kernel_init+0x130/0x1b4
[] kernel_thread_helper+0x10/0x18

handlers:
[] (serial8250_interrupt+0x0/0x188)
Disabling IRQ #19
irq 19: nobody cared (try booting with the "irqpoll" option)
Call Trace:
[] dump_stack+0x8/0x34
[] __report_bad_irq+0x44/0xe8
[] note_interrupt+0x188/0x250
[] handle_percpu_irq+0xb4/0xe0
[] do_IRQ+0x2c/0x40
[] ret_from_irq+0x0/0x4
[] __do_softirq+0x98/0x270
[] do_softirq+0xa0/0xc8
[] irq_exit+0xb4/0xd8
[] ret_from_irq+0x0/0x4
[] __setup_irq+0x1f8/0x440
[] request_threaded_irq+0x124/0x188
[] serial8250_startup+0xb6c/0xca8
[] uart_startup+0x74/0x290
[] uart_open+0x190/0x648
[] tty_open+0x288/0x850
[] chrdev_open+0x1e4/0x370
[] __dentry_open+0xf0/0x348
[] do_last+0x720/0xa10
[] do_filp_open+0x1f0/0x670
[] do_sys_open+0x9c/0x1d8
[] kernel_init+0x130/0x1b4
[] kernel_thread_helper+0x10/0x18

 

grep了一把, 发现是在 drivers/char/tty_io.c 中 创建 /dev/console.

那为什么会open的时候有问题呢? 继续debug

3128 static int __init tty_init(void)
3129 {
3130 cdev_init(&tty_cdev, &tty_fops);
3131 if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
3132 register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
3133 panic("Couldn't register /dev/tty driver\n");
3134 device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL,
3135 "tty");
3136
3137 cdev_init(&console_cdev, &console_fops);
3138 if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
3139 register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
3140 panic("Couldn't register /dev/console driver\n");
3141 device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,
3142 "console");


http://lists.openwall.net/linux-kernel/2010/03/03/197

这段代码看来是 3/3 kernel中刚改过的. 原来 打开 /dev/console 是在 init_post 中做的.

感觉你遇到的问题,跟我的类似阿。
我也不知道,什么时候,谁在/dev下创建的设备。

其实谁创建的,我知道,是udev线程创建的。关键谁让udev创建的?
LVM,块设备,设计到很多东西。

这个问题 今天解决了。

现在看一下表面现象: 表面现象是 irq 19没有人 care。 那么我们的第一反应是:irq 19的中断处理函数没有注册。
但是从kernel给出的信息又说明它找到了handlers:
[] (serial8250_interrupt+0x0/0x188)
因此,没有注册irq 19的中断处理程序的推理不成立。

那么继续想一下,kernel是如何判断这个irq没有人care呢? 具体的代码在note_interrupt 里面。kernel 会记录某一个irq没有被处理的次数。如果在某一段时间里面这个irq没有被处理达到一个大的阀值,kernel 就认为这个irq没有人care。 因此,实际上,这个错误信息是: irq 19 在很长的时间里面没有人处理。

那再回到具体的平台中。平台的IRQ 19是UART,这个中断是连接到MIPS CPU的IP3。熟悉MIPS的同学都知道,中断由 MIPS CPU的中断处理程序再根据具体的中断引脚 调用具体的doIRQ。 比如,在MIPS 中断处理中,如果发现IP3被 set,就会调用 IRQ(19)。当然不同的平台中断的路由不一样,但是大体的思路是一样的。

跟踪代码,发现代码进入了MIPS的中断处理函数,也确实判断了IP3被set,然后也调用了 doIRQ(19)。但是,为什么kernel 认为这个中断没有人处理呢? 实际上,代码也进了 serial8250_interrupt。 这个函数会读 串口的IIR寄存器,这个寄存器反应了串口的中断状态,读出来的值是 0x1,也就是 NO INTERRUPT PENDING. 因此,会认为当前没有串口发生,跳出中断处理函数。 从而导致kernel 认为该中断没有处理。

那么就有个问题。 刚才说 串口中断是接到CPU 的IP3上面。先是cpu认为IP3被set,然后才调用串口中断处理程序。 如果没有串口中断,为什么CPU的IP3会被set,从而MIPS CPU认为当前有中断发生呢?这个是核心所在。 查了一些设定发现,对于龙芯的北桥设定有问题,导致 MIPS CPU的 IP2和IP3一直被set,也就是一直认为有中断发生。 由于在 sys_open((const char __user *) "/dev/console", O_RDWR, 0) 前,串口并没有使用中断因此没有问题。 但是一旦 串口采用中断了以后,立刻问题就出现了。 重新设置了龙芯的北桥后,问题解决。

两个结论:
(1) 要抓住问题的核心。有点时候kernel的错误信息有一些误导。
(2) 串口只有在进入user land前才使用中断。

结贴。

你可能感兴趣的:(kernel)