EMMC读写操作的调用栈
mmc_queue_thread ->
mmc_blk_issue_rq ->
mmc_blk_issue_rw_rq ->
mmc_start_req ->
__mmc_start_data_req ->
mmc_start_request ->
omap_hsmmc_request
首先mmc_queue_thread获取相应的mmc_request然然后调用mq->issue_fn处理reuqest,issue_fn有可能被阻塞在mmc_wait_for_data_req_done,如果此时有新的请求到达,那么有可能会唤醒阻塞的进程 (条件是cur==null, prev!=null)。
然后调用mmc_blk_issue_rq来选择对应的分区,根据req->cmd_flags的命令做不同的事情。REQ_SANITIZE、REQ_DISCARD、REQ_FLUSH分别为
2.1 mmc_blk_issue_secdiscard_rq 和mmc_blk_issue_discard_rq
2.2 mmc_blk_issue_flush
2.3 mmc_blk_issue_rw_rq,这个是我们要分析的读写数据流程
进入mmc_blk_issue_rw_rq函数
如果参数req为空(无新request),或者mmc queue的previous request也为空(无未完成的request),那么mmc_blk_issue_rw_rq直接返回。
mmc_blk_prep_packed_list尝试把当前request和队列中的其他request合并,以增强性能。是否可以合并,要依赖于:
1. 控制器支持packed功能
2. device的MAX_PACKED_WRITES 大于0
3. 只对写request进行packed
正常情况下执行mmc_blk_rw_rq_prep函数,从request构造mmc_request,毕竟下发给host请求,是mmc_request,而不是block层通用的request。如果支持packed功能,那么就用pack_list来构造mmc_request
areq表示async req,实际上,只要参数@req存在,就表明这是一个新request,必然是异步传输的。
mmc_start_req 启动一个非阻塞的request,这个函数会等待前一个request完成,然后把启动当前requeset,并立刻返回
如果mmc_start_req返回的areq不为空,说明完成了上一次的request,
如果使用了bounce buffer,那么需要把传输结果从bounce buffer复制会sg buffer。所谓bounce buffer是因为某些DMA控制器只能处理连续物理内存,此时需要通过bounce buffer来达到物理内存连续性。
检查mmc_start_req返回的状态:
1. 如果是MMC_BLK_SUCCESS或者MMC_BLK_PARTIAL,需要调用blk_end_request通知block设备层,完成了本次读写request。
2. 如果是MMC_BLK_CMD_ERR,那么调用mmc_blk_reset复位host。调用mmc_blk_cmd_err尝试blk_end_request,如果发现reuqest未完成,说明本次操作失败,反之成功start_new_req
3. ....
真正的执行读写请求是在执行函数mmc_start_req里:
1、 首先它会执行到mmc_wait_for_data_req_done函数,等待上一次的命令的完成,如果上一次未完成就会将当前进程加入等待队列休眠,等待被唤醒。当上一次完成后会立即返回,并将上一次命令执行的状态返回给mmc_blk_issue_rw_rq。
2、if (host->areq) {
err = mmc_wait_for_data_req_done(host, host->areq->mrq, areq);
host->areq不为空,说明有正在处理的reuqest,函数mmc_wait_for_data_req_done用来等待这个host->areq,有两个条件会唤醒该MMC上下文: is_done_rcv和is_new_req
if (!err && areq)
start_err = __mmc_start_data_req(host, areq->mrq);
进入__mmc_start_data_req(host, areq->mrq);
首先会将函数指针mmc_wait_data_done赋给mrq->done
if (host->card && host->card->ext_csd.cmdq_mode_en)
mrq->done = mmc_wait_cmdq_done;
else
mrq->done = mmc_wait_data_done;
mmc_wait_data_done会设置context_info->is_done_rcv=true,这正好是唤醒mmc_wait_for_data_req_done的条件之一,然后调wake_up_interruptible(&context_info->wait);唤醒之。
然后会调用mmc_start_request(host, mrq);
mmc_start_reuqest实际调用host->ops->request方法,进入了平台特定的request函数
进入特定的平台之后,会进入相应的中断对硬件进行读写的命令的执行,当命令执行完毕后,会进行函数回调调到刚才的mmc_wait_data_done唤醒等待的进程进行下一次命令的执行。