我是靠谱客的博主 伶俐黑猫,最近开发中收集的这篇文章主要介绍kernel 中断分析之四——中断申请 [下]前言__setup_irq——线程化处理__setup_irq——创建irq handler thread__setup_irq——添加new irqaction_setup_irq——收尾工作nested irq,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言

在kernel 中断分析之四——中断申请 [上]中,request_irq、request_threaded_irq、setup_irq、setup_percpu_irq、request_percpu_irq最终都调用了__setup_irq,本篇对该API进行分析,由于代码比较长,分段分析。

请注意,在分析过程中,遇到一些拿捏不定的地方,以用粗体表示,如果有理解错误,欢迎指正。

__setup_irq——线程化处理

   895 /*
   896  * Internal function to register an irqaction - typically used to
   897  * allocate special interrupts that are part of the architecture.
   898  */
   899 static int
   900 __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
   901 {
   902     struct irqaction *old, **old_ptr;
   903     unsigned long flags, thread_mask = 0;
   904     int ret, nested, shared = 0;
   905     cpumask_var_t mask;
   906
   907     if (!desc)
   908         return -EINVAL;
   909
   910     if (desc->irq_data.chip == &no_irq_chip)
   911         return -ENOSYS;
   912     if (!try_module_get(desc->owner))
   913         return -ENODEV;
   914
   915     /*
   916      * Check whether the interrupt nests into another interrupt
   917      * thread.
   918      */
   919     nested = irq_settings_is_nested_thread(desc);    //判断是否是嵌套中断线程,关于中断嵌套的处理,在后续有分析
   920     if (nested) {
   921         if (!new->thread_fn) {    //嵌套中断不需要有handler,但是thread_fn要有的
   922             ret = -EINVAL;
   923             goto out_mput;
   924         }
   925         /*
   926          * Replace the primary handler which was provided from
   927          * the driver for non nested interrupt handling by the
   928          * dummy function which warns when called.
   929          */
   930         new->handler = irq_nested_primary_handler; //抛出一个警告,nested irq的调用时父中断的handler中处理的,而不是在这里
   931     } else {
   932         if (irq_settings_can_thread(desc))  //有的中断不允许线程化,设置了IRQ_NOTHREAD标志
   933             irq_setup_forced_threading(new); //强制线程化
   934     }

irq_setup_forced_threading

   879 static void irq_setup_forced_threading(struct irqaction *new)
   880 {
   881     if (!force_irqthreads)  //该全局变量用于表示系统是否允许中断线程化
   882         return;
   883     if (new->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT)) //IRQF_NO_THREAD 或者percpu中断或者已经线程化的中断,直接返回
   884         return;
   885
   886     new->flags |= IRQF_ONESHOT;  //线程化中断需要设置IRQF_ONESHOT标志
   887//如果thread为NULL(那么handler必不为NULL),此时为了线程化,强制将handler赋给thread_fn,handler设置为irq_default_primary_handler
   888     if (!new->thread_fn) { 
   889         set_bit(IRQTF_FORCED_THREAD, &new->thread_flags); //thread_flags设置IRQTF_FORCED_THREAD,表示经过强制线程化
   890         new->thread_fn = new->handler;
   891         new->handler = irq_default_primary_handler;
   892     }
   893 }

我对上述代码的理解,不一定正确:目前的内核实现机制,非nested irq,且非IRQ_NOTHREAD,内核都会将其强制线程化。
以下是IRQF_ONESHOT 的注释:

 52  * IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
 53  *                Used by threaded interrupts which need to keep the
 54  *                irq line disabled until the threaded handler has been run.

另外摘录upstream上IRQF_ONESHOT的commit message如下:

For threaded interrupt handlers we expect the hard interrupt handler
part to mask the interrupt on the originating device. The interrupt
line itself is reenabled after the hard interrupt handler has
executed.

This requires access to the originating device from hard interrupt
context which is not always possible. There are devices which can only
be accessed via a bus (i2c, spi, ...). The bus access requires thread
context. For such devices we need to keep the interrupt line masked
until the threaded handler has executed.

Add a new flag IRQF_ONESHOT which allows drivers to request that the
interrupt is not unmasked after the hard interrupt context handler has
been executed and the thread has been woken. The interrupt line is
unmasked after the thread handler function has been executed.

Note that for now IRQF_ONESHOT cannot be used with IRQF_SHARED to
avoid complex accounting mechanisms.

For oneshot interrupts the primary handler simply returns
IRQ_WAKE_THREAD and does nothing else. A generic implementation
irq_oneshot_primary_handler() is provided to avoid useless copies all
over the place.

所以有:
1. 在线程化中断处理函数中,hardirq在interrupt context中执行时是mask产生中断的外设的对应irq line的,完成硬中断操作后中断线被reenable;
2. 然而中断上下文中并不是能够access所有的外设,比如某些设备必须要通过i2c、spi等bus才能access,而bus access需要在进程上下文中才能实现(这里不太明白),也就是说,需要将处理中断的代码放到进程上下文中(我的理解),所以对于这些设备,在进程上下文中也要关闭中断;
3. IRQF_ONESHOT 应运而生,该flag允许中断线程运行期间对应的interrupt line一直是mask的。运行结束后unmask。
设置IRQF_ONESHOT 的情况下,在硬中断处理完毕后,仍然不能打开对应的中断(the irq line disabled),直到线程化handler处理完毕。

__setup_irq——创建irq handler thread

   936     /*
   937      * Create a handler thread when a thread function is supplied
   938      * and the interrupt does not nest into another interrupt
   939      * thread.
   940      */
   941     if (new->thread_fn && !nested) {
   942         struct task_struct *t;
   943         static const struct sched_param param = { 
   944             .sched_priority = MAX_USER_RT_PRIO/2,  //线程的优先级
   945         };
   946  //创建一个名为irq/irq-name的线程,该线程调用irq_thread,参数为新的irqaction,只是创建,并没有唤醒
   947         t = kthread_create(irq_thread, new, "irq/%d-%s", irq,  
   948                    new->name);
   949         if (IS_ERR(t)) {
   950             ret = PTR_ERR(t);
   951             goto out_mput;
   952         }
   953
   954         sched_setscheduler_nocheck(t, SCHED_FIFO, &param); //设置调度策略和优先级
   955
   956         /*
   957          * We keep the reference to the task struct even if
   958          * the thread dies to avoid that the interrupt code
   959          * references an already freed task_struct.
   960          */
   961         get_task_struct(t);  //将该线程的task_struct的reference加1,防止线程die以后task_struct被释放??
   962         new->thread = t;
   963         /* //这边的注释我没有看懂,为什么设置affinity对共享中断很重要,在irq_thread中会check affinity,到那个时候再看做了什么。
   964          * Tell the thread to set its affinity. This is
   965          * important for shared interrupt handlers as we do
   966          * not invoke setup_affinity() for the secondary
   967          * handlers as everything is already set up. Even for
   968          * interrupts marked with IRQF_NO_BALANCE this is
   969          * correct as we want the thread to move to the cpu(s)
   970          * on which the requesting code placed the interrupt.
   971          */
   972         set_bit(IRQTF_AFFINITY, &new->thread_flags);
   973     }

__setup_irq——添加new irqaction

   974
   975     if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {   
   976         ret = -ENOMEM;
   977         goto out_thread;
   978     }
   979//一些interrupt controller注册的时候就已经设置IRQCHIP_ONESHOT_SAFE,这种情况下驱动工程师不需要使用IRQF_ONESHOT
   980     /*
   981      * Drivers are often written to work w/o knowledge about the
   982      * underlying irq chip implementation, so a request for a
   983      * threaded irq without a primary hard irq context handler
   984      * requires the ONESHOT flag to be set. Some irq chips like
   985      * MSI based interrupts are per se one shot safe. Check the
   986      * chip flags, so we can avoid the unmask dance at the end of
   987      * the threaded handler for those.
   988      */
   989     if (desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE) 
   990         new->flags &= ~IRQF_ONESHOT;
   991
   992     /*
   993      * The following block of code has to be executed atomically
   994      */
   995     raw_spin_lock_irqsave(&desc->lock, flags);  //操作irqaction链表的时候要上锁
   996     old_ptr = &desc->action;
   997     old = *old_ptr;
   998     if (old) {   //irqaction链表不为空,说明存在共享中断的情况
   999         /*
  1000          * Can't share interrupts unless both agree to and are
  1001          * the same type (level, edge, polarity). So both flag
  1002          * fields must have IRQF_SHARED set and the bits which
  1003          * set the trigger type must match. Also all must
  1004          * agree on ONESHOT.
  1005          */
  1006         if (!((old->flags & new->flags) & IRQF_SHARED) ||
  1007             ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK) ||
  1008             ((old->flags ^ new->flags) & IRQF_ONESHOT))
  1009             goto mismatch;
  1010//共享中断必须有相同的属性(触发方式、oneshot、percpu等)
  1011         /* All handlers must agree on per-cpuness */
  1012         if ((old->flags & IRQF_PERCPU) !=
  1013             (new->flags & IRQF_PERCPU))
  1014             goto mismatch;
  1015//只是遍历irqaction链表得到thread_mask,每个bit代表一个irqaction,添加的操作在后面
  1016         /* add new interrupt at end of irq queue */ 
  1017         do {
  1018             /*
  1019              * Or all existing action->thread_mask bits,
  1020              * so we can find the next zero bit for this
  1021              * new action.
  1022              */
  1023             thread_mask |= old->thread_mask;  //循环结束之后系统thread_mask中置1的bit位表示所有之前挂载过得共享中断的irqaction
  1024             old_ptr = &old->next;
  1025             old = *old_ptr;
  1026         } while (old);
  1027         shared = 1;  //表示中断共享
  1028     }
  1029
  1030     /*
  1031      * Setup the thread mask for this irqaction for ONESHOT. For
  1032      * !ONESHOT irqs the thread mask is 0 so we can avoid a
  1033      * conditional in irq_wake_thread().
  1034      */
  1035     if (new->flags & IRQF_ONESHOT) {  //线程化处理函数
  1036         /*
  1037          * Unlikely to have 32 resp 64 irqs sharing one line,
  1038          * but who knows.
  1039          */
  1040         if (thread_mask == ~0UL) {  //已经超过了所能挂载的最大共享中断数目,退出。这里我觉得应该加一个警告,否则开发人员可能不知道发生了什么。
  1041             ret = -EBUSY;
  1042             goto out_mask;
  1043         }
  1044         /*
  1045          * The thread_mask for the action is or'ed to
  1046          * desc->thread_active to indicate that the
  1047          * IRQF_ONESHOT thread handler has been woken, but not
  1048          * yet finished. The bit is cleared when a thread
  1049          * completes. When all threads of a shared interrupt
  1050          * line have completed desc->threads_active becomes
  1051          * zero and the interrupt line is unmasked. See
  1052          * handle.c:irq_wake_thread() for further information.
  1053          *
  1054          * If no thread is woken by primary (hard irq context)
  1055          * interrupt handlers, then desc->threads_active is
  1056          * also checked for zero to unmask the irq line in the
  1057          * affected hard irq flow handlers
  1058          * (handle_[fasteoi|level]_irq).
  1059          *
  1060          * The new action gets the first zero bit of
  1061          * thread_mask assigned. See the loop above which or's
  1062          * all existing action->thread_mask bits.
  1063          */
  1064         new->thread_mask = 1 << ffz(thread_mask);   //找到第一个非0的bit作为新irqaction的bit标志位
  1065
  1066     } else if (new->handler == irq_default_primary_handler &&
  1067            !(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {
  1068         /*
  1069          * The interrupt was requested with handler = NULL, so
  1070          * we use the default primary handler for it. But it
  1071          * does not have the oneshot flag set. In combination
  1072          * with level interrupts this is deadly, because the
  1073          * default primary handler just wakes the thread, then
  1074          * the irq lines is reenabled, but the device still
  1075          * has the level irq asserted. Rinse and repeat....
  1076 //存在这样一种情况,handler为NULL,但是并没有使用ONESHOT flag注册,那么在唤醒线程执行后就不会disable对应的中断线,可能造成不停的重入.
  这也说明,在handler为NULL的情况下,驱动工程师应该指明ONESHOT,否则将不work
  1077          * While this works for edge type interrupts, we play
  1078          * it safe and reject unconditionally because we can't
  1079          * say for sure which type this interrupt really
  1080          * has. The type flags are unreliable as the
  1081          * underlying chip implementation can override them.
  1082          */
  1083         pr_err("Threaded irq requested with handler=NULL and !ONESHOT for irq %dn",
  1084                irq);
  1085         ret = -EINVAL;
  1086         goto out_mask;
  1087     }

_setup_irq——收尾工作

1088         if (!shared) { //非共享中断的情况下需要设置触发方式,共享中断的情况下,所有共享中断的irq的触发方式等相同
1089                 init_waitqueue_head(&desc->wait_for_threads); //初始化irqdesc的等待队列,作用后面会讲到
1090                //设置中断的触发方式,主要有以下几种IRQ_TYPE_EDGE_RISING、IRQ_TYPE_EDGE_FALLING、IRQ_TYPE_LEVEL_HIGH、IRQ_TYPE_LEVEL_LOW
1091                 /* Setup the type (level, edge polarity) if configured: */
1092                 if (new->flags & IRQF_TRIGGER_MASK) { //调用chip->irq_set_type进行设置
1093                         ret = __irq_set_trigger(desc, irq,
1094                                         new->flags & IRQF_TRIGGER_MASK);
1095
1096                         if (ret)
1097                                 goto out_mask;
1098                 }
1099
1100                 desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED | 
1101                                   IRQS_ONESHOT | IRQS_WAITING);
1102                 irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS); //清楚IRQD_IRQ_INPROGRESS标志位
1103
1104                 if (new->flags & IRQF_PERCPU) {    //percpu中断对应标志位的设置
1105                         irqd_set(&desc->irq_data, IRQD_PER_CPU);
1106                         irq_settings_set_per_cpu(desc);
1107                 }
1108
1109                 if (new->flags & IRQF_ONESHOT) //oneshot类型的中断标志位设置
1110                         desc->istate |= IRQS_ONESHOT;
1111
1112                 if (irq_settings_can_autoenable(desc))
1113                         irq_startup(desc, true); //desc->irq_data.chip->irq_startup
1114                 else
1115                         /* Undo nested disables: */
1116                         desc->depth = 1;
1117
1118                 /* Exclude IRQ from balancing if requested */
1119                 if (new->flags & IRQF_NOBALANCING) {
1120                         irq_settings_set_no_balancing(desc);
1121                         irqd_set(&desc->irq_data, IRQD_NO_BALANCING);
1122                 }
1123                    //设置默认亲和力
1124                 /* Set default affinity mask once everything is setup */ 
1125                 setup_affinity(irq, desc, mask);
1126
1127         } else if (new->flags & IRQF_TRIGGER_MASK) {
1128                 unsigned int nmsk = new->flags & IRQF_TRIGGER_MASK;
1129                 unsigned int omsk = irq_settings_get_trigger_mask(desc);
1130
1131                 if (nmsk != omsk)  
1132                         /* hope the handler works with current  trigger mode */
1133                         pr_warning("irq %d uses trigger mode %u; requested %un",
1134                                    irq, nmsk, omsk);
1135         }
1137         new->irq = irq;     //设置new irqaction的中断号
1138         *old_ptr = new;    //将new irqaction挂在irqaction链表最后
1139
1140         /* Reset broken irq detection when installing new handler */
1141         desc->irq_count = 0;   //新的irqaction插入后,这两个变量都要清0?
1142         desc->irqs_unhandled = 0;
1143
1144         /*
1145          * Check whether we disabled the irq via the spurious handler
1146          * before. Reenable it and give it another chance.
1147          */
1148         if (shared && (desc->istate & IRQS_SPURIOUS_DISABLED)) {
1149                 desc->istate &= ~IRQS_SPURIOUS_DISABLED;
1150                 __enable_irq(desc, irq, false);
1151         }
1152
1153         raw_spin_unlock_irqrestore(&desc->lock, flags);
1154//唤醒new->thread,严格意义上来说,并不需要唤醒,因为此时没有发生中断,但是hung task会检测sleep超过120s的task,然后报错。实际上,很多平台上都把hung task功能disable了......
1155         /*
1156          * Strictly no need to wake it up, but hung_task complains
1157          * when no hard interrupt wakes the thread up.
1158          */
1159         if (new->thread)
1160                 wake_up_process(new->thread);  
1161
1162         register_irq_proc(irq, desc);  //创建proc下的目录节点
1163         new->dir = NULL;
1164         register_handler_proc(irq, new);
1165         free_cpumask_var(mask);
1166
1167         return 0;

nested irq

关于前面提到的nested irq,目前知道的信息如下:
1. 多个中断共享一个中断线,该中断线被认为是父中断,共享的中断被认为是子中断;
2. 中断在执行过程中被打断,发生了嵌套,打断的环境是进程上下文,也就是在threaded irq中(handle_nested_irq - Handle a nested irq from a irq thread );
3. handle_nested_irq 处理的irq的类型是IRQ_NESTED_THREAD,父中断在初始化中断时调用irq_set_nested_thread设置;
4. 对于IRQ_NESTED_THREAD类型的threaded handler,__setup_irq中不会为其创建单独的线程,子中断在父中断的线程上下文中运行。
5. 多用在GPIO driver中

在内核中找了一个例子,代码如下:

276 static int adp5588_irq_setup(struct adp5588_gpio *dev)
277 {
...
291     for (gpio = 0; gpio < dev->gpio_chip.ngpio; gpio++) {
292         int irq = gpio + dev->irq_base;
293         irq_set_chip_data(irq, dev);
294         irq_set_chip_and_handler(irq, &adp5588_irq_chip,
295                      handle_level_irq);
296         irq_set_nested_thread(irq, 1); //初始化的时候父中断设置子中断的类型
....
306     }
307     //申请中断
308     ret = request_threaded_irq(client->irq,
309                    NULL,
310                    adp5588_irq_handler,
311                    IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
312                    dev_name(&client->dev), dev);
.....
328 }
//在中断线程中调用handle_nested_irq,然后调用子中断的action->thread_fn
243 static irqreturn_t adp5588_irq_handler(int irq, void *devid)
244 {
...
259             while (pending) {
260                 if (pending & (1 << bit)) {
261                     handle_nested_irq(dev->irq_base +
262                               (bank << 3) + bit);
263                     pending &= ~(1 << bit);
264
265                 }
266                 bit++;
267             }
.....
274 }

最后

以上就是伶俐黑猫为你收集整理的kernel 中断分析之四——中断申请 [下]前言__setup_irq——线程化处理__setup_irq——创建irq handler thread__setup_irq——添加new irqaction_setup_irq——收尾工作nested irq的全部内容,希望文章能够帮你解决kernel 中断分析之四——中断申请 [下]前言__setup_irq——线程化处理__setup_irq——创建irq handler thread__setup_irq——添加new irqaction_setup_irq——收尾工作nested irq所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部