近日需要使用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项。
中断系统初始化的过程
用来初始化中断系统的函数位于arch/x86/kernel/irqinit.c,定义如下
void
__init init_IRQ(
void
)
{
int
i;
/*
* On cpu 0, Assign IRQ0_VECTOR..IRQ15_VECTOR's to IRQ 0..15.
* If these IRQ's are handled by legacy interrupt-controllers like PIC,
* then this configuration will likely be static after the boot. If
* these IRQ's are handled by more mordern controllers like IO-APIC,
* then this vector space can be freed and re-used dynamically as the
* irq's migrate etc.
*/
for
(i
=
0
; i
<
legacy_pic
->
nr_legacy_irqs; i
++
)
per_cpu(vector_irq,
0
)[IRQ0_VECTOR
+
i]
=
i;
x86_init.irqs.intr_init();
}
函数写的很简单,留下的疑问是x86_init是做什么的?
在arch/x86/include/asm/x86_init.h中可以找到,x86_init是一个x86_init_ops类型的结构体,其中irqs是一个x86_init_irqs类型的结构体。
struct
x86_init_irqs {
void
(
*
pre_vector_init)(
void
);
void
(
*
intr_init)(
void
);
void
(
*
trap_init)(
void
);
};
在arch/x86/kernel/x86_init.c中找到x86_init的初始默认赋值:
struct
x86_init_ops x86_init __initdata
=
{
...
.irqs
=
{
.pre_vector_init
=
init_ISA_irqs,
.intr_init
=
native_init_IRQ,
.trap_init
=
x86_init_noop,
},
...
};
对于这几个函数,我们又要回到开头的irqinit.c中来寻找了。先看之前调用的intr_init,也就是native_init_IRQ:
void
__init native_init_IRQ(
void
)
{
int
i;
/*
Execute any quirks before the call gates are initialised:
*/
x86_init.irqs.pre_vector_init();
apic_intr_init();
/*
* Cover the whole vector space, no vector can escape
* us. (some of these will be overridden and become
* 'special' SMP interrupts)
*/
for
(i
=
FIRST_EXTERNAL_VECTOR; i
<
NR_VECTORS; i
++
) {
/*
IA32_SYSCALL_VECTOR could be used in trap_init already.
*/
if
(
!
test_bit(i, used_vectors))
set_intr_gate(i, interrupt[i
-
FIRST_EXTERNAL_VECTOR]);
}
if
(
!
acpi_ioapic)
setup_irq(
2
,
&
irq2);
#ifdef CONFIG_X86_32
/*
* External FPU? Set up irq13 if so, for
* original braindamaged IBM FERR coupling.
*/
if
(boot_cpu_data.hard_math
&&
!
cpu_has_fpu)
setup_irq(FPU_IRQ,
&
fpu_irq);
irq_ctx_init(smp_processor_id());
#endif
}
其中pre_vector_init对应着init_ISA_irqs,主要完成了irq_desc的初始化分配。
void
__init init_ISA_irqs(
void
)
{
int
i;
#if
defined(CONFIG_X86_64) || defined(CONFIG_X86_LOCAL_APIC)
init_bsp_APIC();
#endif
legacy_pic
->
init(
0
);
/*
* 16 old-style INTA-cycle interrupts:
*/
for
(i
=
0
; i
<
legacy_pic
->
nr_legacy_irqs; i
++
) {
struct
irq_desc
*
desc
=
irq_to_desc(i);
desc
->
status
=
IRQ_DISABLED;
desc
->
action
=
NULL;
desc
->
depth
=
1
;
set_irq_chip_and_handler_name(i,
&
i8259A_chip,
handle_level_irq,
"
XT
"
);
}
}
完成数据结构的初始化后就是对硬件资源的分配了,不做深究。
转载来自: http://www.cnblogs.com/garychen2272/archive/2011/02/25/1964176.html
248

被折叠的 条评论
为什么被折叠?



