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

时间:2013-06-21 14:23:30

标签: linux-kernel arm linux-device-driver interrupt

我在使用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 / interrupts输出添加到问题...

3 个答案:

答案 0 :(得分:3)

您在这里遇到的是中断抖动。这在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/)中获取更多详细信息,在那里您可以找到有关它们如何协同工作的工作代码,示例和说明。

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

试试这个标志:

int irq_flags;
...
irq_flags = IRQF_TRIGGER_RISING | IRQF_EARLY_RESUME

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