第 1 部分针对 Oracle Solaris 平台设计设备驱动程序
9. 直接内存访问 (Direct Memory Access, DMA)
14. 分层驱动程序接口 (Layered Driver Interface, LDI)
物理功能 (Physical Function, PF) 驱动程序
虚拟功能 (Virtual Function, VF) 驱动程序
以下各节介绍了 SR-IOV 驱动程序的接口。
SR-IOV 驱动程序必须使用 pci_param_get(9F) 接口来获取当前配置的参数的列表。此接口在驱动程序连接期间或任何其他合适的时间被调用。返回的数据是指向参数列表的指针,包含 PF 及其相应 VF 设备的名称-值信息。
int pci_param_get(dev_info_t *dip, pci_param_t *php)
其中:
指向 dev_info 结构的指针。
指向 param 句柄 pci_param_t 的指针。
在调用 pci_param_get 接口后,设备驱动程序应执行以下步骤来获取 PF 和 VF 的参数列表:
调用 pci_plist_get(9F) 接口来获取 PF 设备的参数列表,调用 pci_plist_getvf(9F) 接口来获取所配置的 VF 的参数列表。
调用 pci_plist_lookup(9F) 接口来获取设备参数。
验证所有的 PF 和 VF 参数。
如果参数与当前配置不匹配,则驱动程序应当使设备连接失败。
调用 pci_param_free(9F) 接口来释放用于获取 PF 和所配置的 VF 设备的参数的参数句柄指针。请参见示例 21-2。
注 - 应在配置 VF 之前完成参数验证。
名称-值对是针对每台设备分别定义的。PF 有一组名称-值对,每个已配置的 VF 也有一组名称-值对。名称-值对是可选的,可以为任何或所有设备省略名称-值对。
示例 21-2 SR-IOV pci_param_get(9F) 例程
pci_param_t my_params; pci_plist_t pf_plist; pci_plist_t vf_plist[8]; labelp = NULL; rval = pci_param_get(dip,&my_params); if (rval || (my_params == NULL)) { cmn_err(CE_NOTE, "No params available\n"); goto continue_with_attach; } rval = pci_plist_get(my_params, &pf_list); if (rval || (pf_plist == NULL)) { cmn_err(CE_NOTE, "No params for PF \n"); goto continue_with_attach; } for (i = 0; i < 8; i++) { rval = pci_plist_getvf(my_params, i, &vf_plist[i]); if (rval || (vf_plist[i] == NULL)) { cmn_err(CE_WARN, "No params for VF %d\n", i); continue; } } pci_param_free(my_params); /* * Validate the PF and VF params lists. * Fail the attach if the params are incompatible or exceed the * resources available. */ continue_with_attach:
SR-IOV 设备驱动程序可以使用 pci_param_get_ioctl(9F) 接口从 arg 参数中提取 PF 和 VF 设备的参数(如果这些驱动程序实现了 IOV_VALIDATE_PARAM ioctl)。
int pci_param_get_ioctl(dev_info_t *dip, intptr_t arg, int mode,pci_param_t *php)
其中:
指向 dev_info 结构的指针。
通过驱动程序的 ioctl 调用获取的参数
通过驱动程序的 ioctl 调用获取的参数
指向通过调用 pci_param_get() 或 pci_param_get_ioctl() 接口获取的 param 句柄 pci_param_t 的指针。
在检索参数后,驱动程序应调用 pci_param_free() 接口来释放在该调用中返回的 param 句柄。
pci_plist_get(9F) 接口用于从通过 pci_param_get(9F) 调用或 pci_param_get_ioctl(9F) 调用获取的 param 句柄中获取参数列表。
int pci_plist_get(pci_param_t param, pci_plist_t *plist_p)
其中:
从 pci_param_get() 或 pci_param_get_ioctl() 接口获取的句柄。
指向在成功返回时返回非空 plist 的 pci_plist_t 的指针。
从 pci_plist_get() 调用返回的 plist 仅适用于 PF 功能。结构 pci_plist_t 支持以下数据类型的数组:
int8_t
uint8
int16_t
uint16_t
int32_t
uint32_t
int64_t
uint64_t
char *
pci_plist_getvf(9F) 接口用于获取 VF 设备的名称-值对列表。
int pciv_plist_getvf (pci_param_t param, uint16_t vf_index, pci_plist_t *vfplist_p )
其中:
从 pci_param_get() 或 pci_param_get_ioctl() 接口获取的一个句柄。
介于 0 到 #VFS - 1 之间的一个值。
指向 pci_plist_t 结构的指针。
pciv_vf_config(9F) 接口由 SR-IOV 驱动程序使用,用于获取关于 VF 的配置信息,还用于在驱动程序连接期间配置 VF。
#include <sys/sunddi.h> int pciv_vf_config(dev_info_t *dip, pciv_config_vf_t *vfcfg_p)
其中:
指向 dev_info 结构的指针。
指向 pciv_config_vf 结构的指针。
typedef enum { PCIV_VFCFG_PARAM, PCIV_VF_ENABLE, PCIV_VF_DISABLE PCIV_EVT_VFENABLE_PRE, PCIV_EVT_VFENABLE_POST, PCIV_EVT_VFDISABLE_PRE, PCIV_EVT_VFDISABLE_POST } pciv_vf_config_cmd_t;
pciv_config_vf 结构包含以下字段:
typedef struct pciv_config_vf { int version; pciv_vf_config_cmd_t cmd; uint16_t num_vf; uint16_t first_vf_offset; uint16_t vf_stride; boolean_t ari_cap; uint32_t page_size; } pciv_config_vf_t;
其中:
版本号。
用于指示是否调用此接口来获取配置信息或连接 VF。
PCIV_VFCFG_PARAM-获取配置信息
PCIV_VF_ENABLE-启用 VF
PCIV_EVT_VFENABLE_PRE
PCIV_EVT_VFDISABLE_PRE
PCIV_EVT_VFENABLE_POST
PCIV_EVT_VFDISABLE_POST
在后端定义的 VF 的数量。
两个 VF 之间的距离。
第一个 VF 与 PF 之间的偏移量。
具有 ARI 功能的分层结构。
指定系统页大小。
驱动程序首先应在将 cmd 字段设置为 PCIV_VFCFG_PARAM 的情况下调用 pciv_vfconfig() 接口来获取配置信息。然后,驱动程序应在将 cmd 字段设置为 PCIV_VF_ENABLE 的情况下再次调用此接口来配置 VF。
驱动程序可能会返回以下错误代码之一:
DDI_SUCCESS
DDI_FAILURE
PCIV_REQRESET
PCIV_REQREATTACH
在调用 pciv_vf_config() 接口来启用 VF 之前,驱动程序不是必须通过设置 DDI_CB_FLAG_SRIOV 位来注册回调不可。不过,要在 SR-IOV 框架未配置 VF 时接收通知,驱动程序必须在启用 VF 后通过设置 DDI_CB_FLAG_SRIOV 位来注册回调。有关更多信息,请参见驱动程序回调。
可支持 VF 的所有 PF 驱动程序应该通过在标志参数中设置 DDI_CB_FLAG_SRIOV 标志的情况下调用 ddi_cb_register(9F) 来告诉 PCIe 框架它们的容量。在驱动程序的连接例程中必须调用 ddi_cb_register() 函数。如果 PF 设备驱动程序在其连接例程中调用 pciv_vf_config() 函数来启用 VF,则 PF 驱动程序应在启用 VF 后调用 ddi_cb_register() 函数。
框架需要使用 DDI_CB_FLAG_SRIOV 标志才能执行以下操作:
向 Sparc OVM 代理指示存在可支持 VF 的设备驱动程序。Sparc OVM 代理随后将允许创建 VF 设备。如果没有此功能,用户将无法在 Sparc 平台上创建 VF。
框架在禁用 VF 前后将回调 PF 驱动程序。这将有助于 PF 驱动程序执行内部记帐以持续支持 VF。
pci_plist_lookup(9F) 接口可由驱动程序用来查找受支持的各种数据类型的名称-值对。函数将查找与接口名称指示的名称和类型匹配的 nvpair(名称-值对)。如果找到,将修改 nelem 和 val 以分别包含值中的元素数和数据的起始地址。
pci_plist_lookup() 接口支持以下数据类型:
int pci_plist_lookup_int8(pci_plist_t plist , const char *name,int8_t * val)
int pci_plist_lookup_uint8(pci_plist_t plist , const char *name,uint8_t * val)
int pci_plist_lookup_int16(pci_plist_t plist , const char *name,int16_t * val)
int pci_plist_lookup_uint16(pci_plist_t plist , const char *name, uint16_t * val)
int pci_plist_lookup_int32(pci_plist_t plist , const char *name,int32_t * val)
int pci_plist_lookup_uint32(pci_plist_t plist , const char *name, uint32_t * val)
int pci_plist_lookup_int64(pci_plist_t plist , const char *name,int64_t * val)
int pci_plist_lookup_uint64(pci_plist_t plist , const char *name, uint64_t * val)
int pci_plist_lookup_string(pci_plist_t plist , const char *name, char ** val)
int pci_plist_lookup_plist(pci_plist_t plist , const char *name, pci_plist_t ** val)
int pci_plist_lookup_int8_array(pci_plist_t plist, const char *name,int8_t * val, uint_t *nelem)
int pci_plist_lookup_uint8_array(pci_plist_t plist, const char *name, int8_t * val, uint_t *nelem)
int pci_plist_lookup_int16_array(pci_plist_t plist, const char *name,int16_t * val, uint_t *nelem)
int pci_plist_lookup_uint16_array(pci_plist_t plist, const char *name, uint16_t *val, uint_t *nelem)
int pci_plist_lookup_int32_array(pci_plist_t plist, const char *name,int32_t * val, uint_t *nelem)
int pci_plist_lookup_uint32_array(pci_plist_t plist, const char *name,uint32_t *val, uint_t *nelem)
int pci_plist_lookup_int64_array(pci_plist_t plist, const char *name, int64_t *val, uint_t *nelem)
int pci_plist_lookup_uint64_array(pci_plist_t plist,const char *name,uint64_t * val, uint_t *nelem)
int pci_plist_lookup_string_array(pci_plist_t plist, const char *name,char ** val, uint_t *nelem)
其中:
指向要处理的 pci_plist_t 结构的指针。
要搜索的名称-值对的名称。
存储值中的元素数的地址。
数据的起始地址。
pci_plist_lookup() 函数在成功时返回 0,失败时返回某个错误值。支持以下错误值:
参数无效
未找到匹配的名称-值对
编码或解码方法不受支持
驱动程序在使用 param 句柄获取设备参数后必须调用 pci_param_free(9F) 接口。此调用将释放由 pci_param_get() 和 pci_param_get_ioctl() 接口分配的资源。
int pci_param_free (pci_param_t param)
其中,param 是从 pci_param_get() 或 pci_param_get_ioctl() 接口获取的句柄。
pciv_send(9F) 接口由具有 SR-IOV 功能的 PF 和 VF 驱动程序用来彼此进行通信。尽管 VF 驱动程序只能与其 PF 驱动程序进行通信,但 PF 驱动程序可与其任何 VF 驱动程序进行通信。
int pciv_send(dev_info_t *dip, pciv_pvp_req_t *req
其中:
指向 dev_info 结构的指针。
指向 pciv_pvp_req_t 结构的指针。
pciv_pvp_req_t 的结构为:
typedef struct pciv_pvp_req { int pvp_dstfunc; caddr_t pvp_buf; size_t pvp_nbyte; buf_cb_t pvp_cb; caddr_t pvp_cb_arg; uint_t pvp_flag; } pciv_pvp_req_t;
其中:
如果由 PF 驱动程序调用,则 VF 索引的范围是 1 到 num_vf。如果调用方是 VF 驱动程序,则它应当始终为 PCIV_PF。
要发送的调用方缓冲区的缓冲区地址。
要传送的字节数,必须小于 8k。
回调函数指针,如果 pvp_flag 设置为 PCIV_NOWAIT。
如果 pvp_flag 设置为 PCIV_NOWAIT,调用将立即返回,并在将 pvp_buf 中的数据传送到目标之前调用 pvp_cb 中的回调例程。随后,将允许调用方在其回调例程中释放缓冲区。
typedef void (*buf_cb_t)(int rc, caddr_t buf, size_t size, caddr_t cb_arg);
其中:
用于传输的 DDI 返回代码。
要发送的调用方缓冲区的缓冲区地址。
要传输的字节数。
调用方在调用例程时设置的输入参数。
pvp_cb 的回调输入参数,如果 pvp_flag 设置为 PCIV_NOWAIT。
PCIV_NOWAIT-不等待接收方的响应。
PCIV_WAIT-这是缺省状态。等待,直到接收方确认接收传输。
pciv_send() 接口返回以下返回值之一:
缓冲区已成功发送。
设备驱动程序不支持此操作。调用方可以使用其他机制,如硬件邮箱。
pvp_nbyte 或 pvp_dstfunc 无效。
操作因缺少资源而失败。
远程端未注册用于处理传入的传输的回调。
因意外原因失败。