目录
0.前言
内核版本:4.19
文档目的: 主要以null_dev为例来研究多队列的工作机制, 为了能够凸显多队列的工作流程,只对block层做重点分析说明,对其它代码只做注释性说明。
本文主要介绍跟多队列相关的定时器,这些定时器的出处来源于blk_mq_init_queue函数。
目前对这些定时器的分析还处于混乱状态,可能会有很多错误,建议略过:>
1. 多队列的定时器处理函数
laptop_mode_timer_fn
(1)初始化
laptop_mode_wb_timer定时器是在blk_alloc_queue_node时通过timer_setup(&q->backing_dev_info->laptop_mode_wb_timer,laptop_mode_timer_fn)初始化。laptop_mode_wb_timer内嵌在q->backing_dev_info中, 此定时器主要服务于laptop_mode,定时器回调将唤醒wb线程最终将数据从page cache写入磁盘
(2)调用定时器
laptop_io_completion启动laptop_io_completion定时器
laptop_sync_completion取消laptop_mode_wb_timer定时器
(3)定时器启动/取消时机
在laptop_mode模式或!blk_rq_is_passthrough(req)条件下:
blk_mq_free_request和blk_finish_request会通过调用laptop_io_completion来启动定时器
在在laptop_mode模式下:
ksys_sync通过调用laptop_sync_completion来取消定时器
(4)定时器处理函数
laptop_mode_timer_fn
|--wakeup_flusher_threads_bdi(backing_dev_info, WB_REASON_LAPTOP_TIMER)
|--__wakeup_flusher_threads_bdi(bdi, reason)
|--list_for_each_entry_rcu(wb, &bdi->wb_list, bdi_node)
wb_start_writeback(wb, reason)
|--wb_wakeup(wb)
|--mod_delayed_work(bdi_wq, &wb->dwork, 0)
|--wb_workfn(struct work_struct *work)
|--do {
wb_do_writeback(wb);
} while (!list_empty(&wb->work_list)
wb_init时会调用INIT_DELAYED_WORK(&wb->dwork, wb_workfn)设置wb->dwork的work回调函数为
wb_workfn;
laptop_mode_timer_fn实际就是为了避免数据丢失,将page cache数据写入磁盘的过程。它主要通过唤醒wb->dwork线程来进行处理wb->work_list队列上的所有work
wb_workfn通过wb_do_writeback将page cache数据写入plug队列,并一级级向下分派
blk_rq_timed_out_timer/blk_mq_timeout_work(TODO)
(1) 初始化
blk_alloc_queue_node时通过timer_setup(&q->timeout, blk_rq_timed_out_timer, 0)进行初始化
blk_mq_init_allocated_queue时通过INIT_WORK(&q->timeout_work, blk_mq_timeout_work);
(2) 调用时机
启动q->timeout:
null_queue_rq
|--blk_mq_start_request(bd->rq)
|--mod_timer(&q->timeout, expiry)
|--blk_rq_timed_out_timer(struct timer_list *t)
|--kblockd_schedule_work(&q->timeout_work)
|--blk_mq_timeout_work(work)
当开始对一个request进行处理时,就会启动一个定时器来检测处理是否超时
blk_mq_requeue_work(TODO)
(1) 初始化
blk_alloc_queue_node时:
INIT_DELAYED_WORK(&q->delay_work, blk_delay_work)进行的初始化
(2) 调用
blk_mq_sched_insert_request
|--if (!(rq->rq_flags & RQF_FLUSH_SEQ) && op_is_flush(rq->cmd_flags))
blk_insert_flush(rq)
|--rq->end_io = flush_data_end_io
|--blk_flush_complete_seq(rq, fq, REQ_FSEQ_ACTIONS & ~policy, 0)
|--blk_kick_flush(q, fq, cmd_flags)
|--flush_rq->end_io = flush_end_io
|--blk_flush_queue_rq(flush_rq, false)
|--if (rq->q->mq_ops)
blk_mq_add_to_requeue_list(rq, add_front, true)
|--blk_mq_kick_requeue_list(q)
|--kblockd_mod_delayed_work_on(WORK_CPU_UNBOUND, &q->requeue_work)
|--blk_mq_requeue_work(work)
|--blk_mq_run_hw_queues(q, false)
blk_delay_work(单队列)
flush_end_io
|-- if (queued || fq->flush_queue_delayed)
blk_run_queue_async(q)
|--if (likely(!blk_queue_stopped(q) && !blk_queue_dead(q)))
mod_delayed_work(kblockd_workqueue, &q->delay_work, 0)
blk_delay_work
|--__blk_run_queue(q)