本篇文章是通过学习韦东山老师的中断子系统相关章节而成。
在后续课程中我们把GIC之下的中断控制器分为两类:链式(chained)、层级(hierarchy)。
这个分类并没有官方定义,是我们根据代码概括出来的(Linux内核本来就缺乏文档)。
上图中,左边的"chained intc"就是链式中断控制器。
它底下的4个中断触发时,都会导致GIC的33号中断被触发。
处理中断时,需要分辨:是谁触发了GIC 33号中断?这需要读取"chained intc"中的寄存器。
上图中,右边边的"hierarchy intc"就是层级中断控制器。
它底下的4个中断,跟GIC中的4个中断一一对应。
处理GIC 100~103号中断时,不需要读取"hierarchy intc"的寄存器来分辨是谁触发了中断。
下图中:
request_irq(102, ...)
下图中:
request_irq(236, ...)
/*** struct irq_domain - Hardware interrupt number translation object* @link: Element in global irq_domain list.* @name: Name of interrupt domain* @ops: pointer to irq_domain methods* @host_data: private data pointer for use by owner. Not touched by irq_domain* core code.* @flags: host per irq_domain flags* @mapcount: The number of mapped interrupts** Optional elements* @fwnode: Pointer to firmware node associated with the irq_domain. Pretty easy* to swap it for the of_node via the irq_domain_get_of_node accessor* @gc: Pointer to a list of generic chips. There is a helper function for* setting up one or more generic chips for interrupt controllers* drivers using the generic chip library which uses this pointer.* @parent: Pointer to parent irq_domain to support hierarchy irq_domains* @debugfs_file: dentry for the domain debugfs file** Revmap data, used internally by irq_domain* @revmap_direct_max_irq: The largest hwirq that can be set for controllers that* support direct mapping* @revmap_size: Size of the linear map table @linear_revmap[]* @revmap_tree: Radix map tree for hwirqs that don't fit in the linear map* @linear_revmap: Linear table of hwirq->virq reverse mappings*/
struct irq_domain {struct list_head link;const char *name;const struct irq_domain_ops *ops;void *host_data;unsigned int flags;unsigned int mapcount;/* Optional data */struct fwnode_handle *fwnode;enum irq_domain_bus_token bus_token;struct irq_domain_chip_generic *gc;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHYstruct irq_domain *parent;
#endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFSstruct dentry *debugfs_file;
#endif/* reverse map data. The linear map gets appended to the irq_domain */irq_hw_number_t hwirq_max;unsigned int revmap_direct_max_irq;unsigned int revmap_size;struct radix_tree_root revmap_tree;struct mutex revmap_tree_mutex;unsigned int linear_revmap[];
};
/*** struct irq_desc - interrupt descriptor* @irq_common_data: per irq and chip data passed down to chip functions* @kstat_irqs: irq stats per cpu* @handle_irq: highlevel irq-events handler* @preflow_handler: handler called before the flow handler (currently used by sparc)* @action: the irq action chain* @status: status information* @core_internal_state__do_not_mess_with_it: core internal status information* @depth: disable-depth, for nested irq_disable() calls* @wake_depth: enable depth, for multiple irq_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* @threads_handled: stats field for deferred spurious detection of threaded handlers* @threads_handled_last: comparator field for deferred spurious detection of theraded handlers* @lock: locking for SMP* @affinity_hint: hint to user space for preferred irq affinity* @affinity_notify: context for notification of affinity changes* @pending_mask: pending rebalanced interrupts* @threads_oneshot: bitfield to handle shared oneshot threads* @threads_active: number of irqaction threads currently running* @wait_for_threads: wait queue for sync_irq to wait for threaded handlers* @nr_actions: number of installed actions on this descriptor* @no_suspend_depth: number of irqactions on a irq descriptor with* IRQF_NO_SUSPEND set* @force_resume_depth: number of irqactions on a irq descriptor with* IRQF_FORCE_RESUME set* @rcu: rcu head for delayed free* @kobj: kobject used to represent this struct in sysfs* @request_mutex: mutex to protect request/free before locking desc->lock* @dir: /proc/irq/ procfs entry* @debugfs_file: dentry for the debugfs file* @name: flow handler name for /proc/interrupts output*/
struct irq_desc {struct irq_common_data irq_common_data;struct irq_data irq_data;unsigned int __percpu *kstat_irqs;irq_flow_handler_t handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOIirq_preflow_handler_t preflow_handler;
#endifstruct irqaction *action; /* IRQ action list */unsigned int status_use_accessors;unsigned int core_internal_state__do_not_mess_with_it;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;atomic_t threads_handled;int threads_handled_last;raw_spinlock_t lock;struct cpumask *percpu_enabled;const struct cpumask *percpu_affinity;
#ifdef CONFIG_SMPconst struct cpumask *affinity_hint;struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQcpumask_var_t pending_mask;
#endif
#endifunsigned long threads_oneshot;atomic_t threads_active;wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PM_SLEEPunsigned int nr_actions;unsigned int no_suspend_depth;unsigned int cond_suspend_depth;unsigned int force_resume_depth;
#endif
#ifdef CONFIG_PROC_FSstruct proc_dir_entry *dir;
#endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFSstruct dentry *debugfs_file;const char *dev_name;
#endif
#ifdef CONFIG_SPARSE_IRQstruct rcu_head rcu;struct kobject kobj;
#endifstruct mutex request_mutex;int parent_irq;struct module *owner;const char *name;
} ____cacheline_internodealigned_in_smp;
/*** struct irq_data - per irq chip data passed down to chip functions* @mask: precomputed bitmask for accessing the chip registers* @irq: interrupt number* @hwirq: hardware interrupt number, local to the interrupt domain* @common: point to data shared by all irqchips* @chip: low level interrupt hardware access* @domain: Interrupt translation domain; responsible for mapping* between hwirq number and linux irq number.* @parent_data: pointer to parent struct irq_data to support hierarchy* irq_domain* @chip_data: platform-specific per-chip private data for the chip* methods, to allow shared chip implementations*/
struct irq_data {u32 mask;unsigned int irq;unsigned long hwirq;struct irq_common_data *common;struct irq_chip *chip;struct irq_domain *domain;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHYstruct irq_data *parent_data;
#endifvoid *chip_data;
};
/*** struct irq_chip - hardware interrupt chip descriptor** @parent_device: pointer to parent device for irqchip* @name: name for /proc/interrupts* @irq_startup: start up the interrupt (defaults to ->enable if NULL)* @irq_shutdown: shut down the interrupt (defaults to ->disable if NULL)* @irq_enable: enable the interrupt (defaults to chip->unmask if NULL)* @irq_disable: disable the interrupt* @irq_ack: start of a new interrupt* @irq_mask: mask an interrupt source* @irq_mask_ack: ack and mask an interrupt source* @irq_unmask: unmask an interrupt source* @irq_eoi: end of interrupt* @irq_set_affinity: Set the CPU affinity on SMP machines. If the force* argument is true, it tells the driver to* unconditionally apply the affinity setting. Sanity* checks against the supplied affinity mask are not* required. This is used for CPU hotplug where the* target CPU is not yet set in the cpu_online_mask.* @irq_retrigger: resend an IRQ to the CPU* @irq_set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ* @irq_set_wake: enable/disable power-management wake-on of an IRQ* @irq_bus_lock: function to lock access to slow bus (i2c) chips* @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips* @irq_cpu_online: configure an interrupt source for a secondary CPU* @irq_cpu_offline: un-configure an interrupt source for a secondary CPU* @irq_suspend: function called from core code on suspend once per* chip, when one or more interrupts are installed* @irq_resume: function called from core code on resume once per chip,* when one ore more interrupts are installed* @irq_pm_shutdown: function called from core code on shutdown once per chip* @irq_calc_mask: Optional function to set irq_data.mask for special cases* @irq_print_chip: optional to print special chip info in show_interrupts* @irq_request_resources: optional to request resources before calling* any other callback related to this irq* @irq_release_resources: optional to release resources acquired with* irq_request_resources* @irq_compose_msi_msg: optional to compose message content for MSI* @irq_write_msi_msg: optional to write message content for MSI* @irq_get_irqchip_state: return the internal state of an interrupt* @irq_set_irqchip_state: set the internal state of a interrupt* @irq_set_vcpu_affinity: optional to target a vCPU in a virtual machine* @ipi_send_single: send a single IPI to destination cpus* @ipi_send_mask: send an IPI to destination cpus in cpumask* @flags: chip specific flags*/
struct irq_chip {struct device *parent_device;const char *name;unsigned int (*irq_startup)(struct irq_data *data);void (*irq_shutdown)(struct irq_data *data);void (*irq_enable)(struct irq_data *data);void (*irq_disable)(struct irq_data *data);void (*irq_ack)(struct irq_data *data);void (*irq_mask)(struct irq_data *data);void (*irq_mask_ack)(struct irq_data *data);void (*irq_unmask)(struct irq_data *data);void (*irq_eoi)(struct irq_data *data);int (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);int (*irq_retrigger)(struct irq_data *data);int (*irq_set_type)(struct irq_data *data, unsigned int flow_type);int (*irq_set_wake)(struct irq_data *data, unsigned int on);void (*irq_bus_lock)(struct irq_data *data);void (*irq_bus_sync_unlock)(struct irq_data *data);void (*irq_cpu_online)(struct irq_data *data);void (*irq_cpu_offline)(struct irq_data *data);void (*irq_suspend)(struct irq_data *data);void (*irq_resume)(struct irq_data *data);void (*irq_pm_shutdown)(struct irq_data *data);void (*irq_calc_mask)(struct irq_data *data);void (*irq_print_chip)(struct irq_data *data, struct seq_file *p);int (*irq_request_resources)(struct irq_data *data);void (*irq_release_resources)(struct irq_data *data);void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);int (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);int (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);int (*irq_set_vcpu_affinity)(struct irq_data *data, void *vcpu_info);void (*ipi_send_single)(struct irq_data *data, unsigned int cpu);void (*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest);unsigned long flags;
};