irq domain

本文記錄關(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;
}

參考資料

1.http://www.wowotech.net/linux_kenrel/irq-domain.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容