跳到主要內容

發表文章

disable_irq() might be lazy in Linux kernel

在 Linux kernel 中,如果要 diable 某個 irq,會使用disable_irq(irq) 這個 API ,而這個 API 有一個有趣的實作:它有可能不會"真的"馬上在硬體上關閉該 irq ,而是到了下一次真的有該 irq 觸發時,才在 flow handler 中進行 disable。相關代碼如下: 252 void irq_disable (struct irq_desc * desc ) 253 { 254 irq_state_set_disabled ( desc ); /* set SW status as disabled */ 255 if ( desc -> irq_data . chip -> irq_disable ) { 256 desc -> irq_data . chip -> irq_disable (& desc -> irq_data ); /* if registered irq_disable() callback, use it */ 257 irq_state_set_masked ( desc ); 258 } else if ( irq_settings_disable_unlazy ( desc )) { 259 mask_irq ( desc ); /* if not disable lazy, mask it */ 260 } 261 } 525 void handle_fasteoi_irq (struct irq_desc * desc ) 526 { 527 struct irq_chip * chip = desc -> irq_data . chip ; 528 529 raw_spin_lock (& desc -> lock ); 530 531 if (! irq_may_run ( desc )) 532 g...

bug、慣性與旁觀者

工程人員最怕遇到隨機出現的bug,尤其是IC廠的軟體工程師,既要懷疑自己的軟體流程,又要同時有能力區分是否為IC bug(因為硬體出錯的機率相較於PC平台來說高出很多...)。最近面對一個bug,竟然花了兩週的時間才整個釐清並解決...事後自己反省,覺得有很多值得改善的地方。 問題是這樣的:當系統開機時,發現我負責的模組在約40s~50s左右會隨機的timeout,或遭遇奇怪的錯誤情況,最後讓整個系統當掉。 由於單獨模組都已經通過了壓力測試,所以我第一個反應就是先檢查硬體。觀察了外部訊號,但沒有發現任何異樣。接著觀察相關registers,發現掛掉的時候讀到了非常奇怪的數值。由於該數值是controller直接從DRAM獲得,所以有可能是DRAM不穩嗎?不對,因為幾乎每次都掛在我這邊,如果是DRAM不穩,那應該到處都會著火。 那麼,是bus上出問題嗎?一想到這,心就涼了半截...但還是硬著頭皮觀察IC內部bus訊號(AHB signal,嗯,你不用在乎這是什麼 XD)結果,真的發現在發生問題時,bus signal非常的異常。這下designer緊張了,這可能會影響到是否已下單的wafer是否全部泡湯的問題。於是,一堆人開始出主意:有沒有試過ooo、有沒有試過xxx、#$%︿&**(!... 由於出意見的人都是有掛頭銜的,所以往往都要先確認他們的意見是否成立。嗯,這是我犯的第一個大錯: 花了太多時間應付長官的要求,而非處理事情的本質。 由於長官並不那麼清楚整個始末,但又因為可能是大問題,所以自然吸引人們出意見。但是我應該在當時就說明清楚,而不是默默承受無釐頭的要求( 懂得何時說"No"就跟懂得何時說"Yes"一樣重要 )。嗯...總之,這樣一個禮拜就過去了,客戶也開始著急了起來,質疑什麼問題需要一週還沒任何結論。 接著,長官們沒想法後,總算可以開始自己的實驗,很快地發現某個模組只要一發生錯誤,我的部份再過幾秒後就出問題。所以將重點移到該模組。詢問該模組負責人相關流程後,對於為何會出錯也很納悶,畢竟...嗯,他自己單獨跑也是經過了好幾天的測試...好了,所以問題是出在兩者間的交互作用導致bus出問題嗎?於是我們開始了這兩個模組的co-sim, FPGA CVT...這邊則是我的第二個錯誤: ...

淺讀Linux root file system初始化流程

在Unix的世界中,file system佔據一個極重要的抽象化地位。其中,/ 所代表的rootfs更是所有後續新增file system所必須依賴前提條件。以Linux為例,黑客 Jserv 就曾經詳細說明過 initramfs的背後設計考量 。本篇文章不再重複背景知識,主要將追蹤rootfs初始化的流程作點整理,免得自己日後忘記。 :-) file system與特定CPU架構無關,所以我觀察的起點從init/main.c的start_kernel()開始,這是Linux作完基本CPU初始化後首先跳進的C function(我閱讀的版本為 3.12 )。跟root file system有關的流程羅列如下: start_kernel()         -> vfs_caches_init_early()         -> vfs_caches_init()                 -> mnt_init()                         -> init_rootfs()                         -> init_mount_tree()         -> rest_init()                 -> kernel_thread(kernel_init,...) 其中比較重要的是mnt_int()中的init_rootfs()與init_mout_tree()。init_rootfs()實作如下: int __init init_root...

淺讀CuRT:task, context switch與scheduling

上一篇 整理了CuRT從硬體上電後到跳入C function前所作的事情,本篇文章則會紀錄CuRT如何準備multitasking環境,以及CuRT於執行期的行為分析。 在app/shell/main.c中,我們可以看到main()的開頭為: int main() {         SerialInit();         init_interrupt_control();         init_curt(); ... SerialInit()只是把UART port初始化,方便printf()可以正常運作。init_interrupt_control()則設定pxa255的interrupt controller可以接收外部的interrupt sources,稍後啟動的timer就會發出定期的interrupt驅動scheduling。init_curt()作的事情就比較多了點,讓我們進去瞧一瞧: void init_curt() {         int i;         for (i = 0; i < MAX_PRIO; i++) {                 prio_exist_flag[i] = false;                 init_list(&ready_list[i]);         }         init_list(&delayed_list);         init_list(&blocked_list);         init_list(&termination_wai...

淺讀CuRT:啟動流程

Jserv 所寫的 CuRT 是一個非常輕量級的kernel,運行於ARMv5( PXA255 )。很適合拿來作為熟習ARM基本架構與實驗作業系統的各項技術。今天下載後,發現在我的環境(ubuntu12.04)需要作點修改才能通過編譯並正常執行,改完索性clone了一份上 github ,以利之後實驗修改。這篇文章則是為研讀此kernel所作的一系列紀錄的開頭。 千頭萬緒,從何開始呢?ARM規範其CPU reset後從0x0的位址開始執行,從PXA255的 手冊 上可知,初始的0x0落於boot ROM的範圍: 但是觀察編譯出來的curt_image.elf可以發現,其link address都以0xa0000000為base,這段位址落於SDRAM的範圍: 由於SDRAM必須要有controller控制時序,所以應該會先由boot ROM將其設定好後再搬過去執行。以上幾件事要能夠運行成功,那麼就勢必在初始時要有一段PIC的程式作relocation(或以某種方式remap address range),等等我們便可發現確實存在這樣的一段代碼。 觀察curt_image.elf的disassembly可知道,_start是.text的起頭,此符號定義於arch/arm/mach-pxa/start.S,所以我們就從這個檔案開始看起吧。:-) .text /* exception handler vector table */ _start: b reset_handler b und_handler b swi_handler b abt_pref_handler b abt_data_handler b not_used b irq_handler b fiq_handler 這邊是典型的reset vector的部份,由於除了reset_handler以外的其他mode我目前還不感興趣,所以就直接跳到reset_handler繼續看: reset_handler:  b set_misc set_misc: ... mask_off_int_reg: /* Mask off all IRQs and FIQs */ ... init_clock_reg: /* PXA250...

scheduler實作技巧於FPGA驗證上的應用

在IC設計公司,有一部分的軟體工程師需要與IC設計師緊密合作,負責驗證相關硬體模組功能與驅動程式撰寫。在硬體設計初步完工階段,通常會先在FPGA上進行邏輯方面的軟體驗證,通常軟體在此時會以最簡單的方式,準備好C語言運行環境後,進行bare metal上的控制,確認無誤後,才送交後端進行timing的調整。如果是功能較為豐富的SoC,還會在FPGA上運行完整的作業系統(如Linux),確認在實際應用情況下,所有功能仍然如預期般執行。 bare metal控制的好處是環境單純。容易確認問題是在某個步驟所發生,而且執行效率極高。另一方面,運行作業系統的好處不言而喻,但缺點就是摻雜了太多額外不相干的環節,並且由於FPGA效能問題,一段時間能進行的測試次數相較於bare metal的環境往往少一個數量級以上。所以,我們有時會需要以bare metal為基礎,往上逐漸添加部份系統功能以協助驗證工作。 其中一項常見的需求就是: bare metal環境通常沒有多工能力,但我們需要確認某幾個硬體功能"同時"運作時,是否會有系統執行問題?(bus hanged, interrupt lost, hardware state錯亂...) 為了這項功能,許多人的選擇是想辦法運行完整的作業系統來確認,但在初期的環境準備與後續的除錯,都常常耗費大量不必要的精力與時間。所以有些人則是在bare metel的環境下,以各種怪招來實現不同硬體動作"同時"運行的效果,比如說在某個硬體的ISR中,去驅動另一個硬體的read/write。這種作法往往誨澀難懂,並且掛一漏萬。 其實如果只是要達成上述需求,我們可以有更乾淨的作法:一個簡單的scheduler。只需要準備: context switch task structure與其獨立的stack timer interrupt ramdom timeslot for task 而task間的同步機制通常直接開/關中斷,也就足夠了。不太需要實作完整的task state machine。也就是說,基本上只要將" 一步步寫嵌入式操作系統 "一書的最後一章的範例程式稍作修改,即可備妥以上基礎設施(大概不到200行C與assembly)。有圖有真相,以下是我在實際...

Maximum Partition Number for Each MMC Block Device

今天花時間看一個客戶回報的問題,因為解決方法很簡單,但卻浪費了不少時間,所以將解決過程紀錄下來,作為警惕。:P 問題是這樣的:為什麼對 eMMC  裝置作完 partition 後(切出 3 個 primary partitions, 還有 1 個 extended partition 包含 12 個 logical partitions),只有產生 3 個 primary partitions 以及 1個 extended partition 以及 3 個 logical partitions 的 device node? 我先花了些時間研究 MBR 的格式,然後肉眼觀察 partition 後的 eMMC raw data,看起來 fdisk  與我們加進 Linux kernel 的 host controller driver 應該沒有問題 ,那問題應該就是在 Linux parsing  MBR 的過程,再花了一點時間,觀察 linux/block/partitions/check.c  ,發現會以一個 block device 的 minors 作為 partition 數量的上限 。 好了,所以問題就在於 MMC layer 在將控制權交給 block layer 之前,是如何設定這個數值的。簡單搜尋一下,發現 在這 : 70 /* 71 * The defaults come from config options but can be overriden by module 72 * or bootarg options. 73 */ 74 static int perdev_minors = CONFIG_MMC_BLOCK_MINORS ; 75 呼,真相大白,原來需要調整這個 option...,預設值為 8 ,所以最多只產出 7 個 device node給 partitions 使用。我學到的教訓是:99%的情況下,不要浪費時間懷疑已相當成熟的工具,應先花點時間觀察是否有供使用者調整的選項。 不過從另一方面想,也確實地了解一遍每個步驟以及相互之間的關係,不算損失太多。:P