- 论坛徽章:
- 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[irq]->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[irq]结构中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[irq]->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[irq]结构中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;
- }
复制代码 中断相应和服务
|
|