... ... }
struct block_device {
dev_t bd_dev; /* it's a search key */ struct inode * bd_inode; /* will die */ struct super_block * bd_super; ... ... };
与字符驱动类似,通过dev_t和inode找到设备。 3) 注册驱动
void add_disk(struct gendisk *disk) {
blk_register_region(disk_devt(disk), disk->minors, NULL, exact_match, exact_lock, disk); register_disk(disk); blk_register_queue(disk); ... ... }
blk_register_region()在linux中实现了一种利用哈希表管理设备号的机制。 register_disk()对应alloc_disk(),完成对块设备的注册,其实质是通过
register_disk()->blkdev_get()->__blkdev_get()->rescan_partitions()->add_partitions()添加
分区,建立设备节点。
blk_register_queue()对应blk_init_queue()完成对请求队列的注册,其实质是通过elv_register_queue()注册请求队列的算法。
关于块设备更为具体的代码分析可参看linux那些事。 4.2.2 请求队列
mmc_init_queue申请并初始化一个请求队列,开启负责处理这个请求队列的守护进程。
int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock) {
struct mmc_host *host = card->host; mq->card = card;
mq->queue = blk_init_queue(mmc_request, lock); mq->queue->queuedata = mq; mq->req = NULL;
blk_queue_prep_rq(mq->queue, mmc_prep_request); // 注册mmc_prep_request算法 blk_queue_ordered(mq->queue, QUEUE_ORDERED_DRAIN, NULL); //注册ordered算法
mq->thread = kthread_run(mmc_queue_thread, mq, \ ... ... }
1) mmc_request
它是处理SD卡通用的申请请求的回调函数,或者说是SD卡申请请求的算法。当CPU处于not busy状态,会寻找一个请求,并试图执行它。 /* /drivers/mmc/card/queue.c */ /*
* Generic MMC request handler. This is called for any queue on a * particular host. When the host is not busy, we look for a request * on any queue on this host, and attempt to issue it. This may * not be the queue we were asked to process. */
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 req->cmd_flags |= REQ_QUIET;
__blk_end_request_all(req, -EIO); } return; }
if (!mq->req)
wake_up_process(mq->thread); // 如果队列里没有请求req,唤醒守护进程 }
这里我们需要关注这个处理该SD卡请求队列的算法是何时申请的,也就是何时会去申请请求,何时会去唤醒内核线程。 用到回调函数q->request_fn有三处
? ? ?
块设备驱动注册请求队列blk_register_queue() 驱动程序出错,清空请求队列mmc_cleanup_queue() 实现请求队列机制的blk_fetch_request内部本身
blk_fetch_request()->blk_peek_request()->__elv_next_request()->blk_do_ordered()->...->q->request_fn
我们不必深究所谓的电梯算法,只要知道,它是使数据得以高效通信的一种算法,算法自身决定何时去唤醒守护进程处理请求。 2) blk_init_queue()
如果一个块设备希望使用一个标准的请求处理步骤,那就必须使用blk_init_queue()。这个函数注册了q->request_fn(这里就是mmc_request),并初始化请求队列的数据结构struct request_queue。 /*
* call blk_init_queue(). The function @rfn will be called when there * are requests on the queue that need to be processed. If the device * supports plugging, then @rfn may not be called immediately when requests * are available on the queue, but may be called at some time later instead. */
struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock) {
return blk_init_queue_node(rfn, lock, -1); }
其中的rfn就是请求队列的一个算法,即这里的mmc_request。
struct request_queue *blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id) {
struct request_queue *q = blk_alloc_queue_node(GFP_KERNEL, node_id); q->request_fn = rfn; q->prep_rq_fn = NULL;
q->unplug_fn = generic_unplug_device; q->queue_flags = QUEUE_FLAG_DEFAULT; q->queue_lock = lock;
blk_queue_make_request(q, __make_request); if (!elevator_init(q, NULL)) {
blk_queue_congestion_threshold(q); return q; } ... ... }
3) kthead_run()
注意到mmc_init_queue这个函数的最后,创建并运行一个名为mmcqd的线程,顾名思意,mmc queue deamon它是一个SD卡的处理请求队列的守护进程,或者说内核线程,当系统注册SD卡块设备驱动时,就通过mmc_init_queue()开启了这个内核线程。 4) mmc_queue_thread 看看这个内核线程做了些什么,
相关推荐: