x86架构下Linux中断处理之数据结构定义irq_desc等

引入几个Linux中断子系统下重要数据结构:
1、虚拟中断号virq和硬件中断号hwirq;
2、中断域:irq_domain;
3、中断描述符:irq_desc;
4、中断处理:struct irqaction;
理解这几个数据结构以及之间的关系对于理解Linux中断子系统的构成非常重要!

在描述hwirq转换为virq时, 引入一个概念:irq_domain域,在这个域里hwirq转换为virq。显然irq_domain和中断控制器是一一对应的,每一个中断控制器对应一个irq_domain。
【提出一个问题:怎样使用irq_domain把硬件中断号转化为软件中断号?】

以前中断号(virq)跟硬件密切相关: 对于只有少数中断控制器来说这种方法是比较好的,给出硬件中断号预先确定好它的虚拟中断号,但是当中断控制器数量变多时,实际的中断号就有可能成百上千或者更多,这样操作就会比较麻烦了。
解决办法:virq和hwirq之间的联系取消掉, hwirq仅仅是一个标号而已:也就是虚拟中断号和硬件的固定联系取消掉,当我们需要使用某一个硬件中断时,在irq_desc[]查找一个空缺的项,这个空缺的项的下标就是虚拟中断号。

<include/linux/irqdesc.h>

/**
 * 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
 * @dir:		/proc/irq/ procfs entry
 * @name:		flow handler name for /proc/interrupts output
 */
struct irq_desc {
	struct irq_common_data	irq_common_data;
	struct irq_data		    irq_data;   /* 重要,irq_chip管理结构体 */
	unsigned int __percpu	*kstat_irqs;
	irq_flow_handler_t	    handle_irq; /* 重要,中断流控层处理,调用ISR */
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
	irq_preflow_handler_t	preflow_handler;
#endif
	struct irqaction	*action;	          /* 重要,request_irq注册的中断ISR */
	unsigned int		status_use_accessors; /* 重要,知识irq_data的状态信息 */
	unsigned int		core_internal_state__do_not_mess_with_it; /* 重要,即istate,指示当前desc的状态信息 */
	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;          /* 重要,SMP下同步 */
	struct cpumask		*percpu_enabled;
#ifdef CONFIG_SMP
	const struct cpumask	   *affinity_hint; /* 中断亲和性设置 */
	struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
	cpumask_var_t		pending_mask;
#endif
#endif
	unsigned long		threads_oneshot;
	atomic_t		    threads_active;
	wait_queue_head_t   wait_for_threads;
#ifdef CONFIG_PM_SLEEP
	unsigned int		nr_actions;
	unsigned int		no_suspend_depth;
	unsigned int		cond_suspend_depth;
	unsigned int		force_resume_depth;
#endif
#ifdef CONFIG_PROC_FS
	struct proc_dir_entry  *dir;
#endif
	int			           parent_irq;
	struct module		   *owner;
	const char		       *name;
} ____cacheline_internodealigned_in_smp;

/*
 * struct irq_desc结构体大部分成员都是辅助性的,关键的成员是irq_data、handle_irqs、action、depth、lock、istat。
 * irq_desc[]数组的初始化,就是其主要成员的初始化的过程,在这里做简单的说明:
 * 
 * 1)action指针指向具体的设备驱动提供的中断处理操作,就是所谓的ISR。
 * action本身是一个单向链表结构体,由next指针指向下一个操作,因此action实际上是一个操作链,可以用于共享IRQ线的情况。
 * 2)handle_irq是irq_desc结构中与PIC相关的中断处理函数的接口,通常称作”hard irq handler“。
 * 此函数对应了PIC中的handle_xxx_irq()系列函数(xxx代表触发方式),do_IRQ()就会调用该函数,此函数最终会执行desc->action。
 * 3)irq_data用于描述PIC方法使用的数据,irq_data下面有两个比较重要的结构:chip和state_use_accessors。
 * 前者表示此irq_desc[]元素时用的PIC芯片类型,其中包含对该芯片的基本操作方法的指针;后者表示该chip的状态和属性,其中有些用于判断irq_desc本身应该所处的状态。
 * 4)lock用于SMP下不同core下的同步。
 * 5)depth表示中断嵌套深度,也即一个中断打断了几个其他中断。
 * 6)istate表示该desc目前的状态。
 * */
<include/linux/irq.h>

/**
 * 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_HIERARCHY
	struct irq_data		*parent_data;
#endif
	void			    *chip_data;
};

/**
 * struct irq_chip - hardware interrupt chip descriptor
 *
 * @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
 * @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
 * @flags:		chip specific flags
 */
struct irq_chip {
	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);

	unsigned long	flags;
};
<include/linux/interrupt.h>

/**
 * struct irqaction - per interrupt action descriptor
 * @handler:	interrupt handler function
 * @name:	name of the device
 * @dev_id:	cookie to identify the device
 * @percpu_dev_id:	cookie to identify the device
 * @next:	pointer to the next irqaction for shared interrupts
 * @irq:	interrupt number
 * @flags:	flags (see IRQF_* above)
 * @thread_fn:	interrupt handler function for threaded interrupts
 * @thread:	thread pointer for threaded interrupts
 * @secondary:	pointer to secondary irqaction (force threading)
 * @thread_flags:	flags related to @thread
 * @thread_mask:	bitmask for keeping track of @thread activity
 * @dir:	pointer to the proc/irq/NN/name entry
 */
struct irqaction {
	irq_handler_t		handler;      /* primary handler */
	void			    *dev_id;      /* 用于共享中断-设备号 */
	void __percpu		*percpu_dev_id;
	struct irqaction	*next;        /* 用于共享中断,指向下一个中断处理 */
	irq_handler_t		thread_fn;    /* threaded handler */
	struct task_struct	*thread;      /* 指向所创建的内核线程 */
	struct irqaction	*secondary;   /* 用于中断处理强制线程化,将primary handler设置为thread handler */
	unsigned int		irq;          /* 虚拟中断号,对应中断描述符desc */
	unsigned int		flags;        /* 用于描述handler的特性,其前缀为IRQF_,表示IRQ Flags */
	unsigned long		thread_flags; /* 用于描述thread_fn的特性,其前缀为IRQTF_,表示IRQ Thread Flags */
	unsigned long		thread_mask;  /* 用于共享中断 */
	const char		    *name;
	struct proc_dir_entry	*dir;
} ____cacheline_internodealigned_in_smp;
<include/linux/irqdomain.h>

/**
 * 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
 *
 * Optional elements
 * @of_node: Pointer to device tree nodes associated with the irq_domain. Used
 *           when decoding device tree interrupt specifiers.
 * @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
 *
 * 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;

	/* Optional data */
	struct fwnode_handle           *fwnode;
	enum irq_domain_bus_token      bus_token;
	struct irq_domain_chip_generic *gc;
#ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
	struct irq_domain              *parent;
#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;
	unsigned int    linear_revmap[]; //保存hwirq对应的virq,譬如:linear_revmap[hwirq] = virq
};

上述数据结构体之间的关系如下图所示:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值