Linux Kernel中gicv3实现:SPIs中断routing到指定的CPU
生活随笔
收集整理的這篇文章主要介紹了
Linux Kernel中gicv3实现:SPIs中断routing到指定的CPU
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
快速鏈接:
.
👉👉👉 個人博客筆記導讀目錄(全部) 👈👈👈
文章目錄
- 1、開機時,為每個cpu分配affinity編號
- 2、開機時,將所有共享中斷綁定到cpu0
- 3、kernel API將中斷綁定到CPU :irq_set_affinity()
1、開機時,為每個cpu分配affinity編號
開機時,為每個cpu分配affinity編號,并寫入到mpidr_el1系統寄存器中
setup_arch()–>smp_build_mpidr_hash()
static void __init smp_build_mpidr_hash(void) {u32 i, affinity, fs[4], bits[4], ls;u64 mask = 0;/** Pre-scan the list of MPIDRS and filter out bits that do* not contribute to affinity levels, ie they never toggle.*/for_each_possible_cpu(i)mask |= (cpu_logical_map(i) ^ cpu_logical_map(0));pr_debug("mask of set bits %#llx\n", mask);/** Find and stash the last and first bit set at all affinity levels to* check how many bits are required to represent them.*/for (i = 0; i < 4; i++) {affinity = MPIDR_AFFINITY_LEVEL(mask, i);/** Find the MSB bit and LSB bits position* to determine how many bits are required* to express the affinity level.*/ls = fls(affinity);fs[i] = affinity ? ffs(affinity) - 1 : 0;bits[i] = ls - fs[i];}/** An index can be created from the MPIDR_EL1 by isolating the* significant bits at each affinity level and by shifting* them in order to compress the 32 bits values space to a* compressed set of values. This is equivalent to hashing* the MPIDR_EL1 through shifting and ORing. It is a collision free* hash though not minimal since some levels might contain a number* of CPUs that is not an exact power of 2 and their bit* representation might contain holes, eg MPIDR_EL1[7:0] = {0x2, 0x80}.*/mpidr_hash.shift_aff[0] = MPIDR_LEVEL_SHIFT(0) + fs[0];mpidr_hash.shift_aff[1] = MPIDR_LEVEL_SHIFT(1) + fs[1] - bits[0];mpidr_hash.shift_aff[2] = MPIDR_LEVEL_SHIFT(2) + fs[2] -(bits[1] + bits[0]);mpidr_hash.shift_aff[3] = MPIDR_LEVEL_SHIFT(3) +fs[3] - (bits[2] + bits[1] + bits[0]);mpidr_hash.mask = mask;mpidr_hash.bits = bits[3] + bits[2] + bits[1] + bits[0];pr_debug("MPIDR hash: aff0[%u] aff1[%u] aff2[%u] aff3[%u] mask[%#llx] bits[%u]\n",mpidr_hash.shift_aff[0],mpidr_hash.shift_aff[1],mpidr_hash.shift_aff[2],mpidr_hash.shift_aff[3],mpidr_hash.mask,mpidr_hash.bits);/** 4x is an arbitrary value used to warn on a hash table much bigger* than expected on most systems.*/if (mpidr_hash_size() > 4 * num_possible_cpus())pr_warn("Large number of MPIDR hash buckets detected\n");__flush_dcache_area(&mpidr_hash, sizeof(struct mpidr_hash)); }2、開機時,將所有共享中斷綁定到cpu0
在bootup時gic_dist_init()函數中,將所有的global中斷(中斷號大于32)綁定到bootup的cpu上,其實就是綁定到cpu0上
(irq-gic-v3.c) static void __init gic_dist_init(void) {unsigned int i;u64 affinity;void __iomem *base = gic_data.dist_base;/* Disable the distributor */writel_relaxed(0, base + GICD_CTLR);gic_dist_wait_for_rwp();/** Configure SPIs as non-secure Group-1. This will only matter* if the GIC only has a single security state. This will not* do the right thing if the kernel is running in secure mode,* but that's not the intended use case anyway.*/for (i = 32; i < gic_data.irq_nr; i += 32)writel_relaxed(~0, base + GICD_IGROUPR + i / 8);gic_dist_config(base, gic_data.irq_nr, gic_dist_wait_for_rwp);/* Enable distributor with ARE, Group1 */writel_relaxed(GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A | GICD_CTLR_ENABLE_G1,base + GICD_CTLR);/** Set all global interrupts to the boot CPU only. ARE must be* enabled.*/affinity = gic_mpidr_to_affinity(cpu_logical_map(smp_processor_id()));for (i = 32; i < gic_data.irq_nr; i++)gic_write_irouter(affinity, base + GICD_IROUTER + i * 8); //---------寫gicd_irouter寄存器 }3、kernel API將中斷綁定到CPU :irq_set_affinity()
在底層,也是調用到gic_write_irouter函數,寫gicd_irouter寄存器
中斷irq_set_affinity函數的介紹
(kernel-4.19/include/linux/interrupt.h) static inline int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask) {return __irq_set_affinity(irq, cpumask, false); } (kernel-4.19/kernel/irq/manage.c) int __irq_set_affinity(unsigned int irq, const struct cpumask *mask, bool force) {struct irq_desc *desc = irq_to_desc(irq);unsigned long flags;int ret;if (!desc)return -EINVAL;raw_spin_lock_irqsave(&desc->lock, flags);ret = irq_set_affinity_locked(irq_desc_get_irq_data(desc), mask, force);raw_spin_unlock_irqrestore(&desc->lock, flags);return ret; } EXPORT_SYMBOL_GPL(__irq_set_affinity);(kernel-4.19/kernel/irq/manage.c) int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask,bool force) {struct irq_chip *chip = irq_data_get_irq_chip(data);struct irq_desc *desc = irq_data_to_desc(data);int ret = 0;if (!chip || !chip->irq_set_affinity)return -EINVAL;if (irq_can_move_pcntxt(data) && !irqd_is_setaffinity_pending(data)) {ret = irq_try_set_affinity(data, mask, force);} else {irqd_set_move_pending(data);irq_copy_pending(desc, mask);}if (desc->affinity_notify) {kref_get(&desc->affinity_notify->kref);if (!schedule_work(&desc->affinity_notify->work)) {/* Work was already scheduled, drop our extra ref */kref_put(&desc->affinity_notify->kref,desc->affinity_notify->release);}}irqd_set(data, IRQD_AFFINITY_SET);return ret; }(kernel-4.19/kernel/irq/manage.c) static int irq_try_set_affinity(struct irq_data *data,const struct cpumask *dest, bool force) {int ret = irq_do_set_affinity(data, dest, force);/** In case that the underlying vector management is busy and the* architecture supports the generic pending mechanism then utilize* this to avoid returning an error to user space.*/if (ret == -EBUSY && !force)ret = irq_set_affinity_pending(data, dest);return ret; }(kernel-4.19/kernel/irq/manage.c) int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,bool force) {struct irq_desc *desc = irq_data_to_desc(data);struct irq_chip *chip = irq_data_get_irq_chip(data);int ret;if (!chip || !chip->irq_set_affinity)return -EINVAL;ret = chip->irq_set_affinity(data, mask, force);switch (ret) {case IRQ_SET_MASK_OK:case IRQ_SET_MASK_OK_DONE:cpumask_copy(desc->irq_common_data.affinity, mask);case IRQ_SET_MASK_OK_NOCOPY:irq_validate_effective_affinity(data);irq_set_thread_affinity(desc);ret = 0;}return ret; } (kernel-4.19/drivers/irqchip/irq-gic-v3.c) static struct irq_chip gic_chip = {.name = "GICv3",.irq_mask = gic_mask_irq,.irq_unmask = gic_unmask_irq,.irq_eoi = gic_eoi_irq,.irq_set_type = gic_set_type,.irq_set_affinity = gic_set_affinity,.irq_get_irqchip_state = gic_irq_get_irqchip_state,.irq_set_irqchip_state = gic_irq_set_irqchip_state,.flags = IRQCHIP_SET_TYPE_MASKED |IRQCHIP_SKIP_SET_WAKE |IRQCHIP_MASK_ON_SUSPEND, };static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,bool force) { .....gic_write_irouter(val, reg); ..... }總結
以上是生活随笔為你收集整理的Linux Kernel中gicv3实现:SPIs中断routing到指定的CPU的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [ARM异常]-armv8-aarch6
- 下一篇: optee中utee syscall的实