在Linux Kernel v3.4.113中处理10MHz GPIO中断的性能问题求助
全志H3 GPIO中断处理性能瓶颈解决方案
问题背景
硬件:搭载1.2GHz时钟的全志H3开发板
系统:Linux Kernel v3.4.113
需求:接收时钟频率为10MHz的并行数据
问题:内核GPIO中断处理速度极慢,每秒仅能处理500次左右中断(测试代码预期counter值为10,000,000,实际仅1200、700、1000、900左右)
测试代码如下:
#include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/timer.h> #include <linux/delay.h> #define GPIO_IN 6 unsigned long counter=0; static irqreturn_t my_handler(int irq, void *dev_id) { counter++; return IRQ_HANDLED; } static int __init exemple_init (void) { int err, i = 0; printk(KERN_INFO "Loading GPIO module\n"); if ((err = gpio_request(GPIO_IN, THIS_MODULE->name)) !=0) { printk(KERN_INFO "GPIO Request Error\n"); return err; } if ((err = gpio_direction_input(GPIO_IN)) !=0) { printk(KERN_INFO "GPIO Direction Error\n"); return err; } if ((err = request_irq(gpio_to_irq(GPIO_IN), my_handler, IRQF_TRIGGER_RISING, THIS_MODULE->name, THIS_MODULE->name)) != 0) { printk(KERN_INFO "GPIO IRQ Error\n"); gpio_free(GPIO_IN); return err; } printk(KERN_INFO "Waiting for interrupts ... \n"); for (i = 0; i < 10; i++) { counter = 0; mdelay(1000); printk(KERN_INFO "Frequency: %lu\n", counter); } printk(KERN_INFO "Done \n"); return 0; } static void __exit exemple_exit (void) { free_irq(gpio_to_irq(GPIO_IN), THIS_MODULE->name); gpio_free(GPIO_IN); } module_init(exemple_init); module_exit(exemple_exit); MODULE_AUTHOR("Pierre LE COZ"); MODULE_DESCRIPTION("GPIO interrupt"); MODULE_LICENSE("GPL");
解决方案
一、中断上下文与代码优化
- 启用快速中断模式:在
request_irq时添加IRQF_DISABLED标记,关闭本地CPU其他中断以减少调度开销,修改后的flags参数为:IRQF_TRIGGER_RISING | IRQF_DISABLED - 绑定中断到单核心:避免跨核心调度开销,在
request_irq成功后添加核心绑定代码:cpumask_t mask; cpumask_clear(&mask); cpumask_set_cpu(0, &mask); irq_set_affinity(gpio_to_irq(GPIO_IN), &mask); - 替换忙等待为定时器统计:原代码用
mdelay(1000)忙等待会占用CPU,改用定时器在进程上下文统计:static struct timer_list stat_timer; static int stat_count = 0; static void stat_timer_func(unsigned long data) { printk(KERN_INFO "Frequency: %lu\n", counter); counter = 0; stat_count++; if (stat_count < 10) { mod_timer(&stat_timer, jiffies + msecs_to_jiffies(1000)); } else { printk(KERN_INFO "Done \n"); } } // 在exemple_init的request_irq成功后添加: setup_timer(&stat_timer, stat_timer_func, 0); mod_timer(&stat_timer, jiffies + msecs_to_jiffies(1000)); - 使用原子操作计数:解决多核环境下的计数竞争问题,将
counter替换为原子变量:atomic_t counter = ATOMIC_INIT(0); static irqreturn_t my_handler(int irq, void *dev_id) { atomic_inc(&counter); return IRQ_HANDLED; } // 统计时: unsigned long cnt = atomic_read(&counter); printk(KERN_INFO "Frequency: %lu\n", cnt); atomic_set(&counter, 0);
二、全志H3硬件层面优化
- 提升GPIO控制器时钟:全志H3的GPIO挂载在APB总线,默认时钟可能被降频,直接操作CCU寄存器将GPIO时钟调至最高(如100MHz),具体寄存器地址参考H3 datasheet,修改对应分频系数为1。
- 关闭GPIO硬件防抖:部分GPIO默认开启防抖功能会过滤高频信号,通过GPIO控制器寄存器关闭防抖阈值配置。
- 改用硬件DMA或专用外设:10MHz频率已远超Linux中断处理极限(中断上下文开销至少1μs,而10MHz对应100ns周期),最优方案是放弃GPIO中断,使用H3的并行总线(如FMC)、SPI等带DMA功能的外设接收数据,通过硬件批量传输规避中断开销。
三、内核配置优化
- 关闭抢占与调试选项:重新编译内核,关闭
CONFIG_PREEMPT(抢占式内核增加中断调度开销)、CONFIG_DEBUG_KERNEL相关调试选项,开启CONFIG_HIGH_RES_TIMERS提升定时器精度。 - 设置中断最高优先级:通过
irq_set_priority将GPIO中断优先级设为最高:irq_set_priority(gpio_to_irq(GPIO_IN), IRQ_PRIO_HIGHEST);
内容的提问来源于stack exchange,提问作者pirozy
相关产品推荐
相关产品推荐

