近日需要使用msi中断,遂在网上查找linux下中断方面资料。资料虽多,但是需要组织成系统却有些困难。而LDD3上关于中断虽有提及,但却未涉及msi中断,故有必要自己进行一番学习。
今天阅读了kernel源码中的msi-HOWTO.txt文档,对linux下msi的使用有了一些了解,但还甚为浅薄,无法投入应用。后翻看了一些源码,打算从基本开始了解,以便记忆。本篇将写一些linux内核管理与存储中断服务的内容。
---------------------------------------------------------------------
1. 重要接口
LDD上说,“内核维护了一个中断信号线的注册表,该注册表类似于I/O端口的注册表。模块在使用中断前要先请求一个中断通道(或者中断请求IRQ),然后在使用后释放该通道。”
撇开系统如何遍历各个设备进行初始化,上面两句话说的实际上就是指两个接口函数:
extern int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);
extern void free_irq(unsigned int, void *);
顾名思义,以上两个函数分别用于申请和释放IRQ。
而再一看,会发现其实request_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);
}
所以实际上起到申请IRQ作用的,正是这个request_threaded_irq函数。一查,它位于/kernel/irq/manage.c中。
2.追随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)
{
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 (desc->status & IRQ_NOREQUEST)
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(irq, desc);
retval = __setup_irq(irq, desc, action);
chip_bus_sync_unlock(irq, desc);
if (retval)
kfree(action);
#ifdef CONFIG_DEBUG_SHIRQ
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;
}
可以看到除去一些验证的语句,整个函数主要完成的任务是初始化了一个irqaction类型的struct和一个irq_desc类型的struct,接着对这两个struct进一步赋值和处理,便实现了IRQ申请。至此,我们有理由认为这两个struct是kernel管理IRQ的核心数据结构。因此不妨看看他们都是什么样的。
struct irq_desc {
unsigned int irq;
struct timer_rand_state *timer_rand_state;
unsigned int *kstat_irqs;
#ifdef CONFIG_INTR_REMAP
struct irq_2_iommu *irq_2_iommu;
#endif
irq_flow_handler_t handle_irq;
struct irq_chip *chip;
struct msi_desc *msi_desc;
void *handler_data;
void *chip_data;
struct irqaction *action; /* IRQ action list */
unsigned int status; /* IRQ status */
unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned long last_unhandled; /* Aging timer for unhandled count */
unsigned int irqs_unhandled;
raw_spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_var_t affinity;
const struct cpumask *affinity_hint;
unsigned int node;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
const char *name;
} ____cacheline_internodealigned_in_smp;
irq_desc实际是个用于构成数组的数据结构。这里irq就是我们熟悉的irq号,每个设备申请到一个IRQ,就需要填充一个irq_desc,并由kernel放入所维护的数组中进行管理。在这些需要填充的内容里,irq_chip和irqaction是两个比较有助于理解数据结构的struct。
struct irq_chip {
const char *name;
unsigned int (*startup)(unsigned int irq);
void (*shutdown)(unsigned int irq);
void (*enable)(unsigned int irq);
void (*disable)(unsigned int irq);
void (*ack)(unsigned int irq);
void (*mask)(unsigned int irq);
void (*mask_ack)(unsigned int irq);
void (*unmask)(unsigned int irq);
void (*eoi)(unsigned int irq);
void (*end)(unsigned int irq);
int (*set_affinity)(unsigned int irq,
const struct cpumask *dest);
int (*retrigger)(unsigned int irq);
int (*set_type)(unsigned int irq, unsigned int flow_type);
int (*set_wake)(unsigned int irq, unsigned int on);
void (*bus_lock)(unsigned int irq);
void (*bus_sync_unlock)(unsigned int irq);
/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
void (*release)(unsigned int irq, void *dev_id);
#endif
/*
* For compatibility, ->typename is copied into ->name.
* Will disappear.
*/
const char *typename;
};
这个struct里主要定义了硬件层面上一个系统对一个IRQ的管理接口。
struct irqaction {
irq_handler_t handler;
unsigned long flags;
const char *name;
void *dev_id;
struct irqaction *next;
int irq;
struct proc_dir_entry *dir;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
};
这个struct中handler定义了中断处理函数, *next指向了下一个irqaction,也就是说irqaction是以链表的形式存在的。也就是说,每一个IRQ对应一个irq_desc,而irq_desc维护着irq_chip管理了硬件层面的中断使能,同时irq_desc也维护了一个irqaction链表。
根据所查的资料,实际上,系统在处理一个中断时,会根据中断号调用irq_desc数组中的handle_irq, handle_irq再使用chip控制硬件的使能,接着调用irqaction链表,逐个调用中断处理函数。
回过头来,request一个IRQ的过程实际上就是构造irqaction项,free的过程就是移除不需要的irqaction项。
http://www.cnblogs.com/garychen2272/archive/2011/02/25/1964176.html