调试程序 2PF KDNET 支持

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

在 NIC 上启用内核调试时,内核调试支持将接管物理设备,以便在框中提供内核调试和网络连接。 这适用于使用者低带宽 NIC(1-10 Gbps),但在支持 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 供应商在所有 NIC 中启用 2PF,这些 NIC 支持网络卡固件中的多个 PF。

有关配置 2PF 支持以测试连接的信息,请参阅 使用 KDNET 设置 2PF 内核模式调试。

多个 PF KDNET 体系结构概述

  • 多个 PF (2PF) 功能是向原始 PCI 网络端口(例如 Bus.dev.fun0.0)添加/分配新的 PF。

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

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

  • 使用此方法,两个驱动程序可以并行工作,w/o 干扰彼此的工作。

  • 这两个驱动程序都将在分区的 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。

多个 PFS 功能设计要求

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

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

  3. 仅调试器应使用 KDNET 2PF,以确保调试器拥有调试设备时,没有任何其他从 PCI 2PF 位置访问/运行的 Windows/UEFI 以太网驱动程序(使用 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 应通过 NICS 上的 KDNET 2PF 功能进行配置,其中有多个 PF 功能可用,并且 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 NDIS OID 路由到微型端口驱动程序。

多个 PF 功能 NDIS OID

使用这四个 NDIS OID 运行多个 PF 功能。

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

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

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

  • OID_KDNET_ENUMERATE_PFS OID 与NDIS_KDNET_ENUMERATE_PFS结构相关联。

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

    PfNumber 字段包含 PF 函数号(例如 bus.dev。fun

    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 列表输出缓冲区大小不够大,无法分配实际的 PF 列表,则 OID 处理程序需要返回 E_NOT_SUFFICIENT_BUFFER 错误返回值以及所需的缓冲区大小,因此客户端工具可以分配所需的大小缓冲区,然后客户端可以使用分配正确的缓冲区大小进行另一次调用。 此外,应将 OID 请求状态字段(由 NDIS_IOCTL_OID_REQUEST_INFO.status 描述)设置为等于 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 参数: AddedFunctionNumber 指示 AddedFunctionNumber 在微型端口 PCI 位置(BDF 微型端口)处添加的函数号值。 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 数:包含可添加到给定 BDF 的最大 PF 数的 ULONG。

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

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

2PF 上的 KDNET 的 NDIS OID

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 标头