<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Linux中斷子系統(tǒng)-通用框架處理

          共 5254字,需瀏覽 11分鐘

           ·

          2020-09-17 19:22

          背景

          1. Kernel版本:4.14
          2. ARM64處理器,Contex-A53,雙核
          3. 使用工具:Source Insight 3.5, Visio

          1. 概述

          《Linux中斷子系統(tǒng)(一)-中斷控制器及驅(qū)動分析》講到了底層硬件GIC驅(qū)動,以及Arch-Specific的中斷代碼,本文將研究下通用的中斷處理的過程,屬于硬件無關(guān)層。當(dāng)然,我還是建議你看一下上篇文章。

          這篇文章會解答兩個問題:

          1. 用戶是怎么使用中斷的(中斷注冊)?
          2. 外設(shè)觸發(fā)中斷信號時,最終是怎么調(diào)用到中斷handler的(中斷處理)?

          2. 數(shù)據(jù)結(jié)構(gòu)分析

          先來看一下總的數(shù)據(jù)結(jié)構(gòu),核心是圍繞著struct irq_desc來展開:


          • Linux內(nèi)核的中斷處理,圍繞著中斷描述符結(jié)構(gòu)struct irq_desc展開,內(nèi)核提供了兩種中斷描述符組織形式:

            1. 打開CONFIG_SPARSE_IRQ宏(中斷編號不連續(xù)),中斷描述符以radix-tree來組織,用戶在初始化時進(jìn)行動態(tài)分配,然后再插入radix-tree中;
            2. 關(guān)閉CONFIG_SPARSE_IRQ宏(中斷編號連續(xù)),中斷描述符以數(shù)組的形式組織,并且已經(jīng)分配好;
            3. 不管哪種形式,都可以通過linux irq號來找到對應(yīng)的中斷描述符;
          • 圖的左側(cè)灰色部分,主要在中斷控制器驅(qū)動中進(jìn)行初始化設(shè)置,包括各個結(jié)構(gòu)中函數(shù)指針的指向等,其中struct irq_chip用于對中斷控制器的硬件操作,struct irq_domain與中斷控制器對應(yīng),完成的工作是硬件中斷號到Linux irq的映射;

          • 圖的上側(cè)灰色部分,中斷描述符的創(chuàng)建(這里指CONFIG_SPARSE_IRQ),主要在獲取設(shè)備中斷信息的過程中完成的,從而讓設(shè)備樹中的中斷能與具體的中斷描述符irq_desc匹配;

          • 圖中剩余部分,在設(shè)備申請注冊中斷的過程中進(jìn)行設(shè)置,比如struct irqactionhandler的設(shè)置,這個用于指向我們設(shè)備驅(qū)動程序中的中斷處理函數(shù)了;

          中斷的處理主要有以下幾個功能模塊:

          1. 硬件中斷號到Linux irq中斷號的映射,并創(chuàng)建好irq_desc中斷描述符;
          2. 中斷注冊時,先獲取設(shè)備的中斷號,根據(jù)中斷號找到對應(yīng)的irq_desc,并將設(shè)備的中斷處理函數(shù)添加到irq_desc中;
          3. 設(shè)備觸發(fā)中斷信號時,根據(jù)硬件中斷號得到Linux irq中斷號,找到對應(yīng)的irq_desc,最終調(diào)用到設(shè)備的中斷處理函數(shù);

          上述的描述比較簡單,更詳細(xì)的過程,往下看吧。

          3. 流程分析

          3.1 中斷注冊

          這一次,讓我們以問題的方式來展開:先來讓我們回答第一個問題:用戶是怎么使用中斷的?

          1. 熟悉設(shè)備驅(qū)動的同學(xué)應(yīng)該都清楚,經(jīng)常會在驅(qū)動程序中調(diào)用request_irq()接口或者request_threaded_irq()接口來注冊設(shè)備的中斷處理函數(shù);
          2. request_irq()/request_threaded_irq接口中,都需要用到irq,也就是中斷號,那么這個中斷號是從哪里來的呢?它是Linux irq,它又是如何映射到具體的硬件設(shè)備的中斷號的呢?

          先來看第二個問題:設(shè)備硬件中斷號到Linux irq中斷號的映射


          • 硬件設(shè)備的中斷信息都在設(shè)備樹device tree中進(jìn)行了描述,在系統(tǒng)啟動過程中,這些信息都已經(jīng)加載到內(nèi)存中并得到了解析;
          • 驅(qū)動中通常會使用platform_get_irqirq_of_parse_and_map接口,去根據(jù)設(shè)備樹的信息去創(chuàng)建映射關(guān)系(硬件中斷號到linux irq中斷號映射);
          • 《Linux中斷子系統(tǒng)(一)-中斷控制器及驅(qū)動分析》提到過struct irq_domain用于完成映射工作,因此在irq_create_fwspec_mapping接口中,會先去找到匹配的irq domain,再去回調(diào)該irq domain中的函數(shù)集,通常irq domain都是在中斷控制器驅(qū)動中初始化的,以ARM GICv2為例,最終回調(diào)到gic_irq_domain_hierarchy_ops中的函數(shù);
          • 如果已經(jīng)創(chuàng)建好了映射,那么可以直接進(jìn)行返回linux irq中斷號了,否則的話需要irq_domain_alloc_irqs來創(chuàng)建映射關(guān)系;
          • irq_domain_alloc_irqs完成兩個工作:
            1. 針對linux irq中斷號創(chuàng)建一個irq_desc中斷描述符;
            2. 調(diào)用domain->ops->alloc函數(shù)來完成映射,在ARM GICv2驅(qū)動中對應(yīng)gic_irq_domain_alloc函數(shù),這個函數(shù)很關(guān)鍵,所以下文介紹一下;

          gic_irq_domain_alloc函數(shù)如下:


          • gic_irq_domain_translate:負(fù)責(zé)解析出設(shè)備樹中描述的中斷號和中斷觸發(fā)類型(邊緣觸發(fā)、電平觸發(fā)等);
          • gic_irq_domain_map:將硬件中斷號和linux中斷號綁定到一個結(jié)構(gòu)中,也就完成了映射,此外還綁定了irq_desc結(jié)構(gòu)中的其他字段,最重要的是設(shè)置了irq_desc->handle_irq的函數(shù)指針,這個最終是中斷響應(yīng)時往上執(zhí)行的入口,這個是關(guān)鍵,下文講述中斷處理過程時還會提到;
          • 根據(jù)硬件中斷號的范圍設(shè)置irq_desc->handle_irq的指針,共享中斷入口為handle_fasteoi_irq,私有中斷入口為handle_percpu_devid_irq;

          上述函數(shù)執(zhí)行完成后,完成了兩大工作:

          1. 硬件中斷號與Linux中斷號完成映射,并為Linux中斷號創(chuàng)建了irq_desc中斷描述符;
          2. 數(shù)據(jù)結(jié)構(gòu)的綁定及初始化,關(guān)鍵的地方是設(shè)置了中斷處理往上執(zhí)行的入口;

          再看第一個問題:中斷是怎么來注冊的?

          設(shè)備驅(qū)動中,獲取到了irq中斷號后,通常就會采用request_irq/request_threaded_irq來注冊中斷,其中request_irq用于注冊普通處理的中斷,request_threaded_irq用于注冊線程化處理的中斷;

          在講具體的注冊流程前,先看一下主要的中斷標(biāo)志位:

          #define IRQF_SHARED		0x00000080              //多個設(shè)備共享一個中斷號,需要外設(shè)硬件支持#define IRQF_PROBE_SHARED	0x00000100              //中斷處理程序允許sharing mismatch發(fā)生#define __IRQF_TIMER		0x00000200               //時鐘中斷#define IRQF_PERCPU		0x00000400               //屬于特定CPU的中斷#define IRQF_NOBALANCING	0x00000800               //禁止在CPU之間進(jìn)行中斷均衡處理#define IRQF_IRQPOLL		0x00001000              //中斷被用作輪訓(xùn)#define IRQF_ONESHOT		0x00002000              //一次性觸發(fā)的中斷,不能嵌套,1)在硬件中斷處理完成后才能打開中斷;2)在中斷線程化中保持關(guān)閉狀態(tài),直到該中斷源上的所有thread_fn函數(shù)都執(zhí)行完#define IRQF_NO_SUSPEND		0x00004000      //系統(tǒng)休眠喚醒操作中,不關(guān)閉該中斷#define IRQF_FORCE_RESUME	0x00008000              //系統(tǒng)喚醒過程中必須強(qiáng)制打開該中斷#define IRQF_NO_THREAD		0x00010000      //禁止中斷線程化#define IRQF_EARLY_RESUME	0x00020000      //系統(tǒng)喚醒過程中在syscore階段resume,而不用等到設(shè)備resume階段#define IRQF_COND_SUSPEND	0x00040000      //與NO_SUSPEND的用戶共享中斷時,執(zhí)行本設(shè)備的中斷處理函數(shù)


          • request_irq也是調(diào)用request_threaded_irq,只是在傳參的時候,線程處理函數(shù)thread_fn函數(shù)設(shè)置成NULL;
          • 由于在硬件中斷號和Linux中斷號完成映射后,irq_desc已經(jīng)創(chuàng)建好,可以通過irq_to_desc接口去獲取對應(yīng)的irq_desc;
          • 創(chuàng)建irqaction,并初始化該結(jié)構(gòu)體中的各個字段,其中包括傳入的中斷處理函數(shù)賦值給對應(yīng)的字段;
          • __setup_irq用于完成中斷的相關(guān)設(shè)置,包括中斷線程化的處理:
            1. 中斷線程化用于減少系統(tǒng)關(guān)中斷的時間,增強(qiáng)系統(tǒng)的實時性;
            2. ARM64默認(rèn)開啟了CONFIG_IRQ_FORCED_THREADING,引導(dǎo)參數(shù)傳入threadirqs時,則除了IRQF_NO_THREAD外的中斷,其他的都將強(qiáng)制線程化處理;
            3. 中斷線程化會為每個中斷都創(chuàng)建一個內(nèi)核線程,如果中斷進(jìn)行共享,對應(yīng)irqaction將連接成鏈表,每個irqaction都有thread_mask位圖字段,當(dāng)所有共享中斷都處理完成后才能unmask中斷,解除中斷屏蔽;

          3.2 中斷處理

          當(dāng)完成中斷的注冊后,所有結(jié)構(gòu)的組織關(guān)系都已經(jīng)建立好,剩下的工作就是當(dāng)信號來臨時,進(jìn)行中斷的處理工作。

          來回顧一下《Linux中斷子系統(tǒng)(一)-中斷控制器及驅(qū)動分析》中的Arch-specific處理流程:


          • 中斷收到之后,首先會跳轉(zhuǎn)到異常向量表的入口處,進(jìn)而逐級進(jìn)行回調(diào)處理,最終調(diào)用到generic_handle_irq來進(jìn)行中斷處理。

          generic_handle_irq處理如下圖:


          • generic_handle_irq函數(shù)最終會調(diào)用到desc->handle_irq(),這個也就是對應(yīng)到上文中在建立映射關(guān)系的過程中,調(diào)用irq_domain_set_info函數(shù),設(shè)置好了函數(shù)指針,也就是handle_fasteoi_irqhandle_percpu_devid_irq;
          • handle_fasteoi_irq:處理共享中斷,并且遍歷irqaction鏈表,逐個調(diào)用action->handler()函數(shù),這個函數(shù)正是設(shè)備驅(qū)動程序調(diào)用request_irq/request_threaded_irq接口注冊的中斷處理函數(shù),此外如果中斷線程化處理的話,還會調(diào)用__irq_wake_thread()喚醒內(nèi)核線程;
          • handle_percpu_devid_irq:處理per-CPU中斷處理,在這個過程中會分別調(diào)用中斷控制器的處理函數(shù)進(jìn)行硬件操作,該函數(shù)調(diào)用action->handler()來進(jìn)行中斷處理;

          來看看中斷線程化處理后的喚醒流程吧__handle_irq_event_percpu->__irq_wake_thread


          • __handle_irq_event_percpu->__irq_wake_thread將喚醒irq_thread中斷內(nèi)核線程;
          • irq_thread內(nèi)核線程,將根據(jù)是否為強(qiáng)制中斷線程化對函數(shù)指針handler_fn進(jìn)行初始化,以便后續(xù)進(jìn)行調(diào)用;
          • irq_thread內(nèi)核線程將while(!irq_wait_for_interrupt)循環(huán)進(jìn)行中斷的處理,當(dāng)滿足條件時,執(zhí)行handler_fn,在該函數(shù)中最終調(diào)用action->thread_fn,也就是完成了中斷的處理;
          • irq_wait_for_interrupt函數(shù),將會判斷中斷線程的喚醒條件,如果滿足了,則將當(dāng)前任務(wù)設(shè)置成TASK_RUNNING狀態(tài),并返回0,這樣就能執(zhí)行中斷的處理,否則就調(diào)用schedule()進(jìn)行調(diào)度,讓出CPU,并將任務(wù)設(shè)置成TASK_INTERRUPTIBLE可中斷睡眠狀態(tài);

          3.3 總結(jié)

          中斷的處理,總體來說可以分為兩部分來看:

          1. 從上到下:圍繞irq_desc中斷描述符建立好連接關(guān)系,這個過程就包括:中斷源信息的解析(設(shè)備樹),硬件中斷號到Linux中斷號的映射關(guān)系、irq_desc結(jié)構(gòu)的分配及初始化(內(nèi)部各個結(jié)構(gòu)的組織關(guān)系)、中斷的注冊(填充irq_desc結(jié)構(gòu),包括handler處理函數(shù))等,總而言之,就是完成靜態(tài)關(guān)系創(chuàng)建,為中斷處理做好準(zhǔn)備;
          2. 從下到上,當(dāng)外設(shè)觸發(fā)中斷信號時,中斷控制器接收到信號并發(fā)送到處理器,此時處理器進(jìn)行異常模式切換,并逐步從處理器架構(gòu)相關(guān)代碼逐級回調(diào)。如果涉及到中斷線程化,則還需要進(jìn)行中斷內(nèi)核線程的喚醒操作,最終完成中斷處理函數(shù)的執(zhí)行。
          ? ?

          ? ?推薦閱讀:
          ? ??專輯|Linux文章匯總
          ? ??專輯|程序人生
          ? ??專輯|C語言


          嵌入式Linux
          微信掃描二維碼,關(guān)注我的公眾號?


          瀏覽 53
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩欧美中文在线观看 | 成人免费一级毛片在线播放视频 | 久久影院网红无码视频牛牛夜夜骚 | 在线亚洲免费观看 | 在线播放神尾舞视频 |