您的位置:首页 > 其它

中断之中断请求队列的初始化

2011-11-11 17:28 344 查看
参考文章:http://bbs.chinaunix.net/thread-3566316-1-1.html

参考文章:http://www.linuxidc.com/Linux/2011-02/32129.htm

在“初始化中断向量表”的博文中,我们留下了一个问题,就是

void __init native_init_IRQ(void)
{
int i;

/* Execute any quirks before the call gates are initialised: */
x86_init.irqs.pre_vector_init(); //调用 init_ISA_irqs ,我们将在“中断队列初始化”的博文中详述
.......
}


/*
* The platform setup functions are preset with the default functions
* for standard PC hardware.
*/
struct x86_init_ops x86_init __initdata = {
。。。。。。

.irqs = {.pre_vector_init = init_ISA_irqs,.intr_init = native_init_IRQ,.trap_init = x86_init_noop,},

。。。。。。

}

因此,最终调用init_ISA_irqs,这是个异常重要的函数,我们的中断队列初始化的工作就主要由它来完成。

在讲解init_ISA_irqs之前,我们先来看一些重要的数据结构:


/*
* Core internal functions to deal with irq descriptors
*
* This include will move to kernel/irq once we cleaned up the tree.
* For now it's included from <linux/irq.h>
*/

struct irq_affinity_notify;
struct proc_dir_entry;
struct timer_rand_state;
/**
* struct irq_desc - interrupt descriptor
* @irq_data: per irq and chip data passed down to chip functions
* @timer_rand_state: pointer to timer rand state struct
* @kstat_irqs: irq stats per cpu
* @handle_irq: highlevel irq-events handler
* @preflow_handler: handler called before the flow handler (currently used by sparc)
* @action: the irq action chain
* @status: status information
* @core_internal_state__do_not_mess_with_it: core internal status information
* @depth: disable-depth, for nested irq_disable() calls
* @wake_depth: enable depth, for multiple irq_set_irq_wake() callers
* @irq_count: stats field to detect stalled irqs
* @last_unhandled: aging timer for unhandled count
* @irqs_unhandled: stats field for spurious unhandled interrupts
* @lock: locking for SMP
* @affinity_hint: hint to user space for preferred irq affinity
* @affinity_notify: context for notification of affinity changes
* @pending_mask: pending rebalanced interrupts
* @threads_oneshot: bitfield to handle shared oneshot threads
* @threads_active: number of irqaction threads currently running
* @wait_for_threads: wait queue for sync_irq to wait for threaded handlers
* @dir: /proc/irq/ procfs entry
* @name: flow handler name for /proc/interrupts output
*/
struct irq_desc {
struct irq_data irq_data;
struct timer_rand_state *timer_rand_state;
unsigned int __percpu *kstat_irqs;
irq_flow_handler_t handle_irq; //指向一些函数指针,用于该队列,或者说该共用“中断通道”的控制(并不是对具体中断源的服务)。
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
irq_preflow_handler_t preflow_handler;
#endif
struct irqaction *action; /* IRQ action list 由中断服务程序构成的单链表队列*/
unsigned int status_use_accessors;
unsigned int core_internal_state__do_not_mess_with_it;
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
const struct cpumask *affinity_hint;
struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
unsigned long threads_oneshot;
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;




这个数据结构中的第一字段需要给予特殊的关注,因为这相比于2.4有很大的区别:

/**
* struct irq_data - per irq and irq chip data passed down to chip functions
* @irq:		interrupt number
* @node:		node index useful for balancing
* @state_use_accessors: status information for irq chip functions.
*			Use accessor functions to deal with it
* @chip:		low level interrupt hardware access
* @handler_data:	per-IRQ data for the irq_chip methods
* @chip_data:		platform-specific per-chip private data for the chip
*			methods, to allow shared chip implementations
* @msi_desc:		MSI descriptor
* @affinity:		IRQ affinity on SMP
*
* The fields here need to overlay the ones in irq_desc until we
* cleaned up the direct references and switched everything over to
* irq_data.
*/
struct irq_data {
unsigned int		irq;
unsigned int		node;
unsigned int		state_use_accessors;
struct irq_chip		*chip;
void			*handler_data;
void			*chip_data;
struct msi_desc		*msi_desc;
#ifdef CONFIG_SMP
cpumask_var_t		affinity;
#endif
};











在这个结构中,我们需要关注字段*chip:

/**
* struct irq_chip - hardware interrupt chip descriptor
*
* @name:		name for /proc/interrupts
* @irq_startup:	start up the interrupt (defaults to ->enable if NULL)
* @irq_shutdown:	shut down the interrupt (defaults to ->disable if NULL)
* @irq_enable:		enable the interrupt (defaults to chip->unmask if NULL)
* @irq_disable:	disable the interrupt
* @irq_ack:		start of a new interrupt
* @irq_mask:		mask an interrupt source
* @irq_mask_ack:	ack and mask an interrupt source
* @irq_unmask:		unmask an interrupt source
* @irq_eoi:		end of interrupt
* @irq_set_affinity:	set the CPU affinity on SMP machines
* @irq_retrigger:	resend an IRQ to the CPU
* @irq_set_type:	set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
* @irq_set_wake:	enable/disable power-management wake-on of an IRQ
* @irq_bus_lock:	function to lock access to slow bus (i2c) chips
* @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips
* @irq_cpu_online:	configure an interrupt source for a secondary CPU
* @irq_cpu_offline:	un-configure an interrupt source for a secondary CPU
* @irq_suspend:	function called from core code on suspend once per chip
* @irq_resume:		function called from core code on resume once per chip
* @irq_pm_shutdown:	function called from core code on shutdown once per chip
* @irq_print_chip:	optional to print special chip info in show_interrupts
* @flags:		chip specific flags
*
* @release:		release function solely used by UML
*/
struct irq_chip {
const char	*name;
unsigned int	(*irq_startup)(struct irq_data *data);
void		(*irq_shutdown)(struct irq_data *data);
void		(*irq_enable)(struct irq_data *data);
void		(*irq_disable)(struct irq_data *data);

void		(*irq_ack)(struct irq_data *data);
void		(*irq_mask)(struct irq_data *data);
void		(*irq_mask_ack)(struct irq_data *data);
void		(*irq_unmask)(struct irq_data *data);
void		(*irq_eoi)(struct irq_data *data);

int		(*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
int		(*irq_retrigger)(struct irq_data *data);
int		(*irq_set_type)(struct irq_data *data, unsigned int flow_type);
int		(*irq_set_wake)(struct irq_data *data, unsigned int on);

void		(*irq_bus_lock)(struct irq_data *data);
void		(*irq_bus_sync_unlock)(struct irq_data *data);

void		(*irq_cpu_online)(struct irq_data *data);
void		(*irq_cpu_offline)(struct irq_data *data);

void		(*irq_suspend)(struct irq_data *data);
void		(*irq_resume)(struct irq_data *data);
void		(*irq_pm_shutdown)(struct irq_data *data);

void		(*irq_print_chip)(struct irq_data *data, struct seq_file *p);

unsigned long	flags;

/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
void		(*release)(unsigned int irq, void *dev_id);
#endif
};


这个结构体和2.4内核中的hw_interrupt_type()功能类似,其初始化是在init_ISA_irqs中完成的:

void __init init_ISA_irqs(void)
{
struct irq_chip *chip = legacy_pic->chip;
const char *name = chip->name;
int i;

#if defined(CONFIG_X86_64) || defined(CONFIG_X86_LOCAL_APIC)
init_bsp_APIC();
#endif
legacy_pic->init(0);

for (i = 0; i < legacy_pic->nr_legacy_irqs; i++)
irq_set_chip_and_handler_name(i, chip, handle_level_irq, name);
}


其中,legacy_pic是全局变量:struct legacy_pic *legacy_pic = &default_legacy_pic;

struct legacy_pic default_legacy_pic = {
.nr_legacy_irqs = NR_IRQS_LEGACY,  // = 16
.chip  = &i8259A_chip,
.mask = mask_8259A_irq,
.unmask = unmask_8259A_irq,
.mask_all = mask_8259A,
.restore_mask = unmask_8259A,
.init = init_8259A,
.irq_pending = i8259A_irq_pending,
.make_irq = make_8259A_irq,
};
 


终于找到我们要找的函数了——init8259A(),我们并不会详细的解释这个函数,因为它要求对8259有比较深入的了解,对于这个函数,浏览一下,知道大概的意思即可。:

static void init_8259A(int auto_eoi)
{
unsigned long flags;

i8259A_auto_eoi = auto_eoi;

raw_spin_lock_irqsave(&i8259A_lock, flags);

outb(0xff, PIC_MASTER_IMR);	/* mask all of 8259A-1 */
outb(0xff, PIC_SLAVE_IMR);	/* mask all of 8259A-2 */

/*
* outb_pic - this has to work on a wide range of PC hardware.
*/
outb_pic(0x11, PIC_MASTER_CMD);	/* ICW1: select 8259A-1 init */

/* ICW2: 8259A-1 IR0-7 mapped to 0x30-0x37 on x86-64,
to 0x20-0x27 on i386 */
outb_pic(IRQ0_VECTOR, PIC_MASTER_IMR);

/* 8259A-1 (the master) has a slave on IR2 */
outb_pic(1U << PIC_CASCADE_IR, PIC_MASTER_IMR);

if (auto_eoi)	/* master does Auto EOI */
outb_pic(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR);
else		/* master expects normal EOI */
outb_pic(MASTER_ICW4_DEFAULT, PIC_MASTER_IMR);

outb_pic(0x11, PIC_SLAVE_CMD);	/* ICW1: select 8259A-2 init */

/* ICW2: 8259A-2 IR0-7 mapped to IRQ8_VECTOR */
outb_pic(IRQ8_VECTOR, PIC_SLAVE_IMR);
/* 8259A-2 is a slave on master's IR2 */
outb_pic(PIC_CASCADE_IR, PIC_SLAVE_IMR);
/* (slave's support for AEOI in flat mode is to be investigated) */
outb_pic(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR);

if (auto_eoi)
/*
* In AEOI mode we just have to mask the interrupt
* when acking.
*/
i8259A_chip.irq_mask_ack = disable_8259A_irq;
else
i8259A_chip.irq_mask_ack = mask_and_ack_8259A;

udelay(100);		/* wait for 8259A to initialize */

outb(cached_master_mask, PIC_MASTER_IMR); /* restore master IRQ mask */
outb(cached_slave_mask, PIC_SLAVE_IMR);	  /* restore slave IRQ mask */

raw_spin_unlock_irqrestore(&i8259A_lock, flags);
}





回到init_ISA_irqs中,接下来是:

for (i = 0; i < legacy_pic->nr_legacy_irqs; i++)
irq_set_chip_and_handler_name(i, chip, handle_level_irq, name);

















简单的说就是,前16个中断请求与其余的中断是不同的,因此handle_irq需要被设置成特定的处理函数,而其余的中断只有在需要的时候才会将其handle_irq函数登记。
前16个中断请求通道IR0~IR15是由中断请求控制器8259A控制的。

下面来看一下具体中断服务程序描述项:
首先让我们回到irq_desc中,来看中断服务程序描述项的数据结构irqaction:



















/**
* struct irqaction - per interrupt action descriptor
* @handler:	interrupt handler function
* @flags:	flags (see IRQF_* above)
* @name:	name of the device
* @dev_id:	cookie to identify the device
* @next:	pointer to the next irqaction for shared interrupts
* @irq:	interrupt number
* @dir:	pointer to the proc/irq/NN/name entry
* @thread_fn:	interrupt handler function for threaded interrupts
* @thread:	thread pointer for threaded interrupts
* @thread_flags:	flags related to @thread
* @thread_mask:	bitmask for keeping track of @thread activity
*/
struct irqaction {
irq_handler_t handler;
unsigned long flags;
void *dev_id;
struct irqaction *next;
int irq;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
unsigned long thread_mask;
const char *name;
struct proc_dir_entry *dir;
} ____cacheline_internodealigned_in_smp;











IDT表初始化完成时,irq->action指向NULL,那么这样是没有意义的,因为不会执行相应的服务程序,而只是在栈中绕了一圈就走了。需要将中断服务程序进行登记之后才会有意义,实现这个功能的函数是request_irq:
应该说request_irq并不属于初始化的工作,前文讲到的前16个中断的服务程序是必须在初始阶段就设置好的,但是对于其他的中断服务程序,只有驱动程序需要处理与中断相关的工作,它才会注册一个中断处理程序,比如:e100驱动中注册其中断处理函数(一下博文参考网站:http://bbs.chinaunix.net/thread-3566316-1-1.html)








static int e100_up(struct nic *nic)
{
……
if ((err = request_irq(nic->pdev->irq, e100_intr, IRQF_SHARED,
nic->netdev->name, nic->netdev)))
……
}







好,现在我们来看看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);
}







/**
*	request_threaded_irq - allocate an interrupt line
*	@irq: Interrupt line to allocate 注意,这个irq为中断请求队列的序号,即“中断请求号”,但不是中断号或者中断向量《情景分析》 P
*	@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
*
*	This call allocates interrupt resources and enables the
*	interrupt line and IRQ handling. From the point this
*	call is made your handler function may be invoked. Since
*	your handler function must clear any interrupt the board
*	raises, you must take care both to initialise your hardware
*	and to set up the interrupt handler in the right order.
*
*	If you want to set up a threaded irq handler for your device
*	then you need to supply @handler and @thread_fn. @handler ist
*	still called in hard interrupt context and has to check
*	whether the interrupt originates from the device. If yes it
*	needs to disable the interrupt on the device and return
*	IRQ_WAKE_THREAD which will wake up the handler thread and run
*	@thread_fn. This split handler design is necessary to support
*	shared interrupts.
*
*	Dev_id must be globally unique. Normally the address of the
*	device data structure is used as the cookie. Since the handler
*	receives this value it makes sense to use it.
*
*	If your interrupt is shared you must pass a non NULL dev_id
*	as this is required when freeing the interrupt.
*
*	Flags:
*
*	IRQF_SHARED		Interrupt is shared
*	IRQF_SAMPLE_RANDOM	The interrupt can be used for entropy
*	IRQF_TRIGGER_*		Specify active edge(s) or level
*
*/
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); //获取对应的中断描述符——return (irq < NR_IRQS) ? irq_desc + irq : NULL;

 if (!desc)
return -EINVAL;
/*
关于_IRQ_NOREQUEST
When allocating irqs, wait to clear the IRQ_NOREQUEST flag until the
host map hook has been called.
When freeing irqs, set the IRQ_NOREQUEST flag before calling the host
unmap hook.
*/
//如果IRQ_NOREQUEST置位,意味着中断不能被请求注册。为什么???if (!irq_settings_can_request(desc)) // return !(desc->status_use_accessors & _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(desc);

  //注册irqaction
retval = __setup_irq(irq, desc, action); //*******************************chip_bus_sync_unlock(desc);if (retval)kfree(action);#ifdef CONFIG_DEBUG_SHIRQ_FIXMEif (!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);}#endifreturn retval;}我们首先看一下request_threaded_irq()函数中的各个形参(1)irq:表示申请的中断号。(2)handler:表示中断服务例程(3) thread_fn:中断线程化,此处传递的是NULL。NULL表示没有中断线程化。







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




(4)irqflags:表示中断标志位。(5)devname:表示请求中断的设备的名称。
(6)dev_id: 对应于request_irq()函数中所传递的第五个参数,可取任意值,但必须唯一能够代表发出中断请求的设备,通常取描述该设备的结构体。 共享中断时所用。
我们来看其中的一个核心函数__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;
const char *old_name = NULL;
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;
/*
* Some drivers like serial.c use request_irq() heavily,
* so we have to be careful not to interfere with a
* running system.
*/
     //如果指定了IRQF_SAMPLE_RANDOM,意味着设备将对内核随机数熵池有所贡献,rand_initialize_ir
     //函数处理相应的工作

if (new->flags & IRQF_SAMPLE_RANDOM) { //与随机数产生相关的标志《情景分析》 P209
/*
* This function might sleep, we want to call it first,
* outside of the atomic block.
* Yes, this might clear the entropy pool if the wrong
* driver is attempted to be loaded, without actually
* installing a new handler, but is this really a problem,
* only the sysadmin is able to do this.
*/
rand_initialize_irq(irq); //为中断请求初始化一个数据结构,用来记录中断的时序。
}

/*
* Check whether the interrupt nests into another interrupt
* thread.
*/
nested = irq_settings_is_nested_thread(desc); //???????????????
if (nested) {
if (!new->thread_fn)
return -EINVAL;
/*
* 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))
irq_setup_forced_threading(new);
}

/*
* 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) {  //传递的参数thread_fn是NULL,所以不会被执行
struct task_struct *t;

t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
new->name);
if (IS_ERR(t))
return PTR_ERR(t);
/*
* 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;
}

if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {
ret = -ENOMEM;
goto out_thread;
}

/*
* 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链上,可能一个也没有,可能已经注册了一个或多
    //如果是后者,则需要判断新伙伴是否是允许共享
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.
*/

               //这里的验证表明,它使终使用第一个old来匹备,这意味着action链上的所有节点,都拥有相同的类
           //后面的IRQF_PERCPU也是同样的道理

if (!((old->flags & new->flags) & IRQF_SHARED) ||
((old->flags ^ new->flags) & IRQF_TRIGGER_MASK) ||
((old->flags ^ new->flags) & IRQF_ONESHOT)) {
old_name = old->name;
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 {
thread_mask |= old->thread_mask;
old_ptr = &old->next;
old = *old_ptr;
} while (old);
shared = 1;
}

/*
* Setup the thread mask for this irqaction. Unlikely to have
* 32 resp 64 irqs sharing one line, but who knows.
*/
if (new->flags & IRQF_ONESHOT && thread_mask == ~0UL) {
ret = -EBUSY;
goto out_mask;
}
new->thread_mask = 1 << ffz(thread_mask);

//如果是共享,则仅需要验证新的action的类型与中断描述符是否一致即可。
//否则,这意味着中断描述符的action上一无所有,这是一个新伙计,则需要通过新的action,为中断描符述设置一些标志位、状态位等诸如此类
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);
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(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;

/* 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);
//注册proc
register_irq_proc(irq, desc);
new->dir = NULL;
register_handler_proc(irq, new);
free_cpumask_var(mask);

return 0;

mismatch:
#ifdef CONFIG_DEBUG_SHIRQ
if (!(new->flags & IRQF_PROBE_SHARED)) {
printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq);
if (old_name)
printk(KERN_ERR "current handler: %s\n", old_name);
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;
if (likely(!test_bit(IRQTF_DIED, &new->thread_flags)))
kthread_stop(t);
put_task_struct(t);
}
return ret;
}


































































































                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息