DSP

数据读写SD卡下半部分析

2019-07-13 14:56发布

接上一篇文章 read 系统调用剖析 

在上一文中,作者已经将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发送。
mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s", host->index, subname ? subname : "");
可以看到 mmc_queue_thread 才是实际做事情的,所以我们先来看看他的庐山真面目。

static int mmc_queue_thread(void *d) { struct mmc_queue *mq = d; struct request_queue *q = mq->queue; current->flags |= PF_MEMALLOC; down(&mq->thread_sem); do { struct request *req = NULL; spin_lock_irq(q->queue_lock); set_current_state(TASK_INTERRUPTIBLE); req = blk_fetch_request(q); mq->req = req; spin_unlock_irq(q->queue_lock); if (!req) { if (kthread_should_stop()) { set_current_state(TASK_RUNNING); break; } up(&mq->thread_sem); schedule(); down(&mq->thread_sem); continue; } set_current_state(TASK_RUNNING); mq->issue_fn(mq, req); } while (1); up(&mq->thread_sem); return 0; }

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函数中注册了这个回调函数。

        md->queue.issue_fn = mmc_blk_issue_rq md->queue.data = md; md->disk->major = MMC_BLOCK_MAJOR; md->disk->first_minor = devidx * perdev_minors; md->disk->fops = &mmc_bdops; md->disk->private_data = md; md->disk->queue = md->queue.queue; md->disk->driverfs_dev = parent; set_disk_ro(md->disk, md->read_only || default_ro); md->disk->flags = GENHD_FL_EXT_DEVT;
而mmc_blk_alloc 函数是在mmc_blk_probe 函数中被调用的,所以看起来,在mmc block driver 初始化的时候已经为mmcqd做好了准备。
static int mmc_blk_probe(struct mmc_card *card) { struct mmc_blk_data *md, *part_md; int err; char cap_str[10]; /* * Check that the card supports the command class(es) we need. */ if (!(card->csd.cmdclass & CCC_BLOCK_READ)) return -ENODEV; md = mmc_blk_alloc(card); if (IS_ERR(md)) return PTR_ERR(md);
好了,我们还是继续看看mmc_blk_issue_rq到底又是如何把我们的request发送给sd card driver的。
static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) { int ret; struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; #ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME if (mmc_bus_needs_resume(card->host)) { mmc_resume_bus(card->host); mmc_blk_set_blksize(md, card); } #endif mmc_claim_host(card->host); ret = mmc_blk_part_switch(card, md); if (ret) { ret = 0; goto out; } if (req->cmd_flags & REQ_DISCARD) { if (req->cmd_flags & REQ_SECURE) ret = mmc_blk_issue_secdiscard_rq(mq, req); else ret = mmc_blk_issue_discard_rq(mq, req); } else if (req->cmd_flags & REQ_FLUSH) { ret = mmc_blk_issue_flush(mq, req); } else { ret = mmc_blk_issue_rw_rq(mq, req); } out: mmc_release_host(card->host); return ret; } 这个函数其实也是挺简单的,虽说有几个分支,但是很明显,大部分的request都是通过 ret = mmc_blk_issue_rw_rq(mq, req); 来发送的。自这个函数之后就会调用到mmc 的core部分,之后便进入真正的SD卡的数据传输。更底层的过程我就不在这次讲了,后面专门针对sd driver部分做分析!

3. mmcqd 与IO 的关联

在上一文中曾经讲到request_fn就是block曾的入口函数。
q->request_fn = rfn; blk_queue_make_request(q, __make_request);
所以我们来看看rfn 具体是那个函数呢?

在/kernel/drivers/mmc/card/queue.c 的 mmc_init_queue 中注册了这个函数。
int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock, const char *subname) { struct mmc_host *host = card->host; u64 limit = BLK_BOUNCE_HIGH; int ret; if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask) limit = *mmc_dev(host)->dma_mask; mq->card = card; mq->queue = blk_init_queue(mmc_request, lock);
blk_init_queue(mmc_request, lock);
在上文中已经讲过,这里就不再重复分析。
所以,接下来我们看看IO实际调用的函数为mmc_request
static void mmc_request(struct request_queue *q) { struct mmc_queue *mq = q->queuedata; struct request *req; if (!mq) { while ((req = blk_fetch_request(q)) != NULL) { req->cmd_flags |= REQ_QUIET; __blk_end_request_all(req, -EIO); } return; } if (!mq->req) wake_up_process(mq->thread); }
从上面函数可以看到,如果mq->req 是空的,我们则唤醒,mq->thread 来发送一笔req 下去。 至此mmcqd与上层IO的关联也很清楚了。