(3)MMC卡请求的处理
图 函数mmc_init_queue调用层次图
函数mmc_init_queue初始化一个MMC卡请求队列结构,其中参数mq是mmc请求队列,参数card是加在这个队列里的mmc卡,参数lock是队列锁。函数mmc_init_queue调用层次图如上图。
函数mmc_init_queue分析如下(在drivers/mmc/mmc_queue.c中):
int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
spinlock_t *lock)
{
struct mmc_host *host = card->host;
u64 limit = BLK_BOUNCE_HIGH;
int ret;
if (host->dev->dma_mask && *host->dev->dma_mask)
limit = *host->dev->dma_mask;
mq->card = card;
//初始化块层的请求队列,请求合并策略,赋上请求处理函数mmc_request。
mq->queue = blk_init_queue(mmc_request, lock);
if (!mq->queue)
return -ENOMEM;
//初始化请求队列的扇区及片断限制
blk_queue_prep_rq(mq->queue, mmc_prep_request);//赋上准备请求函数
blk_queue_bounce_limit(mq->queue, limit);
blk_queue_max_sectors(mq->queue, host->max_sectors);
blk_queue_max_phys_segments(mq->queue, host->max_phys_segs);
blk_queue_max_hw_segments(mq->queue, host->max_hw_segs);
blk_queue_max_segment_size(mq->queue, host->max_seg_size);
mq->queue->queuedata = mq;
mq->req = NULL;
mq->sg = kmalloc(sizeof(struct scatterlist) * host->max_phys_segs,
GFP_KERNEL);
if (!mq->sg) {
ret = -ENOMEM;
goto cleanup;
}
init_completion(&mq->thread_complete);
init_waitqueue_head(&mq->thread_wq);
init_MUTEX(&mq->thread_sem);
//创建请求队列处理线程mmc_queue_thread
ret = kernel_thread(mmc_queue_thread, mq, CLONE_KERNEL);
if (ret >= 0) {
wait_for_completion(&mq->thread_complete);
init_completion(&mq->thread_complete);
ret = 0;
goto out;
}
cleanup:
kfree(mq->sg);
mq->sg = NULL;
blk_cleanup_queue(mq->queue);
out:
return ret;
}
函数mmc_prep_request 在准备一个MMC请求时做一些状态转移及保护操作,函数列出如下(在drivers/mmc/ mmc_queue.c中):
static int mmc_prep_request(struct request_queue *q, struct request *req)
{
struct mmc_queue *mq = q->queuedata;
int ret = BLKPREP_KILL;
if (req->flags & REQ_SPECIAL) {
//在req->special 中已建立命令块,表示请求已准备好
BUG_ON(!req->special);
ret = BLKPREP_OK;
} else if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) {
//块I/O请求需要按照协议进行翻译
ret = mq->prep_fn(mq, req);
} else {
//无效的请求
blk_dump_rq_flags(req, "MMC bad request");
}
if (ret == BLKPREP_OK)//请求已准备好,不需再准备了
req->flags |= REQ_DONTPREP;
return ret;
}
函数mmc_blk_prep_rq是准备请求时调用的函数,这里仅做了简单的保护处理,列出如下(在drivers/mmc/mmc_block.c中):
static int mmc_blk_prep_rq(struct mmc_queue *mq, struct request *req)
{
struct mmc_blk_data *md = mq->data;
int stat = BLKPREP_OK;
//如果没有设备,没法完成初始化
if (!md || !mq->card) {
printk(KERN_ERR "%s: killing request - no device/host\n",
req->rq_disk->disk_name);
stat = BLKPREP_KILL;
}
return stat;
}
函数mmc_request是通用MMC请求处理函数,它唤醒请求队列处理线程。它在特定的主控制器上被任何请求队列调用。当主控制器不忙时,我们查找在这个主控制器上的任何一个队列中的请求,并且尝试发出这个请求进行处理。
函数mmc_request分析如下(在driver/mmd/mmc_queue.c中):
static void mmc_request(request_queue_t *q)
{
struct mmc_queue *mq = q->queuedata;
if (!mq->req)//如果有请求,唤醒请求队列处理线程
wake_up(&mq->thread_wq);
}
线程函数mmc_queue_thread调用了具体设备的请求处理函数,利用线程机制来处理请求。函数mmc_queue_thread分析如下:
static int mmc_queue_thread(void *d)
{
struct mmc_queue *mq = d;
struct request_queue *q = mq->queue;
DECLARE_WAITQUEUE(wait, current); //声明一个当前进程的等待队列
//设置当前进程状态,来让线程自己来处理挂起
current->flags |= PF_MEMALLOC|PF_NOFREEZE;
//让线程继承init进程,从而不会使用用户进程资源
daemonize("mmcqd");
complete(&mq->thread_complete); //设置线程完成时的回调函数
down(&mq->thread_sem);
add_wait_queue(&mq->thread_wq, &wait); //加线程到等待队列
do {
struct request *req = NULL;
spin_lock_irq(q->queue_lock);
set_current_state(TASK_INTERRUPTIBLE);
if (!blk_queue_plugged(q)) //如果队列是非堵塞状态,得到下一个请求
mq->req = req = elv_next_request(q);
spin_unlock_irq(q->queue_lock);
if (!req) {//如果请求为空
if (mq->flags & MMC_QUEUE_EXIT)
break;
up(&mq->thread_sem);
schedule();
down(&mq->thread_sem);
continue;
}
set_current_state(TASK_RUNNING);
//这里调用了mmc_blk_issue_rq开始处理请求
mq->issue_fn(mq, req);
} while (1);
remove_wait_queue(&mq->thread_wq, &wait);
up(&mq->thread_sem);
//调用请求处理完后的回调函数
complete_and_exit(&mq->thread_complete, 0);
return 0;
}
函数mmc_blk_issue_rq初始化MMC块请求结构后,向卡发出请求命令,并等待请求的完成,函数分析如下:
static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
{
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
int ret;
//认领控制器,发命令到卡设置card为选中状态
if (mmc_card_claim_host(card))
goto cmd_err;
do {
struct mmc_blk_request brq;
struct mmc_command cmd;
//初始化MMC块请求结构
memset(&brq, 0, sizeof(struct mmc_blk_request));
brq.mrq.cmd = &brq.cmd;
brq.mrq.data = &brq.data;
brq.cmd.arg = req->sector << 9;
brq.cmd.flags = MMC_RSP_R1;
brq.data.timeout_ns = card->csd.tacc_ns * 10;
brq.data.timeout_clks = card->csd.tacc_clks * 10;
brq.data.blksz_bits = md->block_bits;
brq.data.blocks = req->nr_sectors >> (md->block_bits - 9);
brq.stop.opcode = MMC_STOP_TRANSMISSION;
brq.stop.arg = 0;
brq.stop.flags = MMC_RSP_R1B;
if (rq_data_dir(req) == READ) {//读请求
brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK;
brq.data.flags |= MMC_DATA_READ;
} else {//写
brq.cmd.opcode = MMC_WRITE_BLOCK;
brq.cmd.flags = MMC_RSP_R1B;
brq.data.flags |= MMC_DATA_WRITE;
brq.data.blocks = 1;
}
brq.mrq.stop = brq.data.blocks > 1 ? &brq.stop : NULL;
brq.data.sg = mq->sg;
brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg);
//等待请求完成
mmc_wait_for_req(card->host, &brq.mrq);
……
do {
int err;
cmd.opcode = MMC_SEND_STATUS;
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1;
err = mmc_wait_for_cmd(card->host, &cmd, 5);
if (err) {
printk(KERN_ERR "%s: error %d requesting status\n",
req->rq_disk->disk_name, err);
goto cmd_err;
}
} while (!(cmd.resp[0] & R1_READY_FOR_DATA));
//一个块被成功传输
spin_lock_irq(&md->lock);
ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered);
if (!ret) {
//整个请求完全成功完成
add_disk_randomness(req->rq_disk);
blkdev_dequeue_request(req);//从队列中删除请求
end_that_request_last(req);//写一些更新信息
}
spin_unlock_irq(&md->lock);
} while (ret);
mmc_card_release_host(card);
return 1;
cmd_err:
mmc_card_release_host(card);
spin_lock_irq(&md->lock);
do {
//结束请求req上的I/O,操作成功时返回0
ret = end_that_request_chunk(req, 0,
req->current_nr_sectors << 9);
} while (ret);
add_disk_randomness(req->rq_disk);
blkdev_dequeue_request(req);
end_that_request_last(req);
spin_unlock_irq(&md->lock);
return 0;
}
函数mmc_card_claim_host发出命令选择这个卡card,函数列出如下(在 include/linuc/mmc/card.h中):
static inline int mmc_card_claim_host(struct mmc_card *card)
{
return __mmc_claim_host(card->host, card);
}
函数__mmc_claim_host专有地认领一个控制器,参数host是认领的mmc控制器,参数card是去认领控制器的卡。函数__mmc_claim_host为一套操作认领一个控制器,如果card是被传递的一个有效的卡,并且它不是上次被选择的卡,那么在函数返回之前发出命令选择这个卡card。
函数__mmc_claim_host分析如下(在drivers/mmc/mmc.c中):
int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card)
{
DECLARE_WAITQUEUE(wait, current);//给当前进程声明一个等待队列
unsigned long flags;
int err = 0;
add_wait_queue(&host->wq, &wait);//加host->wq到等待队列中
spin_lock_irqsave(&host->lock, flags);
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);//设置当前进程不可中断状态
if (host->card_busy == NULL) //如果没有忙的卡,跳出循环
break;
spin_unlock_irqrestore(&host->lock, flags);
schedule(); //如果有忙的卡,去调度执行
spin_lock_irqsave(&host->lock, flags);
}
set_current_state(TASK_RUNNING);//设置当前进程为运行状态
host->card_busy = card; //指定当前忙的卡
spin_unlock_irqrestore(&host->lock, flags);
remove_wait_queue(&host->wq, &wait); //从等待队列中移去host->wq
//如果卡不是选择状态,发出命令到卡设置为选择状态
if (card != (void *)-1 && host->card_selected != card) {
struct mmc_command cmd;
host->card_selected = card;
cmd.opcode = MMC_SELECT_CARD;
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1;
//等待命令完成
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
}
return err;
}
函数mmc_wait_for_req开始执行一个请求并等待请求完成,函数分析如下(在drivers/mmc/mmc.c中):
int mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
{
DECLARE_COMPLETION(complete);
mrq->done_data = &complete;
mrq->done = mmc_wait_done;
mmc_start_request(host, mrq);
wait_for_completion(&complete);
return 0;
}
函数mmc_start_request开始排队执行一个在控制器上的命令,参数host是执行命令的控制器,参数mrq是将要开始执行的请求。调用者应持有锁并且关中断。
函数mmc_start_request分析如下(在drivers/mmc/mmc.c中):
void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
DBG("MMC: starting cmd %02x arg %08x flags %08x\n",
mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags);
WARN_ON(host->card_busy == NULL);
mrq->cmd->error = 0;
mrq->cmd->mrq = mrq;
if (mrq->data) {
mrq->cmd->data = mrq->data;
mrq->data->error = 0;
mrq->data->mrq = mrq;
if (mrq->stop) {
mrq->data->stop = mrq->stop;
mrq->stop->error = 0;
mrq->stop->mrq = mrq;
}
}
//调用请求处理函数,对于amba主控制器来说就是mmci_request函数
host->ops->request(host, mrq);
}