天天看點

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喚醒等待的程序進行下一次指令的執行。