本文转载自微信公众号《遇见Linux》,作者JeffXie。转载本文请联系邂逅Linux公众号。Linux块设备的多队列机制是在Linux3.13引入的。最初引入多队列时,多队列和单队列并存。如果要研究multi-cohorts,最靠谱的方法当然是在原始patch中研究。补丁原代码:git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git分支:linux-block/v3.10-blk-mq首先看multi-队列架构:以读IO为例,单队列和多队列执行路径相同:read_pages(){...blk_start_plug()/*进程准备存流*/mapping->a_ops->readpages()/*storestream*/blk_finish_plug()/*进程开始泄漏*/...}io_schedule()进程等待io完成后累加(blk_mq_make_request()函数中的请求数大于等于到16request_count>=BLK_MAX_REQUEST_COUNT不需要调用io_schedule(),直接泄漏到块设备驱动)mapping->a_ops->readpages()会一直调用q->make_request_fn()generic_make_request()q->make_request_fn()调用blk_queue_bio()ormulti-queueblk_queue_make_request()__elv_add_request()为什么要引入multipleQueue:multi-queue相对于singlequeue,有一个软队列(以blk_mq为代表)_ctx结构)在每个CPU上避免插入请求时使用spinlock锁,而现在的高速存储设备,比如支持nvme的ssd(小弟刚买了一个,速度真快),访问延迟很小,并且硬件本身支持多队列,(引入的多队列使用每个硬件队列hctx->delayed_work代替request_queue->delay_work)之前的单队列架构已经不能压榨它的性能,成为它的负担。当单个队列插入请求并泄漏到块设备驱动程序时,它总是在request_queue上有一个全局自旋锁,这让人想直接绕过块层。当单个队列插入请求时,会使用request_queue上的全局自旋锁blk_queue_bio(){...spin_lock_irq(q->queue_lock);elv_merge()spin_lock_irq(q->queue_lock);...}的singlequeueleakstoblockdevice驱动时也使用了request_queue上的全局spinlock锁:structrequest_queue*blk_alloc_queue_node()INIT_DELAYED_WORK(&q->delay_work,blk_delay_work);blk_delay_work()__blk_run_queue()q->request_fn(q);__blk_run_queue()函数必须在队列锁中,也就是spin_lock_irq(q->queue_lock);281*__blk_run_queue-runasingledevicequeue282*@q:Thequeuetorun283*284*说明:285*See@blk_run_queue.Thisvariantmustbecalledwiththequeuelock286*heldandinterruptsdisabled.287*/288void__blk_run_queue(structrequest_queue*q)289{290if(unlikely(blk_queue_stopped(q)))291return;292293__blk_run_queue_uncond不使用自旋队列锁请求(q);294:blk_mq_insert_requests()__blk_mq_insert_request()structblk_mq_ctx*ctx=rq->mq_qt_addx;->queuelist,&ctx->rq_list)多队列泄漏到块设备驱动不使用自旋锁锁:staticintblk_mq_init_hw_queues()INIT_DELAYED_WORK(&hctx->delayed_work,blk_mq_work_fn);708staticvoidblk_mq_work_fn(structwork_struct*work)709{710structblk_mq_hw_ctx*hctx;711712hctx=container_of(work,structblk_mq_hw_ctx,delayed_work.work);713__blk_mq_run_hw_queue(hctx);714}__blk_mq_run_hw_queue()没有spinlock锁q->mq_ops->queue_rq(hctx,rq);在多个队列上执行->queue_rq()回调函数。从下图我们可以看出系统使用多队列后的性能提升:(性能我自己没有测试过,应该是客观想象的,对应下图:))
