Linux上传统的块设备层(Block Layer)和IO调度器(如cfq)主要是针对HDD(hard disk drivers)设计的。我们知道,HDD设备的随机IO性能很差,吞吐量大约是几百IOPS(IOs per second),延迟在毫秒级,所以当时IO性能的瓶颈在硬件,而不是内核。但是,随着高速SSD(Solid State Disk)的出现并展现出越来越高的性能,百万级甚至千万级IOPS的数据访问已成为一大趋势,传统的块设备层已无法满足这么高的IOPS需求,逐渐成为系统IO性能的瓶颈。 为了适配现代存设备(高速SSD等)高IOPS、低延迟的IO特征,新的块设备层框架Block multi-queue(blk-mq)应运而生。本文就带大家来了解下Linux 块设备层的blk-mq框架和代码实现。 一、单队列框架和存在的问题 Linux上传统块设备层使用单队列(Single-queue/SQ)架构,如图1所示。简单来说,块设备层负责管理从用户进程到存储设备的IO请求,一方面为上层提供访问不同存储设备的统一接口,隐藏存储设备的复杂性和多样性;另一方面,为存储设备驱动程序提供通用服务,让这些驱动程序以最适合的方式接收来自上层的IO请求。Linux Block Layer主要提供以下几个方面的功能:
![]() 图1. 单队列的Linux block layer设计 图片引用自《Linux Block IO: Introducing Multi-queue SSD Access on Multi-core Systems》 由于采用单队列(每个块设备1个请求队列--Requst Queue)的设计,传统的Block Layer对多核体系的可扩展性(scalability)不佳。当系统配备现代高速存储器件时,单队列引入的软件开销变得突出(在多socket体系中尤为严重),成为IO性能的瓶颈。多核体系中blk-sq的软件开销主要来自三个方面:
![]() 图2. 高IOPS场景下cpu热点数据 图片引用自《High Performance Storage with blk-mq and scsi-mq》
![]() 图3. blk-sq IOPS吞吐量随cpu数量的变化曲线,blk-sq支持的最高吞吐量大概在1MIOPS 图片引用自《Linux Block IO: Introducing Multi-queue SSD Access on Multi-core Systems》 二、多队列框架和解决的问题 针对blk-sq存在的问题,Jens Axboe (Linux内核Block Layer Maintainer)提出了多队列(multi-queue/MQ)的块设备层架构(blk-mq),如图4所示: ![]() 图4. 两层队列的Block Layer设计 图片引用自《Linux Block IO: Introducing Multi-queue SSD Access on Multi-core Systems》 blk-mq中使用了两层队列,将单个请求队列锁的竞争分散多个队列中,极大的提高了Block Layer并发处理IO的能力。两层队列的设计分工明确:
MQ架构解决了SQ架构中请求队列锁竞争和远端内存访问问题,极大的提高了Block Layer的IOPS吞吐量。从图5中,我们可以看到Linux 3.17-rc3 中scsi-mq+blk-mq,与图2相同的高IOPS场景下仅3%的cpu时间耗费在锁获取上。 ![]() 图5. scsi-mq_+blk-mq高IOPS场景下cpu热点数据 图片引用自《High Performance Storage with blk-mq and scsi-mq》 ![]() 图6. IOPS吞吐量随cpu数量的变化曲线,blk-mq更加接近raw设备的性能 图片引用自《Linux Block IO: Introducing Multi-queue SSD Access on Multi-core Systems》
|