linux内核mmc读写分析

                                  MMC 读写分析

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处理reuqestissue_fn有可能被阻塞在mmc_wait_for_data_req_done,如果此时有新的请求到达,那么有可能会唤醒阻塞的进程 (条件是cur==null, prev!=null)

然后调用mmc_blk_issue_rq来选择对应的分区根据req->cmd_flags的命令做不同的事情。REQ_SANITIZEREQ_DISCARDREQ_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 queueprevious request也为空(无未完成的request,那么mmc_blk_issue_rw_rq直接返回。

mmc_blk_prep_packed_list尝试把当前request和队列中的其他request合并,以增强性能。是否可以合并,要依赖于:

1. 控制器支持packed功能

2. deviceMAX_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_rcvis_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唤醒等待的进程进行下一次命令的执行。

你可能感兴趣的:(emmc)