调试程序 2PF KDNET 支持

本主题介绍如何为微型端口 NDIS 驱动程序启用 2PF 调试器支持,以允许高速适配器(通常用于数据中心)提高性能。 此功能在 Windows 11 及更高版本中提供。

在 NIC 上启用内核调试时,内核调试支持将接管物理设备,从而在框中提供内核调试和网络连接。 这适用于 (1-10 Gbps) 的消费者低带宽 NIC,但在支持 10-40 Gbps 以上的高吞吐量设备上,与硬件通信的内核调试扩展性模块通常无法跟上来自 Windows 网络堆栈的流量,因此这会降低整体系统性能。

使用适用于 KDNET 的 PCI 多物理函数 (PF) 功能可以启用调试,而几乎没有性能影响。

物理函数 (PF) 是网络适配器的 PCI Express (PCIe) 功能,支持单根 I/O 虚拟化 (SR-IOV) 接口。 PF 包括 PCIe 配置空间中的 SR-IOV 扩展功能。 该功能用于配置和管理网络适配器的 SR-IOV 功能,例如启用虚拟化和公开 PCIe 虚拟函数 (VF) 。

PF 在其 PCIe 配置空间中支持 SR-IOV 扩展功能结构。 此结构在 PCI-SIG 单根 I/O 虚拟化和共享 1.1 规范中定义。

调试器传输将利用多个已启用或 2PF 的微型端口驱动程序。 若要允许调试高速服务器系统,建议 NIC 供应商在网络卡固件中支持多个 PF 的所有 NIC 中启用 2PF。

有关配置 2PF 支持以测试连接的信息,请参阅 使用 KDNET 设置 2PF Kernel-Mode 调试

多 PF KDNET 体系结构概述

  • 多 PF (2PF) 功能是将新的 PF 添加到原始 PCI 网络端口 (,例如 Bus.dev.fun0.0) 。

  • 新添加的 PF (例如 bus.dev.fun0.1) 仅由 KDNET 用来将调试器数据包路由到目标或从目标路由。

  • Windows 收件箱 NIC 驱动程序将使用原始 PF 将 Windows 网络数据包路由 (TCP/IP) 。

  • 使用此方法,两个驱动程序可以在相互干扰的情况下并行工作。

  • 这两个驱动程序都将在分区的 PCI 配置空间上运行

    • Windows 收件箱驱动程序将在 bus.dev 时用完原始网络端口。fun0.0

    • KDNET-KDNET-Ext。模块将在 bus.dev 用完添加的 PF。fun0.1,此方法可确保 Windows 收件箱 NIC 驱动程序不会因与 KDNET 共享 NIC 而受到影响。

  • kdnet.exe 用户模式工具通过添加特定 IOCTL 代码来添加/删除 KDNET PF,使用 Windows 收件箱驱动程序配置 2PF 功能。

显示两个网络堆栈的关系图,其中一个使用组合的 PCI 卡设置支持 2PF。

多个 PF 功能设计要求

  1. KDNET 2PF 功能需要适用于所有当前的 KD 方案,无论是 NT 前操作系统 (,例如启动管理器、OS 加载程序、WinResume、Hyper-V、SK 等 ) 、NT OS 还是 Windows 桌面。

  2. 为设备添加新的 PF 会导致需要更改 BCD 配置以用于调试设置时,需要重新启动系统。 这意味着其他 PF 的配置必须在启动之间持久化。

  3. 调试器应仅使用 KDNET 2PF,以确保当调试器拥有调试设备时,没有任何其他 Windows/UEFI 以太网驱动程序从 PCI 2PF 位置访问/运行, (使用 dbgsettings::busparams) 配置 2PF 位置。

  4. 即使系统中未启用 KDNET,Windows 或 UEFI 以太网驱动程序也无法耗尽添加的 KDNET 2PF。

  5. 2PF 功能应支持在当前 NIC 上添加/启用和删除/禁用功能的动态机制。

  6. Windows 微型端口驱动程序将通过维护以下 NDIS OID 来实现 2PF 功能。

OID 名称 说明
OID_KDNET_ENUMERATE_PFS 枚举运行微型端口驱动程序的当前 bus.dev.fun (BDF) 上的 PF。
OID_KDNET_ADD_PF 将 PF 添加到运行微型端口驱动程序的当前 BDF。
OID_KDNET_REMOVE_PF 从 BDF 中传递的 中删除添加的 PF。
OID_KDNET_QUERY_PF_INFORMATION 从 BDF 中传递的 查询 PF 信息数据。

OID 及其结构在 ntddndis.h 和 kdnetpf.h 文件中定义,这些文件随公共 WDK 一起发布。

请参阅以下有关每个 OID 的输入/输出参数的详细信息以及 kdnetpf.h 头文件中提供的信息。

  1. KDNET 应通过具有多个 PF 功能的 NICS 上的 KDNET 2PF 功能进行配置,并且 NIC 通过遵循上述所有要求来启用 2PF 功能。

适用于 Windows NIC 驱动程序的 KDNET 多 PF 接口

若要支持 KDNET 多 PF 接口微型端口驱动程序,需要实现以下四个 NDIS OID 的处理。

  • OID_KDNET_ENUMERATE_PFS

  • OID_KDNET_ADD_PF

  • OID_KDNET_REMOVE_PF

  • OID_KDNET_QUERY_PF_INFORMATION

这些 OID 和结构填充在此路径上的公共 WDK 版本的 ntddndis.h 和 kdnetpf.h 文件中:

<WDK root directory>\ddk\inc\ndis

这些文件在 Windows SDK 中也可用,可在此目录中找到。

\Program Files (x86)\Windows Kits\10\Include\<Version for example 10.0.21301.0>\shared

客户端工具 (kdnet.exe) 使用专用 NDIS IOCTL 将 KDNET 2PF NDIS OID 路由到微型端口驱动程序。

多 PF 功能 NDIS OID

使用这四个 NDIS OID 操作多个 PF 功能。

1. 使用 OID 枚举微型端口 BDF 主端口上的 F: OID_KDNET_ENUMERATE_PFS,请参阅下面的定义。

  • OID_KDNET_ENUMERATE_PFS 返回与从中运行微型端口驱动程序的给定主端口关联的所有 BDF 的列表。 端口由 bus.dev.fun (BDF) 表示。 该操作将列出/枚举 仅与 bus.dev.fun (BDF 端口关联的 PF 列表,) 系统中运行微型端口驱动程序的位置,因为每个微型端口驱动程序都可以确定其 BDF 位置。

  • 将通过 NDIS 查询操作将 PF 列表返回到客户端。

  • OID_KDNET_ENUMERATE_PFS OID 与 NDIS_KDNET_ENUMERATE_PFS 结构相关联。

  • OID_KDNET_ENUMERATE_PFS驱动程序处理程序将返回一个缓冲区,其中包含由类型NDIS_KDNET_PF_ENUM_ELEMENT描述的每个 PF 元素的 PFs 列表。

    PfNumber 字段包含 PF 函数号, (例如 bus.dev。有趣的)

    PfState 字段包含 PF 状态的可能值 - NDIS_KDNET_PF_STATE 枚举描述的每个元素类型。

    NDIS_KDNET_PF_STATE::NdisKdNetPfStatePrimary - 这是一个主 PF,通常仅由微型端口驱动程序使用。

    NDIS_KDNET_PF_STATE::NdisKdnetPfStateEnabled - 这是 KDNET 使用的附加辅助 PF。

    NDIS_KDNET_PF_STATE::NdisKdnetPfStateConfigured - 这是添加的 PF,但仅添加/配置,不使用。

  • 如果 PF 列表输出缓冲区大小不足以分配实际 PFS 列表,则 OID 处理程序需要返回 E_NOT_SUFFICIENT_BUFFER 错误返回值以及所需的缓冲区大小,以便客户端工具可以分配所需的大小缓冲区,然后客户端可以使用分配正确的缓冲区大小进行另一次调用。 此外,NDIS_IOCTL_OID_REQUEST_INFO.status) 描述的 OID 请求状态字段 (应设置为等于 NDIS_STATUS_BUFFER_TOO_SHORT

2. 将 PCI PF 添加到微型端口 BDF 主端口 (OID: OID_KDNET_ADD_PF, 请参阅下面的定义)

  • 将 PF 添加到微型端口主端口。 端口由 BDF 表示。

  • 新添加的 PF 将通过 NDIS 查询操作返回到客户端。

  • OID_KDNET_ADD_PF OID 与 NDIS_KDNET_ADD_PF 结构相关联。

  • OID_KDNET_ADD_PF驱动程序处理程序将返回包含添加的 PF 函数号的 ULONG。

  • 此 OID 请求将只有一个 Output 参数: AddedFunctionNumberAddedFunctionNumber指示在 BDF 微型端口) (微型端口 PCI 位置添加的函数编号值。 kdnet.exe 实用工具将收到此值,并将 dbgsettings::busparams 设置为指向添加的 PF 的点。

注意

添加的 PF 可由 KDNET 独占使用,因此 Windows NIC 驱动程序被操纵为在添加的 PF 上显式 *NOT* 运行,因此,当在系统上启用了 KDNET 并且 PF 已添加到端口时,这同样适用。

3. 删除 PCI PF (OID: OID_KDNET_REMOVE_PF,请参阅下面的定义 )

  • 给定端口中删除 PF。 端口由 BDF 表示。

  • OID_KDNET_REMOVE_PF OID 与 NDIS_KDNET_REMOVE_PF 结构相关联。

  • OID_KDNET_REMOVE_PF OID 具有输入 BDF 端口,并通过 NDIS 方法操作返回包含已删除的 PF 函数号的 ULONG。

  • 此函数仅在使用 OID_KDNET_ADD_PF OID 添加的 PF 上成功。

  • 此 OID 请求将具有需要从中删除 BDF 的输入 BDF 端口。 此函数的 Output 参数为 FunctionNumber。 输出 FunctionNumber 将包含已删除的函数编号值。

4. 查询 PCI PF 信息 (OID: OID_KDNET_QUERY_PF_INFORMATION,请参阅下面的定义)

  • 此 OID 代码允许在 给定端口上查询特定的 PF 数据。 端口由 BDF 表示。

  • 请求的 PF 信息将通过 NDIS 方法操作返回到客户端。

  • OID_KDNET_QUERY_PF_INFORMATION OID 与 NDIS_KDNET_QUERY_PF_INFORMATION 结构相关联。

  • OID_KDNET_QUERY_PF_INFORMATION OID 具有输入 BDF 端口,并返回包含以下数据的缓冲区:

    • MAC 地址:分配的新 KDNET PF 的网络地址(如果有)。

    • 使用情况标记:描述拥有 PF 端口的实体。 它包含由NDIS_KDNET_PF_USAGE_TAG枚举描述 常量值。

    • 最大 PF 数:包含一个 ULONG,其中包含可添加到给定 BDF 的 PF 的最大数目。

    • 设备 ID:包含与给定 BDF 端口关联的设备 ID。 对于 NIC FW 将新设备 ID 分配给新添加的 KDNET PF 端口的情况,这是必需的。

  • 此 OID 请求 BDF 端口中传递的任何信息, (BDF 是此操作) 的输入参数,因此它 一定与运行驱动程序的当前 BDF 相关。

NDIS OID for KDNET on 2PF

Ntddndis.h 文件定义 OID。

#if (NDIS_SUPPORT_NDIS686)

 //

 // Optional OIDs to handle network multiple PF feature.

 //
#define OID_KDNET_ENUMERATE_PFS 0x00020222
#define OID_KDNET_ADD_PF 0x00020223
#define OID_KDNET_REMOVE_PF 0x00020224
#define OID_KDNET_QUERY_PF_INFORMATION 0x00020225
#endif // (NDIS_SUPPORT_NDIS686)

Kdnetpf.h 文件描述与 NDIS OID 关联的类型和结构。

#if (NDIS_SUPPORT_NDIS686)

 //
 // Used to query/add/remove Physical function on a network port.
 // These structures are used by these OIDs:
 // OID_KDNET_ENUMERATE_PFS
 // OID_KDNET_ADD_PF
 // OID_KDNET_REMOVE_PF
 // OID_KDNET_QUERY_PF_INFORMATION
 // These OIDs handle PFs that are primary intended to be used by  KDNET.
 //
 //
 // PCI location of the port to query
 //
 typedef struct _NDIS_KDNET_BDF
 {
 ULONG SegmentNumber;
 ULONG BusNumber;
 ULONG DeviceNumber;
 ULONG FunctionNumber;
 ULONG Reserved;
 } NDIS_KDNET_BDF, *PNDIS_KDNET_PCI_BDF;

 //
 // PF supported states.
 //
 typedef enum _NDIS_KDNET_PF_STATE
 {
 NdisKdNetPfStatePrimary = 0x0,
 NdisKdnetPfStateEnabled = 0x1,
 NdisKdnetPfStateConfigured = 0x2,
 } NDIS_KDNET_PF_STATE,*PNDIS_KDNET_PF_STATE;

 //
 // PF Usage Tag
 // Used to indicate the entity that owns the PF.
 // Used by the query NdisKdnetQueryUsageTag.
 //
 typedef enum _NDIS_KDNET_PF_USAGE_TAG
 {
 NdisKdnetPfUsageUnknown = 0x0,
 NdisKdnetPfUsageKdModule = 0x1,
 } NDIS_KDNET_PF_USAGE_TAG,*PNDIS_KDNET_PF_USAGE_TAG;

 //
 // PF element array structure
 //
 typedef struct _NDIS_KDNET_PF_ENUM_ELEMENT
 {
 NDIS_OBJECT_HEADER Header;

 //
 // PF value (e.g. if <bus.dev.fun>, then PF value = fun)
 //
 ULONG PfNumber;

 //
 // The PF state value (defined by NDIS_KDNET_PF_STATE)
 //
 NDIS_KDNET_PF_STATE PfState;

 } NDIS_KDNET_PF_ENUM_ELEMENT, *PNDIS_KDNET_PF_ENUM_ELEMENT;
#define NDIS_KDNET_PF_ENUM_ELEMENT_REVISION_1 1
#define NDIS_SIZEOF_KDNET_PF_ENUM_ELEMENT_REVISION_1 \
 RTL_SIZEOF_THROUGH_FIELD(NDIS_KDNET_PF_ENUM_ELEMENT, PfState)

 //
 // This structure describes the data required to enumerate the list of PF
 // Used by OID_KDNET_ENUMERATE_PFS.
 //
 typedef struct _NDIS_KDNET_ENUMERATE_PFS
 {
 NDIS_OBJECT_HEADER Header;

 //
 // The size of each element is the sizeof(NDIS_KDNET_PF_ENUM_ELEMENT)
 //
 ULONG ElementSize;

 //
 // The number of elements in the returned array
 //
 ULONG NumberOfElements;

 //
 // Offset value to the first element of the returned array.
 // Each array element is defined by NDIS_KDNET_PF_ENUM_ELEMENT.
 //
 ULONG OffsetToFirstElement;
 } NDIS_KDNET_ENUMERATE_PFS, *PNDIS_KDNET_ENUMERATE_PFS;

#define NDIS_KDNET_ENUMERATE_PFS_REVISION_1 1
#define NDIS_SIZEOF_KDNET_ENUMERATE_PFS_REVISION_1 \
 RTL_SIZEOF_THROUGH_FIELD(NDIS_KDNET_ENUMERATE_PFS,
 OffsetToFirstElement)

 //
 // This structure indicates the data required to add a PF to the BDF port.
 // Used by OID_KDNET_ADD_PF.
 //
 typedef struct _NDIS_KDNET_ADD_PF
 {
 NDIS_OBJECT_HEADER Header;

 //
 // One element containing the added PF port number
 //
 ULONG AddedFunctionNumber;
 } NDIS_KDNET_ADD_PF, *PNDIS_KDNET_ADD_PF;

#define NDIS_KDNET_ADD_PF_REVISION_1 1
#define NDIS_SIZEOF_KDNET_ADD_PF_REVISION_1 \
 RTL_SIZEOF_THROUGH_FIELD(NDIS_KDNET_ADD_PF, AddedFunctionNumber)

 //
 // This structure indicates the data required to remove a PF from the BDF port.
 // Used by OID_KDNET_REMOVE_PF.
 //

 typedef struct _NDIS_KDNET_REMOVE_PF
 {
 NDIS_OBJECT_HEADER Header;

 //
 // PCI location that points to the PF that needs to be removed
 //
 NDIS_KDNET_BDF Bdf;

 //
 // One element containing the removed PF port
 //
 ULONG FunctionNumber;
 } NDIS_KDNET_REMOVE_PF, *PNDIS_KDNET_REMOVE_PF;
#define NDIS_KDNET_REMOVE_PF_REVISION_1 1
#define NDIS_SIZEOF_KDNET_REMOVE_PF_REVISION_1 \
 RTL_SIZEOF_THROUGH_FIELD(NDIS_KDNET_REMOVE_PF, FunctionNumber)

 //
 // This structure describes the data required to query the PF management data
 // Used by OID_KDNET_QUERY_PF_INFORMATION
 //
 typedef struct _NDIS_KDNET_QUERY_PF_INFORMATION
 {
 NDIS_OBJECT_HEADER Header;

 //
 // PF PCI location to query for
 //
 NDIS_KDNET_BDF Bdf;

 //
 // PF assigned MAC address
 //
 UCHAR NetworkAdddress[6];

 //
 // PF Usage tag described by NDIS_KDNET_PF_USAGE_TAG
 //
 ULONG UsageTag;

 //
 // Maximum number of Pfs that can be associated to the Primary BDF.
 //
 ULONG MaximumNumberOfSupportedPfs;

 //
 // KDNET PF device ID (Used if there is a new added PF and
 // the FW assigns a new DeviceID to the added KDNET PF)
 //
 ULONG DeviceId;

 } NDIS_KDNET_QUERY_PF_INFORMATION, *PNDIS_KDNET_QUERY_PF_INFORMATION;
#define NDIS_KDNET_QUERY_PF_INFORMATION_REVISION_1 1
#define NDIS_SIZEOF_KDNET_QUERY_PF_INFORMATION_REVISION_1 \
 RTL_SIZEOF_THROUGH_FIELD(NDIS_KDNET_QUERY_PF_INFORMATION, DeviceId)

#endif // (NDIS_SUPPORT_NDIS686)

另请参阅

使用 KDNET 设置 2PF 内核模式调试

网络 OID

kdnetpf.h 标头