精华内容
下载资源
问答
  • 中断线程化的意义和如何注册一个有中断线程化的irq
    千次阅读
    2017-09-21 15:05:46
    中断线程化的意义在于:在 Linux 中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成实时任务得不到及时的处理。中断线程化之后,中断将作为内核线程运行而且被赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级。
    明白原理后对我们程序员来说如何如何注册一个中断线程化的irq呢?
    可以参考下面这段code
    源码在drivers/thermal/hisi_thermal.c 中的hisi_thermal_probe函数中
    	ret = devm_request_threaded_irq(&pdev->dev, data->irq,
    					hisi_thermal_alarm_irq,
    					hisi_thermal_alarm_irq_thread,
    					0, "hisi_thermal", data);
    	if (ret < 0) {
    		dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret);
    		return ret;
    	}
    
    看看devm_request_threaded_irq的原型
    int devm_request_threaded_irq(struct device *dev, unsigned int irq,
    			      irq_handler_t handler, irq_handler_t thread_fn,
    			      unsigned long irqflags, const char *devname,
    			      void *dev_id)
    这里的thread_fn就代表中断要执行的代码,其次对中断程序也有要求
    static irqreturn_t hisi_thermal_alarm_irq(int irq, void *dev)
    {
    	struct hisi_thermal_data *data = dev;
    //核心是关掉irq,并返回IRQ_WAKE_THREAD,这样才会执行thread_fn
    	disable_irq_nosync(irq);
    	data->irq_enabled = false;
    
    	return IRQ_WAKE_THREAD;
    }
    
    static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
    {
    	struct hisi_thermal_data *data = dev;
    	struct hisi_thermal_sensor *sensor;
    	int i;
    
    	mutex_lock(&data->thermal_lock);
    	sensor = &data->sensors[data->irq_bind_sensor];
    
    	dev_crit(&data->pdev->dev, "THERMAL ALARM: T > %d\n",
    		 sensor->thres_temp / 1000);
    	mutex_unlock(&data->thermal_lock);
    
    	for (i = 0; i < HISI_MAX_SENSORS; i++) {
    		if (!data->sensors[i].tzd)
    			continue;
    
    		thermal_zone_device_update(data->sensors[i].tzd,
    					   THERMAL_EVENT_UNSPECIFIED);
    	}
    //这里一般返回IRQ_HANDLED
    	return IRQ_HANDLED;
    }
    
    那为什么中断程序要返回IRQ_WAKE_THREAD 才会执行thread_fn呢?
    最终处理中断的函数为
    irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags)
    {
    	irqreturn_t retval = IRQ_NONE;
    	unsigned int irq = desc->irq_data.irq;
    	struct irqaction *action;
    
    	record_irq_time(desc);
    
    	for_each_action_of_desc(desc, action) {
    		irqreturn_t res;
    
    		trace_irq_handler_entry(irq, action);
    //这里会执行irq的回调函数,可以看到只有这里返回IRQ_WAKE_THREAD,才会调用__irq_wake_thread 来运行中断的线程
    		res = action->handler(irq, action->dev_id);
    		trace_irq_handler_exit(irq, action, res);
    
    		if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts\n",
    			      irq, action->handler))
    			local_irq_disable();
    
    		switch (res) {
    		case IRQ_WAKE_THREAD:
    			/*
    			 * Catch drivers which return WAKE_THREAD but
    			 * did not set up a thread function
    			 */
    			if (unlikely(!action->thread_fn)) {
    				warn_no_thread(irq, action);
    				break;
    			}
    //__irq_wake_thread->wake_up_process 来wakeup在调用devm_request_threaded_irq时候为线程建立的thread
    			__irq_wake_thread(desc, action);
    
    			/* Fall through to add to randomness */
    		case IRQ_HANDLED:
    			*flags |= action->flags;
    			break;
    
    		default:
    			break;
    		}
    
    		retval |= res;
    	}
    
    	return retval;
    }
    
    

    更多相关内容
  • Linux中断线程化分析及中断延时测试.pdf
  • 中断线程化

    千次阅读 2016-08-12 22:13:51
    最近在为3.8版本的Linux内核打RT_PREEMPT补丁,并且优化系统实时性,这篇文章主要对RTlinux中中断线程化部分进行分析。我们知道在RT_PREEMPT补丁中之所以要将中断线程化就是因为硬中断的实时性太高,会影响实时进程...

    最近在为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版本的实时补丁中,正是这一段代码实现了中断的线程化:

    [plain]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    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

    [plain]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    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函数中

    [plain]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    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\n",  
    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\n",  
    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)\n",  
    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函数的完整形式如下:

    [plain]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    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 中断线程化

    千次阅读 2017-05-27 12:24:01
    为什么要进行中断线程化? 在 Linux 中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成...

    为什么要进行中断线程化?
    在 Linux 中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成实时任务得不到及时的处理。中断线程化之后,中断将作为内核线程运行而且被赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级。这样,具有最高优先级的实时任务就能得到优先处理,即使在严重负载下仍有实时性保证。但是,并不是所有的中断都可以被线程化,比如时钟中断,主要用来维护系统时间以及定时器等,其中定时器是操作系统的脉搏,一旦被线程化,就有可能被挂起,这样后果将不堪设想,所以不应当被线程化。

    中断线程化的实现方法是:对于IRQ,在内核初始化阶段init(该函数在内核源码树的文件init/main.c中定义)调用init_hardirqs(该函数在内核源码树的文件kernel/irq/manage.c中定义)来为每一个IRQ创建一个内核线程,IRQ号为0 的中断赋予实时优先级49,IRQ号为1的赋予实时优先级48,依次类推直到25,因此任何IRQ线程的最低实时优先级为25。原来的do_IRQ被分解成两部分,架构相关的放在类似于arch/*/kernel/irq.c的文件中,名称仍然为do_IRQ,而架构独立的部分被放在IRQ子系统的位置 kernel/irq/handle.c中,名称为_do_IRQ。当发生中断时,CPU将执行do_IRQ来处理相应的中断,do_IRQ将做了必要的架构相关的处理后调用_do_IRQ。函数_do_IRQ将判断该中断是否已经被线程化(如果中断描述符的状态字段不包含SA_NODELAY标志说明中断被线程化了),如果是将唤醒相应的处理线程,否则将直接调用handle_IRQ_event(在IRQ子系统位置的kernel/irq /handle.c文件中)来处理。对于已经线程化的情况,中断处理线程被唤醒并开始运行后,将调用do_hardirq(在源码树的IRQ子系统位置的文件kernel/irq/manage.c中定义)来处理相应的中断,该函数将判断是否有中断需要被处理(中断描述符的状态标志 IRQ_INPROGRESS),如果有就调用handle_IRQ_event来处理。handle_IRQ_event将直接调用相应的中断处理句柄来完成中断处理。

    申请中断request_irq()request_threaded_irq()之间的区别?

    static inline int __must_check
    request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
        const char *name, void *dev)
    {
    return request_threaded_irq(irq, handler, NULL, flags, name, dev);
    }
    
    int request_threaded_irq(unsigned int irq, irq_handler_t handler,
             irq_handler_t thread_fn, unsigned long irqflags,
             const char *devname, void *dev_id)
    

    从定义可以看出request_irqrequest_threaded_irq的一个wrapper,只是将其中的thread_fn置为空。

    request_threaded_irq函数实现:

    
    /**
     *  request_threaded_irq - allocate an interrupt line
     *  @irq: Interrupt line to allocate
     *  @handler: Function to be called when the IRQ occurs.
     *        Primary handler for threaded interrupts
     *        If NULL and thread_fn != NULL the default
     *        primary handler is installed
     *  @thread_fn: Function called from the irq handler thread
     *          If NULL, no irq thread is created
     *  @irqflags: Interrupt type flags
     *  @devname: An ascii name for the claiming device
     *  @dev_id: A cookie passed back to the handler function
     */
    int request_threaded_irq(unsigned int irq, irq_handler_t handler,
                 irq_handler_t thread_fn, unsigned long irqflags,
                 const char *devname, void *dev_id)
    {
        struct irqaction *action;
        struct irq_desc *desc;
        int retval;
    
        if (irq == IRQ_NOTCONNECTED)
            return -ENOTCONN;
    
        /*
         * Sanity-check: shared interrupts must pass in a real dev-ID,
         * otherwise we'll have trouble later trying to figure out
         * which interrupt is which (messes up the interrupt freeing
         * logic etc).
         *
         * Also IRQF_COND_SUSPEND only makes sense for shared interrupts and
         * it cannot be set along with IRQF_NO_SUSPEND.
         */
        if (((irqflags & IRQF_SHARED) && !dev_id) ||
            (!(irqflags & IRQF_SHARED) && (irqflags & IRQF_COND_SUSPEND)) ||
            ((irqflags & IRQF_NO_SUSPEND) && (irqflags & IRQF_COND_SUSPEND)))
            return -EINVAL;
    
        desc = irq_to_desc(irq);
        if (!desc)
            return -EINVAL;
    
        if (!irq_settings_can_request(desc) ||
            WARN_ON(irq_settings_is_per_cpu_devid(desc)))
            return -EINVAL;
    
        if (!handler) {
            if (!thread_fn)
                return -EINVAL;
            handler = irq_default_primary_handler;
        }
    
        action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
        if (!action)
            return -ENOMEM;
    
        action->handler = handler;
        action->thread_fn = thread_fn;
        action->flags = irqflags;
        action->name = devname;
        action->dev_id = dev_id;
    
        chip_bus_lock(desc);
        retval = __setup_irq(irq, desc, action);
        chip_bus_sync_unlock(desc);
    
        if (retval) {
            kfree(action->secondary);
            kfree(action);
        }
    
    #ifdef CONFIG_DEBUG_SHIRQ_FIXME
        if (!retval && (irqflags & IRQF_SHARED)) {
            /*
             * It's a shared IRQ -- the driver ought to be prepared for it
             * to happen immediately, so let's make sure....
             * We disable the irq to make sure that a 'real' IRQ doesn't
             * run in parallel with our fake.
             */
            unsigned long flags;
    
            disable_irq(irq);
            local_irq_save(flags);
    
            handler(irq, dev_id);
    
            local_irq_restore(flags);
            enable_irq(irq);
        }
    #endif
        return retval;
    }

    其中调用了__setup_irq 函数,该函数内容如下:

    /*
     * Internal function to register an irqaction - typically used to
     * allocate special interrupts that are part of the architecture.
     */
    static int
    __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
    {
        struct irqaction *old, **old_ptr;
        unsigned long flags, thread_mask = 0;
        int ret, nested, shared = 0;
        cpumask_var_t mask;
    
        if (!desc)
            return -EINVAL;
    
        if (desc->irq_data.chip == &no_irq_chip)
            return -ENOSYS;
        if (!try_module_get(desc->owner))
            return -ENODEV;
    
        new->irq = irq;
    
        /*
         * Check whether the interrupt nests into another interrupt
         * thread.
         */
        nested = irq_settings_is_nested_thread(desc);
        if (nested) {
            if (!new->thread_fn) {
                ret = -EINVAL;
                goto out_mput;
            }
            /*
             * 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;
        } else {
            if (irq_settings_can_thread(desc)) {
                ret = irq_setup_forced_threading(new);
                if (ret)
                    goto out_mput;
            }
        }
    
        /*
         * 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) {
            ret = setup_irq_thread(new, irq, false);
            if (ret)
                goto out_mput;
            if (new->secondary) {
                ret = setup_irq_thread(new->secondary, irq, true);
                if (ret)
                    goto out_thread;
            }
        }
    
        if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {
            ret = -ENOMEM;
            goto out_thread;
        }
    
        /*
         * Drivers are often written to work w/o knowledge about the
         * underlying irq chip implementation, so a request for a
         * threaded irq without a primary hard irq context handler
         * requires the ONESHOT flag to be set. Some irq chips like
         * MSI based interrupts are per se one shot safe. Check the
         * chip flags, so we can avoid the unmask dance at the end of
         * the threaded handler for those.
         */
        if (desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)
            new->flags &= ~IRQF_ONESHOT;
    
        /*
         * The following block of code has to be executed atomically
         */
        raw_spin_lock_irqsave(&desc->lock, flags);
        old_ptr = &desc->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. Also all must
             * agree on ONESHOT.
             */
            if (!((old->flags & new->flags) & IRQF_SHARED) ||
                ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK) ||
                ((old->flags ^ new->flags) & IRQF_ONESHOT))
                goto mismatch;
    
            /* All handlers must agree on per-cpuness */
            if ((old->flags & IRQF_PERCPU) !=
                (new->flags & IRQF_PERCPU))
                goto mismatch;
    
            /* add new interrupt at end of irq queue */
            do {
                /*
                 * Or all existing action->thread_mask bits,
                 * so we can find the next zero bit for this
                 * new action.
                 */
                thread_mask |= old->thread_mask;
                old_ptr = &old->next;
                old = *old_ptr;
            } while (old);
            shared = 1;
        }
    
        /*
         * Setup the thread mask for this irqaction for ONESHOT. For
         * !ONESHOT irqs the thread mask is 0 so we can avoid a
         * conditional in irq_wake_thread().
         */
        if (new->flags & IRQF_ONESHOT) {
            /*
             * Unlikely to have 32 resp 64 irqs sharing one line,
             * but who knows.
             */
            if (thread_mask == ~0UL) {
                ret = -EBUSY;
                goto out_mask;
            }
            /*
             * The thread_mask for the action is or'ed to
             * desc->thread_active to indicate that the
             * IRQF_ONESHOT thread handler has been woken, but not
             * yet finished. The bit is cleared when a thread
             * completes. When all threads of a shared interrupt
             * line have completed desc->threads_active becomes
             * zero and the interrupt line is unmasked. See
             * handle.c:irq_wake_thread() for further information.
             *
             * If no thread is woken by primary (hard irq context)
             * interrupt handlers, then desc->threads_active is
             * also checked for zero to unmask the irq line in the
             * affected hard irq flow handlers
             * (handle_[fasteoi|level]_irq).
             *
             * The new action gets the first zero bit of
             * thread_mask assigned. See the loop above which or's
             * all existing action->thread_mask bits.
             */
            new->thread_mask = 1 << ffz(thread_mask);
    
        } else if (new->handler == irq_default_primary_handler &&
               !(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {
            /*
             * The interrupt was requested with handler = NULL, so
             * we use the default primary handler for it. But it
             * does not have the oneshot flag set. In combination
             * with level interrupts this is deadly, because the
             * default primary handler just wakes the thread, then
             * the irq lines is reenabled, but the device still
             * has the level irq asserted. Rinse and repeat....
             *
             * While this works for edge type interrupts, we play
             * it safe and reject unconditionally because we can't
             * say for sure which type this interrupt really
             * has. The type flags are unreliable as the
             * underlying chip implementation can override them.
             */
            pr_err("Threaded irq requested with handler=NULL and !ONESHOT for irq %d\n",
                   irq);
            ret = -EINVAL;
            goto out_mask;
        }
    
        if (!shared) {
            ret = irq_request_resources(desc);
            if (ret) {
                pr_err("Failed to request resources for %s (irq %d) on irqchip %s\n",
                       new->name, irq, desc->irq_data.chip->name);
                goto out_mask;
            }
    
            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,
                            new->flags & IRQF_TRIGGER_MASK);
    
                if (ret)
                    goto out_mask;
            }
    
            desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED | \
                      IRQS_ONESHOT | IRQS_WAITING);
            irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
    
            if (new->flags & IRQF_PERCPU) {
                irqd_set(&desc->irq_data, IRQD_PER_CPU);
                irq_settings_set_per_cpu(desc);
            }
    
            if (new->flags & IRQF_ONESHOT)
                desc->istate |= IRQS_ONESHOT;
    
            if (irq_settings_can_autoenable(desc))
                irq_startup(desc, true);
            else
                /* Undo nested disables: */
                desc->depth = 1;
    
            /* Exclude IRQ from balancing if requested */
            if (new->flags & IRQF_NOBALANCING) {
                irq_settings_set_no_balancing(desc);
                irqd_set(&desc->irq_data, IRQD_NO_BALANCING);
            }
    
            /* Set default affinity mask once everything is setup */
            setup_affinity(desc, mask);
    
        } else if (new->flags & IRQF_TRIGGER_MASK) {
            unsigned int nmsk = new->flags & IRQF_TRIGGER_MASK;
            unsigned int omsk = irq_settings_get_trigger_mask(desc);
    
            if (nmsk != omsk)
                /* hope the handler works with current  trigger mode */
                pr_warn("irq %d uses trigger mode %u; requested %u\n",
                    irq, nmsk, omsk);
        }
    
        *old_ptr = new;
    
        irq_pm_install_action(desc, 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->istate & IRQS_SPURIOUS_DISABLED)) {
            desc->istate &= ~IRQS_SPURIOUS_DISABLED;
            __enable_irq(desc);
        }
    
        raw_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);
        if (new->secondary)
            wake_up_process(new->secondary->thread);
    
        register_irq_proc(irq, desc);
        new->dir = NULL;
        register_handler_proc(irq, new);
        free_cpumask_var(mask);
    
        return 0;
    
    mismatch:
        if (!(new->flags & IRQF_PROBE_SHARED)) {
            pr_err("Flags mismatch irq %d. %08x (%s) vs. %08x (%s)\n",
                   irq, new->flags, new->name, old->flags, old->name);
    #ifdef CONFIG_DEBUG_SHIRQ
            dump_stack();
    #endif
        }
        ret = -EBUSY;
    
    out_mask:
        raw_spin_unlock_irqrestore(&desc->lock, flags);
        free_cpumask_var(mask);
    
    out_thread:
        if (new->thread) {
            struct task_struct *t = new->thread;
    
            new->thread = NULL;
            kthread_stop(t);
            put_task_struct(t);
        }
        if (new->secondary && new->secondary->thread) {
            struct task_struct *t = new->secondary->thread;
    
            new->secondary->thread = NULL;
            kthread_stop(t);
            put_task_struct(t);
        }
    out_mput:
        module_put(desc->owner);
        return ret;
    }

    其中函数setup_irq_thread 为中断创建了线程,函数内容如下:

    
    static int
    setup_irq_thread(struct irqaction *new, unsigned int irq, bool secondary)
    {
        struct task_struct *t;
        struct sched_param param = {
            .sched_priority = MAX_USER_RT_PRIO/2,
        };
    
        if (!secondary) {
            t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
                       new->name);
        } else {
            t = kthread_create(irq_thread, new, "irq/%d-s-%s", irq,
                       new->name);
            param.sched_priority -= 1;
        }
    
        if (IS_ERR(t))
            return PTR_ERR(t);
    
        sched_setscheduler_nocheck(t, SCHED_FIFO, &param);
    
        /*
         * 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;
        /*
         * Tell the thread to set its affinity. This is
         * important for shared interrupt handlers as we do
         * not invoke setup_affinity() for the secondary
         * handlers as everything is already set up. Even for
         * interrupts marked with IRQF_NO_BALANCE this is
         * correct as we want the thread to move to the cpu(s)
         * on which the requesting code placed the interrupt.
         */
        set_bit(IRQTF_AFFINITY, &new->thread_flags);
        return 0;
    }

    从函数中可以看到kthread_create 实现了中断线程的创建。

    申请中断其它函数原型还有 devm_request_threaded_irq

    
    /**
     *  devm_request_threaded_irq - allocate an interrupt line for a managed device
     */
    int devm_request_threaded_irq(struct device *dev, unsigned int irq,
                      irq_handler_t handler, irq_handler_t thread_fn,
                      unsigned long irqflags, const char *devname,
                      void *dev_id)
    {
        struct irq_devres *dr;
        int rc;
    
        dr = devres_alloc(devm_irq_release, sizeof(struct irq_devres),
                  GFP_KERNEL);
        if (!dr)
            return -ENOMEM;
    
        rc = request_threaded_irq(irq, handler, thread_fn, irqflags, devname,
                      dev_id);
        if (rc) {
            devres_free(dr);
            return rc;
        }
    
        dr->irq = irq;
        dr->dev_id = dev_id;
        devres_add(dev, dr);
    
        return 0;
    }

    可以看到devm_request_threaded 也是通过irqrequest_threaded_irq 来实现中断线程的。

    展开全文
  • linux的中断线程化实现[借鉴].pdf
  • linux中断线程化分析

    千次阅读 2014-04-21 15:12:26
    最近在为3.8版本的Linux内核打RT_PREEMPT补丁,并且优化系统实时性,这篇文章主要对RTlinux中中断线程化部分进行分析。我们知道在RT_PREEMPT补丁中之所以要将中断线程化就是因为硬中断的实时性太高,会影响实时进程...

    最近在为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版本的实时补丁中,正是这一段代码实现了中断的线程化:

    #ifdef CONFIG_IRQ_FORCED_THREADING
    -extern bool force_irqthreads;
    +# ifndef CONFIG_PREEMPT_RT_BASE
    +   extern bool force_irqthreads;
    +# else
    +#  define force_irqthreads	(true)
    +# endif
     #else
    -#define force_irqthreads	(0)
    +#define force_irqthreads	(false)
     #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

    int request_threaded_irq(unsigned int irq, irq_handler_t handler,
    			 irq_handler_t thread_fn, unsigned long irqflags,
    			 const char *devname, void *dev_id)
    {
    	struct irqaction *action;
    	struct irq_desc *desc;
    	int retval;
    
    	/*
    	 * Sanity-check: shared interrupts must pass in a real dev-ID,
    	 * otherwise we'll have trouble later trying to figure out
    	 * which interrupt is which (messes up the interrupt freeing
    	 * logic etc).
    	 */
    	if ((irqflags & IRQF_SHARED) && !dev_id)	//共享中断必须有唯一确定的设备号,不然中断处理函数找不到发出中断请求的设备,注释写的很清楚
    		return -EINVAL;
    
    	desc = irq_to_desc(irq);
    	if (!desc)
    		return -EINVAL;
    
    	if (!irq_settings_can_request(desc) ||
    	    WARN_ON(irq_settings_is_per_cpu_devid(desc)))
    		return -EINVAL;
    
    	if (!handler) {	//handler和thread_fn都没有指针传入肯定是出错了,有thread_fn无handler则将irq_default_primary_handler给handler
    		if (!thread_fn)
    			return -EINVAL;
    		handler = irq_default_primary_handler;
    	}
    
    	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
    	if (!action)
    		return -ENOMEM;
    
    	action->handler = handler;
    	action->thread_fn = thread_fn;
    	action->flags = irqflags;
    	action->name = devname;
    	action->dev_id = dev_id;
    
    	chip_bus_lock(desc);
    	retval = __setup_irq(irq, desc, action);	//在__setup_irq中确定是否线程化并完成中断处理函数绑定
    	chip_bus_sync_unlock(desc);
    
    	if (retval)
    		kfree(action);
    
    #ifdef CONFIG_DEBUG_SHIRQ_FIXME
    	if (!retval && (irqflags & IRQF_SHARED)) {
    		/*
    		 * It's a shared IRQ -- the driver ought to be prepared for it
    		 * to happen immediately, so let's make sure....
    		 * We disable the irq to make sure that a 'real' IRQ doesn't
    		 * run in parallel with our fake.
    		 */
    		unsigned long flags;
    
    		disable_irq(irq);
    		local_irq_save(flags);
    
    		handler(irq, dev_id);
    
    		local_irq_restore(flags);
    		enable_irq(irq);
    	}
    #endif
    	return retval;
    }
    request_threaded_irq函数基本上是将传入的参数放到action结构体,然后调用__setup_irq函数,线程化的具体过程在__setup_irq函数中

    static int
    __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
    {
    	struct irqaction *old, **old_ptr;
    	unsigned long flags, thread_mask = 0;
    	int ret, nested, shared = 0;
    	cpumask_var_t mask;
    
    	if (!desc)
    		return -EINVAL;
    
    	if (desc->irq_data.chip == &no_irq_chip)
    		return -ENOSYS;
    	if (!try_module_get(desc->owner))
    		return -ENODEV;
    
    	/*
    	 * Check whether the interrupt nests into another interrupt
    	 * thread.
    	 */
    	nested = irq_settings_is_nested_thread(desc);
    	if (nested) {
    		if (!new->thread_fn) {
    			ret = -EINVAL;
    			goto out_mput;
    		}
    		/*
    		 * 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;
    	} else {
    		if (irq_settings_can_thread(desc))	//request_irq调用通过设置参数_IRQ_NOTHREAD=0线程化,
    							//没有手动设置IRQ_NOTHREAD=1的中断都被线程化。Linux内核从2.6.39版本开始对中断线程化
    			irq_setup_forced_threading(new);	//实时补丁使force_irqthreads=true,开启强制线程化中断
    	}
    
    	/*
    	 * 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;
    		static const struct sched_param param = {
    			.sched_priority = MAX_USER_RT_PRIO/2,	//所有被线程化中断优先级都为50
    		};
    
    		t = kthread_create(irq_thread, new, "irq/%d-%s", irq,	//为中断创建内核线程
    				   new->name);
    		if (IS_ERR(t)) {
    			ret = PTR_ERR(t);
    			goto out_mput;
    		}
    
    		sched_setscheduler(t, SCHED_FIFO, ¶m);
    
    		/*
    		 * 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;
    		/*
    		 * Tell the thread to set its affinity. This is
    		 * important for shared interrupt handlers as we do
    		 * not invoke setup_affinity() for the secondary
    		 * handlers as everything is already set up. Even for
    		 * interrupts marked with IRQF_NO_BALANCE this is
    		 * correct as we want the thread to move to the cpu(s)
    		 * on which the requesting code placed the interrupt.
    		 */
    		set_bit(IRQTF_AFFINITY, &new->thread_flags);
    	}
    
    	if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {
    		ret = -ENOMEM;
    		goto out_thread;
    	}
    
    	/*
    	 * Drivers are often written to work w/o knowledge about the
    	 * underlying irq chip implementation, so a request for a
    	 * threaded irq without a primary hard irq context handler
    	 * requires the ONESHOT flag to be set. Some irq chips like
    	 * MSI based interrupts are per se one shot safe. Check the
    	 * chip flags, so we can avoid the unmask dance at the end of
    	 * the threaded handler for those.
    	 */
    	if (desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)
    		new->flags &= ~IRQF_ONESHOT;
    
    	/*
    	 * The following block of code has to be executed atomically
    	 */
    	raw_spin_lock_irqsave(&desc->lock, flags);
    	old_ptr = &desc->action;
    	old = *old_ptr;	//action和desc都是指针,用指向指针的指针获取action的地址,再使old指向action
    	if (old) {	//如果该中断号的处理程序链表desc->action本身就是空,就无所谓共享了
    		/*
    		 * 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. Also all must
    		 * agree on ONESHOT.
    		 */
    		if (!((old->flags & new->flags) & IRQF_SHARED) ||
    		    ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK) ||
    		    ((old->flags ^ new->flags) & IRQF_ONESHOT))
    			goto mismatch;
    
    		/* All handlers must agree on per-cpuness */
    		if ((old->flags & IRQF_PERCPU) !=
    		    (new->flags & IRQF_PERCPU))
    			goto mismatch;
    
    		/* add new interrupt at end of irq queue */
    		do {
    			/*
    			 * Or all existing action->thread_mask bits,
    			 * so we can find the next zero bit for this
    			 * new action.
    			 */
    			thread_mask |= old->thread_mask;
    			old_ptr = &old->next;
    			old = *old_ptr;	//在desc->action链表中找到空指针,为里后面将new加进去
    		} while (old);
    		shared = 1;
    	}
    
    	/*
    	 * Setup the thread mask for this irqaction for ONESHOT. For
    	 * !ONESHOT irqs the thread mask is 0 so we can avoid a
    	 * conditional in irq_wake_thread().
    	 */
    	if (new->flags & IRQF_ONESHOT) {
    		/*
    		 * Unlikely to have 32 resp 64 irqs sharing one line,
    		 * but who knows.
    		 */
    		if (thread_mask == ~0UL) {
    			ret = -EBUSY;
    			goto out_mask;
    		}
    		/*
    		 * The thread_mask for the action is or'ed to
    		 * desc->thread_active to indicate that the
    		 * IRQF_ONESHOT thread handler has been woken, but not
    		 * yet finished. The bit is cleared when a thread
    		 * completes. When all threads of a shared interrupt
    		 * line have completed desc->threads_active becomes
    		 * zero and the interrupt line is unmasked. See
    		 * handle.c:irq_wake_thread() for further information.
    		 *
    		 * If no thread is woken by primary (hard irq context)
    		 * interrupt handlers, then desc->threads_active is
    		 * also checked for zero to unmask the irq line in the
    		 * affected hard irq flow handlers
    		 * (handle_[fasteoi|level]_irq).
    		 *
    		 * The new action gets the first zero bit of
    		 * thread_mask assigned. See the loop above which or's
    		 * all existing action->thread_mask bits.
    		 */
    		new->thread_mask = 1 << ffz(thread_mask);
    
    	} else if (new->handler == irq_default_primary_handler &&
    		   !(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {
    		/*
    		 * The interrupt was requested with handler = NULL, so
    		 * we use the default primary handler for it. But it
    		 * does not have the oneshot flag set. In combination
    		 * with level interrupts this is deadly, because the
    		 * default primary handler just wakes the thread, then
    		 * the irq lines is reenabled, but the device still
    		 * has the level irq asserted. Rinse and repeat....
    		 *
    		 * While this works for edge type interrupts, we play
    		 * it safe and reject unconditionally because we can't
    		 * say for sure which type this interrupt really
    		 * has. The type flags are unreliable as the
    		 * underlying chip implementation can override them.
    		 */
    		pr_err("Threaded irq requested with handler=NULL and !ONESHOT for irq %d\n",
    		       irq);
    		ret = -EINVAL;
    		goto out_mask;
    	}
    
    	if (!shared) {	//中断处理链表为空,自己创建链表
    		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_mask;
    		}
    
    		desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED | \
    				  IRQS_ONESHOT | IRQS_WAITING);
    		irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
    
    		if (new->flags & IRQF_PERCPU) {
    			irqd_set(&desc->irq_data, IRQD_PER_CPU);
    			irq_settings_set_per_cpu(desc);
    		}
    
    		if (new->flags & IRQF_ONESHOT)
    			desc->istate |= IRQS_ONESHOT;
    
    		if (irq_settings_can_autoenable(desc))
    			irq_startup(desc, true);
    		else
    			/* Undo nested disables: */
    			desc->depth = 1;
    
    		/* Exclude IRQ from balancing if requested */
    		if (new->flags & IRQF_NOBALANCING) {
    			irq_settings_set_no_balancing(desc);
    			irqd_set(&desc->irq_data, IRQD_NO_BALANCING);
    		}
    
    		if (new->flags & IRQF_NO_SOFTIRQ_CALL)
    			irq_settings_set_no_softirq_call(desc);
    
    		/* Set default affinity mask once everything is setup */
    		setup_affinity(irq, desc, mask);
    
    	} else if (new->flags & IRQF_TRIGGER_MASK) {
    		unsigned int nmsk = new->flags & IRQF_TRIGGER_MASK;
    		unsigned int omsk = irq_settings_get_trigger_mask(desc);
    
    		if (nmsk != omsk)
    			/* hope the handler works with current  trigger mode */
    			pr_warning("irq %d uses trigger mode %u; requested %u\n",
    				   irq, nmsk, omsk);
    	}
    
    	new->irq = irq;
    	*old_ptr = new;	//添加到desc->action链表
    
    	/* 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->istate & IRQS_SPURIOUS_DISABLED)) {
    		desc->istate &= ~IRQS_SPURIOUS_DISABLED;
    		__enable_irq(desc, irq, false);
    	}
    
    	raw_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);	//内核线程开始运行
    
    	register_irq_proc(irq, desc);	//创建/proc/irq/目录及文件(smp_affinity,smp_affinity_list 等 )
    	new->dir = NULL;
    	register_handler_proc(irq, new);
    	free_cpumask_var(mask);	//创建proc/irq/<irq>/handler/ 
    
    	return 0;
    
    mismatch:
    	if (!(new->flags & IRQF_PROBE_SHARED)) {
    		pr_err("Flags mismatch irq %d. %08x (%s) vs. %08x (%s)\n",
    		       irq, new->flags, new->name, old->flags, old->name);
    #ifdef CONFIG_DEBUG_SHIRQ
    		dump_stack();
    #endif
    	}
    	ret = -EBUSY;
    
    out_mask:
    	raw_spin_unlock_irqrestore(&desc->lock, flags);
    	free_cpumask_var(mask);
    
    out_thread:
    	if (new->thread) {
    		struct task_struct *t = new->thread;
    
    		new->thread = NULL;
    		kthread_stop(t);
    		put_task_struct(t);
    	}
    out_mput:
    	module_put(desc->owner);
    	return ret;
    }
    __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函数的完整形式如下:

    int request_threaded_irq(unsigned int irq, irq_handler_t handler,
    			 irq_handler_t thread_fn, unsigned long irqflags,
    			 const char *devname, void *dev_id)
    在没有强制中断线程化的时候,thread_fn不为空即可将该中断线程化。
    展开全文
  • 线程化中断的支持在2009年已经进入Linux官方内核,详见Thomas Gleixner的patch: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=3aa551c9b4c40018f0e261a178e3d2...
  • linux中断线程化

    千次阅读 2011-04-11 17:44:00
    引用网址: http://edu.codepub.com/2010/0209/20378.php linux的中断线程化实现 http://www.ibm.com/developerworks/cn/linux/l-cn-linuxkernelint/index.html  Linux 内核中断内幕 ...
  • 关于内核中断线程化

    千次阅读 2013-12-04 16:40:18
    很久没看内核代码,才发现RT中的中断线程早已合并到主线中,而且远在2009年2.6.29就已经合并了。 Moving interrupts to threads By Jake Edge October 8, 2008 Processing interrupts from the ...
  • linux的中断线程化实现

    千次阅读 2010-02-09 20:25:00
    2.6.25.8内核实现了中断线程化,内核为每一个中断向量建立了一个中断线程,具体就是在结构irq_desc中增加了一个task_struct来代表这个线程: struct irq_desc { irq_flow_handler_t handle_irq; struct irq_chip *...
  • linux中断线程化(转载)

    千次阅读 2012-08-30 16:13:02
    linux中断线程化(转载)  2012-06-02 20:28:34| 分类: linux内核开发 | 标签: |字号大中小 订阅 摘自:http://www.eeworld.com.cn/qrs/2012/0326/article_9459_2.html 中断线程化是实现Linux...
  • linux中断注册及中断线程化

    千次阅读 2018-05-17 21:17:20
    linux中高优先级任务从runable到真正被调度的时间是不确定的,主要有...2,中断上下文具有更高的优先级(包括hw_irq_handler、softirq、tasklet等)可以抢占进程上下文。 一般外设中断被分为top half和bottom half(...
  • 中断优先级和中断线程优先级

    千次阅读 2017-11-12 15:40:00
    中断是一种当今很重要的硬件与cpu通信的方式,主板上集成很多硬件,那么就可以认为会有很多中断,但是cpu的数目往往要少得多,那么肯定会有多个硬件中断竞争一个cpu的情况,任何系统(包括自然界)都不能乱套,肯定...
  • request_threaded_irq----中断线程化

    万次阅读 2016-09-23 10:48:16
    中断线程化之后,中断将作为内核线程运行而且被赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级。这样,具有最高优先级的实时任务就能得到优先处理,即使在严重负载下仍有实时性保证。but,并不是所有的...
  • 前段时间一个交换机项目遇到了所有网卡中断都跑在一个核上,造成负载不均衡的问题,解决思路如下: 1. 首先查看一下 irqbalance service是否正常运行,自动平衡irq负载的service 2. 可以用下面命令绑定不同irq到...
  • 在操作系统中,对于中断的处理一直是一件麻烦事,其实主要是对操作系统的中断管理机制不熟悉。 当中断产生时,内核去执行中断服务程序ISR,中断服务程序中要做尽量少的工作,以提高系统执行效率, 有些人在中断服务...
  • Java 线程阻塞、中断及优雅退出

    千次阅读 2018-07-18 20:39:44
    本文转自:Java 线程阻塞、中断及优雅退出 线程阻塞 一个线程进入阻塞状态的原因可能如下(已排除Deprecated方法): sleep() sleep()使当前线程进入停滞状态(阻塞当前线程),让出CUP的使用、目的是不让当前...
  • Linux创建线程使用kernel_thread(),kthread_run() #include <linux/kthread.h> #include <linux/module.h> #ifndef SLEEP_MILLI_SEC #define SLEEP_MILLI_SEC(nMilliSec) \ do { \ long timeout = ...
  • 先说两个概念,因为这两个概念在...2,线程:每个进程至少有1个线程,多至N个,同一进程下的所有线程共享内存空间,但每个线程独占CPU每个逻辑 核心的运算周期。 换句话说,CPU的运算实质是程序的线程被CPU调用运算。
  • Java中如何实现线程的超时中断

    千次阅读 2018-06-22 15:57:20
    之前在实现熔断降级组件时,需要实现一个接口的超时中断,意思是,业务在使用熔断降级功能时,在平台上设置了一个超时时间,如果在请求进入熔断器开始计时,并且接口在超时时间内没有响应,则需要提早中断该请求并...
  • 1,中断处理程序中不能使用有睡眠功能的函数,如ioremap,kmalloc,msleep等,理由是中断程序并不是进程,没有进程的概念,因此就没有休眠的概念; 2,中断处理程序中的延时可以用忙等待函数来代替,如ndelay,...
  • 一、回顾 ...除了主动切换以外,时钟中断和异常也会导致线程切换。 这次课我们来学习另一种导致线程切换的情景 —— 时钟中断。 二、时钟中断 Windows系统每隔10-20毫秒会触发一次时钟中断,可以调用 GetS
  • KVM中断虚拟(一)

    千次阅读 2021-12-01 09:57:43
    因此,在本章中,我们简单介绍硬件中断的基本原理,然后结合中断的基本原理讨论如何虚拟化中断芯片。我们从起初IBM PC为单核系统设计的PIC(8259A)机制开始,讨论到为多核系统设计的APIC,一直到绕开I/O APIC、从设备...
  • 1)新建:当线程被创建时,它只会短暂地处于这种状态,在这段时间内,主要会执行一些初始的的操作。 2)就绪:在这种状态下,只要调度器把时间片分配给线程,该线程就可以运行了。 3)阻塞:线程能够运行,...
  • java 线程 中断标志位

    千次阅读 2018-05-19 12:19:00
    课程素材来自 http://enjoy.ke.qq.com/ 版权所有java线程中,线程中断方法详解:线程自然终止:自然执行...java线程是协作式,而非抢占式调用一个线程的interrupt() 方法中断一个线程,并不是强行关闭这个线程,只...
  • java线程中断记录

    万次阅读 2020-04-03 22:03:42
    开发者使用多线程处理此类工作。由于此类工作十分耗时,用户有时忍受不了,直接停止该进程。如何做到呢? 2、模拟案例 假设有个耗时任务doSpendTimeThings,这是一个死循环,目的就是模拟耗时任务。 /** * 死循环 ,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 180,338
精华内容 72,135
关键字:

中断线程化