Compute Express Link 驱动程序操作理论¶
Compute Express Link 内存设备是一个实现 CXL.mem 协议的 CXL 组件。它包含一定量的易失性存储器、持久性存储器或两者。它被枚举为 PCI 设备,用于配置和通过 MMIO 邮箱传递消息。它对系统物理地址空间的贡献通过 HDM(主机管理设备内存)解码器处理,这些解码器可以选择性地定义设备对主机桥下的多个设备或跨主机桥交织的地址范围的贡献。
CXL 总线¶
类似于 RAID 驱动程序如何获取磁盘对象并将它们组装成新的逻辑设备,CXL 子系统的任务是获取 PCIe 和 ACPI 对象并将它们组装成 CXL.mem 解码拓扑。CXL.mem 拓扑的运行时配置需求也类似于 RAID,因为具有相同硬件配置的不同环境可能会决定以不同的方式组装拓扑。一种方式可以选择性能(RAID0),跨多个主机桥和端点进行内存条带化,而另一种方式可以选择容错并禁用 CXL.mem 拓扑中的任何条带化。
平台固件在“CXL 根端口”(Linux 术语,表示 CXL 解码拓扑的顶部)枚举一个交织选项菜单。从那里,PCIe 拓扑决定了哪些端点可以参与哪些主机桥解码方案。根端口和端点之间的路径中的每个 PCIe 交换机都引入了一个可以分割交织的点。例如,平台固件可能会说在给定的范围内仅解码到一个主机桥,但该主机桥可能会依次跨多个根端口交织周期。端口和端点之间的中间交换机可能会跨多个下游交换机端口交织周期,等等。
这是一个由“cxl_test”定义的 CXL 拓扑示例列表。“cxl_test”模块生成一个模拟 CXL 拓扑,包含 2 个主机桥,每个主机桥有 2 个根端口。每个根端口都连接到 2 路交换机,端点连接到这些下游端口,总共有 8 个端点
# cxl list -BEMPu -b cxl_test
{
"bus":"root3",
"provider":"cxl_test",
"ports:root3":[
{
"port":"port5",
"host":"cxl_host_bridge.1",
"ports:port5":[
{
"port":"port8",
"host":"cxl_switch_uport.1",
"endpoints:port8":[
{
"endpoint":"endpoint9",
"host":"mem2",
"memdev":{
"memdev":"mem2",
"pmem_size":"256.00 MiB (268.44 MB)",
"ram_size":"256.00 MiB (268.44 MB)",
"serial":"0x1",
"numa_node":1,
"host":"cxl_mem.1"
}
},
{
"endpoint":"endpoint15",
"host":"mem6",
"memdev":{
"memdev":"mem6",
"pmem_size":"256.00 MiB (268.44 MB)",
"ram_size":"256.00 MiB (268.44 MB)",
"serial":"0x5",
"numa_node":1,
"host":"cxl_mem.5"
}
}
]
},
{
"port":"port12",
"host":"cxl_switch_uport.3",
"endpoints:port12":[
{
"endpoint":"endpoint17",
"host":"mem8",
"memdev":{
"memdev":"mem8",
"pmem_size":"256.00 MiB (268.44 MB)",
"ram_size":"256.00 MiB (268.44 MB)",
"serial":"0x7",
"numa_node":1,
"host":"cxl_mem.7"
}
},
{
"endpoint":"endpoint13",
"host":"mem4",
"memdev":{
"memdev":"mem4",
"pmem_size":"256.00 MiB (268.44 MB)",
"ram_size":"256.00 MiB (268.44 MB)",
"serial":"0x3",
"numa_node":1,
"host":"cxl_mem.3"
}
}
]
}
]
},
{
"port":"port4",
"host":"cxl_host_bridge.0",
"ports:port4":[
{
"port":"port6",
"host":"cxl_switch_uport.0",
"endpoints:port6":[
{
"endpoint":"endpoint7",
"host":"mem1",
"memdev":{
"memdev":"mem1",
"pmem_size":"256.00 MiB (268.44 MB)",
"ram_size":"256.00 MiB (268.44 MB)",
"serial":"0",
"numa_node":0,
"host":"cxl_mem.0"
}
},
{
"endpoint":"endpoint14",
"host":"mem5",
"memdev":{
"memdev":"mem5",
"pmem_size":"256.00 MiB (268.44 MB)",
"ram_size":"256.00 MiB (268.44 MB)",
"serial":"0x4",
"numa_node":0,
"host":"cxl_mem.4"
}
}
]
},
{
"port":"port10",
"host":"cxl_switch_uport.2",
"endpoints:port10":[
{
"endpoint":"endpoint16",
"host":"mem7",
"memdev":{
"memdev":"mem7",
"pmem_size":"256.00 MiB (268.44 MB)",
"ram_size":"256.00 MiB (268.44 MB)",
"serial":"0x6",
"numa_node":0,
"host":"cxl_mem.6"
}
},
{
"endpoint":"endpoint11",
"host":"mem3",
"memdev":{
"memdev":"mem3",
"pmem_size":"256.00 MiB (268.44 MB)",
"ram_size":"256.00 MiB (268.44 MB)",
"serial":"0x2",
"numa_node":0,
"host":"cxl_mem.2"
}
}
]
}
]
}
]
}
在该列表中,每个“root”、“port”和“endpoint”对象对应一个内核“struct cxl_port
”对象。“cxl_port”是一个可以将其后代解码为 CXL.mem 的设备。因此,“root”声明非 PCIe 可枚举的平台解码范围并将它们解码为“ports”,“ports”解码为“endpoints”,而“endpoints”表示从 SPA(系统物理地址)到 DPA(设备物理地址)的解码。
继续 RAID 类比,磁盘既有拓扑元数据,也有设备上的元数据,用于确定 RAID 集的组装。CXL 端口拓扑和 CXL 端口链接状态是 CXL.mem 集组装的元数据。CXL 端口拓扑是通过 CXL.mem 设备的到达来枚举的。也就是说,除非 PCIe 核心将 cxl_pci 驱动程序附加到 CXL 内存扩展器,否则 CXL 端口对象没有任何作用。相反,对于热插拔/移除场景,Linux PCI 核心不需要拆除交换机级别的 CXL 资源,因为 endpoint ->remove() 事件会清理为支持该内存扩展器而建立的端口数据。
可以通过以下命令确定端口元数据和给定内存设备可能参与的潜在解码方案
# cxl list -BDMu -d root -m mem3
{
"bus":"root3",
"provider":"cxl_test",
"decoders:root3":[
{
"decoder":"decoder3.1",
"resource":"0x8030000000",
"size":"512.00 MiB (536.87 MB)",
"volatile_capable":true,
"nr_targets":2
},
{
"decoder":"decoder3.3",
"resource":"0x8060000000",
"size":"512.00 MiB (536.87 MB)",
"pmem_capable":true,
"nr_targets":2
},
{
"decoder":"decoder3.0",
"resource":"0x8020000000",
"size":"256.00 MiB (268.44 MB)",
"volatile_capable":true,
"nr_targets":1
},
{
"decoder":"decoder3.2",
"resource":"0x8050000000",
"size":"256.00 MiB (268.44 MB)",
"pmem_capable":true,
"nr_targets":1
}
],
"memdevs:root3":[
{
"memdev":"mem3",
"pmem_size":"256.00 MiB (268.44 MB)",
"ram_size":"256.00 MiB (268.44 MB)",
"serial":"0x2",
"numa_node":0,
"host":"cxl_mem.2"
}
]
}
...它查询 CXL 拓扑以询问“给定内核设备名称为‘mem3’的 CXL 内存扩展器,此设备可能参与哪些平台级别解码范围”。给定的扩展器可以同时参与多个 CXL.mem 交织集,具体取决于它有多少解码器资源。在此示例中,mem3 可以参与一个或多个跨主机桥的 PMEM 交织,一个针对单个主机桥的 PMEM 交织,一个跨 2 个主机桥的易失性内存交织,以及一个仅针对单个主机桥的易失性内存交织。
相反,可以通过以下命令确定可以参与给定平台级别解码方案的内存设备
# cxl list -MDu -d 3.2
[
{
"memdevs":[
{
"memdev":"mem1",
"pmem_size":"256.00 MiB (268.44 MB)",
"ram_size":"256.00 MiB (268.44 MB)",
"serial":"0",
"numa_node":0,
"host":"cxl_mem.0"
},
{
"memdev":"mem5",
"pmem_size":"256.00 MiB (268.44 MB)",
"ram_size":"256.00 MiB (268.44 MB)",
"serial":"0x4",
"numa_node":0,
"host":"cxl_mem.4"
},
{
"memdev":"mem7",
"pmem_size":"256.00 MiB (268.44 MB)",
"ram_size":"256.00 MiB (268.44 MB)",
"serial":"0x6",
"numa_node":0,
"host":"cxl_mem.6"
},
{
"memdev":"mem3",
"pmem_size":"256.00 MiB (268.44 MB)",
"ram_size":"256.00 MiB (268.44 MB)",
"serial":"0x2",
"numa_node":0,
"host":"cxl_mem.2"
}
]
},
{
"root decoders":[
{
"decoder":"decoder3.2",
"resource":"0x8050000000",
"size":"256.00 MiB (268.44 MB)",
"pmem_capable":true,
"nr_targets":1
}
]
}
]
...其中解码器的命名方案是“decoder<port_id>.<instance_id>”。
驱动程序基础设施¶
本节介绍 CXL 内存设备的驱动程序基础设施。
CXL 内存设备¶
这实现了 CXL 设备由 Compute Express Link 规范定义的 PCI 独有功能。即使未启用 CXL,CXL 设备也可能会显示某些功能。虽然此驱动程序专注于 CXL 设备的 PCI 特定方面,但它绑定到特定的 CXL 内存设备类代码,因此 cxl_pci 的实现专注于 CXL 内存设备。
- 该驱动程序有几个职责,主要包括
创建 memX 设备并在 CXL 总线上注册。
枚举设备的寄存器接口并映射它们。
使用 cxl_core 注册 nvdimm 桥接设备。
使用 cxl_core 注册 CXL 邮箱。
-
int __cxl_pci_mbox_send_cmd(struct cxl_mailbox *cxl_mbox, struct cxl_mbox_cmd *mbox_cmd)¶
执行邮箱命令
参数
struct cxl_mailbox *cxl_mbox
CXL 邮箱上下文
struct cxl_mbox_cmd *mbox_cmd
要发送到内存设备的命令。
上下文
任何上下文。需要持有 mbox_mutex。
返回
- -ETIMEDOUT 如果等待完成时发生超时。成功则返回 0。
调用者应检查 mbox_cmd 中的返回值,以确保它成功。
描述
这是 CXL 邮箱发送命令的通用形式,因此仅使用邮箱功能 ID 定义的寄存器 - CXL 2.0 8.2.8.4。内存设备,以及其他类型的 CXL 设备,可能在错误情况下提供更多信息。希望发送邮箱命令的驱动程序设施应使用包装器命令。
CXL 规范允许最多两个邮箱。目的是让主邮箱由操作系统控制,而辅助邮箱由系统固件使用。这允许操作系统和固件与设备通信,而无需相互协调。驱动程序仅使用主邮箱。
CXL 内存端点设备和交换机是参与 CXL.mem 协议的具有 CXL 功能的设备。它们的功能建立在 CXL.io 协议之上,该协议允许通过标准 PCI 机制枚举和配置组件。
cxl_mem 驱动程序拥有启动此 CXL.mem 功能枚举的权限。通过检测到具有 CXL 功能的端点,驱动程序将向上查找其连接到的平台特定端口,并确定路径中是否存在中间交换机。如果存在交换机,则辅助操作是枚举这些交换机(在 cxl_core 中实现)。最后,cxl_mem 驱动程序将它绑定的设备添加为 CXL 端点端口,以用于更高级别的操作。
-
struct cxl_memdev¶
表示 Type-3 内存设备的 CXL 总线对象
定义:
struct cxl_memdev {
struct device dev;
struct cdev cdev;
struct cxl_dev_state *cxlds;
struct work_struct detach_work;
struct cxl_nvdimm_bridge *cxl_nvb;
struct cxl_nvdimm *cxl_nvd;
struct cxl_port *endpoint;
int id;
int depth;
u8 scrub_cycle;
int scrub_region_id;
void *err_rec_array;
};
成员
dev
驱动程序核心设备对象
cdev
用于 ioctl 操作的字符设备核心对象
cxlds
支持此设备的设备状态
detach_work
活动 memdev 在其祖先中失去了一个端口
cxl_nvb
如果存在 cxl_nvd,则协调移除
cxl_nvd
如果设备支持 pmem,则可选的 nvdimm 桥
endpoint
此内存设备与 CXL 端口拓扑的连接
id
此 memdev 实例的 ID 号。
depth
端点端口深度
scrub_cycle
为此设备设置的当前 scrub 周期
scrub_region_id
为其设置当前 scrub 周期的支持区域的 ID 号(如果有)
err_rec_array
存储 memdev 错误记录的 xarrays 列表,用于检查内存修复操作的属性是否来自当前启动。
-
struct cxl_event_state¶
事件日志驱动程序状态
定义:
struct cxl_event_state {
struct cxl_get_event_payload *buf;
struct mutex log_lock;
};
成员
buf
用于接收事件数据的缓冲区
log_lock
序列化 event_buf 和日志使用
-
struct cxl_poison_state¶
驱动程序中毒状态信息
定义:
struct cxl_poison_state {
u32 max_errors;
unsigned long enabled_cmds[BITS_TO_LONGS(CXL_POISON_ENABLED_MAX)];
struct cxl_mbox_poison_out *list_out;
struct mutex lock;
};
成员
max_errors
设备缓存中保存的最大介质错误记录数
enabled_cmds
CEL 中启用的所有中毒命令
list_out
设备返回的中毒列表有效负载
lock
保护中毒列表的读取
描述
中毒列表的读取是同步的,以确保读取器不会获得不完整的列表,因为他们的请求重叠(被中断或前面有)同一 DPA 范围的另一个读取请求。CXL 规范 3.0 第 8.2.9.8.4.1 节
-
struct cxl_fw_state¶
固件上传/激活状态
定义:
struct cxl_fw_state {
unsigned long state[BITS_TO_LONGS(CXL_FW_STATE_BITS)];
bool oneshot;
int num_slots;
int cur_slot;
int next_slot;
};
成员
state
fw_uploader 状态位掩码
oneshot
固件上传是否适合单次传输
num_slots
可用的固件插槽数
cur_slot
当前活动的插槽号
next_slot
新固件的插槽号
-
struct cxl_security_state¶
设备安全状态
定义:
struct cxl_security_state {
unsigned long state;
unsigned long enabled_cmds[BITS_TO_LONGS(CXL_SEC_ENABLED_MAX)];
int poll_tmo_secs;
bool sanitize_active;
struct delayed_work poll_dwork;
struct kernfs_node *sanitize_node;
};
成员
state
上次安全操作的状态
enabled_cmds
CEL 中启用的所有安全命令
poll_tmo_secs
轮询超时
sanitize_active
待处理的清理完成
poll_dwork
轮询工作项
sanitize_node
要通知的清理 sysfs 文件
-
struct cxl_dpa_perf¶
DPA 性能属性条目
定义:
struct cxl_dpa_perf {
struct range dpa_range;
struct access_coordinate coord[ACCESS_COORDINATE_MAX];
struct access_coordinate cdat_coord[ACCESS_COORDINATE_MAX];
int qos_class;
};
成员
dpa_range
DPA 地址的范围
coord
QoS 性能数据(即延迟、带宽)
cdat_coord
来自 CDAT 的原始 QoS 性能数据
qos_class
QoS 类 Cookie
-
struct cxl_dpa_partition¶
DPA 分区描述符
定义:
struct cxl_dpa_partition {
struct resource res;
struct cxl_dpa_perf perf;
enum cxl_partition_mode mode;
};
成员
res
DPA 资源树 (cxlds->dpa_res) 中分区的快捷方式
perf
来自 CDAT 的分区的性能属性
mode
DPA 容量的操作模式,例如 ram、pmem、dynamic...
-
struct cxl_dev_state¶
驱动程序设备状态
定义:
struct cxl_dev_state {
struct device *dev;
struct cxl_memdev *cxlmd;
struct cxl_register_map reg_map;
struct cxl_regs regs;
int cxl_dvsec;
bool rcd;
bool media_ready;
struct resource dpa_res;
struct cxl_dpa_partition part[CXL_NR_PARTITIONS_MAX];
unsigned int nr_partitions;
u64 serial;
enum cxl_devtype type;
struct cxl_mailbox cxl_mbox;
#ifdef CONFIG_CXL_FEATURES;
struct cxl_features_state *cxlfs;
#endif;
};
成员
dev
与此 CXL 状态关联的设备
cxlmd
表示 dev 的 CXL.mem 功能的设备
reg_map
组件和 ras 寄存器映射参数
regs
已解析的寄存器块
cxl_dvsec
PCIe 设备 DVSEC 的偏移量
rcd
在 RCD 模式下运行 (CXL 3.0 9.11.8 连接到 RCH 的 CXL 设备)
media_ready
指示设备介质是否可用
dpa_res
设备的整体 DPA 资源树
part
DPA 分区数组
nr_partitions
DPA 分区数
serial
PCIe 设备序列号
type
通用内存类设备或供应商特定内存设备
cxl_mbox
CXL 邮箱上下文
cxlfs
CXL 功能上下文
描述
cxl_dev_state 表示 CXL 驱动程序/设备状态。它提供与邮箱命令的接口以及有关设备的一些缓存数据。目前仅表示内存设备。
-
struct cxl_memdev_state¶
通用 Type-3 内存设备类驱动程序数据
定义:
struct cxl_memdev_state {
struct cxl_dev_state cxlds;
size_t lsa_size;
char firmware_version[0x10];
u64 total_bytes;
u64 volatile_only_bytes;
u64 persistent_only_bytes;
u64 partition_align_bytes;
u64 active_volatile_bytes;
u64 active_persistent_bytes;
struct cxl_event_state event;
struct cxl_poison_state poison;
struct cxl_security_state security;
struct cxl_fw_state fw;
struct notifier_block mce_notifier;
};
成员
cxlds
跨 Type-2 和 Type-3 设备的核心驱动程序状态
lsa_size
标签存储区域的大小 (CXL 2.0 8.2.9.5.1.1 识别内存设备)
firmware_version
内存设备的固件版本。
total_bytes
所有可能容量的总和
volatile_only_bytes
硬易失性容量
persistent_only_bytes
硬持久性容量
partition_align_bytes
可分区容量的对齐大小
active_volatile_bytes
硬 + 软易失性容量的总和
active_persistent_bytes
硬 + 软持久性容量的总和
event
事件日志驱动程序状态
poison
中毒驱动程序状态信息
security
安全驱动程序状态信息
fw
固件上传/激活状态
mce_notifier
MCE 通知程序
描述
CXL 8.1.12.1 PCI 标头 - 类代码寄存器内存设备定义了通用内存设备功能,例如邮箱的存在以及与之相关的功能,例如识别内存设备和获取分区信息。有关容量参数的详细信息,请参见 CXL 3.0 8.2.9.8.2 容量配置和标签存储。
-
struct cxl_mem_command¶
内存设备命令的驱动程序表示
定义:
struct cxl_mem_command {
struct cxl_command_info info;
enum cxl_opcode opcode;
u32 flags;
#define CXL_CMD_FLAG_FORCE_ENABLE BIT(0);
};
成员
info
UAPI 中存在的命令信息
opcode
用于邮箱协议的实际位
flags
影响驱动程序行为的一组标志。
CXL_CMD_FLAG_FORCE_ENABLE
:在发生错误的情况下,无论硬件可能已通告什么,驱动程序都将启用带有此标志的命令。
描述
cxl_mem_command 是驱动程序内部表示驱动程序支持的命令。某些命令可能不受硬件支持。驱动程序将使用 info 来验证用户传入的字段,然后将 opcode 提交给硬件。
-
struct cxl_hdm¶
HDM 解码器寄存器和缓存/解码功能
定义:
struct cxl_hdm {
struct cxl_component_regs regs;
unsigned int decoder_count;
unsigned int target_count;
unsigned int interleave_mask;
unsigned long iw_cap_mask;
struct cxl_port *port;
};
成员
regs
映射的寄存器,参见
devm_cxl_setup_hdm()
decoder_count
此端口的解码器数量
target_count
对于交换机解码器,最大下游端口目标数
interleave_mask
交织粒度功能,参见 check_interleave_cap()
iw_cap_mask
支持的交织方式的位掩码,参见 check_interleave_cap()
port
映射的 cxl_port,参见
devm_cxl_setup_hdm()
-
void set_exclusive_cxl_commands(struct cxl_memdev_state *mds, unsigned long *cmds)¶
原子地禁用用户 cxl 命令
参数
struct cxl_memdev_state *mds
要操作的设备状态
unsigned long *cmds
要标记为独占的命令的位图
描述
以写入模式获取 cxl_memdev_rwsem 以刷新飞行中的 ioctl 路径调用,然后禁用未来执行在 cmds 中设置了命令 ID 的命令。
-
void clear_exclusive_cxl_commands(struct cxl_memdev_state *mds, unsigned long *cmds)¶
原子地启用用户 cxl 命令
参数
struct cxl_memdev_state *mds
要修改的设备状态
unsigned long *cmds
要标记为对用户空间可用的命令的位图
-
int cxl_mem_get_fw_info(struct cxl_memdev_state *mds)¶
获取固件信息
参数
struct cxl_memdev_state *mds
操作的设备数据
描述
检索指定设备的固件信息。
参见 CXL-3.0 8.2.9.3.1 获取固件信息
返回
如果没有错误,则返回 0;否则返回邮箱命令的结果。
-
int cxl_mem_activate_fw(struct cxl_memdev_state *mds, int slot)¶
激活固件
参数
struct cxl_memdev_state *mds
操作的设备数据
int slot
要激活的插槽号
描述
激活指定设备中给定插槽中的固件。
参见 CXL-3.0 8.2.9.3.3 激活固件
返回
如果没有错误,则返回 0;否则返回邮箱命令的结果。
-
int cxl_mem_abort_fw_xfer(struct cxl_memdev_state *mds)¶
中止正在进行的固件传输
参数
struct cxl_memdev_state *mds
操作的设备数据
描述
中止指定设备的正在进行的固件传输。
参见 CXL-3.0 8.2.9.3.2 传输固件
返回
如果没有错误,则返回 0;否则返回邮箱命令的结果。
CXL 端口¶
端口驱动程序通过 PCI 枚举 dport,并通过代理注册端口时传入的 component_reg_phys 值扫描 HDM(主机管理设备内存)解码器资源。CXL 根端口(由平台固件描述)的所有后代端口都在此驱动程序上下文中进行管理。每个驱动程序实例负责拆除直接后代端口的驱动程序上下文。此锁定的验证由 CONFIG_PROVE_CXL_LOCKING 完成。
此驱动程序提供的主要服务是向其他驱动程序提供 API 以利用解码器,并通过绑定状态向用户空间指示整个 PCIe 拓扑中 CXL.mem 协议的连接。
CXL Core¶
CXL 核心对象(如端口、解码器和区域)在子系统驱动程序 cxl_acpi、cxl_pci 和核心驱动程序(端口驱动程序、区域驱动程序、nvdimm 对象驱动程序等)之间共享。
-
struct cxl_register_map¶
DVSEC 收集的寄存器块映射参数
定义:
struct cxl_register_map {
struct device *host;
void __iomem *base;
resource_size_t resource;
resource_size_t max_size;
u8 reg_type;
union {
struct cxl_component_reg_map component_map;
struct cxl_device_reg_map device_map;
struct cxl_pmu_reg_map pmu_map;
};
};
成员
host
用于 devm 操作和日志记录的设备
base
寄存器块的虚拟基地址 - BAR + block_offset
resource
寄存器块的物理资源基地址
max_size
执行寄存器搜索的最大映射大小
reg_type
参见 enum cxl_regloc_type
{unnamed_union}
anonymous
component_map
组件寄存器的 cxl_reg_map
device_map
设备寄存器的 cxl_reg_maps
pmu_map
CXL 性能监视单元的 cxl_reg_maps
-
struct cxl_decoder¶
通用 CXL HDM 解码器属性
定义:
struct cxl_decoder {
struct device dev;
int id;
struct range hpa_range;
int interleave_ways;
int interleave_granularity;
enum cxl_decoder_type target_type;
struct cxl_region *region;
unsigned long flags;
int (*commit)(struct cxl_decoder *cxld);
void (*reset)(struct cxl_decoder *cxld);
};
成员
dev
此解码器的设备
id
内核设备名称 ID
hpa_range
此解码器映射的主机物理地址范围
interleave_ways
此解码中的 cxl_dports 数量
interleave_granularity
每个 dport 的数据步幅
target_type
加速器与扩展器(type2 与 type3)选择器
region
当前为此解码器分配的区域
flags
内存类型功能和锁定
commit
特定于设备/解码器类型的回调,用于将设置提交到硬件
reset
特定于设备/解码器类型的回调,用于重置硬件设置
-
struct cxl_endpoint_decoder¶
端点/ SPA 到 DPA 解码器
定义:
struct cxl_endpoint_decoder {
struct cxl_decoder cxld;
struct resource *dpa_res;
resource_size_t skip;
enum cxl_decoder_state state;
int part;
int pos;
};
成员
cxld
基本 cxl_decoder_object
dpa_res
此解码器主动声明的 DPA 跨度
skip
dpa_res 中的偏移量,其中映射 cxld.hpa_range
state
autodiscovery 状态
part
此解码器映射的分区索引
pos
cxld.region 中的交织位置
-
struct cxl_switch_decoder¶
交换机特定的 CXL HDM 解码器
定义:
struct cxl_switch_decoder {
struct cxl_decoder cxld;
int nr_targets;
struct cxl_dport *target[];
};
成员
cxld
基本 cxl_decoder 对象
nr_targets
target 中的元素数量
target
当前解码器配置中的活动有序目标列表
描述
“switch”解码器类型表示 cxl_port 的解码器实例,这些实例将 CXL 内存解码拓扑的根路由到端点。它们有两种类型:根级解码器,由平台固件静态定义;以及中间级解码器,其中交织粒度、交织宽度和目标列表是可变的。
-
struct cxl_root_decoder¶
静态平台 CXL 地址解码器
定义:
struct cxl_root_decoder {
struct resource *res;
atomic_t region_id;
cxl_hpa_to_spa_fn hpa_to_spa;
void *platform_data;
struct mutex range_lock;
int qos_class;
struct cxl_switch_decoder cxlsd;
};
成员
res
区域分配的主机/父资源
region_id
下一个区域配置事件的区域 ID
hpa_to_spa
将 CXL 主机物理地址转换为平台系统物理地址
platform_data
特定于平台的配置数据
range_lock
按地址范围同步区域自动发现
qos_class
QoS 性能类 Cookie
cxlsd
基本 cxl 交换机解码器
-
struct cxl_region_params¶
区域设置
定义:
struct cxl_region_params {
enum cxl_config_state state;
uuid_t uuid;
int interleave_ways;
int interleave_granularity;
struct resource *res;
struct cxl_endpoint_decoder *targets[CXL_DECODER_MAX_INTERLEAVE];
int nr_targets;
resource_size_t cache_size;
};
成员
state
允许驱动程序锁定进一步的参数更改
uuid
持久性区域的唯一 ID
interleave_ways
区域中的端点数量
interleave_granularity
每个端点对条带的贡献容量
res
为此区域分配的 iomem 容量
targets
当前解码器配置中的活动有序目标
nr_targets
目标数量
cache_size
如果存在,则为扩展线性缓存大小,否则为零。
描述
状态转换由 cxl_region_rwsem 保护
-
struct cxl_region¶
CXL 区域
定义:
struct cxl_region {
struct device dev;
int id;
enum cxl_partition_mode mode;
enum cxl_decoder_type type;
struct cxl_nvdimm_bridge *cxl_nvb;
struct cxl_pmem_region *cxlr_pmem;
unsigned long flags;
struct cxl_region_params params;
struct access_coordinate coord[ACCESS_COORDINATE_MAX];
struct notifier_block memory_notifier;
struct notifier_block adist_notifier;
};
成员
dev
此区域的设备
id
此区域的 ID。ID 在所有区域中都是全局唯一的
mode
映射容量的操作模式
type
端点解码器目标类型
cxl_nvb
用于协调 cxlr_pmem 设置/关闭的 nvdimm 桥
cxlr_pmem
(对于 pmem 区域)nvdimm 桥的缓存副本
flags
区域状态标志
params
区域的活动 + 配置参数
coord
区域的 QoS 访问坐标
memory_notifier
用于将访问坐标设置为节点的通知程序
adist_notifier
用于计算节点抽象距离的通知程序
-
struct cxl_port¶
上游端口设备和下游端口设备的逻辑集合,用于构建 CXL 内存解码层次结构。
定义:
struct cxl_port {
struct device dev;
struct device *uport_dev;
struct device *host_bridge;
int id;
struct xarray dports;
struct xarray endpoints;
struct xarray regions;
struct cxl_dport *parent_dport;
struct ida decoder_ida;
struct cxl_register_map reg_map;
int nr_dports;
int hdm_end;
int commit_end;
bool dead;
unsigned int depth;
struct cxl_cdat {
void *table;
size_t length;
} cdat;
bool cdat_available;
long pci_latency;
};
成员
dev
此端口的设备
uport_dev
实现上游端口功能的 PCI 或平台设备
host_bridge
此端口的平台附加点的快捷方式
id
端口设备名称的 ID
dports
解码器引用的 cxl_dport 实例
endpoints
cxl_ep 实例,是此端口后代的端点
regions
cxl_region_ref 实例,由此端口映射的区域
parent_dport
指向父端口中的该端口的 dport
decoder_ida
解码器 ID 的分配器
reg_map
组件和 ras 寄存器映射参数
nr_dports
dports 中的条目数
hdm_end
跟踪最后分配的 HDM 解码器实例以进行分配排序
commit_end
跟踪最高提交解码器的游标以进行提交排序
dead
已移除最后一个 ep,强制端口重新创建
depth
此端口相对于根端口的深度。深度 0 为根端口。
cdat
缓存的 CDAT 数据
cdat_available
sysfs 中是否应提供 CDAT 属性
pci_latency
上游延迟(皮秒)
-
struct cxl_root¶
根 cxl_port 项的逻辑集合
定义:
struct cxl_root {
struct cxl_port port;
const struct cxl_root_ops *ops;
};
成员
port
cxl_port 成员
ops
cxl 根操作
-
struct cxl_dport¶
CXL 下游端口
定义:
struct cxl_dport {
struct device *dport_dev;
struct cxl_register_map reg_map;
int port_id;
struct cxl_rcrb_info rcrb;
bool rch;
struct cxl_port *port;
struct cxl_regs regs;
struct access_coordinate coord[ACCESS_COORDINATE_MAX];
long link_latency;
int gpf_dvsec;
};
成员
dport_dev
表示下游链路的 PCI 桥或固件设备
reg_map
组件和 ras 寄存器映射参数
port_id
解码器目标列表中 dport 的唯一硬件标识符
rcrb
有关根复合体寄存器块布局的数据
rch
指示此 dport 是在 RCH 模式还是 VH 模式下枚举的
port
对包含此下游端口的 cxl_port 的引用
regs
已解析的 Dport 寄存器块
coord
访问坐标(带宽和延迟性能属性)
link_latency
计算的 PCIe 下游延迟
gpf_dvsec
缓存的 GPF 端口 DVSEC
-
struct cxl_ep¶
跟踪端点对端口的兴趣
定义:
struct cxl_ep {
struct device *ep;
struct cxl_dport *dport;
struct cxl_port *next;
};
成员
ep
托管通用 CXL 端点(扩展器或加速器)的设备
dport
哪个 dport 在 port 上路由到此端点
next
跨连接到 dport 的链路的 cxl 交换机端口,如果连接到端点则为 NULL
-
struct cxl_region_ref¶
跟踪区域对端口的兴趣
定义:
struct cxl_region_ref {
struct cxl_port *port;
struct cxl_decoder *decoder;
struct cxl_region *region;
struct xarray endpoints;
int nr_targets_set;
int nr_eps;
int nr_targets;
};
成员
port
拓扑中安装此参考的点
decoder
为 port 中的 region 分配的解码器
region
此参考的区域
endpoints
位于 port 下方的区域成员的 cxl_ep 参考
nr_targets_set
跟踪设置期间已编程的目标数
nr_eps
port 下方的端点数
nr_targets
到达 nr_eps 所需的不同目标数
-
struct cxl_endpoint_dvsec_info¶
缓存的 DVSEC 信息
定义:
struct cxl_endpoint_dvsec_info {
bool mem_enabled;
int ranges;
struct cxl_port *port;
struct range dvsec_range[2];
};
成员
mem_enabled
初始化时 DVSEC 中 mem_enabled 的缓存值
ranges
此设备使用的活动 HDM 范围的数量。
port
与此信息实例关联的端点端口
dvsec_range
DVSEC 中范围的缓存属性,PCIE_DEVICE
-
int add_cxl_resources(struct resource *cxl_res)¶
在 iomem_resource 中反映 CXL 固定内存窗口
参数
struct resource *cxl_res
一个独立的资源树,其中每个 CXL 窗口都是同级窗口
描述
遍历 cxl_res 中的每个 CXL 窗口,并将其添加到 iomem_resource,可能会扩展其边界以确保任何冲突的资源都成为子资源。如果窗口被扩展,则它可能与另一个窗口条目冲突,并需要截断或修剪窗口。考虑这种情况
|-- "CXL Window 0" --||----- "CXL Window 1" -----|
|--------------- "System RAM" -------------|
...其中平台固件已在 2 个窗口中建立为系统 RAM 资源,但已将窗口 1 的某些部分留用于动态 CXL 区域配置。在这种情况下,“窗口 0”将跨越整个“系统 RAM”跨度,而“CXL 窗口 1”将被截断到该“系统 RAM”资源末尾之后的剩余尾部。
Compute Express Link Host Managed Device Memory(主机托管设备内存),从 CXL 2.0 规范开始,由每个 CXL 端口和每个 CXL 端点的一系列 HDM 解码器寄存器实例管理。定义用于枚举这些寄存器和功能的常用助手。
-
struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port, struct cxl_endpoint_dvsec_info *info)¶
映射 HDM 解码器组件寄存器
参数
struct cxl_port *port
要映射的 cxl_port
struct cxl_endpoint_dvsec_info *info
缓存的 DVSEC 范围寄存器信息
-
int request_skip(struct cxl_dev_state *cxlds, struct cxl_endpoint_decoder *cxled, const resource_size_t skip_base, const resource_size_t skip_len)¶
在 cxlds->dpa_res 资源树中跟踪 DPA“跳过”
参数
struct cxl_dev_state *cxlds
CXL.mem 设备上下文,它是 cxled 的父级
struct cxl_endpoint_decoder *cxled
端点解码器建立跳过较低 DPA 的新分配
const resource_size_t skip_base
DPA < 新 DPA 分配的开始(DPAnew)
const resource_size_t skip_len
skip_base + skip_len == DPAnew
描述
相对于多个分区中的可用容量,DPA“跳过”源自乱序 DPA 分配事件。这是一个浪费的事件,因为可用的 DPA 被丢弃,但如果部署有例如双 RAM+PMEM 设备,想要使用 PMEM,并且有未分配的 RAM DPA,则必须牺牲空闲 RAM DPA 才能开始分配 PMEM。有关更多详细信息,请参阅 CXL 3.1 8.2.4.19.13 “解码器保护”中的第三个“实现说明”。
“跳过”始终覆盖先前分区中最后分配的 DPA 到当前分区要分配的开始。分配永远不会在分区的中间开始,并且分配始终以相反的顺序解除分配(请参阅 cxl_dpa_free(),或来自强制按顺序分配的自然 devm 展开顺序)。
如果保证 cxlds->nr_partitions <= 2,则“跳过”将始终包含在单个分区中。鉴于 cxlds->nr_partitions 可能 > 2,这会导致“跳过”可能跨越“partition[0] 的尾部容量,partition[1] 的全部,...,partition[N-1] 的全部”以支持从 partition[N] 进行分配的情况。反过来,这与 cxlds->dpa_res 中的分区“struct resource”边界交互,从而需要按分区划分“跳过”请求。也就是说,这是使用“struct resource”树来检测范围冲突,同时也在 cxlds->dpa_res 中跟踪分区边界的怪癖。
-
int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm, struct cxl_endpoint_dvsec_info *info)¶
为每个 HDM 寄存器集添加解码器对象
参数
struct cxl_hdm *cxlhdm
要使用 HDM 功能填充的结构
struct cxl_endpoint_dvsec_info *info
缓存的 DVSEC 范围寄存器信息
-
void cxl_coordinates_combine(struct access_coordinate *out, struct access_coordinate *c1, struct access_coordinate *c2)¶
组合两个输入坐标
参数
struct access_coordinate *out
c1 和 c2 的组合的输出坐标
struct access_coordinate *c1
输入坐标
struct access_coordinate *c2
输入坐标
-
int cxl_endpoint_gather_bandwidth(struct cxl_region *cxlr, struct cxl_endpoint_decoder *cxled, struct xarray *usp_xa, bool *gp_is_root)¶
在一个 xarray 中收集所有端点带宽
参数
struct cxl_region *cxlr
用于带宽计算的 CXL 区域
struct cxl_endpoint_decoder *cxled
要开始的端点解码器
struct xarray *usp_xa
(输出)收集所有带宽坐标的 xarray,按上游设备索引,数据为“struct cxl_perf_ctx”。
bool *gp_is_root
(输出)祖父母是否为 cxl 根的布尔值。
返回
成功则为 0,或 -errno
描述
收集聚合的端点带宽,并将带宽存储在 xarray 中,该 xarray 由交换机的上游设备或 RP 设备索引。每个端点都包含来自端点 CDAT 的 DSLBIS、端点上游链路带宽以及来自交换机 CDAT 的 SSLBIS(用于交换机上游端口到与端点关联的下游端口)的最小带宽。如果设备直接连接到 RP,则不涉及 SSLBIS。
-
struct xarray *cxl_switch_gather_bandwidth(struct cxl_region *cxlr, struct xarray *input_xa, bool *gp_is_root)¶
在一个 xarray 中收集交换机级别的所有带宽
参数
struct cxl_region *cxlr
正在操作的区域
struct xarray *input_xa
由交换机的上游设备索引的 xarray,数据为“struct cxl_perf_ctx”
bool *gp_is_root
(输出)祖父母是否为 cxl 根的布尔值。
返回
- 每个父交换机或根端口的 resulting cxl_perf_ctx 的 xarray
或 ERR_PTR(-errno)
描述
迭代 xarray。获取下游计算带宽、上游链路带宽和上游交换机(如果存在)的 SSLBIS 的最小值。对交换机上游设备或 RP 设备下的结果带宽求和。如果存在多个交换机,则可以对该函数进行多次迭代。
参数
struct xarray *xa
保存 cxl_perf_ctx 的 xarray,该 cxl_perf_ctx 具有每个根端口设备下方计算的带宽。
返回
保存每个主机桥的 cxl_perf_ctx 的 xarray 或 ERR_PTR(-errno)
参数
struct xarray *xa
保存 cxl_perf_ctx 的 xarray,该 cxl_perf_ctx 具有每个主机桥下方计算的带宽。
返回
保存每个 ACPI0017 设备的 cxl_perf_ctx 的 xarray 或 ERR_PTR(-errno)
-
void cxl_region_update_bandwidth(struct cxl_region *cxlr, struct xarray *input_xa)¶
更新区域的带宽访问坐标
参数
struct cxl_region *cxlr
正在操作的区域
struct xarray *input_xa
Xarray 保存每个 ACPI0017 实例的具有计算带宽的 cxl_perf_ctx
重新计算区域的带宽
参数
struct cxl_region *cxlr
要重新计算的 cxl 区域
描述
该函数从下到上遍历拓扑并计算带宽。它从端点开始,在交换机(如果有)处进行处理,在根端口级别进行处理,在主机桥级别进行处理,最后在区域进行聚合。
CXL 核心提供一组可由 CXL 感知驱动程序使用的接口。这些接口允许创建、修改和销毁区域、内存设备、端口和解码器。CXL 感知驱动程序必须通过这些接口向 CXL 核心注册,以便能够参与跨设备交错协调。CXL 核心还建立和维护与 nvdimm 子系统的桥梁。
CXL 核心引入 sysfs 层次结构来控制由核心实例化的设备。
-
struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport_dev, resource_size_t component_reg_phys, struct cxl_dport *parent_dport)¶
在 CXL 内存解码层次结构中注册 cxl_port
参数
struct device *host
devm 操作的主机设备
struct device *uport_dev
实现此上游端口的“物理”设备
resource_size_t component_reg_phys
(可选)用于可配置的 cxl_port 实例
struct cxl_dport *parent_dport
CXL 内存解码层次结构中的下一跳
-
struct cxl_dport *devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev, int port_id, resource_size_t component_reg_phys)¶
将 VH 下游端口数据附加到 cxl_port
参数
struct cxl_port *port
引用此 dport 的 cxl_port
struct device *dport_dev
表示 dport 的固件或 PCI 设备
int port_id
解码器目标列表中此 dport 的标识符
resource_size_t component_reg_phys
CXL 组件寄存器的可选位置
描述
请注意,dports 会附加到端口主机(对于根端口)或端口本身(对于交换机端口)的 devm 发布操作。
-
struct cxl_dport *devm_cxl_add_rch_dport(struct cxl_port *port, struct device *dport_dev, int port_id, resource_size_t rcrb)¶
将 RCH 下游端口数据附加到 cxl_port
参数
struct cxl_port *port
引用此 dport 的 cxl_port
struct device *dport_dev
表示 dport 的固件或 PCI 设备
int port_id
解码器目标列表中此 dport 的标识符
resource_size_t rcrb
根复合体寄存器块的强制位置
描述
请参阅 CXL 3.0 9.11.8 连接到 RCH 的 CXL 设备
参数
struct cxl_dport *dport
路由到 ep_dev 的 dport
struct device *ep_dev
表示端点的设备
描述
中间 CXL 端口根据端点的到达进行扫描。当这些端点离开时,一旦所有关心该端口的端点都已移除,就可以销毁该端口。
-
int cxl_decoder_init(struct cxl_port *port, struct cxl_decoder *cxld)¶
常见的解码器设置/初始化
参数
struct cxl_port *port
此解码器的拥有端口
struct cxl_decoder *cxld
要初始化的常见解码器属性
描述
一个端口可能包含一个或多个解码器。这些解码器中的每一个都启用了 CXL.mem 利用的一些地址空间。预计解码器在通过 cxl_decoder_add()
注册之前由调用方配置
-
struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port, unsigned int nr_targets)¶
分配根级别解码器
参数
struct cxl_port *port
此解码器的拥有 CXL 根
unsigned int nr_targets
下游目标的静态数量
返回
要通过 cxl_decoder_add()
注册的新 cxl 解码器。“CXL 根”解码器是一种将 CXL 资源从顶层/静态平台固件描述解码到 CXL 标准解码拓扑中的解码器。
-
struct cxl_switch_decoder *cxl_switch_decoder_alloc(struct cxl_port *port, unsigned int nr_targets)¶
分配交换机级别解码器
参数
struct cxl_port *port
此解码器的拥有 CXL 交换机端口
unsigned int nr_targets
可动态寻址的下游目标的最大数量
返回
要通过 cxl_decoder_add()
注册的新 cxl 解码器。“交换机”解码器是可以由 PCIe 拓扑和 HDM 解码器功能枚举的任何解码器。这包括位于交换机上游端口/交换机下游端口和主机桥/根端口之间的解码器。
-
struct cxl_endpoint_decoder *cxl_endpoint_decoder_alloc(struct cxl_port *port)¶
分配端点解码器
-
int cxl_decoder_add_locked(struct cxl_decoder *cxld, int *target_map)¶
添加具有目标的解码器
参数
struct cxl_decoder *cxld
由 cxl_
_decoder_alloc() 分配的 cxl 解码器 int *target_map
此解码器可以将内存流量定向到的下游端口的列表。这些数字应与 PCIe 链路功能结构中的端口号相对应。
描述
某些类型的解码器可能没有任何目标。这方面的主要示例是端点设备。一个更令人尴尬的例子是主机桥,其根端口被热添加(技术上可能,尽管不太可能)。
这是 cxl_decoder_add()
的锁定变体。
上下文
进程上下文。期望拥有 cxld 的端口的设备锁被持有。
返回
- 如果解码器未正确配置,则为负错误代码;否则
返回 0。
-
int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map)¶
添加具有目标的解码器
参数
struct cxl_decoder *cxld
由 cxl_
_decoder_alloc() 分配的 cxl 解码器 int *target_map
此解码器可以将内存流量定向到的下游端口的列表。这些数字应与 PCIe 链路功能结构中的端口号相对应。
描述
这是 cxl_decoder_add_locked()
的未锁定变体。请参阅 cxl_decoder_add_locked()
。
上下文
处理上下文。获取和释放拥有 cxld 的端口的设备锁。
-
int __cxl_driver_register(struct cxl_driver *cxl_drv, struct module *owner, const char *modname)¶
为 cxl 总线注册驱动程序
参数
struct cxl_driver *cxl_drv
要附加的 cxl 驱动程序结构
struct module *owner
拥有模块/驱动程序
const char *modname
父驱动程序的 KBUILD_MODNAME
-
int cxl_endpoint_get_perf_coordinates(struct cxl_port *port, struct access_coordinate *coord)¶
检索存储在 CXL 路径的 dport 中的性能数字
参数
struct cxl_port *port
端点 cxl_port
struct access_coordinate *coord
输出性能数据
返回
失败时返回 errno,成功时返回 0。
Compute Express Link 协议分层在 PCIe 之上。CXL 核心为 CXL 交互提供了一组助手函数,这些交互通过 PCIe 发生。
参数
struct cxl_port *port
cxl_port,其 ->uport_dev 是要枚举的 dport 的上游
描述
返回枚举的 dport 的正数或负错误代码。
-
int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm, struct cxl_endpoint_dvsec_info *info)¶
为端点设置 HDM 解码
参数
struct cxl_dev_state *cxlds
设备状态
struct cxl_hdm *cxlhdm
映射的 HDM 解码器功能
struct cxl_endpoint_dvsec_info *info
缓存的 DVSEC 范围寄存器信息
描述
尝试启用端点的 HDM 解码器功能
参数
struct cxl_port *port
从中读取数据的端口
描述
此调用将休眠等待来自 DOE 邮箱的响应。
-
void cxl_dport_init_ras_reporting(struct cxl_dport *dport, struct device *host)¶
在此 dport 上设置 CXL RAS 报告
参数
struct cxl_dport *dport
需要初始化的 cxl_dport
struct device *host
devm 操作的主机设备
-
long cxl_pci_get_latency(struct pci_dev *pdev)¶
计算 PCIe 链路的链路延迟
参数
struct pci_dev *pdev
PCI 设备
返回
计算出的延迟或 0(无延迟)
描述
CXL 内存设备 SW 指南 v1.0 2.11.4 链路延迟计算 链路延迟 = 链路传播延迟 + Flit 延迟 + Retimer 延迟 LinkProgationLatency 可以忽略不计,因此将使用 0 RetimerLatency 假定可以忽略不计,因此将使用 0 FlitLatency = FlitSize / LinkBandwidth FlitSize 由规范定义。CXL rev3.0 4.2.1。 68B flit 用于高达 32GT/s。 >32GT/s,使用 256B flit 大小。 FlitLatency 转换为皮秒。
核心 CXL PMEM 基础设施支持持久性内存配置,并充当与 LIBNVDIMM 子系统的桥梁。如果平台固件至少通告一个支持持久性内存的 CXL 窗口,则会在 CXL 设备拓扑的根添加一个 CXL“桥”设备。该根级别桥对应于 LIBNVDIMM “总线”设备。然后,对于 CXL 设备拓扑中的每个 cxl_memdev,都会添加一个桥设备来托管 LIBNVDIMM dimm 对象。当这些桥注册时,原生 LIBNVDIMM uapi 会转换为 CXL 操作,例如,命名空间标签访问命令。
参数
struct cxl_port *port
与 nvdimm-bridge 关联的 root-cxl-port 的任何后代端口
-
struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host, struct cxl_port *port)¶
添加 LIBNVDIMM 拓扑的根
参数
struct device *host
平台固件根设备
struct cxl_port *port
CXL 拓扑根目录的 CXL 端口
返回
可以托管 cxl_nvdimm 对象的桥设备
-
int devm_cxl_add_nvdimm(struct cxl_port *parent_port, struct cxl_memdev *cxlmd)¶
在 cxl_memdev 和 nvdimm 之间添加桥梁
参数
struct cxl_port *parent_port
(要添加的)cxlmd 端点端口的父端口
struct cxl_memdev *cxlmd
将执行 LIBNVDIMM 操作的 cxl_memdev 实例
返回
成功时返回 0,失败时返回负错误代码。
CXL 设备功能由 PCI DVSEC(指定供应商特定)和/或平台固件提供的描述符枚举。 它们可以定义为一组,例如 CXL 第 8.1.12.2 节内存设备 PCIe 功能和扩展功能授权的设备和组件寄存器,或者它们可以是附加到桥接和端点设备的单个功能。
提供用于枚举和映射这些离散功能的通用基础设施。
-
void cxl_probe_component_regs(struct device *dev, void __iomem *base, struct cxl_component_reg_map *map)¶
检测 CXL 组件寄存器块
参数
struct device *dev
base 映射的主机设备
void __iomem *base
包含 HDM 解码器功能标头的映射
struct cxl_component_reg_map *map
描述找到的寄存器块信息的映射对象
描述
请参阅 CXL 2.0 8.2.4 组件寄存器布局和定义 请参阅 CXL 2.0 8.2.5.5 CXL 设备寄存器接口
探测组件寄存器信息并在映射对象中返回它。
-
void cxl_probe_device_regs(struct device *dev, void __iomem *base, struct cxl_device_reg_map *map)¶
检测 CXL 设备寄存器块
参数
struct device *dev
base 映射的主机设备
void __iomem *base
CXL 2.0 8.2.8 CXL 设备寄存器接口的映射
struct cxl_device_reg_map *map
描述找到的寄存器块信息的映射对象
描述
探测设备寄存器信息并在映射对象中返回它。
-
int cxl_find_regblock_instance(struct pci_dev *pdev, enum cxl_regloc_type type, struct cxl_register_map *map, unsigned int index)¶
按类型/索引查找寄存器块
参数
struct pci_dev *pdev
要枚举的 CXL PCI 设备。
enum cxl_regloc_type type
寄存器块指示器 id
struct cxl_register_map *map
枚举输出,错误时被破坏
unsigned int index
寄存器定位器 DVSEC 中找到的所需 regblock 的特定实例的索引。
返回
如果寄存器块已枚举,则为 0;否则为负错误代码
描述
CXL DVSEC 可能会指向一个或多个寄存器块,按 type 和 index 搜索它们。
-
int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type, struct cxl_register_map *map)¶
按类型查找寄存器块
参数
struct pci_dev *pdev
要枚举的 CXL PCI 设备。
enum cxl_regloc_type type
寄存器块指示器 id
struct cxl_register_map *map
枚举输出,错误时被破坏
返回
如果寄存器块已枚举,则为 0;否则为负错误代码
描述
CXL DVSEC 可能会指向一个或多个寄存器块,按 type 搜索它们。
-
int cxl_count_regblock(struct pci_dev *pdev, enum cxl_regloc_type type)¶
计算给定 regblock 类型的实例。
参数
struct pci_dev *pdev
要枚举的 CXL PCI 设备。
enum cxl_regloc_type type
寄存器块指示器 id
描述
某些 regblock 可能会重复。 计算有多少个实例。
返回
匹配 regblock 的非负计数,否则为负错误代码。
CXL 2.0 Type-3 内存设备邮箱的核心实现。 该实现由 cxl_pci 驱动程序用于初始化设备并实现 cxl_mem.h IOCTL UAPI。 它还实现了用于 LIBNVDIMM 的 cxl_pmem_ctl() 传输的后端。
-
int cxl_internal_send_cmd(struct cxl_mailbox *cxl_mbox, struct cxl_mbox_cmd *mbox_cmd)¶
内核内部接口,用于发送邮箱命令
参数
struct cxl_mailbox *cxl_mbox
CXL 邮箱上下文
struct cxl_mbox_cmd *mbox_cmd
要执行的已初始化命令
上下文
任何上下文。
返回
%>=0 - 在 out 中返回的字节数。
-E2BIG
- 有效负载对于硬件来说太大。
-EBUSY
- 无法获得独占邮箱访问权限。
-EFAULT
- 发生硬件错误。
-ENXIO
- 命令已完成,但设备报告了错误。
-EIO
- 意外的输出大小。
描述
邮箱命令可能会成功执行,但设备本身报告了错误。 虽然这种区别对于来自用户空间的命令可能很有用,但内核只能在两者都成功时使用结果。
-
bool cxl_payload_from_user_allowed(u16 opcode, void *payload_in)¶
检查 in_payload 的内容。
参数
u16 opcode
邮箱命令操作码。
void *payload_in
指向从用户空间传入的输入有效负载的指针。
返回
true - payload_in 通过了 opcode 的检查。
false - payload_in 包含无效或不支持的值。
描述
驱动程序可能会在将来自用户空间的邮箱命令发送到设备之前检查有效负载内容。 其目的是拒绝具有已知不安全的输入有效负载的命令。 此检查并非旨在取代用户对邮箱命令参数的仔细选择,也不保证用户命令会成功或适当。
特定检查由操作码确定。
-
int cxl_validate_cmd_from_user(struct cxl_mbox_cmd *mbox_cmd, struct cxl_mailbox *cxl_mbox, const struct cxl_send_command *send_cmd)¶
检查 CXL_MEM_SEND_COMMAND 的字段。
参数
struct cxl_mbox_cmd *mbox_cmd
已清理和填充的
struct cxl_mbox_cmd
。struct cxl_mailbox *cxl_mbox
CXL 邮箱上下文
const struct cxl_send_command *send_cmd
从用户空间复制的
struct cxl_send_command
。
返回
0
- out_cmd 准备好发送。
-ENOTTY
- 指定了无效命令。
-EINVAL
- 使用了保留字段或无效值。
-ENOMEM
- 输入或输出缓冲区大小不正确。
-EPERM
- 尝试使用受保护的命令。
-EBUSY
- 内核已声明对此操作码的独占访问权限
描述
此命令的结果是 mbox_cmd 中完全验证的命令,可以安全地发送到硬件。
-
int handle_mailbox_cmd_from_user(struct cxl_mailbox *cxl_mbox, struct cxl_mbox_cmd *mbox_cmd, u64 out_payload, s32 *size_out, u32 *retval)¶
为用户空间分派邮箱命令。
参数
struct cxl_mailbox *cxl_mbox
操作的邮箱上下文。
struct cxl_mbox_cmd *mbox_cmd
经过验证的邮箱命令。
u64 out_payload
指向用户空间的输出有效负载的指针。
s32 *size_out
(输入)要复制出的最大有效负载大小。(输出)硬件生成的有效负载大小。
u32 *retval
来自操作的硬件生成的返回代码。
返回
0
- 邮箱事务成功。 这意味着邮箱协议已成功完成,而不是操作本身成功。
-ENOMEM
- 无法分配反弹缓冲区。
-EFAULT
- copy_to/from_user 发生了某些情况。
-EINTR
- 邮箱获取中断。
-EXXX
- 事务级别故障。
描述
代表用户空间请求分派邮箱命令。 输出有效负载已复制到用户空间。
请参阅 cxl_send_cmd()。
-
void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)¶
遍历命令效果日志。
参数
struct cxl_memdev_state *mds
操作的驱动程序数据
size_t size
命令效果日志的长度。
u8 *cel
CEL
描述
迭代 CEL 中的每个条目,并确定驱动程序是否支持该命令。 如果是,则该命令已为设备启用,并且可以在以后使用。
-
int cxl_enumerate_cmds(struct cxl_memdev_state *mds)¶
枚举设备的命令。
参数
struct cxl_memdev_state *mds
操作的驱动程序数据
描述
如果枚举成功完成,则返回 0。
CXL 设备可选地支持某些命令。 此函数将确定硬件支持的命令集,并更新 mds 中的 enabled_cmds 位图。
-
void cxl_mem_get_event_records(struct cxl_memdev_state *mds, u32 status)¶
从设备获取事件记录
参数
struct cxl_memdev_state *mds
操作的驱动程序数据
u32 status
事件状态寄存器值,用于标识哪些事件可用。
描述
检索设备上所有可用的事件记录,将其报告为跟踪事件,并清除它们。
请参阅 CXL rev 3.0 8.2.9.2.2 获取事件记录 请参阅 CXL rev 3.0 8.2.9.2.3 清除事件记录
-
int cxl_mem_get_partition_info(struct cxl_memdev_state *mds)¶
获取分区信息
参数
struct cxl_memdev_state *mds
操作的驱动程序数据
描述
检索指定设备的当前分区信息。 活动值是以字节为单位的当前容量。 如果不为 0,“next”值是以字节为单位的待定值,将在下次冷复位时生效。
请参阅 CXL 8.2.9.5.2.1 获取分区信息
返回
如果没有错误,则返回 0;否则返回邮箱命令的结果。
-
int cxl_dev_state_identify(struct cxl_memdev_state *mds)¶
将 IDENTIFY 命令发送到设备。
参数
struct cxl_memdev_state *mds
操作的驱动程序数据
返回
如果 identify 已成功执行或介质未准备好,则返回 0。
描述
这会将 identify 命令分派到设备,并在成功时填充要导出到 sysfs 的结构。
-
int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)¶
将清理命令发送到设备。
参数
struct cxl_memdev *cxlmd
操作的设备
u16 cmd
特定的清理命令操作码
返回
如果命令已成功执行,则返回 0,无论实际的安全操作是否在后台完成,例如对于清理的情况。 错误返回值可能是邮箱命令的结果,如果不满足安全要求或上下文无效,则为 -EINVAL;如果清理操作已经在进行中,则为 -EBUSY。
描述
请参阅 CXL 3.0 8.2.9.8.5.1 清理和 8.2.9.8.5.2 安全擦除。
CXL 功能:包括邮箱的 CXL 设备支持允许列出、获取和设置可选定义的功能的命令,例如内存备用或后封装备用。 供应商可以为设备定义自定义功能。
有关 API 详细信息,请参阅 devm_cxl_setup_features()
。
CXL 区域¶
CXL 区域表示系统物理地址空间中映射的内存容量。 虽然 CXL 根解码器标识潜在 CXL 内存范围的边界,但区域表示主机桥、交换机和拓扑中端点的 HDM 解码器功能结构映射的活动容量。
区域配置具有排序约束。 UUID 可以随时设置,但仅对持久性区域可见。 1. 交错粒度 2. 交错大小 3. 解码器目标
-
struct cxl_decoder *cxl_port_pick_region_decoder(struct cxl_port *port, struct cxl_endpoint_decoder *cxled, struct cxl_region *cxlr)¶
为区域分配或查找解码器
参数
struct cxl_port *port
cxled 暗示的端点的祖先端口
struct cxl_endpoint_decoder *cxled
要由 port 映射或当前映射的端点解码器
struct cxl_region *cxlr
要建立或验证解码 port 的区域
描述
在区域创建路径中,cxl_port_pick_region_decoder()
是一个分配器,用于查找空闲端口。 在区域组装路径中,它是调用平台固件选择的解码器以用于验证目的。
结果记录在 port 中的 'struct cxl_region_ref
' 中。
-
int cxl_port_attach_region(struct cxl_port *port, struct cxl_region *cxlr, struct cxl_endpoint_decoder *cxled, int pos)¶
通过端点跟踪区域对端口的兴趣
参数
struct cxl_port *port
要添加新区域引用“
struct cxl_region_ref
”的端口struct cxl_region *cxlr
要附加到 port 的区域
struct cxl_endpoint_decoder *cxled
用于创建或进一步固定区域引用的端点解码器
int pos
cxled 在 cxlr 中的交错位置
描述
附加事件是一个验证 CXL 解码设置约束和记录编程 HDM 解码器所需的元数据的机会,特别是解码器目标列表。
步骤如下:
验证是否没有其他具有更高 HPA 的区域已经与 port 关联
如果区域引用尚不存在,则建立一个
此外,分配一个解码器实例,该实例将在 port 上托管 cxlr
通过端点固定区域引用
说明需要多少 port 的目标列表条目来覆盖所有添加的端点。
-
int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled)¶
计算区域中的端点位置
参数
struct cxl_endpoint_decoder *cxled
给定区域的端点解码器成员
描述
端点位置是通过从端点到根解码器的拓扑结构遍历,并迭代应用此计算来计算的
position = position * parent_ways + parent_pos;
...其中 position 是从交换机和根解码器目标列表推断出来的。
返回
- 成功时 position >= 0
失败时 -ENXIO
-
struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd, int id, enum cxl_partition_mode mode, enum cxl_decoder_type type)¶
向解码器添加区域
参数
struct cxl_root_decoder *cxlrd
根解码器
int id
要创建的 memregion id,或失败时 memregion_free()
enum cxl_partition_mode mode
此区域的端点解码器的模式
enum cxl_decoder_type type
选择这是扩展器还是加速器(type-2 或 type-3)
描述
这是区域初始化的第二步。区域存在于一个地址空间中,该地址空间由 cxlrd 映射。
返回
如果该区域已添加到 cxlrd,则为 0,否则返回负错误代码。该区域将被命名为“regionZ”,其中 Z 是唯一的区域编号。
-
int devm_cxl_add_pmem_region(struct cxl_region *cxlr)¶
添加 cxl_region 到 nd_region 的桥梁
参数
struct cxl_region *cxlr
此 pmem 区域桥设备的上层 CXL 区域
返回
成功时返回 0,失败时返回负错误代码。
外部接口¶
CXL IOCTL 接口¶
并非驱动程序支持的所有命令都可以在任何时候供用户空间使用。用户空间可以检查 QUERY 命令的结果以确定实时命令集。或者,它可以发出命令并检查是否失败。
-
struct cxl_command_info¶
从查询返回的命令信息。
定义:
struct cxl_command_info {
__u32 id;
__u32 flags;
#define CXL_MEM_COMMAND_FLAG_MASK GENMASK(1, 0);
#define CXL_MEM_COMMAND_FLAG_ENABLED BIT(0);
#define CXL_MEM_COMMAND_FLAG_EXCLUSIVE BIT(1);
__u32 size_in;
__u32 size_out;
};
成员
id
命令的 ID 号。
flags
指定命令行为的标志。
CXL_MEM_COMMAND_FLAG_USER_ENABLED
给定的命令 ID 受驱动程序支持,并且受设备上相关操作码的支持。
CXL_MEM_COMMAND_FLAG_EXCLUSIVE
具有给定命令 ID 的请求将以 EBUSY 终止,因为内核主动拥有给定资源的管理。例如,当内核主动管理该空间时,无法写入标签存储区域。
size_in
预期输入大小,如果长度可变,则为 ~0。
size_out
预期输出大小,如果长度可变,则为 ~0。
描述
表示驱动程序和硬件都支持的单个命令。这是作为查询 ioctl 的数组的一部分返回的。以下命令将采用可变长度的输入并返回 0 字节的输出。
id = 10
flags = CXL_MEM_COMMAND_FLAG_ENABLED
size_in = ~0
size_out = 0
参见 struct cxl_mem_query_commands
。
-
struct cxl_mem_query_commands¶
查询支持的命令。
定义:
struct cxl_mem_query_commands {
__u32 n_commands;
__u32 rsvd;
struct cxl_command_info __user commands[];
};
成员
n_commands
输入/输出参数。当 n_commands > 0 时,驱动程序将返回 min(num_support_commands, n_commands)。当 n_commands 为 0 时,驱动程序将返回支持的命令的总数。
rsvd
保留供将来使用。
commands
支持的命令的输出数组。此数组必须由用户空间分配,至少为 min(num_support_commands, n_commands)
描述
允许用户空间查询驱动程序和硬件都支持的可用命令。查询中不会返回驱动程序或硬件不支持的命令。
例子
{ .n_commands = 0 } // 获取支持的命令数
{ .n_commands = 15, .commands = buf } // 返回前 15 个(或更少)支持的命令
-
struct cxl_send_command¶
向内存设备发送命令。
定义:
struct cxl_send_command {
__u32 id;
__u32 flags;
union {
struct {
__u16 opcode;
__u16 rsvd;
} raw;
__u32 rsvd;
};
__u32 retval;
struct {
__u32 size;
__u32 rsvd;
__u64 payload;
} in;
struct {
__u32 size;
__u32 rsvd;
__u64 payload;
} out;
};
成员
id
要发送到内存设备的命令。这必须是查询命令返回的命令之一。
flags
命令的标志(输入)。
{unnamed_union}
anonymous
raw
原始命令的特殊字段
raw.opcode
使用 RAW 命令时传递给硬件的操作码。
raw.rsvd
必须为零。
rsvd
必须为零。
retval
来自内存设备的返回值(输出)。
in
与输入有效负载关联的参数。
in.size
提供给设备的有效负载的大小(输入)。
in.rsvd
必须为零。
in.payload
指向有效负载输入的内存的指针,有效负载是小端字节序。
out
与输出有效负载关联的参数。
out.size
从设备接收的有效负载的大小(输入/输出)。此字段由用户空间填写,以告知驱动程序为输出分配了多少空间。它由驱动程序填充,以使用户空间知道实际的输出有效负载有多大。
out.rsvd
必须为零。
out.payload
指向有效负载输出的内存的指针,有效负载是小端字节序。
描述
用户空间向硬件发送命令进行处理的机制。驱动程序将对命令大小进行基本验证。在某些情况下,甚至可能会检查有效负载。用户空间需要为 size_out 分配足够大的缓冲区,在某些情况下,size_out 的长度可能可变。