概述
前言
在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, ¶m); //设置调度策略和优先级
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所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复