本文記錄關(guān)于irq domain的學(xué)習(xí)筆記。
irq domain是什么?
我的理解,irq domain主要是完成從硬件irq no到軟件irq no的轉(zhuǎn)換的。
因為硬件設(shè)備中存在多個級聯(lián)的irq控制器,而每個irq控制器都有自己的irq no,當(dāng)然這個是純硬件的編碼。完全存在多個irq控制器中的irq no都是相同的情況。
linux內(nèi)核為了方便管理,需要在全系統(tǒng)提供一個irq no,它能唯一地描述唯一的某個硬件中斷。這個irq no是個純軟件虛擬出來的。
對于中斷控制器來說,它知道自己有多少個硬件中斷,因此它需要向系統(tǒng)申請響應(yīng)的軟件中斷號,然后把這個硬中斷號到軟中斷號的映射存儲下來,方便后面查閱。
這就是irq domain的作用。
映射關(guān)系有哪些?以及如何存儲?
線性映射
其實,這里說的線性不一定是我們平常認(rèn)為的線性,它只是說存儲映射關(guān)系使用的是數(shù)組,看起來像是線性的。
hw irq no作為數(shù)組的index,而value就是virt irq no(hw irq no指硬件中斷號,virt irq no指軟件中斷號)。
這樣的存儲結(jié)構(gòu)對hw irq no是有要求的。
比如hw irq no不能太大,舉例來說,如果只有兩個中斷,但hw irq no是100和101,難道我們要申請一個數(shù)組大小為102,但是只使用array[100]和array[101],那就太浪費了。
當(dāng)然,hw irq no還需要比較緊密,距離來說,還是只有兩個中斷,分別是1和20,那就需要申請一個數(shù)組大小為21,但是只使用array[1]和array[20],這樣也非常浪費。
Radix Tree map
建立一個Radix Tree來維護(hù)HW interrupt ID到IRQ number映射關(guān)系。HW interrupt ID作為lookup key,在Radix Tree檢索到IRQ number。如果的確不能滿足線性映射的條件,可以考慮Radix Tree map。
no map
有些中斷控制器很強(qiáng),可以通過寄存器配置HW interrupt ID而不是由物理連接決定的。
例如PowerPC 系統(tǒng)使用的MPIC (Multi-Processor Interrupt Controller)。在這種情況下,不需要進(jìn)行映射,我們直接把IRQ number寫入HW interrupt ID配置寄存器就OK了,這時候,生成的HW interrupt ID就是IRQ number,也就不需要進(jìn)行mapping了。
存儲使用的數(shù)據(jù)結(jié)構(gòu)
來看看相關(guān)的數(shù)據(jù)結(jié)構(gòu)體內(nèi)容
struct irq_domain {
......
/* 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[];
};
有個小疑問:irq domain總是把hw no轉(zhuǎn)成virt no,而不做相反的操作。為什么呢?它似乎總是占用hw的角度來處理。
hwirq_max:代表這個domain里最大的hw irq no;
revmap_direct_max_irq:是no map那種映射的最大irq no;
revmap_size和linear_revmap:線性映射用的,revmap_size代表數(shù)組的大小;
revmap_tree:radix tree map使用的,代表的是radix tree的root node
對于線性映射
hwirq_max:一般情況下等于revmap_size?
revmap_direct_max_irq:0
revmap_size:下面的數(shù)組大小
revmap_tree:null
linear_revmap[]:有意義
對于radix tree map
hwirq_max:有意義
revmap_direct_max_irq:0
revmap_size:0
revmap_tree:執(zhí)行root node
linear_revmap[]:null
下面就要來看個函數(shù),來加深對上面數(shù)據(jù)結(jié)構(gòu)的理解
unsigned int irq_find_mapping(struct irq_domain *domain,
irq_hw_number_t hwirq)
{
struct irq_data *data;
// 1st, check no map
if (hwirq < domain->revmap_direct_max_irq) {
data = irq_domain_get_irq_data(domain, hwirq);
if (data && data->hwirq == hwirq)
return hwirq;
}
// 2nd, check linear map
if (hwirq < domain->revmap_size)
return domain->linear_revmap[hwirq];
// 3rd, check radix tree map
rcu_read_lock();
data = radix_tree_lookup(&domain->revmap_tree, hwirq);
rcu_read_unlock();
return data ? data->irq : 0;
}
這個函數(shù)完成在irq domain中,根據(jù)hw irq查詢virt irq的過程。
如代碼注釋,三種映射關(guān)系都考慮了,但是有不同優(yōu)先級,先是no map,再是線性映射,最后是radix tree。
管理irq domain
系統(tǒng)中維護(hù)一個list保存所有的irq domain,對它的讀寫受irq_domain_mutex的保護(hù)。
每個結(jié)構(gòu)體中有個link,可以將自己掛入到全局list中。
static LIST_HEAD(irq_domain_list);
static DEFINE_MUTEX(irq_domain_mutex);
struct irq_domain {
struct list_head link;
......
};
創(chuàng)建irq domain
創(chuàng)建的入口只有這一個,特別注意動態(tài)申請的空間大小,包含了線性映射需要的數(shù)組空間。當(dāng)然,如果不是線性映射,這里的size就是0,也不會額外申請。
由于使用的是kzalloc,所以物理地址是連續(xù)的。irq domain訪問數(shù)組時,數(shù)組雖然不在它的結(jié)構(gòu)體內(nèi),但是物理上連續(xù),可以直接訪問。而size則幫忙指定邊界,不要越界訪問。
struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
irq_hw_number_t hwirq_max, int direct_max,
const struct irq_domain_ops *ops,
void *host_data)
{
struct irq_domain *domain;
// 特別注意這里申請的空間,包含了線性映射需要的數(shù)組空間
domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
GFP_KERNEL, of_node_to_nid(of_node));
// 填充size并初始化radix tree
INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
domain->hwirq_max = hwirq_max;
domain->revmap_size = size;
domain->revmap_direct_max_irq = direct_max;
// 掛入全局list
mutex_lock(&irq_domain_mutex);
list_add(&domain->link, &irq_domain_list);
mutex_unlock(&irq_domain_mutex);
return domain;
}