• linux中断线程化分析【转】


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

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

    __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不为空即可将该中断线程化。

  • 相关阅读:
    理解Unity3d的ForceMode | Understanding ForceMode in Unity3D
    Jexus 网站服务器和 ASP.NET 跨平台开发
    ASP.NET 5 改名 ASP.NET Core 1.0
    计算机文件基本上分为二种:二进制文件和 ASCII(也称纯文本文件)
    分布式系统与集群区别
    网站缓存技术(Redis、Memcached、Ehcache)
    Node.JS
    深入浅出Node.js(一):什么是Node.js
    让我欲罢不能的node.js
    为什么我要用 Node.js? 案例逐一介绍
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/7457910.html
Copyright © 2020-2023  润新知