簡體   English   中英

如何延遲Linux中斷處理程序(我知道通常不會睡覺)

[英]How can I delay in a Linux interrupt handler (I know sleeping usually not possible)

我正在開發一個嵌入式Linux ARM系統,它需要通過按特定順序關閉一些電源(通過GPIO控制)來對電源故障信號作出反應。 這個過程需要盡快啟動,所以我安裝了一個中斷處理程序來檢測這個電源故障。

問題是我們需要在關閉每個電源之間引入一點延遲。 我知道在中斷處理程序中通常不允許延遲,但是如果這個處理程序永遠不會返回(電源失敗!)就完全可以了。

我試圖通過使用所描述的方法來引入延遲這篇文章 ,但我不能為我的生活其實導致可測量的延遲(在示波器上觀察到的)。

我做錯了什么,我該怎么做?

以下是相關代碼。

/* This function sets en_gpio low, then waits until pg_gpio goes low. */
static inline void powerdown(int en_gpio, int pg_gpio)
{
    /* Bring the enable line low. */
    gpio_set_value(en_gpio, 0);
    /* Loop until power good goes low. */
    while (gpio_get_value(pg_gpio) != 0);
}

/* This is my attempt at a delay function. */
#define DELAY_COUNT 1000000000
static void delay(void)
{
    volatile u_int32_t random;
    volatile u_int32_t accum;
    volatile u_int32_t i;

    get_random_bytes((void*)&random, 4);
    accum = 0;
    for (i = 0; i < DELAY_COUNT; i++)
        accum = accum * random;
}

/* This is the interrupt handler. */
static irqreturn_t power_fail_interrupt(int irq, void *dev_id)
{
    powerdown(VCC0V75_EN, VCC0V75_PG);
    delay();
    powerdown(DVDD15_EN, DVDD15_PG);
    delay();
    powerdown(DVDD18_EN, DVDD18_PG);
    delay();
    powerdown(CVDD1_EN, CVDD1_PG);
    delay();
    powerdown(CVDD_EN, CVDD_PG);
    /* It doesn't matter if we get past this point. Power is failing. */
    /* I'm amazed this printk() sometimes gets the message out before power drops! */
    printk(KERN_ALERT "egon_power_fail driver: Power failure detected!\n");
    return IRQ_HANDLED;
}

在硬IRQ處理程序中使用delay函數通常是個壞主意,因為在硬IRQ處理程序中禁用了中斷,系統將掛起,直到您的硬IRQ函數完成。 另一方面,您不能在硬IRQ處理程序中使用sleep函數,因為硬IRQ是原子上下文

將所有這些都納入帳戶,您可能想要使用線程IRQ 這種方式硬IRQ處理程序只喚醒下半部分 IRQ處理程序(在內核線程中執行)。 在此線程處理程序中,您可以使用常規sleep函數。

要實現線程IRQ而不是常規IRQ,只需用request_irq()函數替換request_threaded_irq()函數。 例如,如果你有這樣的IRQ請求:

ret = request_irq(irq, your_irq_handler, IRQF_SHARED,
                  dev_name(&dev->dev), chip);

你可以用這樣的東西替換它:

ret = request_threaded_irq(irq, NULL, your_irq_handler,
                           IRQF_ONESHOT | IRQF_SHARED,
                           dev_name(&dev->dev), chip);

這里NULL意味着將使用標准的硬IRQ處理程序(它只喚醒線程IRQ處理程序),而your_irq_handler()函數將在內核線程中執行(您可以在其中調用sleep函數)。 請求線程IRQ時也應使用IRQF_ONESHOT標志。

還應該提到的是request_threaded_irq()函數的托管版本,名為devm_request_threaded_irq() 使用它(而不是常規的request_threaded_irq() )允許您在驅動程序退出函數(以及錯誤路徑)中省略free_irq()函數。 我建議你使用devm_*函數(如果你的內核版本已經有了它)。 但是如果您決定使用devm_*請不要忘記刪除驅動程序中的所有free_irq()調用。

TL; DR

request_irq()替換為request_threaded_irq() (如上所示),您將能夠在IRQ處理程序中使用sleep

我將其重新構建為兩部分:

  1. 中斷處理程序
  2. 等待中斷處理程序的應用程序,然后執行定時邏輯。

正如您所經歷的那樣,在IRQ處理程序中睡眠是不好的 因此,任何重要的忙碌等待都會影響系統其他部分的響應能力。

交互的具體機制可以是幾種手段中的任何一種。

如果使用Linux設備驅動程序,它可以接受read()操作並在發生中斷時返回一些內容(例如等待時間,甚至是零字節)。 因此,應用程序將打開設備,執行阻塞讀取()以及何時成功返回(無錯誤)在用戶模式下執行所有邏輯(可能)正常優先級。

事實證明我的問題的根本原因是錯誤配置的引腳(中斷信號打開的那個),我的中斷不是事件發生的......我正在看着自己不受控制的鐵軌。 當我正在研究系統的另一部分時,我猜我搞砸了這個...

我最終使用以下函數來實現硬中斷的延遲。 它並不性感,但確實有效,而且很簡單,我相信轉換操作可以避免溢出,如@specializt的評論所指出的那樣。

這段代碼非常適用於單件設備,我今天所做的測試表明它非常穩定。

/* This is my attempt at a delay function. */
/* A count of 8 is approximately 100 microseconds */
static void delay(int delay_count)
{
    volatile u_int32_t random;
    volatile u_int64_t accum;
    volatile u_int32_t i;

    accum = 0;
    for (i = 0; i < delay_count; i++) 
    {
    get_random_bytes((void*)&random, 4);
        accum = accum * random;
    accum = accum >> 32;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM