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