原标題:linux kernel的中斷子系統之(三):IRQ number和中斷描述符
轉載自:蝸窩科技(部落格)
作者:linuxer
一、前言
本文主要圍繞IRQ number和中斷描述符(interrupt deor)這兩個概念描述通用中斷處理過程。第二章主要描述基本概念,包括什麼是IRQ number,什麼是中斷描述符等。第三章描述中斷描述符資料結構的各個成員。第四章描述了初始化中斷描述符相關的接口API。第五章描述中斷描述符相關的接口API。
二、基本概念
1、通用中斷的代碼處理示意圖
一個關于通用中斷處理的示意圖如下:
在linux kernel中,對于每一個外設的IRQ都用struct irq_desc來描述,我們稱之中斷描述符(struct irq_desc)。linux kernel中會有一個資料結構儲存了關于所有IRQ的中斷描述符資訊,我們稱之中斷描述符DB(上圖中紅色框圖内)。當發生中斷後,首先擷取觸發中斷的HW interupt ID,然後通過irq domain翻譯成IRQ nuber,然後通過IRQ number就可以擷取對應的中斷描述符。調用中斷描述符中的highlevel irq-events handler來進行中斷處理就OK了。而highlevel irq-events handler主要進行下面兩個操作:
(1)調用中斷描述符的底層irq chip driver進行mask,ack等callback函數,進行interrupt flow control。
(2)調用該中斷描述符上的action list中的specific handler(我們用這個術語來區分具體中斷handler和high level的handler)。這個步驟不一定會執行,這是和中斷描述符的目前狀态相關,實際上,interrupt flow control是軟體(設定一些标志位,軟體根據标志位進行處理)和硬體(mask或者unmask interrupt controller等)一起控制完成的。
2、中斷的打開和關閉
我們再來看看整個通用中斷處理過程中的開關中斷情況,開關中斷有兩種:
(1)開關local CPU的中斷。對于UP,關閉CPU中斷就關閉了一切,永遠不會被搶占。對于SMP,實際上,沒有關全局中斷這一說,隻能關閉local CPU(代碼運作的那個CPU)
(2)控制interrupt controller,關閉某個IRQ number對應的中斷。更準确的術語是mask或者unmask一個 IRQ。
本節主要描述的是第一種,也就是控制CPU的中斷。當進入high level handler的時候,CPU的中斷是關閉的(硬體在進入IRQ processor mode的時候設定的)。
對于外設的specific handler,舊的核心(2.6.35版本之前)認為有兩種:slow handler和fast handle。在request irq的時候,對于fast handler,需要傳遞IRQF_DISABLED的參數,確定其中斷處理過程中是關閉CPU的中斷,因為是fast handler,執行很快,即便是關閉CPU中斷不會影響系統的性能。但是,并不是每一種外設中斷的handler都是那麼快(例如磁盤),是以就有slow handler的概念,說明其在中斷處理過程中會耗時比較長。對于這種情況,如果在整個specific handler中關閉CPU中斷,對系統的performance會有影響。是以,對于slow handler,在從high level handler轉入specific handler中間會根據IRQF_DISABLED這個flag來決定是否打開中斷,具體代碼如下(來自2.6.23核心):
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
……
if (!(action->flags & IRQF_DISABLED))
local_irq_enable_in_hardirq();
……
}
如果沒有設定IRQF_DISABLED(slow handler),則打開本CPU的中斷。然而,随着軟硬體技術的發展:
(1)硬體方面,CPU越來越快,原來slow handler也可以很快執行完畢
(2)軟體方面,linux kernel提供了更多更好的bottom half的機制
是以,在新的核心中,比如3.14,IRQF_DISABLED被廢棄了。我們可以思考一下,為何要有slow handler?每一個handler不都是應該迅速執行完畢,傳回中斷現場嗎?此外,任意中斷可以打斷slow handler執行,進而導緻中斷嵌套加深,對核心棧也是考驗。是以,新的核心中在interrupt specific handler中是全程關閉CPU中斷的。
3、IRQ number
從CPU的角度看,無論外部的Interrupt controller的結構是多麼複雜,I do not care,我隻關心發生了一個指定外設的中斷,需要調用相應的外設中斷的handler就OK了。更準确的說是通用中斷處理子產品不關心外部interrupt controller的組織細節(電源管理子產品當然要關注具體的裝置(interrupt controller也是裝置)的拓撲結構)。一言以蔽之,通用中斷處理子產品可以用一個線性的table來管理一個個的外部中斷,這個表的每個元素就是一個irq描述符,在kernel中定義如下:
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = {
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
}
};
系統中每一個連接配接外設的中斷線(irq request line)用一個中斷描述符來描述,每一個外設的interrupt request line配置設定一個中斷号(irq number),系統中有多少個中斷線(或者叫做中斷源)就有多少個中斷描述符(struct irq_desc)。NR_IRQS定義了該硬體平台IRQ的最大數目。
總之,一個靜态定義的表格,irq number作為index,每個描述符都是緊密的排在一起,一切看起來很美好,但是現實很殘酷的。有些系統可能會定義一個很大的NR_IRQS,但是隻是想用其中的若幹個,換句話說,這個靜态定義的表格不是每個entry都是有效的,有空洞,如果使用靜态定義的表格就會導緻了記憶體很大的浪費。為什麼會有這種需求?我猜是和各個interrupt controller硬體的interrupt ID映射到irq number的算法有關。在這種情況下,靜态表格不适合了,我們改用一個radix tree來儲存中斷描述符(HW interrupt作為索引)。這時候,每一個中斷描述符都是動态配置設定,然後插入到radix tree中。如果你的系統采用這種政策,那麼需要打開CONFIG_SPARSE_IRQ選項。上面的示意圖描述的是靜态表格的中斷描述符DB,如果打開CONFIG_SPARSE_IRQ選項,系統使用Radix tree來儲存中斷描述符DB,不過概念和靜态表格是類似的。
此外,需要注意的是,在舊核心中,IRQ number和硬體的連接配接有一定的關系,但是,在引入irq domain後,IRQ number已經變成一個單純的number,和硬體沒有任何關系。
三、中斷描述符資料結構
1、底層irq chip相關的資料結構
中斷描述符中應該會包括底層irq chip相關的資料結構,linux kernel中把這些資料組織在一起,形成struct irq_data,具體代碼如下:
struct irq_data {
u32 mask;----------TODO
unsigned int irq;--------IRQ number
unsigned long hwirq;-------HW interrupt ID
unsigned int node;-------NUMA node index
unsigned int state_use_accessors;--------底層狀态,參考IRQD_xxxx
struct irq_chip *chip;----------該中斷描述符對應的irq chip資料結構
struct irq_domain *domain;--------該中斷描述符對應的irq domain資料結構
void *handler_data;--------和外設specific handler相關的私有資料
void *chip_data;---------和中斷控制器相關的私有資料
struct msi_desc *msi_desc;
cpumask_var_t affinity;-------和irq affinity相關
};
中斷有兩種形态,一種就是直接通過signal相連,用電平或者邊緣觸發。另外一種是基于消息的,被稱為MSI (Message Signaled Interrupts)。msi_desc是MSI類型的中斷相關,這裡不再描述。
node成員用來儲存中斷描述符的記憶體位于哪一個memory node上。 對于支援NUMA(Non Uniform Memory Access Architecture)的系統,其記憶體空間并不是均一的,而是被劃分成不同的node,對于不同的memory node,CPU其通路速度是不一樣的。如果一個IRQ大部分(或者固定)由某一個CPU處理,那麼在動态配置設定中斷描述符的時候,應該考慮将記憶體配置設定在該CPU通路速度比較快的memory node上。
2、irq chip資料結構
Interrupt controller描述符(struct irq_chip)包括了若幹和具體Interrupt controller相關的callback函數,我們總結如下:
成員名字
描述
name
該中斷控制器的名字,用于/proc/interrupts中的顯示
irq_startup
start up 指定的irq domain上的HW interrupt ID。如果不設定的話,default會被設定為enable函數
irq_shutdown
shutdown 指定的irq domain上的HW interrupt ID。如果不設定的話,default會被設定為disable函數
irq_enable
enable指定的irq domain上的HW interrupt ID。如果不設定的話,default會被設定為unmask函數
irq_disable
disable指定的irq domain上的HW interrupt ID。
irq_ack
和具體的硬體相關,有些中斷控制器必須在Ack之後(清除pending的狀态)才能接受到新的中斷。
irq_mask
mask指定的irq domain上的HW interrupt ID
irq_mask_ack
mask并ack指定的irq domain上的HW interrupt ID。
irq_unmask
mask指定的irq domain上的HW interrupt ID
irq_eoi
有些interrupt controler(例如GIC)提供了這樣的寄存器接口,讓CPU可以通知interrupt controller,它已經處理完一個中斷
irq_set_affinity
在SMP的情況下,可以通過該callback函數設定CPU affinity
irq_retrigger
重新觸發一次中斷,一般用在中斷丢失的場景下。如果硬體不支援retrigger,可以使用軟體的方法。
irq_set_type
設定指定的irq domain上的HW interrupt ID的觸發方式,電平觸發還是邊緣觸發
irq_set_wake
和電源管理相關,用來enable/disable指定的interrupt source作為喚醒的條件。
irq_bus_lock
有些interrupt controller是連接配接到慢速總線上(例如一個i2c接口的IO expander晶片),在通路這些晶片的時候需要lock住那個慢速bus(隻能有一個client在使用I2C bus)
irq_bus_sync_unlock
unlock慢速總線
irq_suspend
irq_resume
irq_pm_shutdown
電源管理相關的callback函數
irq_calc_mask
TODO
irq_print_chip
/proc/interrupts中的資訊顯示
3、中斷描述符
在linux kernel中,使用struct irq_desc來描述一個外設的中斷,我們稱之中斷描述符,具體代碼如下:
struct irq_desc {
struct irq_data irq_data;
unsigned int __percpu *kstat_irqs;------IRQ的統計資訊
irq_flow_handler_t handle_irq;--------(1)
struct irqaction *action; -----------(2)
unsigned int status_use_accessors;-----中斷描述符的狀态,參考IRQ_xxxx
unsigned int core_internal_state__do_not_mess_with_it;----(3)
unsigned int depth;----------(4)
unsigned int wake_depth;--------(5)
unsigned int irq_count; ---------(6)
unsigned long last_unhandled;
unsigned int irqs_unhandled;
raw_spinlock_t lock;-----------(7)
struct cpumask *percpu_enabled;-------(8)
#ifdef CONFIG_SMP
const struct cpumask *affinity_hint;----和irq affinity相關,後續單獨文檔描述
struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
unsigned long threads_oneshot; -----(9)
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;--------該IRQ對應的proc接口
#endif
int parent_irq;
struct module *owner;
const char *name;
} ____cacheline_internodealigned_in_smp
(1)handle_irq就是highlevel irq-events handler,何謂high level?站在高處自然看不到細節。我認為high level是和specific相對,specific handler處理具體的事務,例如處理一個按鍵中斷、處理一個磁盤中斷。而high level則是對處理各種中斷互動過程的一個抽象,根據下列硬體的不同:
(a)中斷控制器
(b)IRQ trigger type
highlevel irq-events handler可以分成:
(a)處理電平觸發類型的中斷handler(handle_level_irq)
(b)處理邊緣觸發類型的中斷handler(handle_edge_irq)
(c)處理簡單類型的中斷handler(handle_simple_irq)
(d)處理EOI類型的中斷handler(handle_fasteoi_irq)
會另外有一份文檔對high level handler進行更詳細的描述。
(2)action指向一個struct irqaction的連結清單。如果一個interrupt request line允許共享,那麼該連結清單中的成員可以是多個,否則,該連結清單隻有一個節點。
(3)這個有着很長名字的符号core_internal_state__do_not_mess_with_it在具體使用的時候被被簡化成istate,表示internal state。就像這個名字定義的那樣,我們最好不要直接修改它。
#define istate core_internal_state__do_not_mess_with_it
(4)我們可以通過enable和disable一個指定的IRQ來控制核心的并發,進而保護臨界區的資料。對一個IRQ進行enable和disable的操作可以嵌套(當然一定要成對使用),depth是描述嵌套深度的資訊。
(5)wake_depth是和電源管理中的wake up source相關。通過irq_set_irq_wake接口可以enable或者disable一個IRQ中斷是否可以把系統從suspend狀态喚醒。同樣的,對一個IRQ進行wakeup source的enable和disable的操作可以嵌套(當然一定要成對使用),wake_depth是描述嵌套深度的資訊。
(6)irq_count、last_unhandled和irqs_unhandled用于處理broken IRQ 的處理。所謂broken IRQ就是由于種種原因(例如錯誤firmware),IRQ handler沒有定向到指定的IRQ上,當一個IRQ沒有被處理的時候,kernel可以為這個沒有被處理的handler啟動scan過程,讓系統中所有的handler來認領該IRQ。
(7)保護該中斷描述符的spin lock。
(8)一個中斷描述符可能會有兩種情況,一種是該IRQ是global,一旦disable了該irq,那麼對于所有的CPU而言都是disable的。還有一種情況,就是該IRQ是per CPU的,也就是說,在某個CPU上disable了該irq隻是disable了本CPU的IRQ而已,其他的CPU仍然是enable的。percpu_enabled是一個描述該IRQ在各個CPU上是否enable成員。
(9)threads_oneshot、threads_active和wait_for_threads是和IRQ thread相關,後續文檔會專門描述。
四、初始化相關的中斷描述符的接口
1、靜态定義的中斷描述符初始化
int __init early_irq_init(void)
{
int count, i, node = first_online_node;
struct irq_desc *desc;
init_irq_default_affinity();
desc = irq_desc;
count = ARRAY_SIZE(irq_desc);
for (i = 0; i < count; i++) {---周遊整個lookup table,對每一個entry進行初始化
desc[i].kstat_irqs = alloc_percpu(unsigned int);---配置設定per cpu的irq統計資訊需要的記憶體
alloc_masks(&desc[i], GFP_KERNEL, node);---配置設定中斷描述符中需要的cpu mask記憶體
raw_spin_lock_init(&desc[i].lock);---初始化spin lock
lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
desc_set_defaults(i, &desc[i], node, NULL);---設定default值
}
return arch_early_irq_init();---調用arch相關的初始化函數
}
2、使用Radix tree的中斷描述符初始化
int __init early_irq_init(void)
{……
initcnt = arch_probe_nr_irqs();---體系結構相關的代碼來決定預先配置設定的中斷描述符的個數
if (initcnt > nr_irqs)---initcnt是需要在初始化的時候預配置設定的IRQ的個數
nr_irqs = initcnt; ---nr_irqs是目前系統中IRQ number的最大值
for (i = 0; i < initcnt; i++) {--------預先配置設定initcnt個中斷描述符
desc = alloc_desc(i, node, NULL);---配置設定中斷描述符
set_bit(i, allocated_irqs);---設定已經alloc的flag
irq_insert_desc(i, desc);-----插入radix tree
}
……
}
即便是配置了CONFIG_SPARSE_IRQ選項,在中斷描述符初始化的時候,也有機會預先配置設定一定數量的IRQ。這個數量由arch_probe_nr_irqs決定,對于ARM而言,其arch_probe_nr_irqs定義如下:
int __init arch_probe_nr_irqs(void)
{
nr_irqs = machine_desc->nr_irqs ? machine_desc->nr_irqs : NR_IRQS;
return nr_irqs;
}
3、配置設定和釋放中斷描述符
對于使用Radix tree來儲存中斷描述符DB的linux kernel,其中斷描述符是動态配置設定的,可以使用irq_alloc_descs和irq_free_descs來配置設定和釋放中斷描述符。alloc_desc函數也會對中斷描述符進行初始化,初始化的内容和靜态定義的中斷描述符初始化過程是一樣的。最大可以配置設定的ID是IRQ_BITMAP_BITS,定義如下:
#ifdef CONFIG_SPARSE_IRQ
# define IRQ_BITMAP_BITS (NR_IRQS + 8196)---對于Radix tree,除了預配置設定的,還可以動态
#else 配置設定8196個中斷描述符
# define IRQ_BITMAP_BITS NR_IRQS---對于靜态定義的,IRQ最大值就是NR_IRQS
#endif
五、和中斷控制器相關的中斷描述符的接口
這部分的接口主要有兩類,irq_desc_get_xxx和irq_set_xxx,由于get接口API非常簡單,這裡不再描述,主要描述set類别的接口API。此外,還有一些locked版本的set接口API,定義為__irq_set_xxx,這些API的調用者應該已經持有保護irq desc的spinlock,是以,這些locked版本的接口沒有中斷描述符的spin lock進行操作。這些接口有自己特定的使用場合,這裡也不較長的描述了。
1、接口調用時機
kernel提供了若幹的接口API可以讓核心其他子產品可以操作指定IRQ number的描述符結構。中斷描述符中有很多的成員是和底層的中斷控制器相關,例如:
(1)該中斷描述符對應的irq chip
(2)該中斷描述符對應的irq trigger type
(3)high level handler
在過去,系統中各個IRQ number是固定配置設定的,各個IRQ對應的中斷控制器、觸發類型等也都是清楚的,是以,一般都是在machine driver初始化的時候一次性的進行設定。machine driver的初始化過程會包括中斷系統的初始化,在machine driver的中斷初始化函數中,會調用本文定義的這些接口對各個IRQ number對應的中斷描述符進行irq chip、觸發類型的設定。
在引入了device tree、動态配置設定IRQ number以及irq domain這些概念之後,這些接口的調用時機移到各個中斷控制器的初始化以及各個具體硬體驅動初始化過程中,具體如下:
(1)各個中斷控制器定義好自己的struct irq_domain_ops callback函數,主要是map和translate函數。
(2)在各個具體的硬體驅動初始化過程中,通過device tree系統可以知道自己的中斷資訊(連接配接到哪一個interrupt controler、使用該interrupt controller的那個HW interrupt ID,trigger type為何),調用對應的irq domain的translate進行翻譯、解析。之後可以動态申請一個IRQ number并和該硬體外設的HW interrupt ID進行映射,調用irq domain對應的map函數。在map函數中,可以調用本節定義的接口進行中斷描述符底層interrupt controller相關資訊的設定。
2、irq_set_chip
這個接口函數用來設定中斷描述符中desc->irq_data.chip成員,具體代碼如下:
int irq_set_chip(unsigned int irq, struct irq_chip *chip)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0); ----(1)
desc->irq_data.chip = chip;
irq_put_desc_unlock(desc, flags);---------------(2)
irq_reserve_irq(irq);----------------------(3)
return 0;
}
(1)擷取irq number對應的中斷描述符。這裡用關閉中斷+spin lock來保護中斷描述符,flag中就是儲存的關閉中斷之前的狀态flag,後面在(2)中會恢複中斷flag。
(3)前面我們說過,irq number有靜态表格定義的,也有radix tree類型的。對于靜态定義的中斷描述符,沒有alloc的概念。但是,對于radix tree類型,需要首先irq_alloc_desc或者irq_alloc_descs來配置設定一個或者一組IRQ number,在這些alloc函數值,就會set那些那些已經配置設定的IRQ。對于靜态表格而言,其IRQ沒有配置設定,是以,這裡通過irq_reserve_irq函數辨別該IRQ已經配置設定,雖然對于CONFIG_SPARSE_IRQ(使用radix tree)的配置而言,這個操作重複了(在alloc的時候已經設定了)。
3、irq_set_irq_type
這個函數是用來設定該irq number的trigger type的。
int irq_set_irq_type(unsigned int irq, unsigned int type)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
int ret = 0;
type &= IRQ_TYPE_SENSE_MASK;
ret = __irq_set_trigger(desc, irq, type);
irq_put_desc_busunlock(desc, flags);
return ret;
}
來到這個接口函數,第一個問題就是:為何irq_set_chip接口函數使用irq_get_desc_lock來擷取中斷描述符,而irq_set_irq_type這個函數卻需要irq_get_desc_buslock呢?其實也很簡單,irq_set_chip不需要通路底層的irq chip(也就是interrupt controller),但是irq_set_irq_type需要。設定一個IRQ的trigger type最終要調用desc->irq_data.chip->irq_set_type函數對底層的interrupt controller進行設定。這時候,問題來了,對于嵌入SOC内部的interrupt controller,當然沒有問題,因為通路這些中斷控制器的寄存器memory map到了CPU的位址空間,通路非常的快,是以,關閉中斷+spin lock來保護中斷描述符當然沒有問題,但是,如果該interrupt controller是一個I2C接口的IO expander晶片(這類晶片是擴充的IO,也可以提供中斷功能),這時,讓其他CPU進行spin操作太浪費CPU時間了(bus操作太慢了,會spin很久的)。腫麼辦?當然隻能是用其他方法lock住bus了(例如mutex,具體實作是和irq chip中的irq_bus_lock實作相關)。一旦lock住了slow bus,然後就可以關閉中斷了(中斷狀态儲存在flag中)。
解決了bus lock的疑問後,還有一個看起來奇奇怪怪的宏:IRQ_GET_DESC_CHECK_GLOBAL。為何在irq_set_chip函數中不設定檢查(check的參數是0),而在irq_set_irq_type接口函數中要設定global的check,到底是什麼意思呢?既然要檢查,那麼檢查什麼呢?和“global”對應的不是local而是“per CPU”,核心中的宏定義是:IRQ_GET_DESC_CHECK_PERCPU。SMP情況下,從系統角度看,中斷有兩種形态(或者叫mode):
(1)1-N mode。隻有1個processor進行中斷
(2)N-N mode。所有的processor都是獨立的收到中斷,如果有N個processor收到中斷,那麼就有N個處理器來處理該中斷。
聽起來有些抽象,我們還是用GIC作為例子來具體描述。在GIC中,SPI使用1-N模型,而PPI和SGI使用N-N模型。對于SPI,由于采用了1-N模型,系統(硬體加上軟體)必須保證一個中斷被一個CPU處理。對于GIC,一個SPI的中斷可以trigger多個CPU的interrupt line(如果Distributor中的Interrupt Processor Targets Registers有多個bit被設定),但是,該interrupt source和CPU的接口寄存器(例如ack register)隻有一套,也就是說,這些寄存器接口是全局的,是global的,一旦一個CPU ack(讀Interrupt Acknowledge Register,擷取interrupt ID)了該中斷,那麼其他的CPU看到的該interupt source的狀态也是已經ack的狀态。在這種情況下,如果第二個CPU ack該中斷的時候,将擷取一個spurious interrupt ID。
對于PPI或者SGI,使用N-N mode,其interrupt source的寄存器是per CPU的,也就是每個CPU都有自己的、針對該interrupt source的寄存器接口(這些寄存器叫做banked register)。一個CPU 清除了該interrupt source的pending狀态,其他的CPU感覺不到這個變化,它們仍然認為該中斷是pending的。
對于irq_set_irq_type這個接口函數,它是for 1-N mode的interrupt source使用的。如果底層設定該interrupt是per CPU的,那麼irq_set_irq_type要傳回錯誤。
4、irq_set_chip_data
每個irq chip總有自己私有的資料,我們稱之chip data。具體設定chip data的代碼如下:
int irq_set_chip_data(unsigned int irq, void *data)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
desc->irq_data.chip_data = data;
irq_put_desc_unlock(desc, flags);
return 0;
}
多麼清晰、多麼明了,需要文字繼續描述嗎?
5、設定high level handler
這是中斷處理的核心内容,__irq_set_handler就是設定high level handler的接口函數,不過一般不會直接調用,而是通過irq_set_chip_and_handler_name或者irq_set_chip_and_handler來進行設定。具體代碼如下:
void __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, const char *name)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
……
desc->handle_irq = handle;
desc->name = name;
if (handle != handle_bad_irq && is_chained) {
irq_settings_set_noprobe(desc);
irq_settings_set_norequest(desc);
irq_settings_set_nothread(desc);
irq_startup(desc, true);
}
out:
irq_put_desc_busunlock(desc, flags);
}
了解這個函數的關鍵是在is_chained這個參數。這個參數是用在interrupt級聯的情況下。例如中斷控制器B級聯到中斷控制器A的第x個interrupt source上。那麼對于A上的x這個interrupt而言,在設定其IRQ handler參數的時候要設定is_chained參數等于1,由于這個interrupt source用于級聯,是以不能probe、不能被request(已經被中斷控制器B使用了),不能被threaded(具體中斷線程化的概念在其他文檔中描述)傳回搜狐,檢視更多
責任編輯: