歡迎您光臨本站 註冊首頁

linux內核中斷、異常 2

←手機掃碼閱讀     火星人 @ 2014-03-03 , reply:0

linux內核中斷、異常 2.................


異常返回

當執行異常處理的C函數終止時,程序執行一條jmp指令以跳轉到ret_from_exception函數(上面的error_code彙編函數)


view plaincopy to clipboardprint?01.ret_from_exception:  
02.    preempt_stop(CLBR_ANY)  
03.ret_from_intr:  
04.    GET_THREAD_INFO(%ebp)  
05.check_userspace:  
06.    movl PT_EFLAGS(%esp), %eax  # mix EFLAGS and CS  
07.    movb PT_CS(%esp), %al  
08.    andl $(X86_EFLAGS_VM | SEGMENT_RPL_MASK), %eax  
09.    cmpl $USER_RPL, %eax  
10.    /*當被中斷的程序在中斷髮生運行時在內核態*/  
11.    jb resume_kernel        # not returning to v8086 or userspace  
12.    /*在用戶空間時*/  
13.ENTRY(resume_userspace)  
14.    LOCKDEP_SYS_EXIT  
15.    DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt  
16.                    # setting need_resched or sigpending   
17.                    # between sampling and the iret   
18.    TRACE_IRQS_OFF  
19.    movl TI_flags(%ebp), %ecx  
20.    andl $_TIF_WORK_MASK, %ecx  # is there any work to be done on  
21.                    # int/exception return?   
22.    jne work_pending  
23.    jmp restore_all  
24.END(ret_from_exception)  
25.  
26.#ifdef CONFIG_PREEMPT   
27.ENTRY(resume_kernel)  
28.    DISABLE_INTERRUPTS(CLBR_ANY)  
29.    /*允許內核搶佔時,執行need_resched*/  
30.    cmpl $0,TI_preempt_count(%ebp)  # non-zero preempt_count ?  
31.    /*不等於0,被中斷的程序重新開始執行*/  
32.    jnz restore_all  
33.need_resched:  
34.    movl TI_flags(%ebp), %ecx   # need_resched set ?  
35.    testb $_TIF_NEED_RESCHED, %cl  
36.    jz restore_all  
37.    testl $X86_EFLAGS_IF,PT_EFLAGS(%esp)    # interrupts off (exception path) ?  
38.    jz restore_all  
39.    call preempt_schedule_irq  
40.    jmp need_resched  
41.END(resume_kernel)  
42.#endif   
43.    CFI_ENDPROC  
ret_from_exception:
        preempt_stop(CLBR_ANY)
ret_from_intr:
        GET_THREAD_INFO(%ebp)
check_userspace:
        movl PT_EFLAGS(%esp), %eax        # mix EFLAGS and CS
        movb PT_CS(%esp), %al
        andl $(X86_EFLAGS_VM | SEGMENT_RPL_MASK), %eax
        cmpl $USER_RPL, %eax
        /*當被中斷的程序在中斷髮生運行時在內核態*/
        jb resume_kernel                # not returning to v8086 or userspace
        /*在用戶空間時*/
ENTRY(resume_userspace)
        LOCKDEP_SYS_EXIT
        DISABLE_INTERRUPTS(CLBR_ANY)        # make sure we don't miss an interrupt
                                        # setting need_resched or sigpending
                                        # between sampling and the iret
        TRACE_IRQS_OFF
        movl TI_flags(%ebp), %ecx
        andl $_TIF_WORK_MASK, %ecx        # is there any work to be done on
                                        # int/exception return?
        jne work_pending
        jmp restore_all
END(ret_from_exception)

#ifdef CONFIG_PREEMPT
ENTRY(resume_kernel)
        DISABLE_INTERRUPTS(CLBR_ANY)
        /*允許內核搶佔時,執行need_resched*/
        cmpl $0,TI_preempt_count(%ebp)        # non-zero preempt_count ?
        /*不等於0,被中斷的程序重新開始執行*/
        jnz restore_all
need_resched:
        movl TI_flags(%ebp), %ecx        # need_resched set ?
        testb $_TIF_NEED_RESCHED, %cl
        jz restore_all
        testl $X86_EFLAGS_IF,PT_EFLAGS(%esp)        # interrupts off (exception path) ?
        jz restore_all
        call preempt_schedule_irq
        jmp need_resched
END(resume_kernel)
#endif
        CFI_ENDPROC 中斷請求初始化

對於每一個外設,要麼以靜態(聲明為 static 類型的全局變數)或動態(調用request_irq 函數)的方式向 Linux 內核註冊中斷處理程序。不管以何種方式註冊,都會聲明或分配一塊irqaction 結構(其中handler 指向中斷服務程序),然後調用setup_irq() 函數,將irq_desc_t 和irqaction 聯繫起來。irq_desc[]數組中每一項對應一個中斷向量,每一個中斷向量為一個irq_desc類型的變數,該變數中有個action指針,指向irqaction鏈表的首地址。也就是說多個中斷服務(irqaction)可以共享一個中斷向量,這些中斷服務以鏈表的方式依次鏈入,當中斷到來時,同一中斷向量中的所有中斷服務函數都會依次執行一遍。request_irq函數主要是實例化一個irqaction結構,這裡直接看setup_irq函數。

view plaincopy to clipboardprint?01./*
02. * Internal function to register an irqaction - typically used to
03. * allocate special interrupts that are part of the architecture.
04. */  
05. /*將中斷服務鏈入irq_desc->action中*/  
06.static int  
07.__setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)  
08.{  
09.    struct irqaction *old, **old_ptr;  
10.    const char *old_name = NULL;  
11.    unsigned long flags;  
12.    int nested, shared = 0;  
13.    int ret;  
14.  
15.    if (!desc)  
16.        return -EINVAL;  
17.  
18.    if (desc->chip == &no_irq_chip)  
19.        return -ENOSYS;  
20.    /*
21.     * Some drivers like serial.c use request_irq() heavily,
22.     * so we have to be careful not to interfere with a
23.     * running system.
24.     */  
25.    if (new->flags & IRQF_SAMPLE_RANDOM) {  
26.        /*
27.         * This function might sleep, we want to call it first,
28.         * outside of the atomic block.
29.         * Yes, this might clear the entropy pool if the wrong
30.         * driver is attempted to be loaded, without actually
31.         * installing a new handler, but is this really a problem,
32.         * only the sysadmin is able to do this.
33.         */  
34.        rand_initialize_irq(irq);  
35.    }  
36.  
37.    /* Oneshot interrupts are not allowed with shared */  
38.    if ((new->flags & IRQF_ONESHOT) && (new->flags & IRQF_SHARED))  
39.        return -EINVAL;  
40.  
41.    /*
42.     * Check whether the interrupt nests into another interrupt
43.     * thread.
44.     */  
45.     /*如果嵌套在另一個中斷線程中*/  
46.    nested = desc->status & IRQ_NESTED_THREAD;  
47.    if (nested) {  
48.        if (!new->thread_fn)  
49.            return -EINVAL;  
50.        /*
51.         * Replace the primary handler which was provided from
52.         * the driver for non nested interrupt handling by the
53.         * dummy function which warns when called.
54.         */  
55.        new->handler = irq_nested_primary_handler;  
56.    }  
57.  
58.    /*
59.     * Create a handler thread when a thread function is supplied
60.     * and the interrupt does not nest into another interrupt
61.     * thread.
62.     *//*如果提供了中斷線程*/  
63.    if (new->thread_fn && !nested) {  
64.        struct task_struct *t;  
65.        /*創建內核中斷線程*/  
66.        t = kthread_create(irq_thread, new, "irq/%d-%s", irq,  
67.                   new->name);  
68.        if (IS_ERR(t))  
69.            return PTR_ERR(t);  
70.        /*
71.         * We keep the reference to the task struct even if
72.         * the thread dies to avoid that the interrupt code
73.         * references an already freed task_struct.
74.         *//*增加使用計數*/  
75.        get_task_struct(t);  
76.        new->thread = t;  
77.    }  
78.  
79.    /*
80.     * The following block of code has to be executed atomically
81.     */  
82.    spin_lock_irqsave(&desc->lock, flags);  
83.    old_ptr = &desc->action;/*保存action鏈表頭*/  
84.    old = *old_ptr;  
85.    if (old) {/*如果鏈表不為空,也就是說該中斷號對應的有中斷服務函數*/  
86.        /*
87.         * Can't share interrupts unless both agree to and are
88.         * the same type (level, edge, polarity). So both flag
89.         * fields must have IRQF_SHARED set and the bits which
90.         * set the trigger type must match.
91.         */  
92.        if (!((old->flags & new->flags) & IRQF_SHARED) ||  
93.            ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {  
94.            old_name = old->name;  
95.            goto mismatch;  
96.        }  
97.  
98.#if defined(CONFIG_IRQ_PER_CPU)   
99.        /* All handlers must agree on per-cpuness */  
100.        if ((old->flags & IRQF_PERCPU) !=  
101.            (new->flags & IRQF_PERCPU))  
102.            goto mismatch;  
103.#endif   
104.  
105.        /* add new interrupt at end of irq queue */  
106.        do {/*這一步是遍歷到鏈表的最後一個
107.            old->next=NULL為止*/  
108.            old_ptr = &old->next;  
109.            old = *old_ptr;  
110.        } while (old);  
111.        shared = 1;/*共享*/  
112.    }  
113.  
114.    if (!shared) {  
115.        /*設置irq_desc結構中chip成員的還沒設置的指針
116.        ,讓它們指向一些默認函數*/  
117.        irq_chip_set_defaults(desc->chip);  
118.  
119.        init_waitqueue_head(&desc->wait_for_threads);  
120.  
121.        /* Setup the type (level, edge polarity) if configured: */  
122.        /*設置觸發方式*/  
123.        if (new->flags & IRQF_TRIGGER_MASK) {  
124.            ret = __irq_set_trigger(desc, irq,  
125.                    new->flags & IRQF_TRIGGER_MASK);  
126.  
127.            if (ret)  
128.                goto out_thread;  
129.        } else  
130.            compat_irq_chip_set_default_handler(desc);  
131.#if defined(CONFIG_IRQ_PER_CPU)   
132.        if (new->flags & IRQF_PERCPU)  
133.            desc->status |= IRQ_PER_CPU;  
134.#endif   
135.  
136.        desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_ONESHOT |  
137.                  IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);  
138.  
139.        if (new->flags & IRQF_ONESHOT)  
140.            desc->status |= IRQ_ONESHOT;  
141.  
142.        if (!(desc->status & IRQ_NOAUTOEN)) {  
143.            desc->depth = 0;  
144.            desc->status &= ~IRQ_DISABLED;  
145.            desc->chip->startup(irq);  
146.        } else  
147.            /* Undo nested disables: */  
148.            desc->depth = 1;  
149.  
150.        /* Exclude IRQ from balancing if requested */  
151.        if (new->flags & IRQF_NOBALANCING)  
152.            desc->status |= IRQ_NO_BALANCING;  
153.  
154.        /* Set default affinity mask once everything is setup */  
155.        setup_affinity(irq, desc);  
156.  
157.    } else if ((new->flags & IRQF_TRIGGER_MASK)  
158.            && (new->flags & IRQF_TRIGGER_MASK)  
159.                != (desc->status & IRQ_TYPE_SENSE_MASK)) {  
160.        /* hope the handler works with the actual trigger mode... */  
161.        pr_warning("IRQ %d uses trigger mode %d; requested %d\n",  
162.                irq, (int)(desc->status & IRQ_TYPE_SENSE_MASK),  
163.                (int)(new->flags & IRQF_TRIGGER_MASK));  
164.    }  
165.  
166.    new->irq = irq;/*將new的irq設置為irq*/  
167.    *old_ptr = new;/*將new鏈入鏈表中,這個從上面可以看到*/  
168.  
169.    /* Reset broken irq detection when installing new handler */  
170.    desc->irq_count = 0;  
171.    desc->irqs_unhandled = 0;  
172.  
173.    /*
174.     * Check whether we disabled the irq via the spurious handler
175.     * before. Reenable it and give it another chance.
176.     */  
177.    if (shared && (desc->status & IRQ_SPURIOUS_DISABLED)) {  
178.        desc->status &= ~IRQ_SPURIOUS_DISABLED;  
179.        __enable_irq(desc, irq, false);/*啟用中斷*/  
180.    }  
181.  
182.    spin_unlock_irqrestore(&desc->lock, flags);  
183.  
184.    /*
185.     * Strictly no need to wake it up, but hung_task complains
186.     * when no hard interrupt wakes the thread up.
187.     */  
188.    if (new->thread)  
189.        wake_up_process(new->thread);  
190.    /*下面為註冊proc文件系統對應的項*/  
191.    register_irq_proc(irq, desc);  
192.    new->dir = NULL;  
193.    register_handler_proc(irq, new);  
194.  
195.    return 0;  
196.  
197.mismatch:  
198.#ifdef CONFIG_DEBUG_SHIRQ   
199.    if (!(new->flags & IRQF_PROBE_SHARED)) {  
200.        printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq);  
201.        if (old_name)  
202.            printk(KERN_ERR "current handler: %s\n", old_name);  
203.        dump_stack();  
204.    }  
205.#endif   
206.    ret = -EBUSY;  
207.  
208.out_thread:  
209.    spin_unlock_irqrestore(&desc->lock, flags);  
210.    if (new->thread) {  
211.        struct task_struct *t = new->thread;  
212.  
213.        new->thread = NULL;  
214.        if (likely(!test_bit(IRQTF_DIED, &new->thread_flags)))  
215.            kthread_stop(t);  
216.        put_task_struct(t);  
217.    }  
218.    return ret;  
219.}  
/*
* Internal function to register an irqaction - typically used to
* allocate special interrupts that are part of the architecture.
*/
/*將中斷服務鏈入irq_desc->action中*/
static int
__setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{
        struct irqaction *old, **old_ptr;
        const char *old_name = NULL;
        unsigned long flags;
        int nested, shared = 0;
        int ret;

        if (!desc)
                return -EINVAL;

        if (desc->chip == &no_irq_chip)
                return -ENOSYS;
        /*
         * Some drivers like serial.c use request_irq() heavily,
         * so we have to be careful not to interfere with a
         * running system.
         */
        if (new->flags & IRQF_SAMPLE_RANDOM) {
                /*
                 * This function might sleep, we want to call it first,
                 * outside of the atomic block.
                 * Yes, this might clear the entropy pool if the wrong
                 * driver is attempted to be loaded, without actually
                 * installing a new handler, but is this really a problem,
                 * only the sysadmin is able to do this.
                 */
                rand_initialize_irq(irq);
        }

        /* Oneshot interrupts are not allowed with shared */
        if ((new->flags & IRQF_ONESHOT) && (new->flags & IRQF_SHARED))
                return -EINVAL;

        /*
         * Check whether the interrupt nests into another interrupt
         * thread.
         */
         /*如果嵌套在另一個中斷線程中*/
        nested = desc->status & IRQ_NESTED_THREAD;
        if (nested) {
                if (!new->thread_fn)
                        return -EINVAL;
                /*
                 * Replace the primary handler which was provided from
                 * the driver for non nested interrupt handling by the
                 * dummy function which warns when called.
                 */
                new->handler = irq_nested_primary_handler;
        }

        /*
         * Create a handler thread when a thread function is supplied
         * and the interrupt does not nest into another interrupt
         * thread.
         *//*如果提供了中斷線程*/
        if (new->thread_fn && !nested) {
                struct task_struct *t;
                /*創建內核中斷線程*/
                t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
                                   new->name);
                if (IS_ERR(t))
                        return PTR_ERR(t);
                /*
                 * We keep the reference to the task struct even if
                 * the thread dies to avoid that the interrupt code
                 * references an already freed task_struct.
                 *//*增加使用計數*/
                get_task_struct(t);
                new->thread = t;
        }

        /*
         * The following block of code has to be executed atomically
         */
        spin_lock_irqsave(&desc->lock, flags);
        old_ptr = &desc->action;/*保存action鏈表頭*/
        old = *old_ptr;
        if (old) {/*如果鏈表不為空,也就是說該中斷號對應的有中斷服務函數*/
                /*
                 * Can't share interrupts unless both agree to and are
                 * the same type (level, edge, polarity). So both flag
                 * fields must have IRQF_SHARED set and the bits which
                 * set the trigger type must match.
                 */
                if (!((old->flags & new->flags) & IRQF_SHARED) ||
                    ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
                        old_name = old->name;
                        goto mismatch;
                }

#if defined(CONFIG_IRQ_PER_CPU)
                /* All handlers must agree on per-cpuness */
                if ((old->flags & IRQF_PERCPU) !=
                    (new->flags & IRQF_PERCPU))
                        goto mismatch;
#endif

                /* add new interrupt at end of irq queue */
                do {/*這一步是遍歷到鏈表的最後一個
                        old->next=NULL為止*/
                        old_ptr = &old->next;
                        old = *old_ptr;
                } while (old);
                shared = 1;/*共享*/
        }

        if (!shared) {
                /*設置irq_desc結構中chip成員的還沒設置的指針
                ,讓它們指向一些默認函數*/
                irq_chip_set_defaults(desc->chip);

                init_waitqueue_head(&desc->wait_for_threads);

                /* Setup the type (level, edge polarity) if configured: */
                /*設置觸發方式*/
                if (new->flags & IRQF_TRIGGER_MASK) {
                        ret = __irq_set_trigger(desc, irq,
                                        new->flags & IRQF_TRIGGER_MASK);

                        if (ret)
                                goto out_thread;
                } else
                        compat_irq_chip_set_default_handler(desc);
#if defined(CONFIG_IRQ_PER_CPU)
                if (new->flags & IRQF_PERCPU)
                        desc->status |= IRQ_PER_CPU;
#endif

                desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_ONESHOT |
                                  IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);

                if (new->flags & IRQF_ONESHOT)
                        desc->status |= IRQ_ONESHOT;

                if (!(desc->status & IRQ_NOAUTOEN)) {
                        desc->depth = 0;
                        desc->status &= ~IRQ_DISABLED;
                        desc->chip->startup(irq);
                } else
                        /* Undo nested disables: */
                        desc->depth = 1;

                /* Exclude IRQ from balancing if requested */
                if (new->flags & IRQF_NOBALANCING)
                        desc->status |= IRQ_NO_BALANCING;

                /* Set default affinity mask once everything is setup */
                setup_affinity(irq, desc);

        } else if ((new->flags & IRQF_TRIGGER_MASK)
                        && (new->flags & IRQF_TRIGGER_MASK)
                                != (desc->status & IRQ_TYPE_SENSE_MASK)) {
                /* hope the handler works with the actual trigger mode... */
                pr_warning("IRQ %d uses trigger mode %d; requested %d\n",
                                irq, (int)(desc->status & IRQ_TYPE_SENSE_MASK),
                                (int)(new->flags & IRQF_TRIGGER_MASK));
        }

        new->irq = irq;/*將new的irq設置為irq*/
        *old_ptr = new;/*將new鏈入鏈表中,這個從上面可以看到*/

        /* Reset broken irq detection when installing new handler */
        desc->irq_count = 0;
        desc->irqs_unhandled = 0;

        /*
         * Check whether we disabled the irq via the spurious handler
         * before. Reenable it and give it another chance.
         */
        if (shared && (desc->status & IRQ_SPURIOUS_DISABLED)) {
                desc->status &= ~IRQ_SPURIOUS_DISABLED;
                __enable_irq(desc, irq, false);/*啟用中斷*/
        }

        spin_unlock_irqrestore(&desc->lock, flags);

        /*
         * Strictly no need to wake it up, but hung_task complains
         * when no hard interrupt wakes the thread up.
         */
        if (new->thread)
                wake_up_process(new->thread);
        /*下面為註冊proc文件系統對應的項*/
        register_irq_proc(irq, desc);
        new->dir = NULL;
        register_handler_proc(irq, new);

        return 0;

mismatch:
#ifdef CONFIG_DEBUG_SHIRQ
        if (!(new->flags & IRQF_PROBE_SHARED)) {
                printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq);
                if (old_name)
                        printk(KERN_ERR "current handler: %s\n", old_name);
                dump_stack();
        }
#endif
        ret = -EBUSY;

out_thread:
        spin_unlock_irqrestore(&desc->lock, flags);
        if (new->thread) {
                struct task_struct *t = new->thread;

                new->thread = NULL;
                if (likely(!test_bit(IRQTF_DIED, &new->thread_flags)))
                        kthread_stop(t);
                put_task_struct(t);
        }
        return ret;
} 中斷相應和服務



《解決方案》

樓主辛苦

[火星人 ] linux內核中斷、異常 2已經有1098次圍觀

http://coctec.com/docs/service/show-post-1306.html