MMC 非同步請求

原理

快取維護開銷有多大?

這取決於。 快速 eMMC 和具有推測性快取預取的多級快取使快取開銷相對顯著。 如果下一個請求的 DMA 準備工作與當前傳輸並行完成,則 DMA 準備開銷不會影響 MMC 效能。

非阻塞(非同步)MMC 請求的目的是最小化 MMC 請求結束和另一個 MMC 請求開始之間的時間。

使用 mmc_wait_for_req() 時,MMC 控制器在 dma_map_sg 和 dma_unmap_sg 處理時處於空閒狀態。 使用非阻塞 MMC 請求可以並行於活動的 MMC 請求準備下一個作業的快取。

MMC 塊驅動程式

MMC 塊驅動程式中的 mmc_blk_issue_rw_rq() 被設定為非阻塞。

吞吐量的增加與準備請求所需的時間(準備工作的主要部分是 dma_map_sg() 和 dma_unmap_sg())以及記憶體的速度成正比。 MMC/SD 越快,準備請求時間就變得越重要。 在 L2 快取平臺上,大型寫入的預期效能增益約為 5%,大型讀取的預期效能增益約為 10%。 在省電模式下,當時鍾以較低頻率執行時,DMA 準備可能需要更多時間。 只要這些較慢的準備工作與傳輸並行執行,效能就不會受到影響。

來自 IOZone 和 mmc_test 的測量詳細資訊

https://wiki.linaro.org/WorkingGroups/Kernel/Specs/StoragePerfMMC-async-req

MMC 核心 API 擴充套件

有一個新的公共函式 mmc_start_req()。

它為主機啟動一個新的 MMC 命令請求。 該函式並非真正的非阻塞。 如果有正在進行的非同步請求,它會等待該請求完成,然後啟動新請求並返回。 它不會等待新請求完成。 如果沒有正在進行的請求,它會啟動新請求並立即返回。

MMC 主機擴充套件

mmc_host_ops 中有兩個可選成員 - pre_req() 和 post_req() - 主機驅動程式可以實現它們,以便將工作移動到實際的 mmc_host_ops.request() 函式呼叫之前和之後。

在 DMA 情況下,pre_req() 可以執行 dma_map_sg() 並準備 DMA 描述符,而 post_req() 執行 dma_unmap_sg()。

針對第一個請求進行最佳化

一系列請求中的第一個請求無法與之前的傳輸並行準備,因為沒有之前的請求。

pre_req() 中的引數 is_first_req 表示沒有之前的請求。 主機驅動程式可以針對此場景進行最佳化,以最大程度地減少效能損失。 最佳化此問題的一種方法是將當前請求拆分為兩個塊,準備第一個塊並啟動請求,最後準備第二個塊並啟動傳輸。

使用最小的準備開銷處理 is_first_req 場景的虛擬碼

if (is_first_req && req->size > threshold)
   /* start MMC transfer for the complete transfer size */
   mmc_start_command(MMC_CMD_TRANSFER_FULL_SIZE);

   /*
    * Begin to prepare DMA while cmd is being processed by MMC.
    * The first chunk of the request should take the same time
    * to prepare as the "MMC process command time".
    * If prepare time exceeds MMC cmd time
    * the transfer is delayed, guesstimate max 4k as first chunk size.
    */
    prepare_1st_chunk_for_dma(req);
    /* flush pending desc to the DMAC (dmaengine.h) */
    dma_issue_pending(req->dma_desc);

    prepare_2nd_chunk_for_dma(req);
    /*
     * The second issue_pending should be called before MMC runs out
     * of the first chunk. If the MMC runs out of the first data chunk
     * before this call, the transfer is delayed.
     */
    dma_issue_pending(req->dma_desc);