linux网络和中断,超强的Linux中断分析 —— 转自网络

1) IPI中断的初始化。

intr_init_hook调用apic_intr_init(),

后者再调用──如果CONFIG_SMP──smp_intr_init(), 这个函数设置IPI中断的处理, 然后,

apic_intr_init()为另外两个IPI:SPURIOUS_APIC_VECTOR和

ERROR_APIC_VECTOR设置ISR。

2) irq_desc[NR_IRQS]

struct

irq_desc,亦即irq_desc_rq的属性, 如irqaction、depth、

pending_mask等。 include/linux/irq.h:

struct irq_desc {

irq_flow_handler_t handle_irq;

struct

irq_chip *chip;

struct

msi_desc *msi_desc;

void *handler_data;

void *chip_data;

struct irqaction *action;

unsigned

int status;

unsigned

int depth; &nb; unsigned

int wake_depth;

unsigned

int irq_count;

unsigned

int irqs_unhandled;

spinlock_t lock;

#ifdef CONFIG_SMP

cpumask_t affinity;

unsigned

int cpu;

#endif

#if defined(CONFIG_GENERIC_PENDING_IRQ) ||

defined(CONFIG_IRQBALANCE)

cpumask_t pending_mask;

#endif

#ifdef CONFIG_PROC_FS

&nuct

proc_dir_entry *dir;

#endif

const

char *name;

} ____cacheline_internodealigned_in_smp;

Linux有一个全局变量,

包含了了所有的IRQ:(kernel/irq/handle.c)

struct irq_desc irq_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,

#ifdef CONFIG_SMP

.affinity = CPU_MASK_ALL

#endif

}

&np;

}

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

>以下这段是genericirq之前的:

>Linux支持N种可编程中断控制器PIC, 所以有一个struct

hw_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

针对> 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的定义。

typedef struct {

unsigned int __softirq_pending;

unsigned long idle_timestamp;

unsigned int __nmi_count;

unsigned int apic_timer_irqs;

} ____cacheline_aligned irq_cpustat_t;

5). 中断共享

我们知道,多个中断源可以共享一个irq线。 Lin,每个中断源都有自己的

一个struct irqaction,

irqaction结构的定义:

struct irqaction {

irq_handler_t handler;

unsigned long flags;

cpumask_t mask;

const char *name;

void *dev_id;

struct irqaction *next;

int irq;

struct proc_dir_entry *dir;

};

同一个irq可能有多个irqaction,组成一个链表。 struct irq_desc中有个域:

struct irqaction *action;

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

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

&nbs irq_desc[].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的中断源,具有相同的触发机制(都是level trigger,或者都是edge

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

下面是set_irq()函数中判断old和new两个中断源是否可以share同一�

if (!((old->flags &

new->flags) & IRQF_SHARED)

||

((old->flags ^ new->flags)

& IRQF_TRIGGER_MASK)) {

old_name = old->name;

goto mismatch;

}

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

__do_IRQ

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

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

driver的ISR)。

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

64位的X86系统上还可�bsp; 它(通过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->chip->ack(irq);

status = desc->status & ~(IRQ_REPLAY

| IRQ_WAITING);

status |= IRQ_PENDING;

action = NULL;

if (likely(!(status & (IRQ_DISABLED |

IRQ_INPROGRESS)))) {

action = desc->action;

status &= ~IRQ_PENDING;

status |= IRQ_INPROGRESS;

}

desc->status = status;

if (unlikely(!action))

goto out;

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

if (likely(!(desc->status &

IRQ_PENDING)))

break;

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

desc->status &=

~IRQ_PENDING;

}

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

handle_IRQ_event

handle_IRQ_event()依次调用irq_desc[irq]->action链表上的每一个action。

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

再禁用本地中断。

handle_IRQ_event:

irqreturn_t handle_IRQ_event(unsigned int irq, struct

irqacsp; {

irqreturn_t ret, retval = IRQ_NONE;

unsigned int status = 0;

handle_dynamic_tick(action);

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

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

if (!(action->flags &

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

=> } while (action);

if (status & IRQF_SAMPLE_RANDOM)

add_interrupt_randomness(irq);

local_irq_disable();

return retval;

}

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

a). CPU1在处理irq N,结果又来了一个irq N在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) - Part III:

Generic IRQ补丁

FIXME:我记得generic irq补丁是Thomas Gleixner和Ingo

Molnar在大约2.6.17时引入的,

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

generic irq层的引入,是为了剥离irq flow和irq chip过于紧密的耦合。

为driver程序员提供

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

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

request_irq(sp; 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) 它为irq flow提供了一组预定义了的方法:

handle_level_irq() => 针对level type的irq handler

handle_edge_irq() => 针对edge type的irq handler

handle_simple_irq() => 针对Simple and software-decoded IRQS

//FIXME: 我猜测percpu irq不是IPI,而是某种x86没有的东西

handle_percpu_irq() => 针对per-cpu local IRQs

handle_fasteoi_irq() => 针对transparent controllers���用它和edge

//FIXME: 什么叫透明的中断控制器?老子咋看不懂涅?

Irq的flow type, generic irq有以下数种:

#define

IRQ_TYPE_NONE 0x00000000 #define

IRQ_TYPE_EDGE_RISING 0x00000001 #define

IRQ_TYPE_EDGE_FALLING 0x00000002 #define

IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)

#define

IRQ_TYPE_LEVEL_HIGH 0x00000004 #define

IRQ_TYPE_LEVEL_LOW 0x00000008 #define

IRQ_TYPE_SENSE_MASK 0x0000000f #define

IRQ_TYPE_PROBE 0x00000;

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

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

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

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

desc->handle_irq(irq, desc);

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

chip有不同的设置,现在让

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

static void ioapic_register_intr(int irq, unsigned long

trigger)

{

/

if (trigger) {

irq_desc[irq].status |= IRQ_LEVEL;

set_irq_chip_and_handler_name(irq,

&ioapic_chip,

handle_fasteoi_irq, "fasteoi");

&nlse

{

irq_desc[irq].status &= ~IRQ_LEVEL;

set_irq_chip_and_handler_name(irq,

&ioapic_chip,

handle_edge_irq, "edge");

}

}

原来MSI中断也是用handle_edge_irq处理的,见代码:

pci_enable_msi() > msi_capability_init() \

=> arch_setup_msi_irqs() >

arch_setup_msi_irq():

pci_enable_msim() > msim_capability_init() /

set_irq_msi(irq, desc);

write_msi_msg(irq, &msg);

set_irq_chip_and_handler_name(irq, &msi_chip,

handle_edge_irq, "edge");

8.4) genericirq提供的一些public functions

&nchronize_irq : wait for pending IRQ handlers (on other

CPUs) disable_irq_nosync : disable an irq without waiting disable_irq : disable an irq and wait for

completion enable_irq : enable handling of an irq set_irq_wake : control irq power management wakeup free_irq : free an interrupt request_irq : allocate an interrupt line set_irq_chip : set the irq chip for an irq set_irq_type : set the irq type for an irq set_irq_data : set irq type data for an irq set_irq_chip_data : set irq chip data for an irq

8.5) geneirc irq提供的一些internal functions

handle_bad_irq : handldled irqs handle_IRQ_event : irq action chain handler __do_IRQ : original all in>: initialize a dynamically

allocated irq dynamic_irq_cleanup : cleanup a dynamically allocated irq set_irq_msi : set irq type data for an irq handle_simple_irq : Simple and software-decoded IRQs. handle_level_irq : Level type irq handler handle_fasteoi_irq : irq handler for transparent

controllers handle_edge_irq : edge type IRQ handler handle_percpu_irq : Per CPU local irq handler

8.6) irq_chip结构的方法

startup : start up the interrupt (defaults to ->enable if

NULL)

enable中断,使PIC可以handle它

shutdown down the interrupt

(defaults to ->disable if NULL)

enable : enable the interrupt (defaults to chip->unmask if

NULL)

disable : disable the interrupt (defaults to chip->mask if

NULL)

ack : start of a new interrupt

通知PIC:CPU开始处理这个irq了

mask : mask an interrupt source

mask_ack : ack and mask an interrupt source

mask和ack方法的结合,这样在某些平台上可以得到优化

unmask : unmask an interrupt source

eoi : end of interrupt - chip level

end : end of interrupt - flow level

通知PIC:中断处理完毕

set_aU affinity>: resend an IRQ to the CPU

重新创建和递送一个irq

set_type : set the flow type (IRQ_TYPE_LEVEL/etc.) of an

IRQ

设置irq的flow type: level, edge, simple, per-cpu

set_wake : enable/disable power-management wake-on of an

IRQ

是否支持由该irq来wake睡眠中的系统

release : release function solely used by UML

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值