天天看点

linux mmc 读写,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处理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唤醒等待的进程进行下一次命令的执行。