【摘要】本文树妖详细讲解了Linux中与中断相关的内核数据结构及其内部联系。
八、中断相关的数据结构
8.1 irq_desc
- 用于表示IRQ描述符的结构定义如下:\linux-2.6.32.63\include\linux\irq.h
struct irq_desc
{
unsigned int irq; //中断号
unsigned int *kstat_irqs; //2. irq stats per cpu
#ifdef CONFIG_INTR_REMAP
struct irq_2_iommu *irq_2_iommu; //3. iommu with this irq
#endif
//4. highlevel irq-events handler [if NULL, __do_IRQ()]
irq_flow_handler_t handle_irq;
//5. low level interrupt hardware access
struct irq_chip *chip;
//6. MSI descriptor
struct msi_desc *msi_desc;
//7. per-IRQ data for the irq_chip methods
void *handler_data;
//8. platform-specific per-chip private data for the chip methods, to allow shared chip implementations
void *chip_data;
/* IRQ action list */
//9. the irq action chain
struct irqaction *action;
/* IRQ status */
//10. status information
unsigned int status;
/* nested irq disables */
//11. disable-depth, for nested irq_disable() calls
unsigned int depth;
/* nested wake enables */
//12. enable depth, for multiple set_irq_wake() callers
unsigned int wake_depth;
/* For detecting broken IRQs */
//13. stats field to detect stalled irqs
unsigned int irq_count;
/* Aging timer for unhandled count */
//14. aging timer for unhandled count
unsigned long last_unhandled;
//15. stats field for spurious unhandled interrupts
unsigned int irqs_unhandled;
//16. locking for SMP
spinlock_t lock;
#ifdef CONFIG_SMP
//17. IRQ affinity on SMP
cpumask_var_t affinity;
//18. node index useful for balancing
unsigned int node;
#ifdef CONFIG_GENERIC_PENDING_IRQ
//19. pending rebalanced interrupts
cpumask_var_t pending_mask;
#endif
#endif
//20. number of irqaction threads currently running
atomic_t threads_active;
//21. wait queue for sync_irq to wait for threaded handlers
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PROC_FS
//22. /proc/irq/ procfs entry
struct proc_dir_entry *dir;
#endif
//23. flow handler name for /proc/interrupts output
const char *name;
} ____cacheline_internodealigned_in_smp;
- status描述了IRQ的当前状态,irq.h中定义了各种表示当前状态的常数,可用于描述IRQ电路当前的状态。每个常数表示位串中的一个置位的标志位(可以同时设置)
/*
* IRQ line status.
*
* Bits 0-7 are reserved for the IRQF_* bits in linux/interrupt.h
*
* IRQ types
*/
#define IRQ_TYPE_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 high 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 */
/*
IRQ handler active - do not enter!
与IRQ_DISABLED类似,IRQ_INPROGRESS会阻止其余的内核代码执行该处理程序
*/
#define IRQ_INPROGRESS 0x00000100
/*
IRQ disabled - do not enter!
用户表示被设备驱动程序禁用的IRQ电路,标志通知内核不要进入处理程序
*/
#define IRQ_DISABLED 0x00000200
/*
IRQ pending - replay on enable
当CPU产生一个中断但尚未执行对应的处理程序时,IRQ_PENDING标志位置位
*/
#define IRQ_PENDING 0x00000400
/*
IRQ has been replayed but not acked yet
IRQ_REPLAY意味着该IRQ已经禁用,但此前尚有一个未确认的中断
*/
#define IRQ_REPLAY 0x00000800
#define IRQ_AUTODETECT 0x00001000 /* IRQ is being autodetected */
#define IRQ_WAITING 0x00002000 /* IRQ not yet seen - for autodetection */
/*
IRQ level triggered
用于Alpha和PowerPC系统,用于区分电平触发和边沿触发的IRQ
*/
#define IRQ_LEVEL 0x00004000
/*
IRQ masked - shouldn't be seen again
为正确处理发生在中断处理期间的中断,需要IRQ_MASKED标志位
*/
#define IRQ_MASKED 0x00008000
/*
IRQ is per CPU
某个IRQ只能发生在一个CPU上时,将设置IRQ_PER_CPU标志位,在SMP系统中,该标志使几个用于防止并发访问的保护机制变得多余
*/
#define IRQ_PER_CPU 0x00010000
#define IRQ_NOPROBE 0x00020000 /* IRQ is not valid for probing */
#define IRQ_NOREQUEST 0x00040000 /* IRQ cannot be requested */
#define IRQ_NOAUTOEN 0x00080000 /* IRQ will not be enabled on request irq */
#define IRQ_WAKEUP 0x00100000 /* IRQ triggers system wakeup */
#define IRQ_MOVE_PENDING 0x00200000 /* need to re-target IRQ destination */
#define IRQ_NO_BALANCING 0x00400000 /* IRQ is excluded from balancing */
#define IRQ_SPURIOUS_DISABLED 0x00800000 /* IRQ was disabled by the spurious trap */
#define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */
#define IRQ_AFFINITY_SET 0x02000000 /* IRQ affinity was set from userspace*/
#define IRQ_SUSPENDED 0x04000000 /* IRQ has gone through suspend sequence */
#define IRQ_ONESHOT 0x08000000 /* IRQ is not unmasked after hardirq */
#define IRQ_NESTED_THREAD 0x10000000 /* IRQ is nested into another, no own handler thread */
#ifdef CONFIG_IRQ_PER_CPU
#define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU)
#define IRQ_NO_BALANCING_MASK (IRQ_PER_CPU | IRQ_NO_BALANCING)
#else
#define CHECK_IRQ_PER_CPU(var) 0
#define IRQ_NO_BALANCING_MASK IRQ_NO_BALANCING
#endif
8.2 irq_chip
- \linux-2.6.32.63\include\linux\irq.h
struct irq_chip
{
/*
1. name for /proc/interrupts
包含一个短的字符串,用于标识硬件控制器
1) IA-32: XTPIC
2) AMD64: IO-APIC
*/
const char *name;
//2. start up the interrupt (defaults to ->enable if NULL),用于第一次初始化一个IRQ,startup实际上就是将工作转给enable
unsigned int (*startup)(unsigned int irq);
//3. shut down the interrupt (defaults to ->disable if NULL)
void (*shutdown)(unsigned int irq);
//4. enable the interrupt (defaults to chip->unmask if NULL)
void (*enable)(unsigned int irq);
//5. disable the interrupt (defaults to chip->mask if NULL)
void (*disable)(unsigned int irq);
//6. start of a new interrupt
void (*ack)(unsigned int irq);
//7. mask an interrupt source
void (*mask)(unsigned int irq);
//8. ack and mask an interrupt source
void (*mask_ack)(unsigned int irq);
//9. unmask an interrupt source
void (*unmask)(unsigned int irq);
//10. end of interrupt - chip level
void (*eoi)(unsigned int irq);
//11. end of interrupt - flow level
void (*end)(unsigned int irq);
//12. set the CPU affinity on SMP machines
int (*set_affinity)(unsigned int irq, const struct cpumask *dest);
//13. resend an IRQ to the CPU
int (*retrigger)(unsigned int irq);
//14. set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
int (*set_type)(unsigned int irq, unsigned int flow_type);
//15. enable/disable power-management wake-on of an IRQ
int (*set_wake)(unsigned int irq, unsigned int on);
//16. function to lock access to slow bus (i2c) chips
void (*bus_lock)(unsigned int irq);
//17. function to sync and unlock slow bus (i2c) chips
void (*bus_sync_unlock)(unsigned int irq);
/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
//18. release function solely used by UML
void (*release)(unsigned int irq, void *dev_id);
#endif
/*
* For compatibility, ->typename is copied into ->name.
* Will disappear.
*/
//19. obsoleted by name, kept as migration helper
const char *typename;
};
-
该结构需要考虑内核中出现的各个IRQ实现的所有特性。因此,一个该结构的特定实例,通常只定义所有可能方法的一个子集,下面以IO-APIC、i8259A标准中断控制器作为例子:
-
\linux-2.6.32.63\arch\x86\kernel\io_apic.c
static struct irq_chip ioapic_chip __read_mostly = { .name = "IO-APIC", .startup = startup_ioapic_irq, .mask = mask_IO_APIC_irq, .unmask = unmask_IO_APIC_irq, .ack = ack_apic_edge, .eoi = ack_apic_level, #ifdef CONFIG_SMP .set_affinity = set_ioapic_affinity_irq, #endif .retrigger = ioapic_retrigger_irq, };
-
\linux-2.6.32.63\arch\alpha\kernel\irq_i8259.c
struct irq_chip i8259a_irq_type = { .name = "XT-PIC", .startup = i8259a_startup_irq, .shutdown = i8259a_disable_irq, .enable = i8259a_enable_irq, .disable = i8259a_disable_irq, .ack = i8259a_mask_and_ack_irq, .end = i8259a_end_irq, };
-
-
可以看到,运行该设备,只需要定义所有可能处理程序函数的一个子集
8.3 irqaction
- struct irqaction结构是struct irq_desc中和IRQ处理函数相关的成员结构
struct irqaction
{
//1. name、dev_id唯一地标识一个中断处理程序
irq_handler_t handler;
void *dev_id;
void __percpu *percpu_dev_id;
//2. next用于实现共享的IRQ处理程序
struct irqaction *next;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned int irq;
//3. flags是一个标志变量,通过位图描述了IRQ(和相关的中断)的一些特性,位图中的各个标志位可以通过预定义的常数访问
unsigned int flags;
unsigned long thread_flags;
unsigned long thread_mask;
//4. name是一个短字符串,用于标识设备
const char *name;
struct proc_dir_entry *dir;
} ____cacheline_internodealigned_in_smp;
- 几个irqaction实例聚集到一个链表中,链表的所有元素都必须处理同一个IRQ编号,在发生一个共享中断时,内核扫描该链表找出中断实际上的来源设备