spdk探秘-----块设备开发指导

2023-11-04

这里的块设备是一种存储设备,它支持在固定大小的块中读写数据。这些块通常是512或4096字节。这些设备可能是软件中的逻辑结构,或者对应于像NVMe ssd这样的物理设备。

通用库的公共头文件是bdev.h,它是与任何类型的块设备交互所需的全部API。

除了为所有块设备提供一个通用的抽象之外,bdev层还提供了许多有用的特性:

1、I/O请求自动排队,以响应队列满或内存不足的情况

2、热删除支持,即使有I/O流量时。

3、I/O统计信息,如带宽和延迟

4、设备重启和I/O超时跟踪

struct spdk_bdev(bdev)表示一个通用块设备。struct spdk_bdev_desc,称为描述符,表示给定块设备的句柄。描述符用于建立和跟踪使用底层块设备的权限,非常文件描述符。对块设备的请求是异步的,并由spdk_bdev_io对象表示。请求必须在关联的I/O通道上提交。将从消息传递和并发两个方面阐述了I/O通道的设计和动机。

Bdevs可以分层,这样一些Bdevs通过将请求路由到其他Bdevs来服务I/O。这可以用于实现缓存、RAID、逻辑卷管理等。将I/O路由到其他bdev的bdev通常称为虚拟bdev,或简称vbdev。

初始化库

bdev层依赖于头文件include/spdk/thread.h提供的通用消息传递基础设施。最重要的是,只能通过spdk_thread_create()分配的线程才能调用bdev库。

在分配的线程中,可以通过调用spdk_bdev_initialize()来初始化bdev库,这是一个异步操作。在调用完成回调之前,不能调用其他bdev库函数。相反的要卸载bdev库,可以调用spdk_bdev_finish()。

发现块设备

所有块设备都有一个简单的字符串名称。在任何时候都可以通过调用spdk_bdev_get_by_name()获得指向设备对象的指针,也可以使用spdk_bdev_first()和spdk_bdev_next()迭代整个bdevs集合。

一些块设备也可能被赋予别名,这也是字符串名称。别名的行为类似符号链接——它们可以与真名互换使用,以查找块设备。

块设备准备

为了向块设备发送I/O请求,必须首先通过调用spdk_bdev_open_ext()来打开它。这将返回一个描述符。多个用户可能同时打开一个bdev,用户之间读写的协调必须由bdev层之上的机制来处理。如果虚拟bdev模块claimed了bdevA,那么使用写权限打开bdevA可能会失败。因为虚拟bdev模块实现像RAID或逻辑卷管理这样的逻辑,并将它们的I/O转发给较低级别的bdevA,因此它们将这些较低级别的bdevA标记为claimed的,以防止外部用户发出写操作。

当块设备打开时,必须提供回调和上下文,当bdev触发异步事件(如bdev删除)时,将使用适当的spdk_bdev_event_type enum作为参数调用它们。例如,当NVMe SSD热拔时,将对物理NVMe SSD支持的bdev的每个打开的描述符调用回调。在这种情况下,可以将回调看作是关闭描述符的请求,以便释放其他内存。当描述符存打开时不能卸载bdev,因此需要提供回调。

当用户使用完描述符时,他们可以通过调用spdk_bdev_close()来释放它。

描述符可以同时传递给多个线程并在多个线程使用。但是,对于每个线程,必须通过调用spdk_bdev_get_io_channel()来获得一个单独的I/O通道。这将为每个线程分配必要的资源,以便在不获取锁的情况下向bdev提交I/O请求。要释放一个通道,请调用spdk_put_io_channel()。在所有相关联的通道被销毁之前,描述符不能被关闭。

发送I / O

一旦获得了描述符和通道,就可以通过调用各种I/O提交函数(如spdk_bdev_read())来发送I/O。这些调用都接受一个回调函数作为参数,在后面会使用spdk_bdev_io对象的句柄来调用该回调函数。为了响应这个完成,用户必须调用spdk_bdev_free_io()来释放资源。在这个回调中,用户还可以使用函数spdk_bdev_io_get_nvme_status()和spdk_bdev_io_get_scsi_status()来获取他们选择的格式的错误信息。

I/O提交是通过调用spdk_bdev_read()或spdk_bdev_write()等函数来执行的。存储需要写入device的数据的内存必须通过spdk_dma_malloc()或它的变体来分配。在用户空间的直接内存访问(DMA)中会介绍为什么必须使用DMA来分配。在一般情况下,内存中的数据将被直接传输到块设备。这个过程零拷贝。

所有的I/O提交函数都是异步的和非阻塞的。它们不会因为任何原因阻塞或停止线程。但是,I/O提交函数可能有两种失败。首先,它们可能会立即失败并返回一个错误代码。在这种情况下,提供的回调将不会被调用。其次,它们可能异步失败。在这种情况下,相关的spdk_bdev_io将被传递给回调函数,它将报告错误信息。

有些I/O请求类型是可选的,可能不被给定的bdev支持。要查询bdev支持的I/O请求类型,请调用spdk_bdev_io_type_supported()。

重置块设备

为了处理意外的故障条件,bdev库提供了一种通过调用spdk_bdev_reset()来重置的机制。这将向bdev存在I/O通道的每个其他线程传递消息,暂停它,然后将一个复位请求转发给底层bdev模块,并等待完成。完成后,I/O通道将恢复,重置将完成。bdev模块中的特定行为是特定于模块的。例如,NVMe设备将删除所有队列对,执行一个NVMe重置,然后重新创建队列对并继续。最重要的是,无论设备类型是什么,对块设备的所有I/O都将在重置完成之前完成。

编写自定义块设备模块

这个指南是为编写自己的块设备模块以与SPDK的bdev层集成的开发人员准备的。

在SPDK中,块设备模块相当于传统操作系统中的设备驱动程序。模块提供了一组函数指针,用于服务块设备I/O请求。SPDK提供了许多块设备模块,包括NVMe、RAM-disk和Ceph RBD。但是,有些用户希望编写自己的存储,以便与自定义硬件或现有的存储软件堆栈交互。本指南旨在准确地演示如何编写模块。

创建新模块

块设备模块现在位于lib/bdev的子目录中。目前还不能将bdev模块的代码放在其他地方。要创建模块,添加一个带有单个C文件和Makefile的新目录。一个很好的途径是复制现有的“null”bdev模块。

bdev模块将与之交互的主要接口在include/spdk/bdev_module.h中。在这个头文件中定义了一个宏SPDK_BDEV_MODULE_REGISTER来注册一个新的bdev模块。这个宏采用一个指针spdk_bdev_module结构作为参数,该结构用于注册新的bdev模块。spdk_bdev_module结构描述了模块属性初始化(module_init)和卸载(module_fini)函数。可以查看struct spdk_bdev_module的文档了解更多细节。

创建Bdevs

通过调用spdk_bdev_register()在模块中创建新的bdev。新模块必须分配struct spdk_bdev,并初始化,并将它传递给寄存器调用。要初始化的最重要的字段是fn_table,它指向这个数据结构:

/*
* Function table for a block device backend.
*
* The backend block device function table provides a set of APIs to allow
* communication with a backend. The main commands are read/write API
* calls for I/O via submit_request.
*/
struct spdk_bdev_fn_table {
/* Destroy the backend block device object */
int (*destruct)(void *ctx);
/* Process the IO. */
void (*submit_request)(struct spdk_io_channel *ch, struct spdk_bdev_io *);
/* Check if the block device supports a specific I/O type. */
bool (*io_type_supported)(void *ctx, enum spdk_bdev_io_type);
/* Get an I/O channel for the specific bdev for the calling thread. */
struct spdk_io_channel *(*get_io_channel)(void *ctx);
/*
* Output driver-specific configuration to a JSON stream. Optional - may be NULL.
*
* The JSON write context will be initialized with an open object, so the bdev
* driver should write a name (based on the driver name) followed by a JSON value
* (most likely another nested object).
*/
int (*dump_config_json)(void *ctx, struct spdk_json_write_ctx *w);
/* Get spin-time per I/O channel in microseconds.
* Optional - may be NULL.
*/
uint64_t (*get_spin_time)(struct spdk_io_channel *ch);
};

bdev模块必须实现这些函数回调。

当系统不再需要该设备时,调用析构函数来拆除该设备。destruct所做的是由模块决定:可能只是释放内存,也可能是关闭一块硬件。

io_type_supported函数返回是否支持特定的I/O类型。可用的I/O类型有:

enum spdk_bdev_io_type {
SPDK_BDEV_IO_TYPE_INVALID = 0,
SPDK_BDEV_IO_TYPE_READ,
SPDK_BDEV_IO_TYPE_WRITE,
SPDK_BDEV_IO_TYPE_UNMAP,
SPDK_BDEV_IO_TYPE_FLUSH,
SPDK_BDEV_IO_TYPE_RESET,
SPDK_BDEV_IO_TYPE_NVME_ADMIN,
SPDK_BDEV_IO_TYPE_NVME_IO,
SPDK_BDEV_IO_TYPE_NVME_IO_MD,
SPDK_BDEV_IO_TYPE_WRITE_ZEROES,
};

对于最简单的bdev模块,只需要SPDK_BDEV_IO_TYPE_READ和SPDK_BDEV_IO_TYPE_WRITE。SPDK_BDEV_IO_TYPE_UNMAP通常被称为“trim”或“deallocate”,它是一个将一组块标记为不再包含有效数据的请求。SPDK_BDEV_IO_TYPE_FLUSH请求使之前完成的所有写操作具有持久性,许多设备不需要flush。SPDK_BDEV_IO_TYPE_WRITE_ZEROES就像常规的写操作,但是不提供数据缓冲区(它只包含所有的0)。如果不支持它,一般的bdev代码可以通过发送常规的写请求来模拟它。

SPDK_BDEV_IO_TYPE_RESET是一个中止所有I/O并将底层设备返回到初始状态的请求。在以某种方式完成所有I/O之前,不要完成重置请求。

SPDK_BDEV_IO_TYPE_NVME_ADMIN、SPDK_BDEV_IO_TYPE_NVME_IO_MD和SPDK_BDEV_IO_TYPE_NVME_IO_MD都是通过SPDK bdev层传递原始NVMe命令的机制。它们是严格可选的,可能只有在后备存储设备能够处理NVMe命令时才有意义。

get_io_channel函数应该返回一个I/O通道。通用bdev层将每个线程调用一次get_io_channel,缓存结果,并将结果传递给submit_request。它将为调用submit_request的线程使用相应的通道。

submit_request函数被调用来实际地向块设备提交I/O请求。I/O请求完成后,模块必须调用spdk_bdev_io_complete()。I/O不必在submit_request的调用上下文中完成。

创建虚拟Bdevs

如果块设备A通过将I/O路由到其他块设备B来处理I/O请求,则认为A是虚拟的。典型的例子是实现RAID的bdev模块就是个虚拟模块。虚拟bdev的创建方式与常规bdev相同,但需要额外的一个步骤。虚拟模块可以使用spdk_bdev_get_by_name()查找它希望将I/O路由到的底层bdevs,其中字符串名称由用户在配置文件中或通过RPC提供。然后通过打开底层bdev获取描述符,并为bdev创建I/O通道(可能是对get_io_channel回调的响应),模块可以正常运行。最后一步是让虚拟模块用其底层块设备描述符来调用spdk_bdev_module_claim_bdev(),表明该虚拟模块正在使用底层的bdev。这将防止其他用户在该底层模块上打开具有写权限的描述符。这有效地将描述符“提升”为写独占,并且这是一个仅对bdev模块可用的操作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

spdk探秘-----块设备开发指导 的相关文章

  • 非printf形式打印各种数据类型的十六进制和二进制

    转载请标明是引用于 http blog csdn net chenyujing1234 欢迎大家提出意见 一起讨论 一 源码实现 为了适配各种数据类型 且可以通过sizeof得到此类型的大小 所以这里采用模板形式开发 以下是实现此功能的源码
  • 数据库的安全性、完整性、并发控制和恢复

    数据库的安全性 完整性 并发控制和恢复from http bbs chinaunix net viewthread php tid 188100 为了保证数据库数据的安全可靠性和正确有效 DBMS必须提供统一的数据保护功能 数据保护也为数据
  • 浅谈RAID写惩罚(Write Penalty)与解决方案闪存荷尔蒙(FlashHormone)

    浅谈RAID写惩罚 Write Penalty 与解决方案闪存荷尔蒙 FlashHormone 介绍 通常在讨论不同RAID保护类型的性能的时候 结论都会是RAID 1提供比较好的读写性能 RAID 5读性能不错 但是写入性能就不如RAID
  • regionprops函数用法详解

    转自 http apps hi baidu com share detail 24010679 Regionprops 用途是get the properties of region 即用来度量图像区域属性的函数 语法 STATS regi
  • SpringBoot在K8s下实现优雅停机

    在K8s中 当我们实现滚动升级之前 务必要实现应用级别的优雅停机 否则滚动升级时 还是会影响到业务 本文介绍SpringBoot应用实现优雅停机 此次教程基于SpringBoot 2 5 0 1 加入必要依赖
  • ACM PKU 1048 Follow My Logic

    ACM PKU 1048 Follow My Logic 2009 04 02 0 Comments Follow My Logic 题目重述 对于一个逻辑电路和给定的输入值 计算该电路的输出值 该逻辑电路有一个或多个输入端 零个或多个逻辑
  • 单片机实时温度采集并通过串口通信上传电脑显示

    首先说下我所用到的主要器件器件 STC89C52 AT24C08 DS18B20 MAX232 实验源代码 接上18B20温度传感器 另购 后数码管显示出当前温度 include
  • Oracle存储过程总结(一、基本应用)

    1 创建存储过程 create or replace procedure test var name 1 in type var name 2 out type as 声明变量 变量名 变量类型 begin 存储过程的执行体 end tes
  • 竞争条件(race condition)

    在一些操作系统中 协作的进程可能共享一些彼此都能读写的公用存储区 这个公用存储区可能在内存中 可能是在内核数据结构中 也可能是一个共享文件 这里共享存储区的位置并不影响通信的本质及其带来的问题 为了理解实际中进程间通信如何工作 我们考虑一个
  • 全面剖析《自己动手写操作系统》第四章--FAT12文件系统

    一 FAT12 FAT12是DOS时代就开始使用的文件系统 File System 直到现在仍然在软盘上使用 FAT12软盘的被格式化后为 有两个磁头 每个磁头80个柱面 磁道 每个柱面有18个扇区 每个扇区512个字节空间 所以标准软盘的
  • little endian && big-endian

    java 的ClassFile采用big endian存储数据 Intel x86 采用little endian Motorola采用big endian 0x1234 Intel 地址 0x4000 0000 0x34 0x4000 0
  • 索引表简介

    索引表简介 1 索引的内部构造 因为在索引表中涉及到索引的内部构造知识 所以下面会进行简单的介绍 首先 如果没有索引 当你想要去查找某个值的时候 你不得不对数据进行顺序扫描 如果一张表有n行 那么使用顺序扫描的平均扫描行数为n 2 一旦表的
  • 图(一)之邻接表Adjacency List

    开始攻克图的算法 先从最简单的存储开始实现 本文关于邻接表的实现 邻接表是图的存储中最简单也是最基本的存储结构 基于链表的思想实现的 在邻接表中 对于中的每个顶点建立一个单链表 第i个单链表中的节点表示依附于顶点的vi的边 每个节点由3个域
  • 浅谈Linux的文件系统, 新增XFS.Ext3.GFS.ReiserFS.JFS相关知识

    如果您是一位新手 也许 您还不知道如何把文件从Windows拷贝到 Linux上吧 下面 我们将说明Unix文件系统以及mount的工作过程 然后再比较详细地讨论 mount的使用和有关选项 如果您已经了解Unix文件系统是如何工作的 那么
  • 转】M1卡密钥破解,收藏

    M1卡说明及使用proxmark3破解方法 看了网上写的一些关于M1卡的文章 多数有些误导之嫌 首先谈谈M1卡的规格 M1卡的容量为1KB 好多网上写8KB 这里其实是有个误区 应该是8K位 1Byte 1B 8位 其实也就是说8k位想到于
  • android 中文权限解释

    PermissList Key android permission ACCESS CHECKIN PROPERTIES Title 访问检入属性 Memo 允许对检入服务上传的属性进行读 写访问 普通应用程序不能使用此权限 Level 0
  • FLASH位宽为8、16、32时,CPU与外设之间地址线的连接方法

    原文地址 http www eefocus com E5 8D 83 E9 87 8C E7 9F A5 E9 B9 B0 blog 10 03 186914 04945 html FLASH连接CPU时 根据不同的数据宽度 比如16位的N
  • 数据决定AIGC的高度,什么又决定着数据的深度?

    有人曾言 数据决定人工智能发展的天花板 深以为然 随着ChatGPT等AIGC应用所展现出的强大能力 人们意识到通用人工智能的奇点正在来临 越来越多的企业开始涌入这条赛道 在AIGC浪潮席卷全球之际 数据的重要性也愈发被业界所认同 之所以会
  • PPI协议详解 ppi通讯协议 ppi通信协议 vb与ppi协议通讯

    PPI协议详解 ppi通讯协议 ppi通信协议 vb与ppi协议通讯 PPI协议详解 ppi通讯协议 ppi通信协议 vb与ppi协议通讯 我们提供 PPI协议的官方文档 协议更新时间为2005年 下面是我们根据文档解析的PPI读取变量返回
  • Hbase的入门心得:

    Hbase的入门心得 在了解了Hadoop的大家族的核心成员HDFS MapReduce以及资源管理器YARN后 HBase又是一个新颖的技术出现 在超人学院吴超老师的视频里介绍到 HBase Hadoop Database是一个分布式的

随机推荐

  • 「Python 基础」异步 I/O 编程

    I O 密集型应用程序大大提升系统多任务处理能力 异步 I O 模型 一个消息循环 主线程在消息循环中不断重复 读取消息 处理消息 获取线程池 loop get event loop while True 接收事件消息 event loop
  • R语言实现个人信用风险评估(数据科学导引)

    1 案例背景 在很多国家 政府机构会密切监控贷款业务银行需要明确解释申请者的贷款申请被拒绝或者批准的原因 这种可解释性对于贷款申请者也是很重要的 在贷款申请被银行拒绝时 申请者需要知道为什么自己的信用级别不符合银行的要求 通过构建自动化的信
  • es脚本 实现字段之间进行比对

    script script source ctx source extra test lang painless query term user kimchy java BoolQueryBuilder boolQueryBuilder Q
  • 详解比较古怪的字符串拆分函数:strtok函数

    对于字符串中的标点符号 字符呀 如何能够当作分隔符来对原字符串进行分割呢 分割出来的为一段一段的字符 因此 在这里面就用到了 本文 比较古怪的字符串拆分函数 strtok函数 假设 对于这个字符串 woaini wangyijun com
  • 3.4 三级指针

    char p NULL 注 1 可以通过三级指针间接的改变二级指针的指向 2 p表示 三级指针指向的二级指针中保存的内存地址 3 三级指针做函数参数时 主调函数需要传2级指针的地址 4 n级指针可以间接修改n 1级指针的指向 下面的例子通过
  • @Component注解的作用

    Spring自带的 Component注解及扩展 Component 定义Spring管理Bean 也就是将标注 Component注解的类交由spring管理 AspectJ风格的切面可以通过 Compenent注解标识其为Spring管
  • VMware10上新建虚拟机步骤图解 + 安装Centos 7(64位) 系统

    原文 https blog csdn net hometing218 article details 79486172
  • RTKlib单点定位-部分思考

    塔奇克敲代码 博主的博客 RTKLIB源码解析 单点定位 将单点定位部分整理成函数小卡片 为我理解RTKlib提供了很大的帮助 他在单点定位部分列出了一些疑惑 在此我记录下我对部分疑惑的理解 文章目录 1 pntpos函数 2 satpos
  • vcs+verdi,以及Makefile注意点

    Makefile 命令行之前是以Tab开头的不然会报错 gvim里面强制输入tab 使用Ctr v i 直接使用tab键可能输入不成功 注释用 下面是makefile内容 L8 可选debug debug pp debug pp 使能ucl
  • 树莓派通信协议——MQTT的安装及使用

    MQTT是一种基于TCP IP协议栈构建的异步通信协议 是一种轻量级的发布 订阅信息传输协议 基于topic订阅关系的发布和推送 在实践中可空间上 将消息发送者和接受者分离 可以再不可靠的网络环境中进行扩展 适用于设备硬件存储空间有限或网络
  • 【满分】【华为OD机试真题2023 JAVA&JS】木板

    华为OD机试真题 2023年度机试题库全覆盖 刷题指南点这里 木板 时间限制 1s 空间限制 256MB 限定语言 不限 题目描述 小明有n块木板 第i 1 i n 块木板的长度为ai 小明买了一块长度为m的木料 这块木料可以切割成任意块
  • R手册(NLP)--wordcloud2

    文章目录 wordlcoud2函数 letterCloud函数 shiny支持 wordcloud2 R interface to wordcloud for data visualization Wordcloud2主要包括两个函数 wo
  • ‘XXX’ is already defined @typescript-eslint/no-redeclare 警告 问题解决

    上文React Typescript项目环境中搭建并使用redux环境 结束是 其实不算完全写完吧 还会留下一个警告 这个报错 好像是说 这两个值已经定义过了 可能很多人 会觉得小问题 但你会发现 无论你名字怎么改都会一直 带着你新的名字继
  • Jenkins+sonarqube+sonar-scanner扫描java文件乱码怎么解决?

    在jenkins上扫描java项目 提示乱码 有没有知道怎么解决啊 WARN Invalid character encountered in file root jenkins workspace sonar scanner rrdl t
  • Element中table组件根据属性合并行数据

    在实际开发中 要求使用elementUI的table组件对表格数据上下行相邻相同的数据进行合并 在elem官网上查看到是有对应的组件和合并方法
  • apache doris和StarRocks的区别

    Apache Doris是一个分布式的列式存储系统 它的设计目标是提供大规模数据处理的可靠性和高性能 Doris采用了集群方式 通过将数据分布在多个机器上进行处理来提高性能 并提供了SQL查询接口方便用户使用 StarRocks是一个分布式
  • 【Windows10下MySQL(8.0)免费版安装与卸载图文教程】

    WIN10安装MYSQL8 0图文教程 Windows10下MySQL 8 0版本 免费版安装与卸载 一 下载 二 解压 三 创建配置文件以及存放数据的文件夹 四 配置系统环境变量 五 以管理员打开命令窗口 Win键 X键 六 安装 七 连
  • idea access数据库连接_几款好用的数据库,选择适合自己的那盘菜

    先来说一下什么是数据库 通俗讲 它就像物品仓库一样 可以存储电子数据 但是不同的是 它是以一定数据结构进行存储的 用户也可以对其中的数据通过一定的语言方式进行新增 查询 更新 删除等操作 大家接触的比较多的是Excel数据表格 实际它只能称
  • Elasticsearch-head插件安装教程

    目录标题 前言 一 安装node 二 安装Elasticsearch head插件 1 上传压缩包到 opt es路径下去 2 解压安装包 3 192 168 43 10机器修改Gruntfile js 4 192 168 43 10机器修
  • spdk探秘-----块设备开发指导

    这里的块设备是一种存储设备 它支持在固定大小的块中读写数据 这些块通常是512或4096字节 这些设备可能是软件中的逻辑结构 或者对应于像NVMe ssd这样的物理设备 通用库的公共头文件是bdev h 它是与任何类型的块设备交互所需的全部