转自:http://blog.csdn.net/qq405180763/article/details/24120895

版权声明:本文为博主原创文章,未经博主允许不得转载。

最近在为3.8版本的Linux内核打RT_PREEMPT补丁,并且优化系统实时性,这篇文章主要对RTlinux中中断线程化部分进行分析。我们知道在RT_PREEMPT补丁中之所以要将中断线程化就是因为硬中断的实时性太高,会影响实时进程的实时性,所以需要将中断处理程序线程化并设置优先级,使中断处理线程的优先级比实时进程优先级低,从而提高系统实时性。

网上看到一些网友说在2.6.25.8版本的内核,linux引入了中断线程化,具体是不是2.6.25.8版本开始引入中断线程化我没有去求证,因为版本比较老了改动很多,但据我的查证从2.6.30开始内核引入request_threaded_irq函数,从这个版本开始可以通过在申请中断时为request_irq设置不同的参数决定是否线程化该中断。而在2.6.39版内核__setup_irq引入irq_setup_forced_threading函数,开始可以通过#  define force_irqthreads(true)强制使中断线程化,那么从这个版本开始想实现中断线程化就已经变得很简单了,让force_irqthreads为真即可,所以在3.8版本的实时补丁中,正是这一段代码实现了中断的线程化:

  1. #ifdef CONFIG_IRQ_FORCED_THREADING
  2. -extern bool force_irqthreads;
  3. +# ifndef CONFIG_PREEMPT_RT_BASE
  4. +   extern bool force_irqthreads;
  5. +# else
  6. +#  define force_irqthreads (true)
  7. +# endif
  8. #else
  9. -#define force_irqthreads   (0)
  10. +#define force_irqthreads   (false)
  11. #endif

下面我们开始正式介绍中断线程化是怎么实现的。

Linux内核常见申请中断的函数request_irq,在内核源码include/linux/interrupt.h头文件中可以看到request_irq仅包含return request_threaded_irq(irq, handler, NULL, flags, name, dev);调用,request_threaded_irq函数在源码目录kernel/irq/manage.c文件中,下面通过分析manage.c中各个相关函数解读中断线程化的实现过程。

根据request_irq的调用,首先分析request_threaded_irq

  1. int request_threaded_irq(unsigned int irq, irq_handler_t handler,
  2. irq_handler_t thread_fn, unsigned long irqflags,
  3. const char *devname, void *dev_id)
  4. {
  5. struct irqaction *action;
  6. struct irq_desc *desc;
  7. int retval;
  8. /*
  9. * Sanity-check: shared interrupts must pass in a real dev-ID,
  10. * otherwise we'll have trouble later trying to figure out
  11. * which interrupt is which (messes up the interrupt freeing
  12. * logic etc).
  13. */
  14. if ((irqflags & IRQF_SHARED) && !dev_id)    //共享中断必须有唯一确定的设备号,不然中断处理函数找不到发出中断请求的设备,注释写的很清楚
  15. return -EINVAL;
  16. desc = irq_to_desc(irq);
  17. if (!desc)
  18. return -EINVAL;
  19. if (!irq_settings_can_request(desc) ||
  20. WARN_ON(irq_settings_is_per_cpu_devid(desc)))
  21. return -EINVAL;
  22. if (!handler) { //handler和thread_fn都没有指针传入肯定是出错了,有thread_fn无handler则将irq_default_primary_handler给handler
  23. if (!thread_fn)
  24. return -EINVAL;
  25. handler = irq_default_primary_handler;
  26. }
  27. action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
  28. if (!action)
  29. return -ENOMEM;
  30. action->handler = handler;
  31. action->thread_fn = thread_fn;
  32. action->flags = irqflags;
  33. action->name = devname;
  34. action->dev_id = dev_id;
  35. chip_bus_lock(desc);
  36. retval = __setup_irq(irq, desc, action);    //在__setup_irq中确定是否线程化并完成中断处理函数绑定
  37. chip_bus_sync_unlock(desc);
  38. if (retval)
  39. kfree(action);
  40. #ifdef CONFIG_DEBUG_SHIRQ_FIXME
  41. if (!retval && (irqflags & IRQF_SHARED)) {
  42. /*
  43. * It's a shared IRQ -- the driver ought to be prepared for it
  44. * to happen immediately, so let's make sure....
  45. * We disable the irq to make sure that a 'real' IRQ doesn't
  46. * run in parallel with our fake.
  47. */
  48. unsigned long flags;
  49. disable_irq(irq);
  50. local_irq_save(flags);
  51. handler(irq, dev_id);
  52. local_irq_restore(flags);
  53. enable_irq(irq);
  54. }
  55. #endif
  56. return retval;
  57. }

request_threaded_irq函数基本上是将传入的参数放到action结构体,然后调用__setup_irq函数,线程化的具体过程在__setup_irq函数中

  1. static int
  2. __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
  3. {
  4. struct irqaction *old, **old_ptr;
  5. unsigned long flags, thread_mask = 0;
  6. int ret, nested, shared = 0;
  7. cpumask_var_t mask;
  8. if (!desc)
  9. return -EINVAL;
  10. if (desc->irq_data.chip == &no_irq_chip)
  11. return -ENOSYS;
  12. if (!try_module_get(desc->owner))
  13. return -ENODEV;
  14. /*
  15. * Check whether the interrupt nests into another interrupt
  16. * thread.
  17. */
  18. nested = irq_settings_is_nested_thread(desc);
  19. if (nested) {
  20. if (!new->thread_fn) {
  21. ret = -EINVAL;
  22. goto out_mput;
  23. }
  24. /*
  25. * Replace the primary handler which was provided from
  26. * the driver for non nested interrupt handling by the
  27. * dummy function which warns when called.
  28. */
  29. new->handler = irq_nested_primary_handler;
  30. } else {
  31. if (irq_settings_can_thread(desc))  //request_irq调用通过设置参数_IRQ_NOTHREAD=0线程化,
  32. //没有手动设置IRQ_NOTHREAD=1的中断都被线程化。Linux内核从2.6.39版本开始对中断线程化
  33. irq_setup_forced_threading(new);    //实时补丁使force_irqthreads=true,开启强制线程化中断
  34. }
  35. /*
  36. * Create a handler thread when a thread function is supplied
  37. * and the interrupt does not nest into another interrupt
  38. * thread.
  39. */
  40. if (new->thread_fn && !nested) {
  41. struct task_struct *t;
  42. static const struct sched_param param = {
  43. .sched_priority = MAX_USER_RT_PRIO/2,   //所有被线程化中断优先级都为50
  44. };
  45. t = kthread_create(irq_thread, new, "irq/%d-%s", irq,   //为中断创建内核线程
  46. new->name);
  47. if (IS_ERR(t)) {
  48. ret = PTR_ERR(t);
  49. goto out_mput;
  50. }
  51. sched_setscheduler(t, SCHED_FIFO, ¶m);
  52. /*
  53. * We keep the reference to the task struct even if
  54. * the thread dies to avoid that the interrupt code
  55. * references an already freed task_struct.
  56. */
  57. get_task_struct(t);
  58. new->thread = t;
  59. /*
  60. * Tell the thread to set its affinity. This is
  61. * important for shared interrupt handlers as we do
  62. * not invoke setup_affinity() for the secondary
  63. * handlers as everything is already set up. Even for
  64. * interrupts marked with IRQF_NO_BALANCE this is
  65. * correct as we want the thread to move to the cpu(s)
  66. * on which the requesting code placed the interrupt.
  67. */
  68. set_bit(IRQTF_AFFINITY, &new->thread_flags);
  69. }
  70. if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {
  71. ret = -ENOMEM;
  72. goto out_thread;
  73. }
  74. /*
  75. * Drivers are often written to work w/o knowledge about the
  76. * underlying irq chip implementation, so a request for a
  77. * threaded irq without a primary hard irq context handler
  78. * requires the ONESHOT flag to be set. Some irq chips like
  79. * MSI based interrupts are per se one shot safe. Check the
  80. * chip flags, so we can avoid the unmask dance at the end of
  81. * the threaded handler for those.
  82. */
  83. if (desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)
  84. new->flags &= ~IRQF_ONESHOT;
  85. /*
  86. * The following block of code has to be executed atomically
  87. */
  88. raw_spin_lock_irqsave(&desc->lock, flags);
  89. old_ptr = &desc->action;
  90. old = *old_ptr; //action和desc都是指针,用指向指针的指针获取action的地址,再使old指向action
  91. if (old) {  //如果该中断号的处理程序链表desc->action本身就是空,就无所谓共享了
  92. /*
  93. * Can't share interrupts unless both agree to and are
  94. * the same type (level, edge, polarity). So both flag
  95. * fields must have IRQF_SHARED set and the bits which
  96. * set the trigger type must match. Also all must
  97. * agree on ONESHOT.
  98. */
  99. if (!((old->flags & new->flags) & IRQF_SHARED) ||
  100. ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK) ||
  101. ((old->flags ^ new->flags) & IRQF_ONESHOT))
  102. goto mismatch;
  103. /* All handlers must agree on per-cpuness */
  104. if ((old->flags & IRQF_PERCPU) !=
  105. (new->flags & IRQF_PERCPU))
  106. goto mismatch;
  107. /* add new interrupt at end of irq queue */
  108. do {
  109. /*
  110. * Or all existing action->thread_mask bits,
  111. * so we can find the next zero bit for this
  112. * new action.
  113. */
  114. thread_mask |= old->thread_mask;
  115. old_ptr = &old->next;
  116. old = *old_ptr; //在desc->action链表中找到空指针,为里后面将new加进去
  117. } while (old);
  118. shared = 1;
  119. }
  120. /*
  121. * Setup the thread mask for this irqaction for ONESHOT. For
  122. * !ONESHOT irqs the thread mask is 0 so we can avoid a
  123. * conditional in irq_wake_thread().
  124. */
  125. if (new->flags & IRQF_ONESHOT) {
  126. /*
  127. * Unlikely to have 32 resp 64 irqs sharing one line,
  128. * but who knows.
  129. */
  130. if (thread_mask == ~0UL) {
  131. ret = -EBUSY;
  132. goto out_mask;
  133. }
  134. /*
  135. * The thread_mask for the action is or'ed to
  136. * desc->thread_active to indicate that the
  137. * IRQF_ONESHOT thread handler has been woken, but not
  138. * yet finished. The bit is cleared when a thread
  139. * completes. When all threads of a shared interrupt
  140. * line have completed desc->threads_active becomes
  141. * zero and the interrupt line is unmasked. See
  142. * handle.c:irq_wake_thread() for further information.
  143. *
  144. * If no thread is woken by primary (hard irq context)
  145. * interrupt handlers, then desc->threads_active is
  146. * also checked for zero to unmask the irq line in the
  147. * affected hard irq flow handlers
  148. * (handle_[fasteoi|level]_irq).
  149. *
  150. * The new action gets the first zero bit of
  151. * thread_mask assigned. See the loop above which or's
  152. * all existing action->thread_mask bits.
  153. */
  154. new->thread_mask = 1 << ffz(thread_mask);
  155. } else if (new->handler == irq_default_primary_handler &&
  156. !(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {
  157. /*
  158. * The interrupt was requested with handler = NULL, so
  159. * we use the default primary handler for it. But it
  160. * does not have the oneshot flag set. In combination
  161. * with level interrupts this is deadly, because the
  162. * default primary handler just wakes the thread, then
  163. * the irq lines is reenabled, but the device still
  164. * has the level irq asserted. Rinse and repeat....
  165. *
  166. * While this works for edge type interrupts, we play
  167. * it safe and reject unconditionally because we can't
  168. * say for sure which type this interrupt really
  169. * has. The type flags are unreliable as the
  170. * underlying chip implementation can override them.
  171. */
  172. pr_err("Threaded irq requested with handler=NULL and !ONESHOT for irq %d\n",
  173. irq);
  174. ret = -EINVAL;
  175. goto out_mask;
  176. }
  177. if (!shared) {  //中断处理链表为空,自己创建链表
  178. init_waitqueue_head(&desc->wait_for_threads);
  179. /* Setup the type (level, edge polarity) if configured: */
  180. if (new->flags & IRQF_TRIGGER_MASK) {
  181. ret = __irq_set_trigger(desc, irq,
  182. new->flags & IRQF_TRIGGER_MASK);
  183. if (ret)
  184. goto out_mask;
  185. }
  186. desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED | \
  187. IRQS_ONESHOT | IRQS_WAITING);
  188. irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
  189. if (new->flags & IRQF_PERCPU) {
  190. irqd_set(&desc->irq_data, IRQD_PER_CPU);
  191. irq_settings_set_per_cpu(desc);
  192. }
  193. if (new->flags & IRQF_ONESHOT)
  194. desc->istate |= IRQS_ONESHOT;
  195. if (irq_settings_can_autoenable(desc))
  196. irq_startup(desc, true);
  197. else
  198. /* Undo nested disables: */
  199. desc->depth = 1;
  200. /* Exclude IRQ from balancing if requested */
  201. if (new->flags & IRQF_NOBALANCING) {
  202. irq_settings_set_no_balancing(desc);
  203. irqd_set(&desc->irq_data, IRQD_NO_BALANCING);
  204. }
  205. if (new->flags & IRQF_NO_SOFTIRQ_CALL)
  206. irq_settings_set_no_softirq_call(desc);
  207. /* Set default affinity mask once everything is setup */
  208. setup_affinity(irq, desc, mask);
  209. } else if (new->flags & IRQF_TRIGGER_MASK) {
  210. unsigned int nmsk = new->flags & IRQF_TRIGGER_MASK;
  211. unsigned int omsk = irq_settings_get_trigger_mask(desc);
  212. if (nmsk != omsk)
  213. /* hope the handler works with current  trigger mode */
  214. pr_warning("irq %d uses trigger mode %u; requested %u\n",
  215. irq, nmsk, omsk);
  216. }
  217. new->irq = irq;
  218. *old_ptr = new; //添加到desc->action链表
  219. /* Reset broken irq detection when installing new handler */
  220. desc->irq_count = 0;
  221. desc->irqs_unhandled = 0;
  222. /*
  223. * Check whether we disabled the irq via the spurious handler
  224. * before. Reenable it and give it another chance.
  225. */
  226. if (shared && (desc->istate & IRQS_SPURIOUS_DISABLED)) {
  227. desc->istate &= ~IRQS_SPURIOUS_DISABLED;
  228. __enable_irq(desc, irq, false);
  229. }
  230. raw_spin_unlock_irqrestore(&desc->lock, flags);
  231. /*
  232. * Strictly no need to wake it up, but hung_task complains
  233. * when no hard interrupt wakes the thread up.
  234. */
  235. if (new->thread)
  236. wake_up_process(new->thread);    //内核线程开始运行
  237. register_irq_proc(irq, desc);   //创建/proc/irq/目录及文件(smp_affinity,smp_affinity_list 等 )
  238. new->dir = NULL;
  239. register_handler_proc(irq, new);
  240. free_cpumask_var(mask); //创建proc/irq/<irq>/handler/
  241. return 0;
  242. mismatch:
  243. if (!(new->flags & IRQF_PROBE_SHARED)) {
  244. pr_err("Flags mismatch irq %d. %08x (%s) vs. %08x (%s)\n",
  245. irq, new->flags, new->name, old->flags, old->name);
  246. #ifdef CONFIG_DEBUG_SHIRQ
  247. dump_stack();
  248. #endif
  249. }
  250. ret = -EBUSY;
  251. out_mask:
  252. raw_spin_unlock_irqrestore(&desc->lock, flags);
  253. free_cpumask_var(mask);
  254. out_thread:
  255. if (new->thread) {
  256. struct task_struct *t = new->thread;
  257. new->thread = NULL;
  258. kthread_stop(t);
  259. put_task_struct(t);
  260. }
  261. out_mput:
  262. module_put(desc->owner);
  263. return ret;
  264. }

__setup_irq的内容比较多点,首先通过nested判断该中断是否属于其他中断进程,即和别的中断共享同一个中断号,如果不是,判断是否强制将该中断线程化,很明显打了实时补丁后使能强制线程化中断,强制线程化如果thread_fn为空会使thread_fn指向handler,而handler指向默认的句柄函数,其实在强制中断线程化没有开启的情况下,request_threaded_irq函数根据thread_fn是否为空判断是否将该中断线程化。这里强制线程化后thread_fn显然不会为空。
接下来因为是首次在该中断线创建处理函数,申请一个内核线程,设置线程调度策略(FIFO)和优先级(50),为了让使用该中断号的其他进程共享这条中断线,还必须建立一个中断处理进程action的单向链表,设置一些共享标识等。但是如果现在申请的这个中断与其他已经建立中断内核线程的中断共享中断线,那么就不需要再次建立内核线程和队列,只需在队列中找到空指针(一般是末尾)并插入队列即可。做完这些之后唤醒内核进程(kthread_create)创建的内核进程不能马上执行,需要唤醒。

在Linux中申请中断还可以通过request_any_context_irq、devm_request_threaded_irq等函数,他们最终都调用request_threaded_irq,request_threaded_irq函数的完整形式如下:

  1. int request_threaded_irq(unsigned int irq, irq_handler_t handler,
  2. irq_handler_t thread_fn, unsigned long irqflags,
  3. const char *devname, void *dev_id)

在没有强制中断线程化的时候,thread_fn不为空即可将该中断线程化。

linux中断线程化分析【转】的更多相关文章

  1. 记一个实时Linux的中断线程化问题

    背景 有一个项目对实时性要求比较高,于是在linux内核上打了RT_PREEMPT补丁. 最终碰到的一个问题是,芯片本身性能不强,CPU资源不足,急需优化. 初步分析 看了下cpu占用率,除了主应用之 ...

  2. linux中断源码分析 - 中断发生(三)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 回顾 上篇文章linux中断源码分析 - 初始化(二)已经描述了中断描述符表和中断描述符数组的初始化,由于在初始 ...

  3. linux中断源码分析 - 软中断(四)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 在上一篇文章中,我们看到中断实际分为了两个部分,俗称就是一部分是硬中断,一部分是软中断.软中断是专门用于处理中断 ...

  4. linux中断源码分析 - 初始化(二)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 本篇文章主要讲述源码中是如何对中断进行一系列的初始化的. 回顾 在上一篇概述中,介绍了几个对于中断来说非常重要的 ...

  5. linux中断源码分析 - 概述(一)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 关于中断和异常 一般在书中都会把中断和异常一起说明,因为它们具有相同的特点,同时也有不同的地方.在CPU里,中断 ...

  6. Linux中断 - GIC代码分析

    一.前言 GIC(Generic Interrupt Controller)是ARM公司提供的一个通用的中断控制器,其architecture specification目前有四个版本,V1-V4(V ...

  7. 【转】Linux 中断学习之小试牛刀篇

    原文网址:http://www.linuxidc.com/Linux/2011-02/32129.htm 前言 在前面分析了中断的基本原理后,就可以写一个内核中断程序来体验以下,也可以借此程序继续深入 ...

  8. Linux中断管理 (1)Linux中断管理机制

    目录: <Linux中断管理> <Linux中断管理 (1)Linux中断管理机制> <Linux中断管理 (2)软中断和tasklet> <Linux中断管 ...

  9. Linux中断 - 驱动申请中断API

    一.前言 本文主要的议题是作为一个普通的驱动工程师,在撰写自己负责的驱动的时候,如何向Linux Kernel中的中断子系统注册中断处理函数?为了理解注册中断的接口,必须了解一些中断线程化(threa ...

随机推荐

  1. 苹果ATS特性服务器配置指南 HTTPS 安卓可以用 IOS 报错。

    解决方案:https://www.qcloud.com/document/product/400/6973 ATS检测:https://www.qcloud.com/product/ssl#userD ...

  2. 【bzoj1617】[Usaco2008 Mar]River Crossing渡河问题 dp

    题目描述 Farmer John以及他的N(1 <= N <= 2,500)头奶牛打算过一条河,但他们所有的渡河工具,仅仅是一个木筏. 由于奶牛不会划船,在整个渡河过程中,FJ必须始终在木 ...

  3. BZOJ4241 历史研究(莫队)

    如果分块的话与区间众数没有本质区别.这里考虑莫队. 显然莫队时的删除可以用堆维护,但多了一个log不太跑得过. 有一种叫回滚莫队的trick,可以将问题变为只有加入操作.按莫队时分的块依次处理,一块中 ...

  4. BZOJ1022 [SHOI2008]小约翰的游戏John 【博弈论】

    1022: [SHOI2008]小约翰的游戏John Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 3014  Solved: 1914 [Submi ...

  5. Java实验报告(实验四)

    北京电子科技学院(BESTI) 实     验    报     告 课程:Java    班级:1352班      姓名:王国伊    学号:20135207 成绩:             指导 ...

  6. X day2

    题目 官方题解 T1: 我们可以把问题化简为$a\times b \times c \leq n $中的有序$(a,b,c)$有多少组.分三种情况考虑 当$a=b=c$时,答案十分好统计 当$a< ...

  7. 使用snmp4j实现Snmp功能(一)

    相关链接:Snmp学习笔记使用snmp4j实现Snmp功能(一)使用snmp4j实现Snmp功能(二)使用snmp4j实现Snmp功能(三) 上一篇文章讲了Snmp的一些基本概念(Snmp学习笔记), ...

  8. Codeforces Round #331 (Div. 2) B. Wilbur and Array

    B. Wilbur and Array time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  9. 【题解】Matrix BZOJ 4128 矩阵求逆 离散对数 大步小步算法

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4128 大水题一道 使用大步小步算法,把数字的运算换成矩阵的运算就好了 矩阵求逆?这么基础的线 ...

  10. 优化Hadoop Balancer运行速度

    (如果运行hbase的话建议为16384),指定用于在DataNode间传输block数据的最大线程数,老版本的对应参数为dfs.datanode.max.xcievers 2.修改dfs.datan ...