so_brave 发表于 2011-12-28 16:27

linux内核中断、异常 2.................

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;
} 中断相应和服务



cu_Cbear 发表于 2011-12-28 16:36

楼主辛苦
页: [1]
查看完整版本: linux内核中断、异常 2.................