JJJ-1 early_irq_init

源码如下:
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();

    /* Let arch update nr_irqs and return the nr of preallocated irqs */
    // initcnt 为 16
    initcnt = arch_probe_nr_irqs();
    printk(KERN_INFO "NR_IRQS:%d nr_irqs:%d %d\n", NR_IRQS, nr_irqs, initcnt);

    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);
        set_bit(i, allocated_irqs);
        irq_insert_desc(i, desc);
    }
    return arch_early_irq_init();
}

先看看first_online_node,定义在include/linux/nodemask.h,为0

init_irq_default_affinity();函数如下

static void __init init_irq_default_affinity(void)
{
    alloc_cpumask_var(&irq_default_affinity, GFP_NOWAIT);
    cpumask_setall(irq_default_affinity);
}

这个函数大概就是分配一个bitmap,全局变量irq_default_affinity,长度为4个bit,并把它们全部置位

kernel/irq/manage.c:116:cpumask_var_t irq_default_affinity;

cpumask_var_t 的类型定义:

./include/linux/cpumask.h:652:typedef struct cpumask *cpumask_var_t;

include/linux/cpumask.h:15:typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;
NR_CPUS 为 4

#define DECLARE_BITMAP(name,bits) \
			unsigned long name[BITS_TO_LONGS(bits)]

#define BITS_TO_LONGS(nr)     DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))

#define BITS_PER_BYTE       8

#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))

下面看看arch_probe_nr_irqs:

int __init arch_probe_nr_irqs(void)
{
	// 4.8判断这个名字
    nr_irqs = machine_desc->nr_irqs ? machine_desc->nr_irqs : NR_IRQS;
    // machine_desc->nr_irqs为0,NR_IRQS为16
    return nr_irqs;
}

首先看看machine_desc的由来,
定义在:arch/arm/kernel/setup.c:150:const struct machine_desc *machine_desc __initdata;
只是一个指针,在哪里赋值?
setup_arch

此函数在start_kernel函数启动流程中,早于early_irq_init();init_IRQ();
arch/arm/kernel/setup.c
void __init setup_arch(char **cmdline_p)
{
...
const struct machine_desc *mdesc;
...
// 这个值为0x83000000,即为传入设备树的物理地址
// arch/arm/kernel/head-common.S:116:      .long   __atags_pointer                 @ r6
// __atags_pointer参数这个全局变量就是r6的寄存器值;是设备树在内存中的起始地址;
// 根据传入的设备树dtb的首地址完成一些初始化操作;
mdesc = setup_machine_fdt(__atags_pointer);
if (!mdesc)
	mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
machine_desc = mdesc;
...
}

分析函数setup_machine_fdt

/**
 * setup_machine_fdt - Machine setup when an dtb was passed to the kernel
 * @dt_phys: physical address of dt blob
 *
 * If a dtb was passed to the kernel in r2, then use it to choose the
 * correct machine_desc and to setup the system.
 */
const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
{
    const struct machine_desc *mdesc, *mdesc_best = NULL;

#ifdef CONFIG_ARCH_MULTIPLATFORM
    DT_MACHINE_START(GENERIC_DT, "Generic DT based system")
    MACHINE_END

    mdesc_best = &__mach_desc_GENERIC_DT;
#endif
	// dt_phys即为传入的参数__atags_pointer,设备树的物理地址
    if (!dt_phys || !early_init_dt_verify(phys_to_virt(dt_phys)))
        return NULL;
	// 返回的mdesc定义为arch/arm/mach-imx/mach-imx6ul.c:214:DT_MACHINE_START(IMX6UL, "Freescale i.MX6 Ultralite (Device Tree)")
    mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);
	if (!mdesc) {
        const char *prop;
        int size;
        unsigned long dt_root;

        early_print("\nError: unrecognized/unsupported "
                "device tree compatible list:\n[ ");

        dt_root = of_get_flat_dt_root();
        prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
        while (size > 0) {
            early_print("'%s' ", prop);
            size -= strlen(prop) + 1;
            prop += strlen(prop) + 1;
        }
        early_print("]\n\n");

        dump_machine_table(); /* does not return */
    }
	// 4-7
    /* We really don't want to do this, but sometimes firmware provides buggy data */
    if (mdesc->dt_fixup)
        mdesc->dt_fixup();
	//主要是进行早期的初始化,根据DTB配置,进行早期的初始化,以后专门分析设备树的解析在细看把
    early_init_dt_scan_nodes();

    /* Change machine number to match the mdesc we're using */
    // nr静态初始化为~0
    __machine_arch_type = mdesc->nr;

    return mdesc;
}

关于 DT_MACHINE_START

#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,

DT_MACHINE_START(GENERIC_DT, "Generic DT based system")
    MACHINE_END

展开为:

static const struct machine_desc __mach_desc_GENERIC_DT
__used
__attribute__((__section__(".arch.info.init"))) = {
	.nr     = ~0,
	.name       = "Generic DT based system"
}

看of_flat_dt_match_machine

/**
 * of_flat_dt_match_machine - Iterate match tables to find matching machine.
 *
 * @default_match: A machine specific ptr to return in case of no match.
 * @get_next_compat: callback function to return next compatible match table.
 *
 * Iterate through machine match tables to find the best match for the machine
 * compatible string in the FDT.
 */
 // 第二个参数为arch_get_next_mach
const void * __init of_flat_dt_match_machine(const void *default_match,
        const void * (*get_next_compat)(const char * const**))
{
    const void *data = NULL;
    const void *best_data = default_match;
    const char *const *compat;
    unsigned long dt_root;
    unsigned int best_score = ~1, score = 0;

	// 此函数直接返回0
    dt_root = of_get_flat_dt_root();
    // compat就是上面说的__mach_desc_GENERIC_DT
    while ((data = get_next_compat(&compat))) {
        score = of_flat_dt_match(dt_root, compat);
        if (score > 0 && score < best_score) {
            best_data = data;
            best_score = score;
            // 这个分支会走到,best_data 不是NULL
        }
    }
    // 下面这个if不可能走到
    if (!best_data) {
		const char *prop;
        int size;

        pr_err("\n unrecognized device tree list:\n[ ");

        prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
        if (prop) {
            while (size > 0) {
                printk("'%s' ", prop);
                size -= strlen(prop) + 1;
                prop += strlen(prop) + 1;
            }
        }
        printk("]\n\n");
        return NULL;
    }
	// 此函数详见下
    pr_info("Machine model: %s\n", of_flat_dt_get_machine_name());
	// 最终会指向 DT_MACHINE_START(IMX6UL, "Freescale i.MX6 Ultralite (Device Tree)")
    return best_data;
}

第二参数为arch_get_next_mach

static const void * __init arch_get_next_mach(const char *const **match)
{
    static const struct machine_desc *mdesc = __arch_info_begin;
    const struct machine_desc *m = mdesc;

    if (m >= __arch_info_end)
        return NULL;

    mdesc++;
    // dt_compat: array of device tree * 'compatible' strings
    *match = m->dt_compat;
    return m;
}

__arch_info_begin是啥?
arch/arm/kernel/vmlinux.lds.S中:
188     .init.arch.info : {
189         __arch_info_begin = .;
190         *(.arch.info.init)
191         __arch_info_end = .;
192     }
其实这个函数就是依次遍历内存空间中的此类型结构体变量
就是用DT_MACHINE_START

看of_flat_dt_match

/**
 * of_flat_dt_match - Return true if node matches a list of compatible values
 */
int __init of_flat_dt_match(unsigned long node, const char *const *compat)
{
    return of_fdt_match(initial_boot_params, node, compat);
}

initial_boot_params在early_init_dt_verify中赋值->setup_machine_fdt,传入的值其实为phys_to_virt(__atags_pointer)

__atags_pointer设备树物理地址0x83000000的来历:
在这里插入图片描述

再看一下bootloader的参数bootcmd的内容:

=> env print bootcmd
bootcmd=tftp 80800000 zImage; tftp 83000000 imx6ull-alientek-emmc.dtb; bootz 80800000 - 83000000

// arch/arm/kernel/head-common.S:116: .long __atags_pointer @ r6
// __atags_pointer参数这个全局变量就是r6的寄存器值;是设备树在内存中的起始地址;

看of_fdt_match:

/**
 * of_fdt_match - Return true if node matches a list of compatible values
 */
 这里的blob就是作为参数传入的initial_boot_params,0x83000000的虚拟地址
int of_fdt_match(const void *blob, unsigned long node,
                 const char *const *compat)
{
    unsigned int tmp, score = 0;

    if (!compat)
        return 0;

    while (*compat) {
        tmp = of_fdt_is_compatible(blob, node, *compat);
        if (tmp && (score == 0 || (tmp < score)))
            score = tmp;
        compat++;
    }

    return score;
}

/**
 * of_fdt_is_compatible - Return true if given node from the given blob has
 * compat in its compatible list
 * @blob: A device tree blob
 * @node: node to test
 * @compat: compatible string to compare with compatible list.
 *
 * On match, returns a non-zero value with smaller values returned for more
 * specific compatible values.
 */
 这里的blob就是作为参数传入的initial_boot_params,0x83000000的虚拟地址
int of_fdt_is_compatible(const void *blob,
              unsigned long node, const char *compat)
{
    const char *cp;
    int cplen;
    unsigned long l, score = 0;
	// node 为0这里是获取第一个属性值?
    cp = fdt_getprop(blob, node, "compatible", &cplen);
    if (cp == NULL)
        return 0;
    while (cplen > 0) {
        score++;
        if (of_compat_cmp(cp, compat, strlen(compat)) == 0)
            return score;
        l = strlen(cp) + 1;
        cp += l;
        cplen -= l;
    }

    return 0;
}

看fdt_getprop

    fdt_getprop
    const void *fdt_getprop(const void *fdt, int nodeoffset, const char *name, int *lenp)
    eg: mac = fdt_getprop(gd->fdt_blob, node, “mac-address”, &len);
    功能:获得节点node的某个字符串属性值。

看of_flat_dt_get_machine_name

const char * __init of_flat_dt_get_machine_name(void)
{
    const char *name;
    //这个函数很简单,直接返回0
    unsigned long dt_root = of_get_flat_dt_root();
	// 这个函数会调用fdt_getprop,获取根结点的model属性
    name = of_get_flat_dt_prop(dt_root, "model", NULL);
    if (!name) {
        name = of_get_flat_dt_prop(dt_root, "compatible", NULL);
    }
    return name;
}

看 alloc_desc
分配一个 struct irq_desc,关于这个类型定义:可以参考这篇blog,还有struct irq_chip 相关介绍

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
static void nvme_calc_irq_sets(struct irq_affinity *affd, unsigned int nrirqs) { struct nvme_dev *dev = affd->priv; unsigned int nr_read_queues, nr_write_queues = dev->nr_write_queues; if (!nrirqs) { nrirqs = 1; nr_read_queues = 0; } else if (nrirqs == 1 || !nr_write_queues) { nr_read_queues = 0; } else if (nr_write_queues >= nrirqs) { nr_read_queues = 1; } else { nr_read_queues = nrirqs - nr_write_queues; } dev->io_queues[HCTX_TYPE_DEFAULT] = nrirqs - nr_read_queues; affd->set_size[HCTX_TYPE_DEFAULT] = nrirqs - nr_read_queues; dev->io_queues[HCTX_TYPE_READ] = nr_read_queues; affd->set_size[HCTX_TYPE_READ] = nr_read_queues; affd->nr_sets = nr_read_queues ? 2 : 1; }static int nvme_setup_irqs(struct nvme_dev *dev, unsigned int nr_io_queues) { struct pci_dev *pdev = to_pci_dev(dev->dev); struct irq_affinity affd = { //ָ���ж��׺��Եļ��㷽���Ͳ��� .pre_vectors = 1, .calc_sets = nvme_set_irq_affinity, //nvme_calc_irq_sets, .priv = dev, }; unsigned int irq_queues, poll_queues; poll_queues = min(dev->nr_poll_queues, nr_io_queues - 1); dev->io_queues[HCTX_TYPE_POLL] = poll_queues; dev->io_queues[HCTX_TYPE_DEFAULT] = 1; dev->io_queues[HCTX_TYPE_READ] = 0; irq_queues = 1; if (!(dev->ctrl.quirks & NVME_QUIRK_SINGLE_VECTOR)) irq_queues += (nr_io_queues - poll_queues); return pci_alloc_irq_vectors_affinity(pdev, 1, irq_queues, PCI_IRQ_ALL_TYPES | PCI_IRQ_AFFINITY, &affd); } 在 Linux 5.17.12 内核版本中,如何修改 pci_alloc_irq_vectors_affinity() 函数的 affinity_hint 参数来绑定 NVMe 驱动的所有 I/O 队列到同一 CPU 核心上。代码展示
06-09

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值