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);