1. 中斷的概念
中斷是指在CPU正常運行期間,由于內(nèi)外部事件或由程序預先安排的事件引起的 CPU 暫時停止正在運行的程序,轉(zhuǎn)而為該內(nèi)部或外部事件或預先安排的事件服務的程序中去,服務完畢后再返回去繼續(xù)運行被暫時中斷的程序。Linux中通常分為外部中斷(又叫硬件中斷)和內(nèi)部中斷(又叫異常)。
軟件對硬件進行配置后,軟件期望等待硬件的某種狀態(tài)(比如,收到了數(shù)據(jù)),這里有兩種方式,一種是輪詢(polling):CPU 不斷的去讀硬件狀態(tài)。另一種是當硬件完成某種事件后,給 CPU 一個中斷,讓 CPU 停下手上的事情,去處理這個中斷。很顯然,中斷的交互方式提高了系統(tǒng)的吞吐。
當 CPU 收到一個中斷 (IRQ)的時候,會去執(zhí)行該中斷對應的處理函數(shù)(ISR)。普通情況下,會有一個中斷向量表,向量表中定義了 CPU 對應的每一個外設資源的中斷處理程序的入口,當發(fā)生對應的中斷的時候, CPU 直接跳轉(zhuǎn)到這個入口執(zhí)行程序。也就是中斷上下文。(注意:中斷上下文中,不可阻塞睡眠)。
2. Linux 中斷 top/bottom
玩過 MCU 的人都知道,中斷服務程序的設計最好是快速完成任務并退出,因為此刻系統(tǒng)處于被中斷中。但是在 ISR 中又有一些必須完成的事情,比如:清中斷標志,讀/寫數(shù)據(jù),寄存器操作等。
在 Linux 中,同樣也是這個要求,希望盡快的完成 ISR。但事與愿違,有些 ISR 中任務繁重,會消耗很多時間,導致響應速度變差。Linux 中針對這種情況,將中斷分為了兩部分:
- 上半部(top half):收到一個中斷,立即執(zhí)行,有嚴格的時間限制,只做一些必要的工作,比如:應答,復位等。這些工作都是在所有中斷被禁止的情況下完成的。
- 底半部(bottom half):能夠被推遲到后面完成的任務會在底半部進行。在適合的時機,下半部會被開中斷執(zhí)行。
3. 中斷處理程序
驅(qū)動程序可以使用接口:
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
像系統(tǒng)申請注冊一個中斷處理程序。
其中的參數(shù):
中斷標志 flag 的含義:
調(diào)用 request _irq 成功執(zhí)行返回 0。常見錯誤是 -EBUSY,表示給定的中斷線已經(jīng)在使用(或者沒有指定 IRQF_SHARED)
注意:request_irq 函數(shù)可能引起睡眠,所以不允許在中斷上下文或者不允許睡眠的代碼中調(diào)用。
釋放中斷:
const void *free_irq(unsigned int irq, void *dev_id)
用于釋放中斷處理函數(shù)。
注意:Linux 中的中斷處理程序是無須重入的。當給定的中斷處理程序正在執(zhí)行的時候,其中斷線在所有的處理器上都會被屏蔽掉,以防在同一個中斷線上又接收到另一個新的中斷。通常情況下,除了該中斷的其他中斷都是打開的,也就是說其他的中斷線上的重點都能夠被處理,但是當前的中斷線總是被禁止的,故,同一個中斷處理程序是絕對不會被自己嵌套的。
4. 中斷上下文
與進程上下文不一樣,內(nèi)核執(zhí)行中斷服務程序的時候,處于中斷上下文。中斷處理程序并沒有自己的獨立的棧,而是使用了內(nèi)核棧,其大小一般是有限制的(32bit 機器 8KB)。所以其必須短小精悍。同時中斷服務程序是打斷了正常的程序流程,這一點上也必須保證快速的執(zhí)行。同時中斷上下文中是不允許睡眠,阻塞的。
中斷上下文不能睡眠的原因是:
1、 中斷處理的時候,不應該發(fā)生進程切換,因為在中斷context中,唯一能打斷當前中斷handler的只有更高優(yōu)先級的中斷,它不會被進程打斷,如果在 中斷context中休眠,則沒有辦法喚醒它,因為所有的wake_up_xxx都是針對某個進程而言的,而在中斷context中,沒有進程的概念,沒 有一個task_struct(這點對于softirq和tasklet一樣),因此真的休眠了,比如調(diào)用了會導致block的例程,內(nèi)核幾乎肯定會死。
2、schedule()在切換進程時,保存當前的進程上下文(CPU寄存器的值、進程的狀態(tài)以及堆棧中的內(nèi)容),以便以后恢復此進程運行。中斷發(fā)生后,內(nèi)核會先保存當前被中斷的進程上下文(在調(diào)用中斷處理程序后恢復);
但在中斷處理程序里,CPU寄存器的值肯定已經(jīng)變化了吧(最重要的程序計數(shù)器PC、堆棧SP等),如果此時因為睡眠或阻塞操作調(diào)用了schedule(),則保存的進程上下文就不是當前的進程context了.所以不可以在中斷處理程序中調(diào)用schedule()。
3、內(nèi)核中schedule()函數(shù)本身在進來的時候判斷是否處于中斷上下文:
if(unlikely(in_interrupt()))
BUG();
因此,強行調(diào)用schedule()的結(jié)果就是內(nèi)核BUG。
4、中斷handler會使用被中斷的進程內(nèi)核堆棧,但不會對它有任何影響,因為handler使用完后會完全清除它使用的那部分堆棧,恢復被中斷前的原貌。
5、處于中斷context時候,內(nèi)核是不可搶占的。因此,如果休眠,則內(nèi)核一定掛起。
5. 舉例
比如 RTC 驅(qū)動程序 (drivers/char/rtc.c)。在 RTC 驅(qū)動的初始化階段,會調(diào)用到 rtc_init 函數(shù):
module_init(rtc_init);
在這個初始化函數(shù)中調(diào)用到了 request_irq 用于申請中斷資源,并注冊服務程序:
static int __init rtc_init(void)
{
...
rtc_int_handler_ptr = rtc_interrupt;
...
request_irq(RTC_IRQ, rtc_int_handler_ptr, 0, "rtc", NULL)
...
}
RTC_IRQ 是中斷號,和處理器綁定。
rtc_interrupt 是中斷處理程序:
static irqreturn_t rtc_interrupt(int irq, void dev_id)
{
/
- Can be an alarm interrupt, update complete interrupt,
- or a periodic interrupt. We store the status in the
- low byte and the number of interrupts received since
- the last read in the remainder of rtc_irq_data.
*/
spin_lock(&rtc_lock);
rtc_irq_data += 0x100;
rtc_irq_data &= ~0xff;
if (is_hpet_enabled()) {
/*
* In this case it is HPET RTC interrupt handler
* calling us, with the interrupt information
* passed as arg1, instead of irq.
*/
rtc_irq_data |= (unsigned long)irq & 0xF0;
} else {
rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
}
if (rtc_status & RTC_TIMER_ON)
mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);
spin_unlock(&rtc_lock);
wake_up_interruptible(&rtc_wait);
kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
return IRQ_HANDLED;
}
每次收到 RTC 中斷,就會調(diào)用進這個函數(shù)。
6. 中斷處理流程
發(fā)生中斷時,CPU執(zhí)行異常向量vector_irq的代碼, 即異常向量表中的中斷異常的代碼,它是一個跳轉(zhuǎn)指令,跳去執(zhí)行真正的中斷處理程序,在vector_irq里面,最終會調(diào)用中斷處理的總?cè)肟诤瘮?shù)。
C 語言的入口為 :asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
asmlinkage void __exception_irq_entry
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
handle_IRQ(irq, regs);
}
該函數(shù)的入?yún)?irq 為中斷號。
asm_do_IRQ -> handle_IRQ
void handle_IRQ(unsigned int irq, struct pt_regs *regs)
{
__handle_domain_irq(NULL, irq, false, regs);
}
handle_IRQ -> __handle_domain_irq
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_exit();
set_irq_regs(old_regs);
return ret;
}
這里請注意:
先調(diào)用了 irq_enter 標記進入了硬件中斷:
irq_enter是更新一些系統(tǒng)的統(tǒng)計信息,同時在__irq_enter宏中禁止了進程的搶占。雖然在產(chǎn)生IRQ時,ARM會自動把CPSR中的I位置位,禁止新的IRQ請求,直到中斷控制轉(zhuǎn)到相應的流控層后才通過local_irq_enable()打開。那為何還要禁止搶占?這是因為要考慮中斷嵌套的問題,一旦流控層或驅(qū)動程序主動通過local_irq_enable打開了IRQ,而此時該中斷還沒處理完成,新的irq請求到達,這時代碼會再次進入irq_enter,在本次嵌套中斷返回時,內(nèi)核不希望進行搶占調(diào)度,而是要等到最外層的中斷處理完成后才做出調(diào)度動作,所以才有了禁止搶占這一處理
再調(diào)用 generic_handle_irq
最后調(diào)用 irq_exit 刪除進入硬件中斷的標記
__handle_domain_irq -> generic_handle_irq
int generic_handle_irq(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
generic_handle_irq_desc(desc);
return 0;
}
EXPORT_SYMBOL_GPL(generic_handle_irq);
首先在函數(shù)irq_to_desc中根據(jù)發(fā)生中斷的中斷號,去取出它的 irq_desc 中斷描述結(jié)構(gòu),然后調(diào)用 generic_handle_irq_desc:
static inline void generic_handle_irq_desc(struct irq_desc *desc)
{
desc->handle_irq(desc);
}
這里調(diào)用了 handle_irq 函數(shù)。
所以,在上述流程中,還需要分析 irq_to_desc 流程:
struct irq_desc *irq_to_desc(unsigned int irq)
{
return (irq < NR_IRQS) ? irq_desc + irq : NULL;
}
EXPORT_SYMBOL(irq_to_desc);
NR_IRQS 是支持的總的中斷個數(shù),當然,irq 不能夠大于這個數(shù)目。所以返回 irq_desc + irq。
irq_desc 是一個全局的數(shù)組:
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = {
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
}
};
這里是這個數(shù)組的初始化的地方。所有的 handle_irq 函數(shù)都被初始化成為了 handle_bad_irq。
細心的觀眾可能發(fā)現(xiàn)了,調(diào)用這個 desc->handle_irq(desc) 函數(shù),并不是咱們注冊進去的中斷處理函數(shù)啊,因為兩個函數(shù)的原型定義都不一樣。這個 handle_irq 是 irq_flow_handler_t 類型,而我們注冊進去的服務程序是 irq_handler_t,這兩個明顯不是同一個東西,所以這里我們還需要繼續(xù)分析。
6.1 中斷相關的數(shù)據(jù)結(jié)構(gòu)
Linux 中斷相關的數(shù)據(jù)結(jié)構(gòu)有 3 個
irq_desc 結(jié)構(gòu)如下
struct irq_desc {
struct irq_common_data irq_common_data;
struct irq_data irq_data;
unsigned int __percpu *kstat_irqs;
irq_flow_handler_t handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
irq_preflow_handler_t preflow_handler;
#endif
struct irqaction action; / IRQ action list /
unsigned int status_use_accessors;
unsigned int core_internal_state__do_not_mess_with_it;
unsigned int depth; / nested irq disables /
unsigned int wake_depth; / nested wake enables /
unsigned int irq_count; / For detecting broken IRQs /
unsigned long last_unhandled; / Aging timer for unhandled count */
unsigned int irqs_unhandled;
atomic_t threads_handled;
int threads_handled_last;
raw_spinlock_t lock;
struct cpumask *percpu_enabled;
const struct cpumask *percpu_affinity;
#ifdef CONFIG_SMP
const struct cpumask *affinity_hint;
struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
unsigned long threads_oneshot;
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PM_SLEEP
unsigned int nr_actions;
unsigned int no_suspend_depth;
unsigned int cond_suspend_depth;
unsigned int force_resume_depth;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
struct dentry *debugfs_file;
const char *dev_name;
#endif
#ifdef CONFIG_SPARSE_IRQ
struct rcu_head rcu;
struct kobject kobj;
#endif
struct mutex request_mutex;
int parent_irq;
struct module *owner;
const char *name;
} ____cacheline_internodealigned_in_smp;
irqaction 結(jié)構(gòu)如下:
/**
- struct irqaction - per interrupt action descriptor
- @handler: interrupt handler function
- @name: name of the device
- @dev_id: cookie to identify the device
- @percpu_dev_id: cookie to identify the device
- @next: pointer to the next irqaction for shared interrupts
- @irq: interrupt number
- @flags: flags (see IRQF_* above)
- @thread_fn: interrupt handler function for threaded interrupts
- @thread: thread pointer for threaded interrupts
- @secondary: pointer to secondary irqaction (force threading)
- @thread_flags: flags related to @thread
- @thread_mask: bitmask for keeping track of @thread activity
- @dir: pointer to the proc/irq/NN/name entry
*/
struct irqaction {
irq_handler_t handler;
void *dev_id;
void __percpu *percpu_dev_id;
struct irqaction *next;
irq_handler_t thread_fn;
struct task_struct *thread;
struct irqaction *secondary;
unsigned int irq;
unsigned int flags;
unsigned long thread_flags;
unsigned long thread_mask;
const char *name;
struct proc_dir_entry *dir;
} ____cacheline_internodealigned_in_smp;
irq_chip 描述如下:
/**
struct irq_chip - hardware interrupt chip descriptor
@parent_device: pointer to parent device for irqchip
@name: name for /proc/interrupts
@irq_startup: start up the interrupt (defaults to ->enable if NULL)
@irq_shutdown: shut down the interrupt (defaults to ->disable if NULL)
@irq_enable: enable the interrupt (defaults to chip->unmask if NULL)
@irq_disable: disable the interrupt
@irq_ack: start of a new interrupt
@irq_mask: mask an interrupt source
@irq_mask_ack: ack and mask an interrupt source
@irq_unmask: unmask an interrupt source
@irq_eoi: end of interrupt
@irq_set_affinity: Set the CPU affinity on SMP machines. If the force
argument is true, it tells the driver to
unconditionally apply the affinity setting. Sanity
checks against the supplied affinity mask are not
required. This is used for CPU hotplug where the
target CPU is not yet set in the cpu_online_mask.
@irq_retrigger: resend an IRQ to the CPU
@irq_set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
@irq_set_wake: enable/disable power-management wake-on of an IRQ
@irq_bus_lock: function to lock access to slow bus (i2c) chips
@irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips
@irq_cpu_online: configure an interrupt source for a secondary CPU
@irq_cpu_offline: un-configure an interrupt source for a secondary CPU
@irq_suspend: function called from core code on suspend once per
chip, when one or more interrupts are installed
@irq_resume: function called from core code on resume once per chip,
when one ore more interrupts are installed
@irq_pm_shutdown: function called from core code on shutdown once per chip
@irq_calc_mask: Optional function to set irq_data.mask for special cases
@irq_print_chip: optional to print special chip info in show_interrupts
@irq_request_resources: optional to request resources before calling
any other callback related to this irq
@irq_release_resources: optional to release resources acquired with
irq_request_resources
@irq_compose_msi_msg: optional to compose message content for MSI
@irq_write_msi_msg: optional to write message content for MSI
@irq_get_irqchip_state: return the internal state of an interrupt
@irq_set_irqchip_state: set the internal state of a interrupt
@irq_set_vcpu_affinity: optional to target a vCPU in a virtual machine
@ipi_send_single: send a single IPI to destination cpus
@ipi_send_mask: send an IPI to destination cpus in cpumask
@flags: chip specific flags
*/
struct irq_chip {
struct device *parent_device;
const char *name;
unsigned int (*irq_startup)(struct irq_data *data);
void (*irq_shutdown)(struct irq_data *data);
void (*irq_enable)(struct irq_data *data);
void (*irq_disable)(struct irq_data *data);void (*irq_ack)(struct irq_data *data);
void (*irq_mask)(struct irq_data *data);
void (*irq_mask_ack)(struct irq_data *data);
void (*irq_unmask)(struct irq_data *data);
void (*irq_eoi)(struct irq_data *data);int (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
int (*irq_retrigger)(struct irq_data *data);
int (*irq_set_type)(struct irq_data *data, unsigned int flow_type);
int (*irq_set_wake)(struct irq_data *data, unsigned int on);void (*irq_bus_lock)(struct irq_data *data);
void (*irq_bus_sync_unlock)(struct irq_data *data);void (*irq_cpu_online)(struct irq_data *data);
void (*irq_cpu_offline)(struct irq_data *data);void (*irq_suspend)(struct irq_data *data);
void (*irq_resume)(struct irq_data *data);
void (*irq_pm_shutdown)(struct irq_data *data);void (*irq_calc_mask)(struct irq_data *data);
void (*irq_print_chip)(struct irq_data *data, struct seq_file *p);
int (*irq_request_resources)(struct irq_data *data);
void (*irq_release_resources)(struct irq_data *data);void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);int (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);
int (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);int (*irq_set_vcpu_affinity)(struct irq_data *data, void *vcpu_info);
void (*ipi_send_single)(struct irq_data *data, unsigned int cpu);
void (*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest);unsigned long flags;
};
irq_chip 是一串和芯片相關的函數(shù)指針,這里定義的非常的全面,基本上和 IRQ 相關的可能出現(xiàn)的操作都全部定義進去了,具體根據(jù)不同的芯片,需要在不同的芯片的地方去初始化這個結(jié)構(gòu),然后這個結(jié)構(gòu)會嵌入到通用的 IRQ 處理軟件中去使用,使得軟件處理邏輯和芯片邏輯完全的分開。
好,我們接下來繼續(xù)前進。
6.2 初始化 Chip 相關的 IRQ
眾所周知,啟動的時候,C 語言從 start_kernel 開始,在這里面,調(diào)用了和 machine 相關的 IRQ 的初始化 init_IRQ():
asmlinkage __visible void __init start_kernel(void)
{
char *command_line;
char *after_dashes;
.....
early_irq_init();
init_IRQ();
.....
}
在 init_IRQ 中,調(diào)用了machine_desc->init_irq():
void __init init_IRQ(void)
{
int ret;
if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)
irqchip_init();
else
machine_desc->init_irq();
if (IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_CACHE_L2X0) &&
(machine_desc->l2c_aux_mask || machine_desc->l2c_aux_val)) {
if (!outer_cache.write_sec)
outer_cache.write_sec = machine_desc->l2c_write_sec;
ret = l2x0_of_init(machine_desc->l2c_aux_val,
machine_desc->l2c_aux_mask);
if (ret && ret != -ENODEV)
pr_err("L2C: failed to init: %dn", ret);
}
uniphier_cache_init();
}
machine_desc->init_irq() 完成對中斷控制器的初始化,為每個irq_desc結(jié)構(gòu)安裝合適的流控handler,為每個irq_desc結(jié)構(gòu)安裝irq_chip指針,使他指向正確的中斷控制器所對應的irq_chip結(jié)構(gòu)的實例,同時,如果該平臺中的中斷線有多路復用(多個中斷公用一個irq中斷線)的情況,還應該初始化irq_desc中相應的字段和標志,以便實現(xiàn)中斷控制器的級聯(lián)。
這里初始化的時候回調(diào)用到具體的芯片相關的中斷初始化的地方。
例如:
int __init s5p_init_irq_eint(void)
{
int irq;
for (irq = IRQ_EINT(0); irq <= IRQ_EINT(15); irq++)
irq_set_chip(irq, &s5p_irq_vic_eint);
for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) {
irq_set_chip_and_handler(irq, &s5p_irq_eint, handle_level_irq);
set_irq_flags(irq, IRQF_VALID);
}
irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31);
return 0;
}
而在這些里面,都回去調(diào)用類似于:
void
irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
irq_flow_handler_t handle, const char *name);
irq_set_handler(unsigned int irq, irq_flow_handler_t handle)
{
__irq_set_handler(irq, handle, 0, NULL);
}
static inline void
irq_set_chained_handler(unsigned int irq, irq_flow_handler_t handle)
{
__irq_set_handler(irq, handle, 1, NULL);
}
void
irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle,
void *data);
這些函數(shù)定義在 include/linux/irq.h 文件。是對芯片初始化的時候可見的 APIs,用于指定中斷“流控”中的 :
irq_flow_handler_t handle
也就是中斷來的時候,最后那個函數(shù)調(diào)用。
中斷流控函數(shù),分幾種,電平觸發(fā)的中斷,邊沿觸發(fā)的,等:
/*
- Built-in IRQ handlers for various IRQ types,
- callable via desc->handle_irq()
*/
extern void handle_level_irq(struct irq_desc *desc);
extern void handle_fasteoi_irq(struct irq_desc *desc);
extern void handle_edge_irq(struct irq_desc *desc);
extern void handle_edge_eoi_irq(struct irq_desc *desc);
extern void handle_simple_irq(struct irq_desc *desc);
extern void handle_untracked_irq(struct irq_desc *desc);
extern void handle_percpu_irq(struct irq_desc *desc);
extern void handle_percpu_devid_irq(struct irq_desc *desc);
extern void handle_bad_irq(struct irq_desc *desc);
extern void handle_nested_irq(unsigned int irq);
而在這些處理函數(shù)里,會去調(diào)用到 :handle_irq_event
比如:
/**
handle_level_irq - Level type irq handler
@desc: the interrupt description structure for this irq
Level type interrupts are active as long as the hardware line has
the active level. This may require to mask the interrupt and unmask
it after the associated handler has acknowledged the device, so the
interrupt line is back to inactive.
*/
void handle_level_irq(struct irq_desc *desc)
{
raw_spin_lock(&desc->lock);
mask_ack_irq(desc);if (!irq_may_run(desc))
goto out_unlock;desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
/*
- If its disabled or no action available
- keep it masked and get out of here
*/
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
desc->istate |= IRQS_PENDING;
goto out_unlock;
}
kstat_incr_irqs_this_cpu(desc);
handle_irq_event(desc);cond_unmask_irq(desc);
out_unlock:
raw_spin_unlock(&desc->lock);
}
而這個 handle_irq_event 則是調(diào)用了處理,handle_irq_event_percpu:
irqreturn_t handle_irq_event(struct irq_desc *desc)
{
irqreturn_t ret;
desc->istate &= ~IRQS_PENDING;
irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
raw_spin_unlock(&desc->lock);
ret = handle_irq_event_percpu(desc);
raw_spin_lock(&desc->lock);
irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
return ret;
}
handle_irq_event_percpu->__handle_irq_event_percpu-> 【action->handler()】
這里終于看到了調(diào)用 的地方了,就是咱們通過 request_irq 注冊進去的函數(shù)
7. /proc/interrupts
這個 proc 下放置了對應中斷號的中斷次數(shù)和對應的 dev-name
-
cpu
+關注
關注
68文章
11070瀏覽量
216820 -
Linux
+關注
關注
87文章
11508瀏覽量
213549 -
程序
+關注
關注
117文章
3826瀏覽量
82858 -
函數(shù)
+關注
關注
3文章
4379瀏覽量
64737
發(fā)布評論請先 登錄
Linux內(nèi)核中斷設計與實現(xiàn)
linux中斷處理機制 中斷處理過程

淺析linux gpio中斷
面向嵌入式Linux系統(tǒng)的軟中斷設計與實現(xiàn)
Linux 2.6 中斷處理原理簡介
linux驅(qū)動之中斷處理過程C程序部分
Linux驅(qū)動技術之一內(nèi)核中斷
linux中斷處理之IRQ中斷
了解Linux中斷處理原理

riscv中的plic中斷處理與eclic詳解

Linux中斷情景分析

Linux為什么中斷不允許休眠
什么是LInux 操作系統(tǒng)中斷

LInux 操作系統(tǒng)中斷介紹

評論