日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

ARM GIC简介与Linux中断处理分析

發布時間:2024/10/12 linux 65 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ARM GIC简介与Linux中断处理分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

先簡單說明一下GIC(具體詳盡的介紹請查閱ARM GIC相關文檔)

GIC即general interrupt controller。

它是一個架構,版本歷經了GICv1(已棄用),GICv2,GICv3,GICv4。對于不同的GIC版本,arm公司設計了對應的GIC IP

GIC的核心功能:對soc中外設的中斷源的管理,并且提供給軟件,配置以及控制這些中斷源。

下面一張ARM GICv2 的圖

中斷源類型說明:

SGI(Software-generated interrupt):范圍0 - 15,軟件觸發的中斷,一般用于核間通訊

PPI(Private peripheral interrupt ): 范圍16 - 31,私有外設中斷,只對指定的core有效

SPI(Shared peripheral interrupt):范圍32 - 1019,共享中斷,不限定特定的core

控制分兩部分,Distributor和CPU interface

Distributor對中斷的控制包括:
(1)中斷enable或者disable的控制
(2)將當前優先級最高的中斷事件分發到一個或者一組CPU interface
(3)優先級控制
(4)interrupt屬性設定。例如是level-sensitive還是edge-triggered
(5)interrupt group的設定


CPU interface:將GICD發送的中斷信息,通過IRQ,FIQ管腳,傳輸給core

GIC對中斷的處理包括以下4種狀態:

  • inactive:中斷處于無效狀態

  • pending:中斷處于有效狀態,但是cpu沒有響應該中斷

  • active:cpu在響應該中斷

  • active and pending:cpu在響應該中斷,但是該中斷源又發送中斷過來

其轉換過程如下:

發生中斷信號走向:

至此,關于GIC相關的介紹基本可以滿足閱讀Linux內核關于中斷處理的相關code

以下源代碼基于ssd20x官方kernel為例,cortexA7,ARMv7架構,32位

中斷向量入口位于:arch/arm/kernel/entry-armv.S

/** Interrupt handling.*/.macro irq_handler #ifdef CONFIG_MULTI_IRQ_HANDLERldr r1, =handle_arch_irqmov r0, spbadr lr, 9997fldr pc, [r1] #elsearch_irq_handler_default #endif

關于宏CONFIG_MULTI_IRQ_HANDLER,表示"允許每臺機器在運行時指定它自己的IRQ處理程序",因為kernel一般支持多種平臺,多種處理器,所以此選項一般開啟

進入irq_handler有兩種情況

一個是用戶模式下發生了中斷

.align 5 __irq_usr:usr_entrykuser_cmpxchg_checkirq_handlerget_thread_info tskmov why, #0b ret_to_user_from_irqUNWIND(.fnend ) ENDPROC(__irq_usr)

?另一個是內核態時發生了中斷

.align 5 __irq_svc:svc_entryirq_handler#ifdef CONFIG_PREEMPTldr r8, [tsk, #TI_PREEMPT] @ get preempt countldr r0, [tsk, #TI_FLAGS] @ get flagsteq r8, #0 @ if preempt count != 0movne r0, #0 @ force flags to 0tst r0, #_TIF_NEED_RESCHEDblne svc_preempt #endifsvc_exit r5, irq = 1 @ return from exceptionUNWIND(.fnend ) ENDPROC(__irq_svc)

在irq_handler中handle_arch_irq即為動態指定的中斷處理函數

位于arch/arm/kernel/irq.c

#ifdef CONFIG_MULTI_IRQ_HANDLER void __init set_handle_irq(void (*handle_irq)(struct pt_regs *)) {if (handle_arch_irq)return;handle_arch_irq = handle_irq; } #endif

在gic初始化drivers/irqchip/irq-gic.c過程進行設置set_handle_irq(gic_handle_irq);

static int __init __gic_init_bases(struct gic_chip_data *gic,int irq_start,struct fwnode_handle *handle) {char *name;int i, ret;if (WARN_ON(!gic || gic->domain))return -EINVAL;if (gic == &gic_data[0]) {/** Initialize the CPU interface map to all CPUs.* It will be refined as each CPU probes its ID.* This is only necessary for the primary GIC.*/for (i = 0; i < NR_GIC_CPU_IF; i++)gic_cpu_map[i] = 0xff; #ifdef CONFIG_SMPset_smp_cross_call(gic_raise_softirq); #endifcpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING,"AP_IRQ_GIC_STARTING",gic_starting_cpu, NULL);-----初始化過程中注冊具體的處理函數set_handle_irq(gic_handle_irq);if (static_key_true(&supports_deactivate))pr_info("GIC: Using split EOI/Deactivate mode\n");}if (static_key_true(&supports_deactivate) && gic == &gic_data[0]) {name = kasprintf(GFP_KERNEL, "GICv2");gic_init_chip(gic, NULL, name, true);} else {name = kasprintf(GFP_KERNEL, "GIC-%d", (int)(gic-&gic_data[0]));gic_init_chip(gic, NULL, name, false);}ret = gic_init_bases(gic, irq_start, handle);if (ret)kfree(name);return ret; }

?函數gic_handle_irq即為具體的中斷處理函數,此時中斷號是被GIC屏蔽的

static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
?? ?u32 irqstat, irqnr;
?? ?struct gic_chip_data *gic = &gic_data[0];
?? ?void __iomem *cpu_base = gic_data_cpu_base(gic);
?????? ?

?? ?do {

-----從寄存器讀取硬件中斷號
?? ??? ?irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
?? ??? ?irqnr = irqstat & GICC_IAR_INT_ID_MASK;
?????? ?
#if defined(CONFIG_MP_IRQ_TRACE)
???????????? ms_records_irq_count(irqnr);
#endif

-----處理PPI 及 SPI中斷
?? ??? ?if (likely(irqnr > 15 && irqnr < 1020)) {
?? ??? ??? ?if (static_key_true(&supports_deactivate))
?? ??? ??? ??? ?writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
?? ??? ??? ?handle_domain_irq(gic->domain, irqnr, regs);
?? ??? ??? ?continue;
?? ??? ?}

------處理SGI中斷
?? ??? ?if (irqnr < 16) {
?? ??? ??? ?writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
?? ??? ??? ?if (static_key_true(&supports_deactivate))
?? ??? ??? ??? ?writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);
#ifdef CONFIG_SMP
?? ??? ??? ?/*
?? ??? ??? ? * Ensure any shared data written by the CPU sending
?? ??? ??? ? * the IPI is read after we've read the ACK register
?? ??? ??? ? * on the GIC.
?? ??? ??? ? *
?? ??? ??? ? * Pairs with the write barrier in gic_raise_softirq
?? ??? ??? ? */

-----內存屏障
?? ??? ??? ?smp_rmb();

-----進入處理
?? ??? ??? ?handle_IPI(irqnr, regs);
。。。。。
?? ??? ??? ?continue;
?? ??? ?}
?? ??? ?break;
?? ?} while (1);
}

?handle_domain_irq的實現如下:

#ifdef CONFIG_HANDLE_DOMAIN_IRQ
/**
?* __handle_domain_irq - Invoke the handler for a HW irq belonging to a domain
?* @domain:?? ?The domain where to perform the lookup
?* @hwirq:?? ?The HW irq number to convert to a logical one
?* @lookup:?? ?Whether to perform the domain lookup or not
?* @regs:?? ?Register file coming from the low-level handling code
?*
?* Returns:?? ?0 on success, or -EINVAL if conversion has failed
?*/
int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
?? ??? ??? ?bool lookup, struct pt_regs *regs)
{
?? ?struct pt_regs *old_regs = set_irq_regs(regs);
?? ?unsigned int irq = hwirq;
?? ?int ret = 0;

--------關閉搶占

?? ?irq_enter();

#ifdef CONFIG_IRQ_DOMAIN
?? ?if (lookup)
?? ??? ?irq = irq_find_mapping(domain, hwirq);
#endif

?? ?/*
?? ? * Some hardware gives randomly wrong interrupts.? Rather
?? ? * than crashing, do something sensible.
?? ? */
?? ?if (unlikely(!irq || irq >= nr_irqs)) {
?? ??? ?ack_bad_irq(irq);
?? ??? ?ret = -EINVAL;
?? ?} else {

------執行處理
?? ??? ?generic_handle_irq(irq);? ----實際為注冊IRQ號對應的函數:desc->handle_irq(desc);???????
?? ?}

----關閉搶占以及檢測是否有軟中斷需要處理

?? ?irq_exit();
?? ?set_irq_regs(old_regs);
?? ?return ret;
}
#endif

關于搶占,即在適當的時機及時調用更高優先級的任務;

幾個搶占點:

從中斷返回到內核空間時;解鎖或使能軟中斷時;調用 preempt_enable 使能搶占;

顯式調度;任務阻塞時;

不可搶占點:

正處于中斷中;持有鎖時;正在調度時;對Per-CPU操作時

搶占的具體實現為對一個preempt_count變量進行互斥操作,位于thread_info結構中

static __always_inline volatile int *preempt_count_ptr(void)

{

return &current_thread_info()->preempt_count;

}

static __always_inline void __preempt_count_add(int val)

{

*preempt_count_ptr() += val;

}

static __always_inline void __preempt_count_sub(int val)

{

*preempt_count_ptr() -= val;

}

#define preempt_count_add(val) __preempt_count_add(val)

#define preempt_count_sub(val) __preempt_count_sub(val)

#define preempt_count_dec_and_test() __preempt_count_dec_and_test()

#define preempt_count_inc() preempt_count_add(1)

#define preempt_count_dec() preempt_count_sub(1)

借用一張圖詳細描述

關于軟中斷,中斷底半部的一種實現方式,靜態分配,一共10種,可以充分利用SMP性能;

其執行點一般在irq_exit時或者獨立的內核線程中

在irq_exit中會進行有沒有待處理軟中斷的檢測(其實就是判斷preempt_count的BIT8-15)

/*

* Exit an interrupt context. Process softirqs if needed and possible:

*/

void irq_exit(void)

{

#ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED

local_irq_disable();

#else

lockdep_assert_irqs_disabled();

#endif

account_irq_exit_time(current);

-----計數-1

preempt_count_sub(HARDIRQ_OFFSET);

----不在中斷中以及有待處理軟中斷

if (!in_interrupt() && local_softirq_pending())

????????invoke_softirq();? ----實際為__do_softirq或者wakeup_softirqd

tick_irq_exit();

rcu_irq_exit();

trace_hardirq_exit(); /* must be last! */

}

asmlinkage __visible void __softirq_entry __do_softirq(void)

{

unsigned long end = jiffies + MAX_SOFTIRQ_TIME;

unsigned long old_flags = current->flags;

int max_restart = MAX_SOFTIRQ_RESTART;

struct softirq_action *h;

bool in_hardirq;

__u32 pending;

int softirq_bit;

#if defined(CONFIG_MP_IRQ_TRACE)

MSYS_IRQ_INFO irq_info;

#endif

/*

* Mask out PF_MEMALLOC s current task context is borrowed for the

* softirq. A softirq handled such as network RX might set PF_MEMALLOC

* again if the socket is related to swap

*/

current->flags &= ~PF_MEMALLOC;

pending = local_softirq_pending();

account_irq_enter_time(current);

-----軟中斷不可嵌套

__local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);

in_hardirq = lockdep_softirq_start();

restart:

/* Reset the pending bitmask before enabling irqs */

set_softirq_pending(0);

----開啟中斷,此后的部分隨時會被中斷打斷;因此要保證軟中短處理好函數的可重入性

local_irq_enable();

h = softirq_vec;

while ((softirq_bit = ffs(pending))) {

unsigned int vec_nr;

int prev_count;

h += softirq_bit - 1;

vec_nr = h - softirq_vec;

prev_count = preempt_count();

kstat_incr_softirqs_this_cpu(vec_nr);

#if defined(CONFIG_MP_IRQ_TRACE)

irq_info.IRQNumber = vec_nr;

irq_info.action = h->action;

irq_info.timeStart = sched_clock();

#endif

trace_softirq_entry(vec_nr);

---執行處理函數

h->action(h);

trace_softirq_exit(vec_nr);

#if defined(CONFIG_MP_IRQ_TRACE)

irq_info.timeEnd = sched_clock();

if (irq_info.timeEnd - irq_info.timeStart > 2500000)

{

if(sirq_head_initialized)

{

ms_records_sirq(&irq_info);

}

}

#endif

if (unlikely(prev_count != preempt_count())) {

pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n",

vec_nr, softirq_to_name[vec_nr], h->action,

prev_count, preempt_count());

preempt_count_set(prev_count);

}

h++;

pending >>= softirq_bit;

}

rcu_bh_qs();

----處理完后關閉中斷

local_irq_disable();

----再次檢測是否有待處理軟中斷(因為軟中斷處理過程中開啟了中斷)

pending = local_softirq_pending();

if (pending) {

----軟中斷有最大執行時間限制

if (time_before(jiffies, end) && !need_resched() &&

--max_restart)

---有的話繼續執行軟中斷

goto restart;

----軟中斷積累太多,調用獨立的內核線程ksoftirqd執行,每個core都有一個ksoftirqd,其實際上是一個Per-CPU變量

wakeup_softirqd();

}

lockdep_softirq_end(in_hardirq);

account_irq_exit_time(current);

-----軟中斷底半部使能

__local_bh_enable(SOFTIRQ_OFFSET);

WARN_ON_ONCE(in_interrupt());

tsk_restore_flags(current, old_flags, PF_MEMALLOC);

}

總結:

1. GIC分為兩部分,Distributor(處理優先級,中斷保持,信號分發等)和CPU interface(響應及完成中斷等)

2. Linux中斷不可嵌套

3. Linux中斷處理分為上半部(快速處理和響應)和底半部(延遲執行)

4. 底半部常用手段有軟中斷(可以在不同CPU并發),tasklet(同一個tasklet在同一個CPU排隊執行)以及工作隊列(可睡眠)

5. 關于Per-CPU變量,編譯時位于特定的section中,加載內存后,每個cpu都有一份拷貝,各自用各自的獨立拷貝,不存在多cpu訪問互斥問題,只有本cpu線程和本cpu中斷互斥訪問問題

6. 關于內存屏障,因為cpu大多都是流水線工作方式,并且可以亂序執行,內存屏障保證語句前后的指令具備嚴格的先后執行順序

總結

以上是生活随笔為你收集整理的ARM GIC简介与Linux中断处理分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。