Linux Irq domain
Posted Loopers
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux Irq domain相关的知识,希望对你有一定的参考价值。
本节学习下什么是irq domain, 以及irq domain的作用。可以参考内核文档IRQ-domain.txt
为什么引入IRQ-Domain
当早期的系统只存在一个interrupt-controller的时候,而且中断数目也不多的时候,一个很简单的做法就是一个中断号对应到interrupt-contoller的一个号,可以说是简单的线性映射
而当一个系统中有多个interrupt-controller的时候,而且中断号也逐渐增加。linux内核为了应对此问题,引入了IRQ-domain的概念
irq-domain的引入相当于一个中断控制器就是一个irq-domain。就是一个中断区域。这样一来所有的irq-contoller会出现级联的布局。
利用树状的结构可以充分的利用irq数目,而且每一个irq-domain区域可以自己去管理自己interrupt的特性
IRQ-Domain的作用
咋们通过/proc/interrupt的值来看下irq-domain的作用
从这图上可以看出,这些中断只直接连接到root-interrupt-controller的,也就是直接连接到GICv3的中断控制器上的。而Hwirq-num就是dts中配置的irq号
而第一列就是对应的softirq-num,也就是request_irq时传入的irq
中断控制器级联的情况图
hwirq到softirq的映射
当开机之后,内核会自动将dts全部解析,然后会进行填充,对于中断使用如下函数进行解析dts,然后map
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
struct of_phandle_args oirq;
if (of_irq_parse_one(dev, index, &oirq))
return 0;
return irq_create_of_mapping(&oirq);
of_irq_parse_one会将dts中的中断信息进行解析,然后通过irq_create_of_mapping函数进行hwirq到softirq的map
- 首先dts中配置的中断号都是hwirq
- 刚开机hwirq没有对应的softirq的,所以第一次开机需要进行hwirq和softirq之间建立map
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;
if (hint == 0)
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;
通过上述的函数分配virq,也就是softirq
static struct irq_data *irq_domain_insert_irq_data(struct irq_domain *domain,
struct irq_data *child)
struct irq_data *irq_data;
irq_data = kzalloc_node(sizeof(*irq_data), GFP_KERNEL,
irq_data_get_node(child));
if (irq_data)
child->parent_data = irq_data;
irq_data->irq = child->irq;
irq_data->common = child->common;
irq_data->domain = domain;
return irq_data;
分配一个Irq_data结构,然后将virq设置到irq_data结构中
static void irq_domain_set_mapping(struct irq_domain *domain,
irq_hw_number_t hwirq,
struct irq_data *irq_data)
if (hwirq < domain->revmap_size)
domain->linear_revmap[hwirq] = irq_data->irq;
else
mutex_lock(&domain->revmap_tree_mutex);
radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
mutex_unlock(&domain->revmap_tree_mutex);
将hwirq和irq_data→irq建立映射。这里有两种映射方式一种是线性映射,一种是树形映射
irq和irq_desc的关系
在分配一个softirq的时候,其实最终也会分配一个irq_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);
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;
开机已经分配好了的,用于irq比较少
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),
;
通过irq_to_desc函数来获取desc通过irq的值,两种方式
struct irq_desc *irq_to_desc(unsigned int irq)
return radix_tree_lookup(&irq_desc_tree, irq);
struct irq_desc *irq_to_desc(unsigned int irq)
return (irq < NR_IRQS) ? irq_desc + irq : NULL;
申请中断
通过request_irq函数来设置中断的回调函数,最终会设置到Irqaction中去
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
struct irqaction *action;
struct irq_desc *desc;
action->handler = handler; //设置中断回调函数
action->thread_fn = thread_fn; //如果中断是线程化,则需要设置此回调
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
处理中断
irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags)
irqreturn_t retval = IRQ_NONE;
unsigned int irq = desc->irq_data.irq;
struct irqaction *action;
record_irq_time(desc);
for_each_action_of_desc(desc, action)
irqreturn_t res;
trace_irq_handler_entry(irq, action);
res = action->handler(irq, action->dev_id); //处理中断
trace_irq_handler_exit(irq, action, res);
if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts\\n",
irq, action->handler))
local_irq_disable();
switch (res)
case IRQ_WAKE_THREAD:
/*
* Catch drivers which return WAKE_THREAD but
* did not set up a thread function
*/
if (unlikely(!action->thread_fn))
warn_no_thread(irq, action);
break;
__irq_wake_thread(desc, action);
/* Fall through to add to randomness */
case IRQ_HANDLED:
*flags |= action->flags;
break;
default:
break;
retval |= res;
return retval;
- 处理中断,action->handler(irq, action->dev_id);
- 如果是中断线程的话,唤醒线程 __irq_wake_thread(desc, action);
以上是关于Linux Irq domain的主要内容,如果未能解决你的问题,请参考以下文章