设备树学习(二十、番外篇-中断子系统之中断号的映射与反映射[二])

上一篇我们留了一个坑,说是通过下面这个函数获取一个中断描述符以及对应的虚拟中断号。

本节就分析一下怎样获取。

设备树学习(二十、番外篇-中断子系统之中断号的映射与反映射[二])

 

 

在这个开始之前我们先要说的是,因为中断目前分为两种分配方式,一种是通过一次性定义一个全局数组,每个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上的线性映射表的。