Linux中断流程分析

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">我们在认识系统处理中断流程时,先要知道在硬件上,外部设备和cpu和中断控制器是如何连接的。</span>




 当电平触发,或边沿触发一个外设产生一个硬件中断,这个电平信号会送到和这个外设相连的中断控制器,有可能中断控制器是级联的,
这个中断控制器会将中断信号送达上一级中断控制,最后由root中断控制器送到CPU,在这里又会遇到一个问题,一般系统不止一个CPU,
那么到底传给哪个CPU来处理呢,当然这里还有很多处理机制,还要结合gic的硬件结构,我也没有深入学习。

了解了中断硬件结构那么我们来看一下在高通代码中的软件流程
系统起来后,首先会初始化中断控制器,在高通8994代码中,有两个中断控制器
qcom,msm-qgic2 和qcom,msm-tlmm-gp,qcom,msm-tlmm-gp是第二级中断控制器,这个我们可以在device tree 中
grep -r "gpio-controller"
中断控制器在device tree 中都会有 gpio-controller 的标记。
而这些中断控制器会被编译进一个段中如:
IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", msm_gic_of_init);
IRQCHIP_DECLARE(tlmmv3_irq, "qcom,msm-tlmm-gp", irq_msm_gpio_init);

#define IRQCHIP_DECLARE(name,compstr,fn)				\
	static const struct of_device_id irqchip_of_match_##name	\
	__used __section(__irqchip_of_table)				\
	= { .compatible = compstr, .data = fn }

#ifdef CONFIG_IRQCHIP
#define IRQCHIP_OF_MATCH_TABLE()					\
	. = ALIGN(8);							\
	VMLINUX_SYMBOL(__irqchip_begin) = .;				\
	*(__irqchip_of_table)		  				\
	*(__irqchip_of_end)


void __init irqchip_init(void)
{
	of_irq_init(__irqchip_begin);
}
系统起来后会初始化各个模块会调用
asmlinkage void __init start_kernel(void)
{
	···
	init_IRQ();
	···
}

void __init init_IRQ(void)
{
	irqchip_init();
	if (!handle_arch_irq)
		panic("No interrupt controller found.");
}

void __init irqchip_init(void)
{
	of_irq_init(__irqchip_begin);
}

void __init of_irq_init(const struct of_device_id *matches) 
{
	····
	for_each_matching_node(np, matches) {      // 由于传入的np 为空,所以会遍历这个device tree
		if (!of_find_property(np, "interrupt-controller", NULL))  //找到包含"interrupt-controller"属性的节点
			continue;
		 
		desc = kzalloc(sizeof(*desc), GFP_KERNEL);
		if (WARN_ON(!desc))
			goto err;

		desc->dev = np;
		desc->interrupt_parent = of_irq_find_parent(np); //查找这个节点有没有父设备
		if (desc->interrupt_parent == np)
			desc->interrupt_parent = NULL;
		list_add_tail(&desc->list, &intc_desc_list);   //将找到的中断控制器信息添加到链表中
	}

	····
	while (!list_empty(&intc_desc_list)) {
		
		list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {  //遍历保存中断控制器信息的链表
			const struct of_device_id *match;
			int ret;
			of_irq_init_cb_t irq_init_cb;
	
			if (desc->interrupt_parent != parent)  //由于在此parent为空,意思就是找到最顶层的GIC
				continue;

			list_del(&desc->list);
			match = of_match_node(matches, desc->dev);
			if (WARN(!match->data,
			    "of_irq_init: no init function for %s\n",
			    match->compatible)) {
				kfree(desc);
				continue;
			}
			pr_debug("of_irq_init: init %s @ %p, parent %p\n",
				 match->compatible,
				 desc->dev, desc->interrupt_parent);
			irq_init_cb = (of_irq_init_cb_t)match->data;   //在此处,就把我们编译到段中的数据赋值给变量
			ret = irq_init_cb(desc->dev, desc->interrupt_parent); //这里就调到了GIC的初始化函数了
			if (ret) {
				kfree(desc);
				continue;
			}
			
			list_add_tail(&desc->list, &intc_parent_list);
		}

		desc = list_first_entry(&intc_parent_list, typeof(*desc), list);
		if (list_empty(&intc_parent_list) || !desc) {
			pr_err("of_irq_init: children remain, but no parents\n");
			break;
		}
		list_del(&desc->list);
		parent = desc->dev;     //将最顶层的GIC 赋值给parent,接下来的步骤会依次执行它的子GIC的初始化代码
		kfree(desc);
	}
}
接下来我们就来看一下中断控制器的初始化。
在看gic的代码之前要知道什么是 irq_domain irq_number与HW interrupt ID,我这里就不再讲解,我这里是主要讲软件的流程。
int __init gic_of_init(struct device_node *node, struct device_node *parent)
{
	···
	dist_base = of_iomap(node, 0);    //GIC Distributor的地址
	WARN(!dist_base, "unable to map gic dist registers\n");

	cpu_base = of_iomap(node, 1);     //GIC CPU interface的地址 
	WARN(!cpu_base, "unable to map gic cpu registers\n");

	if (of_property_read_u32(node, "cpu-offset", &percpu_offset))
		percpu_offset = 0;
	gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node); 
	···
	if (parent) {			//有可能这个GIC是级联的GIC不是最顶层的GIC,它还是要注册它自己的中断
		irq = irq_of_parse_and_map(node, 0);
		gic_cascade_irq(gic_cnt, irq);
	}
}


void __init gic_init_bases(unsigned int gic_nr, int irq_start,
			   void __iomem *dist_base, void __iomem *cpu_base,
			   u32 percpu_offset, struct device_node *node)
{
	···
	gic_irqs = readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_CTR) & 0x1f;  //读取寄存器中的值
	gic_irqs = (gic_irqs + 1) * 32;  //计算出此GIC能支持多少个中断
	if (gic_irqs > 1020)       
		gic_irqs = 1020;      //最多支持1020个中断
	gic_irqs -= hwirq_base; //减去16个,0-15的中断号是特殊的中断号
	irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id());  //开始分配中断号,从16号开始分配
	if (IS_ERR_VALUE(irq_base)) {
		WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
		     irq_start);
		irq_base = irq_start;
	}
	gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,          //将HW interrupt id和irq number 映射起来
				    hwirq_base, &gic_irq_domain_ops, gic);
	···
}

#define irq_alloc_descs(irq, from, cnt, node)	\
	__irq_alloc_descs(irq, from, cnt, node, THIS_MODULE)


int __ref __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
		  struct module *owner)
{
	···
	start = bitmap_find_next_zero_area(allocated_irqs, IRQ_BITMAP_BITS,  //从from开始,在位图中找到第一个不为0的区域
					   from, cnt, 0);   
	ret = -EEXIST;
	if (irq >=0 && start != irq)
		goto err;

	if (start + cnt > nr_irqs) {
		ret = irq_expand_nr_irqs(start + cnt);
		if (ret)
			goto err;
	}

	bitmap_set(allocated_irqs, start, cnt);  //将分配了的位,置位,也就是在allocated_irqs中,每一位对应了一个中断。
	mutex_unlock(&sparse_irq_lock);
	return alloc_descs(start, cnt, node, owner); //分配中断号,从start开始,一共分配cnt个
	···
}

static int alloc_descs(unsigned int start, unsigned int cnt, int node,
		       struct module *owner)
{
	struct irq_desc *desc;      //中断描述符,每一个中断号,对应了一个中断描述符
	int i;

	for (i = 0; i < cnt; i++) {
		desc = alloc_desc(start + i, node, owner);  //依次分配中断描述符
		if (!desc)
			goto err;
		mutex_lock(&sparse_irq_lock);
		irq_insert_desc(start + i, desc); //将中断号与中断描述符建立关系,这里使用的是基数树,方便在以后,
						  //只要知道了中断号,就可以得到对应的中断描述符
		mutex_unlock(&sparse_irq_lock);
	}
	return start;
	···
}

static struct irq_desc *alloc_desc(int irq, int node, struct module *owner)
{
	···
	desc_set_defaults(irq, desc, node, owner); //将中断描述符赋值,这个只是一些缺省值,后面还会赋值
	···
}

分配中断号就分析到这里,我们再回到gic_init_bases函数中。
现在已经为gic支持的所有中断,分配了中断号,并且每一个中断号都对应有一个中断描述符,接下来就要将硬件id和irq number
映射起来,当中断产生时,CPU会读GIC的寄存器,知道是哪个HW interrupt id,再通过这种映射好的关系,就能得到irq number
再通过irq number得到对象的中断描述符,然后执行handler 再执行注册的 action。


对应HW interrupt id 和 irq number的映射,可以有很多种,我们这里使用的是 IRQ_DOMAIN_MAP_LEGACY
#define IRQ_DOMAIN_MAP_LEGACY 0 /* driver allocated fixed range of irqs.
* ie. legacy 8259, gets irqs 1..15 */
#define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */
#define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */
#define IRQ_DOMAIN_MAP_TREE 3 /* radix tree */

struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
					 unsigned int size,
					 unsigned int first_irq,
					 irq_hw_number_t first_hwirq,
					 const struct irq_domain_ops *ops,
					 void *host_data)
{
	struct irq_domain *domain;
	unsigned int i;

	domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LEGACY, ops, host_data);  //分配irq_domain 
	if (!domain)
		return NULL;

	//其实这种映射类似于线性映射first_irq 对应 first_hwirq,first_irq+i对应first_hwirq+i
	//当知道一个hw interrupt id时 hw_interrupt_id - first_hwirq + first_irq 就得到irq_number了

	domain->revmap_data.legacy.first_irq = first_irq; 
	domain->revmap_data.legacy.first_hwirq = first_hwirq;
	domain->revmap_data.legacy.size = size;

	mutex_lock(&irq_domain_mutex);
	/* Verify that all the irqs are available */  
	for (i = 0; i < size; i++) {
		int irq = first_irq + i;
		struct irq_data *irq_data = irq_get_irq_data(irq);

		if (WARN_ON(!irq_data || irq_data->domain)) {
			mutex_unlock(&irq_domain_mutex);
			irq_domain_free(domain);
			return NULL;
		}
	}

	/* Claim all of the irqs before registering a legacy domain */
	for (i = 0; i < size; i++) {
		struct irq_data *irq_data = irq_get_irq_data(first_irq + i);
		irq_data->hwirq = first_hwirq + i;   //将相应的属性赋值
		irq_data->domain = domain;
	}
	mutex_unlock(&irq_domain_mutex);

	for (i = 0; i < size; i++) {
		int irq = first_irq + i;
		int hwirq = first_hwirq + i;

		/* IRQ0 gets ignored */
		if (!irq)
			continue;

		/* Legacy flags are left to default at this point,
		 * one can then use irq_create_mapping() to
		 * explicitly change them
		 */
		if (ops->map)
			ops->map(domain, irq, hwirq);   //这是一个回调函数,在gic_init_bases传进来的函数,主要是设置chip和handler

		/* Clear norequest flags */
		irq_clear_status_flags(irq, IRQ_NOREQUEST);
	}

	irq_domain_add(domain);     //将注册好的domain 添加到系统的irq_domain_list中
	return domain;
}
传入的映射函数:
static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
				irq_hw_number_t hw)
{
	if (hw < 32) {             //特殊的中断
		irq_set_percpu_devid(irq);
		irq_set_chip_and_handler(irq, &gic_chip,
					 handle_percpu_devid_irq);
		set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
	} else {
		irq_set_chip_and_handler(irq, &gic_chip,      //设置chip 和 handler 
							      //chip: gic_chip
							      //handler:handle_fasteoi_irq
					 handle_fasteoi_irq);
		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
	}
	irq_set_chip_data(irq, d->host_data);  //这里的host_data 是gic私有的,我们不能去修改它
	return 0;
}
gic_chip 主要是一些操作GIC硬件的接口
static struct irq_chip gic_chip = {
	.name			= "GIC",
	.irq_mask		= gic_mask_irq,
	.irq_unmask		= gic_unmask_irq,
	.irq_eoi		= gic_eoi_irq,
	.irq_set_type		= gic_set_type,
	.irq_retrigger		= gic_retrigger,
#ifdef CONFIG_SMP
	.irq_set_affinity	= gic_set_affinity,
#endif
	.irq_disable		= gic_disable_irq,
	.irq_set_wake		= gic_set_wake,
};

handler 也就是 highlevel irq-events handler 

我们这里的gic的highlevel handler用的是handle_fasteoi_irq
还有很多high level handler 比如:
handle_edge_irq
handle_level_irq
handle_nested_irq
···
至此gic的初始化基本完成,这里只介绍了一个大致的流程。
接下来就是申请中断。
内核的申请函数:

int request_threaded_irq(unsigned int irq, irq_handler_t handler,
			 irq_handler_t thread_fn, unsigned long irqflags,
			 const char *devname, void *dev_id)
{
	struct irqaction *action;
	struct irq_desc *desc;
	int retval;

	/*
	 * Sanity-check: shared interrupts must pass in a real dev-ID,
	 * otherwise we'll have trouble later trying to figure out
	 * which interrupt is which (messes up the interrupt freeing
	 * logic etc).
	 */
	if ((irqflags & IRQF_SHARED) && !dev_id)  //如果中断是共享的,但是没有dev_id那么直接返回错误
		return -EINVAL;

	desc = irq_to_desc(irq);   //根据传入的要申请的中断号,找到此中断对应的中断描述符
	if (!desc)
		return -EINVAL;

	if (!irq_settings_can_request(desc) ||          //有些中断号是不能被申请的,比如用于级联的中断号
	    WARN_ON(irq_settings_is_per_cpu_devid(desc))) //这个是针对percpu的另外一种情况,需要调用request_percpu_irq接口
		return -EINVAL;                           //有兴趣可以学习一下

	if (!handler) {
		if (!thread_fn)   //如果handler 和thread_fn都为空,是不行的。
			return -EINVAL;
		handler = irq_default_primary_handler;     //这里设置缺省值,其实是唤醒我们的thread_fn
	}

	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
	if (!action)
		return -ENOMEM;

	//以下为将action 赋值
	action->handler = handler;
	action->thread_fn = thread_fn;
	action->flags = irqflags;
	action->name = devname;
	action->dev_id = dev_id;

	chip_bus_lock(desc);
	retval = __setup_irq(irq, desc, action);   //进行实际的注册
	chip_bus_sync_unlock(desc);

	if (retval)
		kfree(action);

#ifdef CONFIG_DEBUG_SHIRQ_FIXME
	if (!retval && (irqflags & IRQF_SHARED)) {
		unsigned long flags;

		disable_irq(irq);
		local_irq_save(flags);

		handler(irq, dev_id);

		local_irq_restore(flags);
		enable_irq(irq);
	}
#endif
	return retval;
}
再来看一下实际的注册:

static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{
	struct irqaction *old, **old_ptr;
	unsigned long flags, thread_mask = 0;
	int ret, nested, shared = 0;
	cpumask_var_t mask;

	if (!desc)
		return -EINVAL;

	if (desc->irq_data.chip == &no_irq_chip)
		return -ENOSYS;
	if (!try_module_get(desc->owner))
		return -ENODEV;

	nested = irq_settings_is_nested_thread(desc);  //判断此irq是不是嵌套中断,如果是嵌套的,那么它的执行是依赖于父中断的thread_fn
	if (nested) {
		if (!new->thread_fn) {
			ret = -EINVAL;
			goto out_mput;
		}	
		new->handler = irq_nested_primary_handler;  //打印调试信息,正常流程时不会调用的。
	} else {
		if (irq_settings_can_thread(desc))  //强制线程化,在我们的代码中,这个宏是没有打开的。
			irq_setup_forced_threading(new);
	}

	if (new->thread_fn && !nested) { //如果thread_fn不为空,并且没有嵌套,那么内核将创建一个线程。
		struct task_struct *t;

		t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
				   new->name);
		if (IS_ERR(t)) {
			ret = PTR_ERR(t);
			goto out_mput;
		}
		get_task_struct(t);  //为这个threaded handler的task struct增加一次reference count,这样,即便是
				     //该thread异常退出也可以保证它的task struct不会被释放掉
		new->thread = t;
		set_bit(IRQTF_AFFINITY, &new->thread_flags);
	}

	if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {
		ret = -ENOMEM;
		goto out_thread;
	}

	if (desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)
		new->flags &= ~IRQF_ONESHOT;

	raw_spin_lock_irqsave(&desc->lock, flags);
	//一下是中断共享的代码
	old_ptr = &desc->action;
	old = *old_ptr;
	if (old) {
		if (!((old->flags & new->flags) & IRQF_SHARED) ||         //如果是中断共享,那么此中断要和之前的中断的特性一样
		    ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK) ||
		    ((old->flags ^ new->flags) & IRQF_ONESHOT))
			goto mismatch;

		/* All handlers must agree on per-cpuness */
		if ((old->flags & IRQF_PERCPU) !=
		    (new->flags & IRQF_PERCPU))
			goto mismatch;

		/* 将新的action加到队列的最后 */
		do {
			thread_mask |= old->thread_mask;
			old_ptr = &old->next;
			old = *old_ptr;
		} while (old);
		shared = 1;
	}

	if (new->flags & IRQF_ONESHOT) {
		if (thread_mask == ~0UL) {  //如果thread_mask 所有位都为1,表示这个中断号上已经挂了太多的共享中断了
			ret = -EBUSY;
			goto out_mask;
		}
		new->thread_mask = 1 << ffz(thread_mask); //找到为0的位,然后置1,其实就是thread_mask的每一位都代表着一个共享中断

	} else if (new->handler == irq_default_primary_handler &&  //这里有一种情况,就是当中断没有oneshot标记,并且是电平触发,
								   //如果底层的irq chip 也不是oneshot,那就有可能出现,一直触发中断的情况,
								   // 因为这里没有清除中断位的操作
								   // 
		   !(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {

		pr_err("Threaded irq requested with handler=NULL and !ONESHOT for irq %d\n",
		       irq);
		ret = -EINVAL;
		goto out_mask;
	}

	if (!shared) {
		init_waitqueue_head(&desc->wait_for_threads);

		if (new->flags & IRQF_TRIGGER_MASK) {
			ret = __irq_set_trigger(desc, irq,        //这里就是设置中断的触发方式。
					new->flags & IRQF_TRIGGER_MASK);

			if (ret)
				goto out_mask;
		}

		desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED | \
				  IRQS_ONESHOT | IRQS_WAITING);
		irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);

		if (new->flags & IRQF_PERCPU) {
			irqd_set(&desc->irq_data, IRQD_PER_CPU);
			irq_settings_set_per_cpu(desc);
		}

		if (new->flags & IRQF_ONESHOT)
			desc->istate |= IRQS_ONESHOT;

		if (irq_settings_can_autoenable(desc))
			irq_startup(desc, true);
		else
			/* Undo nested disables: */
			desc->depth = 1;

		/* Exclude IRQ from balancing if requested */
		if (new->flags & IRQF_NOBALANCING) {
			irq_settings_set_no_balancing(desc);
			irqd_set(&desc->irq_data, IRQD_NO_BALANCING);
		}

		/* Set default affinity mask once everything is setup */
		setup_affinity(irq, desc, mask);

	} else if (new->flags & IRQF_TRIGGER_MASK) {
		unsigned int nmsk = new->flags & IRQF_TRIGGER_MASK;
		unsigned int omsk = irq_settings_get_trigger_mask(desc);

		if (nmsk != omsk)
			/* hope the handler works with current  trigger mode */
			pr_warning("irq %d uses trigger mode %u; requested %u\n",
				   irq, nmsk, omsk);
	}

	new->irq = irq;
	*old_ptr = new;

	/* Reset broken irq detection when installing new handler */
	desc->irq_count = 0;
	desc->irqs_unhandled = 0;

	
	if (shared && (desc->istate & IRQS_SPURIOUS_DISABLED)) {
		desc->istate &= ~IRQS_SPURIOUS_DISABLED;
		__enable_irq(desc, irq, false);
	}

	raw_spin_unlock_irqrestore(&desc->lock, flags);

	if (new->thread)
		wake_up_process(new->thread);

	register_irq_proc(irq, desc);
	new->dir = NULL;
	register_handler_proc(irq, new);
	free_cpumask_var(mask);

	return 0;
	···
}
整个中断的申请到已完成,接下来就等外部来触发它了。
我这里用gic的一个中断来大致看一下处理流程:
当一个连到GIC上的中断产生时,系统会进入IRQ mode,会找异常向量表,最后会调到函数
handle_arch_irq

我们在gic的初始化代码中看以看到
set_handle_irq(gic_handle_irq);

所以当会调到 gic_handle_irq

static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
	u32 irqstat, irqnr;
	struct gic_chip_data *gic = &gic_data[0];
	void __iomem *cpu_base = gic_data_cpu_base(gic);

	do {
		irqstat = readl_relaxed_no_log(cpu_base + GIC_CPU_INTACK); //读取gic的寄存器,可以得到是哪个硬件中断产生的中断
		irqnr = irqstat & ~0x1c00;

		if (likely(irqnr > 15 && irqnr < 1021)) { 
			irqnr = irq_find_mapping(gic->domain, irqnr); //以hwid 从irq domain中找到相应的irq number
			handle_IRQ(irqnr, regs);  
			uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
			continue;
		}
		if (irqnr < 16) {   //特殊的中断 
				    //ID0~ID15用于SGI,ID16~ID31用于PPI。
				    //PPI类型的中断会送到指定的process上,和其他的process无关。
				    //SGI是通过写GICD_SGIR寄存器而触发的中断
			writel_relaxed_no_log(irqstat, cpu_base + GIC_CPU_EOI);
#ifdef CONFIG_SMP
			handle_IPI(irqnr, regs);
#endif
			uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
			continue;
		}
		break;
	} while (1);
}

void handle_IRQ(unsigned int irq, struct pt_regs *regs)
{
	···
	generic_handle_irq(irq);
	···
}
	
int generic_handle_irq(unsigned int irq)
{
	struct irq_desc *desc = irq_to_desc(irq); //通过irq number得到对应的中断描述符

	if (!desc)
		return -EINVAL;
	generic_handle_irq_desc(irq, desc); //将中断描述符和中断号传入,进一步处理
	return 0;
}

static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
	desc->handle_irq(irq, desc);  //调用我们这个中断描述符中的handler
				      //还记得我们在gic初始化的时候,有一个map函数吗?里面的操作就是将handler_irq 赋值
				      //irq_set_chip_and_handler(irq, &gic_chip, handle_fasteoi_irq);
}

void handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc)
{
	raw_spin_lock(&desc->lock); //获得锁

	if (unlikely(irqd_irq_inprogress(&desc->irq_data))) //如果此中断正在被其它的CPU处理,并且没有被轮询,那么直接退出。
		if (!irq_check_poll(desc))
			goto out;

	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
	kstat_incr_irqs_this_cpu(irq, desc);

	if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
		if (!irq_settings_is_level(desc))
			desc->istate |= IRQS_PENDING;
		mask_irq(desc);
		goto out;
	}

	if (desc->istate & IRQS_ONESHOT)  //如果是oneshot 那么立即mask住。
		mask_irq(desc);

	preflow_handler(desc);
	handle_irq_event(desc);

	if (desc->istate & IRQS_ONESHOT)
		cond_unmask_irq(desc);
	···
}

irqreturn_t handle_irq_event(struct irq_desc *desc)
{
	struct irqaction *action = desc->action;
	irqreturn_t ret;

	desc->istate &= ~IRQS_PENDING;  //清除PENGDING状态
	irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS); //设置正杂处理的状态
	raw_spin_unlock(&desc->lock);   //在执行具体action时,是将锁释放了的。

	ret = handle_irq_event_percpu(desc, action);   //遍历action列表,依次执行

	raw_spin_lock(&desc->lock);
	irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
	return ret;
}

到此,整个中断的流程就结束了,其中还有很多细节没有介绍,我不理解的地方也有很多,这篇文章只是我的一个学习笔记而已。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值