您的位置:首页 > 产品设计 > UI/UE

Request_irq和setup_irq的区别

2010-09-09 10:22 411 查看
http://blog.chinaunix.net/u2/60011/showart_1086511.html Author: Dongas Data: 08-07-19 Linux 内核提供了两个注册中断处理函数的接口:setup_irq和request_irq。这两个函数都定义在kernel/irq/manage.c里。 /* * Internal function to register an irqaction - typically used to * allocate special interrupts that are part of the architecture. */ int setup_irq(unsigned int irq, struct irqaction *new); /* * request_irq - allocate an interrupt line * This call allocates interrupt resources and enables the * interrupt line and IRQ handling. */ int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char *devname, void *dev_id) 这两个函数有什么样的区别呢? 先看看setup_irq Setup_irq通常用在系统时钟(GP Timer)驱动里,注册系统时钟驱动的中断处理函数。 下面举个列子, 如s3c2410 timer驱动: /* arch/arm/mach-s3c2410/time.c */ static struct irqaction s3c2410_timer_irq = { .name = "S3C2410 Timer Tick", .flags = IRQF_DISABLED | IRQF_TIMER, .handler = s3c2410_timer_interrupt, }; static void __init s3c2410_timer_init (void) { s3c2410_timer_setup(); setup_irq(IRQ_TIMER4, &s3c2410_timer_irq); } struct sys_timer s3c24xx_timer = { .init = s3c2410_timer_init, .offset = s3c2410_gettimeoffset, .resume = s3c2410_timer_setup }; struct sys_timer s3c24xx_timer = { .init = s3c2410_timer_init, .offset = s3c2410_gettimeoffset, .resume = s3c2410_timer_setup }; 可以看到,setup_irq的使用流程很简单。首先定义s3c2410 timer驱动的irqaction结构体,该结构体用于描述timer中断的基本属性包括中断名、类别以及该中断handler等。然后通过setup_irq函数将timer的irqaction注册进内核。其中,IRQ_TIMER4为s3c2410 timer的中断号。 再看看request_irq request_irq源码如下: /* kernel/irq/manage.c */ int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char *devname, void *dev_id) { struct irqaction *action; int retval; #ifdef CONFIG_LOCKDEP /* * Lockdep wants atomic interrupt handlers: */ irqflags |= SA_INTERRUPT; #endif /* * 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) /* 使用共享中断但没有提供非NULL的dev_id则返回错误 */ return -EINVAL; if (irq >= NR_IRQS) /* 中断号超出最大值 */ return -EINVAL; if (irq_desc[irq].status & IRQ_NOREQUEST) /* 该中断号已被使用并且未共享 */ return -EINVAL; if (!handler) return -EINVAL; action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC); /* 动态创建一个irqaction */ if (!action) return -ENOMEM; /* 下面几行是根据request_irq 传进来的参数对irqaction结构体赋值 */ action->handler = handler; action->flags = irqflags; cpus_clear(action->mask); action->name = devname; action->next = NULL; action->dev_id = dev_id; select_smp_affinity(irq); retval = setup_irq(irq, action); /* 调用setup_irq注册该中断的irqaction结构体 */ if (retval) kfree(action); return retval; } 由上可以看出,request_irq的大致流程为先对申请的中断线进行安全检测,然后根据request_irq传进来的参数,动态创建该中断对应的irqaction结构体,最后通过setup_irq函数将该irqaction注册进内核适当的位置。 这两个函数的使用流程搞清楚了,那么两者之间的联系也就清楚了: 1) Request_irq的注册过程包含setup_irq,最终是调用setup_irq。 2) Request_irq比setup_irq多一套错误检测机制,即kmalloc前面3行if语句。 而Setup_irq通常是直接注册irqaction,并没针对相应中断线进行错误检测,如该irq 线是否已经被占用等。因此setup_irq通常只用在特定的中断线上,如System timer。除系统时钟驱动外,大部份驱动还是通过request_irq注册中断。 这里有个小问题: 既然Request_irq实际上就是包含了setup_irq的注册过程,那系统时钟驱动(GP Timer Driver)中断可以用request_irq来注册吗? 做个小试验, 将s3c2410 timer驱动的setup_irq那行去掉,改为用request_irq注册。 修改后代码如下: static void __init s3c2410_timer_init (void) { s3c2410_timer_setup(); //setup_irq(IRQ_TIMER4, &s3c2410_timer_irq); request_irq(IRQ_TIMER4, s3c2410_timer_interrupt, IRQF_DISABLED | IRQF_TIMER, "S3C2410 Timer Tick", NULL); } 编译运行。 结果:内核挂掉 为什么呢?很明显,系统时钟驱动中断不能用request_irq注册,大致搜了一下源码也发现,看到其他平台相关的时钟驱动中断部分都是用的setup_irq注册的。 我们来分析一下原因。 看看request_irq和setup_irq 还有哪些细节不一样? 仔细观察后注意到request_irq内有这么一行代码: action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC); 作用为动态创建一个irqaction。 Kmalloc实际上也是使用的slab机制进行分配的。源码如下: /* include/linux/slab.h */ static inline void *kmalloc(size_t size, gfp_t flags) { if (__builtin_constant_p(size)) { int i = 0; #define CACHE(x) \ if (size <= x) \ goto found; \ else \ i++; #include "kmalloc_sizes.h" #undef CACHE { extern void __you_cannot_kmalloc_that_much(void); __you_cannot_kmalloc_that_much(); } found: return kmem_cache_alloc((flags & GFP_DMA) ? malloc_sizes[i].cs_dmacachep : malloc_sizes[i].cs_cachep, flags); } return __kmalloc(size, flags); } 使用slab机制分配内存必须先对slab进行初始化,包括mem_init和kmem_cache_init。 看看kernel的初始化流程: /* init/main.c */ asmlinkage void __init start_kernel(void) { …… time_init(); …… vfs_caches_init_early(); cpuset_init_early(); mem_init(); ß------ initializes the memory data structures kmem_cache_init(); ß---- set up the general caches …… } Time_init函数在mem_init和kmem_cache_init之前被调用,而time_init会调用体系结构相关部分系统时钟驱动的初始化函数。拿s3c2410的例子来说,time_init最终会调用s3c2410_timer_init函数,进行s3c2410时钟驱动的初始化和注册中断处理函数。 具体过程如下: time_init函数定义在arch/arm/kernel/time.c内: void __init time_init(void) { #ifndef CONFIG_GENERIC_TIME if (system_timer->offset == NULL) system_timer->offset = dummy_gettimeoffset; #endif system_timer->init(); ß-这行实际执行的就是s3c2410_timer_init #ifdef CONFIG_NO_IDLE_HZ if (system_timer->dyn_tick) system_timer->dyn_tick->lock = SPIN_LOCK_UNLOCKED; #endif } system_timer在setup_arch(arch/arm/kernel/setup.c)内通过map_desc机制被初始化为s3c24xx_timer. 如上面s3c2410时钟驱动代码所示,s3c24xx_timer的init成员即指向s3c2410_timer_init函数。 现在我们搞清楚了,我们大概的估计是系统时钟驱动(GP Timer Driver)的中断处理函数不能用request_irq注册是因为request_irq内会调用kmalloc动态分配内存创建timer的irqaction结构体。而kmalloc也是使用的slab内存分配机制,使用kmalloc前必须先对kernel的slab以及mem data structure进行初始化。而这部分初始化工作是在系统时钟驱动初始化之后才进行的,所以造成kmalloc失败,从而造成系统时钟驱动的中断未注册成功,进而内核挂掉。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: