ARM-Linux中断系统【转】

转自:https://www.cnblogs.com/arnoldlu/p/7406441.html

1.前言

了解Linux中断子系统,同时也需要了解ARM体系结构中断处理流程;在熟悉整个软硬件架构和流程基础上,才能对流程进行细化,然后找出问题的瓶颈。《2. 梳理中断处理子系统

但是所有的优化都离不开一个量化的过程,有个可靠、高效、可读性强的度量必不可少。《3. 一种测量中断性能手段

最后基于此,进行中断性能的优化。《4.中断性能优化

2. 梳理中断处理子系统

中断系统涉及到软硬件两部分,具体到ARM系统和Linux涉及到很多相关点。

硬件以Cortex-A53为基础,整个GIC架构包括两部分:CPU内部的GIC CPU Interface(Cortex-A53 Chapter 9)和CPU外部的GIC external distributor component。

《ARM Cortex-A53 MPCore Processor Technical Reference Manual》简单介绍了A53核内部的GIC CPU Interface。

《ARM Generic Interrupt Controller Architecture Specification v3/v4》详细介绍了整个GIC架构的方方面面,具体实现比如GIC-600在《GIC-600 Generic Interrupt ControllerTechnical Reference Manual》。

相关阅读记录在《阅读GIC-500 Technical Reference Manual笔记》。

软件方面可以参考蜗窝科技关于中断子系统的一系列文章《Linux中断子系统》,一共9篇文章,讲述了Linux中断的方方面面。

综述》是一个导论性质文档,从更高层次介绍了中断相关软硬件架构;

IRQ number和中断描述符》重点介绍了中断描述符相关数据结构以及API;

在一个中断出发之后,从CPU架构相模块进行现场保护《ARM中断处理过程》-->machine相关中断处理handler将HW Interrupt ID翻译成IRQ number《IRQ Domain介绍》-->IRQ number对应中断例程《High level irq event handler》,以及最终现场恢复流程《ARM中断处理过程》;

驱动申请中断API》是从中断使用者角度介绍如何使用中断;中断处理的下半部包括《softirq》和《tasklet》,以及workqueue 1  2  3  4

GIC代码分析》重点介绍了ARM架构下中断控制器的方方面面。

3. 一种测量中断性能手段

3.1 明确评估标的

评估一个系统的中断性能,首先要明确评估那一段处理的性能。这里评估的是从中断触发开始,到执行中断例程(ISR)这段处理。

这一段从外部设备触发中断,到中断控制器,再到CPU处理,直到ISR的调用执行,涉及到软硬件的方方面面。

3.2 如何对标的进行量化

从硬件触发开始到软件ISR执行时间度量,跨软硬件。测量起来难免会有误差,尤其是两者的时间轴问题不易同步。

好在Linux有周期性Timer,周期性Timer设置一个Load值,从Load开始倒计数,计数到达0的时候触发中断。

然后重新计数,并且可以随时读取当前计数值。

在ISR中读取计数,就可以知道从上次0计数触发中断到当前消耗的Cycle数目,进而得到标的耗时。

3.3 内核实现

内核中主要注册中断、提供修改Load接口、创建proc节点。

中断相关初始化,注册中断处理函数。在ISR中进行Timer Cycle的读取。

提供修改Load接口,供动态修改Load,以达到在不同频率下测试标的的目的。

选取不同频率的load:
echo n > /proc/interrupt_stats

读取Timer中断统计信息:
cat /proc/interrupt_stats > interrupt_xxx.txt

proc文件提供设置Load和读取标的结果的接口。

ARM-Linux中断系统【转】
//===================================================
#include <asm/uaccess.h>

extern unsigned int interrupt_statiscs[1024][2];
extern unsigned int interrupt_period_count;

extern void interrupt_set_period(unsigned int cycles);

static int interrupts_proc_show(struct seq_file *m, void *v)
{
    int i;

    for(i = 0; i < sizeof(interrupt_statiscs)/sizeof(interrupt_statiscs[0]); i++)
        seq_printf(m, "%u, %u, %u\n", interrupt_period_count, interrupt_statiscs[i][0], interrupt_statiscs[i][1]);

    return 0;
}

static ssize_t interrupts_proc_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
{
    unsigned int period_cycles;
    char buf[1];


    if (copy_from_user(buf, user_buf, 1))
        return -EFAULT;
    sscanf(buf, "%u", &period_cycles);
    printk("%s period_cycles %u\n", __func__, period_cycles);

    if (period_cycles > 0 && period_cycles < 5)
    {
        switch(period_cycles)
        {
            case 0:
                period_cycles = 2600000;
                break;
            case 1:
                period_cycles = 260000;
                break;
            case 2:
                period_cycles = 26000;
                break;
            case 3:
                period_cycles = 2600;
                break;
            case 4:
                period_cycles = 1300;
                break;
            default:
                period_cycles = 260000;
        }

        interrupt_set_period(period_cycles);
        printk("%s set interrupt period to %u\n", __func__, period_cycles);
    }
    return 1;
}

static int interrupts_proc_open(struct inode *inode, struct file *file)
{
    return single_open(file, interrupts_proc_show, NULL);
}

static const struct file_operations interrupt_stats_proc_fops = {
    .open        = interrupts_proc_open,
    .write        = interrupts_proc_write,
    .read        = seq_read,
    .llseek        = seq_lseek,
    .release    = single_release,
};

//===================================================

static int __init proc_uptime_init(void)
{
    proc_create("uptime", 0, NULL, &uptime_proc_fops);
    
    proc_create("interrupt_stats", 0, NULL, &interrupt_stats_proc_fops);
    return 0;
}
module_init(proc_uptime_init);
ARM-Linux中断系统【转】

 

3.4 分析结果

当前使用测试Timer是26MHz,辅助衡量Load准确行的是32768时钟计数。

26MHz的时钟,一个Cycle=38.46纳秒,这个精度已经很高了。用于衡量中断性能应该够用。

1. 将测试结果从设备中导出。第1列是当前Load数,第2列是标的耗时,第3列是辅助时钟计数。第1、2列是26MHz时钟,第3列是32K时钟。

将这些结果按照Load不同存储到interrupts_1300.txt/...文件中。

ARM-Linux中断系统【转】
2600000, 321, 3277
2600000, 334, 3277
2600000, 315, 3277
2600000, 321, 3277
2600000, 324, 3277
2600000, 335, 3276
2600000, 355, 3277
2600000, 341, 3277
2600000, 345, 3277
2600000, 346, 3277
...
ARM-Linux中断系统【转】

2. 编写分析脚本

ARM-Linux中断系统【转】
import pandas as pd
import numpy as np
import os
import re
import matplotlib.pyplot as plt
import matplotlib
matplotlib.style.use('ggplot')


cnames = ['index', 'count', 'duration']

output = []
output_cycles = []

timer_freq = 26000000
p = re.compile('^interrupts_([0-9]*).txt$')

filenames = os.listdir('.')
for file in filenames:
    if p.match(file):
        data = []
        interrupt_data = []
        interrupt_stats = []

        duration_data = []

        interrupt_stats = pd.read_csv(file, names = cnames)--------------------读取数据源
        
        #Show the plotting of interrupts time consumption
        ts = pd.Series(interrupt_stats['count'], index=range(len(interrupt_stats['count'])))
        ts.plot(title='%s'%file)
        fig = plt.gcf()
        fig.set_size_inches(25, 4)
        plt.ylabel('Cycles from trigger to ISR.')
        plt.show()
        
        #Convert to time consumption
        for i in interrupt_stats['count'].tolist():
            data.append(float(i)*1000000/timer_freq)------------------------转换成时间单位us

        #Calc the timer duration
        for i in interrupt_stats['duration'].tolist():
            duration_data.append(i)

        #Statistics of interrupts
        interrupt_data = np.array(data)
        output.append([interrupt_data.mean(), interrupt_data.max(), interrupt_data.min(), interrupt_data.std()])----每个case的统计信息
        output_cycles.append([interrupt_stats['count'].mean(),
                              interrupt_stats['count'].max(),
                              interrupt_stats['count'].min(),
                              interrupt_stats['count'].std()])------------------------------------------------------cycles形式的统计信息
        
df = pd.DataFrame(output, columns=['mean(us)','max(us)','min(us)', 'std(us)'], index=['1300', '2600', '26000', '260000', '2600000'])
df.to_csv('interrupt.csv')
pd.pivot_table(df, index='mean(us)')----------pivot table形式展示统计信息

  f2 = pd.DataFrame(output_cycles, columns=['mean(us)','max(us)','min(us)', 'std(us)'], index=['1300', '2600', '26000', '260000', '2600000'])
  pd.pivot_table(df2, index='mean(us)')--------pivot table形式展示统计信息

ARM-Linux中断系统【转】

3. 结果分析

3.1 耗时图表分析

从下面5张图中可以看出标的耗时分布情况,总体来讲数据比较稳定。

  • 26000/260000/2600000会有个别特别长的延时;
  • 所有case的最低值比较接近在90-120左右个Cycles;
  • 随着Load增加,平均耗时呈递增趋势;
  • 对26000/260000/2600000修改了ylim到500,可以看出细节部分。

ARM-Linux中断系统【转】

  ARM-Linux中断系统【转】   ARM-Linux中断系统【转】   ARM-Linux中断系统【转】   ARM-Linux中断系统【转】   ARM-Linux中断系统【转】   ARM-Linux中断系统【转】    3.2 耗时统计信息 下面是每个case的平均值、最大值、最小值、均方差的统计信息。 ARM-Linux中断系统【转】

ARM-Linux中断系统【转】

 

 

4.中断性能优化

中断性能优化可以分为两个阶段:中断公用部分和每个中断例程包括下半部。

4.1 中断共用部分

中断共用部分包括:架构相关代码、中断控制器驱动等。

  • 提高cache命中率?
  • 将相关处理代码放入cache中?
  • 中断和CPU绑定?
  • 级联对中断性能的影响?
  • ......

 

4.2 每中断例程及下半部

 

每中断例程及下半部:首先针对中断例程,尽量短小快速、不睡眠;对下半部,采取合适的方法softirq/tasklet/workqueue。

中断例程的优化,可以通过Tracepoint中中断相关trace进行统计。

/sys/kernel/debug/tracing/events/irq/irq_handler_entry
/sys/kernel/debug/tracing/events/irq/irq_handler_exit
/sys/kernel/debug/tracing/events/irq/softirq_entry
/sys/kernel/debug/tracing/events/irq/softirq_exit
/sys/kernel/debug/tracing/events/irq/softirq_raise

 下面是一个中断例程执行耗时统计信息。

平均值大说明例程需要优化,因为在中断例程执行期间是屏蔽中断的,屏蔽时间太长容易丢中断。

如果均方差大,说明中断例程内流程差异较大,可能存在隐患。

ARM-Linux中断系统【转】
+------------------------+-------+--------+-------+-------+--------+------------------+
|          name          |  mean |  max   |  min  | count |  sum   |       std        |
+------------------------+-------+--------+-------+-------+--------+------------------+
|      dwc_otg_pcd       | 0.457 | 32.196 |  0.0  |  104  | 47.516 |  3.26191450776   |
|        xxxxxxx         | 0.004 | 0.031  |  0.0  |  675  | 2.903  | 0.0106282606939  |
|  dwc_otg_powerdown_up  | 7.644 |  7.66  | 7.629 |   2   | 15.289 |      0.0155      |
|         icp_ps         | 0.006 | 0.031  |  0.0  |   5   | 0.031  |      0.0124      |
|       xxxx_i2c.0       | 0.010 | 0.031  |  0.0  |   48  | 0.459  | 0.0141861232812  |
|       xxxx_timer       | 0.003 | 0.092  |  0.0  |  1378 | 4.305  | 0.00947335198132 |
| dwc_otg_powerdown down | 5.264 |  6.5   | 4.028 |   2   | 10.528 |      1.236       |
+------------------------+-------+--------+-------+-------+--------+------------------+
ARM-Linux中断系统【转】

 下面是这些中断在时间轴上的分不情况,长度表示耗时。可以看出他们的频率,以及相互之间的关系。

 ARM-Linux中断系统【转】

 

 

联系方式:arnoldlu@qq.com
上一篇:1225. 报告系统状态的连续日期


下一篇:Period :KMP