在了解中斷處理機(jī)制前,先看一下PowerPC的中斷源,這里使用的是e300c3的內(nèi)核,從E300核的角度,中斷源可分為異常和外部中斷,異常是e300內(nèi)核產(chǎn)生的,如出現(xiàn)非法指令,或者是訪問(wèn)存儲(chǔ)器時(shí)出現(xiàn)TLB Miss等情況。這種情況太復(fù)雜了,沒(méi)有深究,這里所說(shuō)的中斷為外部中斷。所謂外部中斷,就是通過(guò)e300外部引腳產(chǎn)生的中斷。E300的外部中斷主要有:int#, cint#, mcp#. 這三根pin分別對(duì)應(yīng)一般中斷,critical中斷和machine
check中斷。
在設(shè)備驅(qū)動(dòng)過(guò)程中,用戶可以使用request_irq函數(shù)將外設(shè)的中斷服務(wù)例程掛載到外部中斷處理程序中。外部中斷處理程序,可以直接處理硬件中斷,但是request_irq函數(shù)試用軟件中斷號(hào)進(jìn)行掛載,因此linux必然采用了某種方式進(jìn)行軟硬件中斷的映射。
這里需要了解下中斷向量表的概念,就是它將軟件中斷號(hào)與硬件中斷號(hào)聯(lián)系起來(lái),在系統(tǒng)中,中斷源有很多,當(dāng)某一種中斷發(fā)生時(shí),處理器最早進(jìn)入的是處理函數(shù)的地址,就是中斷向量。每種類(lèi)型對(duì)應(yīng)一個(gè)中斷向量號(hào),一系列的中斷號(hào)構(gòu)成中斷向量表。比如驅(qū)動(dòng)程序關(guān)心的外部中斷,當(dāng)這種中斷發(fā)生時(shí),處理器內(nèi)核(e300)就會(huì)到指定寄存器中去取得中斷處理函數(shù)的入口地址。
硬件中斷號(hào)是當(dāng)中斷發(fā)生時(shí),處理器從外部的PIC(Programable Interrupt Controller)上讀到的數(shù)值。因?yàn)樵赑owerPC Linux中有dts的概念,驅(qū)動(dòng)程序員需要知道自己所負(fù)責(zé)的設(shè)備對(duì)應(yīng)的device node上的interrupt項(xiàng)如何寫(xiě)的問(wèn)題,這里的interrupt上的數(shù)字就是硬件中斷號(hào)。在Linux系統(tǒng)初始化期間,通過(guò)對(duì)PIC的配置,可以將硬件的連接轉(zhuǎn)化成相應(yīng)的硬件中斷號(hào),例如,8315上的8根外設(shè)中斷線IRQ0~IRQ7,可以通過(guò)PIC的配置,分別映射成的硬件中斷號(hào)為10~18,那么當(dāng)IRQ0上有中斷請(qǐng)求時(shí),處理器通過(guò)讀取PIC,就得到P1所對(duì)應(yīng)的硬件中斷號(hào)為10。這些都是硬件層面的東西,當(dāng)驅(qū)動(dòng)程序安裝它們的中斷處理程序時(shí),是基于軟件中斷號(hào)的,也就是內(nèi)核函數(shù)request_irq中的參數(shù)irq是個(gè)軟件中斷號(hào)。
軟件中斷號(hào)是Linux下的概念,為了支持多平臺(tái)的關(guān)系,Linux不直接用硬件中斷號(hào)索引irq_desc。所以當(dāng)外部中斷發(fā)生時(shí),在外部中斷入口函數(shù)中會(huì)通過(guò)讀取PIC來(lái)獲得本次中斷的硬件中斷號(hào),獲得硬件中斷號(hào)后,會(huì)將其映射成對(duì)應(yīng)的軟件中斷號(hào)來(lái)索引irq_desc數(shù)組,從而獲得該軟件中斷號(hào)上的中斷處理函數(shù)。顯然,這種硬件中斷號(hào)到軟件中斷號(hào)的映射關(guān)系,應(yīng)該在設(shè)備可以處理中斷前就要建立好了。在Linux PowerPC中,這種映射關(guān)系是由函數(shù)irq_of_parse_and_map(struct
device_node *dev, int index)來(lái)完成的。
注意:在8315中,關(guān)于MSI的硬件中斷號(hào),可以在系統(tǒng)全局中斷向量寄存器SIVCR中查詢。另外,其實(shí),如果沒(méi)有軟件中斷號(hào),設(shè)備驅(qū)動(dòng)程序也能以硬件中斷號(hào)作索引掛載處理程序,從功能上說(shuō)沒(méi)有任何問(wèn)題。軟件中斷號(hào)的引入,是為了硬件中斷號(hào)對(duì)OS透明。這樣處理能減少OS對(duì)硬件平臺(tái)的依賴性。
下面貼些內(nèi)核的代碼,方便細(xì)細(xì)分析:
linux使用結(jié)構(gòu)體struct irq_map_entry irq_map[NR_IRQS]完成軟件與硬件的中斷號(hào)映射,一般系統(tǒng)自動(dòng)查找可用的軟件號(hào)以對(duì)應(yīng)請(qǐng)求的硬件中斷號(hào)。
在使用open firmware的系統(tǒng)中,驅(qū)動(dòng)程序在執(zhí)行ruquest_irq之前要先進(jìn)行軟硬件中斷號(hào)的映射,具體通過(guò)函數(shù):
irq_of_parse_and_map(struct device_node * dev, int index);
實(shí)現(xiàn),具體函數(shù)調(diào)用過(guò)程如下:
irq_create_of_mapping(struct device_node * controller, u32 * inspec, unsigned int intsize);
irq_create_mapping(struct irq_host *host, irq_hw_number_t hwirq);
程序除了進(jìn)行軟硬件中斷號(hào)的映射外還需要初始后相應(yīng)結(jié)構(gòu)。
下面看看與中斷相關(guān)的另外幾個(gè)主要數(shù)據(jù)結(jié)構(gòu):
1、結(jié)構(gòu)體:struct irq_desc(include/linux/irq.h)
- struct irq_desc {
- unsigned int irq;
- struct timer_rand_state *timer_rand_state;
- unsigned int *kstat_irqs;
- #ifdef CONFIG_INTR_REMAP
- struct irq_2_iommu *irq_2_iommu;
- #endif
- irq_flow_handler_t handle_irq;
- struct irq_chip *chip;
- struct msi_desc *msi_desc;
- void *handler_data;
- void *chip_data;
- struct irqaction *action; /* IRQ action list */
- unsigned int status; /* IRQ status */
-
- 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;
- raw_spinlock_t lock;
- #ifdef CONFIG_SMP
- cpumask_var_t affinity;
- const struct cpumask *affinity_hint;
- unsigned int node;
- #ifdef CONFIG_GENERIC_PENDING_IRQ
- cpumask_var_t pending_mask;
- #endif
- #endif
- atomic_t threads_active;
- wait_queue_head_t wait_for_threads;
- #ifdef CONFIG_PROC_FS
- struct proc_dir_entry *dir;
- #endif
- const char *name;
- } ____cacheline_internodealigned_in_smp;
這個(gè)結(jié)構(gòu)體用來(lái)描述中斷源,是用來(lái)連接硬件中斷和驅(qū)動(dòng)程序中通過(guò)request_irq注冊(cè)的中斷處理函數(shù)之間的橋梁。數(shù)組irq_desc[NR_IRQS](NR_IRQS=225)中每一項(xiàng)都對(duì)應(yīng)一個(gè)相應(yīng)的中斷源,他的每一項(xiàng)對(duì)應(yīng)著中斷向量表中的一項(xiàng),即該數(shù)組的第一項(xiàng)對(duì)應(yīng)著中斷向量表中的第32項(xiàng)(中斷向量號(hào)為0x20),往下依次對(duì)應(yīng)。中斷向量表一共有256項(xiàng)。
部分成員解釋?zhuān)?br>
status:或者是0,或者是從一個(gè)特定的集合中抽取的一個(gè)標(biāo)志位(不太清楚)。這些標(biāo)志位代表了IRQ的狀態(tài)--是否被禁止,有關(guān)IRQ的設(shè)備當(dāng)前是否正被自動(dòng)檢測(cè)等。
chip:是一個(gè)指向hw_interrupt_type(或者irq_chip)的指針。其中定義的函數(shù)是平臺(tái)相關(guān)的(更具體的,是中斷控制器相關(guān)的),很顯然不同平臺(tái)的中斷控制器擁有不同的操作函數(shù),比如enable, disable, mask, unmask某個(gè)中斷的操作。從平臺(tái)移植的角度,chip屏蔽了底層硬件的不同,使得在內(nèi)核中某些代碼成了平臺(tái)無(wú)關(guān)性。但是對(duì)于不同平臺(tái)的Linux,必須由BSP部分負(fù)責(zé)初始化irq_desc中的chip變量。
action:是一個(gè)指向由irqaction結(jié)構(gòu)體組成的一個(gè)單向鏈表的頭的指針。若一個(gè)IRQ只被一個(gè)中斷源使用,那么該鏈表的長(zhǎng)度就是1,當(dāng)有多個(gè)設(shè)備共享一個(gè)中斷源時(shí),該鏈表就會(huì)由多個(gè)irqaction結(jié)構(gòu)體組成,下面對(duì)此有介紹。
depth:irq_desc_t的當(dāng)前用戶的個(gè)數(shù),主要用來(lái)保證事件正在處理的過(guò)程中IRQ不會(huì)被禁止。
從驅(qū)動(dòng)程序開(kāi)發(fā)者角度,不需要直接調(diào)用chip中的函數(shù)。但是在驅(qū)動(dòng)程序request_irq時(shí),因?yàn)橐趇rq_desc所對(duì)應(yīng)的某一項(xiàng)中安裝中斷處理函數(shù),request_irq函數(shù)原型如下:
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
其中第三個(gè)參數(shù)irqflags會(huì)影響到內(nèi)核對(duì)chip中函數(shù)的調(diào)用,所以從這個(gè)角度而言,了解一些chip相關(guān)的中斷enable/disable/mask/unmask對(duì)驅(qū)動(dòng)程序員是有幫助的。
2、結(jié)構(gòu)體:struct irqaction(include/linux/interrupt.h)
- struct irqaction {
- irq_handler_t handler;
- unsigned long flags;
- const char *name;
- void *dev_id;
- struct irqaction *next;
- int irq;
- struct proc_dir_entry *dir;
- irq_handler_t thread_fn;
- struct task_struct *thread;
- unsigned long thread_flags;
- };
這個(gè)結(jié)構(gòu)體包含了處理一種中斷所需要的各種信息,它代表了內(nèi)核接受到特定IRQ之后應(yīng)該采取的操作。
主要成員:
handler—該指針?biāo)赶虻暮瘮?shù)就是在中斷服務(wù)程序,當(dāng)中斷發(fā)生時(shí)內(nèi)核便會(huì)調(diào)用這個(gè)指針指向的函數(shù)。
flags:該標(biāo)志位可以是0,也可以是:SA_INTERRUPT(表示此中斷處理程序是一個(gè)快速中斷處理程序,在2.6中默認(rèn)情況下沒(méi)有這個(gè)標(biāo)志)SA_SAMPLE_RANDOM(表示這個(gè)中斷對(duì)內(nèi)核池有貢獻(xiàn),我理解為就是在中斷時(shí)產(chǎn)生一些隨機(jī)數(shù),這些隨機(jī)數(shù)被用來(lái)作為加密密匙,因?yàn)橹袛嗍请S機(jī)發(fā)生的,如果某種中斷是有頻率的被產(chǎn)生,那么它就不要設(shè)置此標(biāo)志位,還有就是那種設(shè)備容易被攻擊也不應(yīng)該設(shè)置此標(biāo)志位)SA_SHIRQ(此標(biāo)志位表示允許多個(gè)中斷服務(wù)程序共享一個(gè)中斷號(hào),如不設(shè)則一個(gè)程序?qū)?yīng)一個(gè)中斷線)。
mask:在x86上不會(huì)用到。
name:產(chǎn)生中斷的硬件的名字.
dev_id:該標(biāo)志位主要在共享中斷號(hào)時(shí)使用,即你設(shè)置flags=SA_SHIRQ時(shí),有多個(gè)中斷服務(wù)程序共享一個(gè)中斷號(hào)時(shí),內(nèi)核就需要知道在用完中斷程序后該刪除那個(gè)中斷服務(wù)程序。不共享時(shí)此成員為null。
next:如果flags=SA_SHIRQ,那么這就是指向?qū)α兄邢乱粋€(gè)struct irqaction結(jié)構(gòu)體的指針,否則為空。
irq:不用說(shuō)這就是中斷號(hào)了。
3 結(jié)構(gòu)體:struct hw_interrupt_type(include/linux/irq.h)
- struct irq_chip {
- const char *name;
- unsigned int (*startup)(unsigned int irq);
- void (*shutdown)(unsigned int irq);
- void (*enable)(unsigned int irq);
- void (*disable)(unsigned int irq);
-
- void (*ack)(unsigned int irq);
- void (*mask)(unsigned int irq);
- void (*mask_ack)(unsigned int irq);
- void (*unmask)(unsigned int irq);
- void (*eoi)(unsigned int irq);
-
- void (*end)(unsigned int irq);
- int (*set_affinity)(unsigned int irq,
- const struct cpumask *dest);
- int (*retrigger)(unsigned int irq);
- int (*set_type)(unsigned int irq, unsigned int flow_type);
- int (*set_wake)(unsigned int irq, unsigned int on);
-
- void (*bus_lock)(unsigned int irq);
- void (*bus_sync_unlock)(unsigned int irq);
-
- /* Currently used only by UML, might disappear one day.*/
- #ifdef CONFIG_IRQ_RELEASE_METHOD
- void (*release)(unsigned int irq, void *dev_id);
- #endif
- /*
- * For compatibility, ->typename is copied into ->name.
- * Will disappear.
- */
- const char *typename;
- };
它是用來(lái)描述中斷控制器的,也就是一個(gè)抽象的中斷控制器,其成員是一系列指向函數(shù)的指針。
typename:給相應(yīng)的控制器起一個(gè)便于理解的名字。
startup:允許從給定的控制器的IRQ所產(chǎn)生的事件。(基本上與enable相同)
shutdown:禁止從給定的控制器的IRQ所產(chǎn)生的事件。(基本上與disable相同)
以上三個(gè)結(jié)構(gòu)體的關(guān)系可以用下面一張圖來(lái)說(shuō)明IRQ結(jié)構(gòu)間的關(guān)系:

數(shù)組irq_desc_t用來(lái)描述中斷的相關(guān)信息,它有225項(xiàng),每一項(xiàng)代表一個(gè)中斷源,其中字段irq(注意這個(gè)字段就是上圖中的handler字段),此結(jié)構(gòu)體用來(lái)描述中斷控制器,action字段用來(lái)描述處理一種中斷所需要的各種信息,它代表了內(nèi)核接受到特定IRQ之后應(yīng)該采取的操作。
|