首页 文章

使用RT补丁的Linux内核的中断性能 - 应该更好吗?

提问于
浏览
2

我在使用CONFIG_PREEMPT_RT补丁运行Linux内核(3.8.13)的Freescales imx.233上遇到了一点不一致的IRQ / ISR性能 . 我有点惊讶为什么这个处理器(ARM9,454mhz)即使有74kHz的IRQ请求也无法跟上..?

在我的内核配置中,我设置了以下标志:

CONFIG_TINY_PREEMPT_RCU=y
CONFIG_PREEMPT_RCU=y
CONFIG_PREEMPT=y
CONFIG_PREEMPT_RT_BASE=y
CONFIG_HAVE_PREEMPT_LAZY=y
CONFIG_PREEMPT_LAZY=y
CONFIG_PREEMPT_RT_FULL=y
CONFIG_PREEMPT_COUNT=y
CONFIG_DEBUG_PREEMPT=y

在系统上基本没有运行(由buildroot创建),我设置PWM以产生74kHz的脉冲,用作中断 . 然后在ISR中,我只触发另一个GPIO输出引脚,并检查输出 . 我发现有时候我会错过一个中断 - 你可以在这里看到错过的中断:

missed interrupt

并且输出引脚的触发似乎有点不一致,输出引脚通常在“5%窗口”内触发,这可能仍然是可接受的 . 但我担心,当我开始实现数据传输逻辑时,我可能会遇到更多问题,而不仅仅是触发引脚 .

我的简单驱动程序代码如下所示:

#needed includes

uint16_t INPUT_IRQ = 39;
uint16_t OUTPUT_GPIO = 38;

struct test_device *device;

//Prototypes
void irqtest_exit(void);
int irqtest_init(void);
void free_device(void);

//Default functions
module_init(irqtest_init);
module_exit(irqtest_exit);

//triggering flag
uint16_t pulse = 0x1;

irqreturn_t irq_handle_function(int irq, void *device_id)
{
pulse = !pulse;
gpio_set_value(OUTPUT_GPIO, pulse);

return IRQ_HANDLED;
}

struct test_device { 
    int huuhaa;
};

void free_device() {
if (device)
    kfree(device);
}   

int irqtest_init(void) {
    int result = 0;

    device = kmalloc(sizeof *device, GFP_KERNEL);
    device->huuhaa = 10;

    printk("IRB/irqtest_init: Inserting IRQ module\n"); 

    printk("IRB/irqtest_init: Requesting GPIO (%d)\n", INPUT_IRQ); 
    result = gpio_request_one(INPUT_IRQ, GPIOF_IN, "PWM input");

    if (result != 0) {
        free_device();
        printk("IRB/irqtest_init: Failed to set GPIO (%d) as input.. exiting\n", INPUT_IRQ);
        return -EINVAL;
    } 
    result = gpio_request_one(OUTPUT_GPIO, GPIOF_OUT_INIT_LOW , "IR OUTPUT");
    if (result != 0) {
        free_device();
        printk("IRB/irqtest_init: Failed to set GPIO (%d) as output.. exiting\n", OUTPUT_GPIO);
        return -EINVAL;
    } 

    //Set our desired interrupt line as input
    result = gpio_direction_input(INPUT_IRQ);

    if (result != 0) {
        printk("IRB/irqtest_init: Failed to set IRQ as input.. exiting\n");
        free_device();
        return -EINVAL;
    }   

    //Set flags for our interrupt, guessing here..
    irq_flags |=  IRQF_NO_THREAD;
    irq_flags |=  IRQF_NOBALANCING;
    irq_flags |=  IRQF_TRIGGER_RISING;
    irq_flags |=  IRQF_NO_SOFTIRQ_CALL;

    //register interrupt
    result = request_irq(gpio_to_irq(INPUT_IRQ), irq_handle_function, irq_flags, "irq testing", device);

    if (result != 0) {
        printk("IRB/irqtest_init: Failed to reserve GPIO 38\n");
        return -EINVAL;
    } 
    printk("IRB/irqtest_init: insert success\n"); 
    return 0;
}

void irqtest_exit(void) {
    if (device)
        kfree(device);
    gpio_free(INPUT_IRQ);
    gpio_free(OUTPUT_GPIO);

    printk("IRB/irqtest_exit: Removing irqtest module\n");
}


int irqtest_open(struct inode *inode, struct file *filp) {return 0;}
int irqtest_release(struct inode *inode, struct file *filp) {return 0;}

在系统中,我在加载驱动程序后注册了以下中断:

# cat /proc/interrupts 
       CPU0       
 16:      36379         -  MXS Timer Tick
 17:          0         -  mxs-spi
 18:       2103         -  mxs-dma
 60:          0  gpio-mxs  irq testing
118:          0         -  mxs-spi
119:          0         -  mxs-dma
120:          0         -  RTC alarm
124:          0         -  8006c000.serial
127:      68050         -  uart-pl011
128:        151         -  ci13xxx_imx
Err:          0

我想知道我向IRQ申报的旗帜是否合适?我注意到,通过这种配置,我再也无法访问控制台了,所以内核现在似乎完全消耗了这个74kHz的触发器 . 这不可能是正确的吗?我想这对我来说不是什么大问题,因为这只是在数据传输期间,但我仍然觉得我做错了什么..

另外,我想知道用ioremap映射寄存器是否更有效,并通过直接内存写入触发输出?

有没有什么方法可以将中断的优先级提高到更高?或者我可以在数据传输期间以某种方式锁定内核(~400ms),并以某种方式生成我的输出时序?

编辑:忘记添加/ proc / interruptts输出到问题...

3 回答

  • 1

    您在这里遇到的是中断抖动 . 这在Linux上是可以预期的,因为内核会定期禁用各种任务的中断(进入自旋锁,处理中断等) .

    这种情况会发生,无论你是否有PREEMPT_RT,所以期望通过常规中断产生74kHz信号是非常不现实的 .

    现在,ARM具有更高优先级的中断,称为FIQ,永远不会被屏蔽或禁用 .

    Linux不使用FIQ,并不是为了处理可以使用FIQ的事实而构建的,因此您将无法使用通用内核框架 .

    但是,从Linux驱动程序开发的角度来看,只要记住这一点,它就没有什么不同:你必须编写一个处理程序,并将它与IRQ相关联 . 您还必须插入中断控制器,使其为您要使用的中断生成FIQ(有关如何更改它的详细信息取决于平台 . 某些平台具有执行此操作的功能(如imx25和mxc_set_irq_fiq) ,有些人没有.imx23 / 28没有,所以你必须手工完成) .

    设置fiq处理程序的函数唯一可以使用汇编编写的处理程序,因此您必须在汇编中重写处理程序(使用当前代码,但它应该是微不足道的) .

    您可以在Alexandre发布的博客文章(http://free-electrons.com/blog/fiq-handlers-in-the-arm-linux-kernel/)中获取更多详细信息,在那里您可以找到有关它们如何一起工作的工作代码,示例和说明 .

  • 0

    你可以看看我的同事Maxime Ripard在类似的SoC(i.mx28)上使用FIQ做了什么:

    http://free-electrons.com/blog/fiq-handlers-in-the-arm-linux-kernel/

  • 3

    试试这个标志:

    int irq_flags;
    ...
    irq_flags = IRQF_TRIGGER_RISING | IRQF_EARLY_RESUME
    

    我有一个内核3.8.11,找不到IRQF_NO_SOFTIRQ_CALL定义 . 它仅适用于3.8.13?我也没有看到irq_flags定义 . 它在哪里?

相关问题