Linux內核中斷處理體系分析

前一篇博文中:linux內核初始化階段通過early_trap_init()函數完成了把異常向量拷貝到0xFFFF0000開始的地方,這些異常向量大部分指向通過vector_stub宏定義的那段代碼,這段代碼完成的主要工作是計算異常返回地址、保存現場、切換到svc模式、跳轉執行彙編異常處理函數,彙編異常處理函數工作在svc模式,先接管上一異常模式保存的現場,然後調用C處理函數,C函數返回後執行一段彙編代碼完成異常返回工作。這一系列的工作就是基於arm9處理器的內核異常處理的體系架構。

linux內核irq中斷也是異常事件的一類,但是irq中斷髮生源不止一個,arm920t只有一個irq中斷信號接收引腳,對具體的中斷源的判斷還需要依賴片外的中斷控制器,再者,irq中斷引腳是和外設上的中斷引腳相連,外設的種類千奇百態,具體的外設有中斷事件時需要處理器執行特殊的處理工作,我想基於這種背景,必須要專門的一套中斷處理體系結構來管理中斷事件才能讓處理器高效工作。

內核中斷處理體系結構的搭建任務主要落在init_IRQ()函數,他直接由srart_kernel()函數來調用,定義於arch/arm/kernel/irq.c。

void __init init_IRQ(void)
{
	int irq; //中斷號

	for (irq = 0; irq < NR_IRQS; irq++)
		irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE; //通過把每一箇中斷號對應結構體成員“status”狀態字段設置成未請求和未探測狀態

	init_arch_irq(); //init_arch_irq實際上是一個函數指針,最初由setup_arch()函數來設置這個指針
}
函數分析:

內核將所有的中斷統一編號,習慣叫“中斷號”,同時定義了irq_desc[NR_IRQS]結構體數據用來描述每一箇中斷,中斷號就是用來在這個數組裏面定位對應的中斷描述結構體的。irq_desc結構體後面再進行分析,現在先放一邊。

探索init_arch_irq()函數:找了一下發現他在start_kernel-->setup_arch函數中被初始化

void __init setup_arch(char **cmdline_p)
{
	.....
	
	init_arch_irq = mdesc->init_irq; //將void型指針init_arch_irq強制指向struct machine_desc結構體的init_irq成員
	
	.....
}
好,問題又來了:什麼時候事先把“mdesc->init_irq”這個成員填充了初始?
struct machine_desc {
	......
	
	void	(*init_irq)(void);   //是個函數指針,在sourceinside裏邊搜一搜。
	
	......
};
init_irq成員在文件arch/arm/mach-s3c2440/mach-tq2440.c中被賦值爲s3c24xx_init_irq函數
MACHINE_START(S3C2440, "TQ2440")
	.phys_io	= S3C2410_PA_UART,
	.io_pg_offst	= (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
	.boot_params	= S3C2410_SDRAM_PA + 0x100,

	.init_irq	= s3c24xx_init_irq, //終於找到了!! init_IRQ()函數中通過函數指針init_arch_irq調用的真正內容了!!!分析這個函數,看看究竟做了些真麼。
	.map_io		= tq2440_map_io,
	.init_machine	= tq2440_machine_init, 
	.timer		= &s3c24xx_timer,
MACHINE_END
分析到這裏,中斷處理框架算沒開始搭建,因爲還沒有設置好各種中斷的觸發模式和處理函數之類的東西,設置這些內容就是將irq_desc結構體數組中的主要成員初始化,下面看看是不是這樣!
探索s3c24xx_init_irq()函數:也是在arch/arm/kernel/irq.c文件中定義,裏面是個龐大的函數調用關係

功能:完成對中斷控制器的初始化,並且設置中斷描述符的相應的函數指針的值,以在中斷髮生時發生時,調用這些函數來完成芯片級的處理。即建立一箇中斷源對應一個處理函數的映射關係!

void __init s3c24xx_init_irq(void)
{
	unsigned long pend;
	unsigned long last;
	int irqno;
	int i;

	irqdbf("s3c2410_init_irq: clearing interrupt status flags\n");

	/* first, clear all interrupts pending... */
	//(1)在開始設置之前,清除所有中斷源等待
	last = 0;
	for (i = 0; i < 4; i++) {
		pend = __raw_readl(S3C24XX_EINTPEND);

		if (pend == 0 || pend == last)
			break;

		__raw_writel(pend, S3C24XX_EINTPEND);
		printk("irq: clearing pending ext status %08x\n", (int)pend);
		last = pend;
	}

	last = 0;
	for (i = 0; i < 4; i++) {
		pend = __raw_readl(S3C2410_INTPND);

		if (pend == 0 || pend == last)
			break;

		__raw_writel(pend, S3C2410_SRCPND);
		__raw_writel(pend, S3C2410_INTPND);
		printk("irq: clearing pending status %08x\n", (int)pend);
		last = pend;
	}

	last = 0;
	for (i = 0; i < 4; i++) {
		pend = __raw_readl(S3C2410_SUBSRCPND);

		if (pend == 0 || pend == last)
			break;

		printk("irq: clearing subpending status %08x\n", (int)pend);
		__raw_writel(pend, S3C2410_SUBSRCPND);
		last = pend;
	}

	/* register the main interrupts */
	// (2)註冊主要的中斷

	irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n");

	for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) {
		/* set all the s3c2410 internal irqs */

		switch (irqno) {
			/* deal with the special IRQs (cascaded) */

		case IRQ_EINT4t7:
		case IRQ_EINT8t23:
		case IRQ_UART0:
		case IRQ_UART1:
		case IRQ_UART2:
		case IRQ_ADCPARENT:
			set_irq_chip(irqno, &s3c_irq_level_chip);
			set_irq_handler(irqno, handle_level_irq);
			break;

		case IRQ_RESERVED6:
		case IRQ_RESERVED24:
			/* no IRQ here */
			break;

		default:
			//irqdbf("registering irq %d (s3c irq)\n", irqno);
			set_irq_chip(irqno, &s3c_irq_chip);
			set_irq_handler(irqno, handle_edge_irq);
			set_irq_flags(irqno, IRQF_VALID);
		}
	}

	/* setup the cascade irq handlers */
	// (3)設置級聯中斷處理

	set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
	set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);

	set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
	set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
	set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
	set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);

	/* register external interrupts */
	// (4)註冊外部中斷

	for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
		irqdbf("registering irq %d (ext int)\n", irqno);
		set_irq_chip(irqno, &s3c_irq_eint0t4);
		set_irq_handler(irqno, handle_edge_irq);
		set_irq_flags(irqno, IRQF_VALID);
	}

	for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
		irqdbf("registering irq %d (extended s3c irq)\n", irqno);
		set_irq_chip(irqno, &s3c_irqext_chip);
		set_irq_handler(irqno, handle_edge_irq);
		set_irq_flags(irqno, IRQF_VALID);
	}

	/* register the uart interrupts */
	// (5)註冊UART中斷

	irqdbf("s3c2410: registering external interrupts\n");

	for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) {
		irqdbf("registering irq %d (s3c uart0 irq)\n", irqno);
		set_irq_chip(irqno, &s3c_irq_uart0);
		set_irq_handler(irqno, handle_level_irq);
		set_irq_flags(irqno, IRQF_VALID);
	}
	
	for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) {
		irqdbf("registering irq %d (s3c uart1 irq)\n", irqno);
		set_irq_chip(irqno, &s3c_irq_uart1);
		set_irq_handler(irqno, handle_level_irq);//設置處理函數
		set_irq_flags(irqno, IRQF_VALID);
	}

	for (irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) {
		irqdbf("registering irq %d (s3c uart2 irq)\n", irqno);
		set_irq_chip(irqno, &s3c_irq_uart2);
		set_irq_handler(irqno, handle_level_irq);//設置處理函數
		set_irq_flags(irqno, IRQF_VALID);
	}

	for (irqno = IRQ_TC; irqno <= IRQ_ADC; irqno++) {
		irqdbf("registering irq %d (s3c adc irq)\n", irqno);
		set_irq_chip(irqno, &s3c_irq_adc);
		set_irq_handler(irqno, handle_edge_irq);//設置處理函數
		set_irq_flags(irqno, IRQF_VALID);
	}

	irqdbf("s3c2410: registered interrupt handlers\n");
}
函數分析:
主要是使用set_irq_chip()、set_irq_handler()、set_irq_flags()這三個函數來初始化irq_desc結構體中的chip、handle_irq、flags,初始化也就是把他指向某個東東(結構體、函數等)

取一段來分析:

for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) 
{
	irqdbf("registering irq %d (extended s3c irq)\n", irqno);
	set_irq_chip(irqno, &s3c_irqext_chip);
	set_irq_handler(irqno, handle_edge_irq);
	set_irq_flags(irqno, IRQF_VALID);
}

set_irq_chip():執行的效果就是irq_desc[irqno]=&s3c_irqext_chip,把中斷描述符的struct irq_chip *chip結構指針指向針對s3cxxx芯片的設置好的struct irq_chip s3c_irqext_chip結構體,他裏邊的成員主要是用來設置外部中斷的屏蔽與否、觸發方式之類的函數指針

static struct irq_chip s3c_irqext_chip = {
	.name		= "s3c-ext",
	.mask		= s3c_irqext_mask,   //屏蔽中斷源
	.unmask		= s3c_irqext_unmask, //開啓接收中斷源
	.ack		= s3c_irqext_ack,    //響應中斷:通常是清除當前中斷使得可以接收下一個中斷
	.set_type	= s3c_irqext_type,   //觸發方式
	.set_wake	= s3c_irqext_wake    //喚醒相關
};

分析到這裏中斷處理的框架就算是搭建出來了,如下圖所示,圖中用戶註冊的中斷處理函數主要是指驅動開發的時候程序員添加的。


下面以handle_edge_irq中斷處理函數的處理過程進行分析。

④中斷處理函數,就跟我們自己玩裸機的時候自己寫的意思是一樣:清中斷和處理中斷,就是它這裏考慮到的東西比較多,所以看起來複雜,事實它也是比較複雜。

void handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
	spin_lock(&desc->lock);

	desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);

	/*
	 * If we're currently running this IRQ, or its disabled,
	 * we shouldn't process the IRQ. Mark it pending, handle
	 * the necessary masking and go out
	 * 非正常處理
	 */
	if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) ||
		    !desc->action)) {
		desc->status |= (IRQ_PENDING | IRQ_MASKED);
		mask_ack_irq(desc, irq);
		desc = irq_remap_to_desc(irq, desc);
		goto out_unlock;
	}
	kstat_incr_irqs_this_cpu(irq, desc);

	/* Start handling the irq 開始處理中斷 */
	if (desc->chip->ack)
		desc->chip->ack(irq);  //##清中斷處理
	desc = irq_remap_to_desc(irq, desc);

	/* Mark the IRQ currently in progress.*/
	desc->status |= IRQ_INPROGRESS;

	do {
		struct irqaction *action = desc->action; //##取出irq_desc結構裏邊的action成員,action鏈表裏面的是用戶註冊的處理函數
		irqreturn_t action_ret;

		if (unlikely(!action)) {
			desc->chip->mask(irq);
			goto out_unlock;
		}

		/*
		 * When another irq arrived while we were handling
		 * one, we could have masked the irq.
		 * Renable it, if it was not disabled in meantime.
		 * 非正常處理:如果另外一箇中斷到來,屏蔽它
		 */
		if (unlikely((desc->status &
			       (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==
			      (IRQ_PENDING | IRQ_MASKED))) {
			desc->chip->unmask(irq);
			desc->status &= ~IRQ_MASKED;
		}

		desc->status &= ~IRQ_PENDING;
		spin_unlock(&desc->lock);
		action_ret = handle_IRQ_event(irq, action); //##進入handle_IRQ_event函數中發現是:取出action鏈表裏邊的成員,執行action->handle
		if (!noirqdebug)
			note_interrupt(irq, desc, action_ret);
		spin_lock(&desc->lock);

	} while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);

	desc->status &= ~IRQ_INPROGRESS;
out_unlock:
	spin_unlock(&desc->lock);
}

來看看handle_IRQ_event函數:遍歷中斷號對應的中段描述符中的action鏈表,並執行action結構體的handler成員,即處理函數

irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
	irqreturn_t ret, retval = IRQ_NONE;
	unsigned int status = 0;

	if (!(action->flags & IRQF_DISABLED))
		local_irq_enable_in_hardirq();

	do {
		trace_irq_handler_entry(irq, action);
		ret = action->handler(irq, action->dev_id); //執行action->handler函數
		trace_irq_handler_exit(irq, action, ret);

		switch (ret) {
		case IRQ_WAKE_THREAD:
			ret = IRQ_HANDLED;
			if (unlikely(!action->thread_fn)) {
				warn_no_thread(irq, action);
				break;
			}
			if (likely(!test_bit(IRQTF_DIED,
					     &action->thread_flags))) {
				set_bit(IRQTF_RUNTHREAD, &action->thread_flags);
				wake_up_process(action->thread);
			}
		case IRQ_HANDLED:
			status |= action->flags;
			break;

		default:
			break;
		}

		retval |= ret;
		action = action->next;
	} while (action);  //取出鏈表中的成員

	if (status & IRQF_SAMPLE_RANDOM)
		add_interrupt_randomness(irq);
	local_irq_disable();

	return retval;
}

⑤情景分析:按鍵中斷髮生時,函數處理流程:

首先進入異常模式執行b   vector_irq+offset --> 然後vecrot_stubs做一些保護現場工作並切換到svc模式-->跳到跳轉表__usr_irq執行再接管“現場”數據,之後調用asm_do_IRQ,他根據中斷號找到irq_desc[irq].handle_irq

-->假設irq_desc[irq].handle_irq指向handle_edge_irq,這個函數就會進行上面第④點的工作。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章