hsak stands for "Hybrid Storage Acceleration Kit", which is an IO software stack that provides high bandwidth and low latency for new storage media.
当前访问频次受限,请登录后继续访问
HSAK(Hybrid Storage Acceleration Kit)
介绍
随着NVMe SSD、SCM等存储介质性能不断提升,介质层在IO栈中的时延开销不断缩减,软件栈的开销成为瓶颈,急需重构内核IO数据面,减少软件栈的开销,HSAK针对新型存储介质提供高带宽低时延的IO软件栈,相对传统IO软件栈,软件栈开销降低50%以上。 HSAK用户态IO引擎基于开源的SPDK基础上进行开发:
- 对外提供统一的接口,屏蔽开源接口的差异。
- 在开源基础上新增IO数据面增强特性,如DIF功能,磁盘格式化,IO批量下发,trim特性,动态增删盘等特性
- 提供磁盘设备管理,磁盘IO监测,维测工具等商用特性
编译教程
-
下载hsak源码
$ git clone https://gitee.com/openeuler/hsak.git
-
编译和运行依赖
hsak的编译和运行依赖于spdk、dpdk、libboundscheck等组件
-
编译
$ cd hsak
$ mkdir build
$ cd build
$ cmake ..
$ make
注意事项
运行依赖
权限限制
使用约束
- 同一台机器最多使用和管理512个NVMe设备
- 启用HSAK执行IO相关业务时,需要确保系统有至少500M以上连续的空闲大页内存
- 启用用户态IO组件执行相关业务时,需要确保硬盘管理组件(ublock)已经启用
- 启用磁盘管理组件(ublock)执行相关业务时,需确保系统有足够的连续空闲内存,每次初始化ublock组件会申请20MB大页内存
- 每次运行HSAK之前,产品需要调用setup.sh来配置打野,解绑NVMe设备内核态驱动。
- 执行libstorage_init_module成功后方可使用HSAK模块提供的其他接口;每个进程仅能执行一次libstorage_init_module调用
- 执行libstorage_exit_module函数之后不能再使用HSAK提供的其他接口,再多线程场景特别需要注意,在所有线程结束之后再退出HSAK。
- HSAK ublock组件在一个服务器上只能启动一个服务,且最大支持64个ublock客户端并发访问,ublock服务端处理客户端请求的处理上限是20次/s。
- HSAK ublock组件必须早于数据面IO组件和ublock客户端启动,HSAK提供的命令行工具也必须在ublock服务端启动后才能执行。
- 不要注册SIGBUS信号处理函数;spdk针对该信号有单独的处理函数;若该函数被覆盖,会导致spdk注册的SIGBUS处理函数失效,产生coredump
使用说明
nvme.conf.in配置文件
HSAK配置文件默认安装在/etc/spdk/nvme.conf.in,开发人员可以根据实际业务需要对配置文件进行修改,配置文件内容如下:
- [Global]
- ReactorMask: 指定用于轮询IO的核(16进制,不能指定0核,按bit位从低位到高位,分别表示不同CPU核,如:0x1表示0核,0x6表示1、2两个核,以此类推,本字段最大支持34个字符,去掉表示16进制的0x标记,剩余32个计数字符,每个16进制字符最大是F,可表示4个核,所以最多可以支持32*4=128个核)。
- LogLevel:HSAK日志打印级别(0:error;1:warning;2:notice;3:info;4:debug)。
- MemSize:HSAK占用的内存(最小值为500MB)
- MultiQ:是否在同一个块设备上开启多队列。
- E2eDif:DIF类型(1:半程保护;2:全程保护),不同厂商的硬盘对DIF支持能力可能不同,具体请参考硬件厂家资料。
- IoStat:是否使能IO统计开关(Yes\No)
- RpcServer:是否启动rpc侦听线程(Yes\No)
- NvmeCUSE:是否启动CUSE功能(Yes\No),开启后在/dev/spdk目录下生成nvme字符设备
- [Nvme]
- TransportID:指定NVMe控制器的PCI地址和名称,使用格式为:TransportID "trtype:PCIe traddr:0000:09:00.0" nvme0
- RetryCount:IO失败时的重试次数,0表示不重试,最大255.
- TimeoutUsec:IO超市时间,0或者不配置该配置项表示不设置超时时间,单位是us。
- ActionOnTimeout:IO超时行为(None:仅打印信息;Reset:reset控制器;abort:丢弃超时指令),默认None。
- [Reactor]
- BatchSize:支持批量提交提交IO的个数,默认是8,最大是32.
头文件引用
HSAK提供两个对外头文件,开发者在使用HSAK进行开发时需要包含这两个文件:
- bdev_rw.h:定义了数据面用户态IO操作的宏、枚举、数据结构和接口API。
- ublock.h:定义了管理面设备管理、信息获取等功能的宏、枚举、数据结构和接口API。
业务运行
开发者在进行软件开发编译后,运行前,需要先运行setup.sh脚本程序,用于重新绑定NVMe盘驱动到用户态,该脚本默认安装在:/opt/spdk。 执行如下命令将盘驱动从内核态绑定到用户态,同时预留1024个2M大页:
[root@localhost ~]# cd /opt/spdk
[root@localhost spdk]# ./setup.sh
0000:3f:00.0 (8086 2701): nvme -> uio_pci_generic
0000:40:00.0 (8086 2701): nvme -> uio_pci_generic
执行如下命令将盘驱动从用户态恢复到内核态,同时释放预留的大页:
[root@localhost ~]# cd /opt/spdk
[root@localhost spdk]# ./setup.sh reset
0000:3f:00.0 (8086 2701): uio_pci_generic -> nvme
0000:40:00.0 (8086 2701): uio_pci_generic -> nvme
用户态IO读写场景
开发者通过以下顺序调用HSAK接口,实现经由用户态IO通道的业务数据读写:
-
初始化HSAK UIO模块 可调用接口libstorage_init_module,完成HSAK用户态IO通道的初始化。
-
打开磁盘块设备 可调用libstorage_open,打开指定块设备,如需打开多个块设备,需要多次重复调用。
-
申请IO内存 可调用接口libstorage_alloc_io_buf或libstorage_mem_reserve,前者最大可申请单个65K的IO,后者没有限制(除非无可用空间)。
-
对磁盘进行读写操作 根据实际业务需要,可调用如下接口进行读写操作:
- libstorage_async_read
- libstorage_async_readv
- libstorage_async_write
- libstorage_async_writev
- libstorage_sync_read
- libstorage_sync_write
-
释放IO内存 可调用接口libstorage_free_io_buf或libstorage_mem_free,需要与申请时调用的接口对应。
-
关闭磁盘块设备 可调用接口libstorage_close,关闭指定块设备,如果打开了多个块设备,则需要多次重复调用接口进行关闭。
接口名称 功能描述 libstorage_init_module HSAK模块初始化接口 libstorage_open 打开块设备 libstorage_alloc_io_buf 从SPDK的buf_small_pool或者buf_large_pool中分配内存。 libstorage_mem_reserve 从DPDK预留的大页内存中分配内存空间 libstorage_async_read HSAK下发异步IO读请求的接口(读缓冲区为连续buffer)。 libstorage_async_readv HSAK下发异步IO读请求的接口(读缓冲区为离散buffer)。 libstorage_async_write HSAK下发异步IO写请求的接口(写缓冲区为连续buffer)。 libstorage_async_wrtiev HSAK下发异步IO写请求的接口(写缓冲区为离散buff)。 libstorage_sync_read HSAK下发同步IO读请求的接口(读缓冲区为连续buffer)。 libstorage_sync_write HSAK下发同步IO写请求的接口(写缓冲区为连续buffer)。 libstorage_free_io_buf 释放所分配的内存到SPDK的buf_small_pool或者buf_large_pool中 libstorage_mem_free 释放libstorage_mem_reserve所申请的内存空间。 libstorage_close 关闭块设备。 libstorage_exit_module HSAK模块退出接口
盘管理场景
HSAK包含一组C接口,可以对盘进行格式化、创建、删除namespace操作。
-
首先需要调用C接口对HSAK UIO组件进行初始化,如果已经初始化过了,就不需要再调用了。
libstorage_init_module
-
根据业务需要,调用相应的接口进行盘操作,以下接口可单独调用:
-
libstorage_create_namespace
-
libstorage_delete_namespace
-
libstorage_delete_all_namespace
-
libstorage_nvme_create_ctrlr
-
libstorage_nvme_delete_ctrlr
-
libstorage_nvme_reload_ctrlr
-
libstorage_low_level_format_nvm
-
libstorage_deallocate_block
-
-
最后如果退出程序,则需要销毁HSAK UIO,如果还有其他业务在使用,不需要退出,则不用销毁。
libstorage_exit_module
接口名称 功能描述 libstorage_create_namespace 在指定控制器上创建namespace(前提是控制器具有namespace管理能力) libstorage_delete_namespace 在指定控制器上删除namespace。 libstorage_delete_all_namespace 删除指定控制器上所有namespace。 libstorage_nvme_create_ctrlr 根据PCI地址创建NVMe控制器 libstorage_nvme_delete_ctrlr 根据控制器名称销毁NVMe控制器 libstorage_nvme_reload_ctrlr 根据传入的配置文件自动穿件或销毁NVMe控制器 libstorage_low_level_format_nvm 低级格式化NVMe盘 libstorage_deallocate_block 告知NVMe盘可释放的块,用于垃圾回收
数据面盘信息查询
在HSAK的IO数据面提供一组C接口,用于查询盘信息,上层业务可根据查询到的信息进行相关的业务逻辑处理。
-
首先需要调用C接口对HSAK UIO进行初始化,如果已经初始化过了,就不需要再调用了。
libstorage_init_module
-
根据业务需要,调用相应接口进行信息查询,以下接口可单独调用:
-
libstorage_get_nvme_ctrlr_info
-
libstorage_get_mgr_info_by_esn
-
libstorage_get_mgr_smart_by_esn
-
libstorage_get_bdev_ns_info
-
libstorage_get_ctrl_ns_info
-
-
最后如果退出程序,则需要销毁HSAK UIO,如果还有其他业务在使用,不需要退出,则不用销毁
libstorage_exit_module
接口名称 功能描述 libstorage_get_nvme_ctrlr_info 获取所有控制器信息。 libstorage_get_mgr_info_by_esn 数据面获取设备序列号(ESN)对应的磁盘的管理信息。 libstorage_get_mgr_smart_by_esn 数据面获取设备序列号(ESN)对应的磁盘的SMART信息。 libstorage_get_bdev_ns_info 根据设备名称,获取namespace信息。 libstorage_get_ctrl_ns_info 根据控制器名称,获取所有namespace信息。
管理面盘信息查询场景
在HSAK的管理面组件ublock提供一组C接口,用于支持在管理面对盘信息进行查询。
-
首先调用C接口对HSAK ublock服务端进行初始化
-
根据业务需要,在另一个进程中调用HSAK UIO组件初始化接口。
-
如果需要多个进程查询盘信息,则初始化ublock客户端
-
可在ublock服务端进程或客户端进程调用如下接口进行相应的信息查询业务:
-
对于块设备列表,在获取相应信息后需要调用以下接口进行资源释放:
-
最后如果退出程序,则需要销毁HSAK ublock模块(服务端和客户端销毁方法相同)。
接口名称 功能描述 init_ublock 初始化ublock功能模块,本接口必须在其他所有ublock接口之前被调用,同一个进程只能初始化一次,原因是init_ublock接口中会初始化DPDK,而DPDK初始胡所分配的内存同进程PID绑定,一个PID只能绑定一块内存,且DPDK没有提供释放这块内存的接口,只能通过进程退出来释放。 ublock_init 本身是对init_ublock接口的宏定义,可理解为将ublock初始化为需要RPC服务。 ublock_init_norpc 本身是对init_ublock接口的宏定义,可理解为ublock初始化为无RPC服务。 ublock_get_bdevs 业务进程通过调用本接口获取设备列表,获取的设备列表中只有PCI地址,不包含具体设备信息,需要获取具体设备信息,请调用接口ublock_get_bdev。 ublock_get_bdev 进程通过调用本接口获取具体某个设备的信息,设备信息中包括:设备的序列号、型号、fw版本号信息以字符数组形式保持,不是字符串形式。 ublock_get_bdev_by_esn 进程通过调用该接口,根据给定的ESN号获取对应设备的信息,设备信息中:序列号、型号、fw版本号。 ublock_get_SMART_info 进程通过调用本接口获取指定设备的SMART信息。 ublock_get_SMART_info_by_esn 进程通过调用本接口获取ESN号对应设备的SMART信息。 ublock_get_error_log_info 进程通过调用本接口获取设备的Error log信息。 ublock_get_log_page 进程通过调用本接口获取指定设备,指定log page的信息。 ublock_free_bdevs 进程通过调用本接口释放设备列表。 ublock_free_bdev 进程通过调用本接口释放设备资源。 ublock_fini 销毁ublock功能模块,本接口将销毁ublock模块以及内部创建的资源,本接口同ublock初始化接口需要配对使用。
日志管理
HSAK的日志当前是通过syslog默认输出到/var/log/messages中,由操作系统的rsyslog服务管理。如果产品需要自定义日志目录,可以通过rsyslog配置。
-
首先需要在配置文件/etc/rsyslog.conf中增加如下修改:
-
重启rsyslog服务:
if ($programname == 'LibStorage') then { action(type="omfile" fileCreateMode="0600" file="/var/log/HSAK/run.log") stop } -
启动HSAK进程,日志信息即重定向到对应目录。
sysemctl restart rsyslog -
重定向日志如果需要转储,需要用户在/etc/logrotate.d/syslog文件中手动配置。
命令行接口
盘信息查询命令
命令格式
libstorage-list [<commands>] [<device>]
参数说明
-
commands: 只有“help”可选,“libstorage-list help”用于显示帮助内容。
-
device: 指定PCI地址,格式如:0000:09:00.0,允许同时多个,中间用空格隔离,如果不设置具体的PCI地址,则命令行列出所有枚举到的设备信息。
注意事项
-
故障注入功能仅限于开发、调试以及测试场景使用,禁止在用户现网使用,否则会引起业务及安全风险。
-
在执行本命令时,管理组件(ublock)服务端必须已经启动,用户态IO组件(uio)未启动或已正确启动均可。
-
对于未被ublock组件和用户态IO组件占用的盘,在本命令执行过程中会被占用,此时如果ublock组件或用户态IO组件尝试获取盘控制权,可能存储设备访问冲突,导致失败。
盘切换驱动命令
命令格式
libstorage-shutdown reset <device> [<device2> ...]
参数说明
-
reset: 用于对指定盘从uio驱动切换到内核态驱动;
-
device: 指定PCI地址,格式如:0000:09:00.0,允许同时多个,中间用空格隔离。
注意事项
-
libstorage-shutdown reset命令用于将盘从用户态uio驱动切换到内核态nvme驱动。
-
在执行本命令时,管理组件(ublock)服务端必须已经启动,用户态IO组件未启动或已正确启动均可。
-
libstoage-shutdown reset命令为危险动作,请确认在切换nvme设备驱动之前,用户态实例已经停止对nvme设备下发IO,nvme设备上的fd已全部关闭,且访问nvme设备的实例已退出。
获取IO统计数据命令
命令格式
libstorage-iostat [-t <interval>] [-i <count>] [-d <device1,device2,...>]
参数说明
-
-t: 时间间隔,以秒为单位,最小1秒,最大为3600秒。该参数为int型,如果入参值超过int型上限,将被截断成负数或者正数。
-
-i: 收集次数,最小为1,最大为MAX_INT次,如果不设置,默认以时间间隔持续收集。该参数为int型,如果入参超过int型上限,将被截断成负数或者正数。
-
-d:指定块设备名称(eg:nvme0n1,其依赖于/etc/spdk/nvme.conf.in中配置的控制器名称),可以通过本参数收集指定一个或多个设备性能数据,如果不设置本参数,则收集所有识别到的设备性能数据。
注意事项
-
IO统计配置项已使能。
-
进程已经通过用户态IO组件对所需要查询性能信息的盘下发IO操作。
-
如果当前环境上没有任何设备被业务进程占用下发IO,则该命令将在提示:You cannot get iostat info for nvme device no deliver io后退出。
-
在磁盘打开多队列情况下,IO统计工具将该磁盘上多队列的性能数据汇总后统一输出。
-
IO统计工具最多支持8192个磁盘队列的数据记录。
-
IO统计数据输出结果如下:
Device r/s w/s rKB/s wKB/s avgrq-sz avgqu-sz r_await w_await await svctm util% poll-n 设备名称 每秒读IO个数 每秒写IO个数 每秒读IO字节 每秒写IO字节 平均下发IO大小(字节) 磁盘排队的IO深度 IO读时延(us) IO写时延(us) 读写平均时延(us) 单个IO处理时延(us) 设备利用率 轮询超时次数
盘读写命令
命令格式
libstorage-rw <COMMAND> <device> [OPTIONS...]
参数说明
-
COMMAND参数
-
read,从设备读取指定的逻辑块到数据缓存区(默认是标准输出)。
-
write,将数据缓存区(默认是标准输入)的数据写入到NVMe设备的指定逻辑块。
-
help,显示该命令行的帮助信息。
-
-
device: 指定PCI地址,格式如:0000:09:00.0。
-
OPTIONS参数
-
--start-block,-s:读写逻辑块的64位首地址(默认值为0)。
-
--block-count,-c:读写逻辑块的数量(从0开始计数)。
-
--data-size,-z:读写数据的字节数。
-
--namespae-id,-n:设备的namespace id(默认id值是1)。
-
--data,-d:读写用的数据文件(读时保存读出的数据,写时提供写入数据)。
-
--limited-retry,-l:设备控制器进行有限次数的重启来完成设备读写。
-
--force-unit-access,-f:确保指令完成之前从非易失介质中完成读写。
-
--show-command,-v:发送读写命令之前显示指令相关信息。
-
--dry-run,-w:仅显示读写指令相关信息,不进行实际读写操作。
-
--latency,-t:统计命令行端到端读写的时延。
-
--help,-h:显示相关命令的帮助信息。
-
参与贡献
- Fork本仓库
- 新建个人分支
- 提交代码
- 新建Pull Request