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