第 1 部分针对 Oracle Solaris 平台设计设备驱动程序
9. 直接内存访问 (Direct Memory Access, DMA)
14. 分层驱动程序接口 (Layered Driver Interface, LDI)
Ethernet V2 和 ISO 8802-3 (IEEE 802.3)
GLDv3 框架是 MAC 插件和 MAC 驱动程序服务例程与结构的基于函数调用的接口。GLDv3 框架代表符合 GLDv3 的驱动程序实现必要的 STREAMS 入口点,并处理 DLPI 兼容性。
本节讨论以下主题:
GLDv3 为使用 MAC_PLUGIN_IDENT_ETHER 插件类型注册的驱动程序定义驱动程序 API。
GLDv3 设备驱动程序必须执行以下步骤来向 MAC 层注册:
引入以下两个 MAC 头文件:sys/mac_ether.h 和 sys/mac_provider.h。切勿在驱动程序中包含其他任何与 MAC 相关的头文件。
填充 mac_callbacks 结构。
在其 _init() 入口点中调用 mac_fini_ops() 函数。
在其 attach() 入口点中调用 mac_alloc() 函数,以分配 mac_register 结构。
填充 mac_register 结构,并在其 attach() 入口点中调用 mac_register() 函数。
在其 detach() 入口点中调用 mac_unregister() 函数。
在其 _fini() 入口点中调用 mac_fini_ops() 函数。
链接 misc/mac 依赖性:
# ld -N"misc/mac" xx.o -o xx
GLDv3 接口包括在使用 MAC 层注册过程中通告的驱动程序入口点和驱动程序调用的 MAC 入口点。
void mac_init_ops(struct dev_ops *ops, const char *name);
GLDv3 设备驱动程序必须在调用 mod_install(9F) 之前在其 _init(9E) 入口点中调用 mac_init_ops(9F) 函数。
void mac_fini_ops(struct dev_ops *ops);
GLDv3 设备驱动程序必须在调用 mod_remove(9F) 之后在其 _fini(9E) 入口点中调用 mac_fini_ops(9F) 函数。
示例 19-1 mac_init_ops() 和·mac_fini_ops() 函数
int _init(void) { int rv; mac_init_ops(&xx_devops, "xx"); if ((rv = mod_install(&xx_modlinkage)) != DDI_SUCCESS) { mac_fini_ops(&xx_devops); } return (rv); } int _fini(void) { int rv; if ((rv = mod_remove(&xx_modlinkage)) == DDI_SUCCESS) { mac_fini_ops(&xx_devops); } return (rv); }
mac_register_t *mac_alloc(uint_t version);
mac_alloc(9F) 函数分配一个新的 mac_register 结构,并返回此结构的指针。在将新结构传递给 mac_register() 之前初始化结构成员。在 mac_alloc() 返回之前,MAC 层会初始化 MAC 专用元素。 version 的值必须是 MAC_VERSION_V1。
void mac_free(mac_register_t *mregp);
mac_free(9F) 函数释放此前由 mac_alloc() 分配的 mac_register 结构。
int mac_register(mac_register_t *mregp, mac_handle_t *mhp);
为了使用 MAC 层注册新实例,GLDv3 驱动程序必须在 attach(9E) 入口点中调用 mac_register(9F) 函数。mregp 参数是指向 mac_register 注册信息结构的指针。 在成功时,mhp 参数是指向新 MAC 实例的 MAC 句柄指针。mac_tx_update()、mac_link_update () 和 mac_rx() 等其他例程需要此句柄。
示例 19-2 mac_alloc()、mac_register () 和 mac_free() 函数及 mac_register 结构
int xx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { mac_register_t *macp; /* ... */ if ((macp = mac_alloc(MAC_VERSION)) == NULL) { xx_error(dip, "mac_alloc failed"); goto failed; } macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; macp->m_driver = xxp; macp->m_dip = dip; macp->m_src_addr = xxp->xx_curraddr; macp->m_callbacks = &xx_m_callbacks; macp->m_min_sdu = 0; macp->m_max_sdu = ETHERMTU; macp->m_margin = VLAN_TAGSZ; if (mac_register(macp, &xxp->xx_mh) == DDI_SUCCESS) { mac_free(macp); return (DDI_SUCCESS); } /* failed to register with MAC */ mac_free(macp); failed: /* ... */ }
int mac_unregister(mac_handle_t mh);
mac_unregister(9F) 函数取消注册此前通过 mac_register() 注册的 MAC 实例。 mh 参数是 mac_register() 分配的 MAC 句柄。 从 detach(9E) 入口点调用 mac_unregister()。
示例 19-3 mac_unregister() 函数
int xx_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { xx_t *xxp; /* driver soft state */ /* ... */ switch (cmd) { case DDI_DETACH: if (mac_unregister(xxp->xx_mh) != 0) { return (DDI_FAILURE); } /* ... */ }
本节介绍的结构是在 sys/mac_provider.h 头文件中定义的。 在 GLDv3 驱动程序中引入 sys/mac_ether.h 和 sys/mac_provider.h MAC 头文件。切勿包含其他任何于 MAC 相关的头文件。
mac_register(9S) 数据结构是 MAC 注册信息结构,由 mac_alloc() 分配,传递至 mac_register()。 ·在将新结构传递给 mac_register() 之前初始化结构成员。在 mac_alloc() 返回之前,MAC 层会初始化 MAC 专用元素。 m_version 结构成员是 MAC 版本。 切勿修改 MAC 版本。m_type_ident 结构成员是 MAC 类型标识符。 将 MAC 类型标识符设置为 MAC_PLUGIN_IDENT_ETHER。 mac_register 结构的 m_callbacks 成员是指向 mac_callbacks 结构的一个实例的指针。
mac_callbacks(9S) 数据结构是设备驱动程序用于向 MAC 层公开其入口点的结构。 MAC 层使用这些入口点来控制驱动程序。这些入口点用于完成启动和停止适配器、管理多点传送地址、设置混杂模式、查询适配器功能、获取和设置属性等任务。 有关必需和可选 GLDv3 入口点的完整列表,请参见表 19-1。在 mac_register 结构的 m_callbacks 字段中提供指向 mac_callbacks 结构的指针。
mac_callbacks 结构的 mc_callbacks 成员是一个位掩码,结合了指定驱动程序将实现哪些可选入口点的以下标志。mac_callbacks 结构的其他成员是驱动程序各入口点的指针。
显示 mc_ioctl() 入口点。
显示 mc_getcapab() 入口点。
显示·mc_setprop() 入口点。
显示 mc_getprop() 入口点。
显示 mc_propinfo() 入口点。
显示所有属性入口点。设置·MC_PROPERTIES 等同于设置全部三个标志:MC_SETPROP、 MC_GETPROP 和 MC_PROPINFO。
示例 19-4 mac_callbacks 结构
#define XX_M_CALLBACK_FLAGS \ (MC_IOCTL | MC_GETCAPAB | MC_PROPERTIES) static mac_callbacks_t xx_m_callbacks = { XX_M_CALLBACK_FLAGS, xx_m_getstat, /* mc_getstat() */ xx_m_start, /* mc_start() */ xx_m_stop, /* mc_stop() */ xx_m_promisc, /* mc_setpromisc() */ xx_m_multicst, /* mc_multicst() */ xx_m_unicst, /* mc_unicst() */ xx_m_tx, /* mc_tx() */ NULL, /* Reserved, do not use */ xx_m_ioctl, /* mc_ioctl() */ xx_m_getcapab, /* mc_getcapab() */ NULL, /* Reserved, do not use */ NULL, /* Reserved, do not use */ xx_m_setprop, /* mc_setprop() */ xx_m_getprop, /* mc_getprop() */ xx_m_propinfo /* mc_propinfo() */ };
GLDv3 实现一种功能机制,允许框架查询和启用 GLDv3 驱动程序支持的功能。使用 mc_getcapab(9E) 入口点报告功能。如果驱动程序支持某种功能,请通过 mc_getcapab() 传递关于该功能的信息,例如特定于功能的入口点或标志。传递一个指向 mac_callback 结构中 mc_getcapab() 入口点的指针。有关 mac_callbacks 结构的更多信息,请参见GLDv3 MAC 注册数据结构。
boolean_t mc_getcapab(void *driver_handle, mac_capab_t cap, void *cap_data);
cap 参数指定所查询的功能类型。 cap 的值可以是 MAC_CAPAB_HCKSUM(硬件校验和负载转移)、MAC_CAPAB_LSO(大段负载转移)或 MAC_CAPAB_RINGS。使用 cap_data 参数将功能数据返回框架。
如果驱动程序支持 cap 功能,则 mc_getcapab() 入口点必须返回 B_TRUE。如果驱动程序不支持 cap 功能,则 mc_getcapab() 必须返回 B_FALSE。
示例 19-5 mc_getcapab() 入口点
static boolean_t xx_m_getcapab(void *arg, mac_capab_t cap, void *cap_data) { switch (cap) { case MAC_CAPAB_HCKSUM: { uint32_t *txflags = cap_data; *txflags = HCKSUM_INET_FULL_V4 | HCKSUM_IPHDRCKSUM; break; } case MAC_CAPAB_LSO: { /* ... */ break; } case MAC_CAPAB_RINGS: { /* ... */ break; } default: return (B_FALSE); } return (B_TRUE); }
下面各节将介绍所支持的功能和需要返回的相应功能数据。
传送硬件环和接收硬件环都是 DMA 通道,可由设备驱动程序公开。环与环组关联。接收环组与一个或多个 MAC 地址关联,与某个接收组关联的任何 MAC 地址匹配的所有网络通信都必须由 NIC 通过该组的其中一个环进行传送。到接收环组的通信的控制是通过第 2 层分类在硬件中启用的。
接收环到环组的映射可以是动态的也可以是静态的。对于动态环组,环可以根据框架的要求在组之间移动,从而动态地缩小或增加组的大小。但是,对于静态环组,这些环以静态方式指定给组,并且无法更改该指定。
如果某个接收组包含多个环,则 NIC 必须使用某个散列机制(如 RSS(Receive Side Scaling,接收端缩放))在这些环中分散通信流量,从而允许为多个连接指定不同的环。
必须仅将接收组中的一个组指定为缺省组(通常是位于索引 0 处的第一个组)。此接收组具有以下特性:
应至少有一个环。
指定给 NIC 的主 MAC 客户机。主 MAC 客户机指定为 NIC 的主 MAC 地址,通常为 IP 地址。
必须用于接收来自网络的所有多播和广播通信流量。
如果将 NIC 置于混杂模式,则必须使用它来接收与指定给非缺省接收组的 MAC 地址不匹配的所有通信。
就接收环和接收环组的硬件实现而言,需注意以下几点:
如果实现了多个接收环,但不支持第 2 层分类,则硬件应公开单个接收环组和属于框架的该组的所有环。
如果实现了第 2 层硬件分类,但是不支持 RSS,则硬件应注册多个接收组,让每个组包含一个环。
如果同时实现了第 2 层硬件分类和 RSS,则硬件应注册多个接收组,让每个组包含一个或多个环。
如果第 2 层硬件分类和 RSS 都未实现,则硬件不应通告环功能,或不应通告具有单个伪环和环组的环功能(可用来动态轮询适配器的通信)。
向框架注册环涉及的过程包括从框架到驱动程序的各种调用。以下步骤介绍了注册过程:
框架通过调用驱动程序查询驱动程序的 MAC_CAPAB_RINGS 功能。为传送环创建一个调用,为接收环创建一个调用。有关更多信息,请参见MAC_CAPAB_RINGS 功能。
此框架使用从前面的步骤中获得的 mr_rget(9E) 和 mr_gget (9E) 入口点来检索有关特定环或环组的信息。有关更多信息,请参见 mr_rget(9E) 和 mr_gget(9E) 手册页。
当框架要使用某个环时,它使用 mgi_start(9E ) 入口点启动环组,然后使用在前面的步骤中通告的 mri_start(9E) 入口点启动环。
现在,通信流量可以流经环,直到通过 mgi_stop(9E) 和 mri_stop(9E) 入口点将其停止。
为获得有关对硬件传送环和接收环的支持的信息,框架将在 cap 参数中发送 MAC_CAPAB_RINGS,并期待在指向 mac_capab_rings 结构的 cap_data 字段中接收返回的信息。
框架将分配 mac_capab_rings(9S) 结构,并为接收环将 mr_type 成员设置为 MAC_RING_TYPE_RX,或为传送环将其设置为 MAC_RING_TYPE_TX。然后,mac_capab_rings 结构的其余成员由驱动程序填充。
以下字段是在 mac_capab_rings 结构中定义的:
必须设置为 MAC_RINGS_VERSION_1。
环数。
组数。
定义以下值:
MAC_GROUP_TYPE_DYNAMIC-该组是动态的。
MAC_GROUP_TYPE_STATIC-此组为静态组。
有关更多信息,请参见环和环组第 2 层分类。
用于获取有关环组的更多信息的驱动程序入口点。有关更多信息,请参见mr_gget() 入口点。
用于获取有关环的更多信息的驱动程序入口点。有关更多信息,请参见mr_rget() 入口点。
用于向组添加环的驱动程序入口点。请参见 mr_gaddring(9E)。
用于从组删除环的驱动程序入口点。请参见 mr_gremring(9E)。
框架为与 mr_gnum 参数指示的组数对应的每个有效组索引调用 mr_gget(9E) 入口点。有关更多信息,请参见 mr_gget(9E)。调用 mr_gget() 之后,驱动程序在 mac_group_info 结构中返回组信息。此结构本身是由框架预分配的,并由驱动程序填充。
以下字段是在 mac_group_info 结构中定义的:
一个不透明的驱动程序组句柄,框架在将来调用组入口点时使用。
组中的环数。
组标志 MAC_GROUP_DEFAULT 将组标识为缺省组。有关更多信息,请参见环和环组第 2 层分类。
组启动入口点。
组停止入口点。
添加单点传送 MAC 地址入口点。
删除单点传送 MAC 地址入口点。
用来添加硬件 VLAN 过滤、标记和 VLAN 标记剥离的入口点。
用来删除硬件 VLAN 过滤、标记和 VLAN 标记剥离的入口点。
设置 RX 组 MTU 入口点
用于为组检索 SR-IOV 信息的入口点。有关更多信息,请参见环组和 SR-IOV 。
有关详细信息,请参见mac_group_info(9S)和 mac_group_info(9E)。
它定义 NIC 必须允许进行传送和接收的 VLAN ID。也就是说,将丢弃未在所配置的列表中列出的任何有标记数据包。
如果设置了 MAC_GROUP_VLAN_TRANSPARENT_ENABLE 标志,则它还将为该特定 VLAN ID 启用硬件 VLAN 标记和剥离。
对应于 mr_gnum 指示的组数和对 MAC_CAPAB_RINGS 的调用通告的 mr_rnum 指示的环数,框架为每个有效的组和环索引调用 mr_rget(9E) 入口点。有关详细信息,请参见 mr_rget(9E)。
完成对 mr_rget() 的调用后,驱动程序在 mac_ring_info 结构中返回环信息。此结构是由框架预分配的,并由驱动程序填充。
以下字段是在 mac_ring_info 结构中定义的:
一个不透明的驱动程序组句柄,框架在将来调用环入口点时使用。
环启动入口点。
环停止入口点
环统计信息入口点。有关更多信息,请参见GLDv3 网络统计信息。
环传送入口点。有关更多信息,请参见传输数据路径。
环轮询入口点。有关更多信息,请参见接收数据路径。
与此环的中断关联的 DDI 中断句柄。
启用 RX 环上的中断。有关更多信息,请参见接收数据路径。
禁用 RX 环上的中断。有关更多信息,请参见接收数据路径。
有关详细信息,请参见 mac_group_info(9S) 和 mac_ring_info(9S) 手册页。
注 - mri_tx() 必须仅为传送环设置,而 mri_poll() 必须仅为接收环设置。
注 - 如果某个驱动程序实现了环功能,则 mac_callbacks 结构中的 mc_tx() 入口点必须设置为 NULL。
具有 SR-IOV 功能的设备驱动程序使用 MAC_CAPAB_RINGS 功能,通过实现 mgi_getsriov_info(9E) 组入口点来告诉框架它们具有 SR-IOV 功能。PF 驱动程序负责实现该入口点。
在调用 mgi_getsriov_info(9E) 后,驱动程序在 mac_sriov_info 结构中返回 SR-IOV 信息。此结构是由框架预分配的,并由驱动程序填充。
PF(Physical Function,物理功能)驱动程序实例与 VF(Virtual Functions,虚拟功能)注册相同数量的传送和接收环组。PF 驱动程序通告的这些环组很特殊,用于管理 VF。这些环组中没有任何数据流过。它们用于配置单点传送 MAC 地址、设置 MTU、添加 VLAN 过滤器、删除 VLAN 过滤器、删除 VLAN 硬件以及为 VF 执行 VLAN 标记和剥离。
注 - VF 驱动程序对驱动程序希望加入的 MAC 多播组进行编程。PF 驱动程序不会控制这些地址的编程。
由 PF 驱动程序设置的 msi_vf_index 结构成员捕获与环组对应的 VF 索引。这是与驱动程序调用 pci_plist_getvf(9F) 函数时设备驱动程序使用的索引相同的索引。
有关 SR-IOV 驱动程序的详细信息,请参见第 21 章。
为了获得关于硬件校验和负载转移支持的数据,框架将在 cap 参数中发送 MAC_CAPA _HCKSUM。 请参见硬件校验和负载转移功能信息。
要在启用硬件校验和的情况下查询校验和负载转移元数据以及检索每个包的硬件校验和元数据,请使用 mac_hcksum_get(9F)。 请参见mac_hcksum_get()() 函数标志。
要设置校验和负载转移元数据,请使用 mac_hcksum_set(9F)。请参见mac_hcksum_set()() 函数标志。
有关更多信息,请参见硬件校验和:硬件和硬件校验和:MAC 层。
要将关于 MAC_CAPAB_HCKSUM 功能的信息传递给框架,驱动程序必须在指向 uint32_t 的 cap_data 中设置以下标志的组合。 这些标志指明驱动程序能够为外发包执行的硬件校验和负载转移的级别。
1 的补码的部分校验和功能
针对 IPv4 包的 1 的补码的完全校验和能力
针对 IPv6 包的 1 的补码的完全校验和能力
IPv4 头校验和负载转移功能
mac_hcksum_get() 的 flags 参数是以下值的组合:
计算此包的完整校验和。
完整校验和已在硬件中通过验证,证实是正确的。
根据传递给 mac_hcksum_get() 的其他参数计算 1 的补码的部分校验和。HCK_PARTIALCKSUM 与 HCK_FULLCKSUM 互斥。
计算 IP 报头校验和。
IP 头校验和已在硬件中通过验证,证实是正确的。
mac_hcksum_set 的 flags() 参数是以下值的组合:
通过 value 参数计算和传递完整校验和。
完整校验和已在硬件中通过验证,证实是正确的。
通过 value 参数计算和传递部分校验和。HCK_PARTIALCKSUM 与 HCK_FULLCKSUM 互斥。
通过 value 参数计算和传递 IP 头校验和。··
IP 头校验和已在硬件中通过验证,证实是正确的。
为了查询大段(或大量传送)负载转移支持,框架将在 cap 参数中发送 MAC_CAPA _LSO,并等待接收在指向 mac_capab_lso(9S) 结构的 cap_data 中返回的信息。框架将分配 mac_capab_lso 结构,并在 cap_data 中传递指向此结构的指针。 mac_capab_lso 结构包含一个 lso_basic_tcp_ipv4(9S) 结构和一个 lso_flags 成员。 如果驱动程序实例为 IPv4 上的 TCP 支持 LSO,请设置 lso_flags 中的 LSO_TX_BASIC_TCP_IPV4 标志,并将 lso_basic_tcp_ipv4 结构的 lso_max 成员设置为驱动程序实例支持的最大有效载荷大小。
使用 mac_lso_get(9F) 获得每个包的 LSO 元数据。 如果为此包启用了 LSO,则将在 mac_lso_get () flags 参数中设置 HW_LSO 标志。在大段的分段过程中使用的最大段大小 (maximum segment size, MSS) 将通过 mss 参数指向的位置返回。 有关更多信息,请参见大段负载转移。
数据路径入口点包括以下组件:
由驱动程序导出、由 GLDv3 框架调用以发送包的回调。
驱动程序为传输流量控制和接收包而调用的 GLDv3 框架入口点。
注 - 如果驱动程序实现了环功能,则驱动程序发送和接收的所有数据都将通过特定于环的入口点进行传送。
GLDv3 框架调用的用来向驱动程序传送消息块的传送入口点的类型取决于底层驱动程序对 MAC_CAPAB_RINGS 的支持。如果驱动程序支持 MAC_CAPAB_RINGS 功能,则框架将调用 mri_tx(9E) 环入口点。否则,框架将调用 mc_tx(9E) 入口点。
相应地,设备驱动程序必须在 mc_tx() 或 mri_tx() 中提供传送入口点的指针。有关更多信息,请参见GLDv3 MAC 注册数据结构和mr_rget() 入口点。
示例 19-6 mc_tx() 和 mri_tx() 入口点
mblk_t * xx_m_tx(void *arg, mblk_t *mp) { xx_t *xxp = arg; mblk_t *nmp; mutex_enter(&xxp->xx_xmtlock); if (xxp->xx_flags & XX_SUSPENDED) { while ((nmp = mp) != NULL) { xxp->xx_carrier_errors++; mp = mp->b_next; freemsg(nmp); } mutex_exit(&xxp->xx_xmtlock); return (NULL); } while (mp != NULL) { nmp = mp->b_next; mp->b_next = NULL; if (!xx_send(xxp, mp)) { mp->b_next = nmp; break; } mp = nmp; } mutex_exit(&xxp->xx_xmtlock); return (mp); }
以下各节将讨论与将数据传输至硬件相关的主题。
如果驱动程序因硬件资源不足而无法发送包,则驱动程序将返回无法发送的包的子链。此后,在更多描述符可用时,驱动程序必须调用 mac_tx_update(9F) 或 mac_tx_ring(9F) 以通知框架。驱动程序将根据它是否实现了环功能来调用这两个函数之一。
如果驱动程序指定了硬件校验和支持(请参见硬件校验和负载转移),则驱动程序必须执行以下任务:
使用 mac_hcksum_get(9F) 检查每个包的硬件校验和元数据。
对硬件进行编程,以执行所需的校验和计算。
如果驱动程序指定了 LSO 功能(请参见大段(或大量传送)负载转移),则驱动程序必须使用 mac_lso_get(9F) 来查询是否必须在包上执行 LSO。·
管理员配置 VLAN 时,MAC 层将通过 mc_tx() 入口点,在外发包传递至驱动程序之前将所需的 VLAN 头添加到外发包中。不过,如果硬件支持 VLAN 标记,则标记将转移到硬件上。有关更多详细信息,请参见mr_gget() 入口点。
接收数据路径可能是由中断驱动的,也可能是由轮询驱动的。
注意:如果驱动程序不支持环功能,请在驱动程序的中断处理程序中调用mac_rx(9F) 函数,以将包含一个或多个数据包的链沿栈向上传送到 MAC 层。避免在调用 mac_rx() 或 mac_rx_ring() 的过程中持有互斥锁或其他锁。需要特别指出的是,在调用 mac_rx() 或 mac_rx_ring() 期间不得持有传输线程可使用的锁。
在中断模式中,只要数据包链被 NIC 接收并且可供驱动程序选取,就将从驱动程序向框架发送数据包链。数据包链由通过 b_next 彼此连接的一个或多个 mblk_t 组成,并且使每个数据包的处理开销降低。通过调用 mac_rx_ring() 入口点,接收的数据包将以中断模式向上传递给框架。
void mac_rx_ring(mac_handle_t mh, mac_ring_handle_t mrh, mblk_t *mp_chain, int64_tmr_gen_num)
通过 mac_register() 函数向内核进行注册后,mh_handle 将响应设备驱动程序获取的 MAC 句柄。mrh _handle 是作为 mr_rget() 调用的一部分传递给驱动程序的框架环句柄。如果接收环是通过 mri_start() 入口点启动的,则 mr_gen_num 必须设置为框架指定的生成号。驱动程序提供的环生成号将与框架内保留的环生成号进行匹配。如果它们不匹配,则接收的数据包将被视为来自早期指定的环的过时数据包,并将被丢弃。
除了能够通过由中断驱动的路径接收数据包之外,框架还支持基于轮询的数据路径。在轮询模式下,在栈中运行的内核线程通过一个轮询入口点从驱动程序提取数据包。这样便使栈能够有效控制何时处理数据包以及使用何种优先级,同时可基于实际负载减少进入系统的中断数量。此外,轮询还使栈能够对接收的通信流量更有效地实施带宽限制,这在虚拟化方案中尤其重要。主机根据需要在中断模式和轮询模式之间切换。当环处于轮询模式时,驱动程序不应使用 mac_rx_ring() 函数传送通过接收环接收的数据包。这能够得以保证,因为中断在轮询模式下被禁用。相反,框架将调用驱动程序作为 mac_ring_info 结构的一部分公开的 mri_poll() 入口点。有关更多信息,请参见mr_rget() 入口点。
缺省情况下,环在启动以后应处于中断模式。只要环处于中断模式,它就应通过入口点以链的形式向上传递接收的数据包。当主机将环切换到轮询模式时,它将通过 mac_intr 结构(是之前通过 mac_ring_info 结构公开的)调用入口点来禁用其中断。
如果驱动程序指定了硬件校验和支持(请参见硬件校验和负载转移),则驱动程序必须使用 mac_hcksum_set(9F) 函数将硬件校验和元数据与包关联。
VLAN 包必须连同标记一起传递至 MAC 层。切勿剥离数据包中的 VLAN 头。但是,如果硬件支持 VLAN 剥离,并且框架已请求硬件剥离 VLAN 标记,则硬件可以剥离 VLAN 标记以提高性能。有关更多信息,请参见mr_gget() 入口点。
驱动程序可以调用以下函数来通知网络栈驱动程序的状态已更改。
void mac_tx_update(mac_handle_t mh);
void mac_tx_ring_update(mac_handle_t mh, mac_ring_handle_t rh)
mac_tx_update(9F)或 mac_tx_ring(9F) 函数通知框架有更多 TX 描述符可用。如果 mc_tx() 或 mri_tx() 返回了非空数据包链,则驱动程序必须在资源可用后立即调用 mac_tx_update() 或 mac_tx_ring_update(),通知 MAC 层重试之前作为未发送的数据包返回的数据包。有关 mc_tx() 和 mri_tx() 入口点的更多信息,请参见传输数据路径。
void mac_link_update(mac_handle_t mh, link_state_t new_state);
mac_link_update(9F) 函数通知 MAC 层介质链接的状态已更改。new_state 参数必须为以下值之一:
介质链路为启动状态。
介质链路为关闭状态。
介质链路为未知状态。
设备驱动程序为其管理的设备实例维护一组统计信息。MAC 层通过驱动程序的 mc_getstat(9E) 入口点查询这些统计信息。
int mc_getstat(void *driver_handle, uint_t stat, uint64_t *stat_value);
GLDv3 框架使用 stat 指定所查询的统计信息。 驱动程序使用 stat_value 返回 stat 指定的统计信息的值。 如果返回了统计信息的值,则 mc_getstat() 必须返回 0。如果驱动程序不支持 stat 统计信息,mc_getstat() 必须返回 ENOTSUP。
所支持的 GLDv3 统计信息是通用 MAC 统计信息和特定于以太网的统计信息的联合。有关所支持的统计信息的完整列表,请参见 mc_getstat(9E) 手册页。
示例 19-7 mc_getstat() 入口点
int xx_m_getstat(void *arg, uint_t stat, uint64_t *val) { xx_t *xxp = arg; mutex_enter(&xxp->xx_xmtlock); if ((xxp->xx_flags & (XX_RUNNING|XX_SUSPENDED)) == XX_RUNNING) xx_reclaim(xxp); mutex_exit(&xxp->xx_xmtlock); switch (stat) { case MAC_STAT_MULTIRCV: *val = xxp->xx_multircv; break; /* ... */ case ETHER_STAT_MACRCV_ERRORS: *val = xxp->xx_macrcv_errors; break; /* ... */ default: return (ENOTSUP); } return (0); }
mri_stat() 环入口点是一个强制的环入口点,支持环功能的所有设备驱动程序都必须实现该入口点。框架将使用该入口点查询为每个硬件传送环和接收环维护的统计信息。
对于硬件传送环,框架将查询以下统计信息:
MAC_STAT_OERRORS
MAC_STAT_OBYTES
MAC_STAT_OPACKETS
对于硬件接收环,框架将查询以下统计信息:
MAC_STAT_IERRORS
MAC_STAT_RBYTES
MAC_STAT_IPACKETS
使用 mc_propinfo(9E) 入口点返回属性的不可变属性。此信息包括权限、缺省值和允许的值范围。使用 mc_setprop(9E) 为此特定驱动程序实例设置属性值。使用 mc_getprop(9E) 返回属性的当前值。
有关属性及其类型的完整列表,请参见 mc_propinfo(9E) 手册页。
mc_propinfo() 入口点应调用 mac_prop_info_set_perm()、mac_prop_info_set_default() 和 mac_prop_info_set_range() 函数来关联所查询属性的特定属性,例如缺省值、权限或允许的值范围等。
mac_prop_info_set_default_uint8(9F)、mac_prop_info_set_default_str(9F) 和 mac_prop_info_set_default_link_flowctrl(9F) 函数将缺省值与特定属性相关联。mac_prop_info_set_range_uint32(9F) 函数为特定属性关联值的允许范围。
mac_prop_info_set_perm(9F) 函数指定属性的权限。权限可以是以下值之一:
属性为只读
属性为只写
属性可以是读取和写入
如果 mc_propinfo() 入口点未为特定属性调用 mac_prop_info_set_perm(),GLDv3 框架将假设该属性拥有对应于 MAC_PROP_PERM_RW 的读写权限。
除了 mc_propinfo(9E) 手册页中列出的属性之外,驱动程序还可公开驱动程序专用属性。使用 mac_register 结构的 m_priv_props 字段指定驱动程序支持的驱动程序专用属性。框架在 mc_setprop()、 mc_getprop() 或 mc_propinfo() 中传递 MAC_PROP_PRIVATE 属性·ID。 有关更多信息,请参见 mc_propinfo (9E) 手册页。
下表列出了属于 GLDv3 网络设备驱动程序框架的入口点、其他 DDI 函数和数据结构。
表 19-1 GLDv3 接口
|