Chinaunix首页 | 论坛 | 博客
  • 博客访问: 590174
  • 博文数量: 50
  • 博客积分: 4764
  • 博客等级: 上校
  • 技术积分: 597
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-18 09:00
个人简介

资深IT码农,擅长Linux、C/C++、bash

文章分类

全部博文(50)

文章存档

2015年(17)

2014年(2)

2011年(7)

2010年(4)

2009年(20)

分类: LINUX

2010-04-14 11:02:53

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*函数来代替它。

阅读(4288) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~