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
- linux中断子系统 - irq_desc的创建
- Linux内核的中断子系统
- linux中断子系统 - 中断控制器的注册
- Linux kernel的中断子系统之综述
- linux kernel的中断子系统之:softirq
- linux kernel的中断子系统之:tasklet
- Linux的IRQ中断子系统分析
- Linux中断子系统-中断处理
- Linux中断子系统-中断初始化
- Linux中断子系统-中断接口
- linux中断子系统 - 申请中断
- linux中断子系统
- Linux中断子系统
- linux中断子系统
- Linux中断子系统
- Linux 通用中断子系统
- linux下中断子系统
- Linux中断子系统 - softirq
- RxJava学习之基本使用
- 汇编语言之寄存器(CPU工作原理)
- centos 7 epel源 安装jq
- Python模拟post提交表单数据 ——某二手车网站回拨电话的分析与利用
- BSOJ2909 Vijos 1474 南邮OJ1573 雷曼兔
- linux中断子系统 - irq_desc的创建
- C++学习(五)——string使用注意事项(一)
- 数据结构实验之二叉树的建立与遍历
- jzoj 4826. 【NOIP2016提高A组集训第2场10.30】小澳的葫芦 01分数规划+最短路
- mysql登陆数据库的时候报错:mysql: Character set 'utf8' is not a compiled character set and is not specified
- AspectJ 简单的权限管理
- openjudge2971 抓住那头牛
- 设计模式之简单工厂、工厂方法和抽象工厂
- CODEVS 1219骑士游历