跳到主要內容

發表文章

目前顯示的是 2016的文章

[LKML] enhanced printk for NMI in Linux

紀錄一下近期kernel為了NMI(non-maskable interrupt)的情況設計的一個 printk() 加強機制。 我們都知道,原本 NMI 發生時,如果剛好某個 core 正把 printk lock 拿住,就不能在 NMI handler 中去使用printk(), 否則會 deadlock。 SUSE 工程師 Petr Mladek 提了一個patch[1],可以針對 NMI 的情況繼續使用 printk()。 基本的想法: 在發生 NMI 時,呼叫 printk_nmi_enter(),printk內部就會切換到另一個printk func,l og 就會推進 單獨的 buffer ,離開 NMI context 後,再呼叫 printk_nmi_exit() 切回原本printk實作, 然後起一個 irq worker,worker後續會把 NMI buffer copy 進__log_buf。 想法很簡單,但實作上許多tricky之處,請詳閱 patch 說明,或...printk() 任何情況直接用下去就對了 XD [1]  https://patchwork.kernel.org/patch/8899761/

[LKML] Linus的PDCA - BUG_ON() 的使用方式

[BUG_ON()] It's not a "let's check that  everybody did things right", it's a "this is a major design rule in  this core code". "good BUG_ON() in solid code that has been around forever" and "bad BUG_ON() checking something that we know we might be getting wrong".                                                                                                   by Linus Torvald Linus Torvalds 在釋出 4.8 後,很快地發現 kernel 在開啟 CONFIG_DEBUG_VM 時,容易會讓 kernel panic。[1] 原因是因為在 VM 的程式碼中,在檢查到某個異常情況時, VM_BUG_ON()會 使用 BUG_ON()。 根據該筆修改的  Johannes Weiner  說法,他的程式碼只會在 CONFIG_DEBUG_VM 時使用 BUG_ON(),所以認為對一般使用者不會有影響才對。 並且,作者認為,如果已經遇到了異常情況,卻不用 BUG_ON() 將系統停住,不就讓系統的行為更無法預期、更難 debug 了嗎? [ 2 ] Linus 很快地回了一封 mail ,說明 BUG_ON() 的少數使用時機: 開發者的測試環境 對 error handling 還沒想清楚,但是覺得某個地方絕不該發生,通常只應該在 [RFC] 階段,大概對應開發階段的 UT。 然而,即使開啟了 CONFIG_DEBUG_VM,也不該就讓 BUG_ON() 進入,因為對使用者來說,開啟這個選項是想獲得更多訊息, 而不是讓 kernel 死亡。            這類的 BUG_ON() 經過一些測試後,就應該拿掉。        2

[LWN] Atomic usage patterns in the kernel

介紹一篇LWN的文章[1],該文整理了atomic API的幾個使用時機,以及不建議的場合: https://lwn.net/Articles/698315/   1.      Simple flag ( 設後不理 ) NFS RFC實作原本有很多的flag透過bit field表示狀態: unsigned int sl_temp : 1;   /* temp socket */ 由於bit field的更新會造成同一個word的read-modify-write,所以原本作法是透過spin_lock來避免 在SMP下的race condition。但是spin_lock在這個情況下有點太肥,因為會限制其他field的同時存取, 後來改用set_bit()/clear_bit(),由硬體支援的atomic 指令來完成。 (EX: arch/arm64/lib/bitops.S) 所以:如果 只有 bit fields 要更新,可考慮atomic API ,避免不必要的lock 。 2.      Counters 很多kernel的統計資料會透過per-cpu counter逐次累加,需要獲取時才再加總。但是對於不是太常 更新的counter,可以透過一個global counter,然後使用atomic_add()來簡化設計 (EX:許多file system的logging stat)。 所以: per-cpu counter ,可考慮改為單一global counter 。 3.      Exclusive ownership  (設後要理) #1只對flag作update,設後不理。但是如果flag是拿來當作互斥使用時,就要再考慮barrier的問題, 並使用對應的版本:         if (!test_and_set_bit_ lock (BIT_NUM, &bitmap)) {                 /* make use of exclusive ownership here */                 clear_bit_ unlock (BIT_NUM, &bitmap);         }

Consider using bitwise operations to reduce complex loop + if-then-else statements

今天跟同事一起處理某段需要最佳化的程式碼,經過 profiling 後,發現瓶頸出現在某段程式碼片段,類似如下: for (i = 0; i < num_irqs ; i++) {     if (irq_pending(i) & irq_not_masked(i)) {         continue;     } else {         irq_ack(i);     } } 此處的邏輯很清楚,依序檢查所有 irq ,如果 irq 目前正 pending 並且也沒有被 masked 的話,就 bypass ,反之就將其 ack 。由於讀取與設定 irq 的 register 是在一個 32bit word 中涵蓋 32 個 irq 設定,所以解決的方法似乎也顯而易見:直接使用 bitwise operation 取代上述程式。如下: for (i = 0; i < num_irq_regs ; i++) {     should_ack_vector = ~(irq_pending_vector(i) & irq_not_masked_vector(i));     irq_ack_vector(should_ack_vector); } 似乎很簡單?對吧?不過考慮如果是更複雜的情況呢?如果 loop 中還有更多的條件判斷呢?是否存在系統化手法將複雜的條件判斷轉成 bitwise operation?以下是我目前採用的手法: 將條件判斷式視為 input 將欲執行動作視為 output 建出 truth table or boolean 運算式 化簡 step 3 (手算 or 利用現有工具[1]) 根據以上步驟,我們逐一清查整個流程,經過一番整理後,效能提昇了百倍。一方面是因為 register read/write 本來就是相對耗時的動作(相對於 CPU 其他指令),一方面也透過 boolean 化簡得到精簡的讀寫次數。後來在 Hacker's Delight 一書中[2],找到了更多此類技巧的更完整論述,看來是該唸書的時候了。 :-) [1]   http://electronics-course.com/boolean-algebra [2]  http://www.

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