首页
/ Linux 内核 PCI NTB 端点功能详解:pci_epf_ntb 的 configfs 绑定字段与源码实现

Linux 内核 PCI NTB 端点功能详解:pci_epf_ntb 的 configfs 绑定字段与源码实现

2026-09-06 10:23:26作者:江焘钦

本文以 Linux 内核文档 pci-ntb.rst 为主体,完整讲解 PCI NTB(Non-Transparent Bridge,非透明桥)端点功能 pci_epf_ntb 在 configfs 中的绑定流程:标准 EPF 可配置字段(vendorid/deviceid/baseclass_code 等)与 NTB 专有字段(db_count、mw1~mw4、num_mws、spad_count)的取值约束与默认值,并结合 pci-epf-ntb.c 源码剖析这些字段在驱动内部的解析、校验与生效路径,帮助读者掌握在双主机、多 EP 控制器平台上配置 NTB 端点的完整实操方案与底层原理。

1. 背景:PCI NTB 端点功能在 Linux 中的定位

PCI NTB 允许两个主机系统把彼此暴露为"设备"进行通信:NTB 设备通常支持在远端机器上产生中断、把内存区间以 BAR 形式暴露给对端、执行 DMA,并提供双方都可访问的 scratchpad(暂存寄存器区)。在 Linux 的可配置 PCI 端点框架(PCI Endpoint framework)中,这一功能由端点功能驱动 pci-epf-ntb.c 实现:它把 SoC 上的多个 PCI Endpoint(EP)控制器实例配置成"一台主机的事务被路由到另一个 EP 控制器",从而让 HOST1 与 HOST2 以 SoC 作为桥互相通信。

从源码文件头部的注释框图(pci-epf-ntb.c#L9-L35)可以看到该拓扑:

+-------------+                                   +-------------+
|    HOST1    |                                   |    HOST2    |
+------^------+                                   +------^------+
       |                                                 |
+------|-------------------------------------------------|---------+
|  +---v------+                                   +------v------+  |
|  |   EP     |                                   |     EP      |  |
|  |CONTROLLER1|<----------------------------------->CONTROLLER2 |  |
|  +-----------+  SoC With Multiple EP Instances  +-------------+  |
+-------------------------------------------------------------------+

完整的 NTB 硬件建模(Config Region、Self/Peer Scratchpad、Doorbell、Memory Window 五大构件及其 BAR 布局)见配套文档 pci-ntb-function.rst;本文聚焦的 pci-ntb.rst 则回答一个更具体的问题:在 configfs 中创建 pci_epf_ntb 功能设备时,有哪些字段必须/可以配置,各自的取值要求是什么

1.1 编译前提

根据 drivers/pci/endpoint/functions/Kconfigdrivers/pci/endpoint/Kconfig,需要开启:

  • CONFIG_PCI_ENDPOINT("PCI Endpoint Support"):平台具有可工作在端点模式的 PCI 控制器时需要;
  • CONFIG_PCI_EPF_NTB("PCI Endpoint NTB driver"):依赖 PCI_ENDPOINT,并自动 select CONFIGFS_FS
  • CONFIG_PCI_ENDPOINT_CONFIGFS("PCI Endpoint Configfs Support"):开启 configfs 入口,用于配置端点功能设备并将其与端点控制器绑定。

对应的构建规则见 drivers/pci/endpoint/Makefiledrivers/pci/endpoint/functions/Makefile

# drivers/pci/endpoint/Makefile
obj-$(CONFIG_PCI_ENDPOINT_CONFIGFS)   += pci-ep-cfs.o

# drivers/pci/endpoint/functions/Makefile
obj-$(CONFIG_PCI_EPF_NTB)             += pci-epf-ntb.o

2. 第一步:在 configfs 中创建 pci_epf_ntb 设备

文档给出的第一步是:在 pci_epf_ntb 目录下创建一个子目录。按 pci-ntb-howto.rst 的完整操作(前提:系统中至少有两个 EP 控制器设备,且功能驱动已注册):

# 挂载 configfs 并确认设备/驱动就绪
mount -t configfs none /sys/kernel/config
ls /sys/kernel/config/pci_ep/controllers
# 2900000.pcie-ep  2910000.pcie-ep

ls /sys/kernel/config/pci_ep/functions
# pci_epf_ntb  pci_epf_ntb

# 创建功能设备(目录名可自定,如 func1)
cd /sys/kernel/config/pci_ep/
mkdir functions/pci_epf_ntb/func1

mkdir func1 创建的正是会被 pci_epf_ntb 驱动 probe 的功能设备。PCI 端点框架随后会在该目录下填充标准 EPF 可配置字段,例如:

# ls functions/pci_epf_ntb/func1
baseclass_code    deviceid          msi_interrupts    pci-epf-ntb.0
progif_code       secondary         subsys_id         vendorid
cache_line_size   interrupt_pin     msix_interrupts   primary
revid             subclass_code     subsys_vendor_id

2.1 标准 EPF 可配置字段(文档原始表格)

pci-ntb.rst 规定了对 NTB 功能的字段取值要求:

字段 要求
vendorid 应为 0x104c
deviceid 对 TI 的 J721E SoC 应为 0xb00d
revid 无要求(don't care)
progif_code 无要求
subclass_code 应为 0x00
baseclass_code 应为 0x5
cache_line_size 无要求
subsys_vendor_id 无要求
subsys_id 无要求
interrupt_pin 无要求
msi_interrupts 无要求
msix_interrupts 无要求

其中几个"硬要求"在源码中有对应物:baseclass_code = 0x5(PCI 基类 0x05 = 内存控制器类)与 subclass_code = 0x00 对应驱动默认 PCI 头 epf_ntb_header,见 pci-epf-ntb.c#L124-L129

static struct pci_epf_header epf_ntb_header = {
	.vendorid	= PCI_ANY_ID,
	.deviceid	= PCI_ANY_ID,
	.baseclass_code	= PCI_BASE_CLASS_MEMORY,
	.interrupt_pin	= PCI_INTERRUPT_INTA,
};

pci-ntb-howto.rst 的说法,功能设备绑定驱动后框架会用默认值填充条目,pci-epf-ntb 驱动则把 vendorid 填为 0xffff、interrupt_pin 填为 0x0001。因此要让主机侧按预期识别设备,需要显式写入:

# echo 0x104c > functions/pci_epf_ntb/func1/vendorid
# echo 0xb00d > functions/pci_epf_ntb/func1/deviceid

这一步写入的值最终会体现在主机侧的 lspci 输出中(见第 6 节,Texas Instruments Device b00d 即 vendorid=0x104c、deviceid=0xb00d 的结果)。

3. 第二步:配置 NTB 专有字段

3.1 文档规定的 NTB 专有字段表

框架会自动在功能属性目录下再建一个与功能设备同名的子目录(即 pci_epf_ntb.0),其中填充 NTB 专有属性。pci-ntb.rst 给出的字段说明如下:

字段 说明
db_count 门铃(doorbell)数量;默认 = 4
mw1 内存窗口 1 的大小
mw2 内存窗口 2 的大小
mw3 内存窗口 3 的大小
mw4 内存窗口 4 的大小
num_mws 内存窗口数量;最大 = 4
spad_count 暂存寄存器(scratchpad)数量;默认 = 64

这些属性的注册与默认值可以直接在 pci-epf-ntb.c 中逐一印证:

#define SPAD_COUNT			64
#define DB_COUNT			4
#define NTB_MW_OFFSET			2
#define DB_COUNT_MASK			GENMASK(15, 0)
#define MSIX_ENABLE			BIT(16)
#define MAX_DB_COUNT			32
#define MAX_MW				4
  • db_count(默认 4):驱动初始化时校验 ntb->db_count 必须落在 1..MAX_DB_COUNT(32) 区间,否则报 "Invalid db_count" 错误(见 pci-epf-ntb.c#L1301-L1310)。该值同时决定 MSI/MSI-X 向量的数量:pci_epc_set_msi(epc, func_no, vfunc_no, ntb->db_count)pci_epc_set_msix(...),即每个门铃对应一个中断向量。
  • spad_count(默认 64):暂存区大小按 spad_size = spad_count * 4 计算(每个寄存器 32 位,见 pci-epf-ntb.c#L1030-L1037),并写入控制区的 SPAD OFFSET/SPAD COUNT 供主机侧读取。
  • num_mws(最大 4):store 函数直接拒绝大于 MAX_MW(4) 的写入,见 pci-epf-ntb.c#L1947-L1963
static ssize_t epf_ntb_num_mws_store(struct config_item *item,
				     const char *page, size_t len)
{
	...
	if (kstrtou32(page, 0, &val) < 0)
		return -EINVAL;

	if (val > MAX_MW)
		return -EINVAL;

	ntb->num_mws = val;

	return len;
}
  • mw1~mw4:分别表示 4 个内存窗口的大小。注意写入 mwN 前必须先保证 num_mws >= N,否则 store 路径会打印 "Invalid num_nws: %d value" 并返回 -EINVAL(pci-epf-ntb.c#L1937-L1940)。窗口大小在主机下发 CMD_CONFIGURE_MW 时还会再校验一次:请求映射尺寸不能超过 ntb->mws_size[mw],否则返回 -EINVAL(pci-epf-ntb.c#L262-L269)。

这 7 个属性通过 CONFIGFS_ATTR 宏注册进 ntb_group_type,并在 epf_ntb_add_cfs() 中挂到功能设备的 configfs 目录下(pci-epf-ntb.c#L1979-L2022),因此设备 bind 到驱动后才会出现 pci_epf_ntb.0/ 子目录。

3.2 一个可复制的示例配置

来自 pci-ntb-howto.rst 的示例(注意先设 num_mws 再设 mwN):

# echo 4        > functions/pci_epf_ntb/func1/pci_epf_ntb.0/db_count
# echo 128      > functions/pci_epf_ntb/func1/pci_epf_ntb.0/spad_count
# echo 2        > functions/pci_epf_ntb/func1/pci_epf_ntb.0/num_mws
# echo 0x100000 > functions/pci_epf_ntb/func1/pci_epf_ntb.0/mw1
# echo 0x100000 > functions/pci_epf_ntb/func1/pci_epf_ntb.0/mw2

即:4 个门铃、128 个暂存寄存器、2 个内存窗口、每个窗口 0x100000 字节。

3.3 字段如何决定 BAR 分配

这些字段不是"写了就生效"的孤立参数——它们直接决定 BAR 布局。从 struct epf_ntbpci-epf-ntb.c#L76-L84)可见 num_mwsdb_countspad_countmws_size[MAX_MW] 都保存在功能结构体中;在 BAR 分配逻辑里(pci-epf-ntb.c#L1437-L1462 附近),BAR_DB_MW1 的大小由 db_count * 对齐值 + mws_size[0] 决定,其余窗口依次占用后续 BAR。这与 pci-ntb-function.rst 给出的打包 BAR 方案一致:

BAR 承载构件
BAR0 Config Region + Self Scratchpad
BAR1 Peer Scratchpad
BAR2 Doorbell + Memory Window 1
BAR3 Memory Window 2
BAR4 Memory Window 3
BAR5 Memory Window 4

对应源码中的 BAR 枚举为:

enum epf_ntb_bar {
	BAR_CONFIG,
	BAR_PEER_SPAD,
	BAR_DB_MW1,
	BAR_MW2,
	BAR_MW3,
	BAR_MW4,
};

pci-epf-ntb.c#L67-L74)。文档同时说明:对仅支持 64 位 BAR 的平台,每个构件单独占用一个 BAR 会不够用,所以 EPF NTB 采用上述打包方式,基础 NTB 功能只需 3 个 BAR。MW1 与 Doorbell 挤在同一个 BAR 内,因此控制区专门有 MEMORY WINDOW1 OFFSET 字段记录 MW1 在其中的偏移;同理 SPAD OFFSET 记录 self scratchpad 在 BAR0 中的偏移。

4. 第三步:绑定 EP 控制器并启动链路

NTB 功能设备需要同时挂到两个 EP 控制器(分别连接两台主机),使用功能设备下的 primarysecondary 条目(pci-ntb-howto.rst):

# ln -s controllers/2900000.pcie-ep/ functions/pci_epf_ntb/func1/primary
# ln -s controllers/2910000.pcie-ep/ functions/pci_epf_ntb/func1/secondary

随后两个控制器各自与主机建立链路:

# echo 1 > controllers/2900000.pcie-ep/start
# echo 1 > controllers/2910000.pcie-ep/start

两个控制器都建链后,功能设备即处于"就绪"状态。从源码看,epf_ntb 结构体中 epc[2] 数组(pci-epf-ntb.c#L83)正是保存 primary/secondary 两个 epf_ntb_epc 实例,命令处理逻辑会遍历 PRIMARY_INTERFACESECONDARY_INTERFACE 两类接口(如 epf_ntb_link_up()pci-epf-ntb.c#L140-L173)。

5. 底层机制:主机与端点之间的命令协议

上述字段最终服务于主机(RC 侧)与端点(EP 侧)之间的命令交换,协议由主机侧驱动 ntb_hw_epf.c 与端点侧 pci-epf-ntb 共同实现。命令定义在两端完全一致:

#define CMD_CONFIGURE_DOORBELL	1
#define CMD_TEARDOWN_DOORBELL	2
#define CMD_CONFIGURE_MW	3
#define CMD_TEARDOWN_MW		4
#define CMD_LINK_UP		5
#define CMD_LINK_DOWN		6

(端点侧见 pci-epf-ntb.c#L47-L55,主机侧见 ntb_hw_epf.c#L16-L22)。

命令通过 BAR0 的 Config Region 传递,其内存布局由 struct epf_ntb_ctrl 描述(pci-epf-ntb.c#L107-L122):commandargumentcommand_statuslink_statustopology、64 位 addr、64 位 sizenum_mwsmw1_offsetspad_offsetspad_countdb_entry_size 以及最多 32 组 db_data/db_offset。各字段含义(COMMAND、ARGUMENT、TOPOLOGY、ADDRESS/SIZE、MW1 OFFSET、SPAD OFFSET/COUNT、DB ENTRY SIZE、DB DATA)在 pci-ntb-function.rst 中有完整说明,要点是:

  • CMD_CONFIGURE_DOORBELL(0x1):主机在分配并初始化 MSI/MSI-X 后下发;ARGUMENT 低 16 位是门铃数、BIT16 指示用 MSI 还是 MSI-X。端点收到后配置出站 ATU,使写 Doorbell BAR 的事务被路由到主机编程的 MSI/MSI-X 地址。端点侧对应 epf_ntb_configure_msi()/epf_ntb_configure_msix()pci-epf-ntb.c#L383-L427),并把每个中断的 MSI data 写入对端控制区的 db_data,供对端主机"写 BAR 即响铃"。
  • CMD_CONFIGURE_MW(0x2):主机分配一块可被远端主机访问的缓冲区后下发,ADDRESS/SIZE 填缓冲区地址与大小,ARGUMENT 填窗口索引;端点随后用 pci_epc_map_addr() 配置出站 ATU,把 MW BAR 上的事务路由到主机提供的地址(epf_ntb_configure_mw()pci-epf-ntb.c#L235-L283)。这里的 size > ntb->mws_size[mw] 检查正是第 3 节 configfs 中 mw1~mw4 字段的实际约束点。
  • CMD_LINK_UP(0x3):主机侧 NTB 应用绑定到 EP 设备后下发;当端点从两侧主机都收到该命令时,通过 epf_ntb_link_up() 置位 link_statusLINK_STATUS_UP 并向两端各发一个中断,通知两端的 NTB 客户端可以开始通信。

主机侧驱动 ntb_hw_epf.c 还定义了配置区各寄存器的偏移(NTB_EPF_COMMAND = 0x0NTB_EPF_ARGUMENT = 0x4NTB_EPF_SPAD_OFFSET = 0x28NTB_EPF_DB_DATA(n) = 0x34 + n*4 等,见 ntb_hw_epf.c#L16-L45),并约束主机侧门铃槽位布局:slot 0 保留给链路事件、DB 编号从 slot 2 起,故最少需要 3 个向量(NTB_EPF_MIN_DB_COUNT = 3),最多 31 个(NTB_EPF_MAX_DB_COUNT = 31ntb_hw_epf.c#L47-L60)。这也解释了端点侧 MAX_DB_COUNT = 32 的取值:为链路事件与门铃向量预留了完整空间。

6. 主机侧验证与后续使用

端点侧完成绑定并 start 后,在主机(RootComplex 侧)执行 lspci,应看到与 configfs 中写入的 vendorid/deviceid 一致的设备(来自 pci-ntb-howto.rst):

# lspci
0000:00:00.0 PCI bridge: Texas Instruments Device b00d
0000:01:00.0 RAM memory: Texas Instruments Device b00d

主机侧软件遵循 Linux 标准 NTB 软件架构,可配合 ntb_hw_epf 主机驱动使用既有 NTB 客户端(如 NTB Transport、NTB Ping Pong Test、NTB Tool 等);更多 NTB 框架信息见 Non-Transparent Bridge 文档。

7. 小结

  • pci-ntb.rstpci_epf_ntb 设备绑定的"字段清单":标准 EPF 字段中 vendorid=0x104cdeviceid=0xb00d(TI J721E)、baseclass_code=0x5subclass_code=0x00 有硬性要求,其余 don't care;NTB 专有字段为 db_count(默认 4)、mw1~mw4(各窗口大小)、num_mws(最大 4)、spad_count(默认 64)。
  • 这些字段的解析与校验逻辑可在 pci-epf-ntb.c 中逐一对应:num_mws 超过 4 被拒、mwN 要求 num_mws >= N、主机映射请求不得大于 mws_size[mw];字段值最终决定 6 个 BAR 的打包分配与出站 ATU 映射行为。
  • 完整落地流程为:configfs mkdir 创建设备 → 写标准字段与 NTB 字段 → ln -s 绑定 primary/secondary 两个 EP 控制器 → echo 1 > .../start 建链 → 主机侧经 ntb_hw_epf 通过 Config Region 命令(CONFIGURE_DOORBELL / CONFIGURE_MW / LINK_UP)完成 NTB 链路。
  • 相关延伸阅读:PCI 端点框架总览EPF ConfigFS 使用NTB 功能硬件建模NTB 用户指南
登录后查看全文
热门项目推荐
相关项目推荐