JavaScript is required to for searching.
跳过导航链接
退出打印视图
编写设备驱动程序     Oracle Solaris 11.1 Information Library (简体中文)
为本文档评分
search filter icon
search icon

文档信息

前言

第 1 部分针对 Oracle Solaris 平台设计设备驱动程序

1.  Oracle Solaris 设备驱动程序概述

2.  Oracle Solaris 内核和设备树

3.  多线程

4.  属性

5.  管理事件和排队任务

6.  驱动程序自动配置

7.  设备访问:程控 I/O

8.  中断处理程序

9.  直接内存访问 (Direct Memory Access, DMA)

10.  映射设备和内核内存

11.  设备上下文管理

12.  电源管理

13.  强化 Oracle Solaris 驱动程序

14.  分层驱动程序接口 (Layered Driver Interface, LDI)

第 2 部分设计特定种类的设备驱动程序

15.  字符设备驱动程序

16.  块设备驱动程序

17.  SCSI 目标驱动程序

18.  SCSI 主机总线适配器驱动程序

19.  网络设备驱动程序

GLDv3 网络设备驱动程序框架

GLDv3 MAC 注册

GLDv3 MAC 注册过程

GLDv3 MAC 注册函数

GLDv3 MAC 注册数据结构

GLDv3 功能

MAC 环功能

硬件校验和负载转移

大段(或大量传送)负载转移

GLDv3 数据路径

传输数据路径

接收数据路径

GLDv3 状态更改通知

GLDv3 网络统计信息

GLDv3 属性

GLDv3 接口汇总

GLDv2 网络设备驱动程序框架

GLDv2 设备支持

Ethernet V2 和 ISO 8802-3 (IEEE 802.3)

TPR 和 FDDI:SNAP 处理

TPR:源路由

GLDv2 DLPI 提供者

GLDv2 DLPI 原语

GLDv2 I/O 控制函数

GLDv2 驱动程序需求

GLDv2 网络统计信息

GLDv2 声明和数据结构

gld_mac_info 结构

gld_stats 结构

GLDv2 函数参数

GLDv2 入口点

gldm_reset() 入口点

gldm_start() 入口点

gldm_stop() 入口点

gldm_set_mac_addr() 入口点

gldm_set_multicast() 入口点

gldm_set_promiscuous() 入口点

gldm_send() 入口点

gldm_intr() 入口点

gldm_get_stats() 入口点

gldm_ioctl() 入口点

GLDv2 返回值

GLDv2 服务例程

gld_mac_alloc() 函数

gld_mac_free() 函数

gld_register() 函数

gld_unregister() 函数

gld_recv() 函数

gld_sched() 函数

gld_intr() 函数

20.  USB 驱动程序

21.  SR-IOV 驱动程序

第 3 部分生成设备驱动程序

22.  编译、装入、打包和测试驱动程序

23.  调试、测试和调优设备驱动程序

24.  推荐的编码方法

第 4 部分附录

A.  硬件概述

B.  Oracle Solaris DDI/DKI 服务汇总

C.  使设备驱动程序支持 64 位

D.  控制台帧缓存器驱动程序

E.  pci.conf 文件

索引

请告诉我们如何提高我们的文档:
过于简略
不易阅读或难以理解
重要信息缺失
错误的内容
需要翻译的版本
其他
Your rating has been updated
感谢您的反馈!

您的反馈将非常有助于我们提供更好的文档。 您是否愿意参与我们的内容改进并提供进一步的意见?

GLDv3 网络设备驱动程序框架

GLDv3 框架是 MAC 插件和 MAC 驱动程序服务例程与结构的基于函数调用的接口。GLDv3 框架代表符合 GLDv3 的驱动程序实现必要的 STREAMS 入口点,并处理 DLPI 兼容性。

本节讨论以下主题:

GLDv3 MAC 注册

GLDv3 为使用 MAC_PLUGIN_IDENT_ETHER 插件类型注册的驱动程序定义驱动程序 API。

GLDv3 MAC 注册过程

GLDv3 设备驱动程序必须执行以下步骤来向 MAC 层注册:

GLDv3 MAC 注册函数

GLDv3 接口包括在使用 MAC 层注册过程中通告的驱动程序入口点和驱动程序调用的 MAC 入口点。

mac_init_ops() 和·mac_fini_ops() 函数
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_alloc()mac_free() 函数
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 结构。

mac_register()mac_unregister () 函数
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);
                }
        /* ... */
}

GLDv3 MAC 注册数据结构

本节介绍的结构是在 sys/mac_provider.h 头文件中定义的。 在 GLDv3 驱动程序中引入 sys/mac_ether.hsys/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_ETHERmac_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_ioctl() 入口点。

MC_GETCAPAB

显示 mc_getcapab() 入口点。

MC_SETPROP

显示·mc_setprop() 入口点。

MC_GETPROP

显示 mc_getprop() 入口点。

MC_PROPINFO

显示 mc_propinfo() 入口点。

MC_PROPERTIES

显示所有属性入口点。设置·MC_PROPERTIES 等同于设置全部三个标志:MC_SETPROP MC_GETPROPMC_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 实现一种功能机制,允许框架查询和启用 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);
}

MAC 环功能

下面各节将介绍所支持的功能和需要返回的相应功能数据。

环和环组第 2 层分类

传送硬件环和接收硬件环都是 DMA 通道,可由设备驱动程序公开。环与环组关联。接收环组与一个或多个 MAC 地址关联,与某个接收组关联的任何 MAC 地址匹配的所有网络通信都必须由 NIC 通过该组的其中一个环进行传送。到接收环组的通信的控制是通过第 2 层分类在硬件中启用的。

接收环到环组的映射可以是动态的也可以是静态的。对于动态环组,环可以根据框架的要求在组之间移动,从而动态地缩小或增加组的大小。但是,对于静态环组,这些环以静态方式指定给组,并且无法更改该指定。

如果某个接收组包含多个环,则 NIC 必须使用某个散列机制(如 RSS(Receive Side Scaling,接收端缩放))在这些环中分散通信流量,从而允许为多个连接指定不同的环。

必须仅将接收组中的一个组指定为缺省组(通常是位于索引 0 处的第一个组)。此接收组具有以下特性:

就接收环和接收环组的硬件实现而言,需注意以下几点:

注册环和组过程概述

向框架注册环涉及的过程包括从框架到驱动程序的各种调用。以下步骤介绍了注册过程:

  1. 框架通过调用驱动程序查询驱动程序的 MAC_CAPAB_RINGS 功能。为传送环创建一个调用,为接收环创建一个调用。有关更多信息,请参见MAC_CAPAB_RINGS 功能

  2. 此框架使用从前面的步骤中获得的 mr_rget(9E) 和 mr_gget (9E) 入口点来检索有关特定环或环组的信息。有关更多信息,请参见 mr_rget(9E)mr_gget(9E) 手册页。

  3. 当框架要使用某个环时,它使用 mgi_start(9E ) 入口点启动环组,然后使用在前面的步骤中通告的 mri_start(9E) 入口点启动环。

    现在,通信流量可以流经环,直到通过 mgi_stop(9E) 和 mri_stop(9E) 入口点将其停止。

MAC_CAPAB_RINGS 功能

为获得有关对硬件传送环和接收环的支持的信息,框架将在 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 结构中定义的:

mr_version

必须设置为 MAC_RINGS_VERSION_1

mr_rnum

环数。

mr_gnum

组数。

mr_group_type

定义以下值:

  • MAC_GROUP_TYPE_DYNAMIC-该组是动态的。

  • MAC_GROUP_TYPE_STATIC-此组为静态组。

有关更多信息,请参见环和环组第 2 层分类

mr_gget()

用于获取有关环组的更多信息的驱动程序入口点。有关更多信息,请参见mr_gget() 入口点

mr_rget()

用于获取有关环的更多信息的驱动程序入口点。有关更多信息,请参见mr_rget() 入口点

mr_gaddring()

用于向组添加环的驱动程序入口点。请参见 mr_gaddring(9E)

mr_gremring()

用于从组删除环的驱动程序入口点。请参见 mr_gremring(9E)

mr_gget() 入口点

框架为与 mr_gnum 参数指示的组数对应的每个有效组索引调用 mr_gget(9E) 入口点。有关更多信息,请参见 mr_gget(9E)。调用 mr_gget() 之后,驱动程序在 mac_group_info 结构中返回组信息。此结构本身是由框架预分配的,并由驱动程序填充。

以下字段是在 mac_group_info 结构中定义的:

mgi_driver

一个不透明的驱动程序组句柄,框架在将来调用组入口点时使用。

mgi_count

组中的环数。

mgi_flags

组标志 MAC_GROUP_DEFAULT 将组标识为缺省组。有关更多信息,请参见环和环组第 2 层分类

mgi_start

组启动入口点。

mgi_stop

组停止入口点。

mgi_addmac

添加单点传送 MAC 地址入口点。

mgi_remmac

删除单点传送 MAC 地址入口点。

mgi_addvlan

用来添加硬件 VLAN 过滤、标记和 VLAN 标记剥离的入口点。

mgi_remvlan

用来删除硬件 VLAN 过滤、标记和 VLAN 标记剥离的入口点。

mgi_setmtu

设置 RX 组 MTU 入口点

mgi_getsriov_info

用于为组检索 SR-IOV 信息的入口点。有关更多信息,请参见环组和 SR-IOV

有关详细信息,请参见mac_group_info(9S)mac_group_info(9E)


注 - mgi_addmac(9E)mgi_remmac(9E) 入口点仅用于接收组。只要设备驱动程序支持环功能,mc_unicst(9E) 入口点就必须设置为 NULL。



注 - mgi_addvlan() 入口点执行以下操作:


mr_rget() 入口点

对应于 mr_gnum 指示的组数和对 MAC_CAPAB_RINGS 的调用通告的 mr_rnum 指示的环数,框架为每个有效的组和环索引调用 mr_rget(9E) 入口点。有关详细信息,请参见 mr_rget(9E)。

完成对 mr_rget() 的调用后,驱动程序在 mac_ring_info 结构中返回环信息。此结构是由框架预分配的,并由驱动程序填充。

以下字段是在 mac_ring_info 结构中定义的:

mri_driver

一个不透明的驱动程序组句柄,框架在将来调用环入口点时使用。

mri_start

环启动入口点。

mri_stop

环停止入口点

mri_stat

环统计信息入口点。有关更多信息,请参见GLDv3 网络统计信息

mri_tx

环传送入口点。有关更多信息,请参见传输数据路径

mri_poll

环轮询入口点。有关更多信息,请参见接收数据路径

mri_intr_ddi_handle

与此环的中断关联的 DDI 中断句柄。

mri_intr_enable(9E)

启用 RX 环上的中断。有关更多信息,请参见接收数据路径

mri_intr_disable(9E)

禁用 RX 环上的中断。有关更多信息,请参见接收数据路径

有关详细信息,请参见 mac_group_info(9S)mac_ring_info(9S) 手册页。


注 - mri_tx() 必须仅为传送环设置,而 mri_poll() 必须仅为接收环设置。



注 - 如果某个驱动程序实现了环功能,则 mac_callbacks 结构中的 mc_tx() 入口点必须设置为 NULL。


环组和 SR-IOV

具有 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_tcap_data 中设置以下标志的组合。 这些标志指明驱动程序能够为外发包执行的硬件校验和负载转移的级别。

HCKSUM_INET_PARTIAL

1 的补码的部分校验和功能

HCKSUM_INET_FULL_V4

针对 IPv4 包的 1 的补码的完全校验和能力

HCKSUM_INET_FULL_V6

针对 IPv6 包的 1 的补码的完全校验和能力

HCKSUM_IPHDRCKSUM

IPv4 头校验和负载转移功能

mac_hcksum_get()() 函数标志

mac_hcksum_get()flags 参数是以下值的组合:

HCK_FULLCKSUM

计算此包的完整校验和。

HCK_FULLCKSUM_OK

完整校验和已在硬件中通过验证,证实是正确的。

HCK_PARTIALCKSUM

根据传递给 mac_hcksum_get() 的其他参数计算 1 的补码的部分校验和。HCK_PARTIALCKSUM HCK_FULLCKSUM 互斥。

HCK_IPV4_HDRCKSUM

计算 IP 报头校验和。

HCK_IPV4_HDRCKSUM_OK

IP 头校验和已在硬件中通过验证,证实是正确的。

mac_hcksum_set()() 函数标志

mac_hcksum_setflags() 参数是以下值的组合:

HCK_FULLCKSUM

通过 value 参数计算和传递完整校验和。

HCK_FULLCKSUM_OK

完整校验和已在硬件中通过验证,证实是正确的。

HCK_PARTIALCKSUM

通过 value 参数计算和传递部分校验和。HCK_PARTIALCKSUM HCK_FULLCKSUM 互斥。

HCK_IPV4_HDRCKSUM

通过 value 参数计算和传递 IP 头校验和。··

HCK_IPV4_HDRCKSUM_OK

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 框架调用的用来向驱动程序传送消息块的传送入口点的类型取决于底层驱动程序对 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) 以通知框架。驱动程序将根据它是否实现了环功能来调用这两个函数之一。

硬件校验和:硬件

如果驱动程序指定了硬件校验和支持(请参见硬件校验和负载转移),则驱动程序必须执行以下任务:

大段负载转移

如果驱动程序指定了 LSO 功能(请参见大段(或大量传送)负载转移),则驱动程序必须使用 mac_lso_get(9F) 来查询是否必须在包上执行 LSO。·

虚拟 LAN:硬件

管理员配置 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 层

如果驱动程序指定了硬件校验和支持(请参见硬件校验和负载转移),则驱动程序必须使用 mac_hcksum_set(9F) 函数将硬件校验和元数据与包关联。

虚拟 LAN:MAC 层

VLAN 包必须连同标记一起传递至 MAC 层。切勿剥离数据包中的 VLAN 头。但是,如果硬件支持 VLAN 剥离,并且框架已请求硬件剥离 VLAN 标记,则硬件可以剥离 VLAN 标记以提高性能。有关更多信息,请参见mr_gget() 入口点

GLDv3 状态更改通知

驱动程序可以调用以下函数来通知网络栈驱动程序的状态已更改。

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 参数必须为以下值之一:

LINK_STATE_UP

介质链路为启动状态。

LINK_STATE_DOWN

介质链路为关闭状态。

LINK_STATE_UNKNOWN

介质链路为未知状态。

GLDv3 网络统计信息

设备驱动程序为其管理的设备实例维护一组统计信息。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() 环入口点是一个强制的环入口点,支持环功能的所有设备驱动程序都必须实现该入口点。框架将使用该入口点查询为每个硬件传送环和接收环维护的统计信息。

对于硬件传送环,框架将查询以下统计信息:

对于硬件接收环,框架将查询以下统计信息:

GLDv3 属性

使用 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) 函数指定属性的权限。权限可以是以下值之一:

MAC_PROP_PERM_READ

属性为只读

MAC_PROP_PERM_WRITE

属性为只写

MAC_PROP_PERM_RW

属性可以是读取和写入

如果 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 接口汇总

下表列出了属于 GLDv3 网络设备驱动程序框架的入口点、其他 DDI 函数和数据结构。

表 19-1 GLDv3 接口

接口名称
说明
必需的入口点
从驱动程序检索网络统计信息。请参见GLDv3 网络统计信息
启动一个驱动程序实例。GLDv3 框架在尝试任何操作之前都会调用 start 入口点。
停止一个驱动程序实例。MAC 层会在设备分离之前调用停止入口点。
更改设备驱动程序实例的混杂模式。
添加或删除多点传送地址。
设置主单点传送地址。设备必须通过 mac_rx() 开始回传带有与新单点传送地址匹配的目标 MAC 地址的包。 有关 mac_rx() 的信息,请参见接收数据路径
发送一个或多个包。请参见传输数据路径
获取传送和接收环信息。有关更多信息,请参见mr_rget() 入口点
获取传送和接收环信息。有关更多信息,请参见mr_gget() 入口点
向接收组添加环。仅当支持动态环分组时才是必需的。请参见MAC_CAPAB_RINGS 功能
从接收组中删除环。仅当支持动态环分组时才是必需的。请参见MAC_CAPAB_RINGS 功能
mri_tx(9E)
为 TX 环传送数据包。有关更多信息,请参见mr_rget() 入口点
mri_poll()
轮询 RX 环以检查数据包。有关更多信息,请参见mr_rget() 入口点
mri_stat()
环统计信息。有关更多信息,请参见mr_rget() 入口点
启用 RX 环上的中断。有关更多信息,请参见mr_rget() 入口点
禁用 RX 环上的中断。有关更多信息,请参见mr_rget() 入口点
将 MAC 地址编程到 RX 环组的驱动程序硬件中。有关更多信息,请参见mr_gget() 入口点
从 RX 环组的驱动程序硬件中删除之前编程到其中的 MAC 地址。有关更多信息,请参见mr_gget() 入口点
可选入口点
可选 ioctl 驱动程序接口。此工具仅供调试之用。
检索功能。请参见GLDv3 功能
设置属性值。请参见GLDv3 属性
获得属性值。请参见GLDv3 属性
获得关于属性的信息。请参见GLDv3 属性
mri_start()
启动环。有关更多信息,请参见mr_rget() 入口点
mri_stop()
停止环。有关更多信息,请参见mr_rget() 入口点
环组启动。有关更多信息,请参见mr_gget() 入口点
环组停止。有关更多信息,请参见mr_gget() 入口点
mgi_addvlan()
在硬件中启用 VLAN 过滤。有关更多信息,请参见mr_gget() 入口点
mgi_remvlan()
删除之前编程的 VLAN 过滤器。有关更多信息,请参见mr_gget() 入口点
mgi_setmtu()
设置 RX 组 MTU。有关更多信息,请参见mr_gget() 入口点
mgi_get_sriov_info()
获取 SR-IOV 信息。有关更多信息,请参见环组和 SR-IOV
数据结构
注册信息。请参见GLDv3 MAC 注册数据结构
驱动程序回调。请参见GLDv3 MAC 注册数据结构
LSO 元数据。请参见大段(或大量传送)负载转移
TCP/IPv4 的 LSO 元数据。请参见大段(或大量传送)负载转移
有关更多信息,请参见MAC 环功能
有关更多信息,请参见mr_gget() 入口点
有关更多信息,请参见mr_rget() 入口点
mac_intr_t
mac_sriov_info
MAC 注册函数
分配新 mac_register 结构。 请参见GLDv3 MAC 注册
释放 mac_register 结构。
向 MAC 层注册。
从 MAC 层取消注册。
初始化驱动程序的 dev_ops(9S) 结构。
释放驱动程序的 dev_ops 结构。
数据传输函数
向上传递接收到的包。请参见接收数据路径
mac_rx_ring(9F)
向上传递接收到的包。请参见接收数据路径
TX 资源可用。请参见GLDv3 状态更改通知
mac_tx_ring_update(9F )
TX 资源可用。有关更多信息,请参见GLDv3 状态更改通知
链接状态已更改。
检索硬件校验和信息。请参见硬件校验和负载转移传输数据路径
附加硬件校验和信息。请参见硬件校验和负载转移接收数据路径
检索 LSO 信息。请参见大段(或大量传送)负载转移
属性函数
设置属性权限。请参见GLDv3 属性
设置属性值。
设置属性值范围。