调试程序 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 功能。
多个 PFS 功能设计要求
KDNET 2PF 功能需要适用于所有当前的 KD 方案,无论是预 NT OS(例如启动管理器、OS 加载程序、WinResume、Hyper-V、SK 等)、NT OS 还是 Windows 桌面。
为设备添加新的 PF 时,需要重新启动系统,这会导致 BCD 配置需要更改才能调试设置。 这意味着,其他 PF 的配置必须在启动之间持久化。
仅调试器应使用 KDNET 2PF,以确保调试器拥有调试设备时,没有任何其他从 PCI 2PF 位置访问/运行的 Windows/UEFI 以太网驱动程序(使用 dbgsettings::busparams 配置 2PF 位置)。
即使系统中未启用 KDNET,Windows 或 UEFI 以太网驱动程序也无法耗尽已添加的 KDNET 2PF。
2PF 功能应支持一种动态机制,用于在当前 NIC 上添加/启用和删除/禁用该功能。
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 头文件中提供的信息。
- 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)