CXL 驱动程序操作

本节中描述的设备存在于

/sys/bus/cxl/devices/
/dev/cxl/

作为 NDTCL 项目一部分维护的 cxl-cli 库可用于编写与这些设备交互的脚本。

驱动程序

CXL 驱动程序分为多个驱动程序。

  • cxl_core - 基本初始化接口和核心对象创建

  • cxl_port - 初始化根并提供端口枚举接口。

  • cxl_acpi - 初始化根解码器并与 ACPI 数据交互。

  • cxl_p/mem - 初始化内存设备

  • cxl_pci - 使用 cxl_port 枚举实际的 fabric 层次结构。

驱动程序设备

这是一个来自具有 4 个主机桥的单插槽系统的示例。 两个主机桥连接了一个内存设备,这些设备被交织成一个内存区域。 内存区域已转换为 dax。

# ls /sys/bus/cxl/devices/
  dax_region0  decoder3.0  decoder6.0  mem0   port3
  decoder0.0   decoder4.0  decoder6.1  mem1   port4
  decoder1.0   decoder5.0  endpoint5   port1  region0
  decoder2.0   decoder5.1  endpoint6   port2  root0
Digraph of CXL fabric describing host-bridge interleaving

具有主机桥交织内存区域的 CXL fabric 图

在本节中,我们将探讨此配置中存在的设备,但在下面的示例配置中,我们将更深入地探讨更多配置。

基本设备

CXL fabric 中的大多数设备都是某种 端口(因为每个设备主要路由从一个设备到下一个设备的请求,而不是提供直接服务)。

CXL 根 是在 cxl_acpi_probe 期间由 cxl_acpi 驱动程序创建的逻辑对象 - 如果找到 ACPI0017 计算快速链接根对象 设备类。

根包含到以下内容的链接:

  • CEDT 中的 CHBS 定义的 主机桥端口

  • 通常连接到 主机桥端口下游端口

  • CEDT 中的 CFMWS 定义的 根解码器

# ls /sys/bus/cxl/devices/root0
  decoder0.0          dport0  dport5    port2  subsystem
  decoders_committed  dport1  modalias  port3  uevent
  devtype             dport4  port1     port4  uport

# cat /sys/bus/cxl/devices/root0/devtype
  cxl_port

# cat port1/devtype
  cxl_port

# cat decoder0.0/devtype
  cxl_decoder_root

根是 Linux CXL 驱动程序呈现的 CXL fabric 中的第一个 逻辑端口CXL 根 是一种特殊的 交换机端口,因为它只有下游端口连接。

端口

端口 对象最好描述为 交换机端口。它可能代表到根的主机桥或交换机上的实际交换机端口。交换机端口 包含一个或多个解码器,用于将内存请求路由到下游端口,这些端口可能连接到另一个 交换机端口端点端口

# ls /sys/bus/cxl/devices/port1
  decoder1.0          dport0    driver     parent_dport  uport
  decoders_committed  dport113  endpoint5  subsystem
  devtype             dport2    modalias   uevent

# cat devtype
  cxl_port

# cat decoder1.0/devtype
  cxl_decoder_switch

# cat endpoint5/devtype
  cxl_port

在探测 CXL 根 时,在 cxl_acpi_probe 期间探测 fabric 中的 CXL 主机桥。这允许立即逻辑连接到根和主机桥之间。

  • 根具有到主机桥的下游端口连接

  • 主机桥具有到根的上游端口连接。

  • 主机桥具有一个或多个到交换机或端点端口的下游端口连接。

主机桥 是一种特殊的 CXL 交换机端口。它通过 ACPI0016 ID 在 ACPI 规范中明确定义。主机桥 端口将在 acpi_probe 时探测,而实际交换机上的类似端口将在稍后探测。否则,交换机和主机桥端口看起来非常相似 - 它们都包含交换机解码器,这些解码器在上下游端口之间路由访问。

端点

端点 是 fabric 中的终端端口。这是一个 逻辑设备,并且可能是内存设备呈现的许多 逻辑设备 之一。它仍然被认为是 fabric 中的一种 端口

端点 包含 端点解码器 和设备的相干设备属性表(描述设备的功能)。

# ls /sys/bus/cxl/devices/endpoint5
  CDAT        decoders_committed  modalias      uevent
  decoder5.0  devtype             parent_dport  uport
  decoder5.1  driver              subsystem

# cat /sys/bus/cxl/devices/endpoint5/devtype
  cxl_port

# cat /sys/bus/cxl/devices/endpoint5/decoder5.0/devtype
  cxl_decoder_endpoint

内存设备 (memdev)

memdevcxl_pci 驱动程序在 cxl_pci_probe 中探测和添加,并由 cxl_mem 驱动程序管理。它主要通过 /dev/cxl/memN 提供到内存设备的 IOCTL 接口,并公开各种设备配置数据。

# ls /sys/bus/cxl/devices/mem0
  dev       firmware_version    payload_max  security   uevent
  driver    label_storage_size  pmem         serial
  firmware  numa_node           ram          subsystem

内存设备是一个离散的基本对象,不是端口。虽然它所属的物理设备也可能托管 端点,但 端点memdev 之间的关系未在 sysfs 中捕获。

端口关系

在上面描述的示例中,有四个主机桥连接到根,并且两个主机桥连接了一个端点。

Digraph of CXL fabric describing host-bridge interleaving

具有主机桥交织内存区域的 CXL fabric 图

解码器

解码器 是 CXL 主机管理设备内存 (HDM) 解码器的缩写。它是一种通过 CXL fabric 将访问路由到端点的设备,并在端点将 主机物理地址 转换为 设备物理地址

CXL 3.1 规范强烈暗示只有端点解码器应该参与 主机物理地址设备物理地址 的转换。

8.2.4.20 CXL HDM Decoder Capability Structure

IMPLEMENTATION NOTE
CXL Host Bridge and Upstream Switch Port Decode Flow

IMPLEMENTATION NOTE
Device Decode Logic

这些注释暗示存在两组逻辑解码器。

  • 路由解码器 - 一种路由访问但不将 HPA 中的地址转换为 DPA 的解码器。

  • 转换解码器 - 一种将 HPA 中的访问转换为 DPA 以供端点服务的解码器。

CXL 驱动程序区分 3 种解码器类型:根、交换机和端点。只有端点解码器是转换解码器,所有其他解码器都是路由解码器。

注意

平台供应商请注意

Linux 强烈假设端点解码器是 fabric 中唯一主动将 HPA 转换为 DPA 的解码器。 Linux 假设路由解码器将 HPA 不变地传递到 fabric 中的下一个解码器。

因此,假设 fabric 中任何给定的解码器都将具有其上游端口解码器的地址范围的子集。 规范未定义对该方案的任何偏离。 Linux 优先考虑规范定义/架构行为。

如果配置为交织内存访问,则解码器可能具有一个或多个 下游目标。 这将通过 target_list 参数在 sysfs 中显示。

根解码器

根解码器CEDT 的 CFMWS 字段中存在的物理地址和交织配置的逻辑构造。 Linux 将此信息显示为存在于 CXL 根 中的解码器。 我们将此视为 根解码器,尽管从技术上讲,它存在于 CXL 规范和特定于平台的 CXL 根实现的边界上。

Linux 将这些逻辑解码器视为一种 路由解码器,并且是 CXL fabric 中从平台的内存控制器接收内存访问的第一个解码器。

根解码器cxl_acpi_probe 期间创建。 为 CEDT 中的每个 CFMWS 条目创建一个根解码器。

target_list 参数由 CFMWS 目标字段填充。 根解码器的目标是 主机桥,这意味着在根解码器级别完成的交织是 主机桥间交织

只有根解码器才能实现 主机桥间交织

此类交织必须由平台配置并在 ACPI CEDT CFMWS 中描述,因为 CFMWS 中的目标 CXL 主机桥 UID 必须与 CEDT 的 CHBS 字段中的 CXL 主机桥 UID 和 DSDT 中定义的 CXL 主机桥的 UID 字段匹配。

根解码器中的交织设置描述了如何在直接下游目标之间交织访问,而不是整个交织集。

根解码器中描述的内存范围用于

  1. 创建一个内存区域(本示例中为 region0),以及

  2. 将该区域与 IO 内存资源相关联 (kernel/resource.c)

# ls /sys/bus/cxl/devices/decoder0.0/
  cap_pmem           devtype                 region0
  cap_ram            interleave_granularity  size
  cap_type2          interleave_ways         start
  cap_type3          locked                  subsystem
  create_ram_region  modalias                target_list
  delete_region      qos_class               uevent

# cat /sys/bus/cxl/devices/decoder0.0/region0/resource
  0xc050000000

在早期启动期间,当在 EFI 内存映射或 E820 表(在 x86 上)中识别出 CFMWS 区域时,将创建 IO 内存资源。

根解码器定义为单独的 devtype,但由于具有下游目标,因此也是一种 交换机解码器

# cat /sys/bus/cxl/devices/decoder0.0/devtype
  cxl_decoder_root

交换机解码器

任何非根、转换解码器都被认为是 交换机解码器,并将呈现类型 cxl_decoder_switch主机桥CXL 交换机(设备)解码器都是 cxl_decoder_switch 类型。

# ls /sys/bus/cxl/devices/decoder1.0/
  devtype                 locked    size       target_list
  interleave_granularity  modalias  start      target_type
  interleave_ways         region    subsystem  uevent

# cat /sys/bus/cxl/devices/decoder1.0/devtype
  cxl_decoder_switch

# cat /sys/bus/cxl/devices/decoder1.0/region
  region0

交换机解码器 在根解码器定义的区域和下游目标端口之间具有关联。 在交换机解码器中完成的交织是多下游端口交织(或主机桥的 主机桥内交织)。

交换机解码器中的交织设置描述了如何在直接下游目标之间交织访问,而不是整个交织集。

交换机解码器在 cxl_port 驱动程序中的 cxl_switch_port_probe 期间创建,并基于 PCI 设备的 DVSEC 寄存器创建。

如果在启动期间平台对它们进行编程,则在探测期间验证交换机解码器编程(请参阅下面的 自动解码器),如果运行时编程,则在提交时验证(请参阅下面的 运行时编程)。

端点解码器

连接到 CXL fabric 中终端点的任何解码器(端点)都被认为是 端点解码器。 端点解码器的类型为 cxl_decoder_endpoint

# ls /sys/bus/cxl/devices/decoder5.0
  devtype                 locked    start
  dpa_resource            modalias  subsystem
  dpa_size                mode      target_type
  interleave_granularity  region    uevent
  interleave_ways         size

# cat /sys/bus/cxl/devices/decoder5.0/devtype
  cxl_decoder_endpoint

# cat /sys/bus/cxl/devices/decoder5.0/region
  region0

端点解码器 与根解码器定义的区域相关联,并描述与此区域关联的设备本地资源。

与根解码器和交换机解码器不同,端点解码器将 主机物理地址 转换为 设备物理地址 范围。 因此,端点上的交织设置描述了整个交织集

设备物理地址 区域必须按顺序提交。 例如,无法在 0x0 处启动的 DPA 区域之前提交从 0x80000000 启动的 DPA 区域。

截至 Linux v6.15,Linux 不支持不平衡交织设置,交织集中的所有端点都应具有相同的交织设置(粒度和方式必须相同)。

端点解码器在 cxl_port 驱动程序中的 cxl_endpoint_port_probe 期间创建,并基于 PCI 设备的 DVSEC 寄存器创建。

解码器关系

在上面描述的示例中,有一个根解码器,它通过两个主机桥路由内存访问。 每个主机桥都有一个解码器,它将访问路由到其单个端点目标。 每个端点都有一个解码器,它将 HPA 转换为 DPA 并服务于内存请求。

驱动程序通过解码器编程来验证端口之间的关系,因此我们可以认为解码器以与端口类似的分层方式相关。

Digraph of hierarchical relationship between root, switch, and endpoint decoders.

CXL 根、交换机和端点解码器的图。

区域

内存区域

内存区域 是一种逻辑构造,它将 fabric 中的一组 CXL 端口连接到 IO 内存资源。 它最终用于通过 DAX 区域 将这些设备上的内存公开给 DAX 子系统。

RAM 区域示例

# ls /sys/bus/cxl/devices/region0/
  access0      devtype                 modalias  subsystem  uuid
  access1      driver                  mode      target0
  commit       interleave_granularity  resource  target1
  dax_region0  interleave_ways         size      uevent

如果在 BIOS/EFI 编程解码器期间(请参阅 自动解码器),或者通过 根解码器create_ram_regioncreate_pmem_region 接口手动创建区域,则可以在端点探测期间构造内存区域。

内存区域 中的交织设置描述了 交织集 的配置 - 并且是可以预期在端点交织设置中看到的配置。

Digraph of CXL memory region relationships between root and endpoint decoders.

区域基于根解码器配置创建。 必须使用与区域相同的交织设置对端点解码器进行编程。

DAX 区域

DAX 区域 用于将 CXL 内存区域 转换为 DAX 设备。 然后可以通过文件描述符接口直接访问 DAX 设备,或者通过 DAX kmem 驱动程序将其转换为系统 RAM。 有关更多详细信息,请参阅 DAX 驱动程序部分。

# ls /sys/bus/cxl/devices/dax_region0/
  dax0.0      devtype  modalias   uevent
  dax_region  driver   subsystem

邮箱接口

每个设备的邮箱命令接口都在以下位置公开:

/dev/cxl/mem0
/dev/cxl/mem1

这些邮箱可以接收任何规范定义的命令。 只有在设置了构建配置 CXL_MEM_RAW_COMMANDS 时,才能将原始命令(自定义命令)发送到这些接口。 这被认为是调试和/或开发接口,而不是用于创建特定于供应商的命令的官方支持机制(请参阅 fwctl 子系统)。

解码器编程

运行时编程

在探测期间,必须编程的唯一解码器是 根解码器。 实际上,根解码器 是一种逻辑构造,用于描述主机桥级别的内存区域和交织配置 - 如 ACPI CEDT CFMWS 中所述。

所有其他 交换机端点 解码器都可以在运行时由用户编程 - 如果平台支持此类配置。

这种交互创建了 软件定义内存 环境。

有关如何在运行时配置 CXL 解码器的更多信息,请参阅 cxl-cli 文档。

自动解码器

自动解码器是在启动时由 BIOS/EFI 编程的解码器,并且几乎总是被锁定(无法更改)。 这是由可能具有静态配置的平台完成的 - 或者某些怪癖可能会阻止对解码器进行动态运行时更改(例如,需要 CPU 复合体内超出 CXL 范围的其他控制器编程)。

只要与它们关联的设备和内存区域探测没有问题,就会自动探测自动解码器。 在探测自动解码器时,驱动程序的主要责任是确保 fabric 是健全的 - 就像验证运行时编程的区域和解码器一样。

如果 Linux 无法验证自动解码器配置,则内存将不会作为 DAX 设备显示 - 因此不会暴露给页面分配器 - 实际上将其搁置。

交织

Linux CXL 驱动程序支持 跨链接优先 交织。 这决定了如何在每个解码器步骤中编程交织,因为驱动程序验证了解码器与其父解码器之间的关系。

例如,在具有 16 个端点连接到 4 个主机桥的 跨链接优先 交织设置中,linux 期望根、主机桥和端点分别具有以下方式/粒度。

4x4 跨链接优先交织设置

解码器

方式

粒度

4

256

主机桥

4

1024

端点

16

256

在根上,每个给定的访问都将路由到第 ((HPA / 256) % 4)th 个目标主机桥。 在主机桥内,每个 ((HPA / 1024) % 4)th 个目标端点。 每个端点都基于整个 16 个设备交织集进行转换。

不支持不平衡的交织集 - 层次结构中相似的点上的解码器(例如,所有主机桥解码器)必须具有相同的方式和粒度配置。

在根上

根解码器交织由 CEDT 的 CFMWS 字段定义。 CEDT 实际上可以定义多个 CFMWS 配置来描述相同的物理容量,目的是允许用户在运行时决定是将内存作为交织还是非交织联机。

           Subtable Type : 01 [CXL Fixed Memory Window Structure]
     Window base address : 0000000100000000
             Window size : 0000000100000000
Interleave Members (2^n) : 00
   Interleave Arithmetic : 00
            First Target : 00000007

           Subtable Type : 01 [CXL Fixed Memory Window Structure]
     Window base address : 0000000200000000
             Window size : 0000000100000000
Interleave Members (2^n) : 00
   Interleave Arithmetic : 00
            First Target : 00000006

           Subtable Type : 01 [CXL Fixed Memory Window Structure]
     Window base address : 0000000300000000
             Window size : 0000000200000000
Interleave Members (2^n) : 01
   Interleave Arithmetic : 00
            First Target : 00000007
             Next Target : 00000006

在此示例中,CFMWS 为每个主机桥定义了两个离散的非交织 4GB 区域,以及一个以两者为目标的交织 8GB 区域。 这将导致在根中显示 3 个根解码器。

# ls /sys/bus/cxl/devices/root0/decoder*
  decoder0.0  decoder0.1  decoder0.2

# cat /sys/bus/cxl/devices/decoder0.0/target_list start size
  7
  0x100000000
  0x100000000

# cat /sys/bus/cxl/devices/decoder0.1/target_list start size
  6
  0x200000000
  0x100000000

# cat /sys/bus/cxl/devices/decoder0.2/target_list start size
  7,6
  0x300000000
  0x200000000

这些解码器不是运行时可编程的。 它们用于生成 内存区域,以便通过在 交换机端点 解码器上运行时编程的设置将此内存联机。

在主机桥或交换机上

主机桥交换机 解码器可通过以下字段编程

  • start - 与内存区域关联的 HPA 区域

  • size - 区域的大小

  • target_list - 下游端口列表

  • interleave_ways - 要跨越交织的下游端口数

  • interleave_granularity - 要交织的粒度。

Linux 期望交换机解码器的 interleave_granularity 从其上游端口连接派生。 在 跨链接优先 交织配置中,解码器的 interleave_granularity 等于 parent_interleave_granularity * parent_interleave_ways

在端点上

端点解码器 的编程方式与主机桥和交换机解码器类似,但方式和粒度由交织集定义(例如,由关联的 内存区域 定义的交织设置)。

  • start - 与内存区域关联的 HPA 区域

  • size - 区域的大小

  • interleave_ways - 交织集中的端点数

  • interleave_granularity - 要交织的粒度。

这些设置由端点解码器用于将内存请求从 HPA转换为 DPA。 这就是为什么它们必须了解整个交织集。

Linux 不支持不平衡的交织配置。 因此,交织集中的所有端点都必须具有相同的方式和粒度。

示例配置