JJJ--linux irq

文章详细介绍了Linux内核在启动过程中,针对Freescalei.MX6U平台,如何通过`of_irq_init`函数扫描设备树并初始化中断控制器。具体涉及`irq_domain_add_linear`调用,以及`gic_of_init`和`imx_gpc_init`两个中断控制器的初始化。这个过程涉及到Cortex-A7GIC和i.MX6的GPC中断控制器的设置。

以imx6u为例:
有两处调用了irq_domain_add_linear向系统注册irq domain 线性映射。没有no_map和Radix_Tree_map这两种方式(irq_domain_add_nomap 和 irq_domain_add_tree)

调用栈2处分别为:

1、

CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.1.15 #1
Hardware name: Freescale i.MX6 Ultralite (Device Tree)
[<80015ed4>] (unwind_backtrace) from [<80012794>] (show_stack+0x10/0x14)
[<80012794>] (show_stack) from [<806f2218>] (dump_stack+0x80/0xc8)
[<806f2218>] (dump_stack) from [<809dc3bc>] (gic_init_bases+0xa4/0x280)
[<809dc3bc>] (gic_init_bases) from [<809dc648>] (gic_of_init+0xb0/0x108)
[<809dc648>] (gic_of_init) from [<809e76ec>] (of_irq_init+0x158/0x278)
[<809e76ec>] (of_irq_init) from [<809a4328>] (init_IRQ+0x28/0x84)
[<809a4328>] (init_IRQ) from [<809a1ac4>] (start_kernel+0x210/0x3a0)
[<809a1ac4>] (start_kernel) from [<8000807c>] (0x8000807c)

2、

CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.1.15 #1
Hardware name: Freescale i.MX6 Ultralite (Device Tree)
[<80015ed4>] (unwind_backtrace) from [<80012794>] (show_stack+0x10/0x14)
[<80012794>] (show_stack) from [<806f2218>] (dump_stack+0x80/0xc8)
[<806f2218>] (dump_stack) from [<80075498>] (irq_domain_add_hierarchy+0x38/0xc8)
[<80075498>] (irq_domain_add_hierarchy) from [<809aec20>] (imx_gpc_init+0xa0/0x2a0)
[<809aec20>] (imx_gpc_init) from [<809e76ec>] (of_irq_init+0x158/0x278)
[<809e76ec>] (of_irq_init) from [<809a4328>] (init_IRQ+0x28/0x84)
[<809a4328>] (init_IRQ) from [<809a1ac4>] (start_kernel+0x210/0x3a0)
[<809a1ac4>] (start_kernel) from [<8000807c>] (0x8000807c)

由start_kernel找到init_IRQ(arch/arm/kernel/irq.c),到irqchip_init(drivers/irqchip/irqchip.c),到of_irq_init(__irqchip_of_table) drivers/of/irq.c

/**
 * 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 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) {
    	// 这个属性位于imx6ull.dtsi中的interrupt-controller@00a01000节点内部
        if (!of_find_property(np, "interrupt-controller", NULL) ||
                !of_device_is_available(np))   //节点的status为ok或者okey,或者没有status属性就返回true
            continue;
		/*
         * Here, we allocate and populate an intc_desc with the node
         * pointer, interrupt-parent device_node etc.
         */
        desc = kzalloc(sizeof(*desc), GFP_KERNEL);
        if (WARN_ON(!desc))
            goto err;

        desc->dev = 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;
        	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;
            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);
        kfree(desc);
    }
}
                                         

下面是2个struct of_device_id类型的条目,是irq控制器初始化相关:
1、

// drivers/irqchip/irq-gic.c
IRQCHIP_DECLARE(cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init);

// drivers/irqchip/irqchip.h
#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)
OF_DECLARE_2(irqchip, cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init);

//include/linux/of.h
#define OF_DECLARE_2(table, name, compat, fn) \
	_OF_DECLARE(table, name, compat, fn, of_init_fn_2)

_OF_DECLARE(irqchip, cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init, of_init_fn_2)

// include/linux/of.h
#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  }

static const struct of_device_id __of_table_cortex_a7_gic
		__used __section(__irqchip_of_table)
		 = {.compatible = "arm,cortex-a7-gic", 
			.data = (gic_of_init == (of_init_fn_2)NULL?gic_of_init:gic_of_init   }

通过"arm,cortex-a7-gic"匹配,将match->data赋值给init_urq_cb,即gic_of_init,并执行

2、

// arch/arm/mach-imx/gpc.c
OF_DECLARE_2(irqchip, imx_gpc, "fsl,imx6q-gpc", imx_gpc_init);
...
static const struct of_device_id __of_table_imx_gpc
		__used __section(__irqchip_of_table)
		= {.compatible = "fsl,imx6q-gpc",
			.data = (imx_gpc_init == (of_init_fn_2)NULL?imx_gpc_init:imx_gpc_init)}

找出这两个compat对应的设备节点:

// imx6ull.dtsi
...
intc: interrupt-controller@00a01000 {
        compatible = "arm,cortex-a7-gic";
        #interrupt-cells = <3>;
        interrupt-controller;
        reg = <0x00a01000 0x1000>,
              <0x00a02000 0x100>;
    };
...
 gpc: gpc@020dc000 {
                compatible = "fsl,imx6ul-gpc", "fsl,imx6q-gpc";
                reg = <0x020dc000 0x4000>;
                interrupt-controller;
                #interrupt-cells = <3>;
                interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
                interrupt-parent = <&intc>;
                fsl,mf-mix-wakeup-irq = <0xfc00000 0x7d00 0x0 0x1400640>;
            };
...

先看 gic_of_init:在drivers/irqchip/irq-gic.c
gic_of_init

`irq -s` 并不是一个标准的 Linux 命令。你可能是想查看与中断请求(IRQ, Interrupt Request)相关的信息,尤其是系统中各个 IRQ 的使用情况和中断统计。 在 Linux 系统中,正确的命令或方法是通过查看 `/proc/interrupts` 文件来获取中断信息。`-s` 可能是你误记了某个参数,或者你使用的是某个特定工具的缩写(比如 `irqbalance --summary`),但常见的 `irq` 命令并不存在于大多数系统中。 --- ### ✅ 正确查看 IRQ 信息的方法 #### 方法一:查看 `/proc/interrupts` 这是最常用、最直接的方式: ```bash cat /proc/interrupts ``` 输出示例: ``` CPU0 CPU1 0: 123456 78901 IO-APIC-edge timer 1: 100 200 IO-APIC-edge i8042 8: 5 0 IO-APIC-edge rtc0 9: 12345 67890 HPET_MSI-edge acpi 16: 1000 2000 PCI-MSI-edge ahci[0] 40: 999999 8888888 PCI-MSI-edge eth0 ... ``` - 每一行代表一个 IRQ 号。 - 后面的数字表示该中断被触发的次数(按 CPU 分列)。 - 最后一列是设备名称或中断源(如 `eth0`, `i8042`, `timer` 等)。 这个文件由内核维护,实时反映当前系统的中断分布情况。 #### 方法二:持续监控中断变化(类似 `-s` 统计) 如果你想“看统计”(可能你理解的 `-s` 是 summary 或 stats),可以使用如下脚本周期性地读取并计算差值: ```bash #!/bin/bash # irq_stats.sh - 模拟中断统计监控 watch -n 1 'echo "=== $(date) ==="; cat /proc/interrupts | head -10' ``` 或者更高级一点,显示两次之间的增量: ```python #!/usr/bin/env python3 import time import os def read_irq(): with open('/proc/interrupts', 'r') as f: lines = f.readlines() irqs = {} for line in lines[1:]: parts = line.strip().split(':') irq_num = parts[0].strip() counts = list(map(int, parts[1].strip().split()[:-1])) # 所有CPU上的中断数 irqs[irq_num] = sum(counts) # 总中断次数 return irqs print("Monitoring IRQs... (Ctrl+C to stop)") old_irqs = read_irq() try: while True: time.sleep(2) new_irqs = read_irq() print(f"\n--- {time.strftime('%H:%M:%S')} ---") for irq in sorted(new_irqs.keys()): delta = new_irqs[irq] - old_irqs.get(irq, 0) devname = "" # 可以解析最后一列设备名(略复杂) if delta > 0: print(f"IRQ {irq:2} : +{delta:6d}") old_irqs = new_irqs except KeyboardInterrupt: print("\nStopped.") ``` 运行效果:每 2 秒打印一次各 IRQ 中断增加的数量。 保存为 `irq-monitor.py`,运行: ```bash sudo python3 irq-monitor.py ``` > 注意:需要 `root` 权限确保能稳定读取。 --- ### 补充说明 - **`/proc/interrupts`** 是只读文件,提供实时数据。 - 多处理器系统会显示多个 CPU 列。 - MSI(Message Signaled Interrupts)中断通常不显示在传统 IRQ 表中,但也会出现在这里。 - 如果你想查看某个网卡中断绑定情况(用于性能调优),也可以结合此命令分析。 --- ### 查看 IRQ affinity(中断亲和性) 有时你还想看中断被绑定到哪些 CPU 上: ```bash cat /proc/irq/*/smp_affinity ``` 或者具体某个 IRQ(如 IRQ 40): ```bash cat /proc/irq/40/smp_affinity ``` 输出是十六进制 bitmask,表示允许运行的 CPU 集合。 --- ### 总结 你说的 `irq -s` 很可能是误解。Linux 中没有通用的 `irq` 命令行工具。正确方式是: ✅ 使用: ```bash cat /proc/interrupts ``` ✅ 要动态观察: ```bash watch -n 1 'cat /proc/interrupts' ``` ✅ 要统计增量:用 Python 或 shell 脚本计算差值。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值