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