设备树学习(二十、番外篇-中断子系统之中断号的映射与反映射[二])
上一篇我们留了一个坑,说是通过下面这个函数获取一个中断描述符以及对应的虚拟中断号。
本节就分析一下怎样获取。
在这个开始之前我们先要说的是,因为中断目前分为两种分配方式,一种是通过一次性定义一个全局数组,每个virq是数组的一项的下标。
另一种是内核配置了CONFIG_SPARSE_IRQ,采用动态方式分配每一个中断描述符,这样可以避免某些中断号不使用,而浪费内存空间。
我们这里两种方式都分析一下,先分析第一种,不采用CONFIG_SPARSE_IRQ。
//NR_IRQS由对应的SOC来定义支持多少个中断(包括子中断)
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = {
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
}
};
int __init early_irq_init(void)
{
int count, i, node = first_online_node;
struct irq_desc *desc;
init_irq_default_affinity();
printk(KERN_INFO "NR_IRQS: %d\n", NR_IRQS);
desc = irq_desc;
count = ARRAY_SIZE(irq_desc);
//下面在内核启动时对数组中的每个irq_desc 都初始化通用的部分
for (i = 0; i < count; i++) {
desc[i].kstat_irqs = alloc_percpu(unsigned int);
alloc_masks(&desc[i], node);
raw_spin_lock_init(&desc[i].lock);
lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
desc_set_defaults(i, &desc[i], node, NULL, NULL); //我们注意这个函数
}
return arch_early_irq_init();
}
static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
const struct cpumask *affinity, 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; //在整个irq_desc的数组下标就是其虚拟中断号
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);
irqd_set(&desc->irq_data, IRQD_IRQ_MASKED);
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, affinity);
}
接下来正式开始从下面这个函数分析
//这里我们从上面调用开始,假定virq是-1,每次获取一个cnt = 1
//内核默认给这个全局编码赋值就是当前存在的中断描述符数量(因为没采用CONFIG_SPARSE_IRQ,即是最大的中断号了)
int nr_irqs = NR_IRQS;
int irq_domain_alloc_descs(int virq, unsigned int cnt, irq_hw_number_t hwirq,
int node, const struct cpumask *affinity)
{
unsigned int hint;
if (virq >= 0) { //这里传进来的是-1,进不去
virq = __irq_alloc_descs(virq, virq, cnt, node, THIS_MODULE,
affinity);
} else {
hint = hwirq % nr_irqs;
if (hint == 0) //我们这里hwirq 是从0~31的,所以hint 可能为1到32的任何值
hint++;
virq = __irq_alloc_descs(-1, hint, cnt, node, THIS_MODULE,
affinity);
if (virq <= 0 && hint > 1) {
virq = __irq_alloc_descs(-1, 1, cnt, node, THIS_MODULE,
affinity);
}
}
return virq;
}
/**
* irq_alloc_descs - allocate and initialize a range of irq descriptors
* @irq: Allocate for specific irq number if irq >= 0,对于irq大于等于0,是可以分配特定的中断号
* @from: Start the search from this irq number,从这个中断号开始找
* @cnt: Number of consecutive irqs to allocate.
* @node: Preferred node on which the irq descriptor should be allocated
* @owner: Owning module (can be NULL)
* @affinity: Optional pointer to an affinity mask array of size @cnt which
* hints where the irq descriptors should be allocated and which
* default affinities to use
*
* Returns the first irq number or error code
*/
int __ref
__irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
struct module *owner, const struct cpumask *affinity)
{
int start, ret;
if (!cnt) //确认申请的中断号大于等于1个
return -EINVAL;
if (irq >= 0) { //irq>= 0,表示是我们指定中断号就是irq了
if (from > irq) //指定查找的必须要在开始查找的,后面
return -EINVAL;
from = irq; //范围正确,则从指定中断号开始查,这样效率跟快
} else {
/*
* For interrupts which are freely allocated the
* architecture can force a lower bound to the @from
* argument. x86 uses this to exclude the GSI space.
*/
from = arch_dynirq_lower_bound(from); //别的架构定义的弱函数
}
mutex_lock(&sparse_irq_lock);
//从中断表中查找从from开始,没使用(响应位用0表示)的连续的cnt个空缺位置,返回找到的那个空缺的起始位置
start = bitmap_find_next_zero_area(allocated_irqs, IRQ_BITMAP_BITS,
from, cnt, 0);
ret = -EEXIST;
if (irq >=0 && start != irq)
goto unlock; //指定中断,查找到的不等于想要的中断,返回错误
if (start + cnt > nr_irqs) { //确定要用的最大的中断没有超出当前支持的中断数量
ret = irq_expand_nr_irqs(start + cnt); //这里表示想扩充中断数量,只对定义了CONFIG_SPARSE_IRQ有效,我们这里分析的是没定义的,直接返回错误
if (ret)
goto unlock;
}
ret = alloc_descs(start, cnt, node, affinity, owner); //确保可以获取了,这里直接产品能够desc数组获取从start开始地址的cnt个中断描述符
unlock:
mutex_unlock(&sparse_irq_lock);
return ret;
}
unsigned int __weak arch_dynirq_lower_bound(unsigned int from)
{
return from;
}
static int irq_expand_nr_irqs(unsigned int nr)
{
return -ENOMEM;
}
#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]
#ifdef CONFIG_SPARSE_IRQ
# define IRQ_BITMAP_BITS (NR_IRQS + 8196)
#else
# define IRQ_BITMAP_BITS NR_IRQS //目前我们分析的是这种
#endif
//即定义一个long的数组,能够每个bit代表一个中断,数组所有的bit之和刚好能表示完中断的数量
static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS);
static inline int alloc_descs(unsigned int start, unsigned int cnt, int node,
const struct cpumask *affinity,
struct module *owner)
{
u32 i;
//获取cnt个中断描述符
for (i = 0; i < cnt; i++) {
struct irq_desc *desc = irq_to_desc(start + i); //从irq_descs中根据数组下标获取desc
desc->owner = owner; //设置拥有者
}
bitmap_set(allocated_irqs, start, cnt); //标记allocated_irqs中已经获取的位
return start;
}
struct irq_desc *irq_to_desc(unsigned int irq)
{
return (irq < NR_IRQS) ? irq_desc + irq : NULL; //这种方式获取就很简单了,直接返回数组下标的偏移就可以
}
可以看到这里也没初始化,因为在启动时int __init early_irq_init(void)函数已经对所有的中断初始化了,这里就是最直接的一对一的绑定。
数组地址就是中断号的地址。
第二种,配置了CONFIG_SPARSE_IRQ,是采用基数树方式来管理的。
唯一不同的是在定义了CONFIG_SPARSE_IRQ后
#define NR_IRQS_LEGACY 16
#ifndef CONFIG_SPARSE_IRQ
#include <mach/irqs.h>
#else
#define NR_IRQS NR_IRQS_LEGACY //我们配置了,所以是16
#endif
int nr_irqs = NR_IRQS;
#ifdef CONFIG_SPARSE_IRQ
# define IRQ_BITMAP_BITS (NR_IRQS + 8196) //目前这种方式这个位超级多
#else
# define IRQ_BITMAP_BITS NR_IRQS
#endif
int __init arch_probe_nr_irqs(void)
{
nr_irqs = machine_desc->nr_irqs ? machine_desc->nr_irqs : NR_IRQS;
return nr_irqs;
}
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 = arch_probe_nr_irqs();
printk(KERN_INFO "NR_IRQS: %d, nr_irqs: %d, preallocated irqs: %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) //这里两个都是16
nr_irqs = initcnt;
//这里默认动态申请16个,可以看到这里并没有初始化,而且把allocated_irqs的前16bit给置位了,即表示后面也不能别人用
for (i = 0; i < initcnt; i++) {
desc = alloc_desc(i, node, 0, NULL, NULL);
set_bit(i, allocated_irqs);
irq_insert_desc(i, desc); //并把这16个一次从0开始插入到中断描述符的根基数树中
}
return arch_early_irq_init();
}
static 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);
}
动态获取一个desc
static struct irq_desc *alloc_desc(int irq, int node, unsigned int flags,
const struct cpumask *affinity,
struct module *owner)
{
struct irq_desc *desc;
//动态获取一个中断描述符
desc = kzalloc_node(sizeof(*desc), GFP_KERNEL, node);
if (!desc)
return NULL;
/* allocate based on nr_cpu_ids */
desc->kstat_irqs = alloc_percpu(unsigned int); //分配4字节percpu,区域(percpu是内存管理的一种方式)
if (!desc->kstat_irqs) //4字节可以分配,表示前面的分配没越界desc
goto err_desc;
if (alloc_masks(desc, node)) //smp,中确定通知的其它cpu本cpu在获取desc资源,单核是个空函数
goto err_kstat;
//初始化desc
raw_spin_lock_init(&desc->lock);
lockdep_set_class(&desc->lock, &irq_desc_lock_class); //
mutex_init(&desc->request_mutex);
init_rcu_head(&desc->rcu);
//给desc初始化通用的参数
desc_set_defaults(irq, desc, node, affinity, owner);
irqd_set(&desc->irq_data, flags);
kobject_init(&desc->kobj, &irq_kobj_type);
return desc; //返回基本初始化了的一本desc
err_kstat:
free_percpu(desc->kstat_irqs);
err_desc:
kfree(desc);
return NULL;
}
接下来从开始提出的问题开始分析
int irq_domain_alloc_descs(int virq, unsigned int cnt, irq_hw_number_t hwirq,
int node, const struct cpumask *affinity)
{
unsigned int hint;
if (virq >= 0) {
virq = __irq_alloc_descs(virq, virq, cnt, node, THIS_MODULE,
affinity);
} else {
hint = hwirq % nr_irqs; //第一次进来nr_irqs是16,hwirq,对于vic 可能是0~31中的任何值
if (hint == 0)
hint++; //所以hint可能是1~16中间的任何值
virq = __irq_alloc_descs(-1, hint, cnt, node, THIS_MODULE,
affinity);
if (virq <= 0 && hint > 1) {
virq = __irq_alloc_descs(-1, 1, cnt, node, THIS_MODULE,
affinity);
}
}
return virq;
}
上面这个函数和之前一样,不同的是nr_irqs目前是默认的16
下面这个函数也是一样的和前面,不同的是里面的一些参数已经不一样了
/**
* irq_alloc_descs - allocate and initialize a range of irq descriptors
* @irq: Allocate for specific irq number if irq >= 0,对于irq大于等于0,是可以分配特定的中断号
* @from: Start the search from this irq number,从这个中断号开始找
* @cnt: Number of consecutive irqs to allocate.
* @node: Preferred node on which the irq descriptor should be allocated
* @owner: Owning module (can be NULL)
* @affinity: Optional pointer to an affinity mask array of size @cnt which
* hints where the irq descriptors should be allocated and which
* default affinities to use
*
* Returns the first irq number or error code
*/
int __ref
__irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
struct module *owner, const struct cpumask *affinity)
{
int start, ret;
if (!cnt) //确认申请的中断号大于等于1个
return -EINVAL;
if (irq >= 0) { //irq>= 0,表示是我们指定中断号就是irq了
if (from > irq) //指定查找的必须要在开始查找的,后面
return -EINVAL;
from = irq; //范围正确,则从指定中断号开始查,这样效率跟快
} else {
/*
* For interrupts which are freely allocated the
* architecture can force a lower bound to the @from
* argument. x86 uses this to exclude the GSI space.
*/
from = arch_dynirq_lower_bound(from); //别的架构定义的弱函数
}
mutex_lock(&sparse_irq_lock);
//从中断表中查找从from开始,没使用(相应位用0表示)的连续的cnt个空缺位置,
//返回找到的那个空缺的起始位置,一般我们不指定form,都是-1
start = bitmap_find_next_zero_area(allocated_irqs, IRQ_BITMAP_BITS,
from, cnt, 0);
ret = -EEXIST;
if (irq >=0 && start != irq)
goto unlock; //指定中断,查找到的不等于想要的中断,返回错误
if (start + cnt > nr_irqs) { //确定要用的最大的中断没有超出当前支持的中断数量
ret = irq_expand_nr_irqs(start + cnt); //这里表示想扩充中断数量,因为定义了CONFIG_SPARSE_IRQ,所以是有可能要扩充的
if (ret)
goto unlock;
}
ret = alloc_descs(start, cnt, node, affinity, owner); //确保可以获取了,这里直接产品能够desc数组获取从start开始地址的cnt个中断描述符
unlock:
mutex_unlock(&sparse_irq_lock);
return ret;
}
unsigned int __weak arch_dynirq_lower_bound(unsigned int from)
{
return from;
}
static int irq_expand_nr_irqs(unsigned int nr)
{
if (nr > IRQ_BITMAP_BITS) //先保证这个获取的第一个没用的最大数不能超过 16 + 8192
return -ENOMEM;
nr_irqs = nr; //更新一下目前(加上即将)申请的中断数量(即desc数量)
return 0;
}
#ifdef CONFIG_SPARSE_IRQ
# define IRQ_BITMAP_BITS (NR_IRQS + 8196) //目前获取查找的范围很大
#else
# define IRQ_BITMAP_BITS NR_IRQS
#endif
static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS);
static int alloc_descs(unsigned int start, unsigned int cnt, int node,
const struct cpumask *affinity, struct module *owner)
{
const struct cpumask *mask = NULL;
struct irq_desc *desc;
unsigned int flags;
int i;
/* Validate affinity mask(s) */
if (affinity) { //从最前面分析过来,亲和力是NULL的,这里也不是本次重点
for (i = 0, mask = affinity; i < cnt; i++, mask++) {
if (cpumask_empty(mask))
return -EINVAL;
}
}
flags = affinity ? IRQD_AFFINITY_MANAGED | IRQD_MANAGED_SHUTDOWN : 0;
mask = NULL;
for (i = 0; i < cnt; i++) {
if (affinity) {
node = cpu_to_node(cpumask_first(affinity));
mask = affinity;
affinity++;
}
desc = alloc_desc(start + i, node, flags, mask, owner); //动态获取一个中断描述符,前面分析了
if (!desc)
goto err;
irq_insert_desc(start + i, desc); //把这个中断描述符通过,中断号插入到基数树中
irq_sysfs_add(start + i, desc); //加入sysfs中,可以查看
irq_add_debugfs_entry(start + i, desc);
}
bitmap_set(allocated_irqs, start, cnt); //本次申请的置位,表示已经用了
return start;
err:
for (i--; i >= 0; i--)
free_desc(start + i);
return -ENOMEM;
}
所有已经分析完了,那么中断子系统中的中断号在那设置啊?
如下:
static struct irq_desc *alloc_desc(int irq, int node, unsigned int flags,
const struct cpumask *affinity,
struct module *owner)
{
struct irq_desc *desc;
desc = kzalloc_node(sizeof(*desc), GFP_KERNEL, 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, node))
goto err_kstat;
raw_spin_lock_init(&desc->lock);
lockdep_set_class(&desc->lock, &irq_desc_lock_class);
mutex_init(&desc->request_mutex);
init_rcu_head(&desc->rcu);
desc_set_defaults(irq, desc, node, affinity, owner); //设置中断号
irqd_set(&desc->irq_data, flags);
kobject_init(&desc->kobj, &irq_kobj_type);
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,
const struct cpumask *affinity, 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);
irqd_set(&desc->irq_data, IRQD_IRQ_MASKED);
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, affinity);
}
可以看到,和上一节说的是一样的。
可以看到确实是把虚拟的中断号绑定到了,domain上的线性映射表的。