中断队列的初始化

参考文章: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_FIXME
    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;
}


我们首先看一下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;
}



0
0

你可能感兴趣的:(中断队列的初始化)