ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

linux arm32中断子系统学习总结(三)--- 软件子系统

2022-06-26 12:00:28  阅读:229  来源: 互联网

标签:__ handle 中断 irq arm32 --- gic 子系统 desc


 

三、arm32中断处理软件子系统

 

  中断软件子系统负责cpu检测到中断以后的处理,总体来看,可以分为三个部分:中断向量函数、中断控制器驱动部分以及用户接口部分;

  中断向量函数放在中断向量表里面,每一种中断对应一个中断向量函数,软件在初始化时需要创建一个中断向量表,放在内存中并通过协处理器cp15告诉cpu中断向量表的位置;cpu检测到中断后,会自动将处理器模式切换到对应的中断模式,然后将pc指向对应的中断向量函数。中断向量函数中会将处理器模式切换到svc模式,将被中断前处理器的硬件上下文保存起来,然后跳转到中断控制器驱动注册的处理函数,至此,中断传递到中断控制器驱动;

  中断控制器驱动负责管理所有类型的中断,包括外设中断、SMP核间中断以及cpu热插拔等中断;向下提供中断控制器操作接口,向上提供用户处理中断的各种接口;

最后是上层用户接口,上层通过中断控制器提供的接口注册回调函数处理中断事件;

3.1 中断向量表

  由于arm处理器处理中断的方式:cpu执行完当前指令,检测到中断后,会自动将工作模式切换到对应的中断模式,然后将pc指向中断向量表中对应中断的中断向量。

  因此,软件需要创建一个中断向量表,并通过设置协处理器cp15告诉cpu中断向量表的位置。

  中断向量表定义在entry-armv.S文件中:

.L__vectors_start:
    W(b)    vector_rst
    W(b)    vector_und
    W(ldr)    pc, .L__vectors_start + 0x1000
    W(b)    vector_pabt
    W(b)    vector_dabt
    W(b)    vector_addrexcptn
    W(b)    vector_irq
    W(b)    vector_fiq

  __vectors_start的地址定义在vmlinux.lds.S文件中,这个是默认地址,内核初始化过程中会重新设置

__vectors_start = .;
    .vectors 0xffff0000 : AT(__vectors_start) {
        *(.vectors)
    }
    . = __vectors_start + SIZEOF(.vectors);
    __vectors_end = .;

    __stubs_start = .;
    .stubs ADDR(.vectors) + 0x1000 : AT(__stubs_start) {
        *(.stubs)
    }
    . = __stubs_start + SIZEOF(.stubs);
    __stubs_end = .;

  devicemaps_init函数中会申请内存,并将中断向量表拷贝到内存,然后,根据协处理器cp15的寄存器C1的bit[13]是否被置1,将中断向量表的物理地址映射到0xffff0000或者0x0,详细函数调用流程如下图所示:

3.2 中断向量函数的实现

  中断向量表中的跳转函数都通过.macro   vector_stub, name, mode, correction=0宏进行定义,该宏具体如下:

/*
 * Vector stubs.
 *
 * This code is copied to 0xffff1000 so we can use branches in the
 * vectors, rather than ldr's.  Note that this code must not exceed
 * a page size.
 *
 * Common stub entry macro:
 *   Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
 *
 * SP points to a minimal amount of processor-private memory, the address
 * of which is copied into r0 for the mode specific abort handler.
 */
    .macro    vector_stub, name, mode, correction=0
    .align    5

vector_\name:
    .if \correction
    sub    lr, lr, #\correction
    .endif

    @
    @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
    @ (parent CPSR)
    @
    stmia    sp, {r0, lr}        @ save r0, lr
    mrs    lr, spsr
    str    lr, [sp, #8]        @ save spsr

    @
    @ Prepare for SVC32 mode.  IRQs remain disabled.
    @
    mrs    r0, cpsr
    eor    r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
    msr    spsr_cxsf, r0

    @
    @ the branch table must immediately follow this code
    @
    and    lr, lr, #0x0f
 THUMB(    adr    r0, 1f            )
 THUMB(    ldr    lr, [r0, lr, lsl #2]    )
    mov    r0, sp
 ARM(    ldr    lr, [pc, lr, lsl #2]    )
    movs    pc, lr            @ branch to handler in SVC mode
ENDPROC(vector_\name)

  以IRQ中断向量vector_irq为例,通过vector_stub irq, IRQ_MODE, 4宏定义,如下:

/*
 * Interrupt dispatcher
 */
    vector_stub    irq, IRQ_MODE, 4

    .long    __irq_usr            @  0  (USR_26 / USR_32)
    .long    __irq_invalid            @  1  (FIQ_26 / FIQ_32)
    .long    __irq_invalid            @  2  (IRQ_26 / IRQ_32)
    .long    __irq_svc            @  3  (SVC_26 / SVC_32)
    .long    __irq_invalid            @  4
    .long    __irq_invalid            @  5
    .long    __irq_invalid            @  6
    .long    __irq_invalid            @  7
    .long    __irq_invalid            @  8
    .long    __irq_invalid            @  9
    .long    __irq_invalid            @  a
    .long    __irq_invalid            @  b
    .long    __irq_invalid            @  c
    .long    __irq_invalid            @  d
    .long    __irq_invalid            @  e
    .long    __irq_invalid            @  f

  于是,IRQ中断产生后就跳转到vector_stub   irq, IRQ_MODE, 4。

  以程序工作在用户模式时被中断为例,在vector_irq函数中,会将处理器的运行模式切换到SVC模式,然后,进入__irq_svc,保存处理器被中断前的硬件上下文,最后,跳转到中断控制器驱动初始化时绑定的中断入口函数handle_arch_irq(进入这个函数时,cpu本地中断处于屏蔽状态),具体内容如下表:

/* 将r0、lr寄存器(lr就是被中断的下一条指令)以及spsr(用户模式的cpsr)保存到irq模式的栈 */
        stmia sp, {r0, lr}
        mrs lr, spsr --- /* 此时lr保存的是spsr,也就是用户模式的cpsr,也就是被中断前处理器所处的模式 */
        str lr, [sp, #8]
    /* 将spsr寄存器修改为svc模式,为切换到管理模式做准备 */
        mrs r0, cpsr  --- /* 此时中断模式的CPSR,中断还是屏蔽状态 */
        eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
        msr spsr_cxsf, r0
    /* 获取被中断前处理器所处的模式 */
        and lr, lr, #0x0f
        THUMB(adr r0, 1f)
        THUMB(ldr lr, [r0, lr, lsl #2])
    /* 让r0寄存器指向中断模式下堆栈的基地址 */
        mov r0, sp
    /* 将lr设置为__irq_usr(此时的lr只是作为临时寄存器使用),然后跳转到__irq_usr */
        ARM(ldr lr, [pc, lr, lsl #2])
        movs pc, lr  --- /* movs指令会同时将spsr_irq赋值给cpsr,从而实现向svc模式切换 */
__irq_usr:
/* usr_entry进行irq_handler前,硬件上下文的保存*/
        sub sp, sp, #S_FRAME_SIZE --- /* 分配svc模式的栈 */
        /* r0-r12是所有模式公共的,保存到栈 */
        ARM(stmib sp, {r1 - r12})
        THUMB(stmia sp, {r0 - r12})
        /* 将之前保存在中断模式堆栈中的r0_usr,lr,spsr分别存储到r3-r5中 --- 当前r0是中断模式堆栈的基地址 */
        ldmia r0, {r3 - r5}
        add r0, sp, #S_PC
        mov r6, #-1
        /* 保存用户模式下的sp_usr,lr_usr */
        stmia r0, {r4 - r6}
        ARM(stmdb r0, {sp, lr}^)
        THUMB(store_user_sp_lr r0, r1, S_SP - S_PC)
    /* 进入irq_handler */
        irq_handler
            /*
             * Interrupt handling.
            */
                .macro    irq_handler
            #ifdef CONFIG_MULTI_IRQ_HANDLER
                ldr    r1, =handle_arch_irq  --- /* 然后,进入这个函数执行 */
                mov    r0, sp
                badr    lr, 9997f
                ldr    pc, [r1]
            #else
                arch_irq_handler_default
            #endif
            9997:
                .endm
    /* 恢复用户模式 */
        b ret_to_user_from_irq

3.3 中断控制器驱动

3.3.1 中断控制器驱动初始化

  

  如上图所示,中断控制器驱动主要围绕struct gic_chip_data结构体进行;该结构体中包含两个主要的结构体struct irq_chip和struct irq_domain,其中,struct irq_chip用来表示中断控制器芯片,一个系统中可能使用多片中断控制器,一片中断控制器用一个struct irq_chip结构体表示,这个结构体里面填充了中断控制器的操作函数,比如irq_enable和irq_disable;struct irq_domain用于硬件中断号和虚拟中断号的管理。

  中断控制器驱动,主要完成如下3部分功能:

  1. 将上层的中断处理函数绑定给中断向量里面的回调函数,比如为IRQ中断向量中的回调函数handle_arch_irq绑定为gic_handle_irq,还有SMP核间交互的回调函数gic_raise_softirq以及cpu热插拔的回调函数gic_starting_cpu;

  2. 提供中断控制器芯片的操作接口给上层模块调用;

  3. 中断控制器中每个中断以实际的硬件中断号标识,linux内核对每个中断以虚拟中断号标识,因此中断控制器驱动还要管理硬件中断号和虚拟中断号之间的映射。

  中断控制器的初始化流程如下图所示:

  中断控制器驱动初始化的入口函数是gic_of_init,这个函数放在IRQCHIP_DECLARE宏里面,内核对每一种中断控制器都声明一个IRQCHIP_DECLARE宏,如下,宏里面包含的compatible字段和中断控制器初始化入口函数,of_irq_init(__irqchip_of_table)函数中,将设备树中中断控制器节点的compatible字段和这些宏的compatible比较,找到该中断控制器对应的IRQCHIP_DECLARE宏,然后调用里面的回调函数gic_of_init进行中断控制器驱动的初始化;itop4412中断控制器的设备树节点如下,compatible字段是"arm,cortex-a9-gic",于是和IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init)匹配。

IRQCHIP_DECLARE(gic_400, "arm,gic-400", gic_of_init);
IRQCHIP_DECLARE(arm11mp_gic, "arm,arm11mp-gic", gic_of_init);
IRQCHIP_DECLARE(arm1176jzf_dc_gic, "arm,arm1176jzf-devchip-gic", gic_of_init);
IRQCHIP_DECLARE(cortex_a15_gic, "arm,cortex-a15-gic", gic_of_init);
IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init);
IRQCHIP_DECLARE(cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init);
IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init);
IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init);
IRQCHIP_DECLARE(pl390, "arm,pl390", gic_of_init);

  itop4412中断控制器设备树节点:

gic: interrupt-controller@10490000 {
                compatible = "arm,cortex-a9-gic";
                #interrupt-cells = <3>;
                interrupt-controller;
                reg = <0x10490000 0x10000>, <0x10480000 0x10000>;
};

3.3.2 中断管理

  每个硬件中断在中断控制器中有个固定的硬件中断号,在linux内核中对应一个虚拟中断号,多个外设可以共享一条硬件中断线,那么一个虚拟中断号就可以挂接多个中断处理函数以对应多个外设。

  每个硬件中断用中断描述符struct irq_desc描述,这个结构体里面主要包含如下几个内容:

  1. struct irq_domain结构体,负责硬件中断号和虚拟中断号的映射管理;

  2. struct irq_chip结构体,中断控制器芯片的操作函数;

  3. irq_flow_handler_t handle_irq函数,中断产生后,中断向量的回调函数中最终会调用到这个函数;

  4. struct irqaction结构体,用户注册中断时,注册接口会创建一个irqaction结构体,将用户注册的中断处理函数绑定到这个结构体,然后放到中断描述符irq_desc的struct irqaction链表里面,当中断产生时,处理函数irq_flow_handler就会遍历这个链表,逐个处理用户注册的中断处理。

  用户通过request_irq/request_threaded_irq函数注册中断处理函数,在这个函数里面会根据虚拟中断号获取中断描述符,分配action结构体,并填充,包括中断回调函数、线程回调函数等,进行线程化处理逻辑,如果是非共享中断,进行开中断以及cpu亲和性等处理,然后将action结构体放到中断描述符的链表尾部,最后还会在proc文件系统中添加相关信息,具体过程如下:

/*
 * 获取中断描述符
 * 分配action结构体,并填充,包括中断回调函数、线程回调函数等
 * 创建内核线程
 * 如果是非共享中断,进行开中断以及cpu亲和性等处理
 * 将action结构体放到中断描述符的链表尾部
 * 在proc文件系统中添加相关信息
*/
request_irq
    request_threaded_irq
        /*
         * 虚拟中断号获取中断描述符
        */
        desc = irq_to_desc(irq);
        /*
         * 分配结构体struct irqaction,并填充
        */
        action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
        action->handler = handler;
        action->thread_fn = thread_fn;
        action->flags = irqflags;
        action->name = devname;
        action->dev_id = dev_id;
        /*
         * __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
         * 中断注册主要处理函数
         * irq -> 虚拟中断号
         * desc -> 中断描述符
         * action -> 前面kzalloc分配的结构体
        */
        __setup_irq(irq, desc, action)
            new->irq = irq;
            nested = irq_settings_is_nested_thread(desc);
            if (nested)
                /* 如果中断绑定在其他中断线程中,需要特别处理 */
                new->handler = irq_nested_primary_handler;
            else
                /* 判断是否能线程化,进行强制线程化处理 */
                if (irq_settings_can_thread(desc))
                    /*
                     * 强制线程化处理
                     * 填充前面分配的action结构体
                     * 创建secondary action
                    */
                    irq_setup_forced_threading(new)
                        new->flags |= IRQF_ONESHOT;
                        new->secondary = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
                        set_bit(IRQTF_FORCED_THREAD, &new->thread_flags);
                        /*
                         * 1、将线程回调函数设置成中断处理函数new->handler
                         * 2、将中断处理函数设置成默认的处理函数irq_default_primary_handler
                         * ??? 为什么要这样设置???
                        */
                        new->thread_fn = new->handler;
                        new->handler = irq_default_primary_handler;
            /*
             * 创建一个内核线程
            */
            if (new->thread_fn && !nested)
                setup_irq_thread(new, irq, false)
                    t = kthread_create(irq_thread, new, "irq/%d-%s", irq, new->name);
                    /* 设置线程的调度策略 */
                    sched_setscheduler_nocheck(t, SCHED_FIFO, &param);
            /*
             * 中断描述符的action链表为空,也就是第一次挂接action,需要分配资源
            */
            if (!desc->action)
                irq_request_resources(desc)
                    struct irq_data *d = &desc->irq_data;
                    struct irq_chip *c = d->chip;
                    /* 调用gic驱动的回调函数申请资源 */
                    c->irq_request_resources ? c->irq_request_resources(d) : 0;
            /*
             * 共享中断
             * 在已经挂接action的情况下,将当前action挂接到链表中
            */
            old_ptr = &desc->action;
            old = *old_ptr;
            if (old)
                do {
                    /*
                     * Or all existing action->thread_mask bits,
                     * so we can find the next zero bit for this
                     * new action.
                     */
                    thread_mask |= old->thread_mask;
                    old_ptr = &old->next;
                    old = *old_ptr;
                } while (old);  --- /* 循环结束后,old指向链表尾部,指针内容为NULL */
                shared = 1;
            /*
             * 非共享中断要特殊处理
            */
            if (!shared)
                init_waitqueue_head(&desc->wait_for_threads);
                __irq_set_trigger(desc, new->flags & IRQF_TRIGGER_MASK);
                irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
                if (new->flags & IRQF_PERCPU)
                    irqd_set(&desc->irq_data, IRQD_PER_CPU);
                    irq_settings_set_per_cpu(desc);
                /* Exclude IRQ from balancing if requested */
                if (new->flags & IRQF_NOBALANCING)
                    irq_settings_set_no_balancing(desc);
                    irqd_set(&desc->irq_data, IRQD_NO_BALANCING);
                if (irq_settings_can_autoenable(desc))
                    /*
                     * 开启中断,并进行亲和性设置
                    */
                    irq_startup(desc, IRQ_RESEND, IRQ_START_COND)
                        if (irqd_is_started(d)) {
                            irq_enable(desc);
                        } else {
                            switch (__irq_startup_managed(desc, aff, force)) {
                            case IRQ_STARTUP_NORMAL:
                                ret = __irq_startup(desc);
                                irq_setup_affinity(desc);
                                break;
                            case IRQ_STARTUP_MANAGED:
                                irq_do_set_affinity(d, aff, false);
                                ret = __irq_startup(desc);
                                break;
                            case IRQ_STARTUP_ABORT:
                                return 0;
                            }
                        }
            /* 将action结构体放到中断描述符链表的尾部 */
            *old_ptr = new;
            /*
             * 将内核线程设置为可执行状态
            */
            wake_up_process(new->thread);
            /* 在proc文件系统中添加相关信息 */
            register_irq_proc(irq, desc);
            irq_add_debugfs_entry(irq, desc);
            new->dir = NULL;
            register_handler_proc(irq, new);

3.4 完整的中断处理流程

  处理器检测到中断后,会将工作模式切换到对应的中断模式,然后将pc指向中断向量,从而跳转到中断向量执行;在中断向量中,软件会将工作模式切换到SVC模式,保存硬件上下文,然后跳转到控制器驱动初始化时注册的回调函数;在这个回调函数里面会通过硬件中断号找到对应的虚拟中断号,从而找到该中断对应的中断描述符irq_desc;最后依次调用中断描述符irq_desc中用户注册的中断处理函数。

  以处理器工作在用户模式被IRQ中断为例,具体流程如下:

1. 处理器切换到IRQ中断模式,跳转到中断向量vector_irq
    W(b)    vector_irq
    Vector_irq展开后为宏定义vector_stub    irq, IRQ_MODE, 4
2. 在vector_irq中,将处理器模式切换到SVC模式(需要指出的时,此时的IRQ中断通过CPSR寄存器被屏蔽掉了),跳转到__irq_usr
    将lr设置为__irq_usr(此时的lr只是作为临时寄存器使用),然后跳转到__irq_usr
        ARM(ldr lr, [pc, lr, lsl #2])
        movs pc, lr  --- movs指令会同时将spsr_irq赋值给cpsr,从而实现向svc模式切换
3. 在__irq_usr中,调用irq_handler,然后,在irq_handler里面跳转到handle_arch_irq,handle_arch_irq这个函数指针在中断控制器驱动初始化的时候被赋值为gic_handle_irq
    set_handle_irq(gic_handle_irq)
    void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
        handle_arch_irq = handle_irq;
4. gic_handle_irq这个函数里面会进行循环处理完所有待处理的中断,
从中断控制器的寄存器读取硬件中断号,
    irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
    irqnr = irqstat & GICC_IAR_INT_ID_MASK;
    当硬件中断号小于16,表示核间IPI中断,调用handle_IPI
        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);
#endif
            continue;
        }
    当硬件中断号大于15小于1020,表示共享和CPU私有中断,调用handle_domain_irq
        if (likely(irqnr > 15 && irqnr < 1020)) {
            if (static_key_true(&supports_deactivate))
                writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
            isb();
            handle_domain_irq(gic->domain, irqnr, regs);
            continue;
        }
    当读取的硬件中断号无效,则退出while循环
5. IRQ中断,进入handle_domain_irq函数
6. handle_domain_irq函数中,首先会调用irq_enter函数进入中断上下文
    irq_enter();
7. irq_enter函数,将处理器preempt_count变量的HARDIRQ部分+1表示进入硬件中断上下文;系统会根据preempt_count变量来判断是否可以调度及抢占,只有preempt_count值为0时,才可以调度和抢占;那么handle_domain_irq函数在退出前,系统一直处于不可抢占状态,那么当前中断就一直使用被中断进程的上下文,比如内核栈、current以及preempt_count等都一直是被中断进程的上下文
    #define __irq_enter()                    \
    do {                        \
        account_irq_enter_time(current);    \
        preempt_count_add(HARDIRQ_OFFSET);    \
        trace_hardirq_enter();            \
    } while (0)
8. 取出虚拟中断号
    irq = irq_find_mapping(domain, hwirq)
9. 进一步调用上层的处理函数generic_handle_irq
10. 通过generic_handle_irq_desc调用中断描述符的handle_irq,在建立硬件中断号和虚拟中断号的映射关系时,gic_irq_domain_map函数中给handle_irq绑定了处理函数,
    硬件中断号小于32时,handle_irq被绑定为handle_percpu_devid_irq
        if (hw < 32) {
            irq_set_percpu_devid(irq);
            irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data,
                        handle_percpu_devid_irq, NULL, NULL);
            irq_set_status_flags(irq, IRQ_NOAUTOEN);
        }
    硬件中断号大于等于32时,handle_irq被绑定为handle_fasteoi_irq
        irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data,
                             handle_fasteoi_irq, NULL, NULL);
11. 进入handle_fasteoi_irq函数,使用raw_spin_lock对中断描述符访问加自旋锁
    raw_spin_lock(&desc->lock);
    修改中断状态
        desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
12. 如果用户没有注册中断处理函数(即action链表为空),或者该中断处于屏蔽状态,那么将中断状态修改为IRQS_PENDING,调用mask_irq在中断控制器级屏蔽该中断线,然后,退出handle_fasteoi_irq函数
    /*
     * If its disabled or no action available
     * then mask it and get out of here:
     */
    if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
        desc->istate |= IRQS_PENDING;
        mask_irq(desc);
        goto out;
    }
13. 如果是IRQS_ONESHOT类型中断,那么屏蔽该中断
    if (desc->istate & IRQS_ONESHOT)
        mask_irq(desc);
14. 进一步调用handle_irq_event
    handle_irq_event(desc);
15. 进入handle_irq_event函数
    将中断状态修改为IRQD_IRQ_INPROGRESS
        desc->istate &= ~IRQS_PENDING;
        irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
    进一步调用handle_irq_event_percpu
16. 进入handle_irq_event_percpu函数
    扫描action链表,依次调用用户注册的中断处理函数
        for_each_action_of_desc(desc, action)
            irqreturn_t res;
            /*
             * arm32处理器,此时读出的cpsr寄存器bit[7]被置1,说明cpu本地IRQ中断被禁止了,那就是不会发生IRQ硬件中断嵌套的情况了?
             */
            res = action->handler(irq, action->dev_id);
            根据返回值res判断,是否进行了线程化,如果进行了线程化,则唤醒对应的内核线程
                switch (res)
                    case IRQ_WAKE_THREAD:
                        __irq_wake_thread(desc, action);
            非线程化,则修改flag,然后返回
                case IRQ_HANDLED:
17. generic_handle_irq函数返回有,__handle_domain_irq会调用irq_exit函数退出中断上下文
    irq_exit();
18. 进入irq_exit函数
    如果本地中断没有被屏蔽,则会产生告警
        #ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED
            local_irq_disable();
#else
        WARN_ON_ONCE(!irqs_disabled());
#endif
    统计硬件中断退出的次数
        account_irq_exit_time(current);
    退出硬件中断上下文
        preempt_count_sub(HARDIRQ_OFFSET);
    如果不在硬件上下文,并且有软中断需要处理,那么开始执行软中断
        如果没有设置软中断强制线程化,那么直接调用软中断的回调函数执行,此时中断使用的栈还没有完全释放干净,因此使用的还是硬件堆栈
            __do_softirq();
        如果设置了软中断强制线程化,那么调度软中断内核线程运行,每个cpu都绑定了一个软中断内核线程
            wakeup_softirqd();

 

本文大部分内容参考如下博客,梳理总结只为自己更好地理解,如有侵权,请联系删除

参考资料:

https://www.cnblogs.com/LoyenWang/p/12996812.html

 

标签:__,handle,中断,irq,arm32,---,gic,子系统,desc
来源: https://www.cnblogs.com/lztutumo/p/16413238.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有