免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 4624 | 回复: 11
打印 上一主题 下一主题

IRQF_NODELAY与tasklet_schedule之间的矛盾 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-08-05 11:31 |只看该作者 |倒序浏览
本帖最后由 hantor 于 2011-08-05 12:20 编辑

IRQF_NODELAY与tasklet_schedule之间的矛盾

在做Ingo molnar RealTime Linux 驱动,发现了一个很有趣的问题:

(本人使用的是linux2.6.26.8内核,打Ingo molnar RealTime Linux 补丁patch-2.6.26.8-rt16.bz2)

在开发中,尝试了三套方案:

1、在中断中直接调用IO处理函数。

这时候,无论在注册中断函数的时候使用或不使用IRQF_NODELAY标志,程序都能够正常运行。

代码如下:

#define INGO_RT //是否注释此行代码,程序均正常运行。

下面是中断注册程序片段

#ifdef INGO_RT
result = request_irq ( dev->irq,
  mycom_interrupt_callback,  IRQF_SHARED | IRQF_NODELAY, MYDRIVERNAME, (void *)dev);//使用IRQF_NODELAY标志
   #else
result = request_irq ( dev->irq,
  mycom_interrupt_callback,  IRQF_SHARED , MYDRIVERNAME, (void *)dev);//按普通方式注册中断处理函数
#endif

下面是中断处理函数

void mycom_do_tasklet( unsigned long data )



……



static irqreturn_t mycom_interrupt_callback(int irqno, void *dev_id)
{

struct mycom_dev *dev  = (struct mycom_dev *)dev_id;
if(dev->irq != irqno)//when the interrupt happend,the "irqno" is transfer to the interrupt callback fun
  return IRQ_NONE;
tasklet_dev = dev;
// tasklet_schedule( &mycom_tasklet ); //用tasklet实现IO操作,这里注释掉
mycom_do_tasklet( (unsigned long )tasklet_dev );//直接调用IO 操作函数
return IRQ_HANDLED;
}


2、在注册中断的时候使用普通标志注册,中断处理采用tasklet作为底半部

运行结果是:程序正常运行

代码如下:

//#define INGO_RT 将INGO_RT 标志注释掉,注册中断处理函数采用无IRQF_NODELAY方式

void mycom_do_tasklet( unsigned long data );
DECLARE_TASKLET(mycom_tasklet, mycom_do_tasklet, (unsigned long )&tasklet_dev );

下面是中断注册程序片段

#ifdef INGO_RT
result = request_irq ( dev->irq,
  mycom_interrupt_callback,  IRQF_SHARED | IRQF_NODELAY, MYDRIVERNAME, (void *)dev);//使用IRQF_NODELAY标志
   #else
result = request_irq ( dev->irq,
  mycom_interrupt_callback,  IRQF_SHARED , MYDRIVERNAME, (void *)dev);//按普通方式注册中断处理函数
#endif

下面是中断处理函数

void mycom_do_tasklet( unsigned long data )



……



static irqreturn_t mycom_interrupt_callback(int irqno, void *dev_id)
{

struct mycom_dev *dev  = (struct mycom_dev *)dev_id;
if(dev->irq != irqno)//when the interrupt happend,the "irqno" is transfer to the interrupt callback fun
  return IRQ_NONE;
tasklet_dev = dev;
tasklet_schedule( &mycom_tasklet ); //用tasklet实现IO操作
// mycom_do_tasklet( (unsigned long )tasklet_dev );//直接调用IO 操作函数,这里注释掉
return IRQ_HANDLED;
}


3、在注册中断的时候使用IRQF_NODELAY标志注册,中断处理采用tasklet作为底半部

运行结果是:整个系统马上死机,连个OOPS都没有

代码如下:

#define INGO_RT  //使用IRQF_NODELAY标志

void mycom_do_tasklet( unsigned long data );
DECLARE_TASKLET(mycom_tasklet, mycom_do_tasklet, (unsigned long )&tasklet_dev );

下面是中断注册程序片段

#ifdef INGO_RT
result = request_irq ( dev->irq,
  mycom_interrupt_callback,  IRQF_SHARED | IRQF_NODELAY, MYDRIVERNAME, (void *)dev);//使用Ingo RT 中的IRQF_NODELAY标志
   #else
result = request_irq ( dev->irq,
  mycom_interrupt_callback,  IRQF_SHARED , MYDRIVERNAME, (void *)dev);//按普通方式注册中断处理函数
#endif

下面是中断处理函数

void mycom_do_tasklet( unsigned long data );



……



static irqreturn_t mycom_interrupt_callback(int irqno, void *dev_id)
{

struct mycom_dev *dev  = (struct mycom_dev *)dev_id;
if(dev->irq != irqno)//when the interrupt happend,the "irqno" is transfer to the interrupt callback fun
  return IRQ_NONE;
tasklet_dev = dev;
tasklet_schedule( &mycom_tasklet ); //用tasklet实现IO操作
// mycom_do_tasklet( (unsigned long )tasklet_dev );//直接调用IO 操作函数,这里注释掉
return IRQ_HANDLED;
}




从上面这三套方案来看,IRQF_NODELAY与tasklet_schedule是一对矛盾。

根据我个人的理解,注册中断处理函数的时候使用IRQF_NODELAY,那么中断处理就不能延时。

而tasklet本身就是一个小延时后才能调度的小任务。所以使用IRQF_NODELAY就不能使用tasklet。

不能使用tasklet的话,那就意味着要在中断处理函数中做较多的工作,需要较多的时间,而这时候是关中断的。

假设这时候又有一个中断到来呢,即使是只有几个us的间隔。


以上分析纯粹个人试验得出的结果,是否正确,请高手指正。





备注:1、Ingo molnar RealTime Patch是红帽公司提供一个RealTime补丁。在实时linux的众多补丁中,它对源代码的修改最少,可以在很少修改源代码的基础上实现RealTime OS。该补丁可以在http://www.kernel.org/pub/linux/kernel/projects/rt/中下载。
     2、在2.6内核中,比较高版本的中断标志从原来的SA_变成了IRQF_前缀,并且取消了SA_INTERRUPT标志。



结束语:在linux世界里摸爬打滚,谁都可以提供一套代码,没有一套标准的东西,没有足够的参考文档,只能自己摸索,步履维艰啊!!

论坛徽章:
7
丑牛
日期:2013-10-18 14:43:21技术图书徽章
日期:2013-11-03 09:58:03辰龙
日期:2014-01-15 22:57:50午马
日期:2014-09-15 07:04:39丑牛
日期:2014-10-16 14:25:222015年亚洲杯之伊朗
日期:2015-03-16 10:24:352015亚冠之城南
日期:2015-05-31 09:52:32
2 [报告]
发表于 2011-08-05 12:22 |只看该作者
仅中断部分不会导致这个问题,没有任何OOPS是系统处于一种死锁状态。

论坛徽章:
0
3 [报告]
发表于 2011-08-05 12:32 |只看该作者
本帖最后由 hantor 于 2011-08-05 12:36 编辑

恐怕不是死锁那么简单。在方案3的代码中:看模块初始化代码:
#ifdef INGO_RT

static int mycom_init_module(void)
{
    ……
    printk(KERN_WARNING "\nMessage form mycom_init_module,MSG 1: “);
    ……
   #ifdef INGO_RT
result = request_irq ( dev->irq,
  mycom_interrupt_callback,  IRQF_SHARED | IRQF_NODELAY, MYDRIVERNAME, (void *)dev);
   #else
result = request_irq ( dev->irq,
  mycom_interrupt_callback,  IRQF_SHARED , MYDRIVERNAME, (void *)dev)
#endif
   ……
    return 0;
}

假如是出现死锁的话,那么第一个printk(KERN_WARNING "\nMessage form mycom_init_module,MSG 1: “);
应该能输出。而现在是一加载模块,什么输出都没有就死机了。
这个现象只出现在同时使用IRQF_NODELAY注册中断和tasklet的时候,在方案1、2中,两者不同时存在,程序运行得很好。

论坛徽章:
7
丑牛
日期:2013-10-18 14:43:21技术图书徽章
日期:2013-11-03 09:58:03辰龙
日期:2014-01-15 22:57:50午马
日期:2014-09-15 07:04:39丑牛
日期:2014-10-16 14:25:222015年亚洲杯之伊朗
日期:2015-03-16 10:24:352015亚冠之城南
日期:2015-05-31 09:52:32
4 [报告]
发表于 2011-08-05 14:05 |只看该作者
你写的设备操作的中断函数是不符合设备操作规范的。
对于PCI LEVEL中断的设备,在对某的寄存器操作后才会取消中断线的有效状态。你却在半底中去操作,而在中断函数中没做任何动作。
这意味着,你的中断程序完成以后一旦开启中断,则处理器会继续接受一个中断处理。你的第2个方法没问题是很奇怪的,除非设备没有真正开始工作

论坛徽章:
0
5 [报告]
发表于 2011-08-05 14:43 |只看该作者
你说的这个问题,我正在调试。
在中断函数里面确实应该先恢复中断状态寄存器,否则会不断中断。
但这个中断函数在没有任何修改的情况下,在方案1、2里可以运行。

我现在要说的问题是方案3,同时使用IRQF_NODELAY标志注册中断和tasklet,必定死机。
我尝试过在open的时候注册中断,但一执行open就死机;在模块加载的时候注册中断,也同样死机,更没有任何printk输出。

论坛徽章:
7
丑牛
日期:2013-10-18 14:43:21技术图书徽章
日期:2013-11-03 09:58:03辰龙
日期:2014-01-15 22:57:50午马
日期:2014-09-15 07:04:39丑牛
日期:2014-10-16 14:25:222015年亚洲杯之伊朗
日期:2015-03-16 10:24:352015亚冠之城南
日期:2015-05-31 09:52:32
6 [报告]
发表于 2011-08-05 15:05 |只看该作者
本帖最后由 smalloc 于 2011-08-05 15:11 编辑

光是注册中断又没执行tasklet_schedule,怎么会出问题。request_irq 函数又不知道你中断函数里是什么。
我怀疑你注册的时候设备中断硬件发生了。
可能的解释就是你这个版本的中断函数调度和标准不一样。比如,不是NODELAY的时候一个中断结束后会紧接着执行tasklet,而如果是NODELAY的时候会接着处理下一次中断,只有这样才解释通

如果是标准内核,2-3都应该是轮不到tasklet执行的

论坛徽章:
7
丑牛
日期:2013-10-18 14:43:21技术图书徽章
日期:2013-11-03 09:58:03辰龙
日期:2014-01-15 22:57:50午马
日期:2014-09-15 07:04:39丑牛
日期:2014-10-16 14:25:222015年亚洲杯之伊朗
日期:2015-03-16 10:24:352015亚冠之城南
日期:2015-05-31 09:52:32
7 [报告]
发表于 2011-08-05 15:27 |只看该作者
至于你第一条信息没PRINTK出来,情况句复杂了。假设设备一开始就产生了一个中断。而PRINTK打印可能需要另一个中断才能完成。而你这个设备中断优先级可能比PRINTK高。这样你的PRINTK当然就打不出来了。
看起来你这个设备是自己做的。

论坛徽章:
0
8 [报告]
发表于 2011-08-05 15:35 |只看该作者
smalloc ,假设设备一开始就产生了一个中断,但是我的printk函数是在注册中断之前。按道理来说,我的驱动都还没有注册中断,就假设是中断函数或tasklet里有死锁,我的printk也应该先有输出,而不应该是一加载驱动就死机。这又作何解释呢!?

论坛徽章:
7
丑牛
日期:2013-10-18 14:43:21技术图书徽章
日期:2013-11-03 09:58:03辰龙
日期:2014-01-15 22:57:50午马
日期:2014-09-15 07:04:39丑牛
日期:2014-10-16 14:25:222015年亚洲杯之伊朗
日期:2015-03-16 10:24:352015亚冠之城南
日期:2015-05-31 09:52:32
9 [报告]
发表于 2011-08-05 15:57 |只看该作者
回复 8# hantor


    我只是假设PRINTK的输出依赖于某中断才能完成。或许它还延迟了。比如PRINTK函数返回,仍然未完成显示,这也是可能的(这个没看过PRINTK具体怎么实现的,不是很确定),不管怎么说如果PRINTK依赖的中断在中断控制器上的优先级别如果比你的设备接的中断控制器的级别低,在你设备事先产生了一个中断的情况下,PRINTK则不能显示。你可以在这后面插入一个大的延时看看,如果还不PRINTK出来,多半就是这问题了---假设你没搞错PRINTK的输出级别情况下。

这只是猜测一

另外就是很明显仅仅是注册中断函数根本不可能可tasklet冲突,注册程序根本不知道你中断函数里放的什么。
要冲突也只能运行一次中断程序。
我怀疑你的设备是自己公司设计的,肯定有些BUG。

论坛徽章:
0
10 [报告]
发表于 2011-08-05 16:17 |只看该作者
本帖最后由 hantor 于 2011-08-05 17:15 编辑

设备是普通PC的串口,在VM下面搞。
我采用方案1继续调试程序,在模块加载阶段注册中断。正如你所猜测的,中断函数一注册完成就产生了一个中断。其速度还快于module_init的return。
可我在加载模块阶段是没有对串口本身有任何操作的。这就是更奇怪的地方了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP