1、 #endif #if defined(CONFIG_GENERIC_PENDING_IRQ) | defined(CONFIG_IRQBALANCE) cpumask_t pending_mask; #ifdef CONFIG_PROC_FS struct proc_dir_entry *dir; const char *name; _cacheline_internodealigned_in_smp; Linux有一个全局变量, 包含了了所有的IRQ:(kernel/irq/handle.c) struct irq_desc irq_descNR_IRQS _cacheline_align
2、ed = 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, 所以有一个struct hw_interrupt_type,对于i8259A来说,这个结构是i8259
3、A_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
4、体系的: vic_chip 其它目的的: no_irq_chip, dummy_irq_chip4) irq_statNR_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; uns
5、igned long idle_timestamp; unsigned int _nmi_count; /* arch dependent */ unsigned int apic_timer_irqs; _cacheline_aligned irq_cpustat_t;5). 中断共享 我们知道,多个中断源可以共享一个irq线。 Linux的实现方式是,每个中断源都有自己的 一个struct irqaction, irqaction结构的定义: struct irqaction irq_handler_t handler; unsigned long flags; cpumask_t mas
6、k; void *dev_id; struct irqaction *next; int irq; ; 同一个irq可能有多个irqaction,组成一个链表。 struct irq_desc中有个域: 这个链表就包含了所有共享该irq号的中断源(及其对应的handler等信息)。 当device driver 进行request_irq()时,会为它生成一个irqaction,设置相应的值,然后挂载 irq_desc.action队列中(是添加在链表的最后面)。 request_irq(irq, handler, irqflags, devname, dev_id) setup_irq(ir
7、q, 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(都是低电平有
8、效,或者都是高电平有效) 下面是set_irq()函数中判断old和new两个中断源是否可以share同一个irq号的代码: /* * Cant share interrupts unless both agree to and are * the same type (level, edge, polarity). So both flag * fields must have IRQF_SHARED set and the bits which * set the trigger type must match. */ if (!(old-flags & new-flags) & IRQF
9、_SHARED) | (old-flags new- 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()
10、函数已基本不用了。 64位的X86系统上还可能使用 它(通过do_IRQ generic_handle_irq),32位的x86已经完全不用它了。 然而我们还是看一下_do_IRQ函数,因为道理是一样的: _do_IRQ():/*/ /首先给irq_descirq.lock加锁,以免别的CPU访问该desc结构 spin_lock(&desc-lock); /发送ACK给中断控制器 if (desc-chip-ack) desc-ack(irq); * REPLAY is when Linux resends an IRQ that was dropped earlier * WAITING
11、is used by probe to mark irqs that are being tested /*清除IRQ_REPLAY和IRQ_WAITING标志*/ status = desc-status & (IRQ_REPLAY | IRQ_WAITING); /*设置IRQ_PENDING标志。 这个flag的意思是,已经ACK但尚未处理*/ status |= IRQ_PENDING; /* we _want_ to handle it */ * If the IRQ is disabled for whatever reason, we cannot * use the actio
12、n we have. /*如果IRQ被disable了,但是我们收到了中断,说明这是个spurious interrupt, * 有些有BUG的主板等硬件会干这种事 action = NULL; /* 只要IRQ_DISABLED或者IRQ_INPROGRESS被设置,我们就不handle该irq。 * * 对于IRQ_INPROGRESS被设置的情况,说明此irq号的另一个实例正运行在 * 另一个CPU上,我们就不处理了,而是让 _那个_ CPU在运行完它的ISR时再检查 * 一下IRQ_PENDING标志,那时候它会再去处理我们这里逃避的事情的 if (likely(!(status &
13、(IRQ_DISABLED | IRQ_INPROGRESS) /*正常情况下2这都不被设置, *那我们就设置desc-status action = desc-action; status &= IRQ_PENDING; /* we commit to handling,清除pending标志 */ status |= IRQ_INPROGRESS; /* we are handling it ,设置inprogress标志*/status = status; * If there is no IRQ handler or it was disabled, exit early. * Sin
14、ce we set PENDING, if another processor is handling * a different instance of this same irq, the other processor * will take care of it. if (unlikely(!action) goto out; * Edge triggered interrupts need to remember * pending events. * This applies to any hw interrupts that allow a second * instance o
15、f the same irq to arrive while we are in do_IRQ * or in the handler. But the code herefor (;) irqreturn_t action_ret; /真正的IRQ处理是handle_IRQ_event,我们先unlock spin_unlock(& action_ret = handle_IRQ_event(irq, action); /再lock,因为后面还要unlock * 在我们调用handle_IRQ_event时,如果同一个irq又在另一个CPU上 * 来了一次,那个CPU会检测到IRQ_INPR
16、OGRESS标志,只设置了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) - Part II: ha
17、ndle_IRQ_event handle_IRQ_event()依次调用irq_descirq-action链表上的每一个action。 它会先打开中断(如果request_irq时没有设置IRQF_DISABLED标志),然后一个个执行irqaction, 再禁用本地中断。 handle_IRQ_event: irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action) irqreturn_t ret, retval = IRQ_NONE; unsigned int status = 0; handle_d
18、ynamic_tick(action); /如果指定了IRQF_DISABLED,就在关中断的情形下执行ISR /否则的话,在开中断的情形下执行ISR(action- IRQF_DISABLED) local_irq_enable_in_hardirq(); /该循环遍历irq_descirq-action链表,一个个调用其handler域 do ret = action-handler(irq, action-dev_id); if (ret = IRQ_HANDLED) status |= action-flags; retval |= ret; action = action-next;
19、 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_descirq.status的IRQ_PENDING标志,以便CPU1去检查从而再执行一遍。 当如果CPU3又收到一次,也设置IRQ_PENDING标志,这时CPU2设置的信息会丢失。 补救办法
20、:无 b). CPU1在处理器某IRQ之前,先发送ACK给PIC,结果这时候CPU2通过PIC禁用了该irq, 从而导致irq_descirq.status的IRQ_DISABLED标志被设置。 然后CPU1在正要处理 irq时发现对应的IRQ_DISABLED标志置位,于是退出。 这样就丢了一次中断。 在下一次enable_irq()被调用时,检查是否存在的这样的丢失。若然, 调用check_irq_resend()重新generate一次中断。 注意,在_do_IRQ()的一开始会清楚irq_descirq.status的IRQ_REPLAY 标志,这时为了防止对一次irq丢失补救多次。8
21、). 中断处理(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
22、程序员提供的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) 它为irq flow提供了一组预定义了的方法: handle_level_irq() = 针对level type的irq handler handle_edge_irq() = 针对e
23、dge 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, 目前IO-APIC主要用它和edge 什么叫透明的中断控制器?老子咋看不懂涅?Irq的flow type, generic irq有以下数种: #define IRQ_TYPE_
24、NONE 0x00000000 /* Default, unspecified type */ #define IRQ_TYPE_EDGE_RISING 0x00000001 /* Edge rising type */ #define IRQ_TYPE_EDGE_FALLING 0x00000002 /* Edge falling type */ #define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING) #define IRQ_TYPE_LEVEL_HIGH 0x00000004 /* Level hi
25、gh type */ #define IRQ_TYPE_LEVEL_LOW 0x00000008 /* Level low type */ #define IRQ_TYPE_SENSE_MASK 0x0000000f /* Mask of the above */ #define IRQ_TYPE_PROBE 0x00000010 /* Probing in progress */-没有看到simple类型和per-cpu类型,我估计这2者都是其他architectures上的。 这里把EDGE触发的irq又分成了上升沿、下降沿和both, level触发的又分成了低电平有效和high act
26、ive。这5个函数取代了原来的_do_IRQ,由do_IRQ直接调用:handle_irq(irq, desc);而这个irq_descirq.handle_irq又是在哪里设置的呢? 不同的irq chip有不同的设置,现在让我们看一下ioapic_chip上的irqs的设置: static void ioapic_register_intr(int irq, unsigned long trigger) /* 如果不是edge触发的,就设置为handle_fasteoi_irq */ if (trigger) irq_descirq.status |= IRQ_LEVEL; set_irq_chip_and_handler_name(irq, &ioapic_chip, handle_fasteoi_irq, fasteoi); else /* 如果是edge触发的,就设置为handle_edge_irq */ irq_descirq.status &= IRQ_LEVEL; handle_edge_irq, edge
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1