溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Linux中斷子系統(tǒng)domain的示例分析

發(fā)布時間:2022-02-18 10:13:24 來源:億速云 閱讀:182 作者:小新 欄目:開發(fā)技術(shù)

這篇文章主要介紹Linux中斷子系統(tǒng)domain的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

隨著現(xiàn)代CPU的復雜度加大,外設(shè)中斷數(shù)量增加,實際上系統(tǒng)可能同時需要多個中斷控制器進行級聯(lián),面對這樣的趨勢,Linux引入了irq domain這個概念。

對于Linux系統(tǒng)中所有的interrupt controller都會形成樹狀結(jié)構(gòu),對于每個interrupt controller都可以連接若干個外設(shè)的中斷請求,interrupt controller會對連接其上的interrupt source進行編號(也就是HW interrupt ID,后面我們叫hirq),但這個編號僅僅限制在本interrupt controller范圍內(nèi)。既然存在hirq,那么在系統(tǒng)軟件中, 就存在一個全局統(tǒng)管的編號,這個編號可以定位控制器和控制器中的中斷號,我們把這個編號叫做virq(我們在Linux系統(tǒng)中調(diào)用API request_irq中需要的irq號 實際上是virq號)。對于中斷級聯(lián),我們以TI的TiTDA系列為例,如下圖:

Linux中斷子系統(tǒng)domain的示例分析
Linux中斷子系統(tǒng)domain詳解Linux中斷子系統(tǒng)domain詳解

如上圖,該CPU是直接連接在中斷控制器GICv3上面的,但是GIC不是直接和設(shè)備相連,而是與INTR控制器連接,而INTR控制器又與INTA控制器連接,最后INTA控制器才直接和設(shè)備相連。在這里其實就簡單的形成了一個簡單的樹形結(jié)構(gòu)。

?

domain里面的hirq排列可以是樹形的也可以是線性的,本文主要說樹形的

咋們先看看domain的結(jié)構(gòu)(一個中斷控制器就可以看成是一個domain):

struct irq_domain {
   struct list_head link;
   const char *name;    // domain的名字
   const struct irq_domain_ops *ops; // 當前domain的處理函數(shù)
   void *host_data;   // 存放私有數(shù)據(jù)
   unsigned int flags;
   unsigned int mapcount;

   /* Optional data */
   struct fwnode_handle *fwnode;  // 和設(shè)備樹有關(guān)系
   enum irq_domain_bus_token bus_token;  // bus標記,用于匹配domain
   struct irq_domain_chip_generic *gc;#ifdef    CONFIG_IRQ_DOMAIN_HIERARCHY   struct irq_domain *parent;          // 樹形結(jié)構(gòu)的父節(jié)點domain#endif   irq_hw_number_t hwirq_max;    // hwirq樹形(radix tree)支持的最大節(jié)點數(shù)量
   unsigned int revmap_direct_max_irq; // hwirq和virq 1:1映射支持的最大數(shù)量
   unsigned int revmap_size;     // 線性映射支持的最大數(shù)量
   struct radix_tree_root revmap_tree;  // radix tree的樹根
   struct mutex revmap_tree_mutex;
   unsigned int linear_revmap[];       // 線性映射的數(shù)組
}

如上圖可知,domain的結(jié)構(gòu)里面基本都是關(guān)于hirq相關(guān)信息的,因此,我們可以推斷domain就是用于管理hirq的(每個domain都有自己的一套hirq),而domain中的ops字段就是用于操作這些hirq的, 下面是domain的操作函數(shù)ops的結(jié)構(gòu):

struct irq_domain_ops {
   int (*match)(struct irq_domain *d, struct device_node *node,
        enum irq_domain_bus_token bus_token); // 用于匹配domain,優(yōu)先級低于selec
   int (*select)(struct irq_domain *d, struct irq_fwspec *fwspec,
        enum irq_domain_bus_token bus_token); // 用于匹配domain,優(yōu)先級高于match
   int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw); // 線性映射
   void (*unmap)(struct irq_domain *d, unsigned int virq);
   int (*xlate)(struct irq_domain *d, struct device_node *node, // 通過參數(shù),線性hirq獲取
        const u32 *intspec, unsigned int intsize,
        unsigned long *out_hwirq, unsigned int *out_type);#ifdef    CONFIG_IRQ_DOMAIN_HIERARCHY   /* extended V2 interfaces to support hierarchy irq_domains */
   int (*alloc)(struct irq_domain *d, unsigned int virq,//樹形節(jié)點申請,映射(只有樹形才有這個)
        unsigned int nr_irqs, void *arg);
   void (*free)(struct irq_domain *d, unsigned int virq,
        unsigned int nr_irqs);
   int (*activate)(struct irq_domain *d, struct irq_data *irqd, bool reserve); // 中斷激活
   void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data);
   int (*translate)(struct irq_domain *d, struct irq_fwspec *fwspec, //通過參數(shù)對樹形hirq獲取
            unsigned long *out_hwirq, unsigned int *out_type);#endif}

如上圖,對于domain的hirq,我們可以看到結(jié)構(gòu)里面有查詢分配hirq,以及hirq與virq的映射的函數(shù)?,F(xiàn)在,我們已經(jīng)了解了domain的數(shù)據(jù)結(jié)構(gòu),下面是一個中斷控制器注冊一個domain的流程:

irq_domain_add_hierarchy
       irq_domain_create_tree
            __irq_domain_add
                 list_add(&domain->link, &irq_domain_list)

可見最終所有的domain都添加到irq_domain_list鏈表當中的。但是我們知道,我們調(diào)用request_irq用的是virq號,而并不是hirq,因為hirq號不是唯一的,每個domain都可能會存在一個相同的hirq,因此Linux才增加的virq編號,這個編號是獨立的且唯一?,F(xiàn)在domain已經(jīng)注冊好了,但是hirq和virq直接的關(guān)系還沒有建立起來,因此我們還不能用virq來注冊中斷,如下是把hirq和virq做一個映射函數(shù)調(diào)用:

irq_create_fwspec_mapping(傳入domain和參數(shù))
   irq_find_matching_fwspec
       domain->ops->match // 查看當前domain和參數(shù)指定的domain是否匹配
   irq_domain_translate
       domain->ops->translate // 通過參數(shù)申請一個hirq號
   irq_find_mapping // 通過hirq去查詢,這個hirq是否已經(jīng)映射,如果已經(jīng)映射,就返回virq
   irq_domain_alloc_irqs // hirq沒有映射,通過該函數(shù)去申請一個virq并映射
       __irq_domain_alloc_irqs
           irq_domain_alloc_descs // 申請一個irq_desc描述
           irq_domain_alloc_irq_data // 為virq設(shè)置irq_data樹,該virq會為它所在的domain的父domain,一直到root domain添加irq_data
           irq_domain_alloc_irqs_hierarchy // 向domain申請hirq,并和virq做映射
               domain->ops->alloc
                   irq_domain_set_hwirq_and_chip // 將hirq與virq的irq_data關(guān)聯(lián)
           irq_domain_insert_irq//將virq信息插入到每一級irq_data里面(一個domain對應一個irq_data)

如上圖,當驅(qū)動調(diào)用irq_create_fwspec_mapping函數(shù)的時候,該函數(shù)會通過參數(shù)在指定的domain里面的申請hirq,然后申請一個virq,并將virq和hirq關(guān)聯(lián)起來。下圖是,irq_data的結(jié)構(gòu)圖: Linux中斷子系統(tǒng)domain的示例分析

如上圖所示,在domain里面利用linear_revmap(線性)或者revmap_tree(radix tree)來保存irq_data結(jié)構(gòu),在接收到中斷的時候,我們只知道hirq號和對應的domain,因此可以以hirq為key值從revmap_tree或者linear_revmap當中獲取到irq_data數(shù)據(jù)結(jié)構(gòu);當我們調(diào)用request_irq的時候,只填入了virq,因此也可以通過virq做為key在irq_desc_tree或者irq_desc[]數(shù)組中獲取到irq_desc描述,從而獲取到這個描述的irq_data。irq_data包含irq_chip(中斷控制屏蔽掩碼,設(shè)置觸發(fā)方式等函數(shù))、virq(這個值和該irq_data的父節(jié)點parent_data(irq_data)的virq的值相同),hwirq(每個irq_data的hwirq是不同的,對應不同的domain內(nèi)部編號)、domain(指向當前irq_data所在的domain)、chip_data(指向當前irq_data所在domain的私有數(shù)據(jù))、parent_data(指向irq_data的父節(jié)點,這個父節(jié)點存放父domain相關(guān)信息)。

?

linear_revmap和revmap_tree是domain結(jié)構(gòu)里面的變量,每個domain都有,用于保存hirq與irq_data的對應關(guān)系;irq_desc_tree和irq_desc[]是全局變量,用于保存irq_desc結(jié)構(gòu)的鏈表或者樹。

通常在ARMv8體系結(jié)構(gòu)中,中斷處理的流程,如下圖:

Linux中斷子系統(tǒng)domain的示例分析

如上圖,我們這個處理流程是el1模式的,在el1模式的時候,匯編級別的共用中斷函數(shù)為el1_irq。圖中我們可以看見幾個箭頭指示的點,1、handle_arch_irq;2、desc->handle_irq;3、irqaction->handler。我們知道,通常ARM體系結(jié)構(gòu)的CPU都和GIC中斷控制器相連,詳細《ARM中斷控制器-GICv2》,因此,handle_arch_irq這個全局函數(shù)指針,是有通用的中斷控制器GIC驅(qū)動設(shè)置的(GIC驅(qū)動調(diào)用set_handle_irq函數(shù)設(shè)置),這樣CPU上的所有異常中斷都經(jīng)過GIC的回調(diào)函數(shù)處理。GIC的這個回調(diào)函數(shù)gic_handle_irq(就是handle_arch_irq),負責mask中斷和eoi中斷(eoi就是ack控制器)。在這個回調(diào)函數(shù)里面,我們可以通過寄存器值獲取到當前控制的那個中斷號(該domain的hirq)觸發(fā)了中斷,然后通過hirq可以獲取到對應的virq,從而獲取到irq_desc描述,最后調(diào)用irq_desc描述的handle_irq來處理該中斷。由圖中,我們還看到handle_irq這個函數(shù)其實也是注冊的,但是為了方便統(tǒng)一,這個回調(diào)通常都是gic驅(qū)動為每個中斷注冊一個統(tǒng)一的回調(diào)函數(shù)(handle_fasteoi_irq);handle_fasteoi_irq這個函數(shù)支持中斷共享,然后依次調(diào)用掛在irq_desc下面的irqaction結(jié)構(gòu)中的handle函數(shù)(玩家每調(diào)用依次request_irq就會在對應的virq上增加一個irqaction)。

?

irq_desc是一個virq對應的描述結(jié)構(gòu)體,每個virq對應一個該結(jié)構(gòu)。在irq_desc結(jié)構(gòu)體里面有irq_data和irqaction變量,一個用于存放與當前virq映射的hirq信息,另一個用于存放該virq的中斷回調(diào)處理函數(shù)。nr_irqs全局變量保存當前支持最大的中斷數(shù)(可以通過irq_expand_nr_irqs函數(shù)擴展)


以上是“Linux中斷子系統(tǒng)domain的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI