相干加速器 (CXL) 闪存¶
简介¶
IBM Power 架构为 CAPI(相干加速器电源接口)提供支持,该接口在 Power 8 系统上的某些 PCIe 插槽中可用。CAPI 可以被认为是通过 PCIe 的一种特殊的隧道协议,它允许 PCIe 适配器看起来像专用的协处理器,可以读取或写入应用程序的内存并生成页错误。因此,在 CAPI 模式下运行的适配器的主机接口不需要将数据缓冲区映射到设备的内存(IOMMU 旁路),也不需要固定内存。
在 Linux 上,相干加速器 (CXL) 内核服务通过实现虚拟 PCI 主桥将 CAPI 设备呈现为 PCI 设备。这种抽象简化了基础设施和编程模型,允许驱动程序看起来与其他本机 PCI 设备驱动程序类似。
CXL 提供了一种机制,通过该机制,用户空间应用程序可以直接与设备(网络或存储)通信,从而绕过典型的内核/设备驱动程序堆栈。CXL 闪存适配器驱动程序使用户空间应用程序可以直接访问闪存存储。
CXL 闪存适配器驱动程序是一个内核模块,它作为 IBM CXL 闪存适配器的低级设备驱动程序(在 SCSI 磁盘和协议驱动程序之下)位于 SCSI 堆栈中。此驱动程序负责适配器的初始化、设置用户空间访问的特殊路径以及执行错误恢复。它直接与 相干加速器接口 (CXL) 中描述的闪存加速器功能单元 (AFU) 通信。
cxlflash 驱动程序在设备 (LUN) 级别支持两种互斥的操作模式
可以将任何闪存设备 (LUN) 配置为作为常规磁盘设备访问(即:/dev/sdc)。这是默认模式。
可以使用特殊的块库从用户空间访问任何闪存设备 (LUN)。此模式进一步指定了访问设备的手段,并提供了对整个 LUN 的原始访问(称为直接或物理 LUN 访问)或对 LUN 的内核/AFU 介导分区的访问(称为虚拟 LUN 访问)。将磁盘设备分割成虚拟 LUN 由闪存 AFU 提供的特殊转换服务协助完成。
概述¶
相干加速器接口架构 (CAIA) 引入了主上下文的概念。主上下文通常具有内核或虚拟机管理程序授予它的特殊权限,允许它执行 AFU 范围的管理和控制。主上下文可以直接参与每个用户 I/O,也可以不直接参与,但至少参与在允许用户应用程序直接向 AFU 发送请求之前的初始设置。
CXL 闪存适配器驱动程序与 AFU 建立主上下文。它使用内存映射 I/O (MMIO) 进行此控制和设置。适配器问题空间内存映射如下所示
+-------------------------------+ | 512 * 64 KB User MMIO | | (per context) | | User Accessible | +-------------------------------+ | 512 * 128 B per context | | Provisioning and Control | | Trusted Process accessible | +-------------------------------+ | 64 KB Global | | Trusted Process accessible | +-------------------------------+此驱动程序将其自身配置到 SCSI 软件堆栈中作为适配器驱动程序。该驱动程序是唯一被认为是可信进程以编程 MMIO 空间中所示的配置和控制和全局区域的实体。主上下文驱动程序发现所有连接到 CXL 闪存适配器的 LUN,并为从每个路径看到的每个唯一 LUN 实例化 scsi 块设备(/dev/sdb、/dev/sdc 等)。
一旦实例化了这些 scsi 块设备,则写入块库提供的规范的应用程序可以从用户空间访问闪存(无需系统调用)。
此主上下文驱动程序还为此块库提供了一系列 ioctl,以启用此用户空间访问。该驱动程序支持两种访问块设备的模式。
第一种模式称为虚拟模式。在此模式下,可以将单个 scsi 块设备(/dev/sdb)划分为任意数量的不同虚拟 LUN。只要所有虚拟 LUN 的大小之和及其关联的元数据不超过物理容量,就可以调整虚拟 LUN 的大小。
第二种模式称为物理模式。在此模式下,块库可以直接打开单个块设备 (/dev/sdb),并且应用程序可以使用 LUN 的整个空间。
只有物理模式才能提供数据的持久性。即,写入块设备的数据将在应用程序退出和重新启动以及重新启动后仍然存在。虚拟 LUN 不会持久化(即,在应用程序终止或系统重新启动后不会继续存在)。
块库 API¶
打算从用户空间访问 CXL 闪存的应用程序应使用块库,因为它抽象了直接与 cxlflash 驱动程序接口的细节,这些细节对于执行管理操作(即:设置、拆除、调整大小)是必需的。可以将块库视为由 cxlflash 驱动程序专门为在用户空间访问模式下运行的设备 (LUN) 提供的以 IOCTL 实现的服务的“用户”。虽然不要求应用程序了解块库和 cxlflash 驱动程序之间的接口,但下面提供了每个支持的服务 (IOCTL) 的高级概述。
可以在 GitHub 上找到该块库:http://github.com/open-power/capiflash
CXL 闪存驱动程序 LUN IOCTL¶
希望通过用户空间访问与闪存设备 (LUN) 接口的用户(例如块库)需要使用 cxlflash 驱动程序提供的服务。由于这些服务以 ioctl 的形式实现,因此必须首先获取文件描述符句柄,以便在用户和内核之间建立通信通道。此文件描述符是通过打开与 LUN 发现期间创建的 scsi 磁盘设备 (/dev/sdb) 关联的设备特殊文件获得的。根据 cxlflash 驱动程序在 SCSI 协议堆栈中的位置,cxlflash 驱动程序实际上看不到此打开。成功打开后,用户会收到一个文件描述符(以下称为 fd1),该文件描述符应用于发出下面列出的后续 ioctl。
这些 IOCTL 的结构定义可在:uapi/scsi/cxlflash_ioctl.h 中找到
DK_CXLFLASH_ATTACH¶
此 ioctl 使用 CXL 内核服务获取、初始化和启动上下文。这些服务指定一个上下文 ID (u16),通过该 ID 唯一标识上下文及其分配的资源。这些服务还提供第二个文件描述符(以下称为 fd2),块库使用该描述符通过 mmap() 发起与 CXL 闪存设备的内存映射 I/O 并轮询完成事件。此文件描述符由该驱动程序有意安装,而不是 CXL 内核服务安装,以便在发生非用户启动的 close()(例如被终止的进程)的情况下允许中间通知和访问。此设计点在 DK_CXLFLASH_DETACH ioctl 的描述中进一步详细描述。
关于返回给用户的“令牌”(上下文 ID 和 fd2)有几个重要方面
这些令牌仅对创建它们的进程有效。fork 进程的子进程不能继续使用其父进程创建的上下文 ID 或文件描述符(有关详细信息,请参见 DK_CXLFLASH_VLUN_CLONE)。
这些令牌仅在上下文及其创建它们的进程的生命周期内有效。一旦销毁其中任何一个,这些令牌将被视为过时,后续使用将导致错误。
只有在首次附加上下文时,才会返回有效的适配器文件描述符 (fd2 >= 0)。后续附加到现有上下文(存在 DK_CXLFLASH_ATTACH_REUSE_CONTEXT 标志)时,不会提供适配器文件描述符,因为它之前已告知应用程序。
当不再需要上下文时,用户应通过 DK_CXLFLASH_DETACH ioctl 从上下文中分离。当此 ioctl 返回有效的适配器文件描述符,并且存在返回标志 DK_CXLFLASH_APP_CLOSE_ADAP_FD 时,应用程序_必须_在成功分离后关闭适配器文件描述符。
当此 ioctl 返回有效的 fd2,并且存在返回标志 DK_CXLFLASH_APP_CLOSE_ADAP_FD 时,应用程序_必须_在以下情况下关闭 fd2:
在成功分离上下文的最后一个用户之后
在上下文的原始 fd2 上成功恢复之后
在 fork() 的子进程中,在克隆 ioctl 之后,在与源上下文关联的 fd2 上
在任何时候,关闭 fd2 都会使令牌无效。应用程序应谨慎行事,仅在适当的时候(如前面的要点所述)关闭 fd2,以避免过早丢失 I/O。
DK_CXLFLASH_USER_DIRECT¶
此 ioctl 负责将 LUN 过渡到直接(物理)模式访问,并为每个上下文配置 AFU,以便从用户空间进行直接访问。此外,还会向用户返回块大小和最后一个逻辑块地址 (LBA)。
如前所述,在用户空间访问模式下操作时,可以全部或部分访问 LUN。一次只允许一种模式,如果一种模式处于活动状态(存在未完成的引用),则会拒绝以不同模式使用 LUN 的请求。
通过向 AFU 的资源句柄表中添加条目来配置 AFU,以便从用户空间进行直接访问。条目的索引被视为返回给用户的资源句柄。然后,用户可以使用该句柄在 I/O 期间引用 LUN。
DK_CXLFLASH_USER_VIRTUAL¶
此 ioctl 负责将 LUN 过渡到虚拟访问模式,并为每个上下文配置 AFU,以便从用户空间进行虚拟访问。此外,还会向用户返回块大小和最后一个逻辑块地址 (LBA)。
如前所述,在用户空间访问模式下操作时,可以全部或部分访问 LUN。一次只允许一种模式,如果一种模式处于活动状态(存在未完成的引用),则会拒绝以不同模式使用 LUN 的请求。
通过向 AFU 的资源句柄表中添加条目来配置 AFU,以便从用户空间进行虚拟访问。条目的索引被视为返回给用户的资源句柄。然后,用户可以使用该句柄在 I/O 期间引用 LUN。
默认情况下,创建的虚拟 LUN 的大小为 0。用户需要使用 DK_CXLFLASH_VLUN_RESIZE ioctl 来调整虚拟 LUN 的大小,使其增长到所需大小。为了避免在初始创建虚拟 LUN 时执行此大小调整,用户可以选择在 DK_CXLFLASH_USER_VIRTUAL ioctl 中指定大小,这样,当成功返回给用户时,所提供的资源句柄已经引用了已配置的存储。这反映在最后一个 LBA 是非零值。
当可以通过多个端口访问 LUN 时,此 ioctl 将返回,并设置 DK_CXLFLASH_ALL_PORTS_ACTIVE 返回标志。这为用户提供了一个提示,即如果发生 I/O 错误,可以重试 I/O,因为可以通过多个路径访问 LUN。
DK_CXLFLASH_VLUN_RESIZE¶
此 ioctl 负责调整先前创建的虚拟 LUN 的大小,如果对非虚拟模式的 LUN 调用,则会失败。成功后,会向用户返回更新的最后一个 LBA,指示与资源句柄关联的虚拟 LUN 的新大小。
虚拟 LUN 的分区由 cxlflash 驱动程序和 AFU 共同协调。为每个以虚拟模式运行的 LUN 保留一个分配表,用于对 AFU 在提供资源句柄时引用的 LUN 转换表进行编程。
如果 AFU 同步操作耗时过长,则此 ioctl 可以返回 -EAGAIN。除了向用户返回失败外,cxlflash 还会安排异步 AFU 重置。如果用户选择重试该操作,则预计会成功。如果此 ioctl 返回 -EAGAIN 失败,则用户可以重试该操作或将其视为失败。
DK_CXLFLASH_RELEASE¶
此 ioctl 负责释放先前获得的对物理或虚拟 LUN 的引用。可以将其视为 DK_CXLFLASH_USER_DIRECT 或 DK_CXLFLASH_USER_VIRTUAL ioctl 的逆操作。成功后,资源句柄不再有效,并且资源句柄表中的条目可以再次使用。
作为虚拟 LUN 释放过程的一部分,首先将虚拟 LUN 的大小调整为 0,以清除和释放与虚拟 LUN 引用关联的转换表。
DK_CXLFLASH_DETACH¶
此 ioctl 负责取消注册 cxlflash 驱动程序的上下文,并释放未通过 DK_CXLFLASH_RELEASE ioctl 显式释放的未完成资源。成功后,从 DK_CXLFLASH_ATTACH 开始提供给用户的所有“令牌”都将不再有效。
当在成功附加时返回 DK_CXLFLASH_APP_CLOSE_ADAP_FD 标志时,应用程序_必须_在分离上下文的最后一个用户后,关闭与上下文关联的 fd2。
DK_CXLFLASH_VLUN_CLONE¶
此 ioctl 负责将先前创建的上下文克隆到最近创建的上下文。它仅用于支持在进程 fork 后维护用户空间对存储的访问。成功后,子进程(调用 ioctl)将通过与父进程相同的资源句柄访问相同的 LUN,但在不同的上下文下。
CXL 不支持跨进程的上下文共享,因此每次 fork 都必须为子进程建立新的上下文。此 ioctl 简化了用户在此类场景中所需的状态管理和回放。当进程 fork 时,子进程可以通过首先创建一个上下文(通过 DK_CXLFLASH_ATTACH),然后使用此 ioctl 执行从父进程到子进程的克隆,来克隆父进程的上下文。
克隆本身非常简单。资源句柄和 lun 转换表从父上下文复制到子上下文,然后与 AFU 同步。
当在成功附加时返回 DK_CXLFLASH_APP_CLOSE_ADAP_FD 标志时,应用程序_必须_在克隆后关闭与源上下文(仍然驻留在父进程中/可访问)关联的 fd2。这是为了避免子进程的文件描述符表中出现过时的条目。
如果 AFU 同步操作耗时过长,则此 ioctl 可以返回 -EAGAIN。除了向用户返回失败外,cxlflash 还会安排异步 AFU 重置。如果用户选择重试该操作,则预计会成功。如果此 ioctl 返回 -EAGAIN 失败,则用户可以重试该操作或将其视为失败。
DK_CXLFLASH_VERIFY¶
此 ioctl 用于检测各种更改,例如磁盘容量更改、可见 LUN 的数量更改等。在更改影响应用程序(例如 LUN 大小调整)的情况下,cxlflash 驱动程序会将更改的状态报告给应用程序。
当用户想要验证 LUN 是否已根据检查条件更改时,他们会调用此 ioctl。由于用户在内核之外操作,他们会在内核不知情的情况下看到这些类型的事件。当遇到这些事件时,用户的架构行为是调用此 ioctl,指示他们想要验证的内容,并传递任何适当的信息。目前,仅支持使用感知数据验证 LUN 更改(即:大小不同)。
DK_CXLFLASH_RECOVER_AFU¶
此 ioctl 用于驱动指定用户上下文的恢复(如果需要此类操作)。成功恢复后,将重新建立与用户上下文关联的任何状态。
当设备需要重置或正在终止时,用户上下文会置于错误状态。通过在 MMIO 读取中看到所有 0xF,将此错误状态通知给用户。当遇到这种情况时,用户的架构行为是调用此 ioctl 以恢复其上下文。用户也可以随时调用此 ioctl 以检查设备是否运行正常。如果此 ioctl 返回失败,则用户应通过 release/detach ioctl 正常清理其上下文。在他们执行此操作之前,他们持有的上下文不会被放弃。用户也可以选择退出进程,届时他们持有的上下文/资源将在 release fop 中作为一部分被释放。
当在成功附加时返回 DK_CXLFLASH_APP_CLOSE_ADAP_FD 标志时,应用程序_必须_在此 ioctl 返回成功并指示已恢复上下文 (DK_CXLFLASH_RECOVER_AFU_CONTEXT_RESET) 后,取消映射并关闭与原始上下文关联的 fd2。
DK_CXLFLASH_MANAGE_LUN¶
此 ioctl 用于将 LUN 从可用于文件系统访问的模式(旧版)切换到为独占用户空间访问(superpipe)预留的模式。如果 LUN 在多个端口和适配器上可见,则此 ioctl 用于通过其全球节点名称 (WWNN) 唯一标识每个 LUN。
CXL 闪存驱动程序主机 IOCTL¶
cxlflash 驱动程序支持的每个主机适配器实例都有一个与其关联的特殊字符设备,以启用一组主机管理功能。这些字符设备托管在 cxlflash 专用的类中,可以通过 /dev/cxlflash/* 访问。
可以使用以下主机 ioctl API 编写应用程序来执行各种功能。
这些 IOCTL 的结构定义可在:uapi/scsi/cxlflash_ioctl.h 中找到
HT_CXLFLASH_LUN_PROVISION¶
此 ioctl 用于在缺少外部 LUN 管理接口的 cxlflash 设备上创建和删除持久 LUN。仅当与支持 LUN 配置功能的 AFU 一起使用时才有效。
当有足够空间时,可以通过指定 LUN 所在的目标端口和所需的 4K 块大小来创建 LUN。成功后,将返回已创建 LUN 的 LUN ID 和 WWID,并且可以扫描 SCSI 总线以检测 LUN 拓扑的变化。请注意,不支持部分分配。如果由于空间问题导致创建失败,可以查询目标端口以获取其当前的 LUN 几何信息。
要删除 LUN,必须首先将其与 Linux SCSI 子系统解除关联。然后可以通过指定目标端口和 LUN ID 来启动 LUN 删除。成功后,与该端口关联的 LUN 几何信息将被更新,以反映已配置 LUN 的新数量和可用容量。
要查询端口的 LUN 几何信息,需要指定目标端口。成功后,将显示以下信息:
该端口允许配置的最大 LUN 数量
该端口当前已配置的 LUN 数量
该端口已配置 LUN 的最大总容量(4K 块)
该端口当前已配置 LUN 的总容量(4K 块)
通过这些信息,可以计算出可用的 LUN 数量和容量。
HT_CXLFLASH_AFU_DEBUG¶
此 ioctl 用于通过支持命令直通接口来调试 AFU。它仅在使用支持 AFU 调试功能的 AFU 时有效。
除了缓冲区管理外,AFU 调试命令对于 cxlflash 来说是不透明的,并被视为直通。对于确实需要数据传输的调试命令,用户需要提供足够大小的数据缓冲区,并且必须指定相对于主机的数据传输方向。最大传输大小限制为 256K。请注意,不支持部分读取完成 - 当主机读取数据传输时发生错误时,数据缓冲区不会复制回用户。