interruptible_sleep_on_timeout引起的warning
冷胜魁(Seaquester)
lengshengkui@gmail.com
2010-4-14
我有一个lcd driver(由厂商提供),在Linux kernel 2.6.18-164.el5(RHEL v5.4)中会重复打印一个warning:
=======================
BUG: warning at kernel/sched.c:4093/interruptible_sleep_on_timeout() (Not tainted)
[] interruptible_sleep_on_timeout+0x61/0xd4
[] default_wake_function+0x0/0xc
[] lcd_read_byte+0x0/0x68 [lcd_p]
[] lcd_read_byte+0x2a/0x68 [lcd_p]
[] vfs_read+0x7d/0xdf
[] sys_read+0x3c/0x63
[] syscall_call+0x7/0xb
=======================
尽管没有kernel panic,但是十分讨厌。而以前的Kernel 2.6.18-8.el5(RHEL v5.0)没有遇到这个问题。我google了一下,有一些发现,网上有人这样说:
The various sleep_on() function often have race conditions, so the usage should be avoided. It's said that wait_event() and friends can be used in a more safely manner and should be used instead.
AFAIK some checks have been introduced that print a warning once a driver uses one of the old functions.
So, nothing serious, but it should be changed.
尽管他遇到的warning和我的不太一样,但我觉得问题可能就在这里。我仔细的分析了lcd driver的代码,发现lcd_read_byte()函数中调用了一个系统函数:interruptible_sleep_on_timeout()。
do {
status = ~parport_read_status(pd->port) ^ 0x84;
interruptible_sleep_on_timeout(&wq,10);
i++;
} while ((status == 0x04) && (i < LCD_MAX_LOOP));
我在2.6.18-164.el5的source code中找到该函数的定义(kernel/sched.c):
long fastcall __sched
interruptible_sleep_on_timeout(wait_queue_head_t *q, long timeout)
{
SLEEP_ON_VAR
SLEEP_ON_BKLCHECK
current->state = TASK_INTERRUPTIBLE;
SLEEP_ON_HEAD
timeout = schedule_timeout(timeout);
SLEEP_ON_TAIL
return timeout;
}
该函数的里面调用了宏SLEEP_ON_BKLCHECK。SLEEP_ON_BKLCHECK 的定义如下:
#define SLEEP_ON_BKLCHECK \
if (unlikely(!kernel_locked()) && \
sleep_on_bkl_warnings < 10) { \
sleep_on_bkl_warnings++; \
WARN_ON(1); \
}
里面调用了WARN_ON,查看了WARN_ON这个宏的定义,发现正是它打印的warning:
#define WARN_ON(condition) do { \
if (unlikely((condition)!=0)) { \
printk("BUG: warning at %s:%d/%s() (%s)\n", __FILE__, __LINE__, __FUNCTION__, print_tainted()); \
dump_stack(); \
} \
} while (0)
所以我考虑到用 wait_event_interruptible_timeout() 来替换,测试了一下,发现它会导致系统很慢,可能是我的使用方式不对。
我就决定用最简单的方法来替换:msleep()。因为sleep的时间很短,只有10ms,所以我觉得不会用什么问题。
do {
status = ~parport_read_status(pd->port) ^ 0x84;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
interruptible_sleep_on_timeout(&wq,10);
#else
msleep(10);
#endif
i++;
} while ((status == 0x04) && (i < LCD_MAX_LOOP));
测试了一下,看起来工作正常。
总之,interruptible_sleep_on_timeout这几个sleep_on函数已经是不推荐使用的(deprecated),在新的Kernel中可能会被废除,推荐用wait_event*函数来代替它。
阅读(4337) | 评论(0) | 转发(0) |