精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux中斷處理淺析

科技綠洲 ? 來(lái)源:Linux開(kāi)發(fā)架構(gòu)之路 ? 作者:Linux開(kāi)發(fā)架構(gòu)之路 ? 2023-11-09 16:46 ? 次閱讀

1. 中斷的概念

中斷是指在CPU正常運(yùn)行期間,由于內(nèi)外部事件或由程序預(yù)先安排的事件引起的 CPU 暫時(shí)停止正在運(yùn)行的程序,轉(zhuǎn)而為該內(nèi)部或外部事件或預(yù)先安排的事件服務(wù)的程序中去,服務(wù)完畢后再返回去繼續(xù)運(yùn)行被暫時(shí)中斷的程序。Linux中通常分為外部中斷(又叫硬件中斷)和內(nèi)部中斷(又叫異常)。

軟件對(duì)硬件進(jìn)行配置后,軟件期望等待硬件的某種狀態(tài)(比如,收到了數(shù)據(jù)),這里有兩種方式,一種是輪詢(xún)(polling):CPU 不斷的去讀硬件狀態(tài)。另一種是當(dāng)硬件完成某種事件后,給 CPU 一個(gè)中斷,讓 CPU 停下手上的事情,去處理這個(gè)中斷。很顯然,中斷的交互方式提高了系統(tǒng)的吞吐。

當(dāng) CPU 收到一個(gè)中斷 (IRQ)的時(shí)候,會(huì)去執(zhí)行該中斷對(duì)應(yīng)的處理函數(shù)(ISR)。普通情況下,會(huì)有一個(gè)中斷向量表,向量表中定義了 CPU 對(duì)應(yīng)的每一個(gè)外設(shè)資源的中斷處理程序的入口,當(dāng)發(fā)生對(duì)應(yīng)的中斷的時(shí)候, CPU 直接跳轉(zhuǎn)到這個(gè)入口執(zhí)行程序。也就是中斷上下文。(注意:中斷上下文中,不可阻塞睡眠)。

2. Linux 中斷 top/bottom

玩過(guò) MCU 的人都知道,中斷服務(wù)程序的設(shè)計(jì)最好是快速完成任務(wù)并退出,因?yàn)榇丝滔到y(tǒng)處于被中斷中。但是在 ISR 中又有一些必須完成的事情,比如:清中斷標(biāo)志,讀/寫(xiě)數(shù)據(jù),寄存器操作等。

在 Linux 中,同樣也是這個(gè)要求,希望盡快的完成 ISR。但事與愿違,有些 ISR 中任務(wù)繁重,會(huì)消耗很多時(shí)間,導(dǎo)致響應(yīng)速度變差。Linux 中針對(duì)這種情況,將中斷分為了兩部分:

  1. 上半部(top half):收到一個(gè)中斷,立即執(zhí)行,有嚴(yán)格的時(shí)間限制,只做一些必要的工作,比如:應(yīng)答,復(fù)位等。這些工作都是在所有中斷被禁止的情況下完成的。
  2. 底半部(bottom half):能夠被推遲到后面完成的任務(wù)會(huì)在底半部進(jìn)行。在適合的時(shí)機(jī),下半部會(huì)被開(kāi)中斷執(zhí)行。

3. 中斷處理程序

驅(qū)動(dòng)程序可以使用接口

request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)

像系統(tǒng)申請(qǐng)注冊(cè)一個(gè)中斷處理程序。

其中的參數(shù)

圖片

中斷標(biāo)志 flag 的含義:

圖片

調(diào)用 request _irq 成功執(zhí)行返回 0。常見(jiàn)錯(cuò)誤是 -EBUSY,表示給定的中斷線已經(jīng)在使用(或者沒(méi)有指定 IRQF_SHARED)

注意:request_irq 函數(shù)可能引起睡眠,所以不允許在中斷上下文或者不允許睡眠的代碼中調(diào)用。

釋放中斷:

const void *free_irq(unsigned int irq, void *dev_id)

用于釋放中斷處理函數(shù)。

注意:Linux 中的中斷處理程序是無(wú)須重入的。當(dāng)給定的中斷處理程序正在執(zhí)行的時(shí)候,其中斷線在所有的處理器上都會(huì)被屏蔽掉,以防在同一個(gè)中斷線上又接收到另一個(gè)新的中斷。通常情況下,除了該中斷的其他中斷都是打開(kāi)的,也就是說(shuō)其他的中斷線上的重點(diǎn)都能夠被處理,但是當(dāng)前的中斷線總是被禁止的,故,同一個(gè)中斷處理程序是絕對(duì)不會(huì)被自己嵌套的。

4. 中斷上下文

與進(jìn)程上下文不一樣,內(nèi)核執(zhí)行中斷服務(wù)程序的時(shí)候,處于中斷上下文。中斷處理程序并沒(méi)有自己的獨(dú)立的棧,而是使用了內(nèi)核棧,其大小一般是有限制的(32bit 機(jī)器 8KB)。所以其必須短小精悍。同時(shí)中斷服務(wù)程序是打斷了正常的程序流程,這一點(diǎn)上也必須保證快速的執(zhí)行。同時(shí)中斷上下文中是不允許睡眠,阻塞的。

中斷上下文不能睡眠的原因是:

1、 中斷處理的時(shí)候,不應(yīng)該發(fā)生進(jìn)程切換,因?yàn)樵谥袛郼ontext中,唯一能打斷當(dāng)前中斷handler的只有更高優(yōu)先級(jí)的中斷,它不會(huì)被進(jìn)程打斷,如果在 中斷context中休眠,則沒(méi)有辦法喚醒它,因?yàn)樗械膚ake_up_xxx都是針對(duì)某個(gè)進(jìn)程而言的,而在中斷context中,沒(méi)有進(jìn)程的概念,沒(méi) 有一個(gè)task_struct(這點(diǎn)對(duì)于softirq和tasklet一樣),因此真的休眠了,比如調(diào)用了會(huì)導(dǎo)致block的例程,內(nèi)核幾乎肯定會(huì)死。

2、schedule()在切換進(jìn)程時(shí),保存當(dāng)前的進(jìn)程上下文(CPU寄存器的值、進(jìn)程的狀態(tài)以及堆棧中的內(nèi)容),以便以后恢復(fù)此進(jìn)程運(yùn)行。中斷發(fā)生后,內(nèi)核會(huì)先保存當(dāng)前被中斷的進(jìn)程上下文(在調(diào)用中斷處理程序后恢復(fù));

但在中斷處理程序里,CPU寄存器的值肯定已經(jīng)變化了吧(最重要的程序計(jì)數(shù)器PC、堆棧SP等),如果此時(shí)因?yàn)樗呋蜃枞僮髡{(diào)用了schedule(),則保存的進(jìn)程上下文就不是當(dāng)前的進(jìn)程context了.所以不可以在中斷處理程序中調(diào)用schedule()。

3、內(nèi)核中schedule()函數(shù)本身在進(jìn)來(lái)的時(shí)候判斷是否處于中斷上下文:

if(unlikely(in_interrupt()))

BUG();

因此,強(qiáng)行調(diào)用schedule()的結(jié)果就是內(nèi)核BUG。

4、中斷handler會(huì)使用被中斷的進(jìn)程內(nèi)核堆棧,但不會(huì)對(duì)它有任何影響,因?yàn)閔andler使用完后會(huì)完全清除它使用的那部分堆棧,恢復(fù)被中斷前的原貌。

5、處于中斷context時(shí)候,內(nèi)核是不可搶占的。因此,如果休眠,則內(nèi)核一定掛起。

5. 舉例

比如 RTC 驅(qū)動(dòng)程序 (drivers/char/rtc.c)。在 RTC 驅(qū)動(dòng)的初始化階段,會(huì)調(diào)用到 rtc_init 函數(shù):

module_init(rtc_init);

在這個(gè)初始化函數(shù)中調(diào)用到了 request_irq 用于申請(qǐng)中斷資源,并注冊(cè)服務(wù)程序:

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 是中斷號(hào),和處理器綁定。

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 中斷,就會(huì)調(diào)用進(jìn)這個(gè)函數(shù)。

6. 中斷處理流程

發(fā)生中斷時(shí),CPU執(zhí)行異常向量vector_irq的代碼, 即異常向量表中的中斷異常的代碼,它是一個(gè)跳轉(zhuǎn)指令,跳去執(zhí)行真正的中斷處理程序,在vector_irq里面,最終會(huì)調(diào)用中斷處理的總?cè)肟诤瘮?shù)。

C 語(yǔ)言的入口為 :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 為中斷號(hào)。

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;

}

這里請(qǐng)注意:

先調(diào)用了 irq_enter 標(biāo)記進(jìn)入了硬件中斷:

irq_enter是更新一些系統(tǒng)的統(tǒng)計(jì)信息,同時(shí)在__irq_enter宏中禁止了進(jìn)程的搶占。雖然在產(chǎn)生IRQ時(shí),ARM會(huì)自動(dòng)把CPSR中的I位置位,禁止新的IRQ請(qǐng)求,直到中斷控制轉(zhuǎn)到相應(yīng)的流控層后才通過(guò)local_irq_enable()打開(kāi)。那為何還要禁止搶占?這是因?yàn)橐紤]中斷嵌套的問(wèn)題,一旦流控層或驅(qū)動(dòng)程序主動(dòng)通過(guò)local_irq_enable打開(kāi)了IRQ,而此時(shí)該中斷還沒(méi)處理完成,新的irq請(qǐng)求到達(dá),這時(shí)代碼會(huì)再次進(jìn)入irq_enter,在本次嵌套中斷返回時(shí),內(nèi)核不希望進(jìn)行搶占調(diào)度,而是要等到最外層的中斷處理完成后才做出調(diào)度動(dòng)作,所以才有了禁止搶占這一處理

再調(diào)用 generic_handle_irq

最后調(diào)用 irq_exit 刪除進(jìn)入硬件中斷的標(biāo)記

__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ā)生中斷的中斷號(hào),去取出它的 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 是支持的總的中斷個(gè)數(shù),當(dāng)然,irq 不能夠大于這個(gè)數(shù)目。所以返回 irq_desc + irq。

irq_desc 是一個(gè)全局的數(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),
}
};

這里是這個(gè)數(shù)組的初始化的地方。所有的 handle_irq 函數(shù)都被初始化成為了 handle_bad_irq。

細(xì)心的觀眾可能發(fā)現(xiàn)了,調(diào)用這個(gè) desc->handle_irq(desc) 函數(shù),并不是咱們注冊(cè)進(jìn)去的中斷處理函數(shù)啊,因?yàn)閮蓚€(gè)函數(shù)的原型定義都不一樣。這個(gè) handle_irq 是 irq_flow_handler_t 類(lèi)型,而我們注冊(cè)進(jìn)去的服務(wù)程序是 irq_handler_t,這兩個(gè)明顯不是同一個(gè)東西,所以這里我們還需要繼續(xù)分析。

6.1 中斷相關(guān)的數(shù)據(jù)結(jié)構(gòu)

Linux 中斷相關(guān)的數(shù)據(jù)結(jié)構(gòu)有 3 個(gè)

圖片

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 是一串和芯片相關(guān)的函數(shù)指針,這里定義的非常的全面,基本上和 IRQ 相關(guān)的可能出現(xiàn)的操作都全部定義進(jìn)去了,具體根據(jù)不同的芯片,需要在不同的芯片的地方去初始化這個(gè)結(jié)構(gòu),然后這個(gè)結(jié)構(gòu)會(huì)嵌入到通用的 IRQ 處理軟件中去使用,使得軟件處理邏輯和芯片邏輯完全的分開(kāi)。

好,我們接下來(lái)繼續(xù)前進(jìn)。

6.2 初始化 Chip 相關(guān)的 IRQ

眾所周知,啟動(dòng)的時(shí)候,C 語(yǔ)言從 start_kernel 開(kāi)始,在這里面,調(diào)用了和 machine 相關(guān)的 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() 完成對(duì)中斷控制器的初始化,為每個(gè)irq_desc結(jié)構(gòu)安裝合適的流控handler,為每個(gè)irq_desc結(jié)構(gòu)安裝irq_chip指針,使他指向正確的中斷控制器所對(duì)應(yīng)的irq_chip結(jié)構(gòu)的實(shí)例,同時(shí),如果該平臺(tái)中的中斷線有多路復(fù)用(多個(gè)中斷公用一個(gè)irq中斷線)的情況,還應(yīng)該初始化irq_desc中相應(yīng)的字段和標(biāo)志,以便實(shí)現(xiàn)中斷控制器的級(jí)聯(lián)。

這里初始化的時(shí)候回調(diào)用到具體的芯片相關(guān)的中斷初始化的地方。

例如:

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)用類(lèi)似于:

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 文件。是對(duì)芯片初始化的時(shí)候可見(jiàn)的 APIs,用于指定中斷“流控”中的 :

irq_flow_handler_t handle

也就是中斷來(lái)的時(shí)候,最后那個(gè)函數(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ù)里,會(huì)去調(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);
}

而這個(gè) 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)用 的地方了,就是咱們通過(guò) request_irq 注冊(cè)進(jìn)去的函數(shù)

7. /proc/interrupts

這個(gè) proc 下放置了對(duì)應(yīng)中斷號(hào)的中斷次數(shù)和對(duì)應(yīng)的 dev-name

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    10702

    瀏覽量

    209450
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11125

    瀏覽量

    207936
  • 程序
    +關(guān)注

    關(guān)注

    115

    文章

    3720

    瀏覽量

    80366
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4238

    瀏覽量

    61976
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Linux內(nèi)核中斷設(shè)計(jì)與實(shí)現(xiàn)

    裸機(jī)編程中使用中斷比較麻煩,需要配置寄存器、使能IRQ等等。而在Linux驅(qū)動(dòng)編程中,內(nèi)核提供了完善的終端框架,只需要申請(qǐng)中斷,然后注冊(cè)中斷處理
    發(fā)表于 07-29 08:57 ?727次閱讀

    linux中斷處理機(jī)制 中斷處理過(guò)程

    在聊中斷機(jī)制之前,我想先和大家聊一聊中斷機(jī)制出現(xiàn)的前因后果。最一開(kāi)始計(jì)算機(jī)操作系統(tǒng)的設(shè)計(jì)是能夠一次性的執(zhí)行所有的計(jì)算任務(wù)的,這被稱(chēng)為順序執(zhí)行,也是批處理操作系統(tǒng)(Batch system)。
    的頭像 發(fā)表于 08-07 09:18 ?1132次閱讀
    <b class='flag-5'>linux</b><b class='flag-5'>中斷</b><b class='flag-5'>處理</b>機(jī)制 <b class='flag-5'>中斷</b><b class='flag-5'>處理</b>過(guò)程

    淺析linux gpio中斷

    最近用到gpio中斷,gpio中斷也是嵌入式系統(tǒng)用的比較多的。對(duì)于GPIO操作 linux 有一套標(biāo)準(zhǔn)的 API,set value、get value之類(lèi)的,當(dāng)然也有關(guān)于中斷的。
    發(fā)表于 07-26 08:15

    面向嵌入式Linux系統(tǒng)的軟中斷設(shè)計(jì)與實(shí)現(xiàn)

    本文在分析標(biāo)準(zhǔn)Linux 內(nèi)核的軟中斷機(jī)制的演化以及實(shí)現(xiàn)原理的基礎(chǔ)上,提出并實(shí)現(xiàn)了一個(gè)面前嵌入式Linux 系統(tǒng)的軟中斷技術(shù)。該技術(shù)為嵌入式系統(tǒng)開(kāi)發(fā)提供一個(gè)統(tǒng)一的
    發(fā)表于 08-03 11:20 ?16次下載

    Linux 2.6 中斷處理原理簡(jiǎn)介

    Linux 2.6 中斷處理原理簡(jiǎn)介 中斷描述符表(Interrupt Descriptor Table,IDT)是一個(gè)系統(tǒng)表,它與每一個(gè)中斷
    發(fā)表于 02-05 10:52 ?785次閱讀

    淺析單片機(jī)中斷處理過(guò)程

    中斷處理過(guò)程可分為中斷響應(yīng)、中斷處理中斷返回三個(gè)階段。
    的頭像 發(fā)表于 11-06 14:31 ?1.7w次閱讀
    <b class='flag-5'>淺析</b>單片機(jī)<b class='flag-5'>中斷</b><b class='flag-5'>處理</b>過(guò)程

    linux驅(qū)動(dòng)之中斷處理過(guò)程C程序部分

    linux內(nèi)核將所有的中斷統(tǒng)一編號(hào),使用一個(gè)irq_desc[NR_IRQS]的結(jié)構(gòu)體數(shù)組來(lái)描述這些中斷:每個(gè)數(shù)組項(xiàng)對(duì)應(yīng)著一個(gè)中斷源(可能是一個(gè)中斷
    發(fā)表于 05-07 11:13 ?980次閱讀

    Linux驅(qū)動(dòng)技術(shù)之一內(nèi)核中斷

    在硬件上,中斷源可以通過(guò)中斷控制器向CPU提交中斷,進(jìn)而引發(fā)中斷處理程序的執(zhí)行,不過(guò)這種硬件中斷
    發(fā)表于 05-08 13:49 ?605次閱讀

    linux中斷處理之IRQ中斷

    在前一個(gè)專(zhuān)題里曾分析過(guò)所有IRQ中斷處理流程,經(jīng)過(guò)SAVE_ALL保存硬件環(huán)境后,都會(huì)進(jìn)入do_IRQ()進(jìn)行處理,今天接著分析do_IRQ()處理的相關(guān)東西.分為兩部
    發(fā)表于 05-10 10:57 ?2506次閱讀

    了解Linux中斷處理原理

    最簡(jiǎn)單的中斷機(jī)制就是像芯片手冊(cè)上講的那樣,在中斷向量表中填入跳轉(zhuǎn)到對(duì)應(yīng)處理函數(shù)的指令,然后在處理函數(shù)中實(shí)現(xiàn)需要的功能。
    發(fā)表于 05-14 13:49 ?2280次閱讀
    了解<b class='flag-5'>Linux</b><b class='flag-5'>中斷</b><b class='flag-5'>處理</b>原理

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

    淺析riscv中的plic與eclic 1.PLIC中斷處理 2.sifive中斷的編程模型 3.關(guān)于eclic 4.關(guān)于jalmnxti 1.PLIC
    的頭像 發(fā)表于 04-15 13:50 ?1.2w次閱讀
    riscv中的plic<b class='flag-5'>中斷</b><b class='flag-5'>處理</b>與eclic詳解

    Linux中斷情景分析

    在一個(gè)系統(tǒng)中,中斷時(shí)常發(fā)生,而且線程調(diào)度也是由一個(gè)硬件定時(shí)器時(shí)時(shí)刻刻發(fā)出中斷來(lái)支撐的。可以說(shuō)中斷就是linux系統(tǒng)的靈魂。
    發(fā)表于 06-23 14:22 ?506次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>中斷</b>情景分析

    Linux為什么中斷不允許休眠

    Linux 為什么中斷不允許休眠? 所謂的睡眠,就是調(diào)用 schedule 讓出 CPU,調(diào)度器選擇另外個(gè)進(jìn)程繼續(xù)執(zhí)行,這個(gè)過(guò)程涉及進(jìn)程棧空間的切換。 1、假如中斷上下文中調(diào)用 schedule
    的頭像 發(fā)表于 09-27 17:48 ?1055次閱讀

    什么是LInux 操作系統(tǒng)中斷

    LInux 操作系統(tǒng)中斷 什么是系統(tǒng)中斷 這個(gè)沒(méi)啥可說(shuō)的,大家都知道; CPU 在執(zhí)行任務(wù)途中接收到中斷請(qǐng)求,需要保存現(xiàn)場(chǎng)后去處理
    的頭像 發(fā)表于 11-10 11:29 ?489次閱讀
    什么是<b class='flag-5'>LInux</b> 操作系統(tǒng)<b class='flag-5'>中斷</b>

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

    LInux 操作系統(tǒng)中斷 什么是系統(tǒng)中斷 這個(gè)沒(méi)啥可說(shuō)的,大家都知道; CPU 在執(zhí)行任務(wù)途中接收到中斷請(qǐng)求,需要保存現(xiàn)場(chǎng)后去處理
    的頭像 發(fā)表于 11-13 11:36 ?758次閱讀
    <b class='flag-5'>LInux</b> 操作系統(tǒng)<b class='flag-5'>中斷</b>介紹