標籤:blog io 使用 sp on log bs 代碼 ad
CPU上處理的中斷可以分成“硬體中斷”和“軟體中斷”兩類,比如網卡產生的中斷稱為硬體中斷,而如果是軟體使用諸如"int 0x10"(X86平台上)這樣的指令產生中斷稱為軟體中斷,硬體中斷是非同步,其發生的時機是不可知的,但是軟體中斷是同步的,CPU是“確切”知道其發生的時機的。
同樣的,在GPU開來,中斷也可以分成“硬體中斷”和“軟體中斷”兩類,比如熱插拔事件或者vblank事件都會產生“硬體中斷”,這些事件在GPU看來是非同步,GPU不知道這些事情何時發生。GPU也可以使用類似CPU的int指令那樣產生中斷,考慮這樣一種情形:驅動向硬體發送了繪圖命令後必須等到硬體執行完了這些命令後才能進行後續的操作,否則硬體的上一次命令沒有執行完就繼續執行下一次命令會導致錯誤。前面介紹scratch寄存器的時候提及過可以在命令末尾添加一條寫scratch寄存器的命令,發送命令之後驅動使用輪詢的方式輪詢scratch寄存器,當然這種場合使用輪詢肯定是不合適的,實際上顯卡可以採用非強制中斷機制,在完成繪圖命令後執行一個類似“int xx”的命令產生中斷,這裡GPU是“確切”知道中斷髮生的時機的----即在繪圖命令完成的時候。
前面提到的fence就是這種“軟體中斷”的具體應用。
在上一個blog中看到,fence是按照下面的步驟使用的:
radeon_fence_create->radeon_fence_emit->radeon_fence_wait
radeon驅動中的fence機制用於同步GPU和CPU,Fence機制的實現依賴GPU產生的非強制中斷和scratch寄存器。CP完成一個繪圖操作後執行產生中斷的命令,向CPU發送一次中斷訊號,這裡的“產生中斷的命令”其實就是寫CP_INT_STAT寄存器。
在radeon驅動代碼中,完成向ring buffer中填充繪圖命令後,會調用radeon_fence_emit函數(參考GPU命令包章節的代碼),在r600顯卡上最終調用r600_fence_ring_emit函數,該函數中有如下代碼:
2327 void r600_fence_ring_emit(struct radeon_device *rdev,
2328 struct radeon_fence *fence)
......
2347 /* Emit fence sequence & fire IRQ */
2348 radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
2349 radeon_ring_write(rdev, ((rdev->fence_drv.scratch_reg -
PACKET3_SET_CONFIG_REG_OFFSET) >> 2));
2350 radeon_ring_write(rdev, fence->seq);
2351 /* CP_INTERRUPT packet 3 no longer exists, use packet 0 */
2352 radeon_ring_write(rdev, PACKET0(CP_INT_STATUS, 0));
2353 radeon_ring_write(rdev, RB_INT_STAT);
這裡讓GPU執行的命令(2352-2353行代碼)類似在作業系統中讓CPU執行的“int xx” 指令,這兩句代碼的意思是寫CP_INT_STATUS寄存器,但是注意到寄存器CP_INT_STATUS 是一個中斷狀態寄存器,驅動通過MMIO的方式是無法寫這個寄存器的,但是如果CP寫這個寄存器就會產生“軟體中斷”(當前觀察到的現象是這樣的,是否正確)。
通常硬體會有一些寄存器用於表示中斷相關資訊,在硬體產生中斷的時候將相關資訊寫入寄存器中,驅動讀取這些寄存器就能知道和中斷相關的具體資訊。Radeon GPU中除了有這類寄存器表明中斷類型外,scratch寄存器可以派上用場。
在核心radeon驅動中,每一個fence都被分配了唯一的ID號(seq),在radeon_fence_emit中有如下代碼:
71 int radeon_fence_emit(struct radeon_device *rdev, struct radeon_fence *fence )
......
80 fence->seq = atomic_add_return(1, &rdev->fence_drv.seq);
2340-2350行代碼fence->seq的值被寫入一個scratch寄存器,所以當繪圖命令完成中斷產生之前scratch寄存器就會是被置為這個唯一的ID號,讀取scratch寄存器就能夠知道是哪一次繪圖命令產生的中斷。
Fence中斷處理函數是radeon_fence_poll_locked。首先讀取fence編號,知道是那一次fence操作產生的中斷,當產生中斷的fence編號是最後一個編號時,需要將最後一個fence編號賦值為當前編號,同時更新fence定時器。如果不是最後一個fence編號產生的中斷,就需要判斷定時器,然後喚醒fence中斷隊列。
PS. 這篇文章裡只描述了軟體中斷的硬體機制,linux核心drm驅動關於顯卡中斷的代碼也有一套比較複雜的架構,需要深入分析才能理解其全貌。
【原創】Linux環境下的圖形系統和AMD R600顯卡編程(7)——AMD顯卡的軟體中斷