2013-04-10 40 views
0

我在asm/i387.h中使用kernel_fpu_beginkernel_fpu_end函數來保護Linux內核模塊內某些簡單浮點算法的FPU寄存器狀態。在kernel_fpu_end之前調用kernel_fpu_begin兩次

我很好奇在kernel_fpu_end函數之前兩次調用kernel_fpu_begin函數的行爲,反之亦然。例如:

#include <asm/i387.h> 

double foo(unsigned num){ 
    kernel_fpu_begin(); 

    double x = 3.14; 
    x += num; 

    kernel_fpu_end(); 

    return x; 
} 

... 

kernel_fpu_begin(); 

double y = 1.23; 
unsigned z = 42; 
y -= foo(z); 

kernel_fpu_end(); 

foo功能,我打電話kernel_fpu_beginkernel_fpu_end;但在撥打foo之前已撥打kernel_fpu_begin。這會導致未定義的行爲?

此外,我應該甚至在foo函數裏面調用kernel_fpu_end?在kernel_fpu_end調用之後,我返回,這意味着訪問浮點寄存器是不安全的嗎?

我最初的猜測是不使用foo函數中的kernel_fpu_beginkernel_fpu_end調用;但如果foo返回轉換爲無符號而不是 - 程序員不知道在foo之外使用kernel_fpu_beginkernel_fpu_end

回答

4

簡答:不,嵌套kernel_fpu_begin()調用是不正確的,這將導致用戶空間的FPU狀態被破壞。

中的答案:這是行不通的,因爲kernel_fpu_begin()使用當前線程的struct task_struct保存關閉FPU狀態(task_struct有一個體繫結構相關的成員thread,並在x86,thread.fpu持有線程的FPU狀態),並做了第二個kernel_fpu_begin()將覆蓋原始的保存狀態。然後做kernel_fpu_end()將最終恢復錯誤的FPU狀態。

長答案:正如你看到在<asm/i387.h>中的實際實現,細節有點棘手。在較舊的內核中(例如您所看到的3.2源代碼),FPU處理總是「懶惰」 - 內核希望避免重新加載FPU直到真正需要它爲止的開銷,因爲該線程可能會運行並再次被調度沒有實際使用FPU或需要其FPU狀態。因此kernel_fpu_end()只是設置TS標誌,導致FPU的下一個訪問陷阱並導致FPU狀態被重新加載。希望是我們實際上並沒有足夠的時間使用FPU,因此整體上它更便宜。然而,如果你看看更新的內核(我相信3.7或更新的內核),你會發現其實有第二個代碼路徑 - 「渴望」FPU。這是因爲較新的CPU具有「優化的」XSAVEOPT指令,較新的用戶空間更頻繁地使用FPU(對於memcpy中的SSE等)。 XSAVEOPT/XRSTOR的成本較低,實際上避免FPU重新加載的延遲優化的機會不那麼明顯,因此在新CPU上使用新內核時,kernel_fpu_end()會繼續並恢復FPU狀態。 (

然而,在這兩個「懶惰」和「渴望」 FPU模式,還有在task_struct只有一個插槽保存FPU狀態,所以築巢kernel_fpu_begin()最終會破壞用戶空間的FPU狀態。

+0

從理論上講,如果我產生了一個新的內核線程,調用'kernel_fpu_begin'是否安全,因爲狀態會被保存到不同的'task_struct'結構中呢? – 2013-04-17 19:15:39

+1

當然,在另一個線程的上下文中,你可以再次調用kernel_fpu_begin,因爲你正在創建一個新的執行線程,所以這並不是真正的嵌套用法(一個比喻就是你可以使用'mutex_lock() '在同一個互斥體上兩次,當且僅當你有兩個不同的線程) – Roland 2013-04-17 20:56:01

+0

順便說一下,在'kernel_fpu_begin'中,你碰巧知道爲什麼TS標誌只有在TS_USEDFPU沒有被設置時才被清除;如果TS_USEDFPU未設置,TS標誌是否已經被清除? – 2013-04-17 21:04:06

-1

是的,因爲你定義了一些雙變量& foo也返回了double值;你必須使用kernel_fpu_beginkernel_fpu_end電話外fooalso.


Similar Problem也有這裏面有一定的情況下,您可以不使用kernel_fpu_beginkernel_fpu_end調用的代碼。

+2

這是如果嵌套'kernel_fpu_begin',你將破壞FPU狀態 – Roland 2013-04-17 20:59:10

0

我在評論asm/i387.h Linux源代碼(版本3.2)與我所瞭解的發生。

static inline void kernel_fpu_begin(void) 
{ 
     /* get thread_info structure for current thread */ 
     struct thread_info *me = current_thread_info(); 

     /* preempt_count is incremented by 1 
     * (preempt_count > 0 disables preemption, 
     * while preempt_count < 0 signifies a bug) */ 
     preempt_disable(); 

     /* check if FPU has been used before by this thread */ 
     if (me->status & TS_USEDFPU) 
       /* save the FPU state to prevent clobbering of 
       * FPU registers, then reset the TS_USEDFPU flag */ 
       __save_init_fpu(me->task); 
     else 
       /* clear the CR0.TS bit to prevent 
       * unnecessary FPU task context saving */ 
       clts(); 
} 

static inline void kernel_fpu_end(void) 
{ 
     /* set CR0.TS bit (signifying the processor switched 
     * to a new task) to enable FPU task context saving */ 
     stts(); 

     /* attempt to re-enable preemption 
     * (preempt_count is decremented by 1); 
     * reschedule thread if needed 
     * (thread will not be preempted if preempt_count != 0) */ 
     preempt_enable(); 
} 

FXSAVE指令通常用於保存FPU狀態。但是,我相信每次在同一線程中調用kernel_fpu_begin時內存目標保持不變;不幸的是,這意味着FXSAVE將覆蓋先前保存的FPU狀態。

因此,我懷疑你的不能安全嵌套kernel_fpu_begin調用。

我仍然無法理解FPU狀態是如何恢復的,因爲kernel_fpu_end調用看起來不會執行FXRSTOR指令。另外,如果我們不再使用FPU,爲什麼在kernel_fpu_end調用中設置CR0.TS位?

相關問題