2011-03-25 37 views
1

我正在開發在嵌入式CPU上運行的Linux設備驅動程序。該設備驅動程序控制一些外部硬件。外部硬件有它自己的DDR控制器和外部DDR。硬件的DDR通過可移動內存窗口在嵌入式CPU上可見(所以我可以從Linux驅動程序分頁訪問外部DDR)。我正在使用Linux內核版本2.6.33。當線程終止總線錯誤時,如何防止信號量鎖定

我的驅動程序使用sysfs來允許從用戶空間控制外部硬件。例如,外部硬件會生成一個心跳計數器,用於增加外部DDR中的特定地址。驅動程序讀取它以檢測外部硬件是否仍在運行。

如果外部DDR工作不正常,則訪問外部DDR會在嵌入式CPU上產生總線錯誤。爲了防止同時多線程訪問,驅動程序使用信號量。

現在來解決問題。如果一個線程抓取信號量,然後終止一個總線錯誤,信號量仍然被鎖定。隨後的所有調用都會無限期地獲取信號量塊。我可以使用什麼技術來避免這種情況永久掛斷?

一個例子sysfs的功能(簡體):

static ssize_t running_attr_show(struct device *dev, struct device_attribute *attr, char *buffer) 
{ 
    struct my_device * const my_dev = container_of(dev, struct my_device, dev); 
    int ret; 

    if(down_interruptible(&my_dev->sem)) 
    { 
     ret = -ERESTARTSYS; 
    } 
    else 
    { 
     u32 heartbeat; 
     int running; 

     // Following line could cause bus error 
     heartbeat = mwindow_get_reg(&my_dev->mwindow, HEARTBEAT_COUNTER_ADDR); 

     running = (heartbeat != my_dev->last_heartbeat) ? 1 : 0; 
     my_dev->last_heartbeat = heartbeat; 

     ret = sprintf(buffer, "%d\n", result); 

     /* unlock */ 
     up(&my_dev->sem); 
    } 

    return ret; 
} 

回答

1

你需要修改mwindow_get_reg()以及可能對總線錯誤調用架構故障處理程序,以便mwindow_get_reg()可以返回一個錯誤,而不是終止處理。

然後,您可以通過釋放信號量並向用戶空間返回錯誤來優雅地處理該錯誤。

+0

感謝。當然需要考慮一些事情。對於異常處理程序如何知道總線錯誤的原因是來自mwindow_get_reg的調用,您有任何建議嗎?我們有一些代碼已經做了一些探測,它依賴於在訪問之前設置的靜態變量,在故障期間檢測到,以便可以設置另一個靜態,然後在訪問之後進行檢查。在整個過程中,中斷被禁用。這似乎不太優雅,並且意味着通過內存窗口將大量固件複製到目標DDR時,中斷會被禁用很長時間。 – qbert220 2011-03-29 08:41:29

+0

@ qbert220:看看你的架構中'copy_from_user()'的實現 - 該函數必須設置一些東西,這樣不可恢復的頁面錯誤會將EFAULT返回給調用者而不是引起恐慌(你還必須看到頁面錯誤處理程序如何執行其作業)。 – caf 2011-03-29 23:39:18

+0

這是一個相當簡單的架構。 copy_to_user實現調用access_ok(它檢查地址是否在預期的範圍內),如果返回false,則返回-EFAULT。在所有其他情況下,它調用memcpy並返回請求複製的字節數(忽略memcpy返回值)。 – qbert220 2011-03-30 09:45:20

1

感謝@caf,這是我實施的解決方案。

我已將部分mwindow_get_reg轉換爲程序集。對於可能的錯誤讀取,我添加了一個條目到帶有錯誤地址和修復地址的ex_table部分。這會導致異常處理程序跳轉到修復代碼,而不是在此地址發生異常時終止線程。該修正彙編設置一個「故障」標誌,然後我可以在我的C代碼測試:

unsigned long ret = 0; 
int faulted; 

asm volatile(
     " 1:  lwi  %0, %2, 0;   "  // ret = *window_addr 
     " 2:  addik %1, r0, 0;   "  // faulted = 0 
     " 3:         " 
     "   .section .fixup, \"ax\"; "  // fixup code executed if exception occurs 
     " 4:  brid 3b;    "  // jump to next line of c code 
     "   addik %1, r0, 1;   "  // faulted = 1 (in delay slot) 
     "   .previous;     " 
     "   .section __ex_table,\"a\"; " 
     "   .word 1b,4b;    "  // ex_table entry. Gives fault address and jump address if fault occurs 
     "   .previous;     " 
      : "=r" (ret), "=r" (faulted)    // output registers 
      : "r" (window_addr)      // input registers 
); 

if (faulted) 
{ 
    printk(KERN_ERROR "%s: %s: FAULTED!", MODNAME, __FUNCTION__); 
    ret = 0xdeadbeef; 
} 

我也不得不加入到修改我的DBUS異常處理程序如下:

const struct exception_table_entry *fixup; 
fixup = search_exception_tables(regs->pc); 
if (fixup) { 
    printk(KERN_ERROR "DBUS exception: calling fixup\n"); 
    regs->pc = fixup->fixup; 
    return; 
}