超强的Linux中断分析Word格式.docx

上传人:b****5 文档编号:17259565 上传时间:2022-11-29 格式:DOCX 页数:14 大小:22.27KB
下载 相关 举报
超强的Linux中断分析Word格式.docx_第1页
第1页 / 共14页
超强的Linux中断分析Word格式.docx_第2页
第2页 / 共14页
超强的Linux中断分析Word格式.docx_第3页
第3页 / 共14页
超强的Linux中断分析Word格式.docx_第4页
第4页 / 共14页
超强的Linux中断分析Word格式.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

超强的Linux中断分析Word格式.docx

《超强的Linux中断分析Word格式.docx》由会员分享,可在线阅读,更多相关《超强的Linux中断分析Word格式.docx(14页珍藏版)》请在冰豆网上搜索。

超强的Linux中断分析Word格式.docx

#endif

#ifdefined(CONFIG_GENERIC_PENDING_IRQ)||defined(CONFIG_IRQBALANCE)

cpumask_tpending_mask;

#ifdefCONFIG_PROC_FS

structproc_dir_entry*dir;

constchar*name;

}____cacheline_internodealigned_in_smp;

Linux有一个全局变量,包含了了所有的IRQ:

(kernel/irq/handle.c)

structirq_descirq_desc[NR_IRQS]__cacheline_aligned={

[0...NR_IRQS-1]={

.status=IRQ_DISABLED,

.chip=&

no_irq_chip,

.handle_irq=handle_bad_irq,

.depth=1,

.lock=SPIN_LOCK_UNLOCKED,

.affinity=CPU_MASK_ALL

}

3)irq_chip(即在genericirq之前的hw_interrupt_type)

>

以下这段是genericirq之前的:

Linux支持N种可编程中断控制器PIC,所以有一个structhw_interrupt_type,对于i8259A

来说,这个结构是i8259A_irq_type,对于IOAPIC来说,根据设置为电平触发或边沿触发的方式,

分别有ioapic_level_type和ioapic_edge_type两个不同的结构。

在引入genericirq补丁之后,定义了几个irq_chip结构:

针对i386和x86-64各有一个定义的:

ioapic_chip,

i8259A_chip,

lapic_chip,

msi_chip,

ht_irq_chip,

vmi_chip,

针对visw体系的:

cobalt_irq_chip,

piix4_master_irq_type,

piix4_virtual_irq_type

针对voyager体系的:

vic_chip

其它目的的:

no_irq_chip,

dummy_irq_chip

4)irq_stat[NR_CPUS]

Linux定义了一个全局的数组,用来描述每个CPU上的irq处理状态:

(arch/i386/kernel/irq.c)

DEFINE_PER_CPU(irq_cpustat_t,irq_stat)__cacheline_internodealigned_in_smp;

EXPORT_PER_CPU_SYMBOL(irq_stat);

irq_stat_t的定义。

typedefstruct{

unsignedint__softirq_pending;

unsignedlongidle_timestamp;

unsignedint__nmi_count;

/*archdependent*/

unsignedintapic_timer_irqs;

}____cacheline_alignedirq_cpustat_t;

5).中断共享

我们知道,多个中断源可以共享一个irq线。

Linux的实现方式是,每个中断源都有自己的

一个structirqaction,

irqaction结构的定义:

structirqaction{

irq_handler_thandler;

unsignedlongflags;

cpumask_tmask;

void*dev_id;

structirqaction*next;

intirq;

};

同一个irq可能有多个irqaction,组成一个链表。

structirq_desc中有个域:

这个链表就包含了所有共享该irq号的中断源(及其对应的handler等信息)。

当devicedriver

进行request_irq()时,会为它生成一个irqaction,设置相应的值,然后挂载

irq_desc[<

irq>

].action队列中(是添加在链表的最后面)。

request_irq(irq,handler,irqflags,devname,dev_id)>

setup_irq(irq,irqaction)

flags有3个:

IRQF_SHARED:

共享中断号

IRQF_DISABLED:

就是旧时代的SA_INTERRUPT,设置了该标志,则执行ISR时关本地中断

IRQF_SAMPLE_RANDOM:

告诉内核,本中断源可以用作随机数生成器的熵池

只有满足以下条件,irq才可以在多个中断源之间共享:

a).每个中断源都愿意共享irq:

request_irq时指定了IRQF_SHARED

b).试图共享一个irq的中断源,具有相同的触发机制(都是leveltrigger,或者都是edge

trigger),并且具有相同的polarity(都是低电平有效,或者都是高电平有效)

下面是set_irq()函数中判断old和new两个中断源是否可以share同一个irq号的代码:

/*

*Can'

tshareinterruptsunlessbothagreetoandare

*thesametype(level,edge,polarity).Sobothflag

*fieldsmusthaveIRQF_SHAREDsetandthebitswhich

*setthetriggertypemustmatch.

*/

if(!

((old->

flags&

new->

flags)&

IRQF_SHARED)||

((old->

flags^new->

IRQF_TRIGGER_MASK)){

old_name=old->

name;

gotomismatch;

6).中断处理(do_IRQ,__do_IRQ,generic_handle_irq,etc)-PartI:

__do_IRQ

__do_IRQ()是genericirq引入之前的通用中断处理函数(除了IPI中断,其它所有中断/异常

都经过它),它由do_IRQ调用,并调用handle_IRQ_event(而handle_IRQ_event会调用各个

driver的ISR)。

在引入genericirq之后,__do_IRQ()函数已基本不用了。

64位的X86系统上还可能使用

它(通过do_IRQ>

generic_handle_irq),32位的x86已经完全不用它了。

然而我们还是看一下__do_IRQ函数,因为道理是一样的:

__do_IRQ():

/*{{{*/

//首先给irq_desc[irq].lock加锁,以免别的CPU访问该desc结构

spin_lock(&

desc->

lock);

//发送ACK给中断控制器

if(desc->

chip->

ack)

desc->

ack(irq);

*REPLAYiswhenLinuxresendsanIRQthatwasdroppedearlier

*WAITINGisusedbyprobetomarkirqsthatarebeingtested

/*清除IRQ_REPLAY和IRQ_WAITING标志*/

status=desc->

status&

~(IRQ_REPLAY|IRQ_WAITING);

/*设置IRQ_PENDING标志。

这个flag的意思是,已经ACK但尚未处理*/

status|=IRQ_PENDING;

/*we_want_tohandleit*/

*IftheIRQisdisabledforwhateverreason,wecannot

*usetheactionwehave.

/*如果IRQ被disable了,但是我们收到了中断,说明这是个spuriousinterrupt,

*有些有BUG的主板等硬件会干这种事

action=NULL;

/*只要IRQ_DISABLED或者IRQ_INPROGRESS被设置,我们就不handle该irq。

*

*对于IRQ_INPROGRESS被设置的情况,说明此irq号的另一个实例正运行在

*另一个CPU上,我们就不处理了,而是让_那个_CPU在运行完它的ISR时再检查

*一下IRQ_PENDING标志,那时候它会再去处理我们这里逃避的事情的

if(likely(!

(status&

(IRQ_DISABLED|IRQ_INPROGRESS)))){/*正常情况下2这都不被设置,

*那我们就设置desc->

status

action=desc->

action;

status&

=~IRQ_PENDING;

/*wecommittohandling,清除pending标志*/

status|=IRQ_INPROGRESS;

/*wearehandlingit,设置inprogress标志*/

status=status;

*IfthereisnoIRQhandleroritwasdisabled,exitearly.

*SincewesetPENDING,ifanotherprocessorishandling

*adifferentinstanceofthissameirq,theotherprocessor

*willtakecareofit.

if(unlikely(!

action))

gotoout;

*Edgetriggeredinterruptsneedtoremember

*pendingevents.

*Thisappliestoanyhwinterruptsthatallowasecond

*instanceofthesameirqtoarrivewhileweareindo_IRQ

*orinthehandler.Butthecodehere>

for(;

;

){

irqreturn_taction_ret;

//真正的IRQ处理是handle_IRQ_event,我们先unlock

spin_unlock(&

action_ret=handle_IRQ_event(irq,action);

//再lock,因为后面还要unlock

*在我们调用handle_IRQ_event时,如果同一个irq又在另一个CPU上

*来了一次,那个CPU会检测到IRQ_INPROGRESS标志,只设置了IRQ_PENDING

*标志便退出了。

这时我们就会检测到该标志,从而再处理第2次到来的irq

*注意!

IRQ_PENDING只是个逻辑标志,而不是一个counter!

所以,这种方式

*只能处理同一irq的两个实例!

如果发生了更多实例,第3个,第4个……就丢失了

//如果没有第2个需要处理,退出

(desc->

IRQ_PENDING)))

break;

//还有第2个需要处理,那么就清除IRQ_PENDING标志,表示我们已经答应要处理它了

/*}}}*/

7).中断处理(do_IRQ,__do_IRQ,generic_handle_irq,etc)-PartII:

handle_IRQ_event

handle_IRQ_event()依次调用irq_desc[irq]->

action链表上的每一个action。

它会先打开中断(如果request_irq时没有设置IRQF_DISABLED标志),然后一个个执行irqaction,

再禁用本地中断。

handle_IRQ_event:

irqreturn_thandle_IRQ_event(unsignedintirq,structirqaction*action)

{

irqreturn_tret,retval=IRQ_NONE;

unsignedintstatus=0;

handle_dynamic_tick(action);

//如果指定了IRQF_DISABLED,就在关中断的情形下执行ISR

//否则的话,在开中断的情形下执行ISR

(action->

IRQF_DISABLED))

local_irq_enable_in_hardirq();

//该循环遍历irq_desc[irq]->

action链表,一个个调用其handler域

do{

ret=action->

handler(irq,action->

dev_id);

if(ret==IRQ_HANDLED)

status|=action->

flags;

retval|=ret;

action=action->

next;

}while(action);

if(status&

IRQF_SAMPLE_RANDOM)

add_interrupt_randomness(irq);

local_irq_disable();

returnretval;

Linux有两种情况可能导致丢中断,都是在SMP下才会发生的:

a).CPU1在处理irqN,结果又来了一个irqN在CPU2上执行,这时候该CPU2只设置

irq_desc[irq].status的IRQ_PENDING标志,以便CPU1去检查从而再执行一遍。

当如果CPU3又收到一次,也设置IRQ_PENDING标志,这时CPU2设置的信息会丢失。

补救办法:

b).CPU1在处理器某IRQ之前,先发送ACK给PIC,结果这时候CPU2通过PIC禁用了该irq,

从而导致irq_desc[irq].status的IRQ_DISABLED标志被设置。

然后CPU1在正要处理

irq时发现对应的IRQ_DISABLED标志置位,于是退出。

这样就丢了一次中断。

在下一次enable_irq()被调用时,检查是否存在的这样的丢失。

若然,

调用check_irq_resend()重新generate一次中断。

注意,在__do_IRQ()的一开始会清楚irq_desc[irq].status的IRQ_REPLAY

标志,这时为了防止对一次irq丢失「补救」多次。

8).中断处理(do_IRQ,__do_IRQ,generic_handle_irq,etc)-PartIII:

GenericIRQ补丁

FIXME:

我记得genericirq补丁是ThomasGleixner和IngoMolnar在大约2.6.17时引入的,

当时支持i386、x86-64和arm三个体系结构。

genericirq层的引入,是为了剥离irqflow和irqchip过于紧密的耦合。

为driver程序员提供

通用的API来request/enable/disable/free中断,这样程序员不用知道任何底层的中断控制器细节。

8.1)它为driver程序员提供的highlevel的API:

request_irq()

free_irq()

disable_irq()

enable_irq()

disable_irq_nosync()(SMP>

synchronize_irq()(SMP>

set_irq_type()

set_irq_wake()

set_irq_data()

set_irq_chip()

set_irq_chip_data()

8.2)它为irqflow提供了一组预定义了的方法:

handle_level_irq()=>

针对leveltype的irqhandler

handle_edge_irq()=>

针对edgetype的irqhandler

handle_simple_irq()=>

针对Simpleandsoftware-decodedIRQS

//FIXME:

我猜测percpuirq不是IPI,而是某种x86没有的东西

handle_percpu_irq()=>

针对per-cpulocalIRQs

handle_fasteoi_irq()=>

针对transparentcontrollers,目前IO-APIC主要用它和edge

什么叫透明的中断控制器?

老子咋看不懂涅?

Irq的flowtype,genericirq有以下数种:

#defineIRQ_TYPE_NONE0x00000000/*Default,unspecifiedtype*/

#defineIRQ_TYPE_EDGE_RISING0x00000001/*Edgerisingtype*/

#defineIRQ_TYPE_EDGE_FALLING0x00000002/*Edgefallingtype*/

#defineIRQ_TYPE_EDGE_BOTH(IRQ_TYPE_EDGE_FALLING|IRQ_TYPE_EDGE_RISING)

#defineIRQ_TYPE_LEVEL_HIGH0x00000004/*Levelhightype*/

#defineIRQ_TYPE_LEVEL_LOW0x00000008/*Levellowtype*/

#defineIRQ_TYPE_SENSE_MASK0x0000000f/*Maskoftheabove*/

#defineIRQ_TYPE_PROBE0x00000010/*Probinginprogress*/

--没有看到simple类型和per-cpu类型,我估计这2者都是其他architectures上的。

这里把EDGE触发的irq又分成了

上升沿、下降沿和both,level触发的又分成了低电平有效和highactive。

这5个函数取代了原来的__do_IRQ,由do_IRQ直接调用:

handle_irq(irq,desc);

而这个irq_desc[irq].handle_irq又是在哪里设置的呢?

不同的irqchip有不同的设置,现在让

我们看一下ioapic_chip上的irqs的设置:

staticvoidioapic_register_intr(intirq,unsignedlongtrigger)

/*如果不是edge触发的,就设置为handle_fasteoi_irq*//

if(trigger){

irq_desc[irq].status|=IRQ_LEVEL;

set_irq_chip_and_handler_name(irq,&

ioapic_chip,

handle_fasteoi_irq,"

fasteoi"

);

}else{

/*如果是edge触发的,就设置为handle_edge_irq*/

irq_desc[irq].status&

=~IRQ_LEVEL;

handle_edge_irq,"

edge"

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 信息与通信

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1