数据读写SD卡下半部分析-mmqcd

来源:互联网 发布:各种破解软件 编辑:程序博客网 时间:2024/04/28 23:31

在上一文中,作者已经将Read 整个调用过程从Linux 系统调用(SCI,system call interface)至IO调度的整个流程已经讲解的非常清晰明了,在此对作者表示致敬!

那这里我接着以Android SD卡为例子,分析IO调度后数据的处理流向!  本文代码基于linux kernel 3.0.4.

1. mmcqd 

mmcqd 是kernel在/kernel/drivers/mmc/card/queue.c  的mmc_init_queue拉起的一个内核线程,主要作用是把上层IO的request一个个向具体driver发送。

[cpp] view plaincopy
  1. mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s",  
  2.                 host->index, subname ? subname : "");  

可以看到 mmc_queue_thread 才是实际做事情的,所以我们先来看看他的庐山真面目。

[cpp] view plaincopy
  1. static int mmc_queue_thread(void *d)  
  2. {  
  3.         struct mmc_queue *mq = d;  
  4.         struct request_queue *q = mq->queue;  
  5.   
  6.         current->flags |= PF_MEMALLOC;  
  7.   
  8.         down(&mq->thread_sem);  
  9.         do {  
  10.                 struct request *req = NULL;  
  11.   
  12.                 spin_lock_irq(q->queue_lock);  
  13.                 set_current_state(TASK_INTERRUPTIBLE);  
  14.                 req = blk_fetch_request(q);  
  15.                 mq->req = req;  
  16.                 spin_unlock_irq(q->queue_lock);  
  17.   
  18.                 if (!req) {  
  19.                         if (kthread_should_stop()) {  
  20.                                 set_current_state(TASK_RUNNING);  
  21.                                 break;  
  22.                         }  
  23.                         up(&mq->thread_sem);  
  24.                         schedule();  
  25.                         down(&mq->thread_sem);  
  26.                         continue;  
  27.                 }  
  28.                 set_current_state(TASK_RUNNING);  
  29.   
  30.                 mq->issue_fn(mq, req);  
  31.         } while (1);  
  32.         up(&mq->thread_sem);  
  33.   
  34.         return 0;  
  35. }  

2. mmcqd 与sd driver的关联

从上面函数看,mmcqd的工作还是非常简单的, 在blk_fetch_request(q) 获取一笔request后,最终会通过 mq->issue_fn(mq, req) 把这笔request发送下去。 所以issue_fn 这个回调函数看起来应该就是同底层driver 通讯的关键函数。 那我们先来看看req 是如何通过issue_fn 发下去的。

在同目录下的block.c 中的mmc_blk_alloc -》mmc_blk_alloc_req函数中注册了这个回调函数。

[cpp] view plaincopy
  1.         md->queue.issue_fn = mmc_blk_issue_rq  
  2.         md->queue.data = md;  
  3.   
  4.         md->disk->major = MMC_BLOCK_MAJOR;  
  5.         md->disk->first_minor = devidx * perdev_minors;  
  6.         md->disk->fops = &mmc_bdops;  
  7.         md->disk->private_data = md;  
  8.         md->disk->queue = md->queue.queue;  
  9.         md->disk->driverfs_dev = parent;  
  10.         set_disk_ro(md->disk, md->read_only || default_ro);  
  11.         md->disk->flags = GENHD_FL_EXT_DEVT;  

而mmc_blk_alloc 函数是在mmc_blk_probe 函数中被调用的,所以看起来,在mmc block driver 初始化的时候已经为mmcqd做好了准备。
[cpp] view plaincopy
  1. static int mmc_blk_probe(struct mmc_card *card)  
  2. {  
  3.         struct mmc_blk_data *md, *part_md;  
  4.         int err;  
  5.         char cap_str[10];  
  6.   
  7.         /* 
  8.          * Check that the card supports the command class(es) we need. 
  9.          */  
  10.         if (!(card->csd.cmdclass & CCC_BLOCK_READ))  
  11.                 return -ENODEV;  
  12.   
  13.         md = mmc_blk_alloc(card);  
  14.         if (IS_ERR(md))  
  15.                 return PTR_ERR(md);  

好了,我们还是继续看看mmc_blk_issue_rq到底又是如何把我们的request发送给sd card driver的。
[cpp] view plaincopy
  1. static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)  
  2. {  
  3.         int ret;  
  4.         struct mmc_blk_data *md = mq->data;  
  5.         struct mmc_card *card = md->queue.card;  
  6.   
  7. #ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME  
  8.         if (mmc_bus_needs_resume(card->host)) {  
  9.                 mmc_resume_bus(card->host);  
  10.                 mmc_blk_set_blksize(md, card);  
  11.         }  
  12. #endif  
  13.   
  14.         mmc_claim_host(card->host);  
  15.         ret = mmc_blk_part_switch(card, md);  
  16.         if (ret) {  
  17.                 ret = 0;  
  18.                 goto out;  
  19.         }  
  20.         if (req->cmd_flags & REQ_DISCARD) {  
  21.                 if (req->cmd_flags & REQ_SECURE)  
  22.                         ret = mmc_blk_issue_secdiscard_rq(mq, req);  
  23.                 else  
  24.                         ret = mmc_blk_issue_discard_rq(mq, req);  
  25.         } else if (req->cmd_flags & REQ_FLUSH) {  
  26.                 ret = mmc_blk_issue_flush(mq, req);  
  27.         } else {  
  28.                ret = mmc_blk_issue_rw_rq(mq, req);  
  29.         }  
  30.   
  31. out:  
  32.         mmc_release_host(card->host);  
  33.         return ret;  
  34. }  
这个函数其实也是挺简单的,虽说有几个分支,但是很明显,大部分的request都是通过 ret = mmc_blk_issue_rw_rq(mq, req); 来发送的。自这个函数之后就会调用到mmc 的core部分,之后便进入真正的SD卡的数据传输。更底层的过程我就不在这次讲了,后面专门针对sd driver部分做分析!

3. mmcqd 与IO 的关联

在上一文中曾经讲到request_fn就是block曾的入口函数。
[cpp] view plaincopy
  1. q->request_fn    = rfn;  
  2. blk_queue_make_request(q, __make_request);  

所以我们来看看rfn 具体是那个函数呢?

在/kernel/drivers/mmc/card/queue.c 的 mmc_init_queue 中注册了这个函数。
[cpp] view plaincopy
  1. int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,  
  2.                    spinlock_t *lock, const char *subname)  
  3. {  
  4.         struct mmc_host *host = card->host;  
  5.         u64 limit = BLK_BOUNCE_HIGH;  
  6.         int ret;  
  7.   
  8.         if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask)  
  9.                 limit = *mmc_dev(host)->dma_mask;  
  10.   
  11.         mq->card = card;  
  12.         mq->queue = blk_init_queue(mmc_request, lock);  

blk_init_queue(mmc_request, lock);
在上文中已经讲过,这里就不再重复分析。
所以,接下来我们看看IO实际调用的函数为mmc_request
[cpp] view plaincopy
  1. static void mmc_request(struct request_queue *q)  
  2. {  
  3.         struct mmc_queue *mq = q->queuedata;  
  4.         struct request *req;  
  5.   
  6.         if (!mq) {  
  7.                 while ((req = blk_fetch_request(q)) != NULL) {  
  8.                         req->cmd_flags |= REQ_QUIET;  
  9.                         __blk_end_request_all(req, -EIO);  
  10.                 }  
  11.                 return;  
  12.         }  
  13.   
  14.         if (!mq->req)  
  15.                 wake_up_process(mq->thread);  
  16. }  

从上面函数可以看到,如果mq->req 是空的,我们则唤醒,mq->thread 来发送一笔req 下去。 至此mmcqd与上层IO的关联也很清楚了。