ARM Linux对中断的处理--相关数据结构
时间:2010-06-11 来源:tq08g2z
中断处理依赖于中断的类型:I/O中断、时钟中断和处理器间中断。
不管引起中断的电路的种类如何,所有I/O中断处理程序都执行四个相同的基本操作:
1、在内核态堆栈中保存IRQ的值和寄存器的内容。
2、为正在给IRQ线服务的PIC发送一个应答,这将允许PIC进一步发出中断。
3、执行共享这个IRQ的所有设备的中断服务例程。
与中断处理相关的数据结构
Linux每个中断通过一个称为中断描述符的结构irq_desc来描述,各中断的信息都在这个结构中得以体现这个结构在include/irq.h文件中定义:
/**
* struct irq_desc - interrupt descriptor
* @irq: interrupt number for this descriptor
* @timer_rand_state: pointer to timer rand state struct
* @kstat_irqs: irq stats per cpu
* @irq_2_iommu: iommu with this irq
* @handle_irq: highlevel irq-events handler [if NULL, __do_IRQ()]
* @chip: low level interrupt hardware access
* @msi_desc: MSI descriptor
* @handler_data: per-IRQ data for the irq_chip methods
* @chip_data: platform-specific per-chip private data for
* the chip methods, to allow shared chip implementations
* @action: the irq action chain
* @status: status information
* @depth: disable-depth, for nested irq_disable() calls
* @wake_depth: enable depth, for multiple set_irq_wake() callers
* @irq_count: stats field to detect stalled irqs
* @last_unhandled: aging timer for unhandled count
* @irqs_unhandled: stats field for spurious unhandled interrupts
* @lock: locking for SMP
* @affinity: IRQ affinity on SMP
* @node: node index useful for balancing
* @pending_mask: pending rebalanced interrupts
* @threads_active: number of irqaction threads currently running
* @wait_for_threads: wait queue for sync_irq to wait for
* threaded handlers
* @dir: /proc/irq/ procfs entry
* @name: flow handler name for /proc/interrupts output
*/
struct irq_desc {
unsigned int irq;
struct timer_rand_state *timer_rand_state;
unsigned int *kstat_irqs;
#ifdef CONFIG_INTR_REMAP
struct irq_2_iommu *irq_2_iommu;
#endif
irq_flow_handler_t handle_irq;
struct irq_chip *chip;
struct msi_desc *msi_desc;
void *handler_data;
void *chip_data;
struct irqaction *action; /* IRQ action list */
unsigned int status; /* IRQ status */
unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned long last_unhandled; /* Aging timer for unhandled count */
unsigned int irqs_unhandled;
spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_var_t affinity;
unsigned int node;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
const char *name;
} ____cacheline_internodealigned_in_smp;
在具体的ARM芯片中会有很多的中断类型,每一种类型的中断用以上结构来表示。
irq_desc各字段的说明
chip |
指向PIC对象(irq_chip结构),它服务于IRQ线 |
action |
标识当出现IRQ时要调用的中断服务例程。该字段指向IRQ的irqaction链表的第一个元素。 |
status |
描述IRQ线状态的一组标志 |
depth |
如果IRQ线被激活,则显示0,如果IRQ线被禁止了不止一次,则显示一个正数。 |
irq_count |
中断计数器,统计IRQ线上发生的中断的次数(仅在诊断时使用) |
irqs_unhandled |
对在IRQ线上发生的无法处理的中断进行计数 |
lock |
用于串行访问IRQ描述符和PIC的自旋锁。 |
|
|
|
|
如果一个中断内核没有处理,那么这个中断就是意外中断,也就是说,与某个IRQ线相关的中断处理例程(ISR)不存在,或者与某个中断线相关的所有例程都识别不出是否是自己的硬件设备发出的中断。通常,内核检查从IRQ线接收的意外中断的数量,当这条IRQ线的有故障设备没完没了的发中断时,就禁用这条IRQ线,内核不会在每监测到一个意外中断时就立刻禁用IRQ线。由于几个设备可能共享IRQ线,更合适的办法是:内核把中断和意外中断的总次数分别放在irq_desc描述符的irq_count和irqs_unhandled字段中,当第100000次中断产生时,如果意外中断的次数超过99900次内核才禁用这条IRQ线。
描述IRQ线状态的标志
IRQ_INPROGRESS /* IRQ handler active - do not enter! */
IRQ_DISABLED /* IRQ disabled - do not enter! */
IRQ_PENDING /* IRQ pending - replay on enable */
IRQ_REPLAY /* IRQ has been replayed but not acked yet */
IRQ_AUTODETECT /* IRQ is being autodetected */
IRQ_WAITING /* IRQ not yet seen - for autodetection */
IRQ_LEVEL /* IRQ level triggered */
IRQ_MASKED /* IRQ masked - shouldn't be seen again */
IRQ_PER_CPU /* IRQ is per CPU */
IRQ_NOPROBE /* IRQ is not valid for probing */
IRQ_NOREQUEST /* IRQ cannot be requested */
IRQ_NOAUTOEN /* IRQ will not be enabled on request irq */
IRQ_WAKEUP /* IRQ triggers system wakeup */
IRQ_MOVE_PENDING /* need to re-target IRQ destination */
IRQ_NO_BALANCING /* IRQ is excluded from balancing */
IRQ_SPURIOUS_DISABLED /* IRQ was disabled by the spurious trap */
IRQ_MOVE_PCNTXT /* IRQ migration from process context */
IRQ_AFFINITY_SET /* IRQ affinity was set from userspace*/
IRQ_SUSPENDED /* IRQ has gone through suspend sequence */
IRQ_ONESHOT /* IRQ is not unmasked after hardirq */
IRQ_NESTED_THREAD /* IRQ is nested into another, no own handler thread */
irq_desc描述符的depth字段和IRQ_DISABLED标志表示IRQ线是否被禁用。每次调用disable_irq()和disable_irq_nosync()函数,depth字段的值增加,如果depth等于0,函数禁用IRQ线并设置它的IRQ_DISABLED标志。相反,每当调用enable_irq()函数,depth字段的值减少,如果depth变为0,函数激活IRQ线并清楚IRQ_DISABLED标志。
Linux支持许多种类的PIC电路,如PC机的8259A、APIC等。为了以统一的方法处理所有这样的设备,Linux用了一个“PIC对象”,由PIC名字和17个标准方法组成。这种面向对象的方法的优点是,驱动程序不必关注安装在系统中的PIC种类。每个驱动程序可见的中断源透明地连接到适当的控制器。定义PIC对象的数据结构叫irq_chip。内核使用irq_chip结构的成员来完成实际的对于PIC控制器的操作,禁用中断,启用中断等。
/**
* struct irq_chip - hardware interrupt chip descriptor
*
* @name: name for /proc/interrupts
* @startup: start up the interrupt (defaults to ->enable if NULL)
* @shutdown: shut 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
* @mask: mask an interrupt source
* @mask_ack: ack and mask an interrupt source
* @unmask: unmask an interrupt source
* @eoi: end of interrupt - chip level
* @end: end of interrupt - flow level
* @set_affinity: set the CPU affinity on SMP machines
* @retrigger: resend an IRQ to the CPU
* @set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
* @set_wake: enable/disable power-management wake-on of an IRQ
*
* @bus_lock: function to lock access to slow bus (i2c) chips
* @bus_sync_unlock: function to sync and unlock slow bus (i2c) chips
*
* @release: release function solely used by UML
* @typename: obsoleted by name, kept as migration helper
*/
struct irq_chip {
const char *name;
unsigned int (*startup)(unsigned int irq);
void (*shutdown)(unsigned int irq);
void (*enable)(unsigned int irq);
void (*disable)(unsigned int irq);
void (*ack)(unsigned int irq);
void (*mask)(unsigned int irq);
void (*mask_ack)(unsigned int irq);
void (*unmask)(unsigned int irq);
void (*eoi)(unsigned int irq);
void (*end)(unsigned int irq);
int (*set_affinity)(unsigned int irq,
const struct cpumask *dest);
int (*retrigger)(unsigned int irq);
int (*set_type)(unsigned int irq, unsigned int flow_type);
int (*set_wake)(unsigned int irq, unsigned int on);
void (*bus_lock)(unsigned int irq);
void (*bus_sync_unlock)(unsigned int irq);
/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
void (*release)(unsigned int irq, void *dev_id);
#endif
/*
* For compatibility, ->typename is copied into ->name.
* Will disappear.
*/
const char *typename;
};
多个设备能共享一个单独的IRQ。因此,内核要维护多个irqaction描述符,其中的每个描述符涉及一个特定的硬件设备和一个特定的中断。这个结构在文件include/interrupt.h中定义:
/**
* struct irqaction - per interrupt action descriptor
* @handler: interrupt handler function
* @flags: flags (see IRQF_* above)
* @name: name of the device
* @dev_id: cookie to identify the device
* @next: pointer to the next irqaction for shared interrupts
* @irq: interrupt number
* @dir: pointer to the proc/irq/NN/name entry
* @thread_fn: interupt handler function for threaded interrupts
* @thread: thread pointer for threaded interrupts
* @thread_flags: flags related to @thread
*/
struct irqaction {
irq_handler_t handler;
unsigned long flags;
const char *name;
void *dev_id;
struct irqaction *next;
int irq;
struct proc_dir_entry *dir;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
};
irqaction各字段说明
字段名 |
说明 |
handler |
指向一个I/O设备的终端服务例程。这是允许多个设备共享同一个IRQ的关键字段 |
flags |
描述IRQ与I/O设备之间的关系 |
name |
I/O设备名(通过读/proc/interrupts文件,在列出所服务的IRQ时也显示设备名) |
dev_id |
I/O设备的私有字段。典型情况下,它标识I/O设备本身(例如,它可能等于其主设备号和此设备号),或者它指向设备驱动程序的数据 |
next |
指向irqaction链表的下一个元素。链表中的元素指向共享同一个IRQ的硬件设备 |
irq |
中断号 |
dir |
指向proc文件系统中IRQn相关的目录项 |
|
|
|
|
threaded ISRs
irqaction描述符的标志
/*
* These flags used only by the kernel as part of the
* irq handling routines.
*
* IRQF_DISABLED - keep irqs disabled when calling the action handler
* IRQF_SAMPLE_RANDOM - irq is used to feed the random generator
* IRQF_SHARED - allow sharing the irq among several devices
* IRQF_PROBE_SHARED - set by callers when they expect sharing
* mismatches to occur
* IRQF_TIMER - Flag to mark this interrupt as timer interrupt
* IRQF_PERCPU - Interrupt is per cpu
* IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
* IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt
* that is registered first in an shared interrupt is considered for
* performance reasons)
* IRQF_ONESHOT - Interrupt is not reenabled after the hardirq
* handler finished.Used by threaded interrupts which need to keep the
* irq line disabled until the threaded handler has been run.
*/
#define IRQF_DISABLED 0x00000020
#define IRQF_SAMPLE_RANDOM 0x00000040
#define IRQF_SHARED 0x00000080
#define IRQF_PROBE_SHARED 0x00000100
#define IRQF_TIMER 0x00000200
#define IRQF_PERCPU 0x00000400
#define IRQF_NOBALANCING 0x00000800
#define IRQF_IRQPOLL 0x00001000
#define IRQF_ONESHOT 0x00002000
最后,irq_stat数组包含NR_CPUS个元素,系统中的每个CPU对应一个元素。每个元素的类型为irq_cpustat_t,该类型包含几个计数器和内核记录CPU正在做什么的标志。
typedef struct {
unsigned int __softirq_pending;
unsigned int local_timer_irqs;
} ____cacheline_aligned irq_cpustat_t;
irq_cpustat_t结构的字段
字段名 |
描述 |
__softirq_pending |
表示挂起的软中断 |
local_timer_irqs |
本地时钟中断发生的次数 |
在kernel/irq/handle.c中还有定义了一个全局变量,用来表示系统中的所有中断:
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = {
.status = IRQ_DISABLED,
.chip = &no_irq_chip,
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __SPIN_LOCK_UNLOCKED(irq_desc->lock),
}
};
上面的__cacheline_aligned_in_smp在include/linux/cache.h中定义,它是一个宏,在非SMP系统中是空的:
#ifdef CONFIG_SMP
#define __cacheline_aligned_in_smp __cacheline_aligned
#else
#define __cacheline_aligned_in_smp
#endif /* CONFIG_SMP */
内核用结构数组irq_desc[NR_IRQS]来管理中断处理,每一个成员对应于一个中断。