linux中断子系统 - irq_desc的创建

来源:互联网 发布:js 变量未定义 编辑:程序博客网 时间:2024/06/06 08:24

文章系列


linux中断子系统 - 中断及执行流程
linux中断子系统 - 申请中断
linux中断子系统 - irq_desc的创建
linux中断子系统 - 中断控制器的注册

irq_desc的代码主要在kernel/irq/irqdesc.c中

linux4.6.3

1. irq_desc组织方式


1.1 组织方式

irq_desc在内核中有两种组织方式,这是根据宏CONFIG_SPARSE_IRQ是否定义来决定的,这两种方式分别是:

(1)radix-tree方式,这是以基数树的方式来组织irq_desc
(2)数组的方式 ,前面的文章介绍irq结构时,就是用此方式举例的,在系统初始化的时候会定义一个全局数组,详细见下面代码,NR_IRQS就代表总共的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),    }};

我们最终的目标是建立hwirq和virq的映射,上面两种方式代表两种不同的映射方式,数组简单就是线性映射,而基数树具体在这里不说了。

irq_desc创建完后,要做的就是初始化,在内核中有两方面会涉及到irq_desc的初始化,一个是在申请中断的时候(主要是初始化irq_desc->action),另一个是在驱动初始化的时候,本文重点就在这里

1.2 struct irq_desc

struct irq_desc {    struct irq_common_data  irq_common_data;    struct irq_data     irq_data;    unsigned int __percpu   *kstat_irqs;    irq_flow_handler_t  handle_irq;#ifdef CONFIG_IRQ_PREFLOW_FASTEOI    irq_preflow_handler_t   preflow_handler;#endif    struct irqaction    *action;    /* IRQ action list */    unsigned int        status_use_accessors;    unsigned int        core_internal_state__do_not_mess_with_it;    unsigned int        depth;      /* nested irq disables */    unsigned int        wake_depth; /* nested wake enables */    unsigned int        irq_count;  /* For detecting broken IRQs */    unsigned long       last_unhandled; /* Aging timer for unhandled count */    unsigned int        irqs_unhandled;    atomic_t        threads_handled;    int         threads_handled_last;    raw_spinlock_t      lock;    struct cpumask      *percpu_enabled;#ifdef CONFIG_SMP    const struct cpumask    *affinity_hint;    struct irq_affinity_notify *affinity_notify;#ifdef CONFIG_GENERIC_PENDING_IRQ    cpumask_var_t       pending_mask;#endif#endif    unsigned long       threads_oneshot;    atomic_t        threads_active;    wait_queue_head_t       wait_for_threads;#ifdef CONFIG_PM_SLEEP    unsigned int        nr_actions;    unsigned int        no_suspend_depth;    unsigned int        cond_suspend_depth;    unsigned int        force_resume_depth;#endif#ifdef CONFIG_PROC_FS    struct proc_dir_entry   *dir;#endif    int         parent_irq;    struct module       *owner;    const char      *name;} ____cacheline_internodealigned_in_smp;

2.irq_desc的分配


#define irq_alloc_descs(irq, from, cnt, node)   \    __irq_alloc_descs(irq, from, cnt, node, THIS_MODULE)#define irq_alloc_desc(node)            \    irq_alloc_descs(-1, 0, 1, node)#define irq_alloc_desc_at(at, node)     \    irq_alloc_descs(at, at, 1, node)#define irq_alloc_desc_from(from, node)     \    irq_alloc_descs(-1, from, 1, node)#define irq_alloc_descs_from(from, cnt, node)   \    irq_alloc_descs(-1, from, cnt, node)

从上面可以看出最终都是调用到函数__irq_alloc_descs,此函数最终会调用到函数alloc_descs,从第一节我们知道irq_desc组织方式有两种,那么alloc_descs会有两个接口,下面分别介绍:

2.1线性数组方式

static inline int alloc_descs(unsigned int start, unsigned int cnt, int node,                  struct module *owner){    u32 i;    for (i = 0; i < cnt; i++) {        struct irq_desc *desc = irq_to_desc(start + i);        desc->owner = owner;    }    return start;}
struct irq_desc *irq_to_desc(unsigned int irq){    return (irq < NR_IRQS) ? irq_desc + irq : NULL;----直接返回irq_desc[irq]}

2.2基数树方式

static int alloc_descs(unsigned int start, unsigned int cnt, int node,               struct module *owner){    struct irq_desc *desc;    int i;    for (i = 0; i < cnt; i++) {        desc = alloc_desc(start + i, node, owner);------分配一个irq_desc        if (!desc)            goto err;        mutex_lock(&sparse_irq_lock);        irq_insert_desc(start + i, desc);---------------把irq_desc插入到radix tree        mutex_unlock(&sparse_irq_lock);    }    return start;err:    for (i--; i >= 0; i--)        free_desc(start + i);    mutex_lock(&sparse_irq_lock);    bitmap_clear(allocated_irqs, start, cnt);    mutex_unlock(&sparse_irq_lock);    return -ENOMEM;}
static struct irq_desc *alloc_desc(int irq, int node, struct module *owner){    struct irq_desc *desc;    gfp_t gfp = GFP_KERNEL;    desc = kzalloc_node(sizeof(*desc), gfp, node);--------为irq_desc分配内存    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, gfp, node))        goto err_kstat;    raw_spin_lock_init(&desc->lock);    lockdep_set_class(&desc->lock, &irq_desc_lock_class);    init_rcu_head(&desc->rcu);    desc_set_defaults(irq, desc, node, owner);    return desc;err_kstat:    free_percpu(desc->kstat_irqs);err_desc:    kfree(desc);    return NULL;}

3.irq_desc的初始化


各个驱动中断的配置在DT中(DT参考我的文章设备树的解释),DT初始化中断的一个简易流程如下图

这里写图片描述

走到of_irq_to_resource就脱离了DT过程,正式进入到本节的主题,函数调用流程图如下:

这里写图片描述

这些没什么说的,只是一个参数解析过程,重要的是下面这些函数

这里写图片描述

  • A : 找到匹配的irq_domain,irq_domain会在中断控制器注册的时候加入
  • B : 判断irq_domain是否是hierarchy,不同的类型需要不同的操作,从图中可以看出虽然调用函数不一样,但是具体要执行的步骤是一样的:
(1)搜索hwirq是否已经被映射到virq,如果是直接返回(2)如果没有得到virq,那么把hwirq映射到virq(3)调用irq_domain的相关函数初始化irq_desc

上面的简化分析不能说明问题,下面通过代码来仔细分析,只分析不是hierarchy的情况:

一、先分析函数irq_find_mapping

unsigned int irq_find_mapping(struct irq_domain *domain,                  irq_hw_number_t hwirq){    struct irq_data *data;    /* Look for default domain if nececssary */    if (domain == NULL)        domain = irq_default_domain;    if (domain == NULL)        return 0;    if (hwirq < domain->revmap_direct_max_irq) {        data = irq_domain_get_irq_data(domain, hwirq);        if (data && data->hwirq == hwirq)            return hwirq;    }    /* Check if the hwirq is in the linear revmap. */    if (hwirq < domain->revmap_size)        return domain->linear_revmap[hwirq];-----------------线性映射返回    rcu_read_lock();    data = radix_tree_lookup(&domain->revmap_tree, hwirq);    rcu_read_unlock();    return data ? data->irq : 0;-----------------------------基数树映射返回}--------------linear_revmap或者revmap_tree的建立是在中断控制器注册的时候初始化的

二、再分析函数irq_domain_alloc_descs

int irq_domain_alloc_descs(int virq, unsigned int cnt, irq_hw_number_t hwirq,               int node){    unsigned int hint;    if (virq >= 0) {--------------------从上面函数的分析可知道virq==0        virq = irq_alloc_descs(virq, virq, cnt, node);-----真正的irq_desc分配函数    } else {        hint = hwirq % nr_irqs;        if (hint == 0)            hint++;        virq = irq_alloc_descs_from(hint, cnt, node);        if (virq <= 0 && hint > 1)            virq = irq_alloc_descs_from(1, cnt, node);    }    return virq;}

三、最后分析函数irq_domain_associate

int irq_domain_associate(struct irq_domain *domain, unsigned int virq,             irq_hw_number_t hwirq){    struct irq_data *irq_data = irq_get_irq_data(virq);    int ret;    if (WARN(hwirq >= domain->hwirq_max,         "error: hwirq 0x%x is too large for %s\n", (int)hwirq, domain->name))        return -EINVAL;    if (WARN(!irq_data, "error: virq%i is not allocated", virq))        return -EINVAL;    if (WARN(irq_data->domain, "error: virq%i is already associated", virq))        return -EINVAL;    mutex_lock(&irq_domain_mutex);    irq_data->hwirq = hwirq;    irq_data->domain = domain;    if (domain->ops->map) {        ret = domain->ops->map(domain, virq, hwirq);---------------调用irq_domain->irq_domain_ops->map函数初始化irq_desc,具体操作在中断控制器注册文章中介绍        if (ret != 0) {            /*             * If map() returns -EPERM, this interrupt is protected             * by the firmware or some other service and shall not             * be mapped. Don't bother telling the user about it.             */            if (ret != -EPERM) {                pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",                       domain->name, hwirq, virq, ret);            }            irq_data->domain = NULL;            irq_data->hwirq = 0;            mutex_unlock(&irq_domain_mutex);            return ret;        }        /* If not already assigned, give the domain the chip's name */        if (!domain->name && irq_data->chip)            domain->name = irq_data->chip->name;    }    if (hwirq < domain->revmap_size) {        domain->linear_revmap[hwirq] = virq;-----------------------virq设置到domain    } else {        mutex_lock(&revmap_trees_mutex);        radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);--virq设置到domain        mutex_unlock(&revmap_trees_mutex);    }    mutex_unlock(&irq_domain_mutex);    irq_clear_status_flags(virq, IRQ_NOREQUEST);    return 0;}

4. change log

日期 修改内容 内核版本 2016.11.9 增加结构体irq_desc内容 linux4.4
0 0