免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1112 | 回复: 0
打印 上一主题 下一主题

Request_irq和setup_irq的区别 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-12-23 20:01 |只看该作者 |倒序浏览

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)驱动里,注册系统时钟驱动的中断处理函数。
下面举个列子, 如sep4020 timer驱动:
static struct irqaction sep4020_timer_irq = {
.name = "sep4020 Timer Tick",
.flags = SA_INTERRUPT | SA_TIMER,
.handler = sep4020_timer_interrupt,
};

static void __init sep4020_timer_init(void)
{
*(RP)TIMER_T1LCR_V = LATCH;
*(RP)TIMER_T1CR_V = 0x07;
INT_ENABLE(INTSRC_TIMER1);
setup_irq(INTSRC_TIMER1,&sep4020_timer_irq);
}
struct sys_timer sep4020_timer =
{
.init = sep4020_timer_init,
.offset = sep4020_gettimeoffset,
};
可以看到,setup_irq的使用流程很简单。首先定义4020 timer驱动的irqaction结构体,该结构体用于描述timer中断的基本属性包括中断名、类别以及该中断handler等。然后通过setup_irq函数将timer的irqaction注册进内核。其中,INTSRC_TIMER1是 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来注册吗?

做个小试验, 将timer驱动的setup_irq那行去掉,改为用request_irq注册。
修改后代码如下:
static void __init sep4020_timer_init(void)
{
*(RP)TIMER_T1LCR_V = LATCH;
*(RP)TIMER_T1CR_V = 0x07;
INT_ENABLE(INTSRC_TIMER1);
// setup_irq(INTSRC_TIMER1,&sep4020_timer_irq);
       request_irq(INTSRC_TIMER1, sep4020_timer_interrupt,
        SA_INTERRUPT | SA_TIMER, "sep4020 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
                     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.cs_dmacachep :
                     malloc_sizes.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之前被调用,它也是在start_kernel()中调用的,而time_init会调用体系结构相关部分系统时钟驱动的初始化函数。拿我们的4020的例子来说,time_init最终会调用sep4020_timer_init函数,进行sep4020时钟驱动的初始化和注册中断处理函数。
具体过程如下:
time_init函数定义在arch/arm/kernel/time.c内:
void __init time_init(void)
{

       if (system_timer->offset == NULL)
              system_timer->offset = dummy_gettimeoffset;

       system_timer->init();  ß-这行实际执行的就是sep4020_timer_init

}
system_timer在setup_arch(arch/arm/kernel/setup.c)内通过map_desc机制被初始化为sep4020_timer. 如上面sep4020时钟驱动代码所示,sep4020_timer的init成员即指向sep4020_timer_init函数。

现在我们搞清楚了,我们大概的估计是系统时钟驱动(GP Timer Driver)的中断处理函数不能用request_irq注册是因为request_irq内会调用kmalloc动态分配内存创建timer的irqaction结构体。而kmalloc也是使用的slab内存分配机制,使用kmalloc前必须先对kernel的slab以及mem data structure进行初始化。而这部分初始化工作是在系统时钟驱动初始化之后才进行的,所以造成kmalloc失败,从而造成系统时钟驱动的中断未注册成功,进而内核挂掉。


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/99507/showart_2129168.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP