Chinaunix

标题: 周日回来加班,小声问个关于workqueue问题 [打印本页]

作者: imlvye    时间: 2012-02-19 09:14
标题: 周日回来加班,小声问个关于workqueue问题
       linux驱动里面使用create_singlethread_workqueue建立了一个单线程workqueue, 然后在中断处理函数中使用queue_work函数把任务放到workqueue中处理,但任务出来的速度比较慢,而中断的速度比较快,想问下这样长时间下会不会引起workqueue的 溢出或者是简单丢掉下任务?
      然后我看ULK中说flush_workqueue函数会等待工作队列中所以挂起的任务执行完成,但我的实践是调用flush后很快就返回了,好像没有去执行挂起的处理,是不是让workqueue丢掉了呢?
作者: MagicBoy2010    时间: 2012-02-19 12:20
linux驱动里面使用create_singlethread_workqueue建立了一个单线程workqueue, 然后在中断处理函数中使用queue_work函数把任务放到workqueue中处理,但任务出来的速度比较慢,而中断的速度比较快,想问下这样长时间下会不会引起workqueue的 溢出或者是简单丢掉下任务?
      然后我看ULK中说flush_workqueue函数会等待工作队列中所以挂起的任务执行完成,但我的实践是调用flush后很快就返回了,好像没有去执行挂起的处理,是不是让workqueue丢掉了呢?

=============================================================

问题的关键在于,你是否会在中断处理函数中提交不同的struct work_struct对象,换句话说是否你在每次中断到来时要执行不同的操作,如果是,work_struct本身有链表机制,极端情形下导致内存分配失败是可能的。但是现实中,很少会在每次中断,比如网卡显示有数据到达时,提交不同的工作节点。如果你的中断处理函数中提交相同工作结点的话,《深入Linux设备驱动程序内核机制》 6.2.5节“提交工作节点queue_work"的叙述是:"(queue_work_on)函数首先检测work->data的WORK_STRUCT_PENDING位有没有被置1,置1的话意味着此前该work已被提交还没有处理,内核禁止驱动程序在一个工作结点还没处理完就再次提交该结点",所以即便中断发生了10次,但是工作结点可能只提交了一个。  简言之,work_queue应该按照要处理的不同任务提交不同节点,而代表相同任务的工作节点只应该出现一次。flush_workqueue使用COMPLETION机制确保所有在该函数调用前提交的节点都会被执行完毕,但是之后提交的节点它就管不着了。


作者: imlvye    时间: 2012-02-20 09:46
谢谢,我提交的是相同的work节点,那就是说当工作队列处理不过来的时候丢弃了很多。
作者: MagicBoy2010    时间: 2012-02-20 11:10
楼上的其实也不用担心丢掉的工作节点,因为假设网卡自己数据没丢,你只要提交了一个节点,当被调度执行时还是会给DMA到系统内存中的。这个跟softirq机制差不多:同一件事提交一次就够了。
另外感谢版主加分,呵呵。
作者: linuxfellow    时间: 2012-02-20 12:37
本帖最后由 linuxfellow 于 2012-02-20 12:41 编辑

回复 2# MagicBoy2010
这样举个例子会更清楚:

如果3个中断源(S1,S2,S3)共用一个中断信号: INTn
S1 对应的中断下半部为 tasklet1
S2 对应的中断下半部为  tasklet2
S3 对应的中断下半部为  tasklet2
同一个中断信号其下半部有可能重叠:如中断源 S1/S2通过中断INTn能重叠提交他们的下半部。
但中断源S2/S3其下半部就不可同时提交。在实际实现时最好秉承哪里的问题哪里解决的原则,不完全寄希望于系统,在执行完tasklet2之前屏蔽掉S2/S3,这样interrupt reentry就不会发生,有利于系统的性能提高。


   
作者: MagicBoy2010    时间: 2012-02-20 13:10
这样举个例子会更清楚:

如果3个中断源(S1,S2,S3)共用一个中断信号: INTn
S1 对应的中断下半部为 tasklet1
S2 对应的中断下半部为  tasklet2
S3 对应的中断下半部为  tasklet2
同一个中断信号其下半部有可能重叠:如中断源 S1/S2通过中断INTn能重叠提交他们的下半部。
但中断源S2/S3其下半部就不可同时提交。在实际实现时最好秉承哪里的问题哪里解决的原则,不完全寄希望于系统,在执行完tasklet2之前屏蔽掉S2/S3,这样interrupt reentry就不会发生,有利于系统的性能提高。
============================================================

不错,很好的例子。最后一点,tasklet(softirq的一种)在执行时属于中断上下文, in_interrupt(), 所以tasklet在执行时,不会有reentry的问题。
作者: linuxfellow    时间: 2012-02-20 13:24
回复 6# MagicBoy2010
是的, softirq不会reentry. 我的本意是:在tasklet2执行完之前,INTn不要再被S2/S3触发,s2/s3中断的前半部也不应该发生。因为即使触发了,其下半部也会被忽略,白白增加系统中断负担。
   
作者: MagicBoy2010    时间: 2012-02-20 13:38
本帖最后由 MagicBoy2010 于 2012-02-20 13:40 编辑

是的, softirq不会reentry. 我的本意是:在tasklet2执行完之前,INTn不要再被S2/S3触发,s2/s3中断的前半部也不应该发生。因为即使触发了,其下半部也会被忽略,白白增加系统中断负担。
==================================================================
事实上在整个tasklet执行过程中,内核都是将外部中断打开的,所以tasklet执行时,是允许外部中断进来的。你的说法恰好违反了softirq的本意,因为s2/s3的前半部分极大的可能是会重新提交一个tasklet,虽然这次的中断不会导致reentry正在执行的tasklet,但是tasklet在执行时会重新检查有无新的tasklet正在被加入(pending), 具体代码框架:

__do_softirq(void)
{
    ...
restart:
     ...
     do{
     ..
     }while(pending);
   
    pending = local_softirq_pending(); //重新检查有无新的softirq pending...
    if(pending && --max_restart)
       goto restart;

作者: MagicBoy2010    时间: 2012-02-20 13:52
除非出于保护系统共享资源的需要,系统应该尽可能地开放响应外部中断的能力,否则会带来很多问题。这也是为什么Linux内核有hardirq和softirq的主要原因。
作者: linuxfellow    时间: 2012-02-21 08:04
本帖最后由 linuxfellow 于 2012-02-21 08:05 编辑

回复 9# MagicBoy2010
只屏蔽S2/S3中断源,其他中断都开放;如果S2/S3再次发生中断, 只能执行前半部没有任何意义。屏蔽S2/S3对其他中断没有任何影响。


   
作者: MagicBoy2010    时间: 2012-02-21 10:51
只屏蔽S2/S3中断源,其他中断都开放;如果S2/S3再次发生中断, 只能执行前半部没有任何意义。屏蔽S2/S3对其他中断没有任何影响。

============================
假设S2/S3的设备正在处理一个softirq时,其上的设备接收到了,比如最后一个数据包,在它的softirq部分屏蔽其硬件中断,潜在的问题是:1. 硬件中断的丢失, 2. 因为最后一个数据包接收的中断被屏蔽,hardirq无法进入,因此无法标记一个新的softirq需要处理,结果是最后一个数据包无法被接收...
   
作者: linuxfellow    时间: 2012-02-21 13:44
本帖最后由 linuxfellow 于 2012-02-21 13:49 编辑

回复 11# MagicBoy2010
你的这种情况即使S2/S3中断打开,还是丢包了。S2/S3中断打开,hardirq得以运行,但tasklet2不能运行, 数据还是得不到处理;反而没必要的运行了有S2/S3触发的hardirq. 我这里不是把中断信号INT屏蔽掉, 是屏蔽S2/S3中断源; S2/S3不能在触发中断到INT上,但S1还是可以继续中断的。
作者: MagicBoy2010    时间: 2012-02-21 14:19
你的这种情况即使S2/S3中断打开,还是丢包了。S2/S3中断打开,hardirq得以运行,但tasklet2不能运行, 数据还是得不到处理;反而没必要的运行了有S2/S3触发的hardirq. 我这里不是把中断信号INT屏蔽掉, 是屏蔽S2/S3中断源; S2/S3不能在触发中断到INT上,但S1还是可以继续中断的。

===============================================
兄弟,我认为我已经讲得很清楚了,"S2/S3中断打开,hardirq得以运行,但tasklet2不能运行, 数据还是得不到处理", hardirq部分可以raise_softirq_irqoff(TASKLET_SOFTIRQ),这样导致8楼中的代码在do...while...循环后的pending!=0,将goto restart,换句话说在一个softirq处理过程中处理了两次外部中断,而屏蔽S2/S3,8楼中的代码在do...while...之后的pending==0,这个softirq就结束了,它不会再去处理最后一个数据包了。8楼列出的代码我认为已经足够表述softirq的运作机制了:softirq部分,内核会主动打开硬件中断(local_irq_enable),这意味着要让处理器尽快可以接收外部中断。假设一个设备只会发生两次中断,A和B,在A发生时导致它的softirq部分执行,在softirq部分执行时,中断B发生,B最有可能的动作是标示一个新的softirq,但是B的hardirq部分结束后将不会重入A引起的softirq,详见do_softirq代码:if(in_interrupt()) return. 但是B标示的一个pending位被记录了下来,所以在A的softirq部分do...while...循环之后会重新读取pending = local_softirq_pending()。看来明天的博客内容又有新的题材了。。。



   




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2