//init/main.casmlinkage void __init start_kernel(void){...... trap_init(); //空函数...... early_irq_init(); init_IRQ();......}
在start_kernel()函数中调用了early_irq_init()函数,根据内核配置时是否选择了CONFIG_SPARSE_IRQ,而可以选择两个不同版本的该函数early_irq_init()中的一个进行编译。CONFIG_SPARSE_IRQ配置项,用于支持稀疏irq号,对于发行版的内核很有用,它允许定义一个高CONFIG_NR_CPUS值,但仍然不希望消耗太多内存的情况。
//arch/arm/kernel/irq/irqdesc.c/int __init early_irq_init(void){ int i, initcnt, node = first_online_node; struct irq_desc *desc; init_irq_default_affinity(); //由CONFIG_SMP宏决定是否为空函数,见下面分析一 /* Let arch update nr_irqs and return the nr of preallocated irqs */ initcnt = arch_probe_nr_irqs();//体系结构相关的代码来决定预先分配的中断描述符的个数见分析二 if (WARN_ON(nr_irqs > IRQ_BITMAP_BITS)) //见下面分析三 nr_irqs = IRQ_BITMAP_BITS; if (WARN_ON(initcnt > IRQ_BITMAP_BITS)) initcnt = IRQ_BITMAP_BITS; if (initcnt > nr_irqs) nr_irqs = initcnt; for (i = 0; i < initcnt; i++) { desc = alloc_desc(i, node, NULL); //分配 struct irq_desc结构,分析四 set_bit(i, allocated_irqs); //设置相应的位数组,分析三 irq_insert_desc(i, desc); //保存irq_desc指针,分析四 } return arch_early_irq_init(); //kernel/softirq.c中定义,为空函数}
//arch/arm/kernel/irq.cvoid __init init_IRQ(void){ int ret; /* *在.config文件中CONFIG_OF有定义,并且通过分析2可知machine_desc->init_irq为空。 * */ if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq) irqchip_init(); //函数被执行,分析五 else machine_desc->init_irq(); //machine_desc->l2c_aux_mask和machine_desc->l2c_aux_val初始化为零所以下面不执行 if (IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_CACHE_L2X0) && (machine_desc->l2c_aux_mask || machine_desc->l2c_aux_val)) { if (!outer_cache.write_sec) outer_cache.write_sec = machine_desc->l2c_write_sec; ret = l2x0_of_init(machine_desc->l2c_aux_val, machine_desc->l2c_aux_mask); if (ret) pr_err("L2C: failed to init: %d\n", ret); } uniphier_cache_init(); //arch/arm/include/asm/hardware/cache-uniphier.h 为空}
//arch/arm/kernel/irq/irqdesc.c#if defined(CONFIG_SMP)static void __init init_irq_default_affinity(void){ alloc_cpumask_var(&irq_default_affinity, GFP_NOWAIT); cpumask_setall(irq_default_affinity);}#elsestatic void __init init_irq_default_affinity(void){}#endif
//arch/arm/kernel/irq.c#ifdef CONFIG_SPARSE_IRQint __init arch_probe_nr_irqs(void){ nr_irqs = machine_desc->nr_irqs ? machine_desc->nr_irqs : NR_IRQS;//NR_IRQS 16 return nr_irqs; //分析下面的分析,得知返回的是NR_IRQS}#endif/* *NR_IRQS //体系结构相关的代码来决定预先分配的中断描述符的个数 *arch/arm/kernel/irq.c中包含#include *///arch/arm/include/asm/irq.h#define NR_IRQS_LEGACY 16#ifndef CONFIG_SPARSE_IRQ#include #else#define NR_IRQS NR_IRQS_LEGACY#endif/* *machine_desc在setup_arch中赋值,machine_desc指向了arch/arm/mach-s5pv210/s5pv210.c中定义的 *DT_MACHINE_START(S5PV210_DT, "Samsung S5PC110/S5PV210-based board") *具体分析如下 *///arch/arm/kernel/setup.cconst struct machine_desc *machine_desc __initdata;//arch/arm/mach-s5pv210/s5pv210.cDT_MACHINE_START(S5PV210_DT, "Samsung S5PC110/S5PV210-based board") .dt_compat = s5pv210_dt_compat, .map_io = s5pv210_dt_map_io, .restart = s5pv210_dt_restart, .init_late = s5pv210_dt_init_late,MACHINE_END/* * Set of macros to define architecture features. This is built into * a table by the linker. */#define MACHINE_START(_type,_name) \static const struct machine_desc __mach_desc_##_type \ __used \ __attribute__((__section__(".arch.info.init"))) = { \ .nr = MACH_TYPE_##_type, \ .name = _name,#define MACHINE_END \};#define DT_MACHINE_START(_name, _namestr) \static const struct machine_desc __mach_desc_##_name \ __used \ __attribute__((__section__(".arch.info.init"))) = { \ .nr = ~0, \ .name = _namestr,#endif//arch/arm/include/asm/mach/arch.hstruct machine_desc { unsigned int nr; /* architecture number */ const char *name; /* architecture name */ unsigned long atag_offset; /* tagged list (relative) */ const char *const *dt_compat; /* array of device tree * 'compatible' strings */ unsigned int nr_irqs; /* number of IRQs */#ifdef CONFIG_ZONE_DMA phys_addr_t dma_zone_size; /* size of DMA-able area */#endif unsigned int video_start; /* start of video RAM */ unsigned int video_end; /* end of video RAM */ unsigned char reserve_lp0 :1; /* never has lp0 */ unsigned char reserve_lp1 :1; /* never has lp1 */ unsigned char reserve_lp2 :1; /* never has lp2 */ enum reboot_mode reboot_mode; /* default restart mode */ unsigned l2c_aux_val; /* L2 cache aux value */ unsigned l2c_aux_mask; /* L2 cache aux mask */ void (*l2c_write_sec)(unsigned long, unsigned); const struct smp_operations *smp; /* SMP operations */ bool (*smp_init)(void); void (*fixup)(struct tag *, char **); void (*dt_fixup)(void); long long (*pv_fixup)(void); void (*reserve)(void);/* reserve mem blocks */ void (*map_io)(void);/* IO mapping function */ void (*init_early)(void); void (*init_irq)(void); void (*init_time)(void); void (*init_machine)(void); void (*init_late)(void);#ifdef CONFIG_MULTI_IRQ_HANDLER void (*handle_irq)(struct pt_regs *);#endif void (*restart)(enum reboot_mode, const char *);};
//kernel/irq/internals.h#ifdef CONFIG_SPARSE_IRQ#define IRQ_BITMAP_BITS (NR_IRQS + 8196)#else#define IRQ_BITMAP_BITS NR_IRQS#endif//include/linux/types.h#define DECLARE_BITMAP(name,bits) \ unsigned long name[BITS_TO_LONGS(bits)]//arch/arm/include/asm/bitops.h#define set_bit(nr,p) ATOMIC_BITOP(set_bit,nr,p) //_set_bit#define clear_bit(nr,p) ATOMIC_BITOP(clear_bit,nr,p)#define change_bit(nr,p) ATOMIC_BITOP(change_bit,nr,p)#define test_and_set_bit(nr,p) ATOMIC_BITOP(test_and_set_bit,nr,p)#define test_and_clear_bit(nr,p) ATOMIC_BITOP(test_and_clear_bit,nr,p)#define test_and_change_bit(nr,p) ATOMIC_BITOP(test_and_change_bit,nr,p)/* * __builtin_constant_p 是编译器gcc内置函数,用于判断一个值是否为编译时常量, * 如果是常数,函数返回1 ,否则返回0。此内置函数的典型用法是在宏中用于手动编译时优化 */#ifndef CONFIG_SMP //此宏在.config中未定义/* * The __* form of bitops are non-atomic and may be reordered. */#define ATOMIC_BITOP(name,nr,p) \ (__builtin_constant_p(nr) ? ____atomic_##name(nr, p) : _##name(nr,p)) #else#define ATOMIC_BITOP(name,nr,p) _##name(nr,p)#endif//set_bit函数实现分析#define set_bit(nr,p) ATOMIC_BITOP(set_bit,nr,p) //_set_bit//arch/arm/lib/setbit.S/* * linux/arch/arm/lib/setbit.S * * Copyright (C) 1995-1996 Russell King * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include #include "bitops.h" .textbitop _set_bit, orr //arch/arm/lib/bitops.h .macro bitop, name, instrENTRY( \name )UNWIND( .fnstart ) ands ip, r1, #3 strneb r1, [ip] @ assert word-aligned mov r2, #1 and r3, r0, #31 @ Get bit offset mov r0, r0, lsr #5 add r1, r1, r0, lsl #2 @ Get word offset#if __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP) .arch_extension mp ALT_SMP(W(pldw) [r1]) ALT_UP(W(nop))#endif mov r3, r2, lsl r31: ldrex r2, [r1] \instr r2, r2, r3 strex r0, r2, [r1] cmp r0, #0 bne 1b bx lr UNWIND( .fnend )ENDPROC(\name ) .endm
分析四 alloc_desc
//kernel/irq/irqdesc.cstatic RADIX_TREE(irq_desc_tree, GFP_KERNEL); //基数树static void irq_insert_desc(unsigned int irq, struct irq_desc *desc){ radix_tree_insert(&irq_desc_tree, irq, desc);}struct irq_desc *irq_to_desc(unsigned int irq){ return radix_tree_lookup(&irq_desc_tree, irq);}EXPORT_SYMBOL(irq_to_desc);static void delete_irq_desc(unsigned int irq){ radix_tree_delete(&irq_desc_tree, irq);}static struct irq_desc *alloc_desc(int irq, int node, struct module *owner){ struct irq_desc *desc; gfp_t gfp = GFP_KERNEL; desc = kzalloc_node(sizeof(*desc), gfp, node); if (!desc) return NULL; /* allocate based on nr_cpu_ids */ desc->kstat_irqs = alloc_percpu(unsigned int); if (!desc->kstat_irqs) goto err_desc; if (alloc_masks(desc, gfp, node)) goto err_kstat; raw_spin_lock_init(&desc->lock); lockdep_set_class(&desc->lock, &irq_desc_lock_class); desc_set_defaults(irq, desc, node, owner); return desc;err_kstat: free_percpu(desc->kstat_irqs);err_desc: kfree(desc); return NULL;}static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,struct module *owner) { int cpu; desc->irq_common_data.handler_data = NULL; desc->irq_common_data.msi_desc = NULL; desc->irq_data.common = &desc->irq_common_data; desc->irq_data.irq = irq; desc->irq_data.chip = &no_irq_chip; desc->irq_data.chip_data = NULL; irq_settings_clr_and_set(desc, ~0, _IRQ_DEFAULT_INIT_FLAGS); irqd_set(&desc->irq_data, IRQD_IRQ_DISABLED); desc->handle_irq = handle_bad_irq; desc->depth = 1; desc->irq_count = 0; desc->irqs_unhandled = 0; desc->name = NULL; desc->owner = owner; for_each_possible_cpu(cpu) *per_cpu_ptr(desc->kstat_irqs, cpu) = 0; desc_smp_init(desc, node);}//include/linux/irqdesc.h/** * struct irq_desc - interrupt descriptor * @irq_common_data: per irq and chip data passed down to chip functions * @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 * @threads_handled: stats field for deferred spurious detection of threaded handlers * @threads_handled_last: comparator field for deferred spurious detection of theraded handlers * @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 * @nr_actions: number of installed actions on this descriptor * @no_suspend_depth: number of irqactions on a irq descriptor with * IRQF_NO_SUSPEND set * @force_resume_depth: number of irqactions on a irq descriptor with * IRQF_FORCE_RESUME set * @dir: /proc/irq/ procfs entry * @name: flow handler name for /proc/interrupts output */struct irq_desc { struct irq_common_data irq_common_data; struct irq_data irq_data; 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; atomic_t threads_handled; int threads_handled_last; raw_spinlock_t lock; struct cpumask *percpu_enabled;#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_PM_SLEEP unsigned int nr_actions; unsigned int no_suspend_depth; unsigned int cond_suspend_depth; unsigned int force_resume_depth;#endif#ifdef CONFIG_PROC_FS struct proc_dir_entry *dir;#endif int parent_irq; struct module *owner; const char *name;} ____cacheline_internodealigned_in_smp;
分析五
//drivers/irqchip/irqchip.c/* * Copyright (C) 2012 Thomas Petazzoni * * Thomas Petazzoni * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */#include #include #include #include /* * This special of_device_id is the sentinel at the end of the * of_device_id[] array of all irqchips. It is automatically placed at * the end of the array by the linker, thanks to being part of a * special section. */static const struct of_device_idirqchip_of_match_end __used __section(__irqchip_of_table_end);//__irqchip_of_table定义在链接脚本arch/arm/kernel/vmlinux.ldsextern struct of_device_id __irqchip_of_table[];void __init irqchip_init(void){ of_irq_init(__irqchip_of_table); //分析六 acpi_probe_device_table(irqchip);}/* *__irqchip_of_table实际上是一个struct of_device_id结构体的数组, *__irqchip_of_table数组里的每一个元素使用IRQCHIP_DECLARE宏定义而来, *数组的每个元素都被放在了__irqchip_of_table段中 *//* * include/linux/irqchip.h * @name: name that must be unique accross all IRQCHIP_DECLARE of the * same file. * @compstr: compatible string of the irqchip driver * @fn: initialization function */#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)//include/linux/of.h#ifdef CONFIG_OF#define _OF_DECLARE(table, name, compat, fn, fn_type) \ static const struct of_device_id __of_table_##name \ __used __section(__##table##_of_table) \ = { .compatible = compat, \ .data = (fn == (fn_type)NULL) ? fn : fn }#else#define _OF_DECLARE(table, name, compat, fn, fn_type) \ static const struct of_device_id __of_table_##name \ __attribute__((unused)) \ = { .compatible = compat, \ .data = (fn == (fn_type)NULL) ? fn : fn }#endiftypedef int (*of_init_fn_2)(struct device_node *, struct device_node *);typedef void (*of_init_fn_1)(struct device_node *);#define OF_DECLARE_1(table, name, compat, fn) \ _OF_DECLARE(table, name, compat, fn, of_init_fn_1)#define OF_DECLARE_2(table, name, compat, fn) \ _OF_DECLARE(table, name, compat, fn, of_init_fn_2)
分析六 of_irq_init
//drivers/of/irq.cstruct of_intc_desc { struct list_head list; struct device_node *dev; struct device_node *interrupt_parent;};/** * of_irq_init - Scan and init matching interrupt controllers in DT * @matches: 0 terminated array of nodes to match and init function to call * * This function scans the device tree for matching interrupt controller nodes, * and calls their initialization functions in order with parents first. */void __init of_irq_init(const struct of_device_id *matches){ struct device_node *np, *parent = NULL; struct of_intc_desc *desc, *temp_desc; struct list_head intc_desc_list, intc_parent_list; INIT_LIST_HEAD(&intc_desc_list); INIT_LIST_HEAD(&intc_parent_list); for_each_matching_node(np, matches) { if (!of_find_property(np, "interrupt-controller", NULL) || !of_device_is_available(np)) continue; /* * Here, we allocate and populate an of_intc_desc with the node * pointer, interrupt-parent device_node etc. */ desc = kzalloc(sizeof(*desc), GFP_KERNEL); if (WARN_ON(!desc)) { of_node_put(np); goto err; } desc->dev = of_node_get(np); desc->interrupt_parent = of_irq_find_parent(np); if (desc->interrupt_parent == np) desc->interrupt_parent = NULL; list_add_tail(&desc->list, &intc_desc_list); } /* * The root irq controller is the one without an interrupt-parent. * That one goes first, followed by the controllers that reference it, * followed by the ones that reference the 2nd level controllers, etc. */ while (!list_empty(&intc_desc_list)) { /* * Process all controllers with the current 'parent'. * First pass will be looking for NULL as the parent. * The assumption is that NULL parent means a root controller. */ list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) { const struct of_device_id *match; int ret; of_irq_init_cb_t irq_init_cb; if (desc->interrupt_parent != parent) continue; list_del(&desc->list); match = of_match_node(matches, desc->dev); if (WARN(!match->data, "of_irq_init: no init function for %s\n", match->compatible)) { kfree(desc); continue; } pr_debug("of_irq_init: init %s @ %p, parent %p\n", match->compatible, desc->dev, desc->interrupt_parent); irq_init_cb = (of_irq_init_cb_t)match->data; /* *执行IRQCHIP_DECLARE宏定义全局变量里的函数, *此函数就是驱动代码了,这部分驱动代对arm一般 *gic驱动或者vic(pl192)的驱动。 */ ret = irq_init_cb(desc->dev, desc->interrupt_parent); if (ret) { kfree(desc); continue; } /* * This one is now set up; add it to the parent list so * its children can get processed in a subsequent pass. */ list_add_tail(&desc->list, &intc_parent_list); } /* Get the next pending parent that might have children */ desc = list_first_entry_or_null(&intc_parent_list, typeof(*desc), list); if (!desc) { pr_err("of_irq_init: children remain, but no parents\n"); break; } list_del(&desc->list); parent = desc->dev; kfree(desc); } list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) { list_del(&desc->list); kfree(desc); }err: list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) { list_del(&desc->list); of_node_put(desc->dev); kfree(desc); }}
Original: https://www.cnblogs.com/yifeichongtian2021/p/15512288.html
Author: 壹飞冲天
Title: Linux系统中断处理框架(3)【转】
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/585775/
转载文章受原作者版权保护。转载请注明原作者出处!